#!/usr/bin/env python
# encoding: utf-8
#
# The MIT License (MIT)
#
# Copyright (c) 2014-2016 CNRS
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
# AUTHORS
# Hervé BREDIN (http://herve.niderb.fr/)
# Johann POIGNANT
# Claude BARRAS
import tortilla
import requests
import os
import threading
import json
from base64 import b64encode, b64decode
from getpass import getpass
from sseclient import SSEClient
import warnings
import time
class CamomileBadRequest(Exception):
pass
class CamomileUnauthorized(Exception):
pass
class CamomileForbidden(Exception):
pass
class CamomileNotFound(Exception):
pass
class CamomileInternalError(Exception):
pass
class CamomileBadJSON(Exception):
pass
class CamomileErrorHandling(object):
"""Decorator for handling Camomile errors as exceptions
Handles HTTP status code and "keep alive" behavior.
Parameters
----------
resuscitate : boolean, optional
If `resuscitate` is True and user set `keep_alive` to True at login,
automatically try to relogging on connection or authentication errors.
Note that `rescucitate` must be set to False for login/logout methods
as setting it to rue would result in an infinite login loop...
"""
def __init__(self, resuscitate=True):
super(CamomileErrorHandling, self).__init__()
self.resuscitate = resuscitate
def __call__(self, func, *args, **kwargs):
def decorated_method(client, *args, **kwargs):
try:
return func(client, *args, **kwargs)
# (optionnally) resuscitate in case of connection error
except requests.exceptions.ConnectionError as e:
if self.resuscitate and client._keep_alive:
client._resuscitate(max_trials=-1)
return func(client, *args, **kwargs)
raise e
# handle Camomile errors
except requests.exceptions.HTTPError as e:
if not hasattr(e, 'response'):
raise e
try:
content = e.response.json()
except AttributeError as e:
raise CamomileBadJSON(e.response.content)
else:
pass
message = content.get('error', None)
if message is None:
raise e
status_code = e.response.status_code
if status_code == 400:
raise CamomileBadRequest(message)
if status_code == 401:
if self.resuscitate and client._keep_alive:
client._resuscitate(max_trials=-1)
return func(client, *args, **kwargs)
else:
raise CamomileUnauthorized(message)
if status_code == 403:
raise CamomileForbidden(message)
if status_code == 404:
raise CamomileNotFound(message)
if status_code == 500:
raise CamomileInternalError(message)
raise e
# keep name and docstring of the initial function
decorated_method.__name__ = func.__name__
decorated_method.__doc__ = func.__doc__
return decorated_method
class Camomile(object):
"""Client for Camomile REST API
Parameters
----------
url : str
Base URL of Camomile API.
username, password : str, optional
If provided, an attempt is made to log in.
delay : float, optional
If provided, make sure at least `delay` seconds pass between
each request to the Camomile API. Defaults to no delay.
Example
-------
>>> url = 'http://camomile.fr'
>>> client = Camomile(url)
>>> client.login(username='root', password='password')
>>> corpora = client.getCorpora(returns_id=True)
>>> corpus = corpora[0]
>>> layers = client.getLayers(corpus=corpus)
>>> media = client.getMedia(corpus=corpus)
>>> client.logout()
"""
ADMIN = 3
WRITE = 2
READ = 1
def __init__(self, url, username=None, password=None, keep_alive=False,
delay=0., debug=False):
super(Camomile, self).__init__()
# internally rely on tortilla generic API wrapper
# see http://github.com/redodo/tortilla
self._api = tortilla.wrap(url, format='json', delay=delay, debug=debug)
self._url = url;
self._listenerCallbacks = {}
self._thread = None
self._keep_alive = None
if username:
self.login(username, password, keep_alive=keep_alive)
def __enter__(self):
"""
Example
-------
>>> url = 'http://camomile.fr'
>>> with Camomile(url, username='root', password='password') as client:
>>> corpora = client.corpus()
"""
return self
def __exit__(self, type, value, traceback):
self.logout()
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# HELPER FUNCTIONS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def _user(self, id_user=None):
user = self._api.user
if id_user:
user = user(id_user)
return user
def _group(self, id_group=None):
group = self._api.group
if id_group:
group = group(id_group)
return group
def _corpus(self, id_corpus=None):
corpus = self._api.corpus
if id_corpus:
corpus = corpus(id_corpus)
return corpus
def _medium(self, id_medium=None):
medium = self._api.medium
if id_medium:
medium = medium(id_medium)
return medium
def _layer(self, id_layer=None):
layer = self._api.layer
if id_layer:
layer = layer(id_layer)
return layer
def _annotation(self, id_annotation=None):
annotation = self._api.annotation
if id_annotation:
annotation = annotation(id_annotation)
return annotation
def _queue(self, id_queue=None):
queue = self._api.queue
if id_queue:
queue = queue(id_queue)
return queue
def _id(self, result):
if isinstance(result, list):
return [r._id for r in result]
return result._id
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# AUTHENTICATION
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@CamomileErrorHandling(resuscitate=False)
def login(self, username, password=None, keep_alive=False):
"""Login
Parameters
----------
username: str
password : str, optional
keep_alive : boolean, optional
"""
if password is None:
password = getpass()
credential
评论0