Hello community,

here is the log from the commit of package python-google-api-core for 
openSUSE:Factory checked in at 2018-09-13 12:12:17
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-google-api-core (Old)
 and      /work/SRC/openSUSE:Factory/.python-google-api-core.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-google-api-core"

Thu Sep 13 12:12:17 2018 rev:3 rq:635277 version:1.4.0

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/python-google-api-core/python-google-api-core.changes
    2018-05-30 12:37:56.475160126 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-google-api-core.new/python-google-api-core.changes
       2018-09-13 12:14:01.398124007 +0200
@@ -1,0 +2,10 @@
+Wed Sep 12 08:06:43 UTC 2018 - Thomas Bechtold <[email protected]>
+
+- update to version 1.4.0
+  * Add support for gRPC connection management (available when using
+    optional grpc_gcp dependency) (#5553) (#5904)
+  * Update classifiers to drop Python 3.4 and add Python 3.7 (#5702)
+  * Add protobuf_helpers.field_mask to calculate a field mask from
+    two messages (#5320)
+
+-------------------------------------------------------------------

Old:
----
  google-api-core-1.2.0.tar.gz

New:
----
  google-api-core-1.4.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-google-api-core.spec ++++++
--- /var/tmp/diff_new_pack.z8CwgB/_old  2018-09-13 12:14:01.782123572 +0200
+++ /var/tmp/diff_new_pack.z8CwgB/_new  2018-09-13 12:14:01.782123572 +0200
@@ -12,13 +12,13 @@
 # license that conforms to the Open Source Definition (Version 1.9)
 # published by the Open Source Initiative.
 
-# Please submit bugfixes or comments via http://bugs.opensuse.org/
+# Please submit bugfixes or comments via https://bugs.opensuse.org/
 #
 
 
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 Name:           python-google-api-core
-Version:        1.2.0
+Version:        1.4.0
 Release:        0
 Summary:        Google API client core library
 License:        Apache-2.0

++++++ google-api-core-1.2.0.tar.gz -> google-api-core-1.4.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/google-api-core-1.2.0/PKG-INFO 
new/google-api-core-1.4.0/PKG-INFO
--- old/google-api-core-1.2.0/PKG-INFO  2018-05-25 23:28:52.000000000 +0200
+++ new/google-api-core-1.4.0/PKG-INFO  2018-09-11 20:19:45.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: google-api-core
-Version: 1.2.0
+Version: 1.4.0
 Summary: Google API client core library
 Home-page: https://github.com/GoogleCloudPlatform/google-cloud-python
 Author: Google LLC
@@ -29,9 +29,10 @@
 Classifier: Programming Language :: Python :: 2
 Classifier: Programming Language :: Python :: 2.7
 Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.4
 Classifier: Programming Language :: Python :: 3.5
 Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
 Classifier: Operating System :: OS Independent
 Classifier: Topic :: Internet
 Provides-Extra: grpc
+Provides-Extra: grpcio-gcp
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/google-api-core-1.2.0/google/api_core/gapic_v1/client_info.py 
new/google-api-core-1.4.0/google/api_core/gapic_v1/client_info.py
--- old/google-api-core-1.2.0/google/api_core/gapic_v1/client_info.py   
2018-04-25 19:05:26.000000000 +0200
+++ new/google-api-core-1.4.0/google/api_core/gapic_v1/client_info.py   
2018-05-28 18:24:01.000000000 +0200
@@ -23,8 +23,13 @@
 import pkg_resources
 
 _PY_VERSION = platform.python_version()
-_GRPC_VERSION = pkg_resources.get_distribution('grpcio').version
 _API_CORE_VERSION = pkg_resources.get_distribution('google-api-core').version
+
+try:
+    _GRPC_VERSION = pkg_resources.get_distribution('grpcio').version
+except pkg_resources.DistributionNotFound:  # pragma: NO COVER
+    _GRPC_VERSION = None
+
 METRICS_METADATA_KEY = 'x-goog-api-client'
 
 
@@ -38,7 +43,7 @@
     Args:
         python_version (str): The Python interpreter version, for example,
             ``'2.7.13'``.
-        grpc_version (str): The gRPC library version.
+        grpc_version (Optional[str]): The gRPC library version.
         api_core_version (str): The google-api-core library version.
         gapic_version (Optional[str]): The sversion of gapic-generated client
             library, if the library was generated by gapic.
@@ -66,15 +71,18 @@
         # expects these items to be in specific locations.
         ua = 'gl-python/{python_version} '
 
-        if self.client_library_version is not None:
-            ua += 'gccl/{client_library_version} '
+        if self.grpc_version is not None:
+            ua += 'grpc/{grpc_version} '
+
+        ua += 'gax/{api_core_version} '
 
         if self.gapic_version is not None:
             ua += 'gapic/{gapic_version} '
 
-        ua += 'gax/{api_core_version} grpc/{grpc_version}'
+        if self.client_library_version is not None:
+            ua += 'gccl/{client_library_version} '
 
-        return ua.format(**self.__dict__)
+        return ua.format(**self.__dict__).strip()
 
     def to_grpc_metadata(self):
         """Returns the gRPC metadata for this client info."""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/google-api-core-1.2.0/google/api_core/gapic_v1/routing_header.py 
new/google-api-core-1.4.0/google/api_core/gapic_v1/routing_header.py
--- old/google-api-core-1.2.0/google/api_core/gapic_v1/routing_header.py        
2018-04-25 19:05:26.000000000 +0200
+++ new/google-api-core-1.4.0/google/api_core/gapic_v1/routing_header.py        
2018-06-29 20:21:56.000000000 +0200
@@ -22,7 +22,7 @@
 
 from six.moves.urllib.parse import urlencode
 
-ROUTING_METADATA_KEY = 'x-goog-header-params'
+ROUTING_METADATA_KEY = 'x-goog-request-params'
 
 
 def to_routing_header(params):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/google-api-core-1.2.0/google/api_core/grpc_helpers.py 
new/google-api-core-1.4.0/google/api_core/grpc_helpers.py
--- old/google-api-core-1.2.0/google/api_core/grpc_helpers.py   2018-05-18 
18:12:51.000000000 +0200
+++ new/google-api-core-1.4.0/google/api_core/grpc_helpers.py   2018-08-14 
01:16:23.000000000 +0200
@@ -26,6 +26,11 @@
 import google.auth.transport.grpc
 import google.auth.transport.requests
 
+try:
+    import grpc_gcp
+    HAS_GRPC_GCP = True
+except ImportError:
+    HAS_GRPC_GCP = False
 
 # The list of gRPC Callable interfaces that return iterators.
 _STREAM_WRAP_CLASSES = (
@@ -149,7 +154,11 @@
         return _wrap_unary_errors(callable_)
 
 
-def create_channel(target, credentials=None, scopes=None, **kwargs):
+def create_channel(target,
+                   credentials=None,
+                   scopes=None,
+                   ssl_credentials=None,
+                   **kwargs):
     """Create a secure channel with credentials.
 
     Args:
@@ -160,8 +169,10 @@
         scopes (Sequence[str]): A optional list of scopes needed for this
             service. These are only used when credentials are not specified and
             are passed to :func:`google.auth.default`.
+        ssl_credentials (grpc.ChannelCredentials): Optional SSL channel
+            credentials. This can be used to specify different certificates.
         kwargs: Additional key-word args passed to
-            :func:`google.auth.transport.grpc.secure_authorized_channel`.
+            :func:`grpc_gcp.secure_channel` or :func:`grpc.secure_channel`.
 
     Returns:
         grpc.Channel: The created channel.
@@ -174,8 +185,26 @@
 
     request = google.auth.transport.requests.Request()
 
-    return google.auth.transport.grpc.secure_authorized_channel(
-        credentials, request, target, **kwargs)
+    # Create the metadata plugin for inserting the authorization header.
+    metadata_plugin = google.auth.transport.grpc.AuthMetadataPlugin(
+        credentials, request)
+
+    # Create a set of grpc.CallCredentials using the metadata plugin.
+    google_auth_credentials = grpc.metadata_call_credentials(metadata_plugin)
+
+    if ssl_credentials is None:
+        ssl_credentials = grpc.ssl_channel_credentials()
+
+    # Combine the ssl credentials and the authorization credentials.
+    composite_credentials = grpc.composite_channel_credentials(
+        ssl_credentials, google_auth_credentials)
+
+    if HAS_GRPC_GCP:
+        # If grpc_gcp module is available use grpc_gcp.secure_channel,
+        # otherwise, use grpc.secure_channel to create grpc channel.
+        return grpc_gcp.secure_channel(target, composite_credentials, **kwargs)
+    else:
+        return grpc.secure_channel(target, composite_credentials, **kwargs)
 
 
 _MethodCall = collections.namedtuple(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/google-api-core-1.2.0/google/api_core/protobuf_helpers.py 
new/google-api-core-1.4.0/google/api_core/protobuf_helpers.py
--- old/google-api-core-1.2.0/google/api_core/protobuf_helpers.py       
2018-04-25 19:05:26.000000000 +0200
+++ new/google-api-core-1.4.0/google/api_core/protobuf_helpers.py       
2018-07-25 21:43:09.000000000 +0200
@@ -15,11 +15,25 @@
 """Helpers for :mod:`protobuf`."""
 
 import collections
+import copy
 import inspect
 
-from google.protobuf.message import Message
+from google.protobuf import field_mask_pb2
+from google.protobuf import message
+from google.protobuf import wrappers_pb2
 
 _SENTINEL = object()
+_WRAPPER_TYPES = (
+    wrappers_pb2.BoolValue,
+    wrappers_pb2.BytesValue,
+    wrappers_pb2.DoubleValue,
+    wrappers_pb2.FloatValue,
+    wrappers_pb2.Int32Value,
+    wrappers_pb2.Int64Value,
+    wrappers_pb2.StringValue,
+    wrappers_pb2.UInt32Value,
+    wrappers_pb2.UInt64Value,
+)
 
 
 def from_any_pb(pb_type, any_pb):
@@ -73,13 +87,15 @@
             module to find Message subclasses.
 
     Returns:
-        dict[str, Message]: A dictionary with the Message class names as
-            keys, and the Message subclasses themselves as values.
+        dict[str, google.protobuf.message.Message]: A dictionary with the
+            Message class names as keys, and the Message subclasses themselves
+            as values.
     """
     answer = collections.OrderedDict()
     for name in dir(module):
         candidate = getattr(module, name)
-        if inspect.isclass(candidate) and issubclass(candidate, Message):
+        if (inspect.isclass(candidate) and
+                issubclass(candidate, message.Message)):
             answer[name] = candidate
     return answer
 
@@ -143,7 +159,7 @@
 
     # Attempt to get the value from the two types of objects we know about.
     # If we get something else, complain.
-    if isinstance(msg_or_dict, Message):
+    if isinstance(msg_or_dict, message.Message):
         answer = getattr(msg_or_dict, key, default)
     elif isinstance(msg_or_dict, collections.Mapping):
         answer = msg_or_dict.get(key, default)
@@ -186,7 +202,7 @@
         # Assign the dictionary values to the protobuf message.
         for item_key, item_value in value.items():
             set(getattr(msg, key), item_key, item_value)
-    elif isinstance(value, Message):
+    elif isinstance(value, message.Message):
         getattr(msg, key).CopyFrom(value)
     else:
         setattr(msg, key, value)
@@ -205,7 +221,8 @@
         TypeError: If ``msg_or_dict`` is not a Message or dictionary.
     """
     # Sanity check: Is our target object valid?
-    if not isinstance(msg_or_dict, (collections.MutableMapping, Message)):
+    if (not isinstance(msg_or_dict,
+                       (collections.MutableMapping, message.Message))):
         raise TypeError(
             'set() expected a dict or protobuf message, got {!r}.'.format(
                 type(msg_or_dict)))
@@ -247,3 +264,84 @@
     """
     if not get(msg_or_dict, key, default=None):
         set(msg_or_dict, key, value)
+
+
+def field_mask(original, modified):
+    """Create a field mask by comparing two messages.
+
+    Args:
+        original (~google.protobuf.message.Message): the original message.
+            If set to None, this field will be interpretted as an empty
+            message.
+        modified (~google.protobuf.message.Message): the modified message.
+            If set to None, this field will be interpretted as an empty
+            message.
+
+    Returns:
+        google.protobuf.field_mask_pb2.FieldMask: field mask that contains
+        the list of field names that have different values between the two
+        messages. If the messages are equivalent, then the field mask is empty.
+
+    Raises:
+        ValueError: If the ``original`` or ``modified`` are not the same type.
+    """
+    if original is None and modified is None:
+        return field_mask_pb2.FieldMask()
+
+    if original is None and modified is not None:
+        original = copy.deepcopy(modified)
+        original.Clear()
+
+    if modified is None and original is not None:
+        modified = copy.deepcopy(original)
+        modified.Clear()
+
+    if type(original) != type(modified):
+        raise ValueError(
+                'expected that both original and modified should be of the '
+                'same type, received "{!r}" and "{!r}".'.
+                format(type(original), type(modified)))
+
+    return field_mask_pb2.FieldMask(
+        paths=_field_mask_helper(original, modified))
+
+
+def _field_mask_helper(original, modified, current=''):
+    answer = []
+
+    for name in original.DESCRIPTOR.fields_by_name:
+        field_path = _get_path(current, name)
+
+        original_val = getattr(original, name)
+        modified_val = getattr(modified, name)
+
+        if _is_message(original_val) or _is_message(modified_val):
+            if original_val != modified_val:
+                # Wrapper types do not need to include the .value part of the
+                # path.
+                if _is_wrapper(original_val) or _is_wrapper(modified_val):
+                    answer.append(field_path)
+                elif not modified_val.ListFields():
+                    answer.append(field_path)
+                else:
+                    answer.extend(_field_mask_helper(original_val,
+                                                     modified_val, field_path))
+        else:
+            if original_val != modified_val:
+                answer.append(field_path)
+
+    return answer
+
+
+def _get_path(current, name):
+    if not current:
+        return name
+    return '%s.%s' % (current, name)
+
+
+def _is_message(value):
+    return isinstance(value, message.Message)
+
+
+def _is_wrapper(value):
+    return type(value) in _WRAPPER_TYPES
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/google-api-core-1.2.0/google_api_core.egg-info/PKG-INFO 
new/google-api-core-1.4.0/google_api_core.egg-info/PKG-INFO
--- old/google-api-core-1.2.0/google_api_core.egg-info/PKG-INFO 2018-05-25 
23:28:52.000000000 +0200
+++ new/google-api-core-1.4.0/google_api_core.egg-info/PKG-INFO 2018-09-11 
20:19:45.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: google-api-core
-Version: 1.2.0
+Version: 1.4.0
 Summary: Google API client core library
 Home-page: https://github.com/GoogleCloudPlatform/google-cloud-python
 Author: Google LLC
@@ -29,9 +29,10 @@
 Classifier: Programming Language :: Python :: 2
 Classifier: Programming Language :: Python :: 2.7
 Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.4
 Classifier: Programming Language :: Python :: 3.5
 Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
 Classifier: Operating System :: OS Independent
 Classifier: Topic :: Internet
 Provides-Extra: grpc
+Provides-Extra: grpcio-gcp
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/google-api-core-1.2.0/google_api_core.egg-info/requires.txt 
new/google-api-core-1.4.0/google_api_core.egg-info/requires.txt
--- old/google-api-core-1.2.0/google_api_core.egg-info/requires.txt     
2018-05-25 23:28:52.000000000 +0200
+++ new/google-api-core-1.4.0/google_api_core.egg-info/requires.txt     
2018-09-11 20:19:45.000000000 +0200
@@ -11,3 +11,6 @@
 
 [grpc]
 grpcio>=1.8.2
+
+[grpcio-gcp]
+grpcio-gcp>=0.2.2
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/google-api-core-1.2.0/setup.py 
new/google-api-core-1.4.0/setup.py
--- old/google-api-core-1.2.0/setup.py  2018-05-18 18:12:51.000000000 +0200
+++ new/google-api-core-1.4.0/setup.py  2018-09-11 19:52:42.000000000 +0200
@@ -22,7 +22,7 @@
 
 name = 'google-api-core'
 description = 'Google API client core library'
-version = '1.2.0'
+version = '1.4.0'
 # Should be one of:
 # 'Development Status :: 3 - Alpha'
 # 'Development Status :: 4 - Beta'
@@ -39,7 +39,8 @@
     'futures>=3.2.0;python_version<"3.2"'
 ]
 extras = {
-    'grpc': 'grpcio>=1.8.2'
+    'grpc': 'grpcio>=1.8.2',
+    'grpcio-gcp': 'grpcio-gcp>=0.2.2'
 }
 
 
@@ -80,9 +81,9 @@
         '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',
         'Operating System :: OS Independent',
         'Topic :: Internet',
     ],
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/google-api-core-1.2.0/tests/unit/gapic/test_client_info.py 
new/google-api-core-1.4.0/tests/unit/gapic/test_client_info.py
--- old/google-api-core-1.2.0/tests/unit/gapic/test_client_info.py      
2018-04-25 19:05:26.000000000 +0200
+++ new/google-api-core-1.4.0/tests/unit/gapic/test_client_info.py      
2018-05-28 18:24:01.000000000 +0200
@@ -44,12 +44,13 @@
 def test_to_user_agent_minimal():
     info = client_info.ClientInfo(
         python_version='1',
-        grpc_version='2',
-        api_core_version='3')
+        api_core_version='2',
+        grpc_version=None
+    )
 
     user_agent = info.to_user_agent()
 
-    assert user_agent == 'gl-python/1 gax/3 grpc/2'
+    assert user_agent == 'gl-python/1 gax/2'
 
 
 def test_to_user_agent_full():
@@ -62,7 +63,7 @@
 
     user_agent = info.to_user_agent()
 
-    assert user_agent == 'gl-python/1 gccl/5 gapic/4 gax/3 grpc/2'
+    assert user_agent == 'gl-python/1 grpc/2 gax/3 gapic/4 gccl/5'
 
 
 def test_to_grpc_metadata():
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/google-api-core-1.2.0/tests/unit/test_grpc_helpers.py 
new/google-api-core-1.4.0/tests/unit/test_grpc_helpers.py
--- old/google-api-core-1.2.0/tests/unit/test_grpc_helpers.py   2018-05-18 
18:12:51.000000000 +0200
+++ new/google-api-core-1.4.0/tests/unit/test_grpc_helpers.py   2018-08-14 
01:16:23.000000000 +0200
@@ -176,60 +176,157 @@
     wrap_stream_errors.assert_called_once_with(callable_)
 
 
[email protected]('grpc.composite_channel_credentials')
 @mock.patch(
     'google.auth.default',
     return_value=(mock.sentinel.credentials, mock.sentinel.projet))
[email protected]('google.auth.transport.grpc.secure_authorized_channel')
-def test_create_channel_implicit(secure_authorized_channel, default):
[email protected]('grpc.secure_channel')
+def test_create_channel_implicit(
+        grpc_secure_channel, default, composite_creds_call):
     target = 'example.com:443'
+    composite_creds = composite_creds_call.return_value
 
     channel = grpc_helpers.create_channel(target)
 
-    assert channel is secure_authorized_channel.return_value
+    assert channel is grpc_secure_channel.return_value
     default.assert_called_once_with(scopes=None)
-    secure_authorized_channel.assert_called_once_with(
-        mock.sentinel.credentials, mock.ANY, target)
+    if (grpc_helpers.HAS_GRPC_GCP):
+        grpc_secure_channel.assert_called_once_with(
+            target, composite_creds, None)
+    else:
+        grpc_secure_channel.assert_called_once_with(
+            target, composite_creds)
 
 
[email protected]('grpc.composite_channel_credentials')
 @mock.patch(
     'google.auth.default',
     return_value=(mock.sentinel.credentials, mock.sentinel.projet))
[email protected]('google.auth.transport.grpc.secure_authorized_channel')
[email protected]('grpc.secure_channel')
+def test_create_channel_implicit_with_ssl_creds(
+        grpc_secure_channel, default, composite_creds_call):
+    target = 'example.com:443'
+
+    ssl_creds = grpc.ssl_channel_credentials()
+
+    grpc_helpers.create_channel(target, ssl_credentials=ssl_creds)
+
+    default.assert_called_once_with(scopes=None)
+    composite_creds_call.assert_called_once_with(ssl_creds, mock.ANY)
+    composite_creds = composite_creds_call.return_value
+    if (grpc_helpers.HAS_GRPC_GCP):
+        grpc_secure_channel.assert_called_once_with(
+            target, composite_creds, None)
+    else:
+        grpc_secure_channel.assert_called_once_with(
+            target, composite_creds)
+
+
[email protected]('grpc.composite_channel_credentials')
[email protected](
+    'google.auth.default',
+    return_value=(mock.sentinel.credentials, mock.sentinel.projet))
[email protected]('grpc.secure_channel')
 def test_create_channel_implicit_with_scopes(
-        secure_authorized_channel, default):
+        grpc_secure_channel, default, composite_creds_call):
     target = 'example.com:443'
