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

Commit e176020

Browse files
committed
Remove the parent class for grpc transports
1 parent 3cc730c commit e176020

4 files changed

Lines changed: 201 additions & 177 deletions

File tree

gapic/templates/%namespace/%name_%version/%sub/services/%service/transports/base.py.j2

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class {{ service.name }}Transport(abc.ABC):
3030
self, *,
3131
host: str{% if service.host %} = '{{ service.host }}'{% endif %},
3232
credentials: credentials.Credentials = None,
33+
**kwargs,
3334
) -> None:
3435
"""Instantiate the transport.
3536

@@ -59,7 +60,7 @@ class {{ service.name }}Transport(abc.ABC):
5960
@property
6061
def operations_client(self) -> operations_v1.OperationsClient:
6162
"""Return the client designed to process long-running operations."""
62-
raise NotImplementedError
63+
raise NotImplementedError()
6364
{%- endif %}
6465
{%- for method in service.methods.values() %}
6566

@@ -70,7 +71,7 @@ class {{ service.name }}Transport(abc.ABC):
7071
{{ method.output.ident }},
7172
typing.Awaitable[{{ method.output.ident }}]
7273
]]:
73-
raise NotImplementedError
74+
raise NotImplementedError()
7475
{%- endfor %}
7576

7677

gapic/templates/%namespace/%name_%version/%sub/services/%service/transports/grpc.py.j2

Lines changed: 95 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@ import grpc # type: ignore
1919
{{ method.output.ident.python_import }}
2020
{% endfor -%}
2121
{% endfilter %}
22-
from .grpc_base import {{ service.name }}GrpcBaseTransport
22+
from .base import {{ service.name }}Transport
2323

2424

25-
class {{ service.name }}GrpcTransport({{ service.name }}GrpcBaseTransport[grpc.Channel]):
25+
class {{ service.name }}GrpcTransport({{ service.name }}Transport):
2626
"""gRPC backend transport for {{ service.name }}.
2727

