Skip to content

Commit 8c19609

Browse files
authored
fix: restore support for grpcio-gcp (#418)
docs: add a note that grpcio-gcp is only supported in environments with protobuf < 4.x.x docs: raise DeprecationWarning when 'grpcio-gcp' is used fix(deps): require protobuf >= 3.20.1
1 parent 29aa9ef commit 8c19609

File tree

8 files changed

+129
-17
lines changed

8 files changed

+129
-17
lines changed

.github/sync-repo-settings.yaml

+4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ branchProtectionRules:
1111
# No Kokoro: the following are Github actions
1212
- 'lint'
1313
- 'mypy'
14+
- 'unit_grpc_gcp-3.7'
15+
- 'unit_grpc_gcp-3.8'
16+
- 'unit_grpc_gcp-3.9'
17+
- 'unit_grpc_gcp-3.10'
1418
- 'unit-3.7'
1519
- 'unit-3.8'
1620
- 'unit-3.9'

.github/workflows/unittest.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ jobs:
1111
runs-on: ubuntu-latest
1212
strategy:
1313
matrix:
14-
option: ["", "_wo_grpc"]
14+
option: ["", "_grpc_gcp", "_wo_grpc"]
1515
python:
1616
- "3.7"
1717
- "3.8"

google/api_core/grpc_helpers.py

+27-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import collections
1818
import functools
19+
import warnings
1920

2021
import grpc
2122

@@ -24,6 +25,27 @@
2425
import google.auth.credentials
2526
import google.auth.transport.grpc
2627
import google.auth.transport.requests
28+
import google.protobuf
29+
30+
PROTOBUF_VERSION = google.protobuf.__version__
31+
32+
# The grpcio-gcp package only has support for protobuf < 4
33+
if PROTOBUF_VERSION[0:2] == "3.":
34+
try:
35+
import grpc_gcp
36+
37+
warnings.warn(
38+
"""Support for grpcio-gcp is deprecated. This feature will be
39+
removed from `google-api-core` after January 1, 2024. If you need to
40+
continue to use this feature, please pin to a specific version of
41+
`google-api-core`.""",
42+
DeprecationWarning,
43+
)
44+
HAS_GRPC_GCP = True
45+
except ImportError:
46+
HAS_GRPC_GCP = False
47+
else:
48+
HAS_GRPC_GCP = False
2749

2850

2951
# The list of gRPC Callable interfaces that return iterators.
@@ -275,7 +297,9 @@ def create_channel(
275297
default_scopes (Sequence[str]): Default scopes passed by a Google client
276298
library. Use 'scopes' for user-defined scopes.
277299
default_host (str): The default endpoint. e.g., "pubsub.googleapis.com".
278-
kwargs: Additional key-word args passed to :func:`grpc.secure_channel`.
300+
kwargs: Additional key-word args passed to
301+
:func:`grpc_gcp.secure_channel` or :func:`grpc.secure_channel`.
302+
Note: `grpc_gcp` is only supported in environments with protobuf < 4.0.0.
279303
280304
Returns:
281305
grpc.Channel: The created channel.
@@ -294,6 +318,8 @@ def create_channel(
294318
default_host=default_host,
295319
)
296320

321+
if HAS_GRPC_GCP:
322+
return grpc_gcp.secure_channel(target, composite_credentials, **kwargs)
297323
return grpc.secure_channel(target, composite_credentials, **kwargs)
298324

299325

noxfile.py

+15
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
# 'docfx' is excluded since it only needs to run in 'docs-presubmit'
3333
nox.options.sessions = [
3434
"unit",
35+
"unit_grpc_gcp",
3536
"unit_wo_grpc",
3637
"cover",
3738
"pytype",
@@ -142,6 +143,20 @@ def unit(session):
142143
default(session)
143144

144145

146+
@nox.session(python=["3.6", "3.7", "3.8", "3.9", "3.10"])
147+
def unit_grpc_gcp(session):
148+
"""Run the unit test suite with grpcio-gcp installed."""
149+
constraints_path = str(
150+
CURRENT_DIRECTORY / "testing" / f"constraints-{session.python}.txt"
151+
)
152+
# Install grpcio-gcp
153+
session.install("-e", ".[grpcgcp]", "-c", constraints_path)
154+
# Install protobuf < 4.0.0
155+
session.install("protobuf<4.0.0")
156+
157+
default(session)
158+
159+
145160
@nox.session(python=["3.8", "3.10"])
146161
def unit_wo_grpc(session):
147162
"""Run the unit test suite w/o grpcio installed"""

setup.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,14 @@
3030
release_status = "Development Status :: 5 - Production/Stable"
3131
dependencies = [
3232
"googleapis-common-protos >= 1.56.2, < 2.0dev",
33-
"protobuf >= 3.15.0, <5.0.0dev",
33+
"protobuf >= 3.20.1, <5.0.0dev",
3434
"google-auth >= 1.25.0, < 3.0dev",
3535
"requests >= 2.18.0, < 3.0.0dev",
3636
]
3737
extras = {
3838
"grpc": ["grpcio >= 1.33.2, < 2.0dev", "grpcio-status >= 1.33.2, < 2.0dev"],
39+
"grpcgcp": "grpcio-gcp >= 0.2.2, < 1.0dev",
40+
"grpcio-gcp": "grpcio-gcp >= 0.2.2, < 1.0dev",
3941
}
4042

4143

testing/constraints-3.7.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@
66
# e.g., if setup.py has "foo >= 1.14.0, < 2.0.0dev",
77
# Then this file should have foo==1.14.0
88
googleapis-common-protos==1.56.2
9-
protobuf==3.15.0
9+
protobuf==3.20.1
1010
google-auth==1.25.0
1111
requests==2.18.0
1212
packaging==14.3
1313
grpcio==1.33.2
1414
grpcio-status==1.33.2
15+
grpcio-gcp==0.2.2

testing/constraints-3.8.txt

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
googleapis-common-protos==1.56.3
2+
protobuf==4.21.5

tests/unit/test_grpc_helpers.py

+75-13
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,10 @@ def test_create_channel_implicit(grpc_secure_channel, default, composite_creds_c
365365

366366
default.assert_called_once_with(scopes=None, default_scopes=None)
367367

368-
grpc_secure_channel.assert_called_once_with(target, composite_creds)
368+
if grpc_helpers.HAS_GRPC_GCP:
369+
grpc_secure_channel.assert_called_once_with(target, composite_creds, None)
370+
else:
371+
grpc_secure_channel.assert_called_once_with(target, composite_creds)
369372

370373

371374
@mock.patch("google.auth.transport.grpc.AuthMetadataPlugin", autospec=True)
@@ -397,7 +400,10 @@ def test_create_channel_implicit_with_default_host(
397400
mock.sentinel.credentials, mock.sentinel.Request, default_host=default_host
398401
)
399402

400-
grpc_secure_channel.assert_called_once_with(target, composite_creds)
403+
if grpc_helpers.HAS_GRPC_GCP:
404+
grpc_secure_channel.assert_called_once_with(target, composite_creds, None)
405+
else:
406+
grpc_secure_channel.assert_called_once_with(target, composite_creds)
401407

402408

403409
@mock.patch("grpc.composite_channel_credentials")
@@ -420,7 +426,11 @@ def test_create_channel_implicit_with_ssl_creds(
420426

421427
composite_creds_call.assert_called_once_with(ssl_creds, mock.ANY)
422428
composite_creds = composite_creds_call.return_value
423-
grpc_secure_channel.assert_called_once_with(target, composite_creds)
429+
430+
if grpc_helpers.HAS_GRPC_GCP:
431+
grpc_secure_channel.assert_called_once_with(target, composite_creds, None)
432+
else:
433+
grpc_secure_channel.assert_called_once_with(target, composite_creds)
424434

425435

426436
@mock.patch("grpc.composite_channel_credentials")
@@ -442,7 +452,10 @@ def test_create_channel_implicit_with_scopes(
442452

443453
default.assert_called_once_with(scopes=["one", "two"], default_scopes=None)
444454

445-
grpc_secure_channel.assert_called_once_with(target, composite_creds)
455+
if grpc_helpers.HAS_GRPC_GCP:
456+
grpc_secure_channel.assert_called_once_with(target, composite_creds, None)
457+
else:
458+
grpc_secure_channel.assert_called_once_with(target, composite_creds)
446459

447460

448461
@mock.patch("grpc.composite_channel_credentials")
@@ -464,7 +477,10 @@ def test_create_channel_implicit_with_default_scopes(
464477

465478
default.assert_called_once_with(scopes=None, default_scopes=["three", "four"])
466479

467-
grpc_secure_channel.assert_called_once_with(target, composite_creds)
480+
if grpc_helpers.HAS_GRPC_GCP:
481+
grpc_secure_channel.assert_called_once_with(target, composite_creds, None)
482+
else:
483+
grpc_secure_channel.assert_called_once_with(target, composite_creds)
468484

469485

470486
def test_create_channel_explicit_with_duplicate_credentials():
@@ -492,7 +508,11 @@ def test_create_channel_explicit(grpc_secure_channel, auth_creds, composite_cred
492508
)
493509

494510
assert channel is grpc_secure_channel.return_value
495-
grpc_secure_channel.assert_called_once_with(target, composite_creds)
511+
512+
if grpc_helpers.HAS_GRPC_GCP:
513+
grpc_secure_channel.assert_called_once_with(target, composite_creds, None)
514+
else:
515+
grpc_secure_channel.assert_called_once_with(target, composite_creds)
496516

497517

498518
@mock.patch("grpc.composite_channel_credentials")
@@ -512,7 +532,11 @@ def test_create_channel_explicit_scoped(grpc_secure_channel, composite_creds_cal
512532
credentials.with_scopes.assert_called_once_with(scopes, default_scopes=None)
513533

514534
assert channel is grpc_secure_channel.return_value
515-
grpc_secure_channel.assert_called_once_with(target, composite_creds)
535+
536+
if grpc_helpers.HAS_GRPC_GCP:
537+
grpc_secure_channel.assert_called_once_with(target, composite_creds, None)
538+
else:
539+
grpc_secure_channel.assert_called_once_with(target, composite_creds)
516540

517541

518542
@mock.patch("grpc.composite_channel_credentials")
@@ -536,7 +560,11 @@ def test_create_channel_explicit_default_scopes(
536560
)
537561

538562
assert channel is grpc_secure_channel.return_value
539-
grpc_secure_channel.assert_called_once_with(target, composite_creds)
563+
564+
if grpc_helpers.HAS_GRPC_GCP:
565+
grpc_secure_channel.assert_called_once_with(target, composite_creds, None)
566+
else:
567+
grpc_secure_channel.assert_called_once_with(target, composite_creds)
540568

541569

542570
@mock.patch("grpc.composite_channel_credentials")
@@ -558,7 +586,11 @@ def test_create_channel_explicit_with_quota_project(
558586
credentials.with_quota_project.assert_called_once_with("project-foo")
559587

560588
assert channel is grpc_secure_channel.return_value
561-
grpc_secure_channel.assert_called_once_with(target, composite_creds)
589+
590+
if grpc_helpers.HAS_GRPC_GCP:
591+
grpc_secure_channel.assert_called_once_with(target, composite_creds, None)
592+
else:
593+
grpc_secure_channel.assert_called_once_with(target, composite_creds)
562594

563595

564596
@mock.patch("grpc.composite_channel_credentials")
@@ -583,7 +615,11 @@ def test_create_channel_with_credentials_file(
583615
)
584616

585617
assert channel is grpc_secure_channel.return_value
586-
grpc_secure_channel.assert_called_once_with(target, composite_creds)
618+
619+
if grpc_helpers.HAS_GRPC_GCP:
620+
grpc_secure_channel.assert_called_once_with(target, composite_creds, None)
621+
else:
622+
grpc_secure_channel.assert_called_once_with(target, composite_creds)
587623

588624

589625
@mock.patch("grpc.composite_channel_credentials")
@@ -611,7 +647,11 @@ def test_create_channel_with_credentials_file_and_scopes(
611647
)
612648

613649
assert channel is grpc_secure_channel.return_value
614-
grpc_secure_channel.assert_called_once_with(target, composite_creds)
650+
651+
if grpc_helpers.HAS_GRPC_GCP:
652+
grpc_secure_channel.assert_called_once_with(target, composite_creds, None)
653+
else:
654+
grpc_secure_channel.assert_called_once_with(target, composite_creds)
615655

616656

617657
@mock.patch("grpc.composite_channel_credentials")
@@ -639,11 +679,33 @@ def test_create_channel_with_credentials_file_and_default_scopes(
639679
)
640680

641681
assert channel is grpc_secure_channel.return_value
642-
grpc_secure_channel.assert_called_once_with(target, composite_creds)
682+
683+
if grpc_helpers.HAS_GRPC_GCP:
684+
grpc_secure_channel.assert_called_once_with(target, composite_creds, None)
685+
else:
686+
grpc_secure_channel.assert_called_once_with(target, composite_creds)
687+
688+
689+
@pytest.mark.skipif(
690+
not grpc_helpers.HAS_GRPC_GCP, reason="grpc_gcp module not available"
691+
)
692+
@mock.patch("grpc_gcp.secure_channel")
693+
def test_create_channel_with_grpc_gcp(grpc_gcp_secure_channel):
694+
target = "example.com:443"
695+
scopes = ["test_scope"]
696+
697+
credentials = mock.create_autospec(google.auth.credentials.Scoped, instance=True)
698+
credentials.requires_scopes = True
699+
700+
grpc_helpers.create_channel(target, credentials=credentials, scopes=scopes)
701+
grpc_gcp_secure_channel.assert_called()
702+
703+
credentials.with_scopes.assert_called_once_with(scopes, default_scopes=None)
643704

644705

706+
@pytest.mark.skipif(grpc_helpers.HAS_GRPC_GCP, reason="grpc_gcp module not available")
645707
@mock.patch("grpc.secure_channel")
646-
def test_create_channel(grpc_secure_channel):
708+
def test_create_channel_without_grpc_gcp(grpc_secure_channel):
647709
target = "example.com:443"
648710
scopes = ["test_scope"]
649711

0 commit comments

Comments
 (0)