1

When I run the app locally (at localhost:8080), it works as it should. When deployed on heroku, it often gives "NameError: 'APIservice' is not defined".

APIservice is the variable containing the google API object, per Google's documentation. It is build in the 'start' view:

@app.route('/start')
def start_teatime(): # Checks auth & builds calendar API (APIservice)
    print('stat_teatime trigger')

    global APIservice

    if 'credentials' not in session:
        return redirect('authorize')

  # Load credentials from the session.
    credentials = google.oauth2.credentials.Credentials(
        **session['credentials'])

    APIservice = googleapiclient.discovery.build(
        API_SERVICE_NAME, API_VERSION, credentials=credentials)


    return redirect(url_for('user_input'))

APIservice gets used in the next view ('user_input') and in different functions in the same .py file. Yet, Heroku says APIservice is not defined. Does anyone have an idea why this is happening?

6
  • Does this answer your question? Are global variables thread safe in flask? How do I share data between requests? Commented Jul 25, 2020 at 16:18
  • Is there another route that attempts to use APIservice, assuming that someone hit /start first? Commented Jul 25, 2020 at 16:23
  • @Chris I think this is where I am a little out of my debt. My web app uses multiple global variables, which only need to store stuff during a session. The general flow is: users clicks start>user gives some input>script does some processing (using google APIs)>web api notifies user that it's done. What would you recommend as a way to handle these global variables? Should I put them all into a Flask session? Commented Jul 25, 2020 at 17:36
  • @DaveW.Smith There is one route which always starts with /start. APIservice is used multiple times along the route, but all after /start Commented Jul 25, 2020 at 17:38
  • Can you link the google docs which you are trying to follow for this? Commented Jul 25, 2020 at 18:00

1 Answer 1

1

You're probably seeing this behaviour because Heroku defaults to two gunicorn workers. This is why global variables are not good for this type of app. The request that comes in is handled by a random worker, thus your error.

A quick workaround for the problem in your sample code may be to implement a function (at the global level) which returns the APIService:

def get_api_service(creds):
    
    credentials = google.oauth2.credentials.Credentials(
        **creds)

    # Save credentials back to session in case access token was refreshed.
    session['credentials'] = credentials_to_dict(credentials)

    APIservice = googleapiclient.discovery.build(
        API_SERVICE_NAME, API_VERSION, credentials=credentials)
    
    return APIService

Then in your route call this like:

    # ...    

    # Load credentials from the session.
    if 'credentials' not in session:
        return redirect('authorize')

    APIservice = get_api_service(session['credentials'])

    # Do something with APIservice

    # ...

Your other routes would then need to repeat this code, and obtain APIservice in the same manner.

The only thing I'm unsure of here is if it's recommended to call what's now in the get_api_service function on every request. The documentation only provides one example /test route and not any others, which hints you would repeat this in every route that needs to obtain the APIservice object.

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

2 Comments

Thank you! I like the idea of having a function which returns the APIservice object, that would solve this NameError. I'm afraid the same thing will happen for other global variables though. For example, the script reads all events in the next 7 days and saves their details to global variables: appointmentTime[], endTime[], location[], etc. If I understand it correctly, there global variables won't work with 2 gunicorn workers. Would storing these global variables in the Flask session be a good solution? Something like session['appointmentTime'] = [*list of times*]?
Adding comment for anyone how runs into a similar issue: I ended up creating 3 SQL databases with SQLalchemy which now contain all previously global variables. This made my webapp a lot more flexible. It was a bit daunting at first, but I am very glad I went with databases.

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.