From 9ed5ea825a16e08666d8f8b96ec117fee85eecee Mon Sep 17 00:00:00 2001 From: Aaron Pham <29749331+aarnphm@users.noreply.github.com> Date: Wed, 8 Feb 2023 08:03:17 -0800 Subject: [PATCH 01/10] tools: using ruff instead of pylint chore: ignore lint rules in __init__.py Signed-off-by: Aaron Pham <29749331+aarnphm@users.noreply.github.com> --- .github/workflows/ci.yml | 3 +- DEVELOPMENT.md | 2 +- Makefile | 10 +- pyproject.toml | 339 +++++++++++++--------------- requirements/tests-requirements.txt | 2 +- 5 files changed, 165 insertions(+), 191 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6ab8c9d6bd1..7be43c40e6d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -105,8 +105,7 @@ jobs: black --check --pyi typings isort --check . - name: Lint check - if: ${{ github.event_name == 'pull_request' }} - run: git diff --name-only --diff-filter=d "origin/$GITHUB_BASE_REF" -z -- '*.py' | xargs -0 --no-run-if-empty pylint --rcfile pyproject.toml --fail-under 9.0 --exit-zero + run: ruff check src tests examples --fix - name: Type check if: ${{ github.event_name == 'pull_request' }} run: git diff --name-only --diff-filter=d "origin/$GITHUB_BASE_REF" -z -- '*.py[i]' | xargs -0 --no-run-if-empty pyright diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index b53185b663d..262b47901eb 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -210,7 +210,7 @@ bentoml get IrisClassifier --verbose formatter: [black](https://github.com/psf/black), [isort](https://github.com/PyCQA/isort), [buf](https://github.com/bufbuild/buf) -linter: [pylint](https://pylint.org/), [buf](https://github.com/bufbuild/buf) +linter: [ruff](https://github.com/charliermarsh/ruff), [buf](https://github.com/bufbuild/buf) type checker: [pyright](https://github.com/microsoft/pyright) diff --git a/Makefile b/Makefile index ffce5372a69..059e72ef4f0 100644 --- a/Makefile +++ b/Makefile @@ -20,13 +20,9 @@ format: ## Running code formatter: black and isort format-proto: ## Running proto formatter: buf @echo "Formatting proto files..." docker run --init --rm --volume $(GIT_ROOT)/src:/workspace --workdir /workspace bufbuild/buf format --config "/workspace/bentoml/grpc/buf.yaml" -w bentoml/grpc -lint: ## Running lint checker: pylint - @echo "(pylint) Linting bentoml..." - @pylint --rcfile=pyproject.toml --fail-under 9.5 src - @echo "(pylint) Linting examples..." - @pylint --rcfile=pyproject.toml --fail-under 9.5 examples - @echo "(pylint) Linting tests..." - @pylint --rcfile=pyproject.toml --fail-under 9.5 tests +lint: ## Running lint checker: ruff + @echo "(ruff) Linting development project..." + @ruff check src examples tests --fix lint-proto: ## Running proto lint checker: buf @echo "Linting proto files..." docker run --init --rm --volume $(GIT_ROOT)/src:/workspace --workdir /workspace bufbuild/buf lint --config "/workspace/bentoml/grpc/buf.yaml" --error-format msvs bentoml/grpc diff --git a/pyproject.toml b/pyproject.toml index 6cbdc7d6d55..8017353b36a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,63 +11,63 @@ keywords = ["MLOps", "AI", "BentoML", "Model Serving", "Model Deployment"] license = { text = "Apache-2.0" } authors = [{ name = "BentoML Team", email = "contact@bentoml.com" }] classifiers = [ - "License :: OSI Approved :: Apache Software License", - "Operating System :: OS Independent", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: Implementation :: CPython", - "Topic :: Scientific/Engineering :: Artificial Intelligence", - "Topic :: Software Development :: Libraries", + "License :: OSI Approved :: Apache Software License", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: Implementation :: CPython", + "Topic :: Scientific/Engineering :: Artificial Intelligence", + "Topic :: Software Development :: Libraries", ] dependencies = [ - "Jinja2>=3.0.1", - "PyYAML>=5.0", - "aiohttp", - "attrs>=21.1.0", - "cattrs>=22.1.0", - "circus>=0.17.0,!=0.17.2", - "click>=7.0", - "click-option-group", - "cloudpickle", - "deepmerge", - "fs", - "numpy", - # OpenTelemetry is the server dependencies, rather than SDK - # Since there are discrepancies among API and instrumentation packages, - # we should always pin the set version of Opentelemetry suite - "opentelemetry-api==1.14.0", - "opentelemetry-sdk==1.14.0", - "opentelemetry-exporter-otlp-proto-http==1.14.0", - "opentelemetry-instrumentation==0.35b0", - "opentelemetry-instrumentation-aiohttp-client==0.35b0", - "opentelemetry-instrumentation-asgi==0.35b0", - "opentelemetry-semantic-conventions==0.35b0", - "opentelemetry-util-http==0.35b0", - "packaging>=22.0", - "pathspec", - "pip-tools>=6.6.2", - "pip-requirements-parser>=31.2.0", - "prometheus-client>=0.10.0", - "psutil", - "pynvml<12", - "python-dateutil", - "python-multipart", - "python-json-logger", - "requests", - "rich>=11.2.0", - "schema", - "simple-di>=0.1.4", - "starlette", - "uvicorn", - "watchfiles>=0.15.0", - "backports.cached-property;python_version<'3.8'", - "backports.shutil_copytree;python_version<'3.8'", - "importlib-metadata;python_version<'3.8'", - "singledispatchmethod;python_version<'3.8'", + "Jinja2>=3.0.1", + "PyYAML>=5.0", + "aiohttp", + "attrs>=21.1.0", + "cattrs>=22.1.0", + "circus>=0.17.0,!=0.17.2", + "click>=7.0", + "click-option-group", + "cloudpickle", + "deepmerge", + "fs", + "numpy", + # OpenTelemetry is the server dependencies, rather than SDK + # Since there are discrepancies among API and instrumentation packages, + # we should always pin the set version of Opentelemetry suite + "opentelemetry-api==1.14.0", + "opentelemetry-sdk==1.14.0", + "opentelemetry-exporter-otlp-proto-http==1.14.0", + "opentelemetry-instrumentation==0.35b0", + "opentelemetry-instrumentation-aiohttp-client==0.35b0", + "opentelemetry-instrumentation-asgi==0.35b0", + "opentelemetry-semantic-conventions==0.35b0", + "opentelemetry-util-http==0.35b0", + "packaging>=22.0", + "pathspec", + "pip-tools>=6.6.2", + "pip-requirements-parser>=31.2.0", + "prometheus-client>=0.10.0", + "psutil", + "pynvml<12", + "python-dateutil", + "python-multipart", + "python-json-logger", + "requests", + "rich>=11.2.0", + "schema", + "simple-di>=0.1.4", + "starlette", + "uvicorn", + "watchfiles>=0.15.0", + "backports.cached-property;python_version<'3.8'", + "backports.shutil_copytree;python_version<'3.8'", + "importlib-metadata;python_version<'3.8'", + "singledispatchmethod;python_version<'3.8'", ] dynamic = ["version"] @@ -87,14 +87,14 @@ bentoml_cli = ["bentoml_cli/*"] [tool.setuptools.exclude-package-data] "*" = [ - ".git*", - ".bazel*", - "BUILD.bazel", - "WORKSPACE", - "*.md", - "Makefile", - "*.py[cod]", - "typings", + ".git*", + ".bazel*", + "BUILD.bazel", + "WORKSPACE", + "*.md", + "Makefile", + "*.py[cod]", + "typings", ] bentoml = ["grpc/buf.yaml", "_internal/frameworks/FRAMEWORK_TEMPLATE_PY"] bentoml_cli = ["worker/README.md"] @@ -102,41 +102,41 @@ bentoml_cli = ["worker/README.md"] [tool.setuptools.packages.find] where = ["src"] include = [ - # include bentoml packages - "bentoml", - "bentoml.grpc*", - "bentoml.testing*", - "bentoml._internal*", - # include bentoml_cli packages - "bentoml_cli", - "bentoml_cli.worker", + # include bentoml packages + "bentoml", + "bentoml.grpc*", + "bentoml.testing*", + "bentoml._internal*", + # include bentoml_cli packages + "bentoml_cli", + "bentoml_cli.worker", ] [project.optional-dependencies] all = [ - "bentoml[aws]", - "bentoml[io]", - "bentoml[grpc]", - "bentoml[grpc-reflection]", - "bentoml[grpc-channelz]", - "bentoml[tracing]", + "bentoml[aws]", + "bentoml[io]", + "bentoml[grpc]", + "bentoml[grpc-reflection]", + "bentoml[grpc-channelz]", + "bentoml[tracing]", ] aws = ["fs-s3fs"] io = [ - "bentoml[io-json]", - "bentoml[io-image]", - "bentoml[io-pandas]", - "bentoml[io-file]", + "bentoml[io-json]", + "bentoml[io-image]", + "bentoml[io-pandas]", + "bentoml[io-file]", ] # syntatic sugar for bentoml[io-json,io-image,io-pandas,io-file] io-file = ["filetype"] # Currently use for from_sample io-json = ["pydantic<2"] # currently we don't have support for pydantic 2.0 io-image = ["bentoml[io-file]", "Pillow"] io-pandas = ["pandas", "pyarrow"] grpc = [ - "protobuf", - "grpcio", - "grpcio-health-checking", - "opentelemetry-instrumentation-grpc==0.35b0", + "protobuf", + "grpcio", + "grpcio-health-checking", + "opentelemetry-instrumentation-grpc==0.35b0", ] grpc-reflection = ["bentoml[grpc]", "grpcio-reflection"] grpc-channelz = ["bentoml[grpc]", "grpcio-channelz"] @@ -144,9 +144,9 @@ grpc-channelz = ["bentoml[grpc]", "grpcio-channelz"] # versions of BentoML. It is discouraged to use this, instead use any # of the above tracing.* extras. tracing = [ - "bentoml[tracing-jaeger]", - "bentoml[tracing-otlp]", - "bentoml[tracing-zipkin]", + "bentoml[tracing-jaeger]", + "bentoml[tracing-otlp]", + "bentoml[tracing-zipkin]", ] tracing-jaeger = ["opentelemetry-exporter-jaeger==1.14.0"] tracing-zipkin = ["opentelemetry-exporter-zipkin==1.14.0"] @@ -166,46 +166,46 @@ branch = true parallel = true source = ["src/bentoml/"] omit = [ - "src/bentoml/__main__.py", - "src/bentoml/io.py", - "src/bentoml/serve.py", - "src/bentoml/start.py", - "src/bentoml/_internal/types.py", - "src/bentoml/testing/*", - "src/bentoml/grpc/v1alpha1/*", - "src/bentoml/grpc/v1/*", - "src/bentoml/_internal/external_typing/*", - "src/bentoml/_internal/yatai_client/*", - "src/bentoml/_internal/yatai_rest_api_client/*", + "src/bentoml/__main__.py", + "src/bentoml/io.py", + "src/bentoml/serve.py", + "src/bentoml/start.py", + "src/bentoml/_internal/types.py", + "src/bentoml/testing/*", + "src/bentoml/grpc/v1alpha1/*", + "src/bentoml/grpc/v1/*", + "src/bentoml/_internal/external_typing/*", + "src/bentoml/_internal/yatai_client/*", + "src/bentoml/_internal/yatai_rest_api_client/*", ] [tool.coverage.report] show_missing = true precision = 2 omit = [ - 'src/bentoml/__main__.py', - "src/bentoml/io.py", - "src/bentoml/serve.py", - "src/bentoml/start.py", - "src/bentoml/_internal/types.py", - "src/bentoml/testing/*", - "src/bentoml/grpc/v1alpha1/*", - "src/bentoml/grpc/v1/*", - "src/bentoml/_internal/external_typing/*", - "src/bentoml/_internal/yatai_client/*", - "src/bentoml/_internal/yatai_rest_api_client/*", + 'src/bentoml/__main__.py', + "src/bentoml/io.py", + "src/bentoml/serve.py", + "src/bentoml/start.py", + "src/bentoml/_internal/types.py", + "src/bentoml/testing/*", + "src/bentoml/grpc/v1alpha1/*", + "src/bentoml/grpc/v1/*", + "src/bentoml/_internal/external_typing/*", + "src/bentoml/_internal/yatai_client/*", + "src/bentoml/_internal/yatai_rest_api_client/*", ] exclude_lines = [ - "\\#\\s*pragma: no cover", - "^\\s*def __repr__", - "^\\s*raise AssertionError", - "^\\s*raise NotImplementedError", - "^\\s*raise MissingDependencyException", - "^\\s*except ImportError", - "if __name__ == .__main__.:", - "^\\s*if TYPE_CHECKING:", - "^\\s*@(t\\.)?overload( |$)", - "@(abc\\.)?abstractmethod", + "\\#\\s*pragma: no cover", + "^\\s*def __repr__", + "^\\s*raise AssertionError", + "^\\s*raise NotImplementedError", + "^\\s*raise MissingDependencyException", + "^\\s*except ImportError", + "if __name__ == .__main__.:", + "^\\s*if TYPE_CHECKING:", + "^\\s*@(t\\.)?overload( |$)", + "@(abc\\.)?abstractmethod", ] [tool.black] @@ -240,53 +240,32 @@ addopts = ["-rfEX", "-pbentoml.testing.pytest.plugin"] python_files = ["test_*.py", "*_test.py"] testpaths = ["tests"] -[tool.pylint.main] -recursive = true -extension-pkg-allow-list = [ - "numpy", - "tensorflow", - "torch", - "paddle", - "onnxruntime", - "onnx", - "pydantic.schema", -] -ignore-paths = [ - "src/bentoml/_internal/external_typing", - "src/bentoml/grpc/v1alpha1", - "src/bentoml/grpc/v1", +[tool.ruff] +# similar to black's +line-length = 88 +# Never enforce `E501` (line length violations). +ignore = ["E501", "E741", "W605"] +exclude = [ + "bazel-*/", + "venv", + "typings", + "src/bentoml/_internal/external_typing", + "src/bentoml/grpc/v1alpha1", + "src/bentoml/grpc/v1", + "tests/proto", ] -disable = ["coerce-builtin", "no-absolute-import", "C", "R"] -enable = ["c-extension-no-member"] -evaluation = "10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)" -msg-template = "{msg_id}:{symbol} [{line:0>3d}:{column:0>2d}] {obj}: {msg}" -output-format = "colorized" -score = true - -[tool.pylint.classes] -valid-metaclass-classmethod-first-arg = ["cls", "mcls", "kls"] +target-version = "py310" +fix = true -[tool.pylint.logging] -logging-format-style = "old" # using %s formatter for logging (performance-related) +[tool.ruff.per-file-ignores] +# ignore imports violations in __init__.py +"__init__.py" = ["E402"] -[tool.pylint.miscellaneous] -notes = ["FIXME", "XXX", "TODO", "NOTE", "WARNING"] +[tool.ruff.pydocstyle] +convention = "google" -[tool.pylint.refactoring] -# specify functions that should not return -never-returning-functions = ["sys.exit"] - -[tool.pylint.spelling] -spelling-ignore-comment-directives = "fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy:,pylint:,type:" - -[tool.pylint.variables] -init-import = true - -[tool.pylint.typecheck] -contextmanager-decorators = [ - "contextlib.contextmanager", - "bentoml._internal.utils.cached_contextmanager", -] +[tool.ruff.isort] +lines-after-imports = 2 [tool.isort] profile = "black" @@ -294,31 +273,31 @@ line_length = 88 length_sort = true force_single_line = true order_by_type = true -known_first_party = "bentoml" +known_first_party = ["bentoml"] force_alphabetical_sort_within_sections = true skip_glob = [ - "typings/*", - "test/*", - "**/*_pb2*", - "venv/*", - "lib/*", - "grpc-client/thirdparty", - "grpc-client/bentoml", - "bazel-*", + "typings/*", + "test/*", + "**/*_pb2*", + "venv/*", + "lib/*", + "grpc-client/thirdparty", + "grpc-client/bentoml", + "bazel-*", ] [tool.pyright] pythonVersion = "3.11" include = ["src/", "examples/", "tests/"] exclude = [ - 'src/bentoml/_version.py', - 'src/bentoml/__main__.py', - 'src/bentoml/_internal/external_typing/', - 'src/**/*_pb2.py*', - "src/**/*_pb2_grpc.py*", - "grpc-client/thirdparty", - "tests/proto", - "bazel-*", + 'src/bentoml/_version.py', + 'src/bentoml/__main__.py', + 'src/bentoml/_internal/external_typing/', + 'src/**/*_pb2.py*', + "src/**/*_pb2_grpc.py*", + "grpc-client/thirdparty", + "tests/proto", + "bazel-*", ] analysis.useLibraryCodeForTypes = true strictListInference = true diff --git a/requirements/tests-requirements.txt b/requirements/tests-requirements.txt index c2d5c2d27f6..b4743135a4c 100644 --- a/requirements/tests-requirements.txt +++ b/requirements/tests-requirements.txt @@ -8,7 +8,7 @@ codecov==2.1.12 coverage[toml]==7.1.0 setuptools>=63 isort==5.11.4 -pylint==2.16.0 +ruff==0.0.243 pytest-cov==4.0.0 pytest==7.2.1 pytest-xdist[psutil]==3.1.0 From 28b4d9ed69bcf2e7f729c838afd14ba160037763 Mon Sep 17 00:00:00 2001 From: Aaron Pham <29749331+aarnphm@users.noreply.github.com> Date: Wed, 8 Feb 2023 08:10:15 -0800 Subject: [PATCH 02/10] tools: running `ruff check --fix` [generated] Signed-off-by: Aaron Pham <29749331+aarnphm@users.noreply.github.com> --- .../custom_runner/nltk_pretrained_model/service.py | 1 - src/bentoml/_internal/configuration/helpers.py | 2 +- src/bentoml/_internal/container/generate.py | 9 +++++++-- src/bentoml/_internal/frameworks/fastai.py | 2 +- src/bentoml/_internal/io_descriptors/json.py | 1 - src/bentoml/_internal/io_descriptors/pandas.py | 2 +- .../_internal/runner/runner_handle/remote.py | 6 +++--- src/bentoml/_internal/server/metrics/prometheus.py | 1 - src/bentoml/_internal/service/openapi/__init__.py | 1 - src/bentoml/container.py | 1 - src/bentoml_cli/containerize.py | 2 +- src/bentoml_cli/models.py | 1 - tests/e2e/bento_server_grpc/service.py | 4 +--- .../tests/test_custom_components.py | 1 - tests/integration/frameworks/mlflow/test_apis.py | 2 +- tests/integration/frameworks/models/__init__.py | 14 +++++++++++--- tests/integration/frameworks/models/onnx.py | 2 -- 17 files changed, 27 insertions(+), 25 deletions(-) diff --git a/examples/custom_runner/nltk_pretrained_model/service.py b/examples/custom_runner/nltk_pretrained_model/service.py index d396caf9d90..a3adb6a70e1 100644 --- a/examples/custom_runner/nltk_pretrained_model/service.py +++ b/examples/custom_runner/nltk_pretrained_model/service.py @@ -6,7 +6,6 @@ from statistics import mean import nltk -from utils import exponential_buckets from nltk.sentiment import SentimentIntensityAnalyzer import bentoml diff --git a/src/bentoml/_internal/configuration/helpers.py b/src/bentoml/_internal/configuration/helpers.py index 997eeca61ae..976314e196d 100644 --- a/src/bentoml/_internal/configuration/helpers.py +++ b/src/bentoml/_internal/configuration/helpers.py @@ -26,7 +26,7 @@ def import_configuration_spec(version: int) -> ModuleType: # pragma: no cover f"v{version}", globals(), f"bentoml._internal.configuration.v{version}", - exc_msg=f"Configuration version %d does not exists." % version, + exc_msg="Configuration version %d does not exists." % version, ) diff --git a/src/bentoml/_internal/container/generate.py b/src/bentoml/_internal/container/generate.py index 885b1da9b68..fb5bce72b8b 100644 --- a/src/bentoml/_internal/container/generate.py +++ b/src/bentoml/_internal/container/generate.py @@ -43,8 +43,13 @@ def expands_bento_path(*path: str, bento_path: str = BENTO_PATH) -> str: J2_FUNCTION: dict[str, F[t.Any]] = {"expands_bento_path": expands_bento_path} -to_bento_field: t.Callable[[str], str] = lambda s: f"bento__{s}" -to_options_field: t.Callable[[str], str] = lambda s: f"__options__{s}" + +def to_bento_field(s: str): + return f"bento__{s}" + + +def to_options_field(s: str): + return f"__options__{s}" def get_templates_variables( diff --git a/src/bentoml/_internal/frameworks/fastai.py b/src/bentoml/_internal/frameworks/fastai.py index 56f8ca9325d..57ad2ee0c21 100644 --- a/src/bentoml/_internal/frameworks/fastai.py +++ b/src/bentoml/_internal/frameworks/fastai.py @@ -47,7 +47,7 @@ ) try: - import fastai.basics as _ + import fastai.basics as _ # noqa except ImportError: # pragma: no cover raise MissingDependencyException("BentoML only supports fastai v2 onwards.") diff --git a/src/bentoml/_internal/io_descriptors/json.py b/src/bentoml/_internal/io_descriptors/json.py index 2adbea6f631..3615c7d488b 100644 --- a/src/bentoml/_internal/io_descriptors/json.py +++ b/src/bentoml/_internal/io_descriptors/json.py @@ -34,7 +34,6 @@ from google.protobuf import struct_pb2 from typing_extensions import Self - from .. import external_typing as ext from .base import OpenAPIResponse from ..context import InferenceApiContext as Context diff --git a/src/bentoml/_internal/io_descriptors/pandas.py b/src/bentoml/_internal/io_descriptors/pandas.py index 2c1eacb07c9..5dd22b12888 100644 --- a/src/bentoml/_internal/io_descriptors/pandas.py +++ b/src/bentoml/_internal/io_descriptors/pandas.py @@ -1133,7 +1133,7 @@ async def to_proto(self, obj: ext.PdSeries) -> pb.Series: # User shouldn't use mixed dtype in the first place. if obj.dtype.kind == "O": raise InvalidArgument( - f"Series has mixed dtype. Please convert it to a single dtype." + "Series has mixed dtype. Please convert it to a single dtype." ) from None try: fieldpb = npdtype_to_fieldpb_map()[obj.dtype] diff --git a/src/bentoml/_internal/runner/runner_handle/remote.py b/src/bentoml/_internal/runner/runner_handle/remote.py index eaca1a80c01..cebe540244f 100644 --- a/src/bentoml/_internal/runner/runner_handle/remote.py +++ b/src/bentoml/_internal/runner/runner_handle/remote.py @@ -202,10 +202,10 @@ async def async_run_method( }, ) as resp: body = await resp.read() - except aiohttp.ClientOSError as e: - raise RemoteException(f"Failed to connect to runner server.") + except aiohttp.ClientOSError: + raise RemoteException("Failed to connect to runner server.") else: - raise RemoteException(f"Failed to connect to runner server.") from e + raise RemoteException("Failed to connect to runner server.") from e try: content_type = resp.headers["Content-Type"] diff --git a/src/bentoml/_internal/server/metrics/prometheus.py b/src/bentoml/_internal/server/metrics/prometheus.py index 830a99ce709..d466864f627 100644 --- a/src/bentoml/_internal/server/metrics/prometheus.py +++ b/src/bentoml/_internal/server/metrics/prometheus.py @@ -8,7 +8,6 @@ from functools import partial if TYPE_CHECKING: - from prometheus_client.metrics_core import Metric from ... import external_typing as ext diff --git a/src/bentoml/_internal/service/openapi/__init__.py b/src/bentoml/_internal/service/openapi/__init__.py index a95df146219..0b02dd3f0ee 100644 --- a/src/bentoml/_internal/service/openapi/__init__.py +++ b/src/bentoml/_internal/service/openapi/__init__.py @@ -28,7 +28,6 @@ from .specification import OpenAPISpecification if TYPE_CHECKING: - import fastapi from .. import Service from ..inference_api import InferenceAPI diff --git a/src/bentoml/container.py b/src/bentoml/container.py index 785817cddd1..1823d5c33d1 100644 --- a/src/bentoml/container.py +++ b/src/bentoml/container.py @@ -11,7 +11,6 @@ import logging from typing import TYPE_CHECKING -import psutil from simple_di import inject from simple_di import Provide diff --git a/src/bentoml_cli/containerize.py b/src/bentoml_cli/containerize.py index 4c1d1c3874a..3ecea898962 100644 --- a/src/bentoml_cli/containerize.py +++ b/src/bentoml_cli/containerize.py @@ -547,7 +547,7 @@ def containerize( # type: ignore f'Successfully built Bento container for "{bento_tag}" with tag(s) "{",".join(tags)}"', ) instructions = ( - f"To run your newly built Bento container, run:\n" + "To run your newly built Bento container, run:\n" + f" {container_runtime} run -it --rm -p 3000:3000 {tags[0]} serve --production\n" ) diff --git a/src/bentoml_cli/models.py b/src/bentoml_cli/models.py index e52658e1c49..aab5413283f 100644 --- a/src/bentoml_cli/models.py +++ b/src/bentoml_cli/models.py @@ -2,7 +2,6 @@ import json import typing as t -import logging from typing import TYPE_CHECKING import yaml diff --git a/tests/e2e/bento_server_grpc/service.py b/tests/e2e/bento_server_grpc/service.py index df7997b174d..97e46847624 100644 --- a/tests/e2e/bento_server_grpc/service.py +++ b/tests/e2e/bento_server_grpc/service.py @@ -50,8 +50,6 @@ class PythonModelRunner(bentoml.Runner): echo_dataframe: RunnerMethod[RunnableImpl, [pd.DataFrame], pd.DataFrame] else: - from bentoml.grpc.utils import import_generated_stubs - np = LazyLoader("np", globals(), "numpy") pd = LazyLoader("pd", globals(), "pandas") PIL = LazyLoader("PIL", globals(), "PIL") @@ -200,7 +198,7 @@ async def predict_multi_images(original: Image, compared: Image): @svc.api(input=bentoml.io.Text(), output=bentoml.io.Text()) -def ensure_metrics_are_registered(data: str) -> str: # pylint: disable=unused-argument +def ensure_metrics_are_registered(_: str) -> None: histograms = [ m.name for m in bentoml.metrics.text_string_to_metric_families() diff --git a/tests/e2e/bento_server_grpc/tests/test_custom_components.py b/tests/e2e/bento_server_grpc/tests/test_custom_components.py index 7e837d7eac1..268e4777888 100644 --- a/tests/e2e/bento_server_grpc/tests/test_custom_components.py +++ b/tests/e2e/bento_server_grpc/tests/test_custom_components.py @@ -7,7 +7,6 @@ from grpc_health.v1 import health_pb2 as pb_health from google.protobuf import wrappers_pb2 -from bentoml.grpc.v1 import service_pb2 as pb from bentoml.testing.grpc import create_channel from bentoml.testing.grpc import async_client_call diff --git a/tests/integration/frameworks/mlflow/test_apis.py b/tests/integration/frameworks/mlflow/test_apis.py index 39aa29c5818..54c64195117 100644 --- a/tests/integration/frameworks/mlflow/test_apis.py +++ b/tests/integration/frameworks/mlflow/test_apis.py @@ -130,7 +130,7 @@ def test_invalid_load(no_mlmodel: Tag): def test_invalid_signatures_model(URI: Path): with pytest.raises( BentoMLException, - match=f"MLflow pyfunc model support only the `predict` method, *", + match="MLflow pyfunc model support only the `predict` method, *", ): _ = bentoml.mlflow.import_model( MODEL_NAME, diff --git a/tests/integration/frameworks/models/__init__.py b/tests/integration/frameworks/models/__init__.py index af5552809d2..54df3305a2a 100644 --- a/tests/integration/frameworks/models/__init__.py +++ b/tests/integration/frameworks/models/__init__.py @@ -28,8 +28,14 @@ class FrameworkTestModel: class FrameworkTestModelConfiguration: test_inputs: dict[str, list[FrameworkTestModelInput]] load_kwargs: dict[str, t.Any] = attr.Factory(dict) - check_model: t.Callable[[t.Any, dict[str, t.Any]], None] = lambda _, __: None - check_runnable: t.Callable[[t.Any, dict[str, t.Any]], None] = lambda _, __: None + + @staticmethod + def check_model(_, __): + return None + + @staticmethod + def check_runnable(_, __): + return None @attr.define @@ -38,7 +44,9 @@ class FrameworkTestModelInput: expected: t.Any | t.Callable[[t.Any], bool] input_kwargs: dict[str, t.Any] = attr.Factory(dict) - preprocess: t.Callable[[t.Any], t.Any] = lambda v: v + @staticmethod + def preprocess(v: t.Any): + return v def check_output(self, outp: t.Any): if isinstance(self.expected, t.Callable): diff --git a/tests/integration/frameworks/models/onnx.py b/tests/integration/frameworks/models/onnx.py index 8f53492fe5b..c537e95eb7f 100644 --- a/tests/integration/frameworks/models/onnx.py +++ b/tests/integration/frameworks/models/onnx.py @@ -3,7 +3,6 @@ import os import typing as t import tempfile -from typing import Callable from typing import TYPE_CHECKING import onnx @@ -12,7 +11,6 @@ import sklearn import torch.nn as nn import onnxruntime as ort -from pytest import CallInfo from skl2onnx import convert_sklearn from sklearn.datasets import load_iris from sklearn.ensemble import RandomForestClassifier From dcff2fcdd325f02ce6f3fd71f9337762347d3251 Mon Sep 17 00:00:00 2001 From: Aaron Pham <29749331+aarnphm@users.noreply.github.com> Date: Wed, 8 Feb 2023 09:26:12 -0800 Subject: [PATCH 03/10] chore: revert changes from upstream/main reverted changes: ./tests/integration/frameworks/models/__init__.py Signed-off-by: Aaron Pham <29749331+aarnphm@users.noreply.github.com> --- tests/integration/frameworks/models/__init__.py | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/tests/integration/frameworks/models/__init__.py b/tests/integration/frameworks/models/__init__.py index 54df3305a2a..af5552809d2 100644 --- a/tests/integration/frameworks/models/__init__.py +++ b/tests/integration/frameworks/models/__init__.py @@ -28,14 +28,8 @@ class FrameworkTestModel: class FrameworkTestModelConfiguration: test_inputs: dict[str, list[FrameworkTestModelInput]] load_kwargs: dict[str, t.Any] = attr.Factory(dict) - - @staticmethod - def check_model(_, __): - return None - - @staticmethod - def check_runnable(_, __): - return None + check_model: t.Callable[[t.Any, dict[str, t.Any]], None] = lambda _, __: None + check_runnable: t.Callable[[t.Any, dict[str, t.Any]], None] = lambda _, __: None @attr.define @@ -44,9 +38,7 @@ class FrameworkTestModelInput: expected: t.Any | t.Callable[[t.Any], bool] input_kwargs: dict[str, t.Any] = attr.Factory(dict) - @staticmethod - def preprocess(v: t.Any): - return v + preprocess: t.Callable[[t.Any], t.Any] = lambda v: v def check_output(self, outp: t.Any): if isinstance(self.expected, t.Callable): From 47ad8b6844501a1e785e50bcfe720f2a79bf0892 Mon Sep 17 00:00:00 2001 From: Aaron Pham <29749331+aarnphm@users.noreply.github.com> Date: Wed, 8 Feb 2023 09:28:23 -0800 Subject: [PATCH 04/10] chore: ignore do-not-assign-lambda Signed-off-by: Aaron Pham <29749331+aarnphm@users.noreply.github.com> --- tests/integration/frameworks/models/__init__.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/integration/frameworks/models/__init__.py b/tests/integration/frameworks/models/__init__.py index af5552809d2..6ee9dca26ff 100644 --- a/tests/integration/frameworks/models/__init__.py +++ b/tests/integration/frameworks/models/__init__.py @@ -28,8 +28,12 @@ class FrameworkTestModel: class FrameworkTestModelConfiguration: test_inputs: dict[str, list[FrameworkTestModelInput]] load_kwargs: dict[str, t.Any] = attr.Factory(dict) - check_model: t.Callable[[t.Any, dict[str, t.Any]], None] = lambda _, __: None - check_runnable: t.Callable[[t.Any, dict[str, t.Any]], None] = lambda _, __: None + check_model: t.Callable[ # noqa: E731 + [t.Any, dict[str, t.Any]], None + ] = lambda _, __: None + check_runnable: t.Callable[ # noqa: E731 + [t.Any, dict[str, t.Any]], None + ] = lambda _, __: None @attr.define @@ -38,7 +42,7 @@ class FrameworkTestModelInput: expected: t.Any | t.Callable[[t.Any], bool] input_kwargs: dict[str, t.Any] = attr.Factory(dict) - preprocess: t.Callable[[t.Any], t.Any] = lambda v: v + preprocess: t.Callable[[t.Any], t.Any] = lambda v: v # noqa: E731 def check_output(self, outp: t.Any): if isinstance(self.expected, t.Callable): From c1428096d1f5cee080dfbc5b2f9ba9be3386090c Mon Sep 17 00:00:00 2001 From: Aaron Pham <29749331+aarnphm@users.noreply.github.com> Date: Wed, 8 Feb 2023 09:35:09 -0800 Subject: [PATCH 05/10] fix: lint Signed-off-by: Aaron Pham <29749331+aarnphm@users.noreply.github.com> --- examples/custom_model_runner/train.py | 6 +++--- src/bentoml/_internal/batch/spark.py | 4 +--- src/bentoml/client.py | 3 +++ src/bentoml/metrics.py | 3 +-- tests/e2e/bento_server_http/service.py | 3 +-- tests/e2e/bento_server_http/tests/test_io.py | 2 +- tests/integration/frameworks/models/pytorch_lightning.py | 9 +++++---- tests/integration/frameworks/models/tensorflow.py | 5 +++++ tests/integration/frameworks/models/torchscript.py | 2 +- 9 files changed, 21 insertions(+), 16 deletions(-) diff --git a/examples/custom_model_runner/train.py b/examples/custom_model_runner/train.py index 3b945c80429..9c3729fb557 100644 --- a/examples/custom_model_runner/train.py +++ b/examples/custom_model_runner/train.py @@ -5,10 +5,10 @@ import net import torch import torch.optim as optim -import torch.utils.data as data import torch.nn.functional as F from torchvision import datasets from torchvision import transforms +from torch.utils.data import DataLoader from torch.optim.lr_scheduler import StepLR @@ -139,8 +139,8 @@ def main(): ) train_ds = datasets.MNIST("data", train=True, download=True, transform=transform) test_ds = datasets.MNIST("data", train=False, transform=transform) - train_loader = data.DataLoader(train_ds, **train_kwargs) - test_loader = data.DataLoader(test_ds, **test_kwargs) + train_loader = DataLoader(train_ds, **train_kwargs) + test_loader = DataLoader(test_ds, **test_kwargs) model = net.CNN().to(device) optimizer = optim.Adadelta(model.parameters(), lr=args.lr) diff --git a/src/bentoml/_internal/batch/spark.py b/src/bentoml/_internal/batch/spark.py index 597ff2a5c1f..671228d8a48 100644 --- a/src/bentoml/_internal/batch/spark.py +++ b/src/bentoml/_internal/batch/spark.py @@ -36,7 +36,7 @@ logger = logging.getLogger(__name__) -def _distribute_bento(spark: SparkSession, bento: Bento) -> str: +def _distribute_bento(spark: pyspark.sql.session.SparkSession, bento: Bento) -> str: temp_dir = tempfile.mkdtemp() export_path = bento.export(temp_dir) spark.sparkContext.addFile(export_path) @@ -50,7 +50,6 @@ def _load_bento_spark(bento_tag: Tag): try: return load_bento(bento_tag) except Exception: - # Use the default Bento export file name. This relies on the implementation # of _distribute_bento to use default Bento export file name. bento_path = SparkFiles.get(f"{bento_tag.name}-{bento_tag.version}.bento") @@ -67,7 +66,6 @@ def _get_process( def process( iterator: t.Iterable[RecordBatch], ) -> t.Generator[RecordBatch, None, None]: - svc = _load_bento_spark(bento_tag) assert ( diff --git a/src/bentoml/client.py b/src/bentoml/client.py index e6eef78bd65..45420d3a6c3 100644 --- a/src/bentoml/client.py +++ b/src/bentoml/client.py @@ -3,6 +3,7 @@ import json import typing as t import asyncio +import logging import functools from abc import ABC from abc import abstractmethod @@ -23,6 +24,8 @@ if t.TYPE_CHECKING: from types import TracebackType +logger = logging.getLogger(__name__) + class Client(ABC): server_url: str diff --git a/src/bentoml/metrics.py b/src/bentoml/metrics.py index 963a8ceb51a..15efa7f05af 100644 --- a/src/bentoml/metrics.py +++ b/src/bentoml/metrics.py @@ -9,14 +9,13 @@ from ._internal.utils import warn_experimental from ._internal.utils import add_experimental_docstring +from ._internal.configuration.containers import BentoMLContainer if TYPE_CHECKING: from ._internal.server.metrics.prometheus import PrometheusClient logger = logging.getLogger(__name__) -from ._internal.configuration.containers import BentoMLContainer - # NOTE: We have to set our docstring here due to the fact that # we are lazy loading the metrics. This means that the docstring # won't be discovered until the metrics is initialized. diff --git a/tests/e2e/bento_server_http/service.py b/tests/e2e/bento_server_http/service.py index 7d4a2ea4cb4..ce97bf579d5 100644 --- a/tests/e2e/bento_server_http/service.py +++ b/tests/e2e/bento_server_http/service.py @@ -6,6 +6,7 @@ import numpy as np import pandas as pd import pydantic +from fastapi import FastAPI from PIL.Image import Image as PILImage from PIL.Image import fromarray from starlette.requests import Request @@ -190,8 +191,6 @@ async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: svc.add_asgi_middleware(AllowPingMiddleware) # type: ignore (hint not yet supported for hooks) -from fastapi import FastAPI - fastapi_app = FastAPI() diff --git a/tests/e2e/bento_server_http/tests/test_io.py b/tests/e2e/bento_server_http/tests/test_io.py index 68fdb7e7acd..2eacd9a1197 100644 --- a/tests/e2e/bento_server_http/tests/test_io.py +++ b/tests/e2e/bento_server_http/tests/test_io.py @@ -250,7 +250,7 @@ async def test_multipart_image_io(host: str, img_form_data: aiohttp.FormData): @pytest.mark.asyncio -async def test_multipart_image_io(host: str, img_form_data: aiohttp.FormData): +async def test_multipart_different_args(host: str, img_form_data: aiohttp.FormData): await async_request( "POST", f"http://{host}/predict_different_args", diff --git a/tests/integration/frameworks/models/pytorch_lightning.py b/tests/integration/frameworks/models/pytorch_lightning.py index 59deb754a41..b17941afd69 100644 --- a/tests/integration/frameworks/models/pytorch_lightning.py +++ b/tests/integration/frameworks/models/pytorch_lightning.py @@ -1,5 +1,7 @@ from __future__ import annotations +import typing as t + import torch import torch.nn import pytorch_lightning as pl @@ -9,14 +11,13 @@ from . import FrameworkTestModel from . import FrameworkTestModelInput as Input from . import FrameworkTestModelConfiguration as Config +from .torchscript import test_y +from .torchscript import test_x_list framework = bentoml.pytorch_lightning backward_compatible = True -from .torchscript import test_y -from .torchscript import test_x_list - def generate_models(): class LightningLinearModel(pl.LightningModule): @@ -25,7 +26,7 @@ def __init__(self): self.linear = torch.nn.Linear(5, 1, bias=False) torch.nn.init.ones_(self.linear.weight) - def forward(self, x): + def forward(self, x: t.Any): return self.linear(x) yield LightningLinearModel() diff --git a/tests/integration/frameworks/models/tensorflow.py b/tests/integration/frameworks/models/tensorflow.py index dc729756656..9c2269abe96 100644 --- a/tests/integration/frameworks/models/tensorflow.py +++ b/tests/integration/frameworks/models/tensorflow.py @@ -1,5 +1,7 @@ from __future__ import annotations +import typing as t + import keras import numpy as np import tensorflow as tf @@ -10,6 +12,9 @@ from . import FrameworkTestModelInput as Input from . import FrameworkTestModelConfiguration as Config +if t.TYPE_CHECKING: + from bentoml._internal.external_typing import tensorflow as tf_ext + framework = bentoml.tensorflow backward_compatible = True diff --git a/tests/integration/frameworks/models/torchscript.py b/tests/integration/frameworks/models/torchscript.py index 83e22312d61..bdaa0170167 100644 --- a/tests/integration/frameworks/models/torchscript.py +++ b/tests/integration/frameworks/models/torchscript.py @@ -24,7 +24,7 @@ if torch.cuda.is_available(): - torch_x_list.append(torch.Tensor(test_x_nda, device="cuda")) + test_x_list.append(torch.Tensor(test_x_nda, device="cuda")) def generate_models(): From 8651eee1987460a14010091c36fbb2f01d1e2b87 Mon Sep 17 00:00:00 2001 From: Aaron Pham <29749331+aarnphm@users.noreply.github.com> Date: Wed, 8 Feb 2023 13:23:40 -0800 Subject: [PATCH 06/10] chore: update format Co-authored-by: Sauyon Lee <2347889+sauyon@users.noreply.github.com> --- src/bentoml/_internal/configuration/helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bentoml/_internal/configuration/helpers.py b/src/bentoml/_internal/configuration/helpers.py index 976314e196d..24a1c6343fc 100644 --- a/src/bentoml/_internal/configuration/helpers.py +++ b/src/bentoml/_internal/configuration/helpers.py @@ -26,7 +26,7 @@ def import_configuration_spec(version: int) -> ModuleType: # pragma: no cover f"v{version}", globals(), f"bentoml._internal.configuration.v{version}", - exc_msg="Configuration version %d does not exists." % version, + exc_msg=f"Configuration version {version} does not exist.", ) From 04ffa278c779086201f61110721e0e8c51349244 Mon Sep 17 00:00:00 2001 From: Aaron Pham <29749331+aarnphm@users.noreply.github.com> Date: Wed, 8 Feb 2023 13:29:28 -0800 Subject: [PATCH 07/10] chore: revert to check only on CI Signed-off-by: Aaron Pham <29749331+aarnphm@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- Makefile | 6 ++++-- pyproject.toml | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7be43c40e6d..3dd17e7e111 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -105,7 +105,7 @@ jobs: black --check --pyi typings isort --check . - name: Lint check - run: ruff check src tests examples --fix + run: ruff check src tests examples - name: Type check if: ${{ github.event_name == 'pull_request' }} run: git diff --name-only --diff-filter=d "origin/$GITHUB_BASE_REF" -z -- '*.py[i]' | xargs -0 --no-run-if-empty pyright diff --git a/Makefile b/Makefile index 059e72ef4f0..2b834e61dc4 100644 --- a/Makefile +++ b/Makefile @@ -16,13 +16,15 @@ format: ## Running code formatter: black and isort @echo "(black) Formatting stubs..." @find src -name "*.pyi" ! -name "*_pb2*" -exec black --pyi --config pyproject.toml {} \; @echo "(isort) Reordering imports..." - isort . + @isort . + @echo "(ruff) Running fix only..." + @ruff check src examples tests --fix-only format-proto: ## Running proto formatter: buf @echo "Formatting proto files..." docker run --init --rm --volume $(GIT_ROOT)/src:/workspace --workdir /workspace bufbuild/buf format --config "/workspace/bentoml/grpc/buf.yaml" -w bentoml/grpc lint: ## Running lint checker: ruff @echo "(ruff) Linting development project..." - @ruff check src examples tests --fix + @ruff check src examples tests lint-proto: ## Running proto lint checker: buf @echo "Linting proto files..." docker run --init --rm --volume $(GIT_ROOT)/src:/workspace --workdir /workspace bufbuild/buf lint --config "/workspace/bentoml/grpc/buf.yaml" --error-format msvs bentoml/grpc diff --git a/pyproject.toml b/pyproject.toml index 8017353b36a..72fcefc200c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -259,7 +259,7 @@ fix = true [tool.ruff.per-file-ignores] # ignore imports violations in __init__.py -"__init__.py" = ["E402"] +"src/bentoml/__init__.py" = ["E402"] [tool.ruff.pydocstyle] convention = "google" From 548077daebdfa5e17b64de07d4b14a874d7318a2 Mon Sep 17 00:00:00 2001 From: Aaron Pham <29749331+aarnphm@users.noreply.github.com> Date: Wed, 8 Feb 2023 17:01:57 -0800 Subject: [PATCH 08/10] chore: update inline comments for linter rules Signed-off-by: Aaron Pham <29749331+aarnphm@users.noreply.github.com> --- pyproject.toml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 72fcefc200c..4b36cc3a242 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -243,7 +243,10 @@ testpaths = ["tests"] [tool.ruff] # similar to black's line-length = 88 -# Never enforce `E501` (line length violations). +# E501: line too long +# E741: ambiguous variable name +# W605: invalid escape sequence +# tip: use 'ruff rule ' to see the description of a rule ignore = ["E501", "E741", "W605"] exclude = [ "bazel-*/", @@ -255,7 +258,6 @@ exclude = [ "tests/proto", ] target-version = "py310" -fix = true [tool.ruff.per-file-ignores] # ignore imports violations in __init__.py From 7eac6cf96722ac167810b00d00cfe2f63a92fcc4 Mon Sep 17 00:00:00 2001 From: Aaron Pham <29749331+aarnphm@users.noreply.github.com> Date: Wed, 8 Feb 2023 17:17:51 -0800 Subject: [PATCH 09/10] chore: ignore E501 only Signed-off-by: Aaron Pham <29749331+aarnphm@users.noreply.github.com> --- pyproject.toml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 4b36cc3a242..2724f71a8a1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -244,10 +244,8 @@ testpaths = ["tests"] # similar to black's line-length = 88 # E501: line too long -# E741: ambiguous variable name -# W605: invalid escape sequence # tip: use 'ruff rule ' to see the description of a rule -ignore = ["E501", "E741", "W605"] +ignore = ["E501"] exclude = [ "bazel-*/", "venv", From 1d4420b941840cde045a8e93a9cd224e015f08ad Mon Sep 17 00:00:00 2001 From: Aaron Pham <29749331+aarnphm@users.noreply.github.com> Date: Wed, 8 Feb 2023 19:45:18 -0800 Subject: [PATCH 10/10] chore: pyproject.toml Co-authored-by: Sauyon Lee <2347889+sauyon@users.noreply.github.com> --- pyproject.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 2724f71a8a1..e33e47ef293 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -243,8 +243,7 @@ testpaths = ["tests"] [tool.ruff] # similar to black's line-length = 88 -# E501: line too long -# tip: use 'ruff rule ' to see the description of a rule +# We ignore E501 (line too long) here because we keep user-visible strings on one line. ignore = ["E501"] exclude = [ "bazel-*/",