Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Re-factoring Speech operation to use the base class. #2664

Merged
merged 1 commit into from
Nov 2, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,6 @@
speech-usage
Client <speech-client>
speech-encoding
speech-metadata
speech-operation
speech-sample
speech-transcript
Expand Down
7 changes: 0 additions & 7 deletions docs/speech-metadata.rst

This file was deleted.

6 changes: 0 additions & 6 deletions scripts/run_pylint.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,12 +171,6 @@ def get_python_files(all_files=None):

def lint_fileset(filenames, rcfile, description):
"""Lints a group of files using a given rcfile."""
# Only lint filenames that exist. For example, 'git diff --name-only'
# could spit out deleted / renamed files. Another alternative could
# be to use 'git diff --name-status' and filter out files with a
# status of 'D'.
filenames = [filename for filename in filenames
if os.path.exists(filename)]
if filenames:
rc_flag = '--rcfile=%s' % (rcfile,)
pylint_shell_command = ['pylint', rc_flag]
Expand Down
9 changes: 8 additions & 1 deletion scripts/script_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,14 @@ def get_affected_files(allow_limited=True):
print('Diff base not specified, listing all files in repository.')
result = subprocess.check_output(['git', 'ls-files'])

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


def get_required_packages(file_contents):
Expand Down
1 change: 1 addition & 0 deletions speech/google/cloud/speech/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@
from google.cloud.speech.client import Client
from google.cloud.speech.connection import Connection
from google.cloud.speech.encoding import Encoding
from google.cloud.speech.operation import Operation
from google.cloud.speech.transcript import Transcript
15 changes: 9 additions & 6 deletions speech/google/cloud/speech/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@
from base64 import b64encode
import os

from google.cloud.client import Client as BaseClient
from google.cloud._helpers import _to_bytes
from google.cloud._helpers import _bytes_to_unicode
from google.cloud.client import Client as BaseClient
from google.cloud.environment_vars import DISABLE_GRPC

