diff --git a/docs/language-responses.rst b/docs/language-responses.rst index 0ad22fa9bb4f..5584cbcdcfab 100644 --- a/docs/language-responses.rst +++ b/docs/language-responses.rst @@ -1,6 +1,20 @@ Natural Language Response Classes ================================= +Responses +~~~~~~~~~ + +.. automodule:: google.cloud.language.api_responses + :members: + :show-inheritance: + +Sentences +~~~~~~~~~ + +.. automodule:: google.cloud.language.sentence + :members: + :show-inheritance: + Entity ~~~~~~ diff --git a/docs/language-usage.rst b/docs/language-usage.rst index 2f9f3b7f3cea..d8eedb466326 100644 --- a/docs/language-usage.rst +++ b/docs/language-usage.rst @@ -78,15 +78,8 @@ the document's type is plain text: >>> document.doc_type == language.Document.PLAIN_TEXT True -In addition, the document's language defaults to the language on -the client - - .. code-block:: python - - >>> document.language - 'en-US' - >>> document.language == client.language - True +The document's language defaults to ``None``, which will cause the API to +auto-detect the language. In addition, the :meth:`~google.cloud.language.client.Client.document_from_html`, @@ -161,31 +154,27 @@ metadata and other properties. >>> text_content = ("Michelangelo Caravaggio, Italian painter, is " ... "known for 'The Calling of Saint Matthew'.") >>> document = client.document(text_content) - >>> entities = document.analyze_entities() - >>> for entity in entities: + >>> entity_response = document.analyze_entities() + >>> for entity in entity_response.entities: ... print('=' * 20) ... print(' name: %s' % (entity.name,)) ... print(' type: %s' % (entity.entity_type,)) - ... print('wikipedia_url: %s' % (entity.wikipedia_url,)) ... print(' metadata: %s' % (entity.metadata,)) ... print(' salience: %s' % (entity.salience,)) ==================== name: Michelangelo Caravaggio type: PERSON - wikipedia_url: http://en.wikipedia.org/wiki/Caravaggio - metadata: {} + metadata: {'wikipedia_url': 'http://en.wikipedia.org/wiki/Caravaggio'} salience: 0.7615959 ==================== name: Italian type: LOCATION - wikipedia_url: http://en.wikipedia.org/wiki/Italy - metadata: {} + metadata: {'wikipedia_url': 'http://en.wikipedia.org/wiki/Caravaggio'} salience: 0.19960518 ==================== name: The Calling of Saint Matthew type: EVENT - wikipedia_url: http://en.wikipedia.org/wiki/The_Calling_of_St_Matthew_(Caravaggio) - metadata: {} + metadata: {'wikipedia_url': 'http://en.wikipedia.org/wiki/Caravaggio'} salience: 0.038798928 Analyze Sentiment @@ -200,7 +189,8 @@ only supports English text. >>> text_content = "Jogging isn't very fun." >>> document = client.document(text_content) - >>> sentiment = document.analyze_sentiment() + >>> sentiment_response = document.analyze_sentiment() + >>> sentiment = sentiment_response.sentiment >>> print(sentiment.score) -1 >>> print(sentiment.magnitude) @@ -265,14 +255,12 @@ the response is :data:`None`. ... print('=' * 20) ... print(' name: %s' % (entity.name,)) ... print(' type: %s' % (entity.entity_type,)) - ... print('wikipedia_url: %s' % (entity.wikipedia_url,)) ... print(' metadata: %s' % (entity.metadata,)) ... print(' salience: %s' % (entity.salience,)) ==================== name: Moon type: LOCATION - wikipedia_url: http://en.wikipedia.org/wiki/Natural_satellite - metadata: {} + metadata: {'wikipedia_url': 'http://en.wikipedia.org/wiki/Natural_satellite'} salience: 0.11793101 .. _Features: https://cloud.google.com/natural-language/reference/rest/v1beta1/documents/annotateText#Features diff --git a/language/google/cloud/language/api_responses.py b/language/google/cloud/language/api_responses.py new file mode 100644 index 000000000000..8ecdc6aa9805 --- /dev/null +++ b/language/google/cloud/language/api_responses.py @@ -0,0 +1,129 @@ +# Copyright 2017 Google Inc. +# +# 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. + +"""Response types from the Natural Language API.""" + +from google.cloud.language.entity import Entity +from google.cloud.language.sentence import Sentence +from google.cloud.language.sentiment import Sentiment +from google.cloud.language.syntax import Token + + +class EntityResponse(object): + """Object representation of entity responses. + + A representation of a response sent back from the + ``analyzeEntites`` request to the Google Natural language API. + + :type entities: list + :param entities: A list of :class:`~.language.entity.Entity` objects. + + :type language: str + :param language: The language used for analysis. + """ + def __init__(self, entities, language): + self.entities = entities + self.language = language + + @classmethod + def from_api_repr(cls, payload): + """Return an entity response from a JSON representation. + + :type payload: dict + :param payload: A dictionary representing the response. + + :rtype: :class:`~.language.entity.Entity` + :returns: An ``Entity`` object. + """ + return cls( + entities=[Entity.from_api_repr(i) for i in payload['entities']], + language=payload['language'], + ) + + +class SentimentResponse(object): + """Object representation of sentiment responses. + + A representation of a response to an ``analyzeSentiment`` request + to the Google Natural Language API. + + :type sentiment: :class:`~.language.sentiment.Sentiment` + :param sentiment: A Sentiment object. + + :type language: str + :param language: The language used for analyzing sentiment. + + :type sentences: list + :param sentences: A list of :class:`~.language.syntax.Sentence` objects. + """ + def __init__(self, sentiment, language, sentences): + self.sentiment = sentiment + self.language = language + self.sentences = sentences + + @classmethod + def from_api_repr(cls, payload): + """Return an sentiment response from a JSON representation. + + :type payload: dict + :param payload: A dictionary representing the response. + + :rtype: `~.language.sentiment.Sentiment` + :returns: A ``Sentiment`` object. + """ + return cls( + language=payload.get('language'), + sentences=[Sentence.from_api_repr(sentence) for sentence + in payload.get('sentences', ())], + sentiment=Sentiment.from_api_repr(payload['documentSentiment']), + ) + + +class SyntaxResponse(object): + """Object representation of syntax responses. + + A representation of a response to an ``analyzeSyntax`` request + to the Google Natural Language API. + + :type tokens: list + :param tokens: A list of :class:`~.language.syntax.Token` objects. + + :type language: str + :param language: The language used for analyzing sentiment. + + :type sentences: list + :param sentences: A list of :class:`~.language.syntax.Sentence` objects. + """ + def __init__(self, tokens, language, sentences): + self.tokens = tokens + self.language = language + self.sentences = sentences + + @classmethod + def from_api_repr(cls, payload): + """Return an syntax response from a JSON representation. + + :type payload: dict + :param payload: A dictionary representing the response. + + :rtype: `~.language.syntax.Syntax` + :returns: A ``Syntax`` object. + """ + return cls( + language=payload.get('language'), + sentences=[Sentence.from_api_repr(sentence) for sentence in + payload.get('sentences', ())], + tokens=[Token.from_api_repr(token) for token in + payload.get('tokens', ())] + ) diff --git a/language/google/cloud/language/document.py b/language/google/cloud/language/document.py index dc8ac5ecd9bf..3165f3c17597 100644 --- a/language/google/cloud/language/document.py +++ b/language/google/cloud/language/document.py @@ -1,4 +1,4 @@ -# Copyright 2016 Google Inc. +# Copyright 2016-2017 Google Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,15 +19,16 @@ import collections +from google.cloud.language import api_responses from google.cloud.language.entity import Entity from google.cloud.language.sentiment import Sentiment -from google.cloud.language.syntax import Sentence +from google.cloud.language.sentence import Sentence from google.cloud.language.syntax import Token Annotations = collections.namedtuple( 'Annotations', - 'sentences tokens sentiment entities') + ['sentences', 'tokens', 'sentiment', 'entities', 'language']) """Annotations for a document. :type sentences: list @@ -42,6 +43,9 @@ :type entities: list :param entities: List of :class:`~.language.entity.Entity` found in a document. + +:type language: str +:param language: The language used for the annotation. """ @@ -156,9 +160,8 @@ def analyze_entities(self): See `analyzeEntities`_. - :rtype: list - :returns: A list of :class:`~.language.entity.Entity` returned from - the API. + :rtype: :class:`~.language.entity.EntityResponse` + :returns: A representation of the entity response. """ data = { 'document': self._to_dict(), @@ -166,8 +169,7 @@ def analyze_entities(self): } api_response = self.client._connection.api_request( method='POST', path='analyzeEntities', data=data) - return [Entity.from_api_repr(entity) - for entity in api_response['entities']] + return api_responses.EntityResponse.from_api_repr(api_response) def analyze_sentiment(self): """Analyze the sentiment in the current document. @@ -177,13 +179,13 @@ def analyze_sentiment(self): See `analyzeSentiment`_. - :rtype: :class:`.Sentiment` - :returns: The sentiment of the current document. + :rtype: :class:`.SentimentResponse` + :returns: A representation of the sentiment response. """ data = {'document': self._to_dict()} api_response = self.client._connection.api_request( method='POST', path='analyzeSentiment', data=data) - return Sentiment.from_api_repr(api_response['documentSentiment']) + return api_responses.SentimentResponse.from_api_repr(api_response) def analyze_syntax(self): """Analyze the syntax in the current document. @@ -203,8 +205,7 @@ def analyze_syntax(self): } api_response = self.client._connection.api_request( method='POST', path='analyzeSyntax', data=data) - return [Token.from_api_repr(token) - for token in api_response.get('tokens', ())] + return api_responses.SyntaxResponse.from_api_repr(api_response) def annotate_text(self, include_syntax=True, include_entities=True, include_sentiment=True): @@ -271,9 +272,10 @@ def annotate_text(self, include_syntax=True, include_entities=True, entities = [Entity.from_api_repr(entity) for entity in api_response['entities']] annotations = Annotations( + entities=entities, + language=api_response.get('language'), sentences=sentences, - tokens=tokens, sentiment=sentiment, - entities=entities, + tokens=tokens, ) return annotations diff --git a/language/google/cloud/language/entity.py b/language/google/cloud/language/entity.py index f20887dc3f2e..56c810b7f10b 100644 --- a/language/google/cloud/language/entity.py +++ b/language/google/cloud/language/entity.py @@ -1,4 +1,4 @@ -# Copyright 2016 Google Inc. +# Copyright 2016-2017 Google Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -53,10 +53,6 @@ class Entity(object): an organization, or location. The API associates information, such as salience and mentions, with entities. - The only supported metadata (as of August 2016) is ``wikipedia_url``, - so this value will be removed from the passed in ``metadata`` - and put in its own property. - .. _Entity message: https://cloud.google.com/natural-language/\ reference/rest/v1/Entity .. _EntityType enum: https://cloud.google.com/natural-language/\ @@ -84,7 +80,6 @@ class Entity(object): def __init__(self, name, entity_type, metadata, salience, mentions): self.name = name self.entity_type = entity_type - self.wikipedia_url = metadata.pop('wikipedia_url', None) self.metadata = metadata self.salience = salience self.mentions = mentions diff --git a/language/google/cloud/language/sentence.py b/language/google/cloud/language/sentence.py new file mode 100644 index 000000000000..386162ee7010 --- /dev/null +++ b/language/google/cloud/language/sentence.py @@ -0,0 +1,68 @@ +# Copyright 2017 Google Inc. +# +# 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. + +"""Representation of Sentence objects.""" + +from google.cloud.language.sentiment import Sentiment + + +class Sentence(object): + """A Google Cloud Natural Language API sentence object. + + .. _Sentence message: https://cloud.google.com/natural-language/reference\ + /rest/v1/documents/annotateText#Sentence + + See `Sentence message`_. + + :type content: str + :param content: The text that the sentence is composed of. + + :type begin: int + :param begin: The beginning offset of the sentence in the original + document according to the encoding type specified + in the API request. + + :type sentiment: :class:`~google.cloud.language.sentiment.Sentiment` + :param sentiment: + (Optional) For calls to + :meth:`~google.cloud.language.document.Document.annotate_text` where + ``include_sentiment`` is set to true, this field will contain the + sentiment for the sentence. + """ + def __init__(self, content, begin, sentiment=None): + self.content = content + self.begin = begin + self.sentiment = sentiment + + @classmethod + def from_api_repr(cls, payload): + """Convert a sentence from the JSON API into a :class:`Sentence`. + + :param payload: dict + :type payload: The value from the backend. + + :rtype: :class:`Sentence` + :returns: The sentence parsed from the API representation. + """ + text_span = payload['text'] + + # The sentence may or may not have a sentiment; only attempt the + # typecast if one is present. + sentiment = None + if payload.get('sentiment') is not None: + sentiment = Sentiment.from_api_repr(payload['sentiment']) + + # Return a Sentence object. + return cls(text_span['content'], text_span['beginOffset'], + sentiment=sentiment) diff --git a/language/google/cloud/language/syntax.py b/language/google/cloud/language/syntax.py index e2b4ab286f24..9bad2116b332 100644 --- a/language/google/cloud/language/syntax.py +++ b/language/google/cloud/language/syntax.py @@ -1,4 +1,4 @@ -# Copyright 2016 Google Inc. +# Copyright 2016-2017 Google Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,8 +18,6 @@ breaks a document down into tokens and sentences. """ -from google.cloud.language.sentiment import Sentiment - class PartOfSpeech(object): """Part of speech of a :class:`Token`.""" @@ -168,53 +166,3 @@ def from_api_repr(cls, payload): lemma = payload['lemma'] return cls(text_content, text_begin, part_of_speech, edge_index, edge_label, lemma) - - -class Sentence(object): - """A Google Cloud Natural Language API sentence object. - - .. _Sentence message: https://cloud.google.com/natural-language/reference\ - /rest/v1/documents/annotateText#Sentence - - See `Sentence message`_. - - :type content: str - :param content: The text that the sentence is composed of. - - :type begin: int - :param begin: The beginning offset of the sentence in the original - document according to the encoding type specified - in the API request. - - :type sentiment: :class:`~google.cloud.language.sentiment.Sentiment` - :param sentiment: - (Optional) For calls to - :meth:`~google.cloud.language.document.Document.annotate_text` where - ``include_sentiment`` is set to true, this field will contain the - sentiment for the sentence. - """ - - def __init__(self, content, begin, sentiment=None): - self.content = content - self.begin = begin - self.sentiment = sentiment - - @classmethod - def from_api_repr(cls, payload): - """Convert a sentence from the JSON API into a :class:`Sentiment`. - - :param payload: dict - :type payload: The value from the backend. - - :rtype: :class:`Sentence` - :returns: The sentence parsed from the API representation. - """ - text_span = payload['text'] - - try: - sentiment = Sentiment.from_api_repr(payload['sentiment']) - except KeyError: - sentiment = None - - return cls(text_span['content'], text_span['beginOffset'], - sentiment=sentiment) diff --git a/language/setup.py b/language/setup.py index 35e2c78ffb6c..9489c19f3028 100644 --- a/language/setup.py +++ b/language/setup.py @@ -55,7 +55,7 @@ setup( name='google-cloud-language', - version='0.22.2', + version='0.23.0', description='Python Client for Google Cloud Natural Language', long_description=README, namespace_packages=[ diff --git a/language/unit_tests/test_api_responses.py b/language/unit_tests/test_api_responses.py new file mode 100644 index 000000000000..7bdba120a14e --- /dev/null +++ b/language/unit_tests/test_api_responses.py @@ -0,0 +1,170 @@ +# Copyright 2017 Google Inc. +# +# 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 copy import copy +import unittest + + +class TestEntityResponse(unittest.TestCase): + ENTITY_DICT = { + 'mentions': [{'text': {'content': 'Italian'}}], + 'metadata': {'wikipedia_url': 'http://en.wikipedia.org/wiki/Italy'}, + 'name': 'Italian', + 'salience': 0.15, + 'type': 'LOCATION', + } + + def test_constructor(self): + from google.cloud.language.api_responses import EntityResponse + from google.cloud.language.entity import Entity + + entity_response = EntityResponse( + entities=[Entity.from_api_repr(self.ENTITY_DICT)], + language='en', + ) + self._verify_entity_response(entity_response) + + def test_api_repr_factory(self): + from google.cloud.language.api_responses import EntityResponse + + entity_response = EntityResponse.from_api_repr({ + 'entities': [self.ENTITY_DICT], + 'language': 'en', + }) + self._verify_entity_response(entity_response) + + def _verify_entity_response(self, entity_response): + from google.cloud.language.entity import EntityType + + self.assertEqual(len(entity_response.entities), 1) + entity = entity_response.entities[0] + self.assertEqual(entity.name, 'Italian') + self.assertEqual(len(entity.mentions), 1) + self.assertEqual(entity.mentions[0], 'Italian') + self.assertTrue(entity.metadata['wikipedia_url'].endswith('Italy')) + self.assertAlmostEqual(entity.salience, 0.15) + self.assertEqual(entity.entity_type, EntityType.LOCATION) + self.assertEqual(entity_response.language, 'en') + + +class TestSentimentResponse(unittest.TestCase): + SENTIMENT_DICT = { + 'score': 0.4, + 'magnitude': 3.14159, + } + SENTENCE_DICT = { + 'text': { + 'beginOffset': 0, + 'content': 'It is hailing in Wales.', + }, + 'sentiment': SENTIMENT_DICT, + } + + def test_constructor(self): + from google.cloud.language.api_responses import SentimentResponse + from google.cloud.language.sentence import Sentence + from google.cloud.language.sentiment import Sentiment + + sentiment_response = SentimentResponse( + language='en', + sentences=[Sentence.from_api_repr(self.SENTENCE_DICT)], + sentiment=Sentiment.from_api_repr(self.SENTIMENT_DICT), + ) + + self._verify_sentiment_response(sentiment_response) + + def test_api_repr_factory(self): + from google.cloud.language.api_responses import SentimentResponse + + sentiment_response = SentimentResponse.from_api_repr({ + 'documentSentiment': self.SENTIMENT_DICT, + 'language': 'en', + 'sentences': [self.SENTENCE_DICT], + }) + + self._verify_sentiment_response(sentiment_response) + + def _verify_sentiment_response(self, sentiment_response): + from google.cloud.language.sentiment import Sentiment + + self.assertEqual(sentiment_response.language, 'en') + self.assertEqual(len(sentiment_response.sentences), 1) + sentence = sentiment_response.sentences[0] + self.assertEqual(sentence.begin, 0) + self.assertEqual(sentence.content, 'It is hailing in Wales.') + self.assertIsInstance(sentence.sentiment, Sentiment) + self.assertAlmostEqual(sentiment_response.sentiment.score, 0.4) + self.assertAlmostEqual(sentiment_response.sentiment.magnitude, 3.14159) + + +class TestSyntaxResponse(unittest.TestCase): + SENTENCE_DICT = copy(TestSentimentResponse.SENTENCE_DICT) + TOKEN_DICT = { + 'dependencyEdge': { + 'headTokenIndex': 0, + 'label': 'NSUBJ', + }, + 'lemma': 'it', + 'partOfSpeech': { + 'tag': 'PRON', + }, + 'text': { + 'beginOffset': 0, + 'content': 'It' + }, + } + + def test_constructor(self): + from google.cloud.language.api_responses import SyntaxResponse + from google.cloud.language.sentence import Sentence + from google.cloud.language.syntax import Token + + syntax_response = SyntaxResponse( + language='en', + sentences=[Sentence.from_api_repr(self.SENTENCE_DICT)], + tokens=[Token.from_api_repr(self.TOKEN_DICT)], + ) + + self._verify_syntax_response(syntax_response) + + def test_api_repr_factory(self): + from google.cloud.language.api_responses import SyntaxResponse + + syntax_response = SyntaxResponse.from_api_repr({ + 'language': 'en', + 'sentences': [self.SENTENCE_DICT], + 'tokens': [self.TOKEN_DICT], + }) + + self._verify_syntax_response(syntax_response) + + def _verify_syntax_response(self, syntax_response): + from google.cloud.language.sentiment import Sentiment + from google.cloud.language.syntax import PartOfSpeech + + self.assertEqual(syntax_response.language, 'en') + + self.assertEqual(len(syntax_response.sentences), 1) + sentence = syntax_response.sentences[0] + self.assertEqual(sentence.begin, 0) + self.assertEqual(sentence.content, 'It is hailing in Wales.') + self.assertIsInstance(sentence.sentiment, Sentiment) + self.assertEqual(len(syntax_response.tokens), 1) + token = syntax_response.tokens[0] + self.assertEqual(token.text_content, 'It') + self.assertEqual(token.text_begin, 0) + self.assertEqual(token.part_of_speech, PartOfSpeech.PRONOUN) + self.assertEqual(token.edge_index, 0) + self.assertEqual(token.edge_label, 'NSUBJ') + self.assertEqual(token.lemma, 'it') diff --git a/language/unit_tests/test_document.py b/language/unit_tests/test_document.py index 2ddfc0de97d5..a96eac8d24e5 100644 --- a/language/unit_tests/test_document.py +++ b/language/unit_tests/test_document.py @@ -197,8 +197,10 @@ def _verify_entity(self, entity, name, entity_type, wiki_url, salience): self.assertIsInstance(entity, Entity) self.assertEqual(entity.name, name) self.assertEqual(entity.entity_type, entity_type) - self.assertEqual(entity.wikipedia_url, wiki_url) - self.assertEqual(entity.metadata, {}) + if wiki_url: + self.assertEqual(entity.metadata, {'wikipedia_url': wiki_url}) + else: + self.assertEqual(entity.metadata, {}) self.assertEqual(entity.salience, salience) self.assertEqual(entity.mentions, [name]) @@ -274,12 +276,12 @@ def test_analyze_entities(self): client = make_mock_client(response) document = self._make_one(client, content) - entities = document.analyze_entities() - self.assertEqual(len(entities), 2) - entity1 = entities[0] + entity_response = document.analyze_entities() + self.assertEqual(len(entity_response.entities), 2) + entity1 = entity_response.entities[0] self._verify_entity(entity1, name1, EntityType.OTHER, None, salience1) - entity2 = entities[1] + entity2 = entity_response.entities[1] self._verify_entity(entity2, name2, EntityType.LOCATION, wiki2, salience2) @@ -297,6 +299,8 @@ def _verify_sentiment(self, sentiment, score, magnitude): self.assertEqual(sentiment.magnitude, magnitude) def test_analyze_sentiment(self): + from google.cloud.language.api_responses import SentimentResponse + content = 'All the pretty horses.' score = 1 magnitude = 0.6 @@ -310,8 +314,9 @@ def test_analyze_sentiment(self): client = make_mock_client(response) document = self._make_one(client, content) - sentiment = document.analyze_sentiment() - self._verify_sentiment(sentiment, score, magnitude) + sentiment_response = document.analyze_sentiment() + self.assertIsInstance(sentiment_response, SentimentResponse) + self._verify_sentiment(sentiment_response.sentiment, score, magnitude) # Verify the request. expected = self._expected_data(content) @@ -327,6 +332,7 @@ def _verify_token(self, token, text_content, part_of_speech, lemma): self.assertEqual(token.lemma, lemma) def test_analyze_syntax(self): + from google.cloud.language.api_responses import SyntaxResponse from google.cloud.language.document import Encoding from google.cloud.language.syntax import PartOfSpeech @@ -406,7 +412,10 @@ def test_analyze_syntax(self): client = make_mock_client(response) document = self._make_one(client, content) - tokens = document.analyze_syntax() + syntax_response = document.analyze_syntax() + self.assertIsInstance(syntax_response, SyntaxResponse) + + tokens = syntax_response.tokens self.assertEqual(len(tokens), 4) token1 = tokens[0] self._verify_token(token1, name1, PartOfSpeech.NOUN, name1) @@ -424,7 +433,7 @@ def test_analyze_syntax(self): path='analyzeSyntax', method='POST', data=expected) def _verify_sentences(self, include_syntax, annotations): - from google.cloud.language.syntax import Sentence + from google.cloud.language.sentence import Sentence if include_syntax: self.assertEqual(len(annotations.sentences), 1) diff --git a/language/unit_tests/test_entity.py b/language/unit_tests/test_entity.py index 77562e39e612..cd4cb6b8f7d9 100644 --- a/language/unit_tests/test_entity.py +++ b/language/unit_tests/test_entity.py @@ -30,17 +30,17 @@ def test_constructor_defaults(self): name = 'Italian' entity_type = 'LOCATION' wiki_url = 'http://en.wikipedia.org/wiki/Italy' - metadata = {'wikipedia_url': wiki_url} - base_metadata = {'foo': 'bar'} - metadata.update(base_metadata) + metadata = { + 'foo': 'bar', + 'wikipedia_url': wiki_url, + } salience = 0.19960518 mentions = ['Italian'] entity = self._make_one(name, entity_type, metadata, salience, mentions) self.assertEqual(entity.name, name) self.assertEqual(entity.entity_type, entity_type) - self.assertEqual(entity.wikipedia_url, wiki_url) - self.assertEqual(entity.metadata, base_metadata) + self.assertEqual(entity.metadata, metadata) self.assertEqual(entity.salience, salience) self.assertEqual(entity.mentions, mentions) @@ -68,6 +68,5 @@ def test_from_api_repr(self): self.assertEqual(entity.name, name) self.assertEqual(entity.entity_type, entity_type) self.assertEqual(entity.salience, salience) - self.assertEqual(entity.wikipedia_url, wiki_url) - self.assertEqual(entity.metadata, {}) + self.assertEqual(entity.metadata, {'wikipedia_url': wiki_url}) self.assertEqual(entity.mentions, [mention1, mention2, mention3]) diff --git a/language/unit_tests/test_sentence.py b/language/unit_tests/test_sentence.py new file mode 100644 index 000000000000..8c04f1ff4a78 --- /dev/null +++ b/language/unit_tests/test_sentence.py @@ -0,0 +1,74 @@ +# Copyright 2017 Google Inc. +# +# 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 unittest + + +class TestSentence(unittest.TestCase): + + @staticmethod + def _get_target_class(): + from google.cloud.language.sentence import Sentence + + return Sentence + + def _make_one(self, *args, **kw): + return self._get_target_class()(*args, **kw) + + def test_constructor(self): + content = "All the king's horses." + begin = 11 + sentence = self._make_one(content, begin) + self.assertEqual(sentence.content, content) + self.assertEqual(sentence.begin, begin) + + def test_from_api_repr(self): + klass = self._get_target_class() + content = 'All the pretty horses.' + begin = -1 + payload = { + 'text': { + 'content': content, + 'beginOffset': begin, + }, + } + sentence = klass.from_api_repr(payload) + self.assertEqual(sentence.content, content) + self.assertEqual(sentence.begin, begin) + self.assertIsNone(sentence.sentiment) + + def test_from_api_repr_with_sentiment(self): + from google.cloud.language.sentiment import Sentiment + + klass = self._get_target_class() + content = 'All the pretty horses.' + begin = -1 + score = 0.5 + magnitude = 0.5 + payload = { + 'text': { + 'content': content, + 'beginOffset': begin, + }, + 'sentiment': { + 'score': score, + 'magnitude': magnitude, + } + } + sentence = klass.from_api_repr(payload) + self.assertEqual(sentence.content, content) + self.assertEqual(sentence.begin, begin) + self.assertIsInstance(sentence.sentiment, Sentiment) + self.assertEqual(sentence.sentiment.score, score) + self.assertEqual(sentence.sentiment.magnitude, magnitude) diff --git a/language/unit_tests/test_syntax.py b/language/unit_tests/test_syntax.py index 369f9988720c..8c1f994da5ae 100644 --- a/language/unit_tests/test_syntax.py +++ b/language/unit_tests/test_syntax.py @@ -95,62 +95,3 @@ def test_from_api_repr(self): self.assertEqual(token.edge_index, edge_index) self.assertEqual(token.edge_label, edge_label) self.assertEqual(token.lemma, lemma) - - -class TestSentence(unittest.TestCase): - - @staticmethod - def _get_target_class(): - from google.cloud.language.syntax import Sentence - - return Sentence - - def _make_one(self, *args, **kw): - return self._get_target_class()(*args, **kw) - - def test_constructor(self): - content = "All the king's horses." - begin = 11 - sentence = self._make_one(content, begin) - self.assertEqual(sentence.content, content) - self.assertEqual(sentence.begin, begin) - - def test_from_api_repr(self): - klass = self._get_target_class() - content = 'All the pretty horses.' - begin = -1 - payload = { - 'text': { - 'content': content, - 'beginOffset': begin, - }, - } - sentence = klass.from_api_repr(payload) - self.assertEqual(sentence.content, content) - self.assertEqual(sentence.begin, begin) - self.assertEqual(sentence.sentiment, None) - - def test_from_api_repr_with_sentiment(self): - from google.cloud.language.sentiment import Sentiment - - klass = self._get_target_class() - content = 'All the pretty horses.' - begin = -1 - score = 0.5 - magnitude = 0.5 - payload = { - 'text': { - 'content': content, - 'beginOffset': begin, - }, - 'sentiment': { - 'score': score, - 'magnitude': magnitude, - } - } - sentence = klass.from_api_repr(payload) - self.assertEqual(sentence.content, content) - self.assertEqual(sentence.begin, begin) - self.assertIsInstance(sentence.sentiment, Sentiment) - self.assertEqual(sentence.sentiment.score, score) - self.assertEqual(sentence.sentiment.magnitude, magnitude) diff --git a/system_tests/language.py b/system_tests/language.py index d978cda8e13a..f3869fbe8fb9 100644 --- a/system_tests/language.py +++ b/system_tests/language.py @@ -100,8 +100,9 @@ def _check_analyze_entities_result(self, entities): def test_analyze_entities(self): document = Config.CLIENT.document_from_text(self.TEXT_CONTENT) - entities = document.analyze_entities() - self._check_analyze_entities_result(entities) + response = document.analyze_entities() + self.assertEqual(response.language, 'en') + self._check_analyze_entities_result(response.entities) def test_analyze_entities_from_blob(self): # Upload the text to a blob. @@ -114,13 +115,13 @@ def test_analyze_entities_from_blob(self): # Create a document referencing that blob. gcs_url = 'gs://%s/%s' % (bucket_name, blob_name) document = Config.CLIENT.document_from_url(gcs_url) - entities = document.analyze_entities() + entities = document.analyze_entities().entities self._check_analyze_entities_result(entities) def test_analyze_sentiment(self): positive_msg = 'Jogging is fun' document = Config.CLIENT.document_from_text(positive_msg) - sentiment = document.analyze_sentiment() + sentiment = document.analyze_sentiment().sentiment self.assertEqual(sentiment.score, 0.5) self.assertTrue(0.0 < sentiment.magnitude < 1.5) @@ -147,5 +148,5 @@ def _check_analyze_syntax_result(self, tokens): def test_analyze_syntax(self): positive_msg = 'Jogging is fun' document = Config.CLIENT.document_from_text(positive_msg) - tokens = document.analyze_syntax() + tokens = document.analyze_syntax().tokens self._check_analyze_syntax_result(tokens)