SmartFile API Client (Python).

Overview

SmartFile

A SmartFile Open Source project. Read more about how SmartFile uses and contributes to Open Source software.

Travis CI Status Code Coverage Latest PyPI version

Summary

This library includes two API clients. Each one represents one of the supported authentication methods. BasicClient is used for HTTP Basic authentication, using an API key and password. OAuthClient is used for OAuth (version 1) authentication, using tokens, which will require user interaction to complete authentication with the API.

Both clients provide a thin wrapper around an HTTP library, taking care of some of the mundane details for you. The intended use of this library is to refer to the API documentation to discover the API endpoint you wish to call, then use the client library to invoke this call.

SmartFile API information is available at the SmartFile developer site.

Installation

You can install via pip.

$ pip install smartfile

Or via source code / GitHub.

$ git clone https://github.com/smartfile/client-python.git smartfile
$ cd smartfile
$ python setup.py install

More information is available at GitHub and PyPI.

Usage

Choose between Basic and OAuth authentication methods, then continue to use the SmartFile API.

Some of the details this library takes care of are:

  • Encoding and decoding of parameters and return values. You deal with Python types only.
  • URLs, using the API version, endpoint, and object ID, the URL is created for you.
  • Authentication. Provide your API credentials to this library, it will take care of the details.

Basic Authentication

Three methods are supported for providing API credentials using basic authentication.

  1. Parameters when instantiating the client.

    >>> from smartfile import BasicClient
    >>> api = BasicClient('**********', '**********')
    >>> api.get('/ping')
  2. Environment variables.

    Export your credentials via your environment.

    $ export SMARTFILE_API_KEY=**********
    $ export SMARTFILE_API_PASSWORD=**********
    

    And then you can use the client without providing any credentials in your code.

    >>> from smartfile import BasicClient
    >>> # Credentials are read automatically from environment
    >>> api = BasicClient()
    >>> api.get('/ping')
  3. netrc file (not supported with OAuth).

    You can place the following into ~/.netrc:

    machine app.smartfile.com
      login **********
      password **********
    

    And then you can use the client without providing any credentials in your code.

    >>> from smartfile import BasicClient
    >>> # Credentials are read automatically from netrc
    >>> api = BasicClient()
    >>> api.get('/ping')

    You can override the default netrc file location, using the optional netrcfile kwarg.

    >>> from smartfile import BasicClient
    >>> # Credentials are read automatically from netrc
    >>> api = BasicClient(netrcfile='/etc/smartfile.keys')
    >>> api.get('/ping')

OAuth Authentication

Authentication using OAuth authentication is bit more complicated, as it involves tokens and secrets.

>>> from smartfile import OAuthClient
>>> api = OAuthClient('**********', '**********')
>>> # Be sure to only call each method once for each OAuth login
>>>
>>> # This is the first step with the client, which should be left alone
>>> api.get_request_token()
>>> # Redirect users to the following URL:
>>> print "In your browser, go to: " + api.get_authorization_url()
>>> # This example uses raw_input to get the verification from the console:
>>> client_verification = raw_input("What was the verification? :")
>>> api.get_access_token(None, client_verification)
>>> api.get('/ping')

Calling endpoints

Once you instantiate a client, you can use the get/put/post/delete methods to make the corresponding HTTP requests to the API. There is also a shortcut for using the GET method, which is to simply invoke the client.

>>> from smartfile import BasicClient
>>> api = BasicClient('**********', '**********')
>>> api.get('/ping')
>>> # The following is equivalent...
>>> api('/ping')

Some endpoints accept an ID, this might be a numeric value, a path, or name, depending on the object type. For example, a user's id is their unique username. For a file path, the id is it's full path.

>>> import pprint
>>> from smartfile import BasicClient
>>> api = BasicClient('**********', '**********')
>>> # For this endpoint, the id is '/'
>>> pprint.pprint(api.get('/path/info', '/'))
{u'acl': {u'list': True, u'read': True, u'remove': True, u'write': True},
 u'attributes': {},
 u'extension': u'',
 u'id': 7,
 u'isdir': True,
 u'isfile': False,
 u'items': 348,
 u'mime': u'application/x-directory',
 u'name': u'',
 u'owner': None,
 u'path': u'/',
 u'size': 220429838,
 u'tags': [],
 u'time': u'2013-02-23T22:49:30',
 u'url': u'http://localhost:8000/api/2/path/info/'}

File transfers

Uploading and downloading files is supported.

To upload a file:

>>> from smartfile import BasicClient
>>> api = BasicClient()
>>> file = open('test.txt', 'rb')
>>> api.upload('test.txt', file)

Downloading is automatic, if the 'Content-Type' header indicates content other than the expected JSON return value, then a file-like object is returned.

