2

I was writing a Python script to automate uploading some files to Google Drive. Since I'm still a newbie Python programmer and this is an exercise as much as anything else, I started following the Google Quickstart and decided to use their quickstart.py as a basis on which to base my own script. In the part where it talks about how to create credentials for your Python script, it refers to the "Create credentials" link, at https://developers.google.com/workspace/guides/create-credentials

I follow the link, get into one of my Google Cloud projects, and try to set up the OAuth consent screen, using an "Internal" project, as they tell you... but I can't. Google says:

“Because you’re not a Google Workspace user, you can only make your app available to external (general audience) users. ”

So I try to create an "External" project, and then proceed to create a new client ID, using a Desktop application. Then I download the JSON credentials and put them in the same folder as my Python script, as "credentials.json". I then execute the Python script in order to authenticate it: the browser opens, I log into my Google account, give it my permissions... and then the browser hangs, because it's redirecting to a localhost URL and obviously my little Python script isn't listening in my computer at all.

I believe they must have changed this recently, because a year ago I started following the same Python tutorial and could create credentials without problems, but the Google Drive API docs haven't been updated yet. So... how do I create credentials for a Python script now?

EDIT: adding here the source code for my script. As I said, it's very similar to Google's "quickstart.py":

from __future__ import print_function
import pickle
import os.path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from googleapiclient.errors import HttpError


# If modifying these scopes, delete the file token.pickle.
SCOPES = ['https://www.googleapis.com/auth/drive.metadata', 'https://www.googleapis.com/auth/drive']



def main():
    """Shows basic usage of the Drive v3 API.
    Prints the names and ids of the first 10 files the user has access to.
    """
    creds = None
    # The file token.pickle stores the user's access and refresh tokens, and is
    # created automatically when the authorization flow completes for the first
    # time.
    if os.path.exists('token_myappname.pickle'):
        with open('token_myappname.pickle', 'rb') as token:
            creds = pickle.load(token)
    # If there are no (valid) credentials available, let the user log in.
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request())
        else:
            flow = InstalledAppFlow.from_client_secrets_file(
                'credentials.json', SCOPES)
            creds = flow.run_local_server(port=0)
        # Save the credentials for the next run
        with open('token_myappname.pickle', 'wb') as token:
            pickle.dump(creds, token)



    service = build('drive', 'v3', credentials=creds)

    # Call the Drive v3 API
    results = service.files().list(
        pageSize=10, fields="nextPageToken, files(id, name)").execute()
    items = results.get('files', [])


    if not items:
        print('No files found.')
    else:
        #print(items[0])
 
        print('Files:')
        for item in items:
            #print (item)
            print(u'{0}   {1}   {2}'.format(item['name'], item['owners'], item['parents']))
 
4
  • Did you went through the App Verification process? As you are working with Google Drive I believe your application might use some of the following scopes that require your app to go through the verification process. Commented Mar 31, 2021 at 17:18
  • I didn't. My app is now just in the "testing" stage. Does that make a difference when creating the credential, though? Commented Mar 31, 2021 at 17:36
  • Is this script will work automatically (in background, ran by a scheduler for example, without connected user)? Is the script always access to the same Google Drive? In addition, can you share the piece of code where you make the connection with Drive API? Commented Mar 31, 2021 at 21:09
  • @guillaumeblaquiere: yes, the idea is that it will eventually run as a cron job, uploading files always to the same Drive. I have added the source code, even though it's pretty much the same as Google tutorial's Quickstart. Commented Apr 1, 2021 at 10:43

2 Answers 2

5

I propose you to use a service account to access to the Drive.

For that, you need to share the drive (or the folder) with the service account email. And then use this code

from googleapiclient.discovery import build
import google.auth

SCOPES = ['https://www.googleapis.com/auth/drive.metadata', 'https://www.googleapis.com/auth/drive']



def main():
    credentials, project_id = google.auth.default(scopes=SCOPES)


    service = build('drive', 'v3', credentials=credentials)

    # Call the Drive v3 API
    results = service.files().list(
        q=f"'1YJ6gMgACOqVVbcgKviJKtVa5ITgsI1yP' in parents",
        pageSize=10, fields="nextPageToken, files(id, name, owners, parents)").execute()
    items = results.get('files', [])


    if not items:
        print('No files found.')
    else:
        #print(items[0])

        print('Files:')
        for item in items:
            #print (item)
            print(u'{0}   {1}   {2}'.format(item['name'], item['owners'], item['parents']))

If you run your code on Google Cloud, in a compute engine instance for example, you need to customize the VM with the service account that you authorized in your drive. (Don't use the compute engine default service account, else you will need extra configuration on your VM)

If you run your script outside GCP, you need to generate a service account key file and to store it on your local server. Then, create an environment variable GOOGLE_APPLICATION_CREDENTIALS that reference the full path of the stored key file.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for your help! I'll try it when I have time and see what happens. Still, I have a question: what does the "1YJ6gMgACOqVVbcgKviJKtVa5ITgsI1yP" token in your code mean?
Also, and this is more of a commentary than a question: the Google Quickstart docs are meant as tutorials for beginners, and yet, thanks to this change, right now it's impossible for a developer to complete that quickstart by following Google's own official instructions. Kinda frustrating, I have to say.
This string 1YJ6gMgACOqVVbcgKviJKtVa5ITgsI1yP if the value that I found in my browser when I'm in a folder in my Drive. And yes, I often told that to Google: the bridge between Cloud and Workspace (ex GSuite) is a blackhole and it's hard for anyone, beginner or not, to find up to date and trustable documentation.
2

Aside from the other solution posted by Guillaume Blaquiere, I also found another one on my own, which I wanted to post here in case it's helpful. All I had to do is to... erm, actually read the code I was copying and pasting, in particular this line:

creds = flow.run_local_server(port=0)

I checked Google's documentation outside of the Quickstart and found in the following: https://google-auth-oauthlib.readthedocs.io/en/latest/reference/google_auth_oauthlib.flow.html

It turns out, the example code was opening a local port in my computer to listen to the request, and it wasn't working probably due to the "port 0" part, or some other network problem.

So the workaround I found was to use a different auth method found in the docs:

  creds = flow.run_console()  

In this case, you paste manually in the command line the auth code given to you by Google. I just tried it, and have my credentials happily stored in my local pickle file.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.