+    composite_creds = composite_creds_call.return_value
 
     channel = grpc_helpers.create_channel(target, scopes=['one', 'two'])
 
-    assert channel is secure_authorized_channel.return_value
+    assert channel is grpc_secure_channel.return_value
     default.assert_called_once_with(scopes=['one', 'two'])
+    if (grpc_helpers.HAS_GRPC_GCP):
+        grpc_secure_channel.assert_called_once_with(
+            target, composite_creds, None)
+    else:
+        grpc_secure_channel.assert_called_once_with(
+            target, composite_creds)
 
 
[email protected]('google.auth.transport.grpc.secure_authorized_channel')
-def test_create_channel_explicit(secure_authorized_channel):
[email protected]('grpc.composite_channel_credentials')
[email protected]('google.auth.credentials.with_scopes_if_required')
[email protected]('grpc.secure_channel')
+def test_create_channel_explicit(
+        grpc_secure_channel, auth_creds, composite_creds_call):
     target = 'example.com:443'
+    composite_creds = composite_creds_call.return_value
 
     channel = grpc_helpers.create_channel(
         target, credentials=mock.sentinel.credentials)
 
-    assert channel is secure_authorized_channel.return_value
-    secure_authorized_channel.assert_called_once_with(
-        mock.sentinel.credentials, mock.ANY, target)
+    auth_creds.assert_called_once_with(mock.sentinel.credentials, None)
+    assert channel is grpc_secure_channel.return_value
+    if (grpc_helpers.HAS_GRPC_GCP):
+        grpc_secure_channel.assert_called_once_with(
+            target, composite_creds, None)
+    else:
+        grpc_secure_channel.assert_called_once_with(
+            target, composite_creds)
 
 
[email protected]('google.auth.transport.grpc.secure_authorized_channel')
-def test_create_channel_explicit_scoped(unused_secure_authorized_channel):
[email protected]('grpc.composite_channel_credentials')
[email protected]('grpc.secure_channel')
+def test_create_channel_explicit_scoped(
+        grpc_secure_channel, composite_creds_call):
+    target = 'example.com:443'
     scopes = ['1', '2']
