Skip to content

Commit adbbd68

Browse files
authored
Merge pull request googleapis#2664 from dhermes/refactor-speech-operation
Re-factoring Speech operation to use the base class.
2 parents 80d2c5a + 9b422fe commit adbbd68

File tree

15 files changed

+189
-373
lines changed

15 files changed

+189
-373
lines changed

docs/index.rst

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,6 @@
176176
speech-usage
177177
Client <speech-client>
178178
speech-encoding
179-
speech-metadata
180179
speech-operation
181180
speech-sample
182181
speech-transcript

docs/speech-metadata.rst

Lines changed: 0 additions & 7 deletions
This file was deleted.

scripts/run_pylint.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -171,12 +171,6 @@ def get_python_files(all_files=None):
171171

172172
def lint_fileset(filenames, rcfile, description):
173173
"""Lints a group of files using a given rcfile."""
174-
# Only lint filenames that exist. For example, 'git diff --name-only'
175-
# could spit out deleted / renamed files. Another alternative could
176-
# be to use 'git diff --name-status' and filter out files with a
177-
# status of 'D'.
178-
filenames = [filename for filename in filenames
179-
if os.path.exists(filename)]
180174
if filenames:
181175
rc_flag = '--rcfile=%s' % (rcfile,)
182176
pylint_shell_command = ['pylint', rc_flag]

scripts/script_utils.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,14 @@ def get_affected_files(allow_limited=True):
231231
print('Diff base not specified, listing all files in repository.')
232232
result = subprocess.check_output(['git', 'ls-files'])
233233

234-
return result.rstrip('\n').split('\n'), diff_base
234+
# Only return filenames that exist. For example, 'git diff --name-only'
235+
# could spit out deleted / renamed files. Another alternative could
236+
# be to use 'git diff --name-status' and filter out files with a
237+
# status of 'D'.
238+
filenames = [filename
239+
for filename in result.rstrip('\n').split('\n')
240+
if os.path.exists(filename)]
241+
return filenames, diff_base
235242

236243

237244
def get_required_packages(file_contents):

speech/google/cloud/speech/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,5 @@
1717
from google.cloud.speech.client import Client
1818
from google.cloud.speech.connection import Connection
1919
from google.cloud.speech.encoding import Encoding
20+
from google.cloud.speech.operation import Operation
2021
from google.cloud.speech.transcript import Transcript

speech/google/cloud/speech/client.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,11 @@
1717
from base64 import b64encode
1818
import os
1919

