Skip to content

Commit a70815d

Browse files
lukesneeringerdaspecster
authored andcommitted
Cloud Speech Veneer does not raise for > 1 result. (#2962)
* Cloud Speech Veneer does not raise for > 1 result. This commit also: - adds `transcript` and `confidence` convenience methods on result objects - updates unit tests appropriately - moves from google.cloud.grpc.* to google.cloud.proto.* - updates dependencies accordingly * Fix linting error, and make imports follow PEP8. * Fix two more linting issues. * Address @daspecster suggestions. Also finally fix all linting issues. * Update speech usage RST file. * Swap back to a list return, fixes coverage. * Final lint change for @daspecster. * Move imports to one per two lines.
1 parent 6b7b9ac commit a70815d

File tree

10 files changed

+176
-92
lines changed

10 files changed

+176
-92
lines changed

docs/speech-usage.rst

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -88,14 +88,14 @@ Great Britian.
8888
>>> sample = client.sample(source_uri='gs://my-bucket/recording.flac',
8989
... encoding=speech.Encoding.FLAC,
9090
... sample_rate=44100)
91-
>>> alternatives = sample.sync_recognize(
91+
>>> results = sample.sync_recognize(
9292
... speech.Encoding.FLAC, 16000,
9393
... source_uri='gs://my-bucket/recording.flac', language_code='en-GB',
9494
... max_alternatives=2)
95-
>>> for alternative in alternatives:
95+
>>> for result in results:
9696
... print('=' * 20)
97-
... print('transcript: ' + alternative.transcript)
98-
... print('confidence: ' + alternative.confidence)
97+
... print('transcript: ' + result.transcript)
98+
... print('confidence: ' + result.confidence)
9999
====================
100100
transcript: Hello, this is a test
101101
confidence: 0.81
@@ -112,12 +112,12 @@ Example of using the profanity filter.
112112
>>> sample = client.sample(source_uri='gs://my-bucket/recording.flac',
113113
... encoding=speech.Encoding.FLAC,
114114
... sample_rate=44100)
115-
>>> alternatives = sample.sync_recognize(max_alternatives=1,
116-
... profanity_filter=True)
117-
>>> for alternative in alternatives:
115+
>>> results = sample.sync_recognize(max_alternatives=1,
116+
... profanity_filter=True)
117+
>>> for result in results:
118118
... print('=' * 20)
119-
... print('transcript: ' + alternative.transcript)
120-
... print('confidence: ' + alternative.confidence)
119+
... print('transcript: ' + result.transcript)
120+
... print('confidence: ' + result.confidence)
121121
====================
122122
transcript: Hello, this is a f****** test
123123
confidence: 0.81
@@ -134,12 +134,12 @@ words to the vocabulary of the recognizer.
134134
... encoding=speech.Encoding.FLAC,
135135
... sample_rate=44100)
136136
>>> hints = ['hi', 'good afternoon']
137-
>>> alternatives = sample.sync_recognize(max_alternatives=2,
138-
... speech_context=hints)
139-
>>> for alternative in alternatives:
137+
>>> results = sample.sync_recognize(max_alternatives=2,
138+
... speech_context=hints)
139+
>>> for result in results:
140140
... print('=' * 20)
141-
... print('transcript: ' + alternative.transcript)
142-
... print('confidence: ' + alternative.confidence)
141+
... print('transcript: ' + result.transcript)
142+
... print('confidence: ' + result.confidence)
143143
====================
144144
transcript: Hello, this is a test
145145
confidence: 0.81

speech/google/cloud/speech/_gax.py

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,23 @@
1616

1717

1818
from google.cloud.gapic.speech.v1beta1.speech_client import SpeechClient
19-
from google.cloud.grpc.speech.v1beta1.cloud_speech_pb2 import RecognitionAudio
20-
from google.cloud.grpc.speech.v1beta1.cloud_speech_pb2 import RecognitionConfig
21-
from google.cloud.grpc.speech.v1beta1.cloud_speech_pb2 import SpeechContext
22-
from google.cloud.grpc.speech.v1beta1.cloud_speech_pb2 import (
19+
from google.cloud.proto.speech.v1beta1.cloud_speech_pb2 import RecognitionAudio
20+
from google.cloud.proto.speech.v1beta1.cloud_speech_pb2 import (
21+
RecognitionConfig)
22+
from google.cloud.proto.speech.v1beta1.cloud_speech_pb2 import (
23+
SpeechContext)
24+
from google.cloud.proto.speech.v1beta1.cloud_speech_pb2 import (
2325
StreamingRecognitionConfig)
24-
from google.cloud.grpc.speech.v1beta1.cloud_speech_pb2 import (
26+
from google.cloud.proto.speech.v1beta1.cloud_speech_pb2 import (
2527
StreamingRecognizeRequest)
2628
from google.longrunning import operations_grpc
2729

2830
from google.cloud._helpers import make_secure_channel
2931
from google.cloud._helpers import make_secure_stub
3032
from google.cloud._http import DEFAULT_USER_AGENT
3133

32-
from google.cloud.speech.alternative import Alternative
3334
from google.cloud.speech.operation import Operation
35+
from google.cloud.speech.result import Result
3436

3537
OPERATIONS_API_HOST = 'speech.googleapis.com'
3638

@@ -235,15 +237,9 @@ def sync_recognize(self, sample, language_code=None, max_alternatives=None,
235237
words to the vocabulary of the recognizer.
236238
237239
:rtype: list
238-
:returns: A list of dictionaries. One dict for each alternative. Each
239-
dictionary typically contains two keys (though not
240-
all will be present in all cases)
240+
:returns: List of :class:`google.cloud.speech.result.Result` objects.
241241
242-
* ``transcript``: The detected text from the audio recording.
243-
* ``confidence``: The confidence in language detection, float
244-
between 0 and 1.
245-
246-
:raises: ValueError if more than one result is returned or no results.
242+
:raises: ValueError if there are no results.
247243
"""
248244
config = RecognitionConfig(
249245
encoding=sample.encoding, sample_rate=sample.sample_rate,
@@ -254,13 +250,13 @@ def sync_recognize(self, sample, language_code=None, max_alternatives=None,
254250
uri=sample.source_uri)
255251
api = self._gapic_api
256252
api_response = api.sync_recognize(config=config, audio=audio)
257-
if len(api_response.results) == 1:
258-
results = api_response.results.pop()
259-
alternatives = results.alternatives
260-
return [Alternative.from_pb(alternative)
261-
for alternative in alternatives]
262-
else:
263-
raise ValueError('More than one result or none returned from API.')
253+
254+
# Sanity check: If we got no results back, raise an error.
255+
if len(api_response.results) == 0:
256+
raise ValueError('No results returned from the Speech API.')
257+
258+
# Iterate over any results that came back.
259+
return [Result.from_pb(result) for result in api_response.results]
264260

265261

266262
def _stream_requests(sample, language_code=None, max_alternatives=None,

speech/google/cloud/speech/client.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,11 @@ class Client(BaseClient):
6060
def __init__(self, credentials=None, http=None, use_gax=None):
6161
super(Client, self).__init__(credentials=credentials, http=http)
6262
self._connection = Connection(
63-
credentials=self._credentials, http=self._http)
63+
credentials=self._credentials,
64+
http=self._http,
65+
)
66+
67+
# Save on the actual client class whether we use GAX or not.
6468
if use_gax is None:
6569
self._use_gax = _USE_GAX
6670
else:

speech/google/cloud/speech/operation.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@
1414

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

17-
from google.cloud.grpc.speech.v1beta1 import cloud_speech_pb2
17+
from google.cloud.proto.speech.v1beta1 import cloud_speech_pb2
1818

1919
from google.cloud import operation
20-
from google.cloud.speech.alternative import Alternative
20+
from google.cloud.speech.result import Result
2121

2222

2323
operation.register_type(cloud_speech_pb2.AsyncRecognizeMetadata)
@@ -58,11 +58,13 @@ def _update_state(self, operation_pb):
5858
if result_type != 'response':
5959
return
6060

61+
# Retrieve the results.
62+
# If there were no results at all, raise an exception.
6163
pb_results = self.response.results
62-
if len(pb_results) != 1:
63-
raise ValueError('Expected exactly one result, found:',
64-
pb_results)
64+
if len(pb_results) == 0:
65+
raise ValueError('Speech API returned no results.')
6566

66-
result = pb_results[0]
67-
self.results = [Alternative.from_pb(alternative)
68-
for alternative in result.alternatives]
67+
# Save the results to the Operation object.
68+
self.results = []
69+
for pb_result in pb_results:
70+
self.results.append(Result.from_pb(pb_result))

speech/google/cloud/speech/result.py

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,53 @@
1717
from google.cloud.speech.alternative import Alternative
1818

1919

20+
class Result(object):
21+
"""Speech recognition result representation.
22+
23+
This is the object that comes back on sync or async requests
24+
(but not streaming requests).
25+
26+
:type alternatives: list
27+
:param alternatives: List of
28+
:class:`~google.cloud.speech.alternative.Alternative`.
29+
"""
30+
def __init__(self, alternatives):
31+
self.alternatives = alternatives
32+
33+
@classmethod
34+
def from_pb(cls, result):
35+
"""Factory: construct instance of ``SpeechRecognitionResult``.
36+
37+
:type result: :class:`~google.cloud.grpc.speech.v1beta1\
38+
.cloud_speech_pb2.StreamingRecognizeResult`
39+
:param result: Instance of ``StreamingRecognizeResult`` protobuf.
40+
41+
:rtype: :class:`~google.cloud.speech.result.SpeechRecognitionResult`
42+
:returns: Instance of ``SpeechRecognitionResult``.
43+
"""
44+
alternatives = [Alternative.from_pb(result) for result
45+
in result.alternatives]
46+
return cls(alternatives=alternatives)
47+
48+
@property
49+
def confidence(self):
50+
"""Return the confidence for the most probable alternative.
51+
52+
:rtype: float
53+
:returns: Confidence value, between 0 and 1.
54+
"""
55+
return self.alternatives[0].confidence
56+
57+
@property
58+
def transcript(self):
59+
"""Return the transcript for the most probable alternative.
60+
61+
:rtype: str
62+
:returns: Speech transcript.
63+
"""
64+
return self.alternatives[0].transcript
65+
66+
2067
class StreamingSpeechResult(object):
2168
"""Streaming speech result representation.
2269
@@ -46,9 +93,27 @@ def from_pb(cls, response):
4693
:rtype: :class:`~google.cloud.speech.result.StreamingSpeechResult`
4794
:returns: Instance of ``StreamingSpeechResult``.
4895
"""
49-
alternatives = [Alternative.from_pb(alternative)
50-
for alternative in response.alternatives]
96+
alternatives = [Alternative.from_pb(result) for result
97+
in response.alternatives]
5198
is_final = response.is_final
5299
stability = response.stability
53100
return cls(alternatives=alternatives, is_final=is_final,
54101
stability=stability)
102+
103+
@property
104+
def confidence(self):
105+
"""Return the confidence for the most probable alternative.
106+
107+
:rtype: float
108+
:returns: Confidence value, between 0 and 1.
109+
"""
110+
return self.alternatives[0].confidence
111+
112+
@property
113+
def transcript(self):
114+
"""Return the transcript for the most probable alternative.
115+
116+
:rtype: str
117+
:returns: Speech transcript.
118+
"""
119+
return self.alternatives[0].transcript

speech/setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
REQUIREMENTS = [
5252
'google-cloud-core >= 0.22.1, < 0.23dev',
5353
'grpcio >= 1.0.2, < 2.0dev',
54-
'gapic-google-cloud-speech-v1beta1 >= 0.14.0, < 0.15dev',
54+
'gapic-google-cloud-speech-v1beta1 >= 0.15.0, < 0.16dev',
5555
]
5656

5757
setup(

speech/unit_tests/test__gax.py

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,8 @@ def _call_fut(self, sample, language_code, max_alternatives,
3636
def test_ctor(self):
3737
from google.cloud import speech
3838
from google.cloud.speech.sample import Sample
39-
from google.cloud.grpc.speech.v1beta1.cloud_speech_pb2 import (
40-
SpeechContext)
41-
from google.cloud.grpc.speech.v1beta1.cloud_speech_pb2 import (
42-
RecognitionConfig)
43-
from google.cloud.grpc.speech.v1beta1.cloud_speech_pb2 import (
44-
StreamingRecognitionConfig)
45-
from google.cloud.grpc.speech.v1beta1.cloud_speech_pb2 import (
39+
from google.cloud.proto.speech.v1beta1.cloud_speech_pb2 import (
40+
RecognitionConfig, SpeechContext, StreamingRecognitionConfig,
4641
StreamingRecognizeRequest)
4742

4843
sample = Sample(content=self.AUDIO_CONTENT,
@@ -103,10 +98,8 @@ def test_stream_requests(self):
10398
from io import BytesIO
10499
from google.cloud import speech
105100
from google.cloud.speech.sample import Sample
106-
from google.cloud.grpc.speech.v1beta1.cloud_speech_pb2 import (
107-
StreamingRecognitionConfig)
108-
from google.cloud.grpc.speech.v1beta1.cloud_speech_pb2 import (
109-
StreamingRecognizeRequest)
101+
from google.cloud.proto.speech.v1beta1.cloud_speech_pb2 import (
102+
StreamingRecognitionConfig, StreamingRecognizeRequest)
110103

111104
sample = Sample(stream=BytesIO(self.AUDIO_CONTENT),
112105
encoding=speech.Encoding.FLAC,

speech/unit_tests/test_alternative.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ def test_from_api_repr_with_no_confidence(self):
5454
self.assertIsNone(alternative.confidence)
5555

5656
def test_from_pb_with_no_confidence(self):
57-
from google.cloud.grpc.speech.v1beta1 import cloud_speech_pb2
57+
from google.cloud.proto.speech.v1beta1 import cloud_speech_pb2
5858

5959
text = 'the double trouble'
6060
pb_value = cloud_speech_pb2.SpeechRecognitionAlternative(

0 commit comments

Comments
 (0)