+    composite_creds = composite_creds_call.return_value
+
+    credentials = mock.create_autospec(
+        google.auth.credentials.Scoped, instance=True)
+    credentials.requires_scopes = True
+
+    channel = grpc_helpers.create_channel(
+        target,
+        credentials=credentials,
+        scopes=scopes)
+
+    credentials.with_scopes.assert_called_once_with(scopes)
+    assert channel is grpc_secure_channel.return_value
+    if (grpc_helpers.HAS_GRPC_GCP):
+        grpc_secure_channel.assert_called_once_with(
+            target, composite_creds, None)
+    else:
+        grpc_secure_channel.assert_called_once_with(
+            target, composite_creds)
+
+
[email protected](not grpc_helpers.HAS_GRPC_GCP,
+                    reason='grpc_gcp module not available')
[email protected]('grpc_gcp.secure_channel')
+def test_create_channel_with_grpc_gcp(grpc_gcp_secure_channel):
+    target = 'example.com:443'
+    scopes = ['test_scope']
 
     credentials = mock.create_autospec(
         google.auth.credentials.Scoped, instance=True)
     credentials.requires_scopes = True
 
     grpc_helpers.create_channel(
-        mock.sentinel.target,
+        target,
         credentials=credentials,
         scopes=scopes)
+    grpc_gcp_secure_channel.assert_called()
+    credentials.with_scopes.assert_called_once_with(scopes)
 
