def build(import_path: str, app_dir: str, output_path: Optional[str]): sys.path.insert(0, app_dir) node: Union[ClassNode, FunctionNode] = import_attr(import_path) if not isinstance(node, (ClassNode, FunctionNode)): raise TypeError( f"Expected '{import_path}' to be ClassNode or " f"FunctionNode, but got {type(node)}." ) app = build_app(node) config = ServeApplicationSchema( deployments=[deployment_to_schema(d) for d in app.deployments.values()] ).dict() config["import_path"] = import_path if output_path is not None: if not output_path.endswith(".yaml"): raise ValueError("FILE_PATH must end with '.yaml'.") with open(output_path, "w") as f: yaml.safe_dump(config, stream=f, default_flow_style=False, sort_keys=False) else: print(yaml.safe_dump(config, default_flow_style=False, sort_keys=False), end="")
def to_dict(self) -> Dict: """Returns this Application's deployments as a dictionary. This dictionary adheres to the Serve REST API schema. It can be deployed via the Serve REST API. Returns: Dict: The Application's deployments formatted in a dictionary. """ return ServeApplicationSchema(deployments=[ deployment_to_schema(d) for d in self._deployments.values() ]).dict()
def test_use_deployment_import_path(): """Ensure deployment func_or_class becomes import path when schematized.""" d = schema_to_deployment(deployment_to_schema(decorated_f)) assert isinstance(d.func_or_class, str) # CI may change the parent path, so check only that the suffix matches. assert d.func_or_class.endswith("ray.serve.tests.test_schema.decorated_f") serve.start() d.deploy() assert (requests.get("http://localhost:8000/decorated_f").text == "reached decorated_f")
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 deployment = schema_to_deployment(deployment_to_schema(f)) deployment.set_options(func_or_class="ray.serve.tests.test_schema.global_f") assert len(deployment.ray_actor_options) == 0
def test_deployment_to_schema_to_deployment(): @serve.deployment( num_replicas=3, route_prefix="/hello", ray_actor_options={ "runtime_env": { "working_dir": ( "https://github.com/shrekris-anyscale/" "test_module/archive/HEAD.zip" ), "py_modules": [ ( "https://github.com/shrekris-anyscale/" "test_deploy_group/archive/HEAD.zip" ), ], } }, ) def f(): # The body of this function doesn't matter. It gets replaced by # global_f() when the import path in f._func_or_class is overwritten. # This function is used as a convenience to apply the @serve.deployment # decorator without converting global_f() into a Deployment object. pass f._func_or_class = "ray.serve.tests.test_schema.global_f" deployment = schema_to_deployment(deployment_to_schema(f)) assert deployment.num_replicas == 3 assert deployment.route_prefix == "/hello" assert deployment.ray_actor_options["runtime_env"]["working_dir"] == ( "https://github.com/shrekris-anyscale/test_module/archive/HEAD.zip" ) assert deployment.ray_actor_options["runtime_env"]["py_modules"] == [ "https://github.com/shrekris-anyscale/test_deploy_group/archive/HEAD.zip", "https://github.com/shrekris-anyscale/test_module/archive/HEAD.zip", ] serve.start() deployment.deploy() assert ray.get(deployment.get_handle().remote()) == "Hello world!" assert requests.get("http://localhost:8000/hello").text == "Hello world!" serve.shutdown()
def build(import_path: str, app_dir: str, output_path: Optional[str]): sys.path.insert(0, app_dir) node: Union[ClassNode, FunctionNode] = import_attr(import_path) if not isinstance(node, (ClassNode, FunctionNode)): raise TypeError(f"Expected '{import_path}' to be ClassNode or " f"FunctionNode, but got {type(node)}.") app = build_app(node) config = ServeApplicationSchema(deployments=[ deployment_to_schema(d) for d in app.deployments.values() ]).dict() config["import_path"] = import_path config_str = ("# This file was generated using the `serve build` command " f"on Ray v{ray.__version__}.\n\n") config_str += yaml.dump(config, Dumper=ServeBuildDumper, default_flow_style=False, sort_keys=False) with open(output_path, "w") if output_path else sys.stdout as f: f.write(config_str)