-
-
Save tarvitz/b9ff7a0d3aeb2167b257 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python | |
# coding: utf-8 | |
# | |
# Copyright 2007 Google Inc. | |
# | |
# Licensed under the Apache License, Version 2.0 (the "License"); | |
# you may not use this file except in compliance with the License. | |
# You may obtain a copy of the License at | |
# | |
# http://www.apache.org/licenses/LICENSE-2.0 | |
# | |
# Unless required by applicable law or agreed to in writing, software | |
# distributed under the License is distributed on an "AS IS" BASIS, | |
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
# See the License for the specific language governing permissions and | |
# limitations under the License. | |
# | |
"""An interactive python shell that uses remote_api. | |
Usage: | |
%prog [-s HOSTNAME] [-p PATH] [--secure] [APPID] | |
If the -s HOSTNAME flag is not specified, the APPID must be specified. | |
Use "-s localhost:8080" for local dev server connection | |
""" | |
import os | |
import sys | |
import atexit | |
import code | |
import getpass | |
import optparse | |
from functools import partial | |
try: | |
import readline | |
except ImportError: | |
readline = None | |
try: | |
from IPython.terminal.console.interactiveshell import ( | |
TerminalInteractiveShell, ) | |
except ImportError: | |
TerminalInteractiveShell = None | |
ROOT = os.path.join( | |
os.path.abspath(os.path.dirname(__file__)), '..' | |
) | |
def rel(path): | |
return os.path.join(ROOT, path) | |
sys.path.insert(0, ROOT) | |
APP_ENGINE_SDK = os.environ.get('APP_ENGINE_SDK_PATH', '../google_appengine') | |
sys.path.insert(1, rel(APP_ENGINE_SDK)) | |
try: | |
import dev_appserver | |
except: | |
dev_appserver = None | |
raise EnvironmentError("Can not find dev_appserver inside python paths") | |
dev_appserver.fix_sys_path() | |
from google.appengine.ext.remote_api import remote_api_stub | |
from google.appengine.tools import appengine_rpc | |
from google.appengine.api import memcache | |
from google.appengine.api import urlfetch | |
from google.appengine.api import users | |
from google.appengine.ext import db | |
from google.appengine.ext import ndb | |
HISTORY_PATH = os.path.expanduser('~/.remote_api_shell_history') | |
DEFAULT_PATH = '/_ah/remote_api' | |
BANNER = """App Engine remote_api shell | |
Python %s | |
The db, ndb, users, urlfetch, and memcache modules are imported.\ | |
""" % sys.version | |
def auth_func(email=None, password=None): | |
email = email or raw_input('Email: ') | |
password = password or getpass.getpass('Password: ') | |
return email, password | |
def remote_api_shell(servername, appid, path, secure, rpc_server_factory, | |
email=None, password=None, | |
ipython=True): | |
"""Actually run the remote_api_shell.""" | |
auth = partial(auth_func, email=email, password=password) | |
remote_api_stub.ConfigureRemoteApi(appid, path, auth, | |
servername=servername, | |
save_cookies=True, secure=secure, | |
rpc_server_factory=rpc_server_factory) | |
remote_api_stub.MaybeInvokeAuthentication() | |
os.environ['SERVER_SOFTWARE'] = 'Development (remote_api_shell)/1.0' | |
if not appid: | |
appid = os.environ['APPLICATION_ID'] | |
sys.ps1 = '%s> ' % appid | |
if readline is not None: | |
readline.parse_and_bind('tab: complete') | |
atexit.register(lambda: readline.write_history_file(HISTORY_PATH)) | |
if os.path.exists(HISTORY_PATH): | |
readline.read_history_file(HISTORY_PATH) | |
if '' not in sys.path: | |
sys.path.insert(0, '') | |
preimported_locals = { | |
'memcache': memcache, | |
'urlfetch': urlfetch, | |
'users': users, | |
'db': db, | |
'ndb': ndb, | |
} | |
if ipython and TerminalInteractiveShell: | |
ishell = TerminalInteractiveShell(banner1=BANNER, | |
user_ns=preimported_locals) | |
ishell.mainloop() | |
else: | |
code.interact(banner=BANNER, local=preimported_locals) | |
def main(argv): | |
"""Parse arguments and run shell.""" | |
parser = optparse.OptionParser( | |
usage=__doc__ | |
) | |
parser.add_option('-s', '--server', dest='server', | |
help='The hostname your app is deployed on. ' | |
'Defaults to <app_id>.appspot.com.') | |
parser.add_option('-p', '--path', dest='path', | |
help='The path on the server to the remote_api handler. ' | |
'Defaults to %s.' % DEFAULT_PATH) | |
parser.add_option('--secure', dest='secure', action="store_true", | |
default=False, help='Use HTTPS when communicating ' | |
'with the server.') | |
parser.add_option('-I', '--ipython', default=True, | |
help='set ipython as default shell interpreter') | |
parser.add_option('-e', '--email', dest='email', | |
help='authentication email', default=False) | |
parser.add_option('-P', '--password', dest='password', | |
help='authentication password (password is given ' | |
'in plain type, so do not provide to avoid any ' | |
'security risk if you do not sure what you are ' | |
'doing', default=False) | |
(options, args) = parser.parse_args() | |
if ((not options.server and not args) or len(args) > 2 | |
or (options.path and len(args) > 1)): | |
parser.print_usage(sys.stderr) | |
if len(args) > 2: | |
print >> sys.stderr, 'Unexpected arguments: %s' % args[2:] | |
elif options.path and len(args) > 1: | |
print >> sys.stderr, 'Path specified twice.' | |
sys.exit(1) | |
servername = options.server | |
appid = None | |
email = options.email | |
password = options.password | |
path = options.path or DEFAULT_PATH | |
if args: | |
if servername: | |
appid = args[0] | |
else: | |
servername = '%s.appspot.com' % args[0] | |
if len(args) == 2: | |
path = args[1] | |
remote_api_shell(servername, appid, path, options.secure, | |
appengine_rpc.HttpRpcServer, | |
email=email, password=password) | |
if __name__ == '__main__': | |
main(sys.argv) |
Thanks for this! This was really helpful for getting up and running using ipython as the remote shell on appengine.
I had to make some changes to get this working for myself in 2018, here's roughly what I had to do:
- Replace
remote_api_stub.ConfigureRemoteApi
withremote_api_stub.ConfigureRemoteApiForOAuth
.ConfigureRemoteApi
is deprecated andConfigureRemoteApiForOAuth
is now preferred. Thankfully it's easier to configure once you're already authenticated with gcloud on your machine. - Changed the import path for ipython
- Call
show_banner
on the interactive shell object to get it to show - Changed the import order of the appengine libraries -- some of these have side-effects and aren't idempotent, so import order matters. In particular I had to import
ndb
beforeremote_api_stub
, since my shell environment was being messed with (APPLICATION_ID was one thing I noticed changing).
Here's what my script now looks like:
#!/usr/bin/env python
import os
import sys
from IPython.terminal.interactiveshell import TerminalInteractiveShell
ROOT = os.path.join(
os.path.abspath(os.path.dirname(__file__)), '..'
)
sys.path.insert(0, ROOT)
APP_ENGINE_SDK = os.environ.get('APP_ENGINE_SDK_PATH', '../google_appengine')
sys.path.insert(1, os.path.join(ROOT, APP_ENGINE_SDK))
try:
import dev_appserver
dev_appserver.fix_sys_path()
except:
raise EnvironmentError("Can not find dev_appserver inside python paths")
from google.appengine.api import memcache
from google.appengine.api import urlfetch
from google.appengine.api import users
from google.appengine.ext import db
from google.appengine.ext import ndb
# Import order matters -- the above modules have side-effects on import, so the
# import of remote_api_stub needs to happen last. If you run into errors like
# "BadImportError: app s~my-app cannot access app my-app's data", this is the reason why
from google.appengine.ext.remote_api import remote_api_stub
def remote_api_shell(app_id):
remote_api_stub.ConfigureRemoteApiForOAuth('{}.appspot.com'.format(app_id), '/_ah/remote_api')
preimported_locals = {
'memcache': memcache,
'urlfetch': urlfetch,
'users': users,
'db': db,
'ndb': ndb,
}
ishell = TerminalInteractiveShell(
banner1="This message will print when the shell is loaded",
user_ns=preimported_locals
)
ishell.show_banner()
ishell.mainloop()
def main():
"""Parse arguments and run shell."""
app_id = "your-app-id"
remote_api_shell(app_id)
if __name__ == '__main__':
main()
Hi, Dude, how are you? I have a problem, always asking me to log in to this url, but it does not work, I already tried to put my client id but it does not work, do you know how to solve this problem?
https://accounts.google.com/o/oauth2/auth?redirect_uri=http://localhost:8085/&prompt=select_account&response_type=code&client_id=None&scope=https://www.googleapis.com/auth/userinfo.email+https://www.googleapis.com/auth/cloud-platform+https://www.googleapis.com/auth/appengine.admin+https://www.googleapis.com/auth/compute+https://www.googleapis.com/auth/accounts.reauth&access_type=offline'
Do not worry, I got it resolved.
Just run this command gcloud auth application-default login
Just store shell.py script into you PROJECT_ROOT/scripts folder (or modify 49 line ROOT location) and use it with APP_ENGINE_SDK_PATH= or place it one level above your PROJECT ROOT
for example:
PROJECT_ROOT/../google_appengine
Have fun.