+
[email protected](grpc_helpers.HAS_GRPC_GCP,
+                    reason='grpc_gcp module not available')
[email protected]('grpc.secure_channel')
+def test_create_channel_without_grpc_gcp(grpc_secure_channel):
+    target = 'example.com:443'
+    scopes = ['test_scope']
+
+    credentials = mock.create_autospec(
+        google.auth.credentials.Scoped, instance=True)
+    credentials.requires_scopes = True
+
+    grpc_helpers.create_channel(
+        target,
+        credentials=credentials,
+        scopes=scopes)
+    grpc_secure_channel.assert_called()
     credentials.with_scopes.assert_called_once_with(scopes)
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/google-api-core-1.2.0/tests/unit/test_protobuf_helpers.py 
new/google-api-core-1.4.0/tests/unit/test_protobuf_helpers.py
--- old/google-api-core-1.2.0/tests/unit/test_protobuf_helpers.py       
2018-04-25 19:05:26.000000000 +0200
+++ new/google-api-core-1.4.0/tests/unit/test_protobuf_helpers.py       
2018-07-25 21:43:09.000000000 +0200
@@ -18,8 +18,13 @@
 from google.api_core import protobuf_helpers
 from google.longrunning import operations_pb2
 from google.protobuf import any_pb2
+from google.protobuf import message
+from google.protobuf import source_context_pb2
+from google.protobuf import struct_pb2
 from google.protobuf import timestamp_pb2
