Skip to content

Commit cf7378f

Browse files
committed
Make sample.content handle bytes and files.
1 parent e7b1414 commit cf7378f

File tree

7 files changed

+73
-70
lines changed

7 files changed

+73
-70
lines changed

speech/google/cloud/speech/_gax.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -182,9 +182,7 @@ def streaming_recognize(self, sample, language_code=None,
182182
.cloud_speech_pb2.StreamingRecognizeResponse`
183183
:returns: ``StreamingRecognizeResponse`` instances.
184184
"""
185-
if getattr(sample.content, 'closed', None) is None:
186-
raise ValueError('Please use file-like object for data stream.')
187-
if sample.content.closed:
185+
if sample.stream.closed:
188186
raise ValueError('Stream is closed.')
189187

190188
requests = _stream_requests(sample, language_code=language_code,
@@ -252,7 +250,6 @@ def sync_recognize(self, sample, language_code=None, max_alternatives=None,
252250
language_code=language_code, max_alternatives=max_alternatives,
253251
profanity_filter=profanity_filter,
254252
speech_context=SpeechContext(phrases=speech_context))
255-
256253
audio = RecognitionAudio(content=sample.content,
257254
uri=sample.source_uri)
258255
api = self._gapic_api
@@ -337,7 +334,7 @@ def _stream_requests(sample, language_code=None, max_alternatives=None,
337334
yield config_request
338335

339336
while True:
340-
data = sample.content.read(sample.chunk_size)
337+
data = sample.stream.read(sample.chunk_size)
341338
if not data:
342339
break
343340
yield StreamingRecognizeRequest(audio_content=data)

speech/google/cloud/speech/client.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,12 @@ def __init__(self, credentials=None, http=None, use_gax=None):
6666
else:
6767
self._use_gax = use_gax
6868

69-
def sample(self, content=None, source_uri=None, encoding=None,
69+
def sample(self, content=None, source_uri=None, stream=None, encoding=None,
7070
sample_rate=None):
7171
"""Factory: construct Sample to use when making recognize requests.
7272
7373
:type content: bytes
74-
:param content: (Optional) Byte stream of audio.
74+
:param content: (Optional) Bytes containing audio data.
7575
7676
:type source_uri: str
7777
:param source_uri: (Optional) URI that points to a file that contains
@@ -80,6 +80,9 @@ def sample(self, content=None, source_uri=None, encoding=None,
8080
supported, which must be specified in the following
8181
format: ``gs://bucket_name/object_name``.
8282
83+
:type stream: file
84+
:param stream: (Optional) File like object to stream.
85+
8386
:type encoding: str
8487
:param encoding: encoding of audio data sent in all RecognitionAudio
8588
messages, can be one of: :attr:`~.Encoding.LINEAR16`,
@@ -97,7 +100,7 @@ def sample(self, content=None, source_uri=None, encoding=None,
97100
:rtype: :class:`~google.cloud.speech.sample.Sample`
98101
:returns: Instance of ``Sample``.
99102
"""
100-
return Sample(content=content, source_uri=source_uri,
103+
return Sample(content=content, source_uri=source_uri, stream=stream,
101104
encoding=encoding, sample_rate=sample_rate, client=self)
102105

103106
@property

speech/google/cloud/speech/sample.py

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
"""Sample class to handle content for Google Cloud Speech API."""
1616

17+
1718
from google.cloud.speech.encoding import Encoding
1819
from google.cloud.speech.result import StreamingSpeechResult
1920

@@ -22,7 +23,7 @@ class Sample(object):
2223
"""Representation of an audio sample to be used with Google Speech API.
2324
2425
:type content: bytes
25-
:param content: (Optional) Byte stream of audio.
26+
:param content: (Optional) Bytes containing audio data.
2627
2728
:type source_uri: str
2829
:param source_uri: (Optional) URI that points to a file that contains
@@ -31,6 +32,9 @@ class Sample(object):
3132
supported, which must be specified in the following
3233
format: ``gs://bucket_name/object_name``.
3334
35+
:type stream: file
36+
:param stream: (Optional) File like object to stream.
37+
3438
:type encoding: str
3539
:param encoding: encoding of audio data sent in all RecognitionAudio
3640
messages, can be one of: :attr:`~.Encoding.LINEAR16`,
@@ -51,17 +55,19 @@ class Sample(object):
5155
default_encoding = Encoding.FLAC
5256
default_sample_rate = 16000
5357

54-
def __init__(self, content=None, source_uri=None,
58+
def __init__(self, content=None, source_uri=None, stream=None,
5559
encoding=None, sample_rate=None, client=None):
5660
self._client = client
5761

58-
no_source = content is None and source_uri is None
59-
both_source = content is not None and source_uri is not None
60-
if no_source or both_source:
61-
raise ValueError('Supply one of \'content\' or \'source_uri\'')
62+
sources = [content is not None, source_uri is not None,
63+
stream is not None]
64+
if sources.count(True) != 1:
65+
raise ValueError('Supply exactly one of '
66+
'\'content\', \'source_uri\', \'stream\'')
6267

6368
self._content = content
6469
self._source_uri = source_uri
70+
self._stream = stream
6571

6672
if sample_rate is not None and not 8000 <= sample_rate <= 48000:
6773
raise ValueError('The value of sample_rate must be between 8000'
@@ -109,6 +115,15 @@ def sample_rate(self):
109115
"""
110116
return self._sample_rate
111117

118+
@property
119+
def stream(self):
120+
"""Stream the content when it is a file-like object.
121+
122+
:rtype: file
123+
:returns: File like object to stream.
124+
"""
125+
return self._stream
126+
112127
@property
113128
def encoding(self):
114129
"""Audio encoding type

speech/unit_tests/test__gax.py

Lines changed: 1 addition & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -14,43 +14,6 @@
1414

1515
import unittest
1616

17-
import mock
18-
19-
20-
def _make_credentials():
21-
import google.auth.credentials
22-
return mock.Mock(spec=google.auth.credentials.Credentials)
23-
24-
25-
class TestGAPICSpeechAPI(unittest.TestCase):
26-
SAMPLE_RATE = 16000
27-
28-
@staticmethod
29-
def _get_target_class():
30-
from google.cloud.speech._gax import GAPICSpeechAPI
31-
32-
return GAPICSpeechAPI
33-
34-
def _make_one(self, *args, **kw):
35-
return self._get_target_class()(*args, **kw)
36-
37-
def test_use_bytes_instead_of_file_like_object(self):
38-
from google.cloud import speech
39-
from google.cloud.speech.sample import Sample
40-
41-
credentials = _make_credentials()
42-
client = speech.Client(credentials=credentials, use_gax=True)
43-
client.connection = _Connection()
44-
client.connection.credentials = credentials
45-
46-
sample = Sample(content=b'', encoding=speech.Encoding.FLAC,
47-
sample_rate=self.SAMPLE_RATE)
48-
49-
api = self._make_one(client)
50-
with self.assertRaises(ValueError):
51-
api.streaming_recognize(sample)
52-
self.assertEqual(client.connection._requested, [])
53-
5417

5518
class TestSpeechGAXMakeRequests(unittest.TestCase):
5619
SAMPLE_RATE = 16000
@@ -143,7 +106,7 @@ def test_stream_requests(self):
143106
from google.cloud.grpc.speech.v1beta1.cloud_speech_pb2 import (
144107
StreamingRecognizeRequest)
145108

146-
sample = Sample(content=BytesIO(self.AUDIO_CONTENT),
109+
sample = Sample(stream=BytesIO(self.AUDIO_CONTENT),
147110
encoding=speech.Encoding.FLAC,
148111
sample_rate=self.SAMPLE_RATE)
149112
language_code = 'US-en'
@@ -172,10 +135,3 @@ def test_stream_requests(self):
172135
self.assertEqual(streaming_request.audio_content, self.AUDIO_CONTENT)
173136
self.assertIsInstance(config_request.streaming_config,
174137
StreamingRecognitionConfig)
175-
176-
177-
class _Connection(object):
178-
179-
def __init__(self, *responses):
180-
self._responses = responses
181-
self._requested = []

speech/unit_tests/test_client.py

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ class TestClient(unittest.TestCase):
7272
SAMPLE_RATE = 16000
7373
HINTS = ['hi']
7474
AUDIO_SOURCE_URI = 'gs://sample-bucket/sample-recording.flac'
75-
AUDIO_CONTENT = '/9j/4QNURXhpZgAASUkq'
75+
AUDIO_CONTENT = b'testing 1 2 3'
7676

7777
@staticmethod
7878
def _get_target_class():
@@ -125,14 +125,12 @@ def test_sync_recognize_content_with_optional_params_no_gax(self):
125125
from base64 import b64encode
126126

127127
from google.cloud._helpers import _bytes_to_unicode
128-
from google.cloud._helpers import _to_bytes
129128

130129
from google.cloud import speech
131130
from google.cloud.speech.alternative import Alternative
132131
from unit_tests._fixtures import SYNC_RECOGNIZE_RESPONSE
133132

134-
_AUDIO_CONTENT = _to_bytes(self.AUDIO_CONTENT)
135-
_B64_AUDIO_CONTENT = _bytes_to_unicode(b64encode(_AUDIO_CONTENT))
133+
_B64_AUDIO_CONTENT = _bytes_to_unicode(b64encode(self.AUDIO_CONTENT))
136134
RETURNED = SYNC_RECOGNIZE_RESPONSE
137135
REQUEST = {
138136
'config': {
@@ -325,8 +323,7 @@ def speech_api(channel=None):
325323
self.assertIsInstance(low_level, _MockGAPICSpeechAPI)
326324
self.assertIs(low_level._channel, channel_obj)
327325
self.assertEqual(
328-
channel_args,
329-
[(creds, _gax.DEFAULT_USER_AGENT, host)])
326+
channel_args, [(creds, _gax.DEFAULT_USER_AGENT, host)])
330327

331328
results = sample.sync_recognize()
332329

@@ -462,8 +459,9 @@ def speech_api(channel=None):
462459
speech_api.SERVICE_ADDRESS = host
463460

464461
stream.close()
462+
self.assertTrue(stream.closed)
465463

466-
sample = client.sample(content=stream,
464+
sample = client.sample(stream=stream,
467465
encoding=Encoding.LINEAR16,
468466
sample_rate=self.SAMPLE_RATE)
469467

@@ -523,7 +521,7 @@ def speech_api(channel=None):
523521
make_secure_channel=make_channel):
524522
client._speech_api = _gax.GAPICSpeechAPI(client)
525523

526-
sample = client.sample(content=stream,
524+
sample = client.sample(stream=stream,
527525
encoding=Encoding.LINEAR16,
528526
sample_rate=self.SAMPLE_RATE)
529527

@@ -596,7 +594,7 @@ def speech_api(channel=None):
596594
make_secure_channel=make_channel):
597595
client._speech_api = _gax.GAPICSpeechAPI(client)
598596

599-
sample = client.sample(content=stream,
597+
sample = client.sample(stream=stream,
600598
encoding=Encoding.LINEAR16,
601599
sample_rate=self.SAMPLE_RATE)
602600

@@ -640,7 +638,7 @@ def speech_api(channel=None):
640638
make_secure_channel=make_channel):
641639
client._speech_api = _gax.GAPICSpeechAPI(client)
642640

643-
sample = client.sample(content=stream,
641+
sample = client.sample(stream=stream,
644642
encoding=Encoding.LINEAR16,
645643
sample_rate=self.SAMPLE_RATE)
646644

speech/unit_tests/test_sample.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,44 @@ def test_initialize_sample(self):
3838
self.assertEqual(sample.sample_rate, self.SAMPLE_RATE)
3939

4040
def test_content_and_source_uri(self):
41+
from io import BytesIO
42+
4143
with self.assertRaises(ValueError):
4244
self._make_one(content='awefawagaeragere',
4345
source_uri=self.AUDIO_SOURCE_URI)
4446

47+
with self.assertRaises(ValueError):
48+
self._make_one(stream=BytesIO(b'awefawagaeragere'),
49+
source_uri=self.AUDIO_SOURCE_URI)
50+
51+
with self.assertRaises(ValueError):
52+
self._make_one(content='awefawagaeragere',
53+
stream=BytesIO(b'awefawagaeragere'),
54+
source_uri=self.AUDIO_SOURCE_URI)
55+
56+
def test_stream_property(self):
57+
from io import BytesIO
58+
from google.cloud.speech.encoding import Encoding
59+
60+
data = b'abc 1 2 3 4'
61+
stream = BytesIO(data)
62+
sample = self._make_one(stream=stream, encoding=Encoding.FLAC,
63+
sample_rate=self.SAMPLE_RATE)
64+
self.assertEqual(sample.stream, stream)
65+
self.assertEqual(sample.stream.read(), data)
66+
67+
def test_bytes_converts_to_file_like_object(self):
68+
from google.cloud import speech
69+
from google.cloud.speech.sample import Sample
70+
71+
test_bytes = b'testing 1 2 3'
72+
73+
sample = Sample(content=test_bytes, encoding=speech.Encoding.FLAC,
74+
sample_rate=self.SAMPLE_RATE)
75+
self.assertEqual(sample.content, test_bytes)
76+
self.assertEqual(sample.encoding, speech.Encoding.FLAC)
77+
self.assertEqual(sample.sample_rate, self.SAMPLE_RATE)
78+
4579
def test_sample_rates(self):
4680
from google.cloud.speech.encoding import Encoding
4781

system_tests/speech.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ def _make_async_request(self, content=None, source_uri=None,
118118
def _make_streaming_request(self, file_obj, single_utterance=True,
119119
interim_results=False):
120120
client = Config.CLIENT
121-
sample = client.sample(content=file_obj,
121+
sample = client.sample(stream=file_obj,
122122
encoding=speech.Encoding.LINEAR16,
123123
sample_rate=16000)
124124
return sample.streaming_recognize(single_utterance=single_utterance,

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy