diff --git a/python/ray/serve/schema.py b/python/ray/serve/schema.py index 80ed4e9a3..cf1b34f59 100644 --- a/python/ray/serve/schema.py +++ b/python/ray/serve/schema.py @@ -8,7 +8,7 @@ from ray.serve.utils import DEFAULT class RayActorOptionsSchema(BaseModel, extra=Extra.forbid): runtime_env: dict = Field( - default=None, + default={}, description=( "This deployment's runtime_env. working_dir and " "py_modules may contain only remote URIs." @@ -48,7 +48,7 @@ class RayActorOptionsSchema(BaseModel, extra=Extra.forbid): ge=0, ) resources: Dict = Field( - default=None, description=("The custom resources required by each replica.") + default={}, description=("The custom resources required by each replica.") ) accelerator_type: str = Field( default=None, @@ -341,7 +341,7 @@ def schema_to_deployment(s: DeploymentSchema) -> Deployment: if s.ray_actor_options is None: ray_actor_options = None else: - ray_actor_options = s.ray_actor_options.dict() + ray_actor_options = s.ray_actor_options.dict(exclude_unset=True) return deployment( name=s.name, diff --git a/python/ray/serve/tests/test_schema.py b/python/ray/serve/tests/test_schema.py index 28d99183f..37cc7b9d4 100644 --- a/python/ray/serve/tests/test_schema.py +++ b/python/ray/serve/tests/test_schema.py @@ -61,7 +61,7 @@ class TestRayActorOptionsSchema: # Test different runtime_env configurations ray_actor_options_schema = { - "runtime_env": None, + "runtime_env": {}, "num_cpus": 0.2, "num_gpus": 50, "memory": 3, @@ -107,12 +107,12 @@ class TestRayActorOptionsSchema: # Undefined fields should be forbidden in the schema ray_actor_options_schema = { - "runtime_env": None, + "runtime_env": {}, "num_cpus": None, "num_gpus": None, "memory": None, "object_store_memory": None, - "resources": None, + "resources": {}, "accelerator_type": None, } @@ -124,6 +124,15 @@ class TestRayActorOptionsSchema: with pytest.raises(ValidationError): RayActorOptionsSchema.parse_obj(ray_actor_options_schema) + def test_dict_defaults_ray_actor_options(self): + # Dictionary fields should have empty dictionaries as defaults, not None + + ray_actor_options_schema = {} + schema = RayActorOptionsSchema.parse_obj(ray_actor_options_schema) + d = schema.dict() + assert d["runtime_env"] == {} + assert d["resources"] == {} + class TestDeploymentSchema: def get_minimal_deployment_schema(self): @@ -144,12 +153,12 @@ class TestDeploymentSchema: "health_check_period_s": None, "health_check_timeout_s": None, "ray_actor_options": { - "runtime_env": None, + "runtime_env": {}, "num_cpus": None, "num_gpus": None, "memory": None, "object_store_memory": None, - "resources": None, + "resources": {}, "accelerator_type": None, }, } @@ -372,12 +381,12 @@ class TestServeApplicationSchema: "health_check_period_s": None, "health_check_timeout_s": None, "ray_actor_options": { - "runtime_env": None, + "runtime_env": {}, "num_cpus": None, "num_gpus": None, "memory": None, "object_store_memory": None, - "resources": None, + "resources": {}, "accelerator_type": None, }, }, @@ -543,6 +552,24 @@ def test_deployment_to_schema_to_deployment(): serve.shutdown() +def test_unset_fields_schema_to_deployment_ray_actor_options(): + # Ensure unset fields are excluded from ray_actor_options + + @serve.deployment( + num_replicas=3, + route_prefix="/hello", + ray_actor_options={}, + ) + def f(): + pass + + f._func_or_class = "ray.serve.tests.test_schema.global_f" + + deployment = schema_to_deployment(deployment_to_schema(f)) + + assert len(deployment.ray_actor_options) == 0 + + def test_serve_application_to_schema_to_serve_application(): @serve.deployment( num_replicas=1,