Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Runtime environment hooks to load runtime_env from uv run environment #50462

Merged
merged 33 commits into from
Feb 13, 2025
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
5c9038d
Builtin hook to load runtime_env from environment variable
pcmoritz Feb 12, 2025
b39bd70
lint
pcmoritz Feb 12, 2025
1155f66
update
pcmoritz Feb 12, 2025
b269c71
Include UV hook
pcmoritz Feb 12, 2025
e802452
lint
pcmoritz Feb 12, 2025
ded47a9
add test
pcmoritz Feb 12, 2025
7581bb5
lint
pcmoritz Feb 12, 2025
814ac7b
fix lint
pcmoritz Feb 12, 2025
206fef1
add tests
pcmoritz Feb 12, 2025
70f89ab
remove
pcmoritz Feb 12, 2025
8a432ff
update
pcmoritz Feb 12, 2025
b7cc9bb
clean
pcmoritz Feb 12, 2025
9af39b8
add docs
pcmoritz Feb 12, 2025
11daae2
lint
pcmoritz Feb 12, 2025
0c13c7b
update
pcmoritz Feb 12, 2025
fc329d2
fix test
pcmoritz Feb 13, 2025
03bd04c
Update doc/source/ray-core/handling-dependencies.rst
pcmoritz Feb 13, 2025
0ad1c5d
update docs
pcmoritz Feb 13, 2025
4dfe515
Update doc/source/ray-core/handling-dependencies.rst
pcmoritz Feb 13, 2025
81792fa
Update doc/source/ray-core/handling-dependencies.rst
pcmoritz Feb 13, 2025
d5a5af7
Update doc/source/ray-core/handling-dependencies.rst
pcmoritz Feb 13, 2025
3de2b9b
Update doc/source/ray-core/handling-dependencies.rst
pcmoritz Feb 13, 2025
74d6cca
Update doc/source/ray-core/handling-dependencies.rst
pcmoritz Feb 13, 2025
19505d1
Update handling-dependencies.rst
pcmoritz Feb 13, 2025
2a1c2bc
Update python/ray/_private/runtime_env/uv_runtime_env_hook.py
pcmoritz Feb 13, 2025
f086647
Update python/ray/_private/runtime_env/uv_runtime_env_hook.py
pcmoritz Feb 13, 2025
891e229
Update python/ray/_private/runtime_env/uv_runtime_env_hook.py
pcmoritz Feb 13, 2025
cec298c
Update python/ray/tests/test_runtime_env_uv_run.py
pcmoritz Feb 13, 2025
f1eceb5
Update python/ray/tests/test_runtime_env_uv_run.py
pcmoritz Feb 13, 2025
7fb5fd3
fixes
pcmoritz Feb 13, 2025
bb77558
Merge branch 'runtime-env-override-hook' of github.com:ray-project/ra…
pcmoritz Feb 13, 2025
db30a14
update
pcmoritz Feb 13, 2025
c4ee8ed
handle both '--directory value' and '--directory=value'
pcmoritz Feb 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions python/ray/_private/runtime_env/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,48 @@
import json
import os
import sys
from typing import Any, Dict


def runtime_env_override_hook(runtime_env: Dict[str, Any]) -> Dict[str, Any]:
"""Hook to override the runtime environment from RAY_OVERRIDE_RUNTIME_ENV_JSON."""

if "RAY_OVERRIDE_RUNTIME_ENV_JSON" in os.environ:
runtime_env = json.loads(os.environ["RAY_OVERRIDE_RUNTIME_ENV_JSON"])

return runtime_env


def uv_run_runtime_env_hook(runtime_env: Dict[str, Any]) -> Dict[str, Any]:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

now where do we inject this hook? in snapshot_util?

tangent question: How do we deal with uv run on jobs?

Copy link
Contributor Author

@pcmoritz pcmoritz Feb 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the jobs: It is actually very simple, you just submit the job with uv run <script> as the entry point :)

On the hook: Right now you need to define it manually but I think going forward we should just remove the hook and do this by default once it is ironed out a little more and works well :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so we still need to define the env variable RUNTIME_ENV_HOOK (I forget its name)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's right (for now) :)

"""Hook that detects if the driver is run in 'uv run' and sets the runtime environment accordingly."""

import argparse
import psutil

parent = psutil.Process().parent()
cmdline = parent.cmdline()
if cmdline[0] != "uv" or cmdline[1] != "run":
# This means the driver was not run with 'uv run' -- in this case
# we leave the runtime environment unchanged
return runtime_env

# Parse known arguments of uv run that impact the runtime environment
uv_run_parser = argparse.ArgumentParser()
uv_run_parser.add_argument("--directory", nargs="?")
known_args, unknown_args = uv_run_parser.parse_known_args(cmdline)

# Extract the arguments of 'uv run' that are not arguments of the script
uv_run_args = cmdline[: len(cmdline) - len(sys.argv)]
runtime_env["py_executable"] = " ".join(uv_run_args)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how are you differntiating between uv run args vs. script args? Is this something uv is handling under the hood by not modifying sys.argv?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the sys.argv are only the script args (they get passed to the script, which is a subprocess of uv run, by uv run), and cmdline are the full args with both the uv run args and the script args. So uv run args are computed in the line cmdline[: len(cmdline) - len(sys.argv)] above by removing the script args (in sys.argv) from all the args (cmdline)


# If the user specified a working_dir, we will always honor it, otherwise
# use the same working_dir that uv run would use
if "working_dir" not in runtime_env:
runtime_env["working_dir"] = known_args.directory or os.getcwd()

return runtime_env


# List of files to exclude from the Ray directory when using runtime_env for
# Ray development. These are not necessary in the Ray workers.
RAY_WORKER_DEV_EXCLUDES = ["raylet", "gcs_server", "cpp/", "tests/", "core/src"]
25 changes: 25 additions & 0 deletions python/ray/tests/test_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,31 @@ def f():
assert "HOOK_VALUE" in out_str


@pytest.mark.skipif(sys.platform == "win32", reason="Failing on Windows.")
def test_runtime_env_override_hook():
script = """
import ray
import os

@ray.remote
def f():
return os.environ.get("HOOK_KEY")

print(ray.get(f.remote()))
"""

proc = run_string_as_driver_nonblocking(
script,
env={
"RAY_OVERRIDE_RUNTIME_ENV_JSON": '{"env_vars": {"HOOK_KEY": "HOOK_VALUE"}}',
"RAY_RUNTIME_ENV_HOOK": "ray._private.runtime_env.runtime_env_override_hook",
},
)
out_str = proc.stdout.read().decode("ascii") + proc.stderr.read().decode("ascii")
print(out_str)
assert "HOOK_VALUE" in out_str


def test_env_hook_skipped_for_ray_client(start_cluster, monkeypatch):
monkeypatch.setenv("RAY_RUNTIME_ENV_HOOK", "ray.tests.test_output._hook")
cluster, address = start_cluster
Expand Down