>>> from smartfile import BasicClient
>>> api = BasicClient()
>>> api.download('foobar.png')

Tasks

Operations are long-running jobs that are not executed within the time frame of an API call. For such operations, a task is created, and the API can be used to poll the status of the task.

Move files

>>> import logging
>>> from smartfile import BasicClient
>>>
>>> api = BasicClient()
>>>
>>> LOGGER = logging.getLogger(__name__)
>>> LOGGER.setLevel(logging.INFO)
>>>
>>> api.move('file.txt', '/newFolder')
>>>
>>> while True:
>>>     try:
>>>         s = api.get('/task', api['uuid'])
>>>         # Sleep to assure the user does not get rate limited
>>>         time.sleep(1)
>>>         if s['result']['status'] == 'SUCCESS':
>>>             break
>>>         elif s['result']['status'] == 'FAILURE':
>>>             LOGGER.info("Task failure: " + s['uuid'])
>>>     except Exception as e:
>>>         print e
>>>         break

Delete files

>>> from smartfile import BasicClient
>>> api = BasicClient()
>>> api.remove('foobar.png')

Running Tests

To run tests for the test.py file:

nosetests -v tests.py

To run tests for the test_smartfile.py file:

API_KEY='****' API_PASSWORD='****' nosetests test
Comments
  • Remove NETRC Usage

    Remove NETRC Usage

    It appears that this client allows for us to pass in an NETRC file. NETRC is deprecated. Please remove this.

    i.e. https://github.com/smartfile/client-python/blame/04ff2ce406d98f3a299ab956c476ba27feae8956/smartfile/init.py#L135

    opened by Ryanb58 6
  • Adds the download_to_path  keyword argument to the download method.

    Adds the download_to_path keyword argument to the download method.

    Edits smartfile/init.py

    • Adds a download_to_path keyword argument to explicitly specify a path to write the downloaded file contents , if not given the file will be written under the CWD + the file to download name.
    opened by chaps 4
  • README should be changed.

    README should be changed.

    Currently, the readme is of the form "restructured text". To keep things consistent across repositories we should update this to be a part of the markdown family instead.

    opened by Ryanb58 4
  • Fix CI testing

    Fix CI testing

    Previously, the tests weren't even being run. This is because a directory named test was added and the Makefile target test was not PHONY. Once the tests were being run, there was an incompatibility with the string encoding that needed to be resolved for tests to pass.

    opened by cabarnes 3
  • Upload Memory Error

    Upload Memory Error

    When trying to upload a file that has more bytes than a string can hold, I get an OverflowError.

    >>> import smartfile
    
    >>> 
    >>> 
    >>> 
    >>> api = smartfile.BasicClient('**********', '*****************')
    >>> file = open('Downloads/Win10_1511_2_English_x64.iso', 'rb')
    >>> api.upload('test.io', file)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "/usr/lib/python2.7/site-packages/smartfile/__init__.py", line 145, in upload
        return self.post('/path/data/', file=arg)
      File "/usr/lib/python2.7/site-packages/smartfile/__init__.py", line 130, in post
        return self._request('post', endpoint, id=id, data=kwargs)
      File "/usr/lib/python2.7/site-packages/smartfile/__init__.py", line 109, in _request
        return self._do_request(request, url, **kwargs)
      File "/usr/lib/python2.7/site-packages/smartfile/__init__.py", line 206, in _do_request
        return super(BasicClient, self)._do_request(*args, **kwargs)
      File "/usr/lib/python2.7/site-packages/smartfile/__init__.py", line 52, in _do_request
        response = request(url, stream=True, **kwargs)
      File "/usr/lib/python2.7/site-packages/requests/api.py", line 111, in post
        return request('post', url, data=data, json=json, **kwargs)
      File "/usr/lib/python2.7/site-packages/requests/api.py", line 57, in request
        return session.request(method=method, url=url, **kwargs)
      File "/usr/lib/python2.7/site-packages/requests/sessions.py", line 475, in request
        resp = self.send(prep, **send_kwargs)
      File "/usr/lib/python2.7/site-packages/requests/sessions.py", line 585, in send
        r = adapter.send(request, **kwargs)
      File "/usr/lib/python2.7/site-packages/requests/adapters.py", line 403, in send
        timeout=timeout
      File "/usr/lib/python2.7/site-packages/requests/packages/urllib3/connectionpool.py", line 578, in urlopen
        chunked=chunked)
      File "/usr/lib/python2.7/site-packages/requests/packages/urllib3/connectionpool.py", line 362, in _make_request
        conn.request(method, url, **httplib_request_kw)
      File "/usr/lib64/python2.7/httplib.py", line 1057, in request
        self._send_request(method, url, body, headers)
      File "/usr/lib64/python2.7/httplib.py", line 1097, in _send_request
        self.endheaders(body)
      File "/usr/lib64/python2.7/httplib.py", line 1053, in endheaders
        self._send_output(message_body)
      File "/usr/lib64/python2.7/httplib.py", line 897, in _send_output
        self.send(msg)
      File "/usr/lib64/python2.7/httplib.py", line 873, in send
        self.sock.sendall(data)
      File "/usr/lib64/python2.7/ssl.py", line 721, in sendall
        v = self.send(data[count:])
      File "/usr/lib64/python2.7/ssl.py", line 687, in send
        v = self._sslobj.write(data)
    OverflowError: string longer than 2147483647 bytes
    string longer than 2147483647 bytes
    
    

    I believe moving towards chunked uploading would be beneficial to looking past this problem.

    opened by Ryanb58 3
  • Return a response object instead of response.raw

    Return a response object instead of response.raw

    Instead of returning Response.raw, return the Response object, in case we want to do other advanced things with Python Requests, such as those here: http://www.python-requests.org/en/latest/api/#requests.Response

    I wanted to take advantage of Response.iter_content() so that I wouldn't have to load an entire file into memory before writing it to the disk. This wasn't possible before, as _do_request() was returning Response.raw.

    opened by travcunn 3
  • Remove name mangling which prevents subclassing OAuthClient.

    Remove name mangling which prevents subclassing OAuthClient.

    I tried to subclass OAuthClient for a project* but found that the name-mangled instance attributes __access and __client made just rewriting from scratch easier. I've only been working with your guys' library for a day so I apologize if there's a specific reason for the dunder prefix, but inheritance is close to broken in this case.

    * I wanted to get just the response object from _do_request so I could process HTTP requests based on their response code & body instead of catching exceptions thrown by OAuthClient

    opened by mattdeboard 3
  • Allow the user to control the download

    Allow the user to control the download

    Allows the caller of the download method to get the response object back to download the item as needed. This allows the caller to iteratively download in whatever chunk size is desired, to download to whatever location is desired, etc.

    opened by cabarnes 2
  • <class Client> has no function 'upload()'

    has no function 'upload()'

    When the library is installed via pip, it doesn't contain the definition of the upload() function in the Client class. This results in an error when upload() is called on a BasicClient object. It appears the source code I got in my venv/Lib/site-packages/smartfile/init.py is different from what is published on github.

    opened by Odame 2
  • Submit the latest update to PyPI

    Submit the latest update to PyPI

    When we get a chance, we need to add the changes made in https://github.com/smartfile/client-python/pull/7 to PyPI. This way people can just use pip install smartfile to import the latest SmartFile client SDK changes.

    opened by Ryanb58 2
  • Moving files

    Moving files

    Hello, According to the examples I should be able to go api.move('file.txt', '/screenShot') to move file.txt to screenShot.

    However, I am getting the error: Response 409: Error: b'{"detail": "Destination exists:/screenShot"}' The api.move method used to work for me. Now it has stopped. Does anyone know how to fix this?

    Thanks

    opened by LinusSkucas 1
  • a code example for a simple workflow

    a code example for a simple workflow

    I wonder if you could add an end to end code example that implements a simple workflow for temporary accounts

    • create user name x with random password y
    • create folder name x
    • give user x write access to folder x
    • create rule that files are deleted after successful download
    • create rule that user will be deactivated after x days
    opened by dirkpetersen 8
  • Attempting to download .json file parses file as JSON.

    Attempting to download .json file parses file as JSON.

    If you attempt to download a .json file, the library instead parses the JSON content of the file. This is because parsing is triggered by the Content-Type header, which is set to application/json according to the file type being downloaded.

    Downloading should disable the automatic parsing of JSON, or the library should return a response and allow the caller to parse JSON if that is desired.

    opened by btimby 0
  • download method: optional path to write file

    download method: optional path to write file

    When calling the download method , a file object is created with the same path as the requested file to download , paths like /Documents/somefile won't exist usually under the local filesystems where the library is being used .

    The default path to write the file contents could be the local CWD plus the name of the file being downloaded , and the method could accept a keyword argument in order to explicitly tell the desired path where to write the file contents.

    opened by chaps 2
  • Large plain/text files returned as gzip and library doesn't handle this case

    Large plain/text files returned as gzip and library doesn't handle this case

    python --version Python 3.6.1

    smartfile==2.19

    Example from documentation:

    >>> import shutil
    >>> from smartfile import BasicClient
    >>> api = BasicClient()
    >>> f = api.get('/path/data/', 'small.txt')
    >>> with open('small.txt', 'wb') as o:
    >>>     # f.getheaders().get('Content-Encoding') 
    >>>     shutil.copyfileobj(f, o)
    

    works fine with small files, but when file is large (I tested 100Mb text file) f.getheaders().get('Content-Encoding') returns gzip and instead of expected source text file gzip content is downloaded and saved to file.

    It can be handled with:

            if f.getheaders().get('Content-Encoding') == 'gzip':
                o.write(gzip.decompress(f.read()))
            else:
                shutil.copyfileobj(f, o)
    

    but ideally I think library should handle this case. Or, at least, docs have to be updated and mention this possible issue.

    opened by grunichev 0
