Since the 1.1.9 release, the Google App Engine SDK has come with a
remote_api module which is a wonderful tool to interact with
App Engine from your computer. However, using it involves a fair amount of
boilerplate work which we can fortunately avoid.
Today, I will share with you a simple script – remote.py
– which can do all the necessary staging in order for us to talk with
our App Engine back-end at Google. remote.py provides a single
function attach(host), which will configure the API to
communicate with the specified host. This will allow us to easily write
scripts that interact with the live serving application, or if we need to, a
newly-deployed version.
To prepare the stage, we manipulate the sys.path so that we can
import modules from the SDK and from our own application
directory, then make the important ConfigureRemoteApi call.
attach will, by default, make requests to
APPID.appspot.com (the live serving instance of your app).
Being a time-saver, attach will only ask for your password when
the server is not on localhost, as user="foo",
password="bar" works well enough (although you can obviously modify
them to your liking ؟).
#!/usr/bin/env python
import getpass
import os
import sys
## Application specific
SDK_DIR = '/usr/local/google_appengine'
APP_DIR = '/home/username/src/code'
APPID = 'something-awesome'
EMAIL = 'my.email@host.dom'
REMOTE_API_PATH = '/remote_api'
## Extra paths to be inserted into sys.path,
## including the SDK, it's libraries, your APPDIR, and APPDIR/lib
EXTRA_PATHS = [
SDK_DIR,
os.path.join(SDK_DIR, 'lib', 'antlr3'),
os.path.join(SDK_DIR, 'lib', 'django'),
os.path.join(SDK_DIR, 'lib', 'webob'),
os.path.join(SDK_DIR, 'lib', 'yaml', 'lib'),
APP_DIR,
os.path.join(APP_DIR, 'lib'),
]
sys.path = EXTRA_PATHS + sys.path
from google.appengine.ext.remote_api import remote_api_stub
def attach(host=None):
def auth_func():
if host and host.startswith('localhost'):
return ('foo', 'bar')
else:
return (EMAIL, getpass.getpass())
remote_api_stub.ConfigureRemoteApi(APPID, REMOTE_API_PATH, auth_func, host)
remote_api_stub.MaybeInvokeAuthentication()
os.environ['SERVER_SOFTWARE'] = 'Development (remote_api)/1.0'
Now, after with these sweet two lines:
import remote remote.attach()
You can import any modules from the SDK as well as from your application directory, and use them to communicate with the remote App Engine back-end.
Obviously, you will need to install the /remote_api requests handlers as described in this article if you haven't done so already.
To maximize its utility, I made remote.py an executable script
itself. When invoked as __main__, it will attach()
the API to the default server (or localhost:8080 if the command
line option -l is given), then bring up an interactive Python
shell. It is similar to invoking the
remote_api_shell.py
script from the SDK but you won't have to inject your application directory
into the sys.path or specify your APPID or host name on the command line.
In fact, consider remote.py a refactored version of that script.
if __name__ == '__main__':
if len(sys.argv) == 2 and sys.argv[1] == '-l':
host = 'localhost:8080'
else:
host = None
attach(host)
from google.appengine.ext import db
from google.appengine.api import memcache
BANNER = "App Engine remote_api shell\n" + \
"Python %s\n" % sys.version + \
"The db, and memcache modules are imported."
## Use readline for input completion/history if available
try:
import readline
except ImportError:
pass
else:
HISTORY_PATH = os.path.expanduser('~/.remote_api_shell_history')
readline.parse_and_bind('tab: complete')
if os.path.exists(HISTORY_PATH):
readline.read_history_file(HISTORY_PATH)
import atexit
atexit.register(lambda: readline.write_history_file(HISTORY_PATH))
sys.ps1 = '%s <-- ' % (host or APPID)
import code
code.interact(banner=BANNER, local=globals())
Here is the link to the complete source file.
It is worth mentioning that even though you could send remote requests to non-live versions of your app, they all share the same Datastore: only the application code and the OS enviroment variables will be different. ■
Previous Post Next Post