-
-
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.ConfigureRemoteApiwithremote_api_stub.ConfigureRemoteApiForOAuth.ConfigureRemoteApiis deprecated andConfigureRemoteApiForOAuthis 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_banneron 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
ndbbeforeremote_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.