-
Notifications
You must be signed in to change notification settings - Fork 5.9k
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
[core] [1/N] Validate uv options #48479
Changes from 6 commits
97bdec9
b8f757a
dfd13b3
612aa8b
9dc3b77
60c0000
54cf250
6d4087f
d08af64
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
load("@rules_python//python:defs.bzl", "py_library") | ||
|
||
package(default_visibility = ["//visibility:public"]) | ||
|
||
py_library( | ||
name = "validation", | ||
srcs = ["validation.py"], | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
# TODO(hjiang): Move conda related unit test to this file also, after addressing the ` | ||
# yaml` third-party dependency issue. | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's follow what serve does here ( There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we take a look at the so-called unit test There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But yeah, I could make another folder for unit test only, SGTM :) |
||
from python.ray._private.runtime_env import validation | ||
|
||
import unittest | ||
import os | ||
import shutil | ||
from pathlib import Path | ||
import tempfile | ||
|
||
_PIP_LIST = ["requests==1.0.0", "pip-install-test"] | ||
|
||
|
||
class TestVaidationUv(unittest.TestCase): | ||
def test_parse_and_validate_uv(self): | ||
# Valid case w/o duplication. | ||
result = validation.parse_and_validate_uv({"packages": ["tensorflow"]}) | ||
self.assertEqual(result, {"packages": ["tensorflow"]}) | ||
|
||
# Valid case w/ duplication. | ||
result = validation.parse_and_validate_uv( | ||
{"packages": ["tensorflow", "tensorflow"]} | ||
) | ||
self.assertEqual(result, {"packages": ["tensorflow"]}) | ||
|
||
# Valid case, use `list` to represent necessary packages. | ||
result = validation.parse_and_validate_uv( | ||
["requests==1.0.0", "aiohttp", "ray[serve]"] | ||
) | ||
self.assertEqual( | ||
result, {"packages": ["requests==1.0.0", "aiohttp", "ray[serve]"]} | ||
) | ||
|
||
# Invalid case, `str` is not supported for now. | ||
with self.assertRaises(TypeError) as _: | ||
result = validation.parse_and_validate_uv("./requirements.txt") | ||
|
||
# Invalid case, unsupport keys. | ||
with self.assertRaises(ValueError) as _: | ||
result = validation.parse_and_validate_uv({"random_key": "random_value"}) | ||
|
||
|
||
class TestValidatePip(unittest.TestCase): | ||
def setUp(self): | ||
self.tmp_dir = tempfile.mkdtemp() | ||
path = Path(self.tmp_dir) | ||
self.subdir = path / "subdir" | ||
self.subdir.mkdir(parents=True) | ||
self.requirements_file = self.subdir / "requirements.txt" | ||
with self.requirements_file.open(mode="w") as f: | ||
print("\n".join(_PIP_LIST), file=f) | ||
|
||
self.old_dir = os.getcwd() | ||
os.chdir(self.tmp_dir) | ||
|
||
def tearDown(self): | ||
os.chdir(self.old_dir) | ||
shutil.rmtree(self.tmp_dir) | ||
|
||
def test_validate_pip_invalid_types(self): | ||
with self.assertRaises(TypeError) as _: | ||
validation.parse_and_validate_pip(1) | ||
|
||
with self.assertRaises(TypeError) as _: | ||
validation.parse_and_validate_pip(True) | ||
|
||
def test_validate_pip_invalid_path(self): | ||
with self.assertRaises(ValueError) as _: | ||
validation.parse_and_validate_pip("../bad_path.txt") | ||
|
||
def test_validate_pip_valid_file(self): | ||
result = validation.parse_and_validate_pip(str(self.requirements_file)) | ||
self.assertEqual(result["packages"], _PIP_LIST) | ||
self.assertFalse(result["pip_check"]) | ||
self.assertFalse("pip_version" in result) | ||
|
||
def test_validate_pip_valid_list(self): | ||
result = validation.parse_and_validate_pip(_PIP_LIST) | ||
self.assertEqual(result["packages"], _PIP_LIST) | ||
self.assertFalse(result["pip_check"]) | ||
self.assertFalse("pip_version" in result) | ||
|
||
def test_validate_ray(self): | ||
result = validation.parse_and_validate_pip(["pkg1", "ray", "pkg2"]) | ||
self.assertEqual(result["packages"], ["pkg1", "ray", "pkg2"]) | ||
self.assertFalse(result["pip_check"]) | ||
self.assertFalse("pip_version" in result) | ||
|
||
|
||
class TestValidateEnvVars(unittest.TestCase): | ||
def test_type_validation(self): | ||
with self.assertRaises(TypeError) as context: | ||
validation.parse_and_validate_env_vars({"INT_ENV": 1}) | ||
self.assertRegex(str(context.exception), ".*Dict\\[str, str\\]*") | ||
|
||
with self.assertRaises(TypeError) as context: | ||
validation.parse_and_validate_env_vars({1: "hi"}) | ||
self.assertRegex(str(context.exception), ".*Dict\\[str, str\\]*") | ||
|
||
with self.assertRaises(TypeError) as context: | ||
validation.parse_and_validate_env_vars({"hi": 123}) | ||
self.assertRegex( | ||
str(context.exception), ".*value 123 is of type <class 'int'>*" | ||
) | ||
|
||
with self.assertRaises(TypeError) as context: | ||
validation.parse_and_validate_env_vars({"hi": True}) | ||
self.assertRegex( | ||
str(context.exception), ".*value True is of type <class 'bool'>*" | ||
) | ||
|
||
with self.assertRaises(TypeError) as context: | ||
validation.parse_and_validate_env_vars({1.23: "hi"}) | ||
self.assertRegex( | ||
str(context.exception), ".*key 1.23 is of type <class 'float'>*" | ||
) | ||
|
||
|
||
if __name__ == "__main__": | ||
unittest.main() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
If we want to move away from pytest, it's a separate topic. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sadly, our current bazel setup cannot import |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what are the other 2 fields?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry I don't understand, I'm trying to mimic the documentation for
pip
.ray/python/ray/_private/runtime_env/validation.py
Lines 111 to 128 in 056d596
Could you please tell me which part am I missing here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean we should support pip_check field as well and possibly uv_version field.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
About
uv_version
, since the functionality is not implemented yet, I already left a TODO comment at the start of the function. IMO, only implemented feature should be officially documented.I will definitely document it when feature implemented. Let me know if you're fine with it.
About
pip_check
, my concern is it doesn't have 100% compatibility betweenuv
andpip
, in some cases it would report failure, check out https://github.com/astral-sh/uv/pull/2544/filesOne concrete example might be, uv's
pip_check
warns against multiple versions of a package, while pip version doesn't.So I'm hesitant whether to implement it or not; but anyway I left a TODO comment at the beginning, so we don't forget to