diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/__init__.cpython-311.pyc b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f893e3d002ac1cfea156271ef8c85b200cfa6192 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/_clonevirtualenv.cpython-311.pyc b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/_clonevirtualenv.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..46dd0b45709ca3079fb8f45d832abb8699511ca6 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/_clonevirtualenv.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/conda.cpython-311.pyc b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/conda.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7c91ed74927d52395aa40032ea761ad2f2b6fabb Binary files /dev/null and b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/conda.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/conda_utils.cpython-311.pyc b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/conda_utils.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d1f4816ce8fa6ec1f71872eefafb54148312e315 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/conda_utils.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/constants.cpython-311.pyc b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/constants.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b23c7c1b4fd6055adcefbda443cbea893939300c Binary files /dev/null and b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/constants.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/context.cpython-311.pyc b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/context.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9a9732dc4bd3abc22d47dc5deba7973e50dfc618 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/context.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/default_impl.cpython-311.pyc b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/default_impl.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e6a0f4b8223b4a41aa7b74f31afcae2ddea6c608 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/default_impl.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/dependency_utils.cpython-311.pyc b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/dependency_utils.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..65c4ffa0b0566413e226d5026422fbb90d329575 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/dependency_utils.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/image_uri.cpython-311.pyc b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/image_uri.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9f31fed3b37e19590abf6f2cadecbc024c833d49 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/image_uri.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/java_jars.cpython-311.pyc b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/java_jars.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..960ebd7a14c6807fd11553bfa2391145c42fd30a Binary files /dev/null and b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/java_jars.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/mpi.cpython-311.pyc b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/mpi.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bc50f6c7d3fd67a8b68ba9b5c5e28a010f49be64 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/mpi.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/mpi_runner.cpython-311.pyc b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/mpi_runner.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c237b19525c56d88b611899232e45dd845ed28d0 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/mpi_runner.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/nsight.cpython-311.pyc b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/nsight.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0914ef8cce24088bd877e56679d3bd869c7e06f3 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/nsight.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/packaging.cpython-311.pyc b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/packaging.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..28bb6d9de401c3f5f72c4f092cec00ab15b5e106 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/packaging.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/pip.cpython-311.pyc b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/pip.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..24afc949ea29315319571eacb7305a0c57c63ead Binary files /dev/null and b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/pip.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/plugin.cpython-311.pyc b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/plugin.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c80e0f120addf3c3365879177c42118d97810a19 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/plugin.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/plugin_schema_manager.cpython-311.pyc b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/plugin_schema_manager.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b7f6d383b5a6daf415b31bf6328d596b651a1f96 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/plugin_schema_manager.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/protocol.cpython-311.pyc b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/protocol.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f4ce1592aeae6ba6818c30634bf64257386be5ca Binary files /dev/null and b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/protocol.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/py_modules.cpython-311.pyc b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/py_modules.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4a74ff43d14a520cacd7d9129b6ab24debe167f1 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/py_modules.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/setup_hook.cpython-311.pyc b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/setup_hook.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a1e8b20eb392a17b344dff6ccf2bce550cfac4a1 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/setup_hook.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/uri_cache.cpython-311.pyc b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/uri_cache.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..328df184d39cad9ae9c3bdc77029309b3ba10709 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/uri_cache.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/utils.cpython-311.pyc b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/utils.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4752f9da7666235eef6fd66d65af1c15ff6a682a Binary files /dev/null and b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/utils.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/uv.cpython-311.pyc b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/uv.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6a3414448b62307c913391741bde1661791de7fd Binary files /dev/null and b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/uv.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/validation.cpython-311.pyc b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/validation.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3d72f51ae5c84b5a9464955324810b4a4126ee4b Binary files /dev/null and b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/validation.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/virtualenv_utils.cpython-311.pyc b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/virtualenv_utils.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5f5f356899cf4fc40602a89691a5ea4b7c4a617b Binary files /dev/null and b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/virtualenv_utils.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/working_dir.cpython-311.pyc b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/working_dir.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6d415ea407c36f2a8963ed276ba23c206e35cd38 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/__pycache__/working_dir.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/__init__.py b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/__pycache__/__init__.cpython-311.pyc b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6ceda112b22dfbc83555a3afe112eceff87f4af7 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/__pycache__/main.cpython-311.pyc b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/__pycache__/main.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..970e0953f5558778180ab1a9d5697c291d90c765 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/__pycache__/main.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/__pycache__/runtime_env_agent.cpython-311.pyc b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/__pycache__/runtime_env_agent.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..aaccca31aa8355b1a84e4ce76652e90ecf9e7470 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/__pycache__/runtime_env_agent.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/__pycache__/runtime_env_consts.cpython-311.pyc b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/__pycache__/runtime_env_consts.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4ad739828db535656dab8261d6ebab0878a234d8 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/__pycache__/runtime_env_consts.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/main.py b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/main.py new file mode 100644 index 0000000000000000000000000000000000000000..af4efa457ae9f8fbcc442d80506ada744885aca7 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/main.py @@ -0,0 +1,229 @@ +import sys +import os +import argparse +import logging +import pathlib +import ray._private.ray_constants as ray_constants +from ray.core.generated import ( + runtime_env_agent_pb2, +) +from ray._private.utils import open_log +from ray._private.ray_logging import ( + configure_log_file, +) +from ray._private.utils import ( + get_or_create_event_loop, +) +from ray._private.process_watcher import create_check_raylet_task + + +def import_libs(): + my_dir = os.path.abspath(os.path.dirname(__file__)) + sys.path.insert(0, os.path.join(my_dir, "thirdparty_files")) # for aiohttp + sys.path.insert(0, my_dir) # for runtime_env_agent and runtime_env_consts + + +import_libs() + +import runtime_env_consts # noqa: E402 +from runtime_env_agent import RuntimeEnvAgent # noqa: E402 +from aiohttp import web # noqa: E402 + + +def open_capture_files(log_dir): + filename = "runtime_env_agent" + return ( + open_log(pathlib.Path(log_dir) / f"{filename}.out"), + open_log(pathlib.Path(log_dir) / f"{filename}.err"), + ) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Runtime env agent.") + parser.add_argument( + "--node-ip-address", + required=True, + type=str, + help="the IP address of this node.", + ) + parser.add_argument( + "--runtime-env-agent-port", + required=True, + type=int, + default=None, + help="The port on which the runtime env agent will receive HTTP requests.", + ) + + parser.add_argument( + "--gcs-address", required=True, type=str, help="The address (ip:port) of GCS." + ) + parser.add_argument( + "--cluster-id-hex", required=True, type=str, help="The cluster id in hex." + ) + parser.add_argument( + "--runtime-env-dir", + required=True, + type=str, + default=None, + help="Specify the path of the resource directory used by runtime_env.", + ) + + parser.add_argument( + "--logging-level", + required=False, + type=lambda s: logging.getLevelName(s.upper()), + default=ray_constants.LOGGER_LEVEL, + choices=ray_constants.LOGGER_LEVEL_CHOICES, + help=ray_constants.LOGGER_LEVEL_HELP, + ) + parser.add_argument( + "--logging-format", + required=False, + type=str, + default=ray_constants.LOGGER_FORMAT, + help=ray_constants.LOGGER_FORMAT_HELP, + ) + parser.add_argument( + "--logging-filename", + required=False, + type=str, + default=runtime_env_consts.RUNTIME_ENV_AGENT_LOG_FILENAME, + help="Specify the name of log file, " + 'log to stdout if set empty, default is "{}".'.format( + runtime_env_consts.RUNTIME_ENV_AGENT_LOG_FILENAME + ), + ) + parser.add_argument( + "--logging-rotate-bytes", + required=False, + type=int, + default=ray_constants.LOGGING_ROTATE_BYTES, + help="Specify the max bytes for rotating " + "log file, default is {} bytes.".format(ray_constants.LOGGING_ROTATE_BYTES), + ) + parser.add_argument( + "--logging-rotate-backup-count", + required=False, + type=int, + default=ray_constants.LOGGING_ROTATE_BACKUP_COUNT, + help="Specify the backup count of rotated log file, default is {}.".format( + ray_constants.LOGGING_ROTATE_BACKUP_COUNT + ), + ) + parser.add_argument( + "--log-dir", + required=True, + type=str, + default=None, + help="Specify the path of log directory.", + ) + parser.add_argument( + "--temp-dir", + required=True, + type=str, + default=None, + help="Specify the path of the temporary directory use by Ray process.", + ) + + args = parser.parse_args() + + logging_params = dict( + logging_level=args.logging_level, + logging_format=args.logging_format, + log_dir=args.log_dir, + filename=args.logging_filename, + max_bytes=args.logging_rotate_bytes, + backup_count=args.logging_rotate_backup_count, + ) + + # Setup stdout/stderr redirect files + out_file, err_file = open_capture_files(args.log_dir) + configure_log_file(out_file, err_file) + + agent = RuntimeEnvAgent( + runtime_env_dir=args.runtime_env_dir, + logging_params=logging_params, + gcs_address=args.gcs_address, + cluster_id_hex=args.cluster_id_hex, + temp_dir=args.temp_dir, + address=args.node_ip_address, + runtime_env_agent_port=args.runtime_env_agent_port, + ) + + # POST /get_or_create_runtime_env + # body is serialzied protobuf GetOrCreateRuntimeEnvRequest + # reply is serialzied protobuf GetOrCreateRuntimeEnvReply + async def get_or_create_runtime_env(request: web.Request) -> web.Response: + data = await request.read() + request = runtime_env_agent_pb2.GetOrCreateRuntimeEnvRequest() + request.ParseFromString(data) + reply = await agent.GetOrCreateRuntimeEnv(request) + return web.Response( + body=reply.SerializeToString(), content_type="application/octet-stream" + ) + + # POST /delete_runtime_env_if_possible + # body is serialzied protobuf DeleteRuntimeEnvIfPossibleRequest + # reply is serialzied protobuf DeleteRuntimeEnvIfPossibleReply + async def delete_runtime_env_if_possible(request: web.Request) -> web.Response: + data = await request.read() + request = runtime_env_agent_pb2.DeleteRuntimeEnvIfPossibleRequest() + request.ParseFromString(data) + reply = await agent.DeleteRuntimeEnvIfPossible(request) + return web.Response( + body=reply.SerializeToString(), content_type="application/octet-stream" + ) + + # POST /get_runtime_envs_info + # body is serialzied protobuf GetRuntimeEnvsInfoRequest + # reply is serialzied protobuf GetRuntimeEnvsInfoReply + async def get_runtime_envs_info(request: web.Request) -> web.Response: + data = await request.read() + request = runtime_env_agent_pb2.GetRuntimeEnvsInfoRequest() + request.ParseFromString(data) + reply = await agent.GetRuntimeEnvsInfo(request) + return web.Response( + body=reply.SerializeToString(), content_type="application/octet-stream" + ) + + app = web.Application() + + app.router.add_post("/get_or_create_runtime_env", get_or_create_runtime_env) + app.router.add_post( + "/delete_runtime_env_if_possible", delete_runtime_env_if_possible + ) + app.router.add_post("/get_runtime_envs_info", get_runtime_envs_info) + + loop = get_or_create_event_loop() + check_raylet_task = None + if sys.platform not in ["win32", "cygwin"]: + + def parent_dead_callback(msg): + agent._logger.info( + "Raylet is dead! Exiting Runtime Env Agent. " + f"addr: {args.node_ip_address}, " + f"port: {args.runtime_env_agent_port}\n" + f"{msg}" + ) + + # No need to await this task. + check_raylet_task = create_check_raylet_task( + args.log_dir, args.gcs_address, parent_dead_callback, loop + ) + runtime_env_agent_ip = ( + "127.0.0.1" if args.node_ip_address == "127.0.0.1" else "0.0.0.0" + ) + try: + web.run_app( + app, + host=runtime_env_agent_ip, + port=args.runtime_env_agent_port, + loop=loop, + ) + except SystemExit as e: + agent._logger.info(f"SystemExit! {e}") + # We have to poke the task exception, or there's an error message + # "task exception was never retrieved". + if check_raylet_task is not None: + check_raylet_task.exception() + sys.exit(e.code) diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/runtime_env_agent.py b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/runtime_env_agent.py new file mode 100644 index 0000000000000000000000000000000000000000..d76a0e06cbac895b56e571abac16528b457960fc --- /dev/null +++ b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/runtime_env_agent.py @@ -0,0 +1,585 @@ +import asyncio +import logging +import os +import time +import traceback +from collections import defaultdict +from dataclasses import dataclass +from typing import Callable, Dict, List, Set, Tuple +from ray._private.ray_constants import ( + DEFAULT_RUNTIME_ENV_TIMEOUT_SECONDS, +) + +import ray._private.runtime_env.agent.runtime_env_consts as runtime_env_consts +from ray._private.ray_logging import setup_component_logger +from ray._private.runtime_env.conda import CondaPlugin +from ray._private.runtime_env.context import RuntimeEnvContext +from ray._private.runtime_env.default_impl import get_image_uri_plugin_cls +from ray._private.runtime_env.java_jars import JavaJarsPlugin +from ray._private.runtime_env.image_uri import ContainerPlugin +from ray._private.runtime_env.pip import PipPlugin +from ray._private.runtime_env.uv import UvPlugin +from ray._private.gcs_utils import GcsAioClient +from ray._private.runtime_env.plugin import ( + RuntimeEnvPlugin, + create_for_plugin_if_needed, +) +from ray._private.utils import get_or_create_event_loop +from ray._private.runtime_env.plugin import RuntimeEnvPluginManager +from ray._private.runtime_env.py_modules import PyModulesPlugin +from ray._private.runtime_env.working_dir import WorkingDirPlugin +from ray._private.runtime_env.nsight import NsightPlugin +from ray._private.runtime_env.mpi import MPIPlugin +from ray.core.generated import ( + runtime_env_agent_pb2, + agent_manager_pb2, +) +from ray.core.generated.runtime_env_common_pb2 import ( + RuntimeEnvState as ProtoRuntimeEnvState, +) +from ray.runtime_env import RuntimeEnv, RuntimeEnvConfig + +default_logger = logging.getLogger(__name__) + +# TODO(edoakes): this is used for unit tests. We should replace it with a +# better pluggability mechanism once available. +SLEEP_FOR_TESTING_S = os.environ.get("RAY_RUNTIME_ENV_SLEEP_FOR_TESTING_S") + + +@dataclass +class CreatedEnvResult: + # Whether or not the env was installed correctly. + success: bool + # If success is True, will be a serialized RuntimeEnvContext + # If success is False, will be an error message. + result: str + # The time to create a runtime env in ms. + creation_time_ms: int + + +# e.g., "working_dir" +UriType = str + + +class ReferenceTable: + """ + The URI reference table which is used for GC. + When the reference count is decreased to zero, + the URI should be removed from this table and + added to cache if needed. + """ + + def __init__( + self, + uris_parser: Callable[[RuntimeEnv], Tuple[str, UriType]], + unused_uris_callback: Callable[[List[Tuple[str, UriType]]], None], + unused_runtime_env_callback: Callable[[str], None], + ): + # Runtime Environment reference table. The key is serialized runtime env and + # the value is reference count. + self._runtime_env_reference: Dict[str, int] = defaultdict(int) + # URI reference table. The key is URI parsed from runtime env and the value + # is reference count. + self._uri_reference: Dict[str, int] = defaultdict(int) + self._uris_parser = uris_parser + self._unused_uris_callback = unused_uris_callback + self._unused_runtime_env_callback = unused_runtime_env_callback + # send the `DeleteRuntimeEnvIfPossible` RPC when the client exits. The URI won't + # be leaked now because the reference count will be reset to zero when the job + # finished. + self._reference_exclude_sources: Set[str] = { + "client_server", + } + + def _increase_reference_for_uris(self, uris): + default_logger.debug(f"Increase reference for uris {uris}.") + for uri, _ in uris: + self._uri_reference[uri] += 1 + + def _decrease_reference_for_uris(self, uris): + default_logger.debug(f"Decrease reference for uris {uris}.") + unused_uris = list() + for uri, uri_type in uris: + if self._uri_reference[uri] > 0: + self._uri_reference[uri] -= 1 + if self._uri_reference[uri] == 0: + unused_uris.append((uri, uri_type)) + del self._uri_reference[uri] + else: + default_logger.warning(f"URI {uri} does not exist.") + if unused_uris: + default_logger.info(f"Unused uris {unused_uris}.") + self._unused_uris_callback(unused_uris) + return unused_uris + + def _increase_reference_for_runtime_env(self, serialized_env: str): + default_logger.debug(f"Increase reference for runtime env {serialized_env}.") + self._runtime_env_reference[serialized_env] += 1 + + def _decrease_reference_for_runtime_env(self, serialized_env: str): + default_logger.debug(f"Decrease reference for runtime env {serialized_env}.") + unused = False + if self._runtime_env_reference[serialized_env] > 0: + self._runtime_env_reference[serialized_env] -= 1 + if self._runtime_env_reference[serialized_env] == 0: + unused = True + del self._runtime_env_reference[serialized_env] + else: + default_logger.warning(f"Runtime env {serialized_env} does not exist.") + if unused: + default_logger.info(f"Unused runtime env {serialized_env}.") + self._unused_runtime_env_callback(serialized_env) + return unused + + def increase_reference( + self, runtime_env: RuntimeEnv, serialized_env: str, source_process: str + ) -> None: + if source_process in self._reference_exclude_sources: + return + self._increase_reference_for_runtime_env(serialized_env) + uris = self._uris_parser(runtime_env) + self._increase_reference_for_uris(uris) + + def decrease_reference( + self, runtime_env: RuntimeEnv, serialized_env: str, source_process: str + ) -> None: + if source_process in self._reference_exclude_sources: + return list() + self._decrease_reference_for_runtime_env(serialized_env) + uris = self._uris_parser(runtime_env) + self._decrease_reference_for_uris(uris) + + @property + def runtime_env_refs(self) -> Dict[str, int]: + """Return the runtime_env -> ref count mapping. + + Returns: + The mapping of serialized runtime env -> ref count. + """ + return self._runtime_env_reference + + +class RuntimeEnvAgent: + """An RPC server to create and delete runtime envs. + + Attributes: + dashboard_agent: The DashboardAgent object contains global config. + """ + + LOG_FILENAME = "runtime_env_agent.log" + + def __init__( + self, + runtime_env_dir, + logging_params, + gcs_address: str, + cluster_id_hex: str, + temp_dir, + address, + runtime_env_agent_port, + ): + super().__init__() + + self._logger = default_logger + self._logging_params = logging_params + self._logging_params.update(filename=self.LOG_FILENAME) + self._logger = setup_component_logger( + logger_name=default_logger.name, **self._logging_params + ) + # Don't propagate logs to the root logger, because these logs + # might contain sensitive information. Instead, these logs should + # be confined to the runtime env agent log file `self.LOG_FILENAME`. + self._logger.propagate = False + + self._logger.info("Starting runtime env agent at pid %s", os.getpid()) + self._logger.info(f"Parent raylet pid is {os.environ.get('RAY_RAYLET_PID')}") + + self._runtime_env_dir = runtime_env_dir + self._per_job_logger_cache = dict() + # Cache the results of creating envs to avoid repeatedly calling into + # conda and other slow calls. + self._env_cache: Dict[str, CreatedEnvResult] = dict() + # Maps a serialized runtime env to a lock that is used + # to prevent multiple concurrent installs of the same env. + self._env_locks: Dict[str, asyncio.Lock] = dict() + self._gcs_aio_client = GcsAioClient( + address=gcs_address, cluster_id=cluster_id_hex + ) + + self._pip_plugin = PipPlugin(self._runtime_env_dir) + self._uv_plugin = UvPlugin(self._runtime_env_dir) + self._conda_plugin = CondaPlugin(self._runtime_env_dir) + self._py_modules_plugin = PyModulesPlugin( + self._runtime_env_dir, self._gcs_aio_client + ) + self._java_jars_plugin = JavaJarsPlugin( + self._runtime_env_dir, self._gcs_aio_client + ) + self._working_dir_plugin = WorkingDirPlugin( + self._runtime_env_dir, self._gcs_aio_client + ) + self._container_plugin = ContainerPlugin(temp_dir) + # TODO(jonathan-anyscale): change the plugin to ProfilerPlugin + # and unify with nsight and other profilers. + self._nsight_plugin = NsightPlugin(self._runtime_env_dir) + self._mpi_plugin = MPIPlugin() + self._image_uri_plugin = get_image_uri_plugin_cls()(temp_dir) + + # TODO(architkulkarni): "base plugins" and third-party plugins should all go + # through the same code path. We should never need to refer to + # self._xxx_plugin, we should just iterate through self._plugins. + self._base_plugins: List[RuntimeEnvPlugin] = [ + self._working_dir_plugin, + self._uv_plugin, + self._pip_plugin, + self._conda_plugin, + self._py_modules_plugin, + self._java_jars_plugin, + self._container_plugin, + self._nsight_plugin, + self._mpi_plugin, + self._image_uri_plugin, + ] + self._plugin_manager = RuntimeEnvPluginManager() + for plugin in self._base_plugins: + self._plugin_manager.add_plugin(plugin) + + self._reference_table = ReferenceTable( + self.uris_parser, + self.unused_uris_processor, + self.unused_runtime_env_processor, + ) + + self._logger.info( + "Listening to address %s, port %d", address, runtime_env_agent_port + ) + + def uris_parser(self, runtime_env: RuntimeEnv): + result = list() + for name, plugin_setup_context in self._plugin_manager.plugins.items(): + plugin = plugin_setup_context.class_instance + uris = plugin.get_uris(runtime_env) + for uri in uris: + result.append((uri, UriType(name))) + return result + + def unused_uris_processor(self, unused_uris: List[Tuple[str, UriType]]) -> None: + for uri, uri_type in unused_uris: + self._plugin_manager.plugins[str(uri_type)].uri_cache.mark_unused(uri) + + def unused_runtime_env_processor(self, unused_runtime_env: str) -> None: + def delete_runtime_env(): + del self._env_cache[unused_runtime_env] + self._logger.info( + "Runtime env %s removed from env-level cache.", unused_runtime_env + ) + + if unused_runtime_env in self._env_cache: + if not self._env_cache[unused_runtime_env].success: + loop = get_or_create_event_loop() + # Cache the bad runtime env result by ttl seconds. + loop.call_later( + runtime_env_consts.BAD_RUNTIME_ENV_CACHE_TTL_SECONDS, + delete_runtime_env, + ) + else: + delete_runtime_env() + + def get_or_create_logger(self, job_id: bytes, log_files: List[str]): + job_id = job_id.decode() + if job_id not in self._per_job_logger_cache: + params = self._logging_params.copy() + params["filename"] = [f"runtime_env_setup-{job_id}.log", *log_files] + params["logger_name"] = f"runtime_env_{job_id}" + params["propagate"] = False + per_job_logger = setup_component_logger(**params) + self._per_job_logger_cache[job_id] = per_job_logger + return self._per_job_logger_cache[job_id] + + async def GetOrCreateRuntimeEnv(self, request): + self._logger.debug( + f"Got request from {request.source_process} to increase " + "reference for runtime env: " + f"{request.serialized_runtime_env}." + ) + + async def _setup_runtime_env( + runtime_env: RuntimeEnv, + runtime_env_config: RuntimeEnvConfig, + ): + log_files = runtime_env_config.get("log_files", []) + # Use a separate logger for each job. + per_job_logger = self.get_or_create_logger(request.job_id, log_files) + context = RuntimeEnvContext(env_vars=runtime_env.env_vars()) + + # Warn about unrecognized fields in the runtime env. + for name, _ in runtime_env.plugins(): + if name not in self._plugin_manager.plugins: + per_job_logger.warning( + f"runtime_env field {name} is not recognized by " + "Ray and will be ignored. In the future, unrecognized " + "fields in the runtime_env will raise an exception." + ) + + # Creates each runtime env URI by their priority. `working_dir` is special + # because it needs to be created before other plugins. All other plugins are + # created in the priority order (smaller priority value -> earlier to + # create), with a special environment variable being set to the working dir. + # ${RAY_RUNTIME_ENV_CREATE_WORKING_DIR} + + # First create working dir... + working_dir_ctx = self._plugin_manager.plugins[WorkingDirPlugin.name] + await create_for_plugin_if_needed( + runtime_env, + working_dir_ctx.class_instance, + working_dir_ctx.uri_cache, + context, + per_job_logger, + ) + + # Then within the working dir, create the other plugins. + working_dir_uri_or_none = runtime_env.working_dir_uri() + with self._working_dir_plugin.with_working_dir_env(working_dir_uri_or_none): + """Run setup for each plugin unless it has already been cached.""" + for ( + plugin_setup_context + ) in self._plugin_manager.sorted_plugin_setup_contexts(): + plugin = plugin_setup_context.class_instance + if plugin.name != WorkingDirPlugin.name: + uri_cache = plugin_setup_context.uri_cache + await create_for_plugin_if_needed( + runtime_env, plugin, uri_cache, context, per_job_logger + ) + return context + + async def _create_runtime_env_with_retry( + runtime_env, + setup_timeout_seconds, + runtime_env_config: RuntimeEnvConfig, + ) -> Tuple[bool, str, str]: + """ + Create runtime env with retry times. This function won't raise exceptions. + + Args: + runtime_env: The instance of RuntimeEnv class. + setup_timeout_seconds: The timeout of runtime environment creation for + each attempt. + + Returns: + a tuple which contains result (bool), runtime env context (str), error + message(str). + + """ + self._logger.info( + f"Creating runtime env: {serialized_env} with timeout " + f"{setup_timeout_seconds} seconds." + ) + serialized_context = None + error_message = None + for _ in range(runtime_env_consts.RUNTIME_ENV_RETRY_TIMES): + try: + runtime_env_setup_task = _setup_runtime_env( + runtime_env, runtime_env_config + ) + runtime_env_context = await asyncio.wait_for( + runtime_env_setup_task, timeout=setup_timeout_seconds + ) + serialized_context = runtime_env_context.serialize() + error_message = None + break + except Exception as e: + err_msg = f"Failed to create runtime env {serialized_env}." + self._logger.exception(err_msg) + error_message = "".join( + traceback.format_exception(type(e), e, e.__traceback__) + ) + if isinstance(e, asyncio.TimeoutError): + hint = ( + f"Failed to install runtime_env within the " + f"timeout of {setup_timeout_seconds} seconds. Consider " + "increasing the timeout in the runtime_env config. " + "For example: \n" + ' runtime_env={"config": {"setup_timeout_seconds":' + " 1800}, ...}\n" + "If not provided, the default timeout is " + f"{DEFAULT_RUNTIME_ENV_TIMEOUT_SECONDS} seconds. " + ) + error_message = hint + error_message + await asyncio.sleep( + runtime_env_consts.RUNTIME_ENV_RETRY_INTERVAL_MS / 1000 + ) + if error_message: + self._logger.error( + "Runtime env creation failed for %d times, " + "don't retry any more.", + runtime_env_consts.RUNTIME_ENV_RETRY_TIMES, + ) + return False, None, error_message + else: + self._logger.info( + "Successfully created runtime env: %s, the context: %s", + serialized_env, + serialized_context, + ) + return True, serialized_context, None + + try: + serialized_env = request.serialized_runtime_env + runtime_env = RuntimeEnv.deserialize(serialized_env) + except Exception as e: + self._logger.exception( + "[Increase] Failed to parse runtime env: " f"{serialized_env}" + ) + return runtime_env_agent_pb2.GetOrCreateRuntimeEnvReply( + status=agent_manager_pb2.AGENT_RPC_STATUS_FAILED, + error_message="".join( + traceback.format_exception(type(e), e, e.__traceback__) + ), + ) + + # Increase reference + self._reference_table.increase_reference( + runtime_env, serialized_env, request.source_process + ) + + if serialized_env not in self._env_locks: + # async lock to prevent the same env being concurrently installed + self._env_locks[serialized_env] = asyncio.Lock() + + async with self._env_locks[serialized_env]: + if serialized_env in self._env_cache: + serialized_context = self._env_cache[serialized_env] + result = self._env_cache[serialized_env] + if result.success: + context = result.result + self._logger.info( + "Runtime env already created " + f"successfully. Env: {serialized_env}, " + f"context: {context}" + ) + return runtime_env_agent_pb2.GetOrCreateRuntimeEnvReply( + status=agent_manager_pb2.AGENT_RPC_STATUS_OK, + serialized_runtime_env_context=context, + ) + else: + error_message = result.result + self._logger.info( + "Runtime env already failed. " + f"Env: {serialized_env}, " + f"err: {error_message}" + ) + # Recover the reference. + self._reference_table.decrease_reference( + runtime_env, serialized_env, request.source_process + ) + return runtime_env_agent_pb2.GetOrCreateRuntimeEnvReply( + status=agent_manager_pb2.AGENT_RPC_STATUS_FAILED, + error_message=error_message, + ) + + if SLEEP_FOR_TESTING_S: + self._logger.info(f"Sleeping for {SLEEP_FOR_TESTING_S}s.") + time.sleep(int(SLEEP_FOR_TESTING_S)) + + runtime_env_config = RuntimeEnvConfig.from_proto(request.runtime_env_config) + + # accroding to the document of `asyncio.wait_for`, + # None means disable timeout logic + setup_timeout_seconds = ( + None + if runtime_env_config["setup_timeout_seconds"] == -1 + else runtime_env_config["setup_timeout_seconds"] + ) + + start = time.perf_counter() + ( + successful, + serialized_context, + error_message, + ) = await _create_runtime_env_with_retry( + runtime_env, + setup_timeout_seconds, + runtime_env_config, + ) + creation_time_ms = int(round((time.perf_counter() - start) * 1000, 0)) + if not successful: + # Recover the reference. + self._reference_table.decrease_reference( + runtime_env, serialized_env, request.source_process + ) + # Add the result to env cache. + self._env_cache[serialized_env] = CreatedEnvResult( + successful, + serialized_context if successful else error_message, + creation_time_ms, + ) + # Reply the RPC + return runtime_env_agent_pb2.GetOrCreateRuntimeEnvReply( + status=agent_manager_pb2.AGENT_RPC_STATUS_OK + if successful + else agent_manager_pb2.AGENT_RPC_STATUS_FAILED, + serialized_runtime_env_context=serialized_context, + error_message=error_message, + ) + + async def DeleteRuntimeEnvIfPossible(self, request): + self._logger.info( + f"Got request from {request.source_process} to decrease " + "reference for runtime env: " + f"{request.serialized_runtime_env}." + ) + + try: + runtime_env = RuntimeEnv.deserialize(request.serialized_runtime_env) + except Exception as e: + self._logger.exception( + "[Decrease] Failed to parse runtime env: " + f"{request.serialized_runtime_env}" + ) + return runtime_env_agent_pb2.GetOrCreateRuntimeEnvReply( + status=agent_manager_pb2.AGENT_RPC_STATUS_FAILED, + error_message="".join( + traceback.format_exception(type(e), e, e.__traceback__) + ), + ) + + self._reference_table.decrease_reference( + runtime_env, request.serialized_runtime_env, request.source_process + ) + + return runtime_env_agent_pb2.DeleteRuntimeEnvIfPossibleReply( + status=agent_manager_pb2.AGENT_RPC_STATUS_OK + ) + + async def GetRuntimeEnvsInfo(self, request): + """Return the runtime env information of the node.""" + # TODO(sang): Currently, it only includes runtime_env information. + # We should include the URI information which includes, + # URIs + # Caller + # Ref counts + # Cache information + # Metrics (creation time & success) + # Deleted URIs + limit = request.limit if request.HasField("limit") else -1 + runtime_env_states = defaultdict(ProtoRuntimeEnvState) + runtime_env_refs = self._reference_table.runtime_env_refs + for runtime_env, ref_cnt in runtime_env_refs.items(): + runtime_env_states[runtime_env].runtime_env = runtime_env + runtime_env_states[runtime_env].ref_cnt = ref_cnt + for runtime_env, result in self._env_cache.items(): + runtime_env_states[runtime_env].runtime_env = runtime_env + runtime_env_states[runtime_env].success = result.success + if not result.success: + runtime_env_states[runtime_env].error = result.result + runtime_env_states[runtime_env].creation_time_ms = result.creation_time_ms + + reply = runtime_env_agent_pb2.GetRuntimeEnvsInfoReply() + count = 0 + for runtime_env_state in runtime_env_states.values(): + if limit != -1 and count >= limit: + break + count += 1 + reply.runtime_env_states.append(runtime_env_state) + reply.total = len(runtime_env_states) + return reply diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/runtime_env_consts.py b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/runtime_env_consts.py new file mode 100644 index 0000000000000000000000000000000000000000..31545913168fa29397995d4d0c6c118fe583365f --- /dev/null +++ b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/runtime_env_consts.py @@ -0,0 +1,20 @@ +import ray._private.ray_constants as ray_constants + +RUNTIME_ENV_RETRY_TIMES = ray_constants.env_integer("RUNTIME_ENV_RETRY_TIMES", 3) + +RUNTIME_ENV_RETRY_INTERVAL_MS = ray_constants.env_integer( + "RUNTIME_ENV_RETRY_INTERVAL_MS", 1000 +) + +# Cache TTL for bad runtime env. After this time, delete the cache and retry to create +# runtime env if needed. +BAD_RUNTIME_ENV_CACHE_TTL_SECONDS = ray_constants.env_integer( + "BAD_RUNTIME_ENV_CACHE_TTL_SECONDS", 60 * 10 +) + +RUNTIME_ENV_LOG_FILENAME = "runtime_env.log" +RUNTIME_ENV_AGENT_PORT_PREFIX = "RUNTIME_ENV_AGENT_PORT_PREFIX:" +RUNTIME_ENV_AGENT_LOG_FILENAME = "runtime_env_agent.log" +RUNTIME_ENV_AGENT_CHECK_PARENT_INTERVAL_S_ENV_NAME = ( + "RAY_RUNTIME_ENV_AGENT_CHECK_PARENT_INTERVAL_S" # noqa +) diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiohttp-3.11.11.dist-info/INSTALLER b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiohttp-3.11.11.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..a1b589e38a32041e49332e5e81c2d363dc418d68 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiohttp-3.11.11.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiohttp-3.11.11.dist-info/LICENSE.txt b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiohttp-3.11.11.dist-info/LICENSE.txt new file mode 100644 index 0000000000000000000000000000000000000000..e497a322f2091d022983b9c5c043082ab61d1a8c --- /dev/null +++ b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiohttp-3.11.11.dist-info/LICENSE.txt @@ -0,0 +1,13 @@ + Copyright aio-libs contributors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiohttp-3.11.11.dist-info/METADATA b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiohttp-3.11.11.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..f724eee49b251a8bfb154104ca9e4498876b8ec1 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiohttp-3.11.11.dist-info/METADATA @@ -0,0 +1,250 @@ +Metadata-Version: 2.1 +Name: aiohttp +Version: 3.11.11 +Summary: Async http client/server framework (asyncio) +Home-page: https://github.com/aio-libs/aiohttp +Maintainer: aiohttp team +Maintainer-email: team@aiohttp.org +License: Apache-2.0 +Project-URL: Chat: Matrix, https://matrix.to/#/#aio-libs:matrix.org +Project-URL: Chat: Matrix Space, https://matrix.to/#/#aio-libs-space:matrix.org +Project-URL: CI: GitHub Actions, https://github.com/aio-libs/aiohttp/actions?query=workflow%3ACI +Project-URL: Coverage: codecov, https://codecov.io/github/aio-libs/aiohttp +Project-URL: Docs: Changelog, https://docs.aiohttp.org/en/stable/changes.html +Project-URL: Docs: RTD, https://docs.aiohttp.org +Project-URL: GitHub: issues, https://github.com/aio-libs/aiohttp/issues +Project-URL: GitHub: repo, https://github.com/aio-libs/aiohttp +Classifier: Development Status :: 5 - Production/Stable +Classifier: Framework :: AsyncIO +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Operating System :: POSIX +Classifier: Operating System :: MacOS :: MacOS X +Classifier: Operating System :: Microsoft :: Windows +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Topic :: Internet :: WWW/HTTP +Requires-Python: >=3.9 +Description-Content-Type: text/x-rst +License-File: LICENSE.txt +Requires-Dist: aiohappyeyeballs>=2.3.0 +Requires-Dist: aiosignal>=1.1.2 +Requires-Dist: async-timeout<6.0,>=4.0; python_version < "3.11" +Requires-Dist: attrs>=17.3.0 +Requires-Dist: frozenlist>=1.1.1 +Requires-Dist: multidict<7.0,>=4.5 +Requires-Dist: propcache>=0.2.0 +Requires-Dist: yarl<2.0,>=1.17.0 +Provides-Extra: speedups +Requires-Dist: aiodns>=3.2.0; (sys_platform == "linux" or sys_platform == "darwin") and extra == "speedups" +Requires-Dist: Brotli; platform_python_implementation == "CPython" and extra == "speedups" +Requires-Dist: brotlicffi; platform_python_implementation != "CPython" and extra == "speedups" + +================================== +Async http client/server framework +================================== + +.. image:: https://raw.githubusercontent.com/aio-libs/aiohttp/master/docs/aiohttp-plain.svg + :height: 64px + :width: 64px + :alt: aiohttp logo + +| + +.. image:: https://github.com/aio-libs/aiohttp/workflows/CI/badge.svg + :target: https://github.com/aio-libs/aiohttp/actions?query=workflow%3ACI + :alt: GitHub Actions status for master branch + +.. image:: https://codecov.io/gh/aio-libs/aiohttp/branch/master/graph/badge.svg + :target: https://codecov.io/gh/aio-libs/aiohttp + :alt: codecov.io status for master branch + +.. image:: https://img.shields.io/endpoint?url=https://codspeed.io/badge.json + :target: https://codspeed.io/aio-libs/aiohttp + :alt: Codspeed.io status for aiohttp + +.. image:: https://badge.fury.io/py/aiohttp.svg + :target: https://pypi.org/project/aiohttp + :alt: Latest PyPI package version + +.. image:: https://readthedocs.org/projects/aiohttp/badge/?version=latest + :target: https://docs.aiohttp.org/ + :alt: Latest Read The Docs + +.. image:: https://img.shields.io/matrix/aio-libs:matrix.org?label=Discuss%20on%20Matrix%20at%20%23aio-libs%3Amatrix.org&logo=matrix&server_fqdn=matrix.org&style=flat + :target: https://matrix.to/#/%23aio-libs:matrix.org + :alt: Matrix Room — #aio-libs:matrix.org + +.. image:: https://img.shields.io/matrix/aio-libs-space:matrix.org?label=Discuss%20on%20Matrix%20at%20%23aio-libs-space%3Amatrix.org&logo=matrix&server_fqdn=matrix.org&style=flat + :target: https://matrix.to/#/%23aio-libs-space:matrix.org + :alt: Matrix Space — #aio-libs-space:matrix.org + + +Key Features +============ + +- Supports both client and server side of HTTP protocol. +- Supports both client and server Web-Sockets out-of-the-box and avoids + Callback Hell. +- Provides Web-server with middleware and pluggable routing. + + +Getting started +=============== + +Client +------ + +To get something from the web: + +.. code-block:: python + + import aiohttp + import asyncio + + async def main(): + + async with aiohttp.ClientSession() as session: + async with session.get('http://python.org') as response: + + print("Status:", response.status) + print("Content-type:", response.headers['content-type']) + + html = await response.text() + print("Body:", html[:15], "...") + + asyncio.run(main()) + +This prints: + +.. code-block:: + + Status: 200 + Content-type: text/html; charset=utf-8 + Body: ... + +Coming from `requests `_ ? Read `why we need so many lines `_. + +Server +------ + +An example using a simple server: + +.. code-block:: python + + # examples/server_simple.py + from aiohttp import web + + async def handle(request): + name = request.match_info.get('name', "Anonymous") + text = "Hello, " + name + return web.Response(text=text) + + async def wshandle(request): + ws = web.WebSocketResponse() + await ws.prepare(request) + + async for msg in ws: + if msg.type == web.WSMsgType.text: + await ws.send_str("Hello, {}".format(msg.data)) + elif msg.type == web.WSMsgType.binary: + await ws.send_bytes(msg.data) + elif msg.type == web.WSMsgType.close: + break + + return ws + + + app = web.Application() + app.add_routes([web.get('/', handle), + web.get('/echo', wshandle), + web.get('/{name}', handle)]) + + if __name__ == '__main__': + web.run_app(app) + + +Documentation +============= + +https://aiohttp.readthedocs.io/ + + +Demos +===== + +https://github.com/aio-libs/aiohttp-demos + + +External links +============== + +* `Third party libraries + `_ +* `Built with aiohttp + `_ +* `Powered by aiohttp + `_ + +Feel free to make a Pull Request for adding your link to these pages! + + +Communication channels +====================== + +*aio-libs Discussions*: https://github.com/aio-libs/aiohttp/discussions + +*Matrix*: `#aio-libs:matrix.org `_ + +We support `Stack Overflow +`_. +Please add *aiohttp* tag to your question there. + +Requirements +============ + +- attrs_ +- multidict_ +- yarl_ +- frozenlist_ + +Optionally you may install the aiodns_ library (highly recommended for sake of speed). + +.. _aiodns: https://pypi.python.org/pypi/aiodns +.. _attrs: https://github.com/python-attrs/attrs +.. _multidict: https://pypi.python.org/pypi/multidict +.. _frozenlist: https://pypi.org/project/frozenlist/ +.. _yarl: https://pypi.python.org/pypi/yarl +.. _async-timeout: https://pypi.python.org/pypi/async_timeout + +License +======= + +``aiohttp`` is offered under the Apache 2 license. + + +Keepsafe +======== + +The aiohttp community would like to thank Keepsafe +(https://www.getkeepsafe.com) for its support in the early days of +the project. + + +Source code +=========== + +The latest developer version is available in a GitHub repository: +https://github.com/aio-libs/aiohttp + +Benchmarks +========== + +If you are interested in efficiency, the AsyncIO community maintains a +list of benchmarks on the official wiki: +https://github.com/python/asyncio/wiki/Benchmarks diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiohttp-3.11.11.dist-info/RECORD b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiohttp-3.11.11.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..a893715c1f58b8fc401b1b9cdaae6cbf3045eb97 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiohttp-3.11.11.dist-info/RECORD @@ -0,0 +1,132 @@ +aiohttp-3.11.11.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +aiohttp-3.11.11.dist-info/LICENSE.txt,sha256=n4DQ2311WpQdtFchcsJw7L2PCCuiFd3QlZhZQu2Uqes,588 +aiohttp-3.11.11.dist-info/METADATA,sha256=O_1EZEKVeXKI4c8DLjvHPGK3V7eiUup0765nWX0-TJ0,7712 +aiohttp-3.11.11.dist-info/RECORD,, +aiohttp-3.11.11.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +aiohttp-3.11.11.dist-info/WHEEL,sha256=9BFfIe-Zq441iQ0ehutX65O5faGDpmB1Uw3WaQGk4f0,151 +aiohttp-3.11.11.dist-info/top_level.txt,sha256=iv-JIaacmTl-hSho3QmphcKnbRRYx1st47yjz_178Ro,8 +aiohttp/.hash/_cparser.pxd.hash,sha256=hYa9Vje-oMs2eh_7MfCPOh2QW_1x1yCjcZuc7AmwLd0,121 +aiohttp/.hash/_find_header.pxd.hash,sha256=_mbpD6vM-CVCKq3ulUvsOAz5Wdo88wrDzfpOsMQaMNA,125 +aiohttp/.hash/_http_parser.pyx.hash,sha256=GBgZjCNbtZApPhf9-gHpS5Z2WMIzM-vgp5VSZIEvZfk,125 +aiohttp/.hash/_http_writer.pyx.hash,sha256=-UgSF82qclpxjP0og_gcFEsstXRKF9e3Ou4wziAyDvI,125 +aiohttp/.hash/hdrs.py.hash,sha256=v6IaKbsxjsdQxBzhb5AjP0x_9G3rUe84D7avf7AI4cs,116 +aiohttp/__init__.py,sha256=Nqvv0TLm_6R9zrZPfJfRN9SRB04gUzWdYrPpUvTfD7Q,7840 +aiohttp/__pycache__/__init__.cpython-311.pyc,, +aiohttp/__pycache__/abc.cpython-311.pyc,, +aiohttp/__pycache__/base_protocol.cpython-311.pyc,, +aiohttp/__pycache__/client.cpython-311.pyc,, +aiohttp/__pycache__/client_exceptions.cpython-311.pyc,, +aiohttp/__pycache__/client_proto.cpython-311.pyc,, +aiohttp/__pycache__/client_reqrep.cpython-311.pyc,, +aiohttp/__pycache__/client_ws.cpython-311.pyc,, +aiohttp/__pycache__/compression_utils.cpython-311.pyc,, +aiohttp/__pycache__/connector.cpython-311.pyc,, +aiohttp/__pycache__/cookiejar.cpython-311.pyc,, +aiohttp/__pycache__/formdata.cpython-311.pyc,, +aiohttp/__pycache__/hdrs.cpython-311.pyc,, +aiohttp/__pycache__/helpers.cpython-311.pyc,, +aiohttp/__pycache__/http.cpython-311.pyc,, +aiohttp/__pycache__/http_exceptions.cpython-311.pyc,, +aiohttp/__pycache__/http_parser.cpython-311.pyc,, +aiohttp/__pycache__/http_websocket.cpython-311.pyc,, +aiohttp/__pycache__/http_writer.cpython-311.pyc,, +aiohttp/__pycache__/log.cpython-311.pyc,, +aiohttp/__pycache__/multipart.cpython-311.pyc,, +aiohttp/__pycache__/payload.cpython-311.pyc,, +aiohttp/__pycache__/payload_streamer.cpython-311.pyc,, +aiohttp/__pycache__/pytest_plugin.cpython-311.pyc,, +aiohttp/__pycache__/resolver.cpython-311.pyc,, +aiohttp/__pycache__/streams.cpython-311.pyc,, +aiohttp/__pycache__/tcp_helpers.cpython-311.pyc,, +aiohttp/__pycache__/test_utils.cpython-311.pyc,, +aiohttp/__pycache__/tracing.cpython-311.pyc,, +aiohttp/__pycache__/typedefs.cpython-311.pyc,, +aiohttp/__pycache__/web.cpython-311.pyc,, +aiohttp/__pycache__/web_app.cpython-311.pyc,, +aiohttp/__pycache__/web_exceptions.cpython-311.pyc,, +aiohttp/__pycache__/web_fileresponse.cpython-311.pyc,, +aiohttp/__pycache__/web_log.cpython-311.pyc,, +aiohttp/__pycache__/web_middlewares.cpython-311.pyc,, +aiohttp/__pycache__/web_protocol.cpython-311.pyc,, +aiohttp/__pycache__/web_request.cpython-311.pyc,, +aiohttp/__pycache__/web_response.cpython-311.pyc,, +aiohttp/__pycache__/web_routedef.cpython-311.pyc,, +aiohttp/__pycache__/web_runner.cpython-311.pyc,, +aiohttp/__pycache__/web_server.cpython-311.pyc,, +aiohttp/__pycache__/web_urldispatcher.cpython-311.pyc,, +aiohttp/__pycache__/web_ws.cpython-311.pyc,, +aiohttp/__pycache__/worker.cpython-311.pyc,, +aiohttp/_cparser.pxd,sha256=8jGIg-VJ9p3llwCakUYDsPGxA4HiZe9dmK9Jmtlz-5g,4318 +aiohttp/_find_header.pxd,sha256=0GfwFCPN2zxEKTO1_MA5sYq2UfzsG8kcV3aTqvwlz3g,68 +aiohttp/_headers.pxi,sha256=n701k28dVPjwRnx5j6LpJhLTfj7dqu2vJt7f0O60Oyg,2007 +aiohttp/_http_parser.cpython-311-x86_64-linux-gnu.so,sha256=rulksFv5PZ7AOPoQuR2P1THFflfukMumxkEfRfdFk0Y,2826344 +aiohttp/_http_parser.pyx,sha256=wQdADj5LizwC_7nFGr8nIlk6GpoaQeQ0359H0HMKGuM,28241 +aiohttp/_http_writer.cpython-311-x86_64-linux-gnu.so,sha256=zMYCtCYo-xjYJpSCcVx6UdBpGSHm_pMgnpM7JUmsWXA,463752 +aiohttp/_http_writer.pyx,sha256=fiCck_EVgRiTX7VtAoV2AldjuesJMFPev4TWd9Fx8jo,4597 +aiohttp/_websocket/.hash/mask.pxd.hash,sha256=Y0zBddk_ck3pi9-BFzMcpkcvCKvwvZ4GTtZFb9u1nxQ,128 +aiohttp/_websocket/.hash/mask.pyx.hash,sha256=90owpXYM8_kIma4KUcOxhWSk-Uv4NVMBoCYeFM1B3d0,128 +aiohttp/_websocket/.hash/reader_c.pxd.hash,sha256=EoZjkF_tAFEbGvV0oRY2GZOSuAfWFWFjMhXgq6mQExo,132 +aiohttp/_websocket/__init__.py,sha256=Mar3R9_vBN_Ea4lsW7iTAVXD7OKswKPGqF5xgSyt77k,44 +aiohttp/_websocket/__pycache__/__init__.cpython-311.pyc,, +aiohttp/_websocket/__pycache__/helpers.cpython-311.pyc,, +aiohttp/_websocket/__pycache__/models.cpython-311.pyc,, +aiohttp/_websocket/__pycache__/reader.cpython-311.pyc,, +aiohttp/_websocket/__pycache__/reader_c.cpython-311.pyc,, +aiohttp/_websocket/__pycache__/reader_py.cpython-311.pyc,, +aiohttp/_websocket/__pycache__/writer.cpython-311.pyc,, +aiohttp/_websocket/helpers.py,sha256=P-XLv8IUaihKzDenVUqfKU5DJbWE5HvG8uhvUZK8Ic4,5038 +aiohttp/_websocket/mask.cpython-311-x86_64-linux-gnu.so,sha256=hq3aHe5ZVl5ENFRtaXjZcSrbS-ITBwqGgEneGVphY1w,245952 +aiohttp/_websocket/mask.pxd,sha256=sBmZ1Amym9kW4Ge8lj1fLZ7mPPya4LzLdpkQExQXv5M,112 +aiohttp/_websocket/mask.pyx,sha256=BHjOtV0O0w7xp9p0LNADRJvGmgfPn9sGeJvSs0fL__4,1397 +aiohttp/_websocket/models.py,sha256=XAzjs_8JYszWXIgZ6R3ZRrF-tX9Q_6LiD49WRYojopM,2121 +aiohttp/_websocket/reader.py,sha256=eC4qS0c5sOeQ2ebAHLaBpIaTVFaSKX79pY2xvh3Pqyw,1030 +aiohttp/_websocket/reader_c.cpython-311-x86_64-linux-gnu.so,sha256=bnETOmm4gSxKkarM68sAEhBjSd_bQt0GgDD2W7V8cUQ,1906008 +aiohttp/_websocket/reader_c.pxd,sha256=9rMWCpAC1jng7_gtqLjRlqQv9q7UkOn63tIQfq2k8Gc,2444 +aiohttp/_websocket/reader_c.py,sha256=anZsBKZWlL8SO8gArsZMDstH37qBuZOvJA7jtj0Z95M,17975 +aiohttp/_websocket/reader_py.py,sha256=anZsBKZWlL8SO8gArsZMDstH37qBuZOvJA7jtj0Z95M,17975 +aiohttp/_websocket/writer.py,sha256=T3P36iMrzVPPC2XeScserHMD5vd9an6yizWzqDUkRZ0,7077 +aiohttp/abc.py,sha256=JLMOxrKLGTDaPRLfraY1pl-xka53YiHhAH9yaF9QRXQ,6512 +aiohttp/base_protocol.py,sha256=Tp8cxUPQvv9kUPk3w6lAzk6d2MAzV3scwI_3Go3C47c,3025 +aiohttp/client.py,sha256=EPaO_Y4VpTdIAbcOfMwBGrzSfBUcYampYwZN3GO2Vm4,55006 +aiohttp/client_exceptions.py,sha256=uyKbxI2peZhKl7lELBMx3UeusNkfpemPWpGFq0r6JeM,11367 +aiohttp/client_proto.py,sha256=dV7u9floGWG-_xtD2fLUYqiANG6VsJtq0HMlTjf1g-g,10015 +aiohttp/client_reqrep.py,sha256=VAgh0NxP2HvYWx6nX1Pr8FINc1m-W8-5q2zKeZV68n8,43925 +aiohttp/client_ws.py,sha256=_n4hVk71H5rK8TFOIYT0bPTIHOmMCQ3FDFSrU7ctpfI,15031 +aiohttp/compression_utils.py,sha256=0J3EAOR-0HehlYIudJXRu_Kr6hrYCY0IfuJ1px9MhQs,5681 +aiohttp/connector.py,sha256=yW8vyZz4cmXbScbBkCneMF0giSl0WZPJ2NnNw-TegbQ,60225 +aiohttp/cookiejar.py,sha256=PYR1K1mkLa24Hm6c9UEJnAitccNzz97CbsJyQ2ULAlU,17615 +aiohttp/formdata.py,sha256=CUJnCWDNHFcXSYZ_TupaT6rHkY-Q7ghssvWzaYBPIo0,6552 +aiohttp/hdrs.py,sha256=2rj5MyA-6yRdYPhW5UKkW4iNWhEAlGIOSBH5D4FmKNE,5111 +aiohttp/helpers.py,sha256=KqPQECeiJ_EhA93k7-5ZoVdZH0sk_4n0tCoM_E-iMnE,29091 +aiohttp/http.py,sha256=8o8j8xH70OWjnfTWA9V44NR785QPxEPrUtzMXiAVpwc,1842 +aiohttp/http_exceptions.py,sha256=RYmBycJvvPerKkgXXm8v145I1N-fbsgSpcsbNIC-gdE,2961 +aiohttp/http_parser.py,sha256=UqerYPJzA1MqLmeG1jURhTNO1YhwUASK3QVcNEz0me8,36851 +aiohttp/http_websocket.py,sha256=8VXFKw6KQUEmPg48GtRMB37v0gTK7A0inoxXuDxMZEc,842 +aiohttp/http_writer.py,sha256=RRQlUxD7K98aONPrrE9DGkWVWSuTDTzM1cr8xRaRIvw,7031 +aiohttp/log.py,sha256=BbNKx9e3VMIm0xYjZI0IcBBoS7wjdeIeSaiJE7-qK2g,325 +aiohttp/multipart.py,sha256=1jIh7GEFgSL-cnLQzbNBLWXHJlB4WKyy0NBm_i1Y3V4,36942 +aiohttp/payload.py,sha256=rCA9JJI_RMCik_7qNIaC1Bh21aXhABGYK2tsYeaHRQ4,15793 +aiohttp/payload_streamer.py,sha256=ZzEYyfzcjGWkVkK3XR2pBthSCSIykYvY3Wr5cGQ2eTc,2211 +aiohttp/py.typed,sha256=sow9soTwP9T_gEAQSVh7Gb8855h04Nwmhs2We-JRgZM,7 +aiohttp/pytest_plugin.py,sha256=AfJ6VIWzsp5KgpXRREsX3yqGUZrJyfb7zzcMqzWxz7I,12768 +aiohttp/resolver.py,sha256=sJ8-LYCtl_g9f6gn_5X2NFQ9FQ72Q2Mr4_rLxo9NVeI,6375 +aiohttp/streams.py,sha256=s4qMxBLQMMuXHUFxTeOScfX4apL8rWWvC2pxdrrTr98,22300 +aiohttp/tcp_helpers.py,sha256=BSadqVWaBpMFDRWnhaaR941N9MiDZ7bdTrxgCb0CW-M,961 +aiohttp/test_utils.py,sha256=r7kBasmZtC3tQY5OmyMaIl1B9P8Bnnq1oM3npVcAPKs,22811 +aiohttp/tracing.py,sha256=66XQwtdR5DHv8p953eeNL0l8o6iHDaNwH9bBaybHXD4,15137 +aiohttp/typedefs.py,sha256=wUlqwe9Mw9W8jT3HsYJcYk00qP3EMPz3nTkYXmeNN48,1657 +aiohttp/web.py,sha256=As5nqGQy4QXWMXSaOsh0JudSVVJVIt_nr3n0b8CaMb0,18422 +aiohttp/web_app.py,sha256=Zre0QHM9JAp4d7jrj5NRxlPnfTrKLNuA42Rdsh8Q2TI,19554 +aiohttp/web_exceptions.py,sha256=7nIuiwhZ39vJJ9KrWqArA5QcWbUdqkz2CLwEpJapeN8,10360 +aiohttp/web_fileresponse.py,sha256=FRsS0p9r1KU5y8ceG0QXBYnrL6xggjbxcXSmI6qIR4k,16504 +aiohttp/web_log.py,sha256=rX5D7xLOX2B6BMdiZ-chme_KfJfW5IXEoFwLfkfkajs,7865 +aiohttp/web_middlewares.py,sha256=sFI0AgeNjdyAjuz92QtMIpngmJSOxrqe2Jfbs4BNUu0,4165 +aiohttp/web_protocol.py,sha256=WZFtp5Zoxr0AKft1h_1ExiAB6Yw0UwpHUM-edd8Thq0,25522 +aiohttp/web_request.py,sha256=j_SSX9s-d3ZeNyqUTpFIaPUaNdSqHwb7yfc0ufL8xFA,29750 +aiohttp/web_response.py,sha256=65aliDETi7rZ8P76ksuHQI0ZTu1cKpclCSailNu105M,28696 +aiohttp/web_routedef.py,sha256=VT1GAx6BrawoDh5RwBwBu5wSABSqgWwAe74AUCyZAEo,6110 +aiohttp/web_runner.py,sha256=v1G1nKiOOQgFnTSR4IMc6I9ReEFDMaHtMLvO_roDM-A,11786 +aiohttp/web_server.py,sha256=-9WDKUAiR9ll-rSdwXSqG6YjaoW79d1R4y0BGSqgUMA,2888 +aiohttp/web_urldispatcher.py,sha256=TIMxFmhLjERseG0xcZv2Ef9Xuo_GTBRqBqeMkCgL0K8,43825 +aiohttp/web_ws.py,sha256=EOQX3LYqlrkNQHlFTaNpZkQpOYRCZfR-m1bHT4Iseq8,22488 +aiohttp/worker.py,sha256=0lvxRNMjGM47ddlQWtci53ri9YN42Su1Vdd_Z7zMMH0,8040 diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiohttp-3.11.11.dist-info/REQUESTED b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiohttp-3.11.11.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiohttp-3.11.11.dist-info/WHEEL b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiohttp-3.11.11.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..8b0363604372a86351ec451bb3c5afa26234e6f0 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiohttp-3.11.11.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: setuptools (75.6.0) +Root-Is-Purelib: false +Tag: cp311-cp311-manylinux_2_17_x86_64 +Tag: cp311-cp311-manylinux2014_x86_64 + diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiohttp-3.11.11.dist-info/top_level.txt b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiohttp-3.11.11.dist-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..ee4ba4f3d739e094878215c84eb41ba85c80e4a8 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiohttp-3.11.11.dist-info/top_level.txt @@ -0,0 +1 @@ +aiohttp diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiosignal/__init__.py b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiosignal/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..4ad0278993a986cecabf966d7b6ffd0c58f060e4 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiosignal/__init__.py @@ -0,0 +1,36 @@ +from frozenlist import FrozenList + +__version__ = "1.3.2" + +__all__ = ("Signal",) + + +class Signal(FrozenList): + """Coroutine-based signal implementation. + + To connect a callback to a signal, use any list method. + + Signals are fired using the send() coroutine, which takes named + arguments. + """ + + __slots__ = ("_owner",) + + def __init__(self, owner): + super().__init__() + self._owner = owner + + def __repr__(self): + return "".format( + self._owner, self.frozen, list(self) + ) + + async def send(self, *args, **kwargs): + """ + Sends data to all registered receivers. + """ + if not self.frozen: + raise RuntimeError("Cannot send non-frozen signal.") + + for receiver in self: + await receiver(*args, **kwargs) # type: ignore diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiosignal/__init__.pyi b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiosignal/__init__.pyi new file mode 100644 index 0000000000000000000000000000000000000000..d4e3416d72246058259061578a82697e2bc0706e --- /dev/null +++ b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiosignal/__init__.pyi @@ -0,0 +1,12 @@ +from typing import Any, Generic, TypeVar + +from frozenlist import FrozenList + +__all__ = ("Signal",) + +_T = TypeVar("_T") + +class Signal(FrozenList[_T], Generic[_T]): + def __init__(self, owner: Any) -> None: ... + def __repr__(self) -> str: ... + async def send(self, *args: Any, **kwargs: Any) -> None: ... diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiosignal/py.typed b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/aiosignal/py.typed new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/frozenlist-1.5.0.dist-info/INSTALLER b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/frozenlist-1.5.0.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..a1b589e38a32041e49332e5e81c2d363dc418d68 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/frozenlist-1.5.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/frozenlist-1.5.0.dist-info/LICENSE b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/frozenlist-1.5.0.dist-info/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..7082a2d5b9047bfc09589f387053e24ea490bc54 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/frozenlist-1.5.0.dist-info/LICENSE @@ -0,0 +1,201 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2013-2019 Nikolay Kim and Andrew Svetlov + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/frozenlist-1.5.0.dist-info/METADATA b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/frozenlist-1.5.0.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..5b96937af16d2f8fab07eb0cd808e7f9f9d9e509 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/frozenlist-1.5.0.dist-info/METADATA @@ -0,0 +1,477 @@ +Metadata-Version: 2.1 +Name: frozenlist +Version: 1.5.0 +Summary: A list-like structure which implements collections.abc.MutableSequence +Home-page: https://github.com/aio-libs/frozenlist +Maintainer: aiohttp team +Maintainer-email: team@aiohttp.org +License: Apache 2 +Project-URL: Chat: Matrix, https://matrix.to/#/#aio-libs:matrix.org +Project-URL: Chat: Matrix Space, https://matrix.to/#/#aio-libs-space:matrix.org +Project-URL: CI: Github Actions, https://github.com/aio-libs/frozenlist/actions +Project-URL: Code of Conduct, https://github.com/aio-libs/.github/blob/master/CODE_OF_CONDUCT.md +Project-URL: Coverage: codecov, https://codecov.io/github/aio-libs/frozenlist +Project-URL: Docs: Changelog, https://github.com/aio-libs/frozenlist/blob/master/CHANGES.rst#changelog +Project-URL: Docs: RTD, https://frozenlist.aio-libs.org +Project-URL: GitHub: issues, https://github.com/aio-libs/frozenlist/issues +Project-URL: GitHub: repo, https://github.com/aio-libs/frozenlist +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Operating System :: POSIX +Classifier: Operating System :: MacOS :: MacOS X +Classifier: Operating System :: Microsoft :: Windows +Classifier: Programming Language :: Cython +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Requires-Python: >=3.8 +Description-Content-Type: text/x-rst +License-File: LICENSE + +frozenlist +========== + +.. image:: https://github.com/aio-libs/frozenlist/workflows/CI/badge.svg + :target: https://github.com/aio-libs/frozenlist/actions + :alt: GitHub status for master branch + +.. image:: https://codecov.io/gh/aio-libs/frozenlist/branch/master/graph/badge.svg + :target: https://codecov.io/gh/aio-libs/frozenlist + :alt: codecov.io status for master branch + +.. image:: https://img.shields.io/pypi/v/frozenlist.svg?logo=Python&logoColor=white + :target: https://pypi.org/project/frozenlist + :alt: frozenlist @ PyPI + +.. image:: https://readthedocs.org/projects/frozenlist/badge/?version=latest + :target: https://frozenlist.aio-libs.org + :alt: Read The Docs build status badge + +.. image:: https://img.shields.io/matrix/aio-libs:matrix.org?label=Discuss%20on%20Matrix%20at%20%23aio-libs%3Amatrix.org&logo=matrix&server_fqdn=matrix.org&style=flat + :target: https://matrix.to/#/%23aio-libs:matrix.org + :alt: Matrix Room — #aio-libs:matrix.org + +.. image:: https://img.shields.io/matrix/aio-libs-space:matrix.org?label=Discuss%20on%20Matrix%20at%20%23aio-libs-space%3Amatrix.org&logo=matrix&server_fqdn=matrix.org&style=flat + :target: https://matrix.to/#/%23aio-libs-space:matrix.org + :alt: Matrix Space — #aio-libs-space:matrix.org + +Introduction +------------ + +``frozenlist.FrozenList`` is a list-like structure which implements +``collections.abc.MutableSequence``. The list is *mutable* until ``FrozenList.freeze`` +is called, after which list modifications raise ``RuntimeError``: + + +>>> from frozenlist import FrozenList +>>> fl = FrozenList([17, 42]) +>>> fl.append('spam') +>>> fl.append('Vikings') +>>> fl + +>>> fl.freeze() +>>> fl + +>>> fl.frozen +True +>>> fl.append("Monty") +Traceback (most recent call last): + File "", line 1, in + File "frozenlist/_frozenlist.pyx", line 97, in frozenlist._frozenlist.FrozenList.append + self._check_frozen() + File "frozenlist/_frozenlist.pyx", line 19, in frozenlist._frozenlist.FrozenList._check_frozen + raise RuntimeError("Cannot modify frozen list.") +RuntimeError: Cannot modify frozen list. + + +FrozenList is also hashable, but only when frozen. Otherwise it also throws a RuntimeError: + + +>>> fl = FrozenList([17, 42, 'spam']) +>>> hash(fl) +Traceback (most recent call last): + File "", line 1, in + File "frozenlist/_frozenlist.pyx", line 111, in frozenlist._frozenlist.FrozenList.__hash__ + raise RuntimeError("Cannot hash unfrozen list.") +RuntimeError: Cannot hash unfrozen list. +>>> fl.freeze() +>>> hash(fl) +3713081631934410656 +>>> dictionary = {fl: 'Vikings'} # frozen fl can be a dict key +>>> dictionary +{: 'Vikings'} + + +Installation +------------ + +:: + + $ pip install frozenlist + +The library requires Python 3.8 or newer. + + +Documentation +------------- + +https://frozenlist.aio-libs.org + +Communication channels +---------------------- + +We have a *Matrix Space* `#aio-libs-space:matrix.org +`_ which is +also accessible via Gitter. + +Requirements +------------ + +- Python >= 3.8 + +License +------- + +``frozenlist`` is offered under the Apache 2 license. + +Source code +----------- + +The project is hosted on GitHub_ + +Please file an issue in the `bug tracker +`_ if you have found a bug +or have some suggestions to improve the library. + +.. _GitHub: https://github.com/aio-libs/frozenlist + +========= +Changelog +========= + +.. + You should *NOT* be adding new change log entries to this file, this + file is managed by towncrier. You *may* edit previous change logs to + fix problems like typo corrections or such. + To add a new change log entry, please see + https://pip.pypa.io/en/latest/development/contributing/#news-entries + we named the news folder "changes". + + WARNING: Don't drop the next directive! + +.. towncrier release notes start + +1.5.0 (2024-10-22) +================== + +Bug fixes +--------- + +- An incorrect signature of the ``__class_getitem__`` class method + has been fixed, adding a missing ``class_item`` argument under + Python 3.8 and older. + + This change also improves the code coverage of this method that + was previously missing -- by `@webknjaz `__. + + + *Related issues and pull requests on GitHub:* + `#567 `__, `#571 `__. + + +Improved documentation +---------------------- + +- Rendered issue, PR, and commit links now lead to + ``frozenlist``'s repo instead of ``yarl``'s repo. + + + *Related issues and pull requests on GitHub:* + `#573 `__. + +- On the ``Contributing docs`` page, + a link to the ``Towncrier philosophy`` has been fixed. + + + *Related issues and pull requests on GitHub:* + `#574 `__. + + +Packaging updates and notes for downstreams +------------------------------------------- + +- A name of a temporary building directory now reflects + that it's related to ``frozenlist``, not ``yarl``. + + + *Related issues and pull requests on GitHub:* + `#573 `__. + +- Declared Python 3.13 supported officially in the distribution package metadata. + + + *Related issues and pull requests on GitHub:* + `#595 `__. + + +---- + + +1.4.1 (2023-12-15) +================== + +Packaging updates and notes for downstreams +------------------------------------------- + +- Declared Python 3.12 and PyPy 3.8-3.10 supported officially + in the distribution package metadata. + + + *Related issues and pull requests on GitHub:* + `#553 `__. + +- Replaced the packaging is replaced from an old-fashioned ``setup.py`` to an + in-tree `PEP 517 `__ build backend -- by `@webknjaz `__. + + Whenever the end-users or downstream packagers need to build ``frozenlist`` + from source (a Git checkout or an sdist), they may pass a ``config_settings`` + flag ``pure-python``. If this flag is not set, a C-extension will be built + and included into the distribution. + + Here is how this can be done with ``pip``: + + .. code-block:: console + + $ python3 -m pip install . --config-settings=pure-python= + + This will also work with ``-e | --editable``. + + The same can be achieved via ``pypa/build``: + + .. code-block:: console + + $ python3 -m build --config-setting=pure-python= + + Adding ``-w | --wheel`` can force ``pypa/build`` produce a wheel from source + directly, as opposed to building an ``sdist`` and then building from it. + + + *Related issues and pull requests on GitHub:* + `#560 `__. + + +Contributor-facing changes +-------------------------- + +- It is now possible to request line tracing in Cython builds using the + ``with-cython-tracing`` `PEP 517 `__ config setting + -- `@webknjaz `__. + + This can be used in CI and development environment to measure coverage + on Cython modules, but is not normally useful to the end-users or + downstream packagers. + + Here's a usage example: + + .. code-block:: console + + $ python3 -Im pip install . --config-settings=with-cython-tracing=true + + For editable installs, this setting is on by default. Otherwise, it's + off unless requested explicitly. + + The following produces C-files required for the Cython coverage + plugin to map the measurements back to the PYX-files: + + .. code-block:: console + + $ python -Im pip install -e . + + Alternatively, the ``FROZENLIST_CYTHON_TRACING=1`` environment variable + can be set to do the same as the `PEP 517 `__ config setting. + + + *Related issues and pull requests on GitHub:* + `#560 `__. + +- Coverage collection has been implemented for the Cython modules + -- by `@webknjaz `__. + + It will also be reported to Codecov from any non-release CI jobs. + + + *Related issues and pull requests on GitHub:* + `#561 `__. + +- A step-by-step ``Release Guide`` guide has + been added, describing how to release *frozenlist* -- by `@webknjaz `__. + + This is primarily targeting the maintainers. + + + *Related issues and pull requests on GitHub:* + `#563 `__. + +- Detailed ``Contributing Guidelines`` on + authoring the changelog fragments have been published in the + documentation -- by `@webknjaz `__. + + + *Related issues and pull requests on GitHub:* + `#564 `__. + + +---- + + +1.4.0 (2023-07-12) +================== + +The published source distribution package became buildable +under Python 3.12. + + +---- + + +Bugfixes +-------- + +- Removed an unused ``typing.Tuple`` import + `#411 `_ + + +Deprecations and Removals +------------------------- + +- Dropped Python 3.7 support. + `#413 `_ + + +Misc +---- + +- `#410 `_, `#433 `_ + + +---- + + +1.3.3 (2022-11-08) +================== + +- Fixed CI runs when creating a new release, where new towncrier versions + fail when the current version section is already present. + + +---- + + +1.3.2 (2022-11-08) +================== + +Misc +---- + +- Updated the CI runs to better check for test results and to avoid deprecated syntax. `#327 `_ + + +---- + + +1.3.1 (2022-08-02) +================== + +The published source distribution package became buildable +under Python 3.11. + + +---- + + +1.3.0 (2022-01-18) +================== + +Bugfixes +-------- + +- Do not install C sources with binary distributions. + `#250 `_ + + +Deprecations and Removals +------------------------- + +- Dropped Python 3.6 support + `#274 `_ + + +---- + + +1.2.0 (2021-10-16) +================== + +Features +-------- + +- ``FrozenList`` now supports being used as a generic type as per PEP 585, e.g. ``frozen_int_list: FrozenList[int]`` (requires Python 3.9 or newer). + `#172 `_ +- Added support for Python 3.10. + `#227 `_ +- Started shipping platform-specific wheels with the ``musl`` tag targeting typical Alpine Linux runtimes. + `#227 `_ +- Started shipping platform-specific arm64 wheels for Apple Silicon. + `#227 `_ + + +---- + + +1.1.1 (2020-11-14) +================== + +Bugfixes +-------- + +- Provide x86 Windows wheels. + `#169 `_ + + +---- + + +1.1.0 (2020-10-13) +================== + +Features +-------- + +- Add support for hashing of a frozen list. + `#136 `_ + +- Support Python 3.8 and 3.9. + +- Provide wheels for ``aarch64``, ``i686``, ``ppc64le``, ``s390x`` architectures on + Linux as well as ``x86_64``. + + +---- + + +1.0.0 (2019-11-09) +================== + +Deprecations and Removals +------------------------- + +- Dropped support for Python 3.5; only 3.6, 3.7 and 3.8 are supported going forward. + `#24 `_ diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/frozenlist-1.5.0.dist-info/RECORD b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/frozenlist-1.5.0.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..1a8c7e73d1921cbaeb97ddcdf4a125e479ae8320 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/frozenlist-1.5.0.dist-info/RECORD @@ -0,0 +1,12 @@ +frozenlist-1.5.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +frozenlist-1.5.0.dist-info/LICENSE,sha256=b9UkPpLdf5jsacesN3co50kFcJ_1J6W_mNbQJjwE9bY,11332 +frozenlist-1.5.0.dist-info/METADATA,sha256=BpQvB7z2NbU3f4XTQDvhAZ9L08WR4XiYajilj9IY6Yk,13762 +frozenlist-1.5.0.dist-info/RECORD,, +frozenlist-1.5.0.dist-info/WHEEL,sha256=g2F1VBkM0jT4VUJ6zw4l3h6A4vsUecrEFGycpjwnlV0,224 +frozenlist-1.5.0.dist-info/top_level.txt,sha256=jivtxsPXA3nK3WBWW2LW5Mtu_GHt8UZA13NeCs2cKuA,11 +frozenlist/__init__.py,sha256=ymVtnW3MinO-Ux3cBj_PLEpXnmLawk45el8vcX6IkWY,2371 +frozenlist/__init__.pyi,sha256=vMEoES1xGegPtVXoCi9XydEeHsyuIq-KdeXwP5PdsaA,1470 +frozenlist/__pycache__/__init__.cpython-311.pyc,, +frozenlist/_frozenlist.cpython-311-x86_64-linux-gnu.so,sha256=i8wUceqj_Nyr0hb7D8kyPPUbtAtLl4J7MtPQYTzhRug,923584 +frozenlist/_frozenlist.pyx,sha256=4YturclNF7wioO7YX3Vzl7Ldb2-iswe6UrjJOMKSswU,2993 +frozenlist/py.typed,sha256=sow9soTwP9T_gEAQSVh7Gb8855h04Nwmhs2We-JRgZM,7 diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/frozenlist-1.5.0.dist-info/WHEEL b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/frozenlist-1.5.0.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..229b56f7be67f2723cc39947ac5bcde427545f36 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/frozenlist-1.5.0.dist-info/WHEEL @@ -0,0 +1,8 @@ +Wheel-Version: 1.0 +Generator: setuptools (75.2.0) +Root-Is-Purelib: false +Tag: cp311-cp311-manylinux_2_5_x86_64 +Tag: cp311-cp311-manylinux1_x86_64 +Tag: cp311-cp311-manylinux_2_17_x86_64 +Tag: cp311-cp311-manylinux2014_x86_64 + diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/frozenlist-1.5.0.dist-info/top_level.txt b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/frozenlist-1.5.0.dist-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..52f13fc459edf8f3def6f792c432f0b64f313176 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/frozenlist-1.5.0.dist-info/top_level.txt @@ -0,0 +1 @@ +frozenlist diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/idna-3.10.dist-info/INSTALLER b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/idna-3.10.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..a1b589e38a32041e49332e5e81c2d363dc418d68 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/idna-3.10.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/idna-3.10.dist-info/LICENSE.md b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/idna-3.10.dist-info/LICENSE.md new file mode 100644 index 0000000000000000000000000000000000000000..19b6b45242c16a1025465309eec2ca5009319de3 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/idna-3.10.dist-info/LICENSE.md @@ -0,0 +1,31 @@ +BSD 3-Clause License + +Copyright (c) 2013-2024, Kim Davies and contributors. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/idna-3.10.dist-info/METADATA b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/idna-3.10.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..c42623e9423c23b555d9d352bc5dab518ede02c2 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/idna-3.10.dist-info/METADATA @@ -0,0 +1,250 @@ +Metadata-Version: 2.1 +Name: idna +Version: 3.10 +Summary: Internationalized Domain Names in Applications (IDNA) +Author-email: Kim Davies +Requires-Python: >=3.6 +Description-Content-Type: text/x-rst +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: System Administrators +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Internet :: Name Service (DNS) +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Utilities +Requires-Dist: ruff >= 0.6.2 ; extra == "all" +Requires-Dist: mypy >= 1.11.2 ; extra == "all" +Requires-Dist: pytest >= 8.3.2 ; extra == "all" +Requires-Dist: flake8 >= 7.1.1 ; extra == "all" +Project-URL: Changelog, https://github.com/kjd/idna/blob/master/HISTORY.rst +Project-URL: Issue tracker, https://github.com/kjd/idna/issues +Project-URL: Source, https://github.com/kjd/idna +Provides-Extra: all + +Internationalized Domain Names in Applications (IDNA) +===================================================== + +Support for the Internationalized Domain Names in +Applications (IDNA) protocol as specified in `RFC 5891 +`_. This is the latest version of +the protocol and is sometimes referred to as “IDNA 2008”. + +This library also provides support for Unicode Technical +Standard 46, `Unicode IDNA Compatibility Processing +`_. + +This acts as a suitable replacement for the “encodings.idna” +module that comes with the Python standard library, but which +only supports the older superseded IDNA specification (`RFC 3490 +`_). + +Basic functions are simply executed: + +.. code-block:: pycon + + >>> import idna + >>> idna.encode('ドメイン.テスト') + b'xn--eckwd4c7c.xn--zckzah' + >>> print(idna.decode('xn--eckwd4c7c.xn--zckzah')) + ドメイン.テスト + + +Installation +------------ + +This package is available for installation from PyPI: + +.. code-block:: bash + + $ python3 -m pip install idna + + +Usage +----- + +For typical usage, the ``encode`` and ``decode`` functions will take a +domain name argument and perform a conversion to A-labels or U-labels +respectively. + +.. code-block:: pycon + + >>> import idna + >>> idna.encode('ドメイン.テスト') + b'xn--eckwd4c7c.xn--zckzah' + >>> print(idna.decode('xn--eckwd4c7c.xn--zckzah')) + ドメイン.テスト + +You may use the codec encoding and decoding methods using the +``idna.codec`` module: + +.. code-block:: pycon + + >>> import idna.codec + >>> print('домен.испытание'.encode('idna2008')) + b'xn--d1acufc.xn--80akhbyknj4f' + >>> print(b'xn--d1acufc.xn--80akhbyknj4f'.decode('idna2008')) + домен.испытание + +Conversions can be applied at a per-label basis using the ``ulabel`` or +``alabel`` functions if necessary: + +.. code-block:: pycon + + >>> idna.alabel('测试') + b'xn--0zwm56d' + +Compatibility Mapping (UTS #46) ++++++++++++++++++++++++++++++++ + +As described in `RFC 5895 `_, the +IDNA specification does not normalize input from different potential +ways a user may input a domain name. This functionality, known as +a “mapping”, is considered by the specification to be a local +user-interface issue distinct from IDNA conversion functionality. + +This library provides one such mapping that was developed by the +Unicode Consortium. Known as `Unicode IDNA Compatibility Processing +`_, it provides for both a regular +mapping for typical applications, as well as a transitional mapping to +help migrate from older IDNA 2003 applications. Strings are +preprocessed according to Section 4.4 “Preprocessing for IDNA2008” +prior to the IDNA operations. + +For example, “Königsgäßchen” is not a permissible label as *LATIN +CAPITAL LETTER K* is not allowed (nor are capital letters in general). +UTS 46 will convert this into lower case prior to applying the IDNA +conversion. + +.. code-block:: pycon + + >>> import idna + >>> idna.encode('Königsgäßchen') + ... + idna.core.InvalidCodepoint: Codepoint U+004B at position 1 of 'Königsgäßchen' not allowed + >>> idna.encode('Königsgäßchen', uts46=True) + b'xn--knigsgchen-b4a3dun' + >>> print(idna.decode('xn--knigsgchen-b4a3dun')) + königsgäßchen + +Transitional processing provides conversions to help transition from +the older 2003 standard to the current standard. For example, in the +original IDNA specification, the *LATIN SMALL LETTER SHARP S* (ß) was +converted into two *LATIN SMALL LETTER S* (ss), whereas in the current +IDNA specification this conversion is not performed. + +.. code-block:: pycon + + >>> idna.encode('Königsgäßchen', uts46=True, transitional=True) + 'xn--knigsgsschen-lcb0w' + +Implementers should use transitional processing with caution, only in +rare cases where conversion from legacy labels to current labels must be +performed (i.e. IDNA implementations that pre-date 2008). For typical +applications that just need to convert labels, transitional processing +is unlikely to be beneficial and could produce unexpected incompatible +results. + +``encodings.idna`` Compatibility +++++++++++++++++++++++++++++++++ + +Function calls from the Python built-in ``encodings.idna`` module are +mapped to their IDNA 2008 equivalents using the ``idna.compat`` module. +Simply substitute the ``import`` clause in your code to refer to the new +module name. + +Exceptions +---------- + +All errors raised during the conversion following the specification +should raise an exception derived from the ``idna.IDNAError`` base +class. + +More specific exceptions that may be generated as ``idna.IDNABidiError`` +when the error reflects an illegal combination of left-to-right and +right-to-left characters in a label; ``idna.InvalidCodepoint`` when +a specific codepoint is an illegal character in an IDN label (i.e. +INVALID); and ``idna.InvalidCodepointContext`` when the codepoint is +illegal based on its positional context (i.e. it is CONTEXTO or CONTEXTJ +but the contextual requirements are not satisfied.) + +Building and Diagnostics +------------------------ + +The IDNA and UTS 46 functionality relies upon pre-calculated lookup +tables for performance. These tables are derived from computing against +eligibility criteria in the respective standards. These tables are +computed using the command-line script ``tools/idna-data``. + +This tool will fetch relevant codepoint data from the Unicode repository +and perform the required calculations to identify eligibility. There are +three main modes: + +* ``idna-data make-libdata``. Generates ``idnadata.py`` and + ``uts46data.py``, the pre-calculated lookup tables used for IDNA and + UTS 46 conversions. Implementers who wish to track this library against + a different Unicode version may use this tool to manually generate a + different version of the ``idnadata.py`` and ``uts46data.py`` files. + +* ``idna-data make-table``. Generate a table of the IDNA disposition + (e.g. PVALID, CONTEXTJ, CONTEXTO) in the format found in Appendix + B.1 of RFC 5892 and the pre-computed tables published by `IANA + `_. + +* ``idna-data U+0061``. Prints debugging output on the various + properties associated with an individual Unicode codepoint (in this + case, U+0061), that are used to assess the IDNA and UTS 46 status of a + codepoint. This is helpful in debugging or analysis. + +The tool accepts a number of arguments, described using ``idna-data +-h``. Most notably, the ``--version`` argument allows the specification +of the version of Unicode to be used in computing the table data. For +example, ``idna-data --version 9.0.0 make-libdata`` will generate +library data against Unicode 9.0.0. + + +Additional Notes +---------------- + +* **Packages**. The latest tagged release version is published in the + `Python Package Index `_. + +* **Version support**. This library supports Python 3.6 and higher. + As this library serves as a low-level toolkit for a variety of + applications, many of which strive for broad compatibility with older + Python versions, there is no rush to remove older interpreter support. + Removing support for older versions should be well justified in that the + maintenance burden has become too high. + +* **Python 2**. Python 2 is supported by version 2.x of this library. + Use "idna<3" in your requirements file if you need this library for + a Python 2 application. Be advised that these versions are no longer + actively developed. + +* **Testing**. The library has a test suite based on each rule of the + IDNA specification, as well as tests that are provided as part of the + Unicode Technical Standard 46, `Unicode IDNA Compatibility Processing + `_. + +* **Emoji**. It is an occasional request to support emoji domains in + this library. Encoding of symbols like emoji is expressly prohibited by + the technical standard IDNA 2008 and emoji domains are broadly phased + out across the domain industry due to associated security risks. For + now, applications that need to support these non-compliant labels + may wish to consider trying the encode/decode operation in this library + first, and then falling back to using `encodings.idna`. See `the Github + project `_ for more discussion. + diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/idna-3.10.dist-info/RECORD b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/idna-3.10.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..4a47b68eb0234668716c45950639497f8be97a99 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/idna-3.10.dist-info/RECORD @@ -0,0 +1,22 @@ +idna-3.10.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +idna-3.10.dist-info/LICENSE.md,sha256=pZ8LDvNjWHQQmkRhykT_enDVBpboFHZ7-vch1Mmw2w8,1541 +idna-3.10.dist-info/METADATA,sha256=URR5ZyDfQ1PCEGhkYoojqfi2Ra0tau2--lhwG4XSfjI,10158 +idna-3.10.dist-info/RECORD,, +idna-3.10.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81 +idna/__init__.py,sha256=MPqNDLZbXqGaNdXxAFhiqFPKEQXju2jNQhCey6-5eJM,868 +idna/__pycache__/__init__.cpython-311.pyc,, +idna/__pycache__/codec.cpython-311.pyc,, +idna/__pycache__/compat.cpython-311.pyc,, +idna/__pycache__/core.cpython-311.pyc,, +idna/__pycache__/idnadata.cpython-311.pyc,, +idna/__pycache__/intranges.cpython-311.pyc,, +idna/__pycache__/package_data.cpython-311.pyc,, +idna/__pycache__/uts46data.cpython-311.pyc,, +idna/codec.py,sha256=PEew3ItwzjW4hymbasnty2N2OXvNcgHB-JjrBuxHPYY,3422 +idna/compat.py,sha256=RzLy6QQCdl9784aFhb2EX9EKGCJjg0P3PilGdeXXcx8,316 +idna/core.py,sha256=YJYyAMnwiQEPjVC4-Fqu_p4CJ6yKKuDGmppBNQNQpFs,13239 +idna/idnadata.py,sha256=W30GcIGvtOWYwAjZj4ZjuouUutC6ffgNuyjJy7fZ-lo,78306 +idna/intranges.py,sha256=amUtkdhYcQG8Zr-CoMM_kVRacxkivC1WgxN1b63KKdU,1898 +idna/package_data.py,sha256=q59S3OXsc5VI8j6vSD0sGBMyk6zZ4vWFREE88yCJYKs,21 +idna/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +idna/uts46data.py,sha256=rt90K9J40gUSwppDPCrhjgi5AA6pWM65dEGRSf6rIhM,239289 diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/idna-3.10.dist-info/WHEEL b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/idna-3.10.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..3b5e64b5e6c4a210201d1676a891fd57b15cda99 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/idna-3.10.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: flit 3.9.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/__init__.py b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..3640407105dddc9128b617d4222cb642e25c7e23 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/__init__.py @@ -0,0 +1,14 @@ +from ._query import Query, QueryVariable, SimpleQuery +from ._url import URL, cache_clear, cache_configure, cache_info + +__version__ = "1.18.3" + +__all__ = ( + "URL", + "SimpleQuery", + "QueryVariable", + "Query", + "cache_clear", + "cache_configure", + "cache_info", +) diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/__pycache__/__init__.cpython-311.pyc b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2b140a67ee1441e90cab529dc35ef5add6e63f7c Binary files /dev/null and b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/__pycache__/_parse.cpython-311.pyc b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/__pycache__/_parse.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..be1def8a33af4d728fc86ee87172fc2ca48e8a4b Binary files /dev/null and b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/__pycache__/_parse.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/__pycache__/_path.cpython-311.pyc b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/__pycache__/_path.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f237c833a97dce7069bb951559864a23794878e8 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/__pycache__/_path.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/__pycache__/_query.cpython-311.pyc b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/__pycache__/_query.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d1646d58d8b8d10ec817b29f7e90aa82e5355dcd Binary files /dev/null and b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/__pycache__/_query.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/__pycache__/_quoters.cpython-311.pyc b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/__pycache__/_quoters.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2802647eb774f396a762a164c48974244907c33f Binary files /dev/null and b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/__pycache__/_quoters.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/__pycache__/_quoting.cpython-311.pyc b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/__pycache__/_quoting.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1133e4fa4e3c183dda16e81416cebd02717d726d Binary files /dev/null and b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/__pycache__/_quoting.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/__pycache__/_quoting_py.cpython-311.pyc b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/__pycache__/_quoting_py.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a495d72b612de542b844f0817a33be46517c03df Binary files /dev/null and b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/__pycache__/_quoting_py.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/__pycache__/_url.cpython-311.pyc b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/__pycache__/_url.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d81299c40029c50dc1852d6282199eb577f9a682 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/__pycache__/_url.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/_parse.py b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/_parse.py new file mode 100644 index 0000000000000000000000000000000000000000..cc259ea86dc89aa0766d0cd30945fd605743b7b0 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/_parse.py @@ -0,0 +1,189 @@ +"""URL parsing utilities.""" + +import re +import unicodedata +from functools import lru_cache +from typing import Union +from urllib.parse import scheme_chars, uses_netloc + +from ._quoters import QUOTER + +# Leading and trailing C0 control and space to be stripped per WHATWG spec. +# == "".join([chr(i) for i in range(0, 0x20 + 1)]) +WHATWG_C0_CONTROL_OR_SPACE = ( + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10" + "\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f " +) + +# Unsafe bytes to be removed per WHATWG spec +UNSAFE_URL_BYTES_TO_REMOVE = ["\t", "\r", "\n"] +USES_AUTHORITY = frozenset(uses_netloc) + +SplitURLType = tuple[str, str, str, str, str] + + +def split_url(url: str) -> SplitURLType: + """Split URL into parts.""" + # Adapted from urllib.parse.urlsplit + # Only lstrip url as some applications rely on preserving trailing space. + # (https://url.spec.whatwg.org/#concept-basic-url-parser would strip both) + url = url.lstrip(WHATWG_C0_CONTROL_OR_SPACE) + for b in UNSAFE_URL_BYTES_TO_REMOVE: + if b in url: + url = url.replace(b, "") + + scheme = netloc = query = fragment = "" + i = url.find(":") + if i > 0 and url[0] in scheme_chars: + for c in url[1:i]: + if c not in scheme_chars: + break + else: + scheme, url = url[:i].lower(), url[i + 1 :] + has_hash = "#" in url + has_question_mark = "?" in url + if url[:2] == "//": + delim = len(url) # position of end of domain part of url, default is end + if has_hash and has_question_mark: + delim_chars = "/?#" + elif has_question_mark: + delim_chars = "/?" + elif has_hash: + delim_chars = "/#" + else: + delim_chars = "/" + for c in delim_chars: # look for delimiters; the order is NOT important + wdelim = url.find(c, 2) # find first of this delim + if wdelim >= 0 and wdelim < delim: # if found + delim = wdelim # use earliest delim position + netloc = url[2:delim] + url = url[delim:] + has_left_bracket = "[" in netloc + has_right_bracket = "]" in netloc + if (has_left_bracket and not has_right_bracket) or ( + has_right_bracket and not has_left_bracket + ): + raise ValueError("Invalid IPv6 URL") + if has_left_bracket: + bracketed_host = netloc.partition("[")[2].partition("]")[0] + # Valid bracketed hosts are defined in + # https://www.rfc-editor.org/rfc/rfc3986#page-49 + # https://url.spec.whatwg.org/ + if bracketed_host[0] == "v": + if not re.match(r"\Av[a-fA-F0-9]+\..+\Z", bracketed_host): + raise ValueError("IPvFuture address is invalid") + elif ":" not in bracketed_host: + raise ValueError("An IPv4 address cannot be in brackets") + if has_hash: + url, _, fragment = url.partition("#") + if has_question_mark: + url, _, query = url.partition("?") + if netloc and not netloc.isascii(): + _check_netloc(netloc) + return scheme, netloc, url, query, fragment + + +def _check_netloc(netloc: str) -> None: + # Adapted from urllib.parse._checknetloc + # looking for characters like \u2100 that expand to 'a/c' + # IDNA uses NFKC equivalence, so normalize for this check + + # ignore characters already included + # but not the surrounding text + n = netloc.replace("@", "").replace(":", "").replace("#", "").replace("?", "") + normalized_netloc = unicodedata.normalize("NFKC", n) + if n == normalized_netloc: + return + # Note that there are no unicode decompositions for the character '@' so + # its currently impossible to have test coverage for this branch, however if the + # one should be added in the future we want to make sure its still checked. + for c in "/?#@:": # pragma: no branch + if c in normalized_netloc: + raise ValueError( + f"netloc '{netloc}' contains invalid " + "characters under NFKC normalization" + ) + + +@lru_cache # match the same size as urlsplit +def split_netloc( + netloc: str, +) -> tuple[Union[str, None], Union[str, None], Union[str, None], Union[int, None]]: + """Split netloc into username, password, host and port.""" + if "@" not in netloc: + username: Union[str, None] = None + password: Union[str, None] = None + hostinfo = netloc + else: + userinfo, _, hostinfo = netloc.rpartition("@") + username, have_password, password = userinfo.partition(":") + if not have_password: + password = None + + if "[" in hostinfo: + _, _, bracketed = hostinfo.partition("[") + hostname, _, port_str = bracketed.partition("]") + _, _, port_str = port_str.partition(":") + else: + hostname, _, port_str = hostinfo.partition(":") + + if not port_str: + return username or None, password, hostname or None, None + + try: + port = int(port_str) + except ValueError: + raise ValueError("Invalid URL: port can't be converted to integer") + if not (0 <= port <= 65535): + raise ValueError("Port out of range 0-65535") + return username or None, password, hostname or None, port + + +def unsplit_result( + scheme: str, netloc: str, url: str, query: str, fragment: str +) -> str: + """Unsplit a URL without any normalization.""" + if netloc or (scheme and scheme in USES_AUTHORITY) or url[:2] == "//": + if url and url[:1] != "/": + url = f"{scheme}://{netloc}/{url}" if scheme else f"{scheme}:{url}" + else: + url = f"{scheme}://{netloc}{url}" if scheme else f"//{netloc}{url}" + elif scheme: + url = f"{scheme}:{url}" + if query: + url = f"{url}?{query}" + return f"{url}#{fragment}" if fragment else url + + +@lru_cache # match the same size as urlsplit +def make_netloc( + user: Union[str, None], + password: Union[str, None], + host: Union[str, None], + port: Union[int, None], + encode: bool = False, +) -> str: + """Make netloc from parts. + + The user and password are encoded if encode is True. + + The host must already be encoded with _encode_host. + """ + if host is None: + return "" + ret = host + if port is not None: + ret = f"{ret}:{port}" + if user is None and password is None: + return ret + if password is not None: + if not user: + user = "" + elif encode: + user = QUOTER(user) + if encode: + password = QUOTER(password) + user = f"{user}:{password}" + elif user and encode: + user = QUOTER(user) + return f"{user}@{ret}" if user else ret diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/_path.py b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/_path.py new file mode 100644 index 0000000000000000000000000000000000000000..c22f0b4b8cdd9280fd36789e2bc052b1c4938167 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/_path.py @@ -0,0 +1,41 @@ +"""Utilities for working with paths.""" + +from collections.abc import Sequence +from contextlib import suppress + + +def normalize_path_segments(segments: Sequence[str]) -> list[str]: + """Drop '.' and '..' from a sequence of str segments""" + + resolved_path: list[str] = [] + + for seg in segments: + if seg == "..": + # ignore any .. segments that would otherwise cause an + # IndexError when popped from resolved_path if + # resolving for rfc3986 + with suppress(IndexError): + resolved_path.pop() + elif seg != ".": + resolved_path.append(seg) + + if segments and segments[-1] in (".", ".."): + # do some post-processing here. + # if the last segment was a relative dir, + # then we need to append the trailing '/' + resolved_path.append("") + + return resolved_path + + +def normalize_path(path: str) -> str: + # Drop '.' and '..' from str path + prefix = "" + if path and path[0] == "/": + # preserve the "/" root element of absolute paths, copying it to the + # normalised output as per sections 5.2.4 and 6.2.2.3 of rfc3986. + prefix = "/" + path = path[1:] + + segments = path.split("/") + return prefix + "/".join(normalize_path_segments(segments)) diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/_query.py b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/_query.py new file mode 100644 index 0000000000000000000000000000000000000000..6a663fc999c3985e5a5e0229c97db3f85c6e7252 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/_query.py @@ -0,0 +1,118 @@ +"""Query string handling.""" + +import math +from collections.abc import Iterable, Mapping, Sequence +from typing import TYPE_CHECKING, Any, SupportsInt, Union + +from multidict import istr + +from ._quoters import QUERY_PART_QUOTER, QUERY_QUOTER + +SimpleQuery = Union[str, int, float] +QueryVariable = Union[SimpleQuery, Sequence[SimpleQuery]] +Query = Union[ + None, str, Mapping[str, QueryVariable], Sequence[tuple[str, QueryVariable]] +] + + +def query_var(v: QueryVariable) -> str: + """Convert a query variable to a string.""" + cls = type(v) + if cls is int: # Fast path for non-subclassed int + return str(v) + if issubclass(cls, str): + if TYPE_CHECKING: + assert isinstance(v, str) + return v + if cls is float or issubclass(cls, float): + if TYPE_CHECKING: + assert isinstance(v, float) + if math.isinf(v): + raise ValueError("float('inf') is not supported") + if math.isnan(v): + raise ValueError("float('nan') is not supported") + return str(float(v)) + if cls is not bool and isinstance(cls, SupportsInt): + return str(int(v)) + raise TypeError( + "Invalid variable type: value " + "should be str, int or float, got {!r} " + "of type {}".format(v, cls) + ) + + +def get_str_query_from_sequence_iterable( + items: Iterable[tuple[Union[str, istr], QueryVariable]], +) -> str: + """Return a query string from a sequence of (key, value) pairs. + + value is a single value or a sequence of values for the key + + The sequence of values must be a list or tuple. + """ + quoter = QUERY_PART_QUOTER + pairs = [ + f"{quoter(k)}={quoter(v if type(v) is str else query_var(v))}" + for k, val in items + for v in ( + val if type(val) is not str and isinstance(val, (list, tuple)) else (val,) + ) + ] + return "&".join(pairs) + + +def get_str_query_from_iterable( + items: Iterable[tuple[Union[str, istr], SimpleQuery]] +) -> str: + """Return a query string from an iterable. + + The iterable must contain (key, value) pairs. + + The values are not allowed to be sequences, only single values are + allowed. For sequences, use `_get_str_query_from_sequence_iterable`. + """ + quoter = QUERY_PART_QUOTER + # A listcomp is used since listcomps are inlined on CPython 3.12+ and + # they are a bit faster than a generator expression. + pairs = [ + f"{quoter(k)}={quoter(v if type(v) is str else query_var(v))}" for k, v in items + ] + return "&".join(pairs) + + +def get_str_query(*args: Any, **kwargs: Any) -> Union[str, None]: + """Return a query string from supported args.""" + query: Union[str, Mapping[str, QueryVariable], None] + if kwargs: + if args: + msg = "Either kwargs or single query parameter must be present" + raise ValueError(msg) + query = kwargs + elif len(args) == 1: + query = args[0] + else: + raise ValueError("Either kwargs or single query parameter must be present") + + if query is None: + return None + if not query: + return "" + if type(query) is dict: + return get_str_query_from_sequence_iterable(query.items()) + if type(query) is str or isinstance(query, str): + return QUERY_QUOTER(query) + if isinstance(query, Mapping): + return get_str_query_from_sequence_iterable(query.items()) + if isinstance(query, (bytes, bytearray, memoryview)): + msg = "Invalid query type: bytes, bytearray and memoryview are forbidden" + raise TypeError(msg) + if isinstance(query, Sequence): + # We don't expect sequence values if we're given a list of pairs + # already; only mappings like builtin `dict` which can't have the + # same key pointing to multiple values are allowed to use + # `_query_seq_pairs`. + return get_str_query_from_iterable(query) + raise TypeError( + "Invalid query type: only str, mapping or " + "sequence of (key, value) pairs is allowed" + ) diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/_quoters.py b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/_quoters.py new file mode 100644 index 0000000000000000000000000000000000000000..c1d2d7f81ef1e3d61baaa831f95f1bf582b8b806 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/_quoters.py @@ -0,0 +1,32 @@ +"""Quoting and unquoting utilities for URL parts.""" + +from typing import Union +from urllib.parse import quote + +from ._quoting import _Quoter, _Unquoter + +QUOTER = _Quoter(requote=False) +REQUOTER = _Quoter() +PATH_QUOTER = _Quoter(safe="@:", protected="/+", requote=False) +PATH_REQUOTER = _Quoter(safe="@:", protected="/+") +QUERY_QUOTER = _Quoter(safe="?/:@", protected="=+&;", qs=True, requote=False) +QUERY_REQUOTER = _Quoter(safe="?/:@", protected="=+&;", qs=True) +QUERY_PART_QUOTER = _Quoter(safe="?/:@", qs=True, requote=False) +FRAGMENT_QUOTER = _Quoter(safe="?/:@", requote=False) +FRAGMENT_REQUOTER = _Quoter(safe="?/:@") + +UNQUOTER = _Unquoter() +PATH_UNQUOTER = _Unquoter(unsafe="+") +PATH_SAFE_UNQUOTER = _Unquoter(ignore="/%", unsafe="+") +QS_UNQUOTER = _Unquoter(qs=True) + + +def human_quote(s: Union[str, None], unsafe: str) -> Union[str, None]: + if not s: + return s + for c in "%" + unsafe: + if c in s: + s = s.replace(c, f"%{ord(c):02X}") + if s.isprintable(): + return s + return "".join(c if c.isprintable() else quote(c) for c in s) diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/_quoting.py b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/_quoting.py new file mode 100644 index 0000000000000000000000000000000000000000..95e86095d1dec243a96559a39460ba99bdfa6826 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/_quoting.py @@ -0,0 +1,18 @@ +import os +import sys + +__all__ = ("_Quoter", "_Unquoter") + + +NO_EXTENSIONS = bool(os.environ.get("YARL_NO_EXTENSIONS")) # type: bool +if sys.implementation.name != "cpython": + NO_EXTENSIONS = True + + +if not NO_EXTENSIONS: # pragma: no branch + try: + from ._quoting_c import _Quoter, _Unquoter + except ImportError: # pragma: no cover + from ._quoting_py import _Quoter, _Unquoter # type: ignore[assignment] +else: + from ._quoting_py import _Quoter, _Unquoter # type: ignore[assignment] diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/_quoting_c.pyi b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/_quoting_c.pyi new file mode 100644 index 0000000000000000000000000000000000000000..9a6b79adf9106814e32a57cfb51447baba555e13 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/_quoting_c.pyi @@ -0,0 +1,16 @@ +class _Quoter: + def __init__( + self, + *, + safe: str = ..., + protected: str = ..., + qs: bool = ..., + requote: bool = ... + ) -> None: ... + def __call__(self, val: str = ...) -> str: ... + +class _Unquoter: + def __init__( + self, *, ignore: str = ..., unsafe: str = ..., qs: bool = ... + ) -> None: ... + def __call__(self, val: str = ...) -> str: ... diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/_quoting_c.pyx b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/_quoting_c.pyx new file mode 100644 index 0000000000000000000000000000000000000000..067ba96e4efa297669b22fa407af500c7ea1e636 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/_quoting_c.pyx @@ -0,0 +1,423 @@ +# cython: language_level=3 + +from cpython.exc cimport PyErr_NoMemory +from cpython.mem cimport PyMem_Free, PyMem_Malloc, PyMem_Realloc +from cpython.unicode cimport ( + PyUnicode_DATA, + PyUnicode_DecodeASCII, + PyUnicode_DecodeUTF8Stateful, + PyUnicode_GET_LENGTH, + PyUnicode_KIND, + PyUnicode_READ, +) +from libc.stdint cimport uint8_t, uint64_t +from libc.string cimport memcpy, memset + +from string import ascii_letters, digits + + +cdef str GEN_DELIMS = ":/?#[]@" +cdef str SUB_DELIMS_WITHOUT_QS = "!$'()*," +cdef str SUB_DELIMS = SUB_DELIMS_WITHOUT_QS + '+?=;' +cdef str RESERVED = GEN_DELIMS + SUB_DELIMS +cdef str UNRESERVED = ascii_letters + digits + '-._~' +cdef str ALLOWED = UNRESERVED + SUB_DELIMS_WITHOUT_QS +cdef str QS = '+&=;' + +DEF BUF_SIZE = 8 * 1024 # 8KiB +cdef char BUFFER[BUF_SIZE] + +cdef inline Py_UCS4 _to_hex(uint8_t v) noexcept: + if v < 10: + return (v+0x30) # ord('0') == 0x30 + else: + return (v+0x41-10) # ord('A') == 0x41 + + +cdef inline int _from_hex(Py_UCS4 v) noexcept: + if '0' <= v <= '9': + return (v) - 0x30 # ord('0') == 0x30 + elif 'A' <= v <= 'F': + return (v) - 0x41 + 10 # ord('A') == 0x41 + elif 'a' <= v <= 'f': + return (v) - 0x61 + 10 # ord('a') == 0x61 + else: + return -1 + + +cdef inline int _is_lower_hex(Py_UCS4 v) noexcept: + return 'a' <= v <= 'f' + + +cdef inline Py_UCS4 _restore_ch(Py_UCS4 d1, Py_UCS4 d2): + cdef int digit1 = _from_hex(d1) + if digit1 < 0: + return -1 + cdef int digit2 = _from_hex(d2) + if digit2 < 0: + return -1 + return (digit1 << 4 | digit2) + + +cdef uint8_t ALLOWED_TABLE[16] +cdef uint8_t ALLOWED_NOTQS_TABLE[16] + + +cdef inline bint bit_at(uint8_t array[], uint64_t ch) noexcept: + return array[ch >> 3] & (1 << (ch & 7)) + + +cdef inline void set_bit(uint8_t array[], uint64_t ch) noexcept: + array[ch >> 3] |= (1 << (ch & 7)) + + +memset(ALLOWED_TABLE, 0, sizeof(ALLOWED_TABLE)) +memset(ALLOWED_NOTQS_TABLE, 0, sizeof(ALLOWED_NOTQS_TABLE)) + +for i in range(128): + if chr(i) in ALLOWED: + set_bit(ALLOWED_TABLE, i) + set_bit(ALLOWED_NOTQS_TABLE, i) + if chr(i) in QS: + set_bit(ALLOWED_NOTQS_TABLE, i) + +# ----------------- writer --------------------------- + +cdef struct Writer: + char *buf + Py_ssize_t size + Py_ssize_t pos + bint changed + + +cdef inline void _init_writer(Writer* writer): + writer.buf = &BUFFER[0] + writer.size = BUF_SIZE + writer.pos = 0 + writer.changed = 0 + + +cdef inline void _release_writer(Writer* writer): + if writer.buf != BUFFER: + PyMem_Free(writer.buf) + + +cdef inline int _write_char(Writer* writer, Py_UCS4 ch, bint changed): + cdef char * buf + cdef Py_ssize_t size + + if writer.pos == writer.size: + # reallocate + size = writer.size + BUF_SIZE + if writer.buf == BUFFER: + buf = PyMem_Malloc(size) + if buf == NULL: + PyErr_NoMemory() + return -1 + memcpy(buf, writer.buf, writer.size) + else: + buf = PyMem_Realloc(writer.buf, size) + if buf == NULL: + PyErr_NoMemory() + return -1 + writer.buf = buf + writer.size = size + writer.buf[writer.pos] = ch + writer.pos += 1 + writer.changed |= changed + return 0 + + +cdef inline int _write_pct(Writer* writer, uint8_t ch, bint changed): + if _write_char(writer, '%', changed) < 0: + return -1 + if _write_char(writer, _to_hex(ch >> 4), changed) < 0: + return -1 + return _write_char(writer, _to_hex(ch & 0x0f), changed) + + +cdef inline int _write_utf8(Writer* writer, Py_UCS4 symbol): + cdef uint64_t utf = symbol + + if utf < 0x80: + return _write_pct(writer, utf, True) + elif utf < 0x800: + if _write_pct(writer, (0xc0 | (utf >> 6)), True) < 0: + return -1 + return _write_pct(writer, (0x80 | (utf & 0x3f)), True) + elif 0xD800 <= utf <= 0xDFFF: + # surogate pair, ignored + return 0 + elif utf < 0x10000: + if _write_pct(writer, (0xe0 | (utf >> 12)), True) < 0: + return -1 + if _write_pct(writer, (0x80 | ((utf >> 6) & 0x3f)), + True) < 0: + return -1 + return _write_pct(writer, (0x80 | (utf & 0x3f)), True) + elif utf > 0x10FFFF: + # symbol is too large + return 0 + else: + if _write_pct(writer, (0xf0 | (utf >> 18)), True) < 0: + return -1 + if _write_pct(writer, (0x80 | ((utf >> 12) & 0x3f)), + True) < 0: + return -1 + if _write_pct(writer, (0x80 | ((utf >> 6) & 0x3f)), + True) < 0: + return -1 + return _write_pct(writer, (0x80 | (utf & 0x3f)), True) + + +# --------------------- end writer -------------------------- + + +cdef class _Quoter: + cdef bint _qs + cdef bint _requote + + cdef uint8_t _safe_table[16] + cdef uint8_t _protected_table[16] + + def __init__( + self, *, str safe='', str protected='', bint qs=False, bint requote=True, + ): + cdef Py_UCS4 ch + + self._qs = qs + self._requote = requote + + if not self._qs: + memcpy(self._safe_table, + ALLOWED_NOTQS_TABLE, + sizeof(self._safe_table)) + else: + memcpy(self._safe_table, + ALLOWED_TABLE, + sizeof(self._safe_table)) + for ch in safe: + if ord(ch) > 127: + raise ValueError("Only safe symbols with ORD < 128 are allowed") + set_bit(self._safe_table, ch) + + memset(self._protected_table, 0, sizeof(self._protected_table)) + for ch in protected: + if ord(ch) > 127: + raise ValueError("Only safe symbols with ORD < 128 are allowed") + set_bit(self._safe_table, ch) + set_bit(self._protected_table, ch) + + def __call__(self, val): + if val is None: + return None + if type(val) is not str: + if isinstance(val, str): + # derived from str + val = str(val) + else: + raise TypeError("Argument should be str") + return self._do_quote_or_skip(val) + + cdef str _do_quote_or_skip(self, str val): + cdef Py_UCS4 ch + cdef Py_ssize_t length = PyUnicode_GET_LENGTH(val) + cdef Py_ssize_t idx = length + cdef bint must_quote = 0 + cdef Writer writer + cdef int kind = PyUnicode_KIND(val) + cdef const void *data = PyUnicode_DATA(val) + + # If everything in the string is in the safe + # table and all ASCII, we can skip quoting + while idx: + idx -= 1 + ch = PyUnicode_READ(kind, data, idx) + if ch >= 128 or not bit_at(self._safe_table, ch): + must_quote = 1 + break + + if not must_quote: + return val + + _init_writer(&writer) + try: + return self._do_quote(val, length, kind, data, &writer) + finally: + _release_writer(&writer) + + cdef str _do_quote( + self, + str val, + Py_ssize_t length, + int kind, + const void *data, + Writer *writer + ): + cdef Py_UCS4 ch + cdef int changed + cdef Py_ssize_t idx = 0 + + while idx < length: + ch = PyUnicode_READ(kind, data, idx) + idx += 1 + if ch == '%' and self._requote and idx <= length - 2: + ch = _restore_ch( + PyUnicode_READ(kind, data, idx), + PyUnicode_READ(kind, data, idx + 1) + ) + if ch != -1: + idx += 2 + if ch < 128: + if bit_at(self._protected_table, ch): + if _write_pct(writer, ch, True) < 0: + raise + continue + + if bit_at(self._safe_table, ch): + if _write_char(writer, ch, True) < 0: + raise + continue + + changed = (_is_lower_hex(PyUnicode_READ(kind, data, idx - 2)) or + _is_lower_hex(PyUnicode_READ(kind, data, idx - 1))) + if _write_pct(writer, ch, changed) < 0: + raise + continue + else: + ch = '%' + + if self._write(writer, ch) < 0: + raise + + if not writer.changed: + return val + else: + return PyUnicode_DecodeASCII(writer.buf, writer.pos, "strict") + + cdef inline int _write(self, Writer *writer, Py_UCS4 ch): + if self._qs: + if ch == ' ': + return _write_char(writer, '+', True) + + if ch < 128 and bit_at(self._safe_table, ch): + return _write_char(writer, ch, False) + + return _write_utf8(writer, ch) + + +cdef class _Unquoter: + cdef str _ignore + cdef str _unsafe + cdef bint _qs + cdef _Quoter _quoter + cdef _Quoter _qs_quoter + + def __init__(self, *, ignore="", unsafe="", qs=False): + self._ignore = ignore + self._unsafe = unsafe + self._qs = qs + self._quoter = _Quoter() + self._qs_quoter = _Quoter(qs=True) + + def __call__(self, val): + if val is None: + return None + if type(val) is not str: + if isinstance(val, str): + # derived from str + val = str(val) + else: + raise TypeError("Argument should be str") + return self._do_unquote(val) + + cdef str _do_unquote(self, str val): + cdef Py_ssize_t length = PyUnicode_GET_LENGTH(val) + if length == 0: + return val + + cdef list ret = [] + cdef char buffer[4] + cdef Py_ssize_t buflen = 0 + cdef Py_ssize_t consumed + cdef str unquoted + cdef Py_UCS4 ch = 0 + cdef Py_ssize_t idx = 0 + cdef Py_ssize_t start_pct + cdef int kind = PyUnicode_KIND(val) + cdef const void *data = PyUnicode_DATA(val) + cdef bint changed = 0 + while idx < length: + ch = PyUnicode_READ(kind, data, idx) + idx += 1 + if ch == '%' and idx <= length - 2: + changed = 1 + ch = _restore_ch( + PyUnicode_READ(kind, data, idx), + PyUnicode_READ(kind, data, idx + 1) + ) + if ch != -1: + idx += 2 + assert buflen < 4 + buffer[buflen] = ch + buflen += 1 + try: + unquoted = PyUnicode_DecodeUTF8Stateful(buffer, buflen, + NULL, &consumed) + except UnicodeDecodeError: + start_pct = idx - buflen * 3 + buffer[0] = ch + buflen = 1 + ret.append(val[start_pct : idx - 3]) + try: + unquoted = PyUnicode_DecodeUTF8Stateful(buffer, buflen, + NULL, &consumed) + except UnicodeDecodeError: + buflen = 0 + ret.append(val[idx - 3 : idx]) + continue + if not unquoted: + assert consumed == 0 + continue + assert consumed == buflen + buflen = 0 + if self._qs and unquoted in '+=&;': + ret.append(self._qs_quoter(unquoted)) + elif unquoted in self._unsafe or unquoted in self._ignore: + ret.append(self._quoter(unquoted)) + else: + ret.append(unquoted) + continue + else: + ch = '%' + + if buflen: + start_pct = idx - 1 - buflen * 3 + ret.append(val[start_pct : idx - 1]) + buflen = 0 + + if ch == '+': + if not self._qs or ch in self._unsafe: + ret.append('+') + else: + changed = 1 + ret.append(' ') + continue + + if ch in self._unsafe: + changed = 1 + ret.append('%') + h = hex(ord(ch)).upper()[2:] + for ch in h: + ret.append(ch) + continue + + ret.append(ch) + + if not changed: + return val + + if buflen: + ret.append(val[length - buflen * 3 : length]) + + return ''.join(ret) diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/_quoting_py.py b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/_quoting_py.py new file mode 100644 index 0000000000000000000000000000000000000000..7256acd8c82dcdf9dc0674dc5ea04e07250baa4d --- /dev/null +++ b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/_quoting_py.py @@ -0,0 +1,197 @@ +import codecs +import re +from string import ascii_letters, ascii_lowercase, digits +from typing import cast + +BASCII_LOWERCASE = ascii_lowercase.encode("ascii") +BPCT_ALLOWED = {f"%{i:02X}".encode("ascii") for i in range(256)} +GEN_DELIMS = ":/?#[]@" +SUB_DELIMS_WITHOUT_QS = "!$'()*," +SUB_DELIMS = SUB_DELIMS_WITHOUT_QS + "+&=;" +RESERVED = GEN_DELIMS + SUB_DELIMS +UNRESERVED = ascii_letters + digits + "-._~" +ALLOWED = UNRESERVED + SUB_DELIMS_WITHOUT_QS + + +_IS_HEX = re.compile(b"[A-Z0-9][A-Z0-9]") +_IS_HEX_STR = re.compile("[A-Fa-f0-9][A-Fa-f0-9]") + +utf8_decoder = codecs.getincrementaldecoder("utf-8") + + +class _Quoter: + def __init__( + self, + *, + safe: str = "", + protected: str = "", + qs: bool = False, + requote: bool = True, + ) -> None: + self._safe = safe + self._protected = protected + self._qs = qs + self._requote = requote + + def __call__(self, val: str) -> str: + if val is None: + return None + if not isinstance(val, str): + raise TypeError("Argument should be str") + if not val: + return "" + bval = val.encode("utf8", errors="ignore") + ret = bytearray() + pct = bytearray() + safe = self._safe + safe += ALLOWED + if not self._qs: + safe += "+&=;" + safe += self._protected + bsafe = safe.encode("ascii") + idx = 0 + while idx < len(bval): + ch = bval[idx] + idx += 1 + + if pct: + if ch in BASCII_LOWERCASE: + ch = ch - 32 # convert to uppercase + pct.append(ch) + if len(pct) == 3: # pragma: no branch # peephole optimizer + buf = pct[1:] + if not _IS_HEX.match(buf): + ret.extend(b"%25") + pct.clear() + idx -= 2 + continue + try: + unquoted = chr(int(pct[1:].decode("ascii"), base=16)) + except ValueError: + ret.extend(b"%25") + pct.clear() + idx -= 2 + continue + + if unquoted in self._protected: + ret.extend(pct) + elif unquoted in safe: + ret.append(ord(unquoted)) + else: + ret.extend(pct) + pct.clear() + + # special case, if we have only one char after "%" + elif len(pct) == 2 and idx == len(bval): + ret.extend(b"%25") + pct.clear() + idx -= 1 + + continue + + elif ch == ord("%") and self._requote: + pct.clear() + pct.append(ch) + + # special case if "%" is last char + if idx == len(bval): + ret.extend(b"%25") + + continue + + if self._qs and ch == ord(" "): + ret.append(ord("+")) + continue + if ch in bsafe: + ret.append(ch) + continue + + ret.extend((f"%{ch:02X}").encode("ascii")) + + ret2 = ret.decode("ascii") + if ret2 == val: + return val + return ret2 + + +class _Unquoter: + def __init__(self, *, ignore: str = "", unsafe: str = "", qs: bool = False) -> None: + self._ignore = ignore + self._unsafe = unsafe + self._qs = qs + self._quoter = _Quoter() + self._qs_quoter = _Quoter(qs=True) + + def __call__(self, val: str) -> str: + if val is None: + return None + if not isinstance(val, str): + raise TypeError("Argument should be str") + if not val: + return "" + decoder = cast(codecs.BufferedIncrementalDecoder, utf8_decoder()) + ret = [] + idx = 0 + while idx < len(val): + ch = val[idx] + idx += 1 + if ch == "%" and idx <= len(val) - 2: + pct = val[idx : idx + 2] + if _IS_HEX_STR.fullmatch(pct): + b = bytes([int(pct, base=16)]) + idx += 2 + try: + unquoted = decoder.decode(b) + except UnicodeDecodeError: + start_pct = idx - 3 - len(decoder.buffer) * 3 + ret.append(val[start_pct : idx - 3]) + decoder.reset() + try: + unquoted = decoder.decode(b) + except UnicodeDecodeError: + ret.append(val[idx - 3 : idx]) + continue + if not unquoted: + continue + if self._qs and unquoted in "+=&;": + to_add = self._qs_quoter(unquoted) + if to_add is None: # pragma: no cover + raise RuntimeError("Cannot quote None") + ret.append(to_add) + elif unquoted in self._unsafe or unquoted in self._ignore: + to_add = self._quoter(unquoted) + if to_add is None: # pragma: no cover + raise RuntimeError("Cannot quote None") + ret.append(to_add) + else: + ret.append(unquoted) + continue + + if decoder.buffer: + start_pct = idx - 1 - len(decoder.buffer) * 3 + ret.append(val[start_pct : idx - 1]) + decoder.reset() + + if ch == "+": + if not self._qs or ch in self._unsafe: + ret.append("+") + else: + ret.append(" ") + continue + + if ch in self._unsafe: + ret.append("%") + h = hex(ord(ch)).upper()[2:] + for ch in h: + ret.append(ch) + continue + + ret.append(ch) + + if decoder.buffer: + ret.append(val[-len(decoder.buffer) * 3 :]) + + ret2 = "".join(ret) + if ret2 == val: + return val + return ret2 diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/_url.py b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/_url.py new file mode 100644 index 0000000000000000000000000000000000000000..4e4b8a3763c127b41ece5973b25fbd9a4bfb6122 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/_url.py @@ -0,0 +1,1584 @@ +import re +import sys +import warnings +from collections.abc import Mapping, Sequence +from enum import Enum +from functools import _CacheInfo, lru_cache +from ipaddress import ip_address +from typing import TYPE_CHECKING, Any, TypedDict, TypeVar, Union, overload +from urllib.parse import SplitResult, parse_qsl, uses_relative + +import idna +from multidict import MultiDict, MultiDictProxy +from propcache.api import under_cached_property as cached_property + +from ._parse import ( + USES_AUTHORITY, + SplitURLType, + make_netloc, + split_netloc, + split_url, + unsplit_result, +) +from ._path import normalize_path, normalize_path_segments +from ._query import ( + Query, + QueryVariable, + SimpleQuery, + get_str_query, + get_str_query_from_iterable, + get_str_query_from_sequence_iterable, +) +from ._quoters import ( + FRAGMENT_QUOTER, + FRAGMENT_REQUOTER, + PATH_QUOTER, + PATH_REQUOTER, + PATH_SAFE_UNQUOTER, + PATH_UNQUOTER, + QS_UNQUOTER, + QUERY_QUOTER, + QUERY_REQUOTER, + QUOTER, + REQUOTER, + UNQUOTER, + human_quote, +) + +DEFAULT_PORTS = {"http": 80, "https": 443, "ws": 80, "wss": 443, "ftp": 21} +USES_RELATIVE = frozenset(uses_relative) + +# Special schemes https://url.spec.whatwg.org/#special-scheme +# are not allowed to have an empty host https://url.spec.whatwg.org/#url-representation +SCHEME_REQUIRES_HOST = frozenset(("http", "https", "ws", "wss", "ftp")) + + +# reg-name: unreserved / pct-encoded / sub-delims +# this pattern matches anything that is *not* in those classes. and is only used +# on lower-cased ASCII values. +NOT_REG_NAME = re.compile( + r""" + # any character not in the unreserved or sub-delims sets, plus % + # (validated with the additional check for pct-encoded sequences below) + [^a-z0-9\-._~!$&'()*+,;=%] + | + # % only allowed if it is part of a pct-encoded + # sequence of 2 hex digits. + %(?![0-9a-f]{2}) + """, + re.VERBOSE, +) + +_T = TypeVar("_T") + +if sys.version_info >= (3, 11): + from typing import Self +else: + Self = Any + + +class UndefinedType(Enum): + """Singleton type for use with not set sentinel values.""" + + _singleton = 0 + + +UNDEFINED = UndefinedType._singleton + + +class CacheInfo(TypedDict): + """Host encoding cache.""" + + idna_encode: _CacheInfo + idna_decode: _CacheInfo + ip_address: _CacheInfo + host_validate: _CacheInfo + encode_host: _CacheInfo + + +class _InternalURLCache(TypedDict, total=False): + _val: SplitURLType + _origin: "URL" + absolute: bool + scheme: str + raw_authority: str + authority: str + raw_user: Union[str, None] + user: Union[str, None] + raw_password: Union[str, None] + password: Union[str, None] + raw_host: Union[str, None] + host: Union[str, None] + host_subcomponent: Union[str, None] + host_port_subcomponent: Union[str, None] + port: Union[int, None] + explicit_port: Union[int, None] + raw_path: str + path: str + _parsed_query: list[tuple[str, str]] + query: "MultiDictProxy[str]" + raw_query_string: str + query_string: str + path_qs: str + raw_path_qs: str + raw_fragment: str + fragment: str + raw_parts: tuple[str, ...] + parts: tuple[str, ...] + parent: "URL" + raw_name: str + name: str + raw_suffix: str + suffix: str + raw_suffixes: tuple[str, ...] + suffixes: tuple[str, ...] + + +def rewrite_module(obj: _T) -> _T: + obj.__module__ = "yarl" + return obj + + +@lru_cache +def encode_url(url_str: str) -> "URL": + """Parse unencoded URL.""" + cache: _InternalURLCache = {} + host: Union[str, None] + scheme, netloc, path, query, fragment = split_url(url_str) + if not netloc: # netloc + host = "" + else: + if ":" in netloc or "@" in netloc or "[" in netloc: + # Complex netloc + username, password, host, port = split_netloc(netloc) + else: + username = password = port = None + host = netloc + if host is None: + if scheme in SCHEME_REQUIRES_HOST: + msg = ( + "Invalid URL: host is required for " + f"absolute urls with the {scheme} scheme" + ) + raise ValueError(msg) + else: + host = "" + host = _encode_host(host, validate_host=False) + # Remove brackets as host encoder adds back brackets for IPv6 addresses + cache["raw_host"] = host[1:-1] if "[" in host else host + cache["explicit_port"] = port + if password is None and username is None: + # Fast path for URLs without user, password + netloc = host if port is None else f"{host}:{port}" + cache["raw_user"] = None + cache["raw_password"] = None + else: + raw_user = REQUOTER(username) if username else username + raw_password = REQUOTER(password) if password else password + netloc = make_netloc(raw_user, raw_password, host, port) + cache["raw_user"] = raw_user + cache["raw_password"] = raw_password + + if path: + path = PATH_REQUOTER(path) + if netloc and "." in path: + path = normalize_path(path) + if query: + query = QUERY_REQUOTER(query) + if fragment: + fragment = FRAGMENT_REQUOTER(fragment) + + cache["scheme"] = scheme + cache["raw_path"] = "/" if not path and netloc else path + cache["raw_query_string"] = query + cache["raw_fragment"] = fragment + + self = object.__new__(URL) + self._scheme = scheme + self._netloc = netloc + self._path = path + self._query = query + self._fragment = fragment + self._cache = cache + return self + + +@lru_cache +def pre_encoded_url(url_str: str) -> "URL": + """Parse pre-encoded URL.""" + self = object.__new__(URL) + val = split_url(url_str) + self._scheme, self._netloc, self._path, self._query, self._fragment = val + self._cache = {} + return self + + +@lru_cache +def build_pre_encoded_url( + scheme: str, + authority: str, + user: Union[str, None], + password: Union[str, None], + host: str, + port: Union[int, None], + path: str, + query_string: str, + fragment: str, +) -> "URL": + """Build a pre-encoded URL from parts.""" + self = object.__new__(URL) + self._scheme = scheme + if authority: + self._netloc = authority + elif host: + if port is not None: + port = None if port == DEFAULT_PORTS.get(scheme) else port + if user is None and password is None: + self._netloc = host if port is None else f"{host}:{port}" + else: + self._netloc = make_netloc(user, password, host, port) + else: + self._netloc = "" + self._path = path + self._query = query_string + self._fragment = fragment + self._cache = {} + return self + + +def from_parts_uncached( + scheme: str, netloc: str, path: str, query: str, fragment: str +) -> "URL": + """Create a new URL from parts.""" + self = object.__new__(URL) + self._scheme = scheme + self._netloc = netloc + self._path = path + self._query = query + self._fragment = fragment + self._cache = {} + return self + + +from_parts = lru_cache(from_parts_uncached) + + +@rewrite_module +class URL: + # Don't derive from str + # follow pathlib.Path design + # probably URL will not suffer from pathlib problems: + # it's intended for libraries like aiohttp, + # not to be passed into standard library functions like os.open etc. + + # URL grammar (RFC 3986) + # pct-encoded = "%" HEXDIG HEXDIG + # reserved = gen-delims / sub-delims + # gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@" + # sub-delims = "!" / "$" / "&" / "'" / "(" / ")" + # / "*" / "+" / "," / ";" / "=" + # unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" + # URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ] + # hier-part = "//" authority path-abempty + # / path-absolute + # / path-rootless + # / path-empty + # scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) + # authority = [ userinfo "@" ] host [ ":" port ] + # userinfo = *( unreserved / pct-encoded / sub-delims / ":" ) + # host = IP-literal / IPv4address / reg-name + # IP-literal = "[" ( IPv6address / IPvFuture ) "]" + # IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" ) + # IPv6address = 6( h16 ":" ) ls32 + # / "::" 5( h16 ":" ) ls32 + # / [ h16 ] "::" 4( h16 ":" ) ls32 + # / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32 + # / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32 + # / [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32 + # / [ *4( h16 ":" ) h16 ] "::" ls32 + # / [ *5( h16 ":" ) h16 ] "::" h16 + # / [ *6( h16 ":" ) h16 ] "::" + # ls32 = ( h16 ":" h16 ) / IPv4address + # ; least-significant 32 bits of address + # h16 = 1*4HEXDIG + # ; 16 bits of address represented in hexadecimal + # IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet + # dec-octet = DIGIT ; 0-9 + # / %x31-39 DIGIT ; 10-99 + # / "1" 2DIGIT ; 100-199 + # / "2" %x30-34 DIGIT ; 200-249 + # / "25" %x30-35 ; 250-255 + # reg-name = *( unreserved / pct-encoded / sub-delims ) + # port = *DIGIT + # path = path-abempty ; begins with "/" or is empty + # / path-absolute ; begins with "/" but not "//" + # / path-noscheme ; begins with a non-colon segment + # / path-rootless ; begins with a segment + # / path-empty ; zero characters + # path-abempty = *( "/" segment ) + # path-absolute = "/" [ segment-nz *( "/" segment ) ] + # path-noscheme = segment-nz-nc *( "/" segment ) + # path-rootless = segment-nz *( "/" segment ) + # path-empty = 0 + # segment = *pchar + # segment-nz = 1*pchar + # segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" ) + # ; non-zero-length segment without any colon ":" + # pchar = unreserved / pct-encoded / sub-delims / ":" / "@" + # query = *( pchar / "/" / "?" ) + # fragment = *( pchar / "/" / "?" ) + # URI-reference = URI / relative-ref + # relative-ref = relative-part [ "?" query ] [ "#" fragment ] + # relative-part = "//" authority path-abempty + # / path-absolute + # / path-noscheme + # / path-empty + # absolute-URI = scheme ":" hier-part [ "?" query ] + __slots__ = ("_cache", "_scheme", "_netloc", "_path", "_query", "_fragment") + + _scheme: str + _netloc: str + _path: str + _query: str + _fragment: str + + def __new__( + cls, + val: Union[str, SplitResult, "URL", UndefinedType] = UNDEFINED, + *, + encoded: bool = False, + strict: Union[bool, None] = None, + ) -> "URL": + if strict is not None: # pragma: no cover + warnings.warn("strict parameter is ignored") + if type(val) is str: + return pre_encoded_url(val) if encoded else encode_url(val) + if type(val) is cls: + return val + if type(val) is SplitResult: + if not encoded: + raise ValueError("Cannot apply decoding to SplitResult") + return from_parts(*val) + if isinstance(val, str): + return pre_encoded_url(str(val)) if encoded else encode_url(str(val)) + if val is UNDEFINED: + # Special case for UNDEFINED since it might be unpickling and we do + # not want to cache as the `__set_state__` call would mutate the URL + # object in the `pre_encoded_url` or `encoded_url` caches. + self = object.__new__(URL) + self._scheme = self._netloc = self._path = self._query = self._fragment = "" + self._cache = {} + return self + raise TypeError("Constructor parameter should be str") + + @classmethod + def build( + cls, + *, + scheme: str = "", + authority: str = "", + user: Union[str, None] = None, + password: Union[str, None] = None, + host: str = "", + port: Union[int, None] = None, + path: str = "", + query: Union[Query, None] = None, + query_string: str = "", + fragment: str = "", + encoded: bool = False, + ) -> "URL": + """Creates and returns a new URL""" + + if authority and (user or password or host or port): + raise ValueError( + 'Can\'t mix "authority" with "user", "password", "host" or "port".' + ) + if port is not None and not isinstance(port, int): + raise TypeError(f"The port is required to be int, got {type(port)!r}.") + if port and not host: + raise ValueError('Can\'t build URL with "port" but without "host".') + if query and query_string: + raise ValueError('Only one of "query" or "query_string" should be passed') + if ( + scheme is None + or authority is None + or host is None + or path is None + or query_string is None + or fragment is None + ): + raise TypeError( + 'NoneType is illegal for "scheme", "authority", "host", "path", ' + '"query_string", and "fragment" args, use empty string instead.' + ) + + if query: + query_string = get_str_query(query) or "" + + if encoded: + return build_pre_encoded_url( + scheme, + authority, + user, + password, + host, + port, + path, + query_string, + fragment, + ) + + self = object.__new__(URL) + self._scheme = scheme + _host: Union[str, None] = None + if authority: + user, password, _host, port = split_netloc(authority) + _host = _encode_host(_host, validate_host=False) if _host else "" + elif host: + _host = _encode_host(host, validate_host=True) + else: + self._netloc = "" + + if _host is not None: + if port is not None: + port = None if port == DEFAULT_PORTS.get(scheme) else port + if user is None and password is None: + self._netloc = _host if port is None else f"{_host}:{port}" + else: + self._netloc = make_netloc(user, password, _host, port, True) + + path = PATH_QUOTER(path) if path else path + if path and self._netloc: + if "." in path: + path = normalize_path(path) + if path[0] != "/": + msg = ( + "Path in a URL with authority should " + "start with a slash ('/') if set" + ) + raise ValueError(msg) + + self._path = path + if not query and query_string: + query_string = QUERY_QUOTER(query_string) + self._query = query_string + self._fragment = FRAGMENT_QUOTER(fragment) if fragment else fragment + self._cache = {} + return self + + def __init_subclass__(cls): + raise TypeError(f"Inheriting a class {cls!r} from URL is forbidden") + + def __str__(self) -> str: + if not self._path and self._netloc and (self._query or self._fragment): + path = "/" + else: + path = self._path + if (port := self.explicit_port) is not None and port == DEFAULT_PORTS.get( + self._scheme + ): + # port normalization - using None for default ports to remove from rendering + # https://datatracker.ietf.org/doc/html/rfc3986.html#section-6.2.3 + host = self.host_subcomponent + netloc = make_netloc(self.raw_user, self.raw_password, host, None) + else: + netloc = self._netloc + return unsplit_result(self._scheme, netloc, path, self._query, self._fragment) + + def __repr__(self) -> str: + return f"{self.__class__.__name__}('{str(self)}')" + + def __bytes__(self) -> bytes: + return str(self).encode("ascii") + + def __eq__(self, other: object) -> bool: + if type(other) is not URL: + return NotImplemented + + path1 = "/" if not self._path and self._netloc else self._path + path2 = "/" if not other._path and other._netloc else other._path + return ( + self._scheme == other._scheme + and self._netloc == other._netloc + and path1 == path2 + and self._query == other._query + and self._fragment == other._fragment + ) + + def __hash__(self) -> int: + if (ret := self._cache.get("hash")) is None: + path = "/" if not self._path and self._netloc else self._path + ret = self._cache["hash"] = hash( + (self._scheme, self._netloc, path, self._query, self._fragment) + ) + return ret + + def __le__(self, other: object) -> bool: + if type(other) is not URL: + return NotImplemented + return self._val <= other._val + + def __lt__(self, other: object) -> bool: + if type(other) is not URL: + return NotImplemented + return self._val < other._val + + def __ge__(self, other: object) -> bool: + if type(other) is not URL: + return NotImplemented + return self._val >= other._val + + def __gt__(self, other: object) -> bool: + if type(other) is not URL: + return NotImplemented + return self._val > other._val + + def __truediv__(self, name: str) -> "URL": + if not isinstance(name, str): + return NotImplemented + return self._make_child((str(name),)) + + def __mod__(self, query: Query) -> "URL": + return self.update_query(query) + + def __bool__(self) -> bool: + return bool(self._netloc or self._path or self._query or self._fragment) + + def __getstate__(self) -> tuple[SplitResult]: + return (tuple.__new__(SplitResult, self._val),) + + def __setstate__(self, state): + if state[0] is None and isinstance(state[1], dict): + # default style pickle + val = state[1]["_val"] + else: + val, *unused = state + self._scheme, self._netloc, self._path, self._query, self._fragment = val + self._cache = {} + + def _cache_netloc(self) -> None: + """Cache the netloc parts of the URL.""" + c = self._cache + split_loc = split_netloc(self._netloc) + c["raw_user"], c["raw_password"], c["raw_host"], c["explicit_port"] = split_loc + + def is_absolute(self) -> bool: + """A check for absolute URLs. + + Return True for absolute ones (having scheme or starting + with //), False otherwise. + + Is is preferred to call the .absolute property instead + as it is cached. + """ + return self.absolute + + def is_default_port(self) -> bool: + """A check for default port. + + Return True if port is default for specified scheme, + e.g. 'http://python.org' or 'http://python.org:80', False + otherwise. + + Return False for relative URLs. + + """ + if (explicit := self.explicit_port) is None: + # If the explicit port is None, then the URL must be + # using the default port unless its a relative URL + # which does not have an implicit port / default port + return self._netloc != "" + return explicit == DEFAULT_PORTS.get(self._scheme) + + def origin(self) -> "URL": + """Return an URL with scheme, host and port parts only. + + user, password, path, query and fragment are removed. + + """ + # TODO: add a keyword-only option for keeping user/pass maybe? + return self._origin + + @cached_property + def _val(self) -> SplitURLType: + return (self._scheme, self._netloc, self._path, self._query, self._fragment) + + @cached_property + def _origin(self) -> "URL": + """Return an URL with scheme, host and port parts only. + + user, password, path, query and fragment are removed. + """ + if not (netloc := self._netloc): + raise ValueError("URL should be absolute") + if not (scheme := self._scheme): + raise ValueError("URL should have scheme") + if "@" in netloc: + encoded_host = self.host_subcomponent + netloc = make_netloc(None, None, encoded_host, self.explicit_port) + elif not self._path and not self._query and not self._fragment: + return self + return from_parts(scheme, netloc, "", "", "") + + def relative(self) -> "URL": + """Return a relative part of the URL. + + scheme, user, password, host and port are removed. + + """ + if not self._netloc: + raise ValueError("URL should be absolute") + return from_parts("", "", self._path, self._query, self._fragment) + + @cached_property + def absolute(self) -> bool: + """A check for absolute URLs. + + Return True for absolute ones (having scheme or starting + with //), False otherwise. + + """ + # `netloc`` is an empty string for relative URLs + # Checking `netloc` is faster than checking `hostname` + # because `hostname` is a property that does some extra work + # to parse the host from the `netloc` + return self._netloc != "" + + @cached_property + def scheme(self) -> str: + """Scheme for absolute URLs. + + Empty string for relative URLs or URLs starting with // + + """ + return self._scheme + + @cached_property + def raw_authority(self) -> str: + """Encoded authority part of URL. + + Empty string for relative URLs. + + """ + return self._netloc + + @cached_property + def authority(self) -> str: + """Decoded authority part of URL. + + Empty string for relative URLs. + + """ + return make_netloc(self.user, self.password, self.host, self.port) + + @cached_property + def raw_user(self) -> Union[str, None]: + """Encoded user part of URL. + + None if user is missing. + + """ + # not .username + self._cache_netloc() + return self._cache["raw_user"] + + @cached_property + def user(self) -> Union[str, None]: + """Decoded user part of URL. + + None if user is missing. + + """ + if (raw_user := self.raw_user) is None: + return None + return UNQUOTER(raw_user) + + @cached_property + def raw_password(self) -> Union[str, None]: + """Encoded password part of URL. + + None if password is missing. + + """ + self._cache_netloc() + return self._cache["raw_password"] + + @cached_property + def password(self) -> Union[str, None]: + """Decoded password part of URL. + + None if password is missing. + + """ + if (raw_password := self.raw_password) is None: + return None + return UNQUOTER(raw_password) + + @cached_property + def raw_host(self) -> Union[str, None]: + """Encoded host part of URL. + + None for relative URLs. + + When working with IPv6 addresses, use the `host_subcomponent` property instead + as it will return the host subcomponent with brackets. + """ + # Use host instead of hostname for sake of shortness + # May add .hostname prop later + self._cache_netloc() + return self._cache["raw_host"] + + @cached_property + def host(self) -> Union[str, None]: + """Decoded host part of URL. + + None for relative URLs. + + """ + if (raw := self.raw_host) is None: + return None + if raw and raw[-1].isdigit() or ":" in raw: + # IP addresses are never IDNA encoded + return raw + return _idna_decode(raw) + + @cached_property + def host_subcomponent(self) -> Union[str, None]: + """Return the host subcomponent part of URL. + + None for relative URLs. + + https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2 + + `IP-literal = "[" ( IPv6address / IPvFuture ) "]"` + + Examples: + - `http://example.com:8080` -> `example.com` + - `http://example.com:80` -> `example.com` + - `https://127.0.0.1:8443` -> `127.0.0.1` + - `https://[::1]:8443` -> `[::1]` + - `http://[::1]` -> `[::1]` + + """ + if (raw := self.raw_host) is None: + return None + return f"[{raw}]" if ":" in raw else raw + + @cached_property + def host_port_subcomponent(self) -> Union[str, None]: + """Return the host and port subcomponent part of URL. + + Trailing dots are removed from the host part. + + This value is suitable for use in the Host header of an HTTP request. + + None for relative URLs. + + https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2 + `IP-literal = "[" ( IPv6address / IPvFuture ) "]"` + https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.3 + port = *DIGIT + + Examples: + - `http://example.com:8080` -> `example.com:8080` + - `http://example.com:80` -> `example.com` + - `http://example.com.:80` -> `example.com` + - `https://127.0.0.1:8443` -> `127.0.0.1:8443` + - `https://[::1]:8443` -> `[::1]:8443` + - `http://[::1]` -> `[::1]` + + """ + if (raw := self.raw_host) is None: + return None + if raw[-1] == ".": + # Remove all trailing dots from the netloc as while + # they are valid FQDNs in DNS, TLS validation fails. + # See https://github.com/aio-libs/aiohttp/issues/3636. + # To avoid string manipulation we only call rstrip if + # the last character is a dot. + raw = raw.rstrip(".") + port = self.explicit_port + if port is None or port == DEFAULT_PORTS.get(self._scheme): + return f"[{raw}]" if ":" in raw else raw + return f"[{raw}]:{port}" if ":" in raw else f"{raw}:{port}" + + @cached_property + def port(self) -> Union[int, None]: + """Port part of URL, with scheme-based fallback. + + None for relative URLs or URLs without explicit port and + scheme without default port substitution. + + """ + if (explicit_port := self.explicit_port) is not None: + return explicit_port + return DEFAULT_PORTS.get(self._scheme) + + @cached_property + def explicit_port(self) -> Union[int, None]: + """Port part of URL, without scheme-based fallback. + + None for relative URLs or URLs without explicit port. + + """ + self._cache_netloc() + return self._cache["explicit_port"] + + @cached_property + def raw_path(self) -> str: + """Encoded path of URL. + + / for absolute URLs without path part. + + """ + return self._path if self._path or not self._netloc else "/" + + @cached_property + def path(self) -> str: + """Decoded path of URL. + + / for absolute URLs without path part. + + """ + return PATH_UNQUOTER(self._path) if self._path else "/" if self._netloc else "" + + @cached_property + def path_safe(self) -> str: + """Decoded path of URL. + + / for absolute URLs without path part. + + / (%2F) and % (%25) are not decoded + + """ + if self._path: + return PATH_SAFE_UNQUOTER(self._path) + return "/" if self._netloc else "" + + @cached_property + def _parsed_query(self) -> list[tuple[str, str]]: + """Parse query part of URL.""" + return parse_qsl(self._query, keep_blank_values=True) + + @cached_property + def query(self) -> "MultiDictProxy[str]": + """A MultiDictProxy representing parsed query parameters in decoded + representation. + + Empty value if URL has no query part. + + """ + return MultiDictProxy(MultiDict(self._parsed_query)) + + @cached_property + def raw_query_string(self) -> str: + """Encoded query part of URL. + + Empty string if query is missing. + + """ + return self._query + + @cached_property + def query_string(self) -> str: + """Decoded query part of URL. + + Empty string if query is missing. + + """ + return QS_UNQUOTER(self._query) if self._query else "" + + @cached_property + def path_qs(self) -> str: + """Decoded path of URL with query.""" + return self.path if not (q := self.query_string) else f"{self.path}?{q}" + + @cached_property + def raw_path_qs(self) -> str: + """Encoded path of URL with query.""" + if q := self._query: + return f"{self._path}?{q}" if self._path or not self._netloc else f"/?{q}" + return self._path if self._path or not self._netloc else "/" + + @cached_property + def raw_fragment(self) -> str: + """Encoded fragment part of URL. + + Empty string if fragment is missing. + + """ + return self._fragment + + @cached_property + def fragment(self) -> str: + """Decoded fragment part of URL. + + Empty string if fragment is missing. + + """ + return UNQUOTER(self._fragment) if self._fragment else "" + + @cached_property + def raw_parts(self) -> tuple[str, ...]: + """A tuple containing encoded *path* parts. + + ('/',) for absolute URLs if *path* is missing. + + """ + path = self._path + if self._netloc: + return ("/", *path[1:].split("/")) if path else ("/",) + if path and path[0] == "/": + return ("/", *path[1:].split("/")) + return tuple(path.split("/")) + + @cached_property + def parts(self) -> tuple[str, ...]: + """A tuple containing decoded *path* parts. + + ('/',) for absolute URLs if *path* is missing. + + """ + return tuple(UNQUOTER(part) for part in self.raw_parts) + + @cached_property + def parent(self) -> "URL": + """A new URL with last part of path removed and cleaned up query and + fragment. + + """ + path = self._path + if not path or path == "/": + if self._fragment or self._query: + return from_parts(self._scheme, self._netloc, path, "", "") + return self + parts = path.split("/") + return from_parts(self._scheme, self._netloc, "/".join(parts[:-1]), "", "") + + @cached_property + def raw_name(self) -> str: + """The last part of raw_parts.""" + parts = self.raw_parts + if not self._netloc: + return parts[-1] + parts = parts[1:] + return parts[-1] if parts else "" + + @cached_property + def name(self) -> str: + """The last part of parts.""" + return UNQUOTER(self.raw_name) + + @cached_property + def raw_suffix(self) -> str: + name = self.raw_name + i = name.rfind(".") + return name[i:] if 0 < i < len(name) - 1 else "" + + @cached_property + def suffix(self) -> str: + return UNQUOTER(self.raw_suffix) + + @cached_property + def raw_suffixes(self) -> tuple[str, ...]: + name = self.raw_name + if name.endswith("."): + return () + name = name.lstrip(".") + return tuple("." + suffix for suffix in name.split(".")[1:]) + + @cached_property + def suffixes(self) -> tuple[str, ...]: + return tuple(UNQUOTER(suffix) for suffix in self.raw_suffixes) + + def _make_child(self, paths: "Sequence[str]", encoded: bool = False) -> "URL": + """ + add paths to self._path, accounting for absolute vs relative paths, + keep existing, but do not create new, empty segments + """ + parsed: list[str] = [] + needs_normalize: bool = False + for idx, path in enumerate(reversed(paths)): + # empty segment of last is not removed + last = idx == 0 + if path and path[0] == "/": + raise ValueError( + f"Appending path {path!r} starting from slash is forbidden" + ) + # We need to quote the path if it is not already encoded + # This cannot be done at the end because the existing + # path is already quoted and we do not want to double quote + # the existing path. + path = path if encoded else PATH_QUOTER(path) + needs_normalize |= "." in path + segments = path.split("/") + segments.reverse() + # remove trailing empty segment for all but the last path + parsed += segments[1:] if not last and segments[0] == "" else segments + + if (path := self._path) and (old_segments := path.split("/")): + # If the old path ends with a slash, the last segment is an empty string + # and should be removed before adding the new path segments. + old = old_segments[:-1] if old_segments[-1] == "" else old_segments + old.reverse() + parsed += old + + # If the netloc is present, inject a leading slash when adding a + # path to an absolute URL where there was none before. + if (netloc := self._netloc) and parsed and parsed[-1] != "": + parsed.append("") + + parsed.reverse() + if not netloc or not needs_normalize: + return from_parts(self._scheme, netloc, "/".join(parsed), "", "") + + path = "/".join(normalize_path_segments(parsed)) + # If normalizing the path segments removed the leading slash, add it back. + if path and path[0] != "/": + path = f"/{path}" + return from_parts(self._scheme, netloc, path, "", "") + + def with_scheme(self, scheme: str) -> "URL": + """Return a new URL with scheme replaced.""" + # N.B. doesn't cleanup query/fragment + if not isinstance(scheme, str): + raise TypeError("Invalid scheme type") + lower_scheme = scheme.lower() + netloc = self._netloc + if not netloc and lower_scheme in SCHEME_REQUIRES_HOST: + msg = ( + "scheme replacement is not allowed for " + f"relative URLs for the {lower_scheme} scheme" + ) + raise ValueError(msg) + return from_parts(lower_scheme, netloc, self._path, self._query, self._fragment) + + def with_user(self, user: Union[str, None]) -> "URL": + """Return a new URL with user replaced. + + Autoencode user if needed. + + Clear user/password if user is None. + + """ + # N.B. doesn't cleanup query/fragment + if user is None: + password = None + elif isinstance(user, str): + user = QUOTER(user) + password = self.raw_password + else: + raise TypeError("Invalid user type") + if not (netloc := self._netloc): + raise ValueError("user replacement is not allowed for relative URLs") + encoded_host = self.host_subcomponent or "" + netloc = make_netloc(user, password, encoded_host, self.explicit_port) + return from_parts(self._scheme, netloc, self._path, self._query, self._fragment) + + def with_password(self, password: Union[str, None]) -> "URL": + """Return a new URL with password replaced. + + Autoencode password if needed. + + Clear password if argument is None. + + """ + # N.B. doesn't cleanup query/fragment + if password is None: + pass + elif isinstance(password, str): + password = QUOTER(password) + else: + raise TypeError("Invalid password type") + if not (netloc := self._netloc): + raise ValueError("password replacement is not allowed for relative URLs") + encoded_host = self.host_subcomponent or "" + port = self.explicit_port + netloc = make_netloc(self.raw_user, password, encoded_host, port) + return from_parts(self._scheme, netloc, self._path, self._query, self._fragment) + + def with_host(self, host: str) -> "URL": + """Return a new URL with host replaced. + + Autoencode host if needed. + + Changing host for relative URLs is not allowed, use .join() + instead. + + """ + # N.B. doesn't cleanup query/fragment + if not isinstance(host, str): + raise TypeError("Invalid host type") + if not (netloc := self._netloc): + raise ValueError("host replacement is not allowed for relative URLs") + if not host: + raise ValueError("host removing is not allowed") + encoded_host = _encode_host(host, validate_host=True) if host else "" + port = self.explicit_port + netloc = make_netloc(self.raw_user, self.raw_password, encoded_host, port) + return from_parts(self._scheme, netloc, self._path, self._query, self._fragment) + + def with_port(self, port: Union[int, None]) -> "URL": + """Return a new URL with port replaced. + + Clear port to default if None is passed. + + """ + # N.B. doesn't cleanup query/fragment + if port is not None: + if isinstance(port, bool) or not isinstance(port, int): + raise TypeError(f"port should be int or None, got {type(port)}") + if not (0 <= port <= 65535): + raise ValueError(f"port must be between 0 and 65535, got {port}") + if not (netloc := self._netloc): + raise ValueError("port replacement is not allowed for relative URLs") + encoded_host = self.host_subcomponent or "" + netloc = make_netloc(self.raw_user, self.raw_password, encoded_host, port) + return from_parts(self._scheme, netloc, self._path, self._query, self._fragment) + + def with_path( + self, + path: str, + *, + encoded: bool = False, + keep_query: bool = False, + keep_fragment: bool = False, + ) -> "URL": + """Return a new URL with path replaced.""" + netloc = self._netloc + if not encoded: + path = PATH_QUOTER(path) + if netloc: + path = normalize_path(path) if "." in path else path + if path and path[0] != "/": + path = f"/{path}" + query = self._query if keep_query else "" + fragment = self._fragment if keep_fragment else "" + return from_parts(self._scheme, netloc, path, query, fragment) + + @overload + def with_query(self, query: Query) -> "URL": ... + + @overload + def with_query(self, **kwargs: QueryVariable) -> "URL": ... + + def with_query(self, *args: Any, **kwargs: Any) -> "URL": + """Return a new URL with query part replaced. + + Accepts any Mapping (e.g. dict, multidict.MultiDict instances) + or str, autoencode the argument if needed. + + A sequence of (key, value) pairs is supported as well. + + It also can take an arbitrary number of keyword arguments. + + Clear query if None is passed. + + """ + # N.B. doesn't cleanup query/fragment + query = get_str_query(*args, **kwargs) or "" + return from_parts_uncached( + self._scheme, self._netloc, self._path, query, self._fragment + ) + + @overload + def extend_query(self, query: Query) -> "URL": ... + + @overload + def extend_query(self, **kwargs: QueryVariable) -> "URL": ... + + def extend_query(self, *args: Any, **kwargs: Any) -> "URL": + """Return a new URL with query part combined with the existing. + + This method will not remove existing query parameters. + + Example: + >>> url = URL('http://example.com/?a=1&b=2') + >>> url.extend_query(a=3, c=4) + URL('http://example.com/?a=1&b=2&a=3&c=4') + """ + if not (new_query := get_str_query(*args, **kwargs)): + return self + if query := self._query: + # both strings are already encoded so we can use a simple + # string join + query += new_query if query[-1] == "&" else f"&{new_query}" + else: + query = new_query + return from_parts_uncached( + self._scheme, self._netloc, self._path, query, self._fragment + ) + + @overload + def update_query(self, query: Query) -> "URL": ... + + @overload + def update_query(self, **kwargs: QueryVariable) -> "URL": ... + + def update_query(self, *args: Any, **kwargs: Any) -> "URL": + """Return a new URL with query part updated. + + This method will overwrite existing query parameters. + + Example: + >>> url = URL('http://example.com/?a=1&b=2') + >>> url.update_query(a=3, c=4) + URL('http://example.com/?a=3&b=2&c=4') + """ + in_query: Union[str, Mapping[str, QueryVariable], None] + if kwargs: + if args: + msg = "Either kwargs or single query parameter must be present" + raise ValueError(msg) + in_query = kwargs + elif len(args) == 1: + in_query = args[0] + else: + raise ValueError("Either kwargs or single query parameter must be present") + + if in_query is None: + query = "" + elif not in_query: + query = self._query + elif isinstance(in_query, Mapping): + qm: MultiDict[QueryVariable] = MultiDict(self._parsed_query) + qm.update(in_query) + query = get_str_query_from_sequence_iterable(qm.items()) + elif isinstance(in_query, str): + qstr: MultiDict[str] = MultiDict(self._parsed_query) + qstr.update(parse_qsl(in_query, keep_blank_values=True)) + query = get_str_query_from_iterable(qstr.items()) + elif isinstance(in_query, (bytes, bytearray, memoryview)): + msg = "Invalid query type: bytes, bytearray and memoryview are forbidden" + raise TypeError(msg) + elif isinstance(in_query, Sequence): + # We don't expect sequence values if we're given a list of pairs + # already; only mappings like builtin `dict` which can't have the + # same key pointing to multiple values are allowed to use + # `_query_seq_pairs`. + qs: MultiDict[SimpleQuery] = MultiDict(self._parsed_query) + qs.update(in_query) + query = get_str_query_from_iterable(qs.items()) + else: + raise TypeError( + "Invalid query type: only str, mapping or " + "sequence of (key, value) pairs is allowed" + ) + return from_parts_uncached( + self._scheme, self._netloc, self._path, query, self._fragment + ) + + def without_query_params(self, *query_params: str) -> "URL": + """Remove some keys from query part and return new URL.""" + params_to_remove = set(query_params) & self.query.keys() + if not params_to_remove: + return self + return self.with_query( + tuple( + (name, value) + for name, value in self.query.items() + if name not in params_to_remove + ) + ) + + def with_fragment(self, fragment: Union[str, None]) -> "URL": + """Return a new URL with fragment replaced. + + Autoencode fragment if needed. + + Clear fragment to default if None is passed. + + """ + # N.B. doesn't cleanup query/fragment + if fragment is None: + raw_fragment = "" + elif not isinstance(fragment, str): + raise TypeError("Invalid fragment type") + else: + raw_fragment = FRAGMENT_QUOTER(fragment) + if self._fragment == raw_fragment: + return self + return from_parts( + self._scheme, self._netloc, self._path, self._query, raw_fragment + ) + + def with_name( + self, + name: str, + *, + keep_query: bool = False, + keep_fragment: bool = False, + ) -> "URL": + """Return a new URL with name (last part of path) replaced. + + Query and fragment parts are cleaned up. + + Name is encoded if needed. + + """ + # N.B. DOES cleanup query/fragment + if not isinstance(name, str): + raise TypeError("Invalid name type") + if "/" in name: + raise ValueError("Slash in name is not allowed") + name = PATH_QUOTER(name) + if name in (".", ".."): + raise ValueError(". and .. values are forbidden") + parts = list(self.raw_parts) + if netloc := self._netloc: + if len(parts) == 1: + parts.append(name) + else: + parts[-1] = name + parts[0] = "" # replace leading '/' + else: + parts[-1] = name + if parts[0] == "/": + parts[0] = "" # replace leading '/' + + query = self._query if keep_query else "" + fragment = self._fragment if keep_fragment else "" + return from_parts(self._scheme, netloc, "/".join(parts), query, fragment) + + def with_suffix( + self, + suffix: str, + *, + keep_query: bool = False, + keep_fragment: bool = False, + ) -> "URL": + """Return a new URL with suffix (file extension of name) replaced. + + Query and fragment parts are cleaned up. + + suffix is encoded if needed. + """ + if not isinstance(suffix, str): + raise TypeError("Invalid suffix type") + if suffix and not suffix[0] == "." or suffix == ".": + raise ValueError(f"Invalid suffix {suffix!r}") + name = self.raw_name + if not name: + raise ValueError(f"{self!r} has an empty name") + old_suffix = self.raw_suffix + name = name + suffix if not old_suffix else name[: -len(old_suffix)] + suffix + + return self.with_name(name, keep_query=keep_query, keep_fragment=keep_fragment) + + def join(self, url: "URL") -> "URL": + """Join URLs + + Construct a full (“absolute”) URL by combining a “base URL” + (self) with another URL (url). + + Informally, this uses components of the base URL, in + particular the addressing scheme, the network location and + (part of) the path, to provide missing components in the + relative URL. + + """ + if type(url) is not URL: + raise TypeError("url should be URL") + + scheme = url._scheme or self._scheme + if scheme != self._scheme or scheme not in USES_RELATIVE: + return url + + # scheme is in uses_authority as uses_authority is a superset of uses_relative + if (join_netloc := url._netloc) and scheme in USES_AUTHORITY: + return from_parts(scheme, join_netloc, url._path, url._query, url._fragment) + + orig_path = self._path + if join_path := url._path: + if join_path[0] == "/": + path = join_path + elif not orig_path: + path = f"/{join_path}" + elif orig_path[-1] == "/": + path = f"{orig_path}{join_path}" + else: + # … + # and relativizing ".." + # parts[0] is / for absolute urls, + # this join will add a double slash there + path = "/".join([*self.parts[:-1], ""]) + join_path + # which has to be removed + if orig_path[0] == "/": + path = path[1:] + path = normalize_path(path) if "." in path else path + else: + path = orig_path + + return from_parts( + scheme, + self._netloc, + path, + url._query if join_path or url._query else self._query, + url._fragment if join_path or url._fragment else self._fragment, + ) + + def joinpath(self, *other: str, encoded: bool = False) -> "URL": + """Return a new URL with the elements in other appended to the path.""" + return self._make_child(other, encoded=encoded) + + def human_repr(self) -> str: + """Return decoded human readable string for URL representation.""" + user = human_quote(self.user, "#/:?@[]") + password = human_quote(self.password, "#/:?@[]") + if (host := self.host) and ":" in host: + host = f"[{host}]" + path = human_quote(self.path, "#?") + if TYPE_CHECKING: + assert path is not None + query_string = "&".join( + "{}={}".format(human_quote(k, "#&+;="), human_quote(v, "#&+;=")) + for k, v in self.query.items() + ) + fragment = human_quote(self.fragment, "") + if TYPE_CHECKING: + assert fragment is not None + netloc = make_netloc(user, password, host, self.explicit_port) + return unsplit_result(self._scheme, netloc, path, query_string, fragment) + + +_DEFAULT_IDNA_SIZE = 256 +_DEFAULT_ENCODE_SIZE = 512 + + +@lru_cache(_DEFAULT_IDNA_SIZE) +def _idna_decode(raw: str) -> str: + try: + return idna.decode(raw.encode("ascii")) + except UnicodeError: # e.g. '::1' + return raw.encode("ascii").decode("idna") + + +@lru_cache(_DEFAULT_IDNA_SIZE) +def _idna_encode(host: str) -> str: + try: + return idna.encode(host, uts46=True).decode("ascii") + except UnicodeError: + return host.encode("idna").decode("ascii") + + +@lru_cache(_DEFAULT_ENCODE_SIZE) +def _encode_host(host: str, validate_host: bool) -> str: + """Encode host part of URL.""" + # If the host ends with a digit or contains a colon, its likely + # an IP address. + if host and (host[-1].isdigit() or ":" in host): + raw_ip, sep, zone = host.partition("%") + # If it looks like an IP, we check with _ip_compressed_version + # and fall-through if its not an IP address. This is a performance + # optimization to avoid parsing IP addresses as much as possible + # because it is orders of magnitude slower than almost any other + # operation this library does. + # Might be an IP address, check it + # + # IP Addresses can look like: + # https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2 + # - 127.0.0.1 (last character is a digit) + # - 2001:db8::ff00:42:8329 (contains a colon) + # - 2001:db8::ff00:42:8329%eth0 (contains a colon) + # - [2001:db8::ff00:42:8329] (contains a colon -- brackets should + # have been removed before it gets here) + # Rare IP Address formats are not supported per: + # https://datatracker.ietf.org/doc/html/rfc3986#section-7.4 + # + # IP parsing is slow, so its wrapped in an LRU + try: + ip = ip_address(raw_ip) + except ValueError: + pass + else: + # These checks should not happen in the + # LRU to keep the cache size small + host = ip.compressed + if ip.version == 6: + return f"[{host}%{zone}]" if sep else f"[{host}]" + return f"{host}%{zone}" if sep else host + + # IDNA encoding is slow, skip it for ASCII-only strings + if host.isascii(): + # Check for invalid characters explicitly; _idna_encode() does this + # for non-ascii host names. + host = host.lower() + if validate_host and (invalid := NOT_REG_NAME.search(host)): + value, pos, extra = invalid.group(), invalid.start(), "" + if value == "@" or (value == ":" and "@" in host[pos:]): + # this looks like an authority string + extra = ( + ", if the value includes a username or password, " + "use 'authority' instead of 'host'" + ) + raise ValueError( + f"Host {host!r} cannot contain {value!r} (at position {pos}){extra}" + ) from None + return host + + return _idna_encode(host) + + +@rewrite_module +def cache_clear() -> None: + """Clear all LRU caches.""" + _idna_encode.cache_clear() + _idna_decode.cache_clear() + _encode_host.cache_clear() + + +@rewrite_module +def cache_info() -> CacheInfo: + """Report cache statistics.""" + return { + "idna_encode": _idna_encode.cache_info(), + "idna_decode": _idna_decode.cache_info(), + "ip_address": _encode_host.cache_info(), + "host_validate": _encode_host.cache_info(), + "encode_host": _encode_host.cache_info(), + } + + +@rewrite_module +def cache_configure( + *, + idna_encode_size: Union[int, None] = _DEFAULT_IDNA_SIZE, + idna_decode_size: Union[int, None] = _DEFAULT_IDNA_SIZE, + ip_address_size: Union[int, None, UndefinedType] = UNDEFINED, + host_validate_size: Union[int, None, UndefinedType] = UNDEFINED, + encode_host_size: Union[int, None, UndefinedType] = UNDEFINED, +) -> None: + """Configure LRU cache sizes.""" + global _idna_decode, _idna_encode, _encode_host + # ip_address_size, host_validate_size are no longer + # used, but are kept for backwards compatibility. + if ip_address_size is not UNDEFINED or host_validate_size is not UNDEFINED: + warnings.warn( + "cache_configure() no longer accepts the " + "ip_address_size or host_validate_size arguments, " + "they are used to set the encode_host_size instead " + "and will be removed in the future", + DeprecationWarning, + stacklevel=2, + ) + + if encode_host_size is not None: + for size in (ip_address_size, host_validate_size): + if size is None: + encode_host_size = None + elif encode_host_size is UNDEFINED: + if size is not UNDEFINED: + encode_host_size = size + elif size is not UNDEFINED: + if TYPE_CHECKING: + assert isinstance(size, int) + assert isinstance(encode_host_size, int) + encode_host_size = max(size, encode_host_size) + if encode_host_size is UNDEFINED: + encode_host_size = _DEFAULT_ENCODE_SIZE + + if TYPE_CHECKING: + assert not isinstance(encode_host_size, object) + _encode_host = lru_cache(encode_host_size)(_encode_host.__wrapped__) + _idna_decode = lru_cache(idna_decode_size)(_idna_decode.__wrapped__) + _idna_encode = lru_cache(idna_encode_size)(_idna_encode.__wrapped__) diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/py.typed b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/py.typed new file mode 100644 index 0000000000000000000000000000000000000000..dcf2c804da5e19d617a03a6c68aa128d1d1f89a0 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/agent/thirdparty_files/yarl/py.typed @@ -0,0 +1 @@ +# Placeholder diff --git a/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/utils.py b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..a4387ff27b697d4918fdd6c4228a7dfcd7a59cc6 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/ray/_private/runtime_env/utils.py @@ -0,0 +1,117 @@ +import asyncio +import itertools +import logging +import subprocess +import textwrap +import types +from typing import List + + +class SubprocessCalledProcessError(subprocess.CalledProcessError): + """The subprocess.CalledProcessError with stripped stdout.""" + + LAST_N_LINES = 50 + + def __init__(self, *args, cmd_index=None, **kwargs): + self.cmd_index = cmd_index + super().__init__(*args, **kwargs) + + @staticmethod + def _get_last_n_line(str_data: str, last_n_lines: int) -> str: + if last_n_lines < 0: + return str_data + lines = str_data.strip().split("\n") + return "\n".join(lines[-last_n_lines:]) + + def __str__(self): + str_list = ( + [] + if self.cmd_index is None + else [f"Run cmd[{self.cmd_index}] failed with the following details."] + ) + str_list.append(super().__str__()) + out = { + "stdout": self.stdout, + "stderr": self.stderr, + } + for name, s in out.items(): + if s: + subtitle = f"Last {self.LAST_N_LINES} lines of {name}:" + last_n_line_str = self._get_last_n_line(s, self.LAST_N_LINES).strip() + str_list.append( + f"{subtitle}\n{textwrap.indent(last_n_line_str, ' ' * 4)}" + ) + return "\n".join(str_list) + + +async def check_output_cmd( + cmd: List[str], + *, + logger: logging.Logger, + cmd_index_gen: types.GeneratorType = itertools.count(1), + **kwargs, +) -> str: + """Run command with arguments and return its output. + + If the return code was non-zero it raises a CalledProcessError. The + CalledProcessError object will have the return code in the returncode + attribute and any output in the output attribute. + + Args: + cmd: The cmdline should be a sequence of program arguments or else + a single string or path-like object. The program to execute is + the first item in cmd. + logger: The logger instance. + cmd_index_gen: The cmd index generator, default is itertools.count(1). + kwargs: All arguments are passed to the create_subprocess_exec. + + Returns: + The stdout of cmd. + + Raises: + CalledProcessError: If the return code of cmd is not 0. + """ + + cmd_index = next(cmd_index_gen) + logger.info("Run cmd[%s] %s", cmd_index, repr(cmd)) + + proc = None + try: + proc = await asyncio.create_subprocess_exec( + *cmd, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.STDOUT, + **kwargs, + ) + # Use communicate instead of polling stdout: + # * Avoid deadlocks due to streams pausing reading or writing and blocking the + # child process. Please refer to: + # https://docs.python.org/3/library/asyncio-subprocess.html#asyncio.asyncio.subprocess.Process.stderr + # * Avoid mixing multiple outputs of concurrent cmds. + stdout, _ = await proc.communicate() + except asyncio.exceptions.CancelledError as e: + # since Python 3.9, when cancelled, the inner process needs to throw as it is + # for asyncio to timeout properly https://bugs.python.org/issue40607 + raise e + except BaseException as e: + raise RuntimeError(f"Run cmd[{cmd_index}] got exception.") from e + else: + stdout = stdout.decode("utf-8") + if stdout: + logger.info("Output of cmd[%s]: %s", cmd_index, stdout) + else: + logger.info("No output for cmd[%s]", cmd_index) + if proc.returncode != 0: + raise SubprocessCalledProcessError( + proc.returncode, cmd, output=stdout, cmd_index=cmd_index + ) + return stdout + finally: + if proc is not None: + # Kill process. + try: + proc.kill() + except ProcessLookupError: + pass + # Wait process exit. + await proc.wait()