3030# Standard library imports
3131import copy
3232from collections import OrderedDict
33+
3334try :
3435 from email .generator import BytesGenerator
3536except ImportError :
@@ -260,14 +261,17 @@ def build(
260261 else :
261262 discovery_http = http
262263
263- for discovery_url in \
264- _discovery_service_uri_options (discoveryServiceUrl , version ):
264+ for discovery_url in _discovery_service_uri_options (discoveryServiceUrl , version ):
265265 requested_url = uritemplate .expand (discovery_url , params )
266266
267267 try :
268268 content = _retrieve_discovery_doc (
269- requested_url , discovery_http , cache_discovery , cache ,
270- developerKey , num_retries = num_retries
269+ requested_url ,
270+ discovery_http ,
271+ cache_discovery ,
272+ cache ,
273+ developerKey ,
274+ num_retries = num_retries ,
271275 )
272276 return build_from_document (
273277 content ,
@@ -308,13 +312,15 @@ def _discovery_service_uri_options(discoveryServiceUrl, version):
308312 # V1 Discovery won't work if the requested version is None
309313 if discoveryServiceUrl == V1_DISCOVERY_URI and version is None :
310314 logger .warning (
311- "Discovery V1 does not support empty versions. Defaulting to V2..." )
315+ "Discovery V1 does not support empty versions. Defaulting to V2..."
316+ )
312317 urls .pop (0 )
313318 return list (OrderedDict .fromkeys (urls ))
314319
315320
316- def _retrieve_discovery_doc (url , http , cache_discovery ,
317- cache = None , developerKey = None , num_retries = 1 ):
321+ def _retrieve_discovery_doc (
322+ url , http , cache_discovery , cache = None , developerKey = None , num_retries = 1
323+ ):
318324 """Retrieves the discovery_doc from cache or the internet.
319325
320326 Args:
@@ -444,8 +450,20 @@ def build_from_document(
444450 setting up mutual TLS channel.
445451 """
446452
447- if http is not None and credentials is not None :
448- raise ValueError ("Arguments http and credentials are mutually exclusive." )
453+ if client_options is None :
454+ client_options = google .api_core .client_options .ClientOptions ()
455+ if isinstance (client_options , six .moves .collections_abc .Mapping ):
456+ client_options = google .api_core .client_options .from_dict (client_options )
457+
458+ if http is not None :
459+ # if http is passed, the user cannot provide credentials
460+ banned_options = [
461+ (credentials , "credentials" ),
462+ (client_options .credentials_file , "client_options.credentials_file" ),
463+ ]
464+ for option , name in banned_options :
465+ if option is not None :
466+ raise ValueError ("Arguments http and {} are mutually exclusive" .format (name ))
449467
450468 if isinstance (service , six .string_types ):
451469 service = json .loads (service )
@@ -463,11 +481,8 @@ def build_from_document(
463481
464482 # If an API Endpoint is provided on client options, use that as the base URL
465483 base = urljoin (service ["rootUrl" ], service ["servicePath" ])
466- if client_options :
467- if isinstance (client_options , six .moves .collections_abc .Mapping ):
468- client_options = google .api_core .client_options .from_dict (client_options )
469- if client_options .api_endpoint :
470- base = client_options .api_endpoint
484+ if client_options .api_endpoint :
485+ base = client_options .api_endpoint
471486
472487 schema = Schemas (service )
473488
@@ -483,13 +498,30 @@ def build_from_document(
483498 # If so, then the we need to setup authentication if no developerKey is
484499 # specified.
485500 if scopes and not developerKey :
501+ # Make sure the user didn't pass multiple credentials
502+ if client_options .credentials_file and credentials :
503+ raise google .api_core .exceptions .DuplicateCredentialArgs (
504+ "client_options.credentials_file and credentials are mutually exclusive."
505+ )
506+ # Check for credentials file via client options
507+ if client_options .credentials_file :
508+ credentials = _auth .credentials_from_file (
509+ client_options .credentials_file ,
510+ scopes = client_options .scopes ,
511+ quota_project_id = client_options .quota_project_id ,
512+ )
486513 # If the user didn't pass in credentials, attempt to acquire application
487514 # default credentials.
488515 if credentials is None :
489- credentials = _auth .default_credentials ()
516+ credentials = _auth .default_credentials (
517+ scopes = client_options .scopes ,
518+ quota_project_id = client_options .quota_project_id ,
519+ )
490520
491521 # The credentials need to be scoped.
492- credentials = _auth .with_scopes (credentials , scopes )
522+ # If the user provided scopes via client_options don't override them
523+ if not client_options .scopes :
524+ credentials = _auth .with_scopes (credentials , scopes )
493525
494526 # If credentials are provided, create an authorized http instance;
495527 # otherwise, skip authentication.
@@ -519,7 +551,9 @@ def build_from_document(
519551 and client_options .client_encrypted_cert_source
520552 ):
521553 client_cert_to_use = client_options .client_encrypted_cert_source
522- elif adc_cert_path and adc_key_path and mtls .has_default_client_cert_source ():
554+ elif (
555+ adc_cert_path and adc_key_path and mtls .has_default_client_cert_source ()
556+ ):
523557 client_cert_to_use = mtls .default_client_encrypted_cert_source (
524558 adc_cert_path , adc_key_path
525559 )
0 commit comments