chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,540 @@
|
||||
import dataclasses
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, Dict
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
|
||||
import ray
|
||||
import ray._private.ray_constants as ray_constants
|
||||
from ray._private.runtime_env.uri_cache import URICache
|
||||
from ray._private.runtime_env.utils import (
|
||||
SubprocessCalledProcessError,
|
||||
check_output_cmd,
|
||||
)
|
||||
from ray._private.test_utils import (
|
||||
chdir,
|
||||
)
|
||||
from ray.runtime_env import RuntimeEnv
|
||||
from ray.runtime_env.runtime_env import (
|
||||
RuntimeEnvConfig,
|
||||
_merge_runtime_env,
|
||||
)
|
||||
|
||||
|
||||
def test_runtime_env_merge():
|
||||
# Both are None.
|
||||
parent = None
|
||||
child = None
|
||||
assert _merge_runtime_env(parent, child) == {}
|
||||
|
||||
parent = {}
|
||||
child = None
|
||||
assert _merge_runtime_env(parent, child) == {}
|
||||
|
||||
parent = None
|
||||
child = {}
|
||||
assert _merge_runtime_env(parent, child) == {}
|
||||
|
||||
parent = {}
|
||||
child = {}
|
||||
assert _merge_runtime_env(parent, child) == {}
|
||||
|
||||
# Only parent is given.
|
||||
parent = {"conda": ["requests"], "env_vars": {"A": "1"}}
|
||||
child = None
|
||||
assert _merge_runtime_env(parent, child) == parent
|
||||
|
||||
# Only child is given.
|
||||
parent = None
|
||||
child = {"conda": ["requests"], "env_vars": {"A": "1"}}
|
||||
assert _merge_runtime_env(parent, child) == child
|
||||
|
||||
# Successful case.
|
||||
parent = {"conda": ["requests"], "env_vars": {"A": "1"}}
|
||||
child = {"pip": ["requests"], "env_vars": {"B": "2"}}
|
||||
assert _merge_runtime_env(parent, child) == {
|
||||
"conda": ["requests"],
|
||||
"pip": ["requests"],
|
||||
"env_vars": {"A": "1", "B": "2"},
|
||||
}
|
||||
|
||||
# Failure case
|
||||
parent = {"pip": ["requests"], "env_vars": {"A": "1"}}
|
||||
child = {"pip": ["colors"], "env_vars": {"B": "2"}}
|
||||
assert _merge_runtime_env(parent, child) is None
|
||||
|
||||
# Failure case (env_vars)
|
||||
parent = {"pip": ["requests"], "env_vars": {"A": "1"}}
|
||||
child = {"conda": ["requests"], "env_vars": {"A": "2"}}
|
||||
assert _merge_runtime_env(parent, child) is None
|
||||
|
||||
# override = True
|
||||
parent = {"pip": ["requests"], "env_vars": {"A": "1"}}
|
||||
child = {"pip": ["colors"], "env_vars": {"B": "2"}}
|
||||
assert _merge_runtime_env(parent, child, override=True) == {
|
||||
"pip": ["colors"],
|
||||
"env_vars": {"A": "1", "B": "2"},
|
||||
}
|
||||
|
||||
# override = True + env vars
|
||||
parent = {"pip": ["requests"], "env_vars": {"A": "1"}}
|
||||
child = {"pip": ["colors"], "conda": ["requests"], "env_vars": {"A": "2"}}
|
||||
assert _merge_runtime_env(parent, child, override=True) == {
|
||||
"pip": ["colors"],
|
||||
"env_vars": {"A": "2"},
|
||||
"conda": ["requests"],
|
||||
}
|
||||
|
||||
|
||||
def test_current_py_version_supported():
|
||||
"""Test that the running python version is supported.
|
||||
|
||||
This is run as a check in the Ray `runtime_env` `conda` code
|
||||
before downloading the Ray wheel into the conda environment.
|
||||
If Ray wheels are not available for this python version, then
|
||||
the `conda` environment installation will fail.
|
||||
|
||||
When a new python version is added to the Ray wheels, please update
|
||||
`ray_constants.RUNTIME_ENV_CONDA_PY_VERSIONS`. In a subsequent commit,
|
||||
once wheels have been built for the new python version, please update
|
||||
the tests test_get_wheel_filename, test_get_master_wheel_url, and
|
||||
(after the first Ray release with the new python version)
|
||||
test_get_release_wheel_url.
|
||||
"""
|
||||
py_version = sys.version_info[:2]
|
||||
assert py_version in ray_constants.RUNTIME_ENV_CONDA_PY_VERSIONS
|
||||
|
||||
|
||||
def test_compatible_with_dataclasses():
|
||||
"""Test that the output of RuntimeEnv.to_dict() can be used as a dataclass field."""
|
||||
config = RuntimeEnvConfig(setup_timeout_seconds=1)
|
||||
runtime_env = RuntimeEnv(
|
||||
pip={
|
||||
"packages": ["tensorflow", "requests"],
|
||||
"pip_check": False,
|
||||
"pip_version": "==23.3.2;python_version=='3.9.16'",
|
||||
},
|
||||
env_vars={"FOO": "BAR"},
|
||||
config=config,
|
||||
)
|
||||
|
||||
@dataclass
|
||||
class RuntimeEnvDataClass:
|
||||
runtime_env: Dict[str, Any]
|
||||
|
||||
dataclasses.asdict(RuntimeEnvDataClass(runtime_env.to_dict()))
|
||||
|
||||
@dataclass
|
||||
class RuntimeEnvConfigDataClass:
|
||||
config: Dict[str, Any]
|
||||
|
||||
dataclasses.asdict(RuntimeEnvConfigDataClass(config.to_dict()))
|
||||
|
||||
|
||||
@pytest.mark.parametrize("runtime_env_class", [dict, RuntimeEnv])
|
||||
def test_container_option_serialize(runtime_env_class):
|
||||
runtime_env = runtime_env_class(
|
||||
container={"image": "ray:latest", "run_options": ["--name=test"]}
|
||||
)
|
||||
job_config = ray.job_config.JobConfig(runtime_env=runtime_env)
|
||||
job_config_serialized = job_config._serialize()
|
||||
# job_config_serialized is JobConfig protobuf serialized string,
|
||||
# job_config.runtime_env_info.serialized_runtime_env
|
||||
# has container_option info
|
||||
assert job_config_serialized.count(b"ray:latest") == 1
|
||||
assert job_config_serialized.count(b"--name=test") == 1
|
||||
|
||||
|
||||
class TestURICache:
|
||||
def test_zero_cache_size(self):
|
||||
uris_to_sizes = {"5": 5, "3": 3}
|
||||
|
||||
def delete_fn(uri, logger):
|
||||
return uris_to_sizes[uri]
|
||||
|
||||
cache = URICache(delete_fn, max_total_size_bytes=0, debug_mode=True)
|
||||
cache.add("5", 5)
|
||||
assert cache.get_total_size_bytes() == 5
|
||||
cache.mark_unused("5")
|
||||
assert cache.get_total_size_bytes() == 0
|
||||
cache.add("3", 3)
|
||||
cache.add("5", 5)
|
||||
assert cache.get_total_size_bytes() == 8
|
||||
cache.mark_unused("3")
|
||||
cache.mark_unused("5")
|
||||
assert cache.get_total_size_bytes() == 0
|
||||
|
||||
def test_nonzero_cache_size(self):
|
||||
uris_to_sizes = {"a": 4, "b": 4, "c": 4}
|
||||
|
||||
def delete_fn(uri, logger):
|
||||
return uris_to_sizes[uri]
|
||||
|
||||
cache = URICache(delete_fn, max_total_size_bytes=10, debug_mode=True)
|
||||
cache.add("a", 4)
|
||||
cache.add("b", 4)
|
||||
cache.mark_unused("a")
|
||||
assert "a" in cache
|
||||
cache.add("c", 4)
|
||||
# Now we have total size 12, which exceeds the max size 10.
|
||||
assert cache.get_total_size_bytes() == 8
|
||||
# "a" was the only unused URI, so it must have been deleted.
|
||||
assert "b" and "c" in cache and "a" not in cache
|
||||
|
||||
def test_mark_used_nonadded_uri_error(self):
|
||||
cache = URICache(debug_mode=True)
|
||||
with pytest.raises(ValueError):
|
||||
cache.mark_used("nonadded_uri")
|
||||
|
||||
def test_mark_used(self):
|
||||
uris_to_sizes = {"a": 3, "b": 3, "big": 300}
|
||||
|
||||
def delete_fn(uri, logger):
|
||||
return uris_to_sizes[uri]
|
||||
|
||||
cache = URICache(delete_fn, max_total_size_bytes=10, debug_mode=True)
|
||||
cache.add("a", 3)
|
||||
cache.add("b", 3)
|
||||
cache.mark_unused("a")
|
||||
cache.mark_unused("b")
|
||||
assert "a" in cache and "b" in cache
|
||||
assert cache.get_total_size_bytes() == 6
|
||||
|
||||
cache.mark_used("a")
|
||||
cache.add("big", 300)
|
||||
# We are over capacity and the only unused URI is "b", so we delete it
|
||||
assert "a" in cache and "big" in cache and "b" not in cache
|
||||
assert cache.get_total_size_bytes() == 303
|
||||
|
||||
cache.mark_unused("big")
|
||||
assert "big" not in cache
|
||||
assert cache.get_total_size_bytes() == 3
|
||||
|
||||
def test_many_URIs(self):
|
||||
uris_to_sizes = {str(i): i for i in range(1000)}
|
||||
|
||||
def delete_fn(uri, logger):
|
||||
return uris_to_sizes[uri]
|
||||
|
||||
cache = URICache(delete_fn, debug_mode=True)
|
||||
for i in range(1000):
|
||||
cache.add(str(i), i)
|
||||
for i in range(1000):
|
||||
cache.mark_unused(str(i))
|
||||
for i in range(1000):
|
||||
assert str(i) in cache
|
||||
|
||||
def test_delete_fn_called(self):
|
||||
num_delete_fn_calls = 0
|
||||
uris_to_sizes = {"a": 8, "b": 6, "c": 4, "d": 20}
|
||||
|
||||
def delete_fn(uri, logger):
|
||||
nonlocal num_delete_fn_calls
|
||||
num_delete_fn_calls += 1
|
||||
return uris_to_sizes[uri]
|
||||
|
||||
cache = URICache(delete_fn, max_total_size_bytes=10, debug_mode=True)
|
||||
cache.add("a", 8)
|
||||
cache.add("b", 6)
|
||||
cache.mark_unused("b")
|
||||
# Total size is 14 > 10, so we need to delete "b".
|
||||
assert num_delete_fn_calls == 1
|
||||
|
||||
cache.add("c", 4)
|
||||
cache.mark_unused("c")
|
||||
# Total size is 12 > 10, so we delete "c".
|
||||
assert num_delete_fn_calls == 2
|
||||
|
||||
cache.mark_unused("a")
|
||||
# Total size is 8 <= 10, so we shouldn't delete anything.
|
||||
assert num_delete_fn_calls == 2
|
||||
|
||||
cache.add("d", 20)
|
||||
# Total size is 28 > 10, so we delete "a".
|
||||
assert num_delete_fn_calls == 3
|
||||
|
||||
cache.mark_unused("d")
|
||||
# Total size is 20 > 10, so we delete "d".
|
||||
assert num_delete_fn_calls == 4
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def enable_dev_mode(local_env_var_enabled, monkeypatch):
|
||||
enabled = "1" if local_env_var_enabled else "0"
|
||||
monkeypatch.setenv("RAY_RUNTIME_ENV_LOG_TO_DRIVER_ENABLED", enabled)
|
||||
yield
|
||||
|
||||
|
||||
def test_subprocess_error():
|
||||
ex = SubprocessCalledProcessError
|
||||
with pytest.raises(subprocess.SubprocessError) as e:
|
||||
raise ex(123, "abc")
|
||||
assert "test_out" not in str(e.value)
|
||||
assert "test_err" not in str(e.value)
|
||||
with pytest.raises(subprocess.SubprocessError) as e:
|
||||
raise ex(123, "abc", stderr="test_err")
|
||||
assert "test_out" not in str(e.value)
|
||||
assert "test_err" in str(e.value)
|
||||
with pytest.raises(subprocess.SubprocessError) as e:
|
||||
raise ex(123, "abc", output="test_out")
|
||||
assert "test_out" in str(e.value)
|
||||
assert "test_err" not in str(e.value)
|
||||
with pytest.raises(subprocess.SubprocessError) as e:
|
||||
raise ex(123, "abc", output="test_out", stderr="test_err")
|
||||
assert "test_out" in str(e.value)
|
||||
assert "test_err" in str(e.value)
|
||||
|
||||
|
||||
def test_subprocess_error_with_last_n_lines():
|
||||
stdout = "1\n2\n3\n4\n5\n"
|
||||
stderr = "5\n4\n3\n2\n1\n"
|
||||
exception = SubprocessCalledProcessError(888, "abc", output=stdout, stderr=stderr)
|
||||
exception.LAST_N_LINES = 3
|
||||
exception_str = str(exception)
|
||||
assert "cmd" not in exception_str
|
||||
assert "Last 3 lines" in exception_str
|
||||
s = "".join([s.strip() for s in exception_str.splitlines()])
|
||||
assert "345" in s
|
||||
assert "321" in s
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_check_output_cmd():
|
||||
cmd = "dir" if sys.platform.startswith("win") else "pwd"
|
||||
logs = []
|
||||
|
||||
class _FakeLogger:
|
||||
def __getattr__(self, item):
|
||||
def _log(formatter, *args):
|
||||
logs.append(formatter % args)
|
||||
|
||||
return _log
|
||||
|
||||
for _ in range(2):
|
||||
output = await check_output_cmd([cmd], logger=_FakeLogger())
|
||||
assert len(output) > 0
|
||||
|
||||
all_log_string = "\n".join(logs)
|
||||
|
||||
# Check the cmd index generator works.
|
||||
assert "cmd[1]" in all_log_string
|
||||
assert "cmd[2]" in all_log_string
|
||||
|
||||
# Test communicate fails.
|
||||
with mock.patch(
|
||||
"asyncio.subprocess.Process.communicate",
|
||||
side_effect=Exception("fake exception"),
|
||||
):
|
||||
with pytest.raises(RuntimeError) as e:
|
||||
await check_output_cmd([cmd], logger=_FakeLogger())
|
||||
# Make sure the exception has cmd trace info.
|
||||
assert "cmd[3]" in str(e.value)
|
||||
|
||||
# Test asyncio.create_subprocess_exec fails.
|
||||
with pytest.raises(RuntimeError) as e:
|
||||
await check_output_cmd(["not_exist_cmd"], logger=_FakeLogger())
|
||||
# Make sure the exception has cmd trace info.
|
||||
assert "cmd[4]" in str(e.value)
|
||||
|
||||
# Test returncode != 0.
|
||||
with pytest.raises(SubprocessCalledProcessError) as e:
|
||||
await check_output_cmd([cmd, "--abc"], logger=_FakeLogger())
|
||||
# Make sure the exception has cmd trace info.
|
||||
assert "cmd[5]" in str(e.value)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"option",
|
||||
["pip_list", "pip_dict", "conda_name", "conda_dict", "container"],
|
||||
)
|
||||
def test_serialize_deserialize(option):
|
||||
runtime_env = dict()
|
||||
if option == "pip_list":
|
||||
runtime_env["pip"] = ["pkg1", "pkg2"]
|
||||
elif option == "pip_dict":
|
||||
runtime_env["pip"] = {
|
||||
"packages": ["pkg1", "pkg2"],
|
||||
"pip_check": False,
|
||||
"pip_version": "<22,>20",
|
||||
}
|
||||
elif option == "conda_name":
|
||||
runtime_env["conda"] = "env_name"
|
||||
elif option == "conda_dict":
|
||||
runtime_env["conda"] = {"dependencies": ["dep1", "dep2"]}
|
||||
elif option == "container":
|
||||
runtime_env["container"] = {
|
||||
"image": "anyscale/ray-ml:nightly-py38-cpu",
|
||||
"worker_path": "/root/python/ray/_private/workers/default_worker.py",
|
||||
"run_options": ["--cap-drop SYS_ADMIN", "--log-level=debug"],
|
||||
}
|
||||
else:
|
||||
raise ValueError("unexpected option " + str(option))
|
||||
|
||||
typed_runtime_env = RuntimeEnv(**runtime_env)
|
||||
serialized_runtime_env = typed_runtime_env.serialize()
|
||||
cls_runtime_env = RuntimeEnv.deserialize(serialized_runtime_env)
|
||||
cls_runtime_env_dict = cls_runtime_env.to_dict()
|
||||
|
||||
if "pip" in typed_runtime_env and isinstance(typed_runtime_env["pip"], list):
|
||||
pip_config_in_cls_runtime_env = cls_runtime_env_dict.pop("pip")
|
||||
pip_config_in_runtime_env = typed_runtime_env.pop("pip")
|
||||
assert {
|
||||
"packages": pip_config_in_runtime_env,
|
||||
"pip_check": False,
|
||||
} == pip_config_in_cls_runtime_env
|
||||
|
||||
assert cls_runtime_env_dict == typed_runtime_env
|
||||
|
||||
|
||||
def test_runtime_env_interface():
|
||||
# Test the interface related to working_dir
|
||||
default_working_dir = "s3://bucket/key.zip"
|
||||
modify_working_dir = "s3://bucket/key_A.zip"
|
||||
runtime_env = RuntimeEnv(working_dir=default_working_dir)
|
||||
runtime_env_dict = runtime_env.to_dict()
|
||||
assert runtime_env.working_dir_uri() == default_working_dir
|
||||
runtime_env["working_dir"] = modify_working_dir
|
||||
runtime_env_dict["working_dir"] = modify_working_dir
|
||||
assert runtime_env.working_dir_uri() == modify_working_dir
|
||||
assert runtime_env.to_dict() == runtime_env_dict
|
||||
|
||||
runtime_env.pop("working_dir")
|
||||
assert runtime_env.to_dict() == {}
|
||||
|
||||
# Test the interface related to py_modules
|
||||
init_py_modules = ["s3://bucket/key_1.zip", "s3://bucket/key_2.zip"]
|
||||
addition_py_modules = ["s3://bucket/key_3.zip", "s3://bucket/key_4.zip"]
|
||||
runtime_env = RuntimeEnv(py_modules=init_py_modules)
|
||||
runtime_env_dict = runtime_env.to_dict()
|
||||
assert set(runtime_env.py_modules_uris()) == set(init_py_modules)
|
||||
runtime_env["py_modules"].extend(addition_py_modules)
|
||||
runtime_env_dict["py_modules"].extend(addition_py_modules)
|
||||
assert set(runtime_env.py_modules_uris()) == set(
|
||||
init_py_modules + addition_py_modules
|
||||
)
|
||||
assert runtime_env.to_dict() == runtime_env_dict
|
||||
|
||||
runtime_env.pop("py_modules")
|
||||
assert runtime_env.to_dict() == {}
|
||||
|
||||
# Test the interface related to env_vars
|
||||
init_env_vars = {"A": "a", "B": "b"}
|
||||
update_env_vars = {"C": "c"}
|
||||
runtime_env = RuntimeEnv(env_vars=init_env_vars)
|
||||
runtime_env_dict = runtime_env.to_dict()
|
||||
runtime_env["env_vars"].update(update_env_vars)
|
||||
runtime_env_dict["env_vars"].update(update_env_vars)
|
||||
init_env_vars_copy = init_env_vars.copy()
|
||||
init_env_vars_copy.update(update_env_vars)
|
||||
assert runtime_env["env_vars"] == init_env_vars_copy
|
||||
assert runtime_env_dict == runtime_env.to_dict()
|
||||
|
||||
runtime_env.pop("env_vars")
|
||||
assert runtime_env.to_dict() == {}
|
||||
|
||||
# Test the interface related to conda
|
||||
conda_name = "conda"
|
||||
modify_conda_name = "conda_A"
|
||||
conda_config = {"dependencies": ["dep1", "dep2"]}
|
||||
runtime_env = RuntimeEnv(conda=conda_name)
|
||||
runtime_env_dict = runtime_env.to_dict()
|
||||
assert runtime_env.has_conda()
|
||||
assert runtime_env.conda_env_name() == conda_name
|
||||
assert runtime_env.conda_config() is None
|
||||
runtime_env["conda"] = modify_conda_name
|
||||
runtime_env_dict["conda"] = modify_conda_name
|
||||
assert runtime_env_dict == runtime_env.to_dict()
|
||||
assert runtime_env.has_conda()
|
||||
assert runtime_env.conda_env_name() == modify_conda_name
|
||||
assert runtime_env.conda_config() is None
|
||||
runtime_env["conda"] = conda_config
|
||||
runtime_env_dict["conda"] = conda_config
|
||||
assert runtime_env_dict == runtime_env.to_dict()
|
||||
assert runtime_env.has_conda()
|
||||
assert runtime_env.conda_env_name() is None
|
||||
assert runtime_env.conda_config() == json.dumps(conda_config, sort_keys=True)
|
||||
|
||||
runtime_env.pop("conda")
|
||||
assert runtime_env.to_dict() == {"_ray_commit": "{{RAY_COMMIT_SHA}}"}
|
||||
|
||||
# Test the interface related to pip
|
||||
with tempfile.TemporaryDirectory() as tmpdir, chdir(tmpdir):
|
||||
requirement_file = os.path.join(tmpdir, "requirements.txt")
|
||||
requirement_packages = ["dep5", "dep6"]
|
||||
with open(requirement_file, "wt") as f:
|
||||
for package in requirement_packages:
|
||||
f.write(package)
|
||||
f.write("\n")
|
||||
|
||||
pip_packages = ["dep1", "dep2"]
|
||||
addition_pip_packages = ["dep3", "dep4"]
|
||||
runtime_env = RuntimeEnv(pip=pip_packages)
|
||||
runtime_env_dict = runtime_env.to_dict()
|
||||
assert runtime_env.has_pip()
|
||||
assert set(runtime_env.pip_config()["packages"]) == set(pip_packages)
|
||||
assert runtime_env.virtualenv_name() is None
|
||||
runtime_env["pip"]["packages"].extend(addition_pip_packages)
|
||||
runtime_env_dict["pip"]["packages"].extend(addition_pip_packages)
|
||||
# The default value of pip_check is False
|
||||
runtime_env_dict["pip"]["pip_check"] = False
|
||||
assert runtime_env_dict == runtime_env.to_dict()
|
||||
assert runtime_env.has_pip()
|
||||
assert set(runtime_env.pip_config()["packages"]) == set(
|
||||
pip_packages + addition_pip_packages
|
||||
)
|
||||
assert runtime_env.virtualenv_name() is None
|
||||
runtime_env["pip"] = requirement_file
|
||||
runtime_env_dict["pip"] = requirement_packages
|
||||
assert runtime_env.has_pip()
|
||||
assert set(runtime_env.pip_config()["packages"]) == set(requirement_packages)
|
||||
assert runtime_env.virtualenv_name() is None
|
||||
# The default value of pip_check is False
|
||||
runtime_env_dict["pip"] = dict(
|
||||
packages=runtime_env_dict["pip"], pip_check=False
|
||||
)
|
||||
assert runtime_env_dict == runtime_env.to_dict()
|
||||
|
||||
runtime_env.pop("pip")
|
||||
assert runtime_env.to_dict() == {"_ray_commit": "{{RAY_COMMIT_SHA}}"}
|
||||
|
||||
# Test conflict
|
||||
with pytest.raises(ValueError):
|
||||
RuntimeEnv(pip=pip_packages, conda=conda_name)
|
||||
|
||||
runtime_env = RuntimeEnv(pip=pip_packages)
|
||||
runtime_env["conda"] = conda_name
|
||||
with pytest.raises(ValueError):
|
||||
runtime_env.serialize()
|
||||
|
||||
# Test the interface related to container
|
||||
container_init = {
|
||||
"image": "anyscale/ray-ml:nightly-py38-cpu",
|
||||
"run_options": ["--cap-drop SYS_ADMIN", "--log-level=debug"],
|
||||
}
|
||||
update_container = {"image": "test_modify"}
|
||||
runtime_env = RuntimeEnv(container=container_init)
|
||||
runtime_env_dict = runtime_env.to_dict()
|
||||
assert runtime_env.has_py_container()
|
||||
assert runtime_env.py_container_image() == container_init["image"]
|
||||
assert runtime_env.py_container_run_options() == container_init["run_options"]
|
||||
runtime_env["container"].update(update_container)
|
||||
runtime_env_dict["container"].update(update_container)
|
||||
container_copy = container_init
|
||||
container_copy.update(update_container)
|
||||
assert runtime_env_dict == runtime_env.to_dict()
|
||||
assert runtime_env.has_py_container()
|
||||
assert runtime_env.py_container_image() == container_copy["image"]
|
||||
assert runtime_env.py_container_run_options() == container_copy["run_options"]
|
||||
|
||||
runtime_env.pop("container")
|
||||
assert runtime_env.to_dict() == {}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(pytest.main(["-sv", __file__]))
|
||||
Reference in New Issue
Block a user