Skip to content
This repository was archived by the owner on Feb 23, 2026. It is now read-only.
This repository was archived by the owner on Feb 23, 2026. It is now read-only.

Test regression when grpcio is installed but grpcio-status is not #301

Description

@mgorny

CC @atulep

The following commit causes a regression in the test suite:

commit ef6f0fcfdfe771172056e35e3c990998b3b00416
Author: Aza Tulepbergenov <aza.tulepber@gmail.com>
Date: 2021-10-19 20:17:49 +0200
feat: add 'GoogleAPICallError.error_details' property (#286)
Based on 'google.rpc.status.details'.

Prior to this commit, the package handled both missing and present grpc consistently. However, the following condition in exceptions.py:

try:
import grpc
from grpc_status import rpc_status
except ImportError: # pragma: NO COVER
grpc = None
rpc_status = None

means that if grpc_status module is not installed, the exception module behaves as if grpc wasn't present even though other modules (notably tests) behave as if it was present. As a result, people having one but not the other module installed get a bunch of test failures because grpc-based tests are run but e.g. the exception module's from_grpc_error() function fails since grpc is forced to None.

This is especially problematic since grpcio-status is a new optional dependency, and since it's not packaged on Gentoo, we'd effectively have to force people to uninstall grpcio.

Environment details

  • OS type and version: Gentoo Linux
  • Python version: python --version 3.8.12, 3.9.7
  • pip version: pip --version n/a
  • google-api-core version: pip show google-api-core 2.2.0, 2.2.1, git master (d2a729e)

Steps to reproduce

  1. git clone https://github.com/googleapis/python-api-core
  2. cd python-api-core
  3. python3.9 -m venv .venv
  4. . .venv/bin/activate
  5. pip install . grpcio pytest mock proto-plus pytest-asyncio
  6. pytest

Stack trace

Example test failure:

_______________________________________________________ test_wrap_unary_errors ________________________________________________________
self = <google.api_core.grpc_helpers_async._WrappedUnaryUnaryCall object at 0x7fe0e3de3790>
def __await__(self):
try:
> response = yield from self._call.__await__()
.venv/lib/python3.9/site-packages/google/api_core/grpc_helpers_async.py:84: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_mock_self = <AsyncMock id='140603872279952'>, args = (1, 2), kwargs = {'three': 'four'}, self = <AsyncMock id='140603872279952'>
_call = call(1, 2, three='four'), effect = RpcErrorImpl()
async def _execute_mock_call(_mock_self, *args, **kwargs):
self = _mock_self
# This is nearly just like super(), except for special handling
# of coroutines
_call = _Call((args, kwargs), two=True)
self.await_count += 1
self.await_args = _call
self.await_args_list.append(_call)
effect = self.side_effect
if effect is not None:
if _is_exception(effect):
> raise effect
E tests.asyncio.test_grpc_helpers_async.RpcErrorImpl
.venv/lib/python3.9/site-packages/mock/mock.py:2169: RpcErrorImpl
During handling of the above exception, another exception occurred:
@pytest.mark.asyncio
async def test_wrap_unary_errors():
grpc_error = RpcErrorImpl(grpc.StatusCode.INVALID_ARGUMENT)
callable_ = mock.AsyncMock(spec=["__call__"], side_effect=grpc_error)
wrapped_callable = grpc_helpers_async._wrap_unary_errors(callable_)
with pytest.raises(exceptions.InvalidArgument) as exc_info:
> await wrapped_callable(1, 2, three="four")
tests/asyncio/test_grpc_helpers_async.py:57: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
.venv/lib/python3.9/site-packages/google/api_core/grpc_helpers_async.py:87: in __await__
raise exceptions.from_grpc_error(rpc_error) from rpc_error
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
rpc_exc = RpcErrorImpl()
def from_grpc_error(rpc_exc):
"""Create a :class:`GoogleAPICallError` from a :class:`grpc.RpcError`.
Args:
rpc_exc (grpc.RpcError): The gRPC error.
Returns:
GoogleAPICallError: An instance of the appropriate subclass of
:class:`GoogleAPICallError`.
"""
# NOTE(lidiz) All gRPC error shares the parent class grpc.RpcError.
# However, check for grpc.RpcError breaks backward compatibility.
> if isinstance(rpc_exc, grpc.Call) or _is_informative_grpc_error(rpc_exc):
E AttributeError: 'NoneType' object has no attribute 'Call'
.venv/lib/python3.9/site-packages/google/api_core/exceptions.py:532: AttributeError
======================================================= short test summary info =======================================================
FAILED tests/asyncio/test_grpc_helpers_async.py::test_wrap_unary_errors - AttributeError: 'NoneType' object has no attribute 'Call'

Metadata

Metadata

Labels

priority: p2Moderately-important priority. Fix may not be included in next release.type: bugError or flaw in code with unintended results or allowing sub-optimal usage patterns.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions