class ClientBuilder: """ Builder for a Ray Client connection. """ def __init__(self, address: Optional[str]) -> None: self.address = address self._job_config = JobConfig() def env(self, env: Dict[str, Any]) -> "ClientBuilder": """ Set an environment for the session. """ self._job_config.set_runtime_env(env) return self def namespace(self, namespace: str) -> "ClientBuilder": self._job_config.set_ray_namespace(namespace) return self def connect(self) -> ClientInfo: """ Begin a connection to the address passed in via ray.client(...). """ client_info_dict = ray.util.client_connect.connect( self.address, job_config=self._job_config) dashboard_url = ray.get( ray.remote(ray.worker.get_dashboard_url).remote()) return ClientInfo( dashboard_url=dashboard_url, python_version=client_info_dict["python_version"], ray_version=client_info_dict["ray_version"], ray_commit=client_info_dict["ray_commit"], protocol_version=client_info_dict["protocol_version"])
def connect(self, conn_str: str, job_config: JobConfig = None, secure: bool = False, metadata: List[Tuple[str, str]] = None, connection_retries: int = 3, namespace: str = None, *, ignore_version: bool = False) -> Dict[str, Any]: """Connect the Ray Client to a server. Args: conn_str: Connection string, in the form "[host]:port" job_config: The job config of the server. secure: Whether to use a TLS secured gRPC channel metadata: gRPC metadata to send on connect connection_retries: number of connection attempts to make ignore_version: whether to ignore Python or Ray version mismatches. This should only be used for debugging purposes. Returns: Dictionary of connection info, e.g., {"num_clients": 1}. """ # Delay imports until connect to avoid circular imports. from ray.util.client.worker import Worker import ray._private.client_mode_hook if self.client_worker is not None: if self._connected_with_init: return raise Exception( "ray.connect() called, but ray client is already connected") if not self._inside_client_test: # If we're calling a client connect specifically and we're not # currently in client mode, ensure we are. ray._private.client_mode_hook._explicitly_enable_client_mode() if namespace is not None: job_config = job_config or JobConfig() job_config.set_ray_namespace(namespace) if job_config is not None: runtime_env = json.loads(job_config.get_serialized_runtime_env()) if runtime_env.get("pip") or runtime_env.get("conda"): logger.warning("The 'pip' or 'conda' field was specified in " "the runtime env, so it may take some time to " "install the environment before ray.connect() " "returns.") try: self.client_worker = Worker(conn_str, secure=secure, metadata=metadata, connection_retries=connection_retries) self.api.worker = self.client_worker self.client_worker._server_init(job_config) conn_info = self.client_worker.connection_info() self._check_versions(conn_info, ignore_version) self._register_serializers() return conn_info except Exception: self.disconnect() raise
class ClientBuilder: """ Builder for a Ray Client connection. This class can be subclassed by custom builder classes to modify connection behavior to include additional features or altered semantics. One example is the ``_LocalClientBuilder``. """ def __init__(self, address: Optional[str]) -> None: self.address = address self._job_config = JobConfig() def env(self, env: Dict[str, Any]) -> "ClientBuilder": """ Set an environment for the session. Args: env (Dict[st, Any]): A runtime environment to use for this connection. See ``runtime_env.py`` for what values are accepted in this dict. """ self._job_config.set_runtime_env(env) return self def namespace(self, namespace: str) -> "ClientBuilder": """ Sets the namespace for the session. Args: namespace (str): Namespace to use. """ self._job_config.set_ray_namespace(namespace) return self def connect(self) -> ClientContext: """ Begin a connection to the address passed in via ray.client(...). Returns: ClientInfo: Dataclass with information about the setting. This includes the server's version of Python & Ray as well as the dashboard_url. """ client_info_dict = ray.util.client_connect.connect( self.address, job_config=self._job_config) dashboard_url = ray.get( ray.remote(ray.worker.get_dashboard_url).remote()) return ClientContext( dashboard_url=dashboard_url, python_version=client_info_dict["python_version"], ray_version=client_info_dict["ray_version"], ray_commit=client_info_dict["ray_commit"], protocol_version=client_info_dict["protocol_version"], _num_clients=client_info_dict["num_clients"])
def connect( self, conn_str: str, job_config: JobConfig = None, secure: bool = False, metadata: List[Tuple[str, str]] = None, connection_retries: int = 3, namespace: str = None, *, ignore_version: bool = False, _credentials: Optional[grpc.ChannelCredentials] = None, ray_init_kwargs: Optional[Dict[str, Any]] = None, ) -> Dict[str, Any]: """Connect the Ray Client to a server. Args: conn_str: Connection string, in the form "[host]:port" job_config: The job config of the server. secure: Whether to use a TLS secured gRPC channel metadata: gRPC metadata to send on connect connection_retries: number of connection attempts to make ignore_version: whether to ignore Python or Ray version mismatches. This should only be used for debugging purposes. Returns: Dictionary of connection info, e.g., {"num_clients": 1}. """ # Delay imports until connect to avoid circular imports. from ray.util.client.worker import Worker if self.client_worker is not None: if self._connected_with_init: return raise Exception("ray.init() called, but ray client is already connected") if not self._inside_client_test: # If we're calling a client connect specifically and we're not # currently in client mode, ensure we are. _explicitly_enable_client_mode() if namespace is not None: job_config = job_config or JobConfig() job_config.set_ray_namespace(namespace) logging_level = ray_constants.LOGGER_LEVEL logging_format = ray_constants.LOGGER_FORMAT if ray_init_kwargs is not None: if ray_init_kwargs.get("logging_level") is not None: logging_level = ray_init_kwargs["logging_level"] if ray_init_kwargs.get("logging_format") is not None: logging_format = ray_init_kwargs["logging_format"] setup_logger(logging_level, logging_format) try: self.client_worker = Worker( conn_str, secure=secure, _credentials=_credentials, metadata=metadata, connection_retries=connection_retries, ) self.api.worker = self.client_worker self.client_worker._server_init(job_config, ray_init_kwargs) conn_info = self.client_worker.connection_info() self._check_versions(conn_info, ignore_version) self._register_serializers() return conn_info except Exception: self.disconnect() raise
def modify_namespace(job_config: JobConfig): job_config.set_ray_namespace("test_value") return job_config
class ClientBuilder: """ Builder for a Ray Client connection. This class can be subclassed by custom builder classes to modify connection behavior to include additional features or altered semantics. One example is the ``_LocalClientBuilder``. """ def __init__(self, address: Optional[str]) -> None: self.address = address self._job_config = JobConfig() self._remote_init_kwargs = {} # Whether to allow connections to multiple clusters" # " (allow_multiple=True). self._allow_multiple_connections = False self._credentials = None # Set to False if ClientBuilder is being constructed by internal # methods self._deprecation_warn_enabled = True def env(self, env: Dict[str, Any]) -> "ClientBuilder": """ Set an environment for the session. Args: env (Dict[st, Any]): A runtime environment to use for this connection. See :ref:`runtime-environments` for what values are accepted in this dict. """ self._job_config.set_runtime_env(env) return self def namespace(self, namespace: str) -> "ClientBuilder": """ Sets the namespace for the session. Args: namespace (str): Namespace to use. """ self._job_config.set_ray_namespace(namespace) return self def connect(self) -> ClientContext: """ Begin a connection to the address passed in via ray.client(...). Returns: ClientInfo: Dataclass with information about the setting. This includes the server's version of Python & Ray as well as the dashboard_url. """ if self._deprecation_warn_enabled: self._client_deprecation_warn() # Fill runtime env/namespace from environment if not already set. # Should be done *after* the deprecation warning, since warning will # check if those values are already set. self._fill_defaults_from_env() # If it has already connected to the cluster with allow_multiple=True, # connect to the default one is not allowed. # But if it has connected to the default one, connect to other clients # with allow_multiple=True is allowed default_cli_connected = ray.util.client.ray.is_connected() has_cli_connected = ray.util.client.num_connected_contexts() > 0 if ( not self._allow_multiple_connections and not default_cli_connected and has_cli_connected ): raise ValueError( "The client has already connected to the cluster " "with allow_multiple=True. Please set allow_multiple=True" " to proceed" ) old_ray_cxt = None if self._allow_multiple_connections: old_ray_cxt = ray.util.client.ray.set_context(None) client_info_dict = ray.util.client_connect.connect( self.address, job_config=self._job_config, _credentials=self._credentials, ray_init_kwargs=self._remote_init_kwargs, ) get_dashboard_url = ray.remote(ray.worker.get_dashboard_url) dashboard_url = ray.get(get_dashboard_url.options(num_cpus=0).remote()) cxt = ClientContext( dashboard_url=dashboard_url, python_version=client_info_dict["python_version"], ray_version=client_info_dict["ray_version"], ray_commit=client_info_dict["ray_commit"], protocol_version=client_info_dict["protocol_version"], _num_clients=client_info_dict["num_clients"], _context_to_restore=ray.util.client.ray.get_context(), ) if self._allow_multiple_connections: ray.util.client.ray.set_context(old_ray_cxt) return cxt def _fill_defaults_from_env(self): # Check environment variables for default values namespace_env_var = os.environ.get(RAY_NAMESPACE_ENVIRONMENT_VARIABLE) if namespace_env_var and self._job_config.ray_namespace is None: self.namespace(namespace_env_var) runtime_env_var = os.environ.get(RAY_RUNTIME_ENV_ENVIRONMENT_VARIABLE) if runtime_env_var and self._job_config.runtime_env is None: self.env(json.loads(runtime_env_var)) def _init_args(self, **kwargs) -> "ClientBuilder": """ When a client builder is constructed through ray.init, for example `ray.init(ray://..., namespace=...)`, all of the arguments passed into ray.init with non-default values are passed again into this method. Custom client builders can override this method to do their own handling/validation of arguments. """ # Use namespace and runtime_env from ray.init call if kwargs.get("namespace") is not None: self.namespace(kwargs["namespace"]) del kwargs["namespace"] if kwargs.get("runtime_env") is not None: self.env(kwargs["runtime_env"]) del kwargs["runtime_env"] if kwargs.get("allow_multiple") is True: self._allow_multiple_connections = True del kwargs["allow_multiple"] if "_credentials" in kwargs.keys(): self._credentials = kwargs["_credentials"] del kwargs["_credentials"] if kwargs: expected_sig = inspect.signature(ray_driver_init) extra_args = set(kwargs.keys()).difference(expected_sig.parameters.keys()) if len(extra_args) > 0: raise RuntimeError( "Got unexpected kwargs: {}".format(", ".join(extra_args)) ) self._remote_init_kwargs = kwargs unknown = ", ".join(kwargs) logger.info( "Passing the following kwargs to ray.init() " f"on the server: {unknown}" ) return self def _client_deprecation_warn(self) -> None: """ Generates a warning for user's if this ClientBuilder instance was created directly or through ray.client, instead of relying on internal methods (ray.init, or auto init) """ namespace = self._job_config.ray_namespace runtime_env = self._job_config.runtime_env replacement_args = [] if self.address: if isinstance(self, _LocalClientBuilder): # Address might be set for LocalClientBuilder if ray.client() # is called while ray_current_cluster is set # (see _get_builder_from_address). In this case, # leave off the ray:// so the user attaches the driver directly replacement_args.append(f'"{self.address}"') else: replacement_args.append(f'"ray://{self.address}"') if namespace: replacement_args.append(f'namespace="{namespace}"') if runtime_env: # Use a placeholder here, since the real runtime_env would be # difficult to read if formatted in directly replacement_args.append("runtime_env=<your_runtime_env>") args_str = ", ".join(replacement_args) replacement_call = f"ray.init({args_str})" # Note: stack level is set to 3 since we want the warning to reach the # call to ray.client(...).connect(). The intervening frames are # connect() -> client_deprecation_warn() -> warnings.warn() # https://docs.python.org/3/library/warnings.html#available-functions warnings.warn( "Starting a connection through `ray.client` will be deprecated " "in future ray versions in favor of `ray.init`. See the docs for " f"more details: {CLIENT_DOCS_URL}. You can replace your call to " "`ray.client().connect()` with the following:\n" f" {replacement_call}\n", DeprecationWarning, stacklevel=3, )
class ClientBuilder: """ Builder for a Ray Client connection. This class can be subclassed by custom builder classes to modify connection behavior to include additional features or altered semantics. One example is the ``_LocalClientBuilder``. """ def __init__(self, address: Optional[str]) -> None: self.address = address self._job_config = JobConfig() self._fill_defaults_from_env() def env(self, env: Dict[str, Any]) -> "ClientBuilder": """ Set an environment for the session. Args: env (Dict[st, Any]): A runtime environment to use for this connection. See :ref:`runtime-environments` for what values are accepted in this dict. """ self._job_config.set_runtime_env(env) return self def namespace(self, namespace: str) -> "ClientBuilder": """ Sets the namespace for the session. Args: namespace (str): Namespace to use. """ self._job_config.set_ray_namespace(namespace) return self def connect(self) -> ClientContext: """ Begin a connection to the address passed in via ray.client(...). Returns: ClientInfo: Dataclass with information about the setting. This includes the server's version of Python & Ray as well as the dashboard_url. """ client_info_dict = ray.util.client_connect.connect( self.address, job_config=self._job_config) dashboard_url = ray.get( ray.remote(ray.worker.get_dashboard_url).remote()) return ClientContext( dashboard_url=dashboard_url, python_version=client_info_dict["python_version"], ray_version=client_info_dict["ray_version"], ray_commit=client_info_dict["ray_commit"], protocol_version=client_info_dict["protocol_version"], _num_clients=client_info_dict["num_clients"]) def _fill_defaults_from_env(self): # Check environment variables for default values namespace_env_var = os.environ.get(RAY_NAMESPACE_ENVIRONMENT_VARIABLE) if namespace_env_var and self._job_config.ray_namespace is None: self.namespace(namespace_env_var) runtime_env_var = os.environ.get(RAY_RUNTIME_ENV_ENVIRONMENT_VARIABLE) if runtime_env_var and self._job_config.runtime_env is None: self.env(json.loads(runtime_env_var)) def _init_args(self, **kwargs) -> "ClientBuilder": """ When a client builder is constructed through ray.init, for example `ray.init(ray://..., namespace=...)`, all of the arguments passed into ray.init are passed again into this method. Custom client builders can override this method to do their own handling/validation of arguments. """ # Use namespace and runtime_env from ray.init call if kwargs.get("namespace") is not None: self.namespace(kwargs["namespace"]) del kwargs["namespace"] if kwargs.get("runtime_env") is not None: self.env(kwargs["runtime_env"]) del kwargs["runtime_env"] if not kwargs: return self unknown = ", ".join(kwargs) raise RuntimeError( f"Unexpected keyword argument(s) for Ray Client: {unknown}")
class ClientBuilder: """ Builder for a Ray Client connection. This class can be subclassed by custom builder classes to modify connection behavior to include additional features or altered semantics. One example is the ``_LocalClientBuilder``. """ def __init__(self, address: Optional[str]) -> None: self.address = address self._job_config = JobConfig() self._fill_defaults_from_env() self._remote_init_kwargs = {} # Whether to allow connections to multiple clusters" # " (allow_multiple=True). self._allow_multiple_connections = False self._credentials = None def env(self, env: Dict[str, Any]) -> "ClientBuilder": """ Set an environment for the session. Args: env (Dict[st, Any]): A runtime environment to use for this connection. See :ref:`runtime-environments` for what values are accepted in this dict. """ self._job_config.set_runtime_env(env) return self def namespace(self, namespace: str) -> "ClientBuilder": """ Sets the namespace for the session. Args: namespace (str): Namespace to use. """ self._job_config.set_ray_namespace(namespace) return self def connect(self) -> ClientContext: """ Begin a connection to the address passed in via ray.client(...). Returns: ClientInfo: Dataclass with information about the setting. This includes the server's version of Python & Ray as well as the dashboard_url. """ # If it has already connected to the cluster with allow_multiple=True, # connect to the default one is not allowed. # But if it has connected to the default one, connect to other clients # with allow_multiple=True is allowed default_cli_connected = ray.util.client.ray.is_connected() has_cli_connected = ray.util.client.num_connected_contexts() > 0 if not self._allow_multiple_connections and \ not default_cli_connected and has_cli_connected: raise ValueError( "The client has already connected to the cluster " "with allow_multiple=True. Please set allow_multiple=True" " to proceed") old_ray_cxt = None if self._allow_multiple_connections: old_ray_cxt = ray.util.client.ray.set_context(None) client_info_dict = ray.util.client_connect.connect( self.address, job_config=self._job_config, _credentials=self._credentials, ray_init_kwargs=self._remote_init_kwargs) dashboard_url = ray.get( ray.remote(ray.worker.get_dashboard_url).remote()) cxt = ClientContext( dashboard_url=dashboard_url, python_version=client_info_dict["python_version"], ray_version=client_info_dict["ray_version"], ray_commit=client_info_dict["ray_commit"], protocol_version=client_info_dict["protocol_version"], _num_clients=client_info_dict["num_clients"], _context_to_restore=ray.util.client.ray.get_context()) if self._allow_multiple_connections: ray.util.client.ray.set_context(old_ray_cxt) return cxt def _fill_defaults_from_env(self): # Check environment variables for default values namespace_env_var = os.environ.get(RAY_NAMESPACE_ENVIRONMENT_VARIABLE) if namespace_env_var and self._job_config.ray_namespace is None: self.namespace(namespace_env_var) runtime_env_var = os.environ.get(RAY_RUNTIME_ENV_ENVIRONMENT_VARIABLE) if runtime_env_var and self._job_config.runtime_env is None: self.env(json.loads(runtime_env_var)) def _init_args(self, **kwargs) -> "ClientBuilder": """ When a client builder is constructed through ray.init, for example `ray.init(ray://..., namespace=...)`, all of the arguments passed into ray.init are passed again into this method. Custom client builders can override this method to do their own handling/validation of arguments. """ # Use namespace and runtime_env from ray.init call if kwargs.get("namespace") is not None: self.namespace(kwargs["namespace"]) del kwargs["namespace"] if kwargs.get("runtime_env") is not None: self.env(kwargs["runtime_env"]) del kwargs["runtime_env"] if kwargs.get("allow_multiple") is True: self._allow_multiple_connections = True del kwargs["allow_multiple"] if "_credentials" in kwargs.keys(): self._credentials = kwargs["_credentials"] del kwargs["_credentials"] if kwargs: expected_sig = inspect.signature(ray_driver_init) extra_args = set(kwargs.keys()).difference( expected_sig.parameters.keys()) if len(extra_args) > 0: raise RuntimeError("Got unexpected kwargs: {}".format( ", ".join(extra_args))) self._remote_init_kwargs = kwargs unknown = ", ".join(kwargs) logger.info("Passing the following kwargs to ray.init() " f"on the server: {unknown}") return self