11import os
22import inspect
3- from google .auth import credentials
3+ from google .auth import credentials , environment_vars
44from google .auth .exceptions import RefreshError
55from google .api_core .gapic_v1 .client_info import ClientInfo
66from google .cloud import bigquery
@@ -22,7 +22,7 @@ def get_integrations():
2222 target = GcpTarget [integration .upper ()]
2323 kernel_integrations .add_integration (target )
2424 except KeyError as e :
25- Log .error (f"Unknown integration target: { e } " )
25+ Log .error (f"Unknown integration target: { integration . upper () } " )
2626 return kernel_integrations
2727
2828
@@ -45,6 +45,17 @@ def has_gcs(self):
4545 def has_automl (self ):
4646 return GcpTarget .AUTOML in self .integrations
4747
48+ def has_translation (self ):
49+ return GcpTarget .TRANSLATION in self .integrations
50+
51+ def has_natural_language (self ):
52+ return GcpTarget .NATURAL_LANGUAGE in self .integrations
53+
54+ def has_video_intelligence (self ):
55+ return GcpTarget .VIDEO_INTELLIGENCE in self .integrations
56+
57+ def has_vision (self ):
58+ return GcpTarget .VISION in self .integrations
4859
4960class KaggleKernelCredentials (credentials .Credentials ):
5061 """Custom Credentials used to authenticate using the Kernel's connected OAuth account.
@@ -65,6 +76,14 @@ def refresh(self, request):
6576 self .token , self .expiry = client ._get_gcs_access_token ()
6677 elif self .target == GcpTarget .AUTOML :
6778 self .token , self .expiry = client ._get_automl_access_token ()
79+ elif self .target == GcpTarget .TRANSLATION :
80+ self .token , self .expiry = client ._get_translation_access_token ()
81+ elif self .target == GcpTarget .NATURAL_LANGUAGE :
82+ self .token , self .expiry = client ._get_natural_language_access_token ()
83+ elif self .target == GcpTarget .VIDEO_INTELLIGENCE :
84+ self .token , self .expiry = client ._get_video_intelligence_access_token ()
85+ elif self .target == GcpTarget .VISION :
86+ self .token , self .expiry = client ._get_vision_access_token ()
6887 except ConnectionError as e :
6988 Log .error (f"Connection error trying to refresh access token: { e } " )
7089 print ("There was a connection error trying to fetch the access token. "
@@ -78,6 +97,12 @@ def refresh(self, request):
7897 f"Please ensure you have selected a { self .target .service } account in the Notebook Add-ons menu." )
7998 raise RefreshError ('Unable to refresh access token.' ) from e
8099
100+ class KaggleKernelWithProjetCredentials (KaggleKernelCredentials ):
101+ """ Wrapper Kaggle Credentials with quota_project_id.
102+ """
103+ def __init__ (self , parentCredential = None , quota_project_id = None ):
104+ super ().__init__ (target = parentCredential .target )
105+ self ._quota_project_id = quota_project_id
81106
82107class _DataProxyConnection (Connection ):
83108 """Custom Connection class used to proxy the BigQuery client to Kaggle's data proxy."""
@@ -122,13 +147,16 @@ def __init__(self, *args, **kwargs):
122147def has_been_monkeypatched (method ):
123148 return "kaggle_gcp" in inspect .getsourcefile (method )
124149
150+ def is_user_secrets_token_set ():
151+ return "KAGGLE_USER_SECRETS_TOKEN" in os .environ
152+
153+ def is_proxy_token_set ():
154+ return "KAGGLE_DATA_PROXY_TOKEN" in os .environ
155+
125156def init_bigquery ():
126- from google .auth import environment_vars
127157 from google .cloud import bigquery
128158
129- is_proxy_token_set = "KAGGLE_DATA_PROXY_TOKEN" in os .environ
130- is_user_secrets_token_set = "KAGGLE_USER_SECRETS_TOKEN" in os .environ
131- if not (is_proxy_token_set or is_user_secrets_token_set ):
159+ if not (is_proxy_token_set () or is_user_secrets_token_set ()):
132160 return bigquery
133161
134162 # If this Notebook has bigquery integration on startup, preload the Kaggle Credentials
@@ -185,14 +213,24 @@ def patched_init(self, *args, **kwargs):
185213 specified_credentials = kwargs .get ('credentials' )
186214 if specified_credentials is None :
187215 Log .info ("No credentials specified, using KaggleKernelCredentials." )
188- kwargs ['credentials' ] = kaggle_kernel_credentials
216+ # Some GCP services demand the billing and target project must be the same.
217+ # To avoid using default service account based credential as caller credential
218+ # user need to provide ClientOptions with quota_project_id:
219+ # srv.Client(client_options=client_options.ClientOptions(quota_project_id="YOUR PROJECT"))
220+ client_options = kwargs .get ('client_options' )
221+ if client_options != None and client_options .quota_project_id != None :
222+ kwargs ['credentials' ] = KaggleKernelWithProjetCredentials (
223+ parentCredential = kaggle_kernel_credentials ,
224+ quota_project_id = client_options .quota_project_id )
225+ else :
226+ kwargs ['credentials' ] = kaggle_kernel_credentials
189227
190228 kwargs ['client_info' ] = set_kaggle_user_agent (kwargs .get ('client_info' ))
191-
192229 return client_init (self , * args , ** kwargs )
193230
194231 if (not has_been_monkeypatched (client_klass .__init__ )):
195232 client_klass .__init__ = patched_init
233+ Log .info (f"Client patched: { client_klass } " )
196234
197235def set_kaggle_user_agent (client_info : ClientInfo ):
198236 # Add kaggle client user agent in order to attribute usage.
@@ -203,9 +241,8 @@ def set_kaggle_user_agent(client_info: ClientInfo):
203241 return client_info
204242
205243def init_gcs ():
206- is_user_secrets_token_set = "KAGGLE_USER_SECRETS_TOKEN" in os .environ
207244 from google .cloud import storage
208- if not is_user_secrets_token_set :
245+ if not is_user_secrets_token_set () :
209246 return storage
210247
211248 from kaggle_gcp import get_integrations
@@ -220,9 +257,8 @@ def init_gcs():
220257 return storage
221258
222259def init_automl ():
223- is_user_secrets_token_set = "KAGGLE_USER_SECRETS_TOKEN" in os .environ
224260 from google .cloud import automl , automl_v1beta1
225- if not is_user_secrets_token_set :
261+ if not is_user_secrets_token_set () :
226262 return
227263
228264 from kaggle_gcp import get_integrations
@@ -251,10 +287,91 @@ def init_automl():
251287 # the TablesClient is GA.
252288 monkeypatch_client (automl_v1beta1 .TablesClient , kaggle_kernel_credentials )
253289
290+ def init_translation_v2 ():
291+ from google .cloud import translate_v2
292+ if not is_user_secrets_token_set ():
293+ return translate_v2
294+
295+ from kaggle_gcp import get_integrations
296+ if not get_integrations ().has_translation ():
297+ return translate_v2
298+ from kaggle_secrets import GcpTarget
299+ kernel_credentials = KaggleKernelCredentials (target = GcpTarget .TRANSLATION )
300+ monkeypatch_client (translate_v2 .Client , kernel_credentials )
301+ return translate_v2
302+
303+ def init_translation_v3 ():
304+ # Translate v3 exposes different client than translate v2.
305+ from google .cloud import translate_v3
306+ if not is_user_secrets_token_set ():
307+ return translate_v3
308+
309+ from kaggle_gcp import get_integrations
310+ if not get_integrations ().has_translation ():
311+ return translate_v3
312+ from kaggle_secrets import GcpTarget
313+ kernel_credentials = KaggleKernelCredentials (target = GcpTarget .TRANSLATION )
314+ monkeypatch_client (translate_v3 .TranslationServiceClient , kernel_credentials )
315+ return translate_v3
316+
317+ def init_natural_language ():
318+ from google .cloud import language
319+ if not is_user_secrets_token_set ():
320+ return language
321+
322+ from kaggle_gcp import get_integrations
323+ if not get_integrations ().has_natural_language ():
324+ return language
325+
326+ from kaggle_secrets import GcpTarget
327+ kernel_credentials = KaggleKernelCredentials (target = GcpTarget .NATURAL_LANGUAGE )
328+ monkeypatch_client (language .LanguageServiceClient , kernel_credentials )
329+ monkeypatch_client (language .LanguageServiceAsyncClient , kernel_credentials )
330+ return language
331+
332+ def init_video_intelligence ():
333+ from google .cloud import videointelligence
334+ if not is_user_secrets_token_set ():
335+ return videointelligence
336+
337+ from kaggle_gcp import get_integrations
338+ if not get_integrations ().has_video_intelligence ():
339+ return videointelligence
340+
341+ from kaggle_secrets import GcpTarget
342+ kernel_credentials = KaggleKernelCredentials (target = GcpTarget .VIDEO_INTELLIGENCE )
343+ monkeypatch_client (
344+ videointelligence .VideoIntelligenceServiceClient ,
345+ kernel_credentials )
346+ monkeypatch_client (
347+ videointelligence .VideoIntelligenceServiceAsyncClient ,
348+ kernel_credentials )
349+ return videointelligence
350+
351+ def init_vision ():
352+ from google .cloud import vision
353+ if not is_user_secrets_token_set ():
354+ return vision
355+
356+ from kaggle_gcp import get_integrations
357+ if not get_integrations ().has_vision ():
358+ return vision
359+
360+ from kaggle_secrets import GcpTarget
361+ kernel_credentials = KaggleKernelCredentials (target = GcpTarget .VISION )
362+ monkeypatch_client (vision .ImageAnnotatorClient , kernel_credentials )
363+ monkeypatch_client (vision .ImageAnnotatorAsyncClient , kernel_credentials )
364+ return vision
365+
254366def init ():
255367 init_bigquery ()
256368 init_gcs ()
257369 init_automl ()
370+ init_translation_v2 ()
371+ init_translation_v3 ()
372+ init_natural_language ()
373+ init_video_intelligence ()
374+ init_vision ()
258375
259376# We need to initialize the monkeypatching of the client libraries
260377# here since there is a circular dependency between our import hook version
0 commit comments