We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
ValueError: invalid literal for int() with base 10: '$(circus.sockets.{runner_name}) when runner name has a space character in production mode
**Describe the bug**
A Runner object can be instantiated with a `name` argument. It seem as if this name contains a space seperator mentioned ValueError is raised when serving in production mode. As you can see in the screenshot, name of the runner is *Document Klassifikation*. I added some print statements starting with `>>>`:
![image](https://user-images.githubusercontent.com/39236016/173335889-cf02fb9e-b42e-4f31-a8da-9dfc3e4f3899.png)
Removing the space, `name="DocumentKlassifikation"` fixed the problem. 
I tracked the problem down to `/bentoml/_internal/server/cli/runner.py`. Seems the `runner_name` needs to be sanitized before using it for the bind I guess.

**To Reproduce**

Steps to reproduce the issue:
1. Create a Runner object 
2. set argument `name="Document Classification"
3. run `bentoml serve service.py --production`
4. See ValueError


**Expected behavior**
No ValueError is raised.

**Environment:**
 - OS: SLES 12 SP5
 - Python Version 3.10.4
 - BentoML Version bentoml, version 1.0.0rc0


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `bentoml/_internal/runner/runner.py`
Content:
```
1 from __future__ import annotations
2 
3 import typing as t
4 import logging
5 from typing import TYPE_CHECKING
6 from functools import lru_cache
7 
8 import attr
9 
10 from ..types import ParamSpec
11 from ..utils import first_not_none
12 from .resource import Resource
13 from .runnable import Runnable
14 from .strategy import Strategy
15 from .strategy import DefaultStrategy
16 from ...exceptions import StateException
17 from .runner_handle import RunnerHandle
18 from .runner_handle import DummyRunnerHandle
19 
20 if TYPE_CHECKING:
21     from ..models import Model
22     from .runnable import RunnableMethodConfig
23 
24 T = t.TypeVar("T", bound=Runnable)
25 P = ParamSpec("P")
26 R = t.TypeVar("R")
27 
28 logger = logging.getLogger(__name__)
29 
30 
31 @attr.frozen(slots=False)
32 class RunnerMethod(t.Generic[T, P, R]):
33     runner: Runner
34     name: str
35     config: RunnableMethodConfig
36     max_batch_size: int
37     max_latency_ms: int
38 
39     def run(self, *args: t.Any, **kwargs: t.Any) -> t.Any:
40         return self.runner._runner_handle.run_method(  # type: ignore
41             self,
42             *args,
43             **kwargs,
44         )
45 
46     async def async_run(self, *args: t.Any, **kwargs: t.Any) -> t.Any:
47         return await self.runner._runner_handle.async_run_method(  # type: ignore
48             self,
49             *args,
50             **kwargs,
51         )
52 
53 
54 # TODO: Move these to the default configuration file and allow user override
55 GLOBAL_DEFAULT_MAX_BATCH_SIZE = 100
56 GLOBAL_DEFAULT_MAX_LATENCY_MS = 10000
57 
58 
59 @attr.define(slots=False, frozen=True, eq=False)
60 class Runner:
61     runnable_class: t.Type[Runnable]
62     runnable_init_params: dict[str, t.Any]
63     name: str
64     models: list[Model]
65     resource_config: Resource
66     runner_methods: list[RunnerMethod[t.Any, t.Any, t.Any]]
67     scheduling_strategy: t.Type[Strategy]
68 
69     _runner_handle: RunnerHandle = attr.field(init=False, factory=DummyRunnerHandle)
70 
71     def __init__(
72         self,
73         runnable_class: t.Type[Runnable],
74         *,
75         runnable_init_params: t.Dict[str, t.Any] | None = None,
76         name: str | None = None,
77         scheduling_strategy: t.Type[Strategy] = DefaultStrategy,
78         models: t.List[Model] | None = None,
79         cpu: int
80         | None = None,  # TODO: support str and float type here? e.g "500m" or "0.5"
81         nvidia_gpu: int | None = None,
82         custom_resources: t.Dict[str, float] | None = None,
83         max_batch_size: int | None = None,
84         max_latency_ms: int | None = None,
85         method_configs: t.Dict[str, t.Dict[str, int]] | None = None,
86     ) -> None:
87         """
88         Args:
89             runnable_class: runnable class
90             runnable_init_params: runnable init params
91             name: runner name
92             scheduling_strategy: scheduling strategy
93             models: list of required bento models
94             cpu: cpu resource
95             nvidia_gpu: nvidia gpu resource
96             custom_resources: custom resources
97             max_batch_size: max batch size config for micro batching
98             max_latency_ms: max latency config for micro batching
99             method_configs: per method configs
100         """
101 
102         name = runnable_class.__name__ if name is None else name
103         models = [] if models is None else models
104         runner_method_map: dict[str, RunnerMethod[t.Any, t.Any, t.Any]] = {}
105         runnable_init_params = (
106             {} if runnable_init_params is None else runnable_init_params
107         )
108         method_configs = {} if method_configs is None else {}
109         custom_resources = {} if custom_resources is None else {}
110         resource_config = Resource(
111             cpu=cpu,
112             nvidia_gpu=nvidia_gpu,
113             custom_resources=custom_resources or {},
114         )
115 
116         if runnable_class.methods is None:
117             raise ValueError(
118                 f"Runnable class '{runnable_class.__name__}' has no methods!"
119             )
120 
121         for method_name, method in runnable_class.methods.items():
122             method_max_batch_size = method_configs.get(method_name, {}).get(
123                 "max_batch_size"
124             )
125             method_max_latency_ms = method_configs.get(method_name, {}).get(
126                 "max_latency_ms"
127             )
128 
129             runner_method_map[method_name] = RunnerMethod(
130                 runner=self,
131                 name=method_name,
132                 config=method.config,
133                 max_batch_size=first_not_none(
134                     method_max_batch_size,
135                     max_batch_size,
136                     default=GLOBAL_DEFAULT_MAX_BATCH_SIZE,
137                 ),
138                 max_latency_ms=first_not_none(
139                     method_max_latency_ms,
140                     max_latency_ms,
141                     default=GLOBAL_DEFAULT_MAX_LATENCY_MS,
142                 ),
143             )
144 
145         self.__attrs_init__(  # type: ignore
146             runnable_class=runnable_class,
147             runnable_init_params=runnable_init_params,
148             name=name,
149             models=models,
150             resource_config=resource_config,
151             runner_methods=list(runner_method_map.values()),
152             scheduling_strategy=scheduling_strategy,
153         )
154 
155         # Choose the default method:
156         #  1. if there's only one method, it will be set as default
157         #  2. if there's a method named "__call__", it will be set as default
158         #  3. otherwise, there's no default method
159         if len(runner_method_map) == 1:
160             default_method = next(iter(runner_method_map.values()))
161             logger.debug(
162                 f"Default runner method set to `{default_method.name}`, it can be accessed both via `runner.run` and `runner.{default_method.name}.async_run`"
163             )
164         elif "__call__" in runner_method_map:
165             default_method = runner_method_map["__call__"]
166             logger.debug(
167                 "Default runner method set to `__call__`, it can be accessed via `runner.run` or `runner.async_run`"
168             )
169         else:
170             default_method = None
171             logger.warning(
172                 f'No default method found for Runner "{name}", all method access needs to be in the form of `runner.{{method}}.run`'
173             )
174 
175         # set default run method entrypoint
176         if default_method is not None:
177             object.__setattr__(self, "run", default_method.run)
178             object.__setattr__(self, "async_run", default_method.async_run)
179 
180         # set all run method entrypoint
181         for runner_method in self.runner_methods:
182             object.__setattr__(self, runner_method.name, runner_method)
183 
184     @lru_cache(maxsize=1)
185     def get_effective_resource_config(self) -> Resource:
186         return (
187             Resource.from_config(self.name)
188             | self.resource_config
189             | Resource.from_system()
190         )
191 
192     def _init(self, handle_class: t.Type[RunnerHandle]) -> None:
193         if not isinstance(self._runner_handle, DummyRunnerHandle):
194             raise StateException("Runner already initialized")
195 
196         runner_handle = handle_class(self)
197         object.__setattr__(self, "_runner_handle", runner_handle)
198 
199     def _init_local(self) -> None:
200         from .runner_handle.local import LocalRunnerRef
201 
202         self._init(LocalRunnerRef)
203 
204     def init_local(self, quiet: bool = False) -> None:
205         """
206         init local runnable instance, for testing and debugging only
207         """
208         if not quiet:
209             logger.warning("'Runner.init_local' is for debugging and testing only")
210 
211         self._init_local()
212 
213     def init_client(self):
214         """
215         init client for a remote runner instance
216         """
217         from .runner_handle.remote import RemoteRunnerClient
218 
219         self._init(RemoteRunnerClient)
220 
221     def destroy(self):
222         object.__setattr__(self, "_runner_handle", DummyRunnerHandle())
223 
224     @property
225     def scheduled_worker_count(self) -> int:
226         return self.scheduling_strategy.get_worker_count(
227             self.runnable_class,
228             self.get_effective_resource_config(),
229         )
230 
231     def setup_worker(self, worker_id: int) -> None:
232         self.scheduling_strategy.setup_worker(
233             self.runnable_class,
234             self.get_effective_resource_config(),
235             worker_id,
236         )
237
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/bentoml/_internal/runner/runner.py b/bentoml/_internal/runner/runner.py
--- a/bentoml/_internal/runner/runner.py
+++ b/bentoml/_internal/runner/runner.py
@@ -7,6 +7,7 @@
 
 import attr
 
+from ..tag import validate_tag_str
 from ..types import ParamSpec
 from ..utils import first_not_none
 from .resource import Resource
@@ -99,7 +100,16 @@
             method_configs: per method configs
         """
 
-        name = runnable_class.__name__ if name is None else name
+        if name is None:
+            name = runnable_class.__name__
+
+        try:
+            validate_tag_str(name)
+        except ValueError as e:
+            # TODO: link to tag validation documentation
+            raise ValueError(
+                f"Runner name '{name}' is not valid; it must be a valid BentoML Tag name."
+            ) from e
         models = [] if models is None else models
         runner_method_map: dict[str, RunnerMethod[t.Any, t.Any, t.Any]] = {}
         runnable_init_params = (

{"golden_diff": "diff --git a/bentoml/_internal/runner/runner.py b/bentoml/_internal/runner/runner.py\n--- a/bentoml/_internal/runner/runner.py\n+++ b/bentoml/_internal/runner/runner.py\n@@ -7,6 +7,7 @@\n \n import attr\n \n+from ..tag import validate_tag_str\n from ..types import ParamSpec\n from ..utils import first_not_none\n from .resource import Resource\n@@ -99,7 +100,16 @@\n             method_configs: per method configs\n         \"\"\"\n \n-        name = runnable_class.__name__ if name is None else name\n+        if name is None:\n+            name = runnable_class.__name__\n+\n+        try:\n+            validate_tag_str(name)\n+        except ValueError as e:\n+            # TODO: link to tag validation documentation\n+            raise ValueError(\n+                f\"Runner name '{name}' is not valid; it must be a valid BentoML Tag name.\"\n+            ) from e\n         models = [] if models is None else models\n         runner_method_map: dict[str, RunnerMethod[t.Any, t.Any, t.Any]] = {}\n         runnable_init_params = (\n", "issue": "ValueError: invalid literal for int() with base 10: '$(circus.sockets.{runner_name}) when runner name has a space character in production mode\n**Describe the bug**\r\nA Runner object can be instantiated with a `name` argument. It seem as if this name contains a space seperator mentioned ValueError is raised when serving in production mode. As you can see in the screenshot, name of the runner is *Document Klassifikation*. I added some print statements starting with `>>>`:\r\n![image](https://user-images.githubusercontent.com/39236016/173335889-cf02fb9e-b42e-4f31-a8da-9dfc3e4f3899.png)\r\nRemoving the space, `name=\"DocumentKlassifikation\"` fixed the problem. \r\nI tracked the problem down to `/bentoml/_internal/server/cli/runner.py`. Seems the `runner_name` needs to be sanitized before using it for the bind I guess.\r\n\r\n**To Reproduce**\r\n\r\nSteps to reproduce the issue:\r\n1. Create a Runner object \r\n2. set argument `name=\"Document Classification\"\r\n3. run `bentoml serve service.py --production`\r\n4. See ValueError\r\n\r\n\r\n**Expected behavior**\r\nNo ValueError is raised.\r\n\r\n**Environment:**\r\n - OS: SLES 12 SP5\r\n - Python Version 3.10.4\r\n - BentoML Version bentoml, version 1.0.0rc0\r\n\n", "before_files": [{"content": "from __future__ import annotations\n\nimport typing as t\nimport logging\nfrom typing import TYPE_CHECKING\nfrom functools import lru_cache\n\nimport attr\n\nfrom ..types import ParamSpec\nfrom ..utils import first_not_none\nfrom .resource import Resource\nfrom .runnable import Runnable\nfrom .strategy import Strategy\nfrom .strategy import DefaultStrategy\nfrom ...exceptions import StateException\nfrom .runner_handle import RunnerHandle\nfrom .runner_handle import DummyRunnerHandle\n\nif TYPE_CHECKING:\n    from ..models import Model\n    from .runnable import RunnableMethodConfig\n\nT = t.TypeVar(\"T\", bound=Runnable)\nP = ParamSpec(\"P\")\nR = t.TypeVar(\"R\")\n\nlogger = logging.getLogger(__name__)\n\n\n@attr.frozen(slots=False)\nclass RunnerMethod(t.Generic[T, P, R]):\n    runner: Runner\n    name: str\n    config: RunnableMethodConfig\n    max_batch_size: int\n    max_latency_ms: int\n\n    def run(self, *args: t.Any, **kwargs: t.Any) -> t.Any:\n        return self.runner._runner_handle.run_method(  # type: ignore\n            self,\n            *args,\n            **kwargs,\n        )\n\n    async def async_run(self, *args: t.Any, **kwargs: t.Any) -> t.Any:\n        return await self.runner._runner_handle.async_run_method(  # type: ignore\n            self,\n            *args,\n            **kwargs,\n        )\n\n\n# TODO: Move these to the default configuration file and allow user override\nGLOBAL_DEFAULT_MAX_BATCH_SIZE = 100\nGLOBAL_DEFAULT_MAX_LATENCY_MS = 10000\n\n\n@attr.define(slots=False, frozen=True, eq=False)\nclass Runner:\n    runnable_class: t.Type[Runnable]\n    runnable_init_params: dict[str, t.Any]\n    name: str\n    models: list[Model]\n    resource_config: Resource\n    runner_methods: list[RunnerMethod[t.Any, t.Any, t.Any]]\n    scheduling_strategy: t.Type[Strategy]\n\n    _runner_handle: RunnerHandle = attr.field(init=False, factory=DummyRunnerHandle)\n\n    def __init__(\n        self,\n        runnable_class: t.Type[Runnable],\n        *,\n        runnable_init_params: t.Dict[str, t.Any] | None = None,\n        name: str | None = None,\n        scheduling_strategy: t.Type[Strategy] = DefaultStrategy,\n        models: t.List[Model] | None = None,\n        cpu: int\n        | None = None,  # TODO: support str and float type here? e.g \"500m\" or \"0.5\"\n        nvidia_gpu: int | None = None,\n        custom_resources: t.Dict[str, float] | None = None,\n        max_batch_size: int | None = None,\n        max_latency_ms: int | None = None,\n        method_configs: t.Dict[str, t.Dict[str, int]] | None = None,\n    ) -> None:\n        \"\"\"\n        Args:\n            runnable_class: runnable class\n            runnable_init_params: runnable init params\n            name: runner name\n            scheduling_strategy: scheduling strategy\n            models: list of required bento models\n            cpu: cpu resource\n            nvidia_gpu: nvidia gpu resource\n            custom_resources: custom resources\n            max_batch_size: max batch size config for micro batching\n            max_latency_ms: max latency config for micro batching\n            method_configs: per method configs\n        \"\"\"\n\n        name = runnable_class.__name__ if name is None else name\n        models = [] if models is None else models\n        runner_method_map: dict[str, RunnerMethod[t.Any, t.Any, t.Any]] = {}\n        runnable_init_params = (\n            {} if runnable_init_params is None else runnable_init_params\n        )\n        method_configs = {} if method_configs is None else {}\n        custom_resources = {} if custom_resources is None else {}\n        resource_config = Resource(\n            cpu=cpu,\n            nvidia_gpu=nvidia_gpu,\n            custom_resources=custom_resources or {},\n        )\n\n        if runnable_class.methods is None:\n            raise ValueError(\n                f\"Runnable class '{runnable_class.__name__}' has no methods!\"\n            )\n\n        for method_name, method in runnable_class.methods.items():\n            method_max_batch_size = method_configs.get(method_name, {}).get(\n                \"max_batch_size\"\n            )\n            method_max_latency_ms = method_configs.get(method_name, {}).get(\n                \"max_latency_ms\"\n            )\n\n            runner_method_map[method_name] = RunnerMethod(\n                runner=self,\n                name=method_name,\n                config=method.config,\n                max_batch_size=first_not_none(\n                    method_max_batch_size,\n                    max_batch_size,\n                    default=GLOBAL_DEFAULT_MAX_BATCH_SIZE,\n                ),\n                max_latency_ms=first_not_none(\n                    method_max_latency_ms,\n                    max_latency_ms,\n                    default=GLOBAL_DEFAULT_MAX_LATENCY_MS,\n                ),\n            )\n\n        self.__attrs_init__(  # type: ignore\n            runnable_class=runnable_class,\n            runnable_init_params=runnable_init_params,\n            name=name,\n            models=models,\n            resource_config=resource_config,\n            runner_methods=list(runner_method_map.values()),\n            scheduling_strategy=scheduling_strategy,\n        )\n\n        # Choose the default method:\n        #  1. if there's only one method, it will be set as default\n        #  2. if there's a method named \"__call__\", it will be set as default\n        #  3. otherwise, there's no default method\n        if len(runner_method_map) == 1:\n            default_method = next(iter(runner_method_map.values()))\n            logger.debug(\n                f\"Default runner method set to `{default_method.name}`, it can be accessed both via `runner.run` and `runner.{default_method.name}.async_run`\"\n            )\n        elif \"__call__\" in runner_method_map:\n            default_method = runner_method_map[\"__call__\"]\n            logger.debug(\n                \"Default runner method set to `__call__`, it can be accessed via `runner.run` or `runner.async_run`\"\n            )\n        else:\n            default_method = None\n            logger.warning(\n                f'No default method found for Runner \"{name}\", all method access needs to be in the form of `runner.{{method}}.run`'\n            )\n\n        # set default run method entrypoint\n        if default_method is not None:\n            object.__setattr__(self, \"run\", default_method.run)\n            object.__setattr__(self, \"async_run\", default_method.async_run)\n\n        # set all run method entrypoint\n        for runner_method in self.runner_methods:\n            object.__setattr__(self, runner_method.name, runner_method)\n\n    @lru_cache(maxsize=1)\n    def get_effective_resource_config(self) -> Resource:\n        return (\n            Resource.from_config(self.name)\n            | self.resource_config\n            | Resource.from_system()\n        )\n\n    def _init(self, handle_class: t.Type[RunnerHandle]) -> None:\n        if not isinstance(self._runner_handle, DummyRunnerHandle):\n            raise StateException(\"Runner already initialized\")\n\n        runner_handle = handle_class(self)\n        object.__setattr__(self, \"_runner_handle\", runner_handle)\n\n    def _init_local(self) -> None:\n        from .runner_handle.local import LocalRunnerRef\n\n        self._init(LocalRunnerRef)\n\n    def init_local(self, quiet: bool = False) -> None:\n        \"\"\"\n        init local runnable instance, for testing and debugging only\n        \"\"\"\n        if not quiet:\n            logger.warning(\"'Runner.init_local' is for debugging and testing only\")\n\n        self._init_local()\n\n    def init_client(self):\n        \"\"\"\n        init client for a remote runner instance\n        \"\"\"\n        from .runner_handle.remote import RemoteRunnerClient\n\n        self._init(RemoteRunnerClient)\n\n    def destroy(self):\n        object.__setattr__(self, \"_runner_handle\", DummyRunnerHandle())\n\n    @property\n    def scheduled_worker_count(self) -> int:\n        return self.scheduling_strategy.get_worker_count(\n            self.runnable_class,\n            self.get_effective_resource_config(),\n        )\n\n    def setup_worker(self, worker_id: int) -> None:\n        self.scheduling_strategy.setup_worker(\n            self.runnable_class,\n            self.get_effective_resource_config(),\n            worker_id,\n        )\n", "path": "bentoml/_internal/runner/runner.py"}], "after_files": [{"content": "from __future__ import annotations\n\nimport typing as t\nimport logging\nfrom typing import TYPE_CHECKING\nfrom functools import lru_cache\n\nimport attr\n\nfrom ..tag import validate_tag_str\nfrom ..types import ParamSpec\nfrom ..utils import first_not_none\nfrom .resource import Resource\nfrom .runnable import Runnable\nfrom .strategy import Strategy\nfrom .strategy import DefaultStrategy\nfrom ...exceptions import StateException\nfrom .runner_handle import RunnerHandle\nfrom .runner_handle import DummyRunnerHandle\n\nif TYPE_CHECKING:\n    from ..models import Model\n    from .runnable import RunnableMethodConfig\n\nT = t.TypeVar(\"T\", bound=Runnable)\nP = ParamSpec(\"P\")\nR = t.TypeVar(\"R\")\n\nlogger = logging.getLogger(__name__)\n\n\n@attr.frozen(slots=False)\nclass RunnerMethod(t.Generic[T, P, R]):\n    runner: Runner\n    name: str\n    config: RunnableMethodConfig\n    max_batch_size: int\n    max_latency_ms: int\n\n    def run(self, *args: t.Any, **kwargs: t.Any) -> t.Any:\n        return self.runner._runner_handle.run_method(  # type: ignore\n            self,\n            *args,\n            **kwargs,\n        )\n\n    async def async_run(self, *args: t.Any, **kwargs: t.Any) -> t.Any:\n        return await self.runner._runner_handle.async_run_method(  # type: ignore\n            self,\n            *args,\n            **kwargs,\n        )\n\n\n# TODO: Move these to the default configuration file and allow user override\nGLOBAL_DEFAULT_MAX_BATCH_SIZE = 100\nGLOBAL_DEFAULT_MAX_LATENCY_MS = 10000\n\n\n@attr.define(slots=False, frozen=True, eq=False)\nclass Runner:\n    runnable_class: t.Type[Runnable]\n    runnable_init_params: dict[str, t.Any]\n    name: str\n    models: list[Model]\n    resource_config: Resource\n    runner_methods: list[RunnerMethod[t.Any, t.Any, t.Any]]\n    scheduling_strategy: t.Type[Strategy]\n\n    _runner_handle: RunnerHandle = attr.field(init=False, factory=DummyRunnerHandle)\n\n    def __init__(\n        self,\n        runnable_class: t.Type[Runnable],\n        *,\n        runnable_init_params: t.Dict[str, t.Any] | None = None,\n        name: str | None = None,\n        scheduling_strategy: t.Type[Strategy] = DefaultStrategy,\n        models: t.List[Model] | None = None,\n        cpu: int\n        | None = None,  # TODO: support str and float type here? e.g \"500m\" or \"0.5\"\n        nvidia_gpu: int | None = None,\n        custom_resources: t.Dict[str, float] | None = None,\n        max_batch_size: int | None = None,\n        max_latency_ms: int | None = None,\n        method_configs: t.Dict[str, t.Dict[str, int]] | None = None,\n    ) -> None:\n        \"\"\"\n        Args:\n            runnable_class: runnable class\n            runnable_init_params: runnable init params\n            name: runner name\n            scheduling_strategy: scheduling strategy\n            models: list of required bento models\n            cpu: cpu resource\n            nvidia_gpu: nvidia gpu resource\n            custom_resources: custom resources\n            max_batch_size: max batch size config for micro batching\n            max_latency_ms: max latency config for micro batching\n            method_configs: per method configs\n        \"\"\"\n\n        if name is None:\n            name = runnable_class.__name__\n\n        try:\n            validate_tag_str(name)\n        except ValueError as e:\n            # TODO: link to tag validation documentation\n            raise ValueError(\n                f\"Runner name '{name}' is not valid; it must be a valid BentoML Tag name.\"\n            ) from e\n        models = [] if models is None else models\n        runner_method_map: dict[str, RunnerMethod[t.Any, t.Any, t.Any]] = {}\n        runnable_init_params = (\n            {} if runnable_init_params is None else runnable_init_params\n        )\n        method_configs = {} if method_configs is None else {}\n        custom_resources = {} if custom_resources is None else {}\n        resource_config = Resource(\n            cpu=cpu,\n            nvidia_gpu=nvidia_gpu,\n            custom_resources=custom_resources or {},\n        )\n\n        if runnable_class.methods is None:\n            raise ValueError(\n                f\"Runnable class '{runnable_class.__name__}' has no methods!\"\n            )\n\n        for method_name, method in runnable_class.methods.items():\n            method_max_batch_size = method_configs.get(method_name, {}).get(\n                \"max_batch_size\"\n            )\n            method_max_latency_ms = method_configs.get(method_name, {}).get(\n                \"max_latency_ms\"\n            )\n\n            runner_method_map[method_name] = RunnerMethod(\n                runner=self,\n                name=method_name,\n                config=method.config,\n                max_batch_size=first_not_none(\n                    method_max_batch_size,\n                    max_batch_size,\n                    default=GLOBAL_DEFAULT_MAX_BATCH_SIZE,\n                ),\n                max_latency_ms=first_not_none(\n                    method_max_latency_ms,\n                    max_latency_ms,\n                    default=GLOBAL_DEFAULT_MAX_LATENCY_MS,\n                ),\n            )\n\n        self.__attrs_init__(  # type: ignore\n            runnable_class=runnable_class,\n            runnable_init_params=runnable_init_params,\n            name=name,\n            models=models,\n            resource_config=resource_config,\n            runner_methods=list(runner_method_map.values()),\n            scheduling_strategy=scheduling_strategy,\n        )\n\n        # Choose the default method:\n        #  1. if there's only one method, it will be set as default\n        #  2. if there's a method named \"__call__\", it will be set as default\n        #  3. otherwise, there's no default method\n        if len(runner_method_map) == 1:\n            default_method = next(iter(runner_method_map.values()))\n            logger.debug(\n                f\"Default runner method set to `{default_method.name}`, it can be accessed both via `runner.run` and `runner.{default_method.name}.async_run`\"\n            )\n        elif \"__call__\" in runner_method_map:\n            default_method = runner_method_map[\"__call__\"]\n            logger.debug(\n                \"Default runner method set to `__call__`, it can be accessed via `runner.run` or `runner.async_run`\"\n            )\n        else:\n            default_method = None\n            logger.warning(\n                f'No default method found for Runner \"{name}\", all method access needs to be in the form of `runner.{{method}}.run`'\n            )\n\n        # set default run method entrypoint\n        if default_method is not None:\n            object.__setattr__(self, \"run\", default_method.run)\n            object.__setattr__(self, \"async_run\", default_method.async_run)\n\n        # set all run method entrypoint\n        for runner_method in self.runner_methods:\n            object.__setattr__(self, runner_method.name, runner_method)\n\n    @lru_cache(maxsize=1)\n    def get_effective_resource_config(self) -> Resource:\n        return (\n            Resource.from_config(self.name)\n            | self.resource_config\n            | Resource.from_system()\n        )\n\n    def _init(self, handle_class: t.Type[RunnerHandle]) -> None:\n        if not isinstance(self._runner_handle, DummyRunnerHandle):\n            raise StateException(\"Runner already initialized\")\n\n        runner_handle = handle_class(self)\n        object.__setattr__(self, \"_runner_handle\", runner_handle)\n\n    def _init_local(self) -> None:\n        from .runner_handle.local import LocalRunnerRef\n\n        self._init(LocalRunnerRef)\n\n    def init_local(self, quiet: bool = False) -> None:\n        \"\"\"\n        init local runnable instance, for testing and debugging only\n        \"\"\"\n        if not quiet:\n            logger.warning(\"'Runner.init_local' is for debugging and testing only\")\n\n        self._init_local()\n\n    def init_client(self):\n        \"\"\"\n        init client for a remote runner instance\n        \"\"\"\n        from .runner_handle.remote import RemoteRunnerClient\n\n        self._init(RemoteRunnerClient)\n\n    def destroy(self):\n        object.__setattr__(self, \"_runner_handle\", DummyRunnerHandle())\n\n    @property\n    def scheduled_worker_count(self) -> int:\n        return self.scheduling_strategy.get_worker_count(\n            self.runnable_class,\n            self.get_effective_resource_config(),\n        )\n\n    def setup_worker(self, worker_id: int) -> None:\n        self.scheduling_strategy.setup_worker(\n            self.runnable_class,\n            self.get_effective_resource_config(),\n            worker_id,\n        )\n", "path": "bentoml/_internal/runner/runner.py"}]}