-from google.protobuf.message import Message
+from google.protobuf import type_pb2
+from google.protobuf import wrappers_pb2
+from google.type import color_pb2
 from google.type import date_pb2
 from google.type import timeofday_pb2
 
@@ -67,7 +72,7 @@
 
     # Ensure that no non-Message objects were exported.
     for value in answer.values():
-        assert issubclass(value, Message)
+        assert issubclass(value, message.Message)
 
 
 def test_get_dict_absent():
@@ -230,3 +235,225 @@
     operation = operations_pb2.Operation(name='bar')
     protobuf_helpers.setdefault(operation, 'name', 'foo')
     assert operation.name == 'bar'
+
+
+def test_field_mask_invalid_args():
+    with pytest.raises(ValueError):
+        protobuf_helpers.field_mask('foo', any_pb2.Any())
+    with pytest.raises(ValueError):
+        protobuf_helpers.field_mask(any_pb2.Any(), 'bar')
+    with pytest.raises(ValueError):
+        protobuf_helpers.field_mask(any_pb2.Any(), operations_pb2.Operation())
+
+
+def test_field_mask_equal_values():
+    assert protobuf_helpers.field_mask(None, None).paths == []
+
+    original = struct_pb2.Value(number_value=1.0)
+    modified = struct_pb2.Value(number_value=1.0)
+    assert protobuf_helpers.field_mask(original, modified).paths == []
+
+    original = color_pb2.Color(alpha=wrappers_pb2.FloatValue(value=1.0))
+    modified = color_pb2.Color(alpha=wrappers_pb2.FloatValue(value=1.0))
+    assert protobuf_helpers.field_mask(original, modified).paths == []
+
+    original = struct_pb2.ListValue(
+            values=[struct_pb2.Value(number_value=1.0)])
+    modified = struct_pb2.ListValue(
+            values=[struct_pb2.Value(number_value=1.0)])
+    assert protobuf_helpers.field_mask(original, modified).paths == []
+
+    original = struct_pb2.Struct(
+            fields={'bar': struct_pb2.Value(number_value=1.0)})
+    modified = struct_pb2.Struct(
+            fields={'bar': struct_pb2.Value(number_value=1.0)})
+    assert protobuf_helpers.field_mask(original, modified).paths == []
+
+
+def test_field_mask_zero_values():
+    # Singular Values
+    original = color_pb2.Color(red=0.0)
+    modified = None
+    assert protobuf_helpers.field_mask(original, modified).paths == []
+
+    original = None
+    modified = color_pb2.Color(red=0.0)
+    assert protobuf_helpers.field_mask(original, modified).paths == []
+
+    # Repeated Values
+    original = struct_pb2.ListValue(values=[])
+    modified = None
+    assert protobuf_helpers.field_mask(original, modified).paths == []
+
+    original = None
+    modified = struct_pb2.ListValue(values=[])
+    assert protobuf_helpers.field_mask(original, modified).paths == []
+
+    # Maps
+    original = struct_pb2.Struct(fields={})
+    modified = None
+    assert protobuf_helpers.field_mask(original, modified).paths == []
+
+    original = None
+    modified = struct_pb2.Struct(fields={})
+    assert protobuf_helpers.field_mask(original, modified).paths == []
+
+    # Oneofs
+    original = struct_pb2.Value(number_value=0.0)
+    modified = None
+    assert protobuf_helpers.field_mask(original, modified).paths == []
+
+    original = None
+    modified = struct_pb2.Value(number_value=0.0)
+    assert protobuf_helpers.field_mask(original, modified).paths == []
+
+
+def test_field_mask_singular_field_diffs():
+    original = type_pb2.Type(name='name')
+    modified = type_pb2.Type()
+    assert (protobuf_helpers.field_mask(original, modified).paths ==
+            ['name'])
+
+    original = type_pb2.Type(name='name')
+    modified = type_pb2.Type()
+    assert (protobuf_helpers.field_mask(original, modified).paths ==
+            ['name'])
+
+    original = None
+    modified = type_pb2.Type(name='name')
+    assert (protobuf_helpers.field_mask(original, modified).paths ==
+            ['name'])
+
+    original = type_pb2.Type(name='name')
+    modified = None
+    assert (protobuf_helpers.field_mask(original, modified).paths ==
+            ['name'])
+
+
+def test_field_mask_message_diffs():
+    original = type_pb2.Type()
+    modified = type_pb2.Type(source_context=source_context_pb2.SourceContext(
+                            file_name='name'))
+    assert (protobuf_helpers.field_mask(original, modified).paths ==
+            ['source_context.file_name'])
+
+    original = type_pb2.Type(source_context=source_context_pb2.SourceContext(
+                             file_name='name'))
+    modified = type_pb2.Type()
+    assert (protobuf_helpers.field_mask(original, modified).paths ==
+            ['source_context'])
+
+    original = type_pb2.Type(source_context=source_context_pb2.SourceContext(
+                             file_name='name'))
+    modified = type_pb2.Type(source_context=source_context_pb2.SourceContext(
+                             file_name='other_name'))
+    assert (protobuf_helpers.field_mask(original, modified).paths ==
+            ['source_context.file_name'])
+
+    original = None
+    modified = type_pb2.Type(source_context=source_context_pb2.SourceContext(
+                             file_name='name'))
+    assert (protobuf_helpers.field_mask(original, modified).paths ==
+            ['source_context.file_name'])
+
+    original = type_pb2.Type(source_context=source_context_pb2.SourceContext(
+                             file_name='name'))
+    modified = None
+    assert (protobuf_helpers.field_mask(original, modified).paths ==
+            ['source_context'])
+
+
+def test_field_mask_wrapper_type_diffs():
+    original = color_pb2.Color()
+    modified = color_pb2.Color(alpha=wrappers_pb2.FloatValue(value=1.0))
+    assert protobuf_helpers.field_mask(original, modified).paths == ['alpha']
+
+    original = color_pb2.Color(alpha=wrappers_pb2.FloatValue(value=1.0))
+    modified = color_pb2.Color()
+    assert (protobuf_helpers.field_mask(original, modified).paths ==
+            ['alpha'])
+
+    original = color_pb2.Color(alpha=wrappers_pb2.FloatValue(value=1.0))
+    modified = color_pb2.Color(alpha=wrappers_pb2.FloatValue(value=2.0))
+    assert (protobuf_helpers.field_mask(original, modified).paths ==
+            ['alpha'])
+
+    original = None
+    modified = color_pb2.Color(alpha=wrappers_pb2.FloatValue(value=2.0))
+    assert (protobuf_helpers.field_mask(original, modified).paths ==
+            ['alpha'])
+
+    original = color_pb2.Color(alpha=wrappers_pb2.FloatValue(value=1.0))
+    modified = None
+    assert (protobuf_helpers.field_mask(original, modified).paths ==
+            ['alpha'])
+
+
+def test_field_mask_repeated_diffs():
+    original = struct_pb2.ListValue()
+    modified = struct_pb2.ListValue(values=[struct_pb2.Value(number_value=1.0),
+                                    struct_pb2.Value(number_value=2.0)])
+    assert protobuf_helpers.field_mask(original, modified).paths == ['values']
+
+    original = struct_pb2.ListValue(values=[struct_pb2.Value(number_value=1.0),
+                                    struct_pb2.Value(number_value=2.0)])
+    modified = struct_pb2.ListValue()
+    assert protobuf_helpers.field_mask(original, modified).paths == ['values']
+
+    original = None
+    modified = struct_pb2.ListValue(values=[struct_pb2.Value(number_value=1.0),
+                                    struct_pb2.Value(number_value=2.0)])
+    assert protobuf_helpers.field_mask(original, modified).paths == ['values']
+
+    original = struct_pb2.ListValue(values=[struct_pb2.Value(number_value=1.0),
+                                    struct_pb2.Value(number_value=2.0)])
+    modified = None
+    assert protobuf_helpers.field_mask(original, modified).paths == ['values']
+
+    original = struct_pb2.ListValue(values=[struct_pb2.Value(number_value=1.0),
+                                    struct_pb2.Value(number_value=2.0)])
+    modified = struct_pb2.ListValue(values=[struct_pb2.Value(number_value=2.0),
+                                    struct_pb2.Value(number_value=1.0)])
+    assert protobuf_helpers.field_mask(original, modified).paths == ['values']
+
+
+def test_field_mask_map_diffs():
+    original = struct_pb2.Struct()
+    modified = struct_pb2.Struct(
+            fields={'foo': struct_pb2.Value(number_value=1.0)})
+    assert protobuf_helpers.field_mask(original, modified).paths == ['fields']
+
+    original = struct_pb2.Struct(
+            fields={'foo': struct_pb2.Value(number_value=1.0)})
+    modified = struct_pb2.Struct()
+    assert protobuf_helpers.field_mask(original, modified).paths == ['fields']
+
+    original = None
+    modified = struct_pb2.Struct(
+            fields={'foo': struct_pb2.Value(number_value=1.0)})
+    assert protobuf_helpers.field_mask(original, modified).paths == ['fields']
+
+    original = struct_pb2.Struct(
+            fields={'foo': struct_pb2.Value(number_value=1.0)})
+    modified = None
+    assert protobuf_helpers.field_mask(original, modified).paths == ['fields']
+
+    original = struct_pb2.Struct(
+            fields={'foo': struct_pb2.Value(number_value=1.0)})
+    modified = struct_pb2.Struct(
+            fields={'foo': struct_pb2.Value(number_value=2.0)})
+    assert protobuf_helpers.field_mask(original, modified).paths == ['fields']
+
+    original = struct_pb2.Struct(
+            fields={'foo': struct_pb2.Value(number_value=1.0)})
+    modified = struct_pb2.Struct(
+            fields={'bar': struct_pb2.Value(number_value=1.0)})
+    assert protobuf_helpers.field_mask(original, modified).paths == ['fields']
+
+
+def test_field_mask_different_level_diffs():
+    original = color_pb2.Color(alpha=wrappers_pb2.FloatValue(value=1.0))
+    modified = color_pb2.Color(
+            alpha=wrappers_pb2.FloatValue(value=2.0), red=1.0)
+    assert (sorted(protobuf_helpers.field_mask(original, modified).paths) ==
+            ['alpha', 'red'])


Reply via email to