from google.cloud.speech.connection import Connection
from google.cloud.speech.encoding import Encoding
from google.cloud.speech.operation import Operation
Expand Down Expand Up @@ -111,8 +112,8 @@ def async_recognize(self, sample, language_code=None,
and phrases. This can also be used to add new
words to the vocabulary of the recognizer.

:rtype: `~google.cloud.speech.operation.Operation`
:returns: ``Operation`` for asynchronous request to Google Speech API.
:rtype: :class:`~google.cloud.speech.operation.Operation`
:returns: Operation for asynchronous request to Google Speech API.
"""
if sample.encoding is not Encoding.LINEAR16:
raise ValueError('Only LINEAR16 encoding is supported by '
Expand Down Expand Up @@ -279,15 +280,17 @@ def async_recognize(self, sample, language_code=None,
and phrases. This can also be used to add new
words to the vocabulary of the recognizer.

:rtype: `~google.cloud.speech.operation.Operation`
:returns: ``Operation`` for asynchronous request to Google Speech API.
:rtype: :class:`~google.cloud.speech.operation.Operation`
:returns: Operation for asynchronous request to Google Speech API.
"""
data = _build_request_data(sample, language_code, max_alternatives,
profanity_filter, speech_context)
api_response = self._connection.api_request(
method='POST', path='speech:asyncrecognize', data=data)

return Operation.from_api_repr(self, api_response)
operation = Operation.from_dict(api_response, self._client)
operation.caller_metadata['request_type'] = 'AsyncRecognize'
return operation

def sync_recognize(self, sample, language_code=None, max_alternatives=None,
profanity_filter=None, speech_context=None):
Expand Down
2 changes: 1 addition & 1 deletion speech/google/cloud/speech/encoding.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2016 Google Inc. All rights reserved.
# Copyright 2016 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand Down
78 changes: 0 additions & 78 deletions speech/google/cloud/speech/metadata.py

This file was deleted.

135 changes: 35 additions & 100 deletions speech/google/cloud/speech/operation.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2016 Google Inc. All rights reserved.
# Copyright 2016 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand All @@ -14,120 +14,55 @@

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

from google.cloud.speech.metadata import Metadata
from google.cloud.speech.transcript import Transcript
from google.cloud.grpc.speech.v1beta1 import cloud_speech_pb2

This comment was marked as spam.


from google.cloud import operation
from google.cloud.speech.transcript import Transcript


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

:type client: :class:`~google.cloud.speech.client.Client`
:param client: Instance of speech client.

:type name: int
:param name: ID assigned to an operation.
class Operation(operation.Operation):
"""Custom Long-Running Operation for Google Speech API.

:type complete: bool
:param complete: True if operation is complete, else False.
:type name: str
:param name: The fully-qualified path naming the operation.

:type metadata: :class:`~google.cloud.speech.metadata.Metadata`
:param metadata: Instance of ``Metadata`` with operation information.
:type client: :class:`~google.cloud.speech.client.Client`
:param client: Client that created the current operation.

:type results: dict
:param results: Dictionary with transcript and score of operation.
:type caller_metadata: dict
:param caller_metadata: caller-assigned metadata about the operation
"""
def __init__(self, client, name, complete=False, metadata=None,
results=None):
self.client = client
self.name = name
self._complete = complete
self._metadata = metadata
self._results = results

@classmethod
def from_api_repr(cls, client, response):
"""Factory: construct an instance from Google Speech API.

:type client: :class:`~google.cloud.speech.client.Client`
:param client: Instance of speech client.

:type response: dict
:param response: Dictionary response from Google Speech Operations API.

:rtype: :class:`Operation`
:returns: Instance of `~google.cloud.speech.operations.Operation`.
"""
name = response['name']
complete = response.get('done', False)

operation_instance = cls(client, name, complete)
operation_instance._update(response)
return operation_instance
results = None
"""List of transcriptions from the speech-to-text process."""

@property
def complete(self):
"""Completion state of the `Operation`.
def _update_state(self, operation_pb):
"""Update the state of the current object based on operation.

:rtype: bool
:returns: True if already completed, else false.
"""
return self._complete
This mostly does what the base class does, but all populates
results.

@property
def metadata(self):
"""Metadata of operation.
:type operation_pb:
:class:`~google.longrunning.operations_pb2.Operation`
:param operation_pb: Protobuf to be parsed.

:rtype: :class:`~google.cloud.speech.metadata.Metadata`
:returns: Instance of ``Metadata``.
:raises ValueError: If there is more than one entry in ``results``.
"""
return self._metadata
super(Operation, self)._update_state(operation_pb)

@property
def results(self):
"""Results dictionary with transcript information.
result_type = operation_pb.WhichOneof('result')
if result_type != 'response':
return

:rtype: dict
:returns: Dictionary with transcript and confidence score.
"""
return self._results

def poll(self):
"""Check if the operation has finished.

:rtype: bool
:returns: A boolean indicating if the current operation has completed.
:raises: :class:`ValueError <exceptions.ValueError>` if the operation
has already completed.
"""
if self.complete:
raise ValueError('The operation has completed.')
pb_results = self.response.results
if len(pb_results) != 1:
raise ValueError('Expected exactly one result, found:',
pb_results)

path = 'operations/%s' % (self.name,)
api_response = self.client.connection.api_request(method='GET',
path=path)
self._update(api_response)
return self.complete

def _update(self, response):
"""Update Operation instance with latest data from Speech API.

.. _speech_operations: https://cloud.google.com/speech/reference/\
rest/v1beta1/operations

:type response: dict
:param response: Response from Speech API Operations endpoint.
See: `speech_operations`_.
"""
metadata = response.get('metadata', None)
raw_results = response.get('response', {}).get('results', None)
results = []
if raw_results:
for result in raw_results:
for alternative in result['alternatives']:
results.append(Transcript.from_api_repr(alternative))
if metadata:
self._metadata = Metadata.from_api_repr(metadata)

self._results = results
self._complete = response.get('done', False)
result = pb_results[0]
self.results = [Transcript.from_pb(alternative)

This comment was marked as spam.

for alternative in result.alternatives]
5 changes: 4 additions & 1 deletion speech/google/cloud/speech/transcript.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ def from_pb(cls, transcript):
:rtype: :class:`Transcript`
:returns: Instance of ``Transcript``.
"""
return cls(transcript.transcript, transcript.confidence)
confidence = transcript.confidence
if confidence == 0.0: # In the protobof 0.0 means unset.
confidence = None
return cls(transcript.transcript, confidence)

@property
def transcript(self):
Expand Down
3 changes: 3 additions & 0 deletions speech/unit_tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,9 @@ def test_async_recognize_no_gax(self):
sample_rate=self.SAMPLE_RATE)
operation = client.async_recognize(sample)
self.assertIsInstance(operation, Operation)
self.assertIs(operation.client, client)
self.assertEqual(operation.caller_metadata,
{'request_type': 'AsyncRecognize'})
self.assertFalse(operation.complete)
self.assertIsNone(operation.metadata)

Expand Down
Loading