Skip to content

Commit

Permalink
chore: update docs and fix load with using safe_load
Browse files Browse the repository at this point in the history
update some types annotations

Signed-off-by: Aaron Pham <29749331+aarnphm@users.noreply.github.com>
  • Loading branch information
aarnphm committed Sep 27, 2022
1 parent ca9bf60 commit 9547d0e
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 21 deletions.
4 changes: 2 additions & 2 deletions bentoml/_internal/configuration/containers.py
Expand Up @@ -276,13 +276,13 @@ def __init__(
use_regex=True,
)
override_config_map = {
k: yaml.load(v)
k: yaml.safe_load(v)
for k, v in [
split_with_quotes(line, sep="=", quote='"') for line in lines
]
}
try:
override_config = unflatten(override_config_map) # type: ignore
override_config = unflatten(override_config_map)
except ValueError as e:
raise BentoMLConfigException(
f'Failed to parse config options from the env var: {e}. \n *** Note: You can use " to quote the key if it contains special characters. ***'
Expand Down
40 changes: 21 additions & 19 deletions bentoml/_internal/utils/unflatten.py
Expand Up @@ -35,10 +35,12 @@
POSSIBILITY OF SUCH DAMAGE.
"""
from __future__ import annotations
from __future__ import absolute_import

import re
import sys
import typing as t
from operator import itemgetter

if sys.version_info[0] == 2:
Expand All @@ -47,7 +49,7 @@
string_type = str


def unflatten(arg):
def unflatten(arg: dict[str, t.Any]) -> dict[str, t.Any]:
"""Unflatten nested dict/array data.
This function takes a single argument which may either be a
Expand Down Expand Up @@ -80,15 +82,13 @@ def unflatten(arg):
{'foo': [{'bar': 'val'}, {'baz': 'x'}]}
"""
if hasattr(arg, "iteritems"):
items = arg.iteritems()
elif hasattr(arg, "items"):
if hasattr(arg, "items"):
items = arg.items()
else:
items = arg

data = {}
holders = []
data: dict[str, t.Any] = {}
holders: list[t.Any] = []
for flat_key, val in items:
parsed_key = _parse_key(flat_key)
obj = data
Expand Down Expand Up @@ -126,28 +126,30 @@ def unflatten(arg):
return data


def _node_type(value):
def _node_type(value: _holder) -> tuple[object] | t.Literal["terminal"]:
if isinstance(value, _holder):
return (value.node_type,)
else:
return "terminal"


class _holder(dict):
def __init__(self, flat_key):
node_type: type

def __init__(self, flat_key: str):
self.flat_key = flat_key
self.data = {}
self.data: dict[t.Any, t.Any] = {}

def __contains__(self, key):
def __contains__(self, key: t.Any):
return key in self.data

def __getitem__(self, key):
def __getitem__(self, key: t.Any):
return self.data[key]

def get(self, key):
def get(self, key: t.Any):
return self.data.get(key)

def __setitem__(self, key, value):
def __setitem__(self, key: t.Any, value: t.Any):
self.data[key] = value


Expand All @@ -161,9 +163,9 @@ def getvalue(self):
class _list_holder(_holder):
node_type = list

def getvalue(self):
def getvalue(self) -> list[t.Any]:
items = sorted(self.data.items(), key=itemgetter(0))
value = []
value: list[t.Any] = []
for n, (key, val) in enumerate(items):
if key != n:
assert key > n
Expand All @@ -176,12 +178,12 @@ def getvalue(self):
_dot_or_indexes_re = re.compile(r"(\.?\"[^\"]*\")|(\[\d+\])|(\.\w*)|(^\w*)")


def _parse_key(flat_key):
def _parse_key(flat_key: str):
if not isinstance(flat_key, string_type):
raise TypeError("keys must be strings")

split_key = _dot_or_indexes_re.split(flat_key)
parts = [""] if flat_key.startswith(".") else []
parts: list[t.Any] = [""] if flat_key.startswith(".") else []

for i in range(0, len(split_key), 5):
sep = split_key[i]
Expand Down Expand Up @@ -229,8 +231,8 @@ def _parse_key(flat_key):
return parts


def _unparse_key(parsed):
bits = []
def _unparse_key(parsed: list[t.Any]) -> str:
bits: list[str] = []
for part in parsed:
if isinstance(part, string_type):
if part.isidentifier():
Expand Down
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
30 changes: 30 additions & 0 deletions docs/source/guides/configuration.rst
Expand Up @@ -36,6 +36,36 @@ like the example above. For a full configuration schema including all customizab
the BentoML configuration template defined in
`default_configuration.yml <https://github.com/bentoml/BentoML/blob/main/bentoml/_internal/configuration/default_configuration.yaml>`_.


Overrding configuration with environment variables
--------------------------------------------------

Users can also override configuration fields with environment variables. by defining
an oneline value of a "flat" JSON via ``BENTOML_CONFIG_OPTIONS``:

.. code-block:: yaml
$ BENTOML_CONFIG_OPTIONS='runners.pytorch_mnist.resources."nvidia.com/gpu"[0]=0 runners.pytorch_mnist.resources."nvidia.com/gpu"[1]=2' \
bentoml serve pytorch_mnist_demo:latest --production
Which the override configuration will be intepreted as:

.. code-block:: yaml
runners:
pytorch_mnist:
resources:
nvidia.com/gpu: [0, 2]
.. note::

For fields that represents a iterable type, the override array must have a space
separating each element:

.. image:: /_static/img/configuration-override-env.png
:alt: Configuration override environment variable


Docker Deployment
-----------------

Expand Down
5 changes: 5 additions & 0 deletions pyproject.toml
Expand Up @@ -61,6 +61,7 @@ exclude = '''
| dist
| typings
| bentoml/grpc
| grpc-client/thirdparty
)/
| bentoml/_version.py
)
Expand Down Expand Up @@ -213,6 +214,8 @@ skip_glob = [
"**/*_pb2_grpc.py",
"venv/*",
"lib/*",
"grpc-client/thirdparty",
"bazel-*",
]

[tool.pyright]
Expand All @@ -225,6 +228,8 @@ exclude = [
'bentoml/grpc/v1alpha1/',
'**/*_pb2.py',
"**/*_pb2_grpc.py",
"grpc-client/thirdparty",
"bazel-*",
]
useLibraryCodeForTypes = true
strictListInference = true
Expand Down

0 comments on commit 9547d0e

Please sign in to comment.