20-
from google.cloud.client import Client as BaseClient
2120
from google.cloud._helpers import _to_bytes
2221
from google.cloud._helpers import _bytes_to_unicode
22+
from google.cloud.client import Client as BaseClient
2323
from google.cloud.environment_vars import DISABLE_GRPC
24+
2425
from google.cloud.speech.connection import Connection
2526
from google.cloud.speech.encoding import Encoding
2627
from google.cloud.speech.operation import Operation
@@ -111,8 +112,8 @@ def async_recognize(self, sample, language_code=None,
111112
and phrases. This can also be used to add new
112113
words to the vocabulary of the recognizer.
113114
114-
:rtype: `~google.cloud.speech.operation.Operation`
115-
:returns: ``Operation`` for asynchronous request to Google Speech API.
115+
:rtype: :class:`~google.cloud.speech.operation.Operation`
116+
:returns: Operation for asynchronous request to Google Speech API.
116117
"""
117118
if sample.encoding is not Encoding.LINEAR16:
118119
raise ValueError('Only LINEAR16 encoding is supported by '
@@ -279,15 +280,17 @@ def async_recognize(self, sample, language_code=None,
279280
and phrases. This can also be used to add new
280281
words to the vocabulary of the recognizer.
281282
282-
:rtype: `~google.cloud.speech.operation.Operation`
283-
:returns: ``Operation`` for asynchronous request to Google Speech API.
283+
:rtype: :class:`~google.cloud.speech.operation.Operation`
284+
:returns: Operation for asynchronous request to Google Speech API.
284285
"""
285286
data = _build_request_data(sample, language_code, max_alternatives,
286287
profanity_filter, speech_context)
287288
api_response = self._connection.api_request(
288289
method='POST', path='speech:asyncrecognize', data=data)
289290

290-
return Operation.from_api_repr(self, api_response)
291+
operation = Operation.from_dict(api_response, self._client)
292+
operation.caller_metadata['request_type'] = 'AsyncRecognize'
293+
return operation
291294

292295
def sync_recognize(self, sample, language_code=None, max_alternatives=None,
293296
profanity_filter=None, speech_context=None):

speech/google/cloud/speech/encoding.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2016 Google Inc. All rights reserved.
1+
# Copyright 2016 Google Inc.
22
#
33
# Licensed under the Apache License, Version 2.0 (the "License");
44
# you may not use this file except in compliance with the License.

speech/google/cloud/speech/metadata.py

Lines changed: 0 additions & 78 deletions
This file was deleted.
Lines changed: 35 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2016 Google Inc. All rights reserved.
1+
# Copyright 2016 Google Inc.
22
#
33
# Licensed under the Apache License, Version 2.0 (the "License");
44
# you may not use this file except in compliance with the License.
@@ -14,120 +14,55 @@
1414

1515
"""Long running operation representation for Google Speech API"""
1616

17-
from google.cloud.speech.metadata import Metadata
18-
from google.cloud.speech.transcript import Transcript
17+
from google.cloud.grpc.speech.v1beta1 import cloud_speech_pb2
18+
1919
from google.cloud import operation
20+
from google.cloud.speech.transcript import Transcript
2021

2122

22-
class Operation(operation.Operation):
23-
"""Representation of a Google API Long-Running Operation.
23+
operation.register_type(cloud_speech_pb2.AsyncRecognizeMetadata)
24+
operation.register_type(cloud_speech_pb2.AsyncRecognizeResponse)
2425

25-
:type client: :class:`~google.cloud.speech.client.Client`
26-
:param client: Instance of speech client.
2726

28-
:type name: int
29-
:param name: ID assigned to an operation.
27+
class Operation(operation.Operation):
28+
"""Custom Long-Running Operation for Google Speech API.
3029
31-
:type complete: bool
32-
:param complete: True if operation is complete, else False.
30+
:type name: str
31+
:param name: The fully-qualified path naming the operation.
3332
34-
:type metadata: :class:`~google.cloud.speech.metadata.Metadata`
35-
:param metadata: Instance of ``Metadata`` with operation information.
33+
:type client: :class:`~google.cloud.speech.client.Client`
34+
:param client: Client that created the current operation.
3635
37-
:type results: dict
38-
:param results: Dictionary with transcript and score of operation.
36+
:type caller_metadata: dict
37+
:param caller_metadata: caller-assigned metadata about the operation
3938
"""
40-
def __init__(self, client, name, complete=False, metadata=None,
41-
results=None):
42-
self.client = client
43-
self.name = name
44-
self._complete = complete
45-
self._metadata = metadata
46-
self._results = results
47-
48-
@classmethod
49-
def from_api_repr(cls, client, response):
50-
"""Factory: construct an instance from Google Speech API.
51-
52-
:type client: :class:`~google.cloud.speech.client.Client`
53-
:param client: Instance of speech client.
54-
55-
:type response: dict
56-
:param response: Dictionary response from Google Speech Operations API.
57-
58-
:rtype: :class:`Operation`
59-
:returns: Instance of `~google.cloud.speech.operations.Operation`.
60-
"""
61-
name = response['name']
62-
complete = response.get('done', False)
6339

64-
operation_instance = cls(client, name, complete)
65-
operation_instance._update(response)
66-
return operation_instance
40+
results = None
41+
"""List of transcriptions from the speech-to-text process."""
6742

68-
@property
69-
def complete(self):
70-
"""Completion state of the `Operation`.
43+
def _update_state(self, operation_pb):
44+
"""Update the state of the current object based on operation.
7145
72-
:rtype: bool
73-
:returns: True if already completed, else false.
74-
"""
75-
return self._complete
46+
This mostly does what the base class does, but all populates
47+
results.
7648
77-
@property
78-
def metadata(self):
79-
"""Metadata of operation.
49+
:type operation_pb:
50+
:class:`~google.longrunning.operations_pb2.Operation`
51+
:param operation_pb: Protobuf to be parsed.
8052
81-
:rtype: :class:`~google.cloud.speech.metadata.Metadata`
82-
:returns: Instance of ``Metadata``.
53+
:raises ValueError: If there is more than one entry in ``results``.
8354
"""
84-
return self._metadata
55+
super(Operation, self)._update_state(operation_pb)
8556

86-
@property
87-
def results(self):
88-
"""Results dictionary with transcript information.
57+
result_type = operation_pb.WhichOneof('result')
58+
if result_type != 'response':
59+
return
8960

90-
:rtype: dict
91-
:returns: Dictionary with transcript and confidence score.
92-
"""
93-
return self._results
94-
95-
def poll(self):
96-
"""Check if the operation has finished.
97-
98-
:rtype: bool
99-
:returns: A boolean indicating if the current operation has completed.
100-
:raises: :class:`ValueError <exceptions.ValueError>` if the operation
101-
has already completed.
102-
"""
103-
if self.complete:
104-
raise ValueError('The operation has completed.')
61+
pb_results = self.response.results
62+
if len(pb_results) != 1:
63+
raise ValueError('Expected exactly one result, found:',
64+
pb_results)
10565

106-
path = 'operations/%s' % (self.name,)
107-
api_response = self.client.connection.api_request(method='GET',
108-
path=path)
109-
self._update(api_response)
110-
return self.complete
111-
112-
def _update(self, response):
113-
"""Update Operation instance with latest data from Speech API.
114-
115-
.. _speech_operations: https://cloud.google.com/speech/reference/\
116-
rest/v1beta1/operations
117-
118-
:type response: dict
119-
:param response: Response from Speech API Operations endpoint.
120-
See: `speech_operations`_.
121-
"""
122-
metadata = response.get('metadata', None)
123-
raw_results = response.get('response', {}).get('results', None)
124-
results = []
125-
if raw_results:
126-
for result in raw_results:
127-
for alternative in result['alternatives']:
128-
results.append(Transcript.from_api_repr(alternative))
129-
if metadata:
130-
self._metadata = Metadata.from_api_repr(metadata)
131-
132-
self._results = results
133-
self._complete = response.get('done', False)
66+
result = pb_results[0]
67+
self.results = [Transcript.from_pb(alternative)
68+
for alternative in result.alternatives]

speech/google/cloud/speech/transcript.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,10 @@ def from_pb(cls, transcript):
5252
:rtype: :class:`Transcript`
5353
:returns: Instance of ``Transcript``.
5454
"""
55-
return cls(transcript.transcript, transcript.confidence)
55+
confidence = transcript.confidence
56+
if confidence == 0.0: # In the protobof 0.0 means unset.
57+
confidence = None
58+
return cls(transcript.transcript, confidence)
5659

5760
@property
5861
def transcript(self):

speech/unit_tests/test_client.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,9 @@ def test_async_recognize_no_gax(self):
262262
sample_rate=self.SAMPLE_RATE)
263263
operation = client.async_recognize(sample)
264264
self.assertIsInstance(operation, Operation)
265+
self.assertIs(operation.client, client)
266+
self.assertEqual(operation.caller_metadata,
267+
{'request_type': 'AsyncRecognize'})
265268
self.assertFalse(operation.complete)
266269
self.assertIsNone(operation.metadata)
267270

0 commit comments

Comments
 (0)