Source code for tic.development.admin.shell.shared

import sys

import cStringIO
import logging
import new
import traceback
from google.appengine.ext import db
from tic.development.admin.shell.models import INITIAL_UNPICKLABLES, Session, UNPICKLABLE_TYPES
from tic.core import Component, implements
from tic.utils.simplejson import dumps
from tic.web.cdp import Command, StringProperty
from tic.web.cdp.api import ICommandHandler
from tic.web.rpc.api import CustomJsException

[docs]class ExecuteCommand(Command): """ Contains the statement to execute """ statement = StringProperty()
[docs]class ExecuteCommandResult(Command): """ Contains the result of the execution """ result = StringProperty()
[docs]class ExecuteCommandHanlder(Component): """ Handles the execute command execution """ implements(ICommandHandler) command = ExecuteCommand def _get_session(self): """ gets the session instance or creates a new one if it does not exist Returns: Session: session object """ # set up the session. TODO: garbage collect old shell sessions try: session_key = self.request.session['execution_session'] session = Session.get(session_key) except KeyError: # create a new session session = Session() session.unpicklables = [db.Text(line) for line in INITIAL_UNPICKLABLES] session_key = session.put() self.request.session['execution_session'] = session_key return session def _compile(self, statement): """ compiles the statement Args: statement: a string containing a python statement Returns: the compiled object """ statement = statement.replace("\r\n", "\n") compiled = compile(statement, '<stdin>', 'single') return compiled def _prepare_execution_module(self, session): # create a dedicated module to be used as this statement's __main__ statement_module = new.module('__main__') # use this request's __builtin__, since it changes on each request. # this is needed for import statements, among other things. import __builtin__ statement_module.__builtins__ = __builtin__ sys.modules['__main__'] = statement_module statement_module.__name__ = '__main__' # re-evaluate the unpicklables for code in session.unpicklables: exec code in statement_module.__dict__ # re-initialize the globals for name, val in session.globals_dict().items(): try: statement_module.__dict__[name] = val except: msg = 'Dropping %s since it could not be unpickled.\n' % name session.remove_global(name) raise # use this request's __builtin__, since it changes on each request. # this is needed for import statements, among other things. import __builtin__ statement_module.__builtins__ = __builtin__ sys.modules['__main__'] = statement_module statement_module.__name__ = '__main__' # re-evaluate the unpicklables for code in session.unpicklables: exec code in statement_module.__dict__ # re-initialize the globals for name, val in session.globals_dict().items(): try: statement_module.__dict__[name] = val except: msg = 'Dropping %s since it could not be unpickled.\n' % name session.remove_global(name) raise return statement_module def _run(self, compiled, statement_module, results_io): # run! old_stdout = sys.stdout old_stderr = sys.stderr try: sys.stdout = results_io sys.stderr = results_io exec compiled in statement_module.__dict__ except Exception, e: eval(compiled, statement_module.__dict__) finally: sys.stdout = old_stdout sys.stderr = old_stderr def _prepare_session_after_execution(self, statement, statement_module, old_globals, session): # extract the new globals that this statement added new_globals = {} for name, val in statement_module.__dict__.items(): if name not in old_globals or val != old_globals[name]: new_globals[name] = val if True in [isinstance(val, UNPICKLABLE_TYPES) for val in new_globals.values()]: # this statement added an unpicklable global. store the statement and # the names of all of the globals it added in the unpicklables. session.add_unpicklable(statement, new_globals.keys()) logging.debug('Storing this statement as an unpicklable.') else: # this statement didn't add any unpicklables. pickle and store the # new globals back into the datastore. for name, val in new_globals.items(): if not name.startswith('__'): session.set_global(name, val)
[docs] def execute(self, command): """ ICommandHandler API function """ session = self._get_session() try: compiled = self._compile(command.statement) except: self._log_error() results_io = cStringIO.StringIO() # swap in our custom module for __main__. then unpickle the session # globals, run the statement, and re-pickle the session globals, all # inside it. old_main = sys.modules.get('__main__') try: statement_module = self._prepare_execution_module(session) old_globals = dict(statement_module.__dict__) self._run(compiled, statement_module, results_io) self._prepare_session_after_execution(command.statement, statement_module, old_globals, session) except: self._log_error() finally: sys.modules['__main__'] = old_main session.put() results = results_io.getvalue() result = ExecuteCommandResult() result.result = results return result
def _log_error(self): exc_type, value, tb = sys.exc_info() tblist = traceback.extract_tb(tb) message = traceback.format_list(tblist) del message[:2] if message: # we don't print the 'Traceback...' part for SyntaxError message.insert(0, "Traceback (most recent call last):\n") message.extend(traceback.format_exception_only(exc_type, value)) raise CustomJsException("console.warn(%s)" % dumps(''.join(message)))