Skip to content

fix(client/grpc): use SyncGrpcClient._create_channel in sync paths#5601

Open
alvinttang wants to merge 1 commit intobentoml:mainfrom
alvinttang:fix/grpc-sync-client-create-channel
Open

fix(client/grpc): use SyncGrpcClient._create_channel in sync paths#5601
alvinttang wants to merge 1 commit intobentoml:mainfrom
alvinttang:fix/grpc-sync-client-create-channel

Conversation

@alvinttang
Copy link
Copy Markdown

Summary

SyncGrpcClient.from_url and SyncGrpcClient.wait_until_server_ready resolved _create_channel via the wrapper class GrpcClient, which only composes a sync + async grpc client and does not define _create_channel itself. Every sync-kind grpc client construction therefore raised:

AttributeError: type object 'GrpcClient' has no attribute '_create_channel'

before any RPC was attempted. Even if the lookup had fallen back to the async version, that returns grpc.aio.insecure_channel(...) which only supports async with — incompatible with the surrounding sync with block.

Fixes #4683

Root cause

Only the leaf classes SyncGrpcClient and AsyncGrpcClient define their own static _create_channel. GrpcClient (the wrapper) does not. The two sync paths referenced GrpcClient._create_channel directly.

Fix

src/bentoml/_internal/client/grpc.py:

  • SyncGrpcClient.from_url is a @classmethod, so it now uses cls._create_channel(...) — that resolves to SyncGrpcClient._create_channel for the base class and lets subclasses override the channel factory.
  • SyncGrpcClient.wait_until_server_ready is a @staticmethod (no cls), so it now hardcodes SyncGrpcClient._create_channel(...) — same observable behaviour as before, just calling a method that actually exists.

Tests

tests/unit/_internal/client/test_grpc_sync_create_channel.py:

  • test_grpc_client_has_no_create_channel — guards against future regressions that re-introduce the missing attribute on the wrapper class.
  • test_sync_grpc_client_create_channel_returns_sync_context_manager — the resolved helper must return a sync context manager.
  • test_wait_until_server_ready_does_not_raise_create_channel_attribute_errorwait_until_server_ready must not raise the original AttributeError when the helper is not present on the wrapper.
  • test_from_url_does_not_raise_create_channel_attribute_error — same for from_url.

Locally on darwin/arm64:

  • pytest tests/unit/_internal/client/test_grpc_sync_create_channel.py → 4 passed.
  • ruff check clean.

Risk notes

Two single-line call-site swaps inside SyncGrpcClient. Async paths already used AsyncGrpcClient._create_channel and are untouched. No public API change.

`SyncGrpcClient.from_url` and `SyncGrpcClient.wait_until_server_ready`
called `GrpcClient._create_channel`, but `GrpcClient` is a thin wrapper
that does not define `_create_channel`. Only `SyncGrpcClient` and
`AsyncGrpcClient` do. Every grpc-kind sync client construction crashed
with `AttributeError: type object 'GrpcClient' has no attribute
'_create_channel'` before any network call was attempted.

Fix: route sync code paths through `SyncGrpcClient._create_channel`,
which returns a sync `grpc.{insecure,secure}_channel` compatible with
the surrounding `with` block. The async paths already use the correct
`AsyncGrpcClient._create_channel`.

Adds a unit-level regression test covering both call sites and asserting
that `SyncGrpcClient._create_channel` returns a sync context manager.

Fixes bentoml#4683
@alvinttang alvinttang requested a review from a team as a code owner April 25, 2026 12:47
@alvinttang alvinttang requested review from aarnphm and removed request for a team April 25, 2026 12:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: AttributeError: type object 'GrpcClient' has no attribute '_create_channel'

1 participant