not-pegasus commited on
Commit
8ac5b3e
·
verified ·
1 Parent(s): 2f99b25

Add files using upload-large-folder tool

Browse files
Files changed (50) hide show
  1. env/lib/python3.13/site-packages/anyio/__init__.py +111 -0
  2. env/lib/python3.13/site-packages/anyio/from_thread.py +570 -0
  3. env/lib/python3.13/site-packages/anyio/functools.py +347 -0
  4. env/lib/python3.13/site-packages/anyio/lowlevel.py +195 -0
  5. env/lib/python3.13/site-packages/anyio/py.typed +0 -0
  6. env/lib/python3.13/site-packages/anyio/pytest_plugin.py +302 -0
  7. env/lib/python3.13/site-packages/anyio/to_interpreter.py +246 -0
  8. env/lib/python3.13/site-packages/anyio/to_process.py +264 -0
  9. env/lib/python3.13/site-packages/anyio/to_thread.py +74 -0
  10. env/lib/python3.13/site-packages/certifi-2025.11.12.dist-info/INSTALLER +1 -0
  11. env/lib/python3.13/site-packages/certifi-2025.11.12.dist-info/METADATA +78 -0
  12. env/lib/python3.13/site-packages/certifi-2025.11.12.dist-info/RECORD +14 -0
  13. env/lib/python3.13/site-packages/certifi-2025.11.12.dist-info/WHEEL +5 -0
  14. env/lib/python3.13/site-packages/certifi-2025.11.12.dist-info/top_level.txt +1 -0
  15. env/lib/python3.13/site-packages/click/__init__.py +123 -0
  16. env/lib/python3.13/site-packages/click/_compat.py +622 -0
  17. env/lib/python3.13/site-packages/click/_termui_impl.py +852 -0
  18. env/lib/python3.13/site-packages/click/_textwrap.py +51 -0
  19. env/lib/python3.13/site-packages/click/_utils.py +36 -0
  20. env/lib/python3.13/site-packages/click/_winconsole.py +296 -0
  21. env/lib/python3.13/site-packages/click/core.py +0 -0
  22. env/lib/python3.13/site-packages/click/decorators.py +551 -0
  23. env/lib/python3.13/site-packages/click/exceptions.py +308 -0
  24. env/lib/python3.13/site-packages/click/formatting.py +301 -0
  25. env/lib/python3.13/site-packages/click/globals.py +67 -0
  26. env/lib/python3.13/site-packages/click/parser.py +532 -0
  27. env/lib/python3.13/site-packages/click/py.typed +0 -0
  28. env/lib/python3.13/site-packages/click/shell_completion.py +667 -0
  29. env/lib/python3.13/site-packages/click/termui.py +883 -0
  30. env/lib/python3.13/site-packages/click/testing.py +577 -0
  31. env/lib/python3.13/site-packages/click/types.py +1209 -0
  32. env/lib/python3.13/site-packages/click/utils.py +627 -0
  33. env/lib/python3.13/site-packages/shellingham-1.5.4.dist-info/INSTALLER +1 -0
  34. env/lib/python3.13/site-packages/shellingham-1.5.4.dist-info/LICENSE +13 -0
  35. env/lib/python3.13/site-packages/shellingham-1.5.4.dist-info/METADATA +106 -0
  36. env/lib/python3.13/site-packages/shellingham-1.5.4.dist-info/RECORD +21 -0
  37. env/lib/python3.13/site-packages/shellingham-1.5.4.dist-info/WHEEL +6 -0
  38. env/lib/python3.13/site-packages/shellingham-1.5.4.dist-info/top_level.txt +1 -0
  39. env/lib/python3.13/site-packages/shellingham-1.5.4.dist-info/zip-safe +1 -0
  40. env/lib/python3.13/site-packages/tqdm-4.67.1.dist-info/INSTALLER +1 -0
  41. env/lib/python3.13/site-packages/tqdm-4.67.1.dist-info/RECORD +74 -0
  42. env/lib/python3.13/site-packages/tqdm-4.67.1.dist-info/WHEEL +5 -0
  43. env/lib/python3.13/site-packages/typer/__main__.py +3 -0
  44. env/lib/python3.13/site-packages/typer/_completion_classes.py +211 -0
  45. env/lib/python3.13/site-packages/typer/_typing.py +105 -0
  46. env/lib/python3.13/site-packages/typer/cli.py +308 -0
  47. env/lib/python3.13/site-packages/typer/colors.py +20 -0
  48. env/lib/python3.13/site-packages/typer/models.py +544 -0
  49. env/lib/python3.13/site-packages/typer/params.py +479 -0
  50. env/lib/python3.13/site-packages/typer/utils.py +190 -0
env/lib/python3.13/site-packages/anyio/__init__.py ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from ._core._contextmanagers import AsyncContextManagerMixin as AsyncContextManagerMixin
4
+ from ._core._contextmanagers import ContextManagerMixin as ContextManagerMixin
5
+ from ._core._eventloop import current_time as current_time
6
+ from ._core._eventloop import get_all_backends as get_all_backends
7
+ from ._core._eventloop import get_available_backends as get_available_backends
8
+ from ._core._eventloop import get_cancelled_exc_class as get_cancelled_exc_class
9
+ from ._core._eventloop import run as run
10
+ from ._core._eventloop import sleep as sleep
11
+ from ._core._eventloop import sleep_forever as sleep_forever
12
+ from ._core._eventloop import sleep_until as sleep_until
13
+ from ._core._exceptions import BrokenResourceError as BrokenResourceError
14
+ from ._core._exceptions import BrokenWorkerInterpreter as BrokenWorkerInterpreter
15
+ from ._core._exceptions import BrokenWorkerProcess as BrokenWorkerProcess
16
+ from ._core._exceptions import BusyResourceError as BusyResourceError
17
+ from ._core._exceptions import ClosedResourceError as ClosedResourceError
18
+ from ._core._exceptions import ConnectionFailed as ConnectionFailed
19
+ from ._core._exceptions import DelimiterNotFound as DelimiterNotFound
20
+ from ._core._exceptions import EndOfStream as EndOfStream
21
+ from ._core._exceptions import IncompleteRead as IncompleteRead
22
+ from ._core._exceptions import NoEventLoopError as NoEventLoopError
23
+ from ._core._exceptions import RunFinishedError as RunFinishedError
24
+ from ._core._exceptions import TypedAttributeLookupError as TypedAttributeLookupError
25
+ from ._core._exceptions import WouldBlock as WouldBlock
26
+ from ._core._fileio import AsyncFile as AsyncFile
27
+ from ._core._fileio import Path as Path
28
+ from ._core._fileio import open_file as open_file
29
+ from ._core._fileio import wrap_file as wrap_file
30
+ from ._core._resources import aclose_forcefully as aclose_forcefully
31
+ from ._core._signals import open_signal_receiver as open_signal_receiver
32
+ from ._core._sockets import TCPConnectable as TCPConnectable
33
+ from ._core._sockets import UNIXConnectable as UNIXConnectable
34
+ from ._core._sockets import as_connectable as as_connectable
35
+ from ._core._sockets import connect_tcp as connect_tcp
36
+ from ._core._sockets import connect_unix as connect_unix
37
+ from ._core._sockets import create_connected_udp_socket as create_connected_udp_socket
38
+ from ._core._sockets import (
39
+ create_connected_unix_datagram_socket as create_connected_unix_datagram_socket,
40
+ )
41
+ from ._core._sockets import create_tcp_listener as create_tcp_listener
42
+ from ._core._sockets import create_udp_socket as create_udp_socket
43
+ from ._core._sockets import create_unix_datagram_socket as create_unix_datagram_socket
44
+ from ._core._sockets import create_unix_listener as create_unix_listener
45
+ from ._core._sockets import getaddrinfo as getaddrinfo
46
+ from ._core._sockets import getnameinfo as getnameinfo
47
+ from ._core._sockets import notify_closing as notify_closing
48
+ from ._core._sockets import wait_readable as wait_readable
49
+ from ._core._sockets import wait_socket_readable as wait_socket_readable
50
+ from ._core._sockets import wait_socket_writable as wait_socket_writable
51
+ from ._core._sockets import wait_writable as wait_writable
52
+ from ._core._streams import create_memory_object_stream as create_memory_object_stream
53
+ from ._core._subprocesses import open_process as open_process
54
+ from ._core._subprocesses import run_process as run_process
55
+ from ._core._synchronization import CapacityLimiter as CapacityLimiter
56
+ from ._core._synchronization import (
57
+ CapacityLimiterStatistics as CapacityLimiterStatistics,
58
+ )
59
+ from ._core._synchronization import Condition as Condition
60
+ from ._core._synchronization import ConditionStatistics as ConditionStatistics
61
+ from ._core._synchronization import Event as Event
62
+ from ._core._synchronization import EventStatistics as EventStatistics
63
+ from ._core._synchronization import Lock as Lock
64
+ from ._core._synchronization import LockStatistics as LockStatistics
65
+ from ._core._synchronization import ResourceGuard as ResourceGuard
66
+ from ._core._synchronization import Semaphore as Semaphore
67
+ from ._core._synchronization import SemaphoreStatistics as SemaphoreStatistics
68
+ from ._core._tasks import TASK_STATUS_IGNORED as TASK_STATUS_IGNORED
69
+ from ._core._tasks import CancelScope as CancelScope
70
+ from ._core._tasks import create_task_group as create_task_group
71
+ from ._core._tasks import current_effective_deadline as current_effective_deadline
72
+ from ._core._tasks import fail_after as fail_after
73
+ from ._core._tasks import move_on_after as move_on_after
74
+ from ._core._tempfile import NamedTemporaryFile as NamedTemporaryFile
75
+ from ._core._tempfile import SpooledTemporaryFile as SpooledTemporaryFile
76
+ from ._core._tempfile import TemporaryDirectory as TemporaryDirectory
77
+ from ._core._tempfile import TemporaryFile as TemporaryFile
78
+ from ._core._tempfile import gettempdir as gettempdir
79
+ from ._core._tempfile import gettempdirb as gettempdirb
80
+ from ._core._tempfile import mkdtemp as mkdtemp
81
+ from ._core._tempfile import mkstemp as mkstemp
82
+ from ._core._testing import TaskInfo as TaskInfo
83
+ from ._core._testing import get_current_task as get_current_task
84
+ from ._core._testing import get_running_tasks as get_running_tasks
85
+ from ._core._testing import wait_all_tasks_blocked as wait_all_tasks_blocked
86
+ from ._core._typedattr import TypedAttributeProvider as TypedAttributeProvider
87
+ from ._core._typedattr import TypedAttributeSet as TypedAttributeSet
88
+ from ._core._typedattr import typed_attribute as typed_attribute
89
+
90
+ # Re-export imports so they look like they live directly in this package
91
+ for __value in list(locals().values()):
92
+ if getattr(__value, "__module__", "").startswith("anyio."):
93
+ __value.__module__ = __name__
94
+
95
+
96
+ del __value
97
+
98
+
99
+ def __getattr__(attr: str) -> type[BrokenWorkerInterpreter]:
100
+ """Support deprecated aliases."""
101
+ if attr == "BrokenWorkerIntepreter":
102
+ import warnings
103
+
104
+ warnings.warn(
105
+ "The 'BrokenWorkerIntepreter' alias is deprecated, use 'BrokenWorkerInterpreter' instead.",
106
+ DeprecationWarning,
107
+ stacklevel=2,
108
+ )
109
+ return BrokenWorkerInterpreter
110
+
111
+ raise AttributeError(f"module {__name__!r} has no attribute {attr!r}")
env/lib/python3.13/site-packages/anyio/from_thread.py ADDED
@@ -0,0 +1,570 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ __all__ = (
4
+ "BlockingPortal",
5
+ "BlockingPortalProvider",
6
+ "check_cancelled",
7
+ "run",
8
+ "run_sync",
9
+ "start_blocking_portal",
10
+ )
11
+
12
+ import sys
13
+ from collections.abc import Awaitable, Callable, Generator
14
+ from concurrent.futures import Future
15
+ from contextlib import (
16
+ AbstractAsyncContextManager,
17
+ AbstractContextManager,
18
+ contextmanager,
19
+ )
20
+ from dataclasses import dataclass, field
21
+ from inspect import isawaitable
22
+ from threading import Lock, Thread, current_thread, get_ident
23
+ from types import TracebackType
24
+ from typing import (
25
+ Any,
26
+ Generic,
27
+ TypeVar,
28
+ cast,
29
+ overload,
30
+ )
31
+
32
+ from ._core._eventloop import (
33
+ get_async_backend,
34
+ get_cancelled_exc_class,
35
+ threadlocals,
36
+ )
37
+ from ._core._eventloop import run as run_eventloop
38
+ from ._core._exceptions import NoEventLoopError
39
+ from ._core._synchronization import Event
40
+ from ._core._tasks import CancelScope, create_task_group
41
+ from .abc._tasks import TaskStatus
42
+ from .lowlevel import EventLoopToken
43
+
44
+ if sys.version_info >= (3, 11):
45
+ from typing import TypeVarTuple, Unpack
46
+ else:
47
+ from typing_extensions import TypeVarTuple, Unpack
48
+
49
+ T_Retval = TypeVar("T_Retval")
50
+ T_co = TypeVar("T_co", covariant=True)
51
+ PosArgsT = TypeVarTuple("PosArgsT")
52
+
53
+
54
+ def _token_or_error(token: EventLoopToken | None) -> EventLoopToken:
55
+ if token is not None:
56
+ return token
57
+
58
+ try:
59
+ return threadlocals.current_token
60
+ except AttributeError:
61
+ raise NoEventLoopError(
62
+ "Not running inside an AnyIO worker thread, and no event loop token was "
63
+ "provided"
64
+ ) from None
65
+
66
+
67
+ def run(
68
+ func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]],
69
+ *args: Unpack[PosArgsT],
70
+ token: EventLoopToken | None = None,
71
+ ) -> T_Retval:
72
+ """
73
+ Call a coroutine function from a worker thread.
74
+
75
+ :param func: a coroutine function
76
+ :param args: positional arguments for the callable
77
+ :param token: an event loop token to use to get back to the event loop thread
78
+ (required if calling this function from outside an AnyIO worker thread)
79
+ :return: the return value of the coroutine function
80
+ :raises MissingTokenError: if no token was provided and called from outside an
81
+ AnyIO worker thread
82
+ :raises RunFinishedError: if the event loop tied to ``token`` is no longer running
83
+
84
+ .. versionchanged:: 4.11.0
85
+ Added the ``token`` parameter.
86
+
87
+ """
88
+ explicit_token = token is not None
89
+ token = _token_or_error(token)
90
+ return token.backend_class.run_async_from_thread(
91
+ func, args, token=token.native_token if explicit_token else None
92
+ )
93
+
94
+
95
+ def run_sync(
96
+ func: Callable[[Unpack[PosArgsT]], T_Retval],
97
+ *args: Unpack[PosArgsT],
98
+ token: EventLoopToken | None = None,
99
+ ) -> T_Retval:
100
+ """
101
+ Call a function in the event loop thread from a worker thread.
102
+
103
+ :param func: a callable
104
+ :param args: positional arguments for the callable
105
+ :param token: an event loop token to use to get back to the event loop thread
106
+ (required if calling this function from outside an AnyIO worker thread)
107
+ :return: the return value of the callable
108
+ :raises MissingTokenError: if no token was provided and called from outside an
109
+ AnyIO worker thread
110
+ :raises RunFinishedError: if the event loop tied to ``token`` is no longer running
111
+
112
+ .. versionchanged:: 4.11.0
113
+ Added the ``token`` parameter.
114
+
115
+ """
116
+ explicit_token = token is not None
117
+ token = _token_or_error(token)
118
+ return token.backend_class.run_sync_from_thread(
119
+ func, args, token=token.native_token if explicit_token else None
120
+ )
121
+
122
+
123
+ class _BlockingAsyncContextManager(Generic[T_co], AbstractContextManager):
124
+ _enter_future: Future[T_co]
125
+ _exit_future: Future[bool | None]
126
+ _exit_event: Event
127
+ _exit_exc_info: tuple[
128
+ type[BaseException] | None, BaseException | None, TracebackType | None
129
+ ] = (None, None, None)
130
+
131
+ def __init__(
132
+ self, async_cm: AbstractAsyncContextManager[T_co], portal: BlockingPortal
133
+ ):
134
+ self._async_cm = async_cm
135
+ self._portal = portal
136
+
137
+ async def run_async_cm(self) -> bool | None:
138
+ try:
139
+ self._exit_event = Event()
140
+ value = await self._async_cm.__aenter__()
141
+ except BaseException as exc:
142
+ self._enter_future.set_exception(exc)
143
+ raise
144
+ else:
145
+ self._enter_future.set_result(value)
146
+
147
+ try:
148
+ # Wait for the sync context manager to exit.
149
+ # This next statement can raise `get_cancelled_exc_class()` if
150
+ # something went wrong in a task group in this async context
151
+ # manager.
152
+ await self._exit_event.wait()
153
+ finally:
154
+ # In case of cancellation, it could be that we end up here before
155
+ # `_BlockingAsyncContextManager.__exit__` is called, and an
156
+ # `_exit_exc_info` has been set.
157
+ result = await self._async_cm.__aexit__(*self._exit_exc_info)
158
+
159
+ return result
160
+
161
+ def __enter__(self) -> T_co:
162
+ self._enter_future = Future()
163
+ self._exit_future = self._portal.start_task_soon(self.run_async_cm)
164
+ return self._enter_future.result()
165
+
166
+ def __exit__(
167
+ self,
168
+ __exc_type: type[BaseException] | None,
169
+ __exc_value: BaseException | None,
170
+ __traceback: TracebackType | None,
171
+ ) -> bool | None:
172
+ self._exit_exc_info = __exc_type, __exc_value, __traceback
173
+ self._portal.call(self._exit_event.set)
174
+ return self._exit_future.result()
175
+
176
+
177
+ class _BlockingPortalTaskStatus(TaskStatus):
178
+ def __init__(self, future: Future):
179
+ self._future = future
180
+
181
+ def started(self, value: object = None) -> None:
182
+ self._future.set_result(value)
183
+
184
+
185
+ class BlockingPortal:
186
+ """An object that lets external threads run code in an asynchronous event loop."""
187
+
188
+ def __new__(cls) -> BlockingPortal:
189
+ return get_async_backend().create_blocking_portal()
190
+
191
+ def __init__(self) -> None:
192
+ self._event_loop_thread_id: int | None = get_ident()
193
+ self._stop_event = Event()
194
+ self._task_group = create_task_group()
195
+ self._cancelled_exc_class = get_cancelled_exc_class()
196
+
197
+ async def __aenter__(self) -> BlockingPortal:
198
+ await self._task_group.__aenter__()
199
+ return self
200
+
201
+ async def __aexit__(
202
+ self,
203
+ exc_type: type[BaseException] | None,
204
+ exc_val: BaseException | None,
205
+ exc_tb: TracebackType | None,
206
+ ) -> bool:
207
+ await self.stop()
208
+ return await self._task_group.__aexit__(exc_type, exc_val, exc_tb)
209
+
210
+ def _check_running(self) -> None:
211
+ if self._event_loop_thread_id is None:
212
+ raise RuntimeError("This portal is not running")
213
+ if self._event_loop_thread_id == get_ident():
214
+ raise RuntimeError(
215
+ "This method cannot be called from the event loop thread"
216
+ )
217
+
218
+ async def sleep_until_stopped(self) -> None:
219
+ """Sleep until :meth:`stop` is called."""
220
+ await self._stop_event.wait()
221
+
222
+ async def stop(self, cancel_remaining: bool = False) -> None:
223
+ """
224
+ Signal the portal to shut down.
225
+
226
+ This marks the portal as no longer accepting new calls and exits from
227
+ :meth:`sleep_until_stopped`.
228
+
229
+ :param cancel_remaining: ``True`` to cancel all the remaining tasks, ``False``
230
+ to let them finish before returning
231
+
232
+ """
233
+ self._event_loop_thread_id = None
234
+ self._stop_event.set()
235
+ if cancel_remaining:
236
+ self._task_group.cancel_scope.cancel("the blocking portal is shutting down")
237
+
238
+ async def _call_func(
239
+ self,
240
+ func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval] | T_Retval],
241
+ args: tuple[Unpack[PosArgsT]],
242
+ kwargs: dict[str, Any],
243
+ future: Future[T_Retval],
244
+ ) -> None:
245
+ def callback(f: Future[T_Retval]) -> None:
246
+ if f.cancelled():
247
+ if self._event_loop_thread_id == get_ident():
248
+ scope.cancel("the future was cancelled")
249
+ elif self._event_loop_thread_id is not None:
250
+ self.call(scope.cancel, "the future was cancelled")
251
+
252
+ try:
253
+ retval_or_awaitable = func(*args, **kwargs)
254
+ if isawaitable(retval_or_awaitable):
255
+ with CancelScope() as scope:
256
+ future.add_done_callback(callback)
257
+ retval = await retval_or_awaitable
258
+ else:
259
+ retval = retval_or_awaitable
260
+ except self._cancelled_exc_class:
261
+ future.cancel()
262
+ future.set_running_or_notify_cancel()
263
+ except BaseException as exc:
264
+ if not future.cancelled():
265
+ future.set_exception(exc)
266
+
267
+ # Let base exceptions fall through
268
+ if not isinstance(exc, Exception):
269
+ raise
270
+ else:
271
+ if not future.cancelled():
272
+ future.set_result(retval)
273
+ finally:
274
+ scope = None # type: ignore[assignment]
275
+
276
+ def _spawn_task_from_thread(
277
+ self,
278
+ func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval] | T_Retval],
279
+ args: tuple[Unpack[PosArgsT]],
280
+ kwargs: dict[str, Any],
281
+ name: object,
282
+ future: Future[T_Retval],
283
+ ) -> None:
284
+ """
285
+ Spawn a new task using the given callable.
286
+
287
+ Implementers must ensure that the future is resolved when the task finishes.
288
+
289
+ :param func: a callable
290
+ :param args: positional arguments to be passed to the callable
291
+ :param kwargs: keyword arguments to be passed to the callable
292
+ :param name: name of the task (will be coerced to a string if not ``None``)
293
+ :param future: a future that will resolve to the return value of the callable,
294
+ or the exception raised during its execution
295
+
296
+ """
297
+ raise NotImplementedError
298
+
299
+ @overload
300
+ def call(
301
+ self,
302
+ func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]],
303
+ *args: Unpack[PosArgsT],
304
+ ) -> T_Retval: ...
305
+
306
+ @overload
307
+ def call(
308
+ self, func: Callable[[Unpack[PosArgsT]], T_Retval], *args: Unpack[PosArgsT]
309
+ ) -> T_Retval: ...
310
+
311
+ def call(
312
+ self,
313
+ func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval] | T_Retval],
314
+ *args: Unpack[PosArgsT],
315
+ ) -> T_Retval:
316
+ """
317
+ Call the given function in the event loop thread.
318
+
319
+ If the callable returns a coroutine object, it is awaited on.
320
+
321
+ :param func: any callable
322
+ :raises RuntimeError: if the portal is not running or if this method is called
323
+ from within the event loop thread
324
+
325
+ """
326
+ return cast(T_Retval, self.start_task_soon(func, *args).result())
327
+
328
+ @overload
329
+ def start_task_soon(
330
+ self,
331
+ func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]],
332
+ *args: Unpack[PosArgsT],
333
+ name: object = None,
334
+ ) -> Future[T_Retval]: ...
335
+
336
+ @overload
337
+ def start_task_soon(
338
+ self,
339
+ func: Callable[[Unpack[PosArgsT]], T_Retval],
340
+ *args: Unpack[PosArgsT],
341
+ name: object = None,
342
+ ) -> Future[T_Retval]: ...
343
+
344
+ def start_task_soon(
345
+ self,
346
+ func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval] | T_Retval],
347
+ *args: Unpack[PosArgsT],
348
+ name: object = None,
349
+ ) -> Future[T_Retval]:
350
+ """
351
+ Start a task in the portal's task group.
352
+
353
+ The task will be run inside a cancel scope which can be cancelled by cancelling
354
+ the returned future.
355
+
356
+ :param func: the target function
357
+ :param args: positional arguments passed to ``func``
358
+ :param name: name of the task (will be coerced to a string if not ``None``)
359
+ :return: a future that resolves with the return value of the callable if the
360
+ task completes successfully, or with the exception raised in the task
361
+ :raises RuntimeError: if the portal is not running or if this method is called
362
+ from within the event loop thread
363
+ :rtype: concurrent.futures.Future[T_Retval]
364
+
365
+ .. versionadded:: 3.0
366
+
367
+ """
368
+ self._check_running()
369
+ f: Future[T_Retval] = Future()
370
+ self._spawn_task_from_thread(func, args, {}, name, f)
371
+ return f
372
+
373
+ def start_task(
374
+ self,
375
+ func: Callable[..., Awaitable[T_Retval]],
376
+ *args: object,
377
+ name: object = None,
378
+ ) -> tuple[Future[T_Retval], Any]:
379
+ """
380
+ Start a task in the portal's task group and wait until it signals for readiness.
381
+
382
+ This method works the same way as :meth:`.abc.TaskGroup.start`.
383
+
384
+ :param func: the target function
385
+ :param args: positional arguments passed to ``func``
386
+ :param name: name of the task (will be coerced to a string if not ``None``)
387
+ :return: a tuple of (future, task_status_value) where the ``task_status_value``
388
+ is the value passed to ``task_status.started()`` from within the target
389
+ function
390
+ :rtype: tuple[concurrent.futures.Future[T_Retval], Any]
391
+
392
+ .. versionadded:: 3.0
393
+
394
+ """
395
+
396
+ def task_done(future: Future[T_Retval]) -> None:
397
+ if not task_status_future.done():
398
+ if future.cancelled():
399
+ task_status_future.cancel()
400
+ elif future.exception():
401
+ task_status_future.set_exception(future.exception())
402
+ else:
403
+ exc = RuntimeError(
404
+ "Task exited without calling task_status.started()"
405
+ )
406
+ task_status_future.set_exception(exc)
407
+
408
+ self._check_running()
409
+ task_status_future: Future = Future()
410
+ task_status = _BlockingPortalTaskStatus(task_status_future)
411
+ f: Future = Future()
412
+ f.add_done_callback(task_done)
413
+ self._spawn_task_from_thread(func, args, {"task_status": task_status}, name, f)
414
+ return f, task_status_future.result()
415
+
416
+ def wrap_async_context_manager(
417
+ self, cm: AbstractAsyncContextManager[T_co]
418
+ ) -> AbstractContextManager[T_co]:
419
+ """
420
+ Wrap an async context manager as a synchronous context manager via this portal.
421
+
422
+ Spawns a task that will call both ``__aenter__()`` and ``__aexit__()``, stopping
423
+ in the middle until the synchronous context manager exits.
424
+
425
+ :param cm: an asynchronous context manager
426
+ :return: a synchronous context manager
427
+
428
+ .. versionadded:: 2.1
429
+
430
+ """
431
+ return _BlockingAsyncContextManager(cm, self)
432
+
433
+
434
+ @dataclass
435
+ class BlockingPortalProvider:
436
+ """
437
+ A manager for a blocking portal. Used as a context manager. The first thread to
438
+ enter this context manager causes a blocking portal to be started with the specific
439
+ parameters, and the last thread to exit causes the portal to be shut down. Thus,
440
+ there will be exactly one blocking portal running in this context as long as at
441
+ least one thread has entered this context manager.
442
+
443
+ The parameters are the same as for :func:`~anyio.run`.
444
+
445
+ :param backend: name of the backend
446
+ :param backend_options: backend options
447
+
448
+ .. versionadded:: 4.4
449
+ """
450
+
451
+ backend: str = "asyncio"
452
+ backend_options: dict[str, Any] | None = None
453
+ _lock: Lock = field(init=False, default_factory=Lock)
454
+ _leases: int = field(init=False, default=0)
455
+ _portal: BlockingPortal = field(init=False)
456
+ _portal_cm: AbstractContextManager[BlockingPortal] | None = field(
457
+ init=False, default=None
458
+ )
459
+
460
+ def __enter__(self) -> BlockingPortal:
461
+ with self._lock:
462
+ if self._portal_cm is None:
463
+ self._portal_cm = start_blocking_portal(
464
+ self.backend, self.backend_options
465
+ )
466
+ self._portal = self._portal_cm.__enter__()
467
+
468
+ self._leases += 1
469
+ return self._portal
470
+
471
+ def __exit__(
472
+ self,
473
+ exc_type: type[BaseException] | None,
474
+ exc_val: BaseException | None,
475
+ exc_tb: TracebackType | None,
476
+ ) -> None:
477
+ portal_cm: AbstractContextManager[BlockingPortal] | None = None
478
+ with self._lock:
479
+ assert self._portal_cm
480
+ assert self._leases > 0
481
+ self._leases -= 1
482
+ if not self._leases:
483
+ portal_cm = self._portal_cm
484
+ self._portal_cm = None
485
+ del self._portal
486
+
487
+ if portal_cm:
488
+ portal_cm.__exit__(None, None, None)
489
+
490
+
491
+ @contextmanager
492
+ def start_blocking_portal(
493
+ backend: str = "asyncio",
494
+ backend_options: dict[str, Any] | None = None,
495
+ *,
496
+ name: str | None = None,
497
+ ) -> Generator[BlockingPortal, Any, None]:
498
+ """
499
+ Start a new event loop in a new thread and run a blocking portal in its main task.
500
+
501
+ The parameters are the same as for :func:`~anyio.run`.
502
+
503
+ :param backend: name of the backend
504
+ :param backend_options: backend options
505
+ :param name: name of the thread
506
+ :return: a context manager that yields a blocking portal
507
+
508
+ .. versionchanged:: 3.0
509
+ Usage as a context manager is now required.
510
+
511
+ """
512
+
513
+ async def run_portal() -> None:
514
+ async with BlockingPortal() as portal_:
515
+ if name is None:
516
+ current_thread().name = f"{backend}-portal-{id(portal_):x}"
517
+
518
+ future.set_result(portal_)
519
+ await portal_.sleep_until_stopped()
520
+
521
+ def run_blocking_portal() -> None:
522
+ if future.set_running_or_notify_cancel():
523
+ try:
524
+ run_eventloop(
525
+ run_portal, backend=backend, backend_options=backend_options
526
+ )
527
+ except BaseException as exc:
528
+ if not future.done():
529
+ future.set_exception(exc)
530
+
531
+ future: Future[BlockingPortal] = Future()
532
+ thread = Thread(target=run_blocking_portal, daemon=True, name=name)
533
+ thread.start()
534
+ try:
535
+ cancel_remaining_tasks = False
536
+ portal = future.result()
537
+ try:
538
+ yield portal
539
+ except BaseException:
540
+ cancel_remaining_tasks = True
541
+ raise
542
+ finally:
543
+ try:
544
+ portal.call(portal.stop, cancel_remaining_tasks)
545
+ except RuntimeError:
546
+ pass
547
+ finally:
548
+ thread.join()
549
+
550
+
551
+ def check_cancelled() -> None:
552
+ """
553
+ Check if the cancel scope of the host task's running the current worker thread has
554
+ been cancelled.
555
+
556
+ If the host task's current cancel scope has indeed been cancelled, the
557
+ backend-specific cancellation exception will be raised.
558
+
559
+ :raises RuntimeError: if the current thread was not spawned by
560
+ :func:`.to_thread.run_sync`
561
+
562
+ """
563
+ try:
564
+ token: EventLoopToken = threadlocals.current_token
565
+ except AttributeError:
566
+ raise NoEventLoopError(
567
+ "This function can only be called inside an AnyIO worker thread"
568
+ ) from None
569
+
570
+ token.backend_class.check_cancelled()
env/lib/python3.13/site-packages/anyio/functools.py ADDED
@@ -0,0 +1,347 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ __all__ = (
4
+ "AsyncCacheInfo",
5
+ "AsyncCacheParameters",
6
+ "AsyncLRUCacheWrapper",
7
+ "cache",
8
+ "lru_cache",
9
+ "reduce",
10
+ )
11
+
12
+ import functools
13
+ import sys
14
+ from collections import OrderedDict
15
+ from collections.abc import (
16
+ AsyncIterable,
17
+ Awaitable,
18
+ Callable,
19
+ Coroutine,
20
+ Hashable,
21
+ Iterable,
22
+ )
23
+ from functools import update_wrapper
24
+ from inspect import iscoroutinefunction
25
+ from typing import (
26
+ Any,
27
+ Generic,
28
+ NamedTuple,
29
+ TypedDict,
30
+ TypeVar,
31
+ cast,
32
+ final,
33
+ overload,
34
+ )
35
+ from weakref import WeakKeyDictionary
36
+
37
+ from ._core._synchronization import Lock
38
+ from .lowlevel import RunVar, checkpoint
39
+
40
+ if sys.version_info >= (3, 11):
41
+ from typing import ParamSpec
42
+ else:
43
+ from typing_extensions import ParamSpec
44
+
45
+ T = TypeVar("T")
46
+ S = TypeVar("S")
47
+ P = ParamSpec("P")
48
+ lru_cache_items: RunVar[
49
+ WeakKeyDictionary[
50
+ AsyncLRUCacheWrapper[Any, Any],
51
+ OrderedDict[Hashable, tuple[_InitialMissingType, Lock] | tuple[Any, None]],
52
+ ]
53
+ ] = RunVar("lru_cache_items")
54
+
55
+
56
+ class _InitialMissingType:
57
+ pass
58
+
59
+
60
+ initial_missing: _InitialMissingType = _InitialMissingType()
61
+
62
+
63
+ class AsyncCacheInfo(NamedTuple):
64
+ hits: int
65
+ misses: int
66
+ maxsize: int | None
67
+ currsize: int
68
+
69
+
70
+ class AsyncCacheParameters(TypedDict):
71
+ maxsize: int | None
72
+ typed: bool
73
+ always_checkpoint: bool
74
+
75
+
76
+ @final
77
+ class AsyncLRUCacheWrapper(Generic[P, T]):
78
+ def __init__(
79
+ self,
80
+ func: Callable[..., Awaitable[T]],
81
+ maxsize: int | None,
82
+ typed: bool,
83
+ always_checkpoint: bool,
84
+ ):
85
+ self.__wrapped__ = func
86
+ self._hits: int = 0
87
+ self._misses: int = 0
88
+ self._maxsize = max(maxsize, 0) if maxsize is not None else None
89
+ self._currsize: int = 0
90
+ self._typed = typed
91
+ self._always_checkpoint = always_checkpoint
92
+ update_wrapper(self, func)
93
+
94
+ def cache_info(self) -> AsyncCacheInfo:
95
+ return AsyncCacheInfo(self._hits, self._misses, self._maxsize, self._currsize)
96
+
97
+ def cache_parameters(self) -> AsyncCacheParameters:
98
+ return {
99
+ "maxsize": self._maxsize,
100
+ "typed": self._typed,
101
+ "always_checkpoint": self._always_checkpoint,
102
+ }
103
+
104
+ def cache_clear(self) -> None:
105
+ if cache := lru_cache_items.get(None):
106
+ cache.pop(self, None)
107
+ self._hits = self._misses = self._currsize = 0
108
+
109
+ async def __call__(self, *args: P.args, **kwargs: P.kwargs) -> T:
110
+ # Easy case first: if maxsize == 0, no caching is done
111
+ if self._maxsize == 0:
112
+ value = await self.__wrapped__(*args, **kwargs)
113
+ self._misses += 1
114
+ return value
115
+
116
+ # The key is constructed as a flat tuple to avoid memory overhead
117
+ key: tuple[Any, ...] = args
118
+ if kwargs:
119
+ # initial_missing is used as a separator
120
+ key += (initial_missing,) + sum(kwargs.items(), ())
121
+
122
+ if self._typed:
123
+ key += tuple(type(arg) for arg in args)
124
+ if kwargs:
125
+ key += (initial_missing,) + tuple(type(val) for val in kwargs.values())
126
+
127
+ try:
128
+ cache = lru_cache_items.get()
129
+ except LookupError:
130
+ cache = WeakKeyDictionary()
131
+ lru_cache_items.set(cache)
132
+
133
+ try:
134
+ cache_entry = cache[self]
135
+ except KeyError:
136
+ cache_entry = cache[self] = OrderedDict()
137
+
138
+ cached_value: T | _InitialMissingType
139
+ try:
140
+ cached_value, lock = cache_entry[key]
141
+ except KeyError:
142
+ # We're the first task to call this function
143
+ cached_value, lock = (
144
+ initial_missing,
145
+ Lock(fast_acquire=not self._always_checkpoint),
146
+ )
147
+ cache_entry[key] = cached_value, lock
148
+
149
+ if lock is None:
150
+ # The value was already cached
151
+ self._hits += 1
152
+ cache_entry.move_to_end(key)
153
+ if self._always_checkpoint:
154
+ await checkpoint()
155
+
156
+ return cast(T, cached_value)
157
+
158
+ async with lock:
159
+ # Check if another task filled the cache while we acquired the lock
160
+ if (cached_value := cache_entry[key][0]) is initial_missing:
161
+ self._misses += 1
162
+ if self._maxsize is not None and self._currsize >= self._maxsize:
163
+ cache_entry.popitem(last=False)
164
+ else:
165
+ self._currsize += 1
166
+
167
+ value = await self.__wrapped__(*args, **kwargs)
168
+ cache_entry[key] = value, None
169
+ else:
170
+ # Another task filled the cache while we were waiting for the lock
171
+ self._hits += 1
172
+ cache_entry.move_to_end(key)
173
+ value = cast(T, cached_value)
174
+
175
+ return value
176
+
177
+
178
+ class _LRUCacheWrapper(Generic[T]):
179
+ def __init__(self, maxsize: int | None, typed: bool, always_checkpoint: bool):
180
+ self._maxsize = maxsize
181
+ self._typed = typed
182
+ self._always_checkpoint = always_checkpoint
183
+
184
+ @overload
185
+ def __call__( # type: ignore[overload-overlap]
186
+ self, func: Callable[P, Coroutine[Any, Any, T]], /
187
+ ) -> AsyncLRUCacheWrapper[P, T]: ...
188
+
189
+ @overload
190
+ def __call__(
191
+ self, func: Callable[..., T], /
192
+ ) -> functools._lru_cache_wrapper[T]: ...
193
+
194
+ def __call__(
195
+ self, f: Callable[P, Coroutine[Any, Any, T]] | Callable[..., T], /
196
+ ) -> AsyncLRUCacheWrapper[P, T] | functools._lru_cache_wrapper[T]:
197
+ if iscoroutinefunction(f):
198
+ return AsyncLRUCacheWrapper(
199
+ f, self._maxsize, self._typed, self._always_checkpoint
200
+ )
201
+
202
+ return functools.lru_cache(maxsize=self._maxsize, typed=self._typed)(f) # type: ignore[arg-type]
203
+
204
+
205
+ @overload
206
+ def cache( # type: ignore[overload-overlap]
207
+ func: Callable[P, Coroutine[Any, Any, T]], /
208
+ ) -> AsyncLRUCacheWrapper[P, T]: ...
209
+
210
+
211
+ @overload
212
+ def cache(func: Callable[..., T], /) -> functools._lru_cache_wrapper[T]: ...
213
+
214
+
215
+ def cache(
216
+ func: Callable[..., T] | Callable[P, Coroutine[Any, Any, T]], /
217
+ ) -> AsyncLRUCacheWrapper[P, T] | functools._lru_cache_wrapper[T]:
218
+ """
219
+ A convenient shortcut for :func:`lru_cache` with ``maxsize=None``.
220
+
221
+ This is the asynchronous equivalent to :func:`functools.cache`.
222
+
223
+ """
224
+ return lru_cache(maxsize=None)(func)
225
+
226
+
227
+ @overload
228
+ def lru_cache(
229
+ *, maxsize: int | None = ..., typed: bool = ..., always_checkpoint: bool = ...
230
+ ) -> _LRUCacheWrapper[Any]: ...
231
+
232
+
233
+ @overload
234
+ def lru_cache( # type: ignore[overload-overlap]
235
+ func: Callable[P, Coroutine[Any, Any, T]], /
236
+ ) -> AsyncLRUCacheWrapper[P, T]: ...
237
+
238
+
239
+ @overload
240
+ def lru_cache(func: Callable[..., T], /) -> functools._lru_cache_wrapper[T]: ...
241
+
242
+
243
+ def lru_cache(
244
+ func: Callable[P, Coroutine[Any, Any, T]] | Callable[..., T] | None = None,
245
+ /,
246
+ *,
247
+ maxsize: int | None = 128,
248
+ typed: bool = False,
249
+ always_checkpoint: bool = False,
250
+ ) -> (
251
+ AsyncLRUCacheWrapper[P, T] | functools._lru_cache_wrapper[T] | _LRUCacheWrapper[Any]
252
+ ):
253
+ """
254
+ An asynchronous version of :func:`functools.lru_cache`.
255
+
256
+ If a synchronous function is passed, the standard library
257
+ :func:`functools.lru_cache` is applied instead.
258
+
259
+ :param always_checkpoint: if ``True``, every call to the cached function will be
260
+ guaranteed to yield control to the event loop at least once
261
+
262
+ .. note:: Caches and locks are managed on a per-event loop basis.
263
+
264
+ """
265
+ if func is None:
266
+ return _LRUCacheWrapper[Any](maxsize, typed, always_checkpoint)
267
+
268
+ if not callable(func):
269
+ raise TypeError("the first argument must be callable")
270
+
271
+ return _LRUCacheWrapper[T](maxsize, typed, always_checkpoint)(func)
272
+
273
+
274
+ @overload
275
+ async def reduce(
276
+ function: Callable[[T, S], Awaitable[T]],
277
+ iterable: Iterable[S] | AsyncIterable[S],
278
+ /,
279
+ initial: T,
280
+ ) -> T: ...
281
+
282
+
283
+ @overload
284
+ async def reduce(
285
+ function: Callable[[T, T], Awaitable[T]],
286
+ iterable: Iterable[T] | AsyncIterable[T],
287
+ /,
288
+ ) -> T: ...
289
+
290
+
291
+ async def reduce( # type: ignore[misc]
292
+ function: Callable[[T, T], Awaitable[T]] | Callable[[T, S], Awaitable[T]],
293
+ iterable: Iterable[T] | Iterable[S] | AsyncIterable[T] | AsyncIterable[S],
294
+ /,
295
+ initial: T | _InitialMissingType = initial_missing,
296
+ ) -> T:
297
+ """
298
+ Asynchronous version of :func:`functools.reduce`.
299
+
300
+ :param function: a coroutine function that takes two arguments: the accumulated
301
+ value and the next element from the iterable
302
+ :param iterable: an iterable or async iterable
303
+ :param initial: the initial value (if missing, the first element of the iterable is
304
+ used as the initial value)
305
+
306
+ """
307
+ element: Any
308
+ function_called = False
309
+ if isinstance(iterable, AsyncIterable):
310
+ async_it = iterable.__aiter__()
311
+ if initial is initial_missing:
312
+ try:
313
+ value = cast(T, await async_it.__anext__())
314
+ except StopAsyncIteration:
315
+ raise TypeError(
316
+ "reduce() of empty sequence with no initial value"
317
+ ) from None
318
+ else:
319
+ value = cast(T, initial)
320
+
321
+ async for element in async_it:
322
+ value = await function(value, element)
323
+ function_called = True
324
+ elif isinstance(iterable, Iterable):
325
+ it = iter(iterable)
326
+ if initial is initial_missing:
327
+ try:
328
+ value = cast(T, next(it))
329
+ except StopIteration:
330
+ raise TypeError(
331
+ "reduce() of empty sequence with no initial value"
332
+ ) from None
333
+ else:
334
+ value = cast(T, initial)
335
+
336
+ for element in it:
337
+ value = await function(value, element)
338
+ function_called = True
339
+ else:
340
+ raise TypeError("reduce() argument 2 must be an iterable or async iterable")
341
+
342
+ # Make sure there is at least one checkpoint, even if an empty iterable and an
343
+ # initial value were given
344
+ if not function_called:
345
+ await checkpoint()
346
+
347
+ return value
env/lib/python3.13/site-packages/anyio/lowlevel.py ADDED
@@ -0,0 +1,195 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ __all__ = (
4
+ "EventLoopToken",
5
+ "RunvarToken",
6
+ "RunVar",
7
+ "checkpoint",
8
+ "checkpoint_if_cancelled",
9
+ "cancel_shielded_checkpoint",
10
+ "current_token",
11
+ )
12
+
13
+ import enum
14
+ from dataclasses import dataclass
15
+ from types import TracebackType
16
+ from typing import Any, Generic, Literal, TypeVar, final, overload
17
+ from weakref import WeakKeyDictionary
18
+
19
+ from ._core._eventloop import get_async_backend
20
+ from .abc import AsyncBackend
21
+
22
+ T = TypeVar("T")
23
+ D = TypeVar("D")
24
+
25
+
26
+ async def checkpoint() -> None:
27
+ """
28
+ Check for cancellation and allow the scheduler to switch to another task.
29
+
30
+ Equivalent to (but more efficient than)::
31
+
32
+ await checkpoint_if_cancelled()
33
+ await cancel_shielded_checkpoint()
34
+
35
+
36
+ .. versionadded:: 3.0
37
+
38
+ """
39
+ await get_async_backend().checkpoint()
40
+
41
+
42
+ async def checkpoint_if_cancelled() -> None:
43
+ """
44
+ Enter a checkpoint if the enclosing cancel scope has been cancelled.
45
+
46
+ This does not allow the scheduler to switch to a different task.
47
+
48
+ .. versionadded:: 3.0
49
+
50
+ """
51
+ await get_async_backend().checkpoint_if_cancelled()
52
+
53
+
54
+ async def cancel_shielded_checkpoint() -> None:
55
+ """
56
+ Allow the scheduler to switch to another task but without checking for cancellation.
57
+
58
+ Equivalent to (but potentially more efficient than)::
59
+
60
+ with CancelScope(shield=True):
61
+ await checkpoint()
62
+
63
+
64
+ .. versionadded:: 3.0
65
+
66
+ """
67
+ await get_async_backend().cancel_shielded_checkpoint()
68
+
69
+
70
+ @final
71
+ @dataclass(frozen=True, repr=False)
72
+ class EventLoopToken:
73
+ """
74
+ An opaque object that holds a reference to an event loop.
75
+
76
+ .. versionadded:: 4.11.0
77
+ """
78
+
79
+ backend_class: type[AsyncBackend]
80
+ native_token: object
81
+
82
+
83
+ def current_token() -> EventLoopToken:
84
+ """
85
+ Return a token object that can be used to call code in the current event loop from
86
+ another thread.
87
+
88
+ .. versionadded:: 4.11.0
89
+
90
+ """
91
+ backend_class = get_async_backend()
92
+ raw_token = backend_class.current_token()
93
+ return EventLoopToken(backend_class, raw_token)
94
+
95
+
96
+ _run_vars: WeakKeyDictionary[object, dict[RunVar[Any], Any]] = WeakKeyDictionary()
97
+
98
+
99
+ class _NoValueSet(enum.Enum):
100
+ NO_VALUE_SET = enum.auto()
101
+
102
+
103
+ class RunvarToken(Generic[T]):
104
+ __slots__ = "_var", "_value", "_redeemed"
105
+
106
+ def __init__(self, var: RunVar[T], value: T | Literal[_NoValueSet.NO_VALUE_SET]):
107
+ self._var = var
108
+ self._value: T | Literal[_NoValueSet.NO_VALUE_SET] = value
109
+ self._redeemed = False
110
+
111
+ def __enter__(self) -> RunvarToken[T]:
112
+ return self
113
+
114
+ def __exit__(
115
+ self,
116
+ exc_type: type[BaseException] | None,
117
+ exc_val: BaseException | None,
118
+ exc_tb: TracebackType | None,
119
+ ) -> None:
120
+ self._var.reset(self)
121
+
122
+
123
+ class RunVar(Generic[T]):
124
+ """
125
+ Like a :class:`~contextvars.ContextVar`, except scoped to the running event loop.
126
+
127
+ Can be used as a context manager, Just like :class:`~contextvars.ContextVar`, that
128
+ will reset the variable to its previous value when the context block is exited.
129
+ """
130
+
131
+ __slots__ = "_name", "_default"
132
+
133
+ NO_VALUE_SET: Literal[_NoValueSet.NO_VALUE_SET] = _NoValueSet.NO_VALUE_SET
134
+
135
+ def __init__(
136
+ self, name: str, default: T | Literal[_NoValueSet.NO_VALUE_SET] = NO_VALUE_SET
137
+ ):
138
+ self._name = name
139
+ self._default = default
140
+
141
+ @property
142
+ def _current_vars(self) -> dict[RunVar[T], T]:
143
+ native_token = current_token().native_token
144
+ try:
145
+ return _run_vars[native_token]
146
+ except KeyError:
147
+ run_vars = _run_vars[native_token] = {}
148
+ return run_vars
149
+
150
+ @overload
151
+ def get(self, default: D) -> T | D: ...
152
+
153
+ @overload
154
+ def get(self) -> T: ...
155
+
156
+ def get(
157
+ self, default: D | Literal[_NoValueSet.NO_VALUE_SET] = NO_VALUE_SET
158
+ ) -> T | D:
159
+ try:
160
+ return self._current_vars[self]
161
+ except KeyError:
162
+ if default is not RunVar.NO_VALUE_SET:
163
+ return default
164
+ elif self._default is not RunVar.NO_VALUE_SET:
165
+ return self._default
166
+
167
+ raise LookupError(
168
+ f'Run variable "{self._name}" has no value and no default set'
169
+ )
170
+
171
+ def set(self, value: T) -> RunvarToken[T]:
172
+ current_vars = self._current_vars
173
+ token = RunvarToken(self, current_vars.get(self, RunVar.NO_VALUE_SET))
174
+ current_vars[self] = value
175
+ return token
176
+
177
+ def reset(self, token: RunvarToken[T]) -> None:
178
+ if token._var is not self:
179
+ raise ValueError("This token does not belong to this RunVar")
180
+
181
+ if token._redeemed:
182
+ raise ValueError("This token has already been used")
183
+
184
+ if token._value is _NoValueSet.NO_VALUE_SET:
185
+ try:
186
+ del self._current_vars[self]
187
+ except KeyError:
188
+ pass
189
+ else:
190
+ self._current_vars[self] = token._value
191
+
192
+ token._redeemed = True
193
+
194
+ def __repr__(self) -> str:
195
+ return f"<RunVar name={self._name!r}>"
env/lib/python3.13/site-packages/anyio/py.typed ADDED
File without changes
env/lib/python3.13/site-packages/anyio/pytest_plugin.py ADDED
@@ -0,0 +1,302 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import socket
4
+ import sys
5
+ from collections.abc import Callable, Generator, Iterator
6
+ from contextlib import ExitStack, contextmanager
7
+ from inspect import isasyncgenfunction, iscoroutinefunction, ismethod
8
+ from typing import Any, cast
9
+
10
+ import pytest
11
+ from _pytest.fixtures import SubRequest
12
+ from _pytest.outcomes import Exit
13
+
14
+ from . import get_available_backends
15
+ from ._core._eventloop import (
16
+ current_async_library,
17
+ get_async_backend,
18
+ reset_current_async_library,
19
+ set_current_async_library,
20
+ )
21
+ from ._core._exceptions import iterate_exceptions
22
+ from .abc import TestRunner
23
+
24
+ if sys.version_info < (3, 11):
25
+ from exceptiongroup import ExceptionGroup
26
+
27
+ _current_runner: TestRunner | None = None
28
+ _runner_stack: ExitStack | None = None
29
+ _runner_leases = 0
30
+
31
+
32
+ def extract_backend_and_options(backend: object) -> tuple[str, dict[str, Any]]:
33
+ if isinstance(backend, str):
34
+ return backend, {}
35
+ elif isinstance(backend, tuple) and len(backend) == 2:
36
+ if isinstance(backend[0], str) and isinstance(backend[1], dict):
37
+ return cast(tuple[str, dict[str, Any]], backend)
38
+
39
+ raise TypeError("anyio_backend must be either a string or tuple of (string, dict)")
40
+
41
+
42
+ @contextmanager
43
+ def get_runner(
44
+ backend_name: str, backend_options: dict[str, Any]
45
+ ) -> Iterator[TestRunner]:
46
+ global _current_runner, _runner_leases, _runner_stack
47
+ if _current_runner is None:
48
+ asynclib = get_async_backend(backend_name)
49
+ _runner_stack = ExitStack()
50
+ if current_async_library() is None:
51
+ # Since we're in control of the event loop, we can cache the name of the
52
+ # async library
53
+ token = set_current_async_library(backend_name)
54
+ _runner_stack.callback(reset_current_async_library, token)
55
+
56
+ backend_options = backend_options or {}
57
+ _current_runner = _runner_stack.enter_context(
58
+ asynclib.create_test_runner(backend_options)
59
+ )
60
+
61
+ _runner_leases += 1
62
+ try:
63
+ yield _current_runner
64
+ finally:
65
+ _runner_leases -= 1
66
+ if not _runner_leases:
67
+ assert _runner_stack is not None
68
+ _runner_stack.close()
69
+ _runner_stack = _current_runner = None
70
+
71
+
72
+ def pytest_addoption(parser: pytest.Parser) -> None:
73
+ parser.addini(
74
+ "anyio_mode",
75
+ default="strict",
76
+ help='AnyIO plugin mode (either "strict" or "auto")',
77
+ )
78
+
79
+
80
+ def pytest_configure(config: pytest.Config) -> None:
81
+ config.addinivalue_line(
82
+ "markers",
83
+ "anyio: mark the (coroutine function) test to be run asynchronously via anyio.",
84
+ )
85
+ if (
86
+ config.getini("anyio_mode") == "auto"
87
+ and config.pluginmanager.has_plugin("asyncio")
88
+ and config.getini("asyncio_mode") == "auto"
89
+ ):
90
+ config.issue_config_time_warning(
91
+ pytest.PytestConfigWarning(
92
+ "AnyIO auto mode has been enabled together with pytest-asyncio auto "
93
+ "mode. This may cause unexpected behavior."
94
+ ),
95
+ 1,
96
+ )
97
+
98
+
99
+ @pytest.hookimpl(hookwrapper=True)
100
+ def pytest_fixture_setup(fixturedef: Any, request: Any) -> Generator[Any]:
101
+ def wrapper(anyio_backend: Any, request: SubRequest, **kwargs: Any) -> Any:
102
+ # Rebind any fixture methods to the request instance
103
+ if (
104
+ request.instance
105
+ and ismethod(func)
106
+ and type(func.__self__) is type(request.instance)
107
+ ):
108
+ local_func = func.__func__.__get__(request.instance)
109
+ else:
110
+ local_func = func
111
+
112
+ backend_name, backend_options = extract_backend_and_options(anyio_backend)
113
+ if has_backend_arg:
114
+ kwargs["anyio_backend"] = anyio_backend
115
+
116
+ if has_request_arg:
117
+ kwargs["request"] = request
118
+
119
+ with get_runner(backend_name, backend_options) as runner:
120
+ if isasyncgenfunction(local_func):
121
+ yield from runner.run_asyncgen_fixture(local_func, kwargs)
122
+ else:
123
+ yield runner.run_fixture(local_func, kwargs)
124
+
125
+ # Only apply this to coroutine functions and async generator functions in requests
126
+ # that involve the anyio_backend fixture
127
+ func = fixturedef.func
128
+ if isasyncgenfunction(func) or iscoroutinefunction(func):
129
+ if "anyio_backend" in request.fixturenames:
130
+ fixturedef.func = wrapper
131
+ original_argname = fixturedef.argnames
132
+
133
+ if not (has_backend_arg := "anyio_backend" in fixturedef.argnames):
134
+ fixturedef.argnames += ("anyio_backend",)
135
+
136
+ if not (has_request_arg := "request" in fixturedef.argnames):
137
+ fixturedef.argnames += ("request",)
138
+
139
+ try:
140
+ return (yield)
141
+ finally:
142
+ fixturedef.func = func
143
+ fixturedef.argnames = original_argname
144
+
145
+ return (yield)
146
+
147
+
148
+ @pytest.hookimpl(tryfirst=True)
149
+ def pytest_pycollect_makeitem(
150
+ collector: pytest.Module | pytest.Class, name: str, obj: object
151
+ ) -> None:
152
+ if collector.istestfunction(obj, name):
153
+ inner_func = obj.hypothesis.inner_test if hasattr(obj, "hypothesis") else obj
154
+ if iscoroutinefunction(inner_func):
155
+ anyio_auto_mode = collector.config.getini("anyio_mode") == "auto"
156
+ marker = collector.get_closest_marker("anyio")
157
+ own_markers = getattr(obj, "pytestmark", ())
158
+ if (
159
+ anyio_auto_mode
160
+ or marker
161
+ or any(marker.name == "anyio" for marker in own_markers)
162
+ ):
163
+ pytest.mark.usefixtures("anyio_backend")(obj)
164
+
165
+
166
+ @pytest.hookimpl(tryfirst=True)
167
+ def pytest_pyfunc_call(pyfuncitem: Any) -> bool | None:
168
+ def run_with_hypothesis(**kwargs: Any) -> None:
169
+ with get_runner(backend_name, backend_options) as runner:
170
+ runner.run_test(original_func, kwargs)
171
+
172
+ backend = pyfuncitem.funcargs.get("anyio_backend")
173
+ if backend:
174
+ backend_name, backend_options = extract_backend_and_options(backend)
175
+
176
+ if hasattr(pyfuncitem.obj, "hypothesis"):
177
+ # Wrap the inner test function unless it's already wrapped
178
+ original_func = pyfuncitem.obj.hypothesis.inner_test
179
+ if original_func.__qualname__ != run_with_hypothesis.__qualname__:
180
+ if iscoroutinefunction(original_func):
181
+ pyfuncitem.obj.hypothesis.inner_test = run_with_hypothesis
182
+
183
+ return None
184
+
185
+ if iscoroutinefunction(pyfuncitem.obj):
186
+ funcargs = pyfuncitem.funcargs
187
+ testargs = {arg: funcargs[arg] for arg in pyfuncitem._fixtureinfo.argnames}
188
+ with get_runner(backend_name, backend_options) as runner:
189
+ try:
190
+ runner.run_test(pyfuncitem.obj, testargs)
191
+ except ExceptionGroup as excgrp:
192
+ for exc in iterate_exceptions(excgrp):
193
+ if isinstance(exc, (Exit, KeyboardInterrupt, SystemExit)):
194
+ raise exc from excgrp
195
+
196
+ raise
197
+
198
+ return True
199
+
200
+ return None
201
+
202
+
203
+ @pytest.fixture(scope="module", params=get_available_backends())
204
+ def anyio_backend(request: Any) -> Any:
205
+ return request.param
206
+
207
+
208
+ @pytest.fixture
209
+ def anyio_backend_name(anyio_backend: Any) -> str:
210
+ if isinstance(anyio_backend, str):
211
+ return anyio_backend
212
+ else:
213
+ return anyio_backend[0]
214
+
215
+
216
+ @pytest.fixture
217
+ def anyio_backend_options(anyio_backend: Any) -> dict[str, Any]:
218
+ if isinstance(anyio_backend, str):
219
+ return {}
220
+ else:
221
+ return anyio_backend[1]
222
+
223
+
224
+ class FreePortFactory:
225
+ """
226
+ Manages port generation based on specified socket kind, ensuring no duplicate
227
+ ports are generated.
228
+
229
+ This class provides functionality for generating available free ports on the
230
+ system. It is initialized with a specific socket kind and can generate ports
231
+ for given address families while avoiding reuse of previously generated ports.
232
+
233
+ Users should not instantiate this class directly, but use the
234
+ ``free_tcp_port_factory`` and ``free_udp_port_factory`` fixtures instead. For simple
235
+ uses cases, ``free_tcp_port`` and ``free_udp_port`` can be used instead.
236
+ """
237
+
238
+ def __init__(self, kind: socket.SocketKind) -> None:
239
+ self._kind = kind
240
+ self._generated = set[int]()
241
+
242
+ @property
243
+ def kind(self) -> socket.SocketKind:
244
+ """
245
+ The type of socket connection (e.g., :data:`~socket.SOCK_STREAM` or
246
+ :data:`~socket.SOCK_DGRAM`) used to bind for checking port availability
247
+
248
+ """
249
+ return self._kind
250
+
251
+ def __call__(self, family: socket.AddressFamily | None = None) -> int:
252
+ """
253
+ Return an unbound port for the given address family.
254
+
255
+ :param family: if omitted, both IPv4 and IPv6 addresses will be tried
256
+ :return: a port number
257
+
258
+ """
259
+ if family is not None:
260
+ families = [family]
261
+ else:
262
+ families = [socket.AF_INET]
263
+ if socket.has_ipv6:
264
+ families.append(socket.AF_INET6)
265
+
266
+ while True:
267
+ port = 0
268
+ with ExitStack() as stack:
269
+ for family in families:
270
+ sock = stack.enter_context(socket.socket(family, self._kind))
271
+ addr = "::1" if family == socket.AF_INET6 else "127.0.0.1"
272
+ try:
273
+ sock.bind((addr, port))
274
+ except OSError:
275
+ break
276
+
277
+ if not port:
278
+ port = sock.getsockname()[1]
279
+ else:
280
+ if port not in self._generated:
281
+ self._generated.add(port)
282
+ return port
283
+
284
+
285
+ @pytest.fixture(scope="session")
286
+ def free_tcp_port_factory() -> FreePortFactory:
287
+ return FreePortFactory(socket.SOCK_STREAM)
288
+
289
+
290
+ @pytest.fixture(scope="session")
291
+ def free_udp_port_factory() -> FreePortFactory:
292
+ return FreePortFactory(socket.SOCK_DGRAM)
293
+
294
+
295
+ @pytest.fixture
296
+ def free_tcp_port(free_tcp_port_factory: Callable[[], int]) -> int:
297
+ return free_tcp_port_factory()
298
+
299
+
300
+ @pytest.fixture
301
+ def free_udp_port(free_udp_port_factory: Callable[[], int]) -> int:
302
+ return free_udp_port_factory()
env/lib/python3.13/site-packages/anyio/to_interpreter.py ADDED
@@ -0,0 +1,246 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ __all__ = (
4
+ "run_sync",
5
+ "current_default_interpreter_limiter",
6
+ )
7
+
8
+ import atexit
9
+ import os
10
+ import sys
11
+ from collections import deque
12
+ from collections.abc import Callable
13
+ from typing import Any, Final, TypeVar
14
+
15
+ from . import current_time, to_thread
16
+ from ._core._exceptions import BrokenWorkerInterpreter
17
+ from ._core._synchronization import CapacityLimiter
18
+ from .lowlevel import RunVar
19
+
20
+ if sys.version_info >= (3, 11):
21
+ from typing import TypeVarTuple, Unpack
22
+ else:
23
+ from typing_extensions import TypeVarTuple, Unpack
24
+
25
+ if sys.version_info >= (3, 14):
26
+ from concurrent.interpreters import ExecutionFailed, create
27
+
28
+ def _interp_call(
29
+ func: Callable[..., Any], args: tuple[Any, ...]
30
+ ) -> tuple[Any, bool]:
31
+ try:
32
+ retval = func(*args)
33
+ except BaseException as exc:
34
+ return exc, True
35
+ else:
36
+ return retval, False
37
+
38
+ class _Worker:
39
+ last_used: float = 0
40
+
41
+ def __init__(self) -> None:
42
+ self._interpreter = create()
43
+
44
+ def destroy(self) -> None:
45
+ self._interpreter.close()
46
+
47
+ def call(
48
+ self,
49
+ func: Callable[..., T_Retval],
50
+ args: tuple[Any, ...],
51
+ ) -> T_Retval:
52
+ try:
53
+ res, is_exception = self._interpreter.call(_interp_call, func, args)
54
+ except ExecutionFailed as exc:
55
+ raise BrokenWorkerInterpreter(exc.excinfo) from exc
56
+
57
+ if is_exception:
58
+ raise res
59
+
60
+ return res
61
+ elif sys.version_info >= (3, 13):
62
+ import _interpqueues
63
+ import _interpreters
64
+
65
+ UNBOUND: Final = 2 # I have no clue how this works, but it was used in the stdlib
66
+ FMT_UNPICKLED: Final = 0
67
+ FMT_PICKLED: Final = 1
68
+ QUEUE_PICKLE_ARGS: Final = (FMT_PICKLED, UNBOUND)
69
+ QUEUE_UNPICKLE_ARGS: Final = (FMT_UNPICKLED, UNBOUND)
70
+
71
+ _run_func = compile(
72
+ """
73
+ import _interpqueues
74
+ from _interpreters import NotShareableError
75
+ from pickle import loads, dumps, HIGHEST_PROTOCOL
76
+
77
+ QUEUE_PICKLE_ARGS = (1, 2)
78
+ QUEUE_UNPICKLE_ARGS = (0, 2)
79
+
80
+ item = _interpqueues.get(queue_id)[0]
81
+ try:
82
+ func, args = loads(item)
83
+ retval = func(*args)
84
+ except BaseException as exc:
85
+ is_exception = True
86
+ retval = exc
87
+ else:
88
+ is_exception = False
89
+
90
+ try:
91
+ _interpqueues.put(queue_id, (retval, is_exception), *QUEUE_UNPICKLE_ARGS)
92
+ except NotShareableError:
93
+ retval = dumps(retval, HIGHEST_PROTOCOL)
94
+ _interpqueues.put(queue_id, (retval, is_exception), *QUEUE_PICKLE_ARGS)
95
+ """,
96
+ "<string>",
97
+ "exec",
98
+ )
99
+
100
+ class _Worker:
101
+ last_used: float = 0
102
+
103
+ def __init__(self) -> None:
104
+ self._interpreter_id = _interpreters.create()
105
+ self._queue_id = _interpqueues.create(1, *QUEUE_UNPICKLE_ARGS)
106
+ _interpreters.set___main___attrs(
107
+ self._interpreter_id, {"queue_id": self._queue_id}
108
+ )
109
+
110
+ def destroy(self) -> None:
111
+ _interpqueues.destroy(self._queue_id)
112
+ _interpreters.destroy(self._interpreter_id)
113
+
114
+ def call(
115
+ self,
116
+ func: Callable[..., T_Retval],
117
+ args: tuple[Any, ...],
118
+ ) -> T_Retval:
119
+ import pickle
120
+
121
+ item = pickle.dumps((func, args), pickle.HIGHEST_PROTOCOL)
122
+ _interpqueues.put(self._queue_id, item, *QUEUE_PICKLE_ARGS)
123
+ exc_info = _interpreters.exec(self._interpreter_id, _run_func)
124
+ if exc_info:
125
+ raise BrokenWorkerInterpreter(exc_info)
126
+
127
+ res = _interpqueues.get(self._queue_id)
128
+ (res, is_exception), fmt = res[:2]
129
+ if fmt == FMT_PICKLED:
130
+ res = pickle.loads(res)
131
+
132
+ if is_exception:
133
+ raise res
134
+
135
+ return res
136
+ else:
137
+
138
+ class _Worker:
139
+ last_used: float = 0
140
+
141
+ def __init__(self) -> None:
142
+ raise RuntimeError("subinterpreters require at least Python 3.13")
143
+
144
+ def call(
145
+ self,
146
+ func: Callable[..., T_Retval],
147
+ args: tuple[Any, ...],
148
+ ) -> T_Retval:
149
+ raise NotImplementedError
150
+
151
+ def destroy(self) -> None:
152
+ pass
153
+
154
+
155
+ DEFAULT_CPU_COUNT: Final = 8 # this is just an arbitrarily selected value
156
+ MAX_WORKER_IDLE_TIME = (
157
+ 30 # seconds a subinterpreter can be idle before becoming eligible for pruning
158
+ )
159
+
160
+ T_Retval = TypeVar("T_Retval")
161
+ PosArgsT = TypeVarTuple("PosArgsT")
162
+
163
+ _idle_workers = RunVar[deque[_Worker]]("_available_workers")
164
+ _default_interpreter_limiter = RunVar[CapacityLimiter]("_default_interpreter_limiter")
165
+
166
+
167
+ def _stop_workers(workers: deque[_Worker]) -> None:
168
+ for worker in workers:
169
+ worker.destroy()
170
+
171
+ workers.clear()
172
+
173
+
174
+ async def run_sync(
175
+ func: Callable[[Unpack[PosArgsT]], T_Retval],
176
+ *args: Unpack[PosArgsT],
177
+ limiter: CapacityLimiter | None = None,
178
+ ) -> T_Retval:
179
+ """
180
+ Call the given function with the given arguments in a subinterpreter.
181
+
182
+ .. warning:: On Python 3.13, the :mod:`concurrent.interpreters` module was not yet
183
+ available, so the code path for that Python version relies on an undocumented,
184
+ private API. As such, it is recommended to not rely on this function for anything
185
+ mission-critical on Python 3.13.
186
+
187
+ :param func: a callable
188
+ :param args: the positional arguments for the callable
189
+ :param limiter: capacity limiter to use to limit the total number of subinterpreters
190
+ running (if omitted, the default limiter is used)
191
+ :return: the result of the call
192
+ :raises BrokenWorkerInterpreter: if there's an internal error in a subinterpreter
193
+
194
+ """
195
+ if limiter is None:
196
+ limiter = current_default_interpreter_limiter()
197
+
198
+ try:
199
+ idle_workers = _idle_workers.get()
200
+ except LookupError:
201
+ idle_workers = deque()
202
+ _idle_workers.set(idle_workers)
203
+ atexit.register(_stop_workers, idle_workers)
204
+
205
+ async with limiter:
206
+ try:
207
+ worker = idle_workers.pop()
208
+ except IndexError:
209
+ worker = _Worker()
210
+
211
+ try:
212
+ return await to_thread.run_sync(
213
+ worker.call,
214
+ func,
215
+ args,
216
+ limiter=limiter,
217
+ )
218
+ finally:
219
+ # Prune workers that have been idle for too long
220
+ now = current_time()
221
+ while idle_workers:
222
+ if now - idle_workers[0].last_used <= MAX_WORKER_IDLE_TIME:
223
+ break
224
+
225
+ await to_thread.run_sync(idle_workers.popleft().destroy, limiter=limiter)
226
+
227
+ worker.last_used = current_time()
228
+ idle_workers.append(worker)
229
+
230
+
231
+ def current_default_interpreter_limiter() -> CapacityLimiter:
232
+ """
233
+ Return the capacity limiter used by default to limit the number of concurrently
234
+ running subinterpreters.
235
+
236
+ Defaults to the number of CPU cores.
237
+
238
+ :return: a capacity limiter object
239
+
240
+ """
241
+ try:
242
+ return _default_interpreter_limiter.get()
243
+ except LookupError:
244
+ limiter = CapacityLimiter(os.cpu_count() or DEFAULT_CPU_COUNT)
245
+ _default_interpreter_limiter.set(limiter)
246
+ return limiter
env/lib/python3.13/site-packages/anyio/to_process.py ADDED
@@ -0,0 +1,264 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ __all__ = (
4
+ "current_default_process_limiter",
5
+ "process_worker",
6
+ "run_sync",
7
+ )
8
+
9
+ import os
10
+ import pickle
11
+ import subprocess
12
+ import sys
13
+ from collections import deque
14
+ from collections.abc import Callable
15
+ from importlib.util import module_from_spec, spec_from_file_location
16
+ from typing import TypeVar, cast
17
+
18
+ from ._core._eventloop import current_time, get_async_backend, get_cancelled_exc_class
19
+ from ._core._exceptions import BrokenWorkerProcess
20
+ from ._core._subprocesses import open_process
21
+ from ._core._synchronization import CapacityLimiter
22
+ from ._core._tasks import CancelScope, fail_after
23
+ from .abc import ByteReceiveStream, ByteSendStream, Process
24
+ from .lowlevel import RunVar, checkpoint_if_cancelled
25
+ from .streams.buffered import BufferedByteReceiveStream
26
+
27
+ if sys.version_info >= (3, 11):
28
+ from typing import TypeVarTuple, Unpack
29
+ else:
30
+ from typing_extensions import TypeVarTuple, Unpack
31
+
32
+ WORKER_MAX_IDLE_TIME = 300 # 5 minutes
33
+
34
+ T_Retval = TypeVar("T_Retval")
35
+ PosArgsT = TypeVarTuple("PosArgsT")
36
+
37
+ _process_pool_workers: RunVar[set[Process]] = RunVar("_process_pool_workers")
38
+ _process_pool_idle_workers: RunVar[deque[tuple[Process, float]]] = RunVar(
39
+ "_process_pool_idle_workers"
40
+ )
41
+ _default_process_limiter: RunVar[CapacityLimiter] = RunVar("_default_process_limiter")
42
+
43
+
44
+ async def run_sync( # type: ignore[return]
45
+ func: Callable[[Unpack[PosArgsT]], T_Retval],
46
+ *args: Unpack[PosArgsT],
47
+ cancellable: bool = False,
48
+ limiter: CapacityLimiter | None = None,
49
+ ) -> T_Retval:
50
+ """
51
+ Call the given function with the given arguments in a worker process.
52
+
53
+ If the ``cancellable`` option is enabled and the task waiting for its completion is
54
+ cancelled, the worker process running it will be abruptly terminated using SIGKILL
55
+ (or ``terminateProcess()`` on Windows).
56
+
57
+ :param func: a callable
58
+ :param args: positional arguments for the callable
59
+ :param cancellable: ``True`` to allow cancellation of the operation while it's
60
+ running
61
+ :param limiter: capacity limiter to use to limit the total amount of processes
62
+ running (if omitted, the default limiter is used)
63
+ :return: an awaitable that yields the return value of the function.
64
+
65
+ """
66
+
67
+ async def send_raw_command(pickled_cmd: bytes) -> object:
68
+ try:
69
+ await stdin.send(pickled_cmd)
70
+ response = await buffered.receive_until(b"\n", 50)
71
+ status, length = response.split(b" ")
72
+ if status not in (b"RETURN", b"EXCEPTION"):
73
+ raise RuntimeError(
74
+ f"Worker process returned unexpected response: {response!r}"
75
+ )
76
+
77
+ pickled_response = await buffered.receive_exactly(int(length))
78
+ except BaseException as exc:
79
+ workers.discard(process)
80
+ try:
81
+ process.kill()
82
+ with CancelScope(shield=True):
83
+ await process.aclose()
84
+ except ProcessLookupError:
85
+ pass
86
+
87
+ if isinstance(exc, get_cancelled_exc_class()):
88
+ raise
89
+ else:
90
+ raise BrokenWorkerProcess from exc
91
+
92
+ retval = pickle.loads(pickled_response)
93
+ if status == b"EXCEPTION":
94
+ assert isinstance(retval, BaseException)
95
+ raise retval
96
+ else:
97
+ return retval
98
+
99
+ # First pickle the request before trying to reserve a worker process
100
+ await checkpoint_if_cancelled()
101
+ request = pickle.dumps(("run", func, args), protocol=pickle.HIGHEST_PROTOCOL)
102
+
103
+ # If this is the first run in this event loop thread, set up the necessary variables
104
+ try:
105
+ workers = _process_pool_workers.get()
106
+ idle_workers = _process_pool_idle_workers.get()
107
+ except LookupError:
108
+ workers = set()
109
+ idle_workers = deque()
110
+ _process_pool_workers.set(workers)
111
+ _process_pool_idle_workers.set(idle_workers)
112
+ get_async_backend().setup_process_pool_exit_at_shutdown(workers)
113
+
114
+ async with limiter or current_default_process_limiter():
115
+ # Pop processes from the pool (starting from the most recently used) until we
116
+ # find one that hasn't exited yet
117
+ process: Process
118
+ while idle_workers:
119
+ process, idle_since = idle_workers.pop()
120
+ if process.returncode is None:
121
+ stdin = cast(ByteSendStream, process.stdin)
122
+ buffered = BufferedByteReceiveStream(
123
+ cast(ByteReceiveStream, process.stdout)
124
+ )
125
+
126
+ # Prune any other workers that have been idle for WORKER_MAX_IDLE_TIME
127
+ # seconds or longer
128
+ now = current_time()
129
+ killed_processes: list[Process] = []
130
+ while idle_workers:
131
+ if now - idle_workers[0][1] < WORKER_MAX_IDLE_TIME:
132
+ break
133
+
134
+ process_to_kill, idle_since = idle_workers.popleft()
135
+ process_to_kill.kill()
136
+ workers.remove(process_to_kill)
137
+ killed_processes.append(process_to_kill)
138
+
139
+ with CancelScope(shield=True):
140
+ for killed_process in killed_processes:
141
+ await killed_process.aclose()
142
+
143
+ break
144
+
145
+ workers.remove(process)
146
+ else:
147
+ command = [sys.executable, "-u", "-m", __name__]
148
+ process = await open_process(
149
+ command, stdin=subprocess.PIPE, stdout=subprocess.PIPE
150
+ )
151
+ try:
152
+ stdin = cast(ByteSendStream, process.stdin)
153
+ buffered = BufferedByteReceiveStream(
154
+ cast(ByteReceiveStream, process.stdout)
155
+ )
156
+ with fail_after(20):
157
+ message = await buffered.receive(6)
158
+
159
+ if message != b"READY\n":
160
+ raise BrokenWorkerProcess(
161
+ f"Worker process returned unexpected response: {message!r}"
162
+ )
163
+
164
+ main_module_path = getattr(sys.modules["__main__"], "__file__", None)
165
+ pickled = pickle.dumps(
166
+ ("init", sys.path, main_module_path),
167
+ protocol=pickle.HIGHEST_PROTOCOL,
168
+ )
169
+ await send_raw_command(pickled)
170
+ except (BrokenWorkerProcess, get_cancelled_exc_class()):
171
+ raise
172
+ except BaseException as exc:
173
+ process.kill()
174
+ raise BrokenWorkerProcess(
175
+ "Error during worker process initialization"
176
+ ) from exc
177
+
178
+ workers.add(process)
179
+
180
+ with CancelScope(shield=not cancellable):
181
+ try:
182
+ return cast(T_Retval, await send_raw_command(request))
183
+ finally:
184
+ if process in workers:
185
+ idle_workers.append((process, current_time()))
186
+
187
+
188
+ def current_default_process_limiter() -> CapacityLimiter:
189
+ """
190
+ Return the capacity limiter that is used by default to limit the number of worker
191
+ processes.
192
+
193
+ :return: a capacity limiter object
194
+
195
+ """
196
+ try:
197
+ return _default_process_limiter.get()
198
+ except LookupError:
199
+ limiter = CapacityLimiter(os.cpu_count() or 2)
200
+ _default_process_limiter.set(limiter)
201
+ return limiter
202
+
203
+
204
+ def process_worker() -> None:
205
+ # Redirect standard streams to os.devnull so that user code won't interfere with the
206
+ # parent-worker communication
207
+ stdin = sys.stdin
208
+ stdout = sys.stdout
209
+ sys.stdin = open(os.devnull)
210
+ sys.stdout = open(os.devnull, "w")
211
+
212
+ stdout.buffer.write(b"READY\n")
213
+ while True:
214
+ retval = exception = None
215
+ try:
216
+ command, *args = pickle.load(stdin.buffer)
217
+ except EOFError:
218
+ return
219
+ except BaseException as exc:
220
+ exception = exc
221
+ else:
222
+ if command == "run":
223
+ func, args = args
224
+ try:
225
+ retval = func(*args)
226
+ except BaseException as exc:
227
+ exception = exc
228
+ elif command == "init":
229
+ main_module_path: str | None
230
+ sys.path, main_module_path = args
231
+ del sys.modules["__main__"]
232
+ if main_module_path and os.path.isfile(main_module_path):
233
+ # Load the parent's main module but as __mp_main__ instead of
234
+ # __main__ (like multiprocessing does) to avoid infinite recursion
235
+ try:
236
+ spec = spec_from_file_location("__mp_main__", main_module_path)
237
+ if spec and spec.loader:
238
+ main = module_from_spec(spec)
239
+ spec.loader.exec_module(main)
240
+ sys.modules["__main__"] = main
241
+ except BaseException as exc:
242
+ exception = exc
243
+ try:
244
+ if exception is not None:
245
+ status = b"EXCEPTION"
246
+ pickled = pickle.dumps(exception, pickle.HIGHEST_PROTOCOL)
247
+ else:
248
+ status = b"RETURN"
249
+ pickled = pickle.dumps(retval, pickle.HIGHEST_PROTOCOL)
250
+ except BaseException as exc:
251
+ exception = exc
252
+ status = b"EXCEPTION"
253
+ pickled = pickle.dumps(exc, pickle.HIGHEST_PROTOCOL)
254
+
255
+ stdout.buffer.write(b"%s %d\n" % (status, len(pickled)))
256
+ stdout.buffer.write(pickled)
257
+
258
+ # Respect SIGTERM
259
+ if isinstance(exception, SystemExit):
260
+ raise exception
261
+
262
+
263
+ if __name__ == "__main__":
264
+ process_worker()
env/lib/python3.13/site-packages/anyio/to_thread.py ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ __all__ = (
4
+ "run_sync",
5
+ "current_default_thread_limiter",
6
+ )
7
+
8
+ import sys
9
+ from collections.abc import Callable
10
+ from typing import TypeVar
11
+ from warnings import warn
12
+
13
+ from ._core._eventloop import get_async_backend
14
+ from .abc import CapacityLimiter
15
+
16
+ if sys.version_info >= (3, 11):
17
+ from typing import TypeVarTuple, Unpack
18
+ else:
19
+ from typing_extensions import TypeVarTuple, Unpack
20
+
21
+ T_Retval = TypeVar("T_Retval")
22
+ PosArgsT = TypeVarTuple("PosArgsT")
23
+
24
+
25
+ async def run_sync(
26
+ func: Callable[[Unpack[PosArgsT]], T_Retval],
27
+ *args: Unpack[PosArgsT],
28
+ abandon_on_cancel: bool = False,
29
+ cancellable: bool | None = None,
30
+ limiter: CapacityLimiter | None = None,
31
+ ) -> T_Retval:
32
+ """
33
+ Call the given function with the given arguments in a worker thread.
34
+
35
+ If the ``cancellable`` option is enabled and the task waiting for its completion is
36
+ cancelled, the thread will still run its course but its return value (or any raised
37
+ exception) will be ignored.
38
+
39
+ :param func: a callable
40
+ :param args: positional arguments for the callable
41
+ :param abandon_on_cancel: ``True`` to abandon the thread (leaving it to run
42
+ unchecked on own) if the host task is cancelled, ``False`` to ignore
43
+ cancellations in the host task until the operation has completed in the worker
44
+ thread
45
+ :param cancellable: deprecated alias of ``abandon_on_cancel``; will override
46
+ ``abandon_on_cancel`` if both parameters are passed
47
+ :param limiter: capacity limiter to use to limit the total amount of threads running
48
+ (if omitted, the default limiter is used)
49
+ :return: an awaitable that yields the return value of the function.
50
+
51
+ """
52
+ if cancellable is not None:
53
+ abandon_on_cancel = cancellable
54
+ warn(
55
+ "The `cancellable=` keyword argument to `anyio.to_thread.run_sync` is "
56
+ "deprecated since AnyIO 4.1.0; use `abandon_on_cancel=` instead",
57
+ DeprecationWarning,
58
+ stacklevel=2,
59
+ )
60
+
61
+ return await get_async_backend().run_sync_in_worker_thread(
62
+ func, args, abandon_on_cancel=abandon_on_cancel, limiter=limiter
63
+ )
64
+
65
+
66
+ def current_default_thread_limiter() -> CapacityLimiter:
67
+ """
68
+ Return the capacity limiter that is used by default to limit the number of
69
+ concurrent threads.
70
+
71
+ :return: a capacity limiter object
72
+
73
+ """
74
+ return get_async_backend().current_default_thread_limiter()
env/lib/python3.13/site-packages/certifi-2025.11.12.dist-info/INSTALLER ADDED
@@ -0,0 +1 @@
 
 
1
+ pip
env/lib/python3.13/site-packages/certifi-2025.11.12.dist-info/METADATA ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Metadata-Version: 2.4
2
+ Name: certifi
3
+ Version: 2025.11.12
4
+ Summary: Python package for providing Mozilla's CA Bundle.
5
+ Home-page: https://github.com/certifi/python-certifi
6
+ Author: Kenneth Reitz
7
+ Author-email: me@kennethreitz.com
8
+ License: MPL-2.0
9
+ Project-URL: Source, https://github.com/certifi/python-certifi
10
+ Classifier: Development Status :: 5 - Production/Stable
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)
13
+ Classifier: Natural Language :: English
14
+ Classifier: Programming Language :: Python
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3 :: Only
17
+ Classifier: Programming Language :: Python :: 3.7
18
+ Classifier: Programming Language :: Python :: 3.8
19
+ Classifier: Programming Language :: Python :: 3.9
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
24
+ Classifier: Programming Language :: Python :: 3.14
25
+ Requires-Python: >=3.7
26
+ License-File: LICENSE
27
+ Dynamic: author
28
+ Dynamic: author-email
29
+ Dynamic: classifier
30
+ Dynamic: description
31
+ Dynamic: home-page
32
+ Dynamic: license
33
+ Dynamic: license-file
34
+ Dynamic: project-url
35
+ Dynamic: requires-python
36
+ Dynamic: summary
37
+
38
+ Certifi: Python SSL Certificates
39
+ ================================
40
+
41
+ Certifi provides Mozilla's carefully curated collection of Root Certificates for
42
+ validating the trustworthiness of SSL certificates while verifying the identity
43
+ of TLS hosts. It has been extracted from the `Requests`_ project.
44
+
45
+ Installation
46
+ ------------
47
+
48
+ ``certifi`` is available on PyPI. Simply install it with ``pip``::
49
+
50
+ $ pip install certifi
51
+
52
+ Usage
53
+ -----
54
+
55
+ To reference the installed certificate authority (CA) bundle, you can use the
56
+ built-in function::
57
+
58
+ >>> import certifi
59
+
60
+ >>> certifi.where()
61
+ '/usr/local/lib/python3.7/site-packages/certifi/cacert.pem'
62
+
63
+ Or from the command line::
64
+
65
+ $ python -m certifi
66
+ /usr/local/lib/python3.7/site-packages/certifi/cacert.pem
67
+
68
+ Enjoy!
69
+
70
+ .. _`Requests`: https://requests.readthedocs.io/en/master/
71
+
72
+ Addition/Removal of Certificates
73
+ --------------------------------
74
+
75
+ Certifi does not support any addition/removal or other modification of the
76
+ CA trust store content. This project is intended to provide a reliable and
77
+ highly portable root of trust to python deployments. Look to upstream projects
78
+ for methods to use alternate trust.
env/lib/python3.13/site-packages/certifi-2025.11.12.dist-info/RECORD ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ certifi-2025.11.12.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
2
+ certifi-2025.11.12.dist-info/METADATA,sha256=_JprGu_1lWSdHlruRBKcorXnrfvBDhvX_6KRr8HQbLc,2475
3
+ certifi-2025.11.12.dist-info/RECORD,,
4
+ certifi-2025.11.12.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
5
+ certifi-2025.11.12.dist-info/licenses/LICENSE,sha256=6TcW2mucDVpKHfYP5pWzcPBpVgPSH2-D8FPkLPwQyvc,989
6
+ certifi-2025.11.12.dist-info/top_level.txt,sha256=KMu4vUCfsjLrkPbSNdgdekS-pVJzBAJFO__nI8NF6-U,8
7
+ certifi/__init__.py,sha256=1BRSxNMnZW7CZ2oJtYWLoJgfHfcB9i273exwiPwfjJM,94
8
+ certifi/__main__.py,sha256=xBBoj905TUWBLRGANOcf7oi6e-3dMP4cEoG9OyMs11g,243
9
+ certifi/__pycache__/__init__.cpython-313.pyc,,
10
+ certifi/__pycache__/__main__.cpython-313.pyc,,
11
+ certifi/__pycache__/core.cpython-313.pyc,,
12
+ certifi/cacert.pem,sha256=oa1dZD4hxDtb7XTH4IkdzbWPavUcis4eTwINZUqlKhY,283932
13
+ certifi/core.py,sha256=XFXycndG5pf37ayeF8N32HUuDafsyhkVMbO4BAPWHa0,3394
14
+ certifi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
env/lib/python3.13/site-packages/certifi-2025.11.12.dist-info/WHEEL ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
env/lib/python3.13/site-packages/certifi-2025.11.12.dist-info/top_level.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ certifi
env/lib/python3.13/site-packages/click/__init__.py ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Click is a simple Python module inspired by the stdlib optparse to make
3
+ writing command line scripts fun. Unlike other modules, it's based
4
+ around a simple API that does not come with too much magic and is
5
+ composable.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from .core import Argument as Argument
11
+ from .core import Command as Command
12
+ from .core import CommandCollection as CommandCollection
13
+ from .core import Context as Context
14
+ from .core import Group as Group
15
+ from .core import Option as Option
16
+ from .core import Parameter as Parameter
17
+ from .decorators import argument as argument
18
+ from .decorators import command as command
19
+ from .decorators import confirmation_option as confirmation_option
20
+ from .decorators import group as group
21
+ from .decorators import help_option as help_option
22
+ from .decorators import make_pass_decorator as make_pass_decorator
23
+ from .decorators import option as option
24
+ from .decorators import pass_context as pass_context
25
+ from .decorators import pass_obj as pass_obj
26
+ from .decorators import password_option as password_option
27
+ from .decorators import version_option as version_option
28
+ from .exceptions import Abort as Abort
29
+ from .exceptions import BadArgumentUsage as BadArgumentUsage
30
+ from .exceptions import BadOptionUsage as BadOptionUsage
31
+ from .exceptions import BadParameter as BadParameter
32
+ from .exceptions import ClickException as ClickException
33
+ from .exceptions import FileError as FileError
34
+ from .exceptions import MissingParameter as MissingParameter
35
+ from .exceptions import NoSuchOption as NoSuchOption
36
+ from .exceptions import UsageError as UsageError
37
+ from .formatting import HelpFormatter as HelpFormatter
38
+ from .formatting import wrap_text as wrap_text
39
+ from .globals import get_current_context as get_current_context
40
+ from .termui import clear as clear
41
+ from .termui import confirm as confirm
42
+ from .termui import echo_via_pager as echo_via_pager
43
+ from .termui import edit as edit
44
+ from .termui import getchar as getchar
45
+ from .termui import launch as launch
46
+ from .termui import pause as pause
47
+ from .termui import progressbar as progressbar
48
+ from .termui import prompt as prompt
49
+ from .termui import secho as secho
50
+ from .termui import style as style
51
+ from .termui import unstyle as unstyle
52
+ from .types import BOOL as BOOL
53
+ from .types import Choice as Choice
54
+ from .types import DateTime as DateTime
55
+ from .types import File as File
56
+ from .types import FLOAT as FLOAT
57
+ from .types import FloatRange as FloatRange
58
+ from .types import INT as INT
59
+ from .types import IntRange as IntRange
60
+ from .types import ParamType as ParamType
61
+ from .types import Path as Path
62
+ from .types import STRING as STRING
63
+ from .types import Tuple as Tuple
64
+ from .types import UNPROCESSED as UNPROCESSED
65
+ from .types import UUID as UUID
66
+ from .utils import echo as echo
67
+ from .utils import format_filename as format_filename
68
+ from .utils import get_app_dir as get_app_dir
69
+ from .utils import get_binary_stream as get_binary_stream
70
+ from .utils import get_text_stream as get_text_stream
71
+ from .utils import open_file as open_file
72
+
73
+
74
+ def __getattr__(name: str) -> object:
75
+ import warnings
76
+
77
+ if name == "BaseCommand":
78
+ from .core import _BaseCommand
79
+
80
+ warnings.warn(
81
+ "'BaseCommand' is deprecated and will be removed in Click 9.0. Use"
82
+ " 'Command' instead.",
83
+ DeprecationWarning,
84
+ stacklevel=2,
85
+ )
86
+ return _BaseCommand
87
+
88
+ if name == "MultiCommand":
89
+ from .core import _MultiCommand
90
+
91
+ warnings.warn(
92
+ "'MultiCommand' is deprecated and will be removed in Click 9.0. Use"
93
+ " 'Group' instead.",
94
+ DeprecationWarning,
95
+ stacklevel=2,
96
+ )
97
+ return _MultiCommand
98
+
99
+ if name == "OptionParser":
100
+ from .parser import _OptionParser
101
+
102
+ warnings.warn(
103
+ "'OptionParser' is deprecated and will be removed in Click 9.0. The"
104
+ " old parser is available in 'optparse'.",
105
+ DeprecationWarning,
106
+ stacklevel=2,
107
+ )
108
+ return _OptionParser
109
+
110
+ if name == "__version__":
111
+ import importlib.metadata
112
+ import warnings
113
+
114
+ warnings.warn(
115
+ "The '__version__' attribute is deprecated and will be removed in"
116
+ " Click 9.1. Use feature detection or"
117
+ " 'importlib.metadata.version(\"click\")' instead.",
118
+ DeprecationWarning,
119
+ stacklevel=2,
120
+ )
121
+ return importlib.metadata.version("click")
122
+
123
+ raise AttributeError(name)
env/lib/python3.13/site-packages/click/_compat.py ADDED
@@ -0,0 +1,622 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import codecs
4
+ import collections.abc as cabc
5
+ import io
6
+ import os
7
+ import re
8
+ import sys
9
+ import typing as t
10
+ from types import TracebackType
11
+ from weakref import WeakKeyDictionary
12
+
13
+ CYGWIN = sys.platform.startswith("cygwin")
14
+ WIN = sys.platform.startswith("win")
15
+ auto_wrap_for_ansi: t.Callable[[t.TextIO], t.TextIO] | None = None
16
+ _ansi_re = re.compile(r"\033\[[;?0-9]*[a-zA-Z]")
17
+
18
+
19
+ def _make_text_stream(
20
+ stream: t.BinaryIO,
21
+ encoding: str | None,
22
+ errors: str | None,
23
+ force_readable: bool = False,
24
+ force_writable: bool = False,
25
+ ) -> t.TextIO:
26
+ if encoding is None:
27
+ encoding = get_best_encoding(stream)
28
+ if errors is None:
29
+ errors = "replace"
30
+ return _NonClosingTextIOWrapper(
31
+ stream,
32
+ encoding,
33
+ errors,
34
+ line_buffering=True,
35
+ force_readable=force_readable,
36
+ force_writable=force_writable,
37
+ )
38
+
39
+
40
+ def is_ascii_encoding(encoding: str) -> bool:
41
+ """Checks if a given encoding is ascii."""
42
+ try:
43
+ return codecs.lookup(encoding).name == "ascii"
44
+ except LookupError:
45
+ return False
46
+
47
+
48
+ def get_best_encoding(stream: t.IO[t.Any]) -> str:
49
+ """Returns the default stream encoding if not found."""
50
+ rv = getattr(stream, "encoding", None) or sys.getdefaultencoding()
51
+ if is_ascii_encoding(rv):
52
+ return "utf-8"
53
+ return rv
54
+
55
+
56
+ class _NonClosingTextIOWrapper(io.TextIOWrapper):
57
+ def __init__(
58
+ self,
59
+ stream: t.BinaryIO,
60
+ encoding: str | None,
61
+ errors: str | None,
62
+ force_readable: bool = False,
63
+ force_writable: bool = False,
64
+ **extra: t.Any,
65
+ ) -> None:
66
+ self._stream = stream = t.cast(
67
+ t.BinaryIO, _FixupStream(stream, force_readable, force_writable)
68
+ )
69
+ super().__init__(stream, encoding, errors, **extra)
70
+
71
+ def __del__(self) -> None:
72
+ try:
73
+ self.detach()
74
+ except Exception:
75
+ pass
76
+
77
+ def isatty(self) -> bool:
78
+ # https://bitbucket.org/pypy/pypy/issue/1803
79
+ return self._stream.isatty()
80
+
81
+
82
+ class _FixupStream:
83
+ """The new io interface needs more from streams than streams
84
+ traditionally implement. As such, this fix-up code is necessary in
85
+ some circumstances.
86
+
87
+ The forcing of readable and writable flags are there because some tools
88
+ put badly patched objects on sys (one such offender are certain version
89
+ of jupyter notebook).
90
+ """
91
+
92
+ def __init__(
93
+ self,
94
+ stream: t.BinaryIO,
95
+ force_readable: bool = False,
96
+ force_writable: bool = False,
97
+ ):
98
+ self._stream = stream
99
+ self._force_readable = force_readable
100
+ self._force_writable = force_writable
101
+
102
+ def __getattr__(self, name: str) -> t.Any:
103
+ return getattr(self._stream, name)
104
+
105
+ def read1(self, size: int) -> bytes:
106
+ f = getattr(self._stream, "read1", None)
107
+
108
+ if f is not None:
109
+ return t.cast(bytes, f(size))
110
+
111
+ return self._stream.read(size)
112
+
113
+ def readable(self) -> bool:
114
+ if self._force_readable:
115
+ return True
116
+ x = getattr(self._stream, "readable", None)
117
+ if x is not None:
118
+ return t.cast(bool, x())
119
+ try:
120
+ self._stream.read(0)
121
+ except Exception:
122
+ return False
123
+ return True
124
+
125
+ def writable(self) -> bool:
126
+ if self._force_writable:
127
+ return True
128
+ x = getattr(self._stream, "writable", None)
129
+ if x is not None:
130
+ return t.cast(bool, x())
131
+ try:
132
+ self._stream.write(b"")
133
+ except Exception:
134
+ try:
135
+ self._stream.write(b"")
136
+ except Exception:
137
+ return False
138
+ return True
139
+
140
+ def seekable(self) -> bool:
141
+ x = getattr(self._stream, "seekable", None)
142
+ if x is not None:
143
+ return t.cast(bool, x())
144
+ try:
145
+ self._stream.seek(self._stream.tell())
146
+ except Exception:
147
+ return False
148
+ return True
149
+
150
+
151
+ def _is_binary_reader(stream: t.IO[t.Any], default: bool = False) -> bool:
152
+ try:
153
+ return isinstance(stream.read(0), bytes)
154
+ except Exception:
155
+ return default
156
+ # This happens in some cases where the stream was already
157
+ # closed. In this case, we assume the default.
158
+
159
+
160
+ def _is_binary_writer(stream: t.IO[t.Any], default: bool = False) -> bool:
161
+ try:
162
+ stream.write(b"")
163
+ except Exception:
164
+ try:
165
+ stream.write("")
166
+ return False
167
+ except Exception:
168
+ pass
169
+ return default
170
+ return True
171
+
172
+
173
+ def _find_binary_reader(stream: t.IO[t.Any]) -> t.BinaryIO | None:
174
+ # We need to figure out if the given stream is already binary.
175
+ # This can happen because the official docs recommend detaching
176
+ # the streams to get binary streams. Some code might do this, so
177
+ # we need to deal with this case explicitly.
178
+ if _is_binary_reader(stream, False):
179
+ return t.cast(t.BinaryIO, stream)
180
+
181
+ buf = getattr(stream, "buffer", None)
182
+
183
+ # Same situation here; this time we assume that the buffer is
184
+ # actually binary in case it's closed.
185
+ if buf is not None and _is_binary_reader(buf, True):
186
+ return t.cast(t.BinaryIO, buf)
187
+
188
+ return None
189
+
190
+
191
+ def _find_binary_writer(stream: t.IO[t.Any]) -> t.BinaryIO | None:
192
+ # We need to figure out if the given stream is already binary.
193
+ # This can happen because the official docs recommend detaching
194
+ # the streams to get binary streams. Some code might do this, so
195
+ # we need to deal with this case explicitly.
196
+ if _is_binary_writer(stream, False):
197
+ return t.cast(t.BinaryIO, stream)
198
+
199
+ buf = getattr(stream, "buffer", None)
200
+
201
+ # Same situation here; this time we assume that the buffer is
202
+ # actually binary in case it's closed.
203
+ if buf is not None and _is_binary_writer(buf, True):
204
+ return t.cast(t.BinaryIO, buf)
205
+
206
+ return None
207
+
208
+
209
+ def _stream_is_misconfigured(stream: t.TextIO) -> bool:
210
+ """A stream is misconfigured if its encoding is ASCII."""
211
+ # If the stream does not have an encoding set, we assume it's set
212
+ # to ASCII. This appears to happen in certain unittest
213
+ # environments. It's not quite clear what the correct behavior is
214
+ # but this at least will force Click to recover somehow.
215
+ return is_ascii_encoding(getattr(stream, "encoding", None) or "ascii")
216
+
217
+
218
+ def _is_compat_stream_attr(stream: t.TextIO, attr: str, value: str | None) -> bool:
219
+ """A stream attribute is compatible if it is equal to the
220
+ desired value or the desired value is unset and the attribute
221
+ has a value.
222
+ """
223
+ stream_value = getattr(stream, attr, None)
224
+ return stream_value == value or (value is None and stream_value is not None)
225
+
226
+
227
+ def _is_compatible_text_stream(
228
+ stream: t.TextIO, encoding: str | None, errors: str | None
229
+ ) -> bool:
230
+ """Check if a stream's encoding and errors attributes are
231
+ compatible with the desired values.
232
+ """
233
+ return _is_compat_stream_attr(
234
+ stream, "encoding", encoding
235
+ ) and _is_compat_stream_attr(stream, "errors", errors)
236
+
237
+
238
+ def _force_correct_text_stream(
239
+ text_stream: t.IO[t.Any],
240
+ encoding: str | None,
241
+ errors: str | None,
242
+ is_binary: t.Callable[[t.IO[t.Any], bool], bool],
243
+ find_binary: t.Callable[[t.IO[t.Any]], t.BinaryIO | None],
244
+ force_readable: bool = False,
245
+ force_writable: bool = False,
246
+ ) -> t.TextIO:
247
+ if is_binary(text_stream, False):
248
+ binary_reader = t.cast(t.BinaryIO, text_stream)
249
+ else:
250
+ text_stream = t.cast(t.TextIO, text_stream)
251
+ # If the stream looks compatible, and won't default to a
252
+ # misconfigured ascii encoding, return it as-is.
253
+ if _is_compatible_text_stream(text_stream, encoding, errors) and not (
254
+ encoding is None and _stream_is_misconfigured(text_stream)
255
+ ):
256
+ return text_stream
257
+
258
+ # Otherwise, get the underlying binary reader.
259
+ possible_binary_reader = find_binary(text_stream)
260
+
261
+ # If that's not possible, silently use the original reader
262
+ # and get mojibake instead of exceptions.
263
+ if possible_binary_reader is None:
264
+ return text_stream
265
+
266
+ binary_reader = possible_binary_reader
267
+
268
+ # Default errors to replace instead of strict in order to get
269
+ # something that works.
270
+ if errors is None:
271
+ errors = "replace"
272
+
273
+ # Wrap the binary stream in a text stream with the correct
274
+ # encoding parameters.
275
+ return _make_text_stream(
276
+ binary_reader,
277
+ encoding,
278
+ errors,
279
+ force_readable=force_readable,
280
+ force_writable=force_writable,
281
+ )
282
+
283
+
284
+ def _force_correct_text_reader(
285
+ text_reader: t.IO[t.Any],
286
+ encoding: str | None,
287
+ errors: str | None,
288
+ force_readable: bool = False,
289
+ ) -> t.TextIO:
290
+ return _force_correct_text_stream(
291
+ text_reader,
292
+ encoding,
293
+ errors,
294
+ _is_binary_reader,
295
+ _find_binary_reader,
296
+ force_readable=force_readable,
297
+ )
298
+
299
+
300
+ def _force_correct_text_writer(
301
+ text_writer: t.IO[t.Any],
302
+ encoding: str | None,
303
+ errors: str | None,
304
+ force_writable: bool = False,
305
+ ) -> t.TextIO:
306
+ return _force_correct_text_stream(
307
+ text_writer,
308
+ encoding,
309
+ errors,
310
+ _is_binary_writer,
311
+ _find_binary_writer,
312
+ force_writable=force_writable,
313
+ )
314
+
315
+
316
+ def get_binary_stdin() -> t.BinaryIO:
317
+ reader = _find_binary_reader(sys.stdin)
318
+ if reader is None:
319
+ raise RuntimeError("Was not able to determine binary stream for sys.stdin.")
320
+ return reader
321
+
322
+
323
+ def get_binary_stdout() -> t.BinaryIO:
324
+ writer = _find_binary_writer(sys.stdout)
325
+ if writer is None:
326
+ raise RuntimeError("Was not able to determine binary stream for sys.stdout.")
327
+ return writer
328
+
329
+
330
+ def get_binary_stderr() -> t.BinaryIO:
331
+ writer = _find_binary_writer(sys.stderr)
332
+ if writer is None:
333
+ raise RuntimeError("Was not able to determine binary stream for sys.stderr.")
334
+ return writer
335
+
336
+
337
+ def get_text_stdin(encoding: str | None = None, errors: str | None = None) -> t.TextIO:
338
+ rv = _get_windows_console_stream(sys.stdin, encoding, errors)
339
+ if rv is not None:
340
+ return rv
341
+ return _force_correct_text_reader(sys.stdin, encoding, errors, force_readable=True)
342
+
343
+
344
+ def get_text_stdout(encoding: str | None = None, errors: str | None = None) -> t.TextIO:
345
+ rv = _get_windows_console_stream(sys.stdout, encoding, errors)
346
+ if rv is not None:
347
+ return rv
348
+ return _force_correct_text_writer(sys.stdout, encoding, errors, force_writable=True)
349
+
350
+
351
+ def get_text_stderr(encoding: str | None = None, errors: str | None = None) -> t.TextIO:
352
+ rv = _get_windows_console_stream(sys.stderr, encoding, errors)
353
+ if rv is not None:
354
+ return rv
355
+ return _force_correct_text_writer(sys.stderr, encoding, errors, force_writable=True)
356
+
357
+
358
+ def _wrap_io_open(
359
+ file: str | os.PathLike[str] | int,
360
+ mode: str,
361
+ encoding: str | None,
362
+ errors: str | None,
363
+ ) -> t.IO[t.Any]:
364
+ """Handles not passing ``encoding`` and ``errors`` in binary mode."""
365
+ if "b" in mode:
366
+ return open(file, mode)
367
+
368
+ return open(file, mode, encoding=encoding, errors=errors)
369
+
370
+
371
+ def open_stream(
372
+ filename: str | os.PathLike[str],
373
+ mode: str = "r",
374
+ encoding: str | None = None,
375
+ errors: str | None = "strict",
376
+ atomic: bool = False,
377
+ ) -> tuple[t.IO[t.Any], bool]:
378
+ binary = "b" in mode
379
+ filename = os.fspath(filename)
380
+
381
+ # Standard streams first. These are simple because they ignore the
382
+ # atomic flag. Use fsdecode to handle Path("-").
383
+ if os.fsdecode(filename) == "-":
384
+ if any(m in mode for m in ["w", "a", "x"]):
385
+ if binary:
386
+ return get_binary_stdout(), False
387
+ return get_text_stdout(encoding=encoding, errors=errors), False
388
+ if binary:
389
+ return get_binary_stdin(), False
390
+ return get_text_stdin(encoding=encoding, errors=errors), False
391
+
392
+ # Non-atomic writes directly go out through the regular open functions.
393
+ if not atomic:
394
+ return _wrap_io_open(filename, mode, encoding, errors), True
395
+
396
+ # Some usability stuff for atomic writes
397
+ if "a" in mode:
398
+ raise ValueError(
399
+ "Appending to an existing file is not supported, because that"
400
+ " would involve an expensive `copy`-operation to a temporary"
401
+ " file. Open the file in normal `w`-mode and copy explicitly"
402
+ " if that's what you're after."
403
+ )
404
+ if "x" in mode:
405
+ raise ValueError("Use the `overwrite`-parameter instead.")
406
+ if "w" not in mode:
407
+ raise ValueError("Atomic writes only make sense with `w`-mode.")
408
+
409
+ # Atomic writes are more complicated. They work by opening a file
410
+ # as a proxy in the same folder and then using the fdopen
411
+ # functionality to wrap it in a Python file. Then we wrap it in an
412
+ # atomic file that moves the file over on close.
413
+ import errno
414
+ import random
415
+
416
+ try:
417
+ perm: int | None = os.stat(filename).st_mode
418
+ except OSError:
419
+ perm = None
420
+
421
+ flags = os.O_RDWR | os.O_CREAT | os.O_EXCL
422
+
423
+ if binary:
424
+ flags |= getattr(os, "O_BINARY", 0)
425
+
426
+ while True:
427
+ tmp_filename = os.path.join(
428
+ os.path.dirname(filename),
429
+ f".__atomic-write{random.randrange(1 << 32):08x}",
430
+ )
431
+ try:
432
+ fd = os.open(tmp_filename, flags, 0o666 if perm is None else perm)
433
+ break
434
+ except OSError as e:
435
+ if e.errno == errno.EEXIST or (
436
+ os.name == "nt"
437
+ and e.errno == errno.EACCES
438
+ and os.path.isdir(e.filename)
439
+ and os.access(e.filename, os.W_OK)
440
+ ):
441
+ continue
442
+ raise
443
+
444
+ if perm is not None:
445
+ os.chmod(tmp_filename, perm) # in case perm includes bits in umask
446
+
447
+ f = _wrap_io_open(fd, mode, encoding, errors)
448
+ af = _AtomicFile(f, tmp_filename, os.path.realpath(filename))
449
+ return t.cast(t.IO[t.Any], af), True
450
+
451
+
452
+ class _AtomicFile:
453
+ def __init__(self, f: t.IO[t.Any], tmp_filename: str, real_filename: str) -> None:
454
+ self._f = f
455
+ self._tmp_filename = tmp_filename
456
+ self._real_filename = real_filename
457
+ self.closed = False
458
+
459
+ @property
460
+ def name(self) -> str:
461
+ return self._real_filename
462
+
463
+ def close(self, delete: bool = False) -> None:
464
+ if self.closed:
465
+ return
466
+ self._f.close()
467
+ os.replace(self._tmp_filename, self._real_filename)
468
+ self.closed = True
469
+
470
+ def __getattr__(self, name: str) -> t.Any:
471
+ return getattr(self._f, name)
472
+
473
+ def __enter__(self) -> _AtomicFile:
474
+ return self
475
+
476
+ def __exit__(
477
+ self,
478
+ exc_type: type[BaseException] | None,
479
+ exc_value: BaseException | None,
480
+ tb: TracebackType | None,
481
+ ) -> None:
482
+ self.close(delete=exc_type is not None)
483
+
484
+ def __repr__(self) -> str:
485
+ return repr(self._f)
486
+
487
+
488
+ def strip_ansi(value: str) -> str:
489
+ return _ansi_re.sub("", value)
490
+
491
+
492
+ def _is_jupyter_kernel_output(stream: t.IO[t.Any]) -> bool:
493
+ while isinstance(stream, (_FixupStream, _NonClosingTextIOWrapper)):
494
+ stream = stream._stream
495
+
496
+ return stream.__class__.__module__.startswith("ipykernel.")
497
+
498
+
499
+ def should_strip_ansi(
500
+ stream: t.IO[t.Any] | None = None, color: bool | None = None
501
+ ) -> bool:
502
+ if color is None:
503
+ if stream is None:
504
+ stream = sys.stdin
505
+ return not isatty(stream) and not _is_jupyter_kernel_output(stream)
506
+ return not color
507
+
508
+
509
+ # On Windows, wrap the output streams with colorama to support ANSI
510
+ # color codes.
511
+ # NOTE: double check is needed so mypy does not analyze this on Linux
512
+ if sys.platform.startswith("win") and WIN:
513
+ from ._winconsole import _get_windows_console_stream
514
+
515
+ def _get_argv_encoding() -> str:
516
+ import locale
517
+
518
+ return locale.getpreferredencoding()
519
+
520
+ _ansi_stream_wrappers: cabc.MutableMapping[t.TextIO, t.TextIO] = WeakKeyDictionary()
521
+
522
+ def auto_wrap_for_ansi(stream: t.TextIO, color: bool | None = None) -> t.TextIO:
523
+ """Support ANSI color and style codes on Windows by wrapping a
524
+ stream with colorama.
525
+ """
526
+ try:
527
+ cached = _ansi_stream_wrappers.get(stream)
528
+ except Exception:
529
+ cached = None
530
+
531
+ if cached is not None:
532
+ return cached
533
+
534
+ import colorama
535
+
536
+ strip = should_strip_ansi(stream, color)
537
+ ansi_wrapper = colorama.AnsiToWin32(stream, strip=strip)
538
+ rv = t.cast(t.TextIO, ansi_wrapper.stream)
539
+ _write = rv.write
540
+
541
+ def _safe_write(s: str) -> int:
542
+ try:
543
+ return _write(s)
544
+ except BaseException:
545
+ ansi_wrapper.reset_all()
546
+ raise
547
+
548
+ rv.write = _safe_write # type: ignore[method-assign]
549
+
550
+ try:
551
+ _ansi_stream_wrappers[stream] = rv
552
+ except Exception:
553
+ pass
554
+
555
+ return rv
556
+
557
+ else:
558
+
559
+ def _get_argv_encoding() -> str:
560
+ return getattr(sys.stdin, "encoding", None) or sys.getfilesystemencoding()
561
+
562
+ def _get_windows_console_stream(
563
+ f: t.TextIO, encoding: str | None, errors: str | None
564
+ ) -> t.TextIO | None:
565
+ return None
566
+
567
+
568
+ def term_len(x: str) -> int:
569
+ return len(strip_ansi(x))
570
+
571
+
572
+ def isatty(stream: t.IO[t.Any]) -> bool:
573
+ try:
574
+ return stream.isatty()
575
+ except Exception:
576
+ return False
577
+
578
+
579
+ def _make_cached_stream_func(
580
+ src_func: t.Callable[[], t.TextIO | None],
581
+ wrapper_func: t.Callable[[], t.TextIO],
582
+ ) -> t.Callable[[], t.TextIO | None]:
583
+ cache: cabc.MutableMapping[t.TextIO, t.TextIO] = WeakKeyDictionary()
584
+
585
+ def func() -> t.TextIO | None:
586
+ stream = src_func()
587
+
588
+ if stream is None:
589
+ return None
590
+
591
+ try:
592
+ rv = cache.get(stream)
593
+ except Exception:
594
+ rv = None
595
+ if rv is not None:
596
+ return rv
597
+ rv = wrapper_func()
598
+ try:
599
+ cache[stream] = rv
600
+ except Exception:
601
+ pass
602
+ return rv
603
+
604
+ return func
605
+
606
+
607
+ _default_text_stdin = _make_cached_stream_func(lambda: sys.stdin, get_text_stdin)
608
+ _default_text_stdout = _make_cached_stream_func(lambda: sys.stdout, get_text_stdout)
609
+ _default_text_stderr = _make_cached_stream_func(lambda: sys.stderr, get_text_stderr)
610
+
611
+
612
+ binary_streams: cabc.Mapping[str, t.Callable[[], t.BinaryIO]] = {
613
+ "stdin": get_binary_stdin,
614
+ "stdout": get_binary_stdout,
615
+ "stderr": get_binary_stderr,
616
+ }
617
+
618
+ text_streams: cabc.Mapping[str, t.Callable[[str | None, str | None], t.TextIO]] = {
619
+ "stdin": get_text_stdin,
620
+ "stdout": get_text_stdout,
621
+ "stderr": get_text_stderr,
622
+ }
env/lib/python3.13/site-packages/click/_termui_impl.py ADDED
@@ -0,0 +1,852 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ This module contains implementations for the termui module. To keep the
3
+ import time of Click down, some infrequently used functionality is
4
+ placed in this module and only imported as needed.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import collections.abc as cabc
10
+ import contextlib
11
+ import math
12
+ import os
13
+ import shlex
14
+ import sys
15
+ import time
16
+ import typing as t
17
+ from gettext import gettext as _
18
+ from io import StringIO
19
+ from pathlib import Path
20
+ from types import TracebackType
21
+
22
+ from ._compat import _default_text_stdout
23
+ from ._compat import CYGWIN
24
+ from ._compat import get_best_encoding
25
+ from ._compat import isatty
26
+ from ._compat import open_stream
27
+ from ._compat import strip_ansi
28
+ from ._compat import term_len
29
+ from ._compat import WIN
30
+ from .exceptions import ClickException
31
+ from .utils import echo
32
+
33
+ V = t.TypeVar("V")
34
+
35
+ if os.name == "nt":
36
+ BEFORE_BAR = "\r"
37
+ AFTER_BAR = "\n"
38
+ else:
39
+ BEFORE_BAR = "\r\033[?25l"
40
+ AFTER_BAR = "\033[?25h\n"
41
+
42
+
43
+ class ProgressBar(t.Generic[V]):
44
+ def __init__(
45
+ self,
46
+ iterable: cabc.Iterable[V] | None,
47
+ length: int | None = None,
48
+ fill_char: str = "#",
49
+ empty_char: str = " ",
50
+ bar_template: str = "%(bar)s",
51
+ info_sep: str = " ",
52
+ hidden: bool = False,
53
+ show_eta: bool = True,
54
+ show_percent: bool | None = None,
55
+ show_pos: bool = False,
56
+ item_show_func: t.Callable[[V | None], str | None] | None = None,
57
+ label: str | None = None,
58
+ file: t.TextIO | None = None,
59
+ color: bool | None = None,
60
+ update_min_steps: int = 1,
61
+ width: int = 30,
62
+ ) -> None:
63
+ self.fill_char = fill_char
64
+ self.empty_char = empty_char
65
+ self.bar_template = bar_template
66
+ self.info_sep = info_sep
67
+ self.hidden = hidden
68
+ self.show_eta = show_eta
69
+ self.show_percent = show_percent
70
+ self.show_pos = show_pos
71
+ self.item_show_func = item_show_func
72
+ self.label: str = label or ""
73
+
74
+ if file is None:
75
+ file = _default_text_stdout()
76
+
77
+ # There are no standard streams attached to write to. For example,
78
+ # pythonw on Windows.
79
+ if file is None:
80
+ file = StringIO()
81
+
82
+ self.file = file
83
+ self.color = color
84
+ self.update_min_steps = update_min_steps
85
+ self._completed_intervals = 0
86
+ self.width: int = width
87
+ self.autowidth: bool = width == 0
88
+
89
+ if length is None:
90
+ from operator import length_hint
91
+
92
+ length = length_hint(iterable, -1)
93
+
94
+ if length == -1:
95
+ length = None
96
+ if iterable is None:
97
+ if length is None:
98
+ raise TypeError("iterable or length is required")
99
+ iterable = t.cast("cabc.Iterable[V]", range(length))
100
+ self.iter: cabc.Iterable[V] = iter(iterable)
101
+ self.length = length
102
+ self.pos: int = 0
103
+ self.avg: list[float] = []
104
+ self.last_eta: float
105
+ self.start: float
106
+ self.start = self.last_eta = time.time()
107
+ self.eta_known: bool = False
108
+ self.finished: bool = False
109
+ self.max_width: int | None = None
110
+ self.entered: bool = False
111
+ self.current_item: V | None = None
112
+ self._is_atty = isatty(self.file)
113
+ self._last_line: str | None = None
114
+
115
+ def __enter__(self) -> ProgressBar[V]:
116
+ self.entered = True
117
+ self.render_progress()
118
+ return self
119
+
120
+ def __exit__(
121
+ self,
122
+ exc_type: type[BaseException] | None,
123
+ exc_value: BaseException | None,
124
+ tb: TracebackType | None,
125
+ ) -> None:
126
+ self.render_finish()
127
+
128
+ def __iter__(self) -> cabc.Iterator[V]:
129
+ if not self.entered:
130
+ raise RuntimeError("You need to use progress bars in a with block.")
131
+ self.render_progress()
132
+ return self.generator()
133
+
134
+ def __next__(self) -> V:
135
+ # Iteration is defined in terms of a generator function,
136
+ # returned by iter(self); use that to define next(). This works
137
+ # because `self.iter` is an iterable consumed by that generator,
138
+ # so it is re-entry safe. Calling `next(self.generator())`
139
+ # twice works and does "what you want".
140
+ return next(iter(self))
141
+
142
+ def render_finish(self) -> None:
143
+ if self.hidden or not self._is_atty:
144
+ return
145
+ self.file.write(AFTER_BAR)
146
+ self.file.flush()
147
+
148
+ @property
149
+ def pct(self) -> float:
150
+ if self.finished:
151
+ return 1.0
152
+ return min(self.pos / (float(self.length or 1) or 1), 1.0)
153
+
154
+ @property
155
+ def time_per_iteration(self) -> float:
156
+ if not self.avg:
157
+ return 0.0
158
+ return sum(self.avg) / float(len(self.avg))
159
+
160
+ @property
161
+ def eta(self) -> float:
162
+ if self.length is not None and not self.finished:
163
+ return self.time_per_iteration * (self.length - self.pos)
164
+ return 0.0
165
+
166
+ def format_eta(self) -> str:
167
+ if self.eta_known:
168
+ t = int(self.eta)
169
+ seconds = t % 60
170
+ t //= 60
171
+ minutes = t % 60
172
+ t //= 60
173
+ hours = t % 24
174
+ t //= 24
175
+ if t > 0:
176
+ return f"{t}d {hours:02}:{minutes:02}:{seconds:02}"
177
+ else:
178
+ return f"{hours:02}:{minutes:02}:{seconds:02}"
179
+ return ""
180
+
181
+ def format_pos(self) -> str:
182
+ pos = str(self.pos)
183
+ if self.length is not None:
184
+ pos += f"/{self.length}"
185
+ return pos
186
+
187
+ def format_pct(self) -> str:
188
+ return f"{int(self.pct * 100): 4}%"[1:]
189
+
190
+ def format_bar(self) -> str:
191
+ if self.length is not None:
192
+ bar_length = int(self.pct * self.width)
193
+ bar = self.fill_char * bar_length
194
+ bar += self.empty_char * (self.width - bar_length)
195
+ elif self.finished:
196
+ bar = self.fill_char * self.width
197
+ else:
198
+ chars = list(self.empty_char * (self.width or 1))
199
+ if self.time_per_iteration != 0:
200
+ chars[
201
+ int(
202
+ (math.cos(self.pos * self.time_per_iteration) / 2.0 + 0.5)
203
+ * self.width
204
+ )
205
+ ] = self.fill_char
206
+ bar = "".join(chars)
207
+ return bar
208
+
209
+ def format_progress_line(self) -> str:
210
+ show_percent = self.show_percent
211
+
212
+ info_bits = []
213
+ if self.length is not None and show_percent is None:
214
+ show_percent = not self.show_pos
215
+
216
+ if self.show_pos:
217
+ info_bits.append(self.format_pos())
218
+ if show_percent:
219
+ info_bits.append(self.format_pct())
220
+ if self.show_eta and self.eta_known and not self.finished:
221
+ info_bits.append(self.format_eta())
222
+ if self.item_show_func is not None:
223
+ item_info = self.item_show_func(self.current_item)
224
+ if item_info is not None:
225
+ info_bits.append(item_info)
226
+
227
+ return (
228
+ self.bar_template
229
+ % {
230
+ "label": self.label,
231
+ "bar": self.format_bar(),
232
+ "info": self.info_sep.join(info_bits),
233
+ }
234
+ ).rstrip()
235
+
236
+ def render_progress(self) -> None:
237
+ if self.hidden:
238
+ return
239
+
240
+ if not self._is_atty:
241
+ # Only output the label once if the output is not a TTY.
242
+ if self._last_line != self.label:
243
+ self._last_line = self.label
244
+ echo(self.label, file=self.file, color=self.color)
245
+ return
246
+
247
+ buf = []
248
+ # Update width in case the terminal has been resized
249
+ if self.autowidth:
250
+ import shutil
251
+
252
+ old_width = self.width
253
+ self.width = 0
254
+ clutter_length = term_len(self.format_progress_line())
255
+ new_width = max(0, shutil.get_terminal_size().columns - clutter_length)
256
+ if new_width < old_width and self.max_width is not None:
257
+ buf.append(BEFORE_BAR)
258
+ buf.append(" " * self.max_width)
259
+ self.max_width = new_width
260
+ self.width = new_width
261
+
262
+ clear_width = self.width
263
+ if self.max_width is not None:
264
+ clear_width = self.max_width
265
+
266
+ buf.append(BEFORE_BAR)
267
+ line = self.format_progress_line()
268
+ line_len = term_len(line)
269
+ if self.max_width is None or self.max_width < line_len:
270
+ self.max_width = line_len
271
+
272
+ buf.append(line)
273
+ buf.append(" " * (clear_width - line_len))
274
+ line = "".join(buf)
275
+ # Render the line only if it changed.
276
+
277
+ if line != self._last_line:
278
+ self._last_line = line
279
+ echo(line, file=self.file, color=self.color, nl=False)
280
+ self.file.flush()
281
+
282
+ def make_step(self, n_steps: int) -> None:
283
+ self.pos += n_steps
284
+ if self.length is not None and self.pos >= self.length:
285
+ self.finished = True
286
+
287
+ if (time.time() - self.last_eta) < 1.0:
288
+ return
289
+
290
+ self.last_eta = time.time()
291
+
292
+ # self.avg is a rolling list of length <= 7 of steps where steps are
293
+ # defined as time elapsed divided by the total progress through
294
+ # self.length.
295
+ if self.pos:
296
+ step = (time.time() - self.start) / self.pos
297
+ else:
298
+ step = time.time() - self.start
299
+
300
+ self.avg = self.avg[-6:] + [step]
301
+
302
+ self.eta_known = self.length is not None
303
+
304
+ def update(self, n_steps: int, current_item: V | None = None) -> None:
305
+ """Update the progress bar by advancing a specified number of
306
+ steps, and optionally set the ``current_item`` for this new
307
+ position.
308
+
309
+ :param n_steps: Number of steps to advance.
310
+ :param current_item: Optional item to set as ``current_item``
311
+ for the updated position.
312
+
313
+ .. versionchanged:: 8.0
314
+ Added the ``current_item`` optional parameter.
315
+
316
+ .. versionchanged:: 8.0
317
+ Only render when the number of steps meets the
318
+ ``update_min_steps`` threshold.
319
+ """
320
+ if current_item is not None:
321
+ self.current_item = current_item
322
+
323
+ self._completed_intervals += n_steps
324
+
325
+ if self._completed_intervals >= self.update_min_steps:
326
+ self.make_step(self._completed_intervals)
327
+ self.render_progress()
328
+ self._completed_intervals = 0
329
+
330
+ def finish(self) -> None:
331
+ self.eta_known = False
332
+ self.current_item = None
333
+ self.finished = True
334
+
335
+ def generator(self) -> cabc.Iterator[V]:
336
+ """Return a generator which yields the items added to the bar
337
+ during construction, and updates the progress bar *after* the
338
+ yielded block returns.
339
+ """
340
+ # WARNING: the iterator interface for `ProgressBar` relies on
341
+ # this and only works because this is a simple generator which
342
+ # doesn't create or manage additional state. If this function
343
+ # changes, the impact should be evaluated both against
344
+ # `iter(bar)` and `next(bar)`. `next()` in particular may call
345
+ # `self.generator()` repeatedly, and this must remain safe in
346
+ # order for that interface to work.
347
+ if not self.entered:
348
+ raise RuntimeError("You need to use progress bars in a with block.")
349
+
350
+ if not self._is_atty:
351
+ yield from self.iter
352
+ else:
353
+ for rv in self.iter:
354
+ self.current_item = rv
355
+
356
+ # This allows show_item_func to be updated before the
357
+ # item is processed. Only trigger at the beginning of
358
+ # the update interval.
359
+ if self._completed_intervals == 0:
360
+ self.render_progress()
361
+
362
+ yield rv
363
+ self.update(1)
364
+
365
+ self.finish()
366
+ self.render_progress()
367
+
368
+
369
+ def pager(generator: cabc.Iterable[str], color: bool | None = None) -> None:
370
+ """Decide what method to use for paging through text."""
371
+ stdout = _default_text_stdout()
372
+
373
+ # There are no standard streams attached to write to. For example,
374
+ # pythonw on Windows.
375
+ if stdout is None:
376
+ stdout = StringIO()
377
+
378
+ if not isatty(sys.stdin) or not isatty(stdout):
379
+ return _nullpager(stdout, generator, color)
380
+
381
+ # Split and normalize the pager command into parts.
382
+ pager_cmd_parts = shlex.split(os.environ.get("PAGER", ""), posix=False)
383
+ if pager_cmd_parts:
384
+ if WIN:
385
+ if _tempfilepager(generator, pager_cmd_parts, color):
386
+ return
387
+ elif _pipepager(generator, pager_cmd_parts, color):
388
+ return
389
+
390
+ if os.environ.get("TERM") in ("dumb", "emacs"):
391
+ return _nullpager(stdout, generator, color)
392
+ if (WIN or sys.platform.startswith("os2")) and _tempfilepager(
393
+ generator, ["more"], color
394
+ ):
395
+ return
396
+ if _pipepager(generator, ["less"], color):
397
+ return
398
+
399
+ import tempfile
400
+
401
+ fd, filename = tempfile.mkstemp()
402
+ os.close(fd)
403
+ try:
404
+ if _pipepager(generator, ["more"], color):
405
+ return
406
+ return _nullpager(stdout, generator, color)
407
+ finally:
408
+ os.unlink(filename)
409
+
410
+
411
+ def _pipepager(
412
+ generator: cabc.Iterable[str], cmd_parts: list[str], color: bool | None
413
+ ) -> bool:
414
+ """Page through text by feeding it to another program. Invoking a
415
+ pager through this might support colors.
416
+
417
+ Returns `True` if the command was found, `False` otherwise and thus another
418
+ pager should be attempted.
419
+ """
420
+ # Split the command into the invoked CLI and its parameters.
421
+ if not cmd_parts:
422
+ return False
423
+
424
+ import shutil
425
+
426
+ cmd = cmd_parts[0]
427
+ cmd_params = cmd_parts[1:]
428
+
429
+ cmd_filepath = shutil.which(cmd)
430
+ if not cmd_filepath:
431
+ return False
432
+
433
+ # Produces a normalized absolute path string.
434
+ # multi-call binaries such as busybox derive their identity from the symlink
435
+ # less -> busybox. resolve() causes them to misbehave. (eg. less becomes busybox)
436
+ cmd_path = Path(cmd_filepath).absolute()
437
+ cmd_name = cmd_path.name
438
+
439
+ import subprocess
440
+
441
+ # Make a local copy of the environment to not affect the global one.
442
+ env = dict(os.environ)
443
+
444
+ # If we're piping to less and the user hasn't decided on colors, we enable
445
+ # them by default we find the -R flag in the command line arguments.
446
+ if color is None and cmd_name == "less":
447
+ less_flags = f"{os.environ.get('LESS', '')}{' '.join(cmd_params)}"
448
+ if not less_flags:
449
+ env["LESS"] = "-R"
450
+ color = True
451
+ elif "r" in less_flags or "R" in less_flags:
452
+ color = True
453
+
454
+ c = subprocess.Popen(
455
+ [str(cmd_path)] + cmd_params,
456
+ shell=False,
457
+ stdin=subprocess.PIPE,
458
+ env=env,
459
+ errors="replace",
460
+ text=True,
461
+ )
462
+ assert c.stdin is not None
463
+ try:
464
+ for text in generator:
465
+ if not color:
466
+ text = strip_ansi(text)
467
+
468
+ c.stdin.write(text)
469
+ except BrokenPipeError:
470
+ # In case the pager exited unexpectedly, ignore the broken pipe error.
471
+ pass
472
+ except Exception as e:
473
+ # In case there is an exception we want to close the pager immediately
474
+ # and let the caller handle it.
475
+ # Otherwise the pager will keep running, and the user may not notice
476
+ # the error message, or worse yet it may leave the terminal in a broken state.
477
+ c.terminate()
478
+ raise e
479
+ finally:
480
+ # We must close stdin and wait for the pager to exit before we continue
481
+ try:
482
+ c.stdin.close()
483
+ # Close implies flush, so it might throw a BrokenPipeError if the pager
484
+ # process exited already.
485
+ except BrokenPipeError:
486
+ pass
487
+
488
+ # Less doesn't respect ^C, but catches it for its own UI purposes (aborting
489
+ # search or other commands inside less).
490
+ #
491
+ # That means when the user hits ^C, the parent process (click) terminates,
492
+ # but less is still alive, paging the output and messing up the terminal.
493
+ #
494
+ # If the user wants to make the pager exit on ^C, they should set
495
+ # `LESS='-K'`. It's not our decision to make.
496
+ while True:
497
+ try:
498
+ c.wait()
499
+ except KeyboardInterrupt:
500
+ pass
501
+ else:
502
+ break
503
+
504
+ return True
505
+
506
+
507
+ def _tempfilepager(
508
+ generator: cabc.Iterable[str], cmd_parts: list[str], color: bool | None
509
+ ) -> bool:
510
+ """Page through text by invoking a program on a temporary file.
511
+
512
+ Returns `True` if the command was found, `False` otherwise and thus another
513
+ pager should be attempted.
514
+ """
515
+ # Split the command into the invoked CLI and its parameters.
516
+ if not cmd_parts:
517
+ return False
518
+
519
+ import shutil
520
+
521
+ cmd = cmd_parts[0]
522
+
523
+ cmd_filepath = shutil.which(cmd)
524
+ if not cmd_filepath:
525
+ return False
526
+ # Produces a normalized absolute path string.
527
+ # multi-call binaries such as busybox derive their identity from the symlink
528
+ # less -> busybox. resolve() causes them to misbehave. (eg. less becomes busybox)
529
+ cmd_path = Path(cmd_filepath).absolute()
530
+
531
+ import subprocess
532
+ import tempfile
533
+
534
+ fd, filename = tempfile.mkstemp()
535
+ # TODO: This never terminates if the passed generator never terminates.
536
+ text = "".join(generator)
537
+ if not color:
538
+ text = strip_ansi(text)
539
+ encoding = get_best_encoding(sys.stdout)
540
+ with open_stream(filename, "wb")[0] as f:
541
+ f.write(text.encode(encoding))
542
+ try:
543
+ subprocess.call([str(cmd_path), filename])
544
+ except OSError:
545
+ # Command not found
546
+ pass
547
+ finally:
548
+ os.close(fd)
549
+ os.unlink(filename)
550
+
551
+ return True
552
+
553
+
554
+ def _nullpager(
555
+ stream: t.TextIO, generator: cabc.Iterable[str], color: bool | None
556
+ ) -> None:
557
+ """Simply print unformatted text. This is the ultimate fallback."""
558
+ for text in generator:
559
+ if not color:
560
+ text = strip_ansi(text)
561
+ stream.write(text)
562
+
563
+
564
+ class Editor:
565
+ def __init__(
566
+ self,
567
+ editor: str | None = None,
568
+ env: cabc.Mapping[str, str] | None = None,
569
+ require_save: bool = True,
570
+ extension: str = ".txt",
571
+ ) -> None:
572
+ self.editor = editor
573
+ self.env = env
574
+ self.require_save = require_save
575
+ self.extension = extension
576
+
577
+ def get_editor(self) -> str:
578
+ if self.editor is not None:
579
+ return self.editor
580
+ for key in "VISUAL", "EDITOR":
581
+ rv = os.environ.get(key)
582
+ if rv:
583
+ return rv
584
+ if WIN:
585
+ return "notepad"
586
+
587
+ from shutil import which
588
+
589
+ for editor in "sensible-editor", "vim", "nano":
590
+ if which(editor) is not None:
591
+ return editor
592
+ return "vi"
593
+
594
+ def edit_files(self, filenames: cabc.Iterable[str]) -> None:
595
+ import subprocess
596
+
597
+ editor = self.get_editor()
598
+ environ: dict[str, str] | None = None
599
+
600
+ if self.env:
601
+ environ = os.environ.copy()
602
+ environ.update(self.env)
603
+
604
+ exc_filename = " ".join(f'"{filename}"' for filename in filenames)
605
+
606
+ try:
607
+ c = subprocess.Popen(
608
+ args=f"{editor} {exc_filename}", env=environ, shell=True
609
+ )
610
+ exit_code = c.wait()
611
+ if exit_code != 0:
612
+ raise ClickException(
613
+ _("{editor}: Editing failed").format(editor=editor)
614
+ )
615
+ except OSError as e:
616
+ raise ClickException(
617
+ _("{editor}: Editing failed: {e}").format(editor=editor, e=e)
618
+ ) from e
619
+
620
+ @t.overload
621
+ def edit(self, text: bytes | bytearray) -> bytes | None: ...
622
+
623
+ # We cannot know whether or not the type expected is str or bytes when None
624
+ # is passed, so str is returned as that was what was done before.
625
+ @t.overload
626
+ def edit(self, text: str | None) -> str | None: ...
627
+
628
+ def edit(self, text: str | bytes | bytearray | None) -> str | bytes | None:
629
+ import tempfile
630
+
631
+ if text is None:
632
+ data: bytes | bytearray = b""
633
+ elif isinstance(text, (bytes, bytearray)):
634
+ data = text
635
+ else:
636
+ if text and not text.endswith("\n"):
637
+ text += "\n"
638
+
639
+ if WIN:
640
+ data = text.replace("\n", "\r\n").encode("utf-8-sig")
641
+ else:
642
+ data = text.encode("utf-8")
643
+
644
+ fd, name = tempfile.mkstemp(prefix="editor-", suffix=self.extension)
645
+ f: t.BinaryIO
646
+
647
+ try:
648
+ with os.fdopen(fd, "wb") as f:
649
+ f.write(data)
650
+
651
+ # If the filesystem resolution is 1 second, like Mac OS
652
+ # 10.12 Extended, or 2 seconds, like FAT32, and the editor
653
+ # closes very fast, require_save can fail. Set the modified
654
+ # time to be 2 seconds in the past to work around this.
655
+ os.utime(name, (os.path.getatime(name), os.path.getmtime(name) - 2))
656
+ # Depending on the resolution, the exact value might not be
657
+ # recorded, so get the new recorded value.
658
+ timestamp = os.path.getmtime(name)
659
+
660
+ self.edit_files((name,))
661
+
662
+ if self.require_save and os.path.getmtime(name) == timestamp:
663
+ return None
664
+
665
+ with open(name, "rb") as f:
666
+ rv = f.read()
667
+
668
+ if isinstance(text, (bytes, bytearray)):
669
+ return rv
670
+
671
+ return rv.decode("utf-8-sig").replace("\r\n", "\n")
672
+ finally:
673
+ os.unlink(name)
674
+
675
+
676
+ def open_url(url: str, wait: bool = False, locate: bool = False) -> int:
677
+ import subprocess
678
+
679
+ def _unquote_file(url: str) -> str:
680
+ from urllib.parse import unquote
681
+
682
+ if url.startswith("file://"):
683
+ url = unquote(url[7:])
684
+
685
+ return url
686
+
687
+ if sys.platform == "darwin":
688
+ args = ["open"]
689
+ if wait:
690
+ args.append("-W")
691
+ if locate:
692
+ args.append("-R")
693
+ args.append(_unquote_file(url))
694
+ null = open("/dev/null", "w")
695
+ try:
696
+ return subprocess.Popen(args, stderr=null).wait()
697
+ finally:
698
+ null.close()
699
+ elif WIN:
700
+ if locate:
701
+ url = _unquote_file(url)
702
+ args = ["explorer", f"/select,{url}"]
703
+ else:
704
+ args = ["start"]
705
+ if wait:
706
+ args.append("/WAIT")
707
+ args.append("")
708
+ args.append(url)
709
+ try:
710
+ return subprocess.call(args)
711
+ except OSError:
712
+ # Command not found
713
+ return 127
714
+ elif CYGWIN:
715
+ if locate:
716
+ url = _unquote_file(url)
717
+ args = ["cygstart", os.path.dirname(url)]
718
+ else:
719
+ args = ["cygstart"]
720
+ if wait:
721
+ args.append("-w")
722
+ args.append(url)
723
+ try:
724
+ return subprocess.call(args)
725
+ except OSError:
726
+ # Command not found
727
+ return 127
728
+
729
+ try:
730
+ if locate:
731
+ url = os.path.dirname(_unquote_file(url)) or "."
732
+ else:
733
+ url = _unquote_file(url)
734
+ c = subprocess.Popen(["xdg-open", url])
735
+ if wait:
736
+ return c.wait()
737
+ return 0
738
+ except OSError:
739
+ if url.startswith(("http://", "https://")) and not locate and not wait:
740
+ import webbrowser
741
+
742
+ webbrowser.open(url)
743
+ return 0
744
+ return 1
745
+
746
+
747
+ def _translate_ch_to_exc(ch: str) -> None:
748
+ if ch == "\x03":
749
+ raise KeyboardInterrupt()
750
+
751
+ if ch == "\x04" and not WIN: # Unix-like, Ctrl+D
752
+ raise EOFError()
753
+
754
+ if ch == "\x1a" and WIN: # Windows, Ctrl+Z
755
+ raise EOFError()
756
+
757
+ return None
758
+
759
+
760
+ if sys.platform == "win32":
761
+ import msvcrt
762
+
763
+ @contextlib.contextmanager
764
+ def raw_terminal() -> cabc.Iterator[int]:
765
+ yield -1
766
+
767
+ def getchar(echo: bool) -> str:
768
+ # The function `getch` will return a bytes object corresponding to
769
+ # the pressed character. Since Windows 10 build 1803, it will also
770
+ # return \x00 when called a second time after pressing a regular key.
771
+ #
772
+ # `getwch` does not share this probably-bugged behavior. Moreover, it
773
+ # returns a Unicode object by default, which is what we want.
774
+ #
775
+ # Either of these functions will return \x00 or \xe0 to indicate
776
+ # a special key, and you need to call the same function again to get
777
+ # the "rest" of the code. The fun part is that \u00e0 is
778
+ # "latin small letter a with grave", so if you type that on a French
779
+ # keyboard, you _also_ get a \xe0.
780
+ # E.g., consider the Up arrow. This returns \xe0 and then \x48. The
781
+ # resulting Unicode string reads as "a with grave" + "capital H".
782
+ # This is indistinguishable from when the user actually types
783
+ # "a with grave" and then "capital H".
784
+ #
785
+ # When \xe0 is returned, we assume it's part of a special-key sequence
786
+ # and call `getwch` again, but that means that when the user types
787
+ # the \u00e0 character, `getchar` doesn't return until a second
788
+ # character is typed.
789
+ # The alternative is returning immediately, but that would mess up
790
+ # cross-platform handling of arrow keys and others that start with
791
+ # \xe0. Another option is using `getch`, but then we can't reliably
792
+ # read non-ASCII characters, because return values of `getch` are
793
+ # limited to the current 8-bit codepage.
794
+ #
795
+ # Anyway, Click doesn't claim to do this Right(tm), and using `getwch`
796
+ # is doing the right thing in more situations than with `getch`.
797
+
798
+ if echo:
799
+ func = t.cast(t.Callable[[], str], msvcrt.getwche)
800
+ else:
801
+ func = t.cast(t.Callable[[], str], msvcrt.getwch)
802
+
803
+ rv = func()
804
+
805
+ if rv in ("\x00", "\xe0"):
806
+ # \x00 and \xe0 are control characters that indicate special key,
807
+ # see above.
808
+ rv += func()
809
+
810
+ _translate_ch_to_exc(rv)
811
+ return rv
812
+
813
+ else:
814
+ import termios
815
+ import tty
816
+
817
+ @contextlib.contextmanager
818
+ def raw_terminal() -> cabc.Iterator[int]:
819
+ f: t.TextIO | None
820
+ fd: int
821
+
822
+ if not isatty(sys.stdin):
823
+ f = open("/dev/tty")
824
+ fd = f.fileno()
825
+ else:
826
+ fd = sys.stdin.fileno()
827
+ f = None
828
+
829
+ try:
830
+ old_settings = termios.tcgetattr(fd)
831
+
832
+ try:
833
+ tty.setraw(fd)
834
+ yield fd
835
+ finally:
836
+ termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
837
+ sys.stdout.flush()
838
+
839
+ if f is not None:
840
+ f.close()
841
+ except termios.error:
842
+ pass
843
+
844
+ def getchar(echo: bool) -> str:
845
+ with raw_terminal() as fd:
846
+ ch = os.read(fd, 32).decode(get_best_encoding(sys.stdin), "replace")
847
+
848
+ if echo and isatty(sys.stdout):
849
+ sys.stdout.write(ch)
850
+
851
+ _translate_ch_to_exc(ch)
852
+ return ch
env/lib/python3.13/site-packages/click/_textwrap.py ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import collections.abc as cabc
4
+ import textwrap
5
+ from contextlib import contextmanager
6
+
7
+
8
+ class TextWrapper(textwrap.TextWrapper):
9
+ def _handle_long_word(
10
+ self,
11
+ reversed_chunks: list[str],
12
+ cur_line: list[str],
13
+ cur_len: int,
14
+ width: int,
15
+ ) -> None:
16
+ space_left = max(width - cur_len, 1)
17
+
18
+ if self.break_long_words:
19
+ last = reversed_chunks[-1]
20
+ cut = last[:space_left]
21
+ res = last[space_left:]
22
+ cur_line.append(cut)
23
+ reversed_chunks[-1] = res
24
+ elif not cur_line:
25
+ cur_line.append(reversed_chunks.pop())
26
+
27
+ @contextmanager
28
+ def extra_indent(self, indent: str) -> cabc.Iterator[None]:
29
+ old_initial_indent = self.initial_indent
30
+ old_subsequent_indent = self.subsequent_indent
31
+ self.initial_indent += indent
32
+ self.subsequent_indent += indent
33
+
34
+ try:
35
+ yield
36
+ finally:
37
+ self.initial_indent = old_initial_indent
38
+ self.subsequent_indent = old_subsequent_indent
39
+
40
+ def indent_only(self, text: str) -> str:
41
+ rv = []
42
+
43
+ for idx, line in enumerate(text.splitlines()):
44
+ indent = self.initial_indent
45
+
46
+ if idx > 0:
47
+ indent = self.subsequent_indent
48
+
49
+ rv.append(f"{indent}{line}")
50
+
51
+ return "\n".join(rv)
env/lib/python3.13/site-packages/click/_utils.py ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import enum
4
+ import typing as t
5
+
6
+
7
+ class Sentinel(enum.Enum):
8
+ """Enum used to define sentinel values.
9
+
10
+ .. seealso::
11
+
12
+ `PEP 661 - Sentinel Values <https://peps.python.org/pep-0661/>`_.
13
+ """
14
+
15
+ UNSET = object()
16
+ FLAG_NEEDS_VALUE = object()
17
+
18
+ def __repr__(self) -> str:
19
+ return f"{self.__class__.__name__}.{self.name}"
20
+
21
+
22
+ UNSET = Sentinel.UNSET
23
+ """Sentinel used to indicate that a value is not set."""
24
+
25
+ FLAG_NEEDS_VALUE = Sentinel.FLAG_NEEDS_VALUE
26
+ """Sentinel used to indicate an option was passed as a flag without a
27
+ value but is not a flag option.
28
+
29
+ ``Option.consume_value`` uses this to prompt or use the ``flag_value``.
30
+ """
31
+
32
+ T_UNSET = t.Literal[UNSET] # type: ignore[valid-type]
33
+ """Type hint for the :data:`UNSET` sentinel value."""
34
+
35
+ T_FLAG_NEEDS_VALUE = t.Literal[FLAG_NEEDS_VALUE] # type: ignore[valid-type]
36
+ """Type hint for the :data:`FLAG_NEEDS_VALUE` sentinel value."""
env/lib/python3.13/site-packages/click/_winconsole.py ADDED
@@ -0,0 +1,296 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This module is based on the excellent work by Adam Bartoš who
2
+ # provided a lot of what went into the implementation here in
3
+ # the discussion to issue1602 in the Python bug tracker.
4
+ #
5
+ # There are some general differences in regards to how this works
6
+ # compared to the original patches as we do not need to patch
7
+ # the entire interpreter but just work in our little world of
8
+ # echo and prompt.
9
+ from __future__ import annotations
10
+
11
+ import collections.abc as cabc
12
+ import io
13
+ import sys
14
+ import time
15
+ import typing as t
16
+ from ctypes import Array
17
+ from ctypes import byref
18
+ from ctypes import c_char
19
+ from ctypes import c_char_p
20
+ from ctypes import c_int
21
+ from ctypes import c_ssize_t
22
+ from ctypes import c_ulong
23
+ from ctypes import c_void_p
24
+ from ctypes import POINTER
25
+ from ctypes import py_object
26
+ from ctypes import Structure
27
+ from ctypes.wintypes import DWORD
28
+ from ctypes.wintypes import HANDLE
29
+ from ctypes.wintypes import LPCWSTR
30
+ from ctypes.wintypes import LPWSTR
31
+
32
+ from ._compat import _NonClosingTextIOWrapper
33
+
34
+ assert sys.platform == "win32"
35
+ import msvcrt # noqa: E402
36
+ from ctypes import windll # noqa: E402
37
+ from ctypes import WINFUNCTYPE # noqa: E402
38
+
39
+ c_ssize_p = POINTER(c_ssize_t)
40
+
41
+ kernel32 = windll.kernel32
42
+ GetStdHandle = kernel32.GetStdHandle
43
+ ReadConsoleW = kernel32.ReadConsoleW
44
+ WriteConsoleW = kernel32.WriteConsoleW
45
+ GetConsoleMode = kernel32.GetConsoleMode
46
+ GetLastError = kernel32.GetLastError
47
+ GetCommandLineW = WINFUNCTYPE(LPWSTR)(("GetCommandLineW", windll.kernel32))
48
+ CommandLineToArgvW = WINFUNCTYPE(POINTER(LPWSTR), LPCWSTR, POINTER(c_int))(
49
+ ("CommandLineToArgvW", windll.shell32)
50
+ )
51
+ LocalFree = WINFUNCTYPE(c_void_p, c_void_p)(("LocalFree", windll.kernel32))
52
+
53
+ STDIN_HANDLE = GetStdHandle(-10)
54
+ STDOUT_HANDLE = GetStdHandle(-11)
55
+ STDERR_HANDLE = GetStdHandle(-12)
56
+
57
+ PyBUF_SIMPLE = 0
58
+ PyBUF_WRITABLE = 1
59
+
60
+ ERROR_SUCCESS = 0
61
+ ERROR_NOT_ENOUGH_MEMORY = 8
62
+ ERROR_OPERATION_ABORTED = 995
63
+
64
+ STDIN_FILENO = 0
65
+ STDOUT_FILENO = 1
66
+ STDERR_FILENO = 2
67
+
68
+ EOF = b"\x1a"
69
+ MAX_BYTES_WRITTEN = 32767
70
+
71
+ if t.TYPE_CHECKING:
72
+ try:
73
+ # Using `typing_extensions.Buffer` instead of `collections.abc`
74
+ # on Windows for some reason does not have `Sized` implemented.
75
+ from collections.abc import Buffer # type: ignore
76
+ except ImportError:
77
+ from typing_extensions import Buffer
78
+
79
+ try:
80
+ from ctypes import pythonapi
81
+ except ImportError:
82
+ # On PyPy we cannot get buffers so our ability to operate here is
83
+ # severely limited.
84
+ get_buffer = None
85
+ else:
86
+
87
+ class Py_buffer(Structure):
88
+ _fields_ = [ # noqa: RUF012
89
+ ("buf", c_void_p),
90
+ ("obj", py_object),
91
+ ("len", c_ssize_t),
92
+ ("itemsize", c_ssize_t),
93
+ ("readonly", c_int),
94
+ ("ndim", c_int),
95
+ ("format", c_char_p),
96
+ ("shape", c_ssize_p),
97
+ ("strides", c_ssize_p),
98
+ ("suboffsets", c_ssize_p),
99
+ ("internal", c_void_p),
100
+ ]
101
+
102
+ PyObject_GetBuffer = pythonapi.PyObject_GetBuffer
103
+ PyBuffer_Release = pythonapi.PyBuffer_Release
104
+
105
+ def get_buffer(obj: Buffer, writable: bool = False) -> Array[c_char]:
106
+ buf = Py_buffer()
107
+ flags: int = PyBUF_WRITABLE if writable else PyBUF_SIMPLE
108
+ PyObject_GetBuffer(py_object(obj), byref(buf), flags)
109
+
110
+ try:
111
+ buffer_type = c_char * buf.len
112
+ out: Array[c_char] = buffer_type.from_address(buf.buf)
113
+ return out
114
+ finally:
115
+ PyBuffer_Release(byref(buf))
116
+
117
+
118
+ class _WindowsConsoleRawIOBase(io.RawIOBase):
119
+ def __init__(self, handle: int | None) -> None:
120
+ self.handle = handle
121
+
122
+ def isatty(self) -> t.Literal[True]:
123
+ super().isatty()
124
+ return True
125
+
126
+
127
+ class _WindowsConsoleReader(_WindowsConsoleRawIOBase):
128
+ def readable(self) -> t.Literal[True]:
129
+ return True
130
+
131
+ def readinto(self, b: Buffer) -> int:
132
+ bytes_to_be_read = len(b)
133
+ if not bytes_to_be_read:
134
+ return 0
135
+ elif bytes_to_be_read % 2:
136
+ raise ValueError(
137
+ "cannot read odd number of bytes from UTF-16-LE encoded console"
138
+ )
139
+
140
+ buffer = get_buffer(b, writable=True)
141
+ code_units_to_be_read = bytes_to_be_read // 2
142
+ code_units_read = c_ulong()
143
+
144
+ rv = ReadConsoleW(
145
+ HANDLE(self.handle),
146
+ buffer,
147
+ code_units_to_be_read,
148
+ byref(code_units_read),
149
+ None,
150
+ )
151
+ if GetLastError() == ERROR_OPERATION_ABORTED:
152
+ # wait for KeyboardInterrupt
153
+ time.sleep(0.1)
154
+ if not rv:
155
+ raise OSError(f"Windows error: {GetLastError()}")
156
+
157
+ if buffer[0] == EOF:
158
+ return 0
159
+ return 2 * code_units_read.value
160
+
161
+
162
+ class _WindowsConsoleWriter(_WindowsConsoleRawIOBase):
163
+ def writable(self) -> t.Literal[True]:
164
+ return True
165
+
166
+ @staticmethod
167
+ def _get_error_message(errno: int) -> str:
168
+ if errno == ERROR_SUCCESS:
169
+ return "ERROR_SUCCESS"
170
+ elif errno == ERROR_NOT_ENOUGH_MEMORY:
171
+ return "ERROR_NOT_ENOUGH_MEMORY"
172
+ return f"Windows error {errno}"
173
+
174
+ def write(self, b: Buffer) -> int:
175
+ bytes_to_be_written = len(b)
176
+ buf = get_buffer(b)
177
+ code_units_to_be_written = min(bytes_to_be_written, MAX_BYTES_WRITTEN) // 2
178
+ code_units_written = c_ulong()
179
+
180
+ WriteConsoleW(
181
+ HANDLE(self.handle),
182
+ buf,
183
+ code_units_to_be_written,
184
+ byref(code_units_written),
185
+ None,
186
+ )
187
+ bytes_written = 2 * code_units_written.value
188
+
189
+ if bytes_written == 0 and bytes_to_be_written > 0:
190
+ raise OSError(self._get_error_message(GetLastError()))
191
+ return bytes_written
192
+
193
+
194
+ class ConsoleStream:
195
+ def __init__(self, text_stream: t.TextIO, byte_stream: t.BinaryIO) -> None:
196
+ self._text_stream = text_stream
197
+ self.buffer = byte_stream
198
+
199
+ @property
200
+ def name(self) -> str:
201
+ return self.buffer.name
202
+
203
+ def write(self, x: t.AnyStr) -> int:
204
+ if isinstance(x, str):
205
+ return self._text_stream.write(x)
206
+ try:
207
+ self.flush()
208
+ except Exception:
209
+ pass
210
+ return self.buffer.write(x)
211
+
212
+ def writelines(self, lines: cabc.Iterable[t.AnyStr]) -> None:
213
+ for line in lines:
214
+ self.write(line)
215
+
216
+ def __getattr__(self, name: str) -> t.Any:
217
+ return getattr(self._text_stream, name)
218
+
219
+ def isatty(self) -> bool:
220
+ return self.buffer.isatty()
221
+
222
+ def __repr__(self) -> str:
223
+ return f"<ConsoleStream name={self.name!r} encoding={self.encoding!r}>"
224
+
225
+
226
+ def _get_text_stdin(buffer_stream: t.BinaryIO) -> t.TextIO:
227
+ text_stream = _NonClosingTextIOWrapper(
228
+ io.BufferedReader(_WindowsConsoleReader(STDIN_HANDLE)),
229
+ "utf-16-le",
230
+ "strict",
231
+ line_buffering=True,
232
+ )
233
+ return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream))
234
+
235
+
236
+ def _get_text_stdout(buffer_stream: t.BinaryIO) -> t.TextIO:
237
+ text_stream = _NonClosingTextIOWrapper(
238
+ io.BufferedWriter(_WindowsConsoleWriter(STDOUT_HANDLE)),
239
+ "utf-16-le",
240
+ "strict",
241
+ line_buffering=True,
242
+ )
243
+ return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream))
244
+
245
+
246
+ def _get_text_stderr(buffer_stream: t.BinaryIO) -> t.TextIO:
247
+ text_stream = _NonClosingTextIOWrapper(
248
+ io.BufferedWriter(_WindowsConsoleWriter(STDERR_HANDLE)),
249
+ "utf-16-le",
250
+ "strict",
251
+ line_buffering=True,
252
+ )
253
+ return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream))
254
+
255
+
256
+ _stream_factories: cabc.Mapping[int, t.Callable[[t.BinaryIO], t.TextIO]] = {
257
+ 0: _get_text_stdin,
258
+ 1: _get_text_stdout,
259
+ 2: _get_text_stderr,
260
+ }
261
+
262
+
263
+ def _is_console(f: t.TextIO) -> bool:
264
+ if not hasattr(f, "fileno"):
265
+ return False
266
+
267
+ try:
268
+ fileno = f.fileno()
269
+ except (OSError, io.UnsupportedOperation):
270
+ return False
271
+
272
+ handle = msvcrt.get_osfhandle(fileno)
273
+ return bool(GetConsoleMode(handle, byref(DWORD())))
274
+
275
+
276
+ def _get_windows_console_stream(
277
+ f: t.TextIO, encoding: str | None, errors: str | None
278
+ ) -> t.TextIO | None:
279
+ if (
280
+ get_buffer is None
281
+ or encoding not in {"utf-16-le", None}
282
+ or errors not in {"strict", None}
283
+ or not _is_console(f)
284
+ ):
285
+ return None
286
+
287
+ func = _stream_factories.get(f.fileno())
288
+ if func is None:
289
+ return None
290
+
291
+ b = getattr(f, "buffer", None)
292
+
293
+ if b is None:
294
+ return None
295
+
296
+ return func(b)
env/lib/python3.13/site-packages/click/core.py ADDED
The diff for this file is too large to render. See raw diff
 
env/lib/python3.13/site-packages/click/decorators.py ADDED
@@ -0,0 +1,551 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import inspect
4
+ import typing as t
5
+ from functools import update_wrapper
6
+ from gettext import gettext as _
7
+
8
+ from .core import Argument
9
+ from .core import Command
10
+ from .core import Context
11
+ from .core import Group
12
+ from .core import Option
13
+ from .core import Parameter
14
+ from .globals import get_current_context
15
+ from .utils import echo
16
+
17
+ if t.TYPE_CHECKING:
18
+ import typing_extensions as te
19
+
20
+ P = te.ParamSpec("P")
21
+
22
+ R = t.TypeVar("R")
23
+ T = t.TypeVar("T")
24
+ _AnyCallable = t.Callable[..., t.Any]
25
+ FC = t.TypeVar("FC", bound="_AnyCallable | Command")
26
+
27
+
28
+ def pass_context(f: t.Callable[te.Concatenate[Context, P], R]) -> t.Callable[P, R]:
29
+ """Marks a callback as wanting to receive the current context
30
+ object as first argument.
31
+ """
32
+
33
+ def new_func(*args: P.args, **kwargs: P.kwargs) -> R:
34
+ return f(get_current_context(), *args, **kwargs)
35
+
36
+ return update_wrapper(new_func, f)
37
+
38
+
39
+ def pass_obj(f: t.Callable[te.Concatenate[T, P], R]) -> t.Callable[P, R]:
40
+ """Similar to :func:`pass_context`, but only pass the object on the
41
+ context onwards (:attr:`Context.obj`). This is useful if that object
42
+ represents the state of a nested system.
43
+ """
44
+
45
+ def new_func(*args: P.args, **kwargs: P.kwargs) -> R:
46
+ return f(get_current_context().obj, *args, **kwargs)
47
+
48
+ return update_wrapper(new_func, f)
49
+
50
+
51
+ def make_pass_decorator(
52
+ object_type: type[T], ensure: bool = False
53
+ ) -> t.Callable[[t.Callable[te.Concatenate[T, P], R]], t.Callable[P, R]]:
54
+ """Given an object type this creates a decorator that will work
55
+ similar to :func:`pass_obj` but instead of passing the object of the
56
+ current context, it will find the innermost context of type
57
+ :func:`object_type`.
58
+
59
+ This generates a decorator that works roughly like this::
60
+
61
+ from functools import update_wrapper
62
+
63
+ def decorator(f):
64
+ @pass_context
65
+ def new_func(ctx, *args, **kwargs):
66
+ obj = ctx.find_object(object_type)
67
+ return ctx.invoke(f, obj, *args, **kwargs)
68
+ return update_wrapper(new_func, f)
69
+ return decorator
70
+
71
+ :param object_type: the type of the object to pass.
72
+ :param ensure: if set to `True`, a new object will be created and
73
+ remembered on the context if it's not there yet.
74
+ """
75
+
76
+ def decorator(f: t.Callable[te.Concatenate[T, P], R]) -> t.Callable[P, R]:
77
+ def new_func(*args: P.args, **kwargs: P.kwargs) -> R:
78
+ ctx = get_current_context()
79
+
80
+ obj: T | None
81
+ if ensure:
82
+ obj = ctx.ensure_object(object_type)
83
+ else:
84
+ obj = ctx.find_object(object_type)
85
+
86
+ if obj is None:
87
+ raise RuntimeError(
88
+ "Managed to invoke callback without a context"
89
+ f" object of type {object_type.__name__!r}"
90
+ " existing."
91
+ )
92
+
93
+ return ctx.invoke(f, obj, *args, **kwargs)
94
+
95
+ return update_wrapper(new_func, f)
96
+
97
+ return decorator
98
+
99
+
100
+ def pass_meta_key(
101
+ key: str, *, doc_description: str | None = None
102
+ ) -> t.Callable[[t.Callable[te.Concatenate[T, P], R]], t.Callable[P, R]]:
103
+ """Create a decorator that passes a key from
104
+ :attr:`click.Context.meta` as the first argument to the decorated
105
+ function.
106
+
107
+ :param key: Key in ``Context.meta`` to pass.
108
+ :param doc_description: Description of the object being passed,
109
+ inserted into the decorator's docstring. Defaults to "the 'key'
110
+ key from Context.meta".
111
+
112
+ .. versionadded:: 8.0
113
+ """
114
+
115
+ def decorator(f: t.Callable[te.Concatenate[T, P], R]) -> t.Callable[P, R]:
116
+ def new_func(*args: P.args, **kwargs: P.kwargs) -> R:
117
+ ctx = get_current_context()
118
+ obj = ctx.meta[key]
119
+ return ctx.invoke(f, obj, *args, **kwargs)
120
+
121
+ return update_wrapper(new_func, f)
122
+
123
+ if doc_description is None:
124
+ doc_description = f"the {key!r} key from :attr:`click.Context.meta`"
125
+
126
+ decorator.__doc__ = (
127
+ f"Decorator that passes {doc_description} as the first argument"
128
+ " to the decorated function."
129
+ )
130
+ return decorator
131
+
132
+
133
+ CmdType = t.TypeVar("CmdType", bound=Command)
134
+
135
+
136
+ # variant: no call, directly as decorator for a function.
137
+ @t.overload
138
+ def command(name: _AnyCallable) -> Command: ...
139
+
140
+
141
+ # variant: with positional name and with positional or keyword cls argument:
142
+ # @command(namearg, CommandCls, ...) or @command(namearg, cls=CommandCls, ...)
143
+ @t.overload
144
+ def command(
145
+ name: str | None,
146
+ cls: type[CmdType],
147
+ **attrs: t.Any,
148
+ ) -> t.Callable[[_AnyCallable], CmdType]: ...
149
+
150
+
151
+ # variant: name omitted, cls _must_ be a keyword argument, @command(cls=CommandCls, ...)
152
+ @t.overload
153
+ def command(
154
+ name: None = None,
155
+ *,
156
+ cls: type[CmdType],
157
+ **attrs: t.Any,
158
+ ) -> t.Callable[[_AnyCallable], CmdType]: ...
159
+
160
+
161
+ # variant: with optional string name, no cls argument provided.
162
+ @t.overload
163
+ def command(
164
+ name: str | None = ..., cls: None = None, **attrs: t.Any
165
+ ) -> t.Callable[[_AnyCallable], Command]: ...
166
+
167
+
168
+ def command(
169
+ name: str | _AnyCallable | None = None,
170
+ cls: type[CmdType] | None = None,
171
+ **attrs: t.Any,
172
+ ) -> Command | t.Callable[[_AnyCallable], Command | CmdType]:
173
+ r"""Creates a new :class:`Command` and uses the decorated function as
174
+ callback. This will also automatically attach all decorated
175
+ :func:`option`\s and :func:`argument`\s as parameters to the command.
176
+
177
+ The name of the command defaults to the name of the function, converted to
178
+ lowercase, with underscores ``_`` replaced by dashes ``-``, and the suffixes
179
+ ``_command``, ``_cmd``, ``_group``, and ``_grp`` are removed. For example,
180
+ ``init_data_command`` becomes ``init-data``.
181
+
182
+ All keyword arguments are forwarded to the underlying command class.
183
+ For the ``params`` argument, any decorated params are appended to
184
+ the end of the list.
185
+
186
+ Once decorated the function turns into a :class:`Command` instance
187
+ that can be invoked as a command line utility or be attached to a
188
+ command :class:`Group`.
189
+
190
+ :param name: The name of the command. Defaults to modifying the function's
191
+ name as described above.
192
+ :param cls: The command class to create. Defaults to :class:`Command`.
193
+
194
+ .. versionchanged:: 8.2
195
+ The suffixes ``_command``, ``_cmd``, ``_group``, and ``_grp`` are
196
+ removed when generating the name.
197
+
198
+ .. versionchanged:: 8.1
199
+ This decorator can be applied without parentheses.
200
+
201
+ .. versionchanged:: 8.1
202
+ The ``params`` argument can be used. Decorated params are
203
+ appended to the end of the list.
204
+ """
205
+
206
+ func: t.Callable[[_AnyCallable], t.Any] | None = None
207
+
208
+ if callable(name):
209
+ func = name
210
+ name = None
211
+ assert cls is None, "Use 'command(cls=cls)(callable)' to specify a class."
212
+ assert not attrs, "Use 'command(**kwargs)(callable)' to provide arguments."
213
+
214
+ if cls is None:
215
+ cls = t.cast("type[CmdType]", Command)
216
+
217
+ def decorator(f: _AnyCallable) -> CmdType:
218
+ if isinstance(f, Command):
219
+ raise TypeError("Attempted to convert a callback into a command twice.")
220
+
221
+ attr_params = attrs.pop("params", None)
222
+ params = attr_params if attr_params is not None else []
223
+
224
+ try:
225
+ decorator_params = f.__click_params__ # type: ignore
226
+ except AttributeError:
227
+ pass
228
+ else:
229
+ del f.__click_params__ # type: ignore
230
+ params.extend(reversed(decorator_params))
231
+
232
+ if attrs.get("help") is None:
233
+ attrs["help"] = f.__doc__
234
+
235
+ if t.TYPE_CHECKING:
236
+ assert cls is not None
237
+ assert not callable(name)
238
+
239
+ if name is not None:
240
+ cmd_name = name
241
+ else:
242
+ cmd_name = f.__name__.lower().replace("_", "-")
243
+ cmd_left, sep, suffix = cmd_name.rpartition("-")
244
+
245
+ if sep and suffix in {"command", "cmd", "group", "grp"}:
246
+ cmd_name = cmd_left
247
+
248
+ cmd = cls(name=cmd_name, callback=f, params=params, **attrs)
249
+ cmd.__doc__ = f.__doc__
250
+ return cmd
251
+
252
+ if func is not None:
253
+ return decorator(func)
254
+
255
+ return decorator
256
+
257
+
258
+ GrpType = t.TypeVar("GrpType", bound=Group)
259
+
260
+
261
+ # variant: no call, directly as decorator for a function.
262
+ @t.overload
263
+ def group(name: _AnyCallable) -> Group: ...
264
+
265
+
266
+ # variant: with positional name and with positional or keyword cls argument:
267
+ # @group(namearg, GroupCls, ...) or @group(namearg, cls=GroupCls, ...)
268
+ @t.overload
269
+ def group(
270
+ name: str | None,
271
+ cls: type[GrpType],
272
+ **attrs: t.Any,
273
+ ) -> t.Callable[[_AnyCallable], GrpType]: ...
274
+
275
+
276
+ # variant: name omitted, cls _must_ be a keyword argument, @group(cmd=GroupCls, ...)
277
+ @t.overload
278
+ def group(
279
+ name: None = None,
280
+ *,
281
+ cls: type[GrpType],
282
+ **attrs: t.Any,
283
+ ) -> t.Callable[[_AnyCallable], GrpType]: ...
284
+
285
+
286
+ # variant: with optional string name, no cls argument provided.
287
+ @t.overload
288
+ def group(
289
+ name: str | None = ..., cls: None = None, **attrs: t.Any
290
+ ) -> t.Callable[[_AnyCallable], Group]: ...
291
+
292
+
293
+ def group(
294
+ name: str | _AnyCallable | None = None,
295
+ cls: type[GrpType] | None = None,
296
+ **attrs: t.Any,
297
+ ) -> Group | t.Callable[[_AnyCallable], Group | GrpType]:
298
+ """Creates a new :class:`Group` with a function as callback. This
299
+ works otherwise the same as :func:`command` just that the `cls`
300
+ parameter is set to :class:`Group`.
301
+
302
+ .. versionchanged:: 8.1
303
+ This decorator can be applied without parentheses.
304
+ """
305
+ if cls is None:
306
+ cls = t.cast("type[GrpType]", Group)
307
+
308
+ if callable(name):
309
+ return command(cls=cls, **attrs)(name)
310
+
311
+ return command(name, cls, **attrs)
312
+
313
+
314
+ def _param_memo(f: t.Callable[..., t.Any], param: Parameter) -> None:
315
+ if isinstance(f, Command):
316
+ f.params.append(param)
317
+ else:
318
+ if not hasattr(f, "__click_params__"):
319
+ f.__click_params__ = [] # type: ignore
320
+
321
+ f.__click_params__.append(param) # type: ignore
322
+
323
+
324
+ def argument(
325
+ *param_decls: str, cls: type[Argument] | None = None, **attrs: t.Any
326
+ ) -> t.Callable[[FC], FC]:
327
+ """Attaches an argument to the command. All positional arguments are
328
+ passed as parameter declarations to :class:`Argument`; all keyword
329
+ arguments are forwarded unchanged (except ``cls``).
330
+ This is equivalent to creating an :class:`Argument` instance manually
331
+ and attaching it to the :attr:`Command.params` list.
332
+
333
+ For the default argument class, refer to :class:`Argument` and
334
+ :class:`Parameter` for descriptions of parameters.
335
+
336
+ :param cls: the argument class to instantiate. This defaults to
337
+ :class:`Argument`.
338
+ :param param_decls: Passed as positional arguments to the constructor of
339
+ ``cls``.
340
+ :param attrs: Passed as keyword arguments to the constructor of ``cls``.
341
+ """
342
+ if cls is None:
343
+ cls = Argument
344
+
345
+ def decorator(f: FC) -> FC:
346
+ _param_memo(f, cls(param_decls, **attrs))
347
+ return f
348
+
349
+ return decorator
350
+
351
+
352
+ def option(
353
+ *param_decls: str, cls: type[Option] | None = None, **attrs: t.Any
354
+ ) -> t.Callable[[FC], FC]:
355
+ """Attaches an option to the command. All positional arguments are
356
+ passed as parameter declarations to :class:`Option`; all keyword
357
+ arguments are forwarded unchanged (except ``cls``).
358
+ This is equivalent to creating an :class:`Option` instance manually
359
+ and attaching it to the :attr:`Command.params` list.
360
+
361
+ For the default option class, refer to :class:`Option` and
362
+ :class:`Parameter` for descriptions of parameters.
363
+
364
+ :param cls: the option class to instantiate. This defaults to
365
+ :class:`Option`.
366
+ :param param_decls: Passed as positional arguments to the constructor of
367
+ ``cls``.
368
+ :param attrs: Passed as keyword arguments to the constructor of ``cls``.
369
+ """
370
+ if cls is None:
371
+ cls = Option
372
+
373
+ def decorator(f: FC) -> FC:
374
+ _param_memo(f, cls(param_decls, **attrs))
375
+ return f
376
+
377
+ return decorator
378
+
379
+
380
+ def confirmation_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]:
381
+ """Add a ``--yes`` option which shows a prompt before continuing if
382
+ not passed. If the prompt is declined, the program will exit.
383
+
384
+ :param param_decls: One or more option names. Defaults to the single
385
+ value ``"--yes"``.
386
+ :param kwargs: Extra arguments are passed to :func:`option`.
387
+ """
388
+
389
+ def callback(ctx: Context, param: Parameter, value: bool) -> None:
390
+ if not value:
391
+ ctx.abort()
392
+
393
+ if not param_decls:
394
+ param_decls = ("--yes",)
395
+
396
+ kwargs.setdefault("is_flag", True)
397
+ kwargs.setdefault("callback", callback)
398
+ kwargs.setdefault("expose_value", False)
399
+ kwargs.setdefault("prompt", "Do you want to continue?")
400
+ kwargs.setdefault("help", "Confirm the action without prompting.")
401
+ return option(*param_decls, **kwargs)
402
+
403
+
404
+ def password_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]:
405
+ """Add a ``--password`` option which prompts for a password, hiding
406
+ input and asking to enter the value again for confirmation.
407
+
408
+ :param param_decls: One or more option names. Defaults to the single
409
+ value ``"--password"``.
410
+ :param kwargs: Extra arguments are passed to :func:`option`.
411
+ """
412
+ if not param_decls:
413
+ param_decls = ("--password",)
414
+
415
+ kwargs.setdefault("prompt", True)
416
+ kwargs.setdefault("confirmation_prompt", True)
417
+ kwargs.setdefault("hide_input", True)
418
+ return option(*param_decls, **kwargs)
419
+
420
+
421
+ def version_option(
422
+ version: str | None = None,
423
+ *param_decls: str,
424
+ package_name: str | None = None,
425
+ prog_name: str | None = None,
426
+ message: str | None = None,
427
+ **kwargs: t.Any,
428
+ ) -> t.Callable[[FC], FC]:
429
+ """Add a ``--version`` option which immediately prints the version
430
+ number and exits the program.
431
+
432
+ If ``version`` is not provided, Click will try to detect it using
433
+ :func:`importlib.metadata.version` to get the version for the
434
+ ``package_name``.
435
+
436
+ If ``package_name`` is not provided, Click will try to detect it by
437
+ inspecting the stack frames. This will be used to detect the
438
+ version, so it must match the name of the installed package.
439
+
440
+ :param version: The version number to show. If not provided, Click
441
+ will try to detect it.
442
+ :param param_decls: One or more option names. Defaults to the single
443
+ value ``"--version"``.
444
+ :param package_name: The package name to detect the version from. If
445
+ not provided, Click will try to detect it.
446
+ :param prog_name: The name of the CLI to show in the message. If not
447
+ provided, it will be detected from the command.
448
+ :param message: The message to show. The values ``%(prog)s``,
449
+ ``%(package)s``, and ``%(version)s`` are available. Defaults to
450
+ ``"%(prog)s, version %(version)s"``.
451
+ :param kwargs: Extra arguments are passed to :func:`option`.
452
+ :raise RuntimeError: ``version`` could not be detected.
453
+
454
+ .. versionchanged:: 8.0
455
+ Add the ``package_name`` parameter, and the ``%(package)s``
456
+ value for messages.
457
+
458
+ .. versionchanged:: 8.0
459
+ Use :mod:`importlib.metadata` instead of ``pkg_resources``. The
460
+ version is detected based on the package name, not the entry
461
+ point name. The Python package name must match the installed
462
+ package name, or be passed with ``package_name=``.
463
+ """
464
+ if message is None:
465
+ message = _("%(prog)s, version %(version)s")
466
+
467
+ if version is None and package_name is None:
468
+ frame = inspect.currentframe()
469
+ f_back = frame.f_back if frame is not None else None
470
+ f_globals = f_back.f_globals if f_back is not None else None
471
+ # break reference cycle
472
+ # https://docs.python.org/3/library/inspect.html#the-interpreter-stack
473
+ del frame
474
+
475
+ if f_globals is not None:
476
+ package_name = f_globals.get("__name__")
477
+
478
+ if package_name == "__main__":
479
+ package_name = f_globals.get("__package__")
480
+
481
+ if package_name:
482
+ package_name = package_name.partition(".")[0]
483
+
484
+ def callback(ctx: Context, param: Parameter, value: bool) -> None:
485
+ if not value or ctx.resilient_parsing:
486
+ return
487
+
488
+ nonlocal prog_name
489
+ nonlocal version
490
+
491
+ if prog_name is None:
492
+ prog_name = ctx.find_root().info_name
493
+
494
+ if version is None and package_name is not None:
495
+ import importlib.metadata
496
+
497
+ try:
498
+ version = importlib.metadata.version(package_name)
499
+ except importlib.metadata.PackageNotFoundError:
500
+ raise RuntimeError(
501
+ f"{package_name!r} is not installed. Try passing"
502
+ " 'package_name' instead."
503
+ ) from None
504
+
505
+ if version is None:
506
+ raise RuntimeError(
507
+ f"Could not determine the version for {package_name!r} automatically."
508
+ )
509
+
510
+ echo(
511
+ message % {"prog": prog_name, "package": package_name, "version": version},
512
+ color=ctx.color,
513
+ )
514
+ ctx.exit()
515
+
516
+ if not param_decls:
517
+ param_decls = ("--version",)
518
+
519
+ kwargs.setdefault("is_flag", True)
520
+ kwargs.setdefault("expose_value", False)
521
+ kwargs.setdefault("is_eager", True)
522
+ kwargs.setdefault("help", _("Show the version and exit."))
523
+ kwargs["callback"] = callback
524
+ return option(*param_decls, **kwargs)
525
+
526
+
527
+ def help_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]:
528
+ """Pre-configured ``--help`` option which immediately prints the help page
529
+ and exits the program.
530
+
531
+ :param param_decls: One or more option names. Defaults to the single
532
+ value ``"--help"``.
533
+ :param kwargs: Extra arguments are passed to :func:`option`.
534
+ """
535
+
536
+ def show_help(ctx: Context, param: Parameter, value: bool) -> None:
537
+ """Callback that print the help page on ``<stdout>`` and exits."""
538
+ if value and not ctx.resilient_parsing:
539
+ echo(ctx.get_help(), color=ctx.color)
540
+ ctx.exit()
541
+
542
+ if not param_decls:
543
+ param_decls = ("--help",)
544
+
545
+ kwargs.setdefault("is_flag", True)
546
+ kwargs.setdefault("expose_value", False)
547
+ kwargs.setdefault("is_eager", True)
548
+ kwargs.setdefault("help", _("Show this message and exit."))
549
+ kwargs.setdefault("callback", show_help)
550
+
551
+ return option(*param_decls, **kwargs)
env/lib/python3.13/site-packages/click/exceptions.py ADDED
@@ -0,0 +1,308 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import collections.abc as cabc
4
+ import typing as t
5
+ from gettext import gettext as _
6
+ from gettext import ngettext
7
+
8
+ from ._compat import get_text_stderr
9
+ from .globals import resolve_color_default
10
+ from .utils import echo
11
+ from .utils import format_filename
12
+
13
+ if t.TYPE_CHECKING:
14
+ from .core import Command
15
+ from .core import Context
16
+ from .core import Parameter
17
+
18
+
19
+ def _join_param_hints(param_hint: cabc.Sequence[str] | str | None) -> str | None:
20
+ if param_hint is not None and not isinstance(param_hint, str):
21
+ return " / ".join(repr(x) for x in param_hint)
22
+
23
+ return param_hint
24
+
25
+
26
+ class ClickException(Exception):
27
+ """An exception that Click can handle and show to the user."""
28
+
29
+ #: The exit code for this exception.
30
+ exit_code = 1
31
+
32
+ def __init__(self, message: str) -> None:
33
+ super().__init__(message)
34
+ # The context will be removed by the time we print the message, so cache
35
+ # the color settings here to be used later on (in `show`)
36
+ self.show_color: bool | None = resolve_color_default()
37
+ self.message = message
38
+
39
+ def format_message(self) -> str:
40
+ return self.message
41
+
42
+ def __str__(self) -> str:
43
+ return self.message
44
+
45
+ def show(self, file: t.IO[t.Any] | None = None) -> None:
46
+ if file is None:
47
+ file = get_text_stderr()
48
+
49
+ echo(
50
+ _("Error: {message}").format(message=self.format_message()),
51
+ file=file,
52
+ color=self.show_color,
53
+ )
54
+
55
+
56
+ class UsageError(ClickException):
57
+ """An internal exception that signals a usage error. This typically
58
+ aborts any further handling.
59
+
60
+ :param message: the error message to display.
61
+ :param ctx: optionally the context that caused this error. Click will
62
+ fill in the context automatically in some situations.
63
+ """
64
+
65
+ exit_code = 2
66
+
67
+ def __init__(self, message: str, ctx: Context | None = None) -> None:
68
+ super().__init__(message)
69
+ self.ctx = ctx
70
+ self.cmd: Command | None = self.ctx.command if self.ctx else None
71
+
72
+ def show(self, file: t.IO[t.Any] | None = None) -> None:
73
+ if file is None:
74
+ file = get_text_stderr()
75
+ color = None
76
+ hint = ""
77
+ if (
78
+ self.ctx is not None
79
+ and self.ctx.command.get_help_option(self.ctx) is not None
80
+ ):
81
+ hint = _("Try '{command} {option}' for help.").format(
82
+ command=self.ctx.command_path, option=self.ctx.help_option_names[0]
83
+ )
84
+ hint = f"{hint}\n"
85
+ if self.ctx is not None:
86
+ color = self.ctx.color
87
+ echo(f"{self.ctx.get_usage()}\n{hint}", file=file, color=color)
88
+ echo(
89
+ _("Error: {message}").format(message=self.format_message()),
90
+ file=file,
91
+ color=color,
92
+ )
93
+
94
+
95
+ class BadParameter(UsageError):
96
+ """An exception that formats out a standardized error message for a
97
+ bad parameter. This is useful when thrown from a callback or type as
98
+ Click will attach contextual information to it (for instance, which
99
+ parameter it is).
100
+
101
+ .. versionadded:: 2.0
102
+
103
+ :param param: the parameter object that caused this error. This can
104
+ be left out, and Click will attach this info itself
105
+ if possible.
106
+ :param param_hint: a string that shows up as parameter name. This
107
+ can be used as alternative to `param` in cases
108
+ where custom validation should happen. If it is
109
+ a string it's used as such, if it's a list then
110
+ each item is quoted and separated.
111
+ """
112
+
113
+ def __init__(
114
+ self,
115
+ message: str,
116
+ ctx: Context | None = None,
117
+ param: Parameter | None = None,
118
+ param_hint: cabc.Sequence[str] | str | None = None,
119
+ ) -> None:
120
+ super().__init__(message, ctx)
121
+ self.param = param
122
+ self.param_hint = param_hint
123
+
124
+ def format_message(self) -> str:
125
+ if self.param_hint is not None:
126
+ param_hint = self.param_hint
127
+ elif self.param is not None:
128
+ param_hint = self.param.get_error_hint(self.ctx) # type: ignore
129
+ else:
130
+ return _("Invalid value: {message}").format(message=self.message)
131
+
132
+ return _("Invalid value for {param_hint}: {message}").format(
133
+ param_hint=_join_param_hints(param_hint), message=self.message
134
+ )
135
+
136
+
137
+ class MissingParameter(BadParameter):
138
+ """Raised if click required an option or argument but it was not
139
+ provided when invoking the script.
140
+
141
+ .. versionadded:: 4.0
142
+
143
+ :param param_type: a string that indicates the type of the parameter.
144
+ The default is to inherit the parameter type from
145
+ the given `param`. Valid values are ``'parameter'``,
146
+ ``'option'`` or ``'argument'``.
147
+ """
148
+
149
+ def __init__(
150
+ self,
151
+ message: str | None = None,
152
+ ctx: Context | None = None,
153
+ param: Parameter | None = None,
154
+ param_hint: cabc.Sequence[str] | str | None = None,
155
+ param_type: str | None = None,
156
+ ) -> None:
157
+ super().__init__(message or "", ctx, param, param_hint)
158
+ self.param_type = param_type
159
+
160
+ def format_message(self) -> str:
161
+ if self.param_hint is not None:
162
+ param_hint: cabc.Sequence[str] | str | None = self.param_hint
163
+ elif self.param is not None:
164
+ param_hint = self.param.get_error_hint(self.ctx) # type: ignore
165
+ else:
166
+ param_hint = None
167
+
168
+ param_hint = _join_param_hints(param_hint)
169
+ param_hint = f" {param_hint}" if param_hint else ""
170
+
171
+ param_type = self.param_type
172
+ if param_type is None and self.param is not None:
173
+ param_type = self.param.param_type_name
174
+
175
+ msg = self.message
176
+ if self.param is not None:
177
+ msg_extra = self.param.type.get_missing_message(
178
+ param=self.param, ctx=self.ctx
179
+ )
180
+ if msg_extra:
181
+ if msg:
182
+ msg += f". {msg_extra}"
183
+ else:
184
+ msg = msg_extra
185
+
186
+ msg = f" {msg}" if msg else ""
187
+
188
+ # Translate param_type for known types.
189
+ if param_type == "argument":
190
+ missing = _("Missing argument")
191
+ elif param_type == "option":
192
+ missing = _("Missing option")
193
+ elif param_type == "parameter":
194
+ missing = _("Missing parameter")
195
+ else:
196
+ missing = _("Missing {param_type}").format(param_type=param_type)
197
+
198
+ return f"{missing}{param_hint}.{msg}"
199
+
200
+ def __str__(self) -> str:
201
+ if not self.message:
202
+ param_name = self.param.name if self.param else None
203
+ return _("Missing parameter: {param_name}").format(param_name=param_name)
204
+ else:
205
+ return self.message
206
+
207
+
208
+ class NoSuchOption(UsageError):
209
+ """Raised if click attempted to handle an option that does not
210
+ exist.
211
+
212
+ .. versionadded:: 4.0
213
+ """
214
+
215
+ def __init__(
216
+ self,
217
+ option_name: str,
218
+ message: str | None = None,
219
+ possibilities: cabc.Sequence[str] | None = None,
220
+ ctx: Context | None = None,
221
+ ) -> None:
222
+ if message is None:
223
+ message = _("No such option: {name}").format(name=option_name)
224
+
225
+ super().__init__(message, ctx)
226
+ self.option_name = option_name
227
+ self.possibilities = possibilities
228
+
229
+ def format_message(self) -> str:
230
+ if not self.possibilities:
231
+ return self.message
232
+
233
+ possibility_str = ", ".join(sorted(self.possibilities))
234
+ suggest = ngettext(
235
+ "Did you mean {possibility}?",
236
+ "(Possible options: {possibilities})",
237
+ len(self.possibilities),
238
+ ).format(possibility=possibility_str, possibilities=possibility_str)
239
+ return f"{self.message} {suggest}"
240
+
241
+
242
+ class BadOptionUsage(UsageError):
243
+ """Raised if an option is generally supplied but the use of the option
244
+ was incorrect. This is for instance raised if the number of arguments
245
+ for an option is not correct.
246
+
247
+ .. versionadded:: 4.0
248
+
249
+ :param option_name: the name of the option being used incorrectly.
250
+ """
251
+
252
+ def __init__(
253
+ self, option_name: str, message: str, ctx: Context | None = None
254
+ ) -> None:
255
+ super().__init__(message, ctx)
256
+ self.option_name = option_name
257
+
258
+
259
+ class BadArgumentUsage(UsageError):
260
+ """Raised if an argument is generally supplied but the use of the argument
261
+ was incorrect. This is for instance raised if the number of values
262
+ for an argument is not correct.
263
+
264
+ .. versionadded:: 6.0
265
+ """
266
+
267
+
268
+ class NoArgsIsHelpError(UsageError):
269
+ def __init__(self, ctx: Context) -> None:
270
+ self.ctx: Context
271
+ super().__init__(ctx.get_help(), ctx=ctx)
272
+
273
+ def show(self, file: t.IO[t.Any] | None = None) -> None:
274
+ echo(self.format_message(), file=file, err=True, color=self.ctx.color)
275
+
276
+
277
+ class FileError(ClickException):
278
+ """Raised if a file cannot be opened."""
279
+
280
+ def __init__(self, filename: str, hint: str | None = None) -> None:
281
+ if hint is None:
282
+ hint = _("unknown error")
283
+
284
+ super().__init__(hint)
285
+ self.ui_filename: str = format_filename(filename)
286
+ self.filename = filename
287
+
288
+ def format_message(self) -> str:
289
+ return _("Could not open file {filename!r}: {message}").format(
290
+ filename=self.ui_filename, message=self.message
291
+ )
292
+
293
+
294
+ class Abort(RuntimeError):
295
+ """An internal signalling exception that signals Click to abort."""
296
+
297
+
298
+ class Exit(RuntimeError):
299
+ """An exception that indicates that the application should exit with some
300
+ status code.
301
+
302
+ :param code: the status code to exit with.
303
+ """
304
+
305
+ __slots__ = ("exit_code",)
306
+
307
+ def __init__(self, code: int = 0) -> None:
308
+ self.exit_code: int = code
env/lib/python3.13/site-packages/click/formatting.py ADDED
@@ -0,0 +1,301 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import collections.abc as cabc
4
+ from contextlib import contextmanager
5
+ from gettext import gettext as _
6
+
7
+ from ._compat import term_len
8
+ from .parser import _split_opt
9
+
10
+ # Can force a width. This is used by the test system
11
+ FORCED_WIDTH: int | None = None
12
+
13
+
14
+ def measure_table(rows: cabc.Iterable[tuple[str, str]]) -> tuple[int, ...]:
15
+ widths: dict[int, int] = {}
16
+
17
+ for row in rows:
18
+ for idx, col in enumerate(row):
19
+ widths[idx] = max(widths.get(idx, 0), term_len(col))
20
+
21
+ return tuple(y for x, y in sorted(widths.items()))
22
+
23
+
24
+ def iter_rows(
25
+ rows: cabc.Iterable[tuple[str, str]], col_count: int
26
+ ) -> cabc.Iterator[tuple[str, ...]]:
27
+ for row in rows:
28
+ yield row + ("",) * (col_count - len(row))
29
+
30
+
31
+ def wrap_text(
32
+ text: str,
33
+ width: int = 78,
34
+ initial_indent: str = "",
35
+ subsequent_indent: str = "",
36
+ preserve_paragraphs: bool = False,
37
+ ) -> str:
38
+ """A helper function that intelligently wraps text. By default, it
39
+ assumes that it operates on a single paragraph of text but if the
40
+ `preserve_paragraphs` parameter is provided it will intelligently
41
+ handle paragraphs (defined by two empty lines).
42
+
43
+ If paragraphs are handled, a paragraph can be prefixed with an empty
44
+ line containing the ``\\b`` character (``\\x08``) to indicate that
45
+ no rewrapping should happen in that block.
46
+
47
+ :param text: the text that should be rewrapped.
48
+ :param width: the maximum width for the text.
49
+ :param initial_indent: the initial indent that should be placed on the
50
+ first line as a string.
51
+ :param subsequent_indent: the indent string that should be placed on
52
+ each consecutive line.
53
+ :param preserve_paragraphs: if this flag is set then the wrapping will
54
+ intelligently handle paragraphs.
55
+ """
56
+ from ._textwrap import TextWrapper
57
+
58
+ text = text.expandtabs()
59
+ wrapper = TextWrapper(
60
+ width,
61
+ initial_indent=initial_indent,
62
+ subsequent_indent=subsequent_indent,
63
+ replace_whitespace=False,
64
+ )
65
+ if not preserve_paragraphs:
66
+ return wrapper.fill(text)
67
+
68
+ p: list[tuple[int, bool, str]] = []
69
+ buf: list[str] = []
70
+ indent = None
71
+
72
+ def _flush_par() -> None:
73
+ if not buf:
74
+ return
75
+ if buf[0].strip() == "\b":
76
+ p.append((indent or 0, True, "\n".join(buf[1:])))
77
+ else:
78
+ p.append((indent or 0, False, " ".join(buf)))
79
+ del buf[:]
80
+
81
+ for line in text.splitlines():
82
+ if not line:
83
+ _flush_par()
84
+ indent = None
85
+ else:
86
+ if indent is None:
87
+ orig_len = term_len(line)
88
+ line = line.lstrip()
89
+ indent = orig_len - term_len(line)
90
+ buf.append(line)
91
+ _flush_par()
92
+
93
+ rv = []
94
+ for indent, raw, text in p:
95
+ with wrapper.extra_indent(" " * indent):
96
+ if raw:
97
+ rv.append(wrapper.indent_only(text))
98
+ else:
99
+ rv.append(wrapper.fill(text))
100
+
101
+ return "\n\n".join(rv)
102
+
103
+
104
+ class HelpFormatter:
105
+ """This class helps with formatting text-based help pages. It's
106
+ usually just needed for very special internal cases, but it's also
107
+ exposed so that developers can write their own fancy outputs.
108
+
109
+ At present, it always writes into memory.
110
+
111
+ :param indent_increment: the additional increment for each level.
112
+ :param width: the width for the text. This defaults to the terminal
113
+ width clamped to a maximum of 78.
114
+ """
115
+
116
+ def __init__(
117
+ self,
118
+ indent_increment: int = 2,
119
+ width: int | None = None,
120
+ max_width: int | None = None,
121
+ ) -> None:
122
+ self.indent_increment = indent_increment
123
+ if max_width is None:
124
+ max_width = 80
125
+ if width is None:
126
+ import shutil
127
+
128
+ width = FORCED_WIDTH
129
+ if width is None:
130
+ width = max(min(shutil.get_terminal_size().columns, max_width) - 2, 50)
131
+ self.width = width
132
+ self.current_indent: int = 0
133
+ self.buffer: list[str] = []
134
+
135
+ def write(self, string: str) -> None:
136
+ """Writes a unicode string into the internal buffer."""
137
+ self.buffer.append(string)
138
+
139
+ def indent(self) -> None:
140
+ """Increases the indentation."""
141
+ self.current_indent += self.indent_increment
142
+
143
+ def dedent(self) -> None:
144
+ """Decreases the indentation."""
145
+ self.current_indent -= self.indent_increment
146
+
147
+ def write_usage(self, prog: str, args: str = "", prefix: str | None = None) -> None:
148
+ """Writes a usage line into the buffer.
149
+
150
+ :param prog: the program name.
151
+ :param args: whitespace separated list of arguments.
152
+ :param prefix: The prefix for the first line. Defaults to
153
+ ``"Usage: "``.
154
+ """
155
+ if prefix is None:
156
+ prefix = f"{_('Usage:')} "
157
+
158
+ usage_prefix = f"{prefix:>{self.current_indent}}{prog} "
159
+ text_width = self.width - self.current_indent
160
+
161
+ if text_width >= (term_len(usage_prefix) + 20):
162
+ # The arguments will fit to the right of the prefix.
163
+ indent = " " * term_len(usage_prefix)
164
+ self.write(
165
+ wrap_text(
166
+ args,
167
+ text_width,
168
+ initial_indent=usage_prefix,
169
+ subsequent_indent=indent,
170
+ )
171
+ )
172
+ else:
173
+ # The prefix is too long, put the arguments on the next line.
174
+ self.write(usage_prefix)
175
+ self.write("\n")
176
+ indent = " " * (max(self.current_indent, term_len(prefix)) + 4)
177
+ self.write(
178
+ wrap_text(
179
+ args, text_width, initial_indent=indent, subsequent_indent=indent
180
+ )
181
+ )
182
+
183
+ self.write("\n")
184
+
185
+ def write_heading(self, heading: str) -> None:
186
+ """Writes a heading into the buffer."""
187
+ self.write(f"{'':>{self.current_indent}}{heading}:\n")
188
+
189
+ def write_paragraph(self) -> None:
190
+ """Writes a paragraph into the buffer."""
191
+ if self.buffer:
192
+ self.write("\n")
193
+
194
+ def write_text(self, text: str) -> None:
195
+ """Writes re-indented text into the buffer. This rewraps and
196
+ preserves paragraphs.
197
+ """
198
+ indent = " " * self.current_indent
199
+ self.write(
200
+ wrap_text(
201
+ text,
202
+ self.width,
203
+ initial_indent=indent,
204
+ subsequent_indent=indent,
205
+ preserve_paragraphs=True,
206
+ )
207
+ )
208
+ self.write("\n")
209
+
210
+ def write_dl(
211
+ self,
212
+ rows: cabc.Sequence[tuple[str, str]],
213
+ col_max: int = 30,
214
+ col_spacing: int = 2,
215
+ ) -> None:
216
+ """Writes a definition list into the buffer. This is how options
217
+ and commands are usually formatted.
218
+
219
+ :param rows: a list of two item tuples for the terms and values.
220
+ :param col_max: the maximum width of the first column.
221
+ :param col_spacing: the number of spaces between the first and
222
+ second column.
223
+ """
224
+ rows = list(rows)
225
+ widths = measure_table(rows)
226
+ if len(widths) != 2:
227
+ raise TypeError("Expected two columns for definition list")
228
+
229
+ first_col = min(widths[0], col_max) + col_spacing
230
+
231
+ for first, second in iter_rows(rows, len(widths)):
232
+ self.write(f"{'':>{self.current_indent}}{first}")
233
+ if not second:
234
+ self.write("\n")
235
+ continue
236
+ if term_len(first) <= first_col - col_spacing:
237
+ self.write(" " * (first_col - term_len(first)))
238
+ else:
239
+ self.write("\n")
240
+ self.write(" " * (first_col + self.current_indent))
241
+
242
+ text_width = max(self.width - first_col - 2, 10)
243
+ wrapped_text = wrap_text(second, text_width, preserve_paragraphs=True)
244
+ lines = wrapped_text.splitlines()
245
+
246
+ if lines:
247
+ self.write(f"{lines[0]}\n")
248
+
249
+ for line in lines[1:]:
250
+ self.write(f"{'':>{first_col + self.current_indent}}{line}\n")
251
+ else:
252
+ self.write("\n")
253
+
254
+ @contextmanager
255
+ def section(self, name: str) -> cabc.Iterator[None]:
256
+ """Helpful context manager that writes a paragraph, a heading,
257
+ and the indents.
258
+
259
+ :param name: the section name that is written as heading.
260
+ """
261
+ self.write_paragraph()
262
+ self.write_heading(name)
263
+ self.indent()
264
+ try:
265
+ yield
266
+ finally:
267
+ self.dedent()
268
+
269
+ @contextmanager
270
+ def indentation(self) -> cabc.Iterator[None]:
271
+ """A context manager that increases the indentation."""
272
+ self.indent()
273
+ try:
274
+ yield
275
+ finally:
276
+ self.dedent()
277
+
278
+ def getvalue(self) -> str:
279
+ """Returns the buffer contents."""
280
+ return "".join(self.buffer)
281
+
282
+
283
+ def join_options(options: cabc.Sequence[str]) -> tuple[str, bool]:
284
+ """Given a list of option strings this joins them in the most appropriate
285
+ way and returns them in the form ``(formatted_string,
286
+ any_prefix_is_slash)`` where the second item in the tuple is a flag that
287
+ indicates if any of the option prefixes was a slash.
288
+ """
289
+ rv = []
290
+ any_prefix_is_slash = False
291
+
292
+ for opt in options:
293
+ prefix = _split_opt(opt)[0]
294
+
295
+ if prefix == "/":
296
+ any_prefix_is_slash = True
297
+
298
+ rv.append((len(prefix), opt))
299
+
300
+ rv.sort(key=lambda x: x[0])
301
+ return ", ".join(x[1] for x in rv), any_prefix_is_slash
env/lib/python3.13/site-packages/click/globals.py ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import typing as t
4
+ from threading import local
5
+
6
+ if t.TYPE_CHECKING:
7
+ from .core import Context
8
+
9
+ _local = local()
10
+
11
+
12
+ @t.overload
13
+ def get_current_context(silent: t.Literal[False] = False) -> Context: ...
14
+
15
+
16
+ @t.overload
17
+ def get_current_context(silent: bool = ...) -> Context | None: ...
18
+
19
+
20
+ def get_current_context(silent: bool = False) -> Context | None:
21
+ """Returns the current click context. This can be used as a way to
22
+ access the current context object from anywhere. This is a more implicit
23
+ alternative to the :func:`pass_context` decorator. This function is
24
+ primarily useful for helpers such as :func:`echo` which might be
25
+ interested in changing its behavior based on the current context.
26
+
27
+ To push the current context, :meth:`Context.scope` can be used.
28
+
29
+ .. versionadded:: 5.0
30
+
31
+ :param silent: if set to `True` the return value is `None` if no context
32
+ is available. The default behavior is to raise a
33
+ :exc:`RuntimeError`.
34
+ """
35
+ try:
36
+ return t.cast("Context", _local.stack[-1])
37
+ except (AttributeError, IndexError) as e:
38
+ if not silent:
39
+ raise RuntimeError("There is no active click context.") from e
40
+
41
+ return None
42
+
43
+
44
+ def push_context(ctx: Context) -> None:
45
+ """Pushes a new context to the current stack."""
46
+ _local.__dict__.setdefault("stack", []).append(ctx)
47
+
48
+
49
+ def pop_context() -> None:
50
+ """Removes the top level from the stack."""
51
+ _local.stack.pop()
52
+
53
+
54
+ def resolve_color_default(color: bool | None = None) -> bool | None:
55
+ """Internal helper to get the default value of the color flag. If a
56
+ value is passed it's returned unchanged, otherwise it's looked up from
57
+ the current context.
58
+ """
59
+ if color is not None:
60
+ return color
61
+
62
+ ctx = get_current_context(silent=True)
63
+
64
+ if ctx is not None:
65
+ return ctx.color
66
+
67
+ return None
env/lib/python3.13/site-packages/click/parser.py ADDED
@@ -0,0 +1,532 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ This module started out as largely a copy paste from the stdlib's
3
+ optparse module with the features removed that we do not need from
4
+ optparse because we implement them in Click on a higher level (for
5
+ instance type handling, help formatting and a lot more).
6
+
7
+ The plan is to remove more and more from here over time.
8
+
9
+ The reason this is a different module and not optparse from the stdlib
10
+ is that there are differences in 2.x and 3.x about the error messages
11
+ generated and optparse in the stdlib uses gettext for no good reason
12
+ and might cause us issues.
13
+
14
+ Click uses parts of optparse written by Gregory P. Ward and maintained
15
+ by the Python Software Foundation. This is limited to code in parser.py.
16
+
17
+ Copyright 2001-2006 Gregory P. Ward. All rights reserved.
18
+ Copyright 2002-2006 Python Software Foundation. All rights reserved.
19
+ """
20
+
21
+ # This code uses parts of optparse written by Gregory P. Ward and
22
+ # maintained by the Python Software Foundation.
23
+ # Copyright 2001-2006 Gregory P. Ward
24
+ # Copyright 2002-2006 Python Software Foundation
25
+ from __future__ import annotations
26
+
27
+ import collections.abc as cabc
28
+ import typing as t
29
+ from collections import deque
30
+ from gettext import gettext as _
31
+ from gettext import ngettext
32
+
33
+ from ._utils import FLAG_NEEDS_VALUE
34
+ from ._utils import UNSET
35
+ from .exceptions import BadArgumentUsage
36
+ from .exceptions import BadOptionUsage
37
+ from .exceptions import NoSuchOption
38
+ from .exceptions import UsageError
39
+
40
+ if t.TYPE_CHECKING:
41
+ from ._utils import T_FLAG_NEEDS_VALUE
42
+ from ._utils import T_UNSET
43
+ from .core import Argument as CoreArgument
44
+ from .core import Context
45
+ from .core import Option as CoreOption
46
+ from .core import Parameter as CoreParameter
47
+
48
+ V = t.TypeVar("V")
49
+
50
+
51
+ def _unpack_args(
52
+ args: cabc.Sequence[str], nargs_spec: cabc.Sequence[int]
53
+ ) -> tuple[cabc.Sequence[str | cabc.Sequence[str | None] | None], list[str]]:
54
+ """Given an iterable of arguments and an iterable of nargs specifications,
55
+ it returns a tuple with all the unpacked arguments at the first index
56
+ and all remaining arguments as the second.
57
+
58
+ The nargs specification is the number of arguments that should be consumed
59
+ or `-1` to indicate that this position should eat up all the remainders.
60
+
61
+ Missing items are filled with ``UNSET``.
62
+ """
63
+ args = deque(args)
64
+ nargs_spec = deque(nargs_spec)
65
+ rv: list[str | tuple[str | T_UNSET, ...] | T_UNSET] = []
66
+ spos: int | None = None
67
+
68
+ def _fetch(c: deque[V]) -> V | T_UNSET:
69
+ try:
70
+ if spos is None:
71
+ return c.popleft()
72
+ else:
73
+ return c.pop()
74
+ except IndexError:
75
+ return UNSET
76
+
77
+ while nargs_spec:
78
+ nargs = _fetch(nargs_spec)
79
+
80
+ if nargs is None:
81
+ continue
82
+
83
+ if nargs == 1:
84
+ rv.append(_fetch(args)) # type: ignore[arg-type]
85
+ elif nargs > 1:
86
+ x = [_fetch(args) for _ in range(nargs)]
87
+
88
+ # If we're reversed, we're pulling in the arguments in reverse,
89
+ # so we need to turn them around.
90
+ if spos is not None:
91
+ x.reverse()
92
+
93
+ rv.append(tuple(x))
94
+ elif nargs < 0:
95
+ if spos is not None:
96
+ raise TypeError("Cannot have two nargs < 0")
97
+
98
+ spos = len(rv)
99
+ rv.append(UNSET)
100
+
101
+ # spos is the position of the wildcard (star). If it's not `None`,
102
+ # we fill it with the remainder.
103
+ if spos is not None:
104
+ rv[spos] = tuple(args)
105
+ args = []
106
+ rv[spos + 1 :] = reversed(rv[spos + 1 :])
107
+
108
+ return tuple(rv), list(args)
109
+
110
+
111
+ def _split_opt(opt: str) -> tuple[str, str]:
112
+ first = opt[:1]
113
+ if first.isalnum():
114
+ return "", opt
115
+ if opt[1:2] == first:
116
+ return opt[:2], opt[2:]
117
+ return first, opt[1:]
118
+
119
+
120
+ def _normalize_opt(opt: str, ctx: Context | None) -> str:
121
+ if ctx is None or ctx.token_normalize_func is None:
122
+ return opt
123
+ prefix, opt = _split_opt(opt)
124
+ return f"{prefix}{ctx.token_normalize_func(opt)}"
125
+
126
+
127
+ class _Option:
128
+ def __init__(
129
+ self,
130
+ obj: CoreOption,
131
+ opts: cabc.Sequence[str],
132
+ dest: str | None,
133
+ action: str | None = None,
134
+ nargs: int = 1,
135
+ const: t.Any | None = None,
136
+ ):
137
+ self._short_opts = []
138
+ self._long_opts = []
139
+ self.prefixes: set[str] = set()
140
+
141
+ for opt in opts:
142
+ prefix, value = _split_opt(opt)
143
+ if not prefix:
144
+ raise ValueError(f"Invalid start character for option ({opt})")
145
+ self.prefixes.add(prefix[0])
146
+ if len(prefix) == 1 and len(value) == 1:
147
+ self._short_opts.append(opt)
148
+ else:
149
+ self._long_opts.append(opt)
150
+ self.prefixes.add(prefix)
151
+
152
+ if action is None:
153
+ action = "store"
154
+
155
+ self.dest = dest
156
+ self.action = action
157
+ self.nargs = nargs
158
+ self.const = const
159
+ self.obj = obj
160
+
161
+ @property
162
+ def takes_value(self) -> bool:
163
+ return self.action in ("store", "append")
164
+
165
+ def process(self, value: t.Any, state: _ParsingState) -> None:
166
+ if self.action == "store":
167
+ state.opts[self.dest] = value # type: ignore
168
+ elif self.action == "store_const":
169
+ state.opts[self.dest] = self.const # type: ignore
170
+ elif self.action == "append":
171
+ state.opts.setdefault(self.dest, []).append(value) # type: ignore
172
+ elif self.action == "append_const":
173
+ state.opts.setdefault(self.dest, []).append(self.const) # type: ignore
174
+ elif self.action == "count":
175
+ state.opts[self.dest] = state.opts.get(self.dest, 0) + 1 # type: ignore
176
+ else:
177
+ raise ValueError(f"unknown action '{self.action}'")
178
+ state.order.append(self.obj)
179
+
180
+
181
+ class _Argument:
182
+ def __init__(self, obj: CoreArgument, dest: str | None, nargs: int = 1):
183
+ self.dest = dest
184
+ self.nargs = nargs
185
+ self.obj = obj
186
+
187
+ def process(
188
+ self,
189
+ value: str | cabc.Sequence[str | None] | None | T_UNSET,
190
+ state: _ParsingState,
191
+ ) -> None:
192
+ if self.nargs > 1:
193
+ assert isinstance(value, cabc.Sequence)
194
+ holes = sum(1 for x in value if x is UNSET)
195
+ if holes == len(value):
196
+ value = UNSET
197
+ elif holes != 0:
198
+ raise BadArgumentUsage(
199
+ _("Argument {name!r} takes {nargs} values.").format(
200
+ name=self.dest, nargs=self.nargs
201
+ )
202
+ )
203
+
204
+ # We failed to collect any argument value so we consider the argument as unset.
205
+ if value == ():
206
+ value = UNSET
207
+
208
+ state.opts[self.dest] = value # type: ignore
209
+ state.order.append(self.obj)
210
+
211
+
212
+ class _ParsingState:
213
+ def __init__(self, rargs: list[str]) -> None:
214
+ self.opts: dict[str, t.Any] = {}
215
+ self.largs: list[str] = []
216
+ self.rargs = rargs
217
+ self.order: list[CoreParameter] = []
218
+
219
+
220
+ class _OptionParser:
221
+ """The option parser is an internal class that is ultimately used to
222
+ parse options and arguments. It's modelled after optparse and brings
223
+ a similar but vastly simplified API. It should generally not be used
224
+ directly as the high level Click classes wrap it for you.
225
+
226
+ It's not nearly as extensible as optparse or argparse as it does not
227
+ implement features that are implemented on a higher level (such as
228
+ types or defaults).
229
+
230
+ :param ctx: optionally the :class:`~click.Context` where this parser
231
+ should go with.
232
+
233
+ .. deprecated:: 8.2
234
+ Will be removed in Click 9.0.
235
+ """
236
+
237
+ def __init__(self, ctx: Context | None = None) -> None:
238
+ #: The :class:`~click.Context` for this parser. This might be
239
+ #: `None` for some advanced use cases.
240
+ self.ctx = ctx
241
+ #: This controls how the parser deals with interspersed arguments.
242
+ #: If this is set to `False`, the parser will stop on the first
243
+ #: non-option. Click uses this to implement nested subcommands
244
+ #: safely.
245
+ self.allow_interspersed_args: bool = True
246
+ #: This tells the parser how to deal with unknown options. By
247
+ #: default it will error out (which is sensible), but there is a
248
+ #: second mode where it will ignore it and continue processing
249
+ #: after shifting all the unknown options into the resulting args.
250
+ self.ignore_unknown_options: bool = False
251
+
252
+ if ctx is not None:
253
+ self.allow_interspersed_args = ctx.allow_interspersed_args
254
+ self.ignore_unknown_options = ctx.ignore_unknown_options
255
+
256
+ self._short_opt: dict[str, _Option] = {}
257
+ self._long_opt: dict[str, _Option] = {}
258
+ self._opt_prefixes = {"-", "--"}
259
+ self._args: list[_Argument] = []
260
+
261
+ def add_option(
262
+ self,
263
+ obj: CoreOption,
264
+ opts: cabc.Sequence[str],
265
+ dest: str | None,
266
+ action: str | None = None,
267
+ nargs: int = 1,
268
+ const: t.Any | None = None,
269
+ ) -> None:
270
+ """Adds a new option named `dest` to the parser. The destination
271
+ is not inferred (unlike with optparse) and needs to be explicitly
272
+ provided. Action can be any of ``store``, ``store_const``,
273
+ ``append``, ``append_const`` or ``count``.
274
+
275
+ The `obj` can be used to identify the option in the order list
276
+ that is returned from the parser.
277
+ """
278
+ opts = [_normalize_opt(opt, self.ctx) for opt in opts]
279
+ option = _Option(obj, opts, dest, action=action, nargs=nargs, const=const)
280
+ self._opt_prefixes.update(option.prefixes)
281
+ for opt in option._short_opts:
282
+ self._short_opt[opt] = option
283
+ for opt in option._long_opts:
284
+ self._long_opt[opt] = option
285
+
286
+ def add_argument(self, obj: CoreArgument, dest: str | None, nargs: int = 1) -> None:
287
+ """Adds a positional argument named `dest` to the parser.
288
+
289
+ The `obj` can be used to identify the option in the order list
290
+ that is returned from the parser.
291
+ """
292
+ self._args.append(_Argument(obj, dest=dest, nargs=nargs))
293
+
294
+ def parse_args(
295
+ self, args: list[str]
296
+ ) -> tuple[dict[str, t.Any], list[str], list[CoreParameter]]:
297
+ """Parses positional arguments and returns ``(values, args, order)``
298
+ for the parsed options and arguments as well as the leftover
299
+ arguments if there are any. The order is a list of objects as they
300
+ appear on the command line. If arguments appear multiple times they
301
+ will be memorized multiple times as well.
302
+ """
303
+ state = _ParsingState(args)
304
+ try:
305
+ self._process_args_for_options(state)
306
+ self._process_args_for_args(state)
307
+ except UsageError:
308
+ if self.ctx is None or not self.ctx.resilient_parsing:
309
+ raise
310
+ return state.opts, state.largs, state.order
311
+
312
+ def _process_args_for_args(self, state: _ParsingState) -> None:
313
+ pargs, args = _unpack_args(
314
+ state.largs + state.rargs, [x.nargs for x in self._args]
315
+ )
316
+
317
+ for idx, arg in enumerate(self._args):
318
+ arg.process(pargs[idx], state)
319
+
320
+ state.largs = args
321
+ state.rargs = []
322
+
323
+ def _process_args_for_options(self, state: _ParsingState) -> None:
324
+ while state.rargs:
325
+ arg = state.rargs.pop(0)
326
+ arglen = len(arg)
327
+ # Double dashes always handled explicitly regardless of what
328
+ # prefixes are valid.
329
+ if arg == "--":
330
+ return
331
+ elif arg[:1] in self._opt_prefixes and arglen > 1:
332
+ self._process_opts(arg, state)
333
+ elif self.allow_interspersed_args:
334
+ state.largs.append(arg)
335
+ else:
336
+ state.rargs.insert(0, arg)
337
+ return
338
+
339
+ # Say this is the original argument list:
340
+ # [arg0, arg1, ..., arg(i-1), arg(i), arg(i+1), ..., arg(N-1)]
341
+ # ^
342
+ # (we are about to process arg(i)).
343
+ #
344
+ # Then rargs is [arg(i), ..., arg(N-1)] and largs is a *subset* of
345
+ # [arg0, ..., arg(i-1)] (any options and their arguments will have
346
+ # been removed from largs).
347
+ #
348
+ # The while loop will usually consume 1 or more arguments per pass.
349
+ # If it consumes 1 (eg. arg is an option that takes no arguments),
350
+ # then after _process_arg() is done the situation is:
351
+ #
352
+ # largs = subset of [arg0, ..., arg(i)]
353
+ # rargs = [arg(i+1), ..., arg(N-1)]
354
+ #
355
+ # If allow_interspersed_args is false, largs will always be
356
+ # *empty* -- still a subset of [arg0, ..., arg(i-1)], but
357
+ # not a very interesting subset!
358
+
359
+ def _match_long_opt(
360
+ self, opt: str, explicit_value: str | None, state: _ParsingState
361
+ ) -> None:
362
+ if opt not in self._long_opt:
363
+ from difflib import get_close_matches
364
+
365
+ possibilities = get_close_matches(opt, self._long_opt)
366
+ raise NoSuchOption(opt, possibilities=possibilities, ctx=self.ctx)
367
+
368
+ option = self._long_opt[opt]
369
+ if option.takes_value:
370
+ # At this point it's safe to modify rargs by injecting the
371
+ # explicit value, because no exception is raised in this
372
+ # branch. This means that the inserted value will be fully
373
+ # consumed.
374
+ if explicit_value is not None:
375
+ state.rargs.insert(0, explicit_value)
376
+
377
+ value = self._get_value_from_state(opt, option, state)
378
+
379
+ elif explicit_value is not None:
380
+ raise BadOptionUsage(
381
+ opt, _("Option {name!r} does not take a value.").format(name=opt)
382
+ )
383
+
384
+ else:
385
+ value = UNSET
386
+
387
+ option.process(value, state)
388
+
389
+ def _match_short_opt(self, arg: str, state: _ParsingState) -> None:
390
+ stop = False
391
+ i = 1
392
+ prefix = arg[0]
393
+ unknown_options = []
394
+
395
+ for ch in arg[1:]:
396
+ opt = _normalize_opt(f"{prefix}{ch}", self.ctx)
397
+ option = self._short_opt.get(opt)
398
+ i += 1
399
+
400
+ if not option:
401
+ if self.ignore_unknown_options:
402
+ unknown_options.append(ch)
403
+ continue
404
+ raise NoSuchOption(opt, ctx=self.ctx)
405
+ if option.takes_value:
406
+ # Any characters left in arg? Pretend they're the
407
+ # next arg, and stop consuming characters of arg.
408
+ if i < len(arg):
409
+ state.rargs.insert(0, arg[i:])
410
+ stop = True
411
+
412
+ value = self._get_value_from_state(opt, option, state)
413
+
414
+ else:
415
+ value = UNSET
416
+
417
+ option.process(value, state)
418
+
419
+ if stop:
420
+ break
421
+
422
+ # If we got any unknown options we recombine the string of the
423
+ # remaining options and re-attach the prefix, then report that
424
+ # to the state as new larg. This way there is basic combinatorics
425
+ # that can be achieved while still ignoring unknown arguments.
426
+ if self.ignore_unknown_options and unknown_options:
427
+ state.largs.append(f"{prefix}{''.join(unknown_options)}")
428
+
429
+ def _get_value_from_state(
430
+ self, option_name: str, option: _Option, state: _ParsingState
431
+ ) -> str | cabc.Sequence[str] | T_FLAG_NEEDS_VALUE:
432
+ nargs = option.nargs
433
+
434
+ value: str | cabc.Sequence[str] | T_FLAG_NEEDS_VALUE
435
+
436
+ if len(state.rargs) < nargs:
437
+ if option.obj._flag_needs_value:
438
+ # Option allows omitting the value.
439
+ value = FLAG_NEEDS_VALUE
440
+ else:
441
+ raise BadOptionUsage(
442
+ option_name,
443
+ ngettext(
444
+ "Option {name!r} requires an argument.",
445
+ "Option {name!r} requires {nargs} arguments.",
446
+ nargs,
447
+ ).format(name=option_name, nargs=nargs),
448
+ )
449
+ elif nargs == 1:
450
+ next_rarg = state.rargs[0]
451
+
452
+ if (
453
+ option.obj._flag_needs_value
454
+ and isinstance(next_rarg, str)
455
+ and next_rarg[:1] in self._opt_prefixes
456
+ and len(next_rarg) > 1
457
+ ):
458
+ # The next arg looks like the start of an option, don't
459
+ # use it as the value if omitting the value is allowed.
460
+ value = FLAG_NEEDS_VALUE
461
+ else:
462
+ value = state.rargs.pop(0)
463
+ else:
464
+ value = tuple(state.rargs[:nargs])
465
+ del state.rargs[:nargs]
466
+
467
+ return value
468
+
469
+ def _process_opts(self, arg: str, state: _ParsingState) -> None:
470
+ explicit_value = None
471
+ # Long option handling happens in two parts. The first part is
472
+ # supporting explicitly attached values. In any case, we will try
473
+ # to long match the option first.
474
+ if "=" in arg:
475
+ long_opt, explicit_value = arg.split("=", 1)
476
+ else:
477
+ long_opt = arg
478
+ norm_long_opt = _normalize_opt(long_opt, self.ctx)
479
+
480
+ # At this point we will match the (assumed) long option through
481
+ # the long option matching code. Note that this allows options
482
+ # like "-foo" to be matched as long options.
483
+ try:
484
+ self._match_long_opt(norm_long_opt, explicit_value, state)
485
+ except NoSuchOption:
486
+ # At this point the long option matching failed, and we need
487
+ # to try with short options. However there is a special rule
488
+ # which says, that if we have a two character options prefix
489
+ # (applies to "--foo" for instance), we do not dispatch to the
490
+ # short option code and will instead raise the no option
491
+ # error.
492
+ if arg[:2] not in self._opt_prefixes:
493
+ self._match_short_opt(arg, state)
494
+ return
495
+
496
+ if not self.ignore_unknown_options:
497
+ raise
498
+
499
+ state.largs.append(arg)
500
+
501
+
502
+ def __getattr__(name: str) -> object:
503
+ import warnings
504
+
505
+ if name in {
506
+ "OptionParser",
507
+ "Argument",
508
+ "Option",
509
+ "split_opt",
510
+ "normalize_opt",
511
+ "ParsingState",
512
+ }:
513
+ warnings.warn(
514
+ f"'parser.{name}' is deprecated and will be removed in Click 9.0."
515
+ " The old parser is available in 'optparse'.",
516
+ DeprecationWarning,
517
+ stacklevel=2,
518
+ )
519
+ return globals()[f"_{name}"]
520
+
521
+ if name == "split_arg_string":
522
+ from .shell_completion import split_arg_string
523
+
524
+ warnings.warn(
525
+ "Importing 'parser.split_arg_string' is deprecated, it will only be"
526
+ " available in 'shell_completion' in Click 9.0.",
527
+ DeprecationWarning,
528
+ stacklevel=2,
529
+ )
530
+ return split_arg_string
531
+
532
+ raise AttributeError(name)
env/lib/python3.13/site-packages/click/py.typed ADDED
File without changes
env/lib/python3.13/site-packages/click/shell_completion.py ADDED
@@ -0,0 +1,667 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import collections.abc as cabc
4
+ import os
5
+ import re
6
+ import typing as t
7
+ from gettext import gettext as _
8
+
9
+ from .core import Argument
10
+ from .core import Command
11
+ from .core import Context
12
+ from .core import Group
13
+ from .core import Option
14
+ from .core import Parameter
15
+ from .core import ParameterSource
16
+ from .utils import echo
17
+
18
+
19
+ def shell_complete(
20
+ cli: Command,
21
+ ctx_args: cabc.MutableMapping[str, t.Any],
22
+ prog_name: str,
23
+ complete_var: str,
24
+ instruction: str,
25
+ ) -> int:
26
+ """Perform shell completion for the given CLI program.
27
+
28
+ :param cli: Command being called.
29
+ :param ctx_args: Extra arguments to pass to
30
+ ``cli.make_context``.
31
+ :param prog_name: Name of the executable in the shell.
32
+ :param complete_var: Name of the environment variable that holds
33
+ the completion instruction.
34
+ :param instruction: Value of ``complete_var`` with the completion
35
+ instruction and shell, in the form ``instruction_shell``.
36
+ :return: Status code to exit with.
37
+ """
38
+ shell, _, instruction = instruction.partition("_")
39
+ comp_cls = get_completion_class(shell)
40
+
41
+ if comp_cls is None:
42
+ return 1
43
+
44
+ comp = comp_cls(cli, ctx_args, prog_name, complete_var)
45
+
46
+ if instruction == "source":
47
+ echo(comp.source())
48
+ return 0
49
+
50
+ if instruction == "complete":
51
+ echo(comp.complete())
52
+ return 0
53
+
54
+ return 1
55
+
56
+
57
+ class CompletionItem:
58
+ """Represents a completion value and metadata about the value. The
59
+ default metadata is ``type`` to indicate special shell handling,
60
+ and ``help`` if a shell supports showing a help string next to the
61
+ value.
62
+
63
+ Arbitrary parameters can be passed when creating the object, and
64
+ accessed using ``item.attr``. If an attribute wasn't passed,
65
+ accessing it returns ``None``.
66
+
67
+ :param value: The completion suggestion.
68
+ :param type: Tells the shell script to provide special completion
69
+ support for the type. Click uses ``"dir"`` and ``"file"``.
70
+ :param help: String shown next to the value if supported.
71
+ :param kwargs: Arbitrary metadata. The built-in implementations
72
+ don't use this, but custom type completions paired with custom
73
+ shell support could use it.
74
+ """
75
+
76
+ __slots__ = ("value", "type", "help", "_info")
77
+
78
+ def __init__(
79
+ self,
80
+ value: t.Any,
81
+ type: str = "plain",
82
+ help: str | None = None,
83
+ **kwargs: t.Any,
84
+ ) -> None:
85
+ self.value: t.Any = value
86
+ self.type: str = type
87
+ self.help: str | None = help
88
+ self._info = kwargs
89
+
90
+ def __getattr__(self, name: str) -> t.Any:
91
+ return self._info.get(name)
92
+
93
+
94
+ # Only Bash >= 4.4 has the nosort option.
95
+ _SOURCE_BASH = """\
96
+ %(complete_func)s() {
97
+ local IFS=$'\\n'
98
+ local response
99
+
100
+ response=$(env COMP_WORDS="${COMP_WORDS[*]}" COMP_CWORD=$COMP_CWORD \
101
+ %(complete_var)s=bash_complete $1)
102
+
103
+ for completion in $response; do
104
+ IFS=',' read type value <<< "$completion"
105
+
106
+ if [[ $type == 'dir' ]]; then
107
+ COMPREPLY=()
108
+ compopt -o dirnames
109
+ elif [[ $type == 'file' ]]; then
110
+ COMPREPLY=()
111
+ compopt -o default
112
+ elif [[ $type == 'plain' ]]; then
113
+ COMPREPLY+=($value)
114
+ fi
115
+ done
116
+
117
+ return 0
118
+ }
119
+
120
+ %(complete_func)s_setup() {
121
+ complete -o nosort -F %(complete_func)s %(prog_name)s
122
+ }
123
+
124
+ %(complete_func)s_setup;
125
+ """
126
+
127
+ # See ZshComplete.format_completion below, and issue #2703, before
128
+ # changing this script.
129
+ #
130
+ # (TL;DR: _describe is picky about the format, but this Zsh script snippet
131
+ # is already widely deployed. So freeze this script, and use clever-ish
132
+ # handling of colons in ZshComplet.format_completion.)
133
+ _SOURCE_ZSH = """\
134
+ #compdef %(prog_name)s
135
+
136
+ %(complete_func)s() {
137
+ local -a completions
138
+ local -a completions_with_descriptions
139
+ local -a response
140
+ (( ! $+commands[%(prog_name)s] )) && return 1
141
+
142
+ response=("${(@f)$(env COMP_WORDS="${words[*]}" COMP_CWORD=$((CURRENT-1)) \
143
+ %(complete_var)s=zsh_complete %(prog_name)s)}")
144
+
145
+ for type key descr in ${response}; do
146
+ if [[ "$type" == "plain" ]]; then
147
+ if [[ "$descr" == "_" ]]; then
148
+ completions+=("$key")
149
+ else
150
+ completions_with_descriptions+=("$key":"$descr")
151
+ fi
152
+ elif [[ "$type" == "dir" ]]; then
153
+ _path_files -/
154
+ elif [[ "$type" == "file" ]]; then
155
+ _path_files -f
156
+ fi
157
+ done
158
+
159
+ if [ -n "$completions_with_descriptions" ]; then
160
+ _describe -V unsorted completions_with_descriptions -U
161
+ fi
162
+
163
+ if [ -n "$completions" ]; then
164
+ compadd -U -V unsorted -a completions
165
+ fi
166
+ }
167
+
168
+ if [[ $zsh_eval_context[-1] == loadautofunc ]]; then
169
+ # autoload from fpath, call function directly
170
+ %(complete_func)s "$@"
171
+ else
172
+ # eval/source/. command, register function for later
173
+ compdef %(complete_func)s %(prog_name)s
174
+ fi
175
+ """
176
+
177
+ _SOURCE_FISH = """\
178
+ function %(complete_func)s;
179
+ set -l response (env %(complete_var)s=fish_complete COMP_WORDS=(commandline -cp) \
180
+ COMP_CWORD=(commandline -t) %(prog_name)s);
181
+
182
+ for completion in $response;
183
+ set -l metadata (string split "," $completion);
184
+
185
+ if test $metadata[1] = "dir";
186
+ __fish_complete_directories $metadata[2];
187
+ else if test $metadata[1] = "file";
188
+ __fish_complete_path $metadata[2];
189
+ else if test $metadata[1] = "plain";
190
+ echo $metadata[2];
191
+ end;
192
+ end;
193
+ end;
194
+
195
+ complete --no-files --command %(prog_name)s --arguments \
196
+ "(%(complete_func)s)";
197
+ """
198
+
199
+
200
+ class ShellComplete:
201
+ """Base class for providing shell completion support. A subclass for
202
+ a given shell will override attributes and methods to implement the
203
+ completion instructions (``source`` and ``complete``).
204
+
205
+ :param cli: Command being called.
206
+ :param prog_name: Name of the executable in the shell.
207
+ :param complete_var: Name of the environment variable that holds
208
+ the completion instruction.
209
+
210
+ .. versionadded:: 8.0
211
+ """
212
+
213
+ name: t.ClassVar[str]
214
+ """Name to register the shell as with :func:`add_completion_class`.
215
+ This is used in completion instructions (``{name}_source`` and
216
+ ``{name}_complete``).
217
+ """
218
+
219
+ source_template: t.ClassVar[str]
220
+ """Completion script template formatted by :meth:`source`. This must
221
+ be provided by subclasses.
222
+ """
223
+
224
+ def __init__(
225
+ self,
226
+ cli: Command,
227
+ ctx_args: cabc.MutableMapping[str, t.Any],
228
+ prog_name: str,
229
+ complete_var: str,
230
+ ) -> None:
231
+ self.cli = cli
232
+ self.ctx_args = ctx_args
233
+ self.prog_name = prog_name
234
+ self.complete_var = complete_var
235
+
236
+ @property
237
+ def func_name(self) -> str:
238
+ """The name of the shell function defined by the completion
239
+ script.
240
+ """
241
+ safe_name = re.sub(r"\W*", "", self.prog_name.replace("-", "_"), flags=re.ASCII)
242
+ return f"_{safe_name}_completion"
243
+
244
+ def source_vars(self) -> dict[str, t.Any]:
245
+ """Vars for formatting :attr:`source_template`.
246
+
247
+ By default this provides ``complete_func``, ``complete_var``,
248
+ and ``prog_name``.
249
+ """
250
+ return {
251
+ "complete_func": self.func_name,
252
+ "complete_var": self.complete_var,
253
+ "prog_name": self.prog_name,
254
+ }
255
+
256
+ def source(self) -> str:
257
+ """Produce the shell script that defines the completion
258
+ function. By default this ``%``-style formats
259
+ :attr:`source_template` with the dict returned by
260
+ :meth:`source_vars`.
261
+ """
262
+ return self.source_template % self.source_vars()
263
+
264
+ def get_completion_args(self) -> tuple[list[str], str]:
265
+ """Use the env vars defined by the shell script to return a
266
+ tuple of ``args, incomplete``. This must be implemented by
267
+ subclasses.
268
+ """
269
+ raise NotImplementedError
270
+
271
+ def get_completions(self, args: list[str], incomplete: str) -> list[CompletionItem]:
272
+ """Determine the context and last complete command or parameter
273
+ from the complete args. Call that object's ``shell_complete``
274
+ method to get the completions for the incomplete value.
275
+
276
+ :param args: List of complete args before the incomplete value.
277
+ :param incomplete: Value being completed. May be empty.
278
+ """
279
+ ctx = _resolve_context(self.cli, self.ctx_args, self.prog_name, args)
280
+ obj, incomplete = _resolve_incomplete(ctx, args, incomplete)
281
+ return obj.shell_complete(ctx, incomplete)
282
+
283
+ def format_completion(self, item: CompletionItem) -> str:
284
+ """Format a completion item into the form recognized by the
285
+ shell script. This must be implemented by subclasses.
286
+
287
+ :param item: Completion item to format.
288
+ """
289
+ raise NotImplementedError
290
+
291
+ def complete(self) -> str:
292
+ """Produce the completion data to send back to the shell.
293
+
294
+ By default this calls :meth:`get_completion_args`, gets the
295
+ completions, then calls :meth:`format_completion` for each
296
+ completion.
297
+ """
298
+ args, incomplete = self.get_completion_args()
299
+ completions = self.get_completions(args, incomplete)
300
+ out = [self.format_completion(item) for item in completions]
301
+ return "\n".join(out)
302
+
303
+
304
+ class BashComplete(ShellComplete):
305
+ """Shell completion for Bash."""
306
+
307
+ name = "bash"
308
+ source_template = _SOURCE_BASH
309
+
310
+ @staticmethod
311
+ def _check_version() -> None:
312
+ import shutil
313
+ import subprocess
314
+
315
+ bash_exe = shutil.which("bash")
316
+
317
+ if bash_exe is None:
318
+ match = None
319
+ else:
320
+ output = subprocess.run(
321
+ [bash_exe, "--norc", "-c", 'echo "${BASH_VERSION}"'],
322
+ stdout=subprocess.PIPE,
323
+ )
324
+ match = re.search(r"^(\d+)\.(\d+)\.\d+", output.stdout.decode())
325
+
326
+ if match is not None:
327
+ major, minor = match.groups()
328
+
329
+ if major < "4" or major == "4" and minor < "4":
330
+ echo(
331
+ _(
332
+ "Shell completion is not supported for Bash"
333
+ " versions older than 4.4."
334
+ ),
335
+ err=True,
336
+ )
337
+ else:
338
+ echo(
339
+ _("Couldn't detect Bash version, shell completion is not supported."),
340
+ err=True,
341
+ )
342
+
343
+ def source(self) -> str:
344
+ self._check_version()
345
+ return super().source()
346
+
347
+ def get_completion_args(self) -> tuple[list[str], str]:
348
+ cwords = split_arg_string(os.environ["COMP_WORDS"])
349
+ cword = int(os.environ["COMP_CWORD"])
350
+ args = cwords[1:cword]
351
+
352
+ try:
353
+ incomplete = cwords[cword]
354
+ except IndexError:
355
+ incomplete = ""
356
+
357
+ return args, incomplete
358
+
359
+ def format_completion(self, item: CompletionItem) -> str:
360
+ return f"{item.type},{item.value}"
361
+
362
+
363
+ class ZshComplete(ShellComplete):
364
+ """Shell completion for Zsh."""
365
+
366
+ name = "zsh"
367
+ source_template = _SOURCE_ZSH
368
+
369
+ def get_completion_args(self) -> tuple[list[str], str]:
370
+ cwords = split_arg_string(os.environ["COMP_WORDS"])
371
+ cword = int(os.environ["COMP_CWORD"])
372
+ args = cwords[1:cword]
373
+
374
+ try:
375
+ incomplete = cwords[cword]
376
+ except IndexError:
377
+ incomplete = ""
378
+
379
+ return args, incomplete
380
+
381
+ def format_completion(self, item: CompletionItem) -> str:
382
+ help_ = item.help or "_"
383
+ # The zsh completion script uses `_describe` on items with help
384
+ # texts (which splits the item help from the item value at the
385
+ # first unescaped colon) and `compadd` on items without help
386
+ # text (which uses the item value as-is and does not support
387
+ # colon escaping). So escape colons in the item value if and
388
+ # only if the item help is not the sentinel "_" value, as used
389
+ # by the completion script.
390
+ #
391
+ # (The zsh completion script is potentially widely deployed, and
392
+ # thus harder to fix than this method.)
393
+ #
394
+ # See issue #1812 and issue #2703 for further context.
395
+ value = item.value.replace(":", r"\:") if help_ != "_" else item.value
396
+ return f"{item.type}\n{value}\n{help_}"
397
+
398
+
399
+ class FishComplete(ShellComplete):
400
+ """Shell completion for Fish."""
401
+
402
+ name = "fish"
403
+ source_template = _SOURCE_FISH
404
+
405
+ def get_completion_args(self) -> tuple[list[str], str]:
406
+ cwords = split_arg_string(os.environ["COMP_WORDS"])
407
+ incomplete = os.environ["COMP_CWORD"]
408
+ if incomplete:
409
+ incomplete = split_arg_string(incomplete)[0]
410
+ args = cwords[1:]
411
+
412
+ # Fish stores the partial word in both COMP_WORDS and
413
+ # COMP_CWORD, remove it from complete args.
414
+ if incomplete and args and args[-1] == incomplete:
415
+ args.pop()
416
+
417
+ return args, incomplete
418
+
419
+ def format_completion(self, item: CompletionItem) -> str:
420
+ if item.help:
421
+ return f"{item.type},{item.value}\t{item.help}"
422
+
423
+ return f"{item.type},{item.value}"
424
+
425
+
426
+ ShellCompleteType = t.TypeVar("ShellCompleteType", bound="type[ShellComplete]")
427
+
428
+
429
+ _available_shells: dict[str, type[ShellComplete]] = {
430
+ "bash": BashComplete,
431
+ "fish": FishComplete,
432
+ "zsh": ZshComplete,
433
+ }
434
+
435
+
436
+ def add_completion_class(
437
+ cls: ShellCompleteType, name: str | None = None
438
+ ) -> ShellCompleteType:
439
+ """Register a :class:`ShellComplete` subclass under the given name.
440
+ The name will be provided by the completion instruction environment
441
+ variable during completion.
442
+
443
+ :param cls: The completion class that will handle completion for the
444
+ shell.
445
+ :param name: Name to register the class under. Defaults to the
446
+ class's ``name`` attribute.
447
+ """
448
+ if name is None:
449
+ name = cls.name
450
+
451
+ _available_shells[name] = cls
452
+
453
+ return cls
454
+
455
+
456
+ def get_completion_class(shell: str) -> type[ShellComplete] | None:
457
+ """Look up a registered :class:`ShellComplete` subclass by the name
458
+ provided by the completion instruction environment variable. If the
459
+ name isn't registered, returns ``None``.
460
+
461
+ :param shell: Name the class is registered under.
462
+ """
463
+ return _available_shells.get(shell)
464
+
465
+
466
+ def split_arg_string(string: str) -> list[str]:
467
+ """Split an argument string as with :func:`shlex.split`, but don't
468
+ fail if the string is incomplete. Ignores a missing closing quote or
469
+ incomplete escape sequence and uses the partial token as-is.
470
+
471
+ .. code-block:: python
472
+
473
+ split_arg_string("example 'my file")
474
+ ["example", "my file"]
475
+
476
+ split_arg_string("example my\\")
477
+ ["example", "my"]
478
+
479
+ :param string: String to split.
480
+
481
+ .. versionchanged:: 8.2
482
+ Moved to ``shell_completion`` from ``parser``.
483
+ """
484
+ import shlex
485
+
486
+ lex = shlex.shlex(string, posix=True)
487
+ lex.whitespace_split = True
488
+ lex.commenters = ""
489
+ out = []
490
+
491
+ try:
492
+ for token in lex:
493
+ out.append(token)
494
+ except ValueError:
495
+ # Raised when end-of-string is reached in an invalid state. Use
496
+ # the partial token as-is. The quote or escape character is in
497
+ # lex.state, not lex.token.
498
+ out.append(lex.token)
499
+
500
+ return out
501
+
502
+
503
+ def _is_incomplete_argument(ctx: Context, param: Parameter) -> bool:
504
+ """Determine if the given parameter is an argument that can still
505
+ accept values.
506
+
507
+ :param ctx: Invocation context for the command represented by the
508
+ parsed complete args.
509
+ :param param: Argument object being checked.
510
+ """
511
+ if not isinstance(param, Argument):
512
+ return False
513
+
514
+ assert param.name is not None
515
+ # Will be None if expose_value is False.
516
+ value = ctx.params.get(param.name)
517
+ return (
518
+ param.nargs == -1
519
+ or ctx.get_parameter_source(param.name) is not ParameterSource.COMMANDLINE
520
+ or (
521
+ param.nargs > 1
522
+ and isinstance(value, (tuple, list))
523
+ and len(value) < param.nargs
524
+ )
525
+ )
526
+
527
+
528
+ def _start_of_option(ctx: Context, value: str) -> bool:
529
+ """Check if the value looks like the start of an option."""
530
+ if not value:
531
+ return False
532
+
533
+ c = value[0]
534
+ return c in ctx._opt_prefixes
535
+
536
+
537
+ def _is_incomplete_option(ctx: Context, args: list[str], param: Parameter) -> bool:
538
+ """Determine if the given parameter is an option that needs a value.
539
+
540
+ :param args: List of complete args before the incomplete value.
541
+ :param param: Option object being checked.
542
+ """
543
+ if not isinstance(param, Option):
544
+ return False
545
+
546
+ if param.is_flag or param.count:
547
+ return False
548
+
549
+ last_option = None
550
+
551
+ for index, arg in enumerate(reversed(args)):
552
+ if index + 1 > param.nargs:
553
+ break
554
+
555
+ if _start_of_option(ctx, arg):
556
+ last_option = arg
557
+ break
558
+
559
+ return last_option is not None and last_option in param.opts
560
+
561
+
562
+ def _resolve_context(
563
+ cli: Command,
564
+ ctx_args: cabc.MutableMapping[str, t.Any],
565
+ prog_name: str,
566
+ args: list[str],
567
+ ) -> Context:
568
+ """Produce the context hierarchy starting with the command and
569
+ traversing the complete arguments. This only follows the commands,
570
+ it doesn't trigger input prompts or callbacks.
571
+
572
+ :param cli: Command being called.
573
+ :param prog_name: Name of the executable in the shell.
574
+ :param args: List of complete args before the incomplete value.
575
+ """
576
+ ctx_args["resilient_parsing"] = True
577
+ with cli.make_context(prog_name, args.copy(), **ctx_args) as ctx:
578
+ args = ctx._protected_args + ctx.args
579
+
580
+ while args:
581
+ command = ctx.command
582
+
583
+ if isinstance(command, Group):
584
+ if not command.chain:
585
+ name, cmd, args = command.resolve_command(ctx, args)
586
+
587
+ if cmd is None:
588
+ return ctx
589
+
590
+ with cmd.make_context(
591
+ name, args, parent=ctx, resilient_parsing=True
592
+ ) as sub_ctx:
593
+ ctx = sub_ctx
594
+ args = ctx._protected_args + ctx.args
595
+ else:
596
+ sub_ctx = ctx
597
+
598
+ while args:
599
+ name, cmd, args = command.resolve_command(ctx, args)
600
+
601
+ if cmd is None:
602
+ return ctx
603
+
604
+ with cmd.make_context(
605
+ name,
606
+ args,
607
+ parent=ctx,
608
+ allow_extra_args=True,
609
+ allow_interspersed_args=False,
610
+ resilient_parsing=True,
611
+ ) as sub_sub_ctx:
612
+ sub_ctx = sub_sub_ctx
613
+ args = sub_ctx.args
614
+
615
+ ctx = sub_ctx
616
+ args = [*sub_ctx._protected_args, *sub_ctx.args]
617
+ else:
618
+ break
619
+
620
+ return ctx
621
+
622
+
623
+ def _resolve_incomplete(
624
+ ctx: Context, args: list[str], incomplete: str
625
+ ) -> tuple[Command | Parameter, str]:
626
+ """Find the Click object that will handle the completion of the
627
+ incomplete value. Return the object and the incomplete value.
628
+
629
+ :param ctx: Invocation context for the command represented by
630
+ the parsed complete args.
631
+ :param args: List of complete args before the incomplete value.
632
+ :param incomplete: Value being completed. May be empty.
633
+ """
634
+ # Different shells treat an "=" between a long option name and
635
+ # value differently. Might keep the value joined, return the "="
636
+ # as a separate item, or return the split name and value. Always
637
+ # split and discard the "=" to make completion easier.
638
+ if incomplete == "=":
639
+ incomplete = ""
640
+ elif "=" in incomplete and _start_of_option(ctx, incomplete):
641
+ name, _, incomplete = incomplete.partition("=")
642
+ args.append(name)
643
+
644
+ # The "--" marker tells Click to stop treating values as options
645
+ # even if they start with the option character. If it hasn't been
646
+ # given and the incomplete arg looks like an option, the current
647
+ # command will provide option name completions.
648
+ if "--" not in args and _start_of_option(ctx, incomplete):
649
+ return ctx.command, incomplete
650
+
651
+ params = ctx.command.get_params(ctx)
652
+
653
+ # If the last complete arg is an option name with an incomplete
654
+ # value, the option will provide value completions.
655
+ for param in params:
656
+ if _is_incomplete_option(ctx, args, param):
657
+ return param, incomplete
658
+
659
+ # It's not an option name or value. The first argument without a
660
+ # parsed value will provide value completions.
661
+ for param in params:
662
+ if _is_incomplete_argument(ctx, param):
663
+ return param, incomplete
664
+
665
+ # There were no unparsed arguments, the command may be a group that
666
+ # will provide command name completions.
667
+ return ctx.command, incomplete
env/lib/python3.13/site-packages/click/termui.py ADDED
@@ -0,0 +1,883 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import collections.abc as cabc
4
+ import inspect
5
+ import io
6
+ import itertools
7
+ import sys
8
+ import typing as t
9
+ from contextlib import AbstractContextManager
10
+ from gettext import gettext as _
11
+
12
+ from ._compat import isatty
13
+ from ._compat import strip_ansi
14
+ from .exceptions import Abort
15
+ from .exceptions import UsageError
16
+ from .globals import resolve_color_default
17
+ from .types import Choice
18
+ from .types import convert_type
19
+ from .types import ParamType
20
+ from .utils import echo
21
+ from .utils import LazyFile
22
+
23
+ if t.TYPE_CHECKING:
24
+ from ._termui_impl import ProgressBar
25
+
26
+ V = t.TypeVar("V")
27
+
28
+ # The prompt functions to use. The doc tools currently override these
29
+ # functions to customize how they work.
30
+ visible_prompt_func: t.Callable[[str], str] = input
31
+
32
+ _ansi_colors = {
33
+ "black": 30,
34
+ "red": 31,
35
+ "green": 32,
36
+ "yellow": 33,
37
+ "blue": 34,
38
+ "magenta": 35,
39
+ "cyan": 36,
40
+ "white": 37,
41
+ "reset": 39,
42
+ "bright_black": 90,
43
+ "bright_red": 91,
44
+ "bright_green": 92,
45
+ "bright_yellow": 93,
46
+ "bright_blue": 94,
47
+ "bright_magenta": 95,
48
+ "bright_cyan": 96,
49
+ "bright_white": 97,
50
+ }
51
+ _ansi_reset_all = "\033[0m"
52
+
53
+
54
+ def hidden_prompt_func(prompt: str) -> str:
55
+ import getpass
56
+
57
+ return getpass.getpass(prompt)
58
+
59
+
60
+ def _build_prompt(
61
+ text: str,
62
+ suffix: str,
63
+ show_default: bool = False,
64
+ default: t.Any | None = None,
65
+ show_choices: bool = True,
66
+ type: ParamType | None = None,
67
+ ) -> str:
68
+ prompt = text
69
+ if type is not None and show_choices and isinstance(type, Choice):
70
+ prompt += f" ({', '.join(map(str, type.choices))})"
71
+ if default is not None and show_default:
72
+ prompt = f"{prompt} [{_format_default(default)}]"
73
+ return f"{prompt}{suffix}"
74
+
75
+
76
+ def _format_default(default: t.Any) -> t.Any:
77
+ if isinstance(default, (io.IOBase, LazyFile)) and hasattr(default, "name"):
78
+ return default.name
79
+
80
+ return default
81
+
82
+
83
+ def prompt(
84
+ text: str,
85
+ default: t.Any | None = None,
86
+ hide_input: bool = False,
87
+ confirmation_prompt: bool | str = False,
88
+ type: ParamType | t.Any | None = None,
89
+ value_proc: t.Callable[[str], t.Any] | None = None,
90
+ prompt_suffix: str = ": ",
91
+ show_default: bool = True,
92
+ err: bool = False,
93
+ show_choices: bool = True,
94
+ ) -> t.Any:
95
+ """Prompts a user for input. This is a convenience function that can
96
+ be used to prompt a user for input later.
97
+
98
+ If the user aborts the input by sending an interrupt signal, this
99
+ function will catch it and raise a :exc:`Abort` exception.
100
+
101
+ :param text: the text to show for the prompt.
102
+ :param default: the default value to use if no input happens. If this
103
+ is not given it will prompt until it's aborted.
104
+ :param hide_input: if this is set to true then the input value will
105
+ be hidden.
106
+ :param confirmation_prompt: Prompt a second time to confirm the
107
+ value. Can be set to a string instead of ``True`` to customize
108
+ the message.
109
+ :param type: the type to use to check the value against.
110
+ :param value_proc: if this parameter is provided it's a function that
111
+ is invoked instead of the type conversion to
112
+ convert a value.
113
+ :param prompt_suffix: a suffix that should be added to the prompt.
114
+ :param show_default: shows or hides the default value in the prompt.
115
+ :param err: if set to true the file defaults to ``stderr`` instead of
116
+ ``stdout``, the same as with echo.
117
+ :param show_choices: Show or hide choices if the passed type is a Choice.
118
+ For example if type is a Choice of either day or week,
119
+ show_choices is true and text is "Group by" then the
120
+ prompt will be "Group by (day, week): ".
121
+
122
+ .. versionchanged:: 8.3.1
123
+ A space is no longer appended to the prompt.
124
+
125
+ .. versionadded:: 8.0
126
+ ``confirmation_prompt`` can be a custom string.
127
+
128
+ .. versionadded:: 7.0
129
+ Added the ``show_choices`` parameter.
130
+
131
+ .. versionadded:: 6.0
132
+ Added unicode support for cmd.exe on Windows.
133
+
134
+ .. versionadded:: 4.0
135
+ Added the `err` parameter.
136
+
137
+ """
138
+
139
+ def prompt_func(text: str) -> str:
140
+ f = hidden_prompt_func if hide_input else visible_prompt_func
141
+ try:
142
+ # Write the prompt separately so that we get nice
143
+ # coloring through colorama on Windows
144
+ echo(text[:-1], nl=False, err=err)
145
+ # Echo the last character to stdout to work around an issue where
146
+ # readline causes backspace to clear the whole line.
147
+ return f(text[-1:])
148
+ except (KeyboardInterrupt, EOFError):
149
+ # getpass doesn't print a newline if the user aborts input with ^C.
150
+ # Allegedly this behavior is inherited from getpass(3).
151
+ # A doc bug has been filed at https://bugs.python.org/issue24711
152
+ if hide_input:
153
+ echo(None, err=err)
154
+ raise Abort() from None
155
+
156
+ if value_proc is None:
157
+ value_proc = convert_type(type, default)
158
+
159
+ prompt = _build_prompt(
160
+ text, prompt_suffix, show_default, default, show_choices, type
161
+ )
162
+
163
+ if confirmation_prompt:
164
+ if confirmation_prompt is True:
165
+ confirmation_prompt = _("Repeat for confirmation")
166
+
167
+ confirmation_prompt = _build_prompt(confirmation_prompt, prompt_suffix)
168
+
169
+ while True:
170
+ while True:
171
+ value = prompt_func(prompt)
172
+ if value:
173
+ break
174
+ elif default is not None:
175
+ value = default
176
+ break
177
+ try:
178
+ result = value_proc(value)
179
+ except UsageError as e:
180
+ if hide_input:
181
+ echo(_("Error: The value you entered was invalid."), err=err)
182
+ else:
183
+ echo(_("Error: {e.message}").format(e=e), err=err)
184
+ continue
185
+ if not confirmation_prompt:
186
+ return result
187
+ while True:
188
+ value2 = prompt_func(confirmation_prompt)
189
+ is_empty = not value and not value2
190
+ if value2 or is_empty:
191
+ break
192
+ if value == value2:
193
+ return result
194
+ echo(_("Error: The two entered values do not match."), err=err)
195
+
196
+
197
+ def confirm(
198
+ text: str,
199
+ default: bool | None = False,
200
+ abort: bool = False,
201
+ prompt_suffix: str = ": ",
202
+ show_default: bool = True,
203
+ err: bool = False,
204
+ ) -> bool:
205
+ """Prompts for confirmation (yes/no question).
206
+
207
+ If the user aborts the input by sending a interrupt signal this
208
+ function will catch it and raise a :exc:`Abort` exception.
209
+
210
+ :param text: the question to ask.
211
+ :param default: The default value to use when no input is given. If
212
+ ``None``, repeat until input is given.
213
+ :param abort: if this is set to `True` a negative answer aborts the
214
+ exception by raising :exc:`Abort`.
215
+ :param prompt_suffix: a suffix that should be added to the prompt.
216
+ :param show_default: shows or hides the default value in the prompt.
217
+ :param err: if set to true the file defaults to ``stderr`` instead of
218
+ ``stdout``, the same as with echo.
219
+
220
+ .. versionchanged:: 8.3.1
221
+ A space is no longer appended to the prompt.
222
+
223
+ .. versionchanged:: 8.0
224
+ Repeat until input is given if ``default`` is ``None``.
225
+
226
+ .. versionadded:: 4.0
227
+ Added the ``err`` parameter.
228
+ """
229
+ prompt = _build_prompt(
230
+ text,
231
+ prompt_suffix,
232
+ show_default,
233
+ "y/n" if default is None else ("Y/n" if default else "y/N"),
234
+ )
235
+
236
+ while True:
237
+ try:
238
+ # Write the prompt separately so that we get nice
239
+ # coloring through colorama on Windows
240
+ echo(prompt[:-1], nl=False, err=err)
241
+ # Echo the last character to stdout to work around an issue where
242
+ # readline causes backspace to clear the whole line.
243
+ value = visible_prompt_func(prompt[-1:]).lower().strip()
244
+ except (KeyboardInterrupt, EOFError):
245
+ raise Abort() from None
246
+ if value in ("y", "yes"):
247
+ rv = True
248
+ elif value in ("n", "no"):
249
+ rv = False
250
+ elif default is not None and value == "":
251
+ rv = default
252
+ else:
253
+ echo(_("Error: invalid input"), err=err)
254
+ continue
255
+ break
256
+ if abort and not rv:
257
+ raise Abort()
258
+ return rv
259
+
260
+
261
+ def echo_via_pager(
262
+ text_or_generator: cabc.Iterable[str] | t.Callable[[], cabc.Iterable[str]] | str,
263
+ color: bool | None = None,
264
+ ) -> None:
265
+ """This function takes a text and shows it via an environment specific
266
+ pager on stdout.
267
+
268
+ .. versionchanged:: 3.0
269
+ Added the `color` flag.
270
+
271
+ :param text_or_generator: the text to page, or alternatively, a
272
+ generator emitting the text to page.
273
+ :param color: controls if the pager supports ANSI colors or not. The
274
+ default is autodetection.
275
+ """
276
+ color = resolve_color_default(color)
277
+
278
+ if inspect.isgeneratorfunction(text_or_generator):
279
+ i = t.cast("t.Callable[[], cabc.Iterable[str]]", text_or_generator)()
280
+ elif isinstance(text_or_generator, str):
281
+ i = [text_or_generator]
282
+ else:
283
+ i = iter(t.cast("cabc.Iterable[str]", text_or_generator))
284
+
285
+ # convert every element of i to a text type if necessary
286
+ text_generator = (el if isinstance(el, str) else str(el) for el in i)
287
+
288
+ from ._termui_impl import pager
289
+
290
+ return pager(itertools.chain(text_generator, "\n"), color)
291
+
292
+
293
+ @t.overload
294
+ def progressbar(
295
+ *,
296
+ length: int,
297
+ label: str | None = None,
298
+ hidden: bool = False,
299
+ show_eta: bool = True,
300
+ show_percent: bool | None = None,
301
+ show_pos: bool = False,
302
+ fill_char: str = "#",
303
+ empty_char: str = "-",
304
+ bar_template: str = "%(label)s [%(bar)s] %(info)s",
305
+ info_sep: str = " ",
306
+ width: int = 36,
307
+ file: t.TextIO | None = None,
308
+ color: bool | None = None,
309
+ update_min_steps: int = 1,
310
+ ) -> ProgressBar[int]: ...
311
+
312
+
313
+ @t.overload
314
+ def progressbar(
315
+ iterable: cabc.Iterable[V] | None = None,
316
+ length: int | None = None,
317
+ label: str | None = None,
318
+ hidden: bool = False,
319
+ show_eta: bool = True,
320
+ show_percent: bool | None = None,
321
+ show_pos: bool = False,
322
+ item_show_func: t.Callable[[V | None], str | None] | None = None,
323
+ fill_char: str = "#",
324
+ empty_char: str = "-",
325
+ bar_template: str = "%(label)s [%(bar)s] %(info)s",
326
+ info_sep: str = " ",
327
+ width: int = 36,
328
+ file: t.TextIO | None = None,
329
+ color: bool | None = None,
330
+ update_min_steps: int = 1,
331
+ ) -> ProgressBar[V]: ...
332
+
333
+
334
+ def progressbar(
335
+ iterable: cabc.Iterable[V] | None = None,
336
+ length: int | None = None,
337
+ label: str | None = None,
338
+ hidden: bool = False,
339
+ show_eta: bool = True,
340
+ show_percent: bool | None = None,
341
+ show_pos: bool = False,
342
+ item_show_func: t.Callable[[V | None], str | None] | None = None,
343
+ fill_char: str = "#",
344
+ empty_char: str = "-",
345
+ bar_template: str = "%(label)s [%(bar)s] %(info)s",
346
+ info_sep: str = " ",
347
+ width: int = 36,
348
+ file: t.TextIO | None = None,
349
+ color: bool | None = None,
350
+ update_min_steps: int = 1,
351
+ ) -> ProgressBar[V]:
352
+ """This function creates an iterable context manager that can be used
353
+ to iterate over something while showing a progress bar. It will
354
+ either iterate over the `iterable` or `length` items (that are counted
355
+ up). While iteration happens, this function will print a rendered
356
+ progress bar to the given `file` (defaults to stdout) and will attempt
357
+ to calculate remaining time and more. By default, this progress bar
358
+ will not be rendered if the file is not a terminal.
359
+
360
+ The context manager creates the progress bar. When the context
361
+ manager is entered the progress bar is already created. With every
362
+ iteration over the progress bar, the iterable passed to the bar is
363
+ advanced and the bar is updated. When the context manager exits,
364
+ a newline is printed and the progress bar is finalized on screen.
365
+
366
+ Note: The progress bar is currently designed for use cases where the
367
+ total progress can be expected to take at least several seconds.
368
+ Because of this, the ProgressBar class object won't display
369
+ progress that is considered too fast, and progress where the time
370
+ between steps is less than a second.
371
+
372
+ No printing must happen or the progress bar will be unintentionally
373
+ destroyed.
374
+
375
+ Example usage::
376
+
377
+ with progressbar(items) as bar:
378
+ for item in bar:
379
+ do_something_with(item)
380
+
381
+ Alternatively, if no iterable is specified, one can manually update the
382
+ progress bar through the `update()` method instead of directly
383
+ iterating over the progress bar. The update method accepts the number
384
+ of steps to increment the bar with::
385
+
386
+ with progressbar(length=chunks.total_bytes) as bar:
387
+ for chunk in chunks:
388
+ process_chunk(chunk)
389
+ bar.update(chunks.bytes)
390
+
391
+ The ``update()`` method also takes an optional value specifying the
392
+ ``current_item`` at the new position. This is useful when used
393
+ together with ``item_show_func`` to customize the output for each
394
+ manual step::
395
+
396
+ with click.progressbar(
397
+ length=total_size,
398
+ label='Unzipping archive',
399
+ item_show_func=lambda a: a.filename
400
+ ) as bar:
401
+ for archive in zip_file:
402
+ archive.extract()
403
+ bar.update(archive.size, archive)
404
+
405
+ :param iterable: an iterable to iterate over. If not provided the length
406
+ is required.
407
+ :param length: the number of items to iterate over. By default the
408
+ progressbar will attempt to ask the iterator about its
409
+ length, which might or might not work. If an iterable is
410
+ also provided this parameter can be used to override the
411
+ length. If an iterable is not provided the progress bar
412
+ will iterate over a range of that length.
413
+ :param label: the label to show next to the progress bar.
414
+ :param hidden: hide the progressbar. Defaults to ``False``. When no tty is
415
+ detected, it will only print the progressbar label. Setting this to
416
+ ``False`` also disables that.
417
+ :param show_eta: enables or disables the estimated time display. This is
418
+ automatically disabled if the length cannot be
419
+ determined.
420
+ :param show_percent: enables or disables the percentage display. The
421
+ default is `True` if the iterable has a length or
422
+ `False` if not.
423
+ :param show_pos: enables or disables the absolute position display. The
424
+ default is `False`.
425
+ :param item_show_func: A function called with the current item which
426
+ can return a string to show next to the progress bar. If the
427
+ function returns ``None`` nothing is shown. The current item can
428
+ be ``None``, such as when entering and exiting the bar.
429
+ :param fill_char: the character to use to show the filled part of the
430
+ progress bar.
431
+ :param empty_char: the character to use to show the non-filled part of
432
+ the progress bar.
433
+ :param bar_template: the format string to use as template for the bar.
434
+ The parameters in it are ``label`` for the label,
435
+ ``bar`` for the progress bar and ``info`` for the
436
+ info section.
437
+ :param info_sep: the separator between multiple info items (eta etc.)
438
+ :param width: the width of the progress bar in characters, 0 means full
439
+ terminal width
440
+ :param file: The file to write to. If this is not a terminal then
441
+ only the label is printed.
442
+ :param color: controls if the terminal supports ANSI colors or not. The
443
+ default is autodetection. This is only needed if ANSI
444
+ codes are included anywhere in the progress bar output
445
+ which is not the case by default.
446
+ :param update_min_steps: Render only when this many updates have
447
+ completed. This allows tuning for very fast iterators.
448
+
449
+ .. versionadded:: 8.2
450
+ The ``hidden`` argument.
451
+
452
+ .. versionchanged:: 8.0
453
+ Output is shown even if execution time is less than 0.5 seconds.
454
+
455
+ .. versionchanged:: 8.0
456
+ ``item_show_func`` shows the current item, not the previous one.
457
+
458
+ .. versionchanged:: 8.0
459
+ Labels are echoed if the output is not a TTY. Reverts a change
460
+ in 7.0 that removed all output.
461
+
462
+ .. versionadded:: 8.0
463
+ The ``update_min_steps`` parameter.
464
+
465
+ .. versionadded:: 4.0
466
+ The ``color`` parameter and ``update`` method.
467
+
468
+ .. versionadded:: 2.0
469
+ """
470
+ from ._termui_impl import ProgressBar
471
+
472
+ color = resolve_color_default(color)
473
+ return ProgressBar(
474
+ iterable=iterable,
475
+ length=length,
476
+ hidden=hidden,
477
+ show_eta=show_eta,
478
+ show_percent=show_percent,
479
+ show_pos=show_pos,
480
+ item_show_func=item_show_func,
481
+ fill_char=fill_char,
482
+ empty_char=empty_char,
483
+ bar_template=bar_template,
484
+ info_sep=info_sep,
485
+ file=file,
486
+ label=label,
487
+ width=width,
488
+ color=color,
489
+ update_min_steps=update_min_steps,
490
+ )
491
+
492
+
493
+ def clear() -> None:
494
+ """Clears the terminal screen. This will have the effect of clearing
495
+ the whole visible space of the terminal and moving the cursor to the
496
+ top left. This does not do anything if not connected to a terminal.
497
+
498
+ .. versionadded:: 2.0
499
+ """
500
+ if not isatty(sys.stdout):
501
+ return
502
+
503
+ # ANSI escape \033[2J clears the screen, \033[1;1H moves the cursor
504
+ echo("\033[2J\033[1;1H", nl=False)
505
+
506
+
507
+ def _interpret_color(color: int | tuple[int, int, int] | str, offset: int = 0) -> str:
508
+ if isinstance(color, int):
509
+ return f"{38 + offset};5;{color:d}"
510
+
511
+ if isinstance(color, (tuple, list)):
512
+ r, g, b = color
513
+ return f"{38 + offset};2;{r:d};{g:d};{b:d}"
514
+
515
+ return str(_ansi_colors[color] + offset)
516
+
517
+
518
+ def style(
519
+ text: t.Any,
520
+ fg: int | tuple[int, int, int] | str | None = None,
521
+ bg: int | tuple[int, int, int] | str | None = None,
522
+ bold: bool | None = None,
523
+ dim: bool | None = None,
524
+ underline: bool | None = None,
525
+ overline: bool | None = None,
526
+ italic: bool | None = None,
527
+ blink: bool | None = None,
528
+ reverse: bool | None = None,
529
+ strikethrough: bool | None = None,
530
+ reset: bool = True,
531
+ ) -> str:
532
+ """Styles a text with ANSI styles and returns the new string. By
533
+ default the styling is self contained which means that at the end
534
+ of the string a reset code is issued. This can be prevented by
535
+ passing ``reset=False``.
536
+
537
+ Examples::
538
+
539
+ click.echo(click.style('Hello World!', fg='green'))
540
+ click.echo(click.style('ATTENTION!', blink=True))
541
+ click.echo(click.style('Some things', reverse=True, fg='cyan'))
542
+ click.echo(click.style('More colors', fg=(255, 12, 128), bg=117))
543
+
544
+ Supported color names:
545
+
546
+ * ``black`` (might be a gray)
547
+ * ``red``
548
+ * ``green``
549
+ * ``yellow`` (might be an orange)
550
+ * ``blue``
551
+ * ``magenta``
552
+ * ``cyan``
553
+ * ``white`` (might be light gray)
554
+ * ``bright_black``
555
+ * ``bright_red``
556
+ * ``bright_green``
557
+ * ``bright_yellow``
558
+ * ``bright_blue``
559
+ * ``bright_magenta``
560
+ * ``bright_cyan``
561
+ * ``bright_white``
562
+ * ``reset`` (reset the color code only)
563
+
564
+ If the terminal supports it, color may also be specified as:
565
+
566
+ - An integer in the interval [0, 255]. The terminal must support
567
+ 8-bit/256-color mode.
568
+ - An RGB tuple of three integers in [0, 255]. The terminal must
569
+ support 24-bit/true-color mode.
570
+
571
+ See https://en.wikipedia.org/wiki/ANSI_color and
572
+ https://gist.github.com/XVilka/8346728 for more information.
573
+
574
+ :param text: the string to style with ansi codes.
575
+ :param fg: if provided this will become the foreground color.
576
+ :param bg: if provided this will become the background color.
577
+ :param bold: if provided this will enable or disable bold mode.
578
+ :param dim: if provided this will enable or disable dim mode. This is
579
+ badly supported.
580
+ :param underline: if provided this will enable or disable underline.
581
+ :param overline: if provided this will enable or disable overline.
582
+ :param italic: if provided this will enable or disable italic.
583
+ :param blink: if provided this will enable or disable blinking.
584
+ :param reverse: if provided this will enable or disable inverse
585
+ rendering (foreground becomes background and the
586
+ other way round).
587
+ :param strikethrough: if provided this will enable or disable
588
+ striking through text.
589
+ :param reset: by default a reset-all code is added at the end of the
590
+ string which means that styles do not carry over. This
591
+ can be disabled to compose styles.
592
+
593
+ .. versionchanged:: 8.0
594
+ A non-string ``message`` is converted to a string.
595
+
596
+ .. versionchanged:: 8.0
597
+ Added support for 256 and RGB color codes.
598
+
599
+ .. versionchanged:: 8.0
600
+ Added the ``strikethrough``, ``italic``, and ``overline``
601
+ parameters.
602
+
603
+ .. versionchanged:: 7.0
604
+ Added support for bright colors.
605
+
606
+ .. versionadded:: 2.0
607
+ """
608
+ if not isinstance(text, str):
609
+ text = str(text)
610
+
611
+ bits = []
612
+
613
+ if fg:
614
+ try:
615
+ bits.append(f"\033[{_interpret_color(fg)}m")
616
+ except KeyError:
617
+ raise TypeError(f"Unknown color {fg!r}") from None
618
+
619
+ if bg:
620
+ try:
621
+ bits.append(f"\033[{_interpret_color(bg, 10)}m")
622
+ except KeyError:
623
+ raise TypeError(f"Unknown color {bg!r}") from None
624
+
625
+ if bold is not None:
626
+ bits.append(f"\033[{1 if bold else 22}m")
627
+ if dim is not None:
628
+ bits.append(f"\033[{2 if dim else 22}m")
629
+ if underline is not None:
630
+ bits.append(f"\033[{4 if underline else 24}m")
631
+ if overline is not None:
632
+ bits.append(f"\033[{53 if overline else 55}m")
633
+ if italic is not None:
634
+ bits.append(f"\033[{3 if italic else 23}m")
635
+ if blink is not None:
636
+ bits.append(f"\033[{5 if blink else 25}m")
637
+ if reverse is not None:
638
+ bits.append(f"\033[{7 if reverse else 27}m")
639
+ if strikethrough is not None:
640
+ bits.append(f"\033[{9 if strikethrough else 29}m")
641
+ bits.append(text)
642
+ if reset:
643
+ bits.append(_ansi_reset_all)
644
+ return "".join(bits)
645
+
646
+
647
+ def unstyle(text: str) -> str:
648
+ """Removes ANSI styling information from a string. Usually it's not
649
+ necessary to use this function as Click's echo function will
650
+ automatically remove styling if necessary.
651
+
652
+ .. versionadded:: 2.0
653
+
654
+ :param text: the text to remove style information from.
655
+ """
656
+ return strip_ansi(text)
657
+
658
+
659
+ def secho(
660
+ message: t.Any | None = None,
661
+ file: t.IO[t.AnyStr] | None = None,
662
+ nl: bool = True,
663
+ err: bool = False,
664
+ color: bool | None = None,
665
+ **styles: t.Any,
666
+ ) -> None:
667
+ """This function combines :func:`echo` and :func:`style` into one
668
+ call. As such the following two calls are the same::
669
+
670
+ click.secho('Hello World!', fg='green')
671
+ click.echo(click.style('Hello World!', fg='green'))
672
+
673
+ All keyword arguments are forwarded to the underlying functions
674
+ depending on which one they go with.
675
+
676
+ Non-string types will be converted to :class:`str`. However,
677
+ :class:`bytes` are passed directly to :meth:`echo` without applying
678
+ style. If you want to style bytes that represent text, call
679
+ :meth:`bytes.decode` first.
680
+
681
+ .. versionchanged:: 8.0
682
+ A non-string ``message`` is converted to a string. Bytes are
683
+ passed through without style applied.
684
+
685
+ .. versionadded:: 2.0
686
+ """
687
+ if message is not None and not isinstance(message, (bytes, bytearray)):
688
+ message = style(message, **styles)
689
+
690
+ return echo(message, file=file, nl=nl, err=err, color=color)
691
+
692
+
693
+ @t.overload
694
+ def edit(
695
+ text: bytes | bytearray,
696
+ editor: str | None = None,
697
+ env: cabc.Mapping[str, str] | None = None,
698
+ require_save: bool = False,
699
+ extension: str = ".txt",
700
+ ) -> bytes | None: ...
701
+
702
+
703
+ @t.overload
704
+ def edit(
705
+ text: str,
706
+ editor: str | None = None,
707
+ env: cabc.Mapping[str, str] | None = None,
708
+ require_save: bool = True,
709
+ extension: str = ".txt",
710
+ ) -> str | None: ...
711
+
712
+
713
+ @t.overload
714
+ def edit(
715
+ text: None = None,
716
+ editor: str | None = None,
717
+ env: cabc.Mapping[str, str] | None = None,
718
+ require_save: bool = True,
719
+ extension: str = ".txt",
720
+ filename: str | cabc.Iterable[str] | None = None,
721
+ ) -> None: ...
722
+
723
+
724
+ def edit(
725
+ text: str | bytes | bytearray | None = None,
726
+ editor: str | None = None,
727
+ env: cabc.Mapping[str, str] | None = None,
728
+ require_save: bool = True,
729
+ extension: str = ".txt",
730
+ filename: str | cabc.Iterable[str] | None = None,
731
+ ) -> str | bytes | bytearray | None:
732
+ r"""Edits the given text in the defined editor. If an editor is given
733
+ (should be the full path to the executable but the regular operating
734
+ system search path is used for finding the executable) it overrides
735
+ the detected editor. Optionally, some environment variables can be
736
+ used. If the editor is closed without changes, `None` is returned. In
737
+ case a file is edited directly the return value is always `None` and
738
+ `require_save` and `extension` are ignored.
739
+
740
+ If the editor cannot be opened a :exc:`UsageError` is raised.
741
+
742
+ Note for Windows: to simplify cross-platform usage, the newlines are
743
+ automatically converted from POSIX to Windows and vice versa. As such,
744
+ the message here will have ``\n`` as newline markers.
745
+
746
+ :param text: the text to edit.
747
+ :param editor: optionally the editor to use. Defaults to automatic
748
+ detection.
749
+ :param env: environment variables to forward to the editor.
750
+ :param require_save: if this is true, then not saving in the editor
751
+ will make the return value become `None`.
752
+ :param extension: the extension to tell the editor about. This defaults
753
+ to `.txt` but changing this might change syntax
754
+ highlighting.
755
+ :param filename: if provided it will edit this file instead of the
756
+ provided text contents. It will not use a temporary
757
+ file as an indirection in that case. If the editor supports
758
+ editing multiple files at once, a sequence of files may be
759
+ passed as well. Invoke `click.file` once per file instead
760
+ if multiple files cannot be managed at once or editing the
761
+ files serially is desired.
762
+
763
+ .. versionchanged:: 8.2.0
764
+ ``filename`` now accepts any ``Iterable[str]`` in addition to a ``str``
765
+ if the ``editor`` supports editing multiple files at once.
766
+
767
+ """
768
+ from ._termui_impl import Editor
769
+
770
+ ed = Editor(editor=editor, env=env, require_save=require_save, extension=extension)
771
+
772
+ if filename is None:
773
+ return ed.edit(text)
774
+
775
+ if isinstance(filename, str):
776
+ filename = (filename,)
777
+
778
+ ed.edit_files(filenames=filename)
779
+ return None
780
+
781
+
782
+ def launch(url: str, wait: bool = False, locate: bool = False) -> int:
783
+ """This function launches the given URL (or filename) in the default
784
+ viewer application for this file type. If this is an executable, it
785
+ might launch the executable in a new session. The return value is
786
+ the exit code of the launched application. Usually, ``0`` indicates
787
+ success.
788
+
789
+ Examples::
790
+
791
+ click.launch('https://click.palletsprojects.com/')
792
+ click.launch('/my/downloaded/file', locate=True)
793
+
794
+ .. versionadded:: 2.0
795
+
796
+ :param url: URL or filename of the thing to launch.
797
+ :param wait: Wait for the program to exit before returning. This
798
+ only works if the launched program blocks. In particular,
799
+ ``xdg-open`` on Linux does not block.
800
+ :param locate: if this is set to `True` then instead of launching the
801
+ application associated with the URL it will attempt to
802
+ launch a file manager with the file located. This
803
+ might have weird effects if the URL does not point to
804
+ the filesystem.
805
+ """
806
+ from ._termui_impl import open_url
807
+
808
+ return open_url(url, wait=wait, locate=locate)
809
+
810
+
811
+ # If this is provided, getchar() calls into this instead. This is used
812
+ # for unittesting purposes.
813
+ _getchar: t.Callable[[bool], str] | None = None
814
+
815
+
816
+ def getchar(echo: bool = False) -> str:
817
+ """Fetches a single character from the terminal and returns it. This
818
+ will always return a unicode character and under certain rare
819
+ circumstances this might return more than one character. The
820
+ situations which more than one character is returned is when for
821
+ whatever reason multiple characters end up in the terminal buffer or
822
+ standard input was not actually a terminal.
823
+
824
+ Note that this will always read from the terminal, even if something
825
+ is piped into the standard input.
826
+
827
+ Note for Windows: in rare cases when typing non-ASCII characters, this
828
+ function might wait for a second character and then return both at once.
829
+ This is because certain Unicode characters look like special-key markers.
830
+
831
+ .. versionadded:: 2.0
832
+
833
+ :param echo: if set to `True`, the character read will also show up on
834
+ the terminal. The default is to not show it.
835
+ """
836
+ global _getchar
837
+
838
+ if _getchar is None:
839
+ from ._termui_impl import getchar as f
840
+
841
+ _getchar = f
842
+
843
+ return _getchar(echo)
844
+
845
+
846
+ def raw_terminal() -> AbstractContextManager[int]:
847
+ from ._termui_impl import raw_terminal as f
848
+
849
+ return f()
850
+
851
+
852
+ def pause(info: str | None = None, err: bool = False) -> None:
853
+ """This command stops execution and waits for the user to press any
854
+ key to continue. This is similar to the Windows batch "pause"
855
+ command. If the program is not run through a terminal, this command
856
+ will instead do nothing.
857
+
858
+ .. versionadded:: 2.0
859
+
860
+ .. versionadded:: 4.0
861
+ Added the `err` parameter.
862
+
863
+ :param info: The message to print before pausing. Defaults to
864
+ ``"Press any key to continue..."``.
865
+ :param err: if set to message goes to ``stderr`` instead of
866
+ ``stdout``, the same as with echo.
867
+ """
868
+ if not isatty(sys.stdin) or not isatty(sys.stdout):
869
+ return
870
+
871
+ if info is None:
872
+ info = _("Press any key to continue...")
873
+
874
+ try:
875
+ if info:
876
+ echo(info, nl=False, err=err)
877
+ try:
878
+ getchar()
879
+ except (KeyboardInterrupt, EOFError):
880
+ pass
881
+ finally:
882
+ if info:
883
+ echo(err=err)
env/lib/python3.13/site-packages/click/testing.py ADDED
@@ -0,0 +1,577 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import collections.abc as cabc
4
+ import contextlib
5
+ import io
6
+ import os
7
+ import shlex
8
+ import sys
9
+ import tempfile
10
+ import typing as t
11
+ from types import TracebackType
12
+
13
+ from . import _compat
14
+ from . import formatting
15
+ from . import termui
16
+ from . import utils
17
+ from ._compat import _find_binary_reader
18
+
19
+ if t.TYPE_CHECKING:
20
+ from _typeshed import ReadableBuffer
21
+
22
+ from .core import Command
23
+
24
+
25
+ class EchoingStdin:
26
+ def __init__(self, input: t.BinaryIO, output: t.BinaryIO) -> None:
27
+ self._input = input
28
+ self._output = output
29
+ self._paused = False
30
+
31
+ def __getattr__(self, x: str) -> t.Any:
32
+ return getattr(self._input, x)
33
+
34
+ def _echo(self, rv: bytes) -> bytes:
35
+ if not self._paused:
36
+ self._output.write(rv)
37
+
38
+ return rv
39
+
40
+ def read(self, n: int = -1) -> bytes:
41
+ return self._echo(self._input.read(n))
42
+
43
+ def read1(self, n: int = -1) -> bytes:
44
+ return self._echo(self._input.read1(n)) # type: ignore
45
+
46
+ def readline(self, n: int = -1) -> bytes:
47
+ return self._echo(self._input.readline(n))
48
+
49
+ def readlines(self) -> list[bytes]:
50
+ return [self._echo(x) for x in self._input.readlines()]
51
+
52
+ def __iter__(self) -> cabc.Iterator[bytes]:
53
+ return iter(self._echo(x) for x in self._input)
54
+
55
+ def __repr__(self) -> str:
56
+ return repr(self._input)
57
+
58
+
59
+ @contextlib.contextmanager
60
+ def _pause_echo(stream: EchoingStdin | None) -> cabc.Iterator[None]:
61
+ if stream is None:
62
+ yield
63
+ else:
64
+ stream._paused = True
65
+ yield
66
+ stream._paused = False
67
+
68
+
69
+ class BytesIOCopy(io.BytesIO):
70
+ """Patch ``io.BytesIO`` to let the written stream be copied to another.
71
+
72
+ .. versionadded:: 8.2
73
+ """
74
+
75
+ def __init__(self, copy_to: io.BytesIO) -> None:
76
+ super().__init__()
77
+ self.copy_to = copy_to
78
+
79
+ def flush(self) -> None:
80
+ super().flush()
81
+ self.copy_to.flush()
82
+
83
+ def write(self, b: ReadableBuffer) -> int:
84
+ self.copy_to.write(b)
85
+ return super().write(b)
86
+
87
+
88
+ class StreamMixer:
89
+ """Mixes `<stdout>` and `<stderr>` streams.
90
+
91
+ The result is available in the ``output`` attribute.
92
+
93
+ .. versionadded:: 8.2
94
+ """
95
+
96
+ def __init__(self) -> None:
97
+ self.output: io.BytesIO = io.BytesIO()
98
+ self.stdout: io.BytesIO = BytesIOCopy(copy_to=self.output)
99
+ self.stderr: io.BytesIO = BytesIOCopy(copy_to=self.output)
100
+
101
+ def __del__(self) -> None:
102
+ """
103
+ Guarantee that embedded file-like objects are closed in a
104
+ predictable order, protecting against races between
105
+ self.output being closed and other streams being flushed on close
106
+
107
+ .. versionadded:: 8.2.2
108
+ """
109
+ self.stderr.close()
110
+ self.stdout.close()
111
+ self.output.close()
112
+
113
+
114
+ class _NamedTextIOWrapper(io.TextIOWrapper):
115
+ def __init__(
116
+ self, buffer: t.BinaryIO, name: str, mode: str, **kwargs: t.Any
117
+ ) -> None:
118
+ super().__init__(buffer, **kwargs)
119
+ self._name = name
120
+ self._mode = mode
121
+
122
+ @property
123
+ def name(self) -> str:
124
+ return self._name
125
+
126
+ @property
127
+ def mode(self) -> str:
128
+ return self._mode
129
+
130
+
131
+ def make_input_stream(
132
+ input: str | bytes | t.IO[t.Any] | None, charset: str
133
+ ) -> t.BinaryIO:
134
+ # Is already an input stream.
135
+ if hasattr(input, "read"):
136
+ rv = _find_binary_reader(t.cast("t.IO[t.Any]", input))
137
+
138
+ if rv is not None:
139
+ return rv
140
+
141
+ raise TypeError("Could not find binary reader for input stream.")
142
+
143
+ if input is None:
144
+ input = b""
145
+ elif isinstance(input, str):
146
+ input = input.encode(charset)
147
+
148
+ return io.BytesIO(input)
149
+
150
+
151
+ class Result:
152
+ """Holds the captured result of an invoked CLI script.
153
+
154
+ :param runner: The runner that created the result
155
+ :param stdout_bytes: The standard output as bytes.
156
+ :param stderr_bytes: The standard error as bytes.
157
+ :param output_bytes: A mix of ``stdout_bytes`` and ``stderr_bytes``, as the
158
+ user would see it in its terminal.
159
+ :param return_value: The value returned from the invoked command.
160
+ :param exit_code: The exit code as integer.
161
+ :param exception: The exception that happened if one did.
162
+ :param exc_info: Exception information (exception type, exception instance,
163
+ traceback type).
164
+
165
+ .. versionchanged:: 8.2
166
+ ``stderr_bytes`` no longer optional, ``output_bytes`` introduced and
167
+ ``mix_stderr`` has been removed.
168
+
169
+ .. versionadded:: 8.0
170
+ Added ``return_value``.
171
+ """
172
+
173
+ def __init__(
174
+ self,
175
+ runner: CliRunner,
176
+ stdout_bytes: bytes,
177
+ stderr_bytes: bytes,
178
+ output_bytes: bytes,
179
+ return_value: t.Any,
180
+ exit_code: int,
181
+ exception: BaseException | None,
182
+ exc_info: tuple[type[BaseException], BaseException, TracebackType]
183
+ | None = None,
184
+ ):
185
+ self.runner = runner
186
+ self.stdout_bytes = stdout_bytes
187
+ self.stderr_bytes = stderr_bytes
188
+ self.output_bytes = output_bytes
189
+ self.return_value = return_value
190
+ self.exit_code = exit_code
191
+ self.exception = exception
192
+ self.exc_info = exc_info
193
+
194
+ @property
195
+ def output(self) -> str:
196
+ """The terminal output as unicode string, as the user would see it.
197
+
198
+ .. versionchanged:: 8.2
199
+ No longer a proxy for ``self.stdout``. Now has its own independent stream
200
+ that is mixing `<stdout>` and `<stderr>`, in the order they were written.
201
+ """
202
+ return self.output_bytes.decode(self.runner.charset, "replace").replace(
203
+ "\r\n", "\n"
204
+ )
205
+
206
+ @property
207
+ def stdout(self) -> str:
208
+ """The standard output as unicode string."""
209
+ return self.stdout_bytes.decode(self.runner.charset, "replace").replace(
210
+ "\r\n", "\n"
211
+ )
212
+
213
+ @property
214
+ def stderr(self) -> str:
215
+ """The standard error as unicode string.
216
+
217
+ .. versionchanged:: 8.2
218
+ No longer raise an exception, always returns the `<stderr>` string.
219
+ """
220
+ return self.stderr_bytes.decode(self.runner.charset, "replace").replace(
221
+ "\r\n", "\n"
222
+ )
223
+
224
+ def __repr__(self) -> str:
225
+ exc_str = repr(self.exception) if self.exception else "okay"
226
+ return f"<{type(self).__name__} {exc_str}>"
227
+
228
+
229
+ class CliRunner:
230
+ """The CLI runner provides functionality to invoke a Click command line
231
+ script for unittesting purposes in a isolated environment. This only
232
+ works in single-threaded systems without any concurrency as it changes the
233
+ global interpreter state.
234
+
235
+ :param charset: the character set for the input and output data.
236
+ :param env: a dictionary with environment variables for overriding.
237
+ :param echo_stdin: if this is set to `True`, then reading from `<stdin>` writes
238
+ to `<stdout>`. This is useful for showing examples in
239
+ some circumstances. Note that regular prompts
240
+ will automatically echo the input.
241
+ :param catch_exceptions: Whether to catch any exceptions other than
242
+ ``SystemExit`` when running :meth:`~CliRunner.invoke`.
243
+
244
+ .. versionchanged:: 8.2
245
+ Added the ``catch_exceptions`` parameter.
246
+
247
+ .. versionchanged:: 8.2
248
+ ``mix_stderr`` parameter has been removed.
249
+ """
250
+
251
+ def __init__(
252
+ self,
253
+ charset: str = "utf-8",
254
+ env: cabc.Mapping[str, str | None] | None = None,
255
+ echo_stdin: bool = False,
256
+ catch_exceptions: bool = True,
257
+ ) -> None:
258
+ self.charset = charset
259
+ self.env: cabc.Mapping[str, str | None] = env or {}
260
+ self.echo_stdin = echo_stdin
261
+ self.catch_exceptions = catch_exceptions
262
+
263
+ def get_default_prog_name(self, cli: Command) -> str:
264
+ """Given a command object it will return the default program name
265
+ for it. The default is the `name` attribute or ``"root"`` if not
266
+ set.
267
+ """
268
+ return cli.name or "root"
269
+
270
+ def make_env(
271
+ self, overrides: cabc.Mapping[str, str | None] | None = None
272
+ ) -> cabc.Mapping[str, str | None]:
273
+ """Returns the environment overrides for invoking a script."""
274
+ rv = dict(self.env)
275
+ if overrides:
276
+ rv.update(overrides)
277
+ return rv
278
+
279
+ @contextlib.contextmanager
280
+ def isolation(
281
+ self,
282
+ input: str | bytes | t.IO[t.Any] | None = None,
283
+ env: cabc.Mapping[str, str | None] | None = None,
284
+ color: bool = False,
285
+ ) -> cabc.Iterator[tuple[io.BytesIO, io.BytesIO, io.BytesIO]]:
286
+ """A context manager that sets up the isolation for invoking of a
287
+ command line tool. This sets up `<stdin>` with the given input data
288
+ and `os.environ` with the overrides from the given dictionary.
289
+ This also rebinds some internals in Click to be mocked (like the
290
+ prompt functionality).
291
+
292
+ This is automatically done in the :meth:`invoke` method.
293
+
294
+ :param input: the input stream to put into `sys.stdin`.
295
+ :param env: the environment overrides as dictionary.
296
+ :param color: whether the output should contain color codes. The
297
+ application can still override this explicitly.
298
+
299
+ .. versionadded:: 8.2
300
+ An additional output stream is returned, which is a mix of
301
+ `<stdout>` and `<stderr>` streams.
302
+
303
+ .. versionchanged:: 8.2
304
+ Always returns the `<stderr>` stream.
305
+
306
+ .. versionchanged:: 8.0
307
+ `<stderr>` is opened with ``errors="backslashreplace"``
308
+ instead of the default ``"strict"``.
309
+
310
+ .. versionchanged:: 4.0
311
+ Added the ``color`` parameter.
312
+ """
313
+ bytes_input = make_input_stream(input, self.charset)
314
+ echo_input = None
315
+
316
+ old_stdin = sys.stdin
317
+ old_stdout = sys.stdout
318
+ old_stderr = sys.stderr
319
+ old_forced_width = formatting.FORCED_WIDTH
320
+ formatting.FORCED_WIDTH = 80
321
+
322
+ env = self.make_env(env)
323
+
324
+ stream_mixer = StreamMixer()
325
+
326
+ if self.echo_stdin:
327
+ bytes_input = echo_input = t.cast(
328
+ t.BinaryIO, EchoingStdin(bytes_input, stream_mixer.stdout)
329
+ )
330
+
331
+ sys.stdin = text_input = _NamedTextIOWrapper(
332
+ bytes_input, encoding=self.charset, name="<stdin>", mode="r"
333
+ )
334
+
335
+ if self.echo_stdin:
336
+ # Force unbuffered reads, otherwise TextIOWrapper reads a
337
+ # large chunk which is echoed early.
338
+ text_input._CHUNK_SIZE = 1 # type: ignore
339
+
340
+ sys.stdout = _NamedTextIOWrapper(
341
+ stream_mixer.stdout, encoding=self.charset, name="<stdout>", mode="w"
342
+ )
343
+
344
+ sys.stderr = _NamedTextIOWrapper(
345
+ stream_mixer.stderr,
346
+ encoding=self.charset,
347
+ name="<stderr>",
348
+ mode="w",
349
+ errors="backslashreplace",
350
+ )
351
+
352
+ @_pause_echo(echo_input) # type: ignore
353
+ def visible_input(prompt: str | None = None) -> str:
354
+ sys.stdout.write(prompt or "")
355
+ try:
356
+ val = next(text_input).rstrip("\r\n")
357
+ except StopIteration as e:
358
+ raise EOFError() from e
359
+ sys.stdout.write(f"{val}\n")
360
+ sys.stdout.flush()
361
+ return val
362
+
363
+ @_pause_echo(echo_input) # type: ignore
364
+ def hidden_input(prompt: str | None = None) -> str:
365
+ sys.stdout.write(f"{prompt or ''}\n")
366
+ sys.stdout.flush()
367
+ try:
368
+ return next(text_input).rstrip("\r\n")
369
+ except StopIteration as e:
370
+ raise EOFError() from e
371
+
372
+ @_pause_echo(echo_input) # type: ignore
373
+ def _getchar(echo: bool) -> str:
374
+ char = sys.stdin.read(1)
375
+
376
+ if echo:
377
+ sys.stdout.write(char)
378
+
379
+ sys.stdout.flush()
380
+ return char
381
+
382
+ default_color = color
383
+
384
+ def should_strip_ansi(
385
+ stream: t.IO[t.Any] | None = None, color: bool | None = None
386
+ ) -> bool:
387
+ if color is None:
388
+ return not default_color
389
+ return not color
390
+
391
+ old_visible_prompt_func = termui.visible_prompt_func
392
+ old_hidden_prompt_func = termui.hidden_prompt_func
393
+ old__getchar_func = termui._getchar
394
+ old_should_strip_ansi = utils.should_strip_ansi # type: ignore
395
+ old__compat_should_strip_ansi = _compat.should_strip_ansi
396
+ termui.visible_prompt_func = visible_input
397
+ termui.hidden_prompt_func = hidden_input
398
+ termui._getchar = _getchar
399
+ utils.should_strip_ansi = should_strip_ansi # type: ignore
400
+ _compat.should_strip_ansi = should_strip_ansi
401
+
402
+ old_env = {}
403
+ try:
404
+ for key, value in env.items():
405
+ old_env[key] = os.environ.get(key)
406
+ if value is None:
407
+ try:
408
+ del os.environ[key]
409
+ except Exception:
410
+ pass
411
+ else:
412
+ os.environ[key] = value
413
+ yield (stream_mixer.stdout, stream_mixer.stderr, stream_mixer.output)
414
+ finally:
415
+ for key, value in old_env.items():
416
+ if value is None:
417
+ try:
418
+ del os.environ[key]
419
+ except Exception:
420
+ pass
421
+ else:
422
+ os.environ[key] = value
423
+ sys.stdout = old_stdout
424
+ sys.stderr = old_stderr
425
+ sys.stdin = old_stdin
426
+ termui.visible_prompt_func = old_visible_prompt_func
427
+ termui.hidden_prompt_func = old_hidden_prompt_func
428
+ termui._getchar = old__getchar_func
429
+ utils.should_strip_ansi = old_should_strip_ansi # type: ignore
430
+ _compat.should_strip_ansi = old__compat_should_strip_ansi
431
+ formatting.FORCED_WIDTH = old_forced_width
432
+
433
+ def invoke(
434
+ self,
435
+ cli: Command,
436
+ args: str | cabc.Sequence[str] | None = None,
437
+ input: str | bytes | t.IO[t.Any] | None = None,
438
+ env: cabc.Mapping[str, str | None] | None = None,
439
+ catch_exceptions: bool | None = None,
440
+ color: bool = False,
441
+ **extra: t.Any,
442
+ ) -> Result:
443
+ """Invokes a command in an isolated environment. The arguments are
444
+ forwarded directly to the command line script, the `extra` keyword
445
+ arguments are passed to the :meth:`~clickpkg.Command.main` function of
446
+ the command.
447
+
448
+ This returns a :class:`Result` object.
449
+
450
+ :param cli: the command to invoke
451
+ :param args: the arguments to invoke. It may be given as an iterable
452
+ or a string. When given as string it will be interpreted
453
+ as a Unix shell command. More details at
454
+ :func:`shlex.split`.
455
+ :param input: the input data for `sys.stdin`.
456
+ :param env: the environment overrides.
457
+ :param catch_exceptions: Whether to catch any other exceptions than
458
+ ``SystemExit``. If :data:`None`, the value
459
+ from :class:`CliRunner` is used.
460
+ :param extra: the keyword arguments to pass to :meth:`main`.
461
+ :param color: whether the output should contain color codes. The
462
+ application can still override this explicitly.
463
+
464
+ .. versionadded:: 8.2
465
+ The result object has the ``output_bytes`` attribute with
466
+ the mix of ``stdout_bytes`` and ``stderr_bytes``, as the user would
467
+ see it in its terminal.
468
+
469
+ .. versionchanged:: 8.2
470
+ The result object always returns the ``stderr_bytes`` stream.
471
+
472
+ .. versionchanged:: 8.0
473
+ The result object has the ``return_value`` attribute with
474
+ the value returned from the invoked command.
475
+
476
+ .. versionchanged:: 4.0
477
+ Added the ``color`` parameter.
478
+
479
+ .. versionchanged:: 3.0
480
+ Added the ``catch_exceptions`` parameter.
481
+
482
+ .. versionchanged:: 3.0
483
+ The result object has the ``exc_info`` attribute with the
484
+ traceback if available.
485
+ """
486
+ exc_info = None
487
+ if catch_exceptions is None:
488
+ catch_exceptions = self.catch_exceptions
489
+
490
+ with self.isolation(input=input, env=env, color=color) as outstreams:
491
+ return_value = None
492
+ exception: BaseException | None = None
493
+ exit_code = 0
494
+
495
+ if isinstance(args, str):
496
+ args = shlex.split(args)
497
+
498
+ try:
499
+ prog_name = extra.pop("prog_name")
500
+ except KeyError:
501
+ prog_name = self.get_default_prog_name(cli)
502
+
503
+ try:
504
+ return_value = cli.main(args=args or (), prog_name=prog_name, **extra)
505
+ except SystemExit as e:
506
+ exc_info = sys.exc_info()
507
+ e_code = t.cast("int | t.Any | None", e.code)
508
+
509
+ if e_code is None:
510
+ e_code = 0
511
+
512
+ if e_code != 0:
513
+ exception = e
514
+
515
+ if not isinstance(e_code, int):
516
+ sys.stdout.write(str(e_code))
517
+ sys.stdout.write("\n")
518
+ e_code = 1
519
+
520
+ exit_code = e_code
521
+
522
+ except Exception as e:
523
+ if not catch_exceptions:
524
+ raise
525
+ exception = e
526
+ exit_code = 1
527
+ exc_info = sys.exc_info()
528
+ finally:
529
+ sys.stdout.flush()
530
+ sys.stderr.flush()
531
+ stdout = outstreams[0].getvalue()
532
+ stderr = outstreams[1].getvalue()
533
+ output = outstreams[2].getvalue()
534
+
535
+ return Result(
536
+ runner=self,
537
+ stdout_bytes=stdout,
538
+ stderr_bytes=stderr,
539
+ output_bytes=output,
540
+ return_value=return_value,
541
+ exit_code=exit_code,
542
+ exception=exception,
543
+ exc_info=exc_info, # type: ignore
544
+ )
545
+
546
+ @contextlib.contextmanager
547
+ def isolated_filesystem(
548
+ self, temp_dir: str | os.PathLike[str] | None = None
549
+ ) -> cabc.Iterator[str]:
550
+ """A context manager that creates a temporary directory and
551
+ changes the current working directory to it. This isolates tests
552
+ that affect the contents of the CWD to prevent them from
553
+ interfering with each other.
554
+
555
+ :param temp_dir: Create the temporary directory under this
556
+ directory. If given, the created directory is not removed
557
+ when exiting.
558
+
559
+ .. versionchanged:: 8.0
560
+ Added the ``temp_dir`` parameter.
561
+ """
562
+ cwd = os.getcwd()
563
+ dt = tempfile.mkdtemp(dir=temp_dir)
564
+ os.chdir(dt)
565
+
566
+ try:
567
+ yield dt
568
+ finally:
569
+ os.chdir(cwd)
570
+
571
+ if temp_dir is None:
572
+ import shutil
573
+
574
+ try:
575
+ shutil.rmtree(dt)
576
+ except OSError:
577
+ pass
env/lib/python3.13/site-packages/click/types.py ADDED
@@ -0,0 +1,1209 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import collections.abc as cabc
4
+ import enum
5
+ import os
6
+ import stat
7
+ import sys
8
+ import typing as t
9
+ from datetime import datetime
10
+ from gettext import gettext as _
11
+ from gettext import ngettext
12
+
13
+ from ._compat import _get_argv_encoding
14
+ from ._compat import open_stream
15
+ from .exceptions import BadParameter
16
+ from .utils import format_filename
17
+ from .utils import LazyFile
18
+ from .utils import safecall
19
+
20
+ if t.TYPE_CHECKING:
21
+ import typing_extensions as te
22
+
23
+ from .core import Context
24
+ from .core import Parameter
25
+ from .shell_completion import CompletionItem
26
+
27
+ ParamTypeValue = t.TypeVar("ParamTypeValue")
28
+
29
+
30
+ class ParamType:
31
+ """Represents the type of a parameter. Validates and converts values
32
+ from the command line or Python into the correct type.
33
+
34
+ To implement a custom type, subclass and implement at least the
35
+ following:
36
+
37
+ - The :attr:`name` class attribute must be set.
38
+ - Calling an instance of the type with ``None`` must return
39
+ ``None``. This is already implemented by default.
40
+ - :meth:`convert` must convert string values to the correct type.
41
+ - :meth:`convert` must accept values that are already the correct
42
+ type.
43
+ - It must be able to convert a value if the ``ctx`` and ``param``
44
+ arguments are ``None``. This can occur when converting prompt
45
+ input.
46
+ """
47
+
48
+ is_composite: t.ClassVar[bool] = False
49
+ arity: t.ClassVar[int] = 1
50
+
51
+ #: the descriptive name of this type
52
+ name: str
53
+
54
+ #: if a list of this type is expected and the value is pulled from a
55
+ #: string environment variable, this is what splits it up. `None`
56
+ #: means any whitespace. For all parameters the general rule is that
57
+ #: whitespace splits them up. The exception are paths and files which
58
+ #: are split by ``os.path.pathsep`` by default (":" on Unix and ";" on
59
+ #: Windows).
60
+ envvar_list_splitter: t.ClassVar[str | None] = None
61
+
62
+ def to_info_dict(self) -> dict[str, t.Any]:
63
+ """Gather information that could be useful for a tool generating
64
+ user-facing documentation.
65
+
66
+ Use :meth:`click.Context.to_info_dict` to traverse the entire
67
+ CLI structure.
68
+
69
+ .. versionadded:: 8.0
70
+ """
71
+ # The class name without the "ParamType" suffix.
72
+ param_type = type(self).__name__.partition("ParamType")[0]
73
+ param_type = param_type.partition("ParameterType")[0]
74
+
75
+ # Custom subclasses might not remember to set a name.
76
+ if hasattr(self, "name"):
77
+ name = self.name
78
+ else:
79
+ name = param_type
80
+
81
+ return {"param_type": param_type, "name": name}
82
+
83
+ def __call__(
84
+ self,
85
+ value: t.Any,
86
+ param: Parameter | None = None,
87
+ ctx: Context | None = None,
88
+ ) -> t.Any:
89
+ if value is not None:
90
+ return self.convert(value, param, ctx)
91
+
92
+ def get_metavar(self, param: Parameter, ctx: Context) -> str | None:
93
+ """Returns the metavar default for this param if it provides one."""
94
+
95
+ def get_missing_message(self, param: Parameter, ctx: Context | None) -> str | None:
96
+ """Optionally might return extra information about a missing
97
+ parameter.
98
+
99
+ .. versionadded:: 2.0
100
+ """
101
+
102
+ def convert(
103
+ self, value: t.Any, param: Parameter | None, ctx: Context | None
104
+ ) -> t.Any:
105
+ """Convert the value to the correct type. This is not called if
106
+ the value is ``None`` (the missing value).
107
+
108
+ This must accept string values from the command line, as well as
109
+ values that are already the correct type. It may also convert
110
+ other compatible types.
111
+
112
+ The ``param`` and ``ctx`` arguments may be ``None`` in certain
113
+ situations, such as when converting prompt input.
114
+
115
+ If the value cannot be converted, call :meth:`fail` with a
116
+ descriptive message.
117
+
118
+ :param value: The value to convert.
119
+ :param param: The parameter that is using this type to convert
120
+ its value. May be ``None``.
121
+ :param ctx: The current context that arrived at this value. May
122
+ be ``None``.
123
+ """
124
+ return value
125
+
126
+ def split_envvar_value(self, rv: str) -> cabc.Sequence[str]:
127
+ """Given a value from an environment variable this splits it up
128
+ into small chunks depending on the defined envvar list splitter.
129
+
130
+ If the splitter is set to `None`, which means that whitespace splits,
131
+ then leading and trailing whitespace is ignored. Otherwise, leading
132
+ and trailing splitters usually lead to empty items being included.
133
+ """
134
+ return (rv or "").split(self.envvar_list_splitter)
135
+
136
+ def fail(
137
+ self,
138
+ message: str,
139
+ param: Parameter | None = None,
140
+ ctx: Context | None = None,
141
+ ) -> t.NoReturn:
142
+ """Helper method to fail with an invalid value message."""
143
+ raise BadParameter(message, ctx=ctx, param=param)
144
+
145
+ def shell_complete(
146
+ self, ctx: Context, param: Parameter, incomplete: str
147
+ ) -> list[CompletionItem]:
148
+ """Return a list of
149
+ :class:`~click.shell_completion.CompletionItem` objects for the
150
+ incomplete value. Most types do not provide completions, but
151
+ some do, and this allows custom types to provide custom
152
+ completions as well.
153
+
154
+ :param ctx: Invocation context for this command.
155
+ :param param: The parameter that is requesting completion.
156
+ :param incomplete: Value being completed. May be empty.
157
+
158
+ .. versionadded:: 8.0
159
+ """
160
+ return []
161
+
162
+
163
+ class CompositeParamType(ParamType):
164
+ is_composite = True
165
+
166
+ @property
167
+ def arity(self) -> int: # type: ignore
168
+ raise NotImplementedError()
169
+
170
+
171
+ class FuncParamType(ParamType):
172
+ def __init__(self, func: t.Callable[[t.Any], t.Any]) -> None:
173
+ self.name: str = func.__name__
174
+ self.func = func
175
+
176
+ def to_info_dict(self) -> dict[str, t.Any]:
177
+ info_dict = super().to_info_dict()
178
+ info_dict["func"] = self.func
179
+ return info_dict
180
+
181
+ def convert(
182
+ self, value: t.Any, param: Parameter | None, ctx: Context | None
183
+ ) -> t.Any:
184
+ try:
185
+ return self.func(value)
186
+ except ValueError:
187
+ try:
188
+ value = str(value)
189
+ except UnicodeError:
190
+ value = value.decode("utf-8", "replace")
191
+
192
+ self.fail(value, param, ctx)
193
+
194
+
195
+ class UnprocessedParamType(ParamType):
196
+ name = "text"
197
+
198
+ def convert(
199
+ self, value: t.Any, param: Parameter | None, ctx: Context | None
200
+ ) -> t.Any:
201
+ return value
202
+
203
+ def __repr__(self) -> str:
204
+ return "UNPROCESSED"
205
+
206
+
207
+ class StringParamType(ParamType):
208
+ name = "text"
209
+
210
+ def convert(
211
+ self, value: t.Any, param: Parameter | None, ctx: Context | None
212
+ ) -> t.Any:
213
+ if isinstance(value, bytes):
214
+ enc = _get_argv_encoding()
215
+ try:
216
+ value = value.decode(enc)
217
+ except UnicodeError:
218
+ fs_enc = sys.getfilesystemencoding()
219
+ if fs_enc != enc:
220
+ try:
221
+ value = value.decode(fs_enc)
222
+ except UnicodeError:
223
+ value = value.decode("utf-8", "replace")
224
+ else:
225
+ value = value.decode("utf-8", "replace")
226
+ return value
227
+ return str(value)
228
+
229
+ def __repr__(self) -> str:
230
+ return "STRING"
231
+
232
+
233
+ class Choice(ParamType, t.Generic[ParamTypeValue]):
234
+ """The choice type allows a value to be checked against a fixed set
235
+ of supported values.
236
+
237
+ You may pass any iterable value which will be converted to a tuple
238
+ and thus will only be iterated once.
239
+
240
+ The resulting value will always be one of the originally passed choices.
241
+ See :meth:`normalize_choice` for more info on the mapping of strings
242
+ to choices. See :ref:`choice-opts` for an example.
243
+
244
+ :param case_sensitive: Set to false to make choices case
245
+ insensitive. Defaults to true.
246
+
247
+ .. versionchanged:: 8.2.0
248
+ Non-``str`` ``choices`` are now supported. It can additionally be any
249
+ iterable. Before you were not recommended to pass anything but a list or
250
+ tuple.
251
+
252
+ .. versionadded:: 8.2.0
253
+ Choice normalization can be overridden via :meth:`normalize_choice`.
254
+ """
255
+
256
+ name = "choice"
257
+
258
+ def __init__(
259
+ self, choices: cabc.Iterable[ParamTypeValue], case_sensitive: bool = True
260
+ ) -> None:
261
+ self.choices: cabc.Sequence[ParamTypeValue] = tuple(choices)
262
+ self.case_sensitive = case_sensitive
263
+
264
+ def to_info_dict(self) -> dict[str, t.Any]:
265
+ info_dict = super().to_info_dict()
266
+ info_dict["choices"] = self.choices
267
+ info_dict["case_sensitive"] = self.case_sensitive
268
+ return info_dict
269
+
270
+ def _normalized_mapping(
271
+ self, ctx: Context | None = None
272
+ ) -> cabc.Mapping[ParamTypeValue, str]:
273
+ """
274
+ Returns mapping where keys are the original choices and the values are
275
+ the normalized values that are accepted via the command line.
276
+
277
+ This is a simple wrapper around :meth:`normalize_choice`, use that
278
+ instead which is supported.
279
+ """
280
+ return {
281
+ choice: self.normalize_choice(
282
+ choice=choice,
283
+ ctx=ctx,
284
+ )
285
+ for choice in self.choices
286
+ }
287
+
288
+ def normalize_choice(self, choice: ParamTypeValue, ctx: Context | None) -> str:
289
+ """
290
+ Normalize a choice value, used to map a passed string to a choice.
291
+ Each choice must have a unique normalized value.
292
+
293
+ By default uses :meth:`Context.token_normalize_func` and if not case
294
+ sensitive, convert it to a casefolded value.
295
+
296
+ .. versionadded:: 8.2.0
297
+ """
298
+ normed_value = choice.name if isinstance(choice, enum.Enum) else str(choice)
299
+
300
+ if ctx is not None and ctx.token_normalize_func is not None:
301
+ normed_value = ctx.token_normalize_func(normed_value)
302
+
303
+ if not self.case_sensitive:
304
+ normed_value = normed_value.casefold()
305
+
306
+ return normed_value
307
+
308
+ def get_metavar(self, param: Parameter, ctx: Context) -> str | None:
309
+ if param.param_type_name == "option" and not param.show_choices: # type: ignore
310
+ choice_metavars = [
311
+ convert_type(type(choice)).name.upper() for choice in self.choices
312
+ ]
313
+ choices_str = "|".join([*dict.fromkeys(choice_metavars)])
314
+ else:
315
+ choices_str = "|".join(
316
+ [str(i) for i in self._normalized_mapping(ctx=ctx).values()]
317
+ )
318
+
319
+ # Use curly braces to indicate a required argument.
320
+ if param.required and param.param_type_name == "argument":
321
+ return f"{{{choices_str}}}"
322
+
323
+ # Use square braces to indicate an option or optional argument.
324
+ return f"[{choices_str}]"
325
+
326
+ def get_missing_message(self, param: Parameter, ctx: Context | None) -> str:
327
+ """
328
+ Message shown when no choice is passed.
329
+
330
+ .. versionchanged:: 8.2.0 Added ``ctx`` argument.
331
+ """
332
+ return _("Choose from:\n\t{choices}").format(
333
+ choices=",\n\t".join(self._normalized_mapping(ctx=ctx).values())
334
+ )
335
+
336
+ def convert(
337
+ self, value: t.Any, param: Parameter | None, ctx: Context | None
338
+ ) -> ParamTypeValue:
339
+ """
340
+ For a given value from the parser, normalize it and find its
341
+ matching normalized value in the list of choices. Then return the
342
+ matched "original" choice.
343
+ """
344
+ normed_value = self.normalize_choice(choice=value, ctx=ctx)
345
+ normalized_mapping = self._normalized_mapping(ctx=ctx)
346
+
347
+ try:
348
+ return next(
349
+ original
350
+ for original, normalized in normalized_mapping.items()
351
+ if normalized == normed_value
352
+ )
353
+ except StopIteration:
354
+ self.fail(
355
+ self.get_invalid_choice_message(value=value, ctx=ctx),
356
+ param=param,
357
+ ctx=ctx,
358
+ )
359
+
360
+ def get_invalid_choice_message(self, value: t.Any, ctx: Context | None) -> str:
361
+ """Get the error message when the given choice is invalid.
362
+
363
+ :param value: The invalid value.
364
+
365
+ .. versionadded:: 8.2
366
+ """
367
+ choices_str = ", ".join(map(repr, self._normalized_mapping(ctx=ctx).values()))
368
+ return ngettext(
369
+ "{value!r} is not {choice}.",
370
+ "{value!r} is not one of {choices}.",
371
+ len(self.choices),
372
+ ).format(value=value, choice=choices_str, choices=choices_str)
373
+
374
+ def __repr__(self) -> str:
375
+ return f"Choice({list(self.choices)})"
376
+
377
+ def shell_complete(
378
+ self, ctx: Context, param: Parameter, incomplete: str
379
+ ) -> list[CompletionItem]:
380
+ """Complete choices that start with the incomplete value.
381
+
382
+ :param ctx: Invocation context for this command.
383
+ :param param: The parameter that is requesting completion.
384
+ :param incomplete: Value being completed. May be empty.
385
+
386
+ .. versionadded:: 8.0
387
+ """
388
+ from click.shell_completion import CompletionItem
389
+
390
+ str_choices = map(str, self.choices)
391
+
392
+ if self.case_sensitive:
393
+ matched = (c for c in str_choices if c.startswith(incomplete))
394
+ else:
395
+ incomplete = incomplete.lower()
396
+ matched = (c for c in str_choices if c.lower().startswith(incomplete))
397
+
398
+ return [CompletionItem(c) for c in matched]
399
+
400
+
401
+ class DateTime(ParamType):
402
+ """The DateTime type converts date strings into `datetime` objects.
403
+
404
+ The format strings which are checked are configurable, but default to some
405
+ common (non-timezone aware) ISO 8601 formats.
406
+
407
+ When specifying *DateTime* formats, you should only pass a list or a tuple.
408
+ Other iterables, like generators, may lead to surprising results.
409
+
410
+ The format strings are processed using ``datetime.strptime``, and this
411
+ consequently defines the format strings which are allowed.
412
+
413
+ Parsing is tried using each format, in order, and the first format which
414
+ parses successfully is used.
415
+
416
+ :param formats: A list or tuple of date format strings, in the order in
417
+ which they should be tried. Defaults to
418
+ ``'%Y-%m-%d'``, ``'%Y-%m-%dT%H:%M:%S'``,
419
+ ``'%Y-%m-%d %H:%M:%S'``.
420
+ """
421
+
422
+ name = "datetime"
423
+
424
+ def __init__(self, formats: cabc.Sequence[str] | None = None):
425
+ self.formats: cabc.Sequence[str] = formats or [
426
+ "%Y-%m-%d",
427
+ "%Y-%m-%dT%H:%M:%S",
428
+ "%Y-%m-%d %H:%M:%S",
429
+ ]
430
+
431
+ def to_info_dict(self) -> dict[str, t.Any]:
432
+ info_dict = super().to_info_dict()
433
+ info_dict["formats"] = self.formats
434
+ return info_dict
435
+
436
+ def get_metavar(self, param: Parameter, ctx: Context) -> str | None:
437
+ return f"[{'|'.join(self.formats)}]"
438
+
439
+ def _try_to_convert_date(self, value: t.Any, format: str) -> datetime | None:
440
+ try:
441
+ return datetime.strptime(value, format)
442
+ except ValueError:
443
+ return None
444
+
445
+ def convert(
446
+ self, value: t.Any, param: Parameter | None, ctx: Context | None
447
+ ) -> t.Any:
448
+ if isinstance(value, datetime):
449
+ return value
450
+
451
+ for format in self.formats:
452
+ converted = self._try_to_convert_date(value, format)
453
+
454
+ if converted is not None:
455
+ return converted
456
+
457
+ formats_str = ", ".join(map(repr, self.formats))
458
+ self.fail(
459
+ ngettext(
460
+ "{value!r} does not match the format {format}.",
461
+ "{value!r} does not match the formats {formats}.",
462
+ len(self.formats),
463
+ ).format(value=value, format=formats_str, formats=formats_str),
464
+ param,
465
+ ctx,
466
+ )
467
+
468
+ def __repr__(self) -> str:
469
+ return "DateTime"
470
+
471
+
472
+ class _NumberParamTypeBase(ParamType):
473
+ _number_class: t.ClassVar[type[t.Any]]
474
+
475
+ def convert(
476
+ self, value: t.Any, param: Parameter | None, ctx: Context | None
477
+ ) -> t.Any:
478
+ try:
479
+ return self._number_class(value)
480
+ except ValueError:
481
+ self.fail(
482
+ _("{value!r} is not a valid {number_type}.").format(
483
+ value=value, number_type=self.name
484
+ ),
485
+ param,
486
+ ctx,
487
+ )
488
+
489
+
490
+ class _NumberRangeBase(_NumberParamTypeBase):
491
+ def __init__(
492
+ self,
493
+ min: float | None = None,
494
+ max: float | None = None,
495
+ min_open: bool = False,
496
+ max_open: bool = False,
497
+ clamp: bool = False,
498
+ ) -> None:
499
+ self.min = min
500
+ self.max = max
501
+ self.min_open = min_open
502
+ self.max_open = max_open
503
+ self.clamp = clamp
504
+
505
+ def to_info_dict(self) -> dict[str, t.Any]:
506
+ info_dict = super().to_info_dict()
507
+ info_dict.update(
508
+ min=self.min,
509
+ max=self.max,
510
+ min_open=self.min_open,
511
+ max_open=self.max_open,
512
+ clamp=self.clamp,
513
+ )
514
+ return info_dict
515
+
516
+ def convert(
517
+ self, value: t.Any, param: Parameter | None, ctx: Context | None
518
+ ) -> t.Any:
519
+ import operator
520
+
521
+ rv = super().convert(value, param, ctx)
522
+ lt_min: bool = self.min is not None and (
523
+ operator.le if self.min_open else operator.lt
524
+ )(rv, self.min)
525
+ gt_max: bool = self.max is not None and (
526
+ operator.ge if self.max_open else operator.gt
527
+ )(rv, self.max)
528
+
529
+ if self.clamp:
530
+ if lt_min:
531
+ return self._clamp(self.min, 1, self.min_open) # type: ignore
532
+
533
+ if gt_max:
534
+ return self._clamp(self.max, -1, self.max_open) # type: ignore
535
+
536
+ if lt_min or gt_max:
537
+ self.fail(
538
+ _("{value} is not in the range {range}.").format(
539
+ value=rv, range=self._describe_range()
540
+ ),
541
+ param,
542
+ ctx,
543
+ )
544
+
545
+ return rv
546
+
547
+ def _clamp(self, bound: float, dir: t.Literal[1, -1], open: bool) -> float:
548
+ """Find the valid value to clamp to bound in the given
549
+ direction.
550
+
551
+ :param bound: The boundary value.
552
+ :param dir: 1 or -1 indicating the direction to move.
553
+ :param open: If true, the range does not include the bound.
554
+ """
555
+ raise NotImplementedError
556
+
557
+ def _describe_range(self) -> str:
558
+ """Describe the range for use in help text."""
559
+ if self.min is None:
560
+ op = "<" if self.max_open else "<="
561
+ return f"x{op}{self.max}"
562
+
563
+ if self.max is None:
564
+ op = ">" if self.min_open else ">="
565
+ return f"x{op}{self.min}"
566
+
567
+ lop = "<" if self.min_open else "<="
568
+ rop = "<" if self.max_open else "<="
569
+ return f"{self.min}{lop}x{rop}{self.max}"
570
+
571
+ def __repr__(self) -> str:
572
+ clamp = " clamped" if self.clamp else ""
573
+ return f"<{type(self).__name__} {self._describe_range()}{clamp}>"
574
+
575
+
576
+ class IntParamType(_NumberParamTypeBase):
577
+ name = "integer"
578
+ _number_class = int
579
+
580
+ def __repr__(self) -> str:
581
+ return "INT"
582
+
583
+
584
+ class IntRange(_NumberRangeBase, IntParamType):
585
+ """Restrict an :data:`click.INT` value to a range of accepted
586
+ values. See :ref:`ranges`.
587
+
588
+ If ``min`` or ``max`` are not passed, any value is accepted in that
589
+ direction. If ``min_open`` or ``max_open`` are enabled, the
590
+ corresponding boundary is not included in the range.
591
+
592
+ If ``clamp`` is enabled, a value outside the range is clamped to the
593
+ boundary instead of failing.
594
+
595
+ .. versionchanged:: 8.0
596
+ Added the ``min_open`` and ``max_open`` parameters.
597
+ """
598
+
599
+ name = "integer range"
600
+
601
+ def _clamp( # type: ignore
602
+ self, bound: int, dir: t.Literal[1, -1], open: bool
603
+ ) -> int:
604
+ if not open:
605
+ return bound
606
+
607
+ return bound + dir
608
+
609
+
610
+ class FloatParamType(_NumberParamTypeBase):
611
+ name = "float"
612
+ _number_class = float
613
+
614
+ def __repr__(self) -> str:
615
+ return "FLOAT"
616
+
617
+
618
+ class FloatRange(_NumberRangeBase, FloatParamType):
619
+ """Restrict a :data:`click.FLOAT` value to a range of accepted
620
+ values. See :ref:`ranges`.
621
+
622
+ If ``min`` or ``max`` are not passed, any value is accepted in that
623
+ direction. If ``min_open`` or ``max_open`` are enabled, the
624
+ corresponding boundary is not included in the range.
625
+
626
+ If ``clamp`` is enabled, a value outside the range is clamped to the
627
+ boundary instead of failing. This is not supported if either
628
+ boundary is marked ``open``.
629
+
630
+ .. versionchanged:: 8.0
631
+ Added the ``min_open`` and ``max_open`` parameters.
632
+ """
633
+
634
+ name = "float range"
635
+
636
+ def __init__(
637
+ self,
638
+ min: float | None = None,
639
+ max: float | None = None,
640
+ min_open: bool = False,
641
+ max_open: bool = False,
642
+ clamp: bool = False,
643
+ ) -> None:
644
+ super().__init__(
645
+ min=min, max=max, min_open=min_open, max_open=max_open, clamp=clamp
646
+ )
647
+
648
+ if (min_open or max_open) and clamp:
649
+ raise TypeError("Clamping is not supported for open bounds.")
650
+
651
+ def _clamp(self, bound: float, dir: t.Literal[1, -1], open: bool) -> float:
652
+ if not open:
653
+ return bound
654
+
655
+ # Could use math.nextafter here, but clamping an
656
+ # open float range doesn't seem to be particularly useful. It's
657
+ # left up to the user to write a callback to do it if needed.
658
+ raise RuntimeError("Clamping is not supported for open bounds.")
659
+
660
+
661
+ class BoolParamType(ParamType):
662
+ name = "boolean"
663
+
664
+ bool_states: dict[str, bool] = {
665
+ "1": True,
666
+ "0": False,
667
+ "yes": True,
668
+ "no": False,
669
+ "true": True,
670
+ "false": False,
671
+ "on": True,
672
+ "off": False,
673
+ "t": True,
674
+ "f": False,
675
+ "y": True,
676
+ "n": False,
677
+ # Absence of value is considered False.
678
+ "": False,
679
+ }
680
+ """A mapping of string values to boolean states.
681
+
682
+ Mapping is inspired by :py:attr:`configparser.ConfigParser.BOOLEAN_STATES`
683
+ and extends it.
684
+
685
+ .. caution::
686
+ String values are lower-cased, as the ``str_to_bool`` comparison function
687
+ below is case-insensitive.
688
+
689
+ .. warning::
690
+ The mapping is not exhaustive, and does not cover all possible boolean strings
691
+ representations. It will remains as it is to avoid endless bikeshedding.
692
+
693
+ Future work my be considered to make this mapping user-configurable from public
694
+ API.
695
+ """
696
+
697
+ @staticmethod
698
+ def str_to_bool(value: str | bool) -> bool | None:
699
+ """Convert a string to a boolean value.
700
+
701
+ If the value is already a boolean, it is returned as-is. If the value is a
702
+ string, it is stripped of whitespaces and lower-cased, then checked against
703
+ the known boolean states pre-defined in the `BoolParamType.bool_states` mapping
704
+ above.
705
+
706
+ Returns `None` if the value does not match any known boolean state.
707
+ """
708
+ if isinstance(value, bool):
709
+ return value
710
+ return BoolParamType.bool_states.get(value.strip().lower())
711
+
712
+ def convert(
713
+ self, value: t.Any, param: Parameter | None, ctx: Context | None
714
+ ) -> bool:
715
+ normalized = self.str_to_bool(value)
716
+ if normalized is None:
717
+ self.fail(
718
+ _(
719
+ "{value!r} is not a valid boolean. Recognized values: {states}"
720
+ ).format(value=value, states=", ".join(sorted(self.bool_states))),
721
+ param,
722
+ ctx,
723
+ )
724
+ return normalized
725
+
726
+ def __repr__(self) -> str:
727
+ return "BOOL"
728
+
729
+
730
+ class UUIDParameterType(ParamType):
731
+ name = "uuid"
732
+
733
+ def convert(
734
+ self, value: t.Any, param: Parameter | None, ctx: Context | None
735
+ ) -> t.Any:
736
+ import uuid
737
+
738
+ if isinstance(value, uuid.UUID):
739
+ return value
740
+
741
+ value = value.strip()
742
+
743
+ try:
744
+ return uuid.UUID(value)
745
+ except ValueError:
746
+ self.fail(
747
+ _("{value!r} is not a valid UUID.").format(value=value), param, ctx
748
+ )
749
+
750
+ def __repr__(self) -> str:
751
+ return "UUID"
752
+
753
+
754
+ class File(ParamType):
755
+ """Declares a parameter to be a file for reading or writing. The file
756
+ is automatically closed once the context tears down (after the command
757
+ finished working).
758
+
759
+ Files can be opened for reading or writing. The special value ``-``
760
+ indicates stdin or stdout depending on the mode.
761
+
762
+ By default, the file is opened for reading text data, but it can also be
763
+ opened in binary mode or for writing. The encoding parameter can be used
764
+ to force a specific encoding.
765
+
766
+ The `lazy` flag controls if the file should be opened immediately or upon
767
+ first IO. The default is to be non-lazy for standard input and output
768
+ streams as well as files opened for reading, `lazy` otherwise. When opening a
769
+ file lazily for reading, it is still opened temporarily for validation, but
770
+ will not be held open until first IO. lazy is mainly useful when opening
771
+ for writing to avoid creating the file until it is needed.
772
+
773
+ Files can also be opened atomically in which case all writes go into a
774
+ separate file in the same folder and upon completion the file will
775
+ be moved over to the original location. This is useful if a file
776
+ regularly read by other users is modified.
777
+
778
+ See :ref:`file-args` for more information.
779
+
780
+ .. versionchanged:: 2.0
781
+ Added the ``atomic`` parameter.
782
+ """
783
+
784
+ name = "filename"
785
+ envvar_list_splitter: t.ClassVar[str] = os.path.pathsep
786
+
787
+ def __init__(
788
+ self,
789
+ mode: str = "r",
790
+ encoding: str | None = None,
791
+ errors: str | None = "strict",
792
+ lazy: bool | None = None,
793
+ atomic: bool = False,
794
+ ) -> None:
795
+ self.mode = mode
796
+ self.encoding = encoding
797
+ self.errors = errors
798
+ self.lazy = lazy
799
+ self.atomic = atomic
800
+
801
+ def to_info_dict(self) -> dict[str, t.Any]:
802
+ info_dict = super().to_info_dict()
803
+ info_dict.update(mode=self.mode, encoding=self.encoding)
804
+ return info_dict
805
+
806
+ def resolve_lazy_flag(self, value: str | os.PathLike[str]) -> bool:
807
+ if self.lazy is not None:
808
+ return self.lazy
809
+ if os.fspath(value) == "-":
810
+ return False
811
+ elif "w" in self.mode:
812
+ return True
813
+ return False
814
+
815
+ def convert(
816
+ self,
817
+ value: str | os.PathLike[str] | t.IO[t.Any],
818
+ param: Parameter | None,
819
+ ctx: Context | None,
820
+ ) -> t.IO[t.Any]:
821
+ if _is_file_like(value):
822
+ return value
823
+
824
+ value = t.cast("str | os.PathLike[str]", value)
825
+
826
+ try:
827
+ lazy = self.resolve_lazy_flag(value)
828
+
829
+ if lazy:
830
+ lf = LazyFile(
831
+ value, self.mode, self.encoding, self.errors, atomic=self.atomic
832
+ )
833
+
834
+ if ctx is not None:
835
+ ctx.call_on_close(lf.close_intelligently)
836
+
837
+ return t.cast("t.IO[t.Any]", lf)
838
+
839
+ f, should_close = open_stream(
840
+ value, self.mode, self.encoding, self.errors, atomic=self.atomic
841
+ )
842
+
843
+ # If a context is provided, we automatically close the file
844
+ # at the end of the context execution (or flush out). If a
845
+ # context does not exist, it's the caller's responsibility to
846
+ # properly close the file. This for instance happens when the
847
+ # type is used with prompts.
848
+ if ctx is not None:
849
+ if should_close:
850
+ ctx.call_on_close(safecall(f.close))
851
+ else:
852
+ ctx.call_on_close(safecall(f.flush))
853
+
854
+ return f
855
+ except OSError as e:
856
+ self.fail(f"'{format_filename(value)}': {e.strerror}", param, ctx)
857
+
858
+ def shell_complete(
859
+ self, ctx: Context, param: Parameter, incomplete: str
860
+ ) -> list[CompletionItem]:
861
+ """Return a special completion marker that tells the completion
862
+ system to use the shell to provide file path completions.
863
+
864
+ :param ctx: Invocation context for this command.
865
+ :param param: The parameter that is requesting completion.
866
+ :param incomplete: Value being completed. May be empty.
867
+
868
+ .. versionadded:: 8.0
869
+ """
870
+ from click.shell_completion import CompletionItem
871
+
872
+ return [CompletionItem(incomplete, type="file")]
873
+
874
+
875
+ def _is_file_like(value: t.Any) -> te.TypeGuard[t.IO[t.Any]]:
876
+ return hasattr(value, "read") or hasattr(value, "write")
877
+
878
+
879
+ class Path(ParamType):
880
+ """The ``Path`` type is similar to the :class:`File` type, but
881
+ returns the filename instead of an open file. Various checks can be
882
+ enabled to validate the type of file and permissions.
883
+
884
+ :param exists: The file or directory needs to exist for the value to
885
+ be valid. If this is not set to ``True``, and the file does not
886
+ exist, then all further checks are silently skipped.
887
+ :param file_okay: Allow a file as a value.
888
+ :param dir_okay: Allow a directory as a value.
889
+ :param readable: if true, a readable check is performed.
890
+ :param writable: if true, a writable check is performed.
891
+ :param executable: if true, an executable check is performed.
892
+ :param resolve_path: Make the value absolute and resolve any
893
+ symlinks. A ``~`` is not expanded, as this is supposed to be
894
+ done by the shell only.
895
+ :param allow_dash: Allow a single dash as a value, which indicates
896
+ a standard stream (but does not open it). Use
897
+ :func:`~click.open_file` to handle opening this value.
898
+ :param path_type: Convert the incoming path value to this type. If
899
+ ``None``, keep Python's default, which is ``str``. Useful to
900
+ convert to :class:`pathlib.Path`.
901
+
902
+ .. versionchanged:: 8.1
903
+ Added the ``executable`` parameter.
904
+
905
+ .. versionchanged:: 8.0
906
+ Allow passing ``path_type=pathlib.Path``.
907
+
908
+ .. versionchanged:: 6.0
909
+ Added the ``allow_dash`` parameter.
910
+ """
911
+
912
+ envvar_list_splitter: t.ClassVar[str] = os.path.pathsep
913
+
914
+ def __init__(
915
+ self,
916
+ exists: bool = False,
917
+ file_okay: bool = True,
918
+ dir_okay: bool = True,
919
+ writable: bool = False,
920
+ readable: bool = True,
921
+ resolve_path: bool = False,
922
+ allow_dash: bool = False,
923
+ path_type: type[t.Any] | None = None,
924
+ executable: bool = False,
925
+ ):
926
+ self.exists = exists
927
+ self.file_okay = file_okay
928
+ self.dir_okay = dir_okay
929
+ self.readable = readable
930
+ self.writable = writable
931
+ self.executable = executable
932
+ self.resolve_path = resolve_path
933
+ self.allow_dash = allow_dash
934
+ self.type = path_type
935
+
936
+ if self.file_okay and not self.dir_okay:
937
+ self.name: str = _("file")
938
+ elif self.dir_okay and not self.file_okay:
939
+ self.name = _("directory")
940
+ else:
941
+ self.name = _("path")
942
+
943
+ def to_info_dict(self) -> dict[str, t.Any]:
944
+ info_dict = super().to_info_dict()
945
+ info_dict.update(
946
+ exists=self.exists,
947
+ file_okay=self.file_okay,
948
+ dir_okay=self.dir_okay,
949
+ writable=self.writable,
950
+ readable=self.readable,
951
+ allow_dash=self.allow_dash,
952
+ )
953
+ return info_dict
954
+
955
+ def coerce_path_result(
956
+ self, value: str | os.PathLike[str]
957
+ ) -> str | bytes | os.PathLike[str]:
958
+ if self.type is not None and not isinstance(value, self.type):
959
+ if self.type is str:
960
+ return os.fsdecode(value)
961
+ elif self.type is bytes:
962
+ return os.fsencode(value)
963
+ else:
964
+ return t.cast("os.PathLike[str]", self.type(value))
965
+
966
+ return value
967
+
968
+ def convert(
969
+ self,
970
+ value: str | os.PathLike[str],
971
+ param: Parameter | None,
972
+ ctx: Context | None,
973
+ ) -> str | bytes | os.PathLike[str]:
974
+ rv = value
975
+
976
+ is_dash = self.file_okay and self.allow_dash and rv in (b"-", "-")
977
+
978
+ if not is_dash:
979
+ if self.resolve_path:
980
+ rv = os.path.realpath(rv)
981
+
982
+ try:
983
+ st = os.stat(rv)
984
+ except OSError:
985
+ if not self.exists:
986
+ return self.coerce_path_result(rv)
987
+ self.fail(
988
+ _("{name} {filename!r} does not exist.").format(
989
+ name=self.name.title(), filename=format_filename(value)
990
+ ),
991
+ param,
992
+ ctx,
993
+ )
994
+
995
+ if not self.file_okay and stat.S_ISREG(st.st_mode):
996
+ self.fail(
997
+ _("{name} {filename!r} is a file.").format(
998
+ name=self.name.title(), filename=format_filename(value)
999
+ ),
1000
+ param,
1001
+ ctx,
1002
+ )
1003
+ if not self.dir_okay and stat.S_ISDIR(st.st_mode):
1004
+ self.fail(
1005
+ _("{name} {filename!r} is a directory.").format(
1006
+ name=self.name.title(), filename=format_filename(value)
1007
+ ),
1008
+ param,
1009
+ ctx,
1010
+ )
1011
+
1012
+ if self.readable and not os.access(rv, os.R_OK):
1013
+ self.fail(
1014
+ _("{name} {filename!r} is not readable.").format(
1015
+ name=self.name.title(), filename=format_filename(value)
1016
+ ),
1017
+ param,
1018
+ ctx,
1019
+ )
1020
+
1021
+ if self.writable and not os.access(rv, os.W_OK):
1022
+ self.fail(
1023
+ _("{name} {filename!r} is not writable.").format(
1024
+ name=self.name.title(), filename=format_filename(value)
1025
+ ),
1026
+ param,
1027
+ ctx,
1028
+ )
1029
+
1030
+ if self.executable and not os.access(value, os.X_OK):
1031
+ self.fail(
1032
+ _("{name} {filename!r} is not executable.").format(
1033
+ name=self.name.title(), filename=format_filename(value)
1034
+ ),
1035
+ param,
1036
+ ctx,
1037
+ )
1038
+
1039
+ return self.coerce_path_result(rv)
1040
+
1041
+ def shell_complete(
1042
+ self, ctx: Context, param: Parameter, incomplete: str
1043
+ ) -> list[CompletionItem]:
1044
+ """Return a special completion marker that tells the completion
1045
+ system to use the shell to provide path completions for only
1046
+ directories or any paths.
1047
+
1048
+ :param ctx: Invocation context for this command.
1049
+ :param param: The parameter that is requesting completion.
1050
+ :param incomplete: Value being completed. May be empty.
1051
+
1052
+ .. versionadded:: 8.0
1053
+ """
1054
+ from click.shell_completion import CompletionItem
1055
+
1056
+ type = "dir" if self.dir_okay and not self.file_okay else "file"
1057
+ return [CompletionItem(incomplete, type=type)]
1058
+
1059
+
1060
+ class Tuple(CompositeParamType):
1061
+ """The default behavior of Click is to apply a type on a value directly.
1062
+ This works well in most cases, except for when `nargs` is set to a fixed
1063
+ count and different types should be used for different items. In this
1064
+ case the :class:`Tuple` type can be used. This type can only be used
1065
+ if `nargs` is set to a fixed number.
1066
+
1067
+ For more information see :ref:`tuple-type`.
1068
+
1069
+ This can be selected by using a Python tuple literal as a type.
1070
+
1071
+ :param types: a list of types that should be used for the tuple items.
1072
+ """
1073
+
1074
+ def __init__(self, types: cabc.Sequence[type[t.Any] | ParamType]) -> None:
1075
+ self.types: cabc.Sequence[ParamType] = [convert_type(ty) for ty in types]
1076
+
1077
+ def to_info_dict(self) -> dict[str, t.Any]:
1078
+ info_dict = super().to_info_dict()
1079
+ info_dict["types"] = [t.to_info_dict() for t in self.types]
1080
+ return info_dict
1081
+
1082
+ @property
1083
+ def name(self) -> str: # type: ignore
1084
+ return f"<{' '.join(ty.name for ty in self.types)}>"
1085
+
1086
+ @property
1087
+ def arity(self) -> int: # type: ignore
1088
+ return len(self.types)
1089
+
1090
+ def convert(
1091
+ self, value: t.Any, param: Parameter | None, ctx: Context | None
1092
+ ) -> t.Any:
1093
+ len_type = len(self.types)
1094
+ len_value = len(value)
1095
+
1096
+ if len_value != len_type:
1097
+ self.fail(
1098
+ ngettext(
1099
+ "{len_type} values are required, but {len_value} was given.",
1100
+ "{len_type} values are required, but {len_value} were given.",
1101
+ len_value,
1102
+ ).format(len_type=len_type, len_value=len_value),
1103
+ param=param,
1104
+ ctx=ctx,
1105
+ )
1106
+
1107
+ return tuple(
1108
+ ty(x, param, ctx) for ty, x in zip(self.types, value, strict=False)
1109
+ )
1110
+
1111
+
1112
+ def convert_type(ty: t.Any | None, default: t.Any | None = None) -> ParamType:
1113
+ """Find the most appropriate :class:`ParamType` for the given Python
1114
+ type. If the type isn't provided, it can be inferred from a default
1115
+ value.
1116
+ """
1117
+ guessed_type = False
1118
+
1119
+ if ty is None and default is not None:
1120
+ if isinstance(default, (tuple, list)):
1121
+ # If the default is empty, ty will remain None and will
1122
+ # return STRING.
1123
+ if default:
1124
+ item = default[0]
1125
+
1126
+ # A tuple of tuples needs to detect the inner types.
1127
+ # Can't call convert recursively because that would
1128
+ # incorrectly unwind the tuple to a single type.
1129
+ if isinstance(item, (tuple, list)):
1130
+ ty = tuple(map(type, item))
1131
+ else:
1132
+ ty = type(item)
1133
+ else:
1134
+ ty = type(default)
1135
+
1136
+ guessed_type = True
1137
+
1138
+ if isinstance(ty, tuple):
1139
+ return Tuple(ty)
1140
+
1141
+ if isinstance(ty, ParamType):
1142
+ return ty
1143
+
1144
+ if ty is str or ty is None:
1145
+ return STRING
1146
+
1147
+ if ty is int:
1148
+ return INT
1149
+
1150
+ if ty is float:
1151
+ return FLOAT
1152
+
1153
+ if ty is bool:
1154
+ return BOOL
1155
+
1156
+ if guessed_type:
1157
+ return STRING
1158
+
1159
+ if __debug__:
1160
+ try:
1161
+ if issubclass(ty, ParamType):
1162
+ raise AssertionError(
1163
+ f"Attempted to use an uninstantiated parameter type ({ty})."
1164
+ )
1165
+ except TypeError:
1166
+ # ty is an instance (correct), so issubclass fails.
1167
+ pass
1168
+
1169
+ return FuncParamType(ty)
1170
+
1171
+
1172
+ #: A dummy parameter type that just does nothing. From a user's
1173
+ #: perspective this appears to just be the same as `STRING` but
1174
+ #: internally no string conversion takes place if the input was bytes.
1175
+ #: This is usually useful when working with file paths as they can
1176
+ #: appear in bytes and unicode.
1177
+ #:
1178
+ #: For path related uses the :class:`Path` type is a better choice but
1179
+ #: there are situations where an unprocessed type is useful which is why
1180
+ #: it is is provided.
1181
+ #:
1182
+ #: .. versionadded:: 4.0
1183
+ UNPROCESSED = UnprocessedParamType()
1184
+
1185
+ #: A unicode string parameter type which is the implicit default. This
1186
+ #: can also be selected by using ``str`` as type.
1187
+ STRING = StringParamType()
1188
+
1189
+ #: An integer parameter. This can also be selected by using ``int`` as
1190
+ #: type.
1191
+ INT = IntParamType()
1192
+
1193
+ #: A floating point value parameter. This can also be selected by using
1194
+ #: ``float`` as type.
1195
+ FLOAT = FloatParamType()
1196
+
1197
+ #: A boolean parameter. This is the default for boolean flags. This can
1198
+ #: also be selected by using ``bool`` as a type.
1199
+ BOOL = BoolParamType()
1200
+
1201
+ #: A UUID parameter.
1202
+ UUID = UUIDParameterType()
1203
+
1204
+
1205
+ class OptionHelpExtra(t.TypedDict, total=False):
1206
+ envvars: tuple[str, ...]
1207
+ default: str
1208
+ range: str
1209
+ required: str
env/lib/python3.13/site-packages/click/utils.py ADDED
@@ -0,0 +1,627 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import collections.abc as cabc
4
+ import os
5
+ import re
6
+ import sys
7
+ import typing as t
8
+ from functools import update_wrapper
9
+ from types import ModuleType
10
+ from types import TracebackType
11
+
12
+ from ._compat import _default_text_stderr
13
+ from ._compat import _default_text_stdout
14
+ from ._compat import _find_binary_writer
15
+ from ._compat import auto_wrap_for_ansi
16
+ from ._compat import binary_streams
17
+ from ._compat import open_stream
18
+ from ._compat import should_strip_ansi
19
+ from ._compat import strip_ansi
20
+ from ._compat import text_streams
21
+ from ._compat import WIN
22
+ from .globals import resolve_color_default
23
+
24
+ if t.TYPE_CHECKING:
25
+ import typing_extensions as te
26
+
27
+ P = te.ParamSpec("P")
28
+
29
+ R = t.TypeVar("R")
30
+
31
+
32
+ def _posixify(name: str) -> str:
33
+ return "-".join(name.split()).lower()
34
+
35
+
36
+ def safecall(func: t.Callable[P, R]) -> t.Callable[P, R | None]:
37
+ """Wraps a function so that it swallows exceptions."""
38
+
39
+ def wrapper(*args: P.args, **kwargs: P.kwargs) -> R | None:
40
+ try:
41
+ return func(*args, **kwargs)
42
+ except Exception:
43
+ pass
44
+ return None
45
+
46
+ return update_wrapper(wrapper, func)
47
+
48
+
49
+ def make_str(value: t.Any) -> str:
50
+ """Converts a value into a valid string."""
51
+ if isinstance(value, bytes):
52
+ try:
53
+ return value.decode(sys.getfilesystemencoding())
54
+ except UnicodeError:
55
+ return value.decode("utf-8", "replace")
56
+ return str(value)
57
+
58
+
59
+ def make_default_short_help(help: str, max_length: int = 45) -> str:
60
+ """Returns a condensed version of help string."""
61
+ # Consider only the first paragraph.
62
+ paragraph_end = help.find("\n\n")
63
+
64
+ if paragraph_end != -1:
65
+ help = help[:paragraph_end]
66
+
67
+ # Collapse newlines, tabs, and spaces.
68
+ words = help.split()
69
+
70
+ if not words:
71
+ return ""
72
+
73
+ # The first paragraph started with a "no rewrap" marker, ignore it.
74
+ if words[0] == "\b":
75
+ words = words[1:]
76
+
77
+ total_length = 0
78
+ last_index = len(words) - 1
79
+
80
+ for i, word in enumerate(words):
81
+ total_length += len(word) + (i > 0)
82
+
83
+ if total_length > max_length: # too long, truncate
84
+ break
85
+
86
+ if word[-1] == ".": # sentence end, truncate without "..."
87
+ return " ".join(words[: i + 1])
88
+
89
+ if total_length == max_length and i != last_index:
90
+ break # not at sentence end, truncate with "..."
91
+ else:
92
+ return " ".join(words) # no truncation needed
93
+
94
+ # Account for the length of the suffix.
95
+ total_length += len("...")
96
+
97
+ # remove words until the length is short enough
98
+ while i > 0:
99
+ total_length -= len(words[i]) + (i > 0)
100
+
101
+ if total_length <= max_length:
102
+ break
103
+
104
+ i -= 1
105
+
106
+ return " ".join(words[:i]) + "..."
107
+
108
+
109
+ class LazyFile:
110
+ """A lazy file works like a regular file but it does not fully open
111
+ the file but it does perform some basic checks early to see if the
112
+ filename parameter does make sense. This is useful for safely opening
113
+ files for writing.
114
+ """
115
+
116
+ def __init__(
117
+ self,
118
+ filename: str | os.PathLike[str],
119
+ mode: str = "r",
120
+ encoding: str | None = None,
121
+ errors: str | None = "strict",
122
+ atomic: bool = False,
123
+ ):
124
+ self.name: str = os.fspath(filename)
125
+ self.mode = mode
126
+ self.encoding = encoding
127
+ self.errors = errors
128
+ self.atomic = atomic
129
+ self._f: t.IO[t.Any] | None
130
+ self.should_close: bool
131
+
132
+ if self.name == "-":
133
+ self._f, self.should_close = open_stream(filename, mode, encoding, errors)
134
+ else:
135
+ if "r" in mode:
136
+ # Open and close the file in case we're opening it for
137
+ # reading so that we can catch at least some errors in
138
+ # some cases early.
139
+ open(filename, mode).close()
140
+ self._f = None
141
+ self.should_close = True
142
+
143
+ def __getattr__(self, name: str) -> t.Any:
144
+ return getattr(self.open(), name)
145
+
146
+ def __repr__(self) -> str:
147
+ if self._f is not None:
148
+ return repr(self._f)
149
+ return f"<unopened file '{format_filename(self.name)}' {self.mode}>"
150
+
151
+ def open(self) -> t.IO[t.Any]:
152
+ """Opens the file if it's not yet open. This call might fail with
153
+ a :exc:`FileError`. Not handling this error will produce an error
154
+ that Click shows.
155
+ """
156
+ if self._f is not None:
157
+ return self._f
158
+ try:
159
+ rv, self.should_close = open_stream(
160
+ self.name, self.mode, self.encoding, self.errors, atomic=self.atomic
161
+ )
162
+ except OSError as e:
163
+ from .exceptions import FileError
164
+
165
+ raise FileError(self.name, hint=e.strerror) from e
166
+ self._f = rv
167
+ return rv
168
+
169
+ def close(self) -> None:
170
+ """Closes the underlying file, no matter what."""
171
+ if self._f is not None:
172
+ self._f.close()
173
+
174
+ def close_intelligently(self) -> None:
175
+ """This function only closes the file if it was opened by the lazy
176
+ file wrapper. For instance this will never close stdin.
177
+ """
178
+ if self.should_close:
179
+ self.close()
180
+
181
+ def __enter__(self) -> LazyFile:
182
+ return self
183
+
184
+ def __exit__(
185
+ self,
186
+ exc_type: type[BaseException] | None,
187
+ exc_value: BaseException | None,
188
+ tb: TracebackType | None,
189
+ ) -> None:
190
+ self.close_intelligently()
191
+
192
+ def __iter__(self) -> cabc.Iterator[t.AnyStr]:
193
+ self.open()
194
+ return iter(self._f) # type: ignore
195
+
196
+
197
+ class KeepOpenFile:
198
+ def __init__(self, file: t.IO[t.Any]) -> None:
199
+ self._file: t.IO[t.Any] = file
200
+
201
+ def __getattr__(self, name: str) -> t.Any:
202
+ return getattr(self._file, name)
203
+
204
+ def __enter__(self) -> KeepOpenFile:
205
+ return self
206
+
207
+ def __exit__(
208
+ self,
209
+ exc_type: type[BaseException] | None,
210
+ exc_value: BaseException | None,
211
+ tb: TracebackType | None,
212
+ ) -> None:
213
+ pass
214
+
215
+ def __repr__(self) -> str:
216
+ return repr(self._file)
217
+
218
+ def __iter__(self) -> cabc.Iterator[t.AnyStr]:
219
+ return iter(self._file)
220
+
221
+
222
+ def echo(
223
+ message: t.Any | None = None,
224
+ file: t.IO[t.Any] | None = None,
225
+ nl: bool = True,
226
+ err: bool = False,
227
+ color: bool | None = None,
228
+ ) -> None:
229
+ """Print a message and newline to stdout or a file. This should be
230
+ used instead of :func:`print` because it provides better support
231
+ for different data, files, and environments.
232
+
233
+ Compared to :func:`print`, this does the following:
234
+
235
+ - Ensures that the output encoding is not misconfigured on Linux.
236
+ - Supports Unicode in the Windows console.
237
+ - Supports writing to binary outputs, and supports writing bytes
238
+ to text outputs.
239
+ - Supports colors and styles on Windows.
240
+ - Removes ANSI color and style codes if the output does not look
241
+ like an interactive terminal.
242
+ - Always flushes the output.
243
+
244
+ :param message: The string or bytes to output. Other objects are
245
+ converted to strings.
246
+ :param file: The file to write to. Defaults to ``stdout``.
247
+ :param err: Write to ``stderr`` instead of ``stdout``.
248
+ :param nl: Print a newline after the message. Enabled by default.
249
+ :param color: Force showing or hiding colors and other styles. By
250
+ default Click will remove color if the output does not look like
251
+ an interactive terminal.
252
+
253
+ .. versionchanged:: 6.0
254
+ Support Unicode output on the Windows console. Click does not
255
+ modify ``sys.stdout``, so ``sys.stdout.write()`` and ``print()``
256
+ will still not support Unicode.
257
+
258
+ .. versionchanged:: 4.0
259
+ Added the ``color`` parameter.
260
+
261
+ .. versionadded:: 3.0
262
+ Added the ``err`` parameter.
263
+
264
+ .. versionchanged:: 2.0
265
+ Support colors on Windows if colorama is installed.
266
+ """
267
+ if file is None:
268
+ if err:
269
+ file = _default_text_stderr()
270
+ else:
271
+ file = _default_text_stdout()
272
+
273
+ # There are no standard streams attached to write to. For example,
274
+ # pythonw on Windows.
275
+ if file is None:
276
+ return
277
+
278
+ # Convert non bytes/text into the native string type.
279
+ if message is not None and not isinstance(message, (str, bytes, bytearray)):
280
+ out: str | bytes | bytearray | None = str(message)
281
+ else:
282
+ out = message
283
+
284
+ if nl:
285
+ out = out or ""
286
+ if isinstance(out, str):
287
+ out += "\n"
288
+ else:
289
+ out += b"\n"
290
+
291
+ if not out:
292
+ file.flush()
293
+ return
294
+
295
+ # If there is a message and the value looks like bytes, we manually
296
+ # need to find the binary stream and write the message in there.
297
+ # This is done separately so that most stream types will work as you
298
+ # would expect. Eg: you can write to StringIO for other cases.
299
+ if isinstance(out, (bytes, bytearray)):
300
+ binary_file = _find_binary_writer(file)
301
+
302
+ if binary_file is not None:
303
+ file.flush()
304
+ binary_file.write(out)
305
+ binary_file.flush()
306
+ return
307
+
308
+ # ANSI style code support. For no message or bytes, nothing happens.
309
+ # When outputting to a file instead of a terminal, strip codes.
310
+ else:
311
+ color = resolve_color_default(color)
312
+
313
+ if should_strip_ansi(file, color):
314
+ out = strip_ansi(out)
315
+ elif WIN:
316
+ if auto_wrap_for_ansi is not None:
317
+ file = auto_wrap_for_ansi(file, color) # type: ignore
318
+ elif not color:
319
+ out = strip_ansi(out)
320
+
321
+ file.write(out) # type: ignore
322
+ file.flush()
323
+
324
+
325
+ def get_binary_stream(name: t.Literal["stdin", "stdout", "stderr"]) -> t.BinaryIO:
326
+ """Returns a system stream for byte processing.
327
+
328
+ :param name: the name of the stream to open. Valid names are ``'stdin'``,
329
+ ``'stdout'`` and ``'stderr'``
330
+ """
331
+ opener = binary_streams.get(name)
332
+ if opener is None:
333
+ raise TypeError(f"Unknown standard stream '{name}'")
334
+ return opener()
335
+
336
+
337
+ def get_text_stream(
338
+ name: t.Literal["stdin", "stdout", "stderr"],
339
+ encoding: str | None = None,
340
+ errors: str | None = "strict",
341
+ ) -> t.TextIO:
342
+ """Returns a system stream for text processing. This usually returns
343
+ a wrapped stream around a binary stream returned from
344
+ :func:`get_binary_stream` but it also can take shortcuts for already
345
+ correctly configured streams.
346
+
347
+ :param name: the name of the stream to open. Valid names are ``'stdin'``,
348
+ ``'stdout'`` and ``'stderr'``
349
+ :param encoding: overrides the detected default encoding.
350
+ :param errors: overrides the default error mode.
351
+ """
352
+ opener = text_streams.get(name)
353
+ if opener is None:
354
+ raise TypeError(f"Unknown standard stream '{name}'")
355
+ return opener(encoding, errors)
356
+
357
+
358
+ def open_file(
359
+ filename: str | os.PathLike[str],
360
+ mode: str = "r",
361
+ encoding: str | None = None,
362
+ errors: str | None = "strict",
363
+ lazy: bool = False,
364
+ atomic: bool = False,
365
+ ) -> t.IO[t.Any]:
366
+ """Open a file, with extra behavior to handle ``'-'`` to indicate
367
+ a standard stream, lazy open on write, and atomic write. Similar to
368
+ the behavior of the :class:`~click.File` param type.
369
+
370
+ If ``'-'`` is given to open ``stdout`` or ``stdin``, the stream is
371
+ wrapped so that using it in a context manager will not close it.
372
+ This makes it possible to use the function without accidentally
373
+ closing a standard stream:
374
+
375
+ .. code-block:: python
376
+
377
+ with open_file(filename) as f:
378
+ ...
379
+
380
+ :param filename: The name or Path of the file to open, or ``'-'`` for
381
+ ``stdin``/``stdout``.
382
+ :param mode: The mode in which to open the file.
383
+ :param encoding: The encoding to decode or encode a file opened in
384
+ text mode.
385
+ :param errors: The error handling mode.
386
+ :param lazy: Wait to open the file until it is accessed. For read
387
+ mode, the file is temporarily opened to raise access errors
388
+ early, then closed until it is read again.
389
+ :param atomic: Write to a temporary file and replace the given file
390
+ on close.
391
+
392
+ .. versionadded:: 3.0
393
+ """
394
+ if lazy:
395
+ return t.cast(
396
+ "t.IO[t.Any]", LazyFile(filename, mode, encoding, errors, atomic=atomic)
397
+ )
398
+
399
+ f, should_close = open_stream(filename, mode, encoding, errors, atomic=atomic)
400
+
401
+ if not should_close:
402
+ f = t.cast("t.IO[t.Any]", KeepOpenFile(f))
403
+
404
+ return f
405
+
406
+
407
+ def format_filename(
408
+ filename: str | bytes | os.PathLike[str] | os.PathLike[bytes],
409
+ shorten: bool = False,
410
+ ) -> str:
411
+ """Format a filename as a string for display. Ensures the filename can be
412
+ displayed by replacing any invalid bytes or surrogate escapes in the name
413
+ with the replacement character ``�``.
414
+
415
+ Invalid bytes or surrogate escapes will raise an error when written to a
416
+ stream with ``errors="strict"``. This will typically happen with ``stdout``
417
+ when the locale is something like ``en_GB.UTF-8``.
418
+
419
+ Many scenarios *are* safe to write surrogates though, due to PEP 538 and
420
+ PEP 540, including:
421
+
422
+ - Writing to ``stderr``, which uses ``errors="backslashreplace"``.
423
+ - The system has ``LANG=C.UTF-8``, ``C``, or ``POSIX``. Python opens
424
+ stdout and stderr with ``errors="surrogateescape"``.
425
+ - None of ``LANG/LC_*`` are set. Python assumes ``LANG=C.UTF-8``.
426
+ - Python is started in UTF-8 mode with ``PYTHONUTF8=1`` or ``-X utf8``.
427
+ Python opens stdout and stderr with ``errors="surrogateescape"``.
428
+
429
+ :param filename: formats a filename for UI display. This will also convert
430
+ the filename into unicode without failing.
431
+ :param shorten: this optionally shortens the filename to strip of the
432
+ path that leads up to it.
433
+ """
434
+ if shorten:
435
+ filename = os.path.basename(filename)
436
+ else:
437
+ filename = os.fspath(filename)
438
+
439
+ if isinstance(filename, bytes):
440
+ filename = filename.decode(sys.getfilesystemencoding(), "replace")
441
+ else:
442
+ filename = filename.encode("utf-8", "surrogateescape").decode(
443
+ "utf-8", "replace"
444
+ )
445
+
446
+ return filename
447
+
448
+
449
+ def get_app_dir(app_name: str, roaming: bool = True, force_posix: bool = False) -> str:
450
+ r"""Returns the config folder for the application. The default behavior
451
+ is to return whatever is most appropriate for the operating system.
452
+
453
+ To give you an idea, for an app called ``"Foo Bar"``, something like
454
+ the following folders could be returned:
455
+
456
+ Mac OS X:
457
+ ``~/Library/Application Support/Foo Bar``
458
+ Mac OS X (POSIX):
459
+ ``~/.foo-bar``
460
+ Unix:
461
+ ``~/.config/foo-bar``
462
+ Unix (POSIX):
463
+ ``~/.foo-bar``
464
+ Windows (roaming):
465
+ ``C:\Users\<user>\AppData\Roaming\Foo Bar``
466
+ Windows (not roaming):
467
+ ``C:\Users\<user>\AppData\Local\Foo Bar``
468
+
469
+ .. versionadded:: 2.0
470
+
471
+ :param app_name: the application name. This should be properly capitalized
472
+ and can contain whitespace.
473
+ :param roaming: controls if the folder should be roaming or not on Windows.
474
+ Has no effect otherwise.
475
+ :param force_posix: if this is set to `True` then on any POSIX system the
476
+ folder will be stored in the home folder with a leading
477
+ dot instead of the XDG config home or darwin's
478
+ application support folder.
479
+ """
480
+ if WIN:
481
+ key = "APPDATA" if roaming else "LOCALAPPDATA"
482
+ folder = os.environ.get(key)
483
+ if folder is None:
484
+ folder = os.path.expanduser("~")
485
+ return os.path.join(folder, app_name)
486
+ if force_posix:
487
+ return os.path.join(os.path.expanduser(f"~/.{_posixify(app_name)}"))
488
+ if sys.platform == "darwin":
489
+ return os.path.join(
490
+ os.path.expanduser("~/Library/Application Support"), app_name
491
+ )
492
+ return os.path.join(
493
+ os.environ.get("XDG_CONFIG_HOME", os.path.expanduser("~/.config")),
494
+ _posixify(app_name),
495
+ )
496
+
497
+
498
+ class PacifyFlushWrapper:
499
+ """This wrapper is used to catch and suppress BrokenPipeErrors resulting
500
+ from ``.flush()`` being called on broken pipe during the shutdown/final-GC
501
+ of the Python interpreter. Notably ``.flush()`` is always called on
502
+ ``sys.stdout`` and ``sys.stderr``. So as to have minimal impact on any
503
+ other cleanup code, and the case where the underlying file is not a broken
504
+ pipe, all calls and attributes are proxied.
505
+ """
506
+
507
+ def __init__(self, wrapped: t.IO[t.Any]) -> None:
508
+ self.wrapped = wrapped
509
+
510
+ def flush(self) -> None:
511
+ try:
512
+ self.wrapped.flush()
513
+ except OSError as e:
514
+ import errno
515
+
516
+ if e.errno != errno.EPIPE:
517
+ raise
518
+
519
+ def __getattr__(self, attr: str) -> t.Any:
520
+ return getattr(self.wrapped, attr)
521
+
522
+
523
+ def _detect_program_name(
524
+ path: str | None = None, _main: ModuleType | None = None
525
+ ) -> str:
526
+ """Determine the command used to run the program, for use in help
527
+ text. If a file or entry point was executed, the file name is
528
+ returned. If ``python -m`` was used to execute a module or package,
529
+ ``python -m name`` is returned.
530
+
531
+ This doesn't try to be too precise, the goal is to give a concise
532
+ name for help text. Files are only shown as their name without the
533
+ path. ``python`` is only shown for modules, and the full path to
534
+ ``sys.executable`` is not shown.
535
+
536
+ :param path: The Python file being executed. Python puts this in
537
+ ``sys.argv[0]``, which is used by default.
538
+ :param _main: The ``__main__`` module. This should only be passed
539
+ during internal testing.
540
+
541
+ .. versionadded:: 8.0
542
+ Based on command args detection in the Werkzeug reloader.
543
+
544
+ :meta private:
545
+ """
546
+ if _main is None:
547
+ _main = sys.modules["__main__"]
548
+
549
+ if not path:
550
+ path = sys.argv[0]
551
+
552
+ # The value of __package__ indicates how Python was called. It may
553
+ # not exist if a setuptools script is installed as an egg. It may be
554
+ # set incorrectly for entry points created with pip on Windows.
555
+ # It is set to "" inside a Shiv or PEX zipapp.
556
+ if getattr(_main, "__package__", None) in {None, ""} or (
557
+ os.name == "nt"
558
+ and _main.__package__ == ""
559
+ and not os.path.exists(path)
560
+ and os.path.exists(f"{path}.exe")
561
+ ):
562
+ # Executed a file, like "python app.py".
563
+ return os.path.basename(path)
564
+
565
+ # Executed a module, like "python -m example".
566
+ # Rewritten by Python from "-m script" to "/path/to/script.py".
567
+ # Need to look at main module to determine how it was executed.
568
+ py_module = t.cast(str, _main.__package__)
569
+ name = os.path.splitext(os.path.basename(path))[0]
570
+
571
+ # A submodule like "example.cli".
572
+ if name != "__main__":
573
+ py_module = f"{py_module}.{name}"
574
+
575
+ return f"python -m {py_module.lstrip('.')}"
576
+
577
+
578
+ def _expand_args(
579
+ args: cabc.Iterable[str],
580
+ *,
581
+ user: bool = True,
582
+ env: bool = True,
583
+ glob_recursive: bool = True,
584
+ ) -> list[str]:
585
+ """Simulate Unix shell expansion with Python functions.
586
+
587
+ See :func:`glob.glob`, :func:`os.path.expanduser`, and
588
+ :func:`os.path.expandvars`.
589
+
590
+ This is intended for use on Windows, where the shell does not do any
591
+ expansion. It may not exactly match what a Unix shell would do.
592
+
593
+ :param args: List of command line arguments to expand.
594
+ :param user: Expand user home directory.
595
+ :param env: Expand environment variables.
596
+ :param glob_recursive: ``**`` matches directories recursively.
597
+
598
+ .. versionchanged:: 8.1
599
+ Invalid glob patterns are treated as empty expansions rather
600
+ than raising an error.
601
+
602
+ .. versionadded:: 8.0
603
+
604
+ :meta private:
605
+ """
606
+ from glob import glob
607
+
608
+ out = []
609
+
610
+ for arg in args:
611
+ if user:
612
+ arg = os.path.expanduser(arg)
613
+
614
+ if env:
615
+ arg = os.path.expandvars(arg)
616
+
617
+ try:
618
+ matches = glob(arg, recursive=glob_recursive)
619
+ except re.error:
620
+ matches = []
621
+
622
+ if not matches:
623
+ out.append(arg)
624
+ else:
625
+ out.extend(matches)
626
+
627
+ return out
env/lib/python3.13/site-packages/shellingham-1.5.4.dist-info/INSTALLER ADDED
@@ -0,0 +1 @@
 
 
1
+ pip
env/lib/python3.13/site-packages/shellingham-1.5.4.dist-info/LICENSE ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Copyright (c) 2018, Tzu-ping Chung <uranusjr@gmail.com>
2
+
3
+ Permission to use, copy, modify, and distribute this software for any
4
+ purpose with or without fee is hereby granted, provided that the above
5
+ copyright notice and this permission notice appear in all copies.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
env/lib/python3.13/site-packages/shellingham-1.5.4.dist-info/METADATA ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Metadata-Version: 2.1
2
+ Name: shellingham
3
+ Version: 1.5.4
4
+ Summary: Tool to Detect Surrounding Shell
5
+ Home-page: https://github.com/sarugaku/shellingham
6
+ Author: Tzu-ping Chung
7
+ Author-email: uranusjr@gmail.com
8
+ License: ISC License
9
+ Keywords: shell
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Environment :: Console
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: ISC License (ISCL)
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3 :: Only
16
+ Classifier: Programming Language :: Python :: 3.7
17
+ Classifier: Programming Language :: Python :: 3.8
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
+ Requires-Python: >=3.7
24
+ Description-Content-Type: text/x-rst
25
+ License-File: LICENSE
26
+
27
+ =============================================
28
+ Shellingham: Tool to Detect Surrounding Shell
29
+ =============================================
30
+
31
+ .. image:: https://img.shields.io/pypi/v/shellingham.svg
32
+ :target: https://pypi.org/project/shellingham/
33
+
34
+ Shellingham detects what shell the current Python executable is running in.
35
+
36
+
37
+ Usage
38
+ =====
39
+
40
+ .. code-block:: python
41
+
42
+ >>> import shellingham
43
+ >>> shellingham.detect_shell()
44
+ ('bash', '/bin/bash')
45
+
46
+ ``detect_shell`` pokes around the process's running environment to determine
47
+ what shell it is run in. It returns a 2-tuple:
48
+
49
+ * The shell name, always lowercased.
50
+ * The command used to run the shell.
51
+
52
+ ``ShellDetectionFailure`` is raised if ``detect_shell`` fails to detect the
53
+ surrounding shell.
54
+
55
+
56
+ Notes
57
+ =====
58
+
59
+ * The shell name is always lowercased.
60
+ * On Windows, the shell name is the name of the executable, minus the file
61
+ extension.
62
+
63
+
64
+ Notes for Application Developers
65
+ ================================
66
+
67
+ Remember, your application's user is not necessarily using a shell.
68
+ Shellingham raises ``ShellDetectionFailure`` if there is no shell to detect,
69
+ but *your application should almost never do this to your user*.
70
+
71
+ A practical approach to this is to wrap ``detect_shell`` in a try block, and
72
+ provide a sane default on failure
73
+
74
+ .. code-block:: python
75
+
76
+ try:
77
+ shell = shellingham.detect_shell()
78
+ except shellingham.ShellDetectionFailure:
79
+ shell = provide_default()
80
+
81
+
82
+ There are a few choices for you to choose from.
83
+
84
+ * The POSIX standard mandates the environment variable ``SHELL`` to refer to
85
+ "the user's preferred command language interpreter". This is always available
86
+ (even if the user is not in an interactive session), and likely the correct
87
+ choice to launch an interactive sub-shell with.
88
+ * A command ``sh`` is almost guaranteed to exist, likely at ``/bin/sh``, since
89
+ several POSIX tools rely on it. This should be suitable if you want to run a
90
+ (possibly non-interactive) script.
91
+ * All versions of DOS and Windows have an environment variable ``COMSPEC``.
92
+ This can always be used to launch a usable command prompt (e.g. `cmd.exe` on
93
+ Windows).
94
+
95
+ Here's a simple implementation to provide a default shell
96
+
97
+ .. code-block:: python
98
+
99
+ import os
100
+
101
+ def provide_default():
102
+ if os.name == 'posix':
103
+ return os.environ['SHELL']
104
+ elif os.name == 'nt':
105
+ return os.environ['COMSPEC']
106
+ raise NotImplementedError(f'OS {os.name!r} support not available')
env/lib/python3.13/site-packages/shellingham-1.5.4.dist-info/RECORD ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ shellingham-1.5.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
2
+ shellingham-1.5.4.dist-info/LICENSE,sha256=84j9OMrRMRLB3A9mm76A5_hFQe26-3LzAw0sp2QsPJ0,751
3
+ shellingham-1.5.4.dist-info/METADATA,sha256=GD2AIgo3STJieVc53TV8xbs_Sb05DMkZjVGA5UUaB_o,3461
4
+ shellingham-1.5.4.dist-info/RECORD,,
5
+ shellingham-1.5.4.dist-info/WHEEL,sha256=iYlv5fX357PQyRT2o6tw1bN-YcKFFHKqB_LwHO5wP-g,110
6
+ shellingham-1.5.4.dist-info/top_level.txt,sha256=uKMQL5AKxPi4O9_Rbd838QeEs4ImpGQKNbEDZYqgBgk,12
7
+ shellingham-1.5.4.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
8
+ shellingham/__init__.py,sha256=pAKXUPKUdwyErC0ZjS-5w-fRdSbmdcfvnpt_x1yWqtA,635
9
+ shellingham/__pycache__/__init__.cpython-313.pyc,,
10
+ shellingham/__pycache__/_core.cpython-313.pyc,,
11
+ shellingham/__pycache__/nt.cpython-313.pyc,,
12
+ shellingham/_core.py,sha256=v-CTr_7F7cJAtNnzpa1N_Hl8afkY5yiDA4joGmsUBu0,300
13
+ shellingham/nt.py,sha256=m6J6SuwyqVVlxXT9Bc-9F_1x-T5u0gCFFrRAF2LIkeg,4516
14
+ shellingham/posix/__init__.py,sha256=pB69qtvZJ_yIf48nl4-ZfS3wLwwuXuknXOZhBnC2T1o,3129
15
+ shellingham/posix/__pycache__/__init__.cpython-313.pyc,,
16
+ shellingham/posix/__pycache__/_core.cpython-313.pyc,,
17
+ shellingham/posix/__pycache__/proc.cpython-313.pyc,,
18
+ shellingham/posix/__pycache__/ps.cpython-313.pyc,,
19
+ shellingham/posix/_core.py,sha256=_v18UaXbzr4muNhr3-mH1FdSdjZ_dOXQrtUyomIbKYQ,81
20
+ shellingham/posix/proc.py,sha256=nSUxIuQSotvaDW76i0oTQAM9aZ9PXBLFAEktWljSKCo,2659
21
+ shellingham/posix/ps.py,sha256=NGmDKCukhNp0lahwYCaMXphBYaVbhbiR9BtE0OkT8qU,1770
env/lib/python3.13/site-packages/shellingham-1.5.4.dist-info/WHEEL ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ Wheel-Version: 1.0
2
+ Generator: bdist_wheel (0.41.2)
3
+ Root-Is-Purelib: true
4
+ Tag: py2-none-any
5
+ Tag: py3-none-any
6
+
env/lib/python3.13/site-packages/shellingham-1.5.4.dist-info/top_level.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ shellingham
env/lib/python3.13/site-packages/shellingham-1.5.4.dist-info/zip-safe ADDED
@@ -0,0 +1 @@
 
 
1
+
env/lib/python3.13/site-packages/tqdm-4.67.1.dist-info/INSTALLER ADDED
@@ -0,0 +1 @@
 
 
1
+ pip
env/lib/python3.13/site-packages/tqdm-4.67.1.dist-info/RECORD ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ../../../bin/tqdm,sha256=CbWuHrvyWyMkCZcwsU1AQGJnOp3zzMmQKJ-YUqOuPPI,256
2
+ tqdm-4.67.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
3
+ tqdm-4.67.1.dist-info/LICENCE,sha256=3DMlLoKQFeOxUAhvubOkD2rW-zLC9GEM6BL6Z301mGo,1985
4
+ tqdm-4.67.1.dist-info/METADATA,sha256=aIoWMt9SWhmP7FLc_vsSRtMerO6cA1qsrC1-r42P9mk,57675
5
+ tqdm-4.67.1.dist-info/RECORD,,
6
+ tqdm-4.67.1.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
7
+ tqdm-4.67.1.dist-info/entry_points.txt,sha256=ReJCH7Ui3Zyh6M16E4OhsZ1oU7WtMXCfbtoyBhGO29Y,39
8
+ tqdm-4.67.1.dist-info/top_level.txt,sha256=NLiUJNfmc9At15s7JURiwvqMEjUi9G5PMGRrmMYzNSM,5
9
+ tqdm/__init__.py,sha256=9mQNYSSqP99JasubEC1POJLMmhkkBH6cJZxPIR5G2pQ,1572
10
+ tqdm/__main__.py,sha256=bYt9eEaoRQWdejEHFD8REx9jxVEdZptECFsV7F49Ink,30
11
+ tqdm/__pycache__/__init__.cpython-313.pyc,,
12
+ tqdm/__pycache__/__main__.cpython-313.pyc,,
13
+ tqdm/__pycache__/_dist_ver.cpython-313.pyc,,
14
+ tqdm/__pycache__/_main.cpython-313.pyc,,
15
+ tqdm/__pycache__/_monitor.cpython-313.pyc,,
16
+ tqdm/__pycache__/_tqdm.cpython-313.pyc,,
17
+ tqdm/__pycache__/_tqdm_gui.cpython-313.pyc,,
18
+ tqdm/__pycache__/_tqdm_notebook.cpython-313.pyc,,
19
+ tqdm/__pycache__/_tqdm_pandas.cpython-313.pyc,,
20
+ tqdm/__pycache__/_utils.cpython-313.pyc,,
21
+ tqdm/__pycache__/asyncio.cpython-313.pyc,,
22
+ tqdm/__pycache__/auto.cpython-313.pyc,,
23
+ tqdm/__pycache__/autonotebook.cpython-313.pyc,,
24
+ tqdm/__pycache__/cli.cpython-313.pyc,,
25
+ tqdm/__pycache__/dask.cpython-313.pyc,,
26
+ tqdm/__pycache__/gui.cpython-313.pyc,,
27
+ tqdm/__pycache__/keras.cpython-313.pyc,,
28
+ tqdm/__pycache__/notebook.cpython-313.pyc,,
29
+ tqdm/__pycache__/rich.cpython-313.pyc,,
30
+ tqdm/__pycache__/std.cpython-313.pyc,,
31
+ tqdm/__pycache__/tk.cpython-313.pyc,,
32
+ tqdm/__pycache__/utils.cpython-313.pyc,,
33
+ tqdm/__pycache__/version.cpython-313.pyc,,
34
+ tqdm/_dist_ver.py,sha256=m5AdYI-jB-v6P0VJ_70isH_p24EzSOGSwVvuAZmkmKY,23
35
+ tqdm/_main.py,sha256=9ySvgmi_2Sw4CAo5UDW0Q2dxfTryboEWGHohfCJz0sA,283
36
+ tqdm/_monitor.py,sha256=Uku-DPWgzJ7dO5CK08xKJK-E_F6qQ-JB3ksuXczSYR0,3699
37
+ tqdm/_tqdm.py,sha256=LfLCuJ6bpsVo9xilmtBXyEm1vGnUCFrliW85j3J-nD4,283
38
+ tqdm/_tqdm_gui.py,sha256=03Hc8KayxJveieI5-0-2NGiDpLvw9jZekofJUV7CCwk,287
39
+ tqdm/_tqdm_notebook.py,sha256=BuHiLuxu6uEfZFaPJW3RPpPaxaVctEQA3kdSJSDL1hw,307
40
+ tqdm/_tqdm_pandas.py,sha256=c9jptUgigN6axRDhRd4Rif98Tmxeopc1nFNFhIpbFUE,888
41
+ tqdm/_utils.py,sha256=_4E73bfDj4f1s3sM42NLHNrZDOkijZoWq-n6xWLkdZ8,553
42
+ tqdm/asyncio.py,sha256=Kp2rSkNRf9KRqa3d9YpgeZQ7L7EZf2Ki4bSc7UPIyoo,2757
43
+ tqdm/auto.py,sha256=nDZflj6p2zKkjBCNBourrhS81zYfZy1_dQvbckrdW8o,871
44
+ tqdm/autonotebook.py,sha256=Yb9F5uaiBPhfbDDFpbtoG8I2YUw3uQJ89rUDLbfR6ws,956
45
+ tqdm/cli.py,sha256=SbKlN8QyZ2ogenqt-wT_p6_sx2OOdCjCyhoZBFnlmyI,11010
46
+ tqdm/completion.sh,sha256=j79KbSmpIj_E11jfTfBXrGnUTzKXVpQ1vGVQvsyDRl4,946
47
+ tqdm/contrib/__init__.py,sha256=OgSwVXm-vlDJ-2imtoQ9z8qdom4snMSRztH72KMA82A,2494
48
+ tqdm/contrib/__pycache__/__init__.cpython-313.pyc,,
49
+ tqdm/contrib/__pycache__/bells.cpython-313.pyc,,
50
+ tqdm/contrib/__pycache__/concurrent.cpython-313.pyc,,
51
+ tqdm/contrib/__pycache__/discord.cpython-313.pyc,,
52
+ tqdm/contrib/__pycache__/itertools.cpython-313.pyc,,
53
+ tqdm/contrib/__pycache__/logging.cpython-313.pyc,,
54
+ tqdm/contrib/__pycache__/slack.cpython-313.pyc,,
55
+ tqdm/contrib/__pycache__/telegram.cpython-313.pyc,,
56
+ tqdm/contrib/__pycache__/utils_worker.cpython-313.pyc,,
57
+ tqdm/contrib/bells.py,sha256=Yx1HqGCmHrESCAO700j5wE__JCleNODJxedh1ijPLD0,837
58
+ tqdm/contrib/concurrent.py,sha256=K1yjloKS5WRNFyjLRth0DmU5PAnDbF0A-GD27N-J4a8,3986
59
+ tqdm/contrib/discord.py,sha256=MtVIL1s_dxH21G4sL8FBgQ4Wei23ho9Ek5T-AommvNc,5243
60
+ tqdm/contrib/itertools.py,sha256=WdKKQU5eSzsqHu29SN_oH12huYZo0Jihqoi9-nVhwz4,774
61
+ tqdm/contrib/logging.py,sha256=NsYtnKttj2mMrGm58mEdo5a9DP_2vv8pZyrimSuWulA,3760
62
+ tqdm/contrib/slack.py,sha256=eP_Mr5sQonYniHxxQNGue3jk2JkIPmPWFZqIYxnOui0,4007
63
+ tqdm/contrib/telegram.py,sha256=vn_9SATMbbwn2PAbzSDyOX6av3eBB01QBug11P4H-Og,5008
64
+ tqdm/contrib/utils_worker.py,sha256=HJP5Mz1S1xyzEke2JaqJ2sYLHXADYoo2epT5AzQ38eA,1207
65
+ tqdm/dask.py,sha256=9Ei58eVqTossRLhAfWyUFCduXYKjmLmwkaXIy-CHYfs,1319
66
+ tqdm/gui.py,sha256=STIB3K8iDzDgkNUqWIpvcI_u0OGtbGNy5NwpALXhfWs,5479
67
+ tqdm/keras.py,sha256=op9sBkb6q6c6dw2wJ0SD2ZwpPK7yM1Vbg4l1Qiy3MIo,4373
68
+ tqdm/notebook.py,sha256=GtZ3IapLL1v8WNDaTSvPw0bJGTyfp71Vfz5HDnAzx1M,10895
69
+ tqdm/rich.py,sha256=YyMPkEHVyYUVUR3adJKbVX26iTmNKpNMf3DEqmm-m60,5021
70
+ tqdm/std.py,sha256=tWjz6-QCa92aqYjz7PIdkLUCAfiy-lJZheBtZyIIyO0,57461
71
+ tqdm/tk.py,sha256=Gu0uwXwLCGPRGHORdi3WvBLGiseUp_xxX_h_gp9VpK0,6701
72
+ tqdm/tqdm.1,sha256=aILyUPk2S4OPe_uWy2P4AMjUf0oQ6PUW0nLYXB-BWwI,7889
73
+ tqdm/utils.py,sha256=6E0BQw3Sg7uGWKBM_cDn3P42tXswRhzkggbhBgLDjl8,11821
74
+ tqdm/version.py,sha256=-1yWjfu3P0eghVsysHH07fbzdiADNRdzRtYPqOaqR2A,333
env/lib/python3.13/site-packages/tqdm-4.67.1.dist-info/WHEEL ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (75.6.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
env/lib/python3.13/site-packages/typer/__main__.py ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ from .cli import main
2
+
3
+ main()
env/lib/python3.13/site-packages/typer/_completion_classes.py ADDED
@@ -0,0 +1,211 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import importlib.util
2
+ import os
3
+ import re
4
+ import sys
5
+ from typing import Any, Dict, List, Tuple
6
+
7
+ import click
8
+ import click.parser
9
+ import click.shell_completion
10
+
11
+ from ._completion_shared import (
12
+ COMPLETION_SCRIPT_BASH,
13
+ COMPLETION_SCRIPT_FISH,
14
+ COMPLETION_SCRIPT_POWER_SHELL,
15
+ COMPLETION_SCRIPT_ZSH,
16
+ Shells,
17
+ )
18
+
19
+ try:
20
+ from click.shell_completion import split_arg_string as click_split_arg_string
21
+ except ImportError: # pragma: no cover
22
+ # TODO: when removing support for Click < 8.2, remove this import
23
+ from click.parser import ( # type: ignore[no-redef]
24
+ split_arg_string as click_split_arg_string,
25
+ )
26
+
27
+ try:
28
+ import shellingham
29
+ except ImportError: # pragma: no cover
30
+ shellingham = None
31
+
32
+
33
+ def _sanitize_help_text(text: str) -> str:
34
+ """Sanitizes the help text by removing rich tags"""
35
+ if not importlib.util.find_spec("rich"):
36
+ return text
37
+ from . import rich_utils
38
+
39
+ return rich_utils.rich_render_text(text)
40
+
41
+
42
+ class BashComplete(click.shell_completion.BashComplete):
43
+ name = Shells.bash.value
44
+ source_template = COMPLETION_SCRIPT_BASH
45
+
46
+ def source_vars(self) -> Dict[str, Any]:
47
+ return {
48
+ "complete_func": self.func_name,
49
+ "autocomplete_var": self.complete_var,
50
+ "prog_name": self.prog_name,
51
+ }
52
+
53
+ def get_completion_args(self) -> Tuple[List[str], str]:
54
+ cwords = click_split_arg_string(os.environ["COMP_WORDS"])
55
+ cword = int(os.environ["COMP_CWORD"])
56
+ args = cwords[1:cword]
57
+
58
+ try:
59
+ incomplete = cwords[cword]
60
+ except IndexError:
61
+ incomplete = ""
62
+
63
+ return args, incomplete
64
+
65
+ def format_completion(self, item: click.shell_completion.CompletionItem) -> str:
66
+ # TODO: Explore replicating the new behavior from Click, with item types and
67
+ # triggering completion for files and directories
68
+ # return f"{item.type},{item.value}"
69
+ return f"{item.value}"
70
+
71
+ def complete(self) -> str:
72
+ args, incomplete = self.get_completion_args()
73
+ completions = self.get_completions(args, incomplete)
74
+ out = [self.format_completion(item) for item in completions]
75
+ return "\n".join(out)
76
+
77
+
78
+ class ZshComplete(click.shell_completion.ZshComplete):
79
+ name = Shells.zsh.value
80
+ source_template = COMPLETION_SCRIPT_ZSH
81
+
82
+ def source_vars(self) -> Dict[str, Any]:
83
+ return {
84
+ "complete_func": self.func_name,
85
+ "autocomplete_var": self.complete_var,
86
+ "prog_name": self.prog_name,
87
+ }
88
+
89
+ def get_completion_args(self) -> Tuple[List[str], str]:
90
+ completion_args = os.getenv("_TYPER_COMPLETE_ARGS", "")
91
+ cwords = click_split_arg_string(completion_args)
92
+ args = cwords[1:]
93
+ if args and not completion_args.endswith(" "):
94
+ incomplete = args[-1]
95
+ args = args[:-1]
96
+ else:
97
+ incomplete = ""
98
+ return args, incomplete
99
+
100
+ def format_completion(self, item: click.shell_completion.CompletionItem) -> str:
101
+ def escape(s: str) -> str:
102
+ return (
103
+ s.replace('"', '""')
104
+ .replace("'", "''")
105
+ .replace("$", "\\$")
106
+ .replace("`", "\\`")
107
+ .replace(":", r"\\:")
108
+ )
109
+
110
+ # TODO: Explore replicating the new behavior from Click, pay attention to
111
+ # the difference with and without escape
112
+ # return f"{item.type}\n{item.value}\n{item.help if item.help else '_'}"
113
+ if item.help:
114
+ return f'"{escape(item.value)}":"{_sanitize_help_text(escape(item.help))}"'
115
+ else:
116
+ return f'"{escape(item.value)}"'
117
+
118
+ def complete(self) -> str:
119
+ args, incomplete = self.get_completion_args()
120
+ completions = self.get_completions(args, incomplete)
121
+ res = [self.format_completion(item) for item in completions]
122
+ if res:
123
+ args_str = "\n".join(res)
124
+ return f"_arguments '*: :(({args_str}))'"
125
+ else:
126
+ return "_files"
127
+
128
+
129
+ class FishComplete(click.shell_completion.FishComplete):
130
+ name = Shells.fish.value
131
+ source_template = COMPLETION_SCRIPT_FISH
132
+
133
+ def source_vars(self) -> Dict[str, Any]:
134
+ return {
135
+ "complete_func": self.func_name,
136
+ "autocomplete_var": self.complete_var,
137
+ "prog_name": self.prog_name,
138
+ }
139
+
140
+ def get_completion_args(self) -> Tuple[List[str], str]:
141
+ completion_args = os.getenv("_TYPER_COMPLETE_ARGS", "")
142
+ cwords = click_split_arg_string(completion_args)
143
+ args = cwords[1:]
144
+ if args and not completion_args.endswith(" "):
145
+ incomplete = args[-1]
146
+ args = args[:-1]
147
+ else:
148
+ incomplete = ""
149
+ return args, incomplete
150
+
151
+ def format_completion(self, item: click.shell_completion.CompletionItem) -> str:
152
+ # TODO: Explore replicating the new behavior from Click, pay attention to
153
+ # the difference with and without formatted help
154
+ # if item.help:
155
+ # return f"{item.type},{item.value}\t{item.help}"
156
+
157
+ # return f"{item.type},{item.value}
158
+ if item.help:
159
+ formatted_help = re.sub(r"\s", " ", item.help)
160
+ return f"{item.value}\t{_sanitize_help_text(formatted_help)}"
161
+ else:
162
+ return f"{item.value}"
163
+
164
+ def complete(self) -> str:
165
+ complete_action = os.getenv("_TYPER_COMPLETE_FISH_ACTION", "")
166
+ args, incomplete = self.get_completion_args()
167
+ completions = self.get_completions(args, incomplete)
168
+ show_args = [self.format_completion(item) for item in completions]
169
+ if complete_action == "get-args":
170
+ if show_args:
171
+ return "\n".join(show_args)
172
+ elif complete_action == "is-args":
173
+ if show_args:
174
+ # Activate complete args (no files)
175
+ sys.exit(0)
176
+ else:
177
+ # Deactivate complete args (allow files)
178
+ sys.exit(1)
179
+ return "" # pragma: no cover
180
+
181
+
182
+ class PowerShellComplete(click.shell_completion.ShellComplete):
183
+ name = Shells.powershell.value
184
+ source_template = COMPLETION_SCRIPT_POWER_SHELL
185
+
186
+ def source_vars(self) -> Dict[str, Any]:
187
+ return {
188
+ "complete_func": self.func_name,
189
+ "autocomplete_var": self.complete_var,
190
+ "prog_name": self.prog_name,
191
+ }
192
+
193
+ def get_completion_args(self) -> Tuple[List[str], str]:
194
+ completion_args = os.getenv("_TYPER_COMPLETE_ARGS", "")
195
+ incomplete = os.getenv("_TYPER_COMPLETE_WORD_TO_COMPLETE", "")
196
+ cwords = click_split_arg_string(completion_args)
197
+ args = cwords[1:-1] if incomplete else cwords[1:]
198
+ return args, incomplete
199
+
200
+ def format_completion(self, item: click.shell_completion.CompletionItem) -> str:
201
+ return f"{item.value}:::{_sanitize_help_text(item.help) if item.help else ' '}"
202
+
203
+
204
+ def completion_init() -> None:
205
+ click.shell_completion.add_completion_class(BashComplete, Shells.bash.value)
206
+ click.shell_completion.add_completion_class(ZshComplete, Shells.zsh.value)
207
+ click.shell_completion.add_completion_class(FishComplete, Shells.fish.value)
208
+ click.shell_completion.add_completion_class(
209
+ PowerShellComplete, Shells.powershell.value
210
+ )
211
+ click.shell_completion.add_completion_class(PowerShellComplete, Shells.pwsh.value)
env/lib/python3.13/site-packages/typer/_typing.py ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copied from pydantic 1.9.2 (the latest version to support python 3.6.)
2
+ # https://github.com/pydantic/pydantic/blob/v1.9.2/pydantic/typing.py
3
+ # Reduced drastically to only include Typer-specific 3.8+ functionality
4
+ # mypy: ignore-errors
5
+
6
+ import sys
7
+ from typing import (
8
+ Any,
9
+ Callable,
10
+ Optional,
11
+ Tuple,
12
+ Type,
13
+ Union,
14
+ )
15
+
16
+ if sys.version_info >= (3, 9):
17
+ from typing import Annotated, Literal, get_args, get_origin, get_type_hints
18
+ else:
19
+ from typing_extensions import (
20
+ Annotated,
21
+ Literal,
22
+ get_args,
23
+ get_origin,
24
+ get_type_hints,
25
+ )
26
+
27
+ if sys.version_info < (3, 10):
28
+
29
+ def is_union(tp: Optional[Type[Any]]) -> bool:
30
+ return tp is Union
31
+
32
+ else:
33
+ import types
34
+
35
+ def is_union(tp: Optional[Type[Any]]) -> bool:
36
+ return tp is Union or tp is types.UnionType # noqa: E721
37
+
38
+
39
+ __all__ = (
40
+ "NoneType",
41
+ "is_none_type",
42
+ "is_callable_type",
43
+ "is_literal_type",
44
+ "all_literal_values",
45
+ "is_union",
46
+ "Annotated",
47
+ "Literal",
48
+ "get_args",
49
+ "get_origin",
50
+ "get_type_hints",
51
+ )
52
+
53
+
54
+ NoneType = None.__class__
55
+
56
+
57
+ NONE_TYPES: Tuple[Any, Any, Any] = (None, NoneType, Literal[None])
58
+
59
+
60
+ if sys.version_info[:2] == (3, 8):
61
+ # We can use the fast implementation for 3.8 but there is a very weird bug
62
+ # where it can fail for `Literal[None]`.
63
+ # We just need to redefine a useless `Literal[None]` inside the function body to fix this
64
+
65
+ def is_none_type(type_: Any) -> bool:
66
+ Literal[None] # fix edge case
67
+ for none_type in NONE_TYPES:
68
+ if type_ is none_type:
69
+ return True
70
+ return False
71
+
72
+ else:
73
+
74
+ def is_none_type(type_: Any) -> bool:
75
+ for none_type in NONE_TYPES:
76
+ if type_ is none_type:
77
+ return True
78
+ return False
79
+
80
+
81
+ def is_callable_type(type_: Type[Any]) -> bool:
82
+ return type_ is Callable or get_origin(type_) is Callable
83
+
84
+
85
+ def is_literal_type(type_: Type[Any]) -> bool:
86
+ import typing_extensions
87
+
88
+ return get_origin(type_) in (Literal, typing_extensions.Literal)
89
+
90
+
91
+ def literal_values(type_: Type[Any]) -> Tuple[Any, ...]:
92
+ return get_args(type_)
93
+
94
+
95
+ def all_literal_values(type_: Type[Any]) -> Tuple[Any, ...]:
96
+ """
97
+ This method is used to retrieve all Literal values as
98
+ Literal can be used recursively (see https://www.python.org/dev/peps/pep-0586)
99
+ e.g. `Literal[Literal[Literal[1, 2, 3], "foo"], 5, None]`
100
+ """
101
+ if not is_literal_type(type_):
102
+ return (type_,)
103
+
104
+ values = literal_values(type_)
105
+ return tuple(x for value in values for x in all_literal_values(value))
env/lib/python3.13/site-packages/typer/cli.py ADDED
@@ -0,0 +1,308 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import importlib.util
2
+ import re
3
+ import sys
4
+ from pathlib import Path
5
+ from typing import Any, List, Optional
6
+
7
+ import click
8
+ import typer
9
+ import typer.core
10
+ from click import Command, Group, Option
11
+
12
+ from . import __version__
13
+ from .core import HAS_RICH
14
+
15
+ default_app_names = ("app", "cli", "main")
16
+ default_func_names = ("main", "cli", "app")
17
+
18
+ app = typer.Typer()
19
+ utils_app = typer.Typer(help="Extra utility commands for Typer apps.")
20
+ app.add_typer(utils_app, name="utils")
21
+
22
+
23
+ class State:
24
+ def __init__(self) -> None:
25
+ self.app: Optional[str] = None
26
+ self.func: Optional[str] = None
27
+ self.file: Optional[Path] = None
28
+ self.module: Optional[str] = None
29
+
30
+
31
+ state = State()
32
+
33
+
34
+ def maybe_update_state(ctx: click.Context) -> None:
35
+ path_or_module = ctx.params.get("path_or_module")
36
+ if path_or_module:
37
+ file_path = Path(path_or_module)
38
+ if file_path.exists() and file_path.is_file():
39
+ state.file = file_path
40
+ else:
41
+ if not re.fullmatch(r"[a-zA-Z_]\w*(\.[a-zA-Z_]\w*)*", path_or_module):
42
+ typer.echo(
43
+ f"Not a valid file or Python module: {path_or_module}", err=True
44
+ )
45
+ sys.exit(1)
46
+ state.module = path_or_module
47
+ app_name = ctx.params.get("app")
48
+ if app_name:
49
+ state.app = app_name
50
+ func_name = ctx.params.get("func")
51
+ if func_name:
52
+ state.func = func_name
53
+
54
+
55
+ class TyperCLIGroup(typer.core.TyperGroup):
56
+ def list_commands(self, ctx: click.Context) -> List[str]:
57
+ self.maybe_add_run(ctx)
58
+ return super().list_commands(ctx)
59
+
60
+ def get_command(self, ctx: click.Context, name: str) -> Optional[Command]:
61
+ self.maybe_add_run(ctx)
62
+ return super().get_command(ctx, name)
63
+
64
+ def invoke(self, ctx: click.Context) -> Any:
65
+ self.maybe_add_run(ctx)
66
+ return super().invoke(ctx)
67
+
68
+ def maybe_add_run(self, ctx: click.Context) -> None:
69
+ maybe_update_state(ctx)
70
+ maybe_add_run_to_cli(self)
71
+
72
+
73
+ def get_typer_from_module(module: Any) -> Optional[typer.Typer]:
74
+ # Try to get defined app
75
+ if state.app:
76
+ obj = getattr(module, state.app, None)
77
+ if not isinstance(obj, typer.Typer):
78
+ typer.echo(f"Not a Typer object: --app {state.app}", err=True)
79
+ sys.exit(1)
80
+ return obj
81
+ # Try to get defined function
82
+ if state.func:
83
+ func_obj = getattr(module, state.func, None)
84
+ if not callable(func_obj):
85
+ typer.echo(f"Not a function: --func {state.func}", err=True)
86
+ sys.exit(1)
87
+ sub_app = typer.Typer()
88
+ sub_app.command()(func_obj)
89
+ return sub_app
90
+ # Iterate and get a default object to use as CLI
91
+ local_names = dir(module)
92
+ local_names_set = set(local_names)
93
+ # Try to get a default Typer app
94
+ for name in default_app_names:
95
+ if name in local_names_set:
96
+ obj = getattr(module, name, None)
97
+ if isinstance(obj, typer.Typer):
98
+ return obj
99
+ # Try to get any Typer app
100
+ for name in local_names_set - set(default_app_names):
101
+ obj = getattr(module, name)
102
+ if isinstance(obj, typer.Typer):
103
+ return obj
104
+ # Try to get a default function
105
+ for func_name in default_func_names:
106
+ func_obj = getattr(module, func_name, None)
107
+ if callable(func_obj):
108
+ sub_app = typer.Typer()
109
+ sub_app.command()(func_obj)
110
+ return sub_app
111
+ # Try to get any func app
112
+ for func_name in local_names_set - set(default_func_names):
113
+ func_obj = getattr(module, func_name)
114
+ if callable(func_obj):
115
+ sub_app = typer.Typer()
116
+ sub_app.command()(func_obj)
117
+ return sub_app
118
+ return None
119
+
120
+
121
+ def get_typer_from_state() -> Optional[typer.Typer]:
122
+ spec = None
123
+ if state.file:
124
+ module_name = state.file.name
125
+ spec = importlib.util.spec_from_file_location(module_name, str(state.file))
126
+ elif state.module:
127
+ spec = importlib.util.find_spec(state.module)
128
+ if spec is None:
129
+ if state.file:
130
+ typer.echo(f"Could not import as Python file: {state.file}", err=True)
131
+ else:
132
+ typer.echo(f"Could not import as Python module: {state.module}", err=True)
133
+ sys.exit(1)
134
+ module = importlib.util.module_from_spec(spec)
135
+ spec.loader.exec_module(module) # type: ignore
136
+ obj = get_typer_from_module(module)
137
+ return obj
138
+
139
+
140
+ def maybe_add_run_to_cli(cli: click.Group) -> None:
141
+ if "run" not in cli.commands:
142
+ if state.file or state.module:
143
+ obj = get_typer_from_state()
144
+ if obj:
145
+ obj._add_completion = False
146
+ click_obj = typer.main.get_command(obj)
147
+ click_obj.name = "run"
148
+ if not click_obj.help:
149
+ click_obj.help = "Run the provided Typer app."
150
+ cli.add_command(click_obj)
151
+
152
+
153
+ def print_version(ctx: click.Context, param: Option, value: bool) -> None:
154
+ if not value or ctx.resilient_parsing:
155
+ return
156
+ typer.echo(f"Typer version: {__version__}")
157
+ raise typer.Exit()
158
+
159
+
160
+ @app.callback(cls=TyperCLIGroup, no_args_is_help=True)
161
+ def callback(
162
+ ctx: typer.Context,
163
+ *,
164
+ path_or_module: str = typer.Argument(None),
165
+ app: str = typer.Option(None, help="The typer app object/variable to use."),
166
+ func: str = typer.Option(None, help="The function to convert to Typer."),
167
+ version: bool = typer.Option(
168
+ False,
169
+ "--version",
170
+ help="Print version and exit.",
171
+ callback=print_version,
172
+ ),
173
+ ) -> None:
174
+ """
175
+ Run Typer scripts with completion, without having to create a package.
176
+
177
+ You probably want to install completion for the typer command:
178
+
179
+ $ typer --install-completion
180
+
181
+ https://typer.tiangolo.com/
182
+ """
183
+ maybe_update_state(ctx)
184
+
185
+
186
+ def get_docs_for_click(
187
+ *,
188
+ obj: Command,
189
+ ctx: typer.Context,
190
+ indent: int = 0,
191
+ name: str = "",
192
+ call_prefix: str = "",
193
+ title: Optional[str] = None,
194
+ ) -> str:
195
+ docs = "#" * (1 + indent)
196
+ command_name = name or obj.name
197
+ if call_prefix:
198
+ command_name = f"{call_prefix} {command_name}"
199
+ if not title:
200
+ title = f"`{command_name}`" if command_name else "CLI"
201
+ docs += f" {title}\n\n"
202
+ if obj.help:
203
+ docs += f"{_parse_html(obj.help)}\n\n"
204
+ usage_pieces = obj.collect_usage_pieces(ctx)
205
+ if usage_pieces:
206
+ docs += "**Usage**:\n\n"
207
+ docs += "```console\n"
208
+ docs += "$ "
209
+ if command_name:
210
+ docs += f"{command_name} "
211
+ docs += f"{' '.join(usage_pieces)}\n"
212
+ docs += "```\n\n"
213
+ args = []
214
+ opts = []
215
+ for param in obj.get_params(ctx):
216
+ rv = param.get_help_record(ctx)
217
+ if rv is not None:
218
+ if param.param_type_name == "argument":
219
+ args.append(rv)
220
+ elif param.param_type_name == "option":
221
+ opts.append(rv)
222
+ if args:
223
+ docs += "**Arguments**:\n\n"
224
+ for arg_name, arg_help in args:
225
+ docs += f"* `{arg_name}`"
226
+ if arg_help:
227
+ docs += f": {_parse_html(arg_help)}"
228
+ docs += "\n"
229
+ docs += "\n"
230
+ if opts:
231
+ docs += "**Options**:\n\n"
232
+ for opt_name, opt_help in opts:
233
+ docs += f"* `{opt_name}`"
234
+ if opt_help:
235
+ docs += f": {_parse_html(opt_help)}"
236
+ docs += "\n"
237
+ docs += "\n"
238
+ if obj.epilog:
239
+ docs += f"{obj.epilog}\n\n"
240
+ if isinstance(obj, Group):
241
+ group = obj
242
+ commands = group.list_commands(ctx)
243
+ if commands:
244
+ docs += "**Commands**:\n\n"
245
+ for command in commands:
246
+ command_obj = group.get_command(ctx, command)
247
+ assert command_obj
248
+ docs += f"* `{command_obj.name}`"
249
+ command_help = command_obj.get_short_help_str()
250
+ if command_help:
251
+ docs += f": {_parse_html(command_help)}"
252
+ docs += "\n"
253
+ docs += "\n"
254
+ for command in commands:
255
+ command_obj = group.get_command(ctx, command)
256
+ assert command_obj
257
+ use_prefix = ""
258
+ if command_name:
259
+ use_prefix += f"{command_name}"
260
+ docs += get_docs_for_click(
261
+ obj=command_obj, ctx=ctx, indent=indent + 1, call_prefix=use_prefix
262
+ )
263
+ return docs
264
+
265
+
266
+ def _parse_html(input_text: str) -> str:
267
+ if not HAS_RICH: # pragma: no cover
268
+ return input_text
269
+ from . import rich_utils
270
+
271
+ return rich_utils.rich_to_html(input_text)
272
+
273
+
274
+ @utils_app.command()
275
+ def docs(
276
+ ctx: typer.Context,
277
+ name: str = typer.Option("", help="The name of the CLI program to use in docs."),
278
+ output: Optional[Path] = typer.Option(
279
+ None,
280
+ help="An output file to write docs to, like README.md.",
281
+ file_okay=True,
282
+ dir_okay=False,
283
+ ),
284
+ title: Optional[str] = typer.Option(
285
+ None,
286
+ help="The title for the documentation page. If not provided, the name of "
287
+ "the program is used.",
288
+ ),
289
+ ) -> None:
290
+ """
291
+ Generate Markdown docs for a Typer app.
292
+ """
293
+ typer_obj = get_typer_from_state()
294
+ if not typer_obj:
295
+ typer.echo("No Typer app found", err=True)
296
+ raise typer.Abort()
297
+ click_obj = typer.main.get_command(typer_obj)
298
+ docs = get_docs_for_click(obj=click_obj, ctx=ctx, name=name, title=title)
299
+ clean_docs = f"{docs.strip()}\n"
300
+ if output:
301
+ output.write_text(clean_docs)
302
+ typer.echo(f"Docs saved to: {output}")
303
+ else:
304
+ typer.echo(clean_docs)
305
+
306
+
307
+ def main() -> Any:
308
+ return app()
env/lib/python3.13/site-packages/typer/colors.py ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Variable names to colors, just for completion
2
+ BLACK = "black"
3
+ RED = "red"
4
+ GREEN = "green"
5
+ YELLOW = "yellow"
6
+ BLUE = "blue"
7
+ MAGENTA = "magenta"
8
+ CYAN = "cyan"
9
+ WHITE = "white"
10
+
11
+ RESET = "reset"
12
+
13
+ BRIGHT_BLACK = "bright_black"
14
+ BRIGHT_RED = "bright_red"
15
+ BRIGHT_GREEN = "bright_green"
16
+ BRIGHT_YELLOW = "bright_yellow"
17
+ BRIGHT_BLUE = "bright_blue"
18
+ BRIGHT_MAGENTA = "bright_magenta"
19
+ BRIGHT_CYAN = "bright_cyan"
20
+ BRIGHT_WHITE = "bright_white"
env/lib/python3.13/site-packages/typer/models.py ADDED
@@ -0,0 +1,544 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import inspect
2
+ import io
3
+ from typing import (
4
+ TYPE_CHECKING,
5
+ Any,
6
+ Callable,
7
+ Dict,
8
+ List,
9
+ Optional,
10
+ Sequence,
11
+ Type,
12
+ TypeVar,
13
+ Union,
14
+ )
15
+
16
+ import click
17
+ import click.shell_completion
18
+
19
+ if TYPE_CHECKING: # pragma: no cover
20
+ from .core import TyperCommand, TyperGroup
21
+ from .main import Typer
22
+
23
+
24
+ NoneType = type(None)
25
+
26
+ AnyType = Type[Any]
27
+
28
+ Required = ...
29
+
30
+
31
+ class Context(click.Context):
32
+ pass
33
+
34
+
35
+ class FileText(io.TextIOWrapper):
36
+ pass
37
+
38
+
39
+ class FileTextWrite(FileText):
40
+ pass
41
+
42
+
43
+ class FileBinaryRead(io.BufferedReader):
44
+ pass
45
+
46
+
47
+ class FileBinaryWrite(io.BufferedWriter):
48
+ pass
49
+
50
+
51
+ class CallbackParam(click.Parameter):
52
+ pass
53
+
54
+
55
+ class DefaultPlaceholder:
56
+ """
57
+ You shouldn't use this class directly.
58
+
59
+ It's used internally to recognize when a default value has been overwritten, even
60
+ if the new value is `None`.
61
+ """
62
+
63
+ def __init__(self, value: Any):
64
+ self.value = value
65
+
66
+ def __bool__(self) -> bool:
67
+ return bool(self.value)
68
+
69
+
70
+ DefaultType = TypeVar("DefaultType")
71
+
72
+ CommandFunctionType = TypeVar("CommandFunctionType", bound=Callable[..., Any])
73
+
74
+
75
+ def Default(value: DefaultType) -> DefaultType:
76
+ """
77
+ You shouldn't use this function directly.
78
+
79
+ It's used internally to recognize when a default value has been overwritten, even
80
+ if the new value is `None`.
81
+ """
82
+ return DefaultPlaceholder(value) # type: ignore
83
+
84
+
85
+ class CommandInfo:
86
+ def __init__(
87
+ self,
88
+ name: Optional[str] = None,
89
+ *,
90
+ cls: Optional[Type["TyperCommand"]] = None,
91
+ context_settings: Optional[Dict[Any, Any]] = None,
92
+ callback: Optional[Callable[..., Any]] = None,
93
+ help: Optional[str] = None,
94
+ epilog: Optional[str] = None,
95
+ short_help: Optional[str] = None,
96
+ options_metavar: str = "[OPTIONS]",
97
+ add_help_option: bool = True,
98
+ no_args_is_help: bool = False,
99
+ hidden: bool = False,
100
+ deprecated: bool = False,
101
+ # Rich settings
102
+ rich_help_panel: Union[str, None] = None,
103
+ ):
104
+ self.name = name
105
+ self.cls = cls
106
+ self.context_settings = context_settings
107
+ self.callback = callback
108
+ self.help = help
109
+ self.epilog = epilog
110
+ self.short_help = short_help
111
+ self.options_metavar = options_metavar
112
+ self.add_help_option = add_help_option
113
+ self.no_args_is_help = no_args_is_help
114
+ self.hidden = hidden
115
+ self.deprecated = deprecated
116
+ # Rich settings
117
+ self.rich_help_panel = rich_help_panel
118
+
119
+
120
+ class TyperInfo:
121
+ def __init__(
122
+ self,
123
+ typer_instance: Optional["Typer"] = Default(None),
124
+ *,
125
+ name: Optional[str] = Default(None),
126
+ cls: Optional[Type["TyperGroup"]] = Default(None),
127
+ invoke_without_command: bool = Default(False),
128
+ no_args_is_help: bool = Default(False),
129
+ subcommand_metavar: Optional[str] = Default(None),
130
+ chain: bool = Default(False),
131
+ result_callback: Optional[Callable[..., Any]] = Default(None),
132
+ # Command
133
+ context_settings: Optional[Dict[Any, Any]] = Default(None),
134
+ callback: Optional[Callable[..., Any]] = Default(None),
135
+ help: Optional[str] = Default(None),
136
+ epilog: Optional[str] = Default(None),
137
+ short_help: Optional[str] = Default(None),
138
+ options_metavar: str = Default("[OPTIONS]"),
139
+ add_help_option: bool = Default(True),
140
+ hidden: bool = Default(False),
141
+ deprecated: bool = Default(False),
142
+ # Rich settings
143
+ rich_help_panel: Union[str, None] = Default(None),
144
+ ):
145
+ self.typer_instance = typer_instance
146
+ self.name = name
147
+ self.cls = cls
148
+ self.invoke_without_command = invoke_without_command
149
+ self.no_args_is_help = no_args_is_help
150
+ self.subcommand_metavar = subcommand_metavar
151
+ self.chain = chain
152
+ self.result_callback = result_callback
153
+ self.context_settings = context_settings
154
+ self.callback = callback
155
+ self.help = help
156
+ self.epilog = epilog
157
+ self.short_help = short_help
158
+ self.options_metavar = options_metavar
159
+ self.add_help_option = add_help_option
160
+ self.hidden = hidden
161
+ self.deprecated = deprecated
162
+ self.rich_help_panel = rich_help_panel
163
+
164
+
165
+ class ParameterInfo:
166
+ def __init__(
167
+ self,
168
+ *,
169
+ default: Optional[Any] = None,
170
+ param_decls: Optional[Sequence[str]] = None,
171
+ callback: Optional[Callable[..., Any]] = None,
172
+ metavar: Optional[str] = None,
173
+ expose_value: bool = True,
174
+ is_eager: bool = False,
175
+ envvar: Optional[Union[str, List[str]]] = None,
176
+ # Note that shell_complete is not fully supported and will be removed in future versions
177
+ # TODO: Remove shell_complete in a future version (after 0.16.0)
178
+ shell_complete: Optional[
179
+ Callable[
180
+ [click.Context, click.Parameter, str],
181
+ Union[List["click.shell_completion.CompletionItem"], List[str]],
182
+ ]
183
+ ] = None,
184
+ autocompletion: Optional[Callable[..., Any]] = None,
185
+ default_factory: Optional[Callable[[], Any]] = None,
186
+ # Custom type
187
+ parser: Optional[Callable[[str], Any]] = None,
188
+ click_type: Optional[click.ParamType] = None,
189
+ # TyperArgument
190
+ show_default: Union[bool, str] = True,
191
+ show_choices: bool = True,
192
+ show_envvar: bool = True,
193
+ help: Optional[str] = None,
194
+ hidden: bool = False,
195
+ # Choice
196
+ case_sensitive: bool = True,
197
+ # Numbers
198
+ min: Optional[Union[int, float]] = None,
199
+ max: Optional[Union[int, float]] = None,
200
+ clamp: bool = False,
201
+ # DateTime
202
+ formats: Optional[List[str]] = None,
203
+ # File
204
+ mode: Optional[str] = None,
205
+ encoding: Optional[str] = None,
206
+ errors: Optional[str] = "strict",
207
+ lazy: Optional[bool] = None,
208
+ atomic: bool = False,
209
+ # Path
210
+ exists: bool = False,
211
+ file_okay: bool = True,
212
+ dir_okay: bool = True,
213
+ writable: bool = False,
214
+ readable: bool = True,
215
+ resolve_path: bool = False,
216
+ allow_dash: bool = False,
217
+ path_type: Union[None, Type[str], Type[bytes]] = None,
218
+ # Rich settings
219
+ rich_help_panel: Union[str, None] = None,
220
+ ):
221
+ # Check if user has provided multiple custom parsers
222
+ if parser and click_type:
223
+ raise ValueError(
224
+ "Multiple custom type parsers provided. "
225
+ "`parser` and `click_type` may not both be provided."
226
+ )
227
+
228
+ self.default = default
229
+ self.param_decls = param_decls
230
+ self.callback = callback
231
+ self.metavar = metavar
232
+ self.expose_value = expose_value
233
+ self.is_eager = is_eager
234
+ self.envvar = envvar
235
+ self.shell_complete = shell_complete
236
+ self.autocompletion = autocompletion
237
+ self.default_factory = default_factory
238
+ # Custom type
239
+ self.parser = parser
240
+ self.click_type = click_type
241
+ # TyperArgument
242
+ self.show_default = show_default
243
+ self.show_choices = show_choices
244
+ self.show_envvar = show_envvar
245
+ self.help = help
246
+ self.hidden = hidden
247
+ # Choice
248
+ self.case_sensitive = case_sensitive
249
+ # Numbers
250
+ self.min = min
251
+ self.max = max
252
+ self.clamp = clamp
253
+ # DateTime
254
+ self.formats = formats
255
+ # File
256
+ self.mode = mode
257
+ self.encoding = encoding
258
+ self.errors = errors
259
+ self.lazy = lazy
260
+ self.atomic = atomic
261
+ # Path
262
+ self.exists = exists
263
+ self.file_okay = file_okay
264
+ self.dir_okay = dir_okay
265
+ self.writable = writable
266
+ self.readable = readable
267
+ self.resolve_path = resolve_path
268
+ self.allow_dash = allow_dash
269
+ self.path_type = path_type
270
+ # Rich settings
271
+ self.rich_help_panel = rich_help_panel
272
+
273
+
274
+ class OptionInfo(ParameterInfo):
275
+ def __init__(
276
+ self,
277
+ *,
278
+ # ParameterInfo
279
+ default: Optional[Any] = None,
280
+ param_decls: Optional[Sequence[str]] = None,
281
+ callback: Optional[Callable[..., Any]] = None,
282
+ metavar: Optional[str] = None,
283
+ expose_value: bool = True,
284
+ is_eager: bool = False,
285
+ envvar: Optional[Union[str, List[str]]] = None,
286
+ # Note that shell_complete is not fully supported and will be removed in future versions
287
+ # TODO: Remove shell_complete in a future version (after 0.16.0)
288
+ shell_complete: Optional[
289
+ Callable[
290
+ [click.Context, click.Parameter, str],
291
+ Union[List["click.shell_completion.CompletionItem"], List[str]],
292
+ ]
293
+ ] = None,
294
+ autocompletion: Optional[Callable[..., Any]] = None,
295
+ default_factory: Optional[Callable[[], Any]] = None,
296
+ # Custom type
297
+ parser: Optional[Callable[[str], Any]] = None,
298
+ click_type: Optional[click.ParamType] = None,
299
+ # Option
300
+ show_default: Union[bool, str] = True,
301
+ prompt: Union[bool, str] = False,
302
+ confirmation_prompt: bool = False,
303
+ prompt_required: bool = True,
304
+ hide_input: bool = False,
305
+ # TODO: remove is_flag and flag_value in a future release
306
+ is_flag: Optional[bool] = None,
307
+ flag_value: Optional[Any] = None,
308
+ count: bool = False,
309
+ allow_from_autoenv: bool = True,
310
+ help: Optional[str] = None,
311
+ hidden: bool = False,
312
+ show_choices: bool = True,
313
+ show_envvar: bool = True,
314
+ # Choice
315
+ case_sensitive: bool = True,
316
+ # Numbers
317
+ min: Optional[Union[int, float]] = None,
318
+ max: Optional[Union[int, float]] = None,
319
+ clamp: bool = False,
320
+ # DateTime
321
+ formats: Optional[List[str]] = None,
322
+ # File
323
+ mode: Optional[str] = None,
324
+ encoding: Optional[str] = None,
325
+ errors: Optional[str] = "strict",
326
+ lazy: Optional[bool] = None,
327
+ atomic: bool = False,
328
+ # Path
329
+ exists: bool = False,
330
+ file_okay: bool = True,
331
+ dir_okay: bool = True,
332
+ writable: bool = False,
333
+ readable: bool = True,
334
+ resolve_path: bool = False,
335
+ allow_dash: bool = False,
336
+ path_type: Union[None, Type[str], Type[bytes]] = None,
337
+ # Rich settings
338
+ rich_help_panel: Union[str, None] = None,
339
+ ):
340
+ super().__init__(
341
+ default=default,
342
+ param_decls=param_decls,
343
+ callback=callback,
344
+ metavar=metavar,
345
+ expose_value=expose_value,
346
+ is_eager=is_eager,
347
+ envvar=envvar,
348
+ shell_complete=shell_complete,
349
+ autocompletion=autocompletion,
350
+ default_factory=default_factory,
351
+ # Custom type
352
+ parser=parser,
353
+ click_type=click_type,
354
+ # TyperArgument
355
+ show_default=show_default,
356
+ show_choices=show_choices,
357
+ show_envvar=show_envvar,
358
+ help=help,
359
+ hidden=hidden,
360
+ # Choice
361
+ case_sensitive=case_sensitive,
362
+ # Numbers
363
+ min=min,
364
+ max=max,
365
+ clamp=clamp,
366
+ # DateTime
367
+ formats=formats,
368
+ # File
369
+ mode=mode,
370
+ encoding=encoding,
371
+ errors=errors,
372
+ lazy=lazy,
373
+ atomic=atomic,
374
+ # Path
375
+ exists=exists,
376
+ file_okay=file_okay,
377
+ dir_okay=dir_okay,
378
+ writable=writable,
379
+ readable=readable,
380
+ resolve_path=resolve_path,
381
+ allow_dash=allow_dash,
382
+ path_type=path_type,
383
+ # Rich settings
384
+ rich_help_panel=rich_help_panel,
385
+ )
386
+ if is_flag is not None or flag_value is not None:
387
+ import warnings
388
+
389
+ warnings.warn(
390
+ "The 'is_flag' and 'flag_value' parameters are not supported by Typer "
391
+ "and will be removed entirely in a future release.",
392
+ DeprecationWarning,
393
+ stacklevel=2,
394
+ )
395
+ self.prompt = prompt
396
+ self.confirmation_prompt = confirmation_prompt
397
+ self.prompt_required = prompt_required
398
+ self.hide_input = hide_input
399
+ self.count = count
400
+ self.allow_from_autoenv = allow_from_autoenv
401
+
402
+
403
+ class ArgumentInfo(ParameterInfo):
404
+ def __init__(
405
+ self,
406
+ *,
407
+ # ParameterInfo
408
+ default: Optional[Any] = None,
409
+ param_decls: Optional[Sequence[str]] = None,
410
+ callback: Optional[Callable[..., Any]] = None,
411
+ metavar: Optional[str] = None,
412
+ expose_value: bool = True,
413
+ is_eager: bool = False,
414
+ envvar: Optional[Union[str, List[str]]] = None,
415
+ # Note that shell_complete is not fully supported and will be removed in future versions
416
+ # TODO: Remove shell_complete in a future version (after 0.16.0)
417
+ shell_complete: Optional[
418
+ Callable[
419
+ [click.Context, click.Parameter, str],
420
+ Union[List["click.shell_completion.CompletionItem"], List[str]],
421
+ ]
422
+ ] = None,
423
+ autocompletion: Optional[Callable[..., Any]] = None,
424
+ default_factory: Optional[Callable[[], Any]] = None,
425
+ # Custom type
426
+ parser: Optional[Callable[[str], Any]] = None,
427
+ click_type: Optional[click.ParamType] = None,
428
+ # TyperArgument
429
+ show_default: Union[bool, str] = True,
430
+ show_choices: bool = True,
431
+ show_envvar: bool = True,
432
+ help: Optional[str] = None,
433
+ hidden: bool = False,
434
+ # Choice
435
+ case_sensitive: bool = True,
436
+ # Numbers
437
+ min: Optional[Union[int, float]] = None,
438
+ max: Optional[Union[int, float]] = None,
439
+ clamp: bool = False,
440
+ # DateTime
441
+ formats: Optional[List[str]] = None,
442
+ # File
443
+ mode: Optional[str] = None,
444
+ encoding: Optional[str] = None,
445
+ errors: Optional[str] = "strict",
446
+ lazy: Optional[bool] = None,
447
+ atomic: bool = False,
448
+ # Path
449
+ exists: bool = False,
450
+ file_okay: bool = True,
451
+ dir_okay: bool = True,
452
+ writable: bool = False,
453
+ readable: bool = True,
454
+ resolve_path: bool = False,
455
+ allow_dash: bool = False,
456
+ path_type: Union[None, Type[str], Type[bytes]] = None,
457
+ # Rich settings
458
+ rich_help_panel: Union[str, None] = None,
459
+ ):
460
+ super().__init__(
461
+ default=default,
462
+ param_decls=param_decls,
463
+ callback=callback,
464
+ metavar=metavar,
465
+ expose_value=expose_value,
466
+ is_eager=is_eager,
467
+ envvar=envvar,
468
+ shell_complete=shell_complete,
469
+ autocompletion=autocompletion,
470
+ default_factory=default_factory,
471
+ # Custom type
472
+ parser=parser,
473
+ click_type=click_type,
474
+ # TyperArgument
475
+ show_default=show_default,
476
+ show_choices=show_choices,
477
+ show_envvar=show_envvar,
478
+ help=help,
479
+ hidden=hidden,
480
+ # Choice
481
+ case_sensitive=case_sensitive,
482
+ # Numbers
483
+ min=min,
484
+ max=max,
485
+ clamp=clamp,
486
+ # DateTime
487
+ formats=formats,
488
+ # File
489
+ mode=mode,
490
+ encoding=encoding,
491
+ errors=errors,
492
+ lazy=lazy,
493
+ atomic=atomic,
494
+ # Path
495
+ exists=exists,
496
+ file_okay=file_okay,
497
+ dir_okay=dir_okay,
498
+ writable=writable,
499
+ readable=readable,
500
+ resolve_path=resolve_path,
501
+ allow_dash=allow_dash,
502
+ path_type=path_type,
503
+ # Rich settings
504
+ rich_help_panel=rich_help_panel,
505
+ )
506
+
507
+
508
+ class ParamMeta:
509
+ empty = inspect.Parameter.empty
510
+
511
+ def __init__(
512
+ self,
513
+ *,
514
+ name: str,
515
+ default: Any = inspect.Parameter.empty,
516
+ annotation: Any = inspect.Parameter.empty,
517
+ ) -> None:
518
+ self.name = name
519
+ self.default = default
520
+ self.annotation = annotation
521
+
522
+
523
+ class DeveloperExceptionConfig:
524
+ def __init__(
525
+ self,
526
+ *,
527
+ pretty_exceptions_enable: bool = True,
528
+ pretty_exceptions_show_locals: bool = True,
529
+ pretty_exceptions_short: bool = True,
530
+ ) -> None:
531
+ self.pretty_exceptions_enable = pretty_exceptions_enable
532
+ self.pretty_exceptions_show_locals = pretty_exceptions_show_locals
533
+ self.pretty_exceptions_short = pretty_exceptions_short
534
+
535
+
536
+ class TyperPath(click.Path):
537
+ # Overwrite Click's behaviour to be compatible with Typer's autocompletion system
538
+ def shell_complete(
539
+ self, ctx: click.Context, param: click.Parameter, incomplete: str
540
+ ) -> List[click.shell_completion.CompletionItem]:
541
+ """Return an empty list so that the autocompletion functionality
542
+ will work properly from the commandline.
543
+ """
544
+ return []
env/lib/python3.13/site-packages/typer/params.py ADDED
@@ -0,0 +1,479 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import TYPE_CHECKING, Any, Callable, List, Optional, Type, Union, overload
2
+
3
+ import click
4
+
5
+ from .models import ArgumentInfo, OptionInfo
6
+
7
+ if TYPE_CHECKING: # pragma: no cover
8
+ import click.shell_completion
9
+
10
+
11
+ # Overload for Option created with custom type 'parser'
12
+ @overload
13
+ def Option(
14
+ # Parameter
15
+ default: Optional[Any] = ...,
16
+ *param_decls: str,
17
+ callback: Optional[Callable[..., Any]] = None,
18
+ metavar: Optional[str] = None,
19
+ expose_value: bool = True,
20
+ is_eager: bool = False,
21
+ envvar: Optional[Union[str, List[str]]] = None,
22
+ # Note that shell_complete is not fully supported and will be removed in future versions
23
+ # TODO: Remove shell_complete in a future version (after 0.16.0)
24
+ shell_complete: Optional[
25
+ Callable[
26
+ [click.Context, click.Parameter, str],
27
+ Union[List["click.shell_completion.CompletionItem"], List[str]],
28
+ ]
29
+ ] = None,
30
+ autocompletion: Optional[Callable[..., Any]] = None,
31
+ default_factory: Optional[Callable[[], Any]] = None,
32
+ # Custom type
33
+ parser: Optional[Callable[[str], Any]] = None,
34
+ # Option
35
+ show_default: Union[bool, str] = True,
36
+ prompt: Union[bool, str] = False,
37
+ confirmation_prompt: bool = False,
38
+ prompt_required: bool = True,
39
+ hide_input: bool = False,
40
+ # TODO: remove is_flag and flag_value in a future release
41
+ is_flag: Optional[bool] = None,
42
+ flag_value: Optional[Any] = None,
43
+ count: bool = False,
44
+ allow_from_autoenv: bool = True,
45
+ help: Optional[str] = None,
46
+ hidden: bool = False,
47
+ show_choices: bool = True,
48
+ show_envvar: bool = True,
49
+ # Choice
50
+ case_sensitive: bool = True,
51
+ # Numbers
52
+ min: Optional[Union[int, float]] = None,
53
+ max: Optional[Union[int, float]] = None,
54
+ clamp: bool = False,
55
+ # DateTime
56
+ formats: Optional[List[str]] = None,
57
+ # File
58
+ mode: Optional[str] = None,
59
+ encoding: Optional[str] = None,
60
+ errors: Optional[str] = "strict",
61
+ lazy: Optional[bool] = None,
62
+ atomic: bool = False,
63
+ # Path
64
+ exists: bool = False,
65
+ file_okay: bool = True,
66
+ dir_okay: bool = True,
67
+ writable: bool = False,
68
+ readable: bool = True,
69
+ resolve_path: bool = False,
70
+ allow_dash: bool = False,
71
+ path_type: Union[None, Type[str], Type[bytes]] = None,
72
+ # Rich settings
73
+ rich_help_panel: Union[str, None] = None,
74
+ ) -> Any: ...
75
+
76
+
77
+ # Overload for Option created with custom type 'click_type'
78
+ @overload
79
+ def Option(
80
+ # Parameter
81
+ default: Optional[Any] = ...,
82
+ *param_decls: str,
83
+ callback: Optional[Callable[..., Any]] = None,
84
+ metavar: Optional[str] = None,
85
+ expose_value: bool = True,
86
+ is_eager: bool = False,
87
+ envvar: Optional[Union[str, List[str]]] = None,
88
+ # Note that shell_complete is not fully supported and will be removed in future versions
89
+ # TODO: Remove shell_complete in a future version (after 0.16.0)
90
+ shell_complete: Optional[
91
+ Callable[
92
+ [click.Context, click.Parameter, str],
93
+ Union[List["click.shell_completion.CompletionItem"], List[str]],
94
+ ]
95
+ ] = None,
96
+ autocompletion: Optional[Callable[..., Any]] = None,
97
+ default_factory: Optional[Callable[[], Any]] = None,
98
+ # Custom type
99
+ click_type: Optional[click.ParamType] = None,
100
+ # Option
101
+ show_default: Union[bool, str] = True,
102
+ prompt: Union[bool, str] = False,
103
+ confirmation_prompt: bool = False,
104
+ prompt_required: bool = True,
105
+ hide_input: bool = False,
106
+ # TODO: remove is_flag and flag_value in a future release
107
+ is_flag: Optional[bool] = None,
108
+ flag_value: Optional[Any] = None,
109
+ count: bool = False,
110
+ allow_from_autoenv: bool = True,
111
+ help: Optional[str] = None,
112
+ hidden: bool = False,
113
+ show_choices: bool = True,
114
+ show_envvar: bool = True,
115
+ # Choice
116
+ case_sensitive: bool = True,
117
+ # Numbers
118
+ min: Optional[Union[int, float]] = None,
119
+ max: Optional[Union[int, float]] = None,
120
+ clamp: bool = False,
121
+ # DateTime
122
+ formats: Optional[List[str]] = None,
123
+ # File
124
+ mode: Optional[str] = None,
125
+ encoding: Optional[str] = None,
126
+ errors: Optional[str] = "strict",
127
+ lazy: Optional[bool] = None,
128
+ atomic: bool = False,
129
+ # Path
130
+ exists: bool = False,
131
+ file_okay: bool = True,
132
+ dir_okay: bool = True,
133
+ writable: bool = False,
134
+ readable: bool = True,
135
+ resolve_path: bool = False,
136
+ allow_dash: bool = False,
137
+ path_type: Union[None, Type[str], Type[bytes]] = None,
138
+ # Rich settings
139
+ rich_help_panel: Union[str, None] = None,
140
+ ) -> Any: ...
141
+
142
+
143
+ def Option(
144
+ # Parameter
145
+ default: Optional[Any] = ...,
146
+ *param_decls: str,
147
+ callback: Optional[Callable[..., Any]] = None,
148
+ metavar: Optional[str] = None,
149
+ expose_value: bool = True,
150
+ is_eager: bool = False,
151
+ envvar: Optional[Union[str, List[str]]] = None,
152
+ # Note that shell_complete is not fully supported and will be removed in future versions
153
+ # TODO: Remove shell_complete in a future version (after 0.16.0)
154
+ shell_complete: Optional[
155
+ Callable[
156
+ [click.Context, click.Parameter, str],
157
+ Union[List["click.shell_completion.CompletionItem"], List[str]],
158
+ ]
159
+ ] = None,
160
+ autocompletion: Optional[Callable[..., Any]] = None,
161
+ default_factory: Optional[Callable[[], Any]] = None,
162
+ # Custom type
163
+ parser: Optional[Callable[[str], Any]] = None,
164
+ click_type: Optional[click.ParamType] = None,
165
+ # Option
166
+ show_default: Union[bool, str] = True,
167
+ prompt: Union[bool, str] = False,
168
+ confirmation_prompt: bool = False,
169
+ prompt_required: bool = True,
170
+ hide_input: bool = False,
171
+ # TODO: remove is_flag and flag_value in a future release
172
+ is_flag: Optional[bool] = None,
173
+ flag_value: Optional[Any] = None,
174
+ count: bool = False,
175
+ allow_from_autoenv: bool = True,
176
+ help: Optional[str] = None,
177
+ hidden: bool = False,
178
+ show_choices: bool = True,
179
+ show_envvar: bool = True,
180
+ # Choice
181
+ case_sensitive: bool = True,
182
+ # Numbers
183
+ min: Optional[Union[int, float]] = None,
184
+ max: Optional[Union[int, float]] = None,
185
+ clamp: bool = False,
186
+ # DateTime
187
+ formats: Optional[List[str]] = None,
188
+ # File
189
+ mode: Optional[str] = None,
190
+ encoding: Optional[str] = None,
191
+ errors: Optional[str] = "strict",
192
+ lazy: Optional[bool] = None,
193
+ atomic: bool = False,
194
+ # Path
195
+ exists: bool = False,
196
+ file_okay: bool = True,
197
+ dir_okay: bool = True,
198
+ writable: bool = False,
199
+ readable: bool = True,
200
+ resolve_path: bool = False,
201
+ allow_dash: bool = False,
202
+ path_type: Union[None, Type[str], Type[bytes]] = None,
203
+ # Rich settings
204
+ rich_help_panel: Union[str, None] = None,
205
+ ) -> Any:
206
+ return OptionInfo(
207
+ # Parameter
208
+ default=default,
209
+ param_decls=param_decls,
210
+ callback=callback,
211
+ metavar=metavar,
212
+ expose_value=expose_value,
213
+ is_eager=is_eager,
214
+ envvar=envvar,
215
+ shell_complete=shell_complete,
216
+ autocompletion=autocompletion,
217
+ default_factory=default_factory,
218
+ # Custom type
219
+ parser=parser,
220
+ click_type=click_type,
221
+ # Option
222
+ show_default=show_default,
223
+ prompt=prompt,
224
+ confirmation_prompt=confirmation_prompt,
225
+ prompt_required=prompt_required,
226
+ hide_input=hide_input,
227
+ is_flag=is_flag,
228
+ flag_value=flag_value,
229
+ count=count,
230
+ allow_from_autoenv=allow_from_autoenv,
231
+ help=help,
232
+ hidden=hidden,
233
+ show_choices=show_choices,
234
+ show_envvar=show_envvar,
235
+ # Choice
236
+ case_sensitive=case_sensitive,
237
+ # Numbers
238
+ min=min,
239
+ max=max,
240
+ clamp=clamp,
241
+ # DateTime
242
+ formats=formats,
243
+ # File
244
+ mode=mode,
245
+ encoding=encoding,
246
+ errors=errors,
247
+ lazy=lazy,
248
+ atomic=atomic,
249
+ # Path
250
+ exists=exists,
251
+ file_okay=file_okay,
252
+ dir_okay=dir_okay,
253
+ writable=writable,
254
+ readable=readable,
255
+ resolve_path=resolve_path,
256
+ allow_dash=allow_dash,
257
+ path_type=path_type,
258
+ # Rich settings
259
+ rich_help_panel=rich_help_panel,
260
+ )
261
+
262
+
263
+ # Overload for Argument created with custom type 'parser'
264
+ @overload
265
+ def Argument(
266
+ # Parameter
267
+ default: Optional[Any] = ...,
268
+ *,
269
+ callback: Optional[Callable[..., Any]] = None,
270
+ metavar: Optional[str] = None,
271
+ expose_value: bool = True,
272
+ is_eager: bool = False,
273
+ envvar: Optional[Union[str, List[str]]] = None,
274
+ # Note that shell_complete is not fully supported and will be removed in future versions
275
+ # TODO: Remove shell_complete in a future version (after 0.16.0)
276
+ shell_complete: Optional[
277
+ Callable[
278
+ [click.Context, click.Parameter, str],
279
+ Union[List["click.shell_completion.CompletionItem"], List[str]],
280
+ ]
281
+ ] = None,
282
+ autocompletion: Optional[Callable[..., Any]] = None,
283
+ default_factory: Optional[Callable[[], Any]] = None,
284
+ # Custom type
285
+ parser: Optional[Callable[[str], Any]] = None,
286
+ # TyperArgument
287
+ show_default: Union[bool, str] = True,
288
+ show_choices: bool = True,
289
+ show_envvar: bool = True,
290
+ help: Optional[str] = None,
291
+ hidden: bool = False,
292
+ # Choice
293
+ case_sensitive: bool = True,
294
+ # Numbers
295
+ min: Optional[Union[int, float]] = None,
296
+ max: Optional[Union[int, float]] = None,
297
+ clamp: bool = False,
298
+ # DateTime
299
+ formats: Optional[List[str]] = None,
300
+ # File
301
+ mode: Optional[str] = None,
302
+ encoding: Optional[str] = None,
303
+ errors: Optional[str] = "strict",
304
+ lazy: Optional[bool] = None,
305
+ atomic: bool = False,
306
+ # Path
307
+ exists: bool = False,
308
+ file_okay: bool = True,
309
+ dir_okay: bool = True,
310
+ writable: bool = False,
311
+ readable: bool = True,
312
+ resolve_path: bool = False,
313
+ allow_dash: bool = False,
314
+ path_type: Union[None, Type[str], Type[bytes]] = None,
315
+ # Rich settings
316
+ rich_help_panel: Union[str, None] = None,
317
+ ) -> Any: ...
318
+
319
+
320
+ # Overload for Argument created with custom type 'click_type'
321
+ @overload
322
+ def Argument(
323
+ # Parameter
324
+ default: Optional[Any] = ...,
325
+ *,
326
+ callback: Optional[Callable[..., Any]] = None,
327
+ metavar: Optional[str] = None,
328
+ expose_value: bool = True,
329
+ is_eager: bool = False,
330
+ envvar: Optional[Union[str, List[str]]] = None,
331
+ # Note that shell_complete is not fully supported and will be removed in future versions
332
+ # TODO: Remove shell_complete in a future version (after 0.16.0)
333
+ shell_complete: Optional[
334
+ Callable[
335
+ [click.Context, click.Parameter, str],
336
+ Union[List["click.shell_completion.CompletionItem"], List[str]],
337
+ ]
338
+ ] = None,
339
+ autocompletion: Optional[Callable[..., Any]] = None,
340
+ default_factory: Optional[Callable[[], Any]] = None,
341
+ # Custom type
342
+ click_type: Optional[click.ParamType] = None,
343
+ # TyperArgument
344
+ show_default: Union[bool, str] = True,
345
+ show_choices: bool = True,
346
+ show_envvar: bool = True,
347
+ help: Optional[str] = None,
348
+ hidden: bool = False,
349
+ # Choice
350
+ case_sensitive: bool = True,
351
+ # Numbers
352
+ min: Optional[Union[int, float]] = None,
353
+ max: Optional[Union[int, float]] = None,
354
+ clamp: bool = False,
355
+ # DateTime
356
+ formats: Optional[List[str]] = None,
357
+ # File
358
+ mode: Optional[str] = None,
359
+ encoding: Optional[str] = None,
360
+ errors: Optional[str] = "strict",
361
+ lazy: Optional[bool] = None,
362
+ atomic: bool = False,
363
+ # Path
364
+ exists: bool = False,
365
+ file_okay: bool = True,
366
+ dir_okay: bool = True,
367
+ writable: bool = False,
368
+ readable: bool = True,
369
+ resolve_path: bool = False,
370
+ allow_dash: bool = False,
371
+ path_type: Union[None, Type[str], Type[bytes]] = None,
372
+ # Rich settings
373
+ rich_help_panel: Union[str, None] = None,
374
+ ) -> Any: ...
375
+
376
+
377
+ def Argument(
378
+ # Parameter
379
+ default: Optional[Any] = ...,
380
+ *,
381
+ callback: Optional[Callable[..., Any]] = None,
382
+ metavar: Optional[str] = None,
383
+ expose_value: bool = True,
384
+ is_eager: bool = False,
385
+ envvar: Optional[Union[str, List[str]]] = None,
386
+ # Note that shell_complete is not fully supported and will be removed in future versions
387
+ # TODO: Remove shell_complete in a future version (after 0.16.0)
388
+ shell_complete: Optional[
389
+ Callable[
390
+ [click.Context, click.Parameter, str],
391
+ Union[List["click.shell_completion.CompletionItem"], List[str]],
392
+ ]
393
+ ] = None,
394
+ autocompletion: Optional[Callable[..., Any]] = None,
395
+ default_factory: Optional[Callable[[], Any]] = None,
396
+ # Custom type
397
+ parser: Optional[Callable[[str], Any]] = None,
398
+ click_type: Optional[click.ParamType] = None,
399
+ # TyperArgument
400
+ show_default: Union[bool, str] = True,
401
+ show_choices: bool = True,
402
+ show_envvar: bool = True,
403
+ help: Optional[str] = None,
404
+ hidden: bool = False,
405
+ # Choice
406
+ case_sensitive: bool = True,
407
+ # Numbers
408
+ min: Optional[Union[int, float]] = None,
409
+ max: Optional[Union[int, float]] = None,
410
+ clamp: bool = False,
411
+ # DateTime
412
+ formats: Optional[List[str]] = None,
413
+ # File
414
+ mode: Optional[str] = None,
415
+ encoding: Optional[str] = None,
416
+ errors: Optional[str] = "strict",
417
+ lazy: Optional[bool] = None,
418
+ atomic: bool = False,
419
+ # Path
420
+ exists: bool = False,
421
+ file_okay: bool = True,
422
+ dir_okay: bool = True,
423
+ writable: bool = False,
424
+ readable: bool = True,
425
+ resolve_path: bool = False,
426
+ allow_dash: bool = False,
427
+ path_type: Union[None, Type[str], Type[bytes]] = None,
428
+ # Rich settings
429
+ rich_help_panel: Union[str, None] = None,
430
+ ) -> Any:
431
+ return ArgumentInfo(
432
+ # Parameter
433
+ default=default,
434
+ # Arguments can only have one param declaration
435
+ # it will be generated from the param name
436
+ param_decls=None,
437
+ callback=callback,
438
+ metavar=metavar,
439
+ expose_value=expose_value,
440
+ is_eager=is_eager,
441
+ envvar=envvar,
442
+ shell_complete=shell_complete,
443
+ autocompletion=autocompletion,
444
+ default_factory=default_factory,
445
+ # Custom type
446
+ parser=parser,
447
+ click_type=click_type,
448
+ # TyperArgument
449
+ show_default=show_default,
450
+ show_choices=show_choices,
451
+ show_envvar=show_envvar,
452
+ help=help,
453
+ hidden=hidden,
454
+ # Choice
455
+ case_sensitive=case_sensitive,
456
+ # Numbers
457
+ min=min,
458
+ max=max,
459
+ clamp=clamp,
460
+ # DateTime
461
+ formats=formats,
462
+ # File
463
+ mode=mode,
464
+ encoding=encoding,
465
+ errors=errors,
466
+ lazy=lazy,
467
+ atomic=atomic,
468
+ # Path
469
+ exists=exists,
470
+ file_okay=file_okay,
471
+ dir_okay=dir_okay,
472
+ writable=writable,
473
+ readable=readable,
474
+ resolve_path=resolve_path,
475
+ allow_dash=allow_dash,
476
+ path_type=path_type,
477
+ # Rich settings
478
+ rich_help_panel=rich_help_panel,
479
+ )
env/lib/python3.13/site-packages/typer/utils.py ADDED
@@ -0,0 +1,190 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import inspect
2
+ import sys
3
+ from copy import copy
4
+ from typing import Any, Callable, Dict, List, Tuple, Type, cast
5
+
6
+ from ._typing import Annotated, get_args, get_origin, get_type_hints
7
+ from .models import ArgumentInfo, OptionInfo, ParameterInfo, ParamMeta
8
+
9
+
10
+ def _param_type_to_user_string(param_type: Type[ParameterInfo]) -> str:
11
+ # Render a `ParameterInfo` subclass for use in error messages.
12
+ # User code doesn't call `*Info` directly, so errors should present the classes how
13
+ # they were (probably) defined in the user code.
14
+ if param_type is OptionInfo:
15
+ return "`Option`"
16
+ elif param_type is ArgumentInfo:
17
+ return "`Argument`"
18
+ # This line shouldn't be reachable during normal use.
19
+ return f"`{param_type.__name__}`" # pragma: no cover
20
+
21
+
22
+ class AnnotatedParamWithDefaultValueError(Exception):
23
+ argument_name: str
24
+ param_type: Type[ParameterInfo]
25
+
26
+ def __init__(self, argument_name: str, param_type: Type[ParameterInfo]):
27
+ self.argument_name = argument_name
28
+ self.param_type = param_type
29
+
30
+ def __str__(self) -> str:
31
+ param_type_str = _param_type_to_user_string(self.param_type)
32
+ return (
33
+ f"{param_type_str} default value cannot be set in `Annotated`"
34
+ f" for {self.argument_name!r}. Set the default value with `=` instead."
35
+ )
36
+
37
+
38
+ class MixedAnnotatedAndDefaultStyleError(Exception):
39
+ argument_name: str
40
+ annotated_param_type: Type[ParameterInfo]
41
+ default_param_type: Type[ParameterInfo]
42
+
43
+ def __init__(
44
+ self,
45
+ argument_name: str,
46
+ annotated_param_type: Type[ParameterInfo],
47
+ default_param_type: Type[ParameterInfo],
48
+ ):
49
+ self.argument_name = argument_name
50
+ self.annotated_param_type = annotated_param_type
51
+ self.default_param_type = default_param_type
52
+
53
+ def __str__(self) -> str:
54
+ annotated_param_type_str = _param_type_to_user_string(self.annotated_param_type)
55
+ default_param_type_str = _param_type_to_user_string(self.default_param_type)
56
+ msg = f"Cannot specify {annotated_param_type_str} in `Annotated` and"
57
+ if self.annotated_param_type is self.default_param_type:
58
+ msg += " default value"
59
+ else:
60
+ msg += f" {default_param_type_str} as a default value"
61
+ msg += f" together for {self.argument_name!r}"
62
+ return msg
63
+
64
+
65
+ class MultipleTyperAnnotationsError(Exception):
66
+ argument_name: str
67
+
68
+ def __init__(self, argument_name: str):
69
+ self.argument_name = argument_name
70
+
71
+ def __str__(self) -> str:
72
+ return (
73
+ "Cannot specify multiple `Annotated` Typer arguments"
74
+ f" for {self.argument_name!r}"
75
+ )
76
+
77
+
78
+ class DefaultFactoryAndDefaultValueError(Exception):
79
+ argument_name: str
80
+ param_type: Type[ParameterInfo]
81
+
82
+ def __init__(self, argument_name: str, param_type: Type[ParameterInfo]):
83
+ self.argument_name = argument_name
84
+ self.param_type = param_type
85
+
86
+ def __str__(self) -> str:
87
+ param_type_str = _param_type_to_user_string(self.param_type)
88
+ return (
89
+ "Cannot specify `default_factory` and a default value together"
90
+ f" for {param_type_str}"
91
+ )
92
+
93
+
94
+ def _split_annotation_from_typer_annotations(
95
+ base_annotation: Type[Any],
96
+ ) -> Tuple[Type[Any], List[ParameterInfo]]:
97
+ if get_origin(base_annotation) is not Annotated:
98
+ return base_annotation, []
99
+ base_annotation, *maybe_typer_annotations = get_args(base_annotation)
100
+ return base_annotation, [
101
+ annotation
102
+ for annotation in maybe_typer_annotations
103
+ if isinstance(annotation, ParameterInfo)
104
+ ]
105
+
106
+
107
+ def get_params_from_function(func: Callable[..., Any]) -> Dict[str, ParamMeta]:
108
+ if sys.version_info >= (3, 10):
109
+ signature = inspect.signature(func, eval_str=True)
110
+ else:
111
+ signature = inspect.signature(func)
112
+
113
+ type_hints = get_type_hints(func)
114
+ params = {}
115
+ for param in signature.parameters.values():
116
+ annotation, typer_annotations = _split_annotation_from_typer_annotations(
117
+ param.annotation,
118
+ )
119
+ if len(typer_annotations) > 1:
120
+ raise MultipleTyperAnnotationsError(param.name)
121
+
122
+ default = param.default
123
+ if typer_annotations:
124
+ # It's something like `my_param: Annotated[str, Argument()]`
125
+ [parameter_info] = typer_annotations
126
+
127
+ # Forbid `my_param: Annotated[str, Argument()] = Argument("...")`
128
+ if isinstance(param.default, ParameterInfo):
129
+ raise MixedAnnotatedAndDefaultStyleError(
130
+ argument_name=param.name,
131
+ annotated_param_type=type(parameter_info),
132
+ default_param_type=type(param.default),
133
+ )
134
+
135
+ parameter_info = copy(parameter_info)
136
+
137
+ # When used as a default, `Option` takes a default value and option names
138
+ # as positional arguments:
139
+ # `Option(some_value, "--some-argument", "-s")`
140
+ # When used in `Annotated` (ie, what this is handling), `Option` just takes
141
+ # option names as positional arguments:
142
+ # `Option("--some-argument", "-s")`
143
+ # In this case, the `default` attribute of `parameter_info` is actually
144
+ # meant to be the first item of `param_decls`.
145
+ if (
146
+ isinstance(parameter_info, OptionInfo)
147
+ and parameter_info.default is not ...
148
+ ):
149
+ parameter_info.param_decls = (
150
+ cast(str, parameter_info.default),
151
+ *(parameter_info.param_decls or ()),
152
+ )
153
+ parameter_info.default = ...
154
+
155
+ # Forbid `my_param: Annotated[str, Argument('some-default')]`
156
+ if parameter_info.default is not ...:
157
+ raise AnnotatedParamWithDefaultValueError(
158
+ param_type=type(parameter_info),
159
+ argument_name=param.name,
160
+ )
161
+ if param.default is not param.empty:
162
+ # Put the parameter's default (set by `=`) into `parameter_info`, where
163
+ # typer can find it.
164
+ parameter_info.default = param.default
165
+
166
+ default = parameter_info
167
+ elif param.name in type_hints:
168
+ # Resolve forward references.
169
+ annotation = type_hints[param.name]
170
+
171
+ if isinstance(default, ParameterInfo):
172
+ parameter_info = copy(default)
173
+ # Click supports `default` as either
174
+ # - an actual value; or
175
+ # - a factory function (returning a default value.)
176
+ # The two are not interchangeable for static typing, so typer allows
177
+ # specifying `default_factory`. Move the `default_factory` into `default`
178
+ # so click can find it.
179
+ if parameter_info.default is ... and parameter_info.default_factory:
180
+ parameter_info.default = parameter_info.default_factory
181
+ elif parameter_info.default_factory:
182
+ raise DefaultFactoryAndDefaultValueError(
183
+ argument_name=param.name, param_type=type(parameter_info)
184
+ )
185
+ default = parameter_info
186
+
187
+ params[param.name] = ParamMeta(
188
+ name=param.name, default=default, annotation=annotation
189
+ )
190
+ return params