- On January 1, 2020 this library will no longer support Python 2 on the latest released version.
- Previously released library versions will continue to be available. For more information please
+ As of January 1, 2020 this library no longer supports Python 2 on the latest released version.
+ Library versions released prior to that date will continue to be available. For more information please
visit
Python 2 support on Google Cloud.
{% block body %} {% endblock %}
diff --git a/docs/conf.py b/docs/conf.py
index 0a78149..ca73421 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -20,7 +20,7 @@
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.insert(0, os.path.abspath(".."))
-__version__ = "0.1.0"
+__version__ = ""
# -- General configuration ------------------------------------------------
@@ -38,21 +38,18 @@
"sphinx.ext.napoleon",
"sphinx.ext.todo",
"sphinx.ext.viewcode",
+ "recommonmark",
]
# autodoc/autosummary flags
autoclass_content = "both"
-autodoc_default_flags = ["members"]
+autodoc_default_options = {"members": True}
autosummary_generate = True
# Add any paths that contain templates here, relative to this directory.
templates_path = ["_templates"]
-# Allow markdown includes (so releases.md can include CHANGLEOG.md)
-# http://www.sphinx-doc.org/en/master/markdown.html
-source_parsers = {".md": "recommonmark.parser.CommonMarkParser"}
-
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
# source_suffix = ['.rst', '.md']
@@ -66,7 +63,7 @@
# General information about the project.
project = u"google-cloud-core"
-copyright = u"2017, Google"
+copyright = u"2019, Google"
author = u"Google APIs"
# The version info for the project you're documenting, acts as replacement for
@@ -133,9 +130,9 @@
# further. For a list of options available for each theme, see the
# documentation.
html_theme_options = {
- "description": "Google Cloud Client Libraries for Python",
+ "description": "Google Cloud Client Libraries for google-cloud-core",
"github_user": "googleapis",
- "github_repo": "google-cloud-python",
+ "github_repo": "python-cloud-core",
"github_banner": True,
"font_family": "'Roboto', Georgia, sans",
"head_font_family": "'Roboto', Georgia, serif",
@@ -293,7 +290,13 @@
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
- (master_doc, "google-cloud-core", u"google-cloud-core Documentation", [author], 1)
+ (
+ master_doc,
+ "google-cloud-core",
+ u"google-cloud-core Documentation",
+ [author],
+ 1,
+ )
]
# If true, show URL addresses after external links.
@@ -312,7 +315,7 @@
u"google-cloud-core Documentation",
author,
"google-cloud-core",
- "GAPIC library for the {metadata.shortName} v1beta1 service",
+ "google-cloud-core Library",
"APIs",
)
]
@@ -333,14 +336,13 @@
# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {
"python": ("http://python.readthedocs.org/en/latest/", None),
- "gax": ("https://gax-python.readthedocs.org/en/latest/", None),
"google-auth": ("https://google-auth.readthedocs.io/en/stable", None),
- "google-gax": ("https://gax-python.readthedocs.io/en/latest/", None),
- "google.api_core": ("https://googleapis.dev/python/google-api-core/latest", None),
+ "google.api_core": (
+ "https://googleapis.dev/python/google-api-core/latest/",
+ None,
+ ),
"grpc": ("https://grpc.io/grpc/python/", None),
- "requests": ("https://requests.kennethreitz.org/en/stable/", None),
- "fastavro": ("https://fastavro.readthedocs.io/en/stable/", None),
- "pandas": ("https://pandas.pydata.org/pandas-docs/stable/", None),
+
}
diff --git a/google/cloud/client.py b/google/cloud/client.py
index f204ba8..6b9117f 100644
--- a/google/cloud/client.py
+++ b/google/cloud/client.py
@@ -20,6 +20,8 @@
import six
+import google.api_core.client_options
+import google.api_core.exceptions
import google.auth
import google.auth.credentials
import google.auth.transport.requests
@@ -102,6 +104,8 @@ class Client(_ClientFactoryMixin):
(Optional) The OAuth2 Credentials to use for this client. If not
passed (and if no ``_http`` object is passed), falls back to the
default inferred from the environment.
+ client_options (google.api_core.client_options.ClientOptions):
+ (Optional) Custom options for the client.
_http (requests.Session):
(Optional) HTTP object to make requests. Can be any object that
defines ``request()`` with the same interface as
@@ -123,16 +127,35 @@ class Client(_ClientFactoryMixin):
Needs to be set by subclasses.
"""
- def __init__(self, credentials=None, _http=None):
- if credentials is not None and not isinstance(
- credentials, google.auth.credentials.Credentials
- ):
+ def __init__(self, credentials=None, _http=None, client_options=None):
+ if isinstance(client_options, dict):
+ client_options = google.api_core.client_options.from_dict(client_options)
+ if client_options is None:
+ client_options = google.api_core.client_options.ClientOptions()
+
+ if credentials and client_options.credentials_file:
+ raise google.api_core.exceptions.DuplicateCredentialArgs(
+ "'credentials' and 'client_options.credentials_file' are mutually exclusive.")
+
+ if credentials and not isinstance(credentials, google.auth.credentials.Credentials):
raise ValueError(_GOOGLE_AUTH_CREDENTIALS_HELP)
- if credentials is None and _http is None:
- credentials, _ = google.auth.default()
+
+ scopes = client_options.scopes or self.SCOPE
+
+ # if no http is provided, credentials must exist
+ if not _http and credentials is None:
+ if client_options.credentials_file:
+ credentials, _ = google.auth.load_credentials_from_file(
+ client_options.credentials_file, scopes=scopes)
+ else:
+ credentials, _ = google.auth.default(scopes=scopes)
+
self._credentials = google.auth.credentials.with_scopes_if_required(
- credentials, self.SCOPE
- )
+ credentials, scopes=scopes)
+
+ if client_options.quota_project_id:
+ self._credentials = self._credentials.with_quota_project(client_options.quota_project_id)
+
self._http_internal = _http
def __getstate__(self):
@@ -222,6 +245,6 @@ class ClientWithProject(Client, _ClientProjectMixin):
_SET_PROJECT = True # Used by from_service_account_json()
- def __init__(self, project=None, credentials=None, _http=None):
+ def __init__(self, project=None, credentials=None, client_options=None, _http=None):
_ClientProjectMixin.__init__(self, project=project)
- Client.__init__(self, credentials=credentials, _http=_http)
+ Client.__init__(self, credentials=credentials, client_options=client_options, _http=_http)
diff --git a/noxfile.py b/noxfile.py
index 5ac660e..9856032 100644
--- a/noxfile.py
+++ b/noxfile.py
@@ -19,8 +19,6 @@
import nox
-LOCAL_DEPS = (os.path.join("..", "api_core"),)
-
def default(session):
"""Default unit test session.
@@ -32,8 +30,6 @@ def default(session):
# Install all test dependencies, then install local packages in-place.
session.install("mock", "pytest", "pytest-cov", "grpcio >= 1.0.2")
- for local_dep in LOCAL_DEPS:
- session.install("-e", local_dep)
session.install("-e", ".")
# Run py.test against the unit tests.
@@ -45,13 +41,13 @@ def default(session):
"--cov-append",
"--cov-config=.coveragerc",
"--cov-report=",
- "--cov-fail-under=97",
+ "--cov-fail-under=0",
os.path.join("tests", "unit"),
*session.posargs
)
-@nox.session(python=["2.7", "3.5", "3.6", "3.7"])
+@nox.session(python=["2.7", "3.5", "3.6", "3.7", "3.8"])
def unit(session):
"""Default unit test session."""
default(session)
@@ -108,4 +104,4 @@ def docs(session):
os.path.join("docs", "_build", "doctrees", ""),
os.path.join("docs", ""),
os.path.join("docs", "_build", "html", ""),
- )
\ No newline at end of file
+ )
diff --git a/renovate.json b/renovate.json
new file mode 100644
index 0000000..4fa9493
--- /dev/null
+++ b/renovate.json
@@ -0,0 +1,5 @@
+{
+ "extends": [
+ "config:base", ":preserveSemverRanges"
+ ]
+}
diff --git a/scripts/decrypt-secrets.sh b/scripts/decrypt-secrets.sh
new file mode 100755
index 0000000..ff599eb
--- /dev/null
+++ b/scripts/decrypt-secrets.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+# Copyright 2015 Google Inc. All rights reserved.
+#
+# 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.
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+ROOT=$( dirname "$DIR" )
+
+# Work from the project root.
+cd $ROOT
+
+# Use SECRET_MANAGER_PROJECT if set, fallback to cloud-devrel-kokoro-resources.
+PROJECT_ID="${SECRET_MANAGER_PROJECT:-cloud-devrel-kokoro-resources}"
+
+gcloud secrets versions access latest --secret="python-docs-samples-test-env" \
+ > testing/test-env.sh
+gcloud secrets versions access latest \
+ --secret="python-docs-samples-service-account" \
+ > testing/service-account.json
+gcloud secrets versions access latest \
+ --secret="python-docs-samples-client-secrets" \
+ > testing/client-secrets.json
\ No newline at end of file
diff --git a/scripts/readme-gen/readme_gen.py b/scripts/readme-gen/readme_gen.py
new file mode 100644
index 0000000..d309d6e
--- /dev/null
+++ b/scripts/readme-gen/readme_gen.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python
+
+# Copyright 2016 Google Inc
+#
+# 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.
+
+"""Generates READMEs using configuration defined in yaml."""
+
+import argparse
+import io
+import os
+import subprocess
+
+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'))))
+
+README_TMPL = jinja_env.get_template('README.tmpl.rst')
+
+
+def get_help(file):
+ return subprocess.check_output(['python', file, '--help']).decode()
+
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument('source')
+ parser.add_argument('--destination', default='README.rst')
+
+ args = parser.parse_args()
+
+ source = os.path.abspath(args.source)
+ root = os.path.dirname(source)
+ destination = os.path.join(root, args.destination)
+
+ jinja_env.globals['get_help'] = get_help
+
+ with io.open(source, 'r') as f:
+ config = yaml.load(f)
+
+ # This allows get_help to execute in the right directory.
+ os.chdir(root)
+
+ output = README_TMPL.render(config)
+
+ with io.open(destination, 'w') as f:
+ f.write(output)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/scripts/readme-gen/templates/README.tmpl.rst b/scripts/readme-gen/templates/README.tmpl.rst
new file mode 100644
index 0000000..4fd2397
--- /dev/null
+++ b/scripts/readme-gen/templates/README.tmpl.rst
@@ -0,0 +1,87 @@
+{# The following line is a lie. BUT! Once jinja2 is done with it, it will
+ become truth! #}
+.. This file is automatically generated. Do not edit this file directly.
+
+{{product.name}} Python Samples
+===============================================================================
+
+.. image:: https://gstatic.com/cloudssh/images/open-btn.png
+ :target: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/GoogleCloudPlatform/python-docs-samples&page=editor&open_in_editor={{folder}}/README.rst
+
+
+This directory contains samples for {{product.name}}. {{product.description}}
+
+{{description}}
+
+.. _{{product.name}}: {{product.url}}
+
+{% if required_api_url %}
+To run the sample, you need to enable the API at: {{required_api_url}}
+{% endif %}
+
+{% if required_role %}
+To run the sample, you need to have `{{required_role}}` role.
+{% endif %}
+
+{{other_required_steps}}
+
+{% if setup %}
+Setup
+-------------------------------------------------------------------------------
+
+{% for section in setup %}
+
+{% include section + '.tmpl.rst' %}
+
+{% endfor %}
+{% endif %}
+
+{% if samples %}
+Samples
+-------------------------------------------------------------------------------
+
+{% for sample in samples %}
+{{sample.name}}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+{% if not sample.hide_cloudshell_button %}
+.. image:: https://gstatic.com/cloudssh/images/open-btn.png
+ :target: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/GoogleCloudPlatform/python-docs-samples&page=editor&open_in_editor={{folder}}/{{sample.file}},{{folder}}/README.rst
+{% endif %}
+
+
+{{sample.description}}
+
+To run this sample:
+
+.. code-block:: bash
+
+ $ python {{sample.file}}
+{% if sample.show_help %}
+
+ {{get_help(sample.file)|indent}}
+{% endif %}
+
+
+{% endfor %}
+{% endif %}
+
+{% if cloud_client_library %}
+
+The client library
+-------------------------------------------------------------------------------
+
+This sample uses the `Google Cloud Client Library for Python`_.
+You can read the documentation for more details on API usage and use GitHub
+to `browse the source`_ and `report issues`_.
+
+.. _Google Cloud Client Library for Python:
+ https://googlecloudplatform.github.io/google-cloud-python/
+.. _browse the source:
+ https://github.com/GoogleCloudPlatform/google-cloud-python
+.. _report issues:
+ https://github.com/GoogleCloudPlatform/google-cloud-python/issues
+
+{% endif %}
+
+.. _Google Cloud SDK: https://cloud.google.com/sdk/
\ No newline at end of file
diff --git a/scripts/readme-gen/templates/auth.tmpl.rst b/scripts/readme-gen/templates/auth.tmpl.rst
new file mode 100644
index 0000000..1446b94
--- /dev/null
+++ b/scripts/readme-gen/templates/auth.tmpl.rst
@@ -0,0 +1,9 @@
+Authentication
+++++++++++++++
+
+This sample requires you to have authentication setup. Refer to the
+`Authentication Getting Started Guide`_ for instructions on setting up
+credentials for applications.
+
+.. _Authentication Getting Started Guide:
+ https://cloud.google.com/docs/authentication/getting-started
diff --git a/scripts/readme-gen/templates/auth_api_key.tmpl.rst b/scripts/readme-gen/templates/auth_api_key.tmpl.rst
new file mode 100644
index 0000000..11957ce
--- /dev/null
+++ b/scripts/readme-gen/templates/auth_api_key.tmpl.rst
@@ -0,0 +1,14 @@
+Authentication
+++++++++++++++
+
+Authentication for this service is done via an `API Key`_. To obtain an API
+Key:
+
+1. Open the `Cloud Platform Console`_
+2. Make sure that billing is enabled for your project.
+3. From the **Credentials** page, create a new **API Key** or use an existing
+ one for your project.
+
+.. _API Key:
+ https://developers.google.com/api-client-library/python/guide/aaa_apikeys
+.. _Cloud Console: https://console.cloud.google.com/project?_
diff --git a/scripts/readme-gen/templates/install_deps.tmpl.rst b/scripts/readme-gen/templates/install_deps.tmpl.rst
new file mode 100644
index 0000000..a0406db
--- /dev/null
+++ b/scripts/readme-gen/templates/install_deps.tmpl.rst
@@ -0,0 +1,29 @@
+Install Dependencies
+++++++++++++++++++++
+
+#. Clone python-docs-samples and change directory to the sample directory you want to use.
+
+ .. code-block:: bash
+
+ $ git clone https://github.com/GoogleCloudPlatform/python-docs-samples.git
+
+#. Install `pip`_ and `virtualenv`_ if you do not already have them. You may want to refer to the `Python Development Environment Setup Guide`_ for Google Cloud Platform for instructions.
+
+ .. _Python Development Environment Setup Guide:
+ https://cloud.google.com/python/setup
+
+#. Create a virtualenv. Samples are compatible with Python 2.7 and 3.4+.
+
+ .. code-block:: bash
+
+ $ virtualenv env
+ $ source env/bin/activate
+
+#. Install the dependencies needed to run the samples.
+
+ .. code-block:: bash
+
+ $ pip install -r requirements.txt
+
+.. _pip: https://pip.pypa.io/
+.. _virtualenv: https://virtualenv.pypa.io/
diff --git a/scripts/readme-gen/templates/install_portaudio.tmpl.rst b/scripts/readme-gen/templates/install_portaudio.tmpl.rst
new file mode 100644
index 0000000..5ea33d1
--- /dev/null
+++ b/scripts/readme-gen/templates/install_portaudio.tmpl.rst
@@ -0,0 +1,35 @@
+Install PortAudio
++++++++++++++++++
+
+Install `PortAudio`_. This is required by the `PyAudio`_ library to stream
+audio from your computer's microphone. PyAudio depends on PortAudio for cross-platform compatibility, and is installed differently depending on the
+platform.
+
+* For Mac OS X, you can use `Homebrew`_::
+
+ brew install portaudio
+
+ **Note**: if you encounter an error when running `pip install` that indicates
+ it can't find `portaudio.h`, try running `pip install` with the following
+ flags::
+
+ pip install --global-option='build_ext' \
+ --global-option='-I/usr/local/include' \
+ --global-option='-L/usr/local/lib' \
+ pyaudio
+
+* For Debian / Ubuntu Linux::
+
+ apt-get install portaudio19-dev python-all-dev
+
+* Windows may work without having to install PortAudio explicitly (it will get
+ installed with PyAudio).
+
+For more details, see the `PyAudio installation`_ page.
+
+
+.. _PyAudio: https://people.csail.mit.edu/hubert/pyaudio/
+.. _PortAudio: http://www.portaudio.com/
+.. _PyAudio installation:
+ https://people.csail.mit.edu/hubert/pyaudio/#downloads
+.. _Homebrew: http://brew.sh
diff --git a/setup.py b/setup.py
index 3a04a44..eea8633 100644
--- a/setup.py
+++ b/setup.py
@@ -22,7 +22,7 @@
name = "google-cloud-core"
description = "Google Cloud API client core library"
-version = "1.3.0"
+version = "1.4.0"
# Should be one of:
# 'Development Status :: 3 - Alpha'
# 'Development Status :: 4 - Beta'
@@ -60,7 +60,7 @@
author="Google LLC",
author_email="googleapis-packages@google.com",
license="Apache 2.0",
- url="https://github.com/GoogleCloudPlatform/google-cloud-python",
+ url="https://github.com/googleapis/python-cloud-core",
classifiers=[
release_status,
"Intended Audience :: Developers",
@@ -71,7 +71,8 @@
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
- 'Programming Language :: Python :: 3.7',
+ "Programming Language :: Python :: 3.7",
+ "Programming Language :: Python :: 3.8",
"Operating System :: OS Independent",
"Topic :: Internet",
],
@@ -80,7 +81,7 @@
namespace_packages=namespaces,
install_requires=dependencies,
extras_require=extras,
- python_requires='>=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*',
+ python_requires=">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*",
include_package_data=True,
zip_safe=False,
)
diff --git a/synth.metadata b/synth.metadata
new file mode 100644
index 0000000..77b4f46
--- /dev/null
+++ b/synth.metadata
@@ -0,0 +1,18 @@
+{
+ "sources": [
+ {
+ "git": {
+ "name": ".",
+ "remote": "https://github.com/googleapis/python-cloud-core.git",
+ "sha": "5076ce7330e594fa0e1223e92eef61629508be71"
+ }
+ },
+ {
+ "git": {
+ "name": "synthtool",
+ "remote": "https://github.com/googleapis/synthtool.git",
+ "sha": "303271797a360f8a439203413f13a160f2f5b3b4"
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/synth.py b/synth.py
new file mode 100644
index 0000000..29a5f77
--- /dev/null
+++ b/synth.py
@@ -0,0 +1,37 @@
+# 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.
+
+"""This script is used to synthesize generated parts of this library."""
+
+import re
+
+import synthtool as s
+from synthtool import gcp
+
+common = gcp.CommonTemplates()
+
+# ----------------------------------------------------------------------------
+# Add templated files
+# ----------------------------------------------------------------------------
+templated_files = common.py_library(cov_level=100)
+s.move(
+ templated_files,
+ excludes=[
+ "docs/multiprocessing.rst",
+ "noxfile.py",
+ ".flake8",
+ ".coveragerc",
+ "setup.cfg",
+ ],
+)
diff --git a/testing/.gitignore b/testing/.gitignore
new file mode 100644
index 0000000..b05fbd6
--- /dev/null
+++ b/testing/.gitignore
@@ -0,0 +1,3 @@
+test-env.sh
+service-account.json
+client-secrets.json
\ No newline at end of file
diff --git a/tests/unit/test_client.py b/tests/unit/test_client.py
index 7aec69f..59e2ea0 100644
--- a/tests/unit/test_client.py
+++ b/tests/unit/test_client.py
@@ -50,14 +50,14 @@ def _make_one(self, *args, **kw):
def test_unpickleable(self):
import pickle
- CREDENTIALS = _make_credentials()
+ credentials = _make_credentials()
HTTP = object()
- client_obj = self._make_one(credentials=CREDENTIALS, _http=HTTP)
+ client_obj = self._make_one(credentials=credentials, _http=HTTP)
with self.assertRaises(pickle.PicklingError):
pickle.dumps(client_obj)
- def test_constructor_defaults(self):
+ def test_ctor_defaults(self):
credentials = _make_credentials()
patch = mock.patch("google.auth.default", return_value=(credentials, None))
@@ -66,9 +66,9 @@ def test_constructor_defaults(self):
self.assertIs(client_obj._credentials, credentials)
self.assertIsNone(client_obj._http_internal)
- default.assert_called_once_with()
+ default.assert_called_once_with(scopes=None)
- def test_constructor_explicit(self):
+ def test_ctor_explicit(self):
credentials = _make_credentials()
http = mock.sentinel.http
client_obj = self._make_one(credentials=credentials, _http=http)
@@ -76,12 +76,71 @@ def test_constructor_explicit(self):
self.assertIs(client_obj._credentials, credentials)
self.assertIs(client_obj._http_internal, http)
- def test_constructor_bad_credentials(self):
+ def test_ctor_client_options_w_conflicting_creds(self):
+ from google.api_core.exceptions import DuplicateCredentialArgs
+
+ credentials = _make_credentials()
+ client_options = {'credentials_file': '/path/to/creds.json'}
+ with self.assertRaises(DuplicateCredentialArgs):
+ self._make_one(credentials=credentials, client_options=client_options)
+
+ def test_ctor_bad_credentials(self):
credentials = mock.sentinel.credentials
with self.assertRaises(ValueError):
self._make_one(credentials=credentials)
+ def test_ctor_client_options_w_creds_file_scopes(self):
+ credentials = _make_credentials()
+ credentials_file = '/path/to/creds.json'
+ scopes = ['SCOPE1', 'SCOPE2']
+ client_options = {'credentials_file': credentials_file, 'scopes': scopes}
+
+ patch = mock.patch("google.auth.load_credentials_from_file", return_value=(credentials, None))
+ with patch as load_credentials_from_file:
+ client_obj = self._make_one(client_options=client_options)
+
+ self.assertIs(client_obj._credentials, credentials)
+ self.assertIsNone(client_obj._http_internal)
+ load_credentials_from_file.assert_called_once_with(credentials_file, scopes=scopes)
+
+ def test_ctor_client_options_w_quota_project(self):
+ credentials = _make_credentials()
+ quota_project_id = 'quota-project-123'
+ client_options = {'quota_project_id': quota_project_id}
+
+ client_obj = self._make_one(credentials=credentials, client_options=client_options)
+
+ self.assertIs(client_obj._credentials, credentials.with_quota_project.return_value)
+ credentials.with_quota_project.assert_called_once_with(quota_project_id)
+
+ def test_ctor__http_property_existing(self):
+ credentials = _make_credentials()
+ http = object()
+ client = self._make_one(credentials=credentials, _http=http)
+ self.assertIs(client._http_internal, http)
+ self.assertIs(client._http, http)
+
+ def test_ctor__http_property_new(self):
+ from google.cloud.client import _CREDENTIALS_REFRESH_TIMEOUT
+
+ credentials = _make_credentials()
+ client = self._make_one(credentials=credentials)
+ self.assertIsNone(client._http_internal)
+
+ authorized_session_patch = mock.patch(
+ "google.auth.transport.requests.AuthorizedSession",
+ return_value=mock.sentinel.http,
+ )
+ with authorized_session_patch as AuthorizedSession:
+ self.assertIs(client._http, mock.sentinel.http)
+ # Check the mock.
+ AuthorizedSession.assert_called_once_with(credentials, refresh_timeout=_CREDENTIALS_REFRESH_TIMEOUT)
+ # Make sure the cached value is used on subsequent access.
+ self.assertIs(client._http_internal, mock.sentinel.http)
+ self.assertIs(client._http, mock.sentinel.http)
+ self.assertEqual(AuthorizedSession.call_count, 1)
+
def test_from_service_account_json(self):
from google.cloud import _helpers
@@ -114,32 +173,6 @@ def test_from_service_account_json_bad_args(self):
mock.sentinel.filename, credentials=mock.sentinel.credentials
)
- def test__http_property_existing(self):
- credentials = _make_credentials()
- http = object()
- client = self._make_one(credentials=credentials, _http=http)
- self.assertIs(client._http_internal, http)
- self.assertIs(client._http, http)
-
- def test__http_property_new(self):
- from google.cloud.client import _CREDENTIALS_REFRESH_TIMEOUT
- credentials = _make_credentials()
- client = self._make_one(credentials=credentials)
- self.assertIsNone(client._http_internal)
-
- authorized_session_patch = mock.patch(
- "google.auth.transport.requests.AuthorizedSession",
- return_value=mock.sentinel.http,
- )
- with authorized_session_patch as AuthorizedSession:
- self.assertIs(client._http, mock.sentinel.http)
- # Check the mock.
- AuthorizedSession.assert_called_once_with(credentials, refresh_timeout=_CREDENTIALS_REFRESH_TIMEOUT)
- # Make sure the cached value is used on subsequent access.
- self.assertIs(client._http_internal, mock.sentinel.http)
- self.assertIs(client._http, mock.sentinel.http)
- self.assertEqual(AuthorizedSession.call_count, 1)
-
class TestClientWithProject(unittest.TestCase):
@staticmethod
@@ -167,7 +200,7 @@ def test_constructor_defaults(self):
self.assertEqual(client_obj.project, project)
self.assertIs(client_obj._credentials, credentials)
self.assertIsNone(client_obj._http_internal)
- default.assert_called_once_with()
+ default.assert_called_once_with(scopes=None)
_determine_default_project.assert_called_once_with(None)
def test_constructor_missing_project(self):