Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- .gitattributes +2 -0
- .venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiohappyeyeballs/__init__.py +13 -0
- .venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiohappyeyeballs/__pycache__/__init__.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiohappyeyeballs/__pycache__/_staggered.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiohappyeyeballs/__pycache__/impl.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiohappyeyeballs/__pycache__/types.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiohappyeyeballs/__pycache__/utils.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiohappyeyeballs/_staggered.py +202 -0
- .venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiohappyeyeballs/impl.py +221 -0
- .venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiohappyeyeballs/py.typed +0 -0
- .venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiohappyeyeballs/types.py +12 -0
- .venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiohappyeyeballs/utils.py +97 -0
- .venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiosignal/__pycache__/__init__.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attr/__init__.pyi +389 -0
- .venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attr/_cmp.py +160 -0
- .venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attr/_cmp.pyi +13 -0
- .venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attr/_make.py +3055 -0
- .venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attr/_next_gen.py +623 -0
- .venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attr/_typing_compat.pyi +15 -0
- .venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attr/_version_info.py +86 -0
- .venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attr/converters.py +162 -0
- .venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attr/converters.pyi +19 -0
- .venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attr/exceptions.py +95 -0
- .venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attr/filters.py +72 -0
- .venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attr/filters.pyi +6 -0
- .venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attr/py.typed +0 -0
- .venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attr/setters.pyi +20 -0
- .venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attr/validators.py +710 -0
- .venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attr/validators.pyi +86 -0
- .venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attrs-25.1.0.dist-info/INSTALLER +1 -0
- .venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attrs-25.1.0.dist-info/METADATA +232 -0
- .venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attrs-25.1.0.dist-info/RECORD +55 -0
- .venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attrs-25.1.0.dist-info/WHEEL +4 -0
- .venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attrs-25.1.0.dist-info/licenses/LICENSE +21 -0
- .venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attrs/__init__.py +69 -0
- .venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attrs/__init__.pyi +263 -0
- .venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attrs/__pycache__/__init__.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attrs/__pycache__/converters.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attrs/__pycache__/exceptions.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attrs/__pycache__/filters.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attrs/__pycache__/setters.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attrs/__pycache__/validators.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attrs/converters.py +3 -0
- .venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attrs/exceptions.py +3 -0
- .venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attrs/filters.py +3 -0
- .venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attrs/py.typed +0 -0
- .venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attrs/setters.py +3 -0
- .venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attrs/validators.py +3 -0
- .venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/frozenlist/__init__.py +98 -0
- .venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/frozenlist/__init__.pyi +47 -0
.gitattributes
CHANGED
|
@@ -159,3 +159,5 @@ tuning-competition-baseline/.venv/lib/python3.11/site-packages/torch/_inductor/_
|
|
| 159 |
.venv/lib/python3.11/site-packages/xgrammar/xgrammar_bindings.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 160 |
.venv/lib/python3.11/site-packages/ray/_raylet.so filter=lfs diff=lfs merge=lfs -text
|
| 161 |
.venv/lib/python3.11/site-packages/ray/core/libjemalloc.so filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
| 159 |
.venv/lib/python3.11/site-packages/xgrammar/xgrammar_bindings.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 160 |
.venv/lib/python3.11/site-packages/ray/_raylet.so filter=lfs diff=lfs merge=lfs -text
|
| 161 |
.venv/lib/python3.11/site-packages/ray/core/libjemalloc.so filter=lfs diff=lfs merge=lfs -text
|
| 162 |
+
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/_quoting_c.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 163 |
+
.venv/lib/python3.11/site-packages/ray/dag/__pycache__/compiled_dag_node.cpython-311.pyc filter=lfs diff=lfs merge=lfs -text
|
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiohappyeyeballs/__init__.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
__version__ = "2.4.4"
|
| 2 |
+
|
| 3 |
+
from .impl import start_connection
|
| 4 |
+
from .types import AddrInfoType
|
| 5 |
+
from .utils import addr_to_addr_infos, pop_addr_infos_interleave, remove_addr_infos
|
| 6 |
+
|
| 7 |
+
__all__ = (
|
| 8 |
+
"AddrInfoType",
|
| 9 |
+
"addr_to_addr_infos",
|
| 10 |
+
"pop_addr_infos_interleave",
|
| 11 |
+
"remove_addr_infos",
|
| 12 |
+
"start_connection",
|
| 13 |
+
)
|
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiohappyeyeballs/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (568 Bytes). View file
|
|
|
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiohappyeyeballs/__pycache__/_staggered.cpython-311.pyc
ADDED
|
Binary file (8.89 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiohappyeyeballs/__pycache__/impl.cpython-311.pyc
ADDED
|
Binary file (9.98 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiohappyeyeballs/__pycache__/types.cpython-311.pyc
ADDED
|
Binary file (562 Bytes). View file
|
|
|
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiohappyeyeballs/__pycache__/utils.cpython-311.pyc
ADDED
|
Binary file (4.21 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiohappyeyeballs/_staggered.py
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import asyncio
|
| 2 |
+
import contextlib
|
| 3 |
+
from typing import (
|
| 4 |
+
TYPE_CHECKING,
|
| 5 |
+
Any,
|
| 6 |
+
Awaitable,
|
| 7 |
+
Callable,
|
| 8 |
+
Iterable,
|
| 9 |
+
List,
|
| 10 |
+
Optional,
|
| 11 |
+
Set,
|
| 12 |
+
Tuple,
|
| 13 |
+
TypeVar,
|
| 14 |
+
Union,
|
| 15 |
+
)
|
| 16 |
+
|
| 17 |
+
_T = TypeVar("_T")
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
def _set_result(wait_next: "asyncio.Future[None]") -> None:
|
| 21 |
+
"""Set the result of a future if it is not already done."""
|
| 22 |
+
if not wait_next.done():
|
| 23 |
+
wait_next.set_result(None)
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
async def _wait_one(
|
| 27 |
+
futures: "Iterable[asyncio.Future[Any]]",
|
| 28 |
+
loop: asyncio.AbstractEventLoop,
|
| 29 |
+
) -> _T:
|
| 30 |
+
"""Wait for the first future to complete."""
|
| 31 |
+
wait_next = loop.create_future()
|
| 32 |
+
|
| 33 |
+
def _on_completion(fut: "asyncio.Future[Any]") -> None:
|
| 34 |
+
if not wait_next.done():
|
| 35 |
+
wait_next.set_result(fut)
|
| 36 |
+
|
| 37 |
+
for f in futures:
|
| 38 |
+
f.add_done_callback(_on_completion)
|
| 39 |
+
|
| 40 |
+
try:
|
| 41 |
+
return await wait_next
|
| 42 |
+
finally:
|
| 43 |
+
for f in futures:
|
| 44 |
+
f.remove_done_callback(_on_completion)
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
async def staggered_race(
|
| 48 |
+
coro_fns: Iterable[Callable[[], Awaitable[_T]]],
|
| 49 |
+
delay: Optional[float],
|
| 50 |
+
*,
|
| 51 |
+
loop: Optional[asyncio.AbstractEventLoop] = None,
|
| 52 |
+
) -> Tuple[Optional[_T], Optional[int], List[Optional[BaseException]]]:
|
| 53 |
+
"""
|
| 54 |
+
Run coroutines with staggered start times and take the first to finish.
|
| 55 |
+
|
| 56 |
+
This method takes an iterable of coroutine functions. The first one is
|
| 57 |
+
started immediately. From then on, whenever the immediately preceding one
|
| 58 |
+
fails (raises an exception), or when *delay* seconds has passed, the next
|
| 59 |
+
coroutine is started. This continues until one of the coroutines complete
|
| 60 |
+
successfully, in which case all others are cancelled, or until all
|
| 61 |
+
coroutines fail.
|
| 62 |
+
|
| 63 |
+
The coroutines provided should be well-behaved in the following way:
|
| 64 |
+
|
| 65 |
+
* They should only ``return`` if completed successfully.
|
| 66 |
+
|
| 67 |
+
* They should always raise an exception if they did not complete
|
| 68 |
+
successfully. In particular, if they handle cancellation, they should
|
| 69 |
+
probably reraise, like this::
|
| 70 |
+
|
| 71 |
+
try:
|
| 72 |
+
# do work
|
| 73 |
+
except asyncio.CancelledError:
|
| 74 |
+
# undo partially completed work
|
| 75 |
+
raise
|
| 76 |
+
|
| 77 |
+
Args:
|
| 78 |
+
----
|
| 79 |
+
coro_fns: an iterable of coroutine functions, i.e. callables that
|
| 80 |
+
return a coroutine object when called. Use ``functools.partial`` or
|
| 81 |
+
lambdas to pass arguments.
|
| 82 |
+
|
| 83 |
+
delay: amount of time, in seconds, between starting coroutines. If
|
| 84 |
+
``None``, the coroutines will run sequentially.
|
| 85 |
+
|
| 86 |
+
loop: the event loop to use. If ``None``, the running loop is used.
|
| 87 |
+
|
| 88 |
+
Returns:
|
| 89 |
+
-------
|
| 90 |
+
tuple *(winner_result, winner_index, exceptions)* where
|
| 91 |
+
|
| 92 |
+
- *winner_result*: the result of the winning coroutine, or ``None``
|
| 93 |
+
if no coroutines won.
|
| 94 |
+
|
| 95 |
+
- *winner_index*: the index of the winning coroutine in
|
| 96 |
+
``coro_fns``, or ``None`` if no coroutines won. If the winning
|
| 97 |
+
coroutine may return None on success, *winner_index* can be used
|
| 98 |
+
to definitively determine whether any coroutine won.
|
| 99 |
+
|
| 100 |
+
- *exceptions*: list of exceptions returned by the coroutines.
|
| 101 |
+
``len(exceptions)`` is equal to the number of coroutines actually
|
| 102 |
+
started, and the order is the same as in ``coro_fns``. The winning
|
| 103 |
+
coroutine's entry is ``None``.
|
| 104 |
+
|
| 105 |
+
"""
|
| 106 |
+
loop = loop or asyncio.get_running_loop()
|
| 107 |
+
exceptions: List[Optional[BaseException]] = []
|
| 108 |
+
tasks: Set[asyncio.Task[Optional[Tuple[_T, int]]]] = set()
|
| 109 |
+
|
| 110 |
+
async def run_one_coro(
|
| 111 |
+
coro_fn: Callable[[], Awaitable[_T]],
|
| 112 |
+
this_index: int,
|
| 113 |
+
start_next: "asyncio.Future[None]",
|
| 114 |
+
) -> Optional[Tuple[_T, int]]:
|
| 115 |
+
"""
|
| 116 |
+
Run a single coroutine.
|
| 117 |
+
|
| 118 |
+
If the coroutine fails, set the exception in the exceptions list and
|
| 119 |
+
start the next coroutine by setting the result of the start_next.
|
| 120 |
+
|
| 121 |
+
If the coroutine succeeds, return the result and the index of the
|
| 122 |
+
coroutine in the coro_fns list.
|
| 123 |
+
|
| 124 |
+
If SystemExit or KeyboardInterrupt is raised, re-raise it.
|
| 125 |
+
"""
|
| 126 |
+
try:
|
| 127 |
+
result = await coro_fn()
|
| 128 |
+
except (SystemExit, KeyboardInterrupt):
|
| 129 |
+
raise
|
| 130 |
+
except BaseException as e:
|
| 131 |
+
exceptions[this_index] = e
|
| 132 |
+
_set_result(start_next) # Kickstart the next coroutine
|
| 133 |
+
return None
|
| 134 |
+
|
| 135 |
+
return result, this_index
|
| 136 |
+
|
| 137 |
+
start_next_timer: Optional[asyncio.TimerHandle] = None
|
| 138 |
+
start_next: Optional[asyncio.Future[None]]
|
| 139 |
+
task: asyncio.Task[Optional[Tuple[_T, int]]]
|
| 140 |
+
done: Union[asyncio.Future[None], asyncio.Task[Optional[Tuple[_T, int]]]]
|
| 141 |
+
coro_iter = iter(coro_fns)
|
| 142 |
+
this_index = -1
|
| 143 |
+
try:
|
| 144 |
+
while True:
|
| 145 |
+
if coro_fn := next(coro_iter, None):
|
| 146 |
+
this_index += 1
|
| 147 |
+
exceptions.append(None)
|
| 148 |
+
start_next = loop.create_future()
|
| 149 |
+
task = loop.create_task(run_one_coro(coro_fn, this_index, start_next))
|
| 150 |
+
tasks.add(task)
|
| 151 |
+
start_next_timer = (
|
| 152 |
+
loop.call_later(delay, _set_result, start_next) if delay else None
|
| 153 |
+
)
|
| 154 |
+
elif not tasks:
|
| 155 |
+
# We exhausted the coro_fns list and no tasks are running
|
| 156 |
+
# so we have no winner and all coroutines failed.
|
| 157 |
+
break
|
| 158 |
+
|
| 159 |
+
while tasks:
|
| 160 |
+
done = await _wait_one(
|
| 161 |
+
[*tasks, start_next] if start_next else tasks, loop
|
| 162 |
+
)
|
| 163 |
+
if done is start_next:
|
| 164 |
+
# The current task has failed or the timer has expired
|
| 165 |
+
# so we need to start the next task.
|
| 166 |
+
start_next = None
|
| 167 |
+
if start_next_timer:
|
| 168 |
+
start_next_timer.cancel()
|
| 169 |
+
start_next_timer = None
|
| 170 |
+
|
| 171 |
+
# Break out of the task waiting loop to start the next
|
| 172 |
+
# task.
|
| 173 |
+
break
|
| 174 |
+
|
| 175 |
+
if TYPE_CHECKING:
|
| 176 |
+
assert isinstance(done, asyncio.Task)
|
| 177 |
+
|
| 178 |
+
tasks.remove(done)
|
| 179 |
+
if winner := done.result():
|
| 180 |
+
return *winner, exceptions
|
| 181 |
+
finally:
|
| 182 |
+
# We either have:
|
| 183 |
+
# - a winner
|
| 184 |
+
# - all tasks failed
|
| 185 |
+
# - a KeyboardInterrupt or SystemExit.
|
| 186 |
+
|
| 187 |
+
#
|
| 188 |
+
# If the timer is still running, cancel it.
|
| 189 |
+
#
|
| 190 |
+
if start_next_timer:
|
| 191 |
+
start_next_timer.cancel()
|
| 192 |
+
|
| 193 |
+
#
|
| 194 |
+
# If there are any tasks left, cancel them and than
|
| 195 |
+
# wait them so they fill the exceptions list.
|
| 196 |
+
#
|
| 197 |
+
for task in tasks:
|
| 198 |
+
task.cancel()
|
| 199 |
+
with contextlib.suppress(asyncio.CancelledError):
|
| 200 |
+
await task
|
| 201 |
+
|
| 202 |
+
return None, None, exceptions
|
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiohappyeyeballs/impl.py
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Base implementation."""
|
| 2 |
+
|
| 3 |
+
import asyncio
|
| 4 |
+
import collections
|
| 5 |
+
import functools
|
| 6 |
+
import itertools
|
| 7 |
+
import socket
|
| 8 |
+
import sys
|
| 9 |
+
from typing import List, Optional, Sequence, Union
|
| 10 |
+
|
| 11 |
+
from . import _staggered
|
| 12 |
+
from .types import AddrInfoType
|
| 13 |
+
|
| 14 |
+
if sys.version_info < (3, 8, 2): # noqa: UP036
|
| 15 |
+
# asyncio.staggered is broken in Python 3.8.0 and 3.8.1
|
| 16 |
+
# so it must be patched:
|
| 17 |
+
# https://github.com/aio-libs/aiohttp/issues/8556
|
| 18 |
+
# https://bugs.python.org/issue39129
|
| 19 |
+
# https://github.com/python/cpython/pull/17693
|
| 20 |
+
import asyncio.futures
|
| 21 |
+
|
| 22 |
+
asyncio.futures.TimeoutError = asyncio.TimeoutError # type: ignore[attr-defined]
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
async def start_connection(
|
| 26 |
+
addr_infos: Sequence[AddrInfoType],
|
| 27 |
+
*,
|
| 28 |
+
local_addr_infos: Optional[Sequence[AddrInfoType]] = None,
|
| 29 |
+
happy_eyeballs_delay: Optional[float] = None,
|
| 30 |
+
interleave: Optional[int] = None,
|
| 31 |
+
loop: Optional[asyncio.AbstractEventLoop] = None,
|
| 32 |
+
) -> socket.socket:
|
| 33 |
+
"""
|
| 34 |
+
Connect to a TCP server.
|
| 35 |
+
|
| 36 |
+
Create a socket connection to a specified destination. The
|
| 37 |
+
destination is specified as a list of AddrInfoType tuples as
|
| 38 |
+
returned from getaddrinfo().
|
| 39 |
+
|
| 40 |
+
The arguments are, in order:
|
| 41 |
+
|
| 42 |
+
* ``family``: the address family, e.g. ``socket.AF_INET`` or
|
| 43 |
+
``socket.AF_INET6``.
|
| 44 |
+
* ``type``: the socket type, e.g. ``socket.SOCK_STREAM`` or
|
| 45 |
+
``socket.SOCK_DGRAM``.
|
| 46 |
+
* ``proto``: the protocol, e.g. ``socket.IPPROTO_TCP`` or
|
| 47 |
+
``socket.IPPROTO_UDP``.
|
| 48 |
+
* ``canonname``: the canonical name of the address, e.g.
|
| 49 |
+
``"www.python.org"``.
|
| 50 |
+
* ``sockaddr``: the socket address
|
| 51 |
+
|
| 52 |
+
This method is a coroutine which will try to establish the connection
|
| 53 |
+
in the background. When successful, the coroutine returns a
|
| 54 |
+
socket.
|
| 55 |
+
|
| 56 |
+
The expected use case is to use this method in conjunction with
|
| 57 |
+
loop.create_connection() to establish a connection to a server::
|
| 58 |
+
|
| 59 |
+
socket = await start_connection(addr_infos)
|
| 60 |
+
transport, protocol = await loop.create_connection(
|
| 61 |
+
MyProtocol, sock=socket, ...)
|
| 62 |
+
"""
|
| 63 |
+
if not (current_loop := loop):
|
| 64 |
+
current_loop = asyncio.get_running_loop()
|
| 65 |
+
|
| 66 |
+
single_addr_info = len(addr_infos) == 1
|
| 67 |
+
|
| 68 |
+
if happy_eyeballs_delay is not None and interleave is None:
|
| 69 |
+
# If using happy eyeballs, default to interleave addresses by family
|
| 70 |
+
interleave = 1
|
| 71 |
+
|
| 72 |
+
if interleave and not single_addr_info:
|
| 73 |
+
addr_infos = _interleave_addrinfos(addr_infos, interleave)
|
| 74 |
+
|
| 75 |
+
sock: Optional[socket.socket] = None
|
| 76 |
+
# uvloop can raise RuntimeError instead of OSError
|
| 77 |
+
exceptions: List[List[Union[OSError, RuntimeError]]] = []
|
| 78 |
+
if happy_eyeballs_delay is None or single_addr_info:
|
| 79 |
+
# not using happy eyeballs
|
| 80 |
+
for addrinfo in addr_infos:
|
| 81 |
+
try:
|
| 82 |
+
sock = await _connect_sock(
|
| 83 |
+
current_loop, exceptions, addrinfo, local_addr_infos
|
| 84 |
+
)
|
| 85 |
+
break
|
| 86 |
+
except (RuntimeError, OSError):
|
| 87 |
+
continue
|
| 88 |
+
else: # using happy eyeballs
|
| 89 |
+
sock, _, _ = await _staggered.staggered_race(
|
| 90 |
+
(
|
| 91 |
+
functools.partial(
|
| 92 |
+
_connect_sock, current_loop, exceptions, addrinfo, local_addr_infos
|
| 93 |
+
)
|
| 94 |
+
for addrinfo in addr_infos
|
| 95 |
+
),
|
| 96 |
+
happy_eyeballs_delay,
|
| 97 |
+
)
|
| 98 |
+
|
| 99 |
+
if sock is None:
|
| 100 |
+
all_exceptions = [exc for sub in exceptions for exc in sub]
|
| 101 |
+
try:
|
| 102 |
+
first_exception = all_exceptions[0]
|
| 103 |
+
if len(all_exceptions) == 1:
|
| 104 |
+
raise first_exception
|
| 105 |
+
else:
|
| 106 |
+
# If they all have the same str(), raise one.
|
| 107 |
+
model = str(first_exception)
|
| 108 |
+
if all(str(exc) == model for exc in all_exceptions):
|
| 109 |
+
raise first_exception
|
| 110 |
+
# Raise a combined exception so the user can see all
|
| 111 |
+
# the various error messages.
|
| 112 |
+
msg = "Multiple exceptions: {}".format(
|
| 113 |
+
", ".join(str(exc) for exc in all_exceptions)
|
| 114 |
+
)
|
| 115 |
+
# If the errno is the same for all exceptions, raise
|
| 116 |
+
# an OSError with that errno.
|
| 117 |
+
if isinstance(first_exception, OSError):
|
| 118 |
+
first_errno = first_exception.errno
|
| 119 |
+
if all(
|
| 120 |
+
isinstance(exc, OSError) and exc.errno == first_errno
|
| 121 |
+
for exc in all_exceptions
|
| 122 |
+
):
|
| 123 |
+
raise OSError(first_errno, msg)
|
| 124 |
+
elif isinstance(first_exception, RuntimeError) and all(
|
| 125 |
+
isinstance(exc, RuntimeError) for exc in all_exceptions
|
| 126 |
+
):
|
| 127 |
+
raise RuntimeError(msg)
|
| 128 |
+
# We have a mix of OSError and RuntimeError
|
| 129 |
+
# so we have to pick which one to raise.
|
| 130 |
+
# and we raise OSError for compatibility
|
| 131 |
+
raise OSError(msg)
|
| 132 |
+
finally:
|
| 133 |
+
all_exceptions = None # type: ignore[assignment]
|
| 134 |
+
exceptions = None # type: ignore[assignment]
|
| 135 |
+
|
| 136 |
+
return sock
|
| 137 |
+
|
| 138 |
+
|
| 139 |
+
async def _connect_sock(
|
| 140 |
+
loop: asyncio.AbstractEventLoop,
|
| 141 |
+
exceptions: List[List[Union[OSError, RuntimeError]]],
|
| 142 |
+
addr_info: AddrInfoType,
|
| 143 |
+
local_addr_infos: Optional[Sequence[AddrInfoType]] = None,
|
| 144 |
+
) -> socket.socket:
|
| 145 |
+
"""Create, bind and connect one socket."""
|
| 146 |
+
my_exceptions: List[Union[OSError, RuntimeError]] = []
|
| 147 |
+
exceptions.append(my_exceptions)
|
| 148 |
+
family, type_, proto, _, address = addr_info
|
| 149 |
+
sock = None
|
| 150 |
+
try:
|
| 151 |
+
sock = socket.socket(family=family, type=type_, proto=proto)
|
| 152 |
+
sock.setblocking(False)
|
| 153 |
+
if local_addr_infos is not None:
|
| 154 |
+
for lfamily, _, _, _, laddr in local_addr_infos:
|
| 155 |
+
# skip local addresses of different family
|
| 156 |
+
if lfamily != family:
|
| 157 |
+
continue
|
| 158 |
+
try:
|
| 159 |
+
sock.bind(laddr)
|
| 160 |
+
break
|
| 161 |
+
except OSError as exc:
|
| 162 |
+
msg = (
|
| 163 |
+
f"error while attempting to bind on "
|
| 164 |
+
f"address {laddr!r}: "
|
| 165 |
+
f"{exc.strerror.lower()}"
|
| 166 |
+
)
|
| 167 |
+
exc = OSError(exc.errno, msg)
|
| 168 |
+
my_exceptions.append(exc)
|
| 169 |
+
else: # all bind attempts failed
|
| 170 |
+
if my_exceptions:
|
| 171 |
+
raise my_exceptions.pop()
|
| 172 |
+
else:
|
| 173 |
+
raise OSError(f"no matching local address with {family=} found")
|
| 174 |
+
await loop.sock_connect(sock, address)
|
| 175 |
+
return sock
|
| 176 |
+
except (RuntimeError, OSError) as exc:
|
| 177 |
+
my_exceptions.append(exc)
|
| 178 |
+
if sock is not None:
|
| 179 |
+
try:
|
| 180 |
+
sock.close()
|
| 181 |
+
except OSError as e:
|
| 182 |
+
my_exceptions.append(e)
|
| 183 |
+
raise
|
| 184 |
+
raise
|
| 185 |
+
except:
|
| 186 |
+
if sock is not None:
|
| 187 |
+
try:
|
| 188 |
+
sock.close()
|
| 189 |
+
except OSError as e:
|
| 190 |
+
my_exceptions.append(e)
|
| 191 |
+
raise
|
| 192 |
+
raise
|
| 193 |
+
finally:
|
| 194 |
+
exceptions = my_exceptions = None # type: ignore[assignment]
|
| 195 |
+
|
| 196 |
+
|
| 197 |
+
def _interleave_addrinfos(
|
| 198 |
+
addrinfos: Sequence[AddrInfoType], first_address_family_count: int = 1
|
| 199 |
+
) -> List[AddrInfoType]:
|
| 200 |
+
"""Interleave list of addrinfo tuples by family."""
|
| 201 |
+
# Group addresses by family
|
| 202 |
+
addrinfos_by_family: collections.OrderedDict[int, List[AddrInfoType]] = (
|
| 203 |
+
collections.OrderedDict()
|
| 204 |
+
)
|
| 205 |
+
for addr in addrinfos:
|
| 206 |
+
family = addr[0]
|
| 207 |
+
if family not in addrinfos_by_family:
|
| 208 |
+
addrinfos_by_family[family] = []
|
| 209 |
+
addrinfos_by_family[family].append(addr)
|
| 210 |
+
addrinfos_lists = list(addrinfos_by_family.values())
|
| 211 |
+
|
| 212 |
+
reordered: List[AddrInfoType] = []
|
| 213 |
+
if first_address_family_count > 1:
|
| 214 |
+
reordered.extend(addrinfos_lists[0][: first_address_family_count - 1])
|
| 215 |
+
del addrinfos_lists[0][: first_address_family_count - 1]
|
| 216 |
+
reordered.extend(
|
| 217 |
+
a
|
| 218 |
+
for a in itertools.chain.from_iterable(itertools.zip_longest(*addrinfos_lists))
|
| 219 |
+
if a is not None
|
| 220 |
+
)
|
| 221 |
+
return reordered
|
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiohappyeyeballs/py.typed
ADDED
|
File without changes
|
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiohappyeyeballs/types.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Types for aiohappyeyeballs."""
|
| 2 |
+
|
| 3 |
+
import socket
|
| 4 |
+
from typing import Tuple, Union
|
| 5 |
+
|
| 6 |
+
AddrInfoType = Tuple[
|
| 7 |
+
Union[int, socket.AddressFamily],
|
| 8 |
+
Union[int, socket.SocketKind],
|
| 9 |
+
int,
|
| 10 |
+
str,
|
| 11 |
+
Tuple, # type: ignore[type-arg]
|
| 12 |
+
]
|
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiohappyeyeballs/utils.py
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Utility functions for aiohappyeyeballs."""
|
| 2 |
+
|
| 3 |
+
import ipaddress
|
| 4 |
+
import socket
|
| 5 |
+
from typing import Dict, List, Optional, Tuple, Union
|
| 6 |
+
|
| 7 |
+
from .types import AddrInfoType
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
def addr_to_addr_infos(
|
| 11 |
+
addr: Optional[
|
| 12 |
+
Union[Tuple[str, int, int, int], Tuple[str, int, int], Tuple[str, int]]
|
| 13 |
+
],
|
| 14 |
+
) -> Optional[List[AddrInfoType]]:
|
| 15 |
+
"""Convert an address tuple to a list of addr_info tuples."""
|
| 16 |
+
if addr is None:
|
| 17 |
+
return None
|
| 18 |
+
host = addr[0]
|
| 19 |
+
port = addr[1]
|
| 20 |
+
is_ipv6 = ":" in host
|
| 21 |
+
if is_ipv6:
|
| 22 |
+
flowinfo = 0
|
| 23 |
+
scopeid = 0
|
| 24 |
+
addr_len = len(addr)
|
| 25 |
+
if addr_len >= 4:
|
| 26 |
+
scopeid = addr[3] # type: ignore[misc]
|
| 27 |
+
if addr_len >= 3:
|
| 28 |
+
flowinfo = addr[2] # type: ignore[misc]
|
| 29 |
+
addr = (host, port, flowinfo, scopeid)
|
| 30 |
+
family = socket.AF_INET6
|
| 31 |
+
else:
|
| 32 |
+
addr = (host, port)
|
| 33 |
+
family = socket.AF_INET
|
| 34 |
+
return [(family, socket.SOCK_STREAM, socket.IPPROTO_TCP, "", addr)]
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
def pop_addr_infos_interleave(
|
| 38 |
+
addr_infos: List[AddrInfoType], interleave: Optional[int] = None
|
| 39 |
+
) -> None:
|
| 40 |
+
"""
|
| 41 |
+
Pop addr_info from the list of addr_infos by family up to interleave times.
|
| 42 |
+
|
| 43 |
+
The interleave parameter is used to know how many addr_infos for
|
| 44 |
+
each family should be popped of the top of the list.
|
| 45 |
+
"""
|
| 46 |
+
seen: Dict[int, int] = {}
|
| 47 |
+
if interleave is None:
|
| 48 |
+
interleave = 1
|
| 49 |
+
to_remove: List[AddrInfoType] = []
|
| 50 |
+
for addr_info in addr_infos:
|
| 51 |
+
family = addr_info[0]
|
| 52 |
+
if family not in seen:
|
| 53 |
+
seen[family] = 0
|
| 54 |
+
if seen[family] < interleave:
|
| 55 |
+
to_remove.append(addr_info)
|
| 56 |
+
seen[family] += 1
|
| 57 |
+
for addr_info in to_remove:
|
| 58 |
+
addr_infos.remove(addr_info)
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
def _addr_tuple_to_ip_address(
|
| 62 |
+
addr: Union[Tuple[str, int], Tuple[str, int, int, int]],
|
| 63 |
+
) -> Union[
|
| 64 |
+
Tuple[ipaddress.IPv4Address, int], Tuple[ipaddress.IPv6Address, int, int, int]
|
| 65 |
+
]:
|
| 66 |
+
"""Convert an address tuple to an IPv4Address."""
|
| 67 |
+
return (ipaddress.ip_address(addr[0]), *addr[1:])
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
def remove_addr_infos(
|
| 71 |
+
addr_infos: List[AddrInfoType],
|
| 72 |
+
addr: Union[Tuple[str, int], Tuple[str, int, int, int]],
|
| 73 |
+
) -> None:
|
| 74 |
+
"""
|
| 75 |
+
Remove an address from the list of addr_infos.
|
| 76 |
+
|
| 77 |
+
The addr value is typically the return value of
|
| 78 |
+
sock.getpeername().
|
| 79 |
+
"""
|
| 80 |
+
bad_addrs_infos: List[AddrInfoType] = []
|
| 81 |
+
for addr_info in addr_infos:
|
| 82 |
+
if addr_info[-1] == addr:
|
| 83 |
+
bad_addrs_infos.append(addr_info)
|
| 84 |
+
if bad_addrs_infos:
|
| 85 |
+
for bad_addr_info in bad_addrs_infos:
|
| 86 |
+
addr_infos.remove(bad_addr_info)
|
| 87 |
+
return
|
| 88 |
+
# Slow path in case addr is formatted differently
|
| 89 |
+
match_addr = _addr_tuple_to_ip_address(addr)
|
| 90 |
+
for addr_info in addr_infos:
|
| 91 |
+
if match_addr == _addr_tuple_to_ip_address(addr_info[-1]):
|
| 92 |
+
bad_addrs_infos.append(addr_info)
|
| 93 |
+
if bad_addrs_infos:
|
| 94 |
+
for bad_addr_info in bad_addrs_infos:
|
| 95 |
+
addr_infos.remove(bad_addr_info)
|
| 96 |
+
return
|
| 97 |
+
raise ValueError(f"Address {addr} not found in addr_infos")
|
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiosignal/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (1.9 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attr/__init__.pyi
ADDED
|
@@ -0,0 +1,389 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import enum
|
| 2 |
+
import sys
|
| 3 |
+
|
| 4 |
+
from typing import (
|
| 5 |
+
Any,
|
| 6 |
+
Callable,
|
| 7 |
+
Generic,
|
| 8 |
+
Literal,
|
| 9 |
+
Mapping,
|
| 10 |
+
Protocol,
|
| 11 |
+
Sequence,
|
| 12 |
+
TypeVar,
|
| 13 |
+
overload,
|
| 14 |
+
)
|
| 15 |
+
|
| 16 |
+
# `import X as X` is required to make these public
|
| 17 |
+
from . import converters as converters
|
| 18 |
+
from . import exceptions as exceptions
|
| 19 |
+
from . import filters as filters
|
| 20 |
+
from . import setters as setters
|
| 21 |
+
from . import validators as validators
|
| 22 |
+
from ._cmp import cmp_using as cmp_using
|
| 23 |
+
from ._typing_compat import AttrsInstance_
|
| 24 |
+
from ._version_info import VersionInfo
|
| 25 |
+
from attrs import (
|
| 26 |
+
define as define,
|
| 27 |
+
field as field,
|
| 28 |
+
mutable as mutable,
|
| 29 |
+
frozen as frozen,
|
| 30 |
+
_EqOrderType,
|
| 31 |
+
_ValidatorType,
|
| 32 |
+
_ConverterType,
|
| 33 |
+
_ReprArgType,
|
| 34 |
+
_OnSetAttrType,
|
| 35 |
+
_OnSetAttrArgType,
|
| 36 |
+
_FieldTransformer,
|
| 37 |
+
_ValidatorArgType,
|
| 38 |
+
)
|
| 39 |
+
|
| 40 |
+
if sys.version_info >= (3, 10):
|
| 41 |
+
from typing import TypeGuard, TypeAlias
|
| 42 |
+
else:
|
| 43 |
+
from typing_extensions import TypeGuard, TypeAlias
|
| 44 |
+
|
| 45 |
+
if sys.version_info >= (3, 11):
|
| 46 |
+
from typing import dataclass_transform
|
| 47 |
+
else:
|
| 48 |
+
from typing_extensions import dataclass_transform
|
| 49 |
+
|
| 50 |
+
__version__: str
|
| 51 |
+
__version_info__: VersionInfo
|
| 52 |
+
__title__: str
|
| 53 |
+
__description__: str
|
| 54 |
+
__url__: str
|
| 55 |
+
__uri__: str
|
| 56 |
+
__author__: str
|
| 57 |
+
__email__: str
|
| 58 |
+
__license__: str
|
| 59 |
+
__copyright__: str
|
| 60 |
+
|
| 61 |
+
_T = TypeVar("_T")
|
| 62 |
+
_C = TypeVar("_C", bound=type)
|
| 63 |
+
|
| 64 |
+
_FilterType = Callable[["Attribute[_T]", _T], bool]
|
| 65 |
+
|
| 66 |
+
# We subclass this here to keep the protocol's qualified name clean.
|
| 67 |
+
class AttrsInstance(AttrsInstance_, Protocol):
|
| 68 |
+
pass
|
| 69 |
+
|
| 70 |
+
_A = TypeVar("_A", bound=type[AttrsInstance])
|
| 71 |
+
|
| 72 |
+
class _Nothing(enum.Enum):
|
| 73 |
+
NOTHING = enum.auto()
|
| 74 |
+
|
| 75 |
+
NOTHING = _Nothing.NOTHING
|
| 76 |
+
NothingType: TypeAlias = Literal[_Nothing.NOTHING]
|
| 77 |
+
|
| 78 |
+
# NOTE: Factory lies about its return type to make this possible:
|
| 79 |
+
# `x: List[int] # = Factory(list)`
|
| 80 |
+
# Work around mypy issue #4554 in the common case by using an overload.
|
| 81 |
+
|
| 82 |
+
@overload
|
| 83 |
+
def Factory(factory: Callable[[], _T]) -> _T: ...
|
| 84 |
+
@overload
|
| 85 |
+
def Factory(
|
| 86 |
+
factory: Callable[[Any], _T],
|
| 87 |
+
takes_self: Literal[True],
|
| 88 |
+
) -> _T: ...
|
| 89 |
+
@overload
|
| 90 |
+
def Factory(
|
| 91 |
+
factory: Callable[[], _T],
|
| 92 |
+
takes_self: Literal[False],
|
| 93 |
+
) -> _T: ...
|
| 94 |
+
|
| 95 |
+
In = TypeVar("In")
|
| 96 |
+
Out = TypeVar("Out")
|
| 97 |
+
|
| 98 |
+
class Converter(Generic[In, Out]):
|
| 99 |
+
@overload
|
| 100 |
+
def __init__(self, converter: Callable[[In], Out]) -> None: ...
|
| 101 |
+
@overload
|
| 102 |
+
def __init__(
|
| 103 |
+
self,
|
| 104 |
+
converter: Callable[[In, AttrsInstance, Attribute], Out],
|
| 105 |
+
*,
|
| 106 |
+
takes_self: Literal[True],
|
| 107 |
+
takes_field: Literal[True],
|
| 108 |
+
) -> None: ...
|
| 109 |
+
@overload
|
| 110 |
+
def __init__(
|
| 111 |
+
self,
|
| 112 |
+
converter: Callable[[In, Attribute], Out],
|
| 113 |
+
*,
|
| 114 |
+
takes_field: Literal[True],
|
| 115 |
+
) -> None: ...
|
| 116 |
+
@overload
|
| 117 |
+
def __init__(
|
| 118 |
+
self,
|
| 119 |
+
converter: Callable[[In, AttrsInstance], Out],
|
| 120 |
+
*,
|
| 121 |
+
takes_self: Literal[True],
|
| 122 |
+
) -> None: ...
|
| 123 |
+
|
| 124 |
+
class Attribute(Generic[_T]):
|
| 125 |
+
name: str
|
| 126 |
+
default: _T | None
|
| 127 |
+
validator: _ValidatorType[_T] | None
|
| 128 |
+
repr: _ReprArgType
|
| 129 |
+
cmp: _EqOrderType
|
| 130 |
+
eq: _EqOrderType
|
| 131 |
+
order: _EqOrderType
|
| 132 |
+
hash: bool | None
|
| 133 |
+
init: bool
|
| 134 |
+
converter: Converter | None
|
| 135 |
+
metadata: dict[Any, Any]
|
| 136 |
+
type: type[_T] | None
|
| 137 |
+
kw_only: bool
|
| 138 |
+
on_setattr: _OnSetAttrType
|
| 139 |
+
alias: str | None
|
| 140 |
+
|
| 141 |
+
def evolve(self, **changes: Any) -> "Attribute[Any]": ...
|
| 142 |
+
|
| 143 |
+
# NOTE: We had several choices for the annotation to use for type arg:
|
| 144 |
+
# 1) Type[_T]
|
| 145 |
+
# - Pros: Handles simple cases correctly
|
| 146 |
+
# - Cons: Might produce less informative errors in the case of conflicting
|
| 147 |
+
# TypeVars e.g. `attr.ib(default='bad', type=int)`
|
| 148 |
+
# 2) Callable[..., _T]
|
| 149 |
+
# - Pros: Better error messages than #1 for conflicting TypeVars
|
| 150 |
+
# - Cons: Terrible error messages for validator checks.
|
| 151 |
+
# e.g. attr.ib(type=int, validator=validate_str)
|
| 152 |
+
# -> error: Cannot infer function type argument
|
| 153 |
+
# 3) type (and do all of the work in the mypy plugin)
|
| 154 |
+
# - Pros: Simple here, and we could customize the plugin with our own errors.
|
| 155 |
+
# - Cons: Would need to write mypy plugin code to handle all the cases.
|
| 156 |
+
# We chose option #1.
|
| 157 |
+
|
| 158 |
+
# `attr` lies about its return type to make the following possible:
|
| 159 |
+
# attr() -> Any
|
| 160 |
+
# attr(8) -> int
|
| 161 |
+
# attr(validator=<some callable>) -> Whatever the callable expects.
|
| 162 |
+
# This makes this type of assignments possible:
|
| 163 |
+
# x: int = attr(8)
|
| 164 |
+
#
|
| 165 |
+
# This form catches explicit None or no default but with no other arguments
|
| 166 |
+
# returns Any.
|
| 167 |
+
@overload
|
| 168 |
+
def attrib(
|
| 169 |
+
default: None = ...,
|
| 170 |
+
validator: None = ...,
|
| 171 |
+
repr: _ReprArgType = ...,
|
| 172 |
+
cmp: _EqOrderType | None = ...,
|
| 173 |
+
hash: bool | None = ...,
|
| 174 |
+
init: bool = ...,
|
| 175 |
+
metadata: Mapping[Any, Any] | None = ...,
|
| 176 |
+
type: None = ...,
|
| 177 |
+
converter: None = ...,
|
| 178 |
+
factory: None = ...,
|
| 179 |
+
kw_only: bool = ...,
|
| 180 |
+
eq: _EqOrderType | None = ...,
|
| 181 |
+
order: _EqOrderType | None = ...,
|
| 182 |
+
on_setattr: _OnSetAttrArgType | None = ...,
|
| 183 |
+
alias: str | None = ...,
|
| 184 |
+
) -> Any: ...
|
| 185 |
+
|
| 186 |
+
# This form catches an explicit None or no default and infers the type from the
|
| 187 |
+
# other arguments.
|
| 188 |
+
@overload
|
| 189 |
+
def attrib(
|
| 190 |
+
default: None = ...,
|
| 191 |
+
validator: _ValidatorArgType[_T] | None = ...,
|
| 192 |
+
repr: _ReprArgType = ...,
|
| 193 |
+
cmp: _EqOrderType | None = ...,
|
| 194 |
+
hash: bool | None = ...,
|
| 195 |
+
init: bool = ...,
|
| 196 |
+
metadata: Mapping[Any, Any] | None = ...,
|
| 197 |
+
type: type[_T] | None = ...,
|
| 198 |
+
converter: _ConverterType
|
| 199 |
+
| list[_ConverterType]
|
| 200 |
+
| tuple[_ConverterType]
|
| 201 |
+
| None = ...,
|
| 202 |
+
factory: Callable[[], _T] | None = ...,
|
| 203 |
+
kw_only: bool = ...,
|
| 204 |
+
eq: _EqOrderType | None = ...,
|
| 205 |
+
order: _EqOrderType | None = ...,
|
| 206 |
+
on_setattr: _OnSetAttrArgType | None = ...,
|
| 207 |
+
alias: str | None = ...,
|
| 208 |
+
) -> _T: ...
|
| 209 |
+
|
| 210 |
+
# This form catches an explicit default argument.
|
| 211 |
+
@overload
|
| 212 |
+
def attrib(
|
| 213 |
+
default: _T,
|
| 214 |
+
validator: _ValidatorArgType[_T] | None = ...,
|
| 215 |
+
repr: _ReprArgType = ...,
|
| 216 |
+
cmp: _EqOrderType | None = ...,
|
| 217 |
+
hash: bool | None = ...,
|
| 218 |
+
init: bool = ...,
|
| 219 |
+
metadata: Mapping[Any, Any] | None = ...,
|
| 220 |
+
type: type[_T] | None = ...,
|
| 221 |
+
converter: _ConverterType
|
| 222 |
+
| list[_ConverterType]
|
| 223 |
+
| tuple[_ConverterType]
|
| 224 |
+
| None = ...,
|
| 225 |
+
factory: Callable[[], _T] | None = ...,
|
| 226 |
+
kw_only: bool = ...,
|
| 227 |
+
eq: _EqOrderType | None = ...,
|
| 228 |
+
order: _EqOrderType | None = ...,
|
| 229 |
+
on_setattr: _OnSetAttrArgType | None = ...,
|
| 230 |
+
alias: str | None = ...,
|
| 231 |
+
) -> _T: ...
|
| 232 |
+
|
| 233 |
+
# This form covers type=non-Type: e.g. forward references (str), Any
|
| 234 |
+
@overload
|
| 235 |
+
def attrib(
|
| 236 |
+
default: _T | None = ...,
|
| 237 |
+
validator: _ValidatorArgType[_T] | None = ...,
|
| 238 |
+
repr: _ReprArgType = ...,
|
| 239 |
+
cmp: _EqOrderType | None = ...,
|
| 240 |
+
hash: bool | None = ...,
|
| 241 |
+
init: bool = ...,
|
| 242 |
+
metadata: Mapping[Any, Any] | None = ...,
|
| 243 |
+
type: object = ...,
|
| 244 |
+
converter: _ConverterType
|
| 245 |
+
| list[_ConverterType]
|
| 246 |
+
| tuple[_ConverterType]
|
| 247 |
+
| None = ...,
|
| 248 |
+
factory: Callable[[], _T] | None = ...,
|
| 249 |
+
kw_only: bool = ...,
|
| 250 |
+
eq: _EqOrderType | None = ...,
|
| 251 |
+
order: _EqOrderType | None = ...,
|
| 252 |
+
on_setattr: _OnSetAttrArgType | None = ...,
|
| 253 |
+
alias: str | None = ...,
|
| 254 |
+
) -> Any: ...
|
| 255 |
+
@overload
|
| 256 |
+
@dataclass_transform(order_default=True, field_specifiers=(attrib, field))
|
| 257 |
+
def attrs(
|
| 258 |
+
maybe_cls: _C,
|
| 259 |
+
these: dict[str, Any] | None = ...,
|
| 260 |
+
repr_ns: str | None = ...,
|
| 261 |
+
repr: bool = ...,
|
| 262 |
+
cmp: _EqOrderType | None = ...,
|
| 263 |
+
hash: bool | None = ...,
|
| 264 |
+
init: bool = ...,
|
| 265 |
+
slots: bool = ...,
|
| 266 |
+
frozen: bool = ...,
|
| 267 |
+
weakref_slot: bool = ...,
|
| 268 |
+
str: bool = ...,
|
| 269 |
+
auto_attribs: bool = ...,
|
| 270 |
+
kw_only: bool = ...,
|
| 271 |
+
cache_hash: bool = ...,
|
| 272 |
+
auto_exc: bool = ...,
|
| 273 |
+
eq: _EqOrderType | None = ...,
|
| 274 |
+
order: _EqOrderType | None = ...,
|
| 275 |
+
auto_detect: bool = ...,
|
| 276 |
+
collect_by_mro: bool = ...,
|
| 277 |
+
getstate_setstate: bool | None = ...,
|
| 278 |
+
on_setattr: _OnSetAttrArgType | None = ...,
|
| 279 |
+
field_transformer: _FieldTransformer | None = ...,
|
| 280 |
+
match_args: bool = ...,
|
| 281 |
+
unsafe_hash: bool | None = ...,
|
| 282 |
+
) -> _C: ...
|
| 283 |
+
@overload
|
| 284 |
+
@dataclass_transform(order_default=True, field_specifiers=(attrib, field))
|
| 285 |
+
def attrs(
|
| 286 |
+
maybe_cls: None = ...,
|
| 287 |
+
these: dict[str, Any] | None = ...,
|
| 288 |
+
repr_ns: str | None = ...,
|
| 289 |
+
repr: bool = ...,
|
| 290 |
+
cmp: _EqOrderType | None = ...,
|
| 291 |
+
hash: bool | None = ...,
|
| 292 |
+
init: bool = ...,
|
| 293 |
+
slots: bool = ...,
|
| 294 |
+
frozen: bool = ...,
|
| 295 |
+
weakref_slot: bool = ...,
|
| 296 |
+
str: bool = ...,
|
| 297 |
+
auto_attribs: bool = ...,
|
| 298 |
+
kw_only: bool = ...,
|
| 299 |
+
cache_hash: bool = ...,
|
| 300 |
+
auto_exc: bool = ...,
|
| 301 |
+
eq: _EqOrderType | None = ...,
|
| 302 |
+
order: _EqOrderType | None = ...,
|
| 303 |
+
auto_detect: bool = ...,
|
| 304 |
+
collect_by_mro: bool = ...,
|
| 305 |
+
getstate_setstate: bool | None = ...,
|
| 306 |
+
on_setattr: _OnSetAttrArgType | None = ...,
|
| 307 |
+
field_transformer: _FieldTransformer | None = ...,
|
| 308 |
+
match_args: bool = ...,
|
| 309 |
+
unsafe_hash: bool | None = ...,
|
| 310 |
+
) -> Callable[[_C], _C]: ...
|
| 311 |
+
def fields(cls: type[AttrsInstance]) -> Any: ...
|
| 312 |
+
def fields_dict(cls: type[AttrsInstance]) -> dict[str, Attribute[Any]]: ...
|
| 313 |
+
def validate(inst: AttrsInstance) -> None: ...
|
| 314 |
+
def resolve_types(
|
| 315 |
+
cls: _A,
|
| 316 |
+
globalns: dict[str, Any] | None = ...,
|
| 317 |
+
localns: dict[str, Any] | None = ...,
|
| 318 |
+
attribs: list[Attribute[Any]] | None = ...,
|
| 319 |
+
include_extras: bool = ...,
|
| 320 |
+
) -> _A: ...
|
| 321 |
+
|
| 322 |
+
# TODO: add support for returning a proper attrs class from the mypy plugin
|
| 323 |
+
# we use Any instead of _CountingAttr so that e.g. `make_class('Foo',
|
| 324 |
+
# [attr.ib()])` is valid
|
| 325 |
+
def make_class(
|
| 326 |
+
name: str,
|
| 327 |
+
attrs: list[str] | tuple[str, ...] | dict[str, Any],
|
| 328 |
+
bases: tuple[type, ...] = ...,
|
| 329 |
+
class_body: dict[str, Any] | None = ...,
|
| 330 |
+
repr_ns: str | None = ...,
|
| 331 |
+
repr: bool = ...,
|
| 332 |
+
cmp: _EqOrderType | None = ...,
|
| 333 |
+
hash: bool | None = ...,
|
| 334 |
+
init: bool = ...,
|
| 335 |
+
slots: bool = ...,
|
| 336 |
+
frozen: bool = ...,
|
| 337 |
+
weakref_slot: bool = ...,
|
| 338 |
+
str: bool = ...,
|
| 339 |
+
auto_attribs: bool = ...,
|
| 340 |
+
kw_only: bool = ...,
|
| 341 |
+
cache_hash: bool = ...,
|
| 342 |
+
auto_exc: bool = ...,
|
| 343 |
+
eq: _EqOrderType | None = ...,
|
| 344 |
+
order: _EqOrderType | None = ...,
|
| 345 |
+
collect_by_mro: bool = ...,
|
| 346 |
+
on_setattr: _OnSetAttrArgType | None = ...,
|
| 347 |
+
field_transformer: _FieldTransformer | None = ...,
|
| 348 |
+
) -> type: ...
|
| 349 |
+
|
| 350 |
+
# _funcs --
|
| 351 |
+
|
| 352 |
+
# TODO: add support for returning TypedDict from the mypy plugin
|
| 353 |
+
# FIXME: asdict/astuple do not honor their factory args. Waiting on one of
|
| 354 |
+
# these:
|
| 355 |
+
# https://github.com/python/mypy/issues/4236
|
| 356 |
+
# https://github.com/python/typing/issues/253
|
| 357 |
+
# XXX: remember to fix attrs.asdict/astuple too!
|
| 358 |
+
def asdict(
|
| 359 |
+
inst: AttrsInstance,
|
| 360 |
+
recurse: bool = ...,
|
| 361 |
+
filter: _FilterType[Any] | None = ...,
|
| 362 |
+
dict_factory: type[Mapping[Any, Any]] = ...,
|
| 363 |
+
retain_collection_types: bool = ...,
|
| 364 |
+
value_serializer: Callable[[type, Attribute[Any], Any], Any] | None = ...,
|
| 365 |
+
tuple_keys: bool | None = ...,
|
| 366 |
+
) -> dict[str, Any]: ...
|
| 367 |
+
|
| 368 |
+
# TODO: add support for returning NamedTuple from the mypy plugin
|
| 369 |
+
def astuple(
|
| 370 |
+
inst: AttrsInstance,
|
| 371 |
+
recurse: bool = ...,
|
| 372 |
+
filter: _FilterType[Any] | None = ...,
|
| 373 |
+
tuple_factory: type[Sequence[Any]] = ...,
|
| 374 |
+
retain_collection_types: bool = ...,
|
| 375 |
+
) -> tuple[Any, ...]: ...
|
| 376 |
+
def has(cls: type) -> TypeGuard[type[AttrsInstance]]: ...
|
| 377 |
+
def assoc(inst: _T, **changes: Any) -> _T: ...
|
| 378 |
+
def evolve(inst: _T, **changes: Any) -> _T: ...
|
| 379 |
+
|
| 380 |
+
# _config --
|
| 381 |
+
|
| 382 |
+
def set_run_validators(run: bool) -> None: ...
|
| 383 |
+
def get_run_validators() -> bool: ...
|
| 384 |
+
|
| 385 |
+
# aliases --
|
| 386 |
+
|
| 387 |
+
s = attributes = attrs
|
| 388 |
+
ib = attr = attrib
|
| 389 |
+
dataclass = attrs # Technically, partial(attrs, auto_attribs=True) ;)
|
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attr/_cmp.py
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: MIT
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
import functools
|
| 5 |
+
import types
|
| 6 |
+
|
| 7 |
+
from ._make import _make_ne
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
_operation_names = {"eq": "==", "lt": "<", "le": "<=", "gt": ">", "ge": ">="}
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
def cmp_using(
|
| 14 |
+
eq=None,
|
| 15 |
+
lt=None,
|
| 16 |
+
le=None,
|
| 17 |
+
gt=None,
|
| 18 |
+
ge=None,
|
| 19 |
+
require_same_type=True,
|
| 20 |
+
class_name="Comparable",
|
| 21 |
+
):
|
| 22 |
+
"""
|
| 23 |
+
Create a class that can be passed into `attrs.field`'s ``eq``, ``order``,
|
| 24 |
+
and ``cmp`` arguments to customize field comparison.
|
| 25 |
+
|
| 26 |
+
The resulting class will have a full set of ordering methods if at least
|
| 27 |
+
one of ``{lt, le, gt, ge}`` and ``eq`` are provided.
|
| 28 |
+
|
| 29 |
+
Args:
|
| 30 |
+
eq (typing.Callable | None):
|
| 31 |
+
Callable used to evaluate equality of two objects.
|
| 32 |
+
|
| 33 |
+
lt (typing.Callable | None):
|
| 34 |
+
Callable used to evaluate whether one object is less than another
|
| 35 |
+
object.
|
| 36 |
+
|
| 37 |
+
le (typing.Callable | None):
|
| 38 |
+
Callable used to evaluate whether one object is less than or equal
|
| 39 |
+
to another object.
|
| 40 |
+
|
| 41 |
+
gt (typing.Callable | None):
|
| 42 |
+
Callable used to evaluate whether one object is greater than
|
| 43 |
+
another object.
|
| 44 |
+
|
| 45 |
+
ge (typing.Callable | None):
|
| 46 |
+
Callable used to evaluate whether one object is greater than or
|
| 47 |
+
equal to another object.
|
| 48 |
+
|
| 49 |
+
require_same_type (bool):
|
| 50 |
+
When `True`, equality and ordering methods will return
|
| 51 |
+
`NotImplemented` if objects are not of the same type.
|
| 52 |
+
|
| 53 |
+
class_name (str | None): Name of class. Defaults to "Comparable".
|
| 54 |
+
|
| 55 |
+
See `comparison` for more details.
|
| 56 |
+
|
| 57 |
+
.. versionadded:: 21.1.0
|
| 58 |
+
"""
|
| 59 |
+
|
| 60 |
+
body = {
|
| 61 |
+
"__slots__": ["value"],
|
| 62 |
+
"__init__": _make_init(),
|
| 63 |
+
"_requirements": [],
|
| 64 |
+
"_is_comparable_to": _is_comparable_to,
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
# Add operations.
|
| 68 |
+
num_order_functions = 0
|
| 69 |
+
has_eq_function = False
|
| 70 |
+
|
| 71 |
+
if eq is not None:
|
| 72 |
+
has_eq_function = True
|
| 73 |
+
body["__eq__"] = _make_operator("eq", eq)
|
| 74 |
+
body["__ne__"] = _make_ne()
|
| 75 |
+
|
| 76 |
+
if lt is not None:
|
| 77 |
+
num_order_functions += 1
|
| 78 |
+
body["__lt__"] = _make_operator("lt", lt)
|
| 79 |
+
|
| 80 |
+
if le is not None:
|
| 81 |
+
num_order_functions += 1
|
| 82 |
+
body["__le__"] = _make_operator("le", le)
|
| 83 |
+
|
| 84 |
+
if gt is not None:
|
| 85 |
+
num_order_functions += 1
|
| 86 |
+
body["__gt__"] = _make_operator("gt", gt)
|
| 87 |
+
|
| 88 |
+
if ge is not None:
|
| 89 |
+
num_order_functions += 1
|
| 90 |
+
body["__ge__"] = _make_operator("ge", ge)
|
| 91 |
+
|
| 92 |
+
type_ = types.new_class(
|
| 93 |
+
class_name, (object,), {}, lambda ns: ns.update(body)
|
| 94 |
+
)
|
| 95 |
+
|
| 96 |
+
# Add same type requirement.
|
| 97 |
+
if require_same_type:
|
| 98 |
+
type_._requirements.append(_check_same_type)
|
| 99 |
+
|
| 100 |
+
# Add total ordering if at least one operation was defined.
|
| 101 |
+
if 0 < num_order_functions < 4:
|
| 102 |
+
if not has_eq_function:
|
| 103 |
+
# functools.total_ordering requires __eq__ to be defined,
|
| 104 |
+
# so raise early error here to keep a nice stack.
|
| 105 |
+
msg = "eq must be define is order to complete ordering from lt, le, gt, ge."
|
| 106 |
+
raise ValueError(msg)
|
| 107 |
+
type_ = functools.total_ordering(type_)
|
| 108 |
+
|
| 109 |
+
return type_
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
def _make_init():
|
| 113 |
+
"""
|
| 114 |
+
Create __init__ method.
|
| 115 |
+
"""
|
| 116 |
+
|
| 117 |
+
def __init__(self, value):
|
| 118 |
+
"""
|
| 119 |
+
Initialize object with *value*.
|
| 120 |
+
"""
|
| 121 |
+
self.value = value
|
| 122 |
+
|
| 123 |
+
return __init__
|
| 124 |
+
|
| 125 |
+
|
| 126 |
+
def _make_operator(name, func):
|
| 127 |
+
"""
|
| 128 |
+
Create operator method.
|
| 129 |
+
"""
|
| 130 |
+
|
| 131 |
+
def method(self, other):
|
| 132 |
+
if not self._is_comparable_to(other):
|
| 133 |
+
return NotImplemented
|
| 134 |
+
|
| 135 |
+
result = func(self.value, other.value)
|
| 136 |
+
if result is NotImplemented:
|
| 137 |
+
return NotImplemented
|
| 138 |
+
|
| 139 |
+
return result
|
| 140 |
+
|
| 141 |
+
method.__name__ = f"__{name}__"
|
| 142 |
+
method.__doc__ = (
|
| 143 |
+
f"Return a {_operation_names[name]} b. Computed by attrs."
|
| 144 |
+
)
|
| 145 |
+
|
| 146 |
+
return method
|
| 147 |
+
|
| 148 |
+
|
| 149 |
+
def _is_comparable_to(self, other):
|
| 150 |
+
"""
|
| 151 |
+
Check whether `other` is comparable to `self`.
|
| 152 |
+
"""
|
| 153 |
+
return all(func(self, other) for func in self._requirements)
|
| 154 |
+
|
| 155 |
+
|
| 156 |
+
def _check_same_type(self, other):
|
| 157 |
+
"""
|
| 158 |
+
Return True if *self* and *other* are of the same type, False otherwise.
|
| 159 |
+
"""
|
| 160 |
+
return other.value.__class__ is self.value.__class__
|
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attr/_cmp.pyi
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Any, Callable
|
| 2 |
+
|
| 3 |
+
_CompareWithType = Callable[[Any, Any], bool]
|
| 4 |
+
|
| 5 |
+
def cmp_using(
|
| 6 |
+
eq: _CompareWithType | None = ...,
|
| 7 |
+
lt: _CompareWithType | None = ...,
|
| 8 |
+
le: _CompareWithType | None = ...,
|
| 9 |
+
gt: _CompareWithType | None = ...,
|
| 10 |
+
ge: _CompareWithType | None = ...,
|
| 11 |
+
require_same_type: bool = ...,
|
| 12 |
+
class_name: str = ...,
|
| 13 |
+
) -> type: ...
|
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attr/_make.py
ADDED
|
@@ -0,0 +1,3055 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: MIT
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import abc
|
| 6 |
+
import contextlib
|
| 7 |
+
import copy
|
| 8 |
+
import enum
|
| 9 |
+
import functools
|
| 10 |
+
import inspect
|
| 11 |
+
import itertools
|
| 12 |
+
import linecache
|
| 13 |
+
import sys
|
| 14 |
+
import types
|
| 15 |
+
import typing
|
| 16 |
+
|
| 17 |
+
from operator import itemgetter
|
| 18 |
+
|
| 19 |
+
# We need to import _compat itself in addition to the _compat members to avoid
|
| 20 |
+
# having the thread-local in the globals here.
|
| 21 |
+
from . import _compat, _config, setters
|
| 22 |
+
from ._compat import (
|
| 23 |
+
PY_3_10_PLUS,
|
| 24 |
+
PY_3_11_PLUS,
|
| 25 |
+
PY_3_13_PLUS,
|
| 26 |
+
_AnnotationExtractor,
|
| 27 |
+
_get_annotations,
|
| 28 |
+
get_generic_base,
|
| 29 |
+
)
|
| 30 |
+
from .exceptions import (
|
| 31 |
+
DefaultAlreadySetError,
|
| 32 |
+
FrozenInstanceError,
|
| 33 |
+
NotAnAttrsClassError,
|
| 34 |
+
UnannotatedAttributeError,
|
| 35 |
+
)
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
# This is used at least twice, so cache it here.
|
| 39 |
+
_OBJ_SETATTR = object.__setattr__
|
| 40 |
+
_INIT_FACTORY_PAT = "__attr_factory_%s"
|
| 41 |
+
_CLASSVAR_PREFIXES = (
|
| 42 |
+
"typing.ClassVar",
|
| 43 |
+
"t.ClassVar",
|
| 44 |
+
"ClassVar",
|
| 45 |
+
"typing_extensions.ClassVar",
|
| 46 |
+
)
|
| 47 |
+
# we don't use a double-underscore prefix because that triggers
|
| 48 |
+
# name mangling when trying to create a slot for the field
|
| 49 |
+
# (when slots=True)
|
| 50 |
+
_HASH_CACHE_FIELD = "_attrs_cached_hash"
|
| 51 |
+
|
| 52 |
+
_EMPTY_METADATA_SINGLETON = types.MappingProxyType({})
|
| 53 |
+
|
| 54 |
+
# Unique object for unequivocal getattr() defaults.
|
| 55 |
+
_SENTINEL = object()
|
| 56 |
+
|
| 57 |
+
_DEFAULT_ON_SETATTR = setters.pipe(setters.convert, setters.validate)
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
class _Nothing(enum.Enum):
|
| 61 |
+
"""
|
| 62 |
+
Sentinel to indicate the lack of a value when `None` is ambiguous.
|
| 63 |
+
|
| 64 |
+
If extending attrs, you can use ``typing.Literal[NOTHING]`` to show
|
| 65 |
+
that a value may be ``NOTHING``.
|
| 66 |
+
|
| 67 |
+
.. versionchanged:: 21.1.0 ``bool(NOTHING)`` is now False.
|
| 68 |
+
.. versionchanged:: 22.2.0 ``NOTHING`` is now an ``enum.Enum`` variant.
|
| 69 |
+
"""
|
| 70 |
+
|
| 71 |
+
NOTHING = enum.auto()
|
| 72 |
+
|
| 73 |
+
def __repr__(self):
|
| 74 |
+
return "NOTHING"
|
| 75 |
+
|
| 76 |
+
def __bool__(self):
|
| 77 |
+
return False
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
NOTHING = _Nothing.NOTHING
|
| 81 |
+
"""
|
| 82 |
+
Sentinel to indicate the lack of a value when `None` is ambiguous.
|
| 83 |
+
|
| 84 |
+
When using in 3rd party code, use `attrs.NothingType` for type annotations.
|
| 85 |
+
"""
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
class _CacheHashWrapper(int):
|
| 89 |
+
"""
|
| 90 |
+
An integer subclass that pickles / copies as None
|
| 91 |
+
|
| 92 |
+
This is used for non-slots classes with ``cache_hash=True``, to avoid
|
| 93 |
+
serializing a potentially (even likely) invalid hash value. Since `None`
|
| 94 |
+
is the default value for uncalculated hashes, whenever this is copied,
|
| 95 |
+
the copy's value for the hash should automatically reset.
|
| 96 |
+
|
| 97 |
+
See GH #613 for more details.
|
| 98 |
+
"""
|
| 99 |
+
|
| 100 |
+
def __reduce__(self, _none_constructor=type(None), _args=()): # noqa: B008
|
| 101 |
+
return _none_constructor, _args
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
def attrib(
|
| 105 |
+
default=NOTHING,
|
| 106 |
+
validator=None,
|
| 107 |
+
repr=True,
|
| 108 |
+
cmp=None,
|
| 109 |
+
hash=None,
|
| 110 |
+
init=True,
|
| 111 |
+
metadata=None,
|
| 112 |
+
type=None,
|
| 113 |
+
converter=None,
|
| 114 |
+
factory=None,
|
| 115 |
+
kw_only=False,
|
| 116 |
+
eq=None,
|
| 117 |
+
order=None,
|
| 118 |
+
on_setattr=None,
|
| 119 |
+
alias=None,
|
| 120 |
+
):
|
| 121 |
+
"""
|
| 122 |
+
Create a new field / attribute on a class.
|
| 123 |
+
|
| 124 |
+
Identical to `attrs.field`, except it's not keyword-only.
|
| 125 |
+
|
| 126 |
+
Consider using `attrs.field` in new code (``attr.ib`` will *never* go away,
|
| 127 |
+
though).
|
| 128 |
+
|
| 129 |
+
.. warning::
|
| 130 |
+
|
| 131 |
+
Does **nothing** unless the class is also decorated with
|
| 132 |
+
`attr.s` (or similar)!
|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
.. versionadded:: 15.2.0 *convert*
|
| 136 |
+
.. versionadded:: 16.3.0 *metadata*
|
| 137 |
+
.. versionchanged:: 17.1.0 *validator* can be a ``list`` now.
|
| 138 |
+
.. versionchanged:: 17.1.0
|
| 139 |
+
*hash* is `None` and therefore mirrors *eq* by default.
|
| 140 |
+
.. versionadded:: 17.3.0 *type*
|
| 141 |
+
.. deprecated:: 17.4.0 *convert*
|
| 142 |
+
.. versionadded:: 17.4.0
|
| 143 |
+
*converter* as a replacement for the deprecated *convert* to achieve
|
| 144 |
+
consistency with other noun-based arguments.
|
| 145 |
+
.. versionadded:: 18.1.0
|
| 146 |
+
``factory=f`` is syntactic sugar for ``default=attr.Factory(f)``.
|
| 147 |
+
.. versionadded:: 18.2.0 *kw_only*
|
| 148 |
+
.. versionchanged:: 19.2.0 *convert* keyword argument removed.
|
| 149 |
+
.. versionchanged:: 19.2.0 *repr* also accepts a custom callable.
|
| 150 |
+
.. deprecated:: 19.2.0 *cmp* Removal on or after 2021-06-01.
|
| 151 |
+
.. versionadded:: 19.2.0 *eq* and *order*
|
| 152 |
+
.. versionadded:: 20.1.0 *on_setattr*
|
| 153 |
+
.. versionchanged:: 20.3.0 *kw_only* backported to Python 2
|
| 154 |
+
.. versionchanged:: 21.1.0
|
| 155 |
+
*eq*, *order*, and *cmp* also accept a custom callable
|
| 156 |
+
.. versionchanged:: 21.1.0 *cmp* undeprecated
|
| 157 |
+
.. versionadded:: 22.2.0 *alias*
|
| 158 |
+
"""
|
| 159 |
+
eq, eq_key, order, order_key = _determine_attrib_eq_order(
|
| 160 |
+
cmp, eq, order, True
|
| 161 |
+
)
|
| 162 |
+
|
| 163 |
+
if hash is not None and hash is not True and hash is not False:
|
| 164 |
+
msg = "Invalid value for hash. Must be True, False, or None."
|
| 165 |
+
raise TypeError(msg)
|
| 166 |
+
|
| 167 |
+
if factory is not None:
|
| 168 |
+
if default is not NOTHING:
|
| 169 |
+
msg = (
|
| 170 |
+
"The `default` and `factory` arguments are mutually exclusive."
|
| 171 |
+
)
|
| 172 |
+
raise ValueError(msg)
|
| 173 |
+
if not callable(factory):
|
| 174 |
+
msg = "The `factory` argument must be a callable."
|
| 175 |
+
raise ValueError(msg)
|
| 176 |
+
default = Factory(factory)
|
| 177 |
+
|
| 178 |
+
if metadata is None:
|
| 179 |
+
metadata = {}
|
| 180 |
+
|
| 181 |
+
# Apply syntactic sugar by auto-wrapping.
|
| 182 |
+
if isinstance(on_setattr, (list, tuple)):
|
| 183 |
+
on_setattr = setters.pipe(*on_setattr)
|
| 184 |
+
|
| 185 |
+
if validator and isinstance(validator, (list, tuple)):
|
| 186 |
+
validator = and_(*validator)
|
| 187 |
+
|
| 188 |
+
if converter and isinstance(converter, (list, tuple)):
|
| 189 |
+
converter = pipe(*converter)
|
| 190 |
+
|
| 191 |
+
return _CountingAttr(
|
| 192 |
+
default=default,
|
| 193 |
+
validator=validator,
|
| 194 |
+
repr=repr,
|
| 195 |
+
cmp=None,
|
| 196 |
+
hash=hash,
|
| 197 |
+
init=init,
|
| 198 |
+
converter=converter,
|
| 199 |
+
metadata=metadata,
|
| 200 |
+
type=type,
|
| 201 |
+
kw_only=kw_only,
|
| 202 |
+
eq=eq,
|
| 203 |
+
eq_key=eq_key,
|
| 204 |
+
order=order,
|
| 205 |
+
order_key=order_key,
|
| 206 |
+
on_setattr=on_setattr,
|
| 207 |
+
alias=alias,
|
| 208 |
+
)
|
| 209 |
+
|
| 210 |
+
|
| 211 |
+
def _compile_and_eval(script, globs, locs=None, filename=""):
|
| 212 |
+
"""
|
| 213 |
+
Evaluate the script with the given global (globs) and local (locs)
|
| 214 |
+
variables.
|
| 215 |
+
"""
|
| 216 |
+
bytecode = compile(script, filename, "exec")
|
| 217 |
+
eval(bytecode, globs, locs)
|
| 218 |
+
|
| 219 |
+
|
| 220 |
+
def _make_method(name, script, filename, globs, locals=None):
|
| 221 |
+
"""
|
| 222 |
+
Create the method with the script given and return the method object.
|
| 223 |
+
"""
|
| 224 |
+
locs = {} if locals is None else locals
|
| 225 |
+
|
| 226 |
+
# In order of debuggers like PDB being able to step through the code,
|
| 227 |
+
# we add a fake linecache entry.
|
| 228 |
+
count = 1
|
| 229 |
+
base_filename = filename
|
| 230 |
+
while True:
|
| 231 |
+
linecache_tuple = (
|
| 232 |
+
len(script),
|
| 233 |
+
None,
|
| 234 |
+
script.splitlines(True),
|
| 235 |
+
filename,
|
| 236 |
+
)
|
| 237 |
+
old_val = linecache.cache.setdefault(filename, linecache_tuple)
|
| 238 |
+
if old_val == linecache_tuple:
|
| 239 |
+
break
|
| 240 |
+
|
| 241 |
+
filename = f"{base_filename[:-1]}-{count}>"
|
| 242 |
+
count += 1
|
| 243 |
+
|
| 244 |
+
_compile_and_eval(script, globs, locs, filename)
|
| 245 |
+
|
| 246 |
+
return locs[name]
|
| 247 |
+
|
| 248 |
+
|
| 249 |
+
def _make_attr_tuple_class(cls_name, attr_names):
|
| 250 |
+
"""
|
| 251 |
+
Create a tuple subclass to hold `Attribute`s for an `attrs` class.
|
| 252 |
+
|
| 253 |
+
The subclass is a bare tuple with properties for names.
|
| 254 |
+
|
| 255 |
+
class MyClassAttributes(tuple):
|
| 256 |
+
__slots__ = ()
|
| 257 |
+
x = property(itemgetter(0))
|
| 258 |
+
"""
|
| 259 |
+
attr_class_name = f"{cls_name}Attributes"
|
| 260 |
+
attr_class_template = [
|
| 261 |
+
f"class {attr_class_name}(tuple):",
|
| 262 |
+
" __slots__ = ()",
|
| 263 |
+
]
|
| 264 |
+
if attr_names:
|
| 265 |
+
for i, attr_name in enumerate(attr_names):
|
| 266 |
+
attr_class_template.append(
|
| 267 |
+
f" {attr_name} = _attrs_property(_attrs_itemgetter({i}))"
|
| 268 |
+
)
|
| 269 |
+
else:
|
| 270 |
+
attr_class_template.append(" pass")
|
| 271 |
+
globs = {"_attrs_itemgetter": itemgetter, "_attrs_property": property}
|
| 272 |
+
_compile_and_eval("\n".join(attr_class_template), globs)
|
| 273 |
+
return globs[attr_class_name]
|
| 274 |
+
|
| 275 |
+
|
| 276 |
+
# Tuple class for extracted attributes from a class definition.
|
| 277 |
+
# `base_attrs` is a subset of `attrs`.
|
| 278 |
+
_Attributes = _make_attr_tuple_class(
|
| 279 |
+
"_Attributes",
|
| 280 |
+
[
|
| 281 |
+
# all attributes to build dunder methods for
|
| 282 |
+
"attrs",
|
| 283 |
+
# attributes that have been inherited
|
| 284 |
+
"base_attrs",
|
| 285 |
+
# map inherited attributes to their originating classes
|
| 286 |
+
"base_attrs_map",
|
| 287 |
+
],
|
| 288 |
+
)
|
| 289 |
+
|
| 290 |
+
|
| 291 |
+
def _is_class_var(annot):
|
| 292 |
+
"""
|
| 293 |
+
Check whether *annot* is a typing.ClassVar.
|
| 294 |
+
|
| 295 |
+
The string comparison hack is used to avoid evaluating all string
|
| 296 |
+
annotations which would put attrs-based classes at a performance
|
| 297 |
+
disadvantage compared to plain old classes.
|
| 298 |
+
"""
|
| 299 |
+
annot = str(annot)
|
| 300 |
+
|
| 301 |
+
# Annotation can be quoted.
|
| 302 |
+
if annot.startswith(("'", '"')) and annot.endswith(("'", '"')):
|
| 303 |
+
annot = annot[1:-1]
|
| 304 |
+
|
| 305 |
+
return annot.startswith(_CLASSVAR_PREFIXES)
|
| 306 |
+
|
| 307 |
+
|
| 308 |
+
def _has_own_attribute(cls, attrib_name):
|
| 309 |
+
"""
|
| 310 |
+
Check whether *cls* defines *attrib_name* (and doesn't just inherit it).
|
| 311 |
+
"""
|
| 312 |
+
return attrib_name in cls.__dict__
|
| 313 |
+
|
| 314 |
+
|
| 315 |
+
def _collect_base_attrs(cls, taken_attr_names):
|
| 316 |
+
"""
|
| 317 |
+
Collect attr.ibs from base classes of *cls*, except *taken_attr_names*.
|
| 318 |
+
"""
|
| 319 |
+
base_attrs = []
|
| 320 |
+
base_attr_map = {} # A dictionary of base attrs to their classes.
|
| 321 |
+
|
| 322 |
+
# Traverse the MRO and collect attributes.
|
| 323 |
+
for base_cls in reversed(cls.__mro__[1:-1]):
|
| 324 |
+
for a in getattr(base_cls, "__attrs_attrs__", []):
|
| 325 |
+
if a.inherited or a.name in taken_attr_names:
|
| 326 |
+
continue
|
| 327 |
+
|
| 328 |
+
a = a.evolve(inherited=True) # noqa: PLW2901
|
| 329 |
+
base_attrs.append(a)
|
| 330 |
+
base_attr_map[a.name] = base_cls
|
| 331 |
+
|
| 332 |
+
# For each name, only keep the freshest definition i.e. the furthest at the
|
| 333 |
+
# back. base_attr_map is fine because it gets overwritten with every new
|
| 334 |
+
# instance.
|
| 335 |
+
filtered = []
|
| 336 |
+
seen = set()
|
| 337 |
+
for a in reversed(base_attrs):
|
| 338 |
+
if a.name in seen:
|
| 339 |
+
continue
|
| 340 |
+
filtered.insert(0, a)
|
| 341 |
+
seen.add(a.name)
|
| 342 |
+
|
| 343 |
+
return filtered, base_attr_map
|
| 344 |
+
|
| 345 |
+
|
| 346 |
+
def _collect_base_attrs_broken(cls, taken_attr_names):
|
| 347 |
+
"""
|
| 348 |
+
Collect attr.ibs from base classes of *cls*, except *taken_attr_names*.
|
| 349 |
+
|
| 350 |
+
N.B. *taken_attr_names* will be mutated.
|
| 351 |
+
|
| 352 |
+
Adhere to the old incorrect behavior.
|
| 353 |
+
|
| 354 |
+
Notably it collects from the front and considers inherited attributes which
|
| 355 |
+
leads to the buggy behavior reported in #428.
|
| 356 |
+
"""
|
| 357 |
+
base_attrs = []
|
| 358 |
+
base_attr_map = {} # A dictionary of base attrs to their classes.
|
| 359 |
+
|
| 360 |
+
# Traverse the MRO and collect attributes.
|
| 361 |
+
for base_cls in cls.__mro__[1:-1]:
|
| 362 |
+
for a in getattr(base_cls, "__attrs_attrs__", []):
|
| 363 |
+
if a.name in taken_attr_names:
|
| 364 |
+
continue
|
| 365 |
+
|
| 366 |
+
a = a.evolve(inherited=True) # noqa: PLW2901
|
| 367 |
+
taken_attr_names.add(a.name)
|
| 368 |
+
base_attrs.append(a)
|
| 369 |
+
base_attr_map[a.name] = base_cls
|
| 370 |
+
|
| 371 |
+
return base_attrs, base_attr_map
|
| 372 |
+
|
| 373 |
+
|
| 374 |
+
def _transform_attrs(
|
| 375 |
+
cls, these, auto_attribs, kw_only, collect_by_mro, field_transformer
|
| 376 |
+
):
|
| 377 |
+
"""
|
| 378 |
+
Transform all `_CountingAttr`s on a class into `Attribute`s.
|
| 379 |
+
|
| 380 |
+
If *these* is passed, use that and don't look for them on the class.
|
| 381 |
+
|
| 382 |
+
If *collect_by_mro* is True, collect them in the correct MRO order,
|
| 383 |
+
otherwise use the old -- incorrect -- order. See #428.
|
| 384 |
+
|
| 385 |
+
Return an `_Attributes`.
|
| 386 |
+
"""
|
| 387 |
+
cd = cls.__dict__
|
| 388 |
+
anns = _get_annotations(cls)
|
| 389 |
+
|
| 390 |
+
if these is not None:
|
| 391 |
+
ca_list = list(these.items())
|
| 392 |
+
elif auto_attribs is True:
|
| 393 |
+
ca_names = {
|
| 394 |
+
name
|
| 395 |
+
for name, attr in cd.items()
|
| 396 |
+
if isinstance(attr, _CountingAttr)
|
| 397 |
+
}
|
| 398 |
+
ca_list = []
|
| 399 |
+
annot_names = set()
|
| 400 |
+
for attr_name, type in anns.items():
|
| 401 |
+
if _is_class_var(type):
|
| 402 |
+
continue
|
| 403 |
+
annot_names.add(attr_name)
|
| 404 |
+
a = cd.get(attr_name, NOTHING)
|
| 405 |
+
|
| 406 |
+
if not isinstance(a, _CountingAttr):
|
| 407 |
+
a = attrib() if a is NOTHING else attrib(default=a)
|
| 408 |
+
ca_list.append((attr_name, a))
|
| 409 |
+
|
| 410 |
+
unannotated = ca_names - annot_names
|
| 411 |
+
if len(unannotated) > 0:
|
| 412 |
+
raise UnannotatedAttributeError(
|
| 413 |
+
"The following `attr.ib`s lack a type annotation: "
|
| 414 |
+
+ ", ".join(
|
| 415 |
+
sorted(unannotated, key=lambda n: cd.get(n).counter)
|
| 416 |
+
)
|
| 417 |
+
+ "."
|
| 418 |
+
)
|
| 419 |
+
else:
|
| 420 |
+
ca_list = sorted(
|
| 421 |
+
(
|
| 422 |
+
(name, attr)
|
| 423 |
+
for name, attr in cd.items()
|
| 424 |
+
if isinstance(attr, _CountingAttr)
|
| 425 |
+
),
|
| 426 |
+
key=lambda e: e[1].counter,
|
| 427 |
+
)
|
| 428 |
+
|
| 429 |
+
own_attrs = [
|
| 430 |
+
Attribute.from_counting_attr(
|
| 431 |
+
name=attr_name, ca=ca, type=anns.get(attr_name)
|
| 432 |
+
)
|
| 433 |
+
for attr_name, ca in ca_list
|
| 434 |
+
]
|
| 435 |
+
|
| 436 |
+
if collect_by_mro:
|
| 437 |
+
base_attrs, base_attr_map = _collect_base_attrs(
|
| 438 |
+
cls, {a.name for a in own_attrs}
|
| 439 |
+
)
|
| 440 |
+
else:
|
| 441 |
+
base_attrs, base_attr_map = _collect_base_attrs_broken(
|
| 442 |
+
cls, {a.name for a in own_attrs}
|
| 443 |
+
)
|
| 444 |
+
|
| 445 |
+
if kw_only:
|
| 446 |
+
own_attrs = [a.evolve(kw_only=True) for a in own_attrs]
|
| 447 |
+
base_attrs = [a.evolve(kw_only=True) for a in base_attrs]
|
| 448 |
+
|
| 449 |
+
attrs = base_attrs + own_attrs
|
| 450 |
+
|
| 451 |
+
# Mandatory vs non-mandatory attr order only matters when they are part of
|
| 452 |
+
# the __init__ signature and when they aren't kw_only (which are moved to
|
| 453 |
+
# the end and can be mandatory or non-mandatory in any order, as they will
|
| 454 |
+
# be specified as keyword args anyway). Check the order of those attrs:
|
| 455 |
+
had_default = False
|
| 456 |
+
for a in (a for a in attrs if a.init is not False and a.kw_only is False):
|
| 457 |
+
if had_default is True and a.default is NOTHING:
|
| 458 |
+
msg = f"No mandatory attributes allowed after an attribute with a default value or factory. Attribute in question: {a!r}"
|
| 459 |
+
raise ValueError(msg)
|
| 460 |
+
|
| 461 |
+
if had_default is False and a.default is not NOTHING:
|
| 462 |
+
had_default = True
|
| 463 |
+
|
| 464 |
+
if field_transformer is not None:
|
| 465 |
+
attrs = field_transformer(cls, attrs)
|
| 466 |
+
|
| 467 |
+
# Resolve default field alias after executing field_transformer.
|
| 468 |
+
# This allows field_transformer to differentiate between explicit vs
|
| 469 |
+
# default aliases and supply their own defaults.
|
| 470 |
+
attrs = [
|
| 471 |
+
a.evolve(alias=_default_init_alias_for(a.name)) if not a.alias else a
|
| 472 |
+
for a in attrs
|
| 473 |
+
]
|
| 474 |
+
|
| 475 |
+
# Create AttrsClass *after* applying the field_transformer since it may
|
| 476 |
+
# add or remove attributes!
|
| 477 |
+
attr_names = [a.name for a in attrs]
|
| 478 |
+
AttrsClass = _make_attr_tuple_class(cls.__name__, attr_names)
|
| 479 |
+
|
| 480 |
+
return _Attributes((AttrsClass(attrs), base_attrs, base_attr_map))
|
| 481 |
+
|
| 482 |
+
|
| 483 |
+
def _make_cached_property_getattr(cached_properties, original_getattr, cls):
|
| 484 |
+
lines = [
|
| 485 |
+
# Wrapped to get `__class__` into closure cell for super()
|
| 486 |
+
# (It will be replaced with the newly constructed class after construction).
|
| 487 |
+
"def wrapper(_cls):",
|
| 488 |
+
" __class__ = _cls",
|
| 489 |
+
" def __getattr__(self, item, cached_properties=cached_properties, original_getattr=original_getattr, _cached_setattr_get=_cached_setattr_get):",
|
| 490 |
+
" func = cached_properties.get(item)",
|
| 491 |
+
" if func is not None:",
|
| 492 |
+
" result = func(self)",
|
| 493 |
+
" _setter = _cached_setattr_get(self)",
|
| 494 |
+
" _setter(item, result)",
|
| 495 |
+
" return result",
|
| 496 |
+
]
|
| 497 |
+
if original_getattr is not None:
|
| 498 |
+
lines.append(
|
| 499 |
+
" return original_getattr(self, item)",
|
| 500 |
+
)
|
| 501 |
+
else:
|
| 502 |
+
lines.extend(
|
| 503 |
+
[
|
| 504 |
+
" try:",
|
| 505 |
+
" return super().__getattribute__(item)",
|
| 506 |
+
" except AttributeError:",
|
| 507 |
+
" if not hasattr(super(), '__getattr__'):",
|
| 508 |
+
" raise",
|
| 509 |
+
" return super().__getattr__(item)",
|
| 510 |
+
" original_error = f\"'{self.__class__.__name__}' object has no attribute '{item}'\"",
|
| 511 |
+
" raise AttributeError(original_error)",
|
| 512 |
+
]
|
| 513 |
+
)
|
| 514 |
+
|
| 515 |
+
lines.extend(
|
| 516 |
+
[
|
| 517 |
+
" return __getattr__",
|
| 518 |
+
"__getattr__ = wrapper(_cls)",
|
| 519 |
+
]
|
| 520 |
+
)
|
| 521 |
+
|
| 522 |
+
unique_filename = _generate_unique_filename(cls, "getattr")
|
| 523 |
+
|
| 524 |
+
glob = {
|
| 525 |
+
"cached_properties": cached_properties,
|
| 526 |
+
"_cached_setattr_get": _OBJ_SETATTR.__get__,
|
| 527 |
+
"original_getattr": original_getattr,
|
| 528 |
+
}
|
| 529 |
+
|
| 530 |
+
return _make_method(
|
| 531 |
+
"__getattr__",
|
| 532 |
+
"\n".join(lines),
|
| 533 |
+
unique_filename,
|
| 534 |
+
glob,
|
| 535 |
+
locals={
|
| 536 |
+
"_cls": cls,
|
| 537 |
+
},
|
| 538 |
+
)
|
| 539 |
+
|
| 540 |
+
|
| 541 |
+
def _frozen_setattrs(self, name, value):
|
| 542 |
+
"""
|
| 543 |
+
Attached to frozen classes as __setattr__.
|
| 544 |
+
"""
|
| 545 |
+
if isinstance(self, BaseException) and name in (
|
| 546 |
+
"__cause__",
|
| 547 |
+
"__context__",
|
| 548 |
+
"__traceback__",
|
| 549 |
+
"__suppress_context__",
|
| 550 |
+
"__notes__",
|
| 551 |
+
):
|
| 552 |
+
BaseException.__setattr__(self, name, value)
|
| 553 |
+
return
|
| 554 |
+
|
| 555 |
+
raise FrozenInstanceError
|
| 556 |
+
|
| 557 |
+
|
| 558 |
+
def _frozen_delattrs(self, name):
|
| 559 |
+
"""
|
| 560 |
+
Attached to frozen classes as __delattr__.
|
| 561 |
+
"""
|
| 562 |
+
if isinstance(self, BaseException) and name in ("__notes__",):
|
| 563 |
+
BaseException.__delattr__(self, name)
|
| 564 |
+
return
|
| 565 |
+
|
| 566 |
+
raise FrozenInstanceError
|
| 567 |
+
|
| 568 |
+
|
| 569 |
+
def evolve(*args, **changes):
|
| 570 |
+
"""
|
| 571 |
+
Create a new instance, based on the first positional argument with
|
| 572 |
+
*changes* applied.
|
| 573 |
+
|
| 574 |
+
.. tip::
|
| 575 |
+
|
| 576 |
+
On Python 3.13 and later, you can also use `copy.replace` instead.
|
| 577 |
+
|
| 578 |
+
Args:
|
| 579 |
+
|
| 580 |
+
inst:
|
| 581 |
+
Instance of a class with *attrs* attributes. *inst* must be passed
|
| 582 |
+
as a positional argument.
|
| 583 |
+
|
| 584 |
+
changes:
|
| 585 |
+
Keyword changes in the new copy.
|
| 586 |
+
|
| 587 |
+
Returns:
|
| 588 |
+
A copy of inst with *changes* incorporated.
|
| 589 |
+
|
| 590 |
+
Raises:
|
| 591 |
+
TypeError:
|
| 592 |
+
If *attr_name* couldn't be found in the class ``__init__``.
|
| 593 |
+
|
| 594 |
+
attrs.exceptions.NotAnAttrsClassError:
|
| 595 |
+
If *cls* is not an *attrs* class.
|
| 596 |
+
|
| 597 |
+
.. versionadded:: 17.1.0
|
| 598 |
+
.. deprecated:: 23.1.0
|
| 599 |
+
It is now deprecated to pass the instance using the keyword argument
|
| 600 |
+
*inst*. It will raise a warning until at least April 2024, after which
|
| 601 |
+
it will become an error. Always pass the instance as a positional
|
| 602 |
+
argument.
|
| 603 |
+
.. versionchanged:: 24.1.0
|
| 604 |
+
*inst* can't be passed as a keyword argument anymore.
|
| 605 |
+
"""
|
| 606 |
+
try:
|
| 607 |
+
(inst,) = args
|
| 608 |
+
except ValueError:
|
| 609 |
+
msg = (
|
| 610 |
+
f"evolve() takes 1 positional argument, but {len(args)} were given"
|
| 611 |
+
)
|
| 612 |
+
raise TypeError(msg) from None
|
| 613 |
+
|
| 614 |
+
cls = inst.__class__
|
| 615 |
+
attrs = fields(cls)
|
| 616 |
+
for a in attrs:
|
| 617 |
+
if not a.init:
|
| 618 |
+
continue
|
| 619 |
+
attr_name = a.name # To deal with private attributes.
|
| 620 |
+
init_name = a.alias
|
| 621 |
+
if init_name not in changes:
|
| 622 |
+
changes[init_name] = getattr(inst, attr_name)
|
| 623 |
+
|
| 624 |
+
return cls(**changes)
|
| 625 |
+
|
| 626 |
+
|
| 627 |
+
class _ClassBuilder:
|
| 628 |
+
"""
|
| 629 |
+
Iteratively build *one* class.
|
| 630 |
+
"""
|
| 631 |
+
|
| 632 |
+
__slots__ = (
|
| 633 |
+
"_attr_names",
|
| 634 |
+
"_attrs",
|
| 635 |
+
"_base_attr_map",
|
| 636 |
+
"_base_names",
|
| 637 |
+
"_cache_hash",
|
| 638 |
+
"_cls",
|
| 639 |
+
"_cls_dict",
|
| 640 |
+
"_delete_attribs",
|
| 641 |
+
"_frozen",
|
| 642 |
+
"_has_custom_setattr",
|
| 643 |
+
"_has_post_init",
|
| 644 |
+
"_has_pre_init",
|
| 645 |
+
"_is_exc",
|
| 646 |
+
"_on_setattr",
|
| 647 |
+
"_pre_init_has_args",
|
| 648 |
+
"_slots",
|
| 649 |
+
"_weakref_slot",
|
| 650 |
+
"_wrote_own_setattr",
|
| 651 |
+
)
|
| 652 |
+
|
| 653 |
+
def __init__(
|
| 654 |
+
self,
|
| 655 |
+
cls,
|
| 656 |
+
these,
|
| 657 |
+
slots,
|
| 658 |
+
frozen,
|
| 659 |
+
weakref_slot,
|
| 660 |
+
getstate_setstate,
|
| 661 |
+
auto_attribs,
|
| 662 |
+
kw_only,
|
| 663 |
+
cache_hash,
|
| 664 |
+
is_exc,
|
| 665 |
+
collect_by_mro,
|
| 666 |
+
on_setattr,
|
| 667 |
+
has_custom_setattr,
|
| 668 |
+
field_transformer,
|
| 669 |
+
):
|
| 670 |
+
attrs, base_attrs, base_map = _transform_attrs(
|
| 671 |
+
cls,
|
| 672 |
+
these,
|
| 673 |
+
auto_attribs,
|
| 674 |
+
kw_only,
|
| 675 |
+
collect_by_mro,
|
| 676 |
+
field_transformer,
|
| 677 |
+
)
|
| 678 |
+
|
| 679 |
+
self._cls = cls
|
| 680 |
+
self._cls_dict = dict(cls.__dict__) if slots else {}
|
| 681 |
+
self._attrs = attrs
|
| 682 |
+
self._base_names = {a.name for a in base_attrs}
|
| 683 |
+
self._base_attr_map = base_map
|
| 684 |
+
self._attr_names = tuple(a.name for a in attrs)
|
| 685 |
+
self._slots = slots
|
| 686 |
+
self._frozen = frozen
|
| 687 |
+
self._weakref_slot = weakref_slot
|
| 688 |
+
self._cache_hash = cache_hash
|
| 689 |
+
self._has_pre_init = bool(getattr(cls, "__attrs_pre_init__", False))
|
| 690 |
+
self._pre_init_has_args = False
|
| 691 |
+
if self._has_pre_init:
|
| 692 |
+
# Check if the pre init method has more arguments than just `self`
|
| 693 |
+
# We want to pass arguments if pre init expects arguments
|
| 694 |
+
pre_init_func = cls.__attrs_pre_init__
|
| 695 |
+
pre_init_signature = inspect.signature(pre_init_func)
|
| 696 |
+
self._pre_init_has_args = len(pre_init_signature.parameters) > 1
|
| 697 |
+
self._has_post_init = bool(getattr(cls, "__attrs_post_init__", False))
|
| 698 |
+
self._delete_attribs = not bool(these)
|
| 699 |
+
self._is_exc = is_exc
|
| 700 |
+
self._on_setattr = on_setattr
|
| 701 |
+
|
| 702 |
+
self._has_custom_setattr = has_custom_setattr
|
| 703 |
+
self._wrote_own_setattr = False
|
| 704 |
+
|
| 705 |
+
self._cls_dict["__attrs_attrs__"] = self._attrs
|
| 706 |
+
|
| 707 |
+
if frozen:
|
| 708 |
+
self._cls_dict["__setattr__"] = _frozen_setattrs
|
| 709 |
+
self._cls_dict["__delattr__"] = _frozen_delattrs
|
| 710 |
+
|
| 711 |
+
self._wrote_own_setattr = True
|
| 712 |
+
elif on_setattr in (
|
| 713 |
+
_DEFAULT_ON_SETATTR,
|
| 714 |
+
setters.validate,
|
| 715 |
+
setters.convert,
|
| 716 |
+
):
|
| 717 |
+
has_validator = has_converter = False
|
| 718 |
+
for a in attrs:
|
| 719 |
+
if a.validator is not None:
|
| 720 |
+
has_validator = True
|
| 721 |
+
if a.converter is not None:
|
| 722 |
+
has_converter = True
|
| 723 |
+
|
| 724 |
+
if has_validator and has_converter:
|
| 725 |
+
break
|
| 726 |
+
if (
|
| 727 |
+
(
|
| 728 |
+
on_setattr == _DEFAULT_ON_SETATTR
|
| 729 |
+
and not (has_validator or has_converter)
|
| 730 |
+
)
|
| 731 |
+
or (on_setattr == setters.validate and not has_validator)
|
| 732 |
+
or (on_setattr == setters.convert and not has_converter)
|
| 733 |
+
):
|
| 734 |
+
# If class-level on_setattr is set to convert + validate, but
|
| 735 |
+
# there's no field to convert or validate, pretend like there's
|
| 736 |
+
# no on_setattr.
|
| 737 |
+
self._on_setattr = None
|
| 738 |
+
|
| 739 |
+
if getstate_setstate:
|
| 740 |
+
(
|
| 741 |
+
self._cls_dict["__getstate__"],
|
| 742 |
+
self._cls_dict["__setstate__"],
|
| 743 |
+
) = self._make_getstate_setstate()
|
| 744 |
+
|
| 745 |
+
def __repr__(self):
|
| 746 |
+
return f"<_ClassBuilder(cls={self._cls.__name__})>"
|
| 747 |
+
|
| 748 |
+
def build_class(self):
|
| 749 |
+
"""
|
| 750 |
+
Finalize class based on the accumulated configuration.
|
| 751 |
+
|
| 752 |
+
Builder cannot be used after calling this method.
|
| 753 |
+
"""
|
| 754 |
+
if self._slots is True:
|
| 755 |
+
cls = self._create_slots_class()
|
| 756 |
+
else:
|
| 757 |
+
cls = self._patch_original_class()
|
| 758 |
+
if PY_3_10_PLUS:
|
| 759 |
+
cls = abc.update_abstractmethods(cls)
|
| 760 |
+
|
| 761 |
+
# The method gets only called if it's not inherited from a base class.
|
| 762 |
+
# _has_own_attribute does NOT work properly for classmethods.
|
| 763 |
+
if (
|
| 764 |
+
getattr(cls, "__attrs_init_subclass__", None)
|
| 765 |
+
and "__attrs_init_subclass__" not in cls.__dict__
|
| 766 |
+
):
|
| 767 |
+
cls.__attrs_init_subclass__()
|
| 768 |
+
|
| 769 |
+
return cls
|
| 770 |
+
|
| 771 |
+
def _patch_original_class(self):
|
| 772 |
+
"""
|
| 773 |
+
Apply accumulated methods and return the class.
|
| 774 |
+
"""
|
| 775 |
+
cls = self._cls
|
| 776 |
+
base_names = self._base_names
|
| 777 |
+
|
| 778 |
+
# Clean class of attribute definitions (`attr.ib()`s).
|
| 779 |
+
if self._delete_attribs:
|
| 780 |
+
for name in self._attr_names:
|
| 781 |
+
if (
|
| 782 |
+
name not in base_names
|
| 783 |
+
and getattr(cls, name, _SENTINEL) is not _SENTINEL
|
| 784 |
+
):
|
| 785 |
+
# An AttributeError can happen if a base class defines a
|
| 786 |
+
# class variable and we want to set an attribute with the
|
| 787 |
+
# same name by using only a type annotation.
|
| 788 |
+
with contextlib.suppress(AttributeError):
|
| 789 |
+
delattr(cls, name)
|
| 790 |
+
|
| 791 |
+
# Attach our dunder methods.
|
| 792 |
+
for name, value in self._cls_dict.items():
|
| 793 |
+
setattr(cls, name, value)
|
| 794 |
+
|
| 795 |
+
# If we've inherited an attrs __setattr__ and don't write our own,
|
| 796 |
+
# reset it to object's.
|
| 797 |
+
if not self._wrote_own_setattr and getattr(
|
| 798 |
+
cls, "__attrs_own_setattr__", False
|
| 799 |
+
):
|
| 800 |
+
cls.__attrs_own_setattr__ = False
|
| 801 |
+
|
| 802 |
+
if not self._has_custom_setattr:
|
| 803 |
+
cls.__setattr__ = _OBJ_SETATTR
|
| 804 |
+
|
| 805 |
+
return cls
|
| 806 |
+
|
| 807 |
+
def _create_slots_class(self):
|
| 808 |
+
"""
|
| 809 |
+
Build and return a new class with a `__slots__` attribute.
|
| 810 |
+
"""
|
| 811 |
+
cd = {
|
| 812 |
+
k: v
|
| 813 |
+
for k, v in self._cls_dict.items()
|
| 814 |
+
if k not in (*tuple(self._attr_names), "__dict__", "__weakref__")
|
| 815 |
+
}
|
| 816 |
+
|
| 817 |
+
# If our class doesn't have its own implementation of __setattr__
|
| 818 |
+
# (either from the user or by us), check the bases, if one of them has
|
| 819 |
+
# an attrs-made __setattr__, that needs to be reset. We don't walk the
|
| 820 |
+
# MRO because we only care about our immediate base classes.
|
| 821 |
+
# XXX: This can be confused by subclassing a slotted attrs class with
|
| 822 |
+
# XXX: a non-attrs class and subclass the resulting class with an attrs
|
| 823 |
+
# XXX: class. See `test_slotted_confused` for details. For now that's
|
| 824 |
+
# XXX: OK with us.
|
| 825 |
+
if not self._wrote_own_setattr:
|
| 826 |
+
cd["__attrs_own_setattr__"] = False
|
| 827 |
+
|
| 828 |
+
if not self._has_custom_setattr:
|
| 829 |
+
for base_cls in self._cls.__bases__:
|
| 830 |
+
if base_cls.__dict__.get("__attrs_own_setattr__", False):
|
| 831 |
+
cd["__setattr__"] = _OBJ_SETATTR
|
| 832 |
+
break
|
| 833 |
+
|
| 834 |
+
# Traverse the MRO to collect existing slots
|
| 835 |
+
# and check for an existing __weakref__.
|
| 836 |
+
existing_slots = {}
|
| 837 |
+
weakref_inherited = False
|
| 838 |
+
for base_cls in self._cls.__mro__[1:-1]:
|
| 839 |
+
if base_cls.__dict__.get("__weakref__", None) is not None:
|
| 840 |
+
weakref_inherited = True
|
| 841 |
+
existing_slots.update(
|
| 842 |
+
{
|
| 843 |
+
name: getattr(base_cls, name)
|
| 844 |
+
for name in getattr(base_cls, "__slots__", [])
|
| 845 |
+
}
|
| 846 |
+
)
|
| 847 |
+
|
| 848 |
+
base_names = set(self._base_names)
|
| 849 |
+
|
| 850 |
+
names = self._attr_names
|
| 851 |
+
if (
|
| 852 |
+
self._weakref_slot
|
| 853 |
+
and "__weakref__" not in getattr(self._cls, "__slots__", ())
|
| 854 |
+
and "__weakref__" not in names
|
| 855 |
+
and not weakref_inherited
|
| 856 |
+
):
|
| 857 |
+
names += ("__weakref__",)
|
| 858 |
+
|
| 859 |
+
cached_properties = {
|
| 860 |
+
name: cached_property.func
|
| 861 |
+
for name, cached_property in cd.items()
|
| 862 |
+
if isinstance(cached_property, functools.cached_property)
|
| 863 |
+
}
|
| 864 |
+
|
| 865 |
+
# Collect methods with a `__class__` reference that are shadowed in the new class.
|
| 866 |
+
# To know to update them.
|
| 867 |
+
additional_closure_functions_to_update = []
|
| 868 |
+
if cached_properties:
|
| 869 |
+
class_annotations = _get_annotations(self._cls)
|
| 870 |
+
for name, func in cached_properties.items():
|
| 871 |
+
# Add cached properties to names for slotting.
|
| 872 |
+
names += (name,)
|
| 873 |
+
# Clear out function from class to avoid clashing.
|
| 874 |
+
del cd[name]
|
| 875 |
+
additional_closure_functions_to_update.append(func)
|
| 876 |
+
annotation = inspect.signature(func).return_annotation
|
| 877 |
+
if annotation is not inspect.Parameter.empty:
|
| 878 |
+
class_annotations[name] = annotation
|
| 879 |
+
|
| 880 |
+
original_getattr = cd.get("__getattr__")
|
| 881 |
+
if original_getattr is not None:
|
| 882 |
+
additional_closure_functions_to_update.append(original_getattr)
|
| 883 |
+
|
| 884 |
+
cd["__getattr__"] = _make_cached_property_getattr(
|
| 885 |
+
cached_properties, original_getattr, self._cls
|
| 886 |
+
)
|
| 887 |
+
|
| 888 |
+
# We only add the names of attributes that aren't inherited.
|
| 889 |
+
# Setting __slots__ to inherited attributes wastes memory.
|
| 890 |
+
slot_names = [name for name in names if name not in base_names]
|
| 891 |
+
|
| 892 |
+
# There are slots for attributes from current class
|
| 893 |
+
# that are defined in parent classes.
|
| 894 |
+
# As their descriptors may be overridden by a child class,
|
| 895 |
+
# we collect them here and update the class dict
|
| 896 |
+
reused_slots = {
|
| 897 |
+
slot: slot_descriptor
|
| 898 |
+
for slot, slot_descriptor in existing_slots.items()
|
| 899 |
+
if slot in slot_names
|
| 900 |
+
}
|
| 901 |
+
slot_names = [name for name in slot_names if name not in reused_slots]
|
| 902 |
+
cd.update(reused_slots)
|
| 903 |
+
if self._cache_hash:
|
| 904 |
+
slot_names.append(_HASH_CACHE_FIELD)
|
| 905 |
+
|
| 906 |
+
cd["__slots__"] = tuple(slot_names)
|
| 907 |
+
|
| 908 |
+
cd["__qualname__"] = self._cls.__qualname__
|
| 909 |
+
|
| 910 |
+
# Create new class based on old class and our methods.
|
| 911 |
+
cls = type(self._cls)(self._cls.__name__, self._cls.__bases__, cd)
|
| 912 |
+
|
| 913 |
+
# The following is a fix for
|
| 914 |
+
# <https://github.com/python-attrs/attrs/issues/102>.
|
| 915 |
+
# If a method mentions `__class__` or uses the no-arg super(), the
|
| 916 |
+
# compiler will bake a reference to the class in the method itself
|
| 917 |
+
# as `method.__closure__`. Since we replace the class with a
|
| 918 |
+
# clone, we rewrite these references so it keeps working.
|
| 919 |
+
for item in itertools.chain(
|
| 920 |
+
cls.__dict__.values(), additional_closure_functions_to_update
|
| 921 |
+
):
|
| 922 |
+
if isinstance(item, (classmethod, staticmethod)):
|
| 923 |
+
# Class- and staticmethods hide their functions inside.
|
| 924 |
+
# These might need to be rewritten as well.
|
| 925 |
+
closure_cells = getattr(item.__func__, "__closure__", None)
|
| 926 |
+
elif isinstance(item, property):
|
| 927 |
+
# Workaround for property `super()` shortcut (PY3-only).
|
| 928 |
+
# There is no universal way for other descriptors.
|
| 929 |
+
closure_cells = getattr(item.fget, "__closure__", None)
|
| 930 |
+
else:
|
| 931 |
+
closure_cells = getattr(item, "__closure__", None)
|
| 932 |
+
|
| 933 |
+
if not closure_cells: # Catch None or the empty list.
|
| 934 |
+
continue
|
| 935 |
+
for cell in closure_cells:
|
| 936 |
+
try:
|
| 937 |
+
match = cell.cell_contents is self._cls
|
| 938 |
+
except ValueError: # noqa: PERF203
|
| 939 |
+
# ValueError: Cell is empty
|
| 940 |
+
pass
|
| 941 |
+
else:
|
| 942 |
+
if match:
|
| 943 |
+
cell.cell_contents = cls
|
| 944 |
+
return cls
|
| 945 |
+
|
| 946 |
+
def add_repr(self, ns):
|
| 947 |
+
self._cls_dict["__repr__"] = self._add_method_dunders(
|
| 948 |
+
_make_repr(self._attrs, ns, self._cls)
|
| 949 |
+
)
|
| 950 |
+
return self
|
| 951 |
+
|
| 952 |
+
def add_str(self):
|
| 953 |
+
repr = self._cls_dict.get("__repr__")
|
| 954 |
+
if repr is None:
|
| 955 |
+
msg = "__str__ can only be generated if a __repr__ exists."
|
| 956 |
+
raise ValueError(msg)
|
| 957 |
+
|
| 958 |
+
def __str__(self):
|
| 959 |
+
return self.__repr__()
|
| 960 |
+
|
| 961 |
+
self._cls_dict["__str__"] = self._add_method_dunders(__str__)
|
| 962 |
+
return self
|
| 963 |
+
|
| 964 |
+
def _make_getstate_setstate(self):
|
| 965 |
+
"""
|
| 966 |
+
Create custom __setstate__ and __getstate__ methods.
|
| 967 |
+
"""
|
| 968 |
+
# __weakref__ is not writable.
|
| 969 |
+
state_attr_names = tuple(
|
| 970 |
+
an for an in self._attr_names if an != "__weakref__"
|
| 971 |
+
)
|
| 972 |
+
|
| 973 |
+
def slots_getstate(self):
|
| 974 |
+
"""
|
| 975 |
+
Automatically created by attrs.
|
| 976 |
+
"""
|
| 977 |
+
return {name: getattr(self, name) for name in state_attr_names}
|
| 978 |
+
|
| 979 |
+
hash_caching_enabled = self._cache_hash
|
| 980 |
+
|
| 981 |
+
def slots_setstate(self, state):
|
| 982 |
+
"""
|
| 983 |
+
Automatically created by attrs.
|
| 984 |
+
"""
|
| 985 |
+
__bound_setattr = _OBJ_SETATTR.__get__(self)
|
| 986 |
+
if isinstance(state, tuple):
|
| 987 |
+
# Backward compatibility with attrs instances pickled with
|
| 988 |
+
# attrs versions before v22.2.0 which stored tuples.
|
| 989 |
+
for name, value in zip(state_attr_names, state):
|
| 990 |
+
__bound_setattr(name, value)
|
| 991 |
+
else:
|
| 992 |
+
for name in state_attr_names:
|
| 993 |
+
if name in state:
|
| 994 |
+
__bound_setattr(name, state[name])
|
| 995 |
+
|
| 996 |
+
# The hash code cache is not included when the object is
|
| 997 |
+
# serialized, but it still needs to be initialized to None to
|
| 998 |
+
# indicate that the first call to __hash__ should be a cache
|
| 999 |
+
# miss.
|
| 1000 |
+
if hash_caching_enabled:
|
| 1001 |
+
__bound_setattr(_HASH_CACHE_FIELD, None)
|
| 1002 |
+
|
| 1003 |
+
return slots_getstate, slots_setstate
|
| 1004 |
+
|
| 1005 |
+
def make_unhashable(self):
|
| 1006 |
+
self._cls_dict["__hash__"] = None
|
| 1007 |
+
return self
|
| 1008 |
+
|
| 1009 |
+
def add_hash(self):
|
| 1010 |
+
self._cls_dict["__hash__"] = self._add_method_dunders(
|
| 1011 |
+
_make_hash(
|
| 1012 |
+
self._cls,
|
| 1013 |
+
self._attrs,
|
| 1014 |
+
frozen=self._frozen,
|
| 1015 |
+
cache_hash=self._cache_hash,
|
| 1016 |
+
)
|
| 1017 |
+
)
|
| 1018 |
+
|
| 1019 |
+
return self
|
| 1020 |
+
|
| 1021 |
+
def add_init(self):
|
| 1022 |
+
self._cls_dict["__init__"] = self._add_method_dunders(
|
| 1023 |
+
_make_init(
|
| 1024 |
+
self._cls,
|
| 1025 |
+
self._attrs,
|
| 1026 |
+
self._has_pre_init,
|
| 1027 |
+
self._pre_init_has_args,
|
| 1028 |
+
self._has_post_init,
|
| 1029 |
+
self._frozen,
|
| 1030 |
+
self._slots,
|
| 1031 |
+
self._cache_hash,
|
| 1032 |
+
self._base_attr_map,
|
| 1033 |
+
self._is_exc,
|
| 1034 |
+
self._on_setattr,
|
| 1035 |
+
attrs_init=False,
|
| 1036 |
+
)
|
| 1037 |
+
)
|
| 1038 |
+
|
| 1039 |
+
return self
|
| 1040 |
+
|
| 1041 |
+
def add_replace(self):
|
| 1042 |
+
self._cls_dict["__replace__"] = self._add_method_dunders(
|
| 1043 |
+
lambda self, **changes: evolve(self, **changes)
|
| 1044 |
+
)
|
| 1045 |
+
return self
|
| 1046 |
+
|
| 1047 |
+
def add_match_args(self):
|
| 1048 |
+
self._cls_dict["__match_args__"] = tuple(
|
| 1049 |
+
field.name
|
| 1050 |
+
for field in self._attrs
|
| 1051 |
+
if field.init and not field.kw_only
|
| 1052 |
+
)
|
| 1053 |
+
|
| 1054 |
+
def add_attrs_init(self):
|
| 1055 |
+
self._cls_dict["__attrs_init__"] = self._add_method_dunders(
|
| 1056 |
+
_make_init(
|
| 1057 |
+
self._cls,
|
| 1058 |
+
self._attrs,
|
| 1059 |
+
self._has_pre_init,
|
| 1060 |
+
self._pre_init_has_args,
|
| 1061 |
+
self._has_post_init,
|
| 1062 |
+
self._frozen,
|
| 1063 |
+
self._slots,
|
| 1064 |
+
self._cache_hash,
|
| 1065 |
+
self._base_attr_map,
|
| 1066 |
+
self._is_exc,
|
| 1067 |
+
self._on_setattr,
|
| 1068 |
+
attrs_init=True,
|
| 1069 |
+
)
|
| 1070 |
+
)
|
| 1071 |
+
|
| 1072 |
+
return self
|
| 1073 |
+
|
| 1074 |
+
def add_eq(self):
|
| 1075 |
+
cd = self._cls_dict
|
| 1076 |
+
|
| 1077 |
+
cd["__eq__"] = self._add_method_dunders(
|
| 1078 |
+
_make_eq(self._cls, self._attrs)
|
| 1079 |
+
)
|
| 1080 |
+
cd["__ne__"] = self._add_method_dunders(_make_ne())
|
| 1081 |
+
|
| 1082 |
+
return self
|
| 1083 |
+
|
| 1084 |
+
def add_order(self):
|
| 1085 |
+
cd = self._cls_dict
|
| 1086 |
+
|
| 1087 |
+
cd["__lt__"], cd["__le__"], cd["__gt__"], cd["__ge__"] = (
|
| 1088 |
+
self._add_method_dunders(meth)
|
| 1089 |
+
for meth in _make_order(self._cls, self._attrs)
|
| 1090 |
+
)
|
| 1091 |
+
|
| 1092 |
+
return self
|
| 1093 |
+
|
| 1094 |
+
def add_setattr(self):
|
| 1095 |
+
if self._frozen:
|
| 1096 |
+
return self
|
| 1097 |
+
|
| 1098 |
+
sa_attrs = {}
|
| 1099 |
+
for a in self._attrs:
|
| 1100 |
+
on_setattr = a.on_setattr or self._on_setattr
|
| 1101 |
+
if on_setattr and on_setattr is not setters.NO_OP:
|
| 1102 |
+
sa_attrs[a.name] = a, on_setattr
|
| 1103 |
+
|
| 1104 |
+
if not sa_attrs:
|
| 1105 |
+
return self
|
| 1106 |
+
|
| 1107 |
+
if self._has_custom_setattr:
|
| 1108 |
+
# We need to write a __setattr__ but there already is one!
|
| 1109 |
+
msg = "Can't combine custom __setattr__ with on_setattr hooks."
|
| 1110 |
+
raise ValueError(msg)
|
| 1111 |
+
|
| 1112 |
+
# docstring comes from _add_method_dunders
|
| 1113 |
+
def __setattr__(self, name, val):
|
| 1114 |
+
try:
|
| 1115 |
+
a, hook = sa_attrs[name]
|
| 1116 |
+
except KeyError:
|
| 1117 |
+
nval = val
|
| 1118 |
+
else:
|
| 1119 |
+
nval = hook(self, a, val)
|
| 1120 |
+
|
| 1121 |
+
_OBJ_SETATTR(self, name, nval)
|
| 1122 |
+
|
| 1123 |
+
self._cls_dict["__attrs_own_setattr__"] = True
|
| 1124 |
+
self._cls_dict["__setattr__"] = self._add_method_dunders(__setattr__)
|
| 1125 |
+
self._wrote_own_setattr = True
|
| 1126 |
+
|
| 1127 |
+
return self
|
| 1128 |
+
|
| 1129 |
+
def _add_method_dunders(self, method):
|
| 1130 |
+
"""
|
| 1131 |
+
Add __module__ and __qualname__ to a *method* if possible.
|
| 1132 |
+
"""
|
| 1133 |
+
with contextlib.suppress(AttributeError):
|
| 1134 |
+
method.__module__ = self._cls.__module__
|
| 1135 |
+
|
| 1136 |
+
with contextlib.suppress(AttributeError):
|
| 1137 |
+
method.__qualname__ = f"{self._cls.__qualname__}.{method.__name__}"
|
| 1138 |
+
|
| 1139 |
+
with contextlib.suppress(AttributeError):
|
| 1140 |
+
method.__doc__ = (
|
| 1141 |
+
"Method generated by attrs for class "
|
| 1142 |
+
f"{self._cls.__qualname__}."
|
| 1143 |
+
)
|
| 1144 |
+
|
| 1145 |
+
return method
|
| 1146 |
+
|
| 1147 |
+
|
| 1148 |
+
def _determine_attrs_eq_order(cmp, eq, order, default_eq):
|
| 1149 |
+
"""
|
| 1150 |
+
Validate the combination of *cmp*, *eq*, and *order*. Derive the effective
|
| 1151 |
+
values of eq and order. If *eq* is None, set it to *default_eq*.
|
| 1152 |
+
"""
|
| 1153 |
+
if cmp is not None and any((eq is not None, order is not None)):
|
| 1154 |
+
msg = "Don't mix `cmp` with `eq' and `order`."
|
| 1155 |
+
raise ValueError(msg)
|
| 1156 |
+
|
| 1157 |
+
# cmp takes precedence due to bw-compatibility.
|
| 1158 |
+
if cmp is not None:
|
| 1159 |
+
return cmp, cmp
|
| 1160 |
+
|
| 1161 |
+
# If left None, equality is set to the specified default and ordering
|
| 1162 |
+
# mirrors equality.
|
| 1163 |
+
if eq is None:
|
| 1164 |
+
eq = default_eq
|
| 1165 |
+
|
| 1166 |
+
if order is None:
|
| 1167 |
+
order = eq
|
| 1168 |
+
|
| 1169 |
+
if eq is False and order is True:
|
| 1170 |
+
msg = "`order` can only be True if `eq` is True too."
|
| 1171 |
+
raise ValueError(msg)
|
| 1172 |
+
|
| 1173 |
+
return eq, order
|
| 1174 |
+
|
| 1175 |
+
|
| 1176 |
+
def _determine_attrib_eq_order(cmp, eq, order, default_eq):
|
| 1177 |
+
"""
|
| 1178 |
+
Validate the combination of *cmp*, *eq*, and *order*. Derive the effective
|
| 1179 |
+
values of eq and order. If *eq* is None, set it to *default_eq*.
|
| 1180 |
+
"""
|
| 1181 |
+
if cmp is not None and any((eq is not None, order is not None)):
|
| 1182 |
+
msg = "Don't mix `cmp` with `eq' and `order`."
|
| 1183 |
+
raise ValueError(msg)
|
| 1184 |
+
|
| 1185 |
+
def decide_callable_or_boolean(value):
|
| 1186 |
+
"""
|
| 1187 |
+
Decide whether a key function is used.
|
| 1188 |
+
"""
|
| 1189 |
+
if callable(value):
|
| 1190 |
+
value, key = True, value
|
| 1191 |
+
else:
|
| 1192 |
+
key = None
|
| 1193 |
+
return value, key
|
| 1194 |
+
|
| 1195 |
+
# cmp takes precedence due to bw-compatibility.
|
| 1196 |
+
if cmp is not None:
|
| 1197 |
+
cmp, cmp_key = decide_callable_or_boolean(cmp)
|
| 1198 |
+
return cmp, cmp_key, cmp, cmp_key
|
| 1199 |
+
|
| 1200 |
+
# If left None, equality is set to the specified default and ordering
|
| 1201 |
+
# mirrors equality.
|
| 1202 |
+
if eq is None:
|
| 1203 |
+
eq, eq_key = default_eq, None
|
| 1204 |
+
else:
|
| 1205 |
+
eq, eq_key = decide_callable_or_boolean(eq)
|
| 1206 |
+
|
| 1207 |
+
if order is None:
|
| 1208 |
+
order, order_key = eq, eq_key
|
| 1209 |
+
else:
|
| 1210 |
+
order, order_key = decide_callable_or_boolean(order)
|
| 1211 |
+
|
| 1212 |
+
if eq is False and order is True:
|
| 1213 |
+
msg = "`order` can only be True if `eq` is True too."
|
| 1214 |
+
raise ValueError(msg)
|
| 1215 |
+
|
| 1216 |
+
return eq, eq_key, order, order_key
|
| 1217 |
+
|
| 1218 |
+
|
| 1219 |
+
def _determine_whether_to_implement(
|
| 1220 |
+
cls, flag, auto_detect, dunders, default=True
|
| 1221 |
+
):
|
| 1222 |
+
"""
|
| 1223 |
+
Check whether we should implement a set of methods for *cls*.
|
| 1224 |
+
|
| 1225 |
+
*flag* is the argument passed into @attr.s like 'init', *auto_detect* the
|
| 1226 |
+
same as passed into @attr.s and *dunders* is a tuple of attribute names
|
| 1227 |
+
whose presence signal that the user has implemented it themselves.
|
| 1228 |
+
|
| 1229 |
+
Return *default* if no reason for either for or against is found.
|
| 1230 |
+
"""
|
| 1231 |
+
if flag is True or flag is False:
|
| 1232 |
+
return flag
|
| 1233 |
+
|
| 1234 |
+
if flag is None and auto_detect is False:
|
| 1235 |
+
return default
|
| 1236 |
+
|
| 1237 |
+
# Logically, flag is None and auto_detect is True here.
|
| 1238 |
+
for dunder in dunders:
|
| 1239 |
+
if _has_own_attribute(cls, dunder):
|
| 1240 |
+
return False
|
| 1241 |
+
|
| 1242 |
+
return default
|
| 1243 |
+
|
| 1244 |
+
|
| 1245 |
+
def attrs(
|
| 1246 |
+
maybe_cls=None,
|
| 1247 |
+
these=None,
|
| 1248 |
+
repr_ns=None,
|
| 1249 |
+
repr=None,
|
| 1250 |
+
cmp=None,
|
| 1251 |
+
hash=None,
|
| 1252 |
+
init=None,
|
| 1253 |
+
slots=False,
|
| 1254 |
+
frozen=False,
|
| 1255 |
+
weakref_slot=True,
|
| 1256 |
+
str=False,
|
| 1257 |
+
auto_attribs=False,
|
| 1258 |
+
kw_only=False,
|
| 1259 |
+
cache_hash=False,
|
| 1260 |
+
auto_exc=False,
|
| 1261 |
+
eq=None,
|
| 1262 |
+
order=None,
|
| 1263 |
+
auto_detect=False,
|
| 1264 |
+
collect_by_mro=False,
|
| 1265 |
+
getstate_setstate=None,
|
| 1266 |
+
on_setattr=None,
|
| 1267 |
+
field_transformer=None,
|
| 1268 |
+
match_args=True,
|
| 1269 |
+
unsafe_hash=None,
|
| 1270 |
+
):
|
| 1271 |
+
r"""
|
| 1272 |
+
A class decorator that adds :term:`dunder methods` according to the
|
| 1273 |
+
specified attributes using `attr.ib` or the *these* argument.
|
| 1274 |
+
|
| 1275 |
+
Consider using `attrs.define` / `attrs.frozen` in new code (``attr.s`` will
|
| 1276 |
+
*never* go away, though).
|
| 1277 |
+
|
| 1278 |
+
Args:
|
| 1279 |
+
repr_ns (str):
|
| 1280 |
+
When using nested classes, there was no way in Python 2 to
|
| 1281 |
+
automatically detect that. This argument allows to set a custom
|
| 1282 |
+
name for a more meaningful ``repr`` output. This argument is
|
| 1283 |
+
pointless in Python 3 and is therefore deprecated.
|
| 1284 |
+
|
| 1285 |
+
.. caution::
|
| 1286 |
+
Refer to `attrs.define` for the rest of the parameters, but note that they
|
| 1287 |
+
can have different defaults.
|
| 1288 |
+
|
| 1289 |
+
Notably, leaving *on_setattr* as `None` will **not** add any hooks.
|
| 1290 |
+
|
| 1291 |
+
.. versionadded:: 16.0.0 *slots*
|
| 1292 |
+
.. versionadded:: 16.1.0 *frozen*
|
| 1293 |
+
.. versionadded:: 16.3.0 *str*
|
| 1294 |
+
.. versionadded:: 16.3.0 Support for ``__attrs_post_init__``.
|
| 1295 |
+
.. versionchanged:: 17.1.0
|
| 1296 |
+
*hash* supports `None` as value which is also the default now.
|
| 1297 |
+
.. versionadded:: 17.3.0 *auto_attribs*
|
| 1298 |
+
.. versionchanged:: 18.1.0
|
| 1299 |
+
If *these* is passed, no attributes are deleted from the class body.
|
| 1300 |
+
.. versionchanged:: 18.1.0 If *these* is ordered, the order is retained.
|
| 1301 |
+
.. versionadded:: 18.2.0 *weakref_slot*
|
| 1302 |
+
.. deprecated:: 18.2.0
|
| 1303 |
+
``__lt__``, ``__le__``, ``__gt__``, and ``__ge__`` now raise a
|
| 1304 |
+
`DeprecationWarning` if the classes compared are subclasses of
|
| 1305 |
+
each other. ``__eq`` and ``__ne__`` never tried to compared subclasses
|
| 1306 |
+
to each other.
|
| 1307 |
+
.. versionchanged:: 19.2.0
|
| 1308 |
+
``__lt__``, ``__le__``, ``__gt__``, and ``__ge__`` now do not consider
|
| 1309 |
+
subclasses comparable anymore.
|
| 1310 |
+
.. versionadded:: 18.2.0 *kw_only*
|
| 1311 |
+
.. versionadded:: 18.2.0 *cache_hash*
|
| 1312 |
+
.. versionadded:: 19.1.0 *auto_exc*
|
| 1313 |
+
.. deprecated:: 19.2.0 *cmp* Removal on or after 2021-06-01.
|
| 1314 |
+
.. versionadded:: 19.2.0 *eq* and *order*
|
| 1315 |
+
.. versionadded:: 20.1.0 *auto_detect*
|
| 1316 |
+
.. versionadded:: 20.1.0 *collect_by_mro*
|
| 1317 |
+
.. versionadded:: 20.1.0 *getstate_setstate*
|
| 1318 |
+
.. versionadded:: 20.1.0 *on_setattr*
|
| 1319 |
+
.. versionadded:: 20.3.0 *field_transformer*
|
| 1320 |
+
.. versionchanged:: 21.1.0
|
| 1321 |
+
``init=False`` injects ``__attrs_init__``
|
| 1322 |
+
.. versionchanged:: 21.1.0 Support for ``__attrs_pre_init__``
|
| 1323 |
+
.. versionchanged:: 21.1.0 *cmp* undeprecated
|
| 1324 |
+
.. versionadded:: 21.3.0 *match_args*
|
| 1325 |
+
.. versionadded:: 22.2.0
|
| 1326 |
+
*unsafe_hash* as an alias for *hash* (for :pep:`681` compliance).
|
| 1327 |
+
.. deprecated:: 24.1.0 *repr_ns*
|
| 1328 |
+
.. versionchanged:: 24.1.0
|
| 1329 |
+
Instances are not compared as tuples of attributes anymore, but using a
|
| 1330 |
+
big ``and`` condition. This is faster and has more correct behavior for
|
| 1331 |
+
uncomparable values like `math.nan`.
|
| 1332 |
+
.. versionadded:: 24.1.0
|
| 1333 |
+
If a class has an *inherited* classmethod called
|
| 1334 |
+
``__attrs_init_subclass__``, it is executed after the class is created.
|
| 1335 |
+
.. deprecated:: 24.1.0 *hash* is deprecated in favor of *unsafe_hash*.
|
| 1336 |
+
"""
|
| 1337 |
+
if repr_ns is not None:
|
| 1338 |
+
import warnings
|
| 1339 |
+
|
| 1340 |
+
warnings.warn(
|
| 1341 |
+
DeprecationWarning(
|
| 1342 |
+
"The `repr_ns` argument is deprecated and will be removed in or after August 2025."
|
| 1343 |
+
),
|
| 1344 |
+
stacklevel=2,
|
| 1345 |
+
)
|
| 1346 |
+
|
| 1347 |
+
eq_, order_ = _determine_attrs_eq_order(cmp, eq, order, None)
|
| 1348 |
+
|
| 1349 |
+
# unsafe_hash takes precedence due to PEP 681.
|
| 1350 |
+
if unsafe_hash is not None:
|
| 1351 |
+
hash = unsafe_hash
|
| 1352 |
+
|
| 1353 |
+
if isinstance(on_setattr, (list, tuple)):
|
| 1354 |
+
on_setattr = setters.pipe(*on_setattr)
|
| 1355 |
+
|
| 1356 |
+
def wrap(cls):
|
| 1357 |
+
is_frozen = frozen or _has_frozen_base_class(cls)
|
| 1358 |
+
is_exc = auto_exc is True and issubclass(cls, BaseException)
|
| 1359 |
+
has_own_setattr = auto_detect and _has_own_attribute(
|
| 1360 |
+
cls, "__setattr__"
|
| 1361 |
+
)
|
| 1362 |
+
|
| 1363 |
+
if has_own_setattr and is_frozen:
|
| 1364 |
+
msg = "Can't freeze a class with a custom __setattr__."
|
| 1365 |
+
raise ValueError(msg)
|
| 1366 |
+
|
| 1367 |
+
builder = _ClassBuilder(
|
| 1368 |
+
cls,
|
| 1369 |
+
these,
|
| 1370 |
+
slots,
|
| 1371 |
+
is_frozen,
|
| 1372 |
+
weakref_slot,
|
| 1373 |
+
_determine_whether_to_implement(
|
| 1374 |
+
cls,
|
| 1375 |
+
getstate_setstate,
|
| 1376 |
+
auto_detect,
|
| 1377 |
+
("__getstate__", "__setstate__"),
|
| 1378 |
+
default=slots,
|
| 1379 |
+
),
|
| 1380 |
+
auto_attribs,
|
| 1381 |
+
kw_only,
|
| 1382 |
+
cache_hash,
|
| 1383 |
+
is_exc,
|
| 1384 |
+
collect_by_mro,
|
| 1385 |
+
on_setattr,
|
| 1386 |
+
has_own_setattr,
|
| 1387 |
+
field_transformer,
|
| 1388 |
+
)
|
| 1389 |
+
if _determine_whether_to_implement(
|
| 1390 |
+
cls, repr, auto_detect, ("__repr__",)
|
| 1391 |
+
):
|
| 1392 |
+
builder.add_repr(repr_ns)
|
| 1393 |
+
if str is True:
|
| 1394 |
+
builder.add_str()
|
| 1395 |
+
|
| 1396 |
+
eq = _determine_whether_to_implement(
|
| 1397 |
+
cls, eq_, auto_detect, ("__eq__", "__ne__")
|
| 1398 |
+
)
|
| 1399 |
+
if not is_exc and eq is True:
|
| 1400 |
+
builder.add_eq()
|
| 1401 |
+
if not is_exc and _determine_whether_to_implement(
|
| 1402 |
+
cls, order_, auto_detect, ("__lt__", "__le__", "__gt__", "__ge__")
|
| 1403 |
+
):
|
| 1404 |
+
builder.add_order()
|
| 1405 |
+
|
| 1406 |
+
builder.add_setattr()
|
| 1407 |
+
|
| 1408 |
+
nonlocal hash
|
| 1409 |
+
if (
|
| 1410 |
+
hash is None
|
| 1411 |
+
and auto_detect is True
|
| 1412 |
+
and _has_own_attribute(cls, "__hash__")
|
| 1413 |
+
):
|
| 1414 |
+
hash = False
|
| 1415 |
+
|
| 1416 |
+
if hash is not True and hash is not False and hash is not None:
|
| 1417 |
+
# Can't use `hash in` because 1 == True for example.
|
| 1418 |
+
msg = "Invalid value for hash. Must be True, False, or None."
|
| 1419 |
+
raise TypeError(msg)
|
| 1420 |
+
|
| 1421 |
+
if hash is False or (hash is None and eq is False) or is_exc:
|
| 1422 |
+
# Don't do anything. Should fall back to __object__'s __hash__
|
| 1423 |
+
# which is by id.
|
| 1424 |
+
if cache_hash:
|
| 1425 |
+
msg = "Invalid value for cache_hash. To use hash caching, hashing must be either explicitly or implicitly enabled."
|
| 1426 |
+
raise TypeError(msg)
|
| 1427 |
+
elif hash is True or (
|
| 1428 |
+
hash is None and eq is True and is_frozen is True
|
| 1429 |
+
):
|
| 1430 |
+
# Build a __hash__ if told so, or if it's safe.
|
| 1431 |
+
builder.add_hash()
|
| 1432 |
+
else:
|
| 1433 |
+
# Raise TypeError on attempts to hash.
|
| 1434 |
+
if cache_hash:
|
| 1435 |
+
msg = "Invalid value for cache_hash. To use hash caching, hashing must be either explicitly or implicitly enabled."
|
| 1436 |
+
raise TypeError(msg)
|
| 1437 |
+
builder.make_unhashable()
|
| 1438 |
+
|
| 1439 |
+
if _determine_whether_to_implement(
|
| 1440 |
+
cls, init, auto_detect, ("__init__",)
|
| 1441 |
+
):
|
| 1442 |
+
builder.add_init()
|
| 1443 |
+
else:
|
| 1444 |
+
builder.add_attrs_init()
|
| 1445 |
+
if cache_hash:
|
| 1446 |
+
msg = "Invalid value for cache_hash. To use hash caching, init must be True."
|
| 1447 |
+
raise TypeError(msg)
|
| 1448 |
+
|
| 1449 |
+
if PY_3_13_PLUS and not _has_own_attribute(cls, "__replace__"):
|
| 1450 |
+
builder.add_replace()
|
| 1451 |
+
|
| 1452 |
+
if (
|
| 1453 |
+
PY_3_10_PLUS
|
| 1454 |
+
and match_args
|
| 1455 |
+
and not _has_own_attribute(cls, "__match_args__")
|
| 1456 |
+
):
|
| 1457 |
+
builder.add_match_args()
|
| 1458 |
+
|
| 1459 |
+
return builder.build_class()
|
| 1460 |
+
|
| 1461 |
+
# maybe_cls's type depends on the usage of the decorator. It's a class
|
| 1462 |
+
# if it's used as `@attrs` but `None` if used as `@attrs()`.
|
| 1463 |
+
if maybe_cls is None:
|
| 1464 |
+
return wrap
|
| 1465 |
+
|
| 1466 |
+
return wrap(maybe_cls)
|
| 1467 |
+
|
| 1468 |
+
|
| 1469 |
+
_attrs = attrs
|
| 1470 |
+
"""
|
| 1471 |
+
Internal alias so we can use it in functions that take an argument called
|
| 1472 |
+
*attrs*.
|
| 1473 |
+
"""
|
| 1474 |
+
|
| 1475 |
+
|
| 1476 |
+
def _has_frozen_base_class(cls):
|
| 1477 |
+
"""
|
| 1478 |
+
Check whether *cls* has a frozen ancestor by looking at its
|
| 1479 |
+
__setattr__.
|
| 1480 |
+
"""
|
| 1481 |
+
return cls.__setattr__ is _frozen_setattrs
|
| 1482 |
+
|
| 1483 |
+
|
| 1484 |
+
def _generate_unique_filename(cls, func_name):
|
| 1485 |
+
"""
|
| 1486 |
+
Create a "filename" suitable for a function being generated.
|
| 1487 |
+
"""
|
| 1488 |
+
return (
|
| 1489 |
+
f"<attrs generated {func_name} {cls.__module__}."
|
| 1490 |
+
f"{getattr(cls, '__qualname__', cls.__name__)}>"
|
| 1491 |
+
)
|
| 1492 |
+
|
| 1493 |
+
|
| 1494 |
+
def _make_hash(cls, attrs, frozen, cache_hash):
|
| 1495 |
+
attrs = tuple(
|
| 1496 |
+
a for a in attrs if a.hash is True or (a.hash is None and a.eq is True)
|
| 1497 |
+
)
|
| 1498 |
+
|
| 1499 |
+
tab = " "
|
| 1500 |
+
|
| 1501 |
+
unique_filename = _generate_unique_filename(cls, "hash")
|
| 1502 |
+
type_hash = hash(unique_filename)
|
| 1503 |
+
# If eq is custom generated, we need to include the functions in globs
|
| 1504 |
+
globs = {}
|
| 1505 |
+
|
| 1506 |
+
hash_def = "def __hash__(self"
|
| 1507 |
+
hash_func = "hash(("
|
| 1508 |
+
closing_braces = "))"
|
| 1509 |
+
if not cache_hash:
|
| 1510 |
+
hash_def += "):"
|
| 1511 |
+
else:
|
| 1512 |
+
hash_def += ", *"
|
| 1513 |
+
|
| 1514 |
+
hash_def += ", _cache_wrapper=__import__('attr._make')._make._CacheHashWrapper):"
|
| 1515 |
+
hash_func = "_cache_wrapper(" + hash_func
|
| 1516 |
+
closing_braces += ")"
|
| 1517 |
+
|
| 1518 |
+
method_lines = [hash_def]
|
| 1519 |
+
|
| 1520 |
+
def append_hash_computation_lines(prefix, indent):
|
| 1521 |
+
"""
|
| 1522 |
+
Generate the code for actually computing the hash code.
|
| 1523 |
+
Below this will either be returned directly or used to compute
|
| 1524 |
+
a value which is then cached, depending on the value of cache_hash
|
| 1525 |
+
"""
|
| 1526 |
+
|
| 1527 |
+
method_lines.extend(
|
| 1528 |
+
[
|
| 1529 |
+
indent + prefix + hash_func,
|
| 1530 |
+
indent + f" {type_hash},",
|
| 1531 |
+
]
|
| 1532 |
+
)
|
| 1533 |
+
|
| 1534 |
+
for a in attrs:
|
| 1535 |
+
if a.eq_key:
|
| 1536 |
+
cmp_name = f"_{a.name}_key"
|
| 1537 |
+
globs[cmp_name] = a.eq_key
|
| 1538 |
+
method_lines.append(
|
| 1539 |
+
indent + f" {cmp_name}(self.{a.name}),"
|
| 1540 |
+
)
|
| 1541 |
+
else:
|
| 1542 |
+
method_lines.append(indent + f" self.{a.name},")
|
| 1543 |
+
|
| 1544 |
+
method_lines.append(indent + " " + closing_braces)
|
| 1545 |
+
|
| 1546 |
+
if cache_hash:
|
| 1547 |
+
method_lines.append(tab + f"if self.{_HASH_CACHE_FIELD} is None:")
|
| 1548 |
+
if frozen:
|
| 1549 |
+
append_hash_computation_lines(
|
| 1550 |
+
f"object.__setattr__(self, '{_HASH_CACHE_FIELD}', ", tab * 2
|
| 1551 |
+
)
|
| 1552 |
+
method_lines.append(tab * 2 + ")") # close __setattr__
|
| 1553 |
+
else:
|
| 1554 |
+
append_hash_computation_lines(
|
| 1555 |
+
f"self.{_HASH_CACHE_FIELD} = ", tab * 2
|
| 1556 |
+
)
|
| 1557 |
+
method_lines.append(tab + f"return self.{_HASH_CACHE_FIELD}")
|
| 1558 |
+
else:
|
| 1559 |
+
append_hash_computation_lines("return ", tab)
|
| 1560 |
+
|
| 1561 |
+
script = "\n".join(method_lines)
|
| 1562 |
+
return _make_method("__hash__", script, unique_filename, globs)
|
| 1563 |
+
|
| 1564 |
+
|
| 1565 |
+
def _add_hash(cls, attrs):
|
| 1566 |
+
"""
|
| 1567 |
+
Add a hash method to *cls*.
|
| 1568 |
+
"""
|
| 1569 |
+
cls.__hash__ = _make_hash(cls, attrs, frozen=False, cache_hash=False)
|
| 1570 |
+
return cls
|
| 1571 |
+
|
| 1572 |
+
|
| 1573 |
+
def _make_ne():
|
| 1574 |
+
"""
|
| 1575 |
+
Create __ne__ method.
|
| 1576 |
+
"""
|
| 1577 |
+
|
| 1578 |
+
def __ne__(self, other):
|
| 1579 |
+
"""
|
| 1580 |
+
Check equality and either forward a NotImplemented or
|
| 1581 |
+
return the result negated.
|
| 1582 |
+
"""
|
| 1583 |
+
result = self.__eq__(other)
|
| 1584 |
+
if result is NotImplemented:
|
| 1585 |
+
return NotImplemented
|
| 1586 |
+
|
| 1587 |
+
return not result
|
| 1588 |
+
|
| 1589 |
+
return __ne__
|
| 1590 |
+
|
| 1591 |
+
|
| 1592 |
+
def _make_eq(cls, attrs):
|
| 1593 |
+
"""
|
| 1594 |
+
Create __eq__ method for *cls* with *attrs*.
|
| 1595 |
+
"""
|
| 1596 |
+
attrs = [a for a in attrs if a.eq]
|
| 1597 |
+
|
| 1598 |
+
unique_filename = _generate_unique_filename(cls, "eq")
|
| 1599 |
+
lines = [
|
| 1600 |
+
"def __eq__(self, other):",
|
| 1601 |
+
" if other.__class__ is not self.__class__:",
|
| 1602 |
+
" return NotImplemented",
|
| 1603 |
+
]
|
| 1604 |
+
|
| 1605 |
+
globs = {}
|
| 1606 |
+
if attrs:
|
| 1607 |
+
lines.append(" return (")
|
| 1608 |
+
for a in attrs:
|
| 1609 |
+
if a.eq_key:
|
| 1610 |
+
cmp_name = f"_{a.name}_key"
|
| 1611 |
+
# Add the key function to the global namespace
|
| 1612 |
+
# of the evaluated function.
|
| 1613 |
+
globs[cmp_name] = a.eq_key
|
| 1614 |
+
lines.append(
|
| 1615 |
+
f" {cmp_name}(self.{a.name}) == {cmp_name}(other.{a.name})"
|
| 1616 |
+
)
|
| 1617 |
+
else:
|
| 1618 |
+
lines.append(f" self.{a.name} == other.{a.name}")
|
| 1619 |
+
if a is not attrs[-1]:
|
| 1620 |
+
lines[-1] = f"{lines[-1]} and"
|
| 1621 |
+
lines.append(" )")
|
| 1622 |
+
else:
|
| 1623 |
+
lines.append(" return True")
|
| 1624 |
+
|
| 1625 |
+
script = "\n".join(lines)
|
| 1626 |
+
|
| 1627 |
+
return _make_method("__eq__", script, unique_filename, globs)
|
| 1628 |
+
|
| 1629 |
+
|
| 1630 |
+
def _make_order(cls, attrs):
|
| 1631 |
+
"""
|
| 1632 |
+
Create ordering methods for *cls* with *attrs*.
|
| 1633 |
+
"""
|
| 1634 |
+
attrs = [a for a in attrs if a.order]
|
| 1635 |
+
|
| 1636 |
+
def attrs_to_tuple(obj):
|
| 1637 |
+
"""
|
| 1638 |
+
Save us some typing.
|
| 1639 |
+
"""
|
| 1640 |
+
return tuple(
|
| 1641 |
+
key(value) if key else value
|
| 1642 |
+
for value, key in (
|
| 1643 |
+
(getattr(obj, a.name), a.order_key) for a in attrs
|
| 1644 |
+
)
|
| 1645 |
+
)
|
| 1646 |
+
|
| 1647 |
+
def __lt__(self, other):
|
| 1648 |
+
"""
|
| 1649 |
+
Automatically created by attrs.
|
| 1650 |
+
"""
|
| 1651 |
+
if other.__class__ is self.__class__:
|
| 1652 |
+
return attrs_to_tuple(self) < attrs_to_tuple(other)
|
| 1653 |
+
|
| 1654 |
+
return NotImplemented
|
| 1655 |
+
|
| 1656 |
+
def __le__(self, other):
|
| 1657 |
+
"""
|
| 1658 |
+
Automatically created by attrs.
|
| 1659 |
+
"""
|
| 1660 |
+
if other.__class__ is self.__class__:
|
| 1661 |
+
return attrs_to_tuple(self) <= attrs_to_tuple(other)
|
| 1662 |
+
|
| 1663 |
+
return NotImplemented
|
| 1664 |
+
|
| 1665 |
+
def __gt__(self, other):
|
| 1666 |
+
"""
|
| 1667 |
+
Automatically created by attrs.
|
| 1668 |
+
"""
|
| 1669 |
+
if other.__class__ is self.__class__:
|
| 1670 |
+
return attrs_to_tuple(self) > attrs_to_tuple(other)
|
| 1671 |
+
|
| 1672 |
+
return NotImplemented
|
| 1673 |
+
|
| 1674 |
+
def __ge__(self, other):
|
| 1675 |
+
"""
|
| 1676 |
+
Automatically created by attrs.
|
| 1677 |
+
"""
|
| 1678 |
+
if other.__class__ is self.__class__:
|
| 1679 |
+
return attrs_to_tuple(self) >= attrs_to_tuple(other)
|
| 1680 |
+
|
| 1681 |
+
return NotImplemented
|
| 1682 |
+
|
| 1683 |
+
return __lt__, __le__, __gt__, __ge__
|
| 1684 |
+
|
| 1685 |
+
|
| 1686 |
+
def _add_eq(cls, attrs=None):
|
| 1687 |
+
"""
|
| 1688 |
+
Add equality methods to *cls* with *attrs*.
|
| 1689 |
+
"""
|
| 1690 |
+
if attrs is None:
|
| 1691 |
+
attrs = cls.__attrs_attrs__
|
| 1692 |
+
|
| 1693 |
+
cls.__eq__ = _make_eq(cls, attrs)
|
| 1694 |
+
cls.__ne__ = _make_ne()
|
| 1695 |
+
|
| 1696 |
+
return cls
|
| 1697 |
+
|
| 1698 |
+
|
| 1699 |
+
def _make_repr(attrs, ns, cls):
|
| 1700 |
+
unique_filename = _generate_unique_filename(cls, "repr")
|
| 1701 |
+
# Figure out which attributes to include, and which function to use to
|
| 1702 |
+
# format them. The a.repr value can be either bool or a custom
|
| 1703 |
+
# callable.
|
| 1704 |
+
attr_names_with_reprs = tuple(
|
| 1705 |
+
(a.name, (repr if a.repr is True else a.repr), a.init)
|
| 1706 |
+
for a in attrs
|
| 1707 |
+
if a.repr is not False
|
| 1708 |
+
)
|
| 1709 |
+
globs = {
|
| 1710 |
+
name + "_repr": r for name, r, _ in attr_names_with_reprs if r != repr
|
| 1711 |
+
}
|
| 1712 |
+
globs["_compat"] = _compat
|
| 1713 |
+
globs["AttributeError"] = AttributeError
|
| 1714 |
+
globs["NOTHING"] = NOTHING
|
| 1715 |
+
attribute_fragments = []
|
| 1716 |
+
for name, r, i in attr_names_with_reprs:
|
| 1717 |
+
accessor = (
|
| 1718 |
+
"self." + name if i else 'getattr(self, "' + name + '", NOTHING)'
|
| 1719 |
+
)
|
| 1720 |
+
fragment = (
|
| 1721 |
+
"%s={%s!r}" % (name, accessor)
|
| 1722 |
+
if r == repr
|
| 1723 |
+
else "%s={%s_repr(%s)}" % (name, name, accessor)
|
| 1724 |
+
)
|
| 1725 |
+
attribute_fragments.append(fragment)
|
| 1726 |
+
repr_fragment = ", ".join(attribute_fragments)
|
| 1727 |
+
|
| 1728 |
+
if ns is None:
|
| 1729 |
+
cls_name_fragment = '{self.__class__.__qualname__.rsplit(">.", 1)[-1]}'
|
| 1730 |
+
else:
|
| 1731 |
+
cls_name_fragment = ns + ".{self.__class__.__name__}"
|
| 1732 |
+
|
| 1733 |
+
lines = [
|
| 1734 |
+
"def __repr__(self):",
|
| 1735 |
+
" try:",
|
| 1736 |
+
" already_repring = _compat.repr_context.already_repring",
|
| 1737 |
+
" except AttributeError:",
|
| 1738 |
+
" already_repring = {id(self),}",
|
| 1739 |
+
" _compat.repr_context.already_repring = already_repring",
|
| 1740 |
+
" else:",
|
| 1741 |
+
" if id(self) in already_repring:",
|
| 1742 |
+
" return '...'",
|
| 1743 |
+
" else:",
|
| 1744 |
+
" already_repring.add(id(self))",
|
| 1745 |
+
" try:",
|
| 1746 |
+
f" return f'{cls_name_fragment}({repr_fragment})'",
|
| 1747 |
+
" finally:",
|
| 1748 |
+
" already_repring.remove(id(self))",
|
| 1749 |
+
]
|
| 1750 |
+
|
| 1751 |
+
return _make_method(
|
| 1752 |
+
"__repr__", "\n".join(lines), unique_filename, globs=globs
|
| 1753 |
+
)
|
| 1754 |
+
|
| 1755 |
+
|
| 1756 |
+
def _add_repr(cls, ns=None, attrs=None):
|
| 1757 |
+
"""
|
| 1758 |
+
Add a repr method to *cls*.
|
| 1759 |
+
"""
|
| 1760 |
+
if attrs is None:
|
| 1761 |
+
attrs = cls.__attrs_attrs__
|
| 1762 |
+
|
| 1763 |
+
cls.__repr__ = _make_repr(attrs, ns, cls)
|
| 1764 |
+
return cls
|
| 1765 |
+
|
| 1766 |
+
|
| 1767 |
+
def fields(cls):
|
| 1768 |
+
"""
|
| 1769 |
+
Return the tuple of *attrs* attributes for a class.
|
| 1770 |
+
|
| 1771 |
+
The tuple also allows accessing the fields by their names (see below for
|
| 1772 |
+
examples).
|
| 1773 |
+
|
| 1774 |
+
Args:
|
| 1775 |
+
cls (type): Class to introspect.
|
| 1776 |
+
|
| 1777 |
+
Raises:
|
| 1778 |
+
TypeError: If *cls* is not a class.
|
| 1779 |
+
|
| 1780 |
+
attrs.exceptions.NotAnAttrsClassError:
|
| 1781 |
+
If *cls* is not an *attrs* class.
|
| 1782 |
+
|
| 1783 |
+
Returns:
|
| 1784 |
+
tuple (with name accessors) of `attrs.Attribute`
|
| 1785 |
+
|
| 1786 |
+
.. versionchanged:: 16.2.0 Returned tuple allows accessing the fields
|
| 1787 |
+
by name.
|
| 1788 |
+
.. versionchanged:: 23.1.0 Add support for generic classes.
|
| 1789 |
+
"""
|
| 1790 |
+
generic_base = get_generic_base(cls)
|
| 1791 |
+
|
| 1792 |
+
if generic_base is None and not isinstance(cls, type):
|
| 1793 |
+
msg = "Passed object must be a class."
|
| 1794 |
+
raise TypeError(msg)
|
| 1795 |
+
|
| 1796 |
+
attrs = getattr(cls, "__attrs_attrs__", None)
|
| 1797 |
+
|
| 1798 |
+
if attrs is None:
|
| 1799 |
+
if generic_base is not None:
|
| 1800 |
+
attrs = getattr(generic_base, "__attrs_attrs__", None)
|
| 1801 |
+
if attrs is not None:
|
| 1802 |
+
# Even though this is global state, stick it on here to speed
|
| 1803 |
+
# it up. We rely on `cls` being cached for this to be
|
| 1804 |
+
# efficient.
|
| 1805 |
+
cls.__attrs_attrs__ = attrs
|
| 1806 |
+
return attrs
|
| 1807 |
+
msg = f"{cls!r} is not an attrs-decorated class."
|
| 1808 |
+
raise NotAnAttrsClassError(msg)
|
| 1809 |
+
|
| 1810 |
+
return attrs
|
| 1811 |
+
|
| 1812 |
+
|
| 1813 |
+
def fields_dict(cls):
|
| 1814 |
+
"""
|
| 1815 |
+
Return an ordered dictionary of *attrs* attributes for a class, whose keys
|
| 1816 |
+
are the attribute names.
|
| 1817 |
+
|
| 1818 |
+
Args:
|
| 1819 |
+
cls (type): Class to introspect.
|
| 1820 |
+
|
| 1821 |
+
Raises:
|
| 1822 |
+
TypeError: If *cls* is not a class.
|
| 1823 |
+
|
| 1824 |
+
attrs.exceptions.NotAnAttrsClassError:
|
| 1825 |
+
If *cls* is not an *attrs* class.
|
| 1826 |
+
|
| 1827 |
+
Returns:
|
| 1828 |
+
dict[str, attrs.Attribute]: Dict of attribute name to definition
|
| 1829 |
+
|
| 1830 |
+
.. versionadded:: 18.1.0
|
| 1831 |
+
"""
|
| 1832 |
+
if not isinstance(cls, type):
|
| 1833 |
+
msg = "Passed object must be a class."
|
| 1834 |
+
raise TypeError(msg)
|
| 1835 |
+
attrs = getattr(cls, "__attrs_attrs__", None)
|
| 1836 |
+
if attrs is None:
|
| 1837 |
+
msg = f"{cls!r} is not an attrs-decorated class."
|
| 1838 |
+
raise NotAnAttrsClassError(msg)
|
| 1839 |
+
return {a.name: a for a in attrs}
|
| 1840 |
+
|
| 1841 |
+
|
| 1842 |
+
def validate(inst):
|
| 1843 |
+
"""
|
| 1844 |
+
Validate all attributes on *inst* that have a validator.
|
| 1845 |
+
|
| 1846 |
+
Leaves all exceptions through.
|
| 1847 |
+
|
| 1848 |
+
Args:
|
| 1849 |
+
inst: Instance of a class with *attrs* attributes.
|
| 1850 |
+
"""
|
| 1851 |
+
if _config._run_validators is False:
|
| 1852 |
+
return
|
| 1853 |
+
|
| 1854 |
+
for a in fields(inst.__class__):
|
| 1855 |
+
v = a.validator
|
| 1856 |
+
if v is not None:
|
| 1857 |
+
v(inst, a, getattr(inst, a.name))
|
| 1858 |
+
|
| 1859 |
+
|
| 1860 |
+
def _is_slot_attr(a_name, base_attr_map):
|
| 1861 |
+
"""
|
| 1862 |
+
Check if the attribute name comes from a slot class.
|
| 1863 |
+
"""
|
| 1864 |
+
cls = base_attr_map.get(a_name)
|
| 1865 |
+
return cls and "__slots__" in cls.__dict__
|
| 1866 |
+
|
| 1867 |
+
|
| 1868 |
+
def _make_init(
|
| 1869 |
+
cls,
|
| 1870 |
+
attrs,
|
| 1871 |
+
pre_init,
|
| 1872 |
+
pre_init_has_args,
|
| 1873 |
+
post_init,
|
| 1874 |
+
frozen,
|
| 1875 |
+
slots,
|
| 1876 |
+
cache_hash,
|
| 1877 |
+
base_attr_map,
|
| 1878 |
+
is_exc,
|
| 1879 |
+
cls_on_setattr,
|
| 1880 |
+
attrs_init,
|
| 1881 |
+
):
|
| 1882 |
+
has_cls_on_setattr = (
|
| 1883 |
+
cls_on_setattr is not None and cls_on_setattr is not setters.NO_OP
|
| 1884 |
+
)
|
| 1885 |
+
|
| 1886 |
+
if frozen and has_cls_on_setattr:
|
| 1887 |
+
msg = "Frozen classes can't use on_setattr."
|
| 1888 |
+
raise ValueError(msg)
|
| 1889 |
+
|
| 1890 |
+
needs_cached_setattr = cache_hash or frozen
|
| 1891 |
+
filtered_attrs = []
|
| 1892 |
+
attr_dict = {}
|
| 1893 |
+
for a in attrs:
|
| 1894 |
+
if not a.init and a.default is NOTHING:
|
| 1895 |
+
continue
|
| 1896 |
+
|
| 1897 |
+
filtered_attrs.append(a)
|
| 1898 |
+
attr_dict[a.name] = a
|
| 1899 |
+
|
| 1900 |
+
if a.on_setattr is not None:
|
| 1901 |
+
if frozen is True:
|
| 1902 |
+
msg = "Frozen classes can't use on_setattr."
|
| 1903 |
+
raise ValueError(msg)
|
| 1904 |
+
|
| 1905 |
+
needs_cached_setattr = True
|
| 1906 |
+
elif has_cls_on_setattr and a.on_setattr is not setters.NO_OP:
|
| 1907 |
+
needs_cached_setattr = True
|
| 1908 |
+
|
| 1909 |
+
unique_filename = _generate_unique_filename(cls, "init")
|
| 1910 |
+
|
| 1911 |
+
script, globs, annotations = _attrs_to_init_script(
|
| 1912 |
+
filtered_attrs,
|
| 1913 |
+
frozen,
|
| 1914 |
+
slots,
|
| 1915 |
+
pre_init,
|
| 1916 |
+
pre_init_has_args,
|
| 1917 |
+
post_init,
|
| 1918 |
+
cache_hash,
|
| 1919 |
+
base_attr_map,
|
| 1920 |
+
is_exc,
|
| 1921 |
+
needs_cached_setattr,
|
| 1922 |
+
has_cls_on_setattr,
|
| 1923 |
+
"__attrs_init__" if attrs_init else "__init__",
|
| 1924 |
+
)
|
| 1925 |
+
if cls.__module__ in sys.modules:
|
| 1926 |
+
# This makes typing.get_type_hints(CLS.__init__) resolve string types.
|
| 1927 |
+
globs.update(sys.modules[cls.__module__].__dict__)
|
| 1928 |
+
|
| 1929 |
+
globs.update({"NOTHING": NOTHING, "attr_dict": attr_dict})
|
| 1930 |
+
|
| 1931 |
+
if needs_cached_setattr:
|
| 1932 |
+
# Save the lookup overhead in __init__ if we need to circumvent
|
| 1933 |
+
# setattr hooks.
|
| 1934 |
+
globs["_cached_setattr_get"] = _OBJ_SETATTR.__get__
|
| 1935 |
+
|
| 1936 |
+
init = _make_method(
|
| 1937 |
+
"__attrs_init__" if attrs_init else "__init__",
|
| 1938 |
+
script,
|
| 1939 |
+
unique_filename,
|
| 1940 |
+
globs,
|
| 1941 |
+
)
|
| 1942 |
+
init.__annotations__ = annotations
|
| 1943 |
+
|
| 1944 |
+
return init
|
| 1945 |
+
|
| 1946 |
+
|
| 1947 |
+
def _setattr(attr_name: str, value_var: str, has_on_setattr: bool) -> str:
|
| 1948 |
+
"""
|
| 1949 |
+
Use the cached object.setattr to set *attr_name* to *value_var*.
|
| 1950 |
+
"""
|
| 1951 |
+
return f"_setattr('{attr_name}', {value_var})"
|
| 1952 |
+
|
| 1953 |
+
|
| 1954 |
+
def _setattr_with_converter(
|
| 1955 |
+
attr_name: str, value_var: str, has_on_setattr: bool, converter: Converter
|
| 1956 |
+
) -> str:
|
| 1957 |
+
"""
|
| 1958 |
+
Use the cached object.setattr to set *attr_name* to *value_var*, but run
|
| 1959 |
+
its converter first.
|
| 1960 |
+
"""
|
| 1961 |
+
return f"_setattr('{attr_name}', {converter._fmt_converter_call(attr_name, value_var)})"
|
| 1962 |
+
|
| 1963 |
+
|
| 1964 |
+
def _assign(attr_name: str, value: str, has_on_setattr: bool) -> str:
|
| 1965 |
+
"""
|
| 1966 |
+
Unless *attr_name* has an on_setattr hook, use normal assignment. Otherwise
|
| 1967 |
+
relegate to _setattr.
|
| 1968 |
+
"""
|
| 1969 |
+
if has_on_setattr:
|
| 1970 |
+
return _setattr(attr_name, value, True)
|
| 1971 |
+
|
| 1972 |
+
return f"self.{attr_name} = {value}"
|
| 1973 |
+
|
| 1974 |
+
|
| 1975 |
+
def _assign_with_converter(
|
| 1976 |
+
attr_name: str, value_var: str, has_on_setattr: bool, converter: Converter
|
| 1977 |
+
) -> str:
|
| 1978 |
+
"""
|
| 1979 |
+
Unless *attr_name* has an on_setattr hook, use normal assignment after
|
| 1980 |
+
conversion. Otherwise relegate to _setattr_with_converter.
|
| 1981 |
+
"""
|
| 1982 |
+
if has_on_setattr:
|
| 1983 |
+
return _setattr_with_converter(attr_name, value_var, True, converter)
|
| 1984 |
+
|
| 1985 |
+
return f"self.{attr_name} = {converter._fmt_converter_call(attr_name, value_var)}"
|
| 1986 |
+
|
| 1987 |
+
|
| 1988 |
+
def _determine_setters(
|
| 1989 |
+
frozen: bool, slots: bool, base_attr_map: dict[str, type]
|
| 1990 |
+
):
|
| 1991 |
+
"""
|
| 1992 |
+
Determine the correct setter functions based on whether a class is frozen
|
| 1993 |
+
and/or slotted.
|
| 1994 |
+
"""
|
| 1995 |
+
if frozen is True:
|
| 1996 |
+
if slots is True:
|
| 1997 |
+
return (), _setattr, _setattr_with_converter
|
| 1998 |
+
|
| 1999 |
+
# Dict frozen classes assign directly to __dict__.
|
| 2000 |
+
# But only if the attribute doesn't come from an ancestor slot
|
| 2001 |
+
# class.
|
| 2002 |
+
# Note _inst_dict will be used again below if cache_hash is True
|
| 2003 |
+
|
| 2004 |
+
def fmt_setter(
|
| 2005 |
+
attr_name: str, value_var: str, has_on_setattr: bool
|
| 2006 |
+
) -> str:
|
| 2007 |
+
if _is_slot_attr(attr_name, base_attr_map):
|
| 2008 |
+
return _setattr(attr_name, value_var, has_on_setattr)
|
| 2009 |
+
|
| 2010 |
+
return f"_inst_dict['{attr_name}'] = {value_var}"
|
| 2011 |
+
|
| 2012 |
+
def fmt_setter_with_converter(
|
| 2013 |
+
attr_name: str,
|
| 2014 |
+
value_var: str,
|
| 2015 |
+
has_on_setattr: bool,
|
| 2016 |
+
converter: Converter,
|
| 2017 |
+
) -> str:
|
| 2018 |
+
if has_on_setattr or _is_slot_attr(attr_name, base_attr_map):
|
| 2019 |
+
return _setattr_with_converter(
|
| 2020 |
+
attr_name, value_var, has_on_setattr, converter
|
| 2021 |
+
)
|
| 2022 |
+
|
| 2023 |
+
return f"_inst_dict['{attr_name}'] = {converter._fmt_converter_call(attr_name, value_var)}"
|
| 2024 |
+
|
| 2025 |
+
return (
|
| 2026 |
+
("_inst_dict = self.__dict__",),
|
| 2027 |
+
fmt_setter,
|
| 2028 |
+
fmt_setter_with_converter,
|
| 2029 |
+
)
|
| 2030 |
+
|
| 2031 |
+
# Not frozen -- we can just assign directly.
|
| 2032 |
+
return (), _assign, _assign_with_converter
|
| 2033 |
+
|
| 2034 |
+
|
| 2035 |
+
def _attrs_to_init_script(
|
| 2036 |
+
attrs: list[Attribute],
|
| 2037 |
+
is_frozen: bool,
|
| 2038 |
+
is_slotted: bool,
|
| 2039 |
+
call_pre_init: bool,
|
| 2040 |
+
pre_init_has_args: bool,
|
| 2041 |
+
call_post_init: bool,
|
| 2042 |
+
does_cache_hash: bool,
|
| 2043 |
+
base_attr_map: dict[str, type],
|
| 2044 |
+
is_exc: bool,
|
| 2045 |
+
needs_cached_setattr: bool,
|
| 2046 |
+
has_cls_on_setattr: bool,
|
| 2047 |
+
method_name: str,
|
| 2048 |
+
) -> tuple[str, dict, dict]:
|
| 2049 |
+
"""
|
| 2050 |
+
Return a script of an initializer for *attrs*, a dict of globals, and
|
| 2051 |
+
annotations for the initializer.
|
| 2052 |
+
|
| 2053 |
+
The globals are required by the generated script.
|
| 2054 |
+
"""
|
| 2055 |
+
lines = ["self.__attrs_pre_init__()"] if call_pre_init else []
|
| 2056 |
+
|
| 2057 |
+
if needs_cached_setattr:
|
| 2058 |
+
lines.append(
|
| 2059 |
+
# Circumvent the __setattr__ descriptor to save one lookup per
|
| 2060 |
+
# assignment. Note _setattr will be used again below if
|
| 2061 |
+
# does_cache_hash is True.
|
| 2062 |
+
"_setattr = _cached_setattr_get(self)"
|
| 2063 |
+
)
|
| 2064 |
+
|
| 2065 |
+
extra_lines, fmt_setter, fmt_setter_with_converter = _determine_setters(
|
| 2066 |
+
is_frozen, is_slotted, base_attr_map
|
| 2067 |
+
)
|
| 2068 |
+
lines.extend(extra_lines)
|
| 2069 |
+
|
| 2070 |
+
args = []
|
| 2071 |
+
kw_only_args = []
|
| 2072 |
+
attrs_to_validate = []
|
| 2073 |
+
|
| 2074 |
+
# This is a dictionary of names to validator and converter callables.
|
| 2075 |
+
# Injecting this into __init__ globals lets us avoid lookups.
|
| 2076 |
+
names_for_globals = {}
|
| 2077 |
+
annotations = {"return": None}
|
| 2078 |
+
|
| 2079 |
+
for a in attrs:
|
| 2080 |
+
if a.validator:
|
| 2081 |
+
attrs_to_validate.append(a)
|
| 2082 |
+
|
| 2083 |
+
attr_name = a.name
|
| 2084 |
+
has_on_setattr = a.on_setattr is not None or (
|
| 2085 |
+
a.on_setattr is not setters.NO_OP and has_cls_on_setattr
|
| 2086 |
+
)
|
| 2087 |
+
# a.alias is set to maybe-mangled attr_name in _ClassBuilder if not
|
| 2088 |
+
# explicitly provided
|
| 2089 |
+
arg_name = a.alias
|
| 2090 |
+
|
| 2091 |
+
has_factory = isinstance(a.default, Factory)
|
| 2092 |
+
maybe_self = "self" if has_factory and a.default.takes_self else ""
|
| 2093 |
+
|
| 2094 |
+
if a.converter is not None and not isinstance(a.converter, Converter):
|
| 2095 |
+
converter = Converter(a.converter)
|
| 2096 |
+
else:
|
| 2097 |
+
converter = a.converter
|
| 2098 |
+
|
| 2099 |
+
if a.init is False:
|
| 2100 |
+
if has_factory:
|
| 2101 |
+
init_factory_name = _INIT_FACTORY_PAT % (a.name,)
|
| 2102 |
+
if converter is not None:
|
| 2103 |
+
lines.append(
|
| 2104 |
+
fmt_setter_with_converter(
|
| 2105 |
+
attr_name,
|
| 2106 |
+
init_factory_name + f"({maybe_self})",
|
| 2107 |
+
has_on_setattr,
|
| 2108 |
+
converter,
|
| 2109 |
+
)
|
| 2110 |
+
)
|
| 2111 |
+
names_for_globals[converter._get_global_name(a.name)] = (
|
| 2112 |
+
converter.converter
|
| 2113 |
+
)
|
| 2114 |
+
else:
|
| 2115 |
+
lines.append(
|
| 2116 |
+
fmt_setter(
|
| 2117 |
+
attr_name,
|
| 2118 |
+
init_factory_name + f"({maybe_self})",
|
| 2119 |
+
has_on_setattr,
|
| 2120 |
+
)
|
| 2121 |
+
)
|
| 2122 |
+
names_for_globals[init_factory_name] = a.default.factory
|
| 2123 |
+
elif converter is not None:
|
| 2124 |
+
lines.append(
|
| 2125 |
+
fmt_setter_with_converter(
|
| 2126 |
+
attr_name,
|
| 2127 |
+
f"attr_dict['{attr_name}'].default",
|
| 2128 |
+
has_on_setattr,
|
| 2129 |
+
converter,
|
| 2130 |
+
)
|
| 2131 |
+
)
|
| 2132 |
+
names_for_globals[converter._get_global_name(a.name)] = (
|
| 2133 |
+
converter.converter
|
| 2134 |
+
)
|
| 2135 |
+
else:
|
| 2136 |
+
lines.append(
|
| 2137 |
+
fmt_setter(
|
| 2138 |
+
attr_name,
|
| 2139 |
+
f"attr_dict['{attr_name}'].default",
|
| 2140 |
+
has_on_setattr,
|
| 2141 |
+
)
|
| 2142 |
+
)
|
| 2143 |
+
elif a.default is not NOTHING and not has_factory:
|
| 2144 |
+
arg = f"{arg_name}=attr_dict['{attr_name}'].default"
|
| 2145 |
+
if a.kw_only:
|
| 2146 |
+
kw_only_args.append(arg)
|
| 2147 |
+
else:
|
| 2148 |
+
args.append(arg)
|
| 2149 |
+
|
| 2150 |
+
if converter is not None:
|
| 2151 |
+
lines.append(
|
| 2152 |
+
fmt_setter_with_converter(
|
| 2153 |
+
attr_name, arg_name, has_on_setattr, converter
|
| 2154 |
+
)
|
| 2155 |
+
)
|
| 2156 |
+
names_for_globals[converter._get_global_name(a.name)] = (
|
| 2157 |
+
converter.converter
|
| 2158 |
+
)
|
| 2159 |
+
else:
|
| 2160 |
+
lines.append(fmt_setter(attr_name, arg_name, has_on_setattr))
|
| 2161 |
+
|
| 2162 |
+
elif has_factory:
|
| 2163 |
+
arg = f"{arg_name}=NOTHING"
|
| 2164 |
+
if a.kw_only:
|
| 2165 |
+
kw_only_args.append(arg)
|
| 2166 |
+
else:
|
| 2167 |
+
args.append(arg)
|
| 2168 |
+
lines.append(f"if {arg_name} is not NOTHING:")
|
| 2169 |
+
|
| 2170 |
+
init_factory_name = _INIT_FACTORY_PAT % (a.name,)
|
| 2171 |
+
if converter is not None:
|
| 2172 |
+
lines.append(
|
| 2173 |
+
" "
|
| 2174 |
+
+ fmt_setter_with_converter(
|
| 2175 |
+
attr_name, arg_name, has_on_setattr, converter
|
| 2176 |
+
)
|
| 2177 |
+
)
|
| 2178 |
+
lines.append("else:")
|
| 2179 |
+
lines.append(
|
| 2180 |
+
" "
|
| 2181 |
+
+ fmt_setter_with_converter(
|
| 2182 |
+
attr_name,
|
| 2183 |
+
init_factory_name + "(" + maybe_self + ")",
|
| 2184 |
+
has_on_setattr,
|
| 2185 |
+
converter,
|
| 2186 |
+
)
|
| 2187 |
+
)
|
| 2188 |
+
names_for_globals[converter._get_global_name(a.name)] = (
|
| 2189 |
+
converter.converter
|
| 2190 |
+
)
|
| 2191 |
+
else:
|
| 2192 |
+
lines.append(
|
| 2193 |
+
" " + fmt_setter(attr_name, arg_name, has_on_setattr)
|
| 2194 |
+
)
|
| 2195 |
+
lines.append("else:")
|
| 2196 |
+
lines.append(
|
| 2197 |
+
" "
|
| 2198 |
+
+ fmt_setter(
|
| 2199 |
+
attr_name,
|
| 2200 |
+
init_factory_name + "(" + maybe_self + ")",
|
| 2201 |
+
has_on_setattr,
|
| 2202 |
+
)
|
| 2203 |
+
)
|
| 2204 |
+
names_for_globals[init_factory_name] = a.default.factory
|
| 2205 |
+
else:
|
| 2206 |
+
if a.kw_only:
|
| 2207 |
+
kw_only_args.append(arg_name)
|
| 2208 |
+
else:
|
| 2209 |
+
args.append(arg_name)
|
| 2210 |
+
|
| 2211 |
+
if converter is not None:
|
| 2212 |
+
lines.append(
|
| 2213 |
+
fmt_setter_with_converter(
|
| 2214 |
+
attr_name, arg_name, has_on_setattr, converter
|
| 2215 |
+
)
|
| 2216 |
+
)
|
| 2217 |
+
names_for_globals[converter._get_global_name(a.name)] = (
|
| 2218 |
+
converter.converter
|
| 2219 |
+
)
|
| 2220 |
+
else:
|
| 2221 |
+
lines.append(fmt_setter(attr_name, arg_name, has_on_setattr))
|
| 2222 |
+
|
| 2223 |
+
if a.init is True:
|
| 2224 |
+
if a.type is not None and converter is None:
|
| 2225 |
+
annotations[arg_name] = a.type
|
| 2226 |
+
elif converter is not None and converter._first_param_type:
|
| 2227 |
+
# Use the type from the converter if present.
|
| 2228 |
+
annotations[arg_name] = converter._first_param_type
|
| 2229 |
+
|
| 2230 |
+
if attrs_to_validate: # we can skip this if there are no validators.
|
| 2231 |
+
names_for_globals["_config"] = _config
|
| 2232 |
+
lines.append("if _config._run_validators is True:")
|
| 2233 |
+
for a in attrs_to_validate:
|
| 2234 |
+
val_name = "__attr_validator_" + a.name
|
| 2235 |
+
attr_name = "__attr_" + a.name
|
| 2236 |
+
lines.append(f" {val_name}(self, {attr_name}, self.{a.name})")
|
| 2237 |
+
names_for_globals[val_name] = a.validator
|
| 2238 |
+
names_for_globals[attr_name] = a
|
| 2239 |
+
|
| 2240 |
+
if call_post_init:
|
| 2241 |
+
lines.append("self.__attrs_post_init__()")
|
| 2242 |
+
|
| 2243 |
+
# Because this is set only after __attrs_post_init__ is called, a crash
|
| 2244 |
+
# will result if post-init tries to access the hash code. This seemed
|
| 2245 |
+
# preferable to setting this beforehand, in which case alteration to field
|
| 2246 |
+
# values during post-init combined with post-init accessing the hash code
|
| 2247 |
+
# would result in silent bugs.
|
| 2248 |
+
if does_cache_hash:
|
| 2249 |
+
if is_frozen:
|
| 2250 |
+
if is_slotted:
|
| 2251 |
+
init_hash_cache = f"_setattr('{_HASH_CACHE_FIELD}', None)"
|
| 2252 |
+
else:
|
| 2253 |
+
init_hash_cache = f"_inst_dict['{_HASH_CACHE_FIELD}'] = None"
|
| 2254 |
+
else:
|
| 2255 |
+
init_hash_cache = f"self.{_HASH_CACHE_FIELD} = None"
|
| 2256 |
+
lines.append(init_hash_cache)
|
| 2257 |
+
|
| 2258 |
+
# For exceptions we rely on BaseException.__init__ for proper
|
| 2259 |
+
# initialization.
|
| 2260 |
+
if is_exc:
|
| 2261 |
+
vals = ",".join(f"self.{a.name}" for a in attrs if a.init)
|
| 2262 |
+
|
| 2263 |
+
lines.append(f"BaseException.__init__(self, {vals})")
|
| 2264 |
+
|
| 2265 |
+
args = ", ".join(args)
|
| 2266 |
+
pre_init_args = args
|
| 2267 |
+
if kw_only_args:
|
| 2268 |
+
# leading comma & kw_only args
|
| 2269 |
+
args += f"{', ' if args else ''}*, {', '.join(kw_only_args)}"
|
| 2270 |
+
pre_init_kw_only_args = ", ".join(
|
| 2271 |
+
[
|
| 2272 |
+
f"{kw_arg_name}={kw_arg_name}"
|
| 2273 |
+
# We need to remove the defaults from the kw_only_args.
|
| 2274 |
+
for kw_arg_name in (kwa.split("=")[0] for kwa in kw_only_args)
|
| 2275 |
+
]
|
| 2276 |
+
)
|
| 2277 |
+
pre_init_args += ", " if pre_init_args else ""
|
| 2278 |
+
pre_init_args += pre_init_kw_only_args
|
| 2279 |
+
|
| 2280 |
+
if call_pre_init and pre_init_has_args:
|
| 2281 |
+
# If pre init method has arguments, pass same arguments as `__init__`.
|
| 2282 |
+
lines[0] = f"self.__attrs_pre_init__({pre_init_args})"
|
| 2283 |
+
|
| 2284 |
+
# Python <3.12 doesn't allow backslashes in f-strings.
|
| 2285 |
+
NL = "\n "
|
| 2286 |
+
return (
|
| 2287 |
+
f"""def {method_name}(self, {args}):
|
| 2288 |
+
{NL.join(lines) if lines else "pass"}
|
| 2289 |
+
""",
|
| 2290 |
+
names_for_globals,
|
| 2291 |
+
annotations,
|
| 2292 |
+
)
|
| 2293 |
+
|
| 2294 |
+
|
| 2295 |
+
def _default_init_alias_for(name: str) -> str:
|
| 2296 |
+
"""
|
| 2297 |
+
The default __init__ parameter name for a field.
|
| 2298 |
+
|
| 2299 |
+
This performs private-name adjustment via leading-unscore stripping,
|
| 2300 |
+
and is the default value of Attribute.alias if not provided.
|
| 2301 |
+
"""
|
| 2302 |
+
|
| 2303 |
+
return name.lstrip("_")
|
| 2304 |
+
|
| 2305 |
+
|
| 2306 |
+
class Attribute:
|
| 2307 |
+
"""
|
| 2308 |
+
*Read-only* representation of an attribute.
|
| 2309 |
+
|
| 2310 |
+
.. warning::
|
| 2311 |
+
|
| 2312 |
+
You should never instantiate this class yourself.
|
| 2313 |
+
|
| 2314 |
+
The class has *all* arguments of `attr.ib` (except for ``factory`` which is
|
| 2315 |
+
only syntactic sugar for ``default=Factory(...)`` plus the following:
|
| 2316 |
+
|
| 2317 |
+
- ``name`` (`str`): The name of the attribute.
|
| 2318 |
+
- ``alias`` (`str`): The __init__ parameter name of the attribute, after
|
| 2319 |
+
any explicit overrides and default private-attribute-name handling.
|
| 2320 |
+
- ``inherited`` (`bool`): Whether or not that attribute has been inherited
|
| 2321 |
+
from a base class.
|
| 2322 |
+
- ``eq_key`` and ``order_key`` (`typing.Callable` or `None`): The
|
| 2323 |
+
callables that are used for comparing and ordering objects by this
|
| 2324 |
+
attribute, respectively. These are set by passing a callable to
|
| 2325 |
+
`attr.ib`'s ``eq``, ``order``, or ``cmp`` arguments. See also
|
| 2326 |
+
:ref:`comparison customization <custom-comparison>`.
|
| 2327 |
+
|
| 2328 |
+
Instances of this class are frequently used for introspection purposes
|
| 2329 |
+
like:
|
| 2330 |
+
|
| 2331 |
+
- `fields` returns a tuple of them.
|
| 2332 |
+
- Validators get them passed as the first argument.
|
| 2333 |
+
- The :ref:`field transformer <transform-fields>` hook receives a list of
|
| 2334 |
+
them.
|
| 2335 |
+
- The ``alias`` property exposes the __init__ parameter name of the field,
|
| 2336 |
+
with any overrides and default private-attribute handling applied.
|
| 2337 |
+
|
| 2338 |
+
|
| 2339 |
+
.. versionadded:: 20.1.0 *inherited*
|
| 2340 |
+
.. versionadded:: 20.1.0 *on_setattr*
|
| 2341 |
+
.. versionchanged:: 20.2.0 *inherited* is not taken into account for
|
| 2342 |
+
equality checks and hashing anymore.
|
| 2343 |
+
.. versionadded:: 21.1.0 *eq_key* and *order_key*
|
| 2344 |
+
.. versionadded:: 22.2.0 *alias*
|
| 2345 |
+
|
| 2346 |
+
For the full version history of the fields, see `attr.ib`.
|
| 2347 |
+
"""
|
| 2348 |
+
|
| 2349 |
+
# These slots must NOT be reordered because we use them later for
|
| 2350 |
+
# instantiation.
|
| 2351 |
+
__slots__ = ( # noqa: RUF023
|
| 2352 |
+
"name",
|
| 2353 |
+
"default",
|
| 2354 |
+
"validator",
|
| 2355 |
+
"repr",
|
| 2356 |
+
"eq",
|
| 2357 |
+
"eq_key",
|
| 2358 |
+
"order",
|
| 2359 |
+
"order_key",
|
| 2360 |
+
"hash",
|
| 2361 |
+
"init",
|
| 2362 |
+
"metadata",
|
| 2363 |
+
"type",
|
| 2364 |
+
"converter",
|
| 2365 |
+
"kw_only",
|
| 2366 |
+
"inherited",
|
| 2367 |
+
"on_setattr",
|
| 2368 |
+
"alias",
|
| 2369 |
+
)
|
| 2370 |
+
|
| 2371 |
+
def __init__(
|
| 2372 |
+
self,
|
| 2373 |
+
name,
|
| 2374 |
+
default,
|
| 2375 |
+
validator,
|
| 2376 |
+
repr,
|
| 2377 |
+
cmp, # XXX: unused, remove along with other cmp code.
|
| 2378 |
+
hash,
|
| 2379 |
+
init,
|
| 2380 |
+
inherited,
|
| 2381 |
+
metadata=None,
|
| 2382 |
+
type=None,
|
| 2383 |
+
converter=None,
|
| 2384 |
+
kw_only=False,
|
| 2385 |
+
eq=None,
|
| 2386 |
+
eq_key=None,
|
| 2387 |
+
order=None,
|
| 2388 |
+
order_key=None,
|
| 2389 |
+
on_setattr=None,
|
| 2390 |
+
alias=None,
|
| 2391 |
+
):
|
| 2392 |
+
eq, eq_key, order, order_key = _determine_attrib_eq_order(
|
| 2393 |
+
cmp, eq_key or eq, order_key or order, True
|
| 2394 |
+
)
|
| 2395 |
+
|
| 2396 |
+
# Cache this descriptor here to speed things up later.
|
| 2397 |
+
bound_setattr = _OBJ_SETATTR.__get__(self)
|
| 2398 |
+
|
| 2399 |
+
# Despite the big red warning, people *do* instantiate `Attribute`
|
| 2400 |
+
# themselves.
|
| 2401 |
+
bound_setattr("name", name)
|
| 2402 |
+
bound_setattr("default", default)
|
| 2403 |
+
bound_setattr("validator", validator)
|
| 2404 |
+
bound_setattr("repr", repr)
|
| 2405 |
+
bound_setattr("eq", eq)
|
| 2406 |
+
bound_setattr("eq_key", eq_key)
|
| 2407 |
+
bound_setattr("order", order)
|
| 2408 |
+
bound_setattr("order_key", order_key)
|
| 2409 |
+
bound_setattr("hash", hash)
|
| 2410 |
+
bound_setattr("init", init)
|
| 2411 |
+
bound_setattr("converter", converter)
|
| 2412 |
+
bound_setattr(
|
| 2413 |
+
"metadata",
|
| 2414 |
+
(
|
| 2415 |
+
types.MappingProxyType(dict(metadata)) # Shallow copy
|
| 2416 |
+
if metadata
|
| 2417 |
+
else _EMPTY_METADATA_SINGLETON
|
| 2418 |
+
),
|
| 2419 |
+
)
|
| 2420 |
+
bound_setattr("type", type)
|
| 2421 |
+
bound_setattr("kw_only", kw_only)
|
| 2422 |
+
bound_setattr("inherited", inherited)
|
| 2423 |
+
bound_setattr("on_setattr", on_setattr)
|
| 2424 |
+
bound_setattr("alias", alias)
|
| 2425 |
+
|
| 2426 |
+
def __setattr__(self, name, value):
|
| 2427 |
+
raise FrozenInstanceError
|
| 2428 |
+
|
| 2429 |
+
@classmethod
|
| 2430 |
+
def from_counting_attr(cls, name, ca, type=None):
|
| 2431 |
+
# type holds the annotated value. deal with conflicts:
|
| 2432 |
+
if type is None:
|
| 2433 |
+
type = ca.type
|
| 2434 |
+
elif ca.type is not None:
|
| 2435 |
+
msg = "Type annotation and type argument cannot both be present"
|
| 2436 |
+
raise ValueError(msg)
|
| 2437 |
+
inst_dict = {
|
| 2438 |
+
k: getattr(ca, k)
|
| 2439 |
+
for k in Attribute.__slots__
|
| 2440 |
+
if k
|
| 2441 |
+
not in (
|
| 2442 |
+
"name",
|
| 2443 |
+
"validator",
|
| 2444 |
+
"default",
|
| 2445 |
+
"type",
|
| 2446 |
+
"inherited",
|
| 2447 |
+
) # exclude methods and deprecated alias
|
| 2448 |
+
}
|
| 2449 |
+
return cls(
|
| 2450 |
+
name=name,
|
| 2451 |
+
validator=ca._validator,
|
| 2452 |
+
default=ca._default,
|
| 2453 |
+
type=type,
|
| 2454 |
+
cmp=None,
|
| 2455 |
+
inherited=False,
|
| 2456 |
+
**inst_dict,
|
| 2457 |
+
)
|
| 2458 |
+
|
| 2459 |
+
# Don't use attrs.evolve since fields(Attribute) doesn't work
|
| 2460 |
+
def evolve(self, **changes):
|
| 2461 |
+
"""
|
| 2462 |
+
Copy *self* and apply *changes*.
|
| 2463 |
+
|
| 2464 |
+
This works similarly to `attrs.evolve` but that function does not work
|
| 2465 |
+
with :class:`attrs.Attribute`.
|
| 2466 |
+
|
| 2467 |
+
It is mainly meant to be used for `transform-fields`.
|
| 2468 |
+
|
| 2469 |
+
.. versionadded:: 20.3.0
|
| 2470 |
+
"""
|
| 2471 |
+
new = copy.copy(self)
|
| 2472 |
+
|
| 2473 |
+
new._setattrs(changes.items())
|
| 2474 |
+
|
| 2475 |
+
return new
|
| 2476 |
+
|
| 2477 |
+
# Don't use _add_pickle since fields(Attribute) doesn't work
|
| 2478 |
+
def __getstate__(self):
|
| 2479 |
+
"""
|
| 2480 |
+
Play nice with pickle.
|
| 2481 |
+
"""
|
| 2482 |
+
return tuple(
|
| 2483 |
+
getattr(self, name) if name != "metadata" else dict(self.metadata)
|
| 2484 |
+
for name in self.__slots__
|
| 2485 |
+
)
|
| 2486 |
+
|
| 2487 |
+
def __setstate__(self, state):
|
| 2488 |
+
"""
|
| 2489 |
+
Play nice with pickle.
|
| 2490 |
+
"""
|
| 2491 |
+
self._setattrs(zip(self.__slots__, state))
|
| 2492 |
+
|
| 2493 |
+
def _setattrs(self, name_values_pairs):
|
| 2494 |
+
bound_setattr = _OBJ_SETATTR.__get__(self)
|
| 2495 |
+
for name, value in name_values_pairs:
|
| 2496 |
+
if name != "metadata":
|
| 2497 |
+
bound_setattr(name, value)
|
| 2498 |
+
else:
|
| 2499 |
+
bound_setattr(
|
| 2500 |
+
name,
|
| 2501 |
+
(
|
| 2502 |
+
types.MappingProxyType(dict(value))
|
| 2503 |
+
if value
|
| 2504 |
+
else _EMPTY_METADATA_SINGLETON
|
| 2505 |
+
),
|
| 2506 |
+
)
|
| 2507 |
+
|
| 2508 |
+
|
| 2509 |
+
_a = [
|
| 2510 |
+
Attribute(
|
| 2511 |
+
name=name,
|
| 2512 |
+
default=NOTHING,
|
| 2513 |
+
validator=None,
|
| 2514 |
+
repr=True,
|
| 2515 |
+
cmp=None,
|
| 2516 |
+
eq=True,
|
| 2517 |
+
order=False,
|
| 2518 |
+
hash=(name != "metadata"),
|
| 2519 |
+
init=True,
|
| 2520 |
+
inherited=False,
|
| 2521 |
+
alias=_default_init_alias_for(name),
|
| 2522 |
+
)
|
| 2523 |
+
for name in Attribute.__slots__
|
| 2524 |
+
]
|
| 2525 |
+
|
| 2526 |
+
Attribute = _add_hash(
|
| 2527 |
+
_add_eq(
|
| 2528 |
+
_add_repr(Attribute, attrs=_a),
|
| 2529 |
+
attrs=[a for a in _a if a.name != "inherited"],
|
| 2530 |
+
),
|
| 2531 |
+
attrs=[a for a in _a if a.hash and a.name != "inherited"],
|
| 2532 |
+
)
|
| 2533 |
+
|
| 2534 |
+
|
| 2535 |
+
class _CountingAttr:
|
| 2536 |
+
"""
|
| 2537 |
+
Intermediate representation of attributes that uses a counter to preserve
|
| 2538 |
+
the order in which the attributes have been defined.
|
| 2539 |
+
|
| 2540 |
+
*Internal* data structure of the attrs library. Running into is most
|
| 2541 |
+
likely the result of a bug like a forgotten `@attr.s` decorator.
|
| 2542 |
+
"""
|
| 2543 |
+
|
| 2544 |
+
__slots__ = (
|
| 2545 |
+
"_default",
|
| 2546 |
+
"_validator",
|
| 2547 |
+
"alias",
|
| 2548 |
+
"converter",
|
| 2549 |
+
"counter",
|
| 2550 |
+
"eq",
|
| 2551 |
+
"eq_key",
|
| 2552 |
+
"hash",
|
| 2553 |
+
"init",
|
| 2554 |
+
"kw_only",
|
| 2555 |
+
"metadata",
|
| 2556 |
+
"on_setattr",
|
| 2557 |
+
"order",
|
| 2558 |
+
"order_key",
|
| 2559 |
+
"repr",
|
| 2560 |
+
"type",
|
| 2561 |
+
)
|
| 2562 |
+
__attrs_attrs__ = (
|
| 2563 |
+
*tuple(
|
| 2564 |
+
Attribute(
|
| 2565 |
+
name=name,
|
| 2566 |
+
alias=_default_init_alias_for(name),
|
| 2567 |
+
default=NOTHING,
|
| 2568 |
+
validator=None,
|
| 2569 |
+
repr=True,
|
| 2570 |
+
cmp=None,
|
| 2571 |
+
hash=True,
|
| 2572 |
+
init=True,
|
| 2573 |
+
kw_only=False,
|
| 2574 |
+
eq=True,
|
| 2575 |
+
eq_key=None,
|
| 2576 |
+
order=False,
|
| 2577 |
+
order_key=None,
|
| 2578 |
+
inherited=False,
|
| 2579 |
+
on_setattr=None,
|
| 2580 |
+
)
|
| 2581 |
+
for name in (
|
| 2582 |
+
"counter",
|
| 2583 |
+
"_default",
|
| 2584 |
+
"repr",
|
| 2585 |
+
"eq",
|
| 2586 |
+
"order",
|
| 2587 |
+
"hash",
|
| 2588 |
+
"init",
|
| 2589 |
+
"on_setattr",
|
| 2590 |
+
"alias",
|
| 2591 |
+
)
|
| 2592 |
+
),
|
| 2593 |
+
Attribute(
|
| 2594 |
+
name="metadata",
|
| 2595 |
+
alias="metadata",
|
| 2596 |
+
default=None,
|
| 2597 |
+
validator=None,
|
| 2598 |
+
repr=True,
|
| 2599 |
+
cmp=None,
|
| 2600 |
+
hash=False,
|
| 2601 |
+
init=True,
|
| 2602 |
+
kw_only=False,
|
| 2603 |
+
eq=True,
|
| 2604 |
+
eq_key=None,
|
| 2605 |
+
order=False,
|
| 2606 |
+
order_key=None,
|
| 2607 |
+
inherited=False,
|
| 2608 |
+
on_setattr=None,
|
| 2609 |
+
),
|
| 2610 |
+
)
|
| 2611 |
+
cls_counter = 0
|
| 2612 |
+
|
| 2613 |
+
def __init__(
|
| 2614 |
+
self,
|
| 2615 |
+
default,
|
| 2616 |
+
validator,
|
| 2617 |
+
repr,
|
| 2618 |
+
cmp,
|
| 2619 |
+
hash,
|
| 2620 |
+
init,
|
| 2621 |
+
converter,
|
| 2622 |
+
metadata,
|
| 2623 |
+
type,
|
| 2624 |
+
kw_only,
|
| 2625 |
+
eq,
|
| 2626 |
+
eq_key,
|
| 2627 |
+
order,
|
| 2628 |
+
order_key,
|
| 2629 |
+
on_setattr,
|
| 2630 |
+
alias,
|
| 2631 |
+
):
|
| 2632 |
+
_CountingAttr.cls_counter += 1
|
| 2633 |
+
self.counter = _CountingAttr.cls_counter
|
| 2634 |
+
self._default = default
|
| 2635 |
+
self._validator = validator
|
| 2636 |
+
self.converter = converter
|
| 2637 |
+
self.repr = repr
|
| 2638 |
+
self.eq = eq
|
| 2639 |
+
self.eq_key = eq_key
|
| 2640 |
+
self.order = order
|
| 2641 |
+
self.order_key = order_key
|
| 2642 |
+
self.hash = hash
|
| 2643 |
+
self.init = init
|
| 2644 |
+
self.metadata = metadata
|
| 2645 |
+
self.type = type
|
| 2646 |
+
self.kw_only = kw_only
|
| 2647 |
+
self.on_setattr = on_setattr
|
| 2648 |
+
self.alias = alias
|
| 2649 |
+
|
| 2650 |
+
def validator(self, meth):
|
| 2651 |
+
"""
|
| 2652 |
+
Decorator that adds *meth* to the list of validators.
|
| 2653 |
+
|
| 2654 |
+
Returns *meth* unchanged.
|
| 2655 |
+
|
| 2656 |
+
.. versionadded:: 17.1.0
|
| 2657 |
+
"""
|
| 2658 |
+
if self._validator is None:
|
| 2659 |
+
self._validator = meth
|
| 2660 |
+
else:
|
| 2661 |
+
self._validator = and_(self._validator, meth)
|
| 2662 |
+
return meth
|
| 2663 |
+
|
| 2664 |
+
def default(self, meth):
|
| 2665 |
+
"""
|
| 2666 |
+
Decorator that allows to set the default for an attribute.
|
| 2667 |
+
|
| 2668 |
+
Returns *meth* unchanged.
|
| 2669 |
+
|
| 2670 |
+
Raises:
|
| 2671 |
+
DefaultAlreadySetError: If default has been set before.
|
| 2672 |
+
|
| 2673 |
+
.. versionadded:: 17.1.0
|
| 2674 |
+
"""
|
| 2675 |
+
if self._default is not NOTHING:
|
| 2676 |
+
raise DefaultAlreadySetError
|
| 2677 |
+
|
| 2678 |
+
self._default = Factory(meth, takes_self=True)
|
| 2679 |
+
|
| 2680 |
+
return meth
|
| 2681 |
+
|
| 2682 |
+
|
| 2683 |
+
_CountingAttr = _add_eq(_add_repr(_CountingAttr))
|
| 2684 |
+
|
| 2685 |
+
|
| 2686 |
+
class Factory:
|
| 2687 |
+
"""
|
| 2688 |
+
Stores a factory callable.
|
| 2689 |
+
|
| 2690 |
+
If passed as the default value to `attrs.field`, the factory is used to
|
| 2691 |
+
generate a new value.
|
| 2692 |
+
|
| 2693 |
+
Args:
|
| 2694 |
+
factory (typing.Callable):
|
| 2695 |
+
A callable that takes either none or exactly one mandatory
|
| 2696 |
+
positional argument depending on *takes_self*.
|
| 2697 |
+
|
| 2698 |
+
takes_self (bool):
|
| 2699 |
+
Pass the partially initialized instance that is being initialized
|
| 2700 |
+
as a positional argument.
|
| 2701 |
+
|
| 2702 |
+
.. versionadded:: 17.1.0 *takes_self*
|
| 2703 |
+
"""
|
| 2704 |
+
|
| 2705 |
+
__slots__ = ("factory", "takes_self")
|
| 2706 |
+
|
| 2707 |
+
def __init__(self, factory, takes_self=False):
|
| 2708 |
+
self.factory = factory
|
| 2709 |
+
self.takes_self = takes_self
|
| 2710 |
+
|
| 2711 |
+
def __getstate__(self):
|
| 2712 |
+
"""
|
| 2713 |
+
Play nice with pickle.
|
| 2714 |
+
"""
|
| 2715 |
+
return tuple(getattr(self, name) for name in self.__slots__)
|
| 2716 |
+
|
| 2717 |
+
def __setstate__(self, state):
|
| 2718 |
+
"""
|
| 2719 |
+
Play nice with pickle.
|
| 2720 |
+
"""
|
| 2721 |
+
for name, value in zip(self.__slots__, state):
|
| 2722 |
+
setattr(self, name, value)
|
| 2723 |
+
|
| 2724 |
+
|
| 2725 |
+
_f = [
|
| 2726 |
+
Attribute(
|
| 2727 |
+
name=name,
|
| 2728 |
+
default=NOTHING,
|
| 2729 |
+
validator=None,
|
| 2730 |
+
repr=True,
|
| 2731 |
+
cmp=None,
|
| 2732 |
+
eq=True,
|
| 2733 |
+
order=False,
|
| 2734 |
+
hash=True,
|
| 2735 |
+
init=True,
|
| 2736 |
+
inherited=False,
|
| 2737 |
+
)
|
| 2738 |
+
for name in Factory.__slots__
|
| 2739 |
+
]
|
| 2740 |
+
|
| 2741 |
+
Factory = _add_hash(_add_eq(_add_repr(Factory, attrs=_f), attrs=_f), attrs=_f)
|
| 2742 |
+
|
| 2743 |
+
|
| 2744 |
+
class Converter:
|
| 2745 |
+
"""
|
| 2746 |
+
Stores a converter callable.
|
| 2747 |
+
|
| 2748 |
+
Allows for the wrapped converter to take additional arguments. The
|
| 2749 |
+
arguments are passed in the order they are documented.
|
| 2750 |
+
|
| 2751 |
+
Args:
|
| 2752 |
+
converter (Callable): A callable that converts the passed value.
|
| 2753 |
+
|
| 2754 |
+
takes_self (bool):
|
| 2755 |
+
Pass the partially initialized instance that is being initialized
|
| 2756 |
+
as a positional argument. (default: `False`)
|
| 2757 |
+
|
| 2758 |
+
takes_field (bool):
|
| 2759 |
+
Pass the field definition (an :class:`Attribute`) into the
|
| 2760 |
+
converter as a positional argument. (default: `False`)
|
| 2761 |
+
|
| 2762 |
+
.. versionadded:: 24.1.0
|
| 2763 |
+
"""
|
| 2764 |
+
|
| 2765 |
+
__slots__ = (
|
| 2766 |
+
"__call__",
|
| 2767 |
+
"_first_param_type",
|
| 2768 |
+
"_global_name",
|
| 2769 |
+
"converter",
|
| 2770 |
+
"takes_field",
|
| 2771 |
+
"takes_self",
|
| 2772 |
+
)
|
| 2773 |
+
|
| 2774 |
+
def __init__(self, converter, *, takes_self=False, takes_field=False):
|
| 2775 |
+
self.converter = converter
|
| 2776 |
+
self.takes_self = takes_self
|
| 2777 |
+
self.takes_field = takes_field
|
| 2778 |
+
|
| 2779 |
+
ex = _AnnotationExtractor(converter)
|
| 2780 |
+
self._first_param_type = ex.get_first_param_type()
|
| 2781 |
+
|
| 2782 |
+
if not (self.takes_self or self.takes_field):
|
| 2783 |
+
self.__call__ = lambda value, _, __: self.converter(value)
|
| 2784 |
+
elif self.takes_self and not self.takes_field:
|
| 2785 |
+
self.__call__ = lambda value, instance, __: self.converter(
|
| 2786 |
+
value, instance
|
| 2787 |
+
)
|
| 2788 |
+
elif not self.takes_self and self.takes_field:
|
| 2789 |
+
self.__call__ = lambda value, __, field: self.converter(
|
| 2790 |
+
value, field
|
| 2791 |
+
)
|
| 2792 |
+
else:
|
| 2793 |
+
self.__call__ = lambda value, instance, field: self.converter(
|
| 2794 |
+
value, instance, field
|
| 2795 |
+
)
|
| 2796 |
+
|
| 2797 |
+
rt = ex.get_return_type()
|
| 2798 |
+
if rt is not None:
|
| 2799 |
+
self.__call__.__annotations__["return"] = rt
|
| 2800 |
+
|
| 2801 |
+
@staticmethod
|
| 2802 |
+
def _get_global_name(attr_name: str) -> str:
|
| 2803 |
+
"""
|
| 2804 |
+
Return the name that a converter for an attribute name *attr_name*
|
| 2805 |
+
would have.
|
| 2806 |
+
"""
|
| 2807 |
+
return f"__attr_converter_{attr_name}"
|
| 2808 |
+
|
| 2809 |
+
def _fmt_converter_call(self, attr_name: str, value_var: str) -> str:
|
| 2810 |
+
"""
|
| 2811 |
+
Return a string that calls the converter for an attribute name
|
| 2812 |
+
*attr_name* and the value in variable named *value_var* according to
|
| 2813 |
+
`self.takes_self` and `self.takes_field`.
|
| 2814 |
+
"""
|
| 2815 |
+
if not (self.takes_self or self.takes_field):
|
| 2816 |
+
return f"{self._get_global_name(attr_name)}({value_var})"
|
| 2817 |
+
|
| 2818 |
+
if self.takes_self and self.takes_field:
|
| 2819 |
+
return f"{self._get_global_name(attr_name)}({value_var}, self, attr_dict['{attr_name}'])"
|
| 2820 |
+
|
| 2821 |
+
if self.takes_self:
|
| 2822 |
+
return f"{self._get_global_name(attr_name)}({value_var}, self)"
|
| 2823 |
+
|
| 2824 |
+
return f"{self._get_global_name(attr_name)}({value_var}, attr_dict['{attr_name}'])"
|
| 2825 |
+
|
| 2826 |
+
def __getstate__(self):
|
| 2827 |
+
"""
|
| 2828 |
+
Return a dict containing only converter and takes_self -- the rest gets
|
| 2829 |
+
computed when loading.
|
| 2830 |
+
"""
|
| 2831 |
+
return {
|
| 2832 |
+
"converter": self.converter,
|
| 2833 |
+
"takes_self": self.takes_self,
|
| 2834 |
+
"takes_field": self.takes_field,
|
| 2835 |
+
}
|
| 2836 |
+
|
| 2837 |
+
def __setstate__(self, state):
|
| 2838 |
+
"""
|
| 2839 |
+
Load instance from state.
|
| 2840 |
+
"""
|
| 2841 |
+
self.__init__(**state)
|
| 2842 |
+
|
| 2843 |
+
|
| 2844 |
+
_f = [
|
| 2845 |
+
Attribute(
|
| 2846 |
+
name=name,
|
| 2847 |
+
default=NOTHING,
|
| 2848 |
+
validator=None,
|
| 2849 |
+
repr=True,
|
| 2850 |
+
cmp=None,
|
| 2851 |
+
eq=True,
|
| 2852 |
+
order=False,
|
| 2853 |
+
hash=True,
|
| 2854 |
+
init=True,
|
| 2855 |
+
inherited=False,
|
| 2856 |
+
)
|
| 2857 |
+
for name in ("converter", "takes_self", "takes_field")
|
| 2858 |
+
]
|
| 2859 |
+
|
| 2860 |
+
Converter = _add_hash(
|
| 2861 |
+
_add_eq(_add_repr(Converter, attrs=_f), attrs=_f), attrs=_f
|
| 2862 |
+
)
|
| 2863 |
+
|
| 2864 |
+
|
| 2865 |
+
def make_class(
|
| 2866 |
+
name, attrs, bases=(object,), class_body=None, **attributes_arguments
|
| 2867 |
+
):
|
| 2868 |
+
r"""
|
| 2869 |
+
A quick way to create a new class called *name* with *attrs*.
|
| 2870 |
+
|
| 2871 |
+
.. note::
|
| 2872 |
+
|
| 2873 |
+
``make_class()`` is a thin wrapper around `attr.s`, not `attrs.define`
|
| 2874 |
+
which means that it doesn't come with some of the improved defaults.
|
| 2875 |
+
|
| 2876 |
+
For example, if you want the same ``on_setattr`` behavior as in
|
| 2877 |
+
`attrs.define`, you have to pass the hooks yourself: ``make_class(...,
|
| 2878 |
+
on_setattr=setters.pipe(setters.convert, setters.validate)``
|
| 2879 |
+
|
| 2880 |
+
Args:
|
| 2881 |
+
name (str): The name for the new class.
|
| 2882 |
+
|
| 2883 |
+
attrs (list | dict):
|
| 2884 |
+
A list of names or a dictionary of mappings of names to `attr.ib`\
|
| 2885 |
+
s / `attrs.field`\ s.
|
| 2886 |
+
|
| 2887 |
+
The order is deduced from the order of the names or attributes
|
| 2888 |
+
inside *attrs*. Otherwise the order of the definition of the
|
| 2889 |
+
attributes is used.
|
| 2890 |
+
|
| 2891 |
+
bases (tuple[type, ...]): Classes that the new class will subclass.
|
| 2892 |
+
|
| 2893 |
+
class_body (dict):
|
| 2894 |
+
An optional dictionary of class attributes for the new class.
|
| 2895 |
+
|
| 2896 |
+
attributes_arguments: Passed unmodified to `attr.s`.
|
| 2897 |
+
|
| 2898 |
+
Returns:
|
| 2899 |
+
type: A new class with *attrs*.
|
| 2900 |
+
|
| 2901 |
+
.. versionadded:: 17.1.0 *bases*
|
| 2902 |
+
.. versionchanged:: 18.1.0 If *attrs* is ordered, the order is retained.
|
| 2903 |
+
.. versionchanged:: 23.2.0 *class_body*
|
| 2904 |
+
"""
|
| 2905 |
+
if isinstance(attrs, dict):
|
| 2906 |
+
cls_dict = attrs
|
| 2907 |
+
elif isinstance(attrs, (list, tuple)):
|
| 2908 |
+
cls_dict = {a: attrib() for a in attrs}
|
| 2909 |
+
else:
|
| 2910 |
+
msg = "attrs argument must be a dict or a list."
|
| 2911 |
+
raise TypeError(msg)
|
| 2912 |
+
|
| 2913 |
+
pre_init = cls_dict.pop("__attrs_pre_init__", None)
|
| 2914 |
+
post_init = cls_dict.pop("__attrs_post_init__", None)
|
| 2915 |
+
user_init = cls_dict.pop("__init__", None)
|
| 2916 |
+
|
| 2917 |
+
body = {}
|
| 2918 |
+
if class_body is not None:
|
| 2919 |
+
body.update(class_body)
|
| 2920 |
+
if pre_init is not None:
|
| 2921 |
+
body["__attrs_pre_init__"] = pre_init
|
| 2922 |
+
if post_init is not None:
|
| 2923 |
+
body["__attrs_post_init__"] = post_init
|
| 2924 |
+
if user_init is not None:
|
| 2925 |
+
body["__init__"] = user_init
|
| 2926 |
+
|
| 2927 |
+
type_ = types.new_class(name, bases, {}, lambda ns: ns.update(body))
|
| 2928 |
+
|
| 2929 |
+
# For pickling to work, the __module__ variable needs to be set to the
|
| 2930 |
+
# frame where the class is created. Bypass this step in environments where
|
| 2931 |
+
# sys._getframe is not defined (Jython for example) or sys._getframe is not
|
| 2932 |
+
# defined for arguments greater than 0 (IronPython).
|
| 2933 |
+
with contextlib.suppress(AttributeError, ValueError):
|
| 2934 |
+
type_.__module__ = sys._getframe(1).f_globals.get(
|
| 2935 |
+
"__name__", "__main__"
|
| 2936 |
+
)
|
| 2937 |
+
|
| 2938 |
+
# We do it here for proper warnings with meaningful stacklevel.
|
| 2939 |
+
cmp = attributes_arguments.pop("cmp", None)
|
| 2940 |
+
(
|
| 2941 |
+
attributes_arguments["eq"],
|
| 2942 |
+
attributes_arguments["order"],
|
| 2943 |
+
) = _determine_attrs_eq_order(
|
| 2944 |
+
cmp,
|
| 2945 |
+
attributes_arguments.get("eq"),
|
| 2946 |
+
attributes_arguments.get("order"),
|
| 2947 |
+
True,
|
| 2948 |
+
)
|
| 2949 |
+
|
| 2950 |
+
cls = _attrs(these=cls_dict, **attributes_arguments)(type_)
|
| 2951 |
+
# Only add type annotations now or "_attrs()" will complain:
|
| 2952 |
+
cls.__annotations__ = {
|
| 2953 |
+
k: v.type for k, v in cls_dict.items() if v.type is not None
|
| 2954 |
+
}
|
| 2955 |
+
return cls
|
| 2956 |
+
|
| 2957 |
+
|
| 2958 |
+
# These are required by within this module so we define them here and merely
|
| 2959 |
+
# import into .validators / .converters.
|
| 2960 |
+
|
| 2961 |
+
|
| 2962 |
+
@attrs(slots=True, unsafe_hash=True)
|
| 2963 |
+
class _AndValidator:
|
| 2964 |
+
"""
|
| 2965 |
+
Compose many validators to a single one.
|
| 2966 |
+
"""
|
| 2967 |
+
|
| 2968 |
+
_validators = attrib()
|
| 2969 |
+
|
| 2970 |
+
def __call__(self, inst, attr, value):
|
| 2971 |
+
for v in self._validators:
|
| 2972 |
+
v(inst, attr, value)
|
| 2973 |
+
|
| 2974 |
+
|
| 2975 |
+
def and_(*validators):
|
| 2976 |
+
"""
|
| 2977 |
+
A validator that composes multiple validators into one.
|
| 2978 |
+
|
| 2979 |
+
When called on a value, it runs all wrapped validators.
|
| 2980 |
+
|
| 2981 |
+
Args:
|
| 2982 |
+
validators (~collections.abc.Iterable[typing.Callable]):
|
| 2983 |
+
Arbitrary number of validators.
|
| 2984 |
+
|
| 2985 |
+
.. versionadded:: 17.1.0
|
| 2986 |
+
"""
|
| 2987 |
+
vals = []
|
| 2988 |
+
for validator in validators:
|
| 2989 |
+
vals.extend(
|
| 2990 |
+
validator._validators
|
| 2991 |
+
if isinstance(validator, _AndValidator)
|
| 2992 |
+
else [validator]
|
| 2993 |
+
)
|
| 2994 |
+
|
| 2995 |
+
return _AndValidator(tuple(vals))
|
| 2996 |
+
|
| 2997 |
+
|
| 2998 |
+
def pipe(*converters):
|
| 2999 |
+
"""
|
| 3000 |
+
A converter that composes multiple converters into one.
|
| 3001 |
+
|
| 3002 |
+
When called on a value, it runs all wrapped converters, returning the
|
| 3003 |
+
*last* value.
|
| 3004 |
+
|
| 3005 |
+
Type annotations will be inferred from the wrapped converters', if they
|
| 3006 |
+
have any.
|
| 3007 |
+
|
| 3008 |
+
converters (~collections.abc.Iterable[typing.Callable]):
|
| 3009 |
+
Arbitrary number of converters.
|
| 3010 |
+
|
| 3011 |
+
.. versionadded:: 20.1.0
|
| 3012 |
+
"""
|
| 3013 |
+
|
| 3014 |
+
return_instance = any(isinstance(c, Converter) for c in converters)
|
| 3015 |
+
|
| 3016 |
+
if return_instance:
|
| 3017 |
+
|
| 3018 |
+
def pipe_converter(val, inst, field):
|
| 3019 |
+
for c in converters:
|
| 3020 |
+
val = (
|
| 3021 |
+
c(val, inst, field) if isinstance(c, Converter) else c(val)
|
| 3022 |
+
)
|
| 3023 |
+
|
| 3024 |
+
return val
|
| 3025 |
+
|
| 3026 |
+
else:
|
| 3027 |
+
|
| 3028 |
+
def pipe_converter(val):
|
| 3029 |
+
for c in converters:
|
| 3030 |
+
val = c(val)
|
| 3031 |
+
|
| 3032 |
+
return val
|
| 3033 |
+
|
| 3034 |
+
if not converters:
|
| 3035 |
+
# If the converter list is empty, pipe_converter is the identity.
|
| 3036 |
+
A = typing.TypeVar("A")
|
| 3037 |
+
pipe_converter.__annotations__.update({"val": A, "return": A})
|
| 3038 |
+
else:
|
| 3039 |
+
# Get parameter type from first converter.
|
| 3040 |
+
t = _AnnotationExtractor(converters[0]).get_first_param_type()
|
| 3041 |
+
if t:
|
| 3042 |
+
pipe_converter.__annotations__["val"] = t
|
| 3043 |
+
|
| 3044 |
+
last = converters[-1]
|
| 3045 |
+
if not PY_3_11_PLUS and isinstance(last, Converter):
|
| 3046 |
+
last = last.__call__
|
| 3047 |
+
|
| 3048 |
+
# Get return type from last converter.
|
| 3049 |
+
rt = _AnnotationExtractor(last).get_return_type()
|
| 3050 |
+
if rt:
|
| 3051 |
+
pipe_converter.__annotations__["return"] = rt
|
| 3052 |
+
|
| 3053 |
+
if return_instance:
|
| 3054 |
+
return Converter(pipe_converter, takes_self=True, takes_field=True)
|
| 3055 |
+
return pipe_converter
|
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attr/_next_gen.py
ADDED
|
@@ -0,0 +1,623 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: MIT
|
| 2 |
+
|
| 3 |
+
"""
|
| 4 |
+
These are keyword-only APIs that call `attr.s` and `attr.ib` with different
|
| 5 |
+
default values.
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
from functools import partial
|
| 9 |
+
|
| 10 |
+
from . import setters
|
| 11 |
+
from ._funcs import asdict as _asdict
|
| 12 |
+
from ._funcs import astuple as _astuple
|
| 13 |
+
from ._make import (
|
| 14 |
+
_DEFAULT_ON_SETATTR,
|
| 15 |
+
NOTHING,
|
| 16 |
+
_frozen_setattrs,
|
| 17 |
+
attrib,
|
| 18 |
+
attrs,
|
| 19 |
+
)
|
| 20 |
+
from .exceptions import UnannotatedAttributeError
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def define(
|
| 24 |
+
maybe_cls=None,
|
| 25 |
+
*,
|
| 26 |
+
these=None,
|
| 27 |
+
repr=None,
|
| 28 |
+
unsafe_hash=None,
|
| 29 |
+
hash=None,
|
| 30 |
+
init=None,
|
| 31 |
+
slots=True,
|
| 32 |
+
frozen=False,
|
| 33 |
+
weakref_slot=True,
|
| 34 |
+
str=False,
|
| 35 |
+
auto_attribs=None,
|
| 36 |
+
kw_only=False,
|
| 37 |
+
cache_hash=False,
|
| 38 |
+
auto_exc=True,
|
| 39 |
+
eq=None,
|
| 40 |
+
order=False,
|
| 41 |
+
auto_detect=True,
|
| 42 |
+
getstate_setstate=None,
|
| 43 |
+
on_setattr=None,
|
| 44 |
+
field_transformer=None,
|
| 45 |
+
match_args=True,
|
| 46 |
+
):
|
| 47 |
+
r"""
|
| 48 |
+
A class decorator that adds :term:`dunder methods` according to
|
| 49 |
+
:term:`fields <field>` specified using :doc:`type annotations <types>`,
|
| 50 |
+
`field()` calls, or the *these* argument.
|
| 51 |
+
|
| 52 |
+
Since *attrs* patches or replaces an existing class, you cannot use
|
| 53 |
+
`object.__init_subclass__` with *attrs* classes, because it runs too early.
|
| 54 |
+
As a replacement, you can define ``__attrs_init_subclass__`` on your class.
|
| 55 |
+
It will be called by *attrs* classes that subclass it after they're
|
| 56 |
+
created. See also :ref:`init-subclass`.
|
| 57 |
+
|
| 58 |
+
Args:
|
| 59 |
+
slots (bool):
|
| 60 |
+
Create a :term:`slotted class <slotted classes>` that's more
|
| 61 |
+
memory-efficient. Slotted classes are generally superior to the
|
| 62 |
+
default dict classes, but have some gotchas you should know about,
|
| 63 |
+
so we encourage you to read the :term:`glossary entry <slotted
|
| 64 |
+
classes>`.
|
| 65 |
+
|
| 66 |
+
auto_detect (bool):
|
| 67 |
+
Instead of setting the *init*, *repr*, *eq*, and *hash* arguments
|
| 68 |
+
explicitly, assume they are set to True **unless any** of the
|
| 69 |
+
involved methods for one of the arguments is implemented in the
|
| 70 |
+
*current* class (meaning, it is *not* inherited from some base
|
| 71 |
+
class).
|
| 72 |
+
|
| 73 |
+
So, for example by implementing ``__eq__`` on a class yourself,
|
| 74 |
+
*attrs* will deduce ``eq=False`` and will create *neither*
|
| 75 |
+
``__eq__`` *nor* ``__ne__`` (but Python classes come with a
|
| 76 |
+
sensible ``__ne__`` by default, so it *should* be enough to only
|
| 77 |
+
implement ``__eq__`` in most cases).
|
| 78 |
+
|
| 79 |
+
Passing True or False` to *init*, *repr*, *eq*, or *hash*
|
| 80 |
+
overrides whatever *auto_detect* would determine.
|
| 81 |
+
|
| 82 |
+
auto_exc (bool):
|
| 83 |
+
If the class subclasses `BaseException` (which implicitly includes
|
| 84 |
+
any subclass of any exception), the following happens to behave
|
| 85 |
+
like a well-behaved Python exception class:
|
| 86 |
+
|
| 87 |
+
- the values for *eq*, *order*, and *hash* are ignored and the
|
| 88 |
+
instances compare and hash by the instance's ids [#]_ ,
|
| 89 |
+
- all attributes that are either passed into ``__init__`` or have a
|
| 90 |
+
default value are additionally available as a tuple in the
|
| 91 |
+
``args`` attribute,
|
| 92 |
+
- the value of *str* is ignored leaving ``__str__`` to base
|
| 93 |
+
classes.
|
| 94 |
+
|
| 95 |
+
.. [#]
|
| 96 |
+
Note that *attrs* will *not* remove existing implementations of
|
| 97 |
+
``__hash__`` or the equality methods. It just won't add own
|
| 98 |
+
ones.
|
| 99 |
+
|
| 100 |
+
on_setattr (~typing.Callable | list[~typing.Callable] | None | ~typing.Literal[attrs.setters.NO_OP]):
|
| 101 |
+
A callable that is run whenever the user attempts to set an
|
| 102 |
+
attribute (either by assignment like ``i.x = 42`` or by using
|
| 103 |
+
`setattr` like ``setattr(i, "x", 42)``). It receives the same
|
| 104 |
+
arguments as validators: the instance, the attribute that is being
|
| 105 |
+
modified, and the new value.
|
| 106 |
+
|
| 107 |
+
If no exception is raised, the attribute is set to the return value
|
| 108 |
+
of the callable.
|
| 109 |
+
|
| 110 |
+
If a list of callables is passed, they're automatically wrapped in
|
| 111 |
+
an `attrs.setters.pipe`.
|
| 112 |
+
|
| 113 |
+
If left None, the default behavior is to run converters and
|
| 114 |
+
validators whenever an attribute is set.
|
| 115 |
+
|
| 116 |
+
init (bool):
|
| 117 |
+
Create a ``__init__`` method that initializes the *attrs*
|
| 118 |
+
attributes. Leading underscores are stripped for the argument name,
|
| 119 |
+
unless an alias is set on the attribute.
|
| 120 |
+
|
| 121 |
+
.. seealso::
|
| 122 |
+
`init` shows advanced ways to customize the generated
|
| 123 |
+
``__init__`` method, including executing code before and after.
|
| 124 |
+
|
| 125 |
+
repr(bool):
|
| 126 |
+
Create a ``__repr__`` method with a human readable representation
|
| 127 |
+
of *attrs* attributes.
|
| 128 |
+
|
| 129 |
+
str (bool):
|
| 130 |
+
Create a ``__str__`` method that is identical to ``__repr__``. This
|
| 131 |
+
is usually not necessary except for `Exception`\ s.
|
| 132 |
+
|
| 133 |
+
eq (bool | None):
|
| 134 |
+
If True or None (default), add ``__eq__`` and ``__ne__`` methods
|
| 135 |
+
that check two instances for equality.
|
| 136 |
+
|
| 137 |
+
.. seealso::
|
| 138 |
+
`comparison` describes how to customize the comparison behavior
|
| 139 |
+
going as far comparing NumPy arrays.
|
| 140 |
+
|
| 141 |
+
order (bool | None):
|
| 142 |
+
If True, add ``__lt__``, ``__le__``, ``__gt__``, and ``__ge__``
|
| 143 |
+
methods that behave like *eq* above and allow instances to be
|
| 144 |
+
ordered.
|
| 145 |
+
|
| 146 |
+
They compare the instances as if they were tuples of their *attrs*
|
| 147 |
+
attributes if and only if the types of both classes are
|
| 148 |
+
*identical*.
|
| 149 |
+
|
| 150 |
+
If `None` mirror value of *eq*.
|
| 151 |
+
|
| 152 |
+
.. seealso:: `comparison`
|
| 153 |
+
|
| 154 |
+
unsafe_hash (bool | None):
|
| 155 |
+
If None (default), the ``__hash__`` method is generated according
|
| 156 |
+
how *eq* and *frozen* are set.
|
| 157 |
+
|
| 158 |
+
1. If *both* are True, *attrs* will generate a ``__hash__`` for
|
| 159 |
+
you.
|
| 160 |
+
2. If *eq* is True and *frozen* is False, ``__hash__`` will be set
|
| 161 |
+
to None, marking it unhashable (which it is).
|
| 162 |
+
3. If *eq* is False, ``__hash__`` will be left untouched meaning
|
| 163 |
+
the ``__hash__`` method of the base class will be used. If the
|
| 164 |
+
base class is `object`, this means it will fall back to id-based
|
| 165 |
+
hashing.
|
| 166 |
+
|
| 167 |
+
Although not recommended, you can decide for yourself and force
|
| 168 |
+
*attrs* to create one (for example, if the class is immutable even
|
| 169 |
+
though you didn't freeze it programmatically) by passing True or
|
| 170 |
+
not. Both of these cases are rather special and should be used
|
| 171 |
+
carefully.
|
| 172 |
+
|
| 173 |
+
.. seealso::
|
| 174 |
+
|
| 175 |
+
- Our documentation on `hashing`,
|
| 176 |
+
- Python's documentation on `object.__hash__`,
|
| 177 |
+
- and the `GitHub issue that led to the default \ behavior
|
| 178 |
+
<https://github.com/python-attrs/attrs/issues/136>`_ for more
|
| 179 |
+
details.
|
| 180 |
+
|
| 181 |
+
hash (bool | None):
|
| 182 |
+
Deprecated alias for *unsafe_hash*. *unsafe_hash* takes precedence.
|
| 183 |
+
|
| 184 |
+
cache_hash (bool):
|
| 185 |
+
Ensure that the object's hash code is computed only once and stored
|
| 186 |
+
on the object. If this is set to True, hashing must be either
|
| 187 |
+
explicitly or implicitly enabled for this class. If the hash code
|
| 188 |
+
is cached, avoid any reassignments of fields involved in hash code
|
| 189 |
+
computation or mutations of the objects those fields point to after
|
| 190 |
+
object creation. If such changes occur, the behavior of the
|
| 191 |
+
object's hash code is undefined.
|
| 192 |
+
|
| 193 |
+
frozen (bool):
|
| 194 |
+
Make instances immutable after initialization. If someone attempts
|
| 195 |
+
to modify a frozen instance, `attrs.exceptions.FrozenInstanceError`
|
| 196 |
+
is raised.
|
| 197 |
+
|
| 198 |
+
.. note::
|
| 199 |
+
|
| 200 |
+
1. This is achieved by installing a custom ``__setattr__``
|
| 201 |
+
method on your class, so you can't implement your own.
|
| 202 |
+
|
| 203 |
+
2. True immutability is impossible in Python.
|
| 204 |
+
|
| 205 |
+
3. This *does* have a minor a runtime performance `impact
|
| 206 |
+
<how-frozen>` when initializing new instances. In other
|
| 207 |
+
words: ``__init__`` is slightly slower with ``frozen=True``.
|
| 208 |
+
|
| 209 |
+
4. If a class is frozen, you cannot modify ``self`` in
|
| 210 |
+
``__attrs_post_init__`` or a self-written ``__init__``. You
|
| 211 |
+
can circumvent that limitation by using
|
| 212 |
+
``object.__setattr__(self, "attribute_name", value)``.
|
| 213 |
+
|
| 214 |
+
5. Subclasses of a frozen class are frozen too.
|
| 215 |
+
|
| 216 |
+
kw_only (bool):
|
| 217 |
+
Make all attributes keyword-only in the generated ``__init__`` (if
|
| 218 |
+
*init* is False, this parameter is ignored).
|
| 219 |
+
|
| 220 |
+
weakref_slot (bool):
|
| 221 |
+
Make instances weak-referenceable. This has no effect unless
|
| 222 |
+
*slots* is True.
|
| 223 |
+
|
| 224 |
+
field_transformer (~typing.Callable | None):
|
| 225 |
+
A function that is called with the original class object and all
|
| 226 |
+
fields right before *attrs* finalizes the class. You can use this,
|
| 227 |
+
for example, to automatically add converters or validators to
|
| 228 |
+
fields based on their types.
|
| 229 |
+
|
| 230 |
+
.. seealso:: `transform-fields`
|
| 231 |
+
|
| 232 |
+
match_args (bool):
|
| 233 |
+
If True (default), set ``__match_args__`` on the class to support
|
| 234 |
+
:pep:`634` (*Structural Pattern Matching*). It is a tuple of all
|
| 235 |
+
non-keyword-only ``__init__`` parameter names on Python 3.10 and
|
| 236 |
+
later. Ignored on older Python versions.
|
| 237 |
+
|
| 238 |
+
collect_by_mro (bool):
|
| 239 |
+
If True, *attrs* collects attributes from base classes correctly
|
| 240 |
+
according to the `method resolution order
|
| 241 |
+
<https://docs.python.org/3/howto/mro.html>`_. If False, *attrs*
|
| 242 |
+
will mimic the (wrong) behavior of `dataclasses` and :pep:`681`.
|
| 243 |
+
|
| 244 |
+
See also `issue #428
|
| 245 |
+
<https://github.com/python-attrs/attrs/issues/428>`_.
|
| 246 |
+
|
| 247 |
+
getstate_setstate (bool | None):
|
| 248 |
+
.. note::
|
| 249 |
+
|
| 250 |
+
This is usually only interesting for slotted classes and you
|
| 251 |
+
should probably just set *auto_detect* to True.
|
| 252 |
+
|
| 253 |
+
If True, ``__getstate__`` and ``__setstate__`` are generated and
|
| 254 |
+
attached to the class. This is necessary for slotted classes to be
|
| 255 |
+
pickleable. If left None, it's True by default for slotted classes
|
| 256 |
+
and False for dict classes.
|
| 257 |
+
|
| 258 |
+
If *auto_detect* is True, and *getstate_setstate* is left None, and
|
| 259 |
+
**either** ``__getstate__`` or ``__setstate__`` is detected
|
| 260 |
+
directly on the class (meaning: not inherited), it is set to False
|
| 261 |
+
(this is usually what you want).
|
| 262 |
+
|
| 263 |
+
auto_attribs (bool | None):
|
| 264 |
+
If True, look at type annotations to determine which attributes to
|
| 265 |
+
use, like `dataclasses`. If False, it will only look for explicit
|
| 266 |
+
:func:`field` class attributes, like classic *attrs*.
|
| 267 |
+
|
| 268 |
+
If left None, it will guess:
|
| 269 |
+
|
| 270 |
+
1. If any attributes are annotated and no unannotated
|
| 271 |
+
`attrs.field`\ s are found, it assumes *auto_attribs=True*.
|
| 272 |
+
2. Otherwise it assumes *auto_attribs=False* and tries to collect
|
| 273 |
+
`attrs.field`\ s.
|
| 274 |
+
|
| 275 |
+
If *attrs* decides to look at type annotations, **all** fields
|
| 276 |
+
**must** be annotated. If *attrs* encounters a field that is set to
|
| 277 |
+
a :func:`field` / `attr.ib` but lacks a type annotation, an
|
| 278 |
+
`attrs.exceptions.UnannotatedAttributeError` is raised. Use
|
| 279 |
+
``field_name: typing.Any = field(...)`` if you don't want to set a
|
| 280 |
+
type.
|
| 281 |
+
|
| 282 |
+
.. warning::
|
| 283 |
+
|
| 284 |
+
For features that use the attribute name to create decorators
|
| 285 |
+
(for example, :ref:`validators <validators>`), you still *must*
|
| 286 |
+
assign :func:`field` / `attr.ib` to them. Otherwise Python will
|
| 287 |
+
either not find the name or try to use the default value to
|
| 288 |
+
call, for example, ``validator`` on it.
|
| 289 |
+
|
| 290 |
+
Attributes annotated as `typing.ClassVar`, and attributes that are
|
| 291 |
+
neither annotated nor set to an `field()` are **ignored**.
|
| 292 |
+
|
| 293 |
+
these (dict[str, object]):
|
| 294 |
+
A dictionary of name to the (private) return value of `field()`
|
| 295 |
+
mappings. This is useful to avoid the definition of your attributes
|
| 296 |
+
within the class body because you can't (for example, if you want
|
| 297 |
+
to add ``__repr__`` methods to Django models) or don't want to.
|
| 298 |
+
|
| 299 |
+
If *these* is not `None`, *attrs* will *not* search the class body
|
| 300 |
+
for attributes and will *not* remove any attributes from it.
|
| 301 |
+
|
| 302 |
+
The order is deduced from the order of the attributes inside
|
| 303 |
+
*these*.
|
| 304 |
+
|
| 305 |
+
Arguably, this is a rather obscure feature.
|
| 306 |
+
|
| 307 |
+
.. versionadded:: 20.1.0
|
| 308 |
+
.. versionchanged:: 21.3.0 Converters are also run ``on_setattr``.
|
| 309 |
+
.. versionadded:: 22.2.0
|
| 310 |
+
*unsafe_hash* as an alias for *hash* (for :pep:`681` compliance).
|
| 311 |
+
.. versionchanged:: 24.1.0
|
| 312 |
+
Instances are not compared as tuples of attributes anymore, but using a
|
| 313 |
+
big ``and`` condition. This is faster and has more correct behavior for
|
| 314 |
+
uncomparable values like `math.nan`.
|
| 315 |
+
.. versionadded:: 24.1.0
|
| 316 |
+
If a class has an *inherited* classmethod called
|
| 317 |
+
``__attrs_init_subclass__``, it is executed after the class is created.
|
| 318 |
+
.. deprecated:: 24.1.0 *hash* is deprecated in favor of *unsafe_hash*.
|
| 319 |
+
.. versionadded:: 24.3.0
|
| 320 |
+
Unless already present, a ``__replace__`` method is automatically
|
| 321 |
+
created for `copy.replace` (Python 3.13+ only).
|
| 322 |
+
|
| 323 |
+
.. note::
|
| 324 |
+
|
| 325 |
+
The main differences to the classic `attr.s` are:
|
| 326 |
+
|
| 327 |
+
- Automatically detect whether or not *auto_attribs* should be `True`
|
| 328 |
+
(c.f. *auto_attribs* parameter).
|
| 329 |
+
- Converters and validators run when attributes are set by default --
|
| 330 |
+
if *frozen* is `False`.
|
| 331 |
+
- *slots=True*
|
| 332 |
+
|
| 333 |
+
Usually, this has only upsides and few visible effects in everyday
|
| 334 |
+
programming. But it *can* lead to some surprising behaviors, so
|
| 335 |
+
please make sure to read :term:`slotted classes`.
|
| 336 |
+
|
| 337 |
+
- *auto_exc=True*
|
| 338 |
+
- *auto_detect=True*
|
| 339 |
+
- *order=False*
|
| 340 |
+
- Some options that were only relevant on Python 2 or were kept around
|
| 341 |
+
for backwards-compatibility have been removed.
|
| 342 |
+
|
| 343 |
+
"""
|
| 344 |
+
|
| 345 |
+
def do_it(cls, auto_attribs):
|
| 346 |
+
return attrs(
|
| 347 |
+
maybe_cls=cls,
|
| 348 |
+
these=these,
|
| 349 |
+
repr=repr,
|
| 350 |
+
hash=hash,
|
| 351 |
+
unsafe_hash=unsafe_hash,
|
| 352 |
+
init=init,
|
| 353 |
+
slots=slots,
|
| 354 |
+
frozen=frozen,
|
| 355 |
+
weakref_slot=weakref_slot,
|
| 356 |
+
str=str,
|
| 357 |
+
auto_attribs=auto_attribs,
|
| 358 |
+
kw_only=kw_only,
|
| 359 |
+
cache_hash=cache_hash,
|
| 360 |
+
auto_exc=auto_exc,
|
| 361 |
+
eq=eq,
|
| 362 |
+
order=order,
|
| 363 |
+
auto_detect=auto_detect,
|
| 364 |
+
collect_by_mro=True,
|
| 365 |
+
getstate_setstate=getstate_setstate,
|
| 366 |
+
on_setattr=on_setattr,
|
| 367 |
+
field_transformer=field_transformer,
|
| 368 |
+
match_args=match_args,
|
| 369 |
+
)
|
| 370 |
+
|
| 371 |
+
def wrap(cls):
|
| 372 |
+
"""
|
| 373 |
+
Making this a wrapper ensures this code runs during class creation.
|
| 374 |
+
|
| 375 |
+
We also ensure that frozen-ness of classes is inherited.
|
| 376 |
+
"""
|
| 377 |
+
nonlocal frozen, on_setattr
|
| 378 |
+
|
| 379 |
+
had_on_setattr = on_setattr not in (None, setters.NO_OP)
|
| 380 |
+
|
| 381 |
+
# By default, mutable classes convert & validate on setattr.
|
| 382 |
+
if frozen is False and on_setattr is None:
|
| 383 |
+
on_setattr = _DEFAULT_ON_SETATTR
|
| 384 |
+
|
| 385 |
+
# However, if we subclass a frozen class, we inherit the immutability
|
| 386 |
+
# and disable on_setattr.
|
| 387 |
+
for base_cls in cls.__bases__:
|
| 388 |
+
if base_cls.__setattr__ is _frozen_setattrs:
|
| 389 |
+
if had_on_setattr:
|
| 390 |
+
msg = "Frozen classes can't use on_setattr (frozen-ness was inherited)."
|
| 391 |
+
raise ValueError(msg)
|
| 392 |
+
|
| 393 |
+
on_setattr = setters.NO_OP
|
| 394 |
+
break
|
| 395 |
+
|
| 396 |
+
if auto_attribs is not None:
|
| 397 |
+
return do_it(cls, auto_attribs)
|
| 398 |
+
|
| 399 |
+
try:
|
| 400 |
+
return do_it(cls, True)
|
| 401 |
+
except UnannotatedAttributeError:
|
| 402 |
+
return do_it(cls, False)
|
| 403 |
+
|
| 404 |
+
# maybe_cls's type depends on the usage of the decorator. It's a class
|
| 405 |
+
# if it's used as `@attrs` but `None` if used as `@attrs()`.
|
| 406 |
+
if maybe_cls is None:
|
| 407 |
+
return wrap
|
| 408 |
+
|
| 409 |
+
return wrap(maybe_cls)
|
| 410 |
+
|
| 411 |
+
|
| 412 |
+
mutable = define
|
| 413 |
+
frozen = partial(define, frozen=True, on_setattr=None)
|
| 414 |
+
|
| 415 |
+
|
| 416 |
+
def field(
|
| 417 |
+
*,
|
| 418 |
+
default=NOTHING,
|
| 419 |
+
validator=None,
|
| 420 |
+
repr=True,
|
| 421 |
+
hash=None,
|
| 422 |
+
init=True,
|
| 423 |
+
metadata=None,
|
| 424 |
+
type=None,
|
| 425 |
+
converter=None,
|
| 426 |
+
factory=None,
|
| 427 |
+
kw_only=False,
|
| 428 |
+
eq=None,
|
| 429 |
+
order=None,
|
| 430 |
+
on_setattr=None,
|
| 431 |
+
alias=None,
|
| 432 |
+
):
|
| 433 |
+
"""
|
| 434 |
+
Create a new :term:`field` / :term:`attribute` on a class.
|
| 435 |
+
|
| 436 |
+
.. warning::
|
| 437 |
+
|
| 438 |
+
Does **nothing** unless the class is also decorated with
|
| 439 |
+
`attrs.define` (or similar)!
|
| 440 |
+
|
| 441 |
+
Args:
|
| 442 |
+
default:
|
| 443 |
+
A value that is used if an *attrs*-generated ``__init__`` is used
|
| 444 |
+
and no value is passed while instantiating or the attribute is
|
| 445 |
+
excluded using ``init=False``.
|
| 446 |
+
|
| 447 |
+
If the value is an instance of `attrs.Factory`, its callable will
|
| 448 |
+
be used to construct a new value (useful for mutable data types
|
| 449 |
+
like lists or dicts).
|
| 450 |
+
|
| 451 |
+
If a default is not set (or set manually to `attrs.NOTHING`), a
|
| 452 |
+
value *must* be supplied when instantiating; otherwise a
|
| 453 |
+
`TypeError` will be raised.
|
| 454 |
+
|
| 455 |
+
.. seealso:: `defaults`
|
| 456 |
+
|
| 457 |
+
factory (~typing.Callable):
|
| 458 |
+
Syntactic sugar for ``default=attr.Factory(factory)``.
|
| 459 |
+
|
| 460 |
+
validator (~typing.Callable | list[~typing.Callable]):
|
| 461 |
+
Callable that is called by *attrs*-generated ``__init__`` methods
|
| 462 |
+
after the instance has been initialized. They receive the
|
| 463 |
+
initialized instance, the :func:`~attrs.Attribute`, and the passed
|
| 464 |
+
value.
|
| 465 |
+
|
| 466 |
+
The return value is *not* inspected so the validator has to throw
|
| 467 |
+
an exception itself.
|
| 468 |
+
|
| 469 |
+
If a `list` is passed, its items are treated as validators and must
|
| 470 |
+
all pass.
|
| 471 |
+
|
| 472 |
+
Validators can be globally disabled and re-enabled using
|
| 473 |
+
`attrs.validators.get_disabled` / `attrs.validators.set_disabled`.
|
| 474 |
+
|
| 475 |
+
The validator can also be set using decorator notation as shown
|
| 476 |
+
below.
|
| 477 |
+
|
| 478 |
+
.. seealso:: :ref:`validators`
|
| 479 |
+
|
| 480 |
+
repr (bool | ~typing.Callable):
|
| 481 |
+
Include this attribute in the generated ``__repr__`` method. If
|
| 482 |
+
True, include the attribute; if False, omit it. By default, the
|
| 483 |
+
built-in ``repr()`` function is used. To override how the attribute
|
| 484 |
+
value is formatted, pass a ``callable`` that takes a single value
|
| 485 |
+
and returns a string. Note that the resulting string is used as-is,
|
| 486 |
+
which means it will be used directly *instead* of calling
|
| 487 |
+
``repr()`` (the default).
|
| 488 |
+
|
| 489 |
+
eq (bool | ~typing.Callable):
|
| 490 |
+
If True (default), include this attribute in the generated
|
| 491 |
+
``__eq__`` and ``__ne__`` methods that check two instances for
|
| 492 |
+
equality. To override how the attribute value is compared, pass a
|
| 493 |
+
callable that takes a single value and returns the value to be
|
| 494 |
+
compared.
|
| 495 |
+
|
| 496 |
+
.. seealso:: `comparison`
|
| 497 |
+
|
| 498 |
+
order (bool | ~typing.Callable):
|
| 499 |
+
If True (default), include this attributes in the generated
|
| 500 |
+
``__lt__``, ``__le__``, ``__gt__`` and ``__ge__`` methods. To
|
| 501 |
+
override how the attribute value is ordered, pass a callable that
|
| 502 |
+
takes a single value and returns the value to be ordered.
|
| 503 |
+
|
| 504 |
+
.. seealso:: `comparison`
|
| 505 |
+
|
| 506 |
+
hash (bool | None):
|
| 507 |
+
Include this attribute in the generated ``__hash__`` method. If
|
| 508 |
+
None (default), mirror *eq*'s value. This is the correct behavior
|
| 509 |
+
according the Python spec. Setting this value to anything else
|
| 510 |
+
than None is *discouraged*.
|
| 511 |
+
|
| 512 |
+
.. seealso:: `hashing`
|
| 513 |
+
|
| 514 |
+
init (bool):
|
| 515 |
+
Include this attribute in the generated ``__init__`` method.
|
| 516 |
+
|
| 517 |
+
It is possible to set this to False and set a default value. In
|
| 518 |
+
that case this attributed is unconditionally initialized with the
|
| 519 |
+
specified default value or factory.
|
| 520 |
+
|
| 521 |
+
.. seealso:: `init`
|
| 522 |
+
|
| 523 |
+
converter (typing.Callable | Converter):
|
| 524 |
+
A callable that is called by *attrs*-generated ``__init__`` methods
|
| 525 |
+
to convert attribute's value to the desired format.
|
| 526 |
+
|
| 527 |
+
If a vanilla callable is passed, it is given the passed-in value as
|
| 528 |
+
the only positional argument. It is possible to receive additional
|
| 529 |
+
arguments by wrapping the callable in a `Converter`.
|
| 530 |
+
|
| 531 |
+
Either way, the returned value will be used as the new value of the
|
| 532 |
+
attribute. The value is converted before being passed to the
|
| 533 |
+
validator, if any.
|
| 534 |
+
|
| 535 |
+
.. seealso:: :ref:`converters`
|
| 536 |
+
|
| 537 |
+
metadata (dict | None):
|
| 538 |
+
An arbitrary mapping, to be used by third-party code.
|
| 539 |
+
|
| 540 |
+
.. seealso:: `extending-metadata`.
|
| 541 |
+
|
| 542 |
+
type (type):
|
| 543 |
+
The type of the attribute. Nowadays, the preferred method to
|
| 544 |
+
specify the type is using a variable annotation (see :pep:`526`).
|
| 545 |
+
This argument is provided for backwards-compatibility and for usage
|
| 546 |
+
with `make_class`. Regardless of the approach used, the type will
|
| 547 |
+
be stored on ``Attribute.type``.
|
| 548 |
+
|
| 549 |
+
Please note that *attrs* doesn't do anything with this metadata by
|
| 550 |
+
itself. You can use it as part of your own code or for `static type
|
| 551 |
+
checking <types>`.
|
| 552 |
+
|
| 553 |
+
kw_only (bool):
|
| 554 |
+
Make this attribute keyword-only in the generated ``__init__`` (if
|
| 555 |
+
``init`` is False, this parameter is ignored).
|
| 556 |
+
|
| 557 |
+
on_setattr (~typing.Callable | list[~typing.Callable] | None | ~typing.Literal[attrs.setters.NO_OP]):
|
| 558 |
+
Allows to overwrite the *on_setattr* setting from `attr.s`. If left
|
| 559 |
+
None, the *on_setattr* value from `attr.s` is used. Set to
|
| 560 |
+
`attrs.setters.NO_OP` to run **no** `setattr` hooks for this
|
| 561 |
+
attribute -- regardless of the setting in `define()`.
|
| 562 |
+
|
| 563 |
+
alias (str | None):
|
| 564 |
+
Override this attribute's parameter name in the generated
|
| 565 |
+
``__init__`` method. If left None, default to ``name`` stripped
|
| 566 |
+
of leading underscores. See `private-attributes`.
|
| 567 |
+
|
| 568 |
+
.. versionadded:: 20.1.0
|
| 569 |
+
.. versionchanged:: 21.1.0
|
| 570 |
+
*eq*, *order*, and *cmp* also accept a custom callable
|
| 571 |
+
.. versionadded:: 22.2.0 *alias*
|
| 572 |
+
.. versionadded:: 23.1.0
|
| 573 |
+
The *type* parameter has been re-added; mostly for `attrs.make_class`.
|
| 574 |
+
Please note that type checkers ignore this metadata.
|
| 575 |
+
|
| 576 |
+
.. seealso::
|
| 577 |
+
|
| 578 |
+
`attr.ib`
|
| 579 |
+
"""
|
| 580 |
+
return attrib(
|
| 581 |
+
default=default,
|
| 582 |
+
validator=validator,
|
| 583 |
+
repr=repr,
|
| 584 |
+
hash=hash,
|
| 585 |
+
init=init,
|
| 586 |
+
metadata=metadata,
|
| 587 |
+
type=type,
|
| 588 |
+
converter=converter,
|
| 589 |
+
factory=factory,
|
| 590 |
+
kw_only=kw_only,
|
| 591 |
+
eq=eq,
|
| 592 |
+
order=order,
|
| 593 |
+
on_setattr=on_setattr,
|
| 594 |
+
alias=alias,
|
| 595 |
+
)
|
| 596 |
+
|
| 597 |
+
|
| 598 |
+
def asdict(inst, *, recurse=True, filter=None, value_serializer=None):
|
| 599 |
+
"""
|
| 600 |
+
Same as `attr.asdict`, except that collections types are always retained
|
| 601 |
+
and dict is always used as *dict_factory*.
|
| 602 |
+
|
| 603 |
+
.. versionadded:: 21.3.0
|
| 604 |
+
"""
|
| 605 |
+
return _asdict(
|
| 606 |
+
inst=inst,
|
| 607 |
+
recurse=recurse,
|
| 608 |
+
filter=filter,
|
| 609 |
+
value_serializer=value_serializer,
|
| 610 |
+
retain_collection_types=True,
|
| 611 |
+
)
|
| 612 |
+
|
| 613 |
+
|
| 614 |
+
def astuple(inst, *, recurse=True, filter=None):
|
| 615 |
+
"""
|
| 616 |
+
Same as `attr.astuple`, except that collections types are always retained
|
| 617 |
+
and `tuple` is always used as the *tuple_factory*.
|
| 618 |
+
|
| 619 |
+
.. versionadded:: 21.3.0
|
| 620 |
+
"""
|
| 621 |
+
return _astuple(
|
| 622 |
+
inst=inst, recurse=recurse, filter=filter, retain_collection_types=True
|
| 623 |
+
)
|
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attr/_typing_compat.pyi
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Any, ClassVar, Protocol
|
| 2 |
+
|
| 3 |
+
# MYPY is a special constant in mypy which works the same way as `TYPE_CHECKING`.
|
| 4 |
+
MYPY = False
|
| 5 |
+
|
| 6 |
+
if MYPY:
|
| 7 |
+
# A protocol to be able to statically accept an attrs class.
|
| 8 |
+
class AttrsInstance_(Protocol):
|
| 9 |
+
__attrs_attrs__: ClassVar[Any]
|
| 10 |
+
|
| 11 |
+
else:
|
| 12 |
+
# For type checkers without plug-in support use an empty protocol that
|
| 13 |
+
# will (hopefully) be combined into a union.
|
| 14 |
+
class AttrsInstance_(Protocol):
|
| 15 |
+
pass
|
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attr/_version_info.py
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: MIT
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
from functools import total_ordering
|
| 5 |
+
|
| 6 |
+
from ._funcs import astuple
|
| 7 |
+
from ._make import attrib, attrs
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
@total_ordering
|
| 11 |
+
@attrs(eq=False, order=False, slots=True, frozen=True)
|
| 12 |
+
class VersionInfo:
|
| 13 |
+
"""
|
| 14 |
+
A version object that can be compared to tuple of length 1--4:
|
| 15 |
+
|
| 16 |
+
>>> attr.VersionInfo(19, 1, 0, "final") <= (19, 2)
|
| 17 |
+
True
|
| 18 |
+
>>> attr.VersionInfo(19, 1, 0, "final") < (19, 1, 1)
|
| 19 |
+
True
|
| 20 |
+
>>> vi = attr.VersionInfo(19, 2, 0, "final")
|
| 21 |
+
>>> vi < (19, 1, 1)
|
| 22 |
+
False
|
| 23 |
+
>>> vi < (19,)
|
| 24 |
+
False
|
| 25 |
+
>>> vi == (19, 2,)
|
| 26 |
+
True
|
| 27 |
+
>>> vi == (19, 2, 1)
|
| 28 |
+
False
|
| 29 |
+
|
| 30 |
+
.. versionadded:: 19.2
|
| 31 |
+
"""
|
| 32 |
+
|
| 33 |
+
year = attrib(type=int)
|
| 34 |
+
minor = attrib(type=int)
|
| 35 |
+
micro = attrib(type=int)
|
| 36 |
+
releaselevel = attrib(type=str)
|
| 37 |
+
|
| 38 |
+
@classmethod
|
| 39 |
+
def _from_version_string(cls, s):
|
| 40 |
+
"""
|
| 41 |
+
Parse *s* and return a _VersionInfo.
|
| 42 |
+
"""
|
| 43 |
+
v = s.split(".")
|
| 44 |
+
if len(v) == 3:
|
| 45 |
+
v.append("final")
|
| 46 |
+
|
| 47 |
+
return cls(
|
| 48 |
+
year=int(v[0]), minor=int(v[1]), micro=int(v[2]), releaselevel=v[3]
|
| 49 |
+
)
|
| 50 |
+
|
| 51 |
+
def _ensure_tuple(self, other):
|
| 52 |
+
"""
|
| 53 |
+
Ensure *other* is a tuple of a valid length.
|
| 54 |
+
|
| 55 |
+
Returns a possibly transformed *other* and ourselves as a tuple of
|
| 56 |
+
the same length as *other*.
|
| 57 |
+
"""
|
| 58 |
+
|
| 59 |
+
if self.__class__ is other.__class__:
|
| 60 |
+
other = astuple(other)
|
| 61 |
+
|
| 62 |
+
if not isinstance(other, tuple):
|
| 63 |
+
raise NotImplementedError
|
| 64 |
+
|
| 65 |
+
if not (1 <= len(other) <= 4):
|
| 66 |
+
raise NotImplementedError
|
| 67 |
+
|
| 68 |
+
return astuple(self)[: len(other)], other
|
| 69 |
+
|
| 70 |
+
def __eq__(self, other):
|
| 71 |
+
try:
|
| 72 |
+
us, them = self._ensure_tuple(other)
|
| 73 |
+
except NotImplementedError:
|
| 74 |
+
return NotImplemented
|
| 75 |
+
|
| 76 |
+
return us == them
|
| 77 |
+
|
| 78 |
+
def __lt__(self, other):
|
| 79 |
+
try:
|
| 80 |
+
us, them = self._ensure_tuple(other)
|
| 81 |
+
except NotImplementedError:
|
| 82 |
+
return NotImplemented
|
| 83 |
+
|
| 84 |
+
# Since alphabetically "dev0" < "final" < "post1" < "post2", we don't
|
| 85 |
+
# have to do anything special with releaselevel for now.
|
| 86 |
+
return us < them
|
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attr/converters.py
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: MIT
|
| 2 |
+
|
| 3 |
+
"""
|
| 4 |
+
Commonly useful converters.
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
import typing
|
| 8 |
+
|
| 9 |
+
from ._compat import _AnnotationExtractor
|
| 10 |
+
from ._make import NOTHING, Converter, Factory, pipe
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
__all__ = [
|
| 14 |
+
"default_if_none",
|
| 15 |
+
"optional",
|
| 16 |
+
"pipe",
|
| 17 |
+
"to_bool",
|
| 18 |
+
]
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def optional(converter):
|
| 22 |
+
"""
|
| 23 |
+
A converter that allows an attribute to be optional. An optional attribute
|
| 24 |
+
is one which can be set to `None`.
|
| 25 |
+
|
| 26 |
+
Type annotations will be inferred from the wrapped converter's, if it has
|
| 27 |
+
any.
|
| 28 |
+
|
| 29 |
+
Args:
|
| 30 |
+
converter (typing.Callable):
|
| 31 |
+
the converter that is used for non-`None` values.
|
| 32 |
+
|
| 33 |
+
.. versionadded:: 17.1.0
|
| 34 |
+
"""
|
| 35 |
+
|
| 36 |
+
if isinstance(converter, Converter):
|
| 37 |
+
|
| 38 |
+
def optional_converter(val, inst, field):
|
| 39 |
+
if val is None:
|
| 40 |
+
return None
|
| 41 |
+
return converter(val, inst, field)
|
| 42 |
+
|
| 43 |
+
else:
|
| 44 |
+
|
| 45 |
+
def optional_converter(val):
|
| 46 |
+
if val is None:
|
| 47 |
+
return None
|
| 48 |
+
return converter(val)
|
| 49 |
+
|
| 50 |
+
xtr = _AnnotationExtractor(converter)
|
| 51 |
+
|
| 52 |
+
t = xtr.get_first_param_type()
|
| 53 |
+
if t:
|
| 54 |
+
optional_converter.__annotations__["val"] = typing.Optional[t]
|
| 55 |
+
|
| 56 |
+
rt = xtr.get_return_type()
|
| 57 |
+
if rt:
|
| 58 |
+
optional_converter.__annotations__["return"] = typing.Optional[rt]
|
| 59 |
+
|
| 60 |
+
if isinstance(converter, Converter):
|
| 61 |
+
return Converter(optional_converter, takes_self=True, takes_field=True)
|
| 62 |
+
|
| 63 |
+
return optional_converter
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
def default_if_none(default=NOTHING, factory=None):
|
| 67 |
+
"""
|
| 68 |
+
A converter that allows to replace `None` values by *default* or the result
|
| 69 |
+
of *factory*.
|
| 70 |
+
|
| 71 |
+
Args:
|
| 72 |
+
default:
|
| 73 |
+
Value to be used if `None` is passed. Passing an instance of
|
| 74 |
+
`attrs.Factory` is supported, however the ``takes_self`` option is
|
| 75 |
+
*not*.
|
| 76 |
+
|
| 77 |
+
factory (typing.Callable):
|
| 78 |
+
A callable that takes no parameters whose result is used if `None`
|
| 79 |
+
is passed.
|
| 80 |
+
|
| 81 |
+
Raises:
|
| 82 |
+
TypeError: If **neither** *default* or *factory* is passed.
|
| 83 |
+
|
| 84 |
+
TypeError: If **both** *default* and *factory* are passed.
|
| 85 |
+
|
| 86 |
+
ValueError:
|
| 87 |
+
If an instance of `attrs.Factory` is passed with
|
| 88 |
+
``takes_self=True``.
|
| 89 |
+
|
| 90 |
+
.. versionadded:: 18.2.0
|
| 91 |
+
"""
|
| 92 |
+
if default is NOTHING and factory is None:
|
| 93 |
+
msg = "Must pass either `default` or `factory`."
|
| 94 |
+
raise TypeError(msg)
|
| 95 |
+
|
| 96 |
+
if default is not NOTHING and factory is not None:
|
| 97 |
+
msg = "Must pass either `default` or `factory` but not both."
|
| 98 |
+
raise TypeError(msg)
|
| 99 |
+
|
| 100 |
+
if factory is not None:
|
| 101 |
+
default = Factory(factory)
|
| 102 |
+
|
| 103 |
+
if isinstance(default, Factory):
|
| 104 |
+
if default.takes_self:
|
| 105 |
+
msg = "`takes_self` is not supported by default_if_none."
|
| 106 |
+
raise ValueError(msg)
|
| 107 |
+
|
| 108 |
+
def default_if_none_converter(val):
|
| 109 |
+
if val is not None:
|
| 110 |
+
return val
|
| 111 |
+
|
| 112 |
+
return default.factory()
|
| 113 |
+
|
| 114 |
+
else:
|
| 115 |
+
|
| 116 |
+
def default_if_none_converter(val):
|
| 117 |
+
if val is not None:
|
| 118 |
+
return val
|
| 119 |
+
|
| 120 |
+
return default
|
| 121 |
+
|
| 122 |
+
return default_if_none_converter
|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
def to_bool(val):
|
| 126 |
+
"""
|
| 127 |
+
Convert "boolean" strings (for example, from environment variables) to real
|
| 128 |
+
booleans.
|
| 129 |
+
|
| 130 |
+
Values mapping to `True`:
|
| 131 |
+
|
| 132 |
+
- ``True``
|
| 133 |
+
- ``"true"`` / ``"t"``
|
| 134 |
+
- ``"yes"`` / ``"y"``
|
| 135 |
+
- ``"on"``
|
| 136 |
+
- ``"1"``
|
| 137 |
+
- ``1``
|
| 138 |
+
|
| 139 |
+
Values mapping to `False`:
|
| 140 |
+
|
| 141 |
+
- ``False``
|
| 142 |
+
- ``"false"`` / ``"f"``
|
| 143 |
+
- ``"no"`` / ``"n"``
|
| 144 |
+
- ``"off"``
|
| 145 |
+
- ``"0"``
|
| 146 |
+
- ``0``
|
| 147 |
+
|
| 148 |
+
Raises:
|
| 149 |
+
ValueError: For any other value.
|
| 150 |
+
|
| 151 |
+
.. versionadded:: 21.3.0
|
| 152 |
+
"""
|
| 153 |
+
if isinstance(val, str):
|
| 154 |
+
val = val.lower()
|
| 155 |
+
|
| 156 |
+
if val in (True, "true", "t", "yes", "y", "on", "1", 1):
|
| 157 |
+
return True
|
| 158 |
+
if val in (False, "false", "f", "no", "n", "off", "0", 0):
|
| 159 |
+
return False
|
| 160 |
+
|
| 161 |
+
msg = f"Cannot convert value to bool: {val!r}"
|
| 162 |
+
raise ValueError(msg)
|
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attr/converters.pyi
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Callable, Any, overload
|
| 2 |
+
|
| 3 |
+
from attrs import _ConverterType, _CallableConverterType
|
| 4 |
+
|
| 5 |
+
@overload
|
| 6 |
+
def pipe(*validators: _CallableConverterType) -> _CallableConverterType: ...
|
| 7 |
+
@overload
|
| 8 |
+
def pipe(*validators: _ConverterType) -> _ConverterType: ...
|
| 9 |
+
@overload
|
| 10 |
+
def optional(converter: _CallableConverterType) -> _CallableConverterType: ...
|
| 11 |
+
@overload
|
| 12 |
+
def optional(converter: _ConverterType) -> _ConverterType: ...
|
| 13 |
+
@overload
|
| 14 |
+
def default_if_none(default: Any) -> _CallableConverterType: ...
|
| 15 |
+
@overload
|
| 16 |
+
def default_if_none(
|
| 17 |
+
*, factory: Callable[[], Any]
|
| 18 |
+
) -> _CallableConverterType: ...
|
| 19 |
+
def to_bool(val: str | int | bool) -> bool: ...
|
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attr/exceptions.py
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: MIT
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
from typing import ClassVar
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
class FrozenError(AttributeError):
|
| 9 |
+
"""
|
| 10 |
+
A frozen/immutable instance or attribute have been attempted to be
|
| 11 |
+
modified.
|
| 12 |
+
|
| 13 |
+
It mirrors the behavior of ``namedtuples`` by using the same error message
|
| 14 |
+
and subclassing `AttributeError`.
|
| 15 |
+
|
| 16 |
+
.. versionadded:: 20.1.0
|
| 17 |
+
"""
|
| 18 |
+
|
| 19 |
+
msg = "can't set attribute"
|
| 20 |
+
args: ClassVar[tuple[str]] = [msg]
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
class FrozenInstanceError(FrozenError):
|
| 24 |
+
"""
|
| 25 |
+
A frozen instance has been attempted to be modified.
|
| 26 |
+
|
| 27 |
+
.. versionadded:: 16.1.0
|
| 28 |
+
"""
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
class FrozenAttributeError(FrozenError):
|
| 32 |
+
"""
|
| 33 |
+
A frozen attribute has been attempted to be modified.
|
| 34 |
+
|
| 35 |
+
.. versionadded:: 20.1.0
|
| 36 |
+
"""
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
class AttrsAttributeNotFoundError(ValueError):
|
| 40 |
+
"""
|
| 41 |
+
An *attrs* function couldn't find an attribute that the user asked for.
|
| 42 |
+
|
| 43 |
+
.. versionadded:: 16.2.0
|
| 44 |
+
"""
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
class NotAnAttrsClassError(ValueError):
|
| 48 |
+
"""
|
| 49 |
+
A non-*attrs* class has been passed into an *attrs* function.
|
| 50 |
+
|
| 51 |
+
.. versionadded:: 16.2.0
|
| 52 |
+
"""
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
class DefaultAlreadySetError(RuntimeError):
|
| 56 |
+
"""
|
| 57 |
+
A default has been set when defining the field and is attempted to be reset
|
| 58 |
+
using the decorator.
|
| 59 |
+
|
| 60 |
+
.. versionadded:: 17.1.0
|
| 61 |
+
"""
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
class UnannotatedAttributeError(RuntimeError):
|
| 65 |
+
"""
|
| 66 |
+
A class with ``auto_attribs=True`` has a field without a type annotation.
|
| 67 |
+
|
| 68 |
+
.. versionadded:: 17.3.0
|
| 69 |
+
"""
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
class PythonTooOldError(RuntimeError):
|
| 73 |
+
"""
|
| 74 |
+
It was attempted to use an *attrs* feature that requires a newer Python
|
| 75 |
+
version.
|
| 76 |
+
|
| 77 |
+
.. versionadded:: 18.2.0
|
| 78 |
+
"""
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
class NotCallableError(TypeError):
|
| 82 |
+
"""
|
| 83 |
+
A field requiring a callable has been set with a value that is not
|
| 84 |
+
callable.
|
| 85 |
+
|
| 86 |
+
.. versionadded:: 19.2.0
|
| 87 |
+
"""
|
| 88 |
+
|
| 89 |
+
def __init__(self, msg, value):
|
| 90 |
+
super(TypeError, self).__init__(msg, value)
|
| 91 |
+
self.msg = msg
|
| 92 |
+
self.value = value
|
| 93 |
+
|
| 94 |
+
def __str__(self):
|
| 95 |
+
return str(self.msg)
|
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attr/filters.py
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: MIT
|
| 2 |
+
|
| 3 |
+
"""
|
| 4 |
+
Commonly useful filters for `attrs.asdict` and `attrs.astuple`.
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
from ._make import Attribute
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
def _split_what(what):
|
| 11 |
+
"""
|
| 12 |
+
Returns a tuple of `frozenset`s of classes and attributes.
|
| 13 |
+
"""
|
| 14 |
+
return (
|
| 15 |
+
frozenset(cls for cls in what if isinstance(cls, type)),
|
| 16 |
+
frozenset(cls for cls in what if isinstance(cls, str)),
|
| 17 |
+
frozenset(cls for cls in what if isinstance(cls, Attribute)),
|
| 18 |
+
)
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def include(*what):
|
| 22 |
+
"""
|
| 23 |
+
Create a filter that only allows *what*.
|
| 24 |
+
|
| 25 |
+
Args:
|
| 26 |
+
what (list[type, str, attrs.Attribute]):
|
| 27 |
+
What to include. Can be a type, a name, or an attribute.
|
| 28 |
+
|
| 29 |
+
Returns:
|
| 30 |
+
Callable:
|
| 31 |
+
A callable that can be passed to `attrs.asdict`'s and
|
| 32 |
+
`attrs.astuple`'s *filter* argument.
|
| 33 |
+
|
| 34 |
+
.. versionchanged:: 23.1.0 Accept strings with field names.
|
| 35 |
+
"""
|
| 36 |
+
cls, names, attrs = _split_what(what)
|
| 37 |
+
|
| 38 |
+
def include_(attribute, value):
|
| 39 |
+
return (
|
| 40 |
+
value.__class__ in cls
|
| 41 |
+
or attribute.name in names
|
| 42 |
+
or attribute in attrs
|
| 43 |
+
)
|
| 44 |
+
|
| 45 |
+
return include_
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
def exclude(*what):
|
| 49 |
+
"""
|
| 50 |
+
Create a filter that does **not** allow *what*.
|
| 51 |
+
|
| 52 |
+
Args:
|
| 53 |
+
what (list[type, str, attrs.Attribute]):
|
| 54 |
+
What to exclude. Can be a type, a name, or an attribute.
|
| 55 |
+
|
| 56 |
+
Returns:
|
| 57 |
+
Callable:
|
| 58 |
+
A callable that can be passed to `attrs.asdict`'s and
|
| 59 |
+
`attrs.astuple`'s *filter* argument.
|
| 60 |
+
|
| 61 |
+
.. versionchanged:: 23.3.0 Accept field name string as input argument
|
| 62 |
+
"""
|
| 63 |
+
cls, names, attrs = _split_what(what)
|
| 64 |
+
|
| 65 |
+
def exclude_(attribute, value):
|
| 66 |
+
return not (
|
| 67 |
+
value.__class__ in cls
|
| 68 |
+
or attribute.name in names
|
| 69 |
+
or attribute in attrs
|
| 70 |
+
)
|
| 71 |
+
|
| 72 |
+
return exclude_
|
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attr/filters.pyi
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Any
|
| 2 |
+
|
| 3 |
+
from . import Attribute, _FilterType
|
| 4 |
+
|
| 5 |
+
def include(*what: type | str | Attribute[Any]) -> _FilterType[Any]: ...
|
| 6 |
+
def exclude(*what: type | str | Attribute[Any]) -> _FilterType[Any]: ...
|
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attr/py.typed
ADDED
|
File without changes
|
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attr/setters.pyi
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Any, NewType, NoReturn, TypeVar
|
| 2 |
+
|
| 3 |
+
from . import Attribute
|
| 4 |
+
from attrs import _OnSetAttrType
|
| 5 |
+
|
| 6 |
+
_T = TypeVar("_T")
|
| 7 |
+
|
| 8 |
+
def frozen(
|
| 9 |
+
instance: Any, attribute: Attribute[Any], new_value: Any
|
| 10 |
+
) -> NoReturn: ...
|
| 11 |
+
def pipe(*setters: _OnSetAttrType) -> _OnSetAttrType: ...
|
| 12 |
+
def validate(instance: Any, attribute: Attribute[_T], new_value: _T) -> _T: ...
|
| 13 |
+
|
| 14 |
+
# convert is allowed to return Any, because they can be chained using pipe.
|
| 15 |
+
def convert(
|
| 16 |
+
instance: Any, attribute: Attribute[Any], new_value: Any
|
| 17 |
+
) -> Any: ...
|
| 18 |
+
|
| 19 |
+
_NoOpType = NewType("_NoOpType", object)
|
| 20 |
+
NO_OP: _NoOpType
|
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attr/validators.py
ADDED
|
@@ -0,0 +1,710 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: MIT
|
| 2 |
+
|
| 3 |
+
"""
|
| 4 |
+
Commonly useful validators.
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
import operator
|
| 8 |
+
import re
|
| 9 |
+
|
| 10 |
+
from contextlib import contextmanager
|
| 11 |
+
from re import Pattern
|
| 12 |
+
|
| 13 |
+
from ._config import get_run_validators, set_run_validators
|
| 14 |
+
from ._make import _AndValidator, and_, attrib, attrs
|
| 15 |
+
from .converters import default_if_none
|
| 16 |
+
from .exceptions import NotCallableError
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
__all__ = [
|
| 20 |
+
"and_",
|
| 21 |
+
"deep_iterable",
|
| 22 |
+
"deep_mapping",
|
| 23 |
+
"disabled",
|
| 24 |
+
"ge",
|
| 25 |
+
"get_disabled",
|
| 26 |
+
"gt",
|
| 27 |
+
"in_",
|
| 28 |
+
"instance_of",
|
| 29 |
+
"is_callable",
|
| 30 |
+
"le",
|
| 31 |
+
"lt",
|
| 32 |
+
"matches_re",
|
| 33 |
+
"max_len",
|
| 34 |
+
"min_len",
|
| 35 |
+
"not_",
|
| 36 |
+
"optional",
|
| 37 |
+
"or_",
|
| 38 |
+
"set_disabled",
|
| 39 |
+
]
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
def set_disabled(disabled):
|
| 43 |
+
"""
|
| 44 |
+
Globally disable or enable running validators.
|
| 45 |
+
|
| 46 |
+
By default, they are run.
|
| 47 |
+
|
| 48 |
+
Args:
|
| 49 |
+
disabled (bool): If `True`, disable running all validators.
|
| 50 |
+
|
| 51 |
+
.. warning::
|
| 52 |
+
|
| 53 |
+
This function is not thread-safe!
|
| 54 |
+
|
| 55 |
+
.. versionadded:: 21.3.0
|
| 56 |
+
"""
|
| 57 |
+
set_run_validators(not disabled)
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
def get_disabled():
|
| 61 |
+
"""
|
| 62 |
+
Return a bool indicating whether validators are currently disabled or not.
|
| 63 |
+
|
| 64 |
+
Returns:
|
| 65 |
+
bool:`True` if validators are currently disabled.
|
| 66 |
+
|
| 67 |
+
.. versionadded:: 21.3.0
|
| 68 |
+
"""
|
| 69 |
+
return not get_run_validators()
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
@contextmanager
|
| 73 |
+
def disabled():
|
| 74 |
+
"""
|
| 75 |
+
Context manager that disables running validators within its context.
|
| 76 |
+
|
| 77 |
+
.. warning::
|
| 78 |
+
|
| 79 |
+
This context manager is not thread-safe!
|
| 80 |
+
|
| 81 |
+
.. versionadded:: 21.3.0
|
| 82 |
+
"""
|
| 83 |
+
set_run_validators(False)
|
| 84 |
+
try:
|
| 85 |
+
yield
|
| 86 |
+
finally:
|
| 87 |
+
set_run_validators(True)
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
@attrs(repr=False, slots=True, unsafe_hash=True)
|
| 91 |
+
class _InstanceOfValidator:
|
| 92 |
+
type = attrib()
|
| 93 |
+
|
| 94 |
+
def __call__(self, inst, attr, value):
|
| 95 |
+
"""
|
| 96 |
+
We use a callable class to be able to change the ``__repr__``.
|
| 97 |
+
"""
|
| 98 |
+
if not isinstance(value, self.type):
|
| 99 |
+
msg = f"'{attr.name}' must be {self.type!r} (got {value!r} that is a {value.__class__!r})."
|
| 100 |
+
raise TypeError(
|
| 101 |
+
msg,
|
| 102 |
+
attr,
|
| 103 |
+
self.type,
|
| 104 |
+
value,
|
| 105 |
+
)
|
| 106 |
+
|
| 107 |
+
def __repr__(self):
|
| 108 |
+
return f"<instance_of validator for type {self.type!r}>"
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
def instance_of(type):
|
| 112 |
+
"""
|
| 113 |
+
A validator that raises a `TypeError` if the initializer is called with a
|
| 114 |
+
wrong type for this particular attribute (checks are performed using
|
| 115 |
+
`isinstance` therefore it's also valid to pass a tuple of types).
|
| 116 |
+
|
| 117 |
+
Args:
|
| 118 |
+
type (type | tuple[type]): The type to check for.
|
| 119 |
+
|
| 120 |
+
Raises:
|
| 121 |
+
TypeError:
|
| 122 |
+
With a human readable error message, the attribute (of type
|
| 123 |
+
`attrs.Attribute`), the expected type, and the value it got.
|
| 124 |
+
"""
|
| 125 |
+
return _InstanceOfValidator(type)
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
@attrs(repr=False, frozen=True, slots=True)
|
| 129 |
+
class _MatchesReValidator:
|
| 130 |
+
pattern = attrib()
|
| 131 |
+
match_func = attrib()
|
| 132 |
+
|
| 133 |
+
def __call__(self, inst, attr, value):
|
| 134 |
+
"""
|
| 135 |
+
We use a callable class to be able to change the ``__repr__``.
|
| 136 |
+
"""
|
| 137 |
+
if not self.match_func(value):
|
| 138 |
+
msg = f"'{attr.name}' must match regex {self.pattern.pattern!r} ({value!r} doesn't)"
|
| 139 |
+
raise ValueError(
|
| 140 |
+
msg,
|
| 141 |
+
attr,
|
| 142 |
+
self.pattern,
|
| 143 |
+
value,
|
| 144 |
+
)
|
| 145 |
+
|
| 146 |
+
def __repr__(self):
|
| 147 |
+
return f"<matches_re validator for pattern {self.pattern!r}>"
|
| 148 |
+
|
| 149 |
+
|
| 150 |
+
def matches_re(regex, flags=0, func=None):
|
| 151 |
+
r"""
|
| 152 |
+
A validator that raises `ValueError` if the initializer is called with a
|
| 153 |
+
string that doesn't match *regex*.
|
| 154 |
+
|
| 155 |
+
Args:
|
| 156 |
+
regex (str, re.Pattern):
|
| 157 |
+
A regex string or precompiled pattern to match against
|
| 158 |
+
|
| 159 |
+
flags (int):
|
| 160 |
+
Flags that will be passed to the underlying re function (default 0)
|
| 161 |
+
|
| 162 |
+
func (typing.Callable):
|
| 163 |
+
Which underlying `re` function to call. Valid options are
|
| 164 |
+
`re.fullmatch`, `re.search`, and `re.match`; the default `None`
|
| 165 |
+
means `re.fullmatch`. For performance reasons, the pattern is
|
| 166 |
+
always precompiled using `re.compile`.
|
| 167 |
+
|
| 168 |
+
.. versionadded:: 19.2.0
|
| 169 |
+
.. versionchanged:: 21.3.0 *regex* can be a pre-compiled pattern.
|
| 170 |
+
"""
|
| 171 |
+
valid_funcs = (re.fullmatch, None, re.search, re.match)
|
| 172 |
+
if func not in valid_funcs:
|
| 173 |
+
msg = "'func' must be one of {}.".format(
|
| 174 |
+
", ".join(
|
| 175 |
+
sorted((e and e.__name__) or "None" for e in set(valid_funcs))
|
| 176 |
+
)
|
| 177 |
+
)
|
| 178 |
+
raise ValueError(msg)
|
| 179 |
+
|
| 180 |
+
if isinstance(regex, Pattern):
|
| 181 |
+
if flags:
|
| 182 |
+
msg = "'flags' can only be used with a string pattern; pass flags to re.compile() instead"
|
| 183 |
+
raise TypeError(msg)
|
| 184 |
+
pattern = regex
|
| 185 |
+
else:
|
| 186 |
+
pattern = re.compile(regex, flags)
|
| 187 |
+
|
| 188 |
+
if func is re.match:
|
| 189 |
+
match_func = pattern.match
|
| 190 |
+
elif func is re.search:
|
| 191 |
+
match_func = pattern.search
|
| 192 |
+
else:
|
| 193 |
+
match_func = pattern.fullmatch
|
| 194 |
+
|
| 195 |
+
return _MatchesReValidator(pattern, match_func)
|
| 196 |
+
|
| 197 |
+
|
| 198 |
+
@attrs(repr=False, slots=True, unsafe_hash=True)
|
| 199 |
+
class _OptionalValidator:
|
| 200 |
+
validator = attrib()
|
| 201 |
+
|
| 202 |
+
def __call__(self, inst, attr, value):
|
| 203 |
+
if value is None:
|
| 204 |
+
return
|
| 205 |
+
|
| 206 |
+
self.validator(inst, attr, value)
|
| 207 |
+
|
| 208 |
+
def __repr__(self):
|
| 209 |
+
return f"<optional validator for {self.validator!r} or None>"
|
| 210 |
+
|
| 211 |
+
|
| 212 |
+
def optional(validator):
|
| 213 |
+
"""
|
| 214 |
+
A validator that makes an attribute optional. An optional attribute is one
|
| 215 |
+
which can be set to `None` in addition to satisfying the requirements of
|
| 216 |
+
the sub-validator.
|
| 217 |
+
|
| 218 |
+
Args:
|
| 219 |
+
validator
|
| 220 |
+
(typing.Callable | tuple[typing.Callable] | list[typing.Callable]):
|
| 221 |
+
A validator (or validators) that is used for non-`None` values.
|
| 222 |
+
|
| 223 |
+
.. versionadded:: 15.1.0
|
| 224 |
+
.. versionchanged:: 17.1.0 *validator* can be a list of validators.
|
| 225 |
+
.. versionchanged:: 23.1.0 *validator* can also be a tuple of validators.
|
| 226 |
+
"""
|
| 227 |
+
if isinstance(validator, (list, tuple)):
|
| 228 |
+
return _OptionalValidator(_AndValidator(validator))
|
| 229 |
+
|
| 230 |
+
return _OptionalValidator(validator)
|
| 231 |
+
|
| 232 |
+
|
| 233 |
+
@attrs(repr=False, slots=True, unsafe_hash=True)
|
| 234 |
+
class _InValidator:
|
| 235 |
+
options = attrib()
|
| 236 |
+
_original_options = attrib(hash=False)
|
| 237 |
+
|
| 238 |
+
def __call__(self, inst, attr, value):
|
| 239 |
+
try:
|
| 240 |
+
in_options = value in self.options
|
| 241 |
+
except TypeError: # e.g. `1 in "abc"`
|
| 242 |
+
in_options = False
|
| 243 |
+
|
| 244 |
+
if not in_options:
|
| 245 |
+
msg = f"'{attr.name}' must be in {self._original_options!r} (got {value!r})"
|
| 246 |
+
raise ValueError(
|
| 247 |
+
msg,
|
| 248 |
+
attr,
|
| 249 |
+
self._original_options,
|
| 250 |
+
value,
|
| 251 |
+
)
|
| 252 |
+
|
| 253 |
+
def __repr__(self):
|
| 254 |
+
return f"<in_ validator with options {self._original_options!r}>"
|
| 255 |
+
|
| 256 |
+
|
| 257 |
+
def in_(options):
|
| 258 |
+
"""
|
| 259 |
+
A validator that raises a `ValueError` if the initializer is called with a
|
| 260 |
+
value that does not belong in the *options* provided.
|
| 261 |
+
|
| 262 |
+
The check is performed using ``value in options``, so *options* has to
|
| 263 |
+
support that operation.
|
| 264 |
+
|
| 265 |
+
To keep the validator hashable, dicts, lists, and sets are transparently
|
| 266 |
+
transformed into a `tuple`.
|
| 267 |
+
|
| 268 |
+
Args:
|
| 269 |
+
options: Allowed options.
|
| 270 |
+
|
| 271 |
+
Raises:
|
| 272 |
+
ValueError:
|
| 273 |
+
With a human readable error message, the attribute (of type
|
| 274 |
+
`attrs.Attribute`), the expected options, and the value it got.
|
| 275 |
+
|
| 276 |
+
.. versionadded:: 17.1.0
|
| 277 |
+
.. versionchanged:: 22.1.0
|
| 278 |
+
The ValueError was incomplete until now and only contained the human
|
| 279 |
+
readable error message. Now it contains all the information that has
|
| 280 |
+
been promised since 17.1.0.
|
| 281 |
+
.. versionchanged:: 24.1.0
|
| 282 |
+
*options* that are a list, dict, or a set are now transformed into a
|
| 283 |
+
tuple to keep the validator hashable.
|
| 284 |
+
"""
|
| 285 |
+
repr_options = options
|
| 286 |
+
if isinstance(options, (list, dict, set)):
|
| 287 |
+
options = tuple(options)
|
| 288 |
+
|
| 289 |
+
return _InValidator(options, repr_options)
|
| 290 |
+
|
| 291 |
+
|
| 292 |
+
@attrs(repr=False, slots=False, unsafe_hash=True)
|
| 293 |
+
class _IsCallableValidator:
|
| 294 |
+
def __call__(self, inst, attr, value):
|
| 295 |
+
"""
|
| 296 |
+
We use a callable class to be able to change the ``__repr__``.
|
| 297 |
+
"""
|
| 298 |
+
if not callable(value):
|
| 299 |
+
message = (
|
| 300 |
+
"'{name}' must be callable "
|
| 301 |
+
"(got {value!r} that is a {actual!r})."
|
| 302 |
+
)
|
| 303 |
+
raise NotCallableError(
|
| 304 |
+
msg=message.format(
|
| 305 |
+
name=attr.name, value=value, actual=value.__class__
|
| 306 |
+
),
|
| 307 |
+
value=value,
|
| 308 |
+
)
|
| 309 |
+
|
| 310 |
+
def __repr__(self):
|
| 311 |
+
return "<is_callable validator>"
|
| 312 |
+
|
| 313 |
+
|
| 314 |
+
def is_callable():
|
| 315 |
+
"""
|
| 316 |
+
A validator that raises a `attrs.exceptions.NotCallableError` if the
|
| 317 |
+
initializer is called with a value for this particular attribute that is
|
| 318 |
+
not callable.
|
| 319 |
+
|
| 320 |
+
.. versionadded:: 19.1.0
|
| 321 |
+
|
| 322 |
+
Raises:
|
| 323 |
+
attrs.exceptions.NotCallableError:
|
| 324 |
+
With a human readable error message containing the attribute
|
| 325 |
+
(`attrs.Attribute`) name, and the value it got.
|
| 326 |
+
"""
|
| 327 |
+
return _IsCallableValidator()
|
| 328 |
+
|
| 329 |
+
|
| 330 |
+
@attrs(repr=False, slots=True, unsafe_hash=True)
|
| 331 |
+
class _DeepIterable:
|
| 332 |
+
member_validator = attrib(validator=is_callable())
|
| 333 |
+
iterable_validator = attrib(
|
| 334 |
+
default=None, validator=optional(is_callable())
|
| 335 |
+
)
|
| 336 |
+
|
| 337 |
+
def __call__(self, inst, attr, value):
|
| 338 |
+
"""
|
| 339 |
+
We use a callable class to be able to change the ``__repr__``.
|
| 340 |
+
"""
|
| 341 |
+
if self.iterable_validator is not None:
|
| 342 |
+
self.iterable_validator(inst, attr, value)
|
| 343 |
+
|
| 344 |
+
for member in value:
|
| 345 |
+
self.member_validator(inst, attr, member)
|
| 346 |
+
|
| 347 |
+
def __repr__(self):
|
| 348 |
+
iterable_identifier = (
|
| 349 |
+
""
|
| 350 |
+
if self.iterable_validator is None
|
| 351 |
+
else f" {self.iterable_validator!r}"
|
| 352 |
+
)
|
| 353 |
+
return (
|
| 354 |
+
f"<deep_iterable validator for{iterable_identifier}"
|
| 355 |
+
f" iterables of {self.member_validator!r}>"
|
| 356 |
+
)
|
| 357 |
+
|
| 358 |
+
|
| 359 |
+
def deep_iterable(member_validator, iterable_validator=None):
|
| 360 |
+
"""
|
| 361 |
+
A validator that performs deep validation of an iterable.
|
| 362 |
+
|
| 363 |
+
Args:
|
| 364 |
+
member_validator: Validator to apply to iterable members.
|
| 365 |
+
|
| 366 |
+
iterable_validator:
|
| 367 |
+
Validator to apply to iterable itself (optional).
|
| 368 |
+
|
| 369 |
+
Raises
|
| 370 |
+
TypeError: if any sub-validators fail
|
| 371 |
+
|
| 372 |
+
.. versionadded:: 19.1.0
|
| 373 |
+
"""
|
| 374 |
+
if isinstance(member_validator, (list, tuple)):
|
| 375 |
+
member_validator = and_(*member_validator)
|
| 376 |
+
return _DeepIterable(member_validator, iterable_validator)
|
| 377 |
+
|
| 378 |
+
|
| 379 |
+
@attrs(repr=False, slots=True, unsafe_hash=True)
|
| 380 |
+
class _DeepMapping:
|
| 381 |
+
key_validator = attrib(validator=is_callable())
|
| 382 |
+
value_validator = attrib(validator=is_callable())
|
| 383 |
+
mapping_validator = attrib(default=None, validator=optional(is_callable()))
|
| 384 |
+
|
| 385 |
+
def __call__(self, inst, attr, value):
|
| 386 |
+
"""
|
| 387 |
+
We use a callable class to be able to change the ``__repr__``.
|
| 388 |
+
"""
|
| 389 |
+
if self.mapping_validator is not None:
|
| 390 |
+
self.mapping_validator(inst, attr, value)
|
| 391 |
+
|
| 392 |
+
for key in value:
|
| 393 |
+
self.key_validator(inst, attr, key)
|
| 394 |
+
self.value_validator(inst, attr, value[key])
|
| 395 |
+
|
| 396 |
+
def __repr__(self):
|
| 397 |
+
return f"<deep_mapping validator for objects mapping {self.key_validator!r} to {self.value_validator!r}>"
|
| 398 |
+
|
| 399 |
+
|
| 400 |
+
def deep_mapping(key_validator, value_validator, mapping_validator=None):
|
| 401 |
+
"""
|
| 402 |
+
A validator that performs deep validation of a dictionary.
|
| 403 |
+
|
| 404 |
+
Args:
|
| 405 |
+
key_validator: Validator to apply to dictionary keys.
|
| 406 |
+
|
| 407 |
+
value_validator: Validator to apply to dictionary values.
|
| 408 |
+
|
| 409 |
+
mapping_validator:
|
| 410 |
+
Validator to apply to top-level mapping attribute (optional).
|
| 411 |
+
|
| 412 |
+
.. versionadded:: 19.1.0
|
| 413 |
+
|
| 414 |
+
Raises:
|
| 415 |
+
TypeError: if any sub-validators fail
|
| 416 |
+
"""
|
| 417 |
+
return _DeepMapping(key_validator, value_validator, mapping_validator)
|
| 418 |
+
|
| 419 |
+
|
| 420 |
+
@attrs(repr=False, frozen=True, slots=True)
|
| 421 |
+
class _NumberValidator:
|
| 422 |
+
bound = attrib()
|
| 423 |
+
compare_op = attrib()
|
| 424 |
+
compare_func = attrib()
|
| 425 |
+
|
| 426 |
+
def __call__(self, inst, attr, value):
|
| 427 |
+
"""
|
| 428 |
+
We use a callable class to be able to change the ``__repr__``.
|
| 429 |
+
"""
|
| 430 |
+
if not self.compare_func(value, self.bound):
|
| 431 |
+
msg = f"'{attr.name}' must be {self.compare_op} {self.bound}: {value}"
|
| 432 |
+
raise ValueError(msg)
|
| 433 |
+
|
| 434 |
+
def __repr__(self):
|
| 435 |
+
return f"<Validator for x {self.compare_op} {self.bound}>"
|
| 436 |
+
|
| 437 |
+
|
| 438 |
+
def lt(val):
|
| 439 |
+
"""
|
| 440 |
+
A validator that raises `ValueError` if the initializer is called with a
|
| 441 |
+
number larger or equal to *val*.
|
| 442 |
+
|
| 443 |
+
The validator uses `operator.lt` to compare the values.
|
| 444 |
+
|
| 445 |
+
Args:
|
| 446 |
+
val: Exclusive upper bound for values.
|
| 447 |
+
|
| 448 |
+
.. versionadded:: 21.3.0
|
| 449 |
+
"""
|
| 450 |
+
return _NumberValidator(val, "<", operator.lt)
|
| 451 |
+
|
| 452 |
+
|
| 453 |
+
def le(val):
|
| 454 |
+
"""
|
| 455 |
+
A validator that raises `ValueError` if the initializer is called with a
|
| 456 |
+
number greater than *val*.
|
| 457 |
+
|
| 458 |
+
The validator uses `operator.le` to compare the values.
|
| 459 |
+
|
| 460 |
+
Args:
|
| 461 |
+
val: Inclusive upper bound for values.
|
| 462 |
+
|
| 463 |
+
.. versionadded:: 21.3.0
|
| 464 |
+
"""
|
| 465 |
+
return _NumberValidator(val, "<=", operator.le)
|
| 466 |
+
|
| 467 |
+
|
| 468 |
+
def ge(val):
|
| 469 |
+
"""
|
| 470 |
+
A validator that raises `ValueError` if the initializer is called with a
|
| 471 |
+
number smaller than *val*.
|
| 472 |
+
|
| 473 |
+
The validator uses `operator.ge` to compare the values.
|
| 474 |
+
|
| 475 |
+
Args:
|
| 476 |
+
val: Inclusive lower bound for values
|
| 477 |
+
|
| 478 |
+
.. versionadded:: 21.3.0
|
| 479 |
+
"""
|
| 480 |
+
return _NumberValidator(val, ">=", operator.ge)
|
| 481 |
+
|
| 482 |
+
|
| 483 |
+
def gt(val):
|
| 484 |
+
"""
|
| 485 |
+
A validator that raises `ValueError` if the initializer is called with a
|
| 486 |
+
number smaller or equal to *val*.
|
| 487 |
+
|
| 488 |
+
The validator uses `operator.ge` to compare the values.
|
| 489 |
+
|
| 490 |
+
Args:
|
| 491 |
+
val: Exclusive lower bound for values
|
| 492 |
+
|
| 493 |
+
.. versionadded:: 21.3.0
|
| 494 |
+
"""
|
| 495 |
+
return _NumberValidator(val, ">", operator.gt)
|
| 496 |
+
|
| 497 |
+
|
| 498 |
+
@attrs(repr=False, frozen=True, slots=True)
|
| 499 |
+
class _MaxLengthValidator:
|
| 500 |
+
max_length = attrib()
|
| 501 |
+
|
| 502 |
+
def __call__(self, inst, attr, value):
|
| 503 |
+
"""
|
| 504 |
+
We use a callable class to be able to change the ``__repr__``.
|
| 505 |
+
"""
|
| 506 |
+
if len(value) > self.max_length:
|
| 507 |
+
msg = f"Length of '{attr.name}' must be <= {self.max_length}: {len(value)}"
|
| 508 |
+
raise ValueError(msg)
|
| 509 |
+
|
| 510 |
+
def __repr__(self):
|
| 511 |
+
return f"<max_len validator for {self.max_length}>"
|
| 512 |
+
|
| 513 |
+
|
| 514 |
+
def max_len(length):
|
| 515 |
+
"""
|
| 516 |
+
A validator that raises `ValueError` if the initializer is called
|
| 517 |
+
with a string or iterable that is longer than *length*.
|
| 518 |
+
|
| 519 |
+
Args:
|
| 520 |
+
length (int): Maximum length of the string or iterable
|
| 521 |
+
|
| 522 |
+
.. versionadded:: 21.3.0
|
| 523 |
+
"""
|
| 524 |
+
return _MaxLengthValidator(length)
|
| 525 |
+
|
| 526 |
+
|
| 527 |
+
@attrs(repr=False, frozen=True, slots=True)
|
| 528 |
+
class _MinLengthValidator:
|
| 529 |
+
min_length = attrib()
|
| 530 |
+
|
| 531 |
+
def __call__(self, inst, attr, value):
|
| 532 |
+
"""
|
| 533 |
+
We use a callable class to be able to change the ``__repr__``.
|
| 534 |
+
"""
|
| 535 |
+
if len(value) < self.min_length:
|
| 536 |
+
msg = f"Length of '{attr.name}' must be >= {self.min_length}: {len(value)}"
|
| 537 |
+
raise ValueError(msg)
|
| 538 |
+
|
| 539 |
+
def __repr__(self):
|
| 540 |
+
return f"<min_len validator for {self.min_length}>"
|
| 541 |
+
|
| 542 |
+
|
| 543 |
+
def min_len(length):
|
| 544 |
+
"""
|
| 545 |
+
A validator that raises `ValueError` if the initializer is called
|
| 546 |
+
with a string or iterable that is shorter than *length*.
|
| 547 |
+
|
| 548 |
+
Args:
|
| 549 |
+
length (int): Minimum length of the string or iterable
|
| 550 |
+
|
| 551 |
+
.. versionadded:: 22.1.0
|
| 552 |
+
"""
|
| 553 |
+
return _MinLengthValidator(length)
|
| 554 |
+
|
| 555 |
+
|
| 556 |
+
@attrs(repr=False, slots=True, unsafe_hash=True)
|
| 557 |
+
class _SubclassOfValidator:
|
| 558 |
+
type = attrib()
|
| 559 |
+
|
| 560 |
+
def __call__(self, inst, attr, value):
|
| 561 |
+
"""
|
| 562 |
+
We use a callable class to be able to change the ``__repr__``.
|
| 563 |
+
"""
|
| 564 |
+
if not issubclass(value, self.type):
|
| 565 |
+
msg = f"'{attr.name}' must be a subclass of {self.type!r} (got {value!r})."
|
| 566 |
+
raise TypeError(
|
| 567 |
+
msg,
|
| 568 |
+
attr,
|
| 569 |
+
self.type,
|
| 570 |
+
value,
|
| 571 |
+
)
|
| 572 |
+
|
| 573 |
+
def __repr__(self):
|
| 574 |
+
return f"<subclass_of validator for type {self.type!r}>"
|
| 575 |
+
|
| 576 |
+
|
| 577 |
+
def _subclass_of(type):
|
| 578 |
+
"""
|
| 579 |
+
A validator that raises a `TypeError` if the initializer is called with a
|
| 580 |
+
wrong type for this particular attribute (checks are performed using
|
| 581 |
+
`issubclass` therefore it's also valid to pass a tuple of types).
|
| 582 |
+
|
| 583 |
+
Args:
|
| 584 |
+
type (type | tuple[type, ...]): The type(s) to check for.
|
| 585 |
+
|
| 586 |
+
Raises:
|
| 587 |
+
TypeError:
|
| 588 |
+
With a human readable error message, the attribute (of type
|
| 589 |
+
`attrs.Attribute`), the expected type, and the value it got.
|
| 590 |
+
"""
|
| 591 |
+
return _SubclassOfValidator(type)
|
| 592 |
+
|
| 593 |
+
|
| 594 |
+
@attrs(repr=False, slots=True, unsafe_hash=True)
|
| 595 |
+
class _NotValidator:
|
| 596 |
+
validator = attrib()
|
| 597 |
+
msg = attrib(
|
| 598 |
+
converter=default_if_none(
|
| 599 |
+
"not_ validator child '{validator!r}' "
|
| 600 |
+
"did not raise a captured error"
|
| 601 |
+
)
|
| 602 |
+
)
|
| 603 |
+
exc_types = attrib(
|
| 604 |
+
validator=deep_iterable(
|
| 605 |
+
member_validator=_subclass_of(Exception),
|
| 606 |
+
iterable_validator=instance_of(tuple),
|
| 607 |
+
),
|
| 608 |
+
)
|
| 609 |
+
|
| 610 |
+
def __call__(self, inst, attr, value):
|
| 611 |
+
try:
|
| 612 |
+
self.validator(inst, attr, value)
|
| 613 |
+
except self.exc_types:
|
| 614 |
+
pass # suppress error to invert validity
|
| 615 |
+
else:
|
| 616 |
+
raise ValueError(
|
| 617 |
+
self.msg.format(
|
| 618 |
+
validator=self.validator,
|
| 619 |
+
exc_types=self.exc_types,
|
| 620 |
+
),
|
| 621 |
+
attr,
|
| 622 |
+
self.validator,
|
| 623 |
+
value,
|
| 624 |
+
self.exc_types,
|
| 625 |
+
)
|
| 626 |
+
|
| 627 |
+
def __repr__(self):
|
| 628 |
+
return f"<not_ validator wrapping {self.validator!r}, capturing {self.exc_types!r}>"
|
| 629 |
+
|
| 630 |
+
|
| 631 |
+
def not_(validator, *, msg=None, exc_types=(ValueError, TypeError)):
|
| 632 |
+
"""
|
| 633 |
+
A validator that wraps and logically 'inverts' the validator passed to it.
|
| 634 |
+
It will raise a `ValueError` if the provided validator *doesn't* raise a
|
| 635 |
+
`ValueError` or `TypeError` (by default), and will suppress the exception
|
| 636 |
+
if the provided validator *does*.
|
| 637 |
+
|
| 638 |
+
Intended to be used with existing validators to compose logic without
|
| 639 |
+
needing to create inverted variants, for example, ``not_(in_(...))``.
|
| 640 |
+
|
| 641 |
+
Args:
|
| 642 |
+
validator: A validator to be logically inverted.
|
| 643 |
+
|
| 644 |
+
msg (str):
|
| 645 |
+
Message to raise if validator fails. Formatted with keys
|
| 646 |
+
``exc_types`` and ``validator``.
|
| 647 |
+
|
| 648 |
+
exc_types (tuple[type, ...]):
|
| 649 |
+
Exception type(s) to capture. Other types raised by child
|
| 650 |
+
validators will not be intercepted and pass through.
|
| 651 |
+
|
| 652 |
+
Raises:
|
| 653 |
+
ValueError:
|
| 654 |
+
With a human readable error message, the attribute (of type
|
| 655 |
+
`attrs.Attribute`), the validator that failed to raise an
|
| 656 |
+
exception, the value it got, and the expected exception types.
|
| 657 |
+
|
| 658 |
+
.. versionadded:: 22.2.0
|
| 659 |
+
"""
|
| 660 |
+
try:
|
| 661 |
+
exc_types = tuple(exc_types)
|
| 662 |
+
except TypeError:
|
| 663 |
+
exc_types = (exc_types,)
|
| 664 |
+
return _NotValidator(validator, msg, exc_types)
|
| 665 |
+
|
| 666 |
+
|
| 667 |
+
@attrs(repr=False, slots=True, unsafe_hash=True)
|
| 668 |
+
class _OrValidator:
|
| 669 |
+
validators = attrib()
|
| 670 |
+
|
| 671 |
+
def __call__(self, inst, attr, value):
|
| 672 |
+
for v in self.validators:
|
| 673 |
+
try:
|
| 674 |
+
v(inst, attr, value)
|
| 675 |
+
except Exception: # noqa: BLE001, PERF203, S112
|
| 676 |
+
continue
|
| 677 |
+
else:
|
| 678 |
+
return
|
| 679 |
+
|
| 680 |
+
msg = f"None of {self.validators!r} satisfied for value {value!r}"
|
| 681 |
+
raise ValueError(msg)
|
| 682 |
+
|
| 683 |
+
def __repr__(self):
|
| 684 |
+
return f"<or validator wrapping {self.validators!r}>"
|
| 685 |
+
|
| 686 |
+
|
| 687 |
+
def or_(*validators):
|
| 688 |
+
"""
|
| 689 |
+
A validator that composes multiple validators into one.
|
| 690 |
+
|
| 691 |
+
When called on a value, it runs all wrapped validators until one of them is
|
| 692 |
+
satisfied.
|
| 693 |
+
|
| 694 |
+
Args:
|
| 695 |
+
validators (~collections.abc.Iterable[typing.Callable]):
|
| 696 |
+
Arbitrary number of validators.
|
| 697 |
+
|
| 698 |
+
Raises:
|
| 699 |
+
ValueError:
|
| 700 |
+
If no validator is satisfied. Raised with a human-readable error
|
| 701 |
+
message listing all the wrapped validators and the value that
|
| 702 |
+
failed all of them.
|
| 703 |
+
|
| 704 |
+
.. versionadded:: 24.1.0
|
| 705 |
+
"""
|
| 706 |
+
vals = []
|
| 707 |
+
for v in validators:
|
| 708 |
+
vals.extend(v.validators if isinstance(v, _OrValidator) else [v])
|
| 709 |
+
|
| 710 |
+
return _OrValidator(tuple(vals))
|
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attr/validators.pyi
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from types import UnionType
|
| 2 |
+
from typing import (
|
| 3 |
+
Any,
|
| 4 |
+
AnyStr,
|
| 5 |
+
Callable,
|
| 6 |
+
Container,
|
| 7 |
+
ContextManager,
|
| 8 |
+
Iterable,
|
| 9 |
+
Mapping,
|
| 10 |
+
Match,
|
| 11 |
+
Pattern,
|
| 12 |
+
TypeVar,
|
| 13 |
+
overload,
|
| 14 |
+
)
|
| 15 |
+
|
| 16 |
+
from attrs import _ValidatorType
|
| 17 |
+
from attrs import _ValidatorArgType
|
| 18 |
+
|
| 19 |
+
_T = TypeVar("_T")
|
| 20 |
+
_T1 = TypeVar("_T1")
|
| 21 |
+
_T2 = TypeVar("_T2")
|
| 22 |
+
_T3 = TypeVar("_T3")
|
| 23 |
+
_I = TypeVar("_I", bound=Iterable)
|
| 24 |
+
_K = TypeVar("_K")
|
| 25 |
+
_V = TypeVar("_V")
|
| 26 |
+
_M = TypeVar("_M", bound=Mapping)
|
| 27 |
+
|
| 28 |
+
def set_disabled(run: bool) -> None: ...
|
| 29 |
+
def get_disabled() -> bool: ...
|
| 30 |
+
def disabled() -> ContextManager[None]: ...
|
| 31 |
+
|
| 32 |
+
# To be more precise on instance_of use some overloads.
|
| 33 |
+
# If there are more than 3 items in the tuple then we fall back to Any
|
| 34 |
+
@overload
|
| 35 |
+
def instance_of(type: type[_T]) -> _ValidatorType[_T]: ...
|
| 36 |
+
@overload
|
| 37 |
+
def instance_of(type: tuple[type[_T]]) -> _ValidatorType[_T]: ...
|
| 38 |
+
@overload
|
| 39 |
+
def instance_of(
|
| 40 |
+
type: tuple[type[_T1], type[_T2]],
|
| 41 |
+
) -> _ValidatorType[_T1 | _T2]: ...
|
| 42 |
+
@overload
|
| 43 |
+
def instance_of(
|
| 44 |
+
type: tuple[type[_T1], type[_T2], type[_T3]],
|
| 45 |
+
) -> _ValidatorType[_T1 | _T2 | _T3]: ...
|
| 46 |
+
@overload
|
| 47 |
+
def instance_of(type: tuple[type, ...]) -> _ValidatorType[Any]: ...
|
| 48 |
+
@overload
|
| 49 |
+
def instance_of(type: UnionType) -> _ValidatorType[Any]: ...
|
| 50 |
+
def optional(
|
| 51 |
+
validator: (
|
| 52 |
+
_ValidatorType[_T]
|
| 53 |
+
| list[_ValidatorType[_T]]
|
| 54 |
+
| tuple[_ValidatorType[_T]]
|
| 55 |
+
),
|
| 56 |
+
) -> _ValidatorType[_T | None]: ...
|
| 57 |
+
def in_(options: Container[_T]) -> _ValidatorType[_T]: ...
|
| 58 |
+
def and_(*validators: _ValidatorType[_T]) -> _ValidatorType[_T]: ...
|
| 59 |
+
def matches_re(
|
| 60 |
+
regex: Pattern[AnyStr] | AnyStr,
|
| 61 |
+
flags: int = ...,
|
| 62 |
+
func: Callable[[AnyStr, AnyStr, int], Match[AnyStr] | None] | None = ...,
|
| 63 |
+
) -> _ValidatorType[AnyStr]: ...
|
| 64 |
+
def deep_iterable(
|
| 65 |
+
member_validator: _ValidatorArgType[_T],
|
| 66 |
+
iterable_validator: _ValidatorType[_I] | None = ...,
|
| 67 |
+
) -> _ValidatorType[_I]: ...
|
| 68 |
+
def deep_mapping(
|
| 69 |
+
key_validator: _ValidatorType[_K],
|
| 70 |
+
value_validator: _ValidatorType[_V],
|
| 71 |
+
mapping_validator: _ValidatorType[_M] | None = ...,
|
| 72 |
+
) -> _ValidatorType[_M]: ...
|
| 73 |
+
def is_callable() -> _ValidatorType[_T]: ...
|
| 74 |
+
def lt(val: _T) -> _ValidatorType[_T]: ...
|
| 75 |
+
def le(val: _T) -> _ValidatorType[_T]: ...
|
| 76 |
+
def ge(val: _T) -> _ValidatorType[_T]: ...
|
| 77 |
+
def gt(val: _T) -> _ValidatorType[_T]: ...
|
| 78 |
+
def max_len(length: int) -> _ValidatorType[_T]: ...
|
| 79 |
+
def min_len(length: int) -> _ValidatorType[_T]: ...
|
| 80 |
+
def not_(
|
| 81 |
+
validator: _ValidatorType[_T],
|
| 82 |
+
*,
|
| 83 |
+
msg: str | None = None,
|
| 84 |
+
exc_types: type[Exception] | Iterable[type[Exception]] = ...,
|
| 85 |
+
) -> _ValidatorType[_T]: ...
|
| 86 |
+
def or_(*validators: _ValidatorType[_T]) -> _ValidatorType[_T]: ...
|
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attrs-25.1.0.dist-info/INSTALLER
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
pip
|
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attrs-25.1.0.dist-info/METADATA
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Metadata-Version: 2.4
|
| 2 |
+
Name: attrs
|
| 3 |
+
Version: 25.1.0
|
| 4 |
+
Summary: Classes Without Boilerplate
|
| 5 |
+
Project-URL: Documentation, https://www.attrs.org/
|
| 6 |
+
Project-URL: Changelog, https://www.attrs.org/en/stable/changelog.html
|
| 7 |
+
Project-URL: GitHub, https://github.com/python-attrs/attrs
|
| 8 |
+
Project-URL: Funding, https://github.com/sponsors/hynek
|
| 9 |
+
Project-URL: Tidelift, https://tidelift.com/subscription/pkg/pypi-attrs?utm_source=pypi-attrs&utm_medium=pypi
|
| 10 |
+
Author-email: Hynek Schlawack <hs@ox.cx>
|
| 11 |
+
License-Expression: MIT
|
| 12 |
+
License-File: LICENSE
|
| 13 |
+
Keywords: attribute,boilerplate,class
|
| 14 |
+
Classifier: Development Status :: 5 - Production/Stable
|
| 15 |
+
Classifier: Programming Language :: Python :: 3.8
|
| 16 |
+
Classifier: Programming Language :: Python :: 3.9
|
| 17 |
+
Classifier: Programming Language :: Python :: 3.10
|
| 18 |
+
Classifier: Programming Language :: Python :: 3.11
|
| 19 |
+
Classifier: Programming Language :: Python :: 3.12
|
| 20 |
+
Classifier: Programming Language :: Python :: 3.13
|
| 21 |
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
| 22 |
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
| 23 |
+
Classifier: Typing :: Typed
|
| 24 |
+
Requires-Python: >=3.8
|
| 25 |
+
Provides-Extra: benchmark
|
| 26 |
+
Requires-Dist: cloudpickle; (platform_python_implementation == 'CPython') and extra == 'benchmark'
|
| 27 |
+
Requires-Dist: hypothesis; extra == 'benchmark'
|
| 28 |
+
Requires-Dist: mypy>=1.11.1; (platform_python_implementation == 'CPython' and python_version >= '3.10') and extra == 'benchmark'
|
| 29 |
+
Requires-Dist: pympler; extra == 'benchmark'
|
| 30 |
+
Requires-Dist: pytest-codspeed; extra == 'benchmark'
|
| 31 |
+
Requires-Dist: pytest-mypy-plugins; (platform_python_implementation == 'CPython' and python_version >= '3.10') and extra == 'benchmark'
|
| 32 |
+
Requires-Dist: pytest-xdist[psutil]; extra == 'benchmark'
|
| 33 |
+
Requires-Dist: pytest>=4.3.0; extra == 'benchmark'
|
| 34 |
+
Provides-Extra: cov
|
| 35 |
+
Requires-Dist: cloudpickle; (platform_python_implementation == 'CPython') and extra == 'cov'
|
| 36 |
+
Requires-Dist: coverage[toml]>=5.3; extra == 'cov'
|
| 37 |
+
Requires-Dist: hypothesis; extra == 'cov'
|
| 38 |
+
Requires-Dist: mypy>=1.11.1; (platform_python_implementation == 'CPython' and python_version >= '3.10') and extra == 'cov'
|
| 39 |
+
Requires-Dist: pympler; extra == 'cov'
|
| 40 |
+
Requires-Dist: pytest-mypy-plugins; (platform_python_implementation == 'CPython' and python_version >= '3.10') and extra == 'cov'
|
| 41 |
+
Requires-Dist: pytest-xdist[psutil]; extra == 'cov'
|
| 42 |
+
Requires-Dist: pytest>=4.3.0; extra == 'cov'
|
| 43 |
+
Provides-Extra: dev
|
| 44 |
+
Requires-Dist: cloudpickle; (platform_python_implementation == 'CPython') and extra == 'dev'
|
| 45 |
+
Requires-Dist: hypothesis; extra == 'dev'
|
| 46 |
+
Requires-Dist: mypy>=1.11.1; (platform_python_implementation == 'CPython' and python_version >= '3.10') and extra == 'dev'
|
| 47 |
+
Requires-Dist: pre-commit-uv; extra == 'dev'
|
| 48 |
+
Requires-Dist: pympler; extra == 'dev'
|
| 49 |
+
Requires-Dist: pytest-mypy-plugins; (platform_python_implementation == 'CPython' and python_version >= '3.10') and extra == 'dev'
|
| 50 |
+
Requires-Dist: pytest-xdist[psutil]; extra == 'dev'
|
| 51 |
+
Requires-Dist: pytest>=4.3.0; extra == 'dev'
|
| 52 |
+
Provides-Extra: docs
|
| 53 |
+
Requires-Dist: cogapp; extra == 'docs'
|
| 54 |
+
Requires-Dist: furo; extra == 'docs'
|
| 55 |
+
Requires-Dist: myst-parser; extra == 'docs'
|
| 56 |
+
Requires-Dist: sphinx; extra == 'docs'
|
| 57 |
+
Requires-Dist: sphinx-notfound-page; extra == 'docs'
|
| 58 |
+
Requires-Dist: sphinxcontrib-towncrier; extra == 'docs'
|
| 59 |
+
Requires-Dist: towncrier<24.7; extra == 'docs'
|
| 60 |
+
Provides-Extra: tests
|
| 61 |
+
Requires-Dist: cloudpickle; (platform_python_implementation == 'CPython') and extra == 'tests'
|
| 62 |
+
Requires-Dist: hypothesis; extra == 'tests'
|
| 63 |
+
Requires-Dist: mypy>=1.11.1; (platform_python_implementation == 'CPython' and python_version >= '3.10') and extra == 'tests'
|
| 64 |
+
Requires-Dist: pympler; extra == 'tests'
|
| 65 |
+
Requires-Dist: pytest-mypy-plugins; (platform_python_implementation == 'CPython' and python_version >= '3.10') and extra == 'tests'
|
| 66 |
+
Requires-Dist: pytest-xdist[psutil]; extra == 'tests'
|
| 67 |
+
Requires-Dist: pytest>=4.3.0; extra == 'tests'
|
| 68 |
+
Provides-Extra: tests-mypy
|
| 69 |
+
Requires-Dist: mypy>=1.11.1; (platform_python_implementation == 'CPython' and python_version >= '3.10') and extra == 'tests-mypy'
|
| 70 |
+
Requires-Dist: pytest-mypy-plugins; (platform_python_implementation == 'CPython' and python_version >= '3.10') and extra == 'tests-mypy'
|
| 71 |
+
Description-Content-Type: text/markdown
|
| 72 |
+
|
| 73 |
+
<p align="center">
|
| 74 |
+
<a href="https://www.attrs.org/">
|
| 75 |
+
<img src="https://raw.githubusercontent.com/python-attrs/attrs/main/docs/_static/attrs_logo.svg" width="35%" alt="attrs" />
|
| 76 |
+
</a>
|
| 77 |
+
</p>
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
*attrs* is the Python package that will bring back the **joy** of **writing classes** by relieving you from the drudgery of implementing object protocols (aka [dunder methods](https://www.attrs.org/en/latest/glossary.html#term-dunder-methods)).
|
| 81 |
+
[Trusted by NASA](https://docs.github.com/en/account-and-profile/setting-up-and-managing-your-github-profile/customizing-your-profile/personalizing-your-profile#list-of-qualifying-repositories-for-mars-2020-helicopter-contributor-achievement) for Mars missions since 2020!
|
| 82 |
+
|
| 83 |
+
Its main goal is to help you to write **concise** and **correct** software without slowing down your code.
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
## Sponsors
|
| 87 |
+
|
| 88 |
+
*attrs* would not be possible without our [amazing sponsors](https://github.com/sponsors/hynek).
|
| 89 |
+
Especially those generously supporting us at the *The Organization* tier and higher:
|
| 90 |
+
|
| 91 |
+
<!-- sponsor-break-begin -->
|
| 92 |
+
|
| 93 |
+
<p align="center">
|
| 94 |
+
|
| 95 |
+
<!-- [[[cog
|
| 96 |
+
import pathlib, tomllib
|
| 97 |
+
|
| 98 |
+
for sponsor in tomllib.loads(pathlib.Path("pyproject.toml").read_text())["tool"]["sponcon"]["sponsors"]:
|
| 99 |
+
print(f'<a href="{sponsor["url"]}"><img title="{sponsor["title"]}" src="https://www.attrs.org/en/25.1.0/_static/sponsors/{sponsor["img"]}" width="190" /></a>')
|
| 100 |
+
]]] -->
|
| 101 |
+
<a href="https://www.variomedia.de/"><img title="Variomedia AG" src="https://www.attrs.org/en/25.1.0/_static/sponsors/Variomedia.svg" width="190" /></a>
|
| 102 |
+
<a href="https://tidelift.com/?utm_source=lifter&utm_medium=referral&utm_campaign=hynek"><img title="Tidelift" src="https://www.attrs.org/en/25.1.0/_static/sponsors/Tidelift.svg" width="190" /></a>
|
| 103 |
+
<a href="https://klaviyo.com/"><img title="Klaviyo" src="https://www.attrs.org/en/25.1.0/_static/sponsors/Klaviyo.svg" width="190" /></a>
|
| 104 |
+
<a href="https://www.emsys-renewables.com/"><img title="emsys renewables" src="https://www.attrs.org/en/25.1.0/_static/sponsors/emsys-renewables.svg" width="190" /></a>
|
| 105 |
+
<a href="https://filepreviews.io/"><img title="FilePreviews" src="https://www.attrs.org/en/25.1.0/_static/sponsors/FilePreviews.svg" width="190" /></a>
|
| 106 |
+
<a href="https://privacy-solutions.org/"><img title="Privacy Solutions" src="https://www.attrs.org/en/25.1.0/_static/sponsors/Privacy-Solutions.svg" width="190" /></a>
|
| 107 |
+
<a href="https://polar.sh/"><img title="Polar" src="https://www.attrs.org/en/25.1.0/_static/sponsors/Polar.svg" width="190" /></a>
|
| 108 |
+
<!-- [[[end]]] -->
|
| 109 |
+
|
| 110 |
+
</p>
|
| 111 |
+
|
| 112 |
+
<!-- sponsor-break-end -->
|
| 113 |
+
|
| 114 |
+
<p align="center">
|
| 115 |
+
<strong>Please consider <a href="https://github.com/sponsors/hynek">joining them</a> to help make <em>attrs</em>’s maintenance more sustainable!</strong>
|
| 116 |
+
</p>
|
| 117 |
+
|
| 118 |
+
<!-- teaser-end -->
|
| 119 |
+
|
| 120 |
+
## Example
|
| 121 |
+
|
| 122 |
+
*attrs* gives you a class decorator and a way to declaratively define the attributes on that class:
|
| 123 |
+
|
| 124 |
+
<!-- code-begin -->
|
| 125 |
+
|
| 126 |
+
```pycon
|
| 127 |
+
>>> from attrs import asdict, define, make_class, Factory
|
| 128 |
+
|
| 129 |
+
>>> @define
|
| 130 |
+
... class SomeClass:
|
| 131 |
+
... a_number: int = 42
|
| 132 |
+
... list_of_numbers: list[int] = Factory(list)
|
| 133 |
+
...
|
| 134 |
+
... def hard_math(self, another_number):
|
| 135 |
+
... return self.a_number + sum(self.list_of_numbers) * another_number
|
| 136 |
+
|
| 137 |
+
|
| 138 |
+
>>> sc = SomeClass(1, [1, 2, 3])
|
| 139 |
+
>>> sc
|
| 140 |
+
SomeClass(a_number=1, list_of_numbers=[1, 2, 3])
|
| 141 |
+
|
| 142 |
+
>>> sc.hard_math(3)
|
| 143 |
+
19
|
| 144 |
+
>>> sc == SomeClass(1, [1, 2, 3])
|
| 145 |
+
True
|
| 146 |
+
>>> sc != SomeClass(2, [3, 2, 1])
|
| 147 |
+
True
|
| 148 |
+
|
| 149 |
+
>>> asdict(sc)
|
| 150 |
+
{'a_number': 1, 'list_of_numbers': [1, 2, 3]}
|
| 151 |
+
|
| 152 |
+
>>> SomeClass()
|
| 153 |
+
SomeClass(a_number=42, list_of_numbers=[])
|
| 154 |
+
|
| 155 |
+
>>> C = make_class("C", ["a", "b"])
|
| 156 |
+
>>> C("foo", "bar")
|
| 157 |
+
C(a='foo', b='bar')
|
| 158 |
+
```
|
| 159 |
+
|
| 160 |
+
After *declaring* your attributes, *attrs* gives you:
|
| 161 |
+
|
| 162 |
+
- a concise and explicit overview of the class's attributes,
|
| 163 |
+
- a nice human-readable `__repr__`,
|
| 164 |
+
- equality-checking methods,
|
| 165 |
+
- an initializer,
|
| 166 |
+
- and much more,
|
| 167 |
+
|
| 168 |
+
*without* writing dull boilerplate code again and again and *without* runtime performance penalties.
|
| 169 |
+
|
| 170 |
+
---
|
| 171 |
+
|
| 172 |
+
This example uses *attrs*'s modern APIs that have been introduced in version 20.1.0, and the *attrs* package import name that has been added in version 21.3.0.
|
| 173 |
+
The classic APIs (`@attr.s`, `attr.ib`, plus their serious-business aliases) and the `attr` package import name will remain **indefinitely**.
|
| 174 |
+
|
| 175 |
+
Check out [*On The Core API Names*](https://www.attrs.org/en/latest/names.html) for an in-depth explanation!
|
| 176 |
+
|
| 177 |
+
|
| 178 |
+
### Hate Type Annotations!?
|
| 179 |
+
|
| 180 |
+
No problem!
|
| 181 |
+
Types are entirely **optional** with *attrs*.
|
| 182 |
+
Simply assign `attrs.field()` to the attributes instead of annotating them with types:
|
| 183 |
+
|
| 184 |
+
```python
|
| 185 |
+
from attrs import define, field
|
| 186 |
+
|
| 187 |
+
@define
|
| 188 |
+
class SomeClass:
|
| 189 |
+
a_number = field(default=42)
|
| 190 |
+
list_of_numbers = field(factory=list)
|
| 191 |
+
```
|
| 192 |
+
|
| 193 |
+
|
| 194 |
+
## Data Classes
|
| 195 |
+
|
| 196 |
+
On the tin, *attrs* might remind you of `dataclasses` (and indeed, `dataclasses` [are a descendant](https://hynek.me/articles/import-attrs/) of *attrs*).
|
| 197 |
+
In practice it does a lot more and is more flexible.
|
| 198 |
+
For instance, it allows you to define [special handling of NumPy arrays for equality checks](https://www.attrs.org/en/stable/comparison.html#customization), allows more ways to [plug into the initialization process](https://www.attrs.org/en/stable/init.html#hooking-yourself-into-initialization), has a replacement for `__init_subclass__`, and allows for stepping through the generated methods using a debugger.
|
| 199 |
+
|
| 200 |
+
For more details, please refer to our [comparison page](https://www.attrs.org/en/stable/why.html#data-classes), but generally speaking, we are more likely to commit crimes against nature to make things work that one would expect to work, but that are quite complicated in practice.
|
| 201 |
+
|
| 202 |
+
|
| 203 |
+
## Project Information
|
| 204 |
+
|
| 205 |
+
- [**Changelog**](https://www.attrs.org/en/stable/changelog.html)
|
| 206 |
+
- [**Documentation**](https://www.attrs.org/)
|
| 207 |
+
- [**PyPI**](https://pypi.org/project/attrs/)
|
| 208 |
+
- [**Source Code**](https://github.com/python-attrs/attrs)
|
| 209 |
+
- [**Contributing**](https://github.com/python-attrs/attrs/blob/main/.github/CONTRIBUTING.md)
|
| 210 |
+
- [**Third-party Extensions**](https://github.com/python-attrs/attrs/wiki/Extensions-to-attrs)
|
| 211 |
+
- **Get Help**: use the `python-attrs` tag on [Stack Overflow](https://stackoverflow.com/questions/tagged/python-attrs)
|
| 212 |
+
|
| 213 |
+
|
| 214 |
+
### *attrs* for Enterprise
|
| 215 |
+
|
| 216 |
+
Available as part of the [Tidelift Subscription](https://tidelift.com/?utm_source=lifter&utm_medium=referral&utm_campaign=hynek).
|
| 217 |
+
|
| 218 |
+
The maintainers of *attrs* and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source packages you use to build your applications.
|
| 219 |
+
Save time, reduce risk, and improve code health, while paying the maintainers of the exact packages you use.
|
| 220 |
+
|
| 221 |
+
## Release Information
|
| 222 |
+
|
| 223 |
+
### Changes
|
| 224 |
+
|
| 225 |
+
- This release only ensures correct PyPI licensing metadata.
|
| 226 |
+
[#1386](https://github.com/python-attrs/attrs/issues/1386)
|
| 227 |
+
|
| 228 |
+
|
| 229 |
+
|
| 230 |
+
---
|
| 231 |
+
|
| 232 |
+
[Full changelog →](https://www.attrs.org/en/stable/changelog.html)
|
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attrs-25.1.0.dist-info/RECORD
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
attr/__init__.py,sha256=fOYIvt1eGSqQre4uCS3sJWKZ0mwAuC8UD6qba5OS9_U,2057
|
| 2 |
+
attr/__init__.pyi,sha256=QIXnnHPoucmDWkbpNsWTP-cgJ1bn8le7DjyRa_wYdew,11281
|
| 3 |
+
attr/__pycache__/__init__.cpython-311.pyc,,
|
| 4 |
+
attr/__pycache__/_cmp.cpython-311.pyc,,
|
| 5 |
+
attr/__pycache__/_compat.cpython-311.pyc,,
|
| 6 |
+
attr/__pycache__/_config.cpython-311.pyc,,
|
| 7 |
+
attr/__pycache__/_funcs.cpython-311.pyc,,
|
| 8 |
+
attr/__pycache__/_make.cpython-311.pyc,,
|
| 9 |
+
attr/__pycache__/_next_gen.cpython-311.pyc,,
|
| 10 |
+
attr/__pycache__/_version_info.cpython-311.pyc,,
|
| 11 |
+
attr/__pycache__/converters.cpython-311.pyc,,
|
| 12 |
+
attr/__pycache__/exceptions.cpython-311.pyc,,
|
| 13 |
+
attr/__pycache__/filters.cpython-311.pyc,,
|
| 14 |
+
attr/__pycache__/setters.cpython-311.pyc,,
|
| 15 |
+
attr/__pycache__/validators.cpython-311.pyc,,
|
| 16 |
+
attr/_cmp.py,sha256=3umHiBtgsEYtvNP_8XrQwTCdFoZIX4DEur76N-2a3X8,4123
|
| 17 |
+
attr/_cmp.pyi,sha256=U-_RU_UZOyPUEQzXE6RMYQQcjkZRY25wTH99sN0s7MM,368
|
| 18 |
+
attr/_compat.py,sha256=4hlXbWhdDjQCDK6FKF1EgnZ3POiHgtpp54qE0nxaGHg,2704
|
| 19 |
+
attr/_config.py,sha256=dGq3xR6fgZEF6UBt_L0T-eUHIB4i43kRmH0P28sJVw8,843
|
| 20 |
+
attr/_funcs.py,sha256=5-tUKJtp3h5El55EcDl6GWXFp68fT8D8U7uCRN6497I,15854
|
| 21 |
+
attr/_make.py,sha256=XS_pYn_-KNo69Tb8-_y3YUcB3Xus00MwAShh2WulkjQ,94157
|
| 22 |
+
attr/_next_gen.py,sha256=7FRkbtl_N017SuBhf_Vw3mw2c2pGZhtCGOzadgz7tp4,24395
|
| 23 |
+
attr/_typing_compat.pyi,sha256=XDP54TUn-ZKhD62TOQebmzrwFyomhUCoGRpclb6alRA,469
|
| 24 |
+
attr/_version_info.py,sha256=exSqb3b5E-fMSsgZAlEw9XcLpEgobPORCZpcaEglAM4,2121
|
| 25 |
+
attr/_version_info.pyi,sha256=x_M3L3WuB7r_ULXAWjx959udKQ4HLB8l-hsc1FDGNvk,209
|
| 26 |
+
attr/converters.py,sha256=GlDeOzPeTFgeBBLbj9G57Ez5lAk68uhSALRYJ_exe84,3861
|
| 27 |
+
attr/converters.pyi,sha256=orU2bff-VjQa2kMDyvnMQV73oJT2WRyQuw4ZR1ym1bE,643
|
| 28 |
+
attr/exceptions.py,sha256=HRFq4iybmv7-DcZwyjl6M1euM2YeJVK_hFxuaBGAngI,1977
|
| 29 |
+
attr/exceptions.pyi,sha256=zZq8bCUnKAy9mDtBEw42ZhPhAUIHoTKedDQInJD883M,539
|
| 30 |
+
attr/filters.py,sha256=ZBiKWLp3R0LfCZsq7X11pn9WX8NslS2wXM4jsnLOGc8,1795
|
| 31 |
+
attr/filters.pyi,sha256=3J5BG-dTxltBk1_-RuNRUHrv2qu1v8v4aDNAQ7_mifA,208
|
| 32 |
+
attr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 33 |
+
attr/setters.py,sha256=5-dcT63GQK35ONEzSgfXCkbB7pPkaR-qv15mm4PVSzQ,1617
|
| 34 |
+
attr/setters.pyi,sha256=NnVkaFU1BB4JB8E4JuXyrzTUgvtMpj8p3wBdJY7uix4,584
|
| 35 |
+
attr/validators.py,sha256=WaB1HLAHHqRHWsrv_K9H-sJ7ESil3H3Cmv2d8TtVZx4,20046
|
| 36 |
+
attr/validators.pyi,sha256=s2WhKPqskxbsckJfKk8zOuuB088GfgpyxcCYSNFLqNU,2603
|
| 37 |
+
attrs-25.1.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
| 38 |
+
attrs-25.1.0.dist-info/METADATA,sha256=bZidcSPgoF4BvFNQYyqph4NeHVg9r55WXiwAEtbvRnc,10999
|
| 39 |
+
attrs-25.1.0.dist-info/RECORD,,
|
| 40 |
+
attrs-25.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
| 41 |
+
attrs-25.1.0.dist-info/licenses/LICENSE,sha256=iCEVyV38KvHutnFPjsbVy8q_Znyv-HKfQkINpj9xTp8,1109
|
| 42 |
+
attrs/__init__.py,sha256=qeQJZ4O08yczSn840v9bYOaZyRE81WsVi-QCrY3krCU,1107
|
| 43 |
+
attrs/__init__.pyi,sha256=nZmInocjM7tHV4AQw0vxO_fo6oJjL_PonlV9zKKW8DY,7931
|
| 44 |
+
attrs/__pycache__/__init__.cpython-311.pyc,,
|
| 45 |
+
attrs/__pycache__/converters.cpython-311.pyc,,
|
| 46 |
+
attrs/__pycache__/exceptions.cpython-311.pyc,,
|
| 47 |
+
attrs/__pycache__/filters.cpython-311.pyc,,
|
| 48 |
+
attrs/__pycache__/setters.cpython-311.pyc,,
|
| 49 |
+
attrs/__pycache__/validators.cpython-311.pyc,,
|
| 50 |
+
attrs/converters.py,sha256=8kQljrVwfSTRu8INwEk8SI0eGrzmWftsT7rM0EqyohM,76
|
| 51 |
+
attrs/exceptions.py,sha256=ACCCmg19-vDFaDPY9vFl199SPXCQMN_bENs4DALjzms,76
|
| 52 |
+
attrs/filters.py,sha256=VOUMZug9uEU6dUuA0dF1jInUK0PL3fLgP0VBS5d-CDE,73
|
| 53 |
+
attrs/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 54 |
+
attrs/setters.py,sha256=eL1YidYQV3T2h9_SYIZSZR1FAcHGb1TuCTy0E0Lv2SU,73
|
| 55 |
+
attrs/validators.py,sha256=xcy6wD5TtTkdCG1f4XWbocPSO0faBjk5IfVJfP6SUj0,76
|
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attrs-25.1.0.dist-info/WHEEL
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Wheel-Version: 1.0
|
| 2 |
+
Generator: hatchling 1.27.0
|
| 3 |
+
Root-Is-Purelib: true
|
| 4 |
+
Tag: py3-none-any
|
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attrs-25.1.0.dist-info/licenses/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
The MIT License (MIT)
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2015 Hynek Schlawack and the attrs contributors
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
in the Software without restriction, including without limitation the rights
|
| 8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
furnished to do so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in all
|
| 13 |
+
copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 21 |
+
SOFTWARE.
|
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attrs/__init__.py
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: MIT
|
| 2 |
+
|
| 3 |
+
from attr import (
|
| 4 |
+
NOTHING,
|
| 5 |
+
Attribute,
|
| 6 |
+
AttrsInstance,
|
| 7 |
+
Converter,
|
| 8 |
+
Factory,
|
| 9 |
+
NothingType,
|
| 10 |
+
_make_getattr,
|
| 11 |
+
assoc,
|
| 12 |
+
cmp_using,
|
| 13 |
+
define,
|
| 14 |
+
evolve,
|
| 15 |
+
field,
|
| 16 |
+
fields,
|
| 17 |
+
fields_dict,
|
| 18 |
+
frozen,
|
| 19 |
+
has,
|
| 20 |
+
make_class,
|
| 21 |
+
mutable,
|
| 22 |
+
resolve_types,
|
| 23 |
+
validate,
|
| 24 |
+
)
|
| 25 |
+
from attr._next_gen import asdict, astuple
|
| 26 |
+
|
| 27 |
+
from . import converters, exceptions, filters, setters, validators
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
__all__ = [
|
| 31 |
+
"NOTHING",
|
| 32 |
+
"Attribute",
|
| 33 |
+
"AttrsInstance",
|
| 34 |
+
"Converter",
|
| 35 |
+
"Factory",
|
| 36 |
+
"NothingType",
|
| 37 |
+
"__author__",
|
| 38 |
+
"__copyright__",
|
| 39 |
+
"__description__",
|
| 40 |
+
"__doc__",
|
| 41 |
+
"__email__",
|
| 42 |
+
"__license__",
|
| 43 |
+
"__title__",
|
| 44 |
+
"__url__",
|
| 45 |
+
"__version__",
|
| 46 |
+
"__version_info__",
|
| 47 |
+
"asdict",
|
| 48 |
+
"assoc",
|
| 49 |
+
"astuple",
|
| 50 |
+
"cmp_using",
|
| 51 |
+
"converters",
|
| 52 |
+
"define",
|
| 53 |
+
"evolve",
|
| 54 |
+
"exceptions",
|
| 55 |
+
"field",
|
| 56 |
+
"fields",
|
| 57 |
+
"fields_dict",
|
| 58 |
+
"filters",
|
| 59 |
+
"frozen",
|
| 60 |
+
"has",
|
| 61 |
+
"make_class",
|
| 62 |
+
"mutable",
|
| 63 |
+
"resolve_types",
|
| 64 |
+
"setters",
|
| 65 |
+
"validate",
|
| 66 |
+
"validators",
|
| 67 |
+
]
|
| 68 |
+
|
| 69 |
+
__getattr__ = _make_getattr(__name__)
|
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attrs/__init__.pyi
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import sys
|
| 2 |
+
|
| 3 |
+
from typing import (
|
| 4 |
+
Any,
|
| 5 |
+
Callable,
|
| 6 |
+
Mapping,
|
| 7 |
+
Sequence,
|
| 8 |
+
overload,
|
| 9 |
+
TypeVar,
|
| 10 |
+
)
|
| 11 |
+
|
| 12 |
+
# Because we need to type our own stuff, we have to make everything from
|
| 13 |
+
# attr explicitly public too.
|
| 14 |
+
from attr import __author__ as __author__
|
| 15 |
+
from attr import __copyright__ as __copyright__
|
| 16 |
+
from attr import __description__ as __description__
|
| 17 |
+
from attr import __email__ as __email__
|
| 18 |
+
from attr import __license__ as __license__
|
| 19 |
+
from attr import __title__ as __title__
|
| 20 |
+
from attr import __url__ as __url__
|
| 21 |
+
from attr import __version__ as __version__
|
| 22 |
+
from attr import __version_info__ as __version_info__
|
| 23 |
+
from attr import assoc as assoc
|
| 24 |
+
from attr import Attribute as Attribute
|
| 25 |
+
from attr import AttrsInstance as AttrsInstance
|
| 26 |
+
from attr import cmp_using as cmp_using
|
| 27 |
+
from attr import converters as converters
|
| 28 |
+
from attr import Converter as Converter
|
| 29 |
+
from attr import evolve as evolve
|
| 30 |
+
from attr import exceptions as exceptions
|
| 31 |
+
from attr import Factory as Factory
|
| 32 |
+
from attr import fields as fields
|
| 33 |
+
from attr import fields_dict as fields_dict
|
| 34 |
+
from attr import filters as filters
|
| 35 |
+
from attr import has as has
|
| 36 |
+
from attr import make_class as make_class
|
| 37 |
+
from attr import NOTHING as NOTHING
|
| 38 |
+
from attr import resolve_types as resolve_types
|
| 39 |
+
from attr import setters as setters
|
| 40 |
+
from attr import validate as validate
|
| 41 |
+
from attr import validators as validators
|
| 42 |
+
from attr import attrib, asdict as asdict, astuple as astuple
|
| 43 |
+
from attr import NothingType as NothingType
|
| 44 |
+
|
| 45 |
+
if sys.version_info >= (3, 11):
|
| 46 |
+
from typing import dataclass_transform
|
| 47 |
+
else:
|
| 48 |
+
from typing_extensions import dataclass_transform
|
| 49 |
+
|
| 50 |
+
_T = TypeVar("_T")
|
| 51 |
+
_C = TypeVar("_C", bound=type)
|
| 52 |
+
|
| 53 |
+
_EqOrderType = bool | Callable[[Any], Any]
|
| 54 |
+
_ValidatorType = Callable[[Any, "Attribute[_T]", _T], Any]
|
| 55 |
+
_CallableConverterType = Callable[[Any], Any]
|
| 56 |
+
_ConverterType = _CallableConverterType | Converter[Any, Any]
|
| 57 |
+
_ReprType = Callable[[Any], str]
|
| 58 |
+
_ReprArgType = bool | _ReprType
|
| 59 |
+
_OnSetAttrType = Callable[[Any, "Attribute[Any]", Any], Any]
|
| 60 |
+
_OnSetAttrArgType = _OnSetAttrType | list[_OnSetAttrType] | setters._NoOpType
|
| 61 |
+
_FieldTransformer = Callable[
|
| 62 |
+
[type, list["Attribute[Any]"]], list["Attribute[Any]"]
|
| 63 |
+
]
|
| 64 |
+
# FIXME: in reality, if multiple validators are passed they must be in a list
|
| 65 |
+
# or tuple, but those are invariant and so would prevent subtypes of
|
| 66 |
+
# _ValidatorType from working when passed in a list or tuple.
|
| 67 |
+
_ValidatorArgType = _ValidatorType[_T] | Sequence[_ValidatorType[_T]]
|
| 68 |
+
|
| 69 |
+
@overload
|
| 70 |
+
def field(
|
| 71 |
+
*,
|
| 72 |
+
default: None = ...,
|
| 73 |
+
validator: None = ...,
|
| 74 |
+
repr: _ReprArgType = ...,
|
| 75 |
+
hash: bool | None = ...,
|
| 76 |
+
init: bool = ...,
|
| 77 |
+
metadata: Mapping[Any, Any] | None = ...,
|
| 78 |
+
converter: None = ...,
|
| 79 |
+
factory: None = ...,
|
| 80 |
+
kw_only: bool = ...,
|
| 81 |
+
eq: bool | None = ...,
|
| 82 |
+
order: bool | None = ...,
|
| 83 |
+
on_setattr: _OnSetAttrArgType | None = ...,
|
| 84 |
+
alias: str | None = ...,
|
| 85 |
+
type: type | None = ...,
|
| 86 |
+
) -> Any: ...
|
| 87 |
+
|
| 88 |
+
# This form catches an explicit None or no default and infers the type from the
|
| 89 |
+
# other arguments.
|
| 90 |
+
@overload
|
| 91 |
+
def field(
|
| 92 |
+
*,
|
| 93 |
+
default: None = ...,
|
| 94 |
+
validator: _ValidatorArgType[_T] | None = ...,
|
| 95 |
+
repr: _ReprArgType = ...,
|
| 96 |
+
hash: bool | None = ...,
|
| 97 |
+
init: bool = ...,
|
| 98 |
+
metadata: Mapping[Any, Any] | None = ...,
|
| 99 |
+
converter: _ConverterType
|
| 100 |
+
| list[_ConverterType]
|
| 101 |
+
| tuple[_ConverterType]
|
| 102 |
+
| None = ...,
|
| 103 |
+
factory: Callable[[], _T] | None = ...,
|
| 104 |
+
kw_only: bool = ...,
|
| 105 |
+
eq: _EqOrderType | None = ...,
|
| 106 |
+
order: _EqOrderType | None = ...,
|
| 107 |
+
on_setattr: _OnSetAttrArgType | None = ...,
|
| 108 |
+
alias: str | None = ...,
|
| 109 |
+
type: type | None = ...,
|
| 110 |
+
) -> _T: ...
|
| 111 |
+
|
| 112 |
+
# This form catches an explicit default argument.
|
| 113 |
+
@overload
|
| 114 |
+
def field(
|
| 115 |
+
*,
|
| 116 |
+
default: _T,
|
| 117 |
+
validator: _ValidatorArgType[_T] | None = ...,
|
| 118 |
+
repr: _ReprArgType = ...,
|
| 119 |
+
hash: bool | None = ...,
|
| 120 |
+
init: bool = ...,
|
| 121 |
+
metadata: Mapping[Any, Any] | None = ...,
|
| 122 |
+
converter: _ConverterType
|
| 123 |
+
| list[_ConverterType]
|
| 124 |
+
| tuple[_ConverterType]
|
| 125 |
+
| None = ...,
|
| 126 |
+
factory: Callable[[], _T] | None = ...,
|
| 127 |
+
kw_only: bool = ...,
|
| 128 |
+
eq: _EqOrderType | None = ...,
|
| 129 |
+
order: _EqOrderType | None = ...,
|
| 130 |
+
on_setattr: _OnSetAttrArgType | None = ...,
|
| 131 |
+
alias: str | None = ...,
|
| 132 |
+
type: type | None = ...,
|
| 133 |
+
) -> _T: ...
|
| 134 |
+
|
| 135 |
+
# This form covers type=non-Type: e.g. forward references (str), Any
|
| 136 |
+
@overload
|
| 137 |
+
def field(
|
| 138 |
+
*,
|
| 139 |
+
default: _T | None = ...,
|
| 140 |
+
validator: _ValidatorArgType[_T] | None = ...,
|
| 141 |
+
repr: _ReprArgType = ...,
|
| 142 |
+
hash: bool | None = ...,
|
| 143 |
+
init: bool = ...,
|
| 144 |
+
metadata: Mapping[Any, Any] | None = ...,
|
| 145 |
+
converter: _ConverterType
|
| 146 |
+
| list[_ConverterType]
|
| 147 |
+
| tuple[_ConverterType]
|
| 148 |
+
| None = ...,
|
| 149 |
+
factory: Callable[[], _T] | None = ...,
|
| 150 |
+
kw_only: bool = ...,
|
| 151 |
+
eq: _EqOrderType | None = ...,
|
| 152 |
+
order: _EqOrderType | None = ...,
|
| 153 |
+
on_setattr: _OnSetAttrArgType | None = ...,
|
| 154 |
+
alias: str | None = ...,
|
| 155 |
+
type: type | None = ...,
|
| 156 |
+
) -> Any: ...
|
| 157 |
+
@overload
|
| 158 |
+
@dataclass_transform(field_specifiers=(attrib, field))
|
| 159 |
+
def define(
|
| 160 |
+
maybe_cls: _C,
|
| 161 |
+
*,
|
| 162 |
+
these: dict[str, Any] | None = ...,
|
| 163 |
+
repr: bool = ...,
|
| 164 |
+
unsafe_hash: bool | None = ...,
|
| 165 |
+
hash: bool | None = ...,
|
| 166 |
+
init: bool = ...,
|
| 167 |
+
slots: bool = ...,
|
| 168 |
+
frozen: bool = ...,
|
| 169 |
+
weakref_slot: bool = ...,
|
| 170 |
+
str: bool = ...,
|
| 171 |
+
auto_attribs: bool = ...,
|
| 172 |
+
kw_only: bool = ...,
|
| 173 |
+
cache_hash: bool = ...,
|
| 174 |
+
auto_exc: bool = ...,
|
| 175 |
+
eq: bool | None = ...,
|
| 176 |
+
order: bool | None = ...,
|
| 177 |
+
auto_detect: bool = ...,
|
| 178 |
+
getstate_setstate: bool | None = ...,
|
| 179 |
+
on_setattr: _OnSetAttrArgType | None = ...,
|
| 180 |
+
field_transformer: _FieldTransformer | None = ...,
|
| 181 |
+
match_args: bool = ...,
|
| 182 |
+
) -> _C: ...
|
| 183 |
+
@overload
|
| 184 |
+
@dataclass_transform(field_specifiers=(attrib, field))
|
| 185 |
+
def define(
|
| 186 |
+
maybe_cls: None = ...,
|
| 187 |
+
*,
|
| 188 |
+
these: dict[str, Any] | None = ...,
|
| 189 |
+
repr: bool = ...,
|
| 190 |
+
unsafe_hash: bool | None = ...,
|
| 191 |
+
hash: bool | None = ...,
|
| 192 |
+
init: bool = ...,
|
| 193 |
+
slots: bool = ...,
|
| 194 |
+
frozen: bool = ...,
|
| 195 |
+
weakref_slot: bool = ...,
|
| 196 |
+
str: bool = ...,
|
| 197 |
+
auto_attribs: bool = ...,
|
| 198 |
+
kw_only: bool = ...,
|
| 199 |
+
cache_hash: bool = ...,
|
| 200 |
+
auto_exc: bool = ...,
|
| 201 |
+
eq: bool | None = ...,
|
| 202 |
+
order: bool | None = ...,
|
| 203 |
+
auto_detect: bool = ...,
|
| 204 |
+
getstate_setstate: bool | None = ...,
|
| 205 |
+
on_setattr: _OnSetAttrArgType | None = ...,
|
| 206 |
+
field_transformer: _FieldTransformer | None = ...,
|
| 207 |
+
match_args: bool = ...,
|
| 208 |
+
) -> Callable[[_C], _C]: ...
|
| 209 |
+
|
| 210 |
+
mutable = define
|
| 211 |
+
|
| 212 |
+
@overload
|
| 213 |
+
@dataclass_transform(frozen_default=True, field_specifiers=(attrib, field))
|
| 214 |
+
def frozen(
|
| 215 |
+
maybe_cls: _C,
|
| 216 |
+
*,
|
| 217 |
+
these: dict[str, Any] | None = ...,
|
| 218 |
+
repr: bool = ...,
|
| 219 |
+
unsafe_hash: bool | None = ...,
|
| 220 |
+
hash: bool | None = ...,
|
| 221 |
+
init: bool = ...,
|
| 222 |
+
slots: bool = ...,
|
| 223 |
+
frozen: bool = ...,
|
| 224 |
+
weakref_slot: bool = ...,
|
| 225 |
+
str: bool = ...,
|
| 226 |
+
auto_attribs: bool = ...,
|
| 227 |
+
kw_only: bool = ...,
|
| 228 |
+
cache_hash: bool = ...,
|
| 229 |
+
auto_exc: bool = ...,
|
| 230 |
+
eq: bool | None = ...,
|
| 231 |
+
order: bool | None = ...,
|
| 232 |
+
auto_detect: bool = ...,
|
| 233 |
+
getstate_setstate: bool | None = ...,
|
| 234 |
+
on_setattr: _OnSetAttrArgType | None = ...,
|
| 235 |
+
field_transformer: _FieldTransformer | None = ...,
|
| 236 |
+
match_args: bool = ...,
|
| 237 |
+
) -> _C: ...
|
| 238 |
+
@overload
|
| 239 |
+
@dataclass_transform(frozen_default=True, field_specifiers=(attrib, field))
|
| 240 |
+
def frozen(
|
| 241 |
+
maybe_cls: None = ...,
|
| 242 |
+
*,
|
| 243 |
+
these: dict[str, Any] | None = ...,
|
| 244 |
+
repr: bool = ...,
|
| 245 |
+
unsafe_hash: bool | None = ...,
|
| 246 |
+
hash: bool | None = ...,
|
| 247 |
+
init: bool = ...,
|
| 248 |
+
slots: bool = ...,
|
| 249 |
+
frozen: bool = ...,
|
| 250 |
+
weakref_slot: bool = ...,
|
| 251 |
+
str: bool = ...,
|
| 252 |
+
auto_attribs: bool = ...,
|
| 253 |
+
kw_only: bool = ...,
|
| 254 |
+
cache_hash: bool = ...,
|
| 255 |
+
auto_exc: bool = ...,
|
| 256 |
+
eq: bool | None = ...,
|
| 257 |
+
order: bool | None = ...,
|
| 258 |
+
auto_detect: bool = ...,
|
| 259 |
+
getstate_setstate: bool | None = ...,
|
| 260 |
+
on_setattr: _OnSetAttrArgType | None = ...,
|
| 261 |
+
field_transformer: _FieldTransformer | None = ...,
|
| 262 |
+
match_args: bool = ...,
|
| 263 |
+
) -> Callable[[_C], _C]: ...
|
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attrs/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (1.44 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attrs/__pycache__/converters.cpython-311.pyc
ADDED
|
Binary file (270 Bytes). View file
|
|
|
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attrs/__pycache__/exceptions.cpython-311.pyc
ADDED
|
Binary file (270 Bytes). View file
|
|
|
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attrs/__pycache__/filters.cpython-311.pyc
ADDED
|
Binary file (264 Bytes). View file
|
|
|
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attrs/__pycache__/setters.cpython-311.pyc
ADDED
|
Binary file (264 Bytes). View file
|
|
|
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attrs/__pycache__/validators.cpython-311.pyc
ADDED
|
Binary file (270 Bytes). View file
|
|
|
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attrs/converters.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: MIT
|
| 2 |
+
|
| 3 |
+
from attr.converters import * # noqa: F403
|
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attrs/exceptions.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: MIT
|
| 2 |
+
|
| 3 |
+
from attr.exceptions import * # noqa: F403
|
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attrs/filters.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: MIT
|
| 2 |
+
|
| 3 |
+
from attr.filters import * # noqa: F403
|
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attrs/py.typed
ADDED
|
File without changes
|
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attrs/setters.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: MIT
|
| 2 |
+
|
| 3 |
+
from attr.setters import * # noqa: F403
|
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/attrs/validators.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: MIT
|
| 2 |
+
|
| 3 |
+
from attr.validators import * # noqa: F403
|
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/frozenlist/__init__.py
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import sys
|
| 3 |
+
import types
|
| 4 |
+
from collections.abc import MutableSequence
|
| 5 |
+
from functools import total_ordering
|
| 6 |
+
from typing import Any, Type
|
| 7 |
+
|
| 8 |
+
__version__ = "1.5.0"
|
| 9 |
+
|
| 10 |
+
__all__ = ("FrozenList", "PyFrozenList") # type: Tuple[str, ...]
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
NO_EXTENSIONS = bool(os.environ.get("FROZENLIST_NO_EXTENSIONS")) # type: bool
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
@total_ordering
|
| 17 |
+
class FrozenList(MutableSequence):
|
| 18 |
+
__slots__ = ("_frozen", "_items")
|
| 19 |
+
|
| 20 |
+
if sys.version_info >= (3, 9):
|
| 21 |
+
__class_getitem__ = classmethod(types.GenericAlias)
|
| 22 |
+
else:
|
| 23 |
+
|
| 24 |
+
@classmethod
|
| 25 |
+
def __class_getitem__(
|
| 26 |
+
cls: Type["FrozenList"],
|
| 27 |
+
cls_item: Any,
|
| 28 |
+
) -> Type["FrozenList"]:
|
| 29 |
+
return cls
|
| 30 |
+
|
| 31 |
+
def __init__(self, items=None):
|
| 32 |
+
self._frozen = False
|
| 33 |
+
if items is not None:
|
| 34 |
+
items = list(items)
|
| 35 |
+
else:
|
| 36 |
+
items = []
|
| 37 |
+
self._items = items
|
| 38 |
+
|
| 39 |
+
@property
|
| 40 |
+
def frozen(self):
|
| 41 |
+
return self._frozen
|
| 42 |
+
|
| 43 |
+
def freeze(self):
|
| 44 |
+
self._frozen = True
|
| 45 |
+
|
| 46 |
+
def __getitem__(self, index):
|
| 47 |
+
return self._items[index]
|
| 48 |
+
|
| 49 |
+
def __setitem__(self, index, value):
|
| 50 |
+
if self._frozen:
|
| 51 |
+
raise RuntimeError("Cannot modify frozen list.")
|
| 52 |
+
self._items[index] = value
|
| 53 |
+
|
| 54 |
+
def __delitem__(self, index):
|
| 55 |
+
if self._frozen:
|
| 56 |
+
raise RuntimeError("Cannot modify frozen list.")
|
| 57 |
+
del self._items[index]
|
| 58 |
+
|
| 59 |
+
def __len__(self):
|
| 60 |
+
return self._items.__len__()
|
| 61 |
+
|
| 62 |
+
def __iter__(self):
|
| 63 |
+
return self._items.__iter__()
|
| 64 |
+
|
| 65 |
+
def __reversed__(self):
|
| 66 |
+
return self._items.__reversed__()
|
| 67 |
+
|
| 68 |
+
def __eq__(self, other):
|
| 69 |
+
return list(self) == other
|
| 70 |
+
|
| 71 |
+
def __le__(self, other):
|
| 72 |
+
return list(self) <= other
|
| 73 |
+
|
| 74 |
+
def insert(self, pos, item):
|
| 75 |
+
if self._frozen:
|
| 76 |
+
raise RuntimeError("Cannot modify frozen list.")
|
| 77 |
+
self._items.insert(pos, item)
|
| 78 |
+
|
| 79 |
+
def __repr__(self):
|
| 80 |
+
return f"<FrozenList(frozen={self._frozen}, {self._items!r})>"
|
| 81 |
+
|
| 82 |
+
def __hash__(self):
|
| 83 |
+
if self._frozen:
|
| 84 |
+
return hash(tuple(self))
|
| 85 |
+
else:
|
| 86 |
+
raise RuntimeError("Cannot hash unfrozen list.")
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
PyFrozenList = FrozenList
|
| 90 |
+
|
| 91 |
+
|
| 92 |
+
if not NO_EXTENSIONS:
|
| 93 |
+
try:
|
| 94 |
+
from ._frozenlist import FrozenList as CFrozenList # type: ignore
|
| 95 |
+
except ImportError: # pragma: no cover
|
| 96 |
+
pass
|
| 97 |
+
else:
|
| 98 |
+
FrozenList = CFrozenList # type: ignore
|
.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/frozenlist/__init__.pyi
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import (
|
| 2 |
+
Generic,
|
| 3 |
+
Iterable,
|
| 4 |
+
Iterator,
|
| 5 |
+
List,
|
| 6 |
+
MutableSequence,
|
| 7 |
+
Optional,
|
| 8 |
+
TypeVar,
|
| 9 |
+
Union,
|
| 10 |
+
overload,
|
| 11 |
+
)
|
| 12 |
+
|
| 13 |
+
_T = TypeVar("_T")
|
| 14 |
+
_Arg = Union[List[_T], Iterable[_T]]
|
| 15 |
+
|
| 16 |
+
class FrozenList(MutableSequence[_T], Generic[_T]):
|
| 17 |
+
def __init__(self, items: Optional[_Arg[_T]] = None) -> None: ...
|
| 18 |
+
@property
|
| 19 |
+
def frozen(self) -> bool: ...
|
| 20 |
+
def freeze(self) -> None: ...
|
| 21 |
+
@overload
|
| 22 |
+
def __getitem__(self, i: int) -> _T: ...
|
| 23 |
+
@overload
|
| 24 |
+
def __getitem__(self, s: slice) -> FrozenList[_T]: ...
|
| 25 |
+
@overload
|
| 26 |
+
def __setitem__(self, i: int, o: _T) -> None: ...
|
| 27 |
+
@overload
|
| 28 |
+
def __setitem__(self, s: slice, o: Iterable[_T]) -> None: ...
|
| 29 |
+
@overload
|
| 30 |
+
def __delitem__(self, i: int) -> None: ...
|
| 31 |
+
@overload
|
| 32 |
+
def __delitem__(self, i: slice) -> None: ...
|
| 33 |
+
def __len__(self) -> int: ...
|
| 34 |
+
def __iter__(self) -> Iterator[_T]: ...
|
| 35 |
+
def __reversed__(self) -> Iterator[_T]: ...
|
| 36 |
+
def __eq__(self, other: object) -> bool: ...
|
| 37 |
+
def __le__(self, other: FrozenList[_T]) -> bool: ...
|
| 38 |
+
def __ne__(self, other: object) -> bool: ...
|
| 39 |
+
def __lt__(self, other: FrozenList[_T]) -> bool: ...
|
| 40 |
+
def __ge__(self, other: FrozenList[_T]) -> bool: ...
|
| 41 |
+
def __gt__(self, other: FrozenList[_T]) -> bool: ...
|
| 42 |
+
def insert(self, pos: int, item: _T) -> None: ...
|
| 43 |
+
def __repr__(self) -> str: ...
|
| 44 |
+
def __hash__(self) -> int: ...
|
| 45 |
+
|
| 46 |
+
# types for C accelerators are the same
|
| 47 |
+
CFrozenList = PyFrozenList = FrozenList
|