From 6f67864c7dddfbaa4e104f6aa798e433d9554444 Mon Sep 17 00:00:00 2001 From: Anthonios Partheniou Date: Wed, 4 May 2022 11:53:20 +0000 Subject: [PATCH 1/2] chore: automatically format code using isort and black --- noxfile.py | 22 ++++++++++++++++++++++ owlbot.py | 6 +++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/noxfile.py b/noxfile.py index 3e23830ce36..a7a25296124 100644 --- a/noxfile.py +++ b/noxfile.py @@ -16,6 +16,10 @@ import nox import shutil +BLACK_VERSION = "black==22.3.0" +ISORT_VERSION = "isort==5.10.1" +BLACK_PATHS = ["apiclient", "googleapiclient", "scripts", "tests", "describe.py", "noxfile.py", "owlbot.py", "setup.py"] + test_dependencies = [ "django>=2.0.0", "google-auth", @@ -44,6 +48,24 @@ def lint(session): "--statistics", ) +@nox.session(python="3.8") +def format(session): + """ + Run isort to sort imports. Then run black + to format code to uniform standard. + """ + session.install(BLACK_VERSION, ISORT_VERSION) + # Use the --fss option to sort imports using strict alphabetical order. + # See https://pycqa.github.io/isort/docs/configuration/options.html#force-sort-within-sections + session.run( + "isort", + "--fss", + *BLACK_PATHS, + ) + session.run( + "black", + *BLACK_PATHS, + ) @nox.session(python=["3.6", "3.7", "3.8", "3.9", "3.10"]) @nox.parametrize( diff --git a/owlbot.py b/owlbot.py index 1c2710fd41c..f465e32c930 100644 --- a/owlbot.py +++ b/owlbot.py @@ -14,7 +14,7 @@ import synthtool as s from synthtool import gcp - +from pathlib import Path from synthtool.languages import python common = gcp.CommonTemplates() @@ -43,3 +43,7 @@ # ---------------------------------------------------------------------------- python.py_samples(skip_readmes=True) + +for noxfile in Path(".").glob("**/noxfile.py"): + s.shell.run(["nox", "-s", "format"], cwd=noxfile.parent, hide_output=False) + From cef7f799c88db50e933ba080d8cd937b8cfb946a Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Wed, 4 May 2022 12:39:29 +0000 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot=20po?= =?UTF-8?q?st-processor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- apiclient/__init__.py | 8 +- describe.py | 8 +- googleapiclient/_auth.py | 11 +- googleapiclient/_helpers.py | 1 - googleapiclient/channel.py | 159 ++-- googleapiclient/discovery.py | 827 +++++++++--------- googleapiclient/discovery_cache/__init__.py | 28 +- .../discovery_cache/appengine_memcache.py | 7 +- googleapiclient/discovery_cache/base.py | 20 +- googleapiclient/discovery_cache/file_cache.py | 10 +- googleapiclient/errors.py | 9 +- googleapiclient/http.py | 25 +- googleapiclient/mimeparse.py | 3 +- googleapiclient/model.py | 228 ++--- googleapiclient/sample_tools.py | 46 +- googleapiclient/schema.py | 1 + googleapiclient/version.py | 2 +- noxfile.py | 16 +- owlbot.py | 14 +- samples/compute/create_instance.py | 171 ++-- samples/compute/create_instance_test.py | 13 +- samples/compute/noxfile.py | 12 +- scripts/buildprbody.py | 8 +- scripts/changesummary.py | 3 +- scripts/changesummary_test.py | 9 +- scripts/readme-gen/readme_gen.py | 21 +- scripts/updatediscoveryartifacts.py | 2 +- setup.py | 3 +- tests/test__auth.py | 6 +- tests/test__helpers.py | 1 + tests/test_channel.py | 3 +- tests/test_discovery.py | 234 +++-- tests/test_errors.py | 24 +- tests/test_http.py | 149 ++-- tests/test_json_model.py | 12 +- tests/test_mocks.py | 83 +- tests/test_model.py | 14 +- tests/test_protobuf_model.py | 1 + tests/test_schema.py | 1 - 39 files changed, 1187 insertions(+), 1006 deletions(-) diff --git a/apiclient/__init__.py b/apiclient/__init__.py index abacd294724..0af0d776602 100644 --- a/apiclient/__init__.py +++ b/apiclient/__init__.py @@ -1,13 +1,7 @@ """Retain apiclient as an alias for googleapiclient.""" import googleapiclient - -from googleapiclient import channel -from googleapiclient import discovery -from googleapiclient import errors -from googleapiclient import http -from googleapiclient import mimeparse -from googleapiclient import model +from googleapiclient import channel, discovery, errors, http, mimeparse, model try: from googleapiclient import sample_tools diff --git a/describe.py b/describe.py index 49ae2745114..506e251dd04 100755 --- a/describe.py +++ b/describe.py @@ -32,13 +32,11 @@ import string import sys -from googleapiclient.discovery import DISCOVERY_URI -from googleapiclient.discovery import build -from googleapiclient.discovery import build_from_document -from googleapiclient.http import build_http - import uritemplate +from googleapiclient.discovery import DISCOVERY_URI, build, build_from_document +from googleapiclient.http import build_http + DISCOVERY_DOC_DIR = ( pathlib.Path(__file__).parent.resolve() / "googleapiclient" diff --git a/googleapiclient/_auth.py b/googleapiclient/_auth.py index d045fc147d1..065b2ecd307 100644 --- a/googleapiclient/_auth.py +++ b/googleapiclient/_auth.py @@ -41,17 +41,22 @@ def credentials_from_file(filename, scopes=None, quota_project_id=None): """Returns credentials loaded from a file.""" if HAS_GOOGLE_AUTH: - credentials, _ = google.auth.load_credentials_from_file(filename, scopes=scopes, quota_project_id=quota_project_id) + credentials, _ = google.auth.load_credentials_from_file( + filename, scopes=scopes, quota_project_id=quota_project_id + ) return credentials else: raise EnvironmentError( - "client_options.credentials_file is only supported in google-auth.") + "client_options.credentials_file is only supported in google-auth." + ) def default_credentials(scopes=None, quota_project_id=None): """Returns Application Default Credentials.""" if HAS_GOOGLE_AUTH: - credentials, _ = google.auth.default(scopes=scopes, quota_project_id=quota_project_id) + credentials, _ = google.auth.default( + scopes=scopes, quota_project_id=quota_project_id + ) return credentials elif HAS_OAUTH2CLIENT: if scopes is not None or quota_project_id is not None: diff --git a/googleapiclient/_helpers.py b/googleapiclient/_helpers.py index 3d03376cdc2..d23236886be 100644 --- a/googleapiclient/_helpers.py +++ b/googleapiclient/_helpers.py @@ -19,7 +19,6 @@ import logging import urllib - logger = logging.getLogger(__name__) POSITIONAL_WARNING = "WARNING" diff --git a/googleapiclient/channel.py b/googleapiclient/channel.py index 70af779e59f..d6aa852c7e5 100644 --- a/googleapiclient/channel.py +++ b/googleapiclient/channel.py @@ -74,9 +74,8 @@ import datetime import uuid -from googleapiclient import errors from googleapiclient import _helpers as util - +from googleapiclient import errors # The unix time epoch starts at midnight 1970. EPOCH = datetime.datetime.utcfromtimestamp(0) @@ -111,28 +110,28 @@ def _upper_header_keys(headers): class Notification(object): """A Notification from a Channel. - Notifications are not usually constructed directly, but are returned - from functions like notification_from_headers(). + Notifications are not usually constructed directly, but are returned + from functions like notification_from_headers(). - Attributes: - message_number: int, The unique id number of this notification. - state: str, The state of the resource being monitored. - uri: str, The address of the resource being monitored. - resource_id: str, The unique identifier of the version of the resource at - this event. - """ + Attributes: + message_number: int, The unique id number of this notification. + state: str, The state of the resource being monitored. + uri: str, The address of the resource being monitored. + resource_id: str, The unique identifier of the version of the resource at + this event. + """ @util.positional(5) def __init__(self, message_number, state, resource_uri, resource_id): """Notification constructor. - Args: - message_number: int, The unique id number of this notification. - state: str, The state of the resource being monitored. Can be one - of "exists", "not_exists", or "sync". - resource_uri: str, The address of the resource being monitored. - resource_id: str, The identifier of the watched resource. - """ + Args: + message_number: int, The unique id number of this notification. + state: str, The state of the resource being monitored. Can be one + of "exists", "not_exists", or "sync". + resource_uri: str, The address of the resource being monitored. + resource_id: str, The identifier of the watched resource. + """ self.message_number = message_number self.state = state self.resource_uri = resource_uri @@ -142,53 +141,17 @@ def __init__(self, message_number, state, resource_uri, resource_id): class Channel(object): """A Channel for notifications. - Usually not constructed directly, instead it is returned from helper - functions like new_webhook_channel(). - - Attributes: - type: str, The type of delivery mechanism used by this channel. For - example, 'web_hook'. - id: str, A UUID for the channel. - token: str, An arbitrary string associated with the channel that - is delivered to the target address with each event delivered - over this channel. - address: str, The address of the receiving entity where events are - delivered. Specific to the channel type. - expiration: int, The time, in milliseconds from the epoch, when this - channel will expire. - params: dict, A dictionary of string to string, with additional parameters - controlling delivery channel behavior. - resource_id: str, An opaque id that identifies the resource that is - being watched. Stable across different API versions. - resource_uri: str, The canonicalized ID of the watched resource. - """ - - @util.positional(5) - def __init__( - self, - type, - id, - token, - address, - expiration=None, - params=None, - resource_id="", - resource_uri="", - ): - """Create a new Channel. - - In user code, this Channel constructor will not typically be called - manually since there are functions for creating channels for each specific - type with a more customized set of arguments to pass. + Usually not constructed directly, instead it is returned from helper + functions like new_webhook_channel(). - Args: + Attributes: type: str, The type of delivery mechanism used by this channel. For example, 'web_hook'. id: str, A UUID for the channel. token: str, An arbitrary string associated with the channel that is delivered to the target address with each event delivered over this channel. - address: str, The address of the receiving entity where events are + address: str, The address of the receiving entity where events are delivered. Specific to the channel type. expiration: int, The time, in milliseconds from the epoch, when this channel will expire. @@ -198,6 +161,42 @@ def __init__( being watched. Stable across different API versions. resource_uri: str, The canonicalized ID of the watched resource. """ + + @util.positional(5) + def __init__( + self, + type, + id, + token, + address, + expiration=None, + params=None, + resource_id="", + resource_uri="", + ): + """Create a new Channel. + + In user code, this Channel constructor will not typically be called + manually since there are functions for creating channels for each specific + type with a more customized set of arguments to pass. + + Args: + type: str, The type of delivery mechanism used by this channel. For + example, 'web_hook'. + id: str, A UUID for the channel. + token: str, An arbitrary string associated with the channel that + is delivered to the target address with each event delivered + over this channel. + address: str, The address of the receiving entity where events are + delivered. Specific to the channel type. + expiration: int, The time, in milliseconds from the epoch, when this + channel will expire. + params: dict, A dictionary of string to string, with additional parameters + controlling delivery channel behavior. + resource_id: str, An opaque id that identifies the resource that is + being watched. Stable across different API versions. + resource_uri: str, The canonicalized ID of the watched resource. + """ self.type = type self.id = id self.token = token @@ -210,12 +209,12 @@ def __init__( def body(self): """Build a body from the Channel. - Constructs a dictionary that's appropriate for passing into watch() - methods as the value of body argument. + Constructs a dictionary that's appropriate for passing into watch() + methods as the value of body argument. - Returns: - A dictionary representation of the channel. - """ + Returns: + A dictionary representation of the channel. + """ result = { "id": self.id, "token": self.token, @@ -236,13 +235,13 @@ def body(self): def update(self, resp): """Update a channel with information from the response of watch(). - When a request is sent to watch() a resource, the response returned - from the watch() request is a dictionary with updated channel information, - such as the resource_id, which is needed when stopping a subscription. + When a request is sent to watch() a resource, the response returned + from the watch() request is a dictionary with updated channel information, + such as the resource_id, which is needed when stopping a subscription. - Args: - resp: dict, The response from a watch() method. - """ + Args: + resp: dict, The response from a watch() method. + """ for json_name, param_name in CHANNEL_PARAMS.items(): value = resp.get(json_name) if value is not None: @@ -251,20 +250,20 @@ def update(self, resp): def notification_from_headers(channel, headers): """Parse a notification from the webhook request headers, validate - the notification, and return a Notification object. + the notification, and return a Notification object. - Args: - channel: Channel, The channel that the notification is associated with. - headers: dict, A dictionary like object that contains the request headers - from the webhook HTTP request. + Args: + channel: Channel, The channel that the notification is associated with. + headers: dict, A dictionary like object that contains the request headers + from the webhook HTTP request. - Returns: - A Notification object. + Returns: + A Notification object. - Raises: - errors.InvalidNotificationError if the notification is invalid. - ValueError if the X-GOOG-MESSAGE-NUMBER can't be converted to an int. - """ + Raises: + errors.InvalidNotificationError if the notification is invalid. + ValueError if the X-GOOG-MESSAGE-NUMBER can't be converted to an int. + """ headers = _upper_header_keys(headers) channel_id = headers[X_GOOG_CHANNEL_ID] if channel.id != channel_id: diff --git a/googleapiclient/discovery.py b/googleapiclient/discovery.py index 2a9b5f78877..c4d0eadb719 100644 --- a/googleapiclient/discovery.py +++ b/googleapiclient/discovery.py @@ -21,10 +21,11 @@ __author__ = "jcgregorio@google.com (Joe Gregorio)" __all__ = ["build", "build_from_document", "fix_method_name", "key2param"] -# Standard library imports -import copy from collections import OrderedDict import collections.abc + +# Standard library imports +import copy from email.generator import BytesGenerator from email.mime.multipart import MIMEMultipart from email.mime.nonmultipart import MIMENonMultipart @@ -38,44 +39,43 @@ import re import urllib -# Third-party imports -import httplib2 -import uritemplate import google.api_core.client_options -from google.auth.transport import mtls from google.auth.exceptions import MutualTLSChannelError +from google.auth.transport import mtls from google.oauth2 import service_account +# Third-party imports +import httplib2 +import uritemplate + try: import google_auth_httplib2 except ImportError: # pragma: NO COVER google_auth_httplib2 = None # Local imports -from googleapiclient import _auth -from googleapiclient import mimeparse -from googleapiclient.errors import HttpError -from googleapiclient.errors import InvalidJsonError -from googleapiclient.errors import MediaUploadSizeError -from googleapiclient.errors import UnacceptableMimeTypeError -from googleapiclient.errors import UnknownApiNameOrVersion -from googleapiclient.errors import UnknownFileType -from googleapiclient.http import build_http -from googleapiclient.http import BatchHttpRequest -from googleapiclient.http import HttpMock -from googleapiclient.http import HttpMockSequence -from googleapiclient.http import HttpRequest -from googleapiclient.http import MediaFileUpload -from googleapiclient.http import MediaUpload -from googleapiclient.model import JsonModel -from googleapiclient.model import MediaModel -from googleapiclient.model import RawModel +from googleapiclient import _auth, mimeparse +from googleapiclient._helpers import _add_query_parameter, positional +from googleapiclient.errors import ( + HttpError, + InvalidJsonError, + MediaUploadSizeError, + UnacceptableMimeTypeError, + UnknownApiNameOrVersion, + UnknownFileType, +) +from googleapiclient.http import ( + BatchHttpRequest, + HttpMock, + HttpMockSequence, + HttpRequest, + MediaFileUpload, + MediaUpload, + build_http, +) +from googleapiclient.model import JsonModel, MediaModel, RawModel from googleapiclient.schema import Schemas -from googleapiclient._helpers import _add_query_parameter -from googleapiclient._helpers import positional - - # The client library requires a version of httplib2 that supports RETRIES. httplib2.RETRIES = 1 @@ -134,13 +134,13 @@ class _BytesGenerator(BytesGenerator): def fix_method_name(name): """Fix method names to avoid '$' characters and reserved word conflicts. - Args: - name: string, method name. + Args: + name: string, method name. - Returns: - The name with '_' appended if the name is a reserved word and '$' and '-' - replaced with '_'. - """ + Returns: + The name with '_' appended if the name is a reserved word and '$' and '-' + replaced with '_'. + """ name = name.replace("$", "_").replace("-", "_") if keyword.iskeyword(name) or name in RESERVED_WORDS: return name + "_" @@ -151,14 +151,14 @@ def fix_method_name(name): def key2param(key): """Converts key names into parameter names. - For example, converting "max-results" -> "max_results" + For example, converting "max-results" -> "max_results" - Args: - key: string, the method key name. + Args: + key: string, the method key name. - Returns: - A safe method name based on the key name. - """ + Returns: + A safe method name based on the key name. + """ result = [] key = list(key) if not key[0].isalpha(): @@ -193,72 +193,72 @@ def build( ): """Construct a Resource for interacting with an API. - Construct a Resource object for interacting with an API. The serviceName and - version are the names from the Discovery service. - - Args: - serviceName: string, name of the service. - version: string, the version of the service. - http: httplib2.Http, An instance of httplib2.Http or something that acts - like it that HTTP requests will be made through. - discoveryServiceUrl: string, a URI Template that points to the location of - the discovery service. It should have two parameters {api} and - {apiVersion} that when filled in produce an absolute URI to the discovery - document for that service. - developerKey: string, key obtained from - https://code.google.com/apis/console. - model: googleapiclient.Model, converts to and from the wire format. - requestBuilder: googleapiclient.http.HttpRequest, encapsulator for an HTTP - request. - credentials: oauth2client.Credentials or - google.auth.credentials.Credentials, credentials to be used for - authentication. - cache_discovery: Boolean, whether or not to cache the discovery doc. - cache: googleapiclient.discovery_cache.base.CacheBase, an optional - cache object for the discovery documents. - client_options: Mapping object or google.api_core.client_options, client - options to set user options on the client. - (1) The API endpoint should be set through client_options. If API endpoint - is not set, `GOOGLE_API_USE_MTLS_ENDPOINT` environment variable can be used - to control which endpoint to use. - (2) client_cert_source is not supported, client cert should be provided using - client_encrypted_cert_source instead. In order to use the provided client - cert, `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable must be - set to `true`. - More details on the environment variables are here: - https://google.aip.dev/auth/4114 - adc_cert_path: str, client certificate file path to save the application - default client certificate for mTLS. This field is required if you want to - use the default client certificate. `GOOGLE_API_USE_CLIENT_CERTIFICATE` - environment variable must be set to `true` in order to use this field, - otherwise this field doesn't nothing. - More details on the environment variables are here: - https://google.aip.dev/auth/4114 - adc_key_path: str, client encrypted private key file path to save the - application default client encrypted private key for mTLS. This field is - required if you want to use the default client certificate. - `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable must be set to - `true` in order to use this field, otherwise this field doesn't nothing. - More details on the environment variables are here: - https://google.aip.dev/auth/4114 - num_retries: Integer, number of times to retry discovery with - randomized exponential backoff in case of intermittent/connection issues. - static_discovery: Boolean, whether or not to use the static discovery docs - included in the library. The default value for `static_discovery` depends - on the value of `discoveryServiceUrl`. `static_discovery` will default to - `True` when `discoveryServiceUrl` is also not provided, otherwise it will - default to `False`. - always_use_jwt_access: Boolean, whether always use self signed JWT for service - account credentials. This only applies to - google.oauth2.service_account.Credentials. - - Returns: - A Resource object with methods for interacting with the service. - - Raises: - google.auth.exceptions.MutualTLSChannelError: if there are any problems - setting up mutual TLS channel. - """ + Construct a Resource object for interacting with an API. The serviceName and + version are the names from the Discovery service. + + Args: + serviceName: string, name of the service. + version: string, the version of the service. + http: httplib2.Http, An instance of httplib2.Http or something that acts + like it that HTTP requests will be made through. + discoveryServiceUrl: string, a URI Template that points to the location of + the discovery service. It should have two parameters {api} and + {apiVersion} that when filled in produce an absolute URI to the discovery + document for that service. + developerKey: string, key obtained from + https://code.google.com/apis/console. + model: googleapiclient.Model, converts to and from the wire format. + requestBuilder: googleapiclient.http.HttpRequest, encapsulator for an HTTP + request. + credentials: oauth2client.Credentials or + google.auth.credentials.Credentials, credentials to be used for + authentication. + cache_discovery: Boolean, whether or not to cache the discovery doc. + cache: googleapiclient.discovery_cache.base.CacheBase, an optional + cache object for the discovery documents. + client_options: Mapping object or google.api_core.client_options, client + options to set user options on the client. + (1) The API endpoint should be set through client_options. If API endpoint + is not set, `GOOGLE_API_USE_MTLS_ENDPOINT` environment variable can be used + to control which endpoint to use. + (2) client_cert_source is not supported, client cert should be provided using + client_encrypted_cert_source instead. In order to use the provided client + cert, `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable must be + set to `true`. + More details on the environment variables are here: + https://google.aip.dev/auth/4114 + adc_cert_path: str, client certificate file path to save the application + default client certificate for mTLS. This field is required if you want to + use the default client certificate. `GOOGLE_API_USE_CLIENT_CERTIFICATE` + environment variable must be set to `true` in order to use this field, + otherwise this field doesn't nothing. + More details on the environment variables are here: + https://google.aip.dev/auth/4114 + adc_key_path: str, client encrypted private key file path to save the + application default client encrypted private key for mTLS. This field is + required if you want to use the default client certificate. + `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable must be set to + `true` in order to use this field, otherwise this field doesn't nothing. + More details on the environment variables are here: + https://google.aip.dev/auth/4114 + num_retries: Integer, number of times to retry discovery with + randomized exponential backoff in case of intermittent/connection issues. + static_discovery: Boolean, whether or not to use the static discovery docs + included in the library. The default value for `static_discovery` depends + on the value of `discoveryServiceUrl`. `static_discovery` will default to + `True` when `discoveryServiceUrl` is also not provided, otherwise it will + default to `False`. + always_use_jwt_access: Boolean, whether always use self signed JWT for service + account credentials. This only applies to + google.oauth2.service_account.Credentials. + + Returns: + A Resource object with methods for interacting with the service. + + Raises: + google.auth.exceptions.MutualTLSChannelError: if there are any problems + setting up mutual TLS channel. + """ params = {"api": serviceName, "apiVersion": version} # The default value for `static_discovery` depends on the value of @@ -328,16 +328,16 @@ def build( def _discovery_service_uri_options(discoveryServiceUrl, version): """ - Returns Discovery URIs to be used for attempting to build the API Resource. + Returns Discovery URIs to be used for attempting to build the API Resource. - Args: - discoveryServiceUrl: - string, the Original Discovery Service URL preferred by the customer. - version: - string, API Version requested + Args: + discoveryServiceUrl: + string, the Original Discovery Service URL preferred by the customer. + version: + string, API Version requested - Returns: - A list of URIs to be tried for the Service Discovery, in order. + Returns: + A list of URIs to be tried for the Service Discovery, in order. """ if discoveryServiceUrl is not None: @@ -361,29 +361,29 @@ def _retrieve_discovery_doc( cache=None, developerKey=None, num_retries=1, - static_discovery=True + static_discovery=True, ): """Retrieves the discovery_doc from cache or the internet. - Args: - url: string, the URL of the discovery document. - http: httplib2.Http, An instance of httplib2.Http or something that acts - like it through which HTTP requests will be made. - cache_discovery: Boolean, whether or not to cache the discovery doc. - serviceName: string, name of the service. - version: string, the version of the service. - cache: googleapiclient.discovery_cache.base.Cache, an optional cache - object for the discovery documents. - developerKey: string, Key for controlling API usage, generated - from the API Console. - num_retries: Integer, number of times to retry discovery with - randomized exponential backoff in case of intermittent/connection issues. - static_discovery: Boolean, whether or not to use the static discovery docs - included in the library. - - Returns: - A unicode string representation of the discovery document. - """ + Args: + url: string, the URL of the discovery document. + http: httplib2.Http, An instance of httplib2.Http or something that acts + like it through which HTTP requests will be made. + cache_discovery: Boolean, whether or not to cache the discovery doc. + serviceName: string, name of the service. + version: string, the version of the service. + cache: googleapiclient.discovery_cache.base.Cache, an optional cache + object for the discovery documents. + developerKey: string, Key for controlling API usage, generated + from the API Console. + num_retries: Integer, number of times to retry discovery with + randomized exponential backoff in case of intermittent/connection issues. + static_discovery: Boolean, whether or not to use the static discovery docs + included in the library. + + Returns: + A unicode string representation of the discovery document. + """ from . import discovery_cache if cache_discovery: @@ -401,7 +401,9 @@ def _retrieve_discovery_doc( if content: return content else: - raise UnknownApiNameOrVersion("name: %s version: %s" % (serviceName, version)) + raise UnknownApiNameOrVersion( + "name: %s version: %s" % (serviceName, version) + ) actual_url = url # REMOTE_ADDR is defined by the CGI spec [RFC3875] as the environment @@ -451,63 +453,63 @@ def build_from_document( ): """Create a Resource for interacting with an API. - Same as `build()`, but constructs the Resource object from a discovery - document that is it given, as opposed to retrieving one over HTTP. - - Args: - service: string or object, the JSON discovery document describing the API. - The value passed in may either be the JSON string or the deserialized - JSON. - base: string, base URI for all HTTP requests, usually the discovery URI. - This parameter is no longer used as rootUrl and servicePath are included - within the discovery document. (deprecated) - future: string, discovery document with future capabilities (deprecated). - http: httplib2.Http, An instance of httplib2.Http or something that acts - like it that HTTP requests will be made through. - developerKey: string, Key for controlling API usage, generated - from the API Console. - model: Model class instance that serializes and de-serializes requests and - responses. - requestBuilder: Takes an http request and packages it up to be executed. - credentials: oauth2client.Credentials or - google.auth.credentials.Credentials, credentials to be used for - authentication. - client_options: Mapping object or google.api_core.client_options, client - options to set user options on the client. - (1) The API endpoint should be set through client_options. If API endpoint - is not set, `GOOGLE_API_USE_MTLS_ENDPOINT` environment variable can be used - to control which endpoint to use. - (2) client_cert_source is not supported, client cert should be provided using - client_encrypted_cert_source instead. In order to use the provided client - cert, `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable must be - set to `true`. - More details on the environment variables are here: - https://google.aip.dev/auth/4114 - adc_cert_path: str, client certificate file path to save the application - default client certificate for mTLS. This field is required if you want to - use the default client certificate. `GOOGLE_API_USE_CLIENT_CERTIFICATE` - environment variable must be set to `true` in order to use this field, - otherwise this field doesn't nothing. - More details on the environment variables are here: - https://google.aip.dev/auth/4114 - adc_key_path: str, client encrypted private key file path to save the - application default client encrypted private key for mTLS. This field is - required if you want to use the default client certificate. - `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable must be set to - `true` in order to use this field, otherwise this field doesn't nothing. - More details on the environment variables are here: - https://google.aip.dev/auth/4114 - always_use_jwt_access: Boolean, whether always use self signed JWT for service - account credentials. This only applies to - google.oauth2.service_account.Credentials. - - Returns: - A Resource object with methods for interacting with the service. - - Raises: - google.auth.exceptions.MutualTLSChannelError: if there are any problems - setting up mutual TLS channel. - """ + Same as `build()`, but constructs the Resource object from a discovery + document that is it given, as opposed to retrieving one over HTTP. + + Args: + service: string or object, the JSON discovery document describing the API. + The value passed in may either be the JSON string or the deserialized + JSON. + base: string, base URI for all HTTP requests, usually the discovery URI. + This parameter is no longer used as rootUrl and servicePath are included + within the discovery document. (deprecated) + future: string, discovery document with future capabilities (deprecated). + http: httplib2.Http, An instance of httplib2.Http or something that acts + like it that HTTP requests will be made through. + developerKey: string, Key for controlling API usage, generated + from the API Console. + model: Model class instance that serializes and de-serializes requests and + responses. + requestBuilder: Takes an http request and packages it up to be executed. + credentials: oauth2client.Credentials or + google.auth.credentials.Credentials, credentials to be used for + authentication. + client_options: Mapping object or google.api_core.client_options, client + options to set user options on the client. + (1) The API endpoint should be set through client_options. If API endpoint + is not set, `GOOGLE_API_USE_MTLS_ENDPOINT` environment variable can be used + to control which endpoint to use. + (2) client_cert_source is not supported, client cert should be provided using + client_encrypted_cert_source instead. In order to use the provided client + cert, `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable must be + set to `true`. + More details on the environment variables are here: + https://google.aip.dev/auth/4114 + adc_cert_path: str, client certificate file path to save the application + default client certificate for mTLS. This field is required if you want to + use the default client certificate. `GOOGLE_API_USE_CLIENT_CERTIFICATE` + environment variable must be set to `true` in order to use this field, + otherwise this field doesn't nothing. + More details on the environment variables are here: + https://google.aip.dev/auth/4114 + adc_key_path: str, client encrypted private key file path to save the + application default client encrypted private key for mTLS. This field is + required if you want to use the default client certificate. + `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable must be set to + `true` in order to use this field, otherwise this field doesn't nothing. + More details on the environment variables are here: + https://google.aip.dev/auth/4114 + always_use_jwt_access: Boolean, whether always use self signed JWT for service + account credentials. This only applies to + google.oauth2.service_account.Credentials. + + Returns: + A Resource object with methods for interacting with the service. + + Raises: + google.auth.exceptions.MutualTLSChannelError: if there are any problems + setting up mutual TLS channel. + """ if client_options is None: client_options = google.api_core.client_options.ClientOptions() @@ -522,7 +524,9 @@ def build_from_document( ] for option, name in banned_options: if option is not None: - raise ValueError("Arguments http and {} are mutually exclusive".format(name)) + raise ValueError( + "Arguments http and {} are mutually exclusive".format(name) + ) if isinstance(service, str): service = json.loads(service) @@ -562,7 +566,7 @@ def build_from_document( if client_options.credentials_file and credentials: raise google.api_core.exceptions.DuplicateCredentialArgs( "client_options.credentials_file and credentials are mutually exclusive." - ) + ) # Check for credentials file via client options if client_options.credentials_file: credentials = _auth.credentials_from_file( @@ -647,7 +651,9 @@ def build_from_document( if "mtlsRootUrl" in service and ( not client_options or not client_options.api_endpoint ): - mtls_endpoint = urllib.parse.urljoin(service["mtlsRootUrl"], service["servicePath"]) + mtls_endpoint = urllib.parse.urljoin( + service["mtlsRootUrl"], service["servicePath"] + ) use_mtls_endpoint = os.getenv(GOOGLE_API_USE_MTLS_ENDPOINT, "auto") if not use_mtls_endpoint in ("never", "auto", "always"): @@ -681,18 +687,18 @@ def build_from_document( def _cast(value, schema_type): """Convert value to a string based on JSON Schema type. - See http://tools.ietf.org/html/draft-zyp-json-schema-03 for more details on - JSON Schema. + See http://tools.ietf.org/html/draft-zyp-json-schema-03 for more details on + JSON Schema. - Args: - value: any, the value to convert - schema_type: string, the type that value should be interpreted as + Args: + value: any, the value to convert + schema_type: string, the type that value should be interpreted as - Returns: - A string representation of 'value' based on the schema_type. - """ + Returns: + A string representation of 'value' based on the schema_type. + """ if schema_type == "string": - if type(value) == type("") or type(value) == type(u""): + if type(value) == type("") or type(value) == type(""): return value else: return str(value) @@ -703,7 +709,7 @@ def _cast(value, schema_type): elif schema_type == "boolean": return str(bool(value)).lower() else: - if type(value) == type("") or type(value) == type(u""): + if type(value) == type("") or type(value) == type(""): return value else: return str(value) @@ -712,12 +718,12 @@ def _cast(value, schema_type): def _media_size_to_long(maxSize): """Convert a string media size, such as 10GB or 3TB into an integer. - Args: - maxSize: string, size as a string, such as 2MB or 7GB. + Args: + maxSize: string, size as a string, such as 2MB or 7GB. - Returns: - The size as an integer value. - """ + Returns: + The size as an integer value. + """ if len(maxSize) < 2: return 0 units = maxSize[-2:].upper() @@ -731,17 +737,17 @@ def _media_size_to_long(maxSize): def _media_path_url_from_info(root_desc, path_url): """Creates an absolute media path URL. - Constructed using the API root URI and service path from the discovery - document and the relative path for the API method. + Constructed using the API root URI and service path from the discovery + document and the relative path for the API method. - Args: - root_desc: Dictionary; the entire original deserialized discovery document. - path_url: String; the relative URL for the API method. Relative to the API - root, which is specified in the discovery document. + Args: + root_desc: Dictionary; the entire original deserialized discovery document. + path_url: String; the relative URL for the API method. Relative to the API + root, which is specified in the discovery document. - Returns: - String; the absolute URI for media upload for the API method. - """ + Returns: + String; the absolute URI for media upload for the API method. + """ return "%(root)supload/%(service_path)s%(path)s" % { "root": root_desc["rootUrl"], "service_path": root_desc["servicePath"], @@ -752,27 +758,27 @@ def _media_path_url_from_info(root_desc, path_url): def _fix_up_parameters(method_desc, root_desc, http_method, schema): """Updates parameters of an API method with values specific to this library. - Specifically, adds whatever global parameters are specified by the API to the - parameters for the individual method. Also adds parameters which don't - appear in the discovery document, but are available to all discovery based - APIs (these are listed in STACK_QUERY_PARAMETERS). - - SIDE EFFECTS: This updates the parameters dictionary object in the method - description. - - Args: - method_desc: Dictionary with metadata describing an API method. Value comes - from the dictionary of methods stored in the 'methods' key in the - deserialized discovery document. - root_desc: Dictionary; the entire original deserialized discovery document. - http_method: String; the HTTP method used to call the API method described - in method_desc. - schema: Object, mapping of schema names to schema descriptions. - - Returns: - The updated Dictionary stored in the 'parameters' key of the method - description dictionary. - """ + Specifically, adds whatever global parameters are specified by the API to the + parameters for the individual method. Also adds parameters which don't + appear in the discovery document, but are available to all discovery based + APIs (these are listed in STACK_QUERY_PARAMETERS). + + SIDE EFFECTS: This updates the parameters dictionary object in the method + description. + + Args: + method_desc: Dictionary with metadata describing an API method. Value comes + from the dictionary of methods stored in the 'methods' key in the + deserialized discovery document. + root_desc: Dictionary; the entire original deserialized discovery document. + http_method: String; the HTTP method used to call the API method described + in method_desc. + schema: Object, mapping of schema names to schema descriptions. + + Returns: + The updated Dictionary stored in the 'parameters' key of the method + description dictionary. + """ parameters = method_desc.setdefault("parameters", {}) # Add in the parameters common to all methods. @@ -796,31 +802,31 @@ def _fix_up_parameters(method_desc, root_desc, http_method, schema): def _fix_up_media_upload(method_desc, root_desc, path_url, parameters): """Adds 'media_body' and 'media_mime_type' parameters if supported by method. - SIDE EFFECTS: If there is a 'mediaUpload' in the method description, adds - 'media_upload' key to parameters. - - Args: - method_desc: Dictionary with metadata describing an API method. Value comes - from the dictionary of methods stored in the 'methods' key in the - deserialized discovery document. - root_desc: Dictionary; the entire original deserialized discovery document. - path_url: String; the relative URL for the API method. Relative to the API - root, which is specified in the discovery document. - parameters: A dictionary describing method parameters for method described - in method_desc. - - Returns: - Triple (accept, max_size, media_path_url) where: - - accept is a list of strings representing what content types are - accepted for media upload. Defaults to empty list if not in the - discovery document. - - max_size is a long representing the max size in bytes allowed for a - media upload. Defaults to 0L if not in the discovery document. - - media_path_url is a String; the absolute URI for media upload for the - API method. Constructed using the API root URI and service path from - the discovery document and the relative path for the API method. If - media upload is not supported, this is None. - """ + SIDE EFFECTS: If there is a 'mediaUpload' in the method description, adds + 'media_upload' key to parameters. + + Args: + method_desc: Dictionary with metadata describing an API method. Value comes + from the dictionary of methods stored in the 'methods' key in the + deserialized discovery document. + root_desc: Dictionary; the entire original deserialized discovery document. + path_url: String; the relative URL for the API method. Relative to the API + root, which is specified in the discovery document. + parameters: A dictionary describing method parameters for method described + in method_desc. + + Returns: + Triple (accept, max_size, media_path_url) where: + - accept is a list of strings representing what content types are + accepted for media upload. Defaults to empty list if not in the + discovery document. + - max_size is a long representing the max size in bytes allowed for a + media upload. Defaults to 0L if not in the discovery document. + - media_path_url is a String; the absolute URI for media upload for the + API method. Constructed using the API root URI and service path from + the discovery document and the relative path for the API method. If + media upload is not supported, this is None. + """ media_upload = method_desc.get("mediaUpload", {}) accept = media_upload.get("accept", []) max_size = _media_size_to_long(media_upload.get("maxSize", "")) @@ -837,35 +843,35 @@ def _fix_up_media_upload(method_desc, root_desc, path_url, parameters): def _fix_up_method_description(method_desc, root_desc, schema): """Updates a method description in a discovery document. - SIDE EFFECTS: Changes the parameters dictionary in the method description with - extra parameters which are used locally. - - Args: - method_desc: Dictionary with metadata describing an API method. Value comes - from the dictionary of methods stored in the 'methods' key in the - deserialized discovery document. - root_desc: Dictionary; the entire original deserialized discovery document. - schema: Object, mapping of schema names to schema descriptions. - - Returns: - Tuple (path_url, http_method, method_id, accept, max_size, media_path_url) - where: - - path_url is a String; the relative URL for the API method. Relative to - the API root, which is specified in the discovery document. - - http_method is a String; the HTTP method used to call the API method - described in the method description. - - method_id is a String; the name of the RPC method associated with the - API method, and is in the method description in the 'id' key. - - accept is a list of strings representing what content types are - accepted for media upload. Defaults to empty list if not in the - discovery document. - - max_size is a long representing the max size in bytes allowed for a - media upload. Defaults to 0L if not in the discovery document. - - media_path_url is a String; the absolute URI for media upload for the - API method. Constructed using the API root URI and service path from - the discovery document and the relative path for the API method. If - media upload is not supported, this is None. - """ + SIDE EFFECTS: Changes the parameters dictionary in the method description with + extra parameters which are used locally. + + Args: + method_desc: Dictionary with metadata describing an API method. Value comes + from the dictionary of methods stored in the 'methods' key in the + deserialized discovery document. + root_desc: Dictionary; the entire original deserialized discovery document. + schema: Object, mapping of schema names to schema descriptions. + + Returns: + Tuple (path_url, http_method, method_id, accept, max_size, media_path_url) + where: + - path_url is a String; the relative URL for the API method. Relative to + the API root, which is specified in the discovery document. + - http_method is a String; the HTTP method used to call the API method + described in the method description. + - method_id is a String; the name of the RPC method associated with the + API method, and is in the method description in the 'id' key. + - accept is a list of strings representing what content types are + accepted for media upload. Defaults to empty list if not in the + discovery document. + - max_size is a long representing the max size in bytes allowed for a + media upload. Defaults to 0L if not in the discovery document. + - media_path_url is a String; the absolute URI for media upload for the + API method. Constructed using the API root URI and service path from + the discovery document and the relative path for the API method. If + media upload is not supported, this is None. + """ path_url = method_desc["path"] http_method = method_desc["httpMethod"] method_id = method_desc["id"] @@ -902,38 +908,38 @@ def _urljoin(base, url): class ResourceMethodParameters(object): """Represents the parameters associated with a method. - Attributes: - argmap: Map from method parameter name (string) to query parameter name - (string). - required_params: List of required parameters (represented by parameter - name as string). - repeated_params: List of repeated parameters (represented by parameter - name as string). - pattern_params: Map from method parameter name (string) to regular - expression (as a string). If the pattern is set for a parameter, the - value for that parameter must match the regular expression. - query_params: List of parameters (represented by parameter name as string) - that will be used in the query string. - path_params: Set of parameters (represented by parameter name as string) - that will be used in the base URL path. - param_types: Map from method parameter name (string) to parameter type. Type - can be any valid JSON schema type; valid values are 'any', 'array', - 'boolean', 'integer', 'number', 'object', or 'string'. Reference: - http://tools.ietf.org/html/draft-zyp-json-schema-03#section-5.1 - enum_params: Map from method parameter name (string) to list of strings, - where each list of strings is the list of acceptable enum values. - """ + Attributes: + argmap: Map from method parameter name (string) to query parameter name + (string). + required_params: List of required parameters (represented by parameter + name as string). + repeated_params: List of repeated parameters (represented by parameter + name as string). + pattern_params: Map from method parameter name (string) to regular + expression (as a string). If the pattern is set for a parameter, the + value for that parameter must match the regular expression. + query_params: List of parameters (represented by parameter name as string) + that will be used in the query string. + path_params: Set of parameters (represented by parameter name as string) + that will be used in the base URL path. + param_types: Map from method parameter name (string) to parameter type. Type + can be any valid JSON schema type; valid values are 'any', 'array', + 'boolean', 'integer', 'number', 'object', or 'string'. Reference: + http://tools.ietf.org/html/draft-zyp-json-schema-03#section-5.1 + enum_params: Map from method parameter name (string) to list of strings, + where each list of strings is the list of acceptable enum values. + """ def __init__(self, method_desc): """Constructor for ResourceMethodParameters. - Sets default values and defers to set_parameters to populate. + Sets default values and defers to set_parameters to populate. - Args: - method_desc: Dictionary with metadata describing an API method. Value - comes from the dictionary of methods stored in the 'methods' key in - the deserialized discovery document. - """ + Args: + method_desc: Dictionary with metadata describing an API method. Value + comes from the dictionary of methods stored in the 'methods' key in + the deserialized discovery document. + """ self.argmap = {} self.required_params = [] self.repeated_params = [] @@ -950,14 +956,14 @@ def __init__(self, method_desc): def set_parameters(self, method_desc): """Populates maps and lists based on method description. - Iterates through each parameter for the method and parses the values from - the parameter dictionary. + Iterates through each parameter for the method and parses the values from + the parameter dictionary. - Args: - method_desc: Dictionary with metadata describing an API method. Value - comes from the dictionary of methods stored in the 'methods' key in - the deserialized discovery document. - """ + Args: + method_desc: Dictionary with metadata describing an API method. Value + comes from the dictionary of methods stored in the 'methods' key in + the deserialized discovery document. + """ parameters = method_desc.get("parameters", {}) sorted_parameters = OrderedDict(sorted(parameters.items())) for arg, desc in sorted_parameters.items(): @@ -992,13 +998,13 @@ def set_parameters(self, method_desc): def createMethod(methodName, methodDesc, rootDesc, schema): """Creates a method for attaching to a Resource. - Args: - methodName: string, name of the method to use. - methodDesc: object, fragment of deserialized discovery document that - describes the method. - rootDesc: object, the entire deserialized discovery document. - schema: object, mapping of schema names to schema descriptions. - """ + Args: + methodName: string, name of the method to use. + methodDesc: object, fragment of deserialized discovery document that + describes the method. + rootDesc: object, the entire deserialized discovery document. + schema: object, mapping of schema names to schema descriptions. + """ methodName = fix_method_name(methodName) ( pathUrl, @@ -1016,7 +1022,7 @@ def method(self, **kwargs): for name in kwargs: if name not in parameters.argmap: - raise TypeError('Got an unexpected keyword argument {}'.format(name)) + raise TypeError("Got an unexpected keyword argument {}".format(name)) # Remove args that have a value of None. keys = list(kwargs.keys()) @@ -1256,28 +1262,28 @@ def createNextMethod( ): """Creates any _next methods for attaching to a Resource. - The _next methods allow for easy iteration through list() responses. + The _next methods allow for easy iteration through list() responses. - Args: - methodName: string, name of the method to use. - pageTokenName: string, name of request page token field. - nextPageTokenName: string, name of response page token field. - isPageTokenParameter: Boolean, True if request page token is a query - parameter, False if request page token is a field of the request body. - """ + Args: + methodName: string, name of the method to use. + pageTokenName: string, name of request page token field. + nextPageTokenName: string, name of response page token field. + isPageTokenParameter: Boolean, True if request page token is a query + parameter, False if request page token is a field of the request body. + """ methodName = fix_method_name(methodName) def methodNext(self, previous_request, previous_response): """Retrieves the next page of results. -Args: - previous_request: The request for the previous page. (required) - previous_response: The response from the request for the previous page. (required) + Args: + previous_request: The request for the previous page. (required) + previous_response: The response from the request for the previous page. (required) -Returns: - A request object that you can call 'execute()' on to request the next - page. Returns None if there are no more items in the collection. - """ + Returns: + A request object that you can call 'execute()' on to request the next + page. Returns None if there are no more items in the collection. + """ # Retrieve nextPageToken from previous_response # Use as pageToken in previous_request to create new request. @@ -1301,7 +1307,7 @@ def methodNext(self, previous_request, previous_response): request.body = model.serialize(body) request.body_size = len(request.body) if "content-length" in request.headers: - del request.headers["content-length"] + del request.headers["content-length"] logger.debug("Next page request body: %s %s" % (methodName, body)) return request @@ -1325,21 +1331,21 @@ def __init__( ): """Build a Resource from the API description. - Args: - http: httplib2.Http, Object to make http requests with. - baseUrl: string, base URL for the API. All requests are relative to this - URI. - model: googleapiclient.Model, converts to and from the wire format. - requestBuilder: class or callable that instantiates an - googleapiclient.HttpRequest object. - developerKey: string, key obtained from - https://code.google.com/apis/console - resourceDesc: object, section of deserialized discovery document that - describes a resource. Note that the top level discovery document - is considered a resource. - rootDesc: object, the entire deserialized discovery document. - schema: object, mapping of schema names to schema descriptions. - """ + Args: + http: httplib2.Http, Object to make http requests with. + baseUrl: string, base URL for the API. All requests are relative to this + URI. + model: googleapiclient.Model, converts to and from the wire format. + requestBuilder: class or callable that instantiates an + googleapiclient.HttpRequest object. + developerKey: string, key obtained from + https://code.google.com/apis/console + resourceDesc: object, section of deserialized discovery document that + describes a resource. Note that the top level discovery document + is considered a resource. + rootDesc: object, the entire deserialized discovery document. + schema: object, mapping of schema names to schema descriptions. + """ self._dynamic_attrs = [] self._http = http @@ -1356,19 +1362,19 @@ def __init__( def _set_dynamic_attr(self, attr_name, value): """Sets an instance attribute and tracks it in a list of dynamic attributes. - Args: - attr_name: string; The name of the attribute to be set - value: The value being set on the object and tracked in the dynamic cache. - """ + Args: + attr_name: string; The name of the attribute to be set + value: The value being set on the object and tracked in the dynamic cache. + """ self._dynamic_attrs.append(attr_name) self.__dict__[attr_name] = value def __getstate__(self): """Trim the state down to something that can be pickled. - Uses the fact that the instance variable _dynamic_attrs holds attrs that - will be wiped and restored on pickle serialization. - """ + Uses the fact that the instance variable _dynamic_attrs holds attrs that + will be wiped and restored on pickle serialization. + """ state_dict = copy.copy(self.__dict__) for dynamic_attr in self._dynamic_attrs: del state_dict[dynamic_attr] @@ -1378,14 +1384,13 @@ def __getstate__(self): def __setstate__(self, state): """Reconstitute the state of the object from being pickled. - Uses the fact that the instance variable _dynamic_attrs holds attrs that - will be wiped and restored on pickle serialization. - """ + Uses the fact that the instance variable _dynamic_attrs holds attrs that + will be wiped and restored on pickle serialization. + """ self.__dict__.update(state) self._dynamic_attrs = [] self._set_service_methods() - def __enter__(self): return self @@ -1415,17 +1420,17 @@ def _add_basic_methods(self, resourceDesc, rootDesc, schema): def new_batch_http_request(callback=None): """Create a BatchHttpRequest object based on the discovery document. - Args: - callback: callable, A callback to be called for each response, of the - form callback(id, response, exception). The first parameter is the - request id, and the second is the deserialized response object. The - third is an apiclient.errors.HttpError exception object if an HTTP - error occurred while processing the request, or None if no error - occurred. - - Returns: - A BatchHttpRequest object based on the discovery document. - """ + Args: + callback: callable, A callback to be called for each response, of the + form callback(id, response, exception). The first parameter is the + request id, and the second is the deserialized response object. The + third is an apiclient.errors.HttpError exception object if an HTTP + error occurred while processing the request, or None if no error + occurred. + + Returns: + A BatchHttpRequest object based on the discovery document. + """ return BatchHttpRequest(callback=callback, batch_uri=batch_uri) self._set_dynamic_attr("new_batch_http_request", new_batch_http_request) @@ -1456,11 +1461,11 @@ def _add_nested_resources(self, resourceDesc, rootDesc, schema): def createResourceMethod(methodName, methodDesc): """Create a method on the Resource to access a nested Resource. - Args: - methodName: string, name of the method to use. - methodDesc: object, fragment of deserialized discovery document that - describes the method. - """ + Args: + methodName: string, name of the method to use. + methodDesc: object, fragment of deserialized discovery document that + describes the method. + """ methodName = fix_method_name(methodName) def methodResource(self): @@ -1521,13 +1526,13 @@ def _add_next_methods(self, resourceDesc, schema): def _findPageTokenName(fields): """Search field names for one like a page token. - Args: - fields: container of string, names of fields. + Args: + fields: container of string, names of fields. - Returns: - First name that is either 'pageToken' or 'nextPageToken' if one exists, - otherwise None. - """ + Returns: + First name that is either 'pageToken' or 'nextPageToken' if one exists, + otherwise None. + """ return next( (tokenName for tokenName in _PAGE_TOKEN_NAMES if tokenName in fields), None ) @@ -1536,17 +1541,17 @@ def _findPageTokenName(fields): def _methodProperties(methodDesc, schema, name): """Get properties of a field in a method description. - Args: - methodDesc: object, fragment of deserialized discovery document that - describes the method. - schema: object, mapping of schema names to schema descriptions. - name: string, name of top-level field in method description. - - Returns: - Object representing fragment of deserialized discovery document - corresponding to 'properties' field of object corresponding to named field - in method description, if it exists, otherwise empty dict. - """ + Args: + methodDesc: object, fragment of deserialized discovery document that + describes the method. + schema: object, mapping of schema names to schema descriptions. + name: string, name of top-level field in method description. + + Returns: + Object representing fragment of deserialized discovery document + corresponding to 'properties' field of object corresponding to named field + in method description, if it exists, otherwise empty dict. + """ desc = methodDesc.get(name, {}) if "$ref" in desc: desc = schema.get(desc["$ref"], {}) diff --git a/googleapiclient/discovery_cache/__init__.py b/googleapiclient/discovery_cache/__init__.py index 3f59e73bb1c..a2771cd7e5b 100644 --- a/googleapiclient/discovery_cache/__init__.py +++ b/googleapiclient/discovery_cache/__init__.py @@ -16,26 +16,29 @@ from __future__ import absolute_import -import logging import datetime +import logging import os LOGGER = logging.getLogger(__name__) DISCOVERY_DOC_MAX_AGE = 60 * 60 * 24 # 1 day -DISCOVERY_DOC_DIR = os.path.join(os.path.dirname( - os.path.realpath(__file__)), 'documents') +DISCOVERY_DOC_DIR = os.path.join( + os.path.dirname(os.path.realpath(__file__)), "documents" +) + def autodetect(): """Detects an appropriate cache module and returns it. - Returns: - googleapiclient.discovery_cache.base.Cache, a cache object which - is auto detected, or None if no cache object is available. - """ - if 'APPENGINE_RUNTIME' in os.environ: + Returns: + googleapiclient.discovery_cache.base.Cache, a cache object which + is auto detected, or None if no cache object is available. + """ + if "APPENGINE_RUNTIME" in os.environ: try: from google.appengine.api import memcache + from . import appengine_memcache return appengine_memcache.cache @@ -46,10 +49,12 @@ def autodetect(): return file_cache.cache except Exception: - LOGGER.info("file_cache is only supported with oauth2client<4.0.0", - exc_info=False) + LOGGER.info( + "file_cache is only supported with oauth2client<4.0.0", exc_info=False + ) return None + def get_static_doc(serviceName, version): """Retrieves the discovery document from the directory defined in DISCOVERY_DOC_DIR corresponding to the serviceName and version provided. @@ -67,11 +72,10 @@ def get_static_doc(serviceName, version): doc_name = "{}.{}.json".format(serviceName, version) try: - with open(os.path.join(DISCOVERY_DOC_DIR, doc_name), 'r') as f: + with open(os.path.join(DISCOVERY_DOC_DIR, doc_name), "r") as f: content = f.read() except FileNotFoundError: # File does not exist. Nothing to do here. pass return content - diff --git a/googleapiclient/discovery_cache/appengine_memcache.py b/googleapiclient/discovery_cache/appengine_memcache.py index 1d18d7abbb5..73a1decb96e 100644 --- a/googleapiclient/discovery_cache/appengine_memcache.py +++ b/googleapiclient/discovery_cache/appengine_memcache.py @@ -23,7 +23,6 @@ from . import base from ..discovery_cache import DISCOVERY_DOC_MAX_AGE - LOGGER = logging.getLogger(__name__) NAMESPACE = "google-api-client" @@ -35,9 +34,9 @@ class Cache(base.Cache): def __init__(self, max_age): """Constructor. - Args: - max_age: Cache expiration in seconds. - """ + Args: + max_age: Cache expiration in seconds. + """ self._max_age = max_age def get(self, url): diff --git a/googleapiclient/discovery_cache/base.py b/googleapiclient/discovery_cache/base.py index fbe44592416..41f3f3f0af5 100644 --- a/googleapiclient/discovery_cache/base.py +++ b/googleapiclient/discovery_cache/base.py @@ -26,21 +26,21 @@ class Cache(object): def get(self, url): """Gets the content from the memcache with a given key. - Args: - url: string, the key for the cache. + Args: + url: string, the key for the cache. - Returns: - object, the value in the cache for the given key, or None if the key is - not in the cache. - """ + Returns: + object, the value in the cache for the given key, or None if the key is + not in the cache. + """ raise NotImplementedError() @abc.abstractmethod def set(self, url, content): """Sets the given key and content in the cache. - Args: - url: string, the key for the cache. - content: string, the discovery document. - """ + Args: + url: string, the key for the cache. + content: string, the discovery document. + """ raise NotImplementedError() diff --git a/googleapiclient/discovery_cache/file_cache.py b/googleapiclient/discovery_cache/file_cache.py index 36eb29a39c3..84d24c18dcf 100644 --- a/googleapiclient/discovery_cache/file_cache.py +++ b/googleapiclient/discovery_cache/file_cache.py @@ -58,8 +58,8 @@ def _to_timestamp(date): # See also: https://docs.python.org/2/library/datetime.html delta = date - EPOCH return ( - delta.microseconds + (delta.seconds + delta.days * 24 * 3600) * 10 ** 6 - ) / 10 ** 6 + delta.microseconds + (delta.seconds + delta.days * 24 * 3600) * 10**6 + ) / 10**6 def _read_or_initialize_cache(f): @@ -82,9 +82,9 @@ class Cache(base.Cache): def __init__(self, max_age): """Constructor. - Args: - max_age: Cache expiration in seconds. - """ + Args: + max_age: Cache expiration in seconds. + """ self._max_age = max_age self._file = os.path.join(tempfile.gettempdir(), FILENAME) f = LockedFile(self._file, "a+", "r") diff --git a/googleapiclient/errors.py b/googleapiclient/errors.py index 385558c4897..288594a68a5 100644 --- a/googleapiclient/errors.py +++ b/googleapiclient/errors.py @@ -61,7 +61,14 @@ def _get_reason(self): data = self.content.decode("utf-8") if isinstance(data, dict): reason = data["error"]["message"] - error_detail_keyword = next((kw for kw in ["detail", "details", "errors", "message"] if kw in data["error"]), "") + error_detail_keyword = next( + ( + kw + for kw in ["detail", "details", "errors", "message"] + if kw in data["error"] + ), + "", + ) if error_detail_keyword: self.error_details = data["error"][error_detail_keyword] elif isinstance(data, list) and len(data) > 0: diff --git a/googleapiclient/http.py b/googleapiclient/http.py index 927464e9ff7..187f6f5dac8 100644 --- a/googleapiclient/http.py +++ b/googleapiclient/http.py @@ -23,7 +23,6 @@ __author__ = "jcgregorio@google.com (Joe Gregorio)" import copy -import httplib2 import http.client as http_client import io import json @@ -36,6 +35,8 @@ import urllib import uuid +import httplib2 + # TODO(issue 221): Remove this conditional import jibbajabba. try: import ssl @@ -49,18 +50,18 @@ from email.mime.nonmultipart import MIMENonMultipart from email.parser import FeedParser -from googleapiclient import _helpers as util - from googleapiclient import _auth -from googleapiclient.errors import BatchError -from googleapiclient.errors import HttpError -from googleapiclient.errors import InvalidChunkSizeError -from googleapiclient.errors import ResumableUploadError -from googleapiclient.errors import UnexpectedBodyError -from googleapiclient.errors import UnexpectedMethodError +from googleapiclient import _helpers as util +from googleapiclient.errors import ( + BatchError, + HttpError, + InvalidChunkSizeError, + ResumableUploadError, + UnexpectedBodyError, + UnexpectedMethodError, +) from googleapiclient.model import JsonModel - LOGGER = logging.getLogger(__name__) DEFAULT_CHUNK_SIZE = 100 * 1024 * 1024 @@ -172,7 +173,7 @@ def _retry_request( for retry_num in range(num_retries + 1): if retry_num > 0: # Sleep before retrying. - sleep_time = rand() * 2 ** retry_num + sleep_time = rand() * 2**retry_num LOGGER.warning( "Sleeping %.2f seconds before retry %d of %d for %s: %s %s, after %s", sleep_time, @@ -1073,7 +1074,7 @@ def next_chunk(self, http=None, num_retries=0): for retry_num in range(num_retries + 1): if retry_num > 0: - self._sleep(self._rand() * 2 ** retry_num) + self._sleep(self._rand() * 2**retry_num) LOGGER.warning( "Retry #%d for media upload: %s %s, following status: %d" % (retry_num, self.method, self.uri, resp.status) diff --git a/googleapiclient/mimeparse.py b/googleapiclient/mimeparse.py index a1056677c45..d3dedee9c53 100644 --- a/googleapiclient/mimeparse.py +++ b/googleapiclient/mimeparse.py @@ -22,6 +22,7 @@ from a list of candidates. """ from __future__ import absolute_import + from functools import reduce __version__ = "0.1.3" @@ -40,7 +41,7 @@ def parse_mime_type(mime_type): into: ('application', 'xhtml', {'q', '0.5'}) - """ + """ parts = mime_type.split(";") params = dict( [tuple([s.strip() for s in param.split("=", 1)]) for param in parts[1:]] diff --git a/googleapiclient/model.py b/googleapiclient/model.py index 3d1f397692f..4ba27522357 100644 --- a/googleapiclient/model.py +++ b/googleapiclient/model.py @@ -46,59 +46,59 @@ def _abstract(): class Model(object): """Model base class. - All Model classes should implement this interface. - The Model serializes and de-serializes between a wire - format such as JSON and a Python object representation. - """ + All Model classes should implement this interface. + The Model serializes and de-serializes between a wire + format such as JSON and a Python object representation. + """ def request(self, headers, path_params, query_params, body_value): """Updates outgoing requests with a serialized body. - Args: - headers: dict, request headers - path_params: dict, parameters that appear in the request path - query_params: dict, parameters that appear in the query - body_value: object, the request body as a Python object, which must be - serializable. - Returns: - A tuple of (headers, path_params, query, body) - - headers: dict, request headers - path_params: dict, parameters that appear in the request path - query: string, query part of the request URI - body: string, the body serialized in the desired wire format. - """ + Args: + headers: dict, request headers + path_params: dict, parameters that appear in the request path + query_params: dict, parameters that appear in the query + body_value: object, the request body as a Python object, which must be + serializable. + Returns: + A tuple of (headers, path_params, query, body) + + headers: dict, request headers + path_params: dict, parameters that appear in the request path + query: string, query part of the request URI + body: string, the body serialized in the desired wire format. + """ _abstract() def response(self, resp, content): """Convert the response wire format into a Python object. - Args: - resp: httplib2.Response, the HTTP response headers and status - content: string, the body of the HTTP response + Args: + resp: httplib2.Response, the HTTP response headers and status + content: string, the body of the HTTP response - Returns: - The body de-serialized as a Python object. + Returns: + The body de-serialized as a Python object. - Raises: - googleapiclient.errors.HttpError if a non 2xx response is received. - """ + Raises: + googleapiclient.errors.HttpError if a non 2xx response is received. + """ _abstract() class BaseModel(Model): """Base model class. - Subclasses should provide implementations for the "serialize" and - "deserialize" methods, as well as values for the following class attributes. + Subclasses should provide implementations for the "serialize" and + "deserialize" methods, as well as values for the following class attributes. - Attributes: - accept: The value to use for the HTTP Accept header. - content_type: The value to use for the HTTP Content-type header. - no_content_response: The value to return when deserializing a 204 "No - Content" response. - alt_param: The value to supply as the "alt" query parameter for requests. - """ + Attributes: + accept: The value to use for the HTTP Accept header. + content_type: The value to use for the HTTP Content-type header. + no_content_response: The value to return when deserializing a 204 "No + Content" response. + alt_param: The value to supply as the "alt" query parameter for requests. + """ accept = None content_type = None @@ -124,20 +124,20 @@ def _log_request(self, headers, path_params, query, body): def request(self, headers, path_params, query_params, body_value): """Updates outgoing requests with a serialized body. - Args: - headers: dict, request headers - path_params: dict, parameters that appear in the request path - query_params: dict, parameters that appear in the query - body_value: object, the request body as a Python object, which must be - serializable by json. - Returns: - A tuple of (headers, path_params, query, body) - - headers: dict, request headers - path_params: dict, parameters that appear in the request path - query: string, query part of the request URI - body: string, the body serialized as JSON - """ + Args: + headers: dict, request headers + path_params: dict, parameters that appear in the request path + query_params: dict, parameters that appear in the query + body_value: object, the request body as a Python object, which must be + serializable by json. + Returns: + A tuple of (headers, path_params, query, body) + + headers: dict, request headers + path_params: dict, parameters that appear in the request path + query: string, query part of the request URI + body: string, the body serialized as JSON + """ query = self._build_query(query_params) headers["accept"] = self.accept headers["accept-encoding"] = "gzip, deflate" @@ -164,12 +164,12 @@ def request(self, headers, path_params, query_params, body_value): def _build_query(self, params): """Builds a query string. - Args: - params: dict, the query parameters + Args: + params: dict, the query parameters - Returns: - The query parameters properly encoded into an HTTP URI query string. - """ + Returns: + The query parameters properly encoded into an HTTP URI query string. + """ if self.alt_param is not None: params.update({"alt": self.alt_param}) astuples = [] @@ -197,16 +197,16 @@ def _log_response(self, resp, content): def response(self, resp, content): """Convert the response wire format into a Python object. - Args: - resp: httplib2.Response, the HTTP response headers and status - content: string, the body of the HTTP response + Args: + resp: httplib2.Response, the HTTP response headers and status + content: string, the body of the HTTP response - Returns: - The body de-serialized as a Python object. + Returns: + The body de-serialized as a Python object. - Raises: - googleapiclient.errors.HttpError if a non 2xx response is received. - """ + Raises: + googleapiclient.errors.HttpError if a non 2xx response is received. + """ self._log_response(resp, content) # Error handling is TBD, for example, do we retry # for some operation/error combinations? @@ -223,33 +223,33 @@ def response(self, resp, content): def serialize(self, body_value): """Perform the actual Python object serialization. - Args: - body_value: object, the request body as a Python object. + Args: + body_value: object, the request body as a Python object. - Returns: - string, the body in serialized form. - """ + Returns: + string, the body in serialized form. + """ _abstract() def deserialize(self, content): """Perform the actual deserialization from response string to Python - object. + object. - Args: - content: string, the body of the HTTP response + Args: + content: string, the body of the HTTP response - Returns: - The body de-serialized as a Python object. - """ + Returns: + The body de-serialized as a Python object. + """ _abstract() class JsonModel(BaseModel): """Model class for JSON. - Serializes and de-serializes between JSON and the Python - object representation of HTTP request and response bodies. - """ + Serializes and de-serializes between JSON and the Python + object representation of HTTP request and response bodies. + """ accept = "application/json" content_type = "application/json" @@ -258,9 +258,9 @@ class JsonModel(BaseModel): def __init__(self, data_wrapper=False): """Construct a JsonModel. - Args: - data_wrapper: boolean, wrap requests and responses in a data wrapper - """ + Args: + data_wrapper: boolean, wrap requests and responses in a data wrapper + """ self._data_wrapper = data_wrapper def serialize(self, body_value): @@ -294,10 +294,10 @@ def no_content_response(self): class RawModel(JsonModel): """Model class for requests that don't return JSON. - Serializes and de-serializes between JSON and the Python - object representation of HTTP request, and returns the raw bytes - of the response body. - """ + Serializes and de-serializes between JSON and the Python + object representation of HTTP request, and returns the raw bytes + of the response body. + """ accept = "*/*" content_type = "application/json" @@ -314,10 +314,10 @@ def no_content_response(self): class MediaModel(JsonModel): """Model class for requests that return Media. - Serializes and de-serializes between JSON and the Python - object representation of HTTP request, and returns the raw bytes - of the response body. - """ + Serializes and de-serializes between JSON and the Python + object representation of HTTP request, and returns the raw bytes + of the response body. + """ accept = "*/*" content_type = "application/json" @@ -334,9 +334,9 @@ def no_content_response(self): class ProtocolBufferModel(BaseModel): """Model class for protocol buffers. - Serializes and de-serializes the binary protocol buffer sent in the HTTP - request and response bodies. - """ + Serializes and de-serializes the binary protocol buffer sent in the HTTP + request and response bodies. + """ accept = "application/x-protobuf" content_type = "application/x-protobuf" @@ -345,13 +345,13 @@ class ProtocolBufferModel(BaseModel): def __init__(self, protocol_buffer): """Constructs a ProtocolBufferModel. - The serialized protocol buffer returned in an HTTP response will be - de-serialized using the given protocol buffer class. + The serialized protocol buffer returned in an HTTP response will be + de-serialized using the given protocol buffer class. - Args: - protocol_buffer: The protocol buffer class used to de-serialize a - response from the API. - """ + Args: + protocol_buffer: The protocol buffer class used to de-serialize a + response from the API. + """ self._protocol_buffer = protocol_buffer def serialize(self, body_value): @@ -368,24 +368,24 @@ def no_content_response(self): def makepatch(original, modified): """Create a patch object. - Some methods support PATCH, an efficient way to send updates to a resource. - This method allows the easy construction of patch bodies by looking at the - differences between a resource before and after it was modified. - - Args: - original: object, the original deserialized resource - modified: object, the modified deserialized resource - Returns: - An object that contains only the changes from original to modified, in a - form suitable to pass to a PATCH method. - - Example usage: - item = service.activities().get(postid=postid, userid=userid).execute() - original = copy.deepcopy(item) - item['object']['content'] = 'This is updated.' - service.activities.patch(postid=postid, userid=userid, - body=makepatch(original, item)).execute() - """ + Some methods support PATCH, an efficient way to send updates to a resource. + This method allows the easy construction of patch bodies by looking at the + differences between a resource before and after it was modified. + + Args: + original: object, the original deserialized resource + modified: object, the modified deserialized resource + Returns: + An object that contains only the changes from original to modified, in a + form suitable to pass to a PATCH method. + + Example usage: + item = service.activities().get(postid=postid, userid=userid).execute() + original = copy.deepcopy(item) + item['object']['content'] = 'This is updated.' + service.activities.patch(postid=postid, userid=userid, + body=makepatch(original, item)).execute() + """ patch = {} for key, original_value in original.items(): modified_value = modified.get(key, None) diff --git a/googleapiclient/sample_tools.py b/googleapiclient/sample_tools.py index 2b6a21b268c..bdad0a2ae87 100644 --- a/googleapiclient/sample_tools.py +++ b/googleapiclient/sample_tools.py @@ -34,31 +34,29 @@ def init( ): """A common initialization routine for samples. - Many of the sample applications do the same initialization, which has now - been consolidated into this function. This function uses common idioms found - in almost all the samples, i.e. for an API with name 'apiname', the - credentials are stored in a file named apiname.dat, and the - client_secrets.json file is stored in the same directory as the application - main file. - - Args: - argv: list of string, the command-line parameters of the application. - name: string, name of the API. - version: string, version of the API. - doc: string, description of the application. Usually set to __doc__. - file: string, filename of the application. Usually set to __file__. - parents: list of argparse.ArgumentParser, additional command-line flags. - scope: string, The OAuth scope used. - discovery_filename: string, name of local discovery file (JSON). Use when discovery doc not available via URL. - - Returns: - A tuple of (service, flags), where service is the service object and flags - is the parsed command-line flags. - """ + Many of the sample applications do the same initialization, which has now + been consolidated into this function. This function uses common idioms found + in almost all the samples, i.e. for an API with name 'apiname', the + credentials are stored in a file named apiname.dat, and the + client_secrets.json file is stored in the same directory as the application + main file. + + Args: + argv: list of string, the command-line parameters of the application. + name: string, name of the API. + version: string, version of the API. + doc: string, description of the application. Usually set to __doc__. + file: string, filename of the application. Usually set to __file__. + parents: list of argparse.ArgumentParser, additional command-line flags. + scope: string, The OAuth scope used. + discovery_filename: string, name of local discovery file (JSON). Use when discovery doc not available via URL. + + Returns: + A tuple of (service, flags), where service is the service object and flags + is the parsed command-line flags. + """ try: - from oauth2client import client - from oauth2client import file - from oauth2client import tools + from oauth2client import client, file, tools except ImportError: raise ImportError( "googleapiclient.sample_tools requires oauth2client. Please install oauth2client and try again." diff --git a/googleapiclient/schema.py b/googleapiclient/schema.py index 95767ef55c8..93b07df44ac 100644 --- a/googleapiclient/schema.py +++ b/googleapiclient/schema.py @@ -64,6 +64,7 @@ from collections import OrderedDict + from googleapiclient import _helpers as util diff --git a/googleapiclient/version.py b/googleapiclient/version.py index f69cb83d1d2..03b96ced78d 100644 --- a/googleapiclient/version.py +++ b/googleapiclient/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "2.47.0" +__version__ = "2.47.0" diff --git a/noxfile.py b/noxfile.py index a7a25296124..18c4dd36225 100644 --- a/noxfile.py +++ b/noxfile.py @@ -13,12 +13,22 @@ # limitations under the License. import os -import nox import shutil +import nox + BLACK_VERSION = "black==22.3.0" ISORT_VERSION = "isort==5.10.1" -BLACK_PATHS = ["apiclient", "googleapiclient", "scripts", "tests", "describe.py", "noxfile.py", "owlbot.py", "setup.py"] +BLACK_PATHS = [ + "apiclient", + "googleapiclient", + "scripts", + "tests", + "describe.py", + "noxfile.py", + "owlbot.py", + "setup.py", +] test_dependencies = [ "django>=2.0.0", @@ -48,6 +58,7 @@ def lint(session): "--statistics", ) + @nox.session(python="3.8") def format(session): """ @@ -67,6 +78,7 @@ def format(session): *BLACK_PATHS, ) + @nox.session(python=["3.6", "3.7", "3.8", "3.9", "3.10"]) @nox.parametrize( "oauth2client", diff --git a/owlbot.py b/owlbot.py index f465e32c930..2185f6ad405 100644 --- a/owlbot.py +++ b/owlbot.py @@ -12,9 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +from pathlib import Path + import synthtool as s from synthtool import gcp -from pathlib import Path from synthtool.languages import python common = gcp.CommonTemplates() @@ -26,17 +27,17 @@ # Copy kokoro configs. # Docs are excluded as repo docs cannot currently be generated using sphinx. -s.move(templated_files / '.kokoro', excludes=['**/docs/*', 'publish-docs.sh']) -s.move(templated_files / '.trampolinerc') # config file for trampoline_v2 +s.move(templated_files / ".kokoro", excludes=["**/docs/*", "publish-docs.sh"]) +s.move(templated_files / ".trampolinerc") # config file for trampoline_v2 # Also move issue templates -s.move(templated_files / '.github', excludes=['CODEOWNERS', 'workflows']) +s.move(templated_files / ".github", excludes=["CODEOWNERS", "workflows"]) # Move scripts folder needed for samples CI -s.move(templated_files / 'scripts') +s.move(templated_files / "scripts") # Copy CONTRIBUTING.rst -s.move(templated_files / 'CONTRIBUTING.rst') +s.move(templated_files / "CONTRIBUTING.rst") # ---------------------------------------------------------------------------- # Samples templates @@ -46,4 +47,3 @@ for noxfile in Path(".").glob("**/noxfile.py"): s.shell.run(["nox", "-s", "format"], cwd=noxfile.parent, hide_output=False) - diff --git a/samples/compute/create_instance.py b/samples/compute/create_instance.py index f9807699ed6..aa6c271ceb3 100644 --- a/samples/compute/create_instance.py +++ b/samples/compute/create_instance.py @@ -32,158 +32,161 @@ # [START compute_apiary_list_instances] def list_instances(compute, project, zone): result = compute.instances().list(project=project, zone=zone).execute() - return result['items'] if 'items' in result else None + return result["items"] if "items" in result else None + + # [END compute_apiary_list_instances] # [START compute_apiary_create_instance] def create_instance(compute, project, zone, name, bucket): # Get the latest Debian Jessie image. - image_response = compute.images().getFromFamily( - project='debian-cloud', family='debian-9').execute() - source_disk_image = image_response['selfLink'] + image_response = ( + compute.images() + .getFromFamily(project="debian-cloud", family="debian-9") + .execute() + ) + source_disk_image = image_response["selfLink"] # Configure the machine machine_type = "zones/%s/machineTypes/n1-standard-1" % zone startup_script = open( - os.path.join( - os.path.dirname(__file__), 'startup-script.sh'), 'r').read() + os.path.join(os.path.dirname(__file__), "startup-script.sh"), "r" + ).read() image_url = "http://storage.googleapis.com/gce-demo-input/photo.jpg" image_caption = "Ready for dessert?" config = { - 'name': name, - 'machineType': machine_type, - + "name": name, + "machineType": machine_type, # Specify the boot disk and the image to use as a source. - 'disks': [ + "disks": [ { - 'boot': True, - 'autoDelete': True, - 'initializeParams': { - 'sourceImage': source_disk_image, - } + "boot": True, + "autoDelete": True, + "initializeParams": { + "sourceImage": source_disk_image, + }, } ], - # Specify a network interface with NAT to access the public # internet. - 'networkInterfaces': [{ - 'network': 'global/networks/default', - 'accessConfigs': [ - {'type': 'ONE_TO_ONE_NAT', 'name': 'External NAT'} - ] - }], - + "networkInterfaces": [ + { + "network": "global/networks/default", + "accessConfigs": [{"type": "ONE_TO_ONE_NAT", "name": "External NAT"}], + } + ], # Allow the instance to access cloud storage and logging. - 'serviceAccounts': [{ - 'email': 'default', - 'scopes': [ - 'https://www.googleapis.com/auth/devstorage.read_write', - 'https://www.googleapis.com/auth/logging.write' - ] - }], - + "serviceAccounts": [ + { + "email": "default", + "scopes": [ + "https://www.googleapis.com/auth/devstorage.read_write", + "https://www.googleapis.com/auth/logging.write", + ], + } + ], # Metadata is readable from the instance and allows you to # pass configuration from deployment scripts to instances. - 'metadata': { - 'items': [{ - # Startup script is automatically executed by the - # instance upon startup. - 'key': 'startup-script', - 'value': startup_script - }, { - 'key': 'url', - 'value': image_url - }, { - 'key': 'text', - 'value': image_caption - }, { - 'key': 'bucket', - 'value': bucket - }] - } + "metadata": { + "items": [ + { + # Startup script is automatically executed by the + # instance upon startup. + "key": "startup-script", + "value": startup_script, + }, + {"key": "url", "value": image_url}, + {"key": "text", "value": image_caption}, + {"key": "bucket", "value": bucket}, + ] + }, } - return compute.instances().insert( - project=project, - zone=zone, - body=config).execute() + return compute.instances().insert(project=project, zone=zone, body=config).execute() + + # [END compute_apiary_create_instance] # [START compute_apiary_delete_instance] def delete_instance(compute, project, zone, name): - return compute.instances().delete( - project=project, - zone=zone, - instance=name).execute() + return ( + compute.instances().delete(project=project, zone=zone, instance=name).execute() + ) + + # [END compute_apiary_delete_instance] # [START compute_apiary_wait_for_operation] def wait_for_operation(compute, project, zone, operation): - print('Waiting for operation to finish...') + print("Waiting for operation to finish...") while True: - result = compute.zoneOperations().get( - project=project, - zone=zone, - operation=operation).execute() + result = ( + compute.zoneOperations() + .get(project=project, zone=zone, operation=operation) + .execute() + ) - if result['status'] == 'DONE': + if result["status"] == "DONE": print("done.") - if 'error' in result: - raise Exception(result['error']) + if "error" in result: + raise Exception(result["error"]) return result time.sleep(1) + + # [END compute_apiary_wait_for_operation] # [START compute_apiary_run] def main(project, bucket, zone, instance_name, wait=True): - compute = googleapiclient.discovery.build('compute', 'v1') + compute = googleapiclient.discovery.build("compute", "v1") - print('Creating instance.') + print("Creating instance.") operation = create_instance(compute, project, zone, instance_name, bucket) - wait_for_operation(compute, project, zone, operation['name']) + wait_for_operation(compute, project, zone, operation["name"]) instances = list_instances(compute, project, zone) - print('Instances in project %s and zone %s:' % (project, zone)) + print("Instances in project %s and zone %s:" % (project, zone)) for instance in instances: - print(' - ' + instance['name']) + print(" - " + instance["name"]) - print(""" + print( + """ Instance created. It will take a minute or two for the instance to complete work. Check this URL: http://storage.googleapis.com/{}/output.png Once the image is uploaded press enter to delete the instance. -""".format(bucket)) +""".format( + bucket + ) + ) if wait: input() - print('Deleting instance.') + print("Deleting instance.") operation = delete_instance(compute, project, zone, instance_name) - wait_for_operation(compute, project, zone, operation['name']) + wait_for_operation(compute, project, zone, operation["name"]) -if __name__ == '__main__': +if __name__ == "__main__": parser = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawDescriptionHelpFormatter) - parser.add_argument('project_id', help='Your Google Cloud project ID.') - parser.add_argument( - 'bucket_name', help='Your Google Cloud Storage bucket name.') - parser.add_argument( - '--zone', - default='us-central1-f', - help='Compute Engine zone to deploy to.') + description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter + ) + parser.add_argument("project_id", help="Your Google Cloud project ID.") + parser.add_argument("bucket_name", help="Your Google Cloud Storage bucket name.") parser.add_argument( - '--name', default='demo-instance', help='New instance name.') + "--zone", default="us-central1-f", help="Compute Engine zone to deploy to." + ) + parser.add_argument("--name", default="demo-instance", help="New instance name.") args = parser.parse_args() diff --git a/samples/compute/create_instance_test.py b/samples/compute/create_instance_test.py index 5888978cee4..c9d68b7a73f 100644 --- a/samples/compute/create_instance_test.py +++ b/samples/compute/create_instance_test.py @@ -18,19 +18,14 @@ from create_instance import main -PROJECT = os.environ['GOOGLE_CLOUD_PROJECT'] -BUCKET = os.environ['CLOUD_STORAGE_BUCKET'] +PROJECT = os.environ["GOOGLE_CLOUD_PROJECT"] +BUCKET = os.environ["CLOUD_STORAGE_BUCKET"] @pytest.mark.flaky(max_runs=3, min_passes=1) def test_main(capsys): - instance_name = 'test-instance-{}'.format(uuid.uuid4()) - main( - PROJECT, - BUCKET, - 'us-central1-f', - instance_name, - wait=False) + instance_name = "test-instance-{}".format(uuid.uuid4()) + main(PROJECT, BUCKET, "us-central1-f", instance_name, wait=False) out, _ = capsys.readouterr() diff --git a/samples/compute/noxfile.py b/samples/compute/noxfile.py index 38bb0a572b8..3b3ffa5d2b0 100644 --- a/samples/compute/noxfile.py +++ b/samples/compute/noxfile.py @@ -22,7 +22,6 @@ import nox - # WARNING - WARNING - WARNING - WARNING - WARNING # WARNING - WARNING - WARNING - WARNING - WARNING # DO NOT EDIT THIS FILE EVER! @@ -180,6 +179,7 @@ def blacken(session: nox.sessions.Session) -> None: # format = isort + black # + @nox.session def format(session: nox.sessions.Session) -> None: """ @@ -229,9 +229,7 @@ def _session_tests( if os.path.exists("requirements-test.txt"): if os.path.exists("constraints-test.txt"): - session.install( - "-r", "requirements-test.txt", "-c", "constraints-test.txt" - ) + session.install("-r", "requirements-test.txt", "-c", "constraints-test.txt") else: session.install("-r", "requirements-test.txt") with open("requirements-test.txt") as rtfile: @@ -244,9 +242,9 @@ def _session_tests( post_install(session) if "pytest-parallel" in packages: - concurrent_args.extend(['--workers', 'auto', '--tests-per-worker', 'auto']) + concurrent_args.extend(["--workers", "auto", "--tests-per-worker", "auto"]) elif "pytest-xdist" in packages: - concurrent_args.extend(['-n', 'auto']) + concurrent_args.extend(["-n", "auto"]) session.run( "pytest", @@ -276,7 +274,7 @@ def py(session: nox.sessions.Session) -> None: def _get_repo_root() -> Optional[str]: - """ Returns the root folder of the project. """ + """Returns the root folder of the project.""" # Get root of this repository. Assume we don't have directories nested deeper than 10 items. p = Path(os.getcwd()) for i in range(10): diff --git a/scripts/buildprbody.py b/scripts/buildprbody.py index 317bc89cfb2..b205287b04e 100644 --- a/scripts/buildprbody.py +++ b/scripts/buildprbody.py @@ -13,11 +13,11 @@ # limitations under the License. from enum import IntEnum -import numpy as np -import pandas as pd import pathlib from changesummary import ChangeType +import numpy as np +import pandas as pd SCRIPTS_DIR = pathlib.Path(__file__).parent.resolve() CHANGE_SUMMARY_DIR = SCRIPTS_DIR / "temp" @@ -48,7 +48,9 @@ def get_commit_uri(self, name): sha = None api_link = "" - file_path = pathlib.Path(self._change_summary_directory) / "{0}.sha".format(name) + file_path = pathlib.Path(self._change_summary_directory) / "{0}.sha".format( + name + ) if file_path.is_file(): with open(file_path, "r") as f: sha = f.readline().rstrip() diff --git a/scripts/changesummary.py b/scripts/changesummary.py index 7375a023b98..2742b00b2e2 100644 --- a/scripts/changesummary.py +++ b/scripts/changesummary.py @@ -15,9 +15,10 @@ from enum import IntEnum import json from multiprocessing import Pool -import pandas as pd import pathlib + import numpy as np +import pandas as pd BRANCH_ARTIFACTS_DIR = ( pathlib.Path(__file__).parent.resolve() diff --git a/scripts/changesummary_test.py b/scripts/changesummary_test.py index cbc4ff0a425..445c0aa56a1 100644 --- a/scripts/changesummary_test.py +++ b/scripts/changesummary_test.py @@ -20,12 +20,9 @@ import shutil import unittest +from changesummary import ChangeSummary, ChangeType, DirectoryDoesNotExist import pandas as pd -from changesummary import ChangeSummary -from changesummary import ChangeType -from changesummary import DirectoryDoesNotExist - SCRIPTS_DIR = pathlib.Path(__file__).parent.resolve() NEW_ARTIFACTS_DIR = SCRIPTS_DIR / "test_resources" / "new_artifacts_dir" CURRENT_ARTIFACTS_DIR = SCRIPTS_DIR / "test_resources" / "current_artifacts_dir" @@ -193,7 +190,9 @@ def test_detect_discovery_changes(self): # Confirm that key "schemas.ProjectReference.newkey" exists for bigquery self.assertEqual( result[ - (result["Name"] == "bigquery") & (result["Added"]) & (result["Count"] == 1) + (result["Name"] == "bigquery") + & (result["Added"]) + & (result["Count"] == 1) ]["Key"].iloc[0], "schemas.ProjectReference.newkey", ) diff --git a/scripts/readme-gen/readme_gen.py b/scripts/readme-gen/readme_gen.py index d309d6e9751..acb3f9a18f1 100644 --- a/scripts/readme-gen/readme_gen.py +++ b/scripts/readme-gen/readme_gen.py @@ -24,23 +24,24 @@ import jinja2 import yaml - jinja_env = jinja2.Environment( trim_blocks=True, loader=jinja2.FileSystemLoader( - os.path.abspath(os.path.join(os.path.dirname(__file__), 'templates')))) + os.path.abspath(os.path.join(os.path.dirname(__file__), "templates")) + ), +) -README_TMPL = jinja_env.get_template('README.tmpl.rst') +README_TMPL = jinja_env.get_template("README.tmpl.rst") def get_help(file): - return subprocess.check_output(['python', file, '--help']).decode() + return subprocess.check_output(["python", file, "--help"]).decode() def main(): parser = argparse.ArgumentParser() - parser.add_argument('source') - parser.add_argument('--destination', default='README.rst') + parser.add_argument("source") + parser.add_argument("--destination", default="README.rst") args = parser.parse_args() @@ -48,9 +49,9 @@ def main(): root = os.path.dirname(source) destination = os.path.join(root, args.destination) - jinja_env.globals['get_help'] = get_help + jinja_env.globals["get_help"] = get_help - with io.open(source, 'r') as f: + with io.open(source, "r") as f: config = yaml.load(f) # This allows get_help to execute in the right directory. @@ -58,9 +59,9 @@ def main(): output = README_TMPL.render(config) - with io.open(destination, 'w') as f: + with io.open(destination, "w") as f: f.write(output) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/scripts/updatediscoveryartifacts.py b/scripts/updatediscoveryartifacts.py index 761d42f8486..a4d9ef32bed 100644 --- a/scripts/updatediscoveryartifacts.py +++ b/scripts/updatediscoveryartifacts.py @@ -18,9 +18,9 @@ import subprocess import tempfile -import describe import changesummary +import describe SCRIPTS_DIR = pathlib.Path(__file__).parent.resolve() DISCOVERY_DOC_DIR = ( diff --git a/setup.py b/setup.py index a5412bb91a2..d03f117ba33 100644 --- a/setup.py +++ b/setup.py @@ -27,6 +27,7 @@ import io import os + from setuptools import setup packages = ["apiclient", "googleapiclient", "googleapiclient/discovery_cache"] @@ -63,7 +64,7 @@ version=version, description="Google API Client Library for Python", long_description=readme, - long_description_content_type='text/markdown', + long_description_content_type="text/markdown", author="Google LLC", author_email="googleapis-packages@google.com", url="https://github.com/googleapis/google-api-python-client/", diff --git a/tests/test__auth.py b/tests/test__auth.py index 08aa83c32c5..6809647b785 100644 --- a/tests/test__auth.py +++ b/tests/test__auth.py @@ -13,11 +13,11 @@ # limitations under the License. import unittest -import mock import google.auth.credentials import google_auth_httplib2 import httplib2 +import mock import oauth2client.client from googleapiclient import _auth @@ -89,7 +89,9 @@ class CredentialsWithScopes( self.assertNotEqual(credentials, returned) self.assertEqual(returned, credentials.with_scopes.return_value) - credentials.with_scopes.assert_called_once_with(mock.sentinel.scopes, default_scopes=None) + credentials.with_scopes.assert_called_once_with( + mock.sentinel.scopes, default_scopes=None + ) def test_authorized_http(self): credentials = mock.Mock(spec=google.auth.credentials.Credentials) diff --git a/tests/test__helpers.py b/tests/test__helpers.py index ab0bd4bdb38..51e5c595260 100644 --- a/tests/test__helpers.py +++ b/tests/test__helpers.py @@ -18,6 +18,7 @@ import urllib import mock + from googleapiclient import _helpers diff --git a/tests/test_channel.py b/tests/test_channel.py index fe337314965..2e8b2f2fca5 100644 --- a/tests/test_channel.py +++ b/tests/test_channel.py @@ -6,8 +6,7 @@ import datetime import unittest -from googleapiclient import channel -from googleapiclient import errors +from googleapiclient import channel, errors class TestChannel(unittest.TestCase): diff --git a/tests/test_discovery.py b/tests/test_discovery.py index bdc180bbb85..63d1a71b0c0 100644 --- a/tests/test_discovery.py +++ b/tests/test_discovery.py @@ -27,7 +27,6 @@ from collections import defaultdict import copy import datetime -import httplib2 import io import itertools import json @@ -38,58 +37,60 @@ import unittest import urllib -from parameterized import parameterized -import mock - +import google.api_core.exceptions import google.auth.credentials -from google.auth.transport import mtls from google.auth.exceptions import MutualTLSChannelError +from google.auth.transport import mtls import google_auth_httplib2 -import google.api_core.exceptions +import httplib2 +import mock +from oauth2client import GOOGLE_TOKEN_URI +from oauth2client.client import GoogleCredentials, OAuth2Credentials +from parameterized import parameterized +import uritemplate -from googleapiclient.discovery import _fix_up_media_upload -from googleapiclient.discovery import _fix_up_method_description -from googleapiclient.discovery import _fix_up_parameters -from googleapiclient.discovery import _urljoin -from googleapiclient.discovery import build -from googleapiclient.discovery import build_from_document -from googleapiclient.discovery import DISCOVERY_URI -from googleapiclient.discovery import key2param -from googleapiclient.discovery import MEDIA_BODY_PARAMETER_DEFAULT_VALUE -from googleapiclient.discovery import MEDIA_MIME_TYPE_PARAMETER_DEFAULT_VALUE -from googleapiclient.discovery import ResourceMethodParameters -from googleapiclient.discovery import STACK_QUERY_PARAMETERS -from googleapiclient.discovery import STACK_QUERY_PARAMETER_DEFAULT_VALUE -from googleapiclient.discovery import V1_DISCOVERY_URI -from googleapiclient.discovery import V2_DISCOVERY_URI +from googleapiclient import _helpers as util +from googleapiclient.discovery import ( + DISCOVERY_URI, + MEDIA_BODY_PARAMETER_DEFAULT_VALUE, + MEDIA_MIME_TYPE_PARAMETER_DEFAULT_VALUE, + STACK_QUERY_PARAMETER_DEFAULT_VALUE, + STACK_QUERY_PARAMETERS, + V1_DISCOVERY_URI, + V2_DISCOVERY_URI, + ResourceMethodParameters, + _fix_up_media_upload, + _fix_up_method_description, + _fix_up_parameters, + _urljoin, + build, + build_from_document, + key2param, +) from googleapiclient.discovery_cache import DISCOVERY_DOC_MAX_AGE from googleapiclient.discovery_cache.base import Cache -from googleapiclient.errors import HttpError -from googleapiclient.errors import InvalidJsonError -from googleapiclient.errors import MediaUploadSizeError -from googleapiclient.errors import ResumableUploadError -from googleapiclient.errors import UnacceptableMimeTypeError -from googleapiclient.errors import UnknownApiNameOrVersion -from googleapiclient.errors import UnknownFileType -from googleapiclient.http import build_http -from googleapiclient.http import BatchHttpRequest -from googleapiclient.http import HttpMock -from googleapiclient.http import HttpMockSequence -from googleapiclient.http import MediaFileUpload -from googleapiclient.http import MediaIoBaseUpload -from googleapiclient.http import MediaUpload -from googleapiclient.http import MediaUploadProgress -from googleapiclient.http import tunnel_patch +from googleapiclient.errors import ( + HttpError, + InvalidJsonError, + MediaUploadSizeError, + ResumableUploadError, + UnacceptableMimeTypeError, + UnknownApiNameOrVersion, + UnknownFileType, +) +from googleapiclient.http import ( + BatchHttpRequest, + HttpMock, + HttpMockSequence, + MediaFileUpload, + MediaIoBaseUpload, + MediaUpload, + MediaUploadProgress, + build_http, + tunnel_patch, +) from googleapiclient.model import JsonModel from googleapiclient.schema import Schemas -from oauth2client import GOOGLE_TOKEN_URI -from oauth2client.client import OAuth2Credentials, GoogleCredentials - - -from googleapiclient import _helpers as util - -import uritemplate - DATA_DIR = os.path.join(os.path.dirname(__file__), "data") @@ -295,7 +296,7 @@ def test_fix_up_media_upload_no_initial_valid_full(self): "media_body": MEDIA_BODY_PARAMETER_DEFAULT_VALUE, "media_mime_type": MEDIA_MIME_TYPE_PARAMETER_DEFAULT_VALUE, } - ten_gb = 10 * 2 ** 30 + ten_gb = 10 * 2**30 self._base_fix_up_method_description_test( valid_method_desc, {}, @@ -337,7 +338,7 @@ def test_fix_up_media_upload_with_initial_valid_full(self): "media_body": MEDIA_BODY_PARAMETER_DEFAULT_VALUE, "media_mime_type": MEDIA_MIME_TYPE_PARAMETER_DEFAULT_VALUE, } - ten_gb = 10 * 2 ** 30 + ten_gb = 10 * 2**30 self._base_fix_up_method_description_test( valid_method_desc, initial_parameters, @@ -454,7 +455,13 @@ def test_tests_should_be_run_with_strict_positional_enforcement(self): def test_failed_to_parse_discovery_json(self): self.http = HttpMock(datafile("malformed.json"), {"status": "200"}) try: - plus = build("plus", "v1", http=self.http, cache_discovery=False, static_discovery=False) + plus = build( + "plus", + "v1", + http=self.http, + cache_discovery=False, + static_discovery=False, + ) self.fail("should have raised an exception over malformed JSON.") except InvalidJsonError: pass @@ -472,7 +479,13 @@ def test_unknown_api_name_or_version(self): def test_credentials_and_http_mutually_exclusive(self): http = HttpMock(datafile("plus.json"), {"status": "200"}) with self.assertRaises(ValueError): - build("plus", "v1", http=http, credentials=mock.sentinel.credentials, static_discovery=False) + build( + "plus", + "v1", + http=http, + credentials=mock.sentinel.credentials, + static_discovery=False, + ) def test_credentials_file_and_http_mutually_exclusive(self): http = HttpMock(datafile("plus.json"), {"status": "200"}) @@ -583,7 +596,11 @@ def test_building_with_developer_key_skips_adc(self): def test_building_with_context_manager(self): discovery = read_datafile("plus.json") with mock.patch("httplib2.Http") as http: - with build_from_document(discovery, base="https://www.googleapis.com/", credentials=self.MOCK_CREDENTIALS) as plus: + with build_from_document( + discovery, + base="https://www.googleapis.com/", + credentials=self.MOCK_CREDENTIALS, + ) as plus: self.assertIsNotNone(plus) self.assertTrue(hasattr(plus, "activities")) plus._http.http.close.assert_called_once() @@ -652,7 +669,8 @@ def test_scopes_from_client_options(self): with mock.patch("googleapiclient._auth.default_credentials") as default: plus = build_from_document( - discovery, client_options={"scopes": ["1", "2"]}, + discovery, + client_options={"scopes": ["1", "2"]}, ) default.assert_called_once_with(scopes=["1", "2"], quota_project_id=None) @@ -687,25 +705,35 @@ def test_credentials_file_from_client_options(self): def test_self_signed_jwt_enabled(self): service_account_file_path = os.path.join(DATA_DIR, "service_account.json") - creds = google.oauth2.service_account.Credentials.from_service_account_file(service_account_file_path) + creds = google.oauth2.service_account.Credentials.from_service_account_file( + service_account_file_path + ) discovery = read_datafile("logging.json") - with mock.patch("google.oauth2.service_account.Credentials._create_self_signed_jwt") as _create_self_signed_jwt: + with mock.patch( + "google.oauth2.service_account.Credentials._create_self_signed_jwt" + ) as _create_self_signed_jwt: build_from_document( discovery, credentials=creds, always_use_jwt_access=True, ) - _create_self_signed_jwt.assert_called_with("https://logging.googleapis.com/") + _create_self_signed_jwt.assert_called_with( + "https://logging.googleapis.com/" + ) def test_self_signed_jwt_disabled(self): service_account_file_path = os.path.join(DATA_DIR, "service_account.json") - creds = google.oauth2.service_account.Credentials.from_service_account_file(service_account_file_path) + creds = google.oauth2.service_account.Credentials.from_service_account_file( + service_account_file_path + ) discovery = read_datafile("logging.json") - with mock.patch("google.oauth2.service_account.Credentials._create_self_signed_jwt") as _create_self_signed_jwt: + with mock.patch( + "google.oauth2.service_account.Credentials._create_self_signed_jwt" + ) as _create_self_signed_jwt: build_from_document( discovery, credentials=creds, @@ -954,7 +982,7 @@ def test_userip_is_added_to_discovery_uri(self): "v1", http=http, developerKey=None, - discoveryServiceUrl="http://example.com" + discoveryServiceUrl="http://example.com", ) self.fail("Should have raised an exception.") except HttpError as e: @@ -972,7 +1000,7 @@ def test_userip_missing_is_not_added_to_discovery_uri(self): "v1", http=http, developerKey=None, - discoveryServiceUrl="http://example.com" + discoveryServiceUrl="http://example.com", ) self.fail("Should have raised an exception.") except HttpError as e: @@ -1004,7 +1032,9 @@ def test_discovery_loading_from_v2_discovery_uri(self): ({"status": "200"}, read_datafile("zoo.json", "rb")), ] ) - zoo = build("zoo", "v1", http=http, cache_discovery=False, static_discovery=False) + zoo = build( + "zoo", "v1", http=http, cache_discovery=False, static_discovery=False + ) self.assertTrue(hasattr(zoo, "animals")) def test_api_endpoint_override_from_client_options(self): @@ -1019,7 +1049,12 @@ def test_api_endpoint_override_from_client_options(self): api_endpoint=api_endpoint ) zoo = build( - "zoo", "v1", http=http, cache_discovery=False, client_options=options, static_discovery=False + "zoo", + "v1", + http=http, + cache_discovery=False, + client_options=options, + static_discovery=False, ) self.assertEqual(zoo._baseUrl, api_endpoint) @@ -1042,12 +1077,26 @@ def test_api_endpoint_override_from_client_options_dict(self): self.assertEqual(zoo._baseUrl, api_endpoint) def test_discovery_with_empty_version_uses_v2(self): - http = HttpMockSequence([({"status": "200"}, read_datafile("zoo.json", "rb")),]) - build("zoo", version=None, http=http, cache_discovery=False, static_discovery=False) + http = HttpMockSequence( + [ + ({"status": "200"}, read_datafile("zoo.json", "rb")), + ] + ) + build( + "zoo", + version=None, + http=http, + cache_discovery=False, + static_discovery=False, + ) validate_discovery_requests(self, http, "zoo", None, V2_DISCOVERY_URI) def test_discovery_with_empty_version_preserves_custom_uri(self): - http = HttpMockSequence([({"status": "200"}, read_datafile("zoo.json", "rb")),]) + http = HttpMockSequence( + [ + ({"status": "200"}, read_datafile("zoo.json", "rb")), + ] + ) custom_discovery_uri = "https://foo.bar/$discovery" build( "zoo", @@ -1060,8 +1109,18 @@ def test_discovery_with_empty_version_preserves_custom_uri(self): validate_discovery_requests(self, http, "zoo", None, custom_discovery_uri) def test_discovery_with_valid_version_uses_v1(self): - http = HttpMockSequence([({"status": "200"}, read_datafile("zoo.json", "rb")),]) - build("zoo", version="v123", http=http, cache_discovery=False, static_discovery=False) + http = HttpMockSequence( + [ + ({"status": "200"}, read_datafile("zoo.json", "rb")), + ] + ) + build( + "zoo", + version="v123", + http=http, + cache_discovery=False, + static_discovery=False, + ) validate_discovery_requests(self, http, "zoo", "v123", V1_DISCOVERY_URI) @@ -1075,7 +1134,13 @@ def test_repeated_500_retries_and_fails(self): ) with self.assertRaises(HttpError): with mock.patch("time.sleep") as mocked_sleep: - build("zoo", "v1", http=http, cache_discovery=False, static_discovery=False) + build( + "zoo", + "v1", + http=http, + cache_discovery=False, + static_discovery=False, + ) mocked_sleep.assert_called_once() # We also want to verify that we stayed with v1 discovery @@ -1091,7 +1156,13 @@ def test_v2_repeated_500_retries_and_fails(self): ) with self.assertRaises(HttpError): with mock.patch("time.sleep") as mocked_sleep: - build("zoo", "v1", http=http, cache_discovery=False, static_discovery=False) + build( + "zoo", + "v1", + http=http, + cache_discovery=False, + static_discovery=False, + ) mocked_sleep.assert_called_once() # We also want to verify that we switched to v2 discovery @@ -1105,7 +1176,9 @@ def test_single_500_retries_and_succeeds(self): ] ) with mock.patch("time.sleep") as mocked_sleep: - zoo = build("zoo", "v1", http=http, cache_discovery=False, static_discovery=False) + zoo = build( + "zoo", "v1", http=http, cache_discovery=False, static_discovery=False + ) self.assertTrue(hasattr(zoo, "animals")) mocked_sleep.assert_called_once() @@ -1121,7 +1194,9 @@ def test_single_500_then_404_retries_and_succeeds(self): ] ) with mock.patch("time.sleep") as mocked_sleep: - zoo = build("zoo", "v1", http=http, cache_discovery=False, static_discovery=False) + zoo = build( + "zoo", "v1", http=http, cache_discovery=False, static_discovery=False + ) self.assertTrue(hasattr(zoo, "animals")) mocked_sleep.assert_called_once() @@ -1197,21 +1272,22 @@ def import_mock(name, *args, **kwargs): class DiscoveryFromStaticDocument(unittest.TestCase): def test_retrieve_from_local_when_static_discovery_true(self): http = HttpMockSequence([({"status": "400"}, "")]) - drive = build("drive", "v3", http=http, cache_discovery=False, - static_discovery=True) + drive = build( + "drive", "v3", http=http, cache_discovery=False, static_discovery=True + ) self.assertIsNotNone(drive) self.assertTrue(hasattr(drive, "files")) def test_retrieve_from_internet_when_static_discovery_false(self): http = HttpMockSequence([({"status": "400"}, "")]) with self.assertRaises(HttpError): - build("drive", "v3", http=http, cache_discovery=False, - static_discovery=False) + build( + "drive", "v3", http=http, cache_discovery=False, static_discovery=False + ) def test_unknown_api_when_static_discovery_true(self): with self.assertRaises(UnknownApiNameOrVersion): - build("doesnotexist", "v3", cache_discovery=False, - static_discovery=True) + build("doesnotexist", "v3", cache_discovery=False, static_discovery=True) class DictCache(Cache): @@ -1397,7 +1473,9 @@ def test_batch_request_from_discovery(self): def test_batch_request_from_default(self): self.http = HttpMock(datafile("plus.json"), {"status": "200"}) # plus does not define a batchPath - plus = build("plus", "v1", http=self.http, cache_discovery=False, static_discovery=False) + plus = build( + "plus", "v1", http=self.http, cache_discovery=False, static_discovery=False + ) batch_request = plus.new_batch_http_request() self.assertEqual(batch_request._batch_uri, "https://www.googleapis.com/batch") @@ -1409,7 +1487,9 @@ def test_tunnel_patch(self): ] ) http = tunnel_patch(http) - zoo = build("zoo", "v1", http=http, cache_discovery=False, static_discovery=False) + zoo = build( + "zoo", "v1", http=http, cache_discovery=False, static_discovery=False + ) resp = zoo.animals().patch(name="lion", body='{"description": "foo"}').execute() self.assertTrue("x-http-method-override" in resp) diff --git a/tests/test_errors.py b/tests/test_errors.py index 7a2cfdedadb..76fb2afc41f 100644 --- a/tests/test_errors.py +++ b/tests/test_errors.py @@ -22,12 +22,11 @@ import unittest -import httplib2 +import httplib2 from googleapiclient.errors import HttpError - JSON_ERROR_CONTENT = b""" { "error": { @@ -96,7 +95,10 @@ def test_bad_json_body(self): b"{", {"status": "400", "content-type": "application/json"}, reason="Failed" ) error = HttpError(resp, content) - self.assertEqual(str(error), '') + self.assertEqual( + str(error), + '', + ) def test_with_uri(self): """Test handling of passing in the request uri.""" @@ -125,13 +127,19 @@ def test_non_json(self): """Test handling of non-JSON bodies""" resp, content = fake_response(b"Invalid request", {"status": "400"}) error = HttpError(resp, content) - self.assertEqual(str(error), '') + self.assertEqual( + str(error), + '', + ) def test_missing_reason(self): """Test an empty dict with a missing resp.reason.""" resp, content = fake_response(b"}NOT OK", {"status": "400"}, reason=None) error = HttpError(resp, content) - self.assertEqual(str(error), '') + self.assertEqual( + str(error), + '', + ) def test_error_detail_for_missing_message_in_error(self): """Test handling of data with missing 'details' or 'detail' element.""" @@ -142,5 +150,9 @@ def test_error_detail_for_missing_message_in_error(self): ) error = HttpError(resp, content) expected_error_details = "[{'domain': 'global', 'reason': 'required', 'message': 'country is required', 'locationType': 'parameter', 'location': 'country'}]" - self.assertEqual(str(error), '' % expected_error_details) + self.assertEqual( + str(error), + '' + % expected_error_details, + ) self.assertEqual(str(error.error_details), expected_error_details) diff --git a/tests/test_http.py b/tests/test_http.py index e55e0566202..3233f668ce7 100644 --- a/tests/test_http.py +++ b/tests/test_http.py @@ -22,41 +22,42 @@ __author__ = "jcgregorio@google.com (Joe Gregorio)" +import io from io import FileIO # Do not remove the httplib2 import import json -import httplib2 -import io import logging -import mock import os -import unittest -import urllib import random import socket import ssl import time +import unittest +import urllib + +import httplib2 +import mock +from oauth2client.client import Credentials from googleapiclient.discovery import build -from googleapiclient.errors import BatchError -from googleapiclient.errors import HttpError -from googleapiclient.errors import InvalidChunkSizeError -from googleapiclient.http import build_http -from googleapiclient.http import BatchHttpRequest -from googleapiclient.http import HttpMock -from googleapiclient.http import HttpMockSequence -from googleapiclient.http import HttpRequest -from googleapiclient.http import MAX_URI_LENGTH -from googleapiclient.http import MediaFileUpload -from googleapiclient.http import MediaInMemoryUpload -from googleapiclient.http import MediaIoBaseDownload -from googleapiclient.http import MediaIoBaseUpload -from googleapiclient.http import MediaUpload -from googleapiclient.http import _StreamSlice -from googleapiclient.http import set_user_agent +from googleapiclient.errors import BatchError, HttpError, InvalidChunkSizeError +from googleapiclient.http import ( + MAX_URI_LENGTH, + BatchHttpRequest, + HttpMock, + HttpMockSequence, + HttpRequest, + MediaFileUpload, + MediaInMemoryUpload, + MediaIoBaseDownload, + MediaIoBaseUpload, + MediaUpload, + _StreamSlice, + build_http, + set_user_agent, +) from googleapiclient.model import JsonModel -from oauth2client.client import Credentials class MockCredentials(Credentials): @@ -327,7 +328,6 @@ def test_media_io_base_upload_serializable(self): except NotImplementedError: pass - def test_media_io_base_upload_from_bytes(self): f = open(datafile("small.png"), "rb") fd = io.BytesIO(f.read()) @@ -383,8 +383,8 @@ def test_media_io_base_next_chunk_retries(self): ) model = JsonModel() - uri = u"https://www.googleapis.com/someapi/v1/upload/?foo=bar" - method = u"POST" + uri = "https://www.googleapis.com/someapi/v1/upload/?foo=bar" + method = "POST" request = HttpRequest( http, model.response, uri, method=method, headers={}, resumable=upload ) @@ -407,8 +407,8 @@ def test_media_io_base_next_chunk_no_retry_403_not_configured(self): ) model = JsonModel() - uri = u"https://www.googleapis.com/someapi/v1/upload/?foo=bar" - method = u"POST" + uri = "https://www.googleapis.com/someapi/v1/upload/?foo=bar" + method = "POST" request = HttpRequest( http, model.response, uri, method=method, headers={}, resumable=upload ) @@ -420,7 +420,6 @@ def test_media_io_base_next_chunk_no_retry_403_not_configured(self): request.execute(num_retries=3) request._sleep.assert_not_called() - def test_media_io_base_empty_file(self): fd = io.BytesIO() upload = MediaIoBaseUpload( @@ -429,14 +428,26 @@ def test_media_io_base_empty_file(self): http = HttpMockSequence( [ - ({"status": "200", "location": "https://www.googleapis.com/someapi/v1/upload?foo=bar"}, "{}"), - ({"status": "200", "location": "https://www.googleapis.com/someapi/v1/upload?foo=bar"}, "{}") + ( + { + "status": "200", + "location": "https://www.googleapis.com/someapi/v1/upload?foo=bar", + }, + "{}", + ), + ( + { + "status": "200", + "location": "https://www.googleapis.com/someapi/v1/upload?foo=bar", + }, + "{}", + ), ] ) model = JsonModel() - uri = u"https://www.googleapis.com/someapi/v1/upload/?foo=bar" - method = u"POST" + uri = "https://www.googleapis.com/someapi/v1/upload/?foo=bar" + method = "POST" request = HttpRequest( http, model.response, uri, method=method, headers={}, resumable=upload ) @@ -912,14 +923,14 @@ class TestHttpRequest(unittest.TestCase): def test_unicode(self): http = HttpMock(datafile("zoo.json"), headers={"status": "200"}) model = JsonModel() - uri = u"https://www.googleapis.com/someapi/v1/collection/?foo=bar" - method = u"POST" + uri = "https://www.googleapis.com/someapi/v1/collection/?foo=bar" + method = "POST" request = HttpRequest( http, model.response, uri, method=method, - body=u"{}", + body="{}", headers={"content-type": "application/json"}, ) request.execute() @@ -931,8 +942,8 @@ def test_unicode(self): def test_empty_content_type(self): """Test for #284""" http = HttpMock(None, headers={"status": 200}) - uri = u"https://www.googleapis.com/someapi/v1/upload/?foo=bar" - method = u"POST" + uri = "https://www.googleapis.com/someapi/v1/upload/?foo=bar" + method = "POST" request = HttpRequest( http, _postproc_none, uri, method=method, headers={"content-type": ""} ) @@ -944,7 +955,7 @@ def test_no_retry_connection_errors(self): request = HttpRequest( HttpMockWithNonRetriableErrors(1, {"status": "200"}, '{"foo": "bar"}'), model.response, - u"https://www.example.com/json_api_endpoint", + "https://www.example.com/json_api_endpoint", ) request._sleep = lambda _x: 0 # do nothing request._rand = lambda: 10 @@ -956,12 +967,12 @@ def test_retry_connection_errors_non_resumable(self): request = HttpRequest( HttpMockWithErrors(5, {"status": "200"}, '{"foo": "bar"}'), model.response, - u"https://www.example.com/json_api_endpoint", + "https://www.example.com/json_api_endpoint", ) request._sleep = lambda _x: 0 # do nothing request._rand = lambda: 10 response = request.execute(num_retries=5) - self.assertEqual({u"foo": u"bar"}, response) + self.assertEqual({"foo": "bar"}, response) def test_retry_connection_errors_resumable(self): with open(datafile("small.png"), "rb") as small_png_file: @@ -976,34 +987,38 @@ def test_retry_connection_errors_resumable(self): 5, {"status": "200", "location": "location"}, '{"foo": "bar"}' ), model.response, - u"https://www.example.com/file_upload", + "https://www.example.com/file_upload", method="POST", resumable=upload, ) request._sleep = lambda _x: 0 # do nothing request._rand = lambda: 10 response = request.execute(num_retries=5) - self.assertEqual({u"foo": u"bar"}, response) + self.assertEqual({"foo": "bar"}, response) def test_retry(self): num_retries = 6 resp_seq = [({"status": "500"}, "")] * (num_retries - 4) resp_seq.append(({"status": "403"}, RATE_LIMIT_EXCEEDED_RESPONSE)) - resp_seq.append(({"status": "403"}, USER_RATE_LIMIT_EXCEEDED_RESPONSE_NO_STATUS)) - resp_seq.append(({"status": "403"}, USER_RATE_LIMIT_EXCEEDED_RESPONSE_WITH_STATUS)) + resp_seq.append( + ({"status": "403"}, USER_RATE_LIMIT_EXCEEDED_RESPONSE_NO_STATUS) + ) + resp_seq.append( + ({"status": "403"}, USER_RATE_LIMIT_EXCEEDED_RESPONSE_WITH_STATUS) + ) resp_seq.append(({"status": "429"}, "")) resp_seq.append(({"status": "200"}, "{}")) http = HttpMockSequence(resp_seq) model = JsonModel() - uri = u"https://www.googleapis.com/someapi/v1/collection/?foo=bar" - method = u"POST" + uri = "https://www.googleapis.com/someapi/v1/collection/?foo=bar" + method = "POST" request = HttpRequest( http, model.response, uri, method=method, - body=u"{}", + body="{}", headers={"content-type": "application/json"}, ) @@ -1023,14 +1038,14 @@ def test_no_retry_succeeds(self): http = HttpMockSequence(resp_seq) model = JsonModel() - uri = u"https://www.googleapis.com/someapi/v1/collection/?foo=bar" - method = u"POST" + uri = "https://www.googleapis.com/someapi/v1/collection/?foo=bar" + method = "POST" request = HttpRequest( http, model.response, uri, method=method, - body=u"{}", + body="{}", headers={"content-type": "application/json"}, ) @@ -1045,14 +1060,14 @@ def test_no_retry_succeeds(self): def test_no_retry_fails_fast(self): http = HttpMockSequence([({"status": "500"}, ""), ({"status": "200"}, "{}")]) model = JsonModel() - uri = u"https://www.googleapis.com/someapi/v1/collection/?foo=bar" - method = u"POST" + uri = "https://www.googleapis.com/someapi/v1/collection/?foo=bar" + method = "POST" request = HttpRequest( http, model.response, uri, method=method, - body=u"{}", + body="{}", headers={"content-type": "application/json"}, ) @@ -1068,14 +1083,14 @@ def test_no_retry_403_not_configured_fails_fast(self): [({"status": "403"}, NOT_CONFIGURED_RESPONSE), ({"status": "200"}, "{}")] ) model = JsonModel() - uri = u"https://www.googleapis.com/someapi/v1/collection/?foo=bar" - method = u"POST" + uri = "https://www.googleapis.com/someapi/v1/collection/?foo=bar" + method = "POST" request = HttpRequest( http, model.response, uri, method=method, - body=u"{}", + body="{}", headers={"content-type": "application/json"}, ) @@ -1089,14 +1104,14 @@ def test_no_retry_403_not_configured_fails_fast(self): def test_no_retry_403_fails_fast(self): http = HttpMockSequence([({"status": "403"}, ""), ({"status": "200"}, "{}")]) model = JsonModel() - uri = u"https://www.googleapis.com/someapi/v1/collection/?foo=bar" - method = u"POST" + uri = "https://www.googleapis.com/someapi/v1/collection/?foo=bar" + method = "POST" request = HttpRequest( http, model.response, uri, method=method, - body=u"{}", + body="{}", headers={"content-type": "application/json"}, ) @@ -1110,14 +1125,14 @@ def test_no_retry_403_fails_fast(self): def test_no_retry_401_fails_fast(self): http = HttpMockSequence([({"status": "401"}, ""), ({"status": "200"}, "{}")]) model = JsonModel() - uri = u"https://www.googleapis.com/someapi/v1/collection/?foo=bar" - method = u"POST" + uri = "https://www.googleapis.com/someapi/v1/collection/?foo=bar" + method = "POST" request = HttpRequest( http, model.response, uri, method=method, - body=u"{}", + body="{}", headers={"content-type": "application/json"}, ) @@ -1136,14 +1151,14 @@ def test_no_retry_403_list_fails(self): ] ) model = JsonModel() - uri = u"https://www.googleapis.com/someapi/v1/collection/?foo=bar" - method = u"POST" + uri = "https://www.googleapis.com/someapi/v1/collection/?foo=bar" + method = "POST" request = HttpRequest( http, model.response, uri, method=method, - body=u"{}", + body="{}", headers={"content-type": "application/json"}, ) @@ -1198,7 +1213,7 @@ def test_serialize_request(self): None, "https://www.googleapis.com/someapi/v1/collection/?foo=bar", method="POST", - body=u"{}", + body="{}", headers={"content-type": "application/json"}, methodId=None, resumable=None, @@ -1531,7 +1546,7 @@ def test_http_errors_passed_to_callback(self): self.assertEqual( "Authorization Required", callbacks.exceptions["1"].resp.reason ) - self.assertEqual({u"baz": u"qux"}, callbacks.responses["2"]) + self.assertEqual({"baz": "qux"}, callbacks.responses["2"]) self.assertEqual(None, callbacks.exceptions["2"]) def test_execute_global_callback(self): diff --git a/tests/test_json_model.py b/tests/test_json_model.py index 533361c36e5..a34cd6d94b5 100644 --- a/tests/test_json_model.py +++ b/tests/test_json_model.py @@ -23,20 +23,20 @@ __author__ = "jcgregorio@google.com (Joe Gregorio)" import io -import httplib2 import json import platform import unittest import urllib -import googleapiclient.model +import httplib2 from googleapiclient import version as googleapiclient_version from googleapiclient.errors import HttpError +import googleapiclient.model from googleapiclient.model import JsonModel _LIBRARY_VERSION = googleapiclient_version.__version__ -CSV_TEXT_MOCK = 'column1,column2,column3\nstring1,1.2,string2' +CSV_TEXT_MOCK = "column1,column2,column3\nstring1,1.2,string2" class Model(unittest.TestCase): @@ -116,7 +116,7 @@ def test_json_build_query(self): path_params = {} query_params = { "foo": 1, - "bar": u"\N{COMET}", + "bar": "\N{COMET}", "baz": ["fe", "fi", "fo", "fum"], # Repeated parameters "qux": [], } @@ -131,7 +131,7 @@ def test_json_build_query(self): query_dict = urllib.parse.parse_qs(query[1:]) self.assertEqual(query_dict["foo"], ["1"]) - self.assertEqual(query_dict["bar"], [u"\N{COMET}"]) + self.assertEqual(query_dict["bar"], ["\N{COMET}"]) self.assertEqual(query_dict["baz"], ["fe", "fi", "fo", "fum"]) self.assertTrue("qux" not in query_dict) self.assertEqual(body, "{}") @@ -301,7 +301,7 @@ def test_no_data_wrapper_deserialize_text_format(self): def test_no_data_wrapper_deserialize_raise_type_error(self): buffer = io.StringIO() - buffer.write('String buffer') + buffer.write("String buffer") model = JsonModel(data_wrapper=False) resp = httplib2.Response({"status": "500"}) resp.reason = "The JSON object must be str, bytes or bytearray, not StringIO" diff --git a/tests/test_mocks.py b/tests/test_mocks.py index 287da958552..1f542b594c4 100644 --- a/tests/test_mocks.py +++ b/tests/test_mocks.py @@ -22,17 +22,14 @@ __author__ = "jcgregorio@google.com (Joe Gregorio)" -import httplib2 import os import unittest -from googleapiclient.errors import HttpError -from googleapiclient.errors import UnexpectedBodyError -from googleapiclient.errors import UnexpectedMethodError -from googleapiclient.discovery import build -from googleapiclient.http import RequestMockBuilder -from googleapiclient.http import HttpMock +import httplib2 +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError, UnexpectedBodyError, UnexpectedMethodError +from googleapiclient.http import HttpMock, RequestMockBuilder DATA_DIR = os.path.join(os.path.dirname(__file__), "data") @@ -48,7 +45,13 @@ def setUp(self): def test_default_response(self): requestBuilder = RequestMockBuilder({}) - plus = build("plus", "v1", http=self.http, requestBuilder=requestBuilder, static_discovery=False) + plus = build( + "plus", + "v1", + http=self.http, + requestBuilder=requestBuilder, + static_discovery=False, + ) activity = plus.activities().get(activityId="tag:blah").execute() self.assertEqual({}, activity) @@ -56,7 +59,13 @@ def test_simple_response(self): requestBuilder = RequestMockBuilder( {"plus.activities.get": (None, '{"foo": "bar"}')} ) - plus = build("plus", "v1", http=self.http, requestBuilder=requestBuilder, static_discovery=False) + plus = build( + "plus", + "v1", + http=self.http, + requestBuilder=requestBuilder, + static_discovery=False, + ) activity = plus.activities().get(activityId="tag:blah").execute() self.assertEqual({"foo": "bar"}, activity) @@ -64,7 +73,13 @@ def test_simple_response(self): def test_unexpected_call(self): requestBuilder = RequestMockBuilder({}, check_unexpected=True) - plus = build("plus", "v1", http=self.http, requestBuilder=requestBuilder, static_discovery=False) + plus = build( + "plus", + "v1", + http=self.http, + requestBuilder=requestBuilder, + static_discovery=False, + ) try: plus.activities().get(activityId="tag:blah").execute() @@ -76,7 +91,13 @@ def test_simple_unexpected_body(self): requestBuilder = RequestMockBuilder( {"zoo.animals.insert": (None, '{"data": {"foo": "bar"}}', None)} ) - zoo = build("zoo", "v1", http=self.zoo_http, requestBuilder=requestBuilder, static_discovery=False) + zoo = build( + "zoo", + "v1", + http=self.zoo_http, + requestBuilder=requestBuilder, + static_discovery=False, + ) try: zoo.animals().insert(body="{}").execute() @@ -88,7 +109,13 @@ def test_simple_expected_body(self): requestBuilder = RequestMockBuilder( {"zoo.animals.insert": (None, '{"data": {"foo": "bar"}}', "{}")} ) - zoo = build("zoo", "v1", http=self.zoo_http, requestBuilder=requestBuilder, static_discovery=False) + zoo = build( + "zoo", + "v1", + http=self.zoo_http, + requestBuilder=requestBuilder, + static_discovery=False, + ) try: zoo.animals().insert(body="").execute() @@ -106,7 +133,13 @@ def test_simple_wrong_body(self): ) } ) - zoo = build("zoo", "v1", http=self.zoo_http, requestBuilder=requestBuilder, static_discovery=False) + zoo = build( + "zoo", + "v1", + http=self.zoo_http, + requestBuilder=requestBuilder, + static_discovery=False, + ) try: zoo.animals().insert(body='{"data": {"foo": "blah"}}').execute() @@ -124,7 +157,13 @@ def test_simple_matching_str_body(self): ) } ) - zoo = build("zoo", "v1", http=self.zoo_http, requestBuilder=requestBuilder, static_discovery=False) + zoo = build( + "zoo", + "v1", + http=self.zoo_http, + requestBuilder=requestBuilder, + static_discovery=False, + ) activity = zoo.animals().insert(body={"data": {"foo": "bar"}}).execute() self.assertEqual({"foo": "bar"}, activity) @@ -139,7 +178,13 @@ def test_simple_matching_dict_body(self): ) } ) - zoo = build("zoo", "v1", http=self.zoo_http, requestBuilder=requestBuilder, static_discovery=False) + zoo = build( + "zoo", + "v1", + http=self.zoo_http, + requestBuilder=requestBuilder, + static_discovery=False, + ) activity = zoo.animals().insert(body={"data": {"foo": "bar"}}).execute() self.assertEqual({"foo": "bar"}, activity) @@ -149,7 +194,13 @@ def test_errors(self): requestBuilder = RequestMockBuilder( {"plus.activities.list": (errorResponse, b"{}")} ) - plus = build("plus", "v1", http=self.http, requestBuilder=requestBuilder, static_discovery=False) + plus = build( + "plus", + "v1", + http=self.http, + requestBuilder=requestBuilder, + static_discovery=False, + ) try: activity = ( diff --git a/tests/test_model.py b/tests/test_model.py index 07d226100c9..cd868c47ba5 100644 --- a/tests/test_model.py +++ b/tests/test_model.py @@ -25,9 +25,7 @@ import unittest -from googleapiclient.model import BaseModel -from googleapiclient.model import makepatch - +from googleapiclient.model import BaseModel, makepatch TEST_CASES = [ # (message, original, modified, expected) @@ -76,15 +74,15 @@ def test_build_query(self): test_cases = [ ("hello", "world", "?hello=world"), - ("hello", u"world", "?hello=world"), + ("hello", "world", "?hello=world"), + ("hello", "세계", "?hello=%EC%84%B8%EA%B3%84"), ("hello", "세계", "?hello=%EC%84%B8%EA%B3%84"), - ("hello", u"세계", "?hello=%EC%84%B8%EA%B3%84"), ("hello", "こんにちは", "?hello=%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF"), - ("hello", u"こんにちは", "?hello=%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF"), + ("hello", "こんにちは", "?hello=%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF"), + ("hello", "你好", "?hello=%E4%BD%A0%E5%A5%BD"), ("hello", "你好", "?hello=%E4%BD%A0%E5%A5%BD"), - ("hello", u"你好", "?hello=%E4%BD%A0%E5%A5%BD"), ("hello", "مرحبا", "?hello=%D9%85%D8%B1%D8%AD%D8%A8%D8%A7"), - ("hello", u"مرحبا", "?hello=%D9%85%D8%B1%D8%AD%D8%A8%D8%A7"), + ("hello", "مرحبا", "?hello=%D9%85%D8%B1%D8%AD%D8%A8%D8%A7"), ] for case in test_cases: diff --git a/tests/test_protobuf_model.py b/tests/test_protobuf_model.py index 68e39632c2f..47b6ee6472b 100644 --- a/tests/test_protobuf_model.py +++ b/tests/test_protobuf_model.py @@ -23,6 +23,7 @@ __author__ = "mmcdonald@google.com (Matt McDonald)" import unittest + import httplib2 from googleapiclient.model import ProtocolBufferModel diff --git a/tests/test_schema.py b/tests/test_schema.py index b288908cc8f..5b04968149a 100644 --- a/tests/test_schema.py +++ b/tests/test_schema.py @@ -23,7 +23,6 @@ from googleapiclient.schema import Schemas - DATA_DIR = os.path.join(os.path.dirname(__file__), "data")