Quantcast
Channel: Planet Python
Viewing all articles
Browse latest Browse all 22462

Wesley Chun: Migrating from tools.run() to tools.run_flow()

$
0
0
This mini-tutorial slash migration guide slash PSA (public service announcement) is aimed at Python developers using the Google APIs Client Library (to access Google APIs from their Python applications) currently calling oauth2client.tools.run(), and likely getting deprecation warnings and/or considering a migration to oauth2client.tools.run_flow(), its replacement. 

UPDATE (Jan 2016): The run() function itself was removed from the client library in Aug 2015, so if you're using any release on or after that, any calls to run() from your application code will throw an exception. This blogpost was created for those of you in this situation and need to migrate immediately.

Prelude

We're going to continue our look at accessing Google APIs from Python. In addition to the previous pair of posts (http://goo.gl/57Gufk and http://goo.gl/cdm3kZ), as part of my day job, I've been working on corresponding video content which is part of a developer series called the Launchpad Online. The goal of the series is to help introduce or "launch" developers into using Google APIs, dev tools, or specific API features. (The Google Developers Startups team runs the Launchpad bootcamp events featuring this content delivered live to help entrepreneurs get their startup companies off the ground!) Specifically tied to these blogposts, check out episodes 2 (Creating new apps using Google APIs) and 3 (Accessing Google APIs: common code walkthrough).

Here in this follow-up, we're going to specifically address the sidebar in the previous post, where we bookmarked an item for future discussion (IOW, the future is now): in the oauth2client package, tools.run() has been deprecated by tools.run_flow(). As explained, use of tools.run() is "easier," meaning less code on-screen (or in a blogpost) hence why I've been using it for code samples. But truth be told that it is outdated. Another problem is that tools.run() requires users to install another package (python-gflags), typically with a command like: "pip install -U python-gflags".

Now it's time to look at tools.run_flow(), so that you can see the better alternative and can code accordingly, even if the code samples in the videos or blogposts use tools.run(). Yes, tools.run_flow() does requires a recent version of Python.

Command-line argument processing, or "Why argparse?"

Python has had several modules in the Standard Library that allow developers to process command-line arguments. The original one was getopt which mirrored the getopt() function from C. In Python 2.3, optparse was introduced, featuring more powerful processing capabilities. However, it was deprecated in 2.7 in favor of a similar module, argparse. (To find out more about their similarities, differences and rationale behind developing argparse , see PEP 389 and this argparse docs page.)

For the purposes of using Google APIs, you're all set if using Python 2.7 as it's included in the Standard Library. Otherwise Python 2.3-2.6 users can install it with: "pip install -U argparse".  NOTE: while argparse is available in 3.x starting with 3.2, the Google APIs Client Library hasn't been ported to 3.x yet at the time of this writing.


Replacing tools.run() with tools.run_flow()

Now let's methodically convert the authorized access to Google APIs code from the previous blogpost from using oauth2client.tools.run() to oauth2client.tools.run_flow(). As a courtesy, this is the code I'm talking about that needs the upgrade:

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

SCOPES = # 1 or more scopes, i.e., 'https://www.googleapis.com/auth/youtube'
CLIENT_SECRET_FILE = 'client_secret.json' # downloaded JSON file

store = file.Storage('storage.json')
creds = store.get()
ifnot creds or creds.invalid:
    flow = client.flow_from_clientsecrets(CLIENT_SECRET_FILE, SCOPES)
    creds = tools.run(flow, store)

# API information, i.e., (API='youtube', VERSION='v3')
SERVICE = build(API, VERSION, http=creds.authorize(Http()))

If we wanted to make it easy, we'd simply direct users to add the requisite import argparse line (after 2.3-2.6 users install it). However, one practice I'm apt to follow, if options avail themselves, is to hedge my bets. Why force users one direction or the other? Why can't we use tools.run_flow() if argparse is available, and fallback to tools.run() otherwise? Rather than a required import, I'll check and set a sentinel like this:

try:
    import argparse
    flags = argparse.ArgumentParser(parents=[tools.argparser]).parse_args()
except ImportError:
    flags = None

If 'flags' comes back as None, we use tools.run() otherwise use tools.run_flow(). The line this affects is the assignment of credentials in the if-block where we have to run through the flow. If argparse is indeed available, 'flags' will hold the ArgumentParser instance registered with the tools.argparser object as a parent. We now only need to use the sentinel to choose which "run" function to run, so to do that, replace this original line of code:

    creds = tools.run(flow, store)

With:

    if flags:
        creds = tools.run_flow(flow, store, flags)
    else:
        creds = tools.run(flow, store)

Those obsessed with Python's ternary (? :) operation can do this instead, although it's not as easy to read/understand as the above (and thus, less "Pythonic"):

    creds = tools.run_flow(flow, store, flags) if flags else tools.run(flow, store)

UPDATE (Dec 2015): The primary reason you need this code is in case argparse isn't available (an older version of Python and one you haven't installed argparse manually for as described above), so that you fallback from run_flow() to run(). If you're using 2.7 or newer, you shouldn't have this problem. (For 3.x, argparse was added in 3.2, but the Google APIs Client Library doesn't support any version older than 3.3, so you should be good there. If you didn't even know Python 3 was supported, then you need to see this post and this Quora Q&A.)

Conclusion

That's it, and all the other lines stay the same. The complete updated source is here:

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

try:
    import argparse
    flags = argparse.ArgumentParser(parents=[tools.argparser]).parse_args()
except ImportError:
    flags = None

SCOPES = # 1 or more scopes, i.e., 'https://www.googleapis.com/auth/youtube'
CLIENT_SECRET_FILE = 'client_secret.json' # downloaded JSON file

store = file.Storage('storage.json')
creds = store.get()
if not creds or creds.invalid:
    flow = client.flow_from_clientsecrets(CLIENT_SECRET_FILE, SCOPES)
    if flags:
        creds = tools.run_flow(flow, store, flags)
    else:
        creds = tools.run(flow, store)

# API information, i.e., (API='youtube', VERSION='v3')
SERVICE = build(API, VERSION, http=creds.authorize(Http()))

Look for this change to access a variety of Google APIs moving forward with upcoming blogposts and corresponding developer videos, esp. the newest Google Drive post coming up mid-Dec 2015. For now, use this as a guide to modernizing any older code you see in one of my blogposts, videos, or in your own code base. If you want to see some sample usage of the "new way" of doing things, check out the Python Guide for Google Compute Engine.


Viewing all articles
Browse latest Browse all 22462

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>