Box SDK for Python

Overview

Box Python SDK

https://travis-ci.org/box/box-python-sdk.png?branch=master Documentation Status https://coveralls.io/repos/github/box/box-python-sdk/badge.svg?branch=master

Installing

pip install boxsdk

The current version of the SDK is v2.x — if you're looking for the code or documentation for v1.5.x, please see the 1.5 branch. Note that all new features and fixes will be made on the 2.x branch; you should consider upgrading from 1.5.x at your earliest convenience. See the changelog for a list of breaking changes and added features between the major versions.

Getting Started

To get started with the SDK, get a Developer Token from the Configuration page of your app in the Box Developer Console. You can use this token to make test calls for your own Box account.

The SDK provides an interactive DevelopmentClient that makes it easy to test out the SDK in a REPL. This client will automatically prompt for a new Developer Token when it requires one, and will log HTTP requests and responses to aid in debugging and understanding how the SDK makes API calls.

>>> from boxsdk import DevelopmentClient
>>> client = DevelopmentClient()
Enter developer token: <ENTER DEVELOPER TOKEN HERE>
>>> user = client.user().get()
GET https://api.box.com/2.0/users/me {'headers': {'Authorization': '---wXyZ',
            'User-Agent': 'box-python-sdk-2.0.0',
            'X-Box-UA': 'agent=box-python-sdk/2.0.0; env=python/3.6.5'},
'params': None}
"GET https://api.box.com/2.0/users/me" 200 454
{'Date': 'Thu, 01 Nov 2018 23:32:11 GMT', 'Content-Type': 'application/json', 'Transfer-Encoding': 'chunked', 'Connection': 'keep-alive', 'Strict-Transport-Security': 'max-age=31536000', 'Cache-Control': 'no-cache, no-store', 'Content-Encoding': 'gzip', 'Vary': 'Accept-Encoding', 'BOX-REQUEST-ID': '0b50luc09ahp56m2jmkla8mgmh2', 'Age': '0'}
{'address': '',
'avatar_url': 'https://cloud.app.box.com/api/avatar/large/123456789',
'created_at': '2012-06-07T11:14:50-07:00',
'id': '123456789',
'job_title': '',
'language': 'en',
'login': '[email protected]',
'max_upload_size': 16106127360,
'modified_at': '2018-10-30T17:01:27-07:00',
'name': 'Example User',
'phone': '',
'space_amount': 1000000000000000.0,
'space_used': 14330018065,
'status': 'active',
'timezone': 'America/Los_Angeles',
'type': 'user'}

>>> print('The current user ID is {0}'.format(user.id))
The current user ID is 123456789

Outside of a REPL, you can initialize a new Client with just the Developer Token to get started.

from boxsdk import OAuth2, Client

auth = OAuth2(
    client_id='YOUR_CLIENT_ID',
    client_secret='YOUR_CLIENT_SECRET',
    access_token='YOUR_DEVELOPER_TOKEN',
)
client = Client(auth)

user = client.user().get()
print('The current user ID is {0}'.format(user.id))

Authorization

The Box API uses OAuth2 for auth. The SDK makes it relatively painless to work with OAuth2 tokens.

Server-to-Server Auth with JWT

The Python SDK supports your JWT Authentication applications.

Authenticating with a JWT requires some extra dependencies. To get them, simply

pip install "boxsdk[jwt]"

Instead of instantiating your Client with an instance of OAuth2, instead use an instance of JWTAuth.

from boxsdk import JWTAuth

auth = JWTAuth(
    client_id='YOUR_CLIENT_ID',
    client_secret='YOUR_CLIENT_SECRET',
    enterprise_id='YOUR_ENTERPRISE_ID',
    jwt_key_id='YOUR_JWT_KEY_ID',
    rsa_private_key_file_sys_path='CERT.PEM',
    rsa_private_key_passphrase='PASSPHRASE',
    store_tokens=your_store_tokens_callback_method,
)

access_token = auth.authenticate_instance()

from boxsdk import Client

client = Client(auth)

This client is able to create application users:

ned_stark_user = client.create_user('Ned Stark')

These users can then be authenticated:

ned_auth = JWTAuth(
    client_id='YOUR_CLIENT_ID',
    client_secret='YOUR_CLIENT_SECRET',
    user=ned_stark_user,
    jwt_key_id='YOUR_JWT_KEY_ID',
    rsa_private_key_file_sys_path='CERT.PEM',
    rsa_private_key_passphrase='PASSPHRASE',
    store_tokens=your_store_tokens_callback_method,
)
ned_auth.authenticate_user()
ned_client = Client(ned_auth)

Requests made with ned_client (or objects returned from ned_client's methods) will be performed on behalf of the newly created app user.

Traditional 3-legged OAuth2

Get the Authorization URL

from boxsdk import OAuth2

oauth = OAuth2(
    client_id='YOUR_CLIENT_ID',
    client_secret='YOUR_CLIENT_SECRET',
    store_tokens=your_store_tokens_callback_method,
)

auth_url, csrf_token = oauth.get_authorization_url('http://YOUR_REDIRECT_URL')

store_tokens is a callback used to store the access token and refresh token. You might want to define something like this:

def store_tokens(access_token, refresh_token):
    # store the tokens at secure storage (e.g. Keychain)

The SDK will keep the tokens in memory for the duration of the Python script run, so you don't always need to pass store_tokens.

Authenticate (Get Access/Refresh Tokens)

If you navigate the user to the auth_url, the user will eventually get redirected to http://YOUR_REDIRECT_URL?code=YOUR_AUTH_CODE. After getting the code, you will be able to use the code to exchange for an access token and refresh token.

The SDK handles all the work for you; all you need to do is run:

# Make sure that the csrf token you get from the `state` parameter
# in the final redirect URI is the same token you get from the
# get_authorization_url method.
assert 'THE_CSRF_TOKEN_YOU_GOT' == csrf_token
access_token, refresh_token = oauth.authenticate('YOUR_AUTH_CODE')

Create an Authenticated Client

from boxsdk import Client

client = Client(oauth)

And that's it! You can start using the client to do all kinds of cool stuff and the SDK will handle the token refresh for you automatically.

Instantiate a Client Given an Access and a Refresh Token

Alternatively, you can instantiate an OAuth2 object with the access token and refresh token. Once you have an oauth object you can pass that into the Client object to instantiate a client and begin making calls.

from boxsdk import Client, OAuth2

oauth = OAuth2(
    client_id='YOUR_CLIENT_ID',
    client_secret='YOUR_CLIENT_SECRET',
    access_token='ACCESS_TOKEN',
    refresh_token='REFRESH_TOKEN',
)

client = Client(oauth)
user = client.user().get()

This will retrieve the current user! From here you can use the client you created to start making calls.

Other Auth Options

For advanced uses of the SDK, three additional auth classes are provided:

  • CooperativelyManagedOAuth2: Allows multiple auth instances to share tokens.
  • RemoteOAuth2: Allows use of the SDK on clients without access to your application's client secret. Instead, you provide a retrieve_access_token callback. That callback should perform the token refresh, perhaps on your server that does have access to the client secret.
  • RedisManagedOAuth2: Stores access and refresh tokens in Redis. This allows multiple processes (possibly spanning multiple machines) to share access tokens while synchronizing token refresh. This could be useful for a multiprocess web server, for example.

Usage Documentation

Full documentation of the available functionality with example code is available in the SDK documentation pages, and there is also method-level documentation available on ReadTheDocs.

Making API Calls Manually

The Box API is continually evolving. As such, there are API endpoints available that are not specifically supported by the SDK. You can still use these endpoints by using the make_request method of the Client.

# https://developer.box.com/en/reference/get-metadata-templates-id/
# Returns a Python dictionary containing the result of the API request
json_response = client.make_request(
    'GET',
    client.get_url('metadata_templates', 'enterprise', 'customer', 'schema'),
).json()

make_request() takes two parameters:

  • method -an HTTP verb like GET or POST
  • url - the URL of the requested API endpoint

The Client class and Box objects have a get_url method. Pass it an endpoint to get the correct URL for use with that object and endpoint.

For API calls which require a body, make_request() accepts **kwargs after method and url.

# https://developer.box.com/en/reference/put-terms-of-service-user-statuses-id/
# Updates a user's ToS status

# JSONify the body
body = json.dumps({"is_accepted":true})

# Pass body as "data" argument
client.make_request(method, url, data = body)

Other Client Options

Logging Client

For more insight into the network calls the SDK is making, you can use the LoggingClient class. This class logs information about network requests and responses made to the Box API.

>>> from boxsdk import LoggingClient
>>> client = LoggingClient()
>>> client.user().get()
GET https://api.box.com/2.0/users/me {'headers': {u'Authorization': u'Bearer ---------------------------kBjp',
             u'User-Agent': u'box-python-sdk-1.5.0'},
 'params': None}
{"type":"user","id":"..","name":"Jeffrey Meadows","login":"..",..}
<boxsdk.object.user.User at 0x10615b8d0>

