Skip to content

Commit

Permalink
Merge pull request #18 from Clarifai/revert-17-fixlang
Browse files Browse the repository at this point in the history
Revert "refer to the server for languages"
  • Loading branch information
zeiler committed Oct 29, 2015
2 parents 9542afa + c6203a3 commit 5349d0e
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 86 deletions.
2 changes: 1 addition & 1 deletion clarifai/client/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
from __future__ import absolute_import

from .client import ClarifaiApi, ApiError, ApiThrottledError, ApiBadRequestError, ApiClientError
from .client import ClarifaiApi, ApiError, ApiThrottledError, ApiBadRequestError
74 changes: 55 additions & 19 deletions clarifai/client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ def iteritems(d):
def iteritems(d):
return d.iteritems()

#logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)


Expand All @@ -42,11 +43,11 @@ def __str__(self):
def __repr__(self):
return "Error: '%s'" % str(self.msg)


class ApiClientError(ApiError):
"""Thrown when client side validation fails"""
pass


class ApiThrottledError(Exception):
"""The usage limit throttle was hit. Client should wait for wait_seconds before retrying."""

Expand Down Expand Up @@ -76,14 +77,37 @@ class ClarifaiApi(object):
app_id: the client_id for an application you've created in your Clarifai account.
app_secret: the client_secret for the same application.
base_url: Base URL of the API endpoints.
model: Name of the recognition model to query. Defaults to None so that server side defaults
in your app settings are used.
model: Name of the recognition model to query. Defaults to None so that server side defaults
in your app settings are used.
wait_on_throttle: When the API returns a 429 throttled error, sleep for the amount of time
reported in the X-Throttle-Wait-Seconds HTTP response header.
language: set the default language using it's two letter (with options -XX variant) ISO 639-1
code to use for all requests. Defaults to None so that server side defaults in your app settings
are used.
"""
_SUPPORTED_LANGUAGES = {
'ar': 'Arabic',
'bn': 'Bengali',
'da': 'Danish',
'de': 'German',
'en': 'English',
'es': 'Spanish',
'fi': 'Finnish',
'fr': 'French',
'hi': 'Hindi',
'it': 'Italian',
'ja': 'Japanese',
'nl': 'Dutch',
'no': 'Norwegian',
'pa': 'Punjabi',
'pl': 'Polish',
'pt': 'Portuguese',
'ru': 'Russian',
'sv': 'Swedish',
'tr': 'Turkish',
'zh': 'Chinese (Simplified)',
'zh-TW': 'Chinese (Traditional)'
}

def __init__(self, app_id=None, app_secret=None, base_url='https://api.clarifai.com',
model=None, wait_on_throttle=True, language=None):
Expand All @@ -106,8 +130,7 @@ def __init__(self, app_id=None, app_secret=None, base_url='https://api.clarifai.
'multiop': "/".join([self._base_url, '%s/multiop/' % API_VERSION]),
'feedback': "/".join([self._base_url, '%s/feedback/' % API_VERSION]),
'token': "/".join([self._base_url, '%s/token/' % API_VERSION]),
'info': "/".join([self._base_url, '%s/info/' % API_VERSION]),
'languages': "/".join([self._base_url, '%s/info/languages' % API_VERSION])
'info': "/".join([self._base_url, '%s/info/' % API_VERSION])
}
self.access_token = None
self.api_info = None
Expand All @@ -121,15 +144,25 @@ def language(self):

@language.setter
def language(self, lang_code):
self._language = self._sanitize_param(lang_code, default=None)
self._language = self._parse_language(lang_code)

def get_languages(self):
response = self._parse_response(self._get_raw_response(
self._get_json_headers,
self._get_json_response,
self._url_for_op('languages'),
{}))
return response['languages']
def _parse_language(self, lang_code):
"""
Checks to see if the language code is supported and sanitizes it.
Args:
lang_code: language code
Returns:
lang_code: validated and sanitized language code
Raises:
ApiClientError: if the language code that was provided is not supported
"""
if lang_code is not None and lang_code not in self._SUPPORTED_LANGUAGES:
raise ApiClientError('Invalid language code {code}. Should be one of {supported}'
.format(code=lang_code, supported=self._SUPPORTED_LANGUAGES.items()))
return self._sanitize_param(lang_code, default=None)

def get_access_token(self, renew=False):
""" Get an access token using your app_id and app_secret.
Expand Down Expand Up @@ -176,6 +209,9 @@ def get_info(self):
self.api_info = response['results']
return self.api_info

