# -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals
import json
from io import IOBase
from wolframclient.utils import six
from wolframclient.utils.api import oauth, urllib
__all__ = [
'SecuredAuthenticationKey', 'UserIDPassword', 'OAuthSessionBase',
'OAuthAsyncSessionBase'
]
[docs]class SecuredAuthenticationKey(object):
''' Represents a Secured Authentication Key generated using the Wolfram Language
function `GenerateSecuredAuthenticationKey[]`
It is used as an input when authenticating a cloud session.
'''
is_xauth = False
def __init__(self, consumer_key, consumer_secret):
self.consumer_key = consumer_key
self.consumer_secret = consumer_secret
[docs]class UserIDPassword(object):
''' Represents user credentials used to login to a cloud.
It is used as an input when authenticating a cloud session.
'''
is_xauth = True
def __init__(self, user, password):
self.user = user
self.password = password
[docs]class OAuthSessionBase(object):
""" A family of classes dealing with authentication with OAuth method."""
DEFAULT_CONTENT_TYPE = {
'Content-Type': 'application/x-www-form-urlencoded',
'User-Agent': 'WolframClientForPython/1.0'
}
def __init__(self,
server,
consumer_key,
consumer_secret,
signature_method=None,
client_class=oauth.Client):
self.consumer_key = consumer_key
self.consumer_secret = consumer_secret
self.signature_method = signature_method or oauth.SIGNATURE_HMAC
self.client_class = client_class
self._client = None
self._oauth_token = None
self._oauth_token_secret = None
self.server = server
[docs] def authenticate(self):
""" Authenticate with a given server using the user credentials"""
raise NotImplementedError
[docs] def signed_request(self, uri, headers={}, data=None, method='POST'):
""" Sign a given request and issue it."""
raise NotImplementedError
[docs] def authorized(self):
"""Return a reasonably accurate state of the authentication status."""
return self._client is not None and bool(
self._client.client_secret) and bool(
self._client.resource_owner_key) and bool(
self._client.resource_owner_secret)
def _update_client(self):
self._client = self.client_class(
self.consumer_key,
client_secret=self.consumer_secret,
resource_owner_key=self._oauth_token,
resource_owner_secret=self._oauth_token_secret,
signature_type=oauth.SIGNATURE_TYPE_AUTH_HEADER,
realm=self.server.cloudbase,
encoding='iso-8859-1')
def _update_token_from_request_body(self, body):
try:
token = json.loads(body)
self._oauth_token = token['oauth_token']
self._oauth_token_secret = token['oauth_token_secret']
except:
token = urllib.parse_qs(body)
self._oauth_token = token[b'oauth_token'][0]
self._oauth_token_secret = token[b'oauth_token_secret'][0]
[docs]class OAuthAsyncSessionBase(OAuthSessionBase):
[docs] async def authenticate(self):
""" Asynchronous OAuth authentication class dealing with various tokens and signing requests. """
raise NotImplementedError
[docs] async def signed_request(self, uri, headers={}, data=None, method='POST'):
""" Sign a given request and issue it asynchronously."""
raise NotImplementedError
class WolframAPICallBase(object):
"""Perform an API call to a given target.
The API call is actually performed when :func:`~wolframclient.evaluation.WolframAPICall.perform`
is called.
Parameters can be added using one of the various functions that this class exposes. They
can be of many types including: string, files, WL serializable python objects, binary data with arbitrary
content-type (e.g: *image/png*).
"""
def __init__(self, target, api, permission_key=None):
self.target = target
self.api = api
self.parameters = {}
self.files = {}
self.permission_key = permission_key
def set_parameter(self, name, value):
"""Add a new API input parameter from a serializable python object."""
self.parameters[name] = value
return self
def add_file_parameter(self, name, fp, filename=None, content_type=None):
"""Add a new API input parameter from a file pointer `fp`"""
if content_type is None:
self.files[name] = fp
else:
fname = filename or 'tmp_%s' % name
self.files[name] = (fname, fp, content_type)
return self
def add_binary_parameter(self,
name,
data,
filename=None,
content_type='application/octet-stream'):
"""Add a new API input parameter as a blob of binary data."""
if isinstance(data, IOBase):
return self.add_file_parameter(
name, data, filename=filename, content_type=content_type)
if isinstance(data, six.binary_type):
fname = filename or 'tmp_%s' % name
self.files[name] = (fname, data, content_type)
return self
else:
raise TypeError('Input data must be bytes or IOBase.')
def add_image_data_parameter(self,
name,
image_data,
filename=None,
content_type='image/png'):
"""Add a new API image input parameter from binary data.
If the data in `image_data` does not represent an image in the `PNG` format, the optional parameter
`content_type` must be set accordingly to the appropriate content type, e.g. *image/jpeg*, *image/gif*, etc.
"""
return self.add_binary_parameter(name, image_data, filename,
content_type)
def perform(self, **kwargs):
"""Make the API call and return the result."""
raise NotImplementedError
def perform_future(self, **kwargs):
"""Make the API call asynchronously and return a future object."""
raise NotImplementedError
def __repr__(self):
return '<%s api=%s, parameters=%s>' % (
self.__class__.__name__, self.api,
set().union(self.parameters.keys(), self.files.keys()) or None)
def __str__(self):
return repr(self)