Beyonic API Python official client library simplified examples using Flask, Django and Fast API.

Beyonic API Python official client library simplified examples using Flask, Django and Fast API.

Harun Mbaabu Mwenda 46 Sep 1, 2022
Python API Client for Twitter API v2

?? Python Client For Twitter API v2 ?? Why Twitter Stream ? Twitter-Stream.py a python API client for Twitter API v2 now supports FilteredStream, Samp

Twitivity 31 Nov 19, 2022
Dns-Client-Server - Dns Client Server For Python

Dns-client-server DNS Server: supporting all types of queries and replies. Shoul

Nishant Badgujar 1 Feb 15, 2022
Raphtory-client - The python client for the Raphtory project

Raphtory Client This is the python client for the Raphtory project Install via p

Raphtory 5 Apr 28, 2022
Drcom-pt-client - Drcom Pt version client with refresh timer

drcom-pt-client Drcom Pt version client with refresh timer Dr.com Pt版本客户端 可用于网页认

null 4 Nov 16, 2022
Official Python client for the MonkeyLearn API. Build and consume machine learning models for language processing from your Python apps.

MonkeyLearn API for Python Official Python client for the MonkeyLearn API. Build and run machine learning models for language processing from your Pyt

MonkeyLearn 157 Nov 22, 2022
🖥️ Python - P1 Monitor API Asynchronous Python Client

