-
Notifications
You must be signed in to change notification settings - Fork 6.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Runtime environment hooks to load runtime_env from uv run environment (…
…#50462) <!-- Thank you for your contribution! Please review https://github.com/ray-project/ray/blob/master/CONTRIBUTING.rst before opening a pull request. --> <!-- Please add a reviewer to the assignee section when you create a PR. If you don't have the access to it, we will shortly find a reviewer and assign them to your PR. --> ## Why are these changes needed? Next step after #50160 to make it more convenient to use UV with Ray. This is a useful runtime environment hook for mirroring the environment of `uv run` to the workers (currently the args to uv run and the working_dir). This is useful because it will allow people to intuitively use `uv run` in a distributed application with the same behavior as for a single python process. This only modifies the environment if the driver was run with `uv run` and could conceivably become the default for drivers run with uv run. This is currently a developer API as implied by the fact that it is in the `_private` namespace. It is currently for experimentation and can needs to be opted in via ```shell export RAY_RUNTIME_ENV_HOOK=ray._private.runtime_env.uv_runtime_env_hook.hook ``` If it works well, we might make it the default in the `uv run` case. ## Related issue number <!-- For example: "Closes #1234" --> ## Checks - [ ] I've signed off every commit(by using the -s flag, i.e., `git commit -s`) in this PR. - [ ] I've run `scripts/format.sh` to lint the changes in this PR. - [ ] I've included any doc changes needed for https://docs.ray.io/en/master/. - [ ] I've added any new APIs to the API Reference. For example, if I added a method in Tune, I've added it in `doc/source/tune/api/` under the corresponding `.rst` file. - [ ] I've made sure the tests are passing. Note that there might be a few flaky tests, see the recent failures at https://flakey-tests.ray.io/ - Testing Strategy - [ ] Unit tests - [ ] Release tests - [ ] This PR is not tested :( --------- Signed-off-by: Philipp Moritz <pcmoritz@gmail.com> Co-authored-by: Edward Oakes <ed.nmi.oakes@gmail.com> Co-authored-by: angelinalg <122562471+angelinalg@users.noreply.github.com>
- Loading branch information
1 parent
d2004b6
commit c694360
Showing
3 changed files
with
288 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
import argparse | ||
import os | ||
from pathlib import Path | ||
import sys | ||
from typing import Any, Dict, Optional | ||
|
||
import psutil | ||
|
||
|
||
def hook(runtime_env: Optional[Dict[str, Any]]) -> Dict[str, Any]: | ||
"""Hook that detects if the driver is run in 'uv run' and sets the runtime environment accordingly.""" | ||
|
||
runtime_env = runtime_env or {} | ||
|
||
parent = psutil.Process().parent() | ||
cmdline = parent.cmdline() | ||
if os.path.basename(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 | ||
|
||
# Extract the arguments of 'uv run' that are not arguments of the script | ||
uv_run_args = cmdline[: len(cmdline) - len(sys.argv)] | ||
|
||
# Remove the "--directory" argument since it has already been taken into | ||
# account when setting the current working directory of the current process | ||
parser = argparse.ArgumentParser() | ||
parser.add_argument("--directory", nargs="?") | ||
_, remaining_uv_run_args = parser.parse_known_args(uv_run_args) | ||
|
||
runtime_env["py_executable"] = " ".join(remaining_uv_run_args) | ||
|
||
# If the user specified a working_dir, we 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"] = os.getcwd() | ||
|
||
# In the last part of the function we do some error checking that should catch | ||
# the most common cases of how things are different in Ray, i.e. not the whole | ||
# file system will be available on the workers, only the working_dir. | ||
|
||
# First parse the arguments we need to check | ||
uv_run_parser = argparse.ArgumentParser() | ||
uv_run_parser.add_argument("--with-requirements", nargs="?") | ||
uv_run_parser.add_argument("--project", nargs="?") | ||
uv_run_parser.add_argument("--no-project", action="store_true") | ||
known_args, _ = uv_run_parser.parse_known_args(uv_run_args) | ||
|
||
working_dir = Path(runtime_env["working_dir"]).resolve() | ||
|
||
# Check if the requirements.txt file is in the working_dir | ||
if known_args.with_requirements and not Path( | ||
known_args.with_requirements | ||
).resolve().is_relative_to(working_dir): | ||
raise RuntimeError( | ||
f"You specified --with-requirements={known_args.with_requirements} but " | ||
f"the requirements file is not in the working_dir {runtime_env['working_dir']}, " | ||
"so the workers will not have access to the file. Make sure " | ||
"the requirements file is in the working directory. " | ||
"You can do so by specifying --directory in 'uv run', by changing the current " | ||
"working directory before running 'uv run', or by using the 'working_dir' " | ||
"parameter of the runtime_environment." | ||
) | ||
|
||
# Check if the pyproject.toml file is in the working_dir | ||
pyproject = None | ||
if known_args.no_project: | ||
pyproject = None | ||
elif known_args.project: | ||
pyproject = Path(known_args.project) | ||
else: | ||
# Walk up the directory tree until pyproject.toml is found | ||
current_path = Path.cwd().resolve() | ||
while current_path != current_path.parent: | ||
if (current_path / "pyproject.toml").exists(): | ||
pyproject = Path(current_path / "pyproject.toml") | ||
break | ||
current_path = current_path.parent | ||
|
||
if pyproject and not pyproject.resolve().is_relative_to(working_dir): | ||
raise RuntimeError( | ||
f"Your {pyproject.resolve()} is not in the working_dir {runtime_env['working_dir']}, " | ||
"so the workers will not have access to the file. Make sure " | ||
"the pyproject.toml file is in the working directory. " | ||
"You can do so by specifying --directory in 'uv run', by changing the current " | ||
"working directory before running 'uv run', or by using the 'working_dir' " | ||
"parameter of the runtime_environment." | ||
) | ||
|
||
return runtime_env | ||
|
||
|
||
if __name__ == "__main__": | ||
import json | ||
|
||
# This is used for unit testing if the runtime_env_hook picks up the | ||
# right settings. | ||
runtime_env = json.loads(sys.argv[1]) | ||
print(json.dumps(hook(runtime_env))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters