diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 3d44f3e4ba6..7d352657707 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 00000000000..24c6fa88fef --- /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 # 🦕 diff --git a/.github/release-please.yml b/.github/release-please.yml new file mode 100644 index 00000000000..4507ad0598a --- /dev/null +++ b/.github/release-please.yml @@ -0,0 +1 @@ +releaseType: python diff --git a/.gitignore b/.gitignore index cf2c4a6983d..1637b1d3b1e 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 e0c966b5a08..ab8cebb4bf2 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 c21dc6d4ff9..00000000000 --- 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 18a4c35325b..8f43917d92f 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 18a4c35325b..8f43917d92f 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 00000000000..b4756d089ee --- /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 00000000000..6a8864b888f --- /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 00000000000..8f43917d92f --- /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 0efc3be388d..e8c4251f3ed 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 00000000000..1b810b0be5a --- /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 similarity index 94% rename from CHANGELOG rename to CHANGELOG.md index 841446eb8b8..b0eb4fb1dac 100644 --- a/CHANGELOG +++ b/CHANGELOG.md @@ -1,4 +1,14 @@ -v1.8.0 +# 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. @@ -9,7 +19,7 @@ v1.8.0 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 +### v1.7.12 Version 1.7.12 Bugfix release @@ -39,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 @@ -51,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 @@ -73,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 @@ -100,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 @@ -129,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 @@ -153,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 @@ -170,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 @@ -179,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 @@ -198,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 @@ -221,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 @@ -230,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. @@ -258,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 @@ -268,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 @@ -278,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 @@ -296,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 @@ -309,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. @@ -320,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. @@ -345,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. @@ -369,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 @@ -395,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 @@ -423,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. @@ -451,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. @@ -459,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: @@ -484,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. @@ -499,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. diff --git a/README.md b/README.md index ee38961d622..1ed3078f635 100644 --- a/README.md +++ b/README.md @@ -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 b9db848ef3c..c9218dd8540 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.8.0" - # Set default logging handler to avoid "No handler found" warnings. import logging diff --git a/googleapiclient/discovery.py b/googleapiclient/discovery.py index 3158fb3eb9c..66d4927aaeb 100644 --- a/googleapiclient/discovery.py +++ b/googleapiclient/discovery.py @@ -131,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: diff --git a/googleapiclient/http.py b/googleapiclient/http.py index cf9a509bb1b..41256668b2e 100644 --- a/googleapiclient/http.py +++ b/googleapiclient/http.py @@ -81,6 +81,11 @@ _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 @@ def request( 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") diff --git a/googleapiclient/model.py b/googleapiclient/model.py index 554056e3ef2..f58549c49ea 100644 --- a/googleapiclient/model.py +++ b/googleapiclient/model.py @@ -27,12 +27,13 @@ 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 @@ def request(self, headers, path_params, query_params, body_value): 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 @@ def response(self, resp, content): 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 00000000000..438ff41c585 --- /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 00000000000..f45d8f110c3 --- /dev/null +++ b/renovate.json @@ -0,0 +1,5 @@ +{ + "extends": [ + "config:base" + ] +} diff --git a/setup.py b/setup.py index cf135a999dc..60974fbdff4 100644 --- a/setup.py +++ b/setup.py @@ -28,6 +28,8 @@ 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"] @@ -44,19 +46,22 @@ "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.*", @@ -68,7 +73,6 @@ "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 00000000000..d06a0f15bb1 --- /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 00000000000..017717db652 --- /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_http.py b/tests/test_http.py index ce27e2e0926..2c0756e6c53 100644 --- a/tests/test_http.py +++ b/tests/test_http.py @@ -132,32 +132,31 @@ def __init__(self, num_errors, success_json, success_data): 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 @@ def test_media_io_base_download_handle_4xx(self): 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 @@ def test_no_retry_connection_errors(self): 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 @@ def test_retry_connection_errors_resumable(self): 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 @@ def test_retry_connection_errors_resumable(self): ) 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): diff --git a/tests/test_json_model.py b/tests/test_json_model.py index 0064f3fd7ce..68578039e63 100644 --- a/tests/test_json_model.py +++ b/tests/test_json_model.py @@ -26,17 +26,19 @@ 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 @@ def test_x_goog_api_client(self): 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 1d9bc13915e..00000000000 --- 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 []