For more control over how the information is logged, use the LoggingNetwork class directly.

from boxsdk import Client
from boxsdk.network.logging_network import LoggingNetwork

# Use a custom logger
client = Client(oauth, network_layer=LoggingNetwork(logger))

Developer Token Client

The Box Developer Console allows for the creation of short-lived developer tokens. The SDK makes it easy to use these tokens. Use the get_new_token_callback parameter to control how the client will get new developer tokens as needed. The default is to prompt standard input for a token.

Development Client

For exploring the Box API, or to quickly get going using the SDK, the DevelopmentClient class combines the LoggingClient with the DeveloperTokenClient.

Customization

Custom Subclasses

Custom object subclasses can be defined:

from boxsdk import Client
from boxsdk import Folder

class MyFolderSubclass(Folder):
    pass

client = Client(oauth)
client.translator.register('folder', MyFolderSubclass)
folder = client.folder('0')

>>> print folder
>>> <Box MyFolderSubclass - 0>

If an object subclass is registered in this way, instances of this subclass will be returned from all SDK methods that previously returned an instance of the parent. See BaseAPIJSONObjectMeta and Translator to see how the SDK performs dynamic lookups to determine return types.

Contributing

See CONTRIBUTING.rst.

Developer Setup

Create a virtual environment and install packages -

mkvirtualenv boxsdk
pip install -r requirements-dev.txt

Testing

Run all tests using -

tox

The tox tests include code style checks via pep8 and pylint.

The tox tests are configured to run on Python 2.7, 3.4, 3.5, 3.6, 3.7, and PyPy (our CI is configured to run PyPy tests on PyPy 4.0).

Questions, Bugs, and Feature Requests?

Need to contact us directly? Browse the issues tickets! Or, if that doesn't work, file a new one and we will get back to you. If you have general questions about the Box API, you can post to the Box Developer Forum.

Copyright and License

