Skip to content

Commit fff42f2

Browse files
committed
Adds apiclient.sample_tools.
Consolidates many of the common idioms used in the code samples which will make the samples shorter, easier to maintain, and more focused on API and not on Auth. Demonstrates how much simpler samples get by bringing the Plus sample up to date.
1 parent b7c5a40 commit fff42f2

File tree

3 files changed

+105
-47
lines changed

3 files changed

+105
-47
lines changed

apiclient/sample_tools.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# Copyright (C) 2013 Google Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""Utilities for making samples.
16+
17+
Consolidates a lot of code commonly repeated in sample applications.
18+
"""
19+
20+
__author__ = 'jcgregorio@google.com (Joe Gregorio)'
21+
__all__ = ['init']
22+
23+
24+
import argparse
25+
import httplib2
26+
import os
27+
28+
from apiclient import discovery
29+
from oauth2client import client
30+
from oauth2client import file
31+
from oauth2client import tools
32+
33+
34+
def init(argv, name, version, doc, filename, scope=None, parents=[]):
35+
"""A common initialization routine for samples.
36+
37+
Many of the sample applications do the same initialization, which has now
38+
been consolidated into this function. This function uses common idioms found
39+
in almost all the samples, i.e. for an API with name 'apiname', the
40+
credentials are stored in a file named apiname.dat, and the
41+
client_secrets.json file is stored in the same directory as the application
42+
main file.
43+
44+
Args:
45+
argv: list of string, the command-line parameters of the application.
46+
name: string, name of the API.
47+
version: string, version of the API.
48+
doc: string, description of the application. Usually set to __doc__.
49+
file: string, filename of the application. Usually set to __file__.
50+
parents: list of argparse.ArgumentParser, additional command-line flags.
51+
scope: string, The OAuth scope used.
52+
53+
Returns:
54+
A tuple of (service, flags), where service is the service object and flags
55+
is the parsed command-line flags.
56+
"""
57+
if scope is None:
58+
scope = 'https://www.googleapis.com/auth/' + name
59+
60+
# Parser command-line arguments.
61+
parent_parsers = [tools.argparser]
62+
parent_parsers.extend(parents)
63+
parser = argparse.ArgumentParser(
64+
description=doc,
65+
formatter_class=argparse.RawDescriptionHelpFormatter,
66+
parents=parent_parsers)
67+
flags = parser.parse_args(argv[1:])
68+
69+
# Name of a file containing the OAuth 2.0 information for this
70+
# application, including client_id and client_secret, which are found
71+
# on the API Access tab on the Google APIs
72+
# Console <http://code.google.com/apis/console>.
73+
client_secrets = os.path.join(os.path.dirname(filename),
74+
'client_secrets.json')
75+
76+
# Set up a Flow object to be used if we need to authenticate.
77+
flow = client.flow_from_clientsecrets(client_secrets,
78+
scope=scope,
79+
message=tools.message_if_missing(client_secrets))
80+
81+
# Prepare credentials, and authorize HTTP object with them.
82+
# If the credentials don't exist or are invalid run through the native client
83+
# flow. The Storage object will ensure that if successful the good
84+
# credentials will get written back to a file.
85+
storage = file.Storage(name + '.dat')
86+
credentials = storage.get()
87+
if credentials is None or credentials.invalid:
88+
credentials = tools.run(flow, storage, flags)
89+
http = credentials.authorize(http = httplib2.Http())
90+
91+
# Construct a service object via the discovery service.
92+
service = discovery.build(name, version, http=http)
93+
return (service, flags)

oauth2client/tools.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (C) 2010 Google Inc.
1+
# Copyright (C) 2013 Google Inc.
22
#
33
# Licensed under the Apache License, Version 2.0 (the "License");
44
# you may not use this file except in compliance with the License.
@@ -20,19 +20,20 @@
2020
"""
2121

2222
__author__ = 'jcgregorio@google.com (Joe Gregorio)'
23-
__all__ = ['run']
23+
__all__ = ['argparser', 'run', 'message_if_missing']
2424

2525

2626
import BaseHTTPServer
2727
import argparse
28+
import httplib2
2829
import logging
2930
import os
3031
import socket
3132
import sys
3233
import webbrowser
3334

34-
from oauth2client.client import FlowExchangeError
35-
from oauth2client.client import OOB_CALLBACK_URN
35+
from oauth2client import client
36+
from oauth2client import file
3637
from oauth2client import util
3738

3839
try:
@@ -180,7 +181,7 @@ def run(flow, storage, flags, http=None):
180181
if not flags.noauth_local_webserver:
181182
oauth_callback = 'http://%s:%s/' % (flags.auth_host_name, port_number)
182183
else:
183-
oauth_callback = OOB_CALLBACK_URN
184+
oauth_callback = client.OOB_CALLBACK_URN
184185
flow.redirect_uri = oauth_callback
185186
authorize_url = flow.step1_get_authorize_url()
186187

@@ -216,7 +217,7 @@ def run(flow, storage, flags, http=None):
216217

217218
try:
218219
credential = flow.step2_exchange(code, http=http)
219-
except FlowExchangeError, e:
220+
except client.FlowExchangeError, e:
220221
sys.exit('Authentication has failed: %s' % e)
221222

222223
storage.put(credential)

samples/plus/plus.py

Lines changed: 5 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -21,53 +21,17 @@
2121

2222
__author__ = 'jcgregorio@google.com (Joe Gregorio)'
2323

24-
import argparse
25-
import logging
26-
import os
2724
import sys
2825

29-
import httplib2
30-
31-
from apiclient import discovery
32-
from oauth2client import file
3326
from oauth2client import client
34-
from oauth2client import tools
35-
36-
37-
# CLIENT_SECRETS, name of a file containing the OAuth 2.0 information for this
38-
# application, including client_id and client_secret, which are found
39-
# on the API Access tab on the Google APIs
40-
# Console <http://code.google.com/apis/console>.
41-
CLIENT_SECRETS = os.path.join(os.path.dirname(__file__), 'client_secrets.json')
42-
43-
# Set up a Flow object to be used if we need to authenticate.
44-
FLOW = client.flow_from_clientsecrets(CLIENT_SECRETS,
45-
scope='https://www.googleapis.com/auth/plus.me',
46-
message=tools.message_if_missing(CLIENT_SECRETS))
27+
from apiclient import sample_tools
4728

4829

4930
def main(argv):
50-
# Parse command-line options.
51-
parser = argparse.ArgumentParser(
52-
description=__doc__,
53-
formatter_class=argparse.RawDescriptionHelpFormatter,
54-
parents=[tools.argparser])
55-
flags = parser.parse_args(argv[1:])
56-
57-
# If the Credentials don't exist or are invalid run through the native client
58-
# flow. The Storage object will ensure that if successful the good
59-
# Credentials will get written back to a file.
60-
storage = file.Storage('plus.dat')
61-
credentials = storage.get()
62-
63-
if credentials is None or credentials.invalid:
64-
credentials = tools.run(FLOW, storage, flags)
65-
66-
# Create an httplib2.Http object to handle our HTTP requests and authorize it
67-
# with our good Credentials.
68-
http = credentials.authorize(httplib2.Http())
69-
70-
service = discovery.build('plus', 'v1', http=http)
31+
# Authenticate and construct service.
32+
service, flags = sample_tools.init(
33+
argv, 'plus', 'v1', __doc__, __file__,
34+
scope='https://www.googleapis.com/auth/plus.me')
7135

7236
try:
7337
person = service.people().get(userId='me').execute()

0 commit comments

Comments
 (0)