Add methods to api_core used by new autogenerator. (#6267)
* Add dispatch and deserialize methods.
This adds convenience methods used by client libraries produced by
gapic-generator-python.
* Mark test as Python 3 only.
* Fix import order.
* Address @theacodes feedback.
* Move dispatch to gapic_v2; alias remaining gapic_v1 modules.
* Fix import order.
diff --git a/google/api_core/gapic_v2/__init__.py b/google/api_core/gapic_v2/__init__.py
new file mode 100644
index 0000000..4a5cc70
--- /dev/null
+++ b/google/api_core/gapic_v2/__init__.py
@@ -0,0 +1,27 @@
+# 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
+#
+# 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.
+
+from google.api_core.gapic_v1 import client_info
+from google.api_core.gapic_v1 import config
+from google.api_core.gapic_v1 import method
+from google.api_core.gapic_v1 import routing_header
+from google.api_core.gapic_v2 import dispatch
+
+__all__ = [
+ 'client_info',
+ 'config',
+ 'dispatch',
+ 'method',
+ 'routing_header',
+]
diff --git a/google/api_core/gapic_v2/dispatch.py b/google/api_core/gapic_v2/dispatch.py
new file mode 100644
index 0000000..7b107a2
--- /dev/null
+++ b/google/api_core/gapic_v2/dispatch.py
@@ -0,0 +1,37 @@
+# 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
+#
+# 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 functools
+
+
+def dispatch(func):
+ """Return a decorated method that dispatches on the second argument.
+
+ This is the equivalent of :meth:`functools.singledispatch`, but for
+ bound methods.
+ """
+ base_dispatcher = functools.singledispatch(func)
+
+ # Define a wrapper function that works off args[1] instead of args[0].
+ # This is needed because we are overloading *methods*, and their first
+ # argument is always `self`.
+ @functools.wraps(base_dispatcher)
+ def wrapper(*args, **kwargs):
+ return base_dispatcher.dispatch(args[1].__class__)(*args, **kwargs)
+
+ # The register function is not changed, so let singledispatch do the work.
+ wrapper.register = base_dispatcher.register
+
+ # Done; return the decorated method.
+ return wrapper
diff --git a/google/api_core/operation.py b/google/api_core/operation.py
index 51a7a96..c76ac78 100644
--- a/google/api_core/operation.py
+++ b/google/api_core/operation.py
@@ -94,6 +94,18 @@
return protobuf_helpers.from_any_pb(
self._metadata_type, self._operation.metadata)
+ @classmethod
+ def deserialize(self, payload):
+ """Deserialize a ``google.longrunning.Operation`` protocol buffer.
+
+ Args:
+ payload (bytes): A serialized operation protocol buffer.
+
+ Returns:
+ ~.operations_pb2.Operation: An Operation protobuf object.
+ """
+ return operations_pb2.Operation.FromString(payload)
+
def _set_result_from_operation(self):
"""Set the result or exception from the operation if it is complete."""
# This must be done in a lock to prevent the polling thread
diff --git a/tests/unit/gapic/test_dispatch.py b/tests/unit/gapic/test_dispatch.py
new file mode 100644
index 0000000..aa7d871
--- /dev/null
+++ b/tests/unit/gapic/test_dispatch.py
@@ -0,0 +1,36 @@
+# 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
+#
+# 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 pytest
+import six
+
+from google.api_core.gapic_v2.dispatch import dispatch
+
+
+@pytest.mark.skipif(six.PY2, reason='dispatch only works on Python 3.')
+def test_dispatch():
+ class Foo(object):
+ @dispatch
+ def bar(self, number, letter):
+ return 'Brought by the letter {} and the number {}.'.format(
+ letter, number,
+ )
+
+ @bar.register(str)
+ def _bar_with_string(self, letter):
+ return self.bar(11, letter)
+
+ foo = Foo()
+ assert foo.bar(8, 'L') == 'Brought by the letter L and the number 8.'
+ assert foo.bar('Z') == 'Brought by the letter Z and the number 11.'
diff --git a/tests/unit/test_operation.py b/tests/unit/test_operation.py
index 211fea6..240eb86 100644
--- a/tests/unit/test_operation.py
+++ b/tests/unit/test_operation.py
@@ -221,3 +221,11 @@
assert future._metadata_type == struct_pb2.Struct
assert future.operation.name == TEST_OPERATION_NAME
assert future.done
+
+
+def test_deserialize():
+ op = make_operation_proto(name='foobarbaz')
+ serialized = op.SerializeToString()
+ deserialized_op = operation.Operation.deserialize(serialized)
+ assert op.name == deserialized_op.name
+ assert type(op) is type(deserialized_op)