Copyright 2019 Box, Inc. All rights reserved.

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.
Comments
  • Authentication Issue

    Authentication Issue

    • [ x ] I have checked that the SDK documentation doesn't solve my issue.
    • [ x ] I have checked that the API documentation doesn't solve my issue.
    • [ x ] I have searched the Box Developer Forums and reached out to Box Support directly and my issue hasn't yet been resolved.
    • [ x ] I have searched Issues on the GitHub and my exact issue isn't already reported.

    Description of the Issue

    I've been having issues with Box authentication through Python since I started using it. I am trying to use the Box Python SDK to generate permanent download links for thousands of files. I have figured out how to generate all of the links, but unfortunately when I first generated them they were not permanent. I now need to go and update all of the links, but the first time I did this, due to all of my authentication issues, I put in a developer token each hour for many many hours (weeks of time). I don't want to have to do this ever again so I am hoping to solve the authentication issue.

    I have tried all of the different authentication methods recommended in the documentation. However, the only method that is successful for me is using the developer token. Here are some of the problems I ran into using various authentication methods:

    • using JWT: 'Access denied - insufficient permission' error
    • using JWT with user access token: I am not sure how to find the jwt_key_id, rsa_private_key_file_sys_path, or the rsa_private_key_passphrase. I am not sure if those are generated within the box developer or if I have to find them elsewhere? Particularly, the example for the rsa_private_key_file_sys_path has "CERT.PEM" as the example file but I have not had success generating such a file with the box developer. I thought that maybe this was the .json file that is generated from creating a public/private key-pair, but it doesn't seem to be the right type of file. I have also tried the following lines of code but I end up with an "'User' object has no attribute ‘authenticate_user'" attribute error, which I think can only be fixed by finding the above jwt_key_id, rsa_private_key_file_sys_path, and the rsa_private_key_passphrase:
    auth = JWTAuth.from_settings_file('config.json’) #generated from the box developer website
    client = Client(auth)
    user = client.user(user_id='#######’) #my user ID
    user.authenticate_user()
    user_client = client.as_user(user)
    
    • using CCGAuth: "The "box_subject_type" value is unauthorized for this client_id" error
    • using OAuth2.0 Auth: "Message: No "refresh_token" parameter found" error and I am unsure where to find the refresh token on the Box Developer website for the app.

    I’m sorry if this is generally straight forward for other developers to figure out, but I am just a grad student who isn’t really familiar with this type of developer programming and I find the API for all of the Box ecosystem pretty opaque.

    I should also note that I have tried all of the above authentication methods with both the initial Box CLI app and another trial app that I generated which allows me to create the downloadable .json files with public/private key pairs.

    Steps to Reproduce

    All of the lines of code I tried for authorization I directly copied from the SDK auth wiki page and inserted my own user ID, .json file, etc., generated using the Box Developer. This leads me to believe that this might be an issue with setting up the Box Developer side of things--unless that code only works in very specific cases, but if so, then more detailed instructions should be added to the wiki for how to set that up properly so that the sample code will work.

    Expected Behavior

    Successful authentication that did not require a development token to be put in each hour.

    Versions Used

    Python SDK: 3.3.0 Python: 3.6.13

    question 
    opened by ericasaw 36
  • Requests done with JWT authentication keep returning a 404

    Requests done with JWT authentication keep returning a 404

    Hello,

    I have been struggling for some days with this issue. At the beginning I thought I was doing something wrong but I think there is something else.

    I followed the regular documentation for JWT:

    from boxsdk import JWTAuth
    from boxsdk import Client
    
    auth = JWTAuth(
        client_id='clientID',
        client_secret='clientSecret',
        enterprise_id='ent_ID',
        jwt_key_id='jwtKey',
        rsa_private_key_file_sys_path='/some/path/.ssh/box_private_key.pem',
        rsa_private_key_passphrase='keyPassphrase',
    )
    
    access_token = auth.authenticate_instance()
    
    client = Client(auth)
    
    print client.file(file_id='226905808148').get()['name']
    

    Now when running this I get the following error:

    boxsdk.exception.BoxAPIException: 
    Message: Not Found
    Status: 404
    Code: not_found
    Request id: 37750090159cc237ec9612
    Headers: {'Content-Length': '244', 'Content-Encoding': 'gzip', 'Age': '0', 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains', 'Vary': 'Accept-Encoding', 'Server': 'Server', 'Connection': 'keep-alive', 'Cache-Control': 'no-cache, no-store', 'Date': 'Wed, 27 Sep 2017 22:17:34 GMT', 'Content-Type': 'application/json'}
    URL: https://api.box.com/2.0/files/226905808148
    Method: GET
    Context info: {u'errors': [{u'reason': u'invalid_parameter', u'message': u"Invalid value 'f_226905808148'. 'item' with value 'f_226905808148' not found", u'name': u'item'}]}
    

    I tried different things including a "hack" which was using the access token to validate an http request:

    from boxsdk import JWTAuth
    import requests
    
    auth = JWTAuth(
        client_id='clientID',
        client_secret='clientSecret',
        enterprise_id='ent_ID',
        jwt_key_id='jwtKey',
        rsa_private_key_file_sys_path='/some/path/.ssh/box_private_key.pem',
        rsa_private_key_passphrase='keyPassphrase',
    )
    
    access_token = auth.authenticate_instance()
    
    headers = {
        'Authorization': 'Bearer ' + access_token,
    }
    
    r = requests.get('https://api.box.com/2.0/files/226905808148', headers=headers)
    
    print r.text
    

    And I got the following error, that matches the previous error:

    {
    	"type": "error",
    	"status": 404,
    	"code": "not_found",
    	"context_info": {
    		"errors": [{
    			"reason": "invalid_parameter",
    			"name": "item",
    			"message": "Invalid value 'f_226905808148'. 'item' with value 'f_226905808148' not found"
    		}]
    	},
    	"help_url": "http:\/\/developers.box.com\/docs\/#errors",
    	"message": "Not Found",
    	"request_id": "4462211659cc2829437c9"
    }
    

    I decided to test the same http method but this time with a token generated from the dev console:

    console_token = 'some_dev_console_generated_token'
    
    headers = {
        'Authorization': 'Bearer ' + console_token,
    }
    
    r = requests.get('https://api.box.com/2.0/files/226905808148', headers=headers)
    
    print r.text
    

    And this seems to be fine:

    {
    	"type": "file",
    	"id": "226905808148",
    	"file_version": {
    		"type": "file_version",
    		"id": "239594515476",
    		"sha1": "21c41745877c5ad2a44accec48bdb7cf776f3331"
    	},
    	"sequence_id": "0",
    	"etag": "0",
    	"sha1": "21c41745877c5ad2a44accec48bdb7cf776f3331",
    	"name": "the_raven.txt",
    	"description": "",
    	"size": 6877,
    	"path_collection": {
    		"total_count": 3,
    		"entries": [{
    			"type": "folder",
    			"id": "0",
    			"sequence_id": null,
    			"etag": null,
    			"name": "All Files"
    		}, {
    			"type": "folder",
    			"id": "31850680225",
    			"sequence_id": "0",
    			"etag": "0",
    			"name": "ITCOE"
    		}, {
    			"type": "folder",
    			"id": "31850693387",
    			"sequence_id": "0",
    			"etag": "0",
    			"name": "software"
    		}]
    	},
    	"created_at": "2017-09-20T19:37:52-07:00",
    	"modified_at": "2017-09-20T19:37:52-07:00",
    	"trashed_at": null,
    	"purged_at": null,
    	"content_created_at": "2017-05-25T20:29:27-07:00",
    	"content_modified_at": "2017-05-25T20:29:27-07:00",
    	"created_by": {
    		"type": "user",
    		"id": "242123783",
    		"name": "Marvin Solano",
    		"login": "[email protected]"
    	},
    	"modified_by": {
    		"type": "user",
    		"id": "242123783",
    		"name": "Marvin Solano",
    		"login": "[email protected]"
    	},
    	"owned_by": {
    		"type": "user",
    		"id": "242123783",
    		"name": "Marvin Solano",
    		"login": "[email protected]"
    	},
    	"shared_link": null,
    	"parent": {
    		"type": "folder",
    		"id": "31850693387",
    		"sequence_id": "0",
    		"etag": "0",
    		"name": "software"
    	},
    	"item_status": "active"
    }
    

    I generated a test token (access_token = auth.authenticate_instance()) with the SDK to test it with Postman and I got the same response.

    Any ideas of what I might be doing wrong?

    Regards Marvin

    opened by msolano00 18
  • Box SDK get_shared_link_download_url throwing error

    Box SDK get_shared_link_download_url throwing error

    We are currently seeing this error:

    BoxAPIException: Message: Bad Request Status: 400 Code: bad_request Request id: 472788704576acf6cd4ec5 Headers: {'Content-Length': '227', 'Content-Encoding': 'gzip', 'Age': '0', 'Vary': 'Accept-Encoding', 'Server': 'ATS', 'Connection': 'keep-alive', 'Cache-Control': 'no-cache, no-store', 'Date': 'Wed, 22 Jun 2016 17:48:28 GMT', 'Content-Type': 'application/json'} URL: https://api.box.com/2.0/files/64281490105 Method: PUT

    Our flow is as such:

    unshared_at = datetime.now() + timedelta(days=1) file = self.client.file(file_id=file_id) return file.get_shared_link_download_url(access=u'open', unshared_at=unshared_at)

    This only started happening this morning. We're wondering what changed.

    opened by kthurimella 18
  • Python SDK Error

    Python SDK Error

    Submitting per Tatyana @ Box (Case 2182873):

    User [email protected] is getting

    error: Request "GET https://api.box.com/2.0/folders/0/items" failed with ConnectionError exception: ConnectionError(MaxRetryError("HTTPSConnectionPool(host='api.box.com', port=443): Max retries exceeded with url: /2.0/folders/0/items?offset=0 (Caused by NewConnectionError('<urllib3.connection.VerifiedHTTPSConnection object at 0x000001623BC88988>: Failed to establish a new connection: [Errno 11001] getaddrinfo failed'))"))

    when trying to view folder items. Dani is making a GET call to https://api.box.com/2.0/folders/0/items. He is authenticating as [email protected]. Dani is using the Python SDK, version 2.8.0. His app's details are below:

    When using postman, we are able to get the folder information on and off network.

    Last email from Tatyana:

    Thanks, Dani.

    When querying within a few minutes of that timestamp for your app's activity, these are the IP addresses that are being used:

    194.9.245.34
    104.129.196.179
    208.49.203.14
    3.230.237.143

    However, when I looked at any GET calls made from any of these IPs, I get no results.

    Since you can make the call successfully in Postman, I agree that this is definitely an issue with the SDK.

    bug 
    opened by WillBGE 17
  • 15min">

    "Connection reset by peer" errors occur after pauses >15min

    Observed

    1. Our application initializes a connection with the BoxSDK near the beginning of execution to fetch IDs and locate files.
    2. Our application then does intense processing that may take anywhere from a few minutes to upwards of half an hour, depending on data ingestion sizes.
    3. Our application then attempts to save new artifacts to Box, and immediately raises this Exception: WARNING:boxsdk.network.default_network:[31mRequest "GET https://api.box.com/2.0/folders/140796660496/items" failed with ConnectionError exception: ConnectionError(ProtocolError('Connection aborted.', ConnectionResetError(104, 'Connection reset by peer')))
    • This Exception is only observed if the gap in Step 2 is longer than 15 minutes. This behaviour has only been observed since August 12, 2022
    • We are using Python 3.8.5
    • We are using boxsdk==2.12.1 (attempted upgrading to 3.4.0 as well)

    Currently we are wrapping the exception with function that re-initializes the boxsdk.Client() object and retries. While this does work, we would like some clarity on why connections are forcibly terminated on Box's side and what the best practice should be here.

    dontstale 
    opened by exdevlin 16
  • Access token expiry on OAuth2 authentication for long running script with authentication

    Access token expiry on OAuth2 authentication for long running script with authentication

    Hi,

    1. Problem Statement. : Recursive function call to connect to Box API to get the box file metadata Access token and refresh tokens are refreshed at the expiry (1 hour) and written back to configuration file and the recent tokens are used continuously to authenticate

    2. Issue noticed : At the expiry of the tokens (60-80 minutes) Box API fails with the below error message and the script terminates abruptly .

    Client id — wfe8a4wdxfaflu6vugr4vtnu19wqhmdu API key — OAuth2.0 SDK version —BoxSDK (2.11.0)

    1. Below are the logs .

    Authentication Issue happening randomly even though the Refersh and Access Tokens are valid, Below is the error log . No handlers could be found for logger "boxsdk.network.default_network" Message: No "refresh_token" parameter found Status: 400 URL: https://api.box.com/oauth2/token Method: POST Headers: {'Transfer-Encoding': 'chunked', 'Set-Cookie': 'box_visitor_id=602b5dfae771f0.30863027; expires=Wed, 16-Feb-2022 05:54:02 GMT; Max-Age=31536000; path=/; domain=.box.com; secure, bv=OPS-44271; expires=Tue, 23-Feb-2021 05:54:02 GMT; Max-Age=604800; path=/; domain=.app.box.com; secure, cn=13; expires=Wed, 16-Feb-2022 05:54:02 GMT; Max-Age=31536000; path=/; domain=.app.box.com; secure, site_preference=desktop; path=/; domain=.box.com; secure', 'Strict-Transport-Security': 'max-age=31536000', 'Connection': 'keep-alive', 'Cache-Control': 'no-store', 'Date': 'Tue, 16 Feb 2021 05:54:03 GMT', 'Content-Type': 'application/json'} Message: No "refresh_token" parameter found Status: 400 URL: https://api.box.com/oauth2/token Method: POST Headers: {'Transfer-Encoding': 'chunked', 'Set-Cookie': 'box_visitor_id=602b5dfc21a093.04408389; expires=Wed, 16-Feb-2022 05:54:04 GMT; Max-Age=31536000; path=/; domain=.box.com; secure, bv=OPS-44271; expires=Tue, 23-Feb-2021 05:54:04 GMT; Max-Age=604800; path=/; domain=.app.box.com; secure, cn=91; expires=Wed, 16-Feb-2022 05:54:04 GMT; Max-Age=31536000; path=/; domain=.app.box.com; secure, site_preference=desktop; path=/; domain=.box.com; secure', 'Strict-Transport-Security': 'max-age=31536000', 'Connection': 'keep-alive', 'Cache-Control': 'no-store', 'Date': 'Tue, 16 Feb 2021 05:54:04 GMT', 'Content-Type': 'application/json'} Message: No "refresh_token" parameter found Status: 400 URL: https://api.box.com/oauth2/token Method: POST Headers: {'Transfer-Encoding': 'chunked', 'Set-Cookie': 'box_visitor_id=602b5dfd495a32.42643028; expires=Wed, 16-Feb-2022 05:54:05 GMT; Max-Age=31536000; path=/; domain=.box.com; secure, bv=OPS-44271; expires=Tue, 23-Feb-2021 05:54:05 GMT; Max-Age=604800; path=/; domain=.app.box.com; secure, cn=21; expires=Wed, 16-Feb-2022 05:54:05 GMT; Max-Age=31536000; path=/; domain=.app.box.com; secure, site_preference=desktop; path=/; domain=.box.com; secure', 'Strict-Transport-Security': 'max-age=31536000', 'Connection': 'keep-alive', 'Cache-Control': 'no-store', 'Date': 'Tue, 16 Feb 2021 05:54:05 GMT', 'Content-Type': 'application/json’}

    1. Upon debugging it is noticed that the tokens which are renewed and written back to configuration file is valid . This is verified by making a separate authentication check through CURL command . But the script terminates stating no refresh token found .
    stale 
    opened by anusham009 16
  • docs: Remove obsolete`LoggingNetwork` docs from `README`

    docs: Remove obsolete`LoggingNetwork` docs from `README`

    According to Change Log documentation, logging_network support was removed in version 2.0.0. README documentation was never updated to reflect this removal. This PR resolves that discrepancy between docs and changelog.

    opened by allanparsons 13
  • AttributeError: module 'boxsdk.object' has no attribute 'collaboration'

    AttributeError: module 'boxsdk.object' has no attribute 'collaboration'

    Our applications are working very well with boxsdk version 1.5.5. When we update boxsdk to version 2.0..0.0, our application can work at source. But if we freeze our sources into stand-alone executables with Pyinstaller, we got an error like below.

    Traceback (most recent call last): File "Box-Report\boxreport.py", line 9, in File "", line 971, in _find_and_load File "", line 955, in _find_and_load_unlocked File "", line 665, in load_unlocked File "c:\users\changqli\appdata\local\anaconda3\envs\pydev36\lib\site-packages\PyInstaller\loader\pyimod03_importers.py", line 627, in exec_module exec(bytecode, module.dict) File "site-packages\boxsdk_init.py", line 8, in AttributeError: module 'boxsdk.object' has no attribute 'collaboration' [4548] Failed to execute script boxreport

    We want to solve this issue, can we get any help? Our environments are windows 7, 64 bits and Pyinstaller version 3.4.

    needs-triage 
    opened by c-rei 13
  • Content bytes option

    Content bytes option

    Added the option to include bytes when requesting the contents of a file. This is a feature that is available in API calls, but not originally in the boxsdk for python.

    Example:

    bytes = [0,60]
    client.file(file_id=id).content(bytes=bytes)
    

    CLA signed

    opened by kd2718 12
  • What can I do to stop the logs information of a client from box-python-sdk?

    What can I do to stop the logs information of a client from box-python-sdk?

    Description of the Issue

    // Replace this text with a description of what problem you're having.
    // Please include as much detail as possible to help us troubleshoot!
    // If it isn't obvious, please include how the behavior you expect differs from what actually happened.
    // This is really important so we know how to start troubleshooting your issue.

    Versions Used

    Box Python SDK: // Replace with the version of the Python SDK you're using.
    Python: // Replace with the version of Python your application is running on.

    Steps to Reproduce

    // Please include detailed steps to reproduce the issue you're seeing, if possible.
    // If you don't have a reproducible error, please make sure that you give us as much detail
    // as you can about what your application was doing when the error occurred.
    // Good steps to reproduce the problem help speed up debugging for us and gets your issue resolved sooner!

    Error Message, Including Traceback

    // Replace with the full error output you're seeing, if applicable.
    // Please include the full stack trace to help us identify where the error is happening.

    opened by c-rei 11
  •  '_RSAPrivateKey' object has no attribute 'sign'

    '_RSAPrivateKey' object has no attribute 'sign'

    • [x ] I have checked that the SDK documentation doesn't solve my issue.
    • [x ] I have checked that the API documentation doesn't solve my issue.
    • [ x] I have searched the Box Developer Forums and my issue isn't already reported (or if it has been reported, I have attached a link to it, for reference).
    • [ x] I have searched Issues in this repo and my issue isn't already reported.

    Description of the Issue

    When trying to access the folder structure from a Linux server, it sometimes succeeds, and sometimes I get: File "/dist/shows/shared/lib/python2.7/site-packages/jwt/api_jws.py" in encode 114. signature = alg_obj.sign(signing_input, key)

    File "/dist/shows/shared/lib/python2.7/site-packages/jwt/algorithms.py" in sign 313. return key.sign(msg, padding.PKCS1v15(), self.hash_alg())

    Exception Type: AttributeError at /attachments/add/red/international/internationalizr/712768/ Exception Value: '_RSAPrivateKey' object has no attribute 'sign'

    This seems to happen more often when the browser request to the server is coming from a Mac Big Sur. This doesn't make sense, since it is still the Linux server issuing the Box call.

    Steps to Reproduce

    rootFolder = self.client.folder(folder_id='0') where self.client is the box client

    Versions Used

    Python SDK: 2.12.1 Python: 2.7.5

    bug 
    opened by peternye 10
  • OAuth2 refresh token issue

    OAuth2 refresh token issue

    Hello there,

    • [X] I have checked that the SDK documentation doesn't solve my issue.
    • [X] I have checked that the API documentation doesn't solve my issue.
    • [X] I have searched the Box Developer Forums and my issue isn't already reported (or if it has been reported, I have attached a link to it, for reference).
    • [X] I have searched Issues in this repo and my issue isn't already reported.

    Description of the Issue

    I am running into some issues related authentication using OAuth2. It appears my refresh token is not getting refreshed, like the store_tokens callback is doing nothing or the .refresh() method does not do anything.

    Steps to Reproduce

    Here is an example script, auth.py:

    #!/usr/bin/env python3
    # -*- coding: UTF-8 -*-
    
    # Python standard library
    from __future__ import print_function
    import configparser, os, sys 
    
    # 3rd party imports
    from boxsdk import Client, OAuth2
    
    
    def err(*message, **kwargs):
        """Prints any provided args to standard error.
        kwargs can be provided to modify print functions 
        behavior.
        @param message <any>:
            Values printed to standard error
        @params kwargs <print()>
            Key words to modify print function behavior
        """
        print(*message, file=sys.stderr, **kwargs)
    
    
    def fatal(*message, **kwargs):
        """Prints any provided args to standard error
        and exits with an exit code of 1.
        @param message <any>:
            Values printed to standard error
        @params kwargs <print()>
            Key words to modify print function behavior
        """
        err(*message, **kwargs)
        sys.exit(1)
    
    
    def parsed(config_file = None, required = [
            'client_id', 'client_secret', 
            'access_token', 'refresh_token'
            ]
        ):
        """Parses config file in TOML format. This file should contain
        keys for client_id, client_secret, access_token, refresh_token.
        The `auth` function below will update the values of access_token
        and refresh_token to keep the users token alive. This is needed 
        because the developer tokens have a short one hour life-span.
        @Example `config_file`:
        [secrets]
        client_id = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
        client_secret = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
        access_token = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
        refresh_token = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    
        @param config_file <str>:
            Path to config file, default location: ~/.config/bx/bx.toml 
        @return [client_id, client_secret, access_token, refresh_token]:
            Returns a list of <str> with authenication information:
                [0] client_id
                [1] client_secret
                [2] access_token
                [3] refresh_token
        """
        # Information to parse from config file
        secrets, missing = [], []
        if not config_file:
            # Set to default location
            # ~/.config/bx/bx.toml
            home = os.path.expanduser("~")
            # TODO: add ENV variable to
            # override this default PATH
            config_file = os.path.join(home, ".config", "bx", "bx.toml")  
    
        # Read and parse in config file 
        config = configparser.ConfigParser()
        config.read(config_file)
        # Get authentication information,
        # Collect missing required info
        # to pass to user later
        for k in required:
            try:
                v = config['secrets'][k]
                secrets.append(v)
            except KeyError as e:
                missing.append(k)
    
        if missing:
            # User is missing required 
            # Authentication information
            fatal(
                'Fatal: bx config {0} is missing these required fields:\n\t{1}'.format(
                    config_file,
                    missing
                )
            )
    
        return secrets
    
    
    def update(access_token, refresh_token, config_file=None):
        """Callback to update the authentication tokens. This function is 
        passed to the `boxsdk OAuth2` constructor to save new `access_token` 
        and `refresh_token`. The boxsdk will automatically refresh your tokens
        if they are less than 60 days old and they have not already been re-
        freshed. This callback ensures that when a token is refreshed, we can
        save it and use it later.
        """
        if not config_file:
            # Set to default location
            # ~/.config/bx/bx.toml
            home = os.path.expanduser("~")
            # TODO: add ENV variable to
            # override this default PATH
            config_file = os.path.join(home, ".config", "bx", "bx.toml")
        # Read and parse in config file 
        config = configparser.ConfigParser()
        config.read(config_file)
        # Save the new `access_token` 
        # and `refresh_token`
        config['secrets']['access_token'] = access_token
        config['secrets']['refresh_token'] = refresh_token
        with open(config_file, 'w') as ofh:
            # Method for writing is weird, but
            # this is what the docs say todo
            config.write(ofh)
    
    
    def authenticate(client_id, client_secret, access_token, refresh_token):
        """Authenticates a user with their client id, client secret, and tokens.
        By default, authentication information is stored in "~/.config/bx/bx.toml".
        Here is an example of bx.toml file:
        [secrets]
        client_id = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
        client_secret = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
        access_token = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
        refresh_token = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    
        NOTE: This operation needs to be performed prior to any Box API calls. A 
        Box developer token has a short life-span of only an hour. This function will
        automatically refresh a token, to extend its life, when called. A token that
        has an expiration date past 60 days CANNOT be refreshed. In this scenario, a 
        user will need to create a new token prior to running the tool. A new token 
        can be create by creating an new 0auth2 app here: http://developers.box.com/
        """
        auth = OAuth2(
            client_id=client_id,
            client_secret=client_secret,
            refresh_token=refresh_token,
            store_tokens = update
        )
    
        try:
            access_token, refresh_token = auth.refresh(None)
        except Exception as e:
            # User may not have refreshed 
            # their token in 60 or more days
            err(e)
            err("\nFatal: Authentication token has expired!")
            fatal(" - Create a new token at: https://developer.box.com/")
        
        return access_token, refresh_token
    
    
    
    if __name__ == '__main__':
        try:
            # Use user provided config
            test_file = sys.argv[1]
        except IndexError:
            # Test auth config file parser
            home = os.path.expanduser("~")
            test_file = os.path.join(home, ".config", "bx-dev", "bx.toml")
    
        client_id, client_secret, access_token, refresh_token = parsed(
            config_file = test_file
        )
    
        # Test token refresh 
        new_access_token, new_refresh_token = authenticate(
             client_id = client_id, 
             client_secret = client_secret,
             access_token = access_token, 
             refresh_token = refresh_token
        )
    
        # Manually update tokens
        update(
            access_token = new_access_token, 
            refresh_token = new_refresh_token, 
            config_file = test_file
        )
    
        # Test authentication
        auth = OAuth2(
            client_id = client_id,
            client_secret = client_secret,
            access_token = new_access_token,
            refresh_token = new_refresh_token,
            store_tokens = update
        )
    
        # Get user info
        client = Client(auth)
        user = client.user().get()
        print("The current user ID is {0}".format(user.id))
    

    Here is an example config file, config.toml:

    [secrets]
    client_id = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    client_secret = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    access_token = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    refresh_token = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    

    Here is example usage of the script:

    python3 auth.py config.toml
    

    Expected Behavior

    I was hoping the token would get refreshed and saved in the config file. I am not sure of the best way to get a refresh token. From http://developers.box.com/, I only see a Developer token option. I have been using an access_token and refresh_token that I generated for connecting Box to Rclone. With that being said, I got this script to work a few times by running Rclone and then copying the new refresh_token and access_token Rclone updated in its config file. That worked a few times, and then it randomly stopped working. The tokens provided to the python-sdk are the same tokens that Rclone is using. Rclone works fine with those tokens.

    Is there a way to refresh a developer token in a headless manner (working on a remote server)? Similar to how this OAuth2 refresh is supposed to behave? I just need a token to programmatically interact with Box, that I can refresh without a browser getting involved. It seems like Rclone has figured this out. If I checkout the config file it uses, it automatically gets updated when the token expires. Also, it is possible to create a token with a longer life span, say like a year or so. I will be using this token to programmatically interact with files I own on Box.

    Error Message, Including Stack Trace

    Here is the error:

    $ python3 auth.py config.toml
    "POST https://api.box.com/oauth2/token" 400 69
    {'Date': 'Wed, 14 Dec 2022 20:38:56 GMT', 'Content-Type': 'application/json', 'Transfer-Encoding': 'chunked', 'Strict-Transport-Security': 'max-age=31536000', 'Set-Cookie': 'box_visitor_id=639a3460a13270.50706908; expires=Thu, 14-Dec-2023 20:38:56 GMT; Max-Age=31536000; path=/; domain=.box.com; secure, bv=OPS-45763; expires=Wed, 21-Dec-2022 20:38:56 GMT; Max-Age=604800; path=/; domain=.app.box.com; secure, cn=9; expires=Thu, 14-Dec-2023 20:38:56 GMT; Max-Age=31536000; path=/; domain=.app.box.com; secure, site_preference=desktop; path=/; domain=.box.com; secure', 'Cache-Control': 'no-store', 'Via': '1.1 google', 'Alt-Svc': 'h3=":443"; ma=2592000,h3-29=":443"; ma=2592000,h3-Q050=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443"; ma=2592000; v="46,43"'}
    {'error': 'invalid_grant', 'error_description': 'Invalid refresh token'}
    
    
    Message: Invalid refresh token
    Status: 400
    URL: https://api.box.com/oauth2/token
    Method: POST
    Headers: {'Date': 'Wed, 14 Dec 2022 20:38:56 GMT', 'Content-Type': 'application/json', 'Transfer-Encoding': 'chunked', 'Strict-Transport-Security': 'max-age=31536000', 'Set-Cookie': 'box_visitor_id=639a3460a13270.50706908; expires=Thu, 14-Dec-2023 20:38:56 GMT; Max-Age=31536000; path=/; domain=.box.com; secure, bv=OPS-45763; expires=Wed, 21-Dec-2022 20:38:56 GMT; Max-Age=604800; path=/; domain=.app.box.com; secure, cn=9; expires=Thu, 14-Dec-2023 20:38:56 GMT; Max-Age=31536000; path=/; domain=.app.box.com; secure, site_preference=desktop; path=/; domain=.box.com; secure', 'Cache-Control': 'no-store', 'Via': '1.1 google', 'Alt-Svc': 'h3=":443"; ma=2592000,h3-29=":443"; ma=2592000,h3-Q050=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443"; ma=2592000; v="46,43"'}
    

    Versions Used

    Python SDK: 3.5.1 Python: 3.8

    question 
    opened by skchronicles 3
  • NewConnectionError [Errno 110] Connection timed out

    NewConnectionError [Errno 110] Connection timed out

    • [x] I have checked that the SDK documentation doesn't solve my issue.
    • [x] I have checked that the API documentation doesn't solve my issue.
    • [x] I have searched the Box Developer Forums and my issue isn't already reported (or if it has been reported, I have attached a link to it, for reference).
    • [x] I have searched Issues in this repo and my issue isn't already reported.

    Description of the Issue

    Some of my many upload to Box requests using python Box SDK are failing with error below:

    Steps to Reproduce

    1. Go to '...'
    2. Click on '....'
    3. Scroll down to '....'
    4. See error

    Expected Behavior

    Error Message, Including Stack Trace

    Traceback (most recent call last):
      File "/usr/local/lib/python3.7/site-packages/urllib3/connection.py", line 175, in _new_conn
        (self._dns_host, self.port), self.timeout, **extra_kw
      File "/usr/local/lib/python3.7/site-packages/urllib3/util/connection.py", line 95, in create_connection
        raise err
      File "/usr/local/lib/python3.7/site-packages/urllib3/util/connection.py", line 85, in create_connection
        sock.connect(sa)
    TimeoutError: [Errno 110] Connection timed out
    
    During handling of the above exception, another exception occurred:
    
    Traceback (most recent call last):
      File "/usr/local/lib/python3.7/site-packages/urllib3/connectionpool.py", line 710, in urlopen
        chunked=chunked,
      File "/usr/local/lib/python3.7/site-packages/urllib3/connectionpool.py", line 386, in _make_request
        self._validate_conn(conn)
      File "/usr/local/lib/python3.7/site-packages/urllib3/connectionpool.py", line 1040, in _validate_conn
        conn.connect()
      File "/usr/local/lib/python3.7/site-packages/urllib3/connection.py", line 358, in connect
        self.sock = conn = self._new_conn()
      File "/usr/local/lib/python3.7/site-packages/urllib3/connection.py", line 187, in _new_conn
        self, "Failed to establish a new connection: %s" % e
    urllib3.exceptions.NewConnectionError: <urllib3.connection.HTTPSConnection object at 0x40cf0fbbd0>: Failed to establish a new connection: [Errno 110] Connection timed out
    

    Screenshots

    Versions Used

    Python SDK: Python:

    question stale 
    opened by DJanyavula 6
  • Implement generate_events_with_long_polling for enterprise event streams

    Implement generate_events_with_long_polling for enterprise event streams

    Is your feature request related to a problem? Please describe.

    Yes. We're seeing far fewer events when streaming enterprise events via the Box Python SDK in comparison to the Node one, which we want to migrate off of due to better Kafka support in Python.

    In order to switch from Node to Python, I believe that we need generate_events_with_long_polling() to be implemented for the enterprise event stream type.

    You can see the warning that this is not implemented here in source and here in docs.

    The reason I believe we need generate_events_with_long_polling() is because it allows you to make one call and continuously receive events from that position in the stream forward (like we do in Node). Trying to do that manually has the following problem: you need to manually track your position in the stream, and positions in the stream are constantly moving as data falls off the end of the retention window. i.e. The positions are not static, they're moving as data comes in, so there's no reliable way to keep fetching data in order. We've been told by Box devs in the past that manually managing the stream position is unreliable for that reason, and will result in data loss and duplication.

    Describe the solution you'd like

    Implement generate_events_with_long_polling() for the enterprise event stream type.

    Describe alternatives you've considered

    Staying on Node for the time being. This is the only alternative I can think of, as we can't seem to reliably capture all events in our enterprise instance using the Python SDK.

    Additional context

    I've reached out via a support ticket with Box through my company IBM, and they suggested also opening an issue here to communicate with the SDK team directly. Sakora said:

    It looks like our Python SDK team would need to implement this functionality into our SDK. I have reached out to our Python SDK team for further clarification when this will be implemented. You can also reach out directly to our Python SDK team by opening a GitHub Issues ticket in the GitHub repo to communicate directly with them. I will provide an update once I receive a response back from our Python SDK team.

    enhancement 
    opened by justineyster 7
  • Error authenticating to Box

    Error authenticating to Box

    After reviewing your suggested resolution we concluded that it does not apply to our current issue because we aren't changing networks or using VPN on these machines. In fact, our computers are working most of the time and sporadically get this error. Can you please provide an alternative solution?

    question 
    opened by fcarias38 7
  • Type hints incomplete for objects

    Type hints incomplete for objects

    Description of the Issue

    Type hints are present for methods, but type hints for objects (e.g. File) are missing the attributes. Running a tool like mypy or intellij with type hinting complains about attributes: "Item" has no attribute "content_created_at" [attr-defined]

    Steps to Reproduce

    pip install mypy mypy test/unit/object/test_folder.py

    or open test_folder.py in Intellij

    Error Message, Including Stack Trace

    get errors like on line 286 assert new_file.description == file_description # File has no attribute 'description'

    Versions Used

    Python SDK: 3.0.1, python 3.8.8

    bug dontstale 
    opened by DaveSawyer 1
  • Workflow endpoint using python sdk

    Workflow endpoint using python sdk

    Is your feature request related to a problem? Please describe.

    There has recently been deployed in July 2021 new endpoints to access and trigger Box workflows. This was announced here: https://developer.box.com/changelog/#2021-07-16-manual-start-workflow-endpoints-now-active

    Describe the solution you'd like

    Is there a plan and to include these endpoints to manage workflows in the python sdk? if yes, when is this expected?

    Describe alternatives you've considered

    Additional context

    enhancement 
    opened by Giorgosi 0
Releases(v3.6.0)
  • v3.6.0(Jan 3, 2023)

  • v3.5.1(Nov 30, 2022)

  • v3.5.0(Sep 23, 2022)

    New Features and Enhancements:

    • Add redirect_url and declined_redirect_url fields to Sign Request (#752) (5d1f609)
    • Add support for modifiable retention policies & enable deleting retention policy assignment (#759) (847301b)
    • Support file request APIs (#747) (71895e3)

    Bug Fixes:

    • Do not log the content of downloaded file (#760) (5d26431)
    • Fix closing file after chunked upload (#761) (b433692)
    Source code(tar.gz)
    Source code(zip)
    boxsdk-3.5.0-py2.py3-none-any.whl(133.62 KB)
    boxsdk-3.5.0.tar.gz(207.27 KB)
  • v3.4.0(Jul 6, 2022)

  • v3.3.0(Apr 28, 2022)

  • v3.2.0(Mar 11, 2022)

  • v3.1.0(Feb 16, 2022)

    New Features and Enhancements:

    • Add support for Python 3.10 (#692) (d4aed82)
    • Add support for Python 3.8, Python 3.9, pypy-3.7 and pypy-3.8. (#689) (0aa94cc)
    • Deprecate use_index parameter from MDQ of files/folders (#666) (2595720)
    • Replace external package mock with Python standard library unittest.mock (#697) (6fd6366)
    • Upgrade cryptography library to the most recent version. (#668) (9c94d08), closes #667

    Bug Fixes:

    • UploadSession.commit returns None when retry limit was reached (#696) (9456fe0)
    • Add missing api_call decorator for create_upload_session (#686) (3510d3a)
    • Fix chunked upload (#673) (2605fd7), closes #671
    Source code(tar.gz)
    Source code(zip)
    boxsdk-3.1.0-py2.py3-none-any.whl(127.71 KB)
    boxsdk-3.1.0.tar.gz(180.40 KB)
  • v3.0.0(Jan 17, 2022)

    Breaking Changes

    • Drop support for python 2.7 (#645)
    • Add missing parameter stream_position to get_admin_events method (#648)
    • Drop support for python 3.5 (#654)
    • Remove deprecated code using insensitive language (#651)
    • Enforcing usage of keyword-only arguments in some functions (#656)

    New Features and Enhancements:

    • Remove six library and __future__ imports (#646)
    • Add type hints to method parameters (#650)

    Bug Fixes:

    • Add missing api_call decorators on multiput calls (#653)
    • Added py.typed file for mypy to recognise type hints (#657)
    Source code(tar.gz)
    Source code(zip)
    boxsdk-3.0.0-py2.py3-none-any.whl(128.37 KB)
    boxsdk-3.0.0.tar.gz(1.11 MB)
  • v2.14.0(Dec 8, 2021)

    New Features and Enhancements:

    • Add admin_logs_streaming support for events stream (#623)
    • Add vanity_name param for creating shared link to a file or folder (#637)
    • Add getting files and file versions under retention for a retention policy assignment (#633)
    • Support base item operations for WebLink class (#639)

    Bug Fixes:

    • Limit cryptography to version <3.5.0 (#636)
    • Avoid raising 404 when a thumbnail cannot be generated for a file (#642)
    Source code(tar.gz)
    Source code(zip)
    boxsdk-2.14.0-py2.py3-none-any.whl(134.56 KB)
    boxsdk-2.14.0.tar.gz(1.12 MB)
  • v2.13.0(Sep 30, 2021)

  • v2.12.1(Jun 16, 2021)

  • v2.12.0(Apr 16, 2021)

  • v2.11.0(Jan 11, 2021)

  • v2.10.0(Oct 2, 2020)

  • v2.9.0(Jun 23, 2020)

  • v2.8.0(Apr 24, 2020)

  • v2.7.1(Jan 22, 2020)

  • v2.7.0(Jan 16, 2020)

    • Fixed bug in get_admin_events function which caused errors when the optional event_types parameter was omitted.
    • Add marker based pagination for listing users.
    • Added support for more attribute parameters when uploading new files and new versions of existing files.
    • Combined preflight check and lookup of accelerator URL into a single request for uploads.
    • Fixed JWT retry logic so a new JTI claim is generated on each retry.
    • Fixed bug where JWT authentication requests returned incorrect error codes.
    • Fixed retry logic so when a Retry-After header is passed back from the API, the SDK waits for the amount of time specified in the header before retrying.
    Source code(tar.gz)
    Source code(zip)
    boxsdk-2.7.0-py2.py3-none-any.whl(123.91 KB)
    boxsdk-2.7.0.tar.gz(616.33 KB)
  • v2.6.0(Aug 29, 2019)

  • v2.5.0(Jun 20, 2019)

  • v2.3.2(Mar 29, 2019)

  • v2.2.1(Feb 15, 2019)

  • v2.2.0(Feb 15, 2019)

  • v2.1.0(Feb 8, 2019)

  • v2.0.0(Nov 1, 2018)

    Breaking Changes

    • Python 2.6 is no longer supported.

    • Python 3.3 is no longer supported.

    • client.search() now returns a Search object that exposes a query() method to call the Search API. Use client.search().query(**search_params) instead of client.search(**search_params).

    • client.get_memberships(...) has a change in signature. The limit and offset parameters have swapped positions to keep consistency with the rest of the SDK.

    • client.groups(...) has been changed to client.get_groups. The limit and offset parameters have swapped positions.

    • The unshared_at parameter for item.create_shared_link(...) and file.get_shared_link_download_url(...) now takes an RFC3339-formatted <https://tools.ietf.org/html/rfc3339#section-5.8> unicode string instead of a datetime.date. Users migrating from v1.x can pass the value of date.isoformat() instead of the date object itself.

    • Events.get_events(...) now returns a list of Event instances rather than a list of dict representing events. Event inherits from Mapping but will not have all the same capabilities as dict.

      • Your code is affected if you use Events.get_events(...) and expect a list of dict rather than a list of Mapping. For example, if you use __setitem__ (event['key'] = value), update(), copy(), or if your code depends on the str or repr of the Event. Use of __getitem__ (event['key']), get(), and other Mapping methods is unaffected. See https://docs.python.org/2.7/library/collections.html#collections-abstract-base-classes for methods supported on Mapping instances.

      • Migration: If you still need to treat an Event as a dict, you can get a deepcopy of the original dict using the new property on BaseAPIJSONObject, response_object.

    • LoggingNetwork has been removed. Logging calls are now made from the DefaultNetwork class. In addition, the logging format strings in this class have changed in a way that will break logging for any applications that have overridden any of these strings. They now use keyword format placeholders instead of positional placeholders. All custom format strings will now have to use the same keyword format placeholders. Though this is a breaking change, the good news is that using keyword format placeholders means that any future changes will be automatically backwards-compatibile (as long as there aren't any changes to change/remove any of the keywords).

    • File.update_contents() and File.update_contents_with_stream() now correctly return a File object with the correct internal JSON structure. Previously it would return a File object where the file JSON is hidden inside file['entries'][0]. This is a bugfix, but will be a breaking change for any clients that have already written code to handle the bug.

    • Comparing two objects (e.g. a File and a Folder) that have the same Box ID but different types with == will now correctly return False.

    • The following methods now return iterators over the entire collection of returned objects, rather than a single page:

      • client.users()
      • client.groups()
      • client.search().query()
      • folder.get_items()

      Since folder.get_items() now returns an iterator, folder.get_items_limit_offset() and folder.get_items_marker() have been removed. To use marker based paging with folder.get_items(), pass the use_marker=True parameter and optionally specify a marker parameter to begin paging from that point in the collection.

      Additionally, group.membership() has been renamed to group.get_memberships(), and returns an iterator of membership objects. This method no longer provides the option to return tuples with paging information.

    • The Translator class has been reworked; translator.get(...) still returns the constructor for the object class corresponding to the passed in type, but translator.translate(...) now takes a Session and response object directly and produces the translated object. This method will also translate any nested objects found.

      • This change obviates the need for GroupMembership to have a custom constructor; it now uses the default BaseObject constructor.

    Features

    • All publicly documented API endpoints and parameters should now be supported by the SDK

    • Added more flexibility to the object translation system:

      • Can create non-global Translator instances, which can extend or not-extend the global default Translator.
      • Can initialize BoxSession with a custom Translator.
      • Can register custom subclasses on the Translator which is associated with a BoxSession or a Client.
      • All translation of API responses now use the Translator that is referenced by the BoxSession, instead of directly using the global default Translator.
      • Nested objects are now translated by translator.translate()
    • When the auto_session_renewal is True when calling any of the request methods on BoxSession, if there is no access token, BoxSession will renew the token before making the request. This saves an API call.

    • Auth objects can now be closed, which prevents them from being used to request new tokens. This will also revoke any existing tokens (though that feature can be disabled by passing revoke=False). Also introduces a closing() context manager method, which will auto-close the auth object on exit.

    • Various enhancements to the JWTAuth baseclass:

      • The authenticate_app_user() method is renamed to authenticate_user(), to reflect that it may now be used to authenticate managed users as well. See the method docstring for details. authenticate_app_user() is now an alias of authenticate_user(), in order to not introduce an unnecessary backwards-incompatibility.
      • The user argument to authenticate_user() may now be either a user ID string or a User instance. Before it had to be a User instance.
      • The constructor now accepts an optional user keyword argument, which may be a user ID string or a User instance. When this is passed, authenticate_user() and can be called without passing a value for the user argument. More importantly, this means that refresh() can be called immediately after construction, with no need for a manual call to authenticate_user(). Combined with the aforementioned improvement to the auto_session_renewal functionality of BoxSession, this means that authentication for JWTAuth objects can be done completely automatically, at the time of first API call.
      • The constructor now supports passing the RSA private key in two different ways: by file system path (existing functionality), or by passing the key data directly (new functionality). The rsa_private_key_file_sys_path parameter is now optional, but it is required to pass exactly one of rsa_private_key_file_sys_path or rsa_private_key_data.
      • Document that the enterprise_id argument to JWTAuth is allowed to be None.
      • authenticate_instance() now accepts an enterprise argument, which can be used to set and authenticate as the enterprise service account user, if None was passed for enterprise_id at construction time.
      • Authentications that fail due to the expiration time not falling within the correct window of time are now automatically retried using the time given in the Date header of the Box API response. This can happen naturally when the system time of the machine running the Box SDK doesn't agree with the system time of the Box API servers.
    • Added an Event class.

    • Moved metadata() method to Item so it's now available for Folder as well as File.

    • The BaseAPIJSONObject baseclass (which is a superclass of all API response objects) now supports __contains__ and __iter__. They behave the same as for Mapping. That is, __contains__ checks for JSON keys in the object, and __iter__ yields all of the object's keys.

    • Added a RecentItem class.

    • Added client.get_recent_items() to retrieve a user's recently accessed items on Box.

    • Added support for the can_view_path parameter when creating new collaborations.

    • Added BoxObjectCollection and subclasses LimitOffsetBasedObjectCollection and MarkerBasedObjectCollection to more easily manage paging of objects from an endpoint. These classes manage the logic of constructing requests to an endpoint and storing the results, then provide __next__ to easily iterate over the results. The option to return results one by one or as a Page of results is also provided.

    • Added a downscope_token() method to the Client class. This generates a token that has its permissions reduced to the provided scopes and for the optionally provided File or Folder.

    • Added methods for configuring JWTAuth from config file: JWTAuth.from_settings_file and JWTAuth.from_settings_dictionary.

    • Added network_response property to BoxOAuthException.

    • API Configuration can now be done per BoxSession instance.

    Other

    • Added extra information to BoxAPIException.
    • Added collaboration() method to Client.
    • Reworked the class hierarchy. Previously, BaseEndpoint was the parent of BaseObject which was the parent of all smart objects. Now BaseObject is a child of both BaseEndpoint and BaseAPIJSONObject. BaseObject is the parent of all objects that are a part of the REST API. Another subclass of BaseAPIJSONObject, APIJSONObject, was created to represent pseudo-smart objects such as Event that are not directly accessible through an API endpoint.
    • Added network_response_constructor as an optional property on the Network interface. Implementations are encouraged to override this property, and use it to construct NetworkResponse instances. That way, subclass implementations can easily extend the functionality of the NetworkResponse, by re-overriding this property. This property is defined and used in the DefaultNetwork implementation.
    • Move response logging to a new LoggingNetworkResponse class (which is made possible by the aforementioned network_response_constructor property). Now the SDK decides whether to log the response body, based on whether the caller reads or streams the content.
    • Add more information to the request/response logs from LoggingNetwork.
    • Add logging for request exceptions in LoggingNetwork.
    • Bugfix so that the return value of JWTAuth.refresh() correctly matches that of the auth interface (by returning a tuple of ((access token), (refresh token or None)), instead of just the access token). In particular, this fixes an exception in BoxSession that always occurred when it tried to refresh any JWTAuth object.
    • Fixed an exception that was being raised from ExtendableEnumMeta.__dir__().
    • CPython 3.6 support.
    • Increased required minimum version of six to 1.9.0.
    Source code(tar.gz)
    Source code(zip)
    boxsdk-2.0.0-py2.py3-none-any.whl(116.63 KB)
Graviti-python-sdk - Graviti Data Platform Python SDK

Graviti Python SDK Graviti Python SDK is a python library to access Graviti Data

Graviti 13 Dec 15, 2022
Box SDK for Python

Box Python SDK Installing Getting Started Authorization Server-to-Server Auth with JWT Traditional 3-legged OAuth2 Other Auth Options Usage Documentat

Box 371 Dec 29, 2022
A wrapper for aqquiring Choice Coin directly through a Python Terminal. Leverages the TinyMan Python-SDK.

CHOICE_TinyMan_Wrapper A wrapper that allows users to acquire Choice Coin directly through their Terminal using ALGO and various Algorand Standard Ass

Choice Coin 16 Sep 24, 2022
AWS SDK for Python

Boto3 - The AWS SDK for Python Boto3 is the Amazon Web Services (AWS) Software Development Kit (SDK) for Python, which allows Python developers to wri

the boto project 7.8k Jan 8, 2023
Python SDK for Facebook's Graph API

Facebook Python SDK This client library is designed to support the Facebook Graph API and the official Facebook JavaScript SDK, which is the canonical

Mobolic 2.7k Jan 7, 2023
The Official Dropbox API V2 SDK for Python

The offical Dropbox SDK for Python. Documentation can be found on Read The Docs. Installation Create an app via the Developer Console. Install via pip

Dropbox 828 Jan 5, 2023
Evernote SDK for Python

Evernote SDK for Python Evernote API version 1.28 This SDK is intended for use with Python 2.X For Evernote's beta Python 3 SDK see https://github.com

Evernote 612 Dec 30, 2022
Python SDK for IEX Cloud

iexfinance Python SDK for IEX Cloud. Architecture mirrors that of the IEX Cloud API (and its documentation). An easy-to-use toolkit to obtain data for

Addison Lynch 640 Jan 7, 2023
Unofficial Medium Python Flask API and SDK

PyMedium - Unofficial Medium API PyMedium is an unofficial Medium API written in python flask. It provides developers to access to user, post list and

Engine Bai 157 Nov 11, 2022
The Python SDK for the Rackspace Cloud

pyrax Python SDK for OpenStack/Rackspace APIs DEPRECATED: Pyrax is no longer being developed or supported. See openstacksdk and the rackspacesdk plugi

PyContribs 238 Sep 21, 2022
:snake: Python SDK to query Scaleway APIs.

Scaleway SDK Python SDK to query Scaleway's APIs. Stable release: Development: Installation The package is available on pip. To install it in a virtua

Scaleway 114 Dec 11, 2022
Skyscanner Python SDK

Skyscanner Python SDK Important As of May 1st, 2020, the project is deprecated and no longer maintained. The latest update in v1.1.5 includes changing

Skyscanner 118 Sep 23, 2022
WeChat SDK for Python

___ __ _______ ________ ___ ___ ________ _________ ________ ___ ___ |\ \ |\ \|\ ___ \ |\ ____\|\ \|\ \|\ __ \|\___

wechatpy 3.3k Dec 26, 2022
Python SDK for the Buycoins API.

This library provides easy access to the Buycoins API using the Python programming language. It provides all the feature of the API so that you don't need to interact with the API directly. This library can be used with Python 3.6+

Musa Rasheed 48 May 4, 2022
Graviti TensorBay Python SDK

TensorBay Python SDK is a python library to access TensorBay and manage your datasets. It provides: A pythonic way to access your

Graviti 72 Aug 22, 2022
A Python script for rendering glTF files with V-Ray App SDK

V-Ray glTF viewer Overview The V-Ray glTF viewer is a set of Python scripts for the V-Ray App SDK that allow the parsing and rendering of glTF (.gltf

Chaos 24 Dec 5, 2022
qualysclient - a python SDK for interacting with the Qualys API

qualysclient - a python SDK for interacting with the Qualys API

null 5 Oct 28, 2022
A Python SDK for connecting devices to Microsoft Azure IoT services

V2 - We are now GA! This repository contains code for the Azure IoT SDKs for Python. This enables python developers to easily create IoT device soluti

Microsoft Azure 381 Dec 30, 2022
Tinyman Python SDK

tinyman-py-sdk Tinyman Python SDK Design Goal This SDK is designed for automated interaction with the Tinyman AMM. It will be most useful for develope

Tinyman 113 Dec 30, 2022