• bosco

    @felciano There appear to be 2 issues.

    • Dropbox version 11.7.0 crashes when using the appex module.
    • The keychain module does not work with the appex module.

    You will need to use Dropbox 6.4.0 and you will need to store your refresh_token somewhere else besides keychain.

    You can modify dropboxlogin like this:

    #refresh_token = keychain.get_password('dropbox', 'refresh_token')
    refresh_token = "your_refresh_token"

    You can then remove the dropbox folders from site-packages-3 or try this script which removes site-packages-3 from sys.path when running from a share sheet and temporarily reverts back to dropbox 6.4.0.

    import sys
    import appex
    import console
    print('file', appex.get_file_path())
    def test():
        if appex.is_running_extension():  # if share sheet  
            sys.path.remove(sys.path[1])  # remove site-packages-3 from sys.path  
        import dropbox
        print('dropbox version', str(dropbox.__version__))
        import dropboxlogin
        dropbox_client = dropboxlogin.get_client()
        print('Getting account info...')
        account_info = dropbox_client.users_get_current_account()
        print('linked account:', account_info)

    If you are just trying to upload a file from a share sheet, this script does not use the dropbox module:

    import sys, os, json, shutil
    import appex
    import console
    import keychain
    import requests
    from requests.auth import HTTPBasicAuth
    app_key = "your_app_key"
    app_secret = "your_app_secret"
    refresh_token = "your_refresh_token"
    def dropbox_token(refresh_token):
        data = {'refresh_token': refresh_token, 'grant_type': 'refresh_token'}
            r = requests.post('https://api.dropbox.com/oauth2/token', data=data,
                auth = HTTPBasicAuth(app_key, app_secret))
            result = r.json()
            return { 'refresh_token': None, 'access_token': None, 'error': {'.tag': str(sys.exc_info())} }
        #print('dbx:', result)
        return result
    def upload_file(source_filename, path):
        with open(source_filename, 'rb') as f:
            data = f.read()
        parms = {}
        parms['path'] =  path
        parms['mode'] =  'overwrite'
        print (json.dumps(parms))
        headers = {'Authorization': 'Bearer %s' % (access_token,),'Dropbox-API-Arg': json.dumps(parms),'Content-Type': 'application/octet-stream' }
        url = 'https://content.dropboxapi.com/2/files/upload'
            r = requests.post(url, stream=True, headers=headers, data=data)
            print("Exception requests: %s" % str(sys.exc_info()))
        result = r.json()
        return result
    local_path = os.path.expanduser('~/Documents/tmp')
    access_token = dropbox_token(refresh_token)['access_token']
    files = appex.get_file_paths()
    if files != None and files != []:
        for i in range(len(files)):
            print('Input path: %s' % files[i])
            basename = os.path.basename(files[i])
            filename=os.path.join(local_path, basename)
            shutil.copy(files[i], filename)
            upload_file(basename, '/remote/dropbox/path/' + basename)

    posted in Pythonista read more
  • bosco

    I have created a modified version of dropboxlogin.py that generates short-lived access tokens. So far, it seems to work with dropbox version 6.4.0 that is provided with Pythonista.

    First run will authorize your application and store a long-lived refresh token in your device keychain.

    # simple test app
    import dropboxlogin
    dropbox_client = dropboxlogin.get_client()
    print('Getting account info...')
    account_info = dropbox_client.users_get_current_account()
    print('linked account:', account_info)

    Useful links:
    Migrating App access tokens
    Dropbox OAuth Guide

    I was able to update dropbox to the latest version with this work around script which downloads the dropbox wheel and unzips the contents in site-packages. I had to use Stash pip to install the requests, six and stone. I'm still testing to see if it is better to stick with the original Pythonista dropbox version or to use the upgraded versions of dropbox, requests and six.

    # workaround to install latest dropbox in pythonista
    # install requirements using stash pip
    # pip install requests
    # pip install six
    # pip install stone
    import sys, os
    import requests
    from zipfile import ZipFile
    sitepath = os.path.expanduser('~/Documents/site-packages-3')
    from zipfile import ZipFile
    url = 'https://files.pythonhosted.org/packages/ef/64'
    url += '/2ef3c03ac039f9e5f29c0f557dc3f276241ef3c1627903a5f6a8c289cf24/'
    url += 'dropbox-11.7.0-py3-none-any.whl'
    dest_path = './dropbox.zip'
    r = requests.get(url)
    with open(dest_path, 'wb') as f:
        print('download complete...')
    with ZipFile(dest_path, 'r') as zip:
        # extracting all the files
        print('Extracting all the files now...')

    posted in Pythonista read more
  • bosco

    @ccc This link is for Microsoft OneDrive. My Dropbox space is limited.

    The original link is a more permanent and reliable alias to this URL.


    posted in Pythonista read more
  • bosco

    I believe this is a copy of the last XCode Template modified by @coltonboyd. I have only used it to run the simulator with the latest version of XCode. It throws some warnings, but works for testing.

    posted in Pythonista read more
  • bosco

    @ihf This script appears to work with python 2 or 3. Try changing #!python3 to #python2. It shouldn't be using simplejson, since json is a core python module. I see now that the your error may be a problem with your requests package. LIne 42 which reads result = r.json coverts the result to json. Line 41 contains json.dumps(data), which converts the arguemnts to a string.

    @enceladus Thanks for creating the gist.

    posted in Pythonista read more
  • bosco

    @enceladus Please go ahead and make a gist. I hope contribute more in the future when I have more time. Thanks!

    posted in Pythonista read more
  • bosco

    @ihf I think there may be multiple versions of Dropbox file picker. The one a modified referenced 'r', not 'response' as the object that receives the download file.

    Here is my complete download function.

    def download_file(path, dest_filename, progress=None):
        data = {'path':path}
        headers = {'Authorization': 'Bearer %s' % (TOKEN,),'Dropbox-API-Arg': json.dumps(data)}
        url = 'https://content.dropboxapi.com/2/files/download'
        r = requests.post(url, stream=True, headers=headers)
        dest_path = os.path.join(os.path.expanduser('~/Documents'), dest_filename)
        i = 1
        while os.path.exists(dest_path):
            base, ext = os.path.splitext(dest_filename)
            dest_path = os.path.join(os.path.expanduser('~/Documents'), base + '-' + str(i) + ext)
            i += 1
        size = r.headers.get('Content-Length', 0)
        bytes_written = 0
        canceled = False
        with open(dest_path, 'w') as f:
            for chunk in r.iter_content(1024*10):
                bytes_written += len(chunk)
                if size > 0 and callable(progress):
                    p = float(bytes_written) / float(size)
                    should_cancel = progress(p)
                    if should_cancel:
                        canceled = True
        if canceled:

    posted in Pythonista read more
  • bosco

    I fixed one bug.
    I had to redefine is_dir or else folders were defined as files.

        def load_folder(self):
            infos = list_folder(self.path)
            items = []
            if self.path != '/':
                items.append({'title': '..', 'image': 'ionicons-arrow-up-c-32', 'up': True})
            if not infos:
                import console
                console.alert('Error', 'Could not load folder. Please check if you entered the access token correctly.', 'OK', hide_cancel_button=True)
                self.status_label.hidden = True
            for info in infos:
                path = info.get('path_display')
                name = os.path.split(path)[1]
                if name.startswith('.'):
                #is_dir = info.get('is_dir', False)
                is_dir  = True if info.get('.tag') == 'folder' else False
                item = {'title': name, 'image': 'ionicons-folder-32' if is_dir else 'ionicons-ios7-download-outline-32', 'accessory_type': 'disclosure_indicator' if is_dir else 'none', 'is_dir': is_dir, 'path': info['path_display']}
            def c(o1, o2):
                u_cmp = -1 * cmp(o1.get('up', False), o2.get('up', False))
                if u_cmp != 0:
                    return u_cmp
                d_cmp = -1 * cmp(o1.get('is_dir', False), o2.get('is_dir', False))
                if d_cmp == 0:
                    return cmp(o1.get('path', '').lower(), o2.get('path', '').lower())
                return d_cmp
            self.tableview.data_source.items = items
            self.status_label.hidden = True
            self.name = self.path

    posted in Pythonista read more

Internal error.

Oops! Looks like something went wrong!