??️ Asynchronous Python client for the P1 Monitor

Klaas Schoute 9 Dec 12, 2022
Python API Client for Close

Close API A convenient Python wrapper for the Close API. API docs: http://developer.close.com Support: [email protected] Installation pip install clos

Close 56 Nov 30, 2022
Python client for CoinPayments API

pyCoinPayments - Python API client for CoinPayments Updates This library has now been converted to work with python3 This is an unofficial client for

James 27 Sep 21, 2022
DEPRECATED - Official Python Client for the Discogs API

⚠️ DEPRECATED This repository is no longer maintained. You can still use a REST client like Requests or other third-party Python library to access the

Discogs 483 Dec 31, 2022
The Foursquare API client for Python

foursquare Python client for the foursquare API. Philosophy: Map foursquare's endpoints one-to-one Clean, simple, Pythonic calls Only handle raw data,

Mike Lewis 400 Dec 19, 2022
Python Client for Instagram API

This project is not actively maintained. Proceed at your own risk! python-instagram A Python 2/3 client for the Instagram REST and Search APIs Install

Facebook Archive 2.9k Dec 30, 2022
A Python Client for News API

newsapi-python A Python client for the News API. License Provided under MIT License by Matt Lisivick. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRAN

Matt Lisivick 281 Dec 29, 2022
Python client for the Socrata Open Data API

sodapy sodapy is a python client for the Socrata Open Data API. Installation You can install with pip install sodapy. If you want to install from sour

Cristina 368 Dec 9, 2022
Python client for the Echo Nest API

Pyechonest Tap into The Echo Nest's Musical Brain for the best music search, information, recommendations and remix tools on the web. Pyechonest is an

The Echo Nest 655 Dec 29, 2022
A Python Tumblr API v2 Client

PyTumblr Installation Install via pip: $ pip install pytumblr Install from source: $ git clone https://github.com/tumblr/pytumblr.git $ cd pytumblr $

Tumblr 677 Dec 21, 2022
A super awesome Twitter API client for Python.

birdy birdy is a super awesome Twitter API client for Python in just a little under 400 LOC. TL;DR Features Future proof dynamic API with full REST an

Inueni 259 Dec 28, 2022
Cord Python API Client

Cord Python API Client The data programming platform for AI ?? Features Minimal low-level Python client that allows you to interact with Cord's API Su

Cord 52 Nov 25, 2022
Pure Python 3 MTProto API Telegram client library, for bots too!

Telethon ⭐️ Thanks everyone who has starred the project, it means a lot! Telethon is an asyncio Python 3 MTProto library to interact with Telegram's A

LonamiWebs 7.3k Jan 1, 2023