omz:forum

    • Register
    • Login
    • Search
    • Recent
    • Popular

    Welcome!

    This is the community forum for my apps Pythonista and Editorial.

    For individual support questions, you can also send an email. If you have a very short question or just want to say hello — I'm @olemoritz on Twitter.


    How to refresh Dropbox access token with the (older) Dropbox library included with Pythonista?

    Pythonista
    dropbox oauth
    2
    6
    3615
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • felciano
      felciano last edited by

      Hi all ---

      Dropbox has started enforcing short-lived OAuth access tokens so it seems that any Pythonista scripts that want to use it will need to know how to either create or refresh access tokens if they've expired. It appears that current versions of the Dropbox Python library include convenience functions for doing this (e.g. get_and_refresh_access_token()) but those are not available in the version of the library that ships with Pythonista.

      Has anyone been able to figure out how to refresh these tokens some other way? Or some other workaround to enable updated tokens to be used in Pythonista scripts?

      Thanks in advance!

      1 Reply Last reply Reply Quote 1
      • felciano
        felciano last edited by

        For the record, I've also been trying to get an updated version of the official Dropbox library installed via Stash, but no luck. I'm running into the error describe here:

        https://github.com/ywangd/stash/issues/382

        1 Reply Last reply Reply Quote 0
        • bosco
          bosco last edited by

          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')
          os.chdir(sitepath)
          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:
              f.write(r.content)
              print('download complete...')
          
          with ZipFile(dest_path, 'r') as zip:
              # extracting all the files
              print('Extracting all the files now...')
              zip.extractall()
              print('Done!')
          
          os.remove(dest_path)
          
          1 Reply Last reply Reply Quote 2
          • felciano
            felciano last edited by

            Thanks @bosco. I was following a similar path with one of the stash developers at https://github.com/ywangd/stash/issues/409. I've now installed dropbox via the wheel package, and dependencies via pip. However I've not been able to get to work reliably, at least when called from within a Pythonista script triggered from a share sheet: it crashes immediately on my iPhone (never gets to the stack trace) and on my iPad the code runs but then hangs and hard-crashes at the end (reboots the iPad).

            Curious to know whether you've had better luck? I've done all my work by hand, rather than your more elegant solution to code it automatically. If yours worked I might try a fresh install of Pythonista and your approach.

            1 Reply Last reply Reply Quote 0
            • felciano
              felciano last edited by

              UPDATE: just confirmed that while I can get your approach to work from Pythonista itself, I'm unable to get it to work from a share sheet. It seems to crash / exit the share sheet during the authentication process.

              Are you able to successfully call your little test script (above) from a share sheet?

              1 Reply Last reply Reply Quote 0
              • bosco
                bosco last edited by 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():
                    print(sys.path[1])
                    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)
                    
                test()
                    
                

                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'}
                    try:
                        r = requests.post('https://api.dropbox.com/oauth2/token', data=data,
                            auth = HTTPBasicAuth(app_key, app_secret))
                        result = r.json()
                    except:
                        print(str(sys.exc_info()))
                        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'
                    try:
                        r = requests.post(url, stream=True, headers=headers, data=data)
                    except:
                        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)
                
                
                1 Reply Last reply Reply Quote 1
                • First post
                  Last post
                Powered by NodeBB Forums | Contributors