2828
{{ service.meta.doc|rst(width=72, indent=4) }}
@@ -34,6 +34,71 @@ class {{ service.name }}GrpcTransport({{ service.name }}GrpcBaseTransport[grpc.C
3434
It sends protocol buffers over the wire using gRPC (which is built on
3535
top of HTTP/2); the ``grpcio`` package must be installed.
3636
"""
37+
_stubs: Dict[str, Callable]
38+
39+
def __init__(self, *,
40+
host: str{% if service.host %} = '{{ service.host }}'{% endif %},
41+
credentials: credentials.Credentials = None,
42+
channel: grpc.Channel = None,
43+
api_mtls_endpoint: str = None,
44+
client_cert_source: Callable[[], Tuple[bytes, bytes]] = None) -> None:
45+
"""Instantiate the transport.
46+
47+
Args:
48+
host ({% if service.host %}Optional[str]{% else %}str{% endif %}):
49+
{{- ' ' }}The hostname to connect to.
50+
credentials (Optional[google.auth.credentials.Credentials]): The
51+
authorization credentials to attach to requests. These
52+
credentials identify the application to the service; if none
53+
are specified, the client will attempt to ascertain the
54+
credentials from the environment.
55+
This argument is ignored if ``channel`` is provided.
56+
channel (Optional[grpc.Channel]): A ``Channel`` instance through
57+
which to make calls.
58+
api_mtls_endpoint (Optional[str]): The mutual TLS endpoint. If
59+
provided, it overrides the ``host`` argument and tries to create
60+
a mutual TLS channel with client SSL credentials from
61+
``client_cert_source`` or applicatin default SSL credentials.
62+
client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): A
63+
callback to provide client SSL certificate bytes and private key
64+
bytes, both in PEM format. It is ignored if ``api_mtls_endpoint``
65+
is None.
66+
67+
Raises:
68+
google.auth.exceptions.MutualTlsChannelError: If mutual TLS transport
69+
creation failed for any reason.
70+
"""
71+
if channel:
72+
# Sanity check: Ensure that channel and credentials are not both
73+
# provided.
74+
credentials = False
75+
76+
# If a channel was explicitly provided, set it.
77+
self._grpc_channel = channel
78+
elif api_mtls_endpoint:
79+
host = api_mtls_endpoint if ":" in api_mtls_endpoint else api_mtls_endpoint + ":443"
80+
81+
# Create SSL credentials with client_cert_source or application
82+
# default SSL credentials.
83+
if client_cert_source:
84+
cert, key = client_cert_source()
85+
ssl_credentials = grpc.ssl_channel_credentials(
86+
certificate_chain=cert, private_key=key
87+
)
88+
else:
89+
ssl_credentials = SslCredentials().ssl_credentials
90+
91+
# create a new channel. The provided one is ignored.
92+
self._grpc_channel = type(self).create_channel(
93+
host,
94+
credentials=credentials,
95+
ssl_credentials=ssl_credentials,
96+
scopes=self.AUTH_SCOPES,
97+
)
98+
99+
# Run the base constructor.
100+
super().__init__(host=host, credentials=credentials)
101+
self._stubs = {} # type: Dict[str, Callable]
37102

38103
@classmethod
39104
def create_channel(cls,
@@ -66,6 +131,23 @@ class {{ service.name }}GrpcTransport({{ service.name }}GrpcBaseTransport[grpc.C
66131
**kwargs
67132
)
68133

134+
@property
135+
def grpc_channel(self) -> grpc.Channel:
136+
"""Create the channel designed to connect to this service.
137+
138+
This property caches on the instance; repeated calls return
139+
the same channel.
140+
"""
141+
# Sanity check: Only create a new channel if we do not already
142+
# have one.
143+
if not hasattr(self, '_grpc_channel'):
144+
self._grpc_channel = self.create_channel(
145+
self._host,
146+
credentials=self._credentials,
147+
)
148+
149+
# Return the channel from cache.
150+
return self._grpc_channel
69151
{%- if service.has_lro %}
70152

71153
@property
@@ -103,7 +185,17 @@ class {{ service.name }}GrpcTransport({{ service.name }}GrpcBaseTransport[grpc.C
103185
A function that, when called, will call the underlying RPC
104186
on the server.
105187
"""
106-
return super().{{ method.name|snake_case }}
188+
# Generate a "stub function" on-the-fly which will actually make
189+
# the request.
190+
# gRPC handles serialization and deserialization, so we just need
191+
# to pass in the functions for each.
192+
if '{{ method.name|snake_case }}' not in self._stubs:
193+
self._stubs['{{ method.name|snake_case }}'] = self.grpc_channel.{{ method.grpc_stub_type }}(
194+
'/{{ '.'.join(method.meta.address.package) }}.{{ service.name }}/{{ method.name }}',
195+
request_serializer={{ method.input.ident }}.{% if method.input.ident.python_import.module.endswith('_pb2') %}SerializeToString{% else %}serialize{% endif %},
196+
response_deserializer={{ method.output.ident }}.{% if method.output.ident.python_import.module.endswith('_pb2') %}FromString{% else %}deserialize{% endif %},
197+
)
198+
return self._stubs['{{ method.name|snake_case }}']
107199
{%- endfor %}
108200

109201

gapic/templates/%namespace/%name_%version/%sub/services/%service/transports/grpc_asyncio.py.j2

Lines changed: 103 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@
33
{% block content %}
44
from typing import Awaitable, Callable, Dict, Sequence, Tuple
55

6-
from google.api_core import grpc_helpers_async # type: ignore
6+
from google.api_core import grpc_helpers_async # type: ignore
77
{%- if service.has_lro %}
8-
from google.api_core import operations_v1 # type: ignore
8+
from google.api_core import operations_v1 # type: ignore
99
{%- endif %}
10-
from google.auth import credentials # type: ignore
10+
from google.auth import credentials # type: ignore
11+
from google.auth.transport.grpc import SslCredentials # type: ignore
1112

13+
import grpc # type: ignore
1214
from grpc.experimental import aio # type: ignore
1315

1416
{% filter sort_lines -%}
@@ -17,10 +19,11 @@ from grpc.experimental import aio # type: ignore
1719
{{ method.output.ident.python_import }}
1820
{% endfor -%}
1921
{% endfilter %}
20-
from .grpc_base import {{ service.name }}GrpcBaseTransport
22+
from .base import {{ service.name }}Transport
23+
from .grpc import {{ service.name }}GrpcTransport
2124

2225

23-
class {{ service.grpc_asyncio_transport_name }}({{ service.name }}GrpcBaseTransport[aio.Channel]):
26+
class {{ service.grpc_asyncio_transport_name }}({{ service.name }}Transport):
2427
"""gRPC AsyncIO backend transport for {{ service.name }}.
2528

2629
{{ service.meta.doc|rst(width=72, indent=4) }}
@@ -33,6 +36,9 @@ class {{ service.grpc_asyncio_transport_name }}({{ service.name }}GrpcBaseTransp
3336
top of HTTP/2); the ``grpcio`` package must be installed.
3437
"""
3538

39+
_grpc_channel: aio.Channel
40+
_stubs: Dict[str, Callable] = {}
41+
3642
@classmethod
3743
def create_channel(cls,
3844
host: str{% if service.host %} = '{{ service.host }}'{% endif %},
@@ -64,6 +70,87 @@ class {{ service.grpc_asyncio_transport_name }}({{ service.name }}GrpcBaseTransp
6470
**kwargs
6571
)
6672

73+
def __init__(self, *,
74+
host: str{% if service.host %} = '{{ service.host }}'{% endif %},
75+
credentials: credentials.Credentials = None,
76+
channel: aio.Channel = None,
77+
api_mtls_endpoint: str = None,
78+
client_cert_source: Callable[[], Tuple[bytes, bytes]] = None) -> None:
79+
"""Instantiate the transport.
80+
81+
Args:
82+
host ({% if service.host %}Optional[str]{% else %}str{% endif %}):
83+
{{- ' ' }}The hostname to connect to.
84+
credentials (Optional[google.auth.credentials.Credentials]): The
85+
authorization credentials to attach to requests. These
86+
credentials identify the application to the service; if none
87+
are specified, the client will attempt to ascertain the
88+
credentials from the environment.
89+
This argument is ignored if ``channel`` is provided.
90+
channel (Optional[aio.Channel]): A ``Channel`` instance through
91+
which to make calls.
92+
api_mtls_endpoint (Optional[str]): The mutual TLS endpoint. If
93+
provided, it overrides the ``host`` argument and tries to create
94+
a mutual TLS channel with client SSL credentials from
95+
``client_cert_source`` or applicatin default SSL credentials.
96+
client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): A
97+
callback to provide client SSL certificate bytes and private key
98+
bytes, both in PEM format. It is ignored if ``api_mtls_endpoint``
99+
is None.
100+
101+
Raises:
102+
google.auth.exceptions.MutualTlsChannelError: If mutual TLS transport
103+
creation failed for any reason.
104+
"""
105+
if channel:
106+
# Sanity check: Ensure that channel and credentials are not both
107+
# provided.
108+
credentials = False
109+
110+
# If a channel was explicitly provided, set it.
111+
self._grpc_channel = channel
112+
elif api_mtls_endpoint:
113+
host = api_mtls_endpoint if ":" in api_mtls_endpoint else api_mtls_endpoint + ":443"
114+
115+
# Create SSL credentials with client_cert_source or application
116+
# default SSL credentials.
117+
if client_cert_source:
118+
cert, key = client_cert_source()
119+
ssl_credentials = grpc.ssl_channel_credentials(
120+
certificate_chain=cert, private_key=key
121+
)
122+
else:
123+
ssl_credentials = SslCredentials().ssl_credentials
124+
125+
# create a new channel. The provided one is ignored.
126+
self._grpc_channel = type(self).create_channel(
127+
host,
128+
credentials=credentials,
129+
ssl_credentials=ssl_credentials,
130+
scopes=self.AUTH_SCOPES,
131+
)
132+
133+
# Run the base constructor.
134+
super().__init__(host=host, credentials=credentials)
135+
self._stubs = {}
136+
137+
@property
138+
def grpc_channel(self) -> aio.Channel:
139+
"""Create the channel designed to connect to this service.
140+
141+
This property caches on the instance; repeated calls return
142+
the same channel.
143+
"""
144+
# Sanity check: Only create a new channel if we do not already
145+
# have one.
146+
if not hasattr(self, '_grpc_channel'):
147+
self._grpc_channel = self.create_channel(
148+
self._host,
149+
credentials=self._credentials,
150+
)
151+
152+
# Return the channel from cache.
153+
return self._grpc_channel
67154
{%- if service.has_lro %}
68155

69156
@property
@@ -101,7 +188,17 @@ class {{ service.grpc_asyncio_transport_name }}({{ service.name }}GrpcBaseTransp
101188
A function that, when called, will call the underlying RPC
102189
on the server.
103190
"""
104-
return super().{{ method.name|snake_case }}
191+
# Generate a "stub function" on-the-fly which will actually make
192+
# the request.
193+
# gRPC handles serialization and deserialization, so we just need
194+
# to pass in the functions for each.
195+
if '{{ method.name|snake_case }}' not in self._stubs:
196+
self._stubs['{{ method.name|snake_case }}'] = self.grpc_channel.{{ method.grpc_stub_type }}(
197+
'/{{ '.'.join(method.meta.address.package) }}.{{ service.name }}/{{ method.name }}',
198+
request_serializer={{ method.input.ident }}.{% if method.input.ident.python_import.module.endswith('_pb2') %}SerializeToString{% else %}serialize{% endif %},
199+
response_deserializer={{ method.output.ident }}.{% if method.output.ident.python_import.module.endswith('_pb2') %}FromString{% else %}deserialize{% endif %},
200+
)
201+
return self._stubs['{{ method.name|snake_case }}']
105202
{%- endfor %}
106203

107204

0 commit comments

Comments
 (0)