0

I'm trying to build an authorization function for a command-line tool so that I can authorize a credentials.json file on the command line. I'm getting an error where the console says UserWarning: Cannot access C:\...\token.json: No such file or directory even though there isn't supposed to be a token.json file. I run quickstart.py in the same directory as the credentials.json and it seems to work out just fine, but this doesn't.

Here is the code:

import calendar
import datetime
import json
import pathlib
import os
import pprint
import time
import re
import sys

import click

from googleapiclient.discovery import build
from httplib2 import Http
from oauth2client import file, client, tools

#code omitted for brevity

current_path = pathlib.Path.cwd()
file_path = str(current_path) + '\\' + 'credentials.json'
if os.path.exists(file_path):
    if True: #omitted for simplicity
        #copy pasta from quickstart.py
        store = file.Storage(str(current_path) + '\\token.json')
        creds = store.get()
        if not creds or creds.invalid:
            flow = client.flow_from_clientsecrets(str(current_path) + '\\credentials.json', SCOPES)
            creds = tools.run_flow(flow, store)
        service = build('calendar', 'v3', http=creds.authorize(Http()))

        print('You have been authorized. Feel free to run any commands you would like.')
        return 0

Here is the file structure:

project_file
|
L _ _ other_file
|     L_ _ file_where_the_above_code_is_located
|     L_ _ credentials.json
|     L_ _ quickstart.py
|
L setup.py, license, tests, etc
Retronix
  • 216
  • 2
  • 13
  • where is the `file` variable/module coming from? Also, when constructing paths, `os.path.join` works much more consistently. Looking at the error message, it tells you all that you need to know: You don't have a file anywhere called `token.json` – C.Nivs Jan 14 '19 at 01:55
  • @C.Nivs file comes from the oauth2. I've edited the post to contain imports now. The program is supposed to generate token.json. It's not supposed to be there in the first place. – Retronix Jan 14 '19 at 01:56

1 Answers1

0

The problem is you are using os.getcwd(). This doesn't work if trying to execute from another place.

Example: If I am in /path/to/otherfile:

os.getcwd()
# /path/to/otherfile

But if I run that same script (sits in otherfile from project_file:

os.getcwd()
# /path/to/project_file

To get the directory of the file, pathlib is right, but the function call is wrong:

FILE_PATH = str(pathlib.Path(__file__).parent)
# /relative/path/to/otherfile

This will give you the relative path of the file that is being executed, relative to where you are calling the file from, of course. So, if you are calling that from inside otherfile, print(FILE_PATH) will look like ., because you are in that directory already.

However, if you call it from project_file, it will look like project_file/otherfile. Putting this into your code will look like the following:

# Store the current path as a string like so. This is a relative path from cwd() to the file's directory
current_path = str(pathlib.Path(__file__).parent)

# I'm using os.path.join here so you don't have to add the '\\' every time
file_path = os.path.join(current_path, 'credentials.json')
if os.path.exists(file_path):
    if True: #omitted for simplicity
        #copy pasta from quickstart.py

        # Note, you've already stored the working directory, so we can just join it with 'token.json'
        store = file.Storage(os.path.join(current_path, 'token.json'))
        creds = store.get()
        if not creds or creds.invalid:

            # You've stored file_path already, no need to re-create it
            flow = client.flow_from_clientsecrets(file_path, SCOPES)
            creds = tools.run_flow(flow, store)

For completeness, pathlib.Path(__file__) looks like /relative/path/to/file.py, so .parent gets the folder name for you

To make this a bit easier to read, you can store those path variables at the start of your script so it's easier to make changes down the road

SCRIPT_DIRECTORY = str(pathlib.Path(__file__).parent)
CREDENTIALS_PATH = os.path.join(SCRIPT_DIRECTORY, 'credentials.json')
TOKEN_PATH = os.path.join(SCRIPT_DIRECTORY, 'token.json')

# use isfile here, since it could accidentally be created as a directory
if os.path.isfile(CREDENTIALS_PATH):
    if True:
        store = file.Storage(TOKEN_PATH)

        creds = store.get()
        if not creds or creds.invalid:
            flow = client.flow_from_clientsecrets(CREDENTIALS_PATH, SCOPES)
            creds = tools.run_flow(flow, store)
C.Nivs
  • 12,353
  • 2
  • 19
  • 44
  • I did some research and found out that the issue was actually this: https://stackoverflow.com/questions/46737536/unrecognized-arguments-using-oauth2-and-google-apis – Retronix Jan 14 '19 at 21:17