def get_languages(self):
return self._SUPPORTED_LANGUAGES

def _url_for_op(self, ops):
if not isinstance(ops, list):
ops = [ops]
Expand All @@ -202,7 +238,7 @@ def tag(self, files, model=None, local_ids=None, meta=None, select_classes=None,
back in order).
meta: a string of any extra information to accompany the request. This has to be a string, so
if passing structured data, pass a json.dumps(meta) string.
select_classes: to select only a subset of all possible classes, enter a comma separated list
select_classes: to select only a subset of all possible classes, enter a comma separated list
of classes you want to predict. Ex: "dog,cat,tree,car,boat"
language: set the default language using it's two letter (with options -XX variant) ISO 639-1
code to use for all requests.
Expand All @@ -217,7 +253,7 @@ def tag(self, files, model=None, local_ids=None, meta=None, select_classes=None,
clarifai_api.tag([open('/path/to/local/image.jpeg'),
open('/path/to/local/image2.jpeg')])
"""
return self._multi_data_op(files, ['tag'], model=model, local_ids=local_ids, meta=meta,
return self._multi_data_op(files, ['tag'], model=model, local_ids=local_ids, meta=meta,
select_classes=select_classes,
language=language)

Expand Down Expand Up @@ -273,7 +309,7 @@ def tag_and_embed(self, files, model=None, local_ids=None, meta=None, select_cla
back in order).
meta: a string of any extra information to accompany the request. This has to be a string, so
if passing structured data, pass a json.dumps(meta) string.
select_classes: to select only a subset of all possible classes, enter a comma separated list
select_classes: to select only a subset of all possible classes, enter a comma separated list
of classes you want to predict. Ex: "dog,cat,tree,car,boat"
language: set the default language using it's two letter (with options -XX variant) ISO 639-1
code to use for all requests.
Expand Down Expand Up @@ -307,7 +343,7 @@ def tag_urls(self, urls, model=None, local_ids=None, meta=None, select_classes=N
back in order).
meta: a string of any extra information to accompany the request. This has to be a string, so
if passing structured data, pass a json.dumps(meta) string.
select_classes: to select only a subset of all possible classes, enter a comma separated list
select_classes: to select only a subset of all possible classes, enter a comma separated list
of classes you want to predict. Ex: "dog,cat,tree,car,boat"
language: set the default language using it's two letter (with options -XX variant) ISO 639-1
code to use for all requests.
Expand Down Expand Up @@ -370,7 +406,7 @@ def tag_and_embed_urls(self, urls, model=None, local_ids=None, meta=None, select
back in order).
meta: a string of any extra information to accompany the request. This has to be a string, so
if passing structured data, pass a json.dumps(meta) string.
select_classes: to select only a subset of all possible classes, enter a comma separated list
select_classes: to select only a subset of all possible classes, enter a comma separated list
of classes you want to predict. Ex: "dog,cat,tree,car,boat"
language: set the default language using it's two letter (with options -XX variant) ISO 639-1
code to use for all requests.
Expand Down Expand Up @@ -560,7 +596,7 @@ def _setup_multi_data(self, ops, num_cases, model=None, local_ids=None, meta=Non
elif self._model: # use the variable passed into __init__
data['model'] = self._model
if language: # use the variable passed into method
data['language'] = self._sanitize_param(language, default=None)
data['language'] = self._parse_language(language)
elif self.language: # use the variable passed into __init__
data['language'] = self.language
if local_ids:
Expand Down
85 changes: 19 additions & 66 deletions tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,19 @@
import os
import hashlib
import unittest
from clarifai.client import ClarifaiApi, ApiClientError
from clarifai.client import ClarifaiApi

class TestClarifaiApi(unittest.TestCase):
"""
test the Clarifai API Python client with all supported features
"""
image_url = 'http://clarifai-img.s3.amazonaws.com/test/toddler-flowers.jpeg'
video_url = 'http://techslides.com/demos/sample-videos/small.mp4'

def get_client(self, *args, **kwargs):
return ClarifaiApi()

def test_api_connection(self):
api = self.get_client()
api = ClarifaiApi()
self.assertTrue(api)

def test_get_info(self):
api = self.get_client()
api = ClarifaiApi()
response = api.get_info()
self.assertTrue(response.get('api_version'))
self.assertTrue(len(response) > 0)
Expand All @@ -34,20 +29,14 @@ def test_tag_one_image(self):
""" tag one image, from url and disk """
# tag image from online URL
image_url = 'http://clarifai-img.s3.amazonaws.com/test/toddler-flowers.jpeg'
api = self.get_client()
api = ClarifaiApi()
response = api.tag_image_urls(image_url)
self.assertTrue(response)
result = response['results'][0]
self.assertTrue(result['url'] == image_url)

tag = result['result']['tag']
self.assertTrue('probs' in tag, 'probs not included in response')
self.assertTrue('classes' in tag, 'classes not included in response')
self.assertTrue(len(tag['probs']) == len(tag['classes']), 'lengths of tag fields did not match')
self.assertTrue(response['results'][0]['url'] == image_url)

# tag image from local fs
image_file = 'tests/data/toddler-flowers.jpeg'
api = self.get_client()
api = ClarifaiApi()
if os.path.exists(image_file):
with open(image_file, 'rb') as fb:
response = api.tag_images(fb)
Expand All @@ -60,15 +49,15 @@ def test_tag_images(self):
image_files = ['metro-north.jpg', 'octopus.jpg', 'tahoe.jpg', 'thai-market.jpg']
image_urls = [os.path.join(image_url_base, one_file) for one_file in image_files]

api = self.get_client()
api = ClarifaiApi()
response = api.tag_image_urls(image_urls)
self.assertTrue(response)

# tag images frmo local fs
image_dir = 'tests/data'
image_files = ['metro-north.jpg', 'octopus.jpg', 'tahoe.jpg', 'thai-market.jpg']

api = self.get_client()
api = ClarifaiApi()
if os.path.exists(image_dir):
image_files = [open(os.path.join(image_dir, one_file), 'rb') for one_file in image_files]
response = api.tag_images(image_files)
Expand All @@ -79,7 +68,7 @@ def test_tag_images(self):
def test_unicode_urls(self):
image_url = u'http://www.alvaronoboa.com/wp-content/uploads/2013/02/Álvaro-Noboa-y-Annabella-Azín-Votaciones-41-1024x682.jpg'

api = self.get_client()
api = ClarifaiApi()
response = api.tag_image_urls(image_url)
self.assertTrue(response)
self.assertTrue(response['results'][0]['url'] == image_url)
Expand All @@ -89,13 +78,13 @@ def test_tag_gif(self):
# source: http://media.giphy.com/media/fRZn2vraBGiA0/giphy.gif
image_url = 'http://media.giphy.com/media/fRZn2vraBGiA0/giphy.gif'

api = self.get_client()
api = ClarifaiApi()
response = api.tag_image_urls(image_url)
self.assertTrue(response)
self.assertTrue(response['results'][0]['url'] == image_url)

image_file = 'tests/data/water-ocean-turtle.gif'
api = self.get_client()
api = ClarifaiApi()
if os.path.exists(image_file):
with open(image_file, 'rb') as fb:
response = api.tag_images(fb)
Expand All @@ -105,30 +94,30 @@ def test_tag_one_video(self):
# video source: http://techslides.com/demos/sample-videos/small.mp4
video_url = 'http://techslides.com/demos/sample-videos/small.mp4'

api = self.get_client()
api = ClarifaiApi()
response = api.tag_image_urls(video_url)
self.assertTrue(response)
self.assertTrue(response['results'][0]['url'] == video_url)

def test_tag_one_video_from_localfs(self):
# video source: http://techslides.com/demos/sample-videos/small.mp4
video_file = 'tests/data/small.mp4'
api = self.get_client()
api = ClarifaiApi()
if os.path.exists(video_file):
with open(video_file, 'rb') as fb:
response = api.tag_images(fb)
self.assertTrue(response)

def test_embed_one_image(self):
image_url = 'http://clarifai-img.s3.amazonaws.com/test/toddler-flowers.jpeg'
api = self.get_client()
api = ClarifaiApi()
response = api.embed_image_urls(image_url)
self.assertTrue(response)
self.assertTrue(response['results'][0]['url'] == image_url)

def test_embed_one_image_from_localfs(self):
image_file = 'tests/data/toddler-flowers.jpeg'
api = self.get_client()
api = ClarifaiApi()
if os.path.exists(image_file):
with open(image_file, 'rb') as fb:
response = api.embed_images(fb)
Expand All @@ -139,15 +128,15 @@ def test_tag_n_embed_one_image(self):
image_files = ['metro-north.jpg', 'octopus.jpg', 'tahoe.jpg', 'thai-market.jpg']
image_urls = [os.path.join(image_url_base, one_file) for one_file in image_files]

api = self.get_client()
api = ClarifaiApi()
response = api.tag_and_embed_image_urls(image_urls)
self.assertTrue(response)

def test_tag_n_embed_from_localfs(self):
image_dir = 'tests/data'
image_files = ['metro-north.jpg', 'octopus.jpg', 'tahoe.jpg', 'thai-market.jpg']

api = self.get_client()
api = ClarifaiApi()
if os.path.exists(image_dir):
image_files = [open(os.path.join(image_dir, one_file), 'rb') for one_file in image_files]
response = api.tag_and_embed_images(image_files)
Expand All @@ -162,7 +151,7 @@ def test_send_feedback(self):
'http://clarifai-img.s3.amazonaws.com/test/metro-north.jpg', \
'http://clarifai-img.s3.amazonaws.com/test/octopus.jpg']

api = self.get_client()
api = ClarifaiApi()

response = api.feedback(urls=urls[0], add_tags='train')
self.assertTrue(response)
Expand All @@ -184,42 +173,6 @@ def test_send_feedback(self):
response = api.feedback(urls=urls, similar_docids=docids[:2], dissimilar_docids=docids[1:])
self.assertTrue(response)

def test_i18n(self):
with self.assertRaises(ApiClientError):
api = ClarifaiApi(language='aaa')

api = self.get_client()
request_body = api._setup_multi_data([], ['urls'], language='en')
self.assertEqual(request_body['language'], 'en', 'language field was not set')

languages = api.get_languages()
self.assertTrue(len(languages), 'did not return any languages')
self.assertTrue('en' in languages, 'english code not included in languages')

def test_concept_ids(self):
"""new models should return concept_ids"""
api = self.get_client()

api.set_model('general-v1.3')
response = api.tag_image_urls(self.image_url)
tag = response['results'][0]['result']['tag']
self.assertTrue('concept_ids' in tag, 'concept_ids not included in new model')
self.assertTrue(tag['concept_ids'][0].startswith('ai_'), 'concept id doesn\'t start with ai_')

response = api.tag_image_urls(self.video_url)
self.assertTrue(response['results'][0]['result']['tag']['concept_ids'][0][0].startswith('ai_'),
"video concept_ids didn't start wit ai_")


api.set_model('general-v1.1')
response = api.tag_image_urls(self.image_url)
tag = response['results'][0]['result']['tag']
self.assertTrue('concept_ids' not in tag, 'concept_ids included in old model')

response = api.tag_image_urls(self.video_url)
tag = response['results'][0]['result']['tag']
self.assertTrue('concept_ids' not in tag, 'concept_ids included in old model')


if __name__ == '__main__':
unittest.main()

0 comments on commit 5349d0e

Please sign in to comment.