diff options
author | Haibo Huang <hhb@google.com> | 2020-04-22 23:50:54 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2020-04-22 23:50:54 +0000 |
commit | 4fd3d4c4bad3b5edccba7cc76bbde8e29e4c13f2 (patch) | |
tree | 2a7502f1ce06ddd0d11041b6cccb532279e09f8b | |
parent | ee04b09c8f369d8e1be7cec5a94c5596fe1640d4 (diff) | |
parent | b4903596ac5a6b86c29aeba73f1f9c006daf41ff (diff) | |
download | google-api-python-client-4fd3d4c4bad3b5edccba7cc76bbde8e29e4c13f2.tar.gz |
Upgrade python/google-api-python-client to v1.8.1 am: cf97e39f87 am: b4903596ac
Change-Id: I80aef0d9ed3435ca828d2fcdeb6eeafaf7c34d3e
-rw-r--r-- | .github/ISSUE_TEMPLATE/bug_report.md | 22 | ||||
-rw-r--r-- | .github/PULL_REQUEST_TEMPLATE.md | 7 | ||||
-rw-r--r-- | .github/release-please.yml | 1 | ||||
-rw-r--r-- | .gitignore | 2 | ||||
-rwxr-xr-x | .kokoro/build.sh | 32 | ||||
-rw-r--r-- | .kokoro/common.cfg | 19 | ||||
-rw-r--r-- | .kokoro/continuous/continuous.cfg | 2 | ||||
-rw-r--r-- | .kokoro/presubmit/presubmit.cfg | 2 | ||||
-rwxr-xr-x | .kokoro/release.sh | 34 | ||||
-rw-r--r-- | .kokoro/release/common.cfg | 64 | ||||
-rw-r--r-- | .kokoro/release/release.cfg | 1 | ||||
-rwxr-xr-x | .kokoro/trampoline.sh | 17 | ||||
-rw-r--r-- | .repo-metadata.json | 11 | ||||
-rw-r--r-- | CHANGELOG.md (renamed from CHANGELOG) | 103 | ||||
-rw-r--r-- | METADATA | 6 | ||||
-rw-r--r-- | README.md | 4 | ||||
-rw-r--r-- | googleapiclient/__init__.py | 2 | ||||
-rw-r--r-- | googleapiclient/discovery.py | 23 | ||||
-rw-r--r-- | googleapiclient/http.py | 28 | ||||
-rw-r--r-- | googleapiclient/model.py | 7 | ||||
-rw-r--r-- | noxfile.py | 78 | ||||
-rw-r--r-- | renovate.json | 5 | ||||
-rw-r--r-- | setup.py | 24 | ||||
-rw-r--r-- | synth.metadata | 12 | ||||
-rw-r--r-- | synth.py | 30 | ||||
-rw-r--r-- | tests/test_discovery.py | 144 | ||||
-rw-r--r-- | tests/test_http.py | 63 | ||||
-rw-r--r-- | tests/test_json_model.py | 6 | ||||
-rw-r--r-- | tox.ini | 24 |
29 files changed, 563 insertions, 210 deletions
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 3d44f3e4b..7d3526577 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -11,23 +11,33 @@ Thanks for stopping by to let us know something could be better! Please run down the following list and make sure you've tried the usual "quick fixes": - Search the issues already opened: https://github.com/googleapis/google-api-python-client/issues - - If you have a question, post on Stackoverflow under the `google-api` tag. - - If you are reporting an issue or requesting a feature for a G Suite API, please use their [public issue tracker](https://gsuite-developers.googleblog.com/2017/03/a-new-issue-tracker-for-g-suite.html) + - Search StackOverflow: https://stackoverflow.com/questions/tagged/google-cloud-platform+python If you are still having issues, please be sure to include as much information as possible: #### Environment details - - OS: - - Python version: - - pip version: - - `google-api-python-client` version: + - OS type and version: + - Python version: `python --version` + - pip version: `pip --version` + - `google-api-python-client` version: `pip show google-api-python-client` #### Steps to reproduce 1. ? 2. ? +#### Code example + +```python +# example +``` + +#### Stack trace +``` +# example +``` + Making sure to follow these steps will guarantee the quickest resolution possible. Thanks! diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..24c6fa88f --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,7 @@ +Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: +- [ ] Make sure to open an issue as a [bug/issue](https://github.com/googleapis/google-api-python-client/issues/new/choose) before writing your code! That way we can discuss the change, evaluate designs, and agree on the general idea +- [ ] Ensure the tests and linter pass +- [ ] Code coverage does not decrease (if any source code was changed) +- [ ] Appropriate docs were updated (if necessary) + +Fixes #<issue_number_goes_here> 🦕 diff --git a/.github/release-please.yml b/.github/release-please.yml new file mode 100644 index 000000000..4507ad059 --- /dev/null +++ b/.github/release-please.yml @@ -0,0 +1 @@ +releaseType: python diff --git a/.gitignore b/.gitignore index cf2c4a698..1637b1d3b 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,7 @@ build/ dist/ # Test files -.tox/ +.nox/ # Coverage files .coverage diff --git a/.kokoro/build.sh b/.kokoro/build.sh index e0c966b5a..ab8cebb4b 100755 --- a/.kokoro/build.sh +++ b/.kokoro/build.sh @@ -1,4 +1,17 @@ #!/bin/bash +# Copyright 2018 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. set -eo pipefail @@ -7,7 +20,20 @@ cd github/google-api-python-client # Disable buffering, so that the logs stream through. export PYTHONUNBUFFERED=1 -python3 -m pip install --upgrade tox +# Debug: show build environment +env | grep KOKORO -# Run tests -tox +# Setup service account credentials. +export GOOGLE_APPLICATION_CREDENTIALS=${KOKORO_GFILE_DIR}/service-account.json + +# Setup project id. +export PROJECT_ID=$(cat "${KOKORO_GFILE_DIR}/project-id.json") + +# Remove old nox +python3.6 -m pip uninstall --yes --quiet nox-automation + +# Install nox +python3.6 -m pip install --upgrade --quiet nox +python3.6 -m nox --version + +python3.6 -m nox diff --git a/.kokoro/common.cfg b/.kokoro/common.cfg deleted file mode 100644 index c21dc6d4f..000000000 --- a/.kokoro/common.cfg +++ /dev/null @@ -1,19 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -# Download trampoline resources. -gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" - -# Use the trampoline script to run in docker. -build_file: "google-api-python-client/.kokoro/trampoline.sh" - -# Configure the docker image for kokoro-trampoline. -env_vars: { - key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-kokoro-resources/python-multi" -} - -# Tell the trampoline which build file to use. -env_vars: { - key: "TRAMPOLINE_BUILD_FILE" - value: "github/google-api-python-client/.kokoro/build.sh" -} diff --git a/.kokoro/continuous/continuous.cfg b/.kokoro/continuous/continuous.cfg index 18a4c3532..8f43917d9 100644 --- a/.kokoro/continuous/continuous.cfg +++ b/.kokoro/continuous/continuous.cfg @@ -1 +1 @@ -# Format: //devtools/kokoro/config/proto/build.proto +# Format: //devtools/kokoro/config/proto/build.proto
\ No newline at end of file diff --git a/.kokoro/presubmit/presubmit.cfg b/.kokoro/presubmit/presubmit.cfg index 18a4c3532..8f43917d9 100644 --- a/.kokoro/presubmit/presubmit.cfg +++ b/.kokoro/presubmit/presubmit.cfg @@ -1 +1 @@ -# Format: //devtools/kokoro/config/proto/build.proto +# Format: //devtools/kokoro/config/proto/build.proto
\ No newline at end of file diff --git a/.kokoro/release.sh b/.kokoro/release.sh new file mode 100755 index 000000000..b4756d089 --- /dev/null +++ b/.kokoro/release.sh @@ -0,0 +1,34 @@ +#!/bin/bash +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +#!/bin/bash + +set -eo pipefail + +# Start the releasetool reporter +python3 -m pip install gcp-releasetool +python3 -m releasetool publish-reporter-script > /tmp/publisher-script; source /tmp/publisher-script + +# Ensure that we have the latest versions of Twine, Wheel, and Setuptools. +python3 -m pip install --upgrade twine wheel setuptools + +# Disable buffering, so that the logs stream through. +export PYTHONUNBUFFERED=1 + +# Move into the package, build the distribution and upload. +TWINE_PASSWORD=$(cat "${KOKORO_KEYSTORE_DIR}/73713_google_cloud_pypi_password") +cd github/google-api-python-client +python3 setup.py sdist bdist_wheel +twine upload --username gcloudpypi --password "${TWINE_PASSWORD}" dist/* diff --git a/.kokoro/release/common.cfg b/.kokoro/release/common.cfg new file mode 100644 index 000000000..6a8864b88 --- /dev/null +++ b/.kokoro/release/common.cfg @@ -0,0 +1,64 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +# Build logs will be here +action { + define_artifacts { + regex: "**/*sponge_log.xml" + } +} + +# Download trampoline resources. +gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" + +# Use the trampoline script to run in docker. +build_file: "google-api-python-client/.kokoro/trampoline.sh" + +# Configure the docker image for kokoro-trampoline. +env_vars: { + key: "TRAMPOLINE_IMAGE" + value: "gcr.io/cloud-devrel-kokoro-resources/python-multi" +} +env_vars: { + key: "TRAMPOLINE_BUILD_FILE" + value: "github/google-api-python-client/.kokoro/release.sh" +} + +# Fetch the token needed for reporting release status to GitHub +before_action { + fetch_keystore { + keystore_resource { + keystore_config_id: 73713 + keyname: "yoshi-automation-github-key" + } + } +} + +# Fetch PyPI password +before_action { + fetch_keystore { + keystore_resource { + keystore_config_id: 73713 + keyname: "google_cloud_pypi_password" + } + } +} + +# Fetch magictoken to use with Magic Github Proxy +before_action { + fetch_keystore { + keystore_resource { + keystore_config_id: 73713 + keyname: "releasetool-magictoken" + } + } +} + +# Fetch api key to use with Magic Github Proxy +before_action { + fetch_keystore { + keystore_resource { + keystore_config_id: 73713 + keyname: "magic-github-proxy-api-key" + } + } +} diff --git a/.kokoro/release/release.cfg b/.kokoro/release/release.cfg new file mode 100644 index 000000000..8f43917d9 --- /dev/null +++ b/.kokoro/release/release.cfg @@ -0,0 +1 @@ +# Format: //devtools/kokoro/config/proto/build.proto
\ No newline at end of file diff --git a/.kokoro/trampoline.sh b/.kokoro/trampoline.sh index 0efc3be38..e8c4251f3 100755 --- a/.kokoro/trampoline.sh +++ b/.kokoro/trampoline.sh @@ -12,13 +12,12 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. + set -eo pipefail -# Always run the cleanup script, regardless of the success of bouncing into -# the container. -function cleanup() { - chmod +x ${KOKORO_GFILE_DIR}/trampoline_cleanup.sh - ${KOKORO_GFILE_DIR}/trampoline_cleanup.sh - echo "cleanup"; -} -trap cleanup EXIT -python3 "${KOKORO_GFILE_DIR}/trampoline_v1.py" + +python3 "${KOKORO_GFILE_DIR}/trampoline_v1.py" || ret_code=$? + +chmod +x ${KOKORO_GFILE_DIR}/trampoline_cleanup.sh +${KOKORO_GFILE_DIR}/trampoline_cleanup.sh || true + +exit ${ret_code} diff --git a/.repo-metadata.json b/.repo-metadata.json new file mode 100644 index 000000000..1b810b0be --- /dev/null +++ b/.repo-metadata.json @@ -0,0 +1,11 @@ + +{ + "name": "google-api-python-client", + "name_pretty": "Google API Python Client", + "client_documentation": "https://github.com/googleapis/google-api-python-client/tree/master/docs#google-api-client-library-for-python-docs", + "issue_tracker": "https://github.com/googleapis/google-api-python-client/issues", + "release_level": "ga", + "language": "python", + "repo": "googleapis/google-api-python-client", + "distribution_name": "google-api-python-client" + }
\ No newline at end of file diff --git a/CHANGELOG b/CHANGELOG.md index 92b744e57..b0eb4fb1d 100644 --- a/CHANGELOG +++ b/CHANGELOG.md @@ -1,4 +1,25 @@ -v1.7.12 +# Changelog + +### [1.8.1](https://www.github.com/googleapis/google-api-python-client/compare/v1.8.0...v1.8.1) (2020-04-20) + + +### Bug Fixes + +* Adding ConnectionError to retry mechanism ([#822](https://www.github.com/googleapis/google-api-python-client/issues/822)) ([c7516a2](https://www.github.com/googleapis/google-api-python-client/commit/c7516a2ea2c229479633690c109f8763dc0b30ed)), closes [googleapis#558](https://www.github.com/googleapis/googleapis/issues/558) +* replace '-' in method names with '_' ([#863](https://www.github.com/googleapis/google-api-python-client/issues/863)) ([8ed729f](https://www.github.com/googleapis/google-api-python-client/commit/8ed729f1d868a8713ab442bf0bf59e77ba36afb6)) + +### v1.8.0 + Version 1.8.0 + + Release to support API endpoint override. + + New Features + - Add api endpoint override. ([#829](https://github.com/googleapis/google-api-python-client/pull/829)) + + Implementation Changes + - Don't set http.redirect_codes if the attr doesn't exist and allow more httplib2 versions. ([#841](https://github.com/googleapis/google-api-python-client/pull/841)) + +### v1.7.12 Version 1.7.12 Bugfix release @@ -28,7 +49,7 @@ v1.7.12 - Blacken ([#772](https://github.com/googleapis/google-api-python-client/pull/722)) - Move kokoro configs ([#832](https://github.com/googleapis/google-api-python-client/pull/832)) -v1.7.11 +### v1.7.11 Version 1.7.11 Bugfix release @@ -40,7 +61,7 @@ v1.7.11 - Fix typo in filename used in 'docs/auth.md' ([#736](https://github.com/googleapis/google-api-python-client/pull/736)) -v1.7.10 +### v1.7.10 Version 1.7.10 Bugfix release @@ -62,21 +83,21 @@ v1.7.10 - tox.ini: Look for Python syntax errors and undefined names ([#721](https://github.com/googleapis/google-api-python-client/pull/721)) -v1.7.9 +### v1.7.9 Version 1.7.9 Bugfix release - Remove Django Samples. ([#657](https://github.com/googleapis/google-api-python-client/pull/657)) - Call request_orig with kwargs ([#658](https://github.com/googleapis/google-api-python-client/pull/658)) -v1.7.8 +### v1.7.8 Version 1.7.8 Bugfix release - Convert '$' in method name to '_' ([#616](https://github.com/googleapis/google-api-python-client/pull/616)) - Alias unitest2 import as unittest in test__auth.py ([#613](https://github.com/googleapis/google-api-python-client/pull/613)) -v1.7.7 +### v1.7.7 Version 1.7.7 Bugfix release @@ -89,28 +110,28 @@ v1.7.7 - Add badges ([#455](https://github.com/google/google-api-python-client/pull/455)) -v1.7.6 +### v1.7.6 Version 1.7.6 Bugfix release - Add client-side limit for batch requests (#585) -v1.7.5 +### v1.7.5 Version 1.7.5 Bugfix release - Fix the client to respect the passed in developerKey and credentials -v1.7.4 +### v1.7.4 Version 1.7.4 Bugfix release - Catch ServerNotFoundError to retry the request (#532) -v1.7.3 +### v1.7.3 Version 1.7.3 Bugfix release @@ -118,21 +139,21 @@ v1.7.3 - Make apiclient.sample_tools gracefully fail to import (#525). -v1.7.2 +### v1.7.2 Version 1.7.2 Bugfix release - Remove unnecessary check in apiclient/__ini__.py (#522). -v1.7.1 +### v1.7.1 Version 1.7.1 Bugfix release - Remove unnecessary check in setup.py (#518). -v1.7.0 +### v1.7.0 Version 1.7.0 This release drops the hard requirement on oauth2client and installs @@ -142,7 +163,7 @@ v1.7.0 - Drop oauth2client dependency (#499) - Include tests in source distribution (#514) -v1.6.7 +### v1.6.7 Version 1.6.7 Bugfix release @@ -159,7 +180,7 @@ v1.6.7 - discovery.py: remove unused oauth2client import. (#492) - Update README to reference GCP API client libraries. (#490) -v1.6.6 +### v1.6.6 Version 1.6.6 Bugfix release @@ -168,7 +189,7 @@ v1.6.6 - Increase the default media chunksize to 100MB. (#482) - Remove unnecessary parsing of mime headers in HttpRequest.__init__ (#467) -v1.6.5 +### v1.6.5 Version 1.6.5 Bugfix release @@ -187,14 +208,14 @@ v1.6.5 - Handle variant error format gracefully. (#459) - Avoid testing against Django >= 2.0.0 on Python 2. (#460) -v1.6.4 +### v1.6.4 Version 1.6.4 Bugfix release - Warn when google-auth credentials are used but google-auth-httplib2 isn't available. (#443) -v1.6.3 +### v1.6.3 Version 1.6.3 Bugfix release @@ -210,7 +231,7 @@ v1.6.3 - Don't treat httplib2.Credentials as oauth credentials. (#425) - Various fixes to the Django sample. (#413) -v1.6.2 +### v1.6.2 Version 1.6.2 Bugfix release @@ -219,14 +240,14 @@ v1.6.2 when a developerKey was specified. (#347) - Official support for Python 3.5 and 3.6. (#341) -v1.6.1 +### v1.6.1 Version 1.6.1 Bugfix release - Fixed a bug where using google-auth with scoped credentials would fail. (#328) -v1.6.0 +### v1.6.0 Version 1.6.0 Release to drop support for Python 2.6 and add support for google-auth. @@ -247,7 +268,7 @@ v1.6.0 - Fixed resumable upload failure when receiving a 308 response. (#312) - Clarified the support versions of Python 3. (#316) -v1.5.5 +### v1.5.5 Version 1.5.5 Bugfix release @@ -257,7 +278,7 @@ v1.5.5 - Refresh all discovery docs, not just the preferred ones. (#298) - Update minimum httplib2 dependency to >=0.9.2. -v1.5.4 +### v1.5.4 Version 1.5.4 Bugfix release @@ -267,14 +288,14 @@ v1.5.4 - Allow oauth2client 4.0.0, with the caveat that file-based discovery caching is disabled. -v1.5.3 +### v1.5.3 Version 1.5.3 Bugfix release - Fixed import error with oauth2client >= 3.0.0. (#270) -v1.5.2 +### v1.5.2 Version 1.5.2 Bugfix release @@ -285,7 +306,7 @@ v1.5.2 - Obtain access token if necessary in BatchHttpRequest.execute(). (#232) - Warn when running tests using HttpMock without having a cache. (#261) -v1.5.1 +### v1.5.1 Version 1.5.1 Bugfix release @@ -298,7 +319,7 @@ v1.5.1 - Use named loggers instead of the root logger. (#206) - New search console example. (#212) -v1.5.0 +### v1.5.0 Version 1.5.0 Release to support oauth2client >= 2.0.0. @@ -309,22 +330,22 @@ v1.5.0 - Handle SSL errors with retries (#160) - Fix incompatibility with oauth2client v2.0.0 (#182) -v1.4.2 +### v1.4.2 Version 1.4.2 Add automatic caching for the discovery docs. -v1.4.1 +### v1.4.1 Version 1.4.1 Add the googleapiclient.discovery.Resource.new_batch_http_request method. -v1.4.0 +### v1.4.0 Version 1.4.0 Python 3 support. -v1.3.2 +### v1.3.2 Version 1.3.2 Small bugfix release. @@ -334,12 +355,12 @@ v1.3.2 - Better handling of `content-length` in media requests. - Add support for methodPath entries containing colon. -v1.3.1 +### v1.3.1 Version 1.3.1 Quick release for a fix around aliasing in v1.3. -v1.3 +### v1.3 Version 1.3 Add support for the Google Application Default Credentials. @@ -358,7 +379,7 @@ v1.3 setup.py attempts to detect this and prevents it. Simply remove the previous version and reinstall to fix this. -v1.2 +### v1.2 Version 1.2 The use of the gflags library is now deprecated, and is no longer a @@ -384,7 +405,7 @@ v1.2 - Update AdExchange Buyer API examples to version v1.2. -v1.1 +### v1.1 Version 1.1 Add PEM support to SignedJWTAssertionCredentials (used to only support @@ -412,12 +433,12 @@ v1.1 - Ensure that dataWrapper feature is checked before using the 'data' value. - HMAC verification does not use a constant time algorithm. -v1.0 +### v1.0 Version 1.0 - Changes to the code for running tests and building releases. -v1.0c3 +### v1.0c3 Version 1.0 Release Candidate 3 - In samples and oauth2 decorator, escape untrusted content before displaying it. @@ -440,7 +461,7 @@ v1.0c3 - oauth2client support for URL-encoded format of exchange token response (e.g. Facebook) - Build cleaner and easier to read docs for dynamic surfaces. -v1.0c2 +### v1.0c2 Version 1.0 Release Candidate 2 - Parameter values of None should be treated as missing. Fixes issue #144. @@ -448,7 +469,7 @@ v1.0c2 - Move all remaining samples over to client_secrets.json. Fixes issue #156. - Make locked_file.py understand win32file primitives for better awesomeness. -v1.0c1 +### v1.0c1 Version 1.0 Release Candidate 1 - Documentation for the library has switched to epydoc: @@ -473,7 +494,7 @@ v1.0c1 * new analytics api samples. Reviewed here: http://codereview.appspot.com/5494058/ - Convert all inline samples to the Farm API for consistency. -v1.0beta8 +### v1.0beta8 - Updated meda upload support. - Many fixes for batch requests. - Better handling for requests that don't require a body. @@ -488,7 +509,7 @@ v1.0beta8 'body' parameter in your call. The solution is to remove the unneeded body={} parameter. -v1.0beta7 +### v1.0beta7 - Support for batch requests. http://code.google.com/p/google-api-python-client/wiki/Batch - Support for media upload. http://code.google.com/p/google-api-python-client/wiki/MediaUpload - Better handling for APIs that return something other than JSON. @@ -9,10 +9,10 @@ third_party { type: GIT value: "https://github.com/google/google-api-python-client" } - version: "v1.7.12" + version: "v1.8.1" last_upgrade_date { year: 2020 - month: 3 - day: 11 + month: 4 + day: 20 } } @@ -10,12 +10,14 @@ These client libraries are officially supported by Google. However, the librari See the [docs folder](docs/README.md) for more detailed instructions and additional documentation. -## Google Cloud Platform / Google Ads +## Other Google API libraries For Google Cloud Platform APIs such as Datastore, Cloud Storage or Pub/Sub, we recommend using [Cloud Client Libraries for Python](https://github.com/GoogleCloudPlatform/google-cloud-python). For Google Ads API, we recommend using [Google Ads API Client Library for Python](https://github.com/googleads/google-ads-python/). +For Google Firebase Admin API, we recommend using [Firebase Admin Python SDK](https://github.com/firebase/firebase-admin-python). + ## Installation Install this library in a [virtualenv](https://virtualenv.pypa.io/en/latest/) using pip. virtualenv is a tool to diff --git a/googleapiclient/__init__.py b/googleapiclient/__init__.py index 140b946fb..c9218dd85 100644 --- a/googleapiclient/__init__.py +++ b/googleapiclient/__init__.py @@ -12,8 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "1.7.12" - # Set default logging handler to avoid "No handler found" warnings. import logging diff --git a/googleapiclient/discovery.py b/googleapiclient/discovery.py index 87403b9e8..66d4927aa 100644 --- a/googleapiclient/discovery.py +++ b/googleapiclient/discovery.py @@ -46,6 +46,7 @@ import re # Third-party imports import httplib2 import uritemplate +import google.api_core.client_options # Local imports from googleapiclient import _auth @@ -130,10 +131,10 @@ def fix_method_name(name): name: string, method name. Returns: - The name with '_' appended if the name is a reserved word and '$' + The name with '_' appended if the name is a reserved word and '$' and '-' replaced with '_'. """ - name = name.replace("$", "_") + name = name.replace("$", "_").replace("-", "_") if keyword.iskeyword(name) or name in RESERVED_WORDS: return name + "_" else: @@ -176,6 +177,7 @@ def build( credentials=None, cache_discovery=True, cache=None, + client_options=None, ): """Construct a Resource for interacting with an API. @@ -202,6 +204,8 @@ def build( 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: Dictionary or google.api_core.client_options, Client options to set user + options on the client. API endpoint should be set through client_options. Returns: A Resource object with methods for interacting with the service. @@ -228,6 +232,7 @@ def build( model=model, requestBuilder=requestBuilder, credentials=credentials, + client_options=client_options ) except HttpError as e: if e.resp.status == http_client.NOT_FOUND: @@ -304,6 +309,7 @@ def build_from_document( model=None, requestBuilder=HttpRequest, credentials=None, + client_options=None ): """Create a Resource for interacting with an API. @@ -328,6 +334,8 @@ def build_from_document( credentials: oauth2client.Credentials or google.auth.credentials.Credentials, credentials to be used for authentication. + client_options: Dictionary or google.api_core.client_options, Client options to set user + options on the client. API endpoint should be set through client_options. Returns: A Resource object with methods for interacting with the service. @@ -350,7 +358,16 @@ def build_from_document( ) raise InvalidJsonError() - base = urljoin(service["rootUrl"], service["servicePath"]) + # If an API Endpoint is provided on client options, use that as the base URL + base = urljoin(service['rootUrl'], service["servicePath"]) + if client_options: + if type(client_options) == dict: + client_options = google.api_core.client_options.from_dict( + client_options + ) + if client_options.api_endpoint: + base = client_options.api_endpoint + schema = Schemas(service) # If the http client is not specified, then we must construct an http client diff --git a/googleapiclient/http.py b/googleapiclient/http.py index 719664de1..41256668b 100644 --- a/googleapiclient/http.py +++ b/googleapiclient/http.py @@ -81,6 +81,11 @@ DEFAULT_HTTP_TIMEOUT_SEC = 60 _LEGACY_BATCH_URI = "https://www.googleapis.com/batch" +if six.PY2: + # That's a builtin python3 exception, nonexistent in python2. + # Defined to None to avoid NameError while trying to catch it + ConnectionError = None + def _should_retry_response(resp_status, content): """Determines whether a response should be retried. @@ -177,6 +182,10 @@ def _retry_request( # It's important that this be before socket.error as it's a subclass # socket.timeout has no errorcode exception = socket_timeout + except ConnectionError as connection_error: + # Needs to be before socket.error as it's a subclass of + # OSError (socket.error) + exception = connection_error except socket.error as socket_error: # errno's contents differ by platform, so we have to match by name. if socket.errno.errorcode.get(socket_error.errno) not in { @@ -1751,16 +1760,18 @@ class HttpMockSequence(object): connection_type=None, ): resp, content = self._iterable.pop(0) - if content == "echo_request_headers": + content = six.ensure_binary(content) + + if content == b"echo_request_headers": content = headers - elif content == "echo_request_headers_as_json": + elif content == b"echo_request_headers_as_json": content = json.dumps(headers) - elif content == "echo_request_body": + elif content == b"echo_request_body": if hasattr(body, "read"): content = body.read() else: content = body - elif content == "echo_request_uri": + elif content == b"echo_request_uri": content = uri if isinstance(content, six.text_type): content = content.encode("utf-8") @@ -1891,6 +1902,13 @@ def build_http(): # for Resumable Uploads rather than Permanent Redirects. # This asks httplib2 to exclude 308s from the status codes # it treats as redirects - http.redirect_codes = http.redirect_codes - {308} + try: + http.redirect_codes = http.redirect_codes - {308} + except AttributeError: + # Apache Beam tests depend on this library and cannot + # currently upgrade their httplib2 version + # http.redirect_codes does not exist in previous versions + # of httplib2, so pass + pass return http diff --git a/googleapiclient/model.py b/googleapiclient/model.py index 554056e3e..f58549c49 100644 --- a/googleapiclient/model.py +++ b/googleapiclient/model.py @@ -27,12 +27,13 @@ __author__ = "jcgregorio@google.com (Joe Gregorio)" import json import logging import platform +import pkg_resources from six.moves.urllib.parse import urlencode -from googleapiclient import __version__ from googleapiclient.errors import HttpError +_LIBRARY_VERSION = pkg_resources.get_distribution("google-api-python-client").version _PY_VERSION = platform.python_version() LOGGER = logging.getLogger(__name__) @@ -152,7 +153,7 @@ class BaseModel(Model): else: headers["x-goog-api-client"] = "" headers["x-goog-api-client"] += "gdcl/%s gl-python/%s" % ( - __version__, + _LIBRARY_VERSION, _PY_VERSION, ) @@ -218,7 +219,7 @@ class BaseModel(Model): return self.no_content_response return self.deserialize(content) else: - LOGGER.debug("Content from bad request was: %s" % content) + LOGGER.debug("Content from bad request was: %r" % content) raise HttpError(resp, content) def serialize(self, body_value): diff --git a/noxfile.py b/noxfile.py new file mode 100644 index 000000000..438ff41c5 --- /dev/null +++ b/noxfile.py @@ -0,0 +1,78 @@ + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import nox + +test_dependencies = [ + "google-auth", + "google-auth-httplib2", + "mox", + "pyopenssl", + "pytest", + "pytest-cov", + "webtest", + "coverage", + "unittest2", + "mock", +] + + +@nox.session(python=["3.7"]) +def lint(session): + session.install("flake8") + session.run( + "flake8", + "googleapiclient", + "tests", + "--count", + "--select=E9,F63,F7,F82", + "--show-source", + "--statistics", + ) + + +@nox.parametrize( + "oauth2client", + [ + "oauth2client<2dev", + "oauth2client>=2,<=3dev", + "oauth2client>=3,<=4dev", + "oauth2client>=4,<=5dev", + ], +) +@nox.session(python=["2.7", "3.5", "3.6", "3.7"]) +def unit(session, oauth2client): + session.install(*test_dependencies) + session.install(oauth2client) + if session.python < "3.0": + session.install("django<2.0.0") + else: + session.install("django>=2.0.0") + + session.install('.') + + # Run py.test against the unit tests. + session.run( + "py.test", + "--quiet", + "--cov=googleapiclient", + "--cov=tests", + "--cov-append", + "--cov-config=.coveragerc", + "--cov-report=", + "--cov-fail-under=85", + "tests", + *session.posargs, + )
\ No newline at end of file diff --git a/renovate.json b/renovate.json new file mode 100644 index 000000000..f45d8f110 --- /dev/null +++ b/renovate.json @@ -0,0 +1,5 @@ +{ + "extends": [ + "config:base" + ] +} @@ -28,31 +28,40 @@ if (3, 1) <= sys.version_info < (3, 4): print("google-api-python-client requires python3 version >= 3.4.", file=sys.stderr) sys.exit(1) +import io +import os from setuptools import setup packages = ["apiclient", "googleapiclient", "googleapiclient/discovery_cache"] install_requires = [ - "httplib2>=0.17.0,<1dev", + # NOTE: Apache Beam tests depend on this library and cannot + # currently upgrade their httplib2 version. + # Please see https://github.com/googleapis/google-api-python-client/pull/841 + "httplib2>=0.9.2,<1dev", "google-auth>=1.4.1", "google-auth-httplib2>=0.0.3", + "google-api-core>=1.13.0,<2dev", "six>=1.6.1,<2dev", "uritemplate>=3.0.0,<4dev", ] -long_desc = """The Google API Client for Python is a client library for -accessing the Plus, Moderator, and many other Google APIs.""" +package_root = os.path.abspath(os.path.dirname(__file__)) -import googleapiclient +readme_filename = os.path.join(package_root, "README.md") +with io.open(readme_filename, encoding="utf-8") as readme_file: + readme = readme_file.read() -version = googleapiclient.__version__ +version = "1.8.1" setup( name="google-api-python-client", version=version, description="Google API Client Library for Python", - long_description=long_desc, - author="Google Inc.", + long_description=readme, + long_description_content_type='text/markdown', + author="Google LLC", + author_email="googleapis-packages@google.com", url="http://github.com/google/google-api-python-client/", install_requires=install_requires, python_requires=">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*", @@ -64,7 +73,6 @@ setup( "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", diff --git a/synth.metadata b/synth.metadata new file mode 100644 index 000000000..d06a0f15b --- /dev/null +++ b/synth.metadata @@ -0,0 +1,12 @@ +{ + "updateTime": "2020-03-30T20:47:31.469660Z", + "sources": [ + { + "git": { + "name": "synthtool", + "remote": "https://github.com/googleapis/synthtool.git", + "sha": "f5e8c88d9870d8aa4eb43fa0b39f07e02bfbe4df" + } + } + ] +}
\ No newline at end of file diff --git a/synth.py b/synth.py new file mode 100644 index 000000000..017717db6 --- /dev/null +++ b/synth.py @@ -0,0 +1,30 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import synthtool as s +from synthtool import gcp + +common = gcp.CommonTemplates() + +# ---------------------------------------------------------------------------- +# Add templated files +# ---------------------------------------------------------------------------- +templated_files = common.py_library() + +# 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']) + +# Also move issue templates +s.move(templated_files / '.github')
\ No newline at end of file diff --git a/tests/test_discovery.py b/tests/test_discovery.py index f85035ef4..6400f2147 100644 --- a/tests/test_discovery.py +++ b/tests/test_discovery.py @@ -466,7 +466,7 @@ class DiscoveryFromDocument(unittest.TestCase): plus = build_from_document( discovery, base=base, credentials=self.MOCK_CREDENTIALS ) - self.assertEquals("https://www.googleapis.com/plus/v1/", plus._baseUrl) + self.assertEqual("https://www.googleapis.com/plus/v1/", plus._baseUrl) def test_building_with_optional_http_with_authorization(self): discovery = open(datafile("plus.json")).read() @@ -503,7 +503,7 @@ class DiscoveryFromDocument(unittest.TestCase): plus = build_from_document( discovery, base="https://www.googleapis.com/", http=http ) - self.assertEquals(plus._http, http) + self.assertEqual(plus._http, http) def test_building_with_developer_key_skips_adc(self): discovery = open(datafile("plus.json")).read() @@ -515,6 +515,25 @@ class DiscoveryFromDocument(unittest.TestCase): # application default credentials were used. self.assertNotIsInstance(plus._http, google_auth_httplib2.AuthorizedHttp) + def test_api_endpoint_override_from_client_options(self): + discovery = open(datafile("plus.json")).read() + api_endpoint = "https://foo.googleapis.com/" + options = google.api_core.client_options.ClientOptions( + api_endpoint=api_endpoint + ) + plus = build_from_document(discovery, client_options=options) + + self.assertEqual(plus._baseUrl, api_endpoint) + + def test_api_endpoint_override_from_client_options_dict(self): + discovery = open(datafile("plus.json")).read() + api_endpoint = "https://foo.googleapis.com/" + plus = build_from_document( + discovery, client_options={"api_endpoint": api_endpoint} + ) + + self.assertEqual(plus._baseUrl, api_endpoint) + class DiscoveryFromHttp(unittest.TestCase): def setUp(self): @@ -588,6 +607,39 @@ class DiscoveryFromHttp(unittest.TestCase): zoo = build("zoo", "v1", http=http, cache_discovery=False) self.assertTrue(hasattr(zoo, "animals")) + def test_api_endpoint_override_from_client_options(self): + http = HttpMockSequence( + [ + ({"status": "404"}, "Not found"), + ({"status": "200"}, open(datafile("zoo.json"), "rb").read()), + ] + ) + api_endpoint = "https://foo.googleapis.com/" + options = google.api_core.client_options.ClientOptions( + api_endpoint=api_endpoint + ) + zoo = build( + "zoo", "v1", http=http, cache_discovery=False, client_options=options + ) + self.assertEqual(zoo._baseUrl, api_endpoint) + + def test_api_endpoint_override_from_client_options_dict(self): + http = HttpMockSequence( + [ + ({"status": "404"}, "Not found"), + ({"status": "200"}, open(datafile("zoo.json"), "rb").read()), + ] + ) + api_endpoint = "https://foo.googleapis.com/" + zoo = build( + "zoo", + "v1", + http=http, + cache_discovery=False, + client_options={"api_endpoint": api_endpoint}, + ) + self.assertEqual(zoo._baseUrl, api_endpoint) + class DiscoveryFromAppEngineCache(unittest.TestCase): def test_appengine_memcache(self): @@ -928,8 +980,8 @@ class Discovery(unittest.TestCase): self.http = HttpMock(datafile("zoo.json"), {"status": "200"}) zoo = build("zoo", "v1", http=self.http) request = zoo.animals().crossbreed(media_body=datafile("small.png")) - self.assertEquals("image/png", request.headers["content-type"]) - self.assertEquals(b"PNG", request.body[1:4]) + self.assertEqual("image/png", request.headers["content-type"]) + self.assertEqual(b"PNG", request.body[1:4]) def test_simple_media_raise_correct_exceptions(self): self.http = HttpMock(datafile("zoo.json"), {"status": "200"}) @@ -952,8 +1004,8 @@ class Discovery(unittest.TestCase): zoo = build("zoo", "v1", http=self.http) request = zoo.animals().insert(media_body=datafile("small.png")) - self.assertEquals("image/png", request.headers["content-type"]) - self.assertEquals(b"PNG", request.body[1:4]) + self.assertEqual("image/png", request.headers["content-type"]) + self.assertEqual(b"PNG", request.body[1:4]) assertUrisEqual( self, "https://www.googleapis.com/upload/zoo/v1/animals?uploadType=media&alt=json", @@ -973,8 +1025,8 @@ class Discovery(unittest.TestCase): request = zoo.animals().insert( media_body=datafile("small-png"), media_mime_type="image/png" ) - self.assertEquals("image/png", request.headers["content-type"]) - self.assertEquals(b"PNG", request.body[1:4]) + self.assertEqual("image/png", request.headers["content-type"]) + self.assertEqual(b"PNG", request.body[1:4]) assertUrisEqual( self, "https://www.googleapis.com/upload/zoo/v1/animals?uploadType=media&alt=json", @@ -1045,13 +1097,13 @@ class Discovery(unittest.TestCase): media_upload = MediaFileUpload(datafile("small.png"), resumable=True) request = zoo.animals().insert(media_body=media_upload, body={}) self.assertTrue(request.headers["content-type"].startswith("application/json")) - self.assertEquals('{"data": {}}', request.body) - self.assertEquals(media_upload, request.resumable) + self.assertEqual('{"data": {}}', request.body) + self.assertEqual(media_upload, request.resumable) - self.assertEquals("image/png", request.resumable.mimetype()) + self.assertEqual("image/png", request.resumable.mimetype()) self.assertNotEquals(request.body, None) - self.assertEquals(request.resumable_uri, None) + self.assertEqual(request.resumable_uri, None) http = HttpMockSequence( [ @@ -1078,32 +1130,32 @@ class Discovery(unittest.TestCase): ) status, body = request.next_chunk(http=http) - self.assertEquals(None, body) + self.assertEqual(None, body) self.assertTrue(isinstance(status, MediaUploadProgress)) - self.assertEquals(0, status.resumable_progress) + self.assertEqual(0, status.resumable_progress) # Two requests should have been made and the resumable_uri should have been # updated for each one. - self.assertEquals(request.resumable_uri, "http://upload.example.com/2") - self.assertEquals(media_upload, request.resumable) - self.assertEquals(0, request.resumable_progress) + self.assertEqual(request.resumable_uri, "http://upload.example.com/2") + self.assertEqual(media_upload, request.resumable) + self.assertEqual(0, request.resumable_progress) # This next chuck call should upload the first chunk status, body = request.next_chunk(http=http) - self.assertEquals(request.resumable_uri, "http://upload.example.com/3") - self.assertEquals(media_upload, request.resumable) - self.assertEquals(13, request.resumable_progress) + self.assertEqual(request.resumable_uri, "http://upload.example.com/3") + self.assertEqual(media_upload, request.resumable) + self.assertEqual(13, request.resumable_progress) # This call will upload the next chunk status, body = request.next_chunk(http=http) - self.assertEquals(request.resumable_uri, "http://upload.example.com/4") - self.assertEquals(media_upload.size() - 1, request.resumable_progress) - self.assertEquals('{"data": {}}', request.body) + self.assertEqual(request.resumable_uri, "http://upload.example.com/4") + self.assertEqual(media_upload.size() - 1, request.resumable_progress) + self.assertEqual('{"data": {}}', request.body) # Final call to next_chunk should complete the upload. status, body = request.next_chunk(http=http) - self.assertEquals(body, {"foo": "bar"}) - self.assertEquals(status, None) + self.assertEqual(body, {"foo": "bar"}) + self.assertEqual(status, None) def test_resumable_media_good_upload(self): """Not a multipart upload.""" @@ -1112,12 +1164,12 @@ class Discovery(unittest.TestCase): media_upload = MediaFileUpload(datafile("small.png"), resumable=True) request = zoo.animals().insert(media_body=media_upload, body=None) - self.assertEquals(media_upload, request.resumable) + self.assertEqual(media_upload, request.resumable) - self.assertEquals("image/png", request.resumable.mimetype()) + self.assertEqual("image/png", request.resumable.mimetype()) - self.assertEquals(request.body, None) - self.assertEquals(request.resumable_uri, None) + self.assertEqual(request.body, None) + self.assertEqual(request.resumable_uri, None) http = HttpMockSequence( [ @@ -1143,26 +1195,26 @@ class Discovery(unittest.TestCase): ) status, body = request.next_chunk(http=http) - self.assertEquals(None, body) + self.assertEqual(None, body) self.assertTrue(isinstance(status, MediaUploadProgress)) - self.assertEquals(13, status.resumable_progress) + self.assertEqual(13, status.resumable_progress) # Two requests should have been made and the resumable_uri should have been # updated for each one. - self.assertEquals(request.resumable_uri, "http://upload.example.com/2") + self.assertEqual(request.resumable_uri, "http://upload.example.com/2") - self.assertEquals(media_upload, request.resumable) - self.assertEquals(13, request.resumable_progress) + self.assertEqual(media_upload, request.resumable) + self.assertEqual(13, request.resumable_progress) status, body = request.next_chunk(http=http) - self.assertEquals(request.resumable_uri, "http://upload.example.com/3") - self.assertEquals(media_upload.size() - 1, request.resumable_progress) - self.assertEquals(request.body, None) + self.assertEqual(request.resumable_uri, "http://upload.example.com/3") + self.assertEqual(media_upload.size() - 1, request.resumable_progress) + self.assertEqual(request.body, None) # Final call to next_chunk should complete the upload. status, body = request.next_chunk(http=http) - self.assertEquals(body, {"foo": "bar"}) - self.assertEquals(status, None) + self.assertEqual(body, {"foo": "bar"}) + self.assertEqual(status, None) def test_resumable_media_good_upload_from_execute(self): """Not a multipart upload.""" @@ -1201,7 +1253,7 @@ class Discovery(unittest.TestCase): ) body = request.execute(http=http) - self.assertEquals(body, {"foo": "bar"}) + self.assertEqual(body, {"foo": "bar"}) def test_resumable_media_fail_unknown_response_code_first_request(self): """Not a multipart upload.""" @@ -1247,7 +1299,7 @@ class Discovery(unittest.TestCase): ) status, body = request.next_chunk(http=http) - self.assertEquals( + self.assertEqual( status.resumable_progress, 7, "Should have first checked length and then tried to PUT more.", @@ -1571,9 +1623,9 @@ class Discovery(unittest.TestCase): media_upload = MediaFileUpload(datafile("empty"), resumable=True) request = zoo.animals().insert(media_body=media_upload, body=None) - self.assertEquals(media_upload, request.resumable) - self.assertEquals(request.body, None) - self.assertEquals(request.resumable_uri, None) + self.assertEqual(media_upload, request.resumable) + self.assertEqual(request.body, None) + self.assertEqual(request.resumable_uri, None) http = HttpMockSequence( [ @@ -1590,9 +1642,9 @@ class Discovery(unittest.TestCase): ) status, body = request.next_chunk(http=http) - self.assertEquals(None, body) + self.assertEqual(None, body) self.assertTrue(isinstance(status, MediaUploadProgress)) - self.assertEquals(0, status.progress()) + self.assertEqual(0, status.progress()) class Next(unittest.TestCase): diff --git a/tests/test_http.py b/tests/test_http.py index 2bf5060c0..2c0756e6c 100644 --- a/tests/test_http.py +++ b/tests/test_http.py @@ -132,32 +132,31 @@ class HttpMockWithErrors(object): def request(self, *args, **kwargs): if not self.num_errors: return httplib2.Response(self.success_json), self.success_data + elif self.num_errors == 5 and PY3: + ex = ConnectionResetError # noqa: F821 + elif self.num_errors == 4: + ex = httplib2.ServerNotFoundError() + elif self.num_errors == 3: + ex = socket.error() + ex.errno = socket.errno.EPIPE + elif self.num_errors == 2: + ex = ssl.SSLError() else: - self.num_errors -= 1 - if self.num_errors == 1: # initial == 2 - raise ssl.SSLError() - if self.num_errors == 3: # initial == 4 - raise httplib2.ServerNotFoundError() - else: # initial != 2,4 - if self.num_errors == 2: - # first try a broken pipe error (#218) - ex = socket.error() - ex.errno = socket.errno.EPIPE + # Initialize the timeout error code to the platform's error code. + try: + # For Windows: + ex = socket.error() + ex.errno = socket.errno.WSAETIMEDOUT + except AttributeError: + # For Linux/Mac: + if PY3: + ex = socket.timeout() else: - # Initialize the timeout error code to the platform's error code. - try: - # For Windows: - ex = socket.error() - ex.errno = socket.errno.WSAETIMEDOUT - except AttributeError: - # For Linux/Mac: - if PY3: - ex = socket.timeout() - else: - ex = socket.error() - ex.errno = socket.errno.ETIMEDOUT - # Now raise the correct error. - raise ex + ex = socket.error() + ex.errno = socket.errno.ETIMEDOUT + + self.num_errors -= 1 + raise ex class HttpMockWithNonRetriableErrors(object): @@ -562,14 +561,14 @@ class TestMediaIoBaseDownload(unittest.TestCase): def test_media_io_base_download_retries_connection_errors(self): self.request.http = HttpMockWithErrors( - 4, {"status": "200", "content-range": "0-2/3"}, b"123" + 5, {"status": "200", "content-range": "0-2/3"}, b"123" ) download = MediaIoBaseDownload(fd=self.fd, request=self.request, chunksize=3) download._sleep = lambda _x: 0 # do nothing download._rand = lambda: 10 - status, done = download.next_chunk(num_retries=4) + status, done = download.next_chunk(num_retries=5) self.assertEqual(self.fd.getvalue(), b"123") self.assertEqual(True, done) @@ -899,13 +898,13 @@ class TestHttpRequest(unittest.TestCase): def test_retry_connection_errors_non_resumable(self): model = JsonModel() request = HttpRequest( - HttpMockWithErrors(4, {"status": "200"}, '{"foo": "bar"}'), + HttpMockWithErrors(5, {"status": "200"}, '{"foo": "bar"}'), model.response, u"https://www.example.com/json_api_endpoint", ) request._sleep = lambda _x: 0 # do nothing request._rand = lambda: 10 - response = request.execute(num_retries=4) + response = request.execute(num_retries=5) self.assertEqual({u"foo": u"bar"}, response) def test_retry_connection_errors_resumable(self): @@ -918,7 +917,7 @@ class TestHttpRequest(unittest.TestCase): request = HttpRequest( HttpMockWithErrors( - 4, {"status": "200", "location": "location"}, '{"foo": "bar"}' + 5, {"status": "200", "location": "location"}, '{"foo": "bar"}' ), model.response, u"https://www.example.com/file_upload", @@ -927,7 +926,7 @@ class TestHttpRequest(unittest.TestCase): ) request._sleep = lambda _x: 0 # do nothing request._rand = lambda: 10 - response = request.execute(num_retries=4) + response = request.execute(num_retries=5) self.assertEqual({u"foo": u"bar"}, response) def test_retry(self): @@ -1122,7 +1121,7 @@ class TestBatch(unittest.TestCase): def test_id_to_from_content_id_header(self): batch = BatchHttpRequest() - self.assertEquals("12", batch._header_to_id(batch._id_to_header("12"))) + self.assertEqual("12", batch._header_to_id(batch._id_to_header("12"))) def test_invalid_content_id_header(self): batch = BatchHttpRequest() @@ -1646,7 +1645,7 @@ class TestHttpBuild(unittest.TestCase): def test_build_http_default_timeout_can_be_set_to_zero(self): socket.setdefaulttimeout(0) http = build_http() - self.assertEquals(http.timeout, 0) + self.assertEqual(http.timeout, 0) def test_build_http_default_308_is_excluded_as_redirect(self): http = build_http() diff --git a/tests/test_json_model.py b/tests/test_json_model.py index 0064f3fd7..68578039e 100644 --- a/tests/test_json_model.py +++ b/tests/test_json_model.py @@ -26,17 +26,19 @@ __author__ = "jcgregorio@google.com (Joe Gregorio)" import copy import json import os +import pkg_resources import platform import unittest2 as unittest import httplib2 import googleapiclient.model -from googleapiclient import __version__ from googleapiclient.errors import HttpError from googleapiclient.model import JsonModel from six.moves.urllib.parse import parse_qs +_LIBRARY_VERSION = pkg_resources.get_distribution("google-api-python-client").version + class Model(unittest.TestCase): def test_json_no_body(self): @@ -171,7 +173,7 @@ class Model(unittest.TestCase): headers["x-goog-api-client"], "gccl/1.23.4" + " gdcl/" - + __version__ + + _LIBRARY_VERSION + " gl-python/" + platform.python_version(), ) diff --git a/tox.ini b/tox.ini deleted file mode 100644 index 1d9bc1391..000000000 --- a/tox.ini +++ /dev/null @@ -1,24 +0,0 @@ -[tox] -envlist = py{27,34,35,36,37}-oauth2client{1,2,3,4} - -[testenv] -deps = - oauth2client1: oauth2client<2dev - oauth2client2: oauth2client>=2,<=3dev - oauth2client3: oauth2client>=3,<=4dev - oauth2client4: oauth2client>=4,<=5dev - google-auth - google-auth-httplib2 - mox - pyopenssl - django<2.0.0; python_version < '3.0.0' - django>=2.0.0; python_version > '3.0.0' - flake8 - webtest - nose - coverage>=3.6,<3.99 - unittest2 - mock -commands = - flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics - nosetests --with-coverage --cover-package=googleapiclient --nocapture --cover-erase --cover-tests --cover-branches --cover-min-percentage=85 [] |