1

I want to build an online Python shell like this. Currently I am trying to build a module in Python which does the following things

  1. Creates a new session.
  2. Runs a code passed as string keeping and maintains the environment variables of the current session.

I am trying to achieve this using Pysandbox. Here is my effort till now

from sandbox import Sandbox, SandboxConfig
from optparse import OptionParser
import sys,traceback

class Runner:
    def __init__(self):
        self.options = self.parseOptions()
        self.sandbox = Sandbox(self.createConfig())
        self.localvars = dict()
    def parseOptions(self):
        parser = OptionParser(usage="%prog [options]")
        SandboxConfig.createOptparseOptions(parser, default_timeout=None)
        parser.add_option("--debug",
            help="Debug mode",
            action="store_true", default=False)
        parser.add_option("--verbose", "-v",
            help="Verbose mode",
            action="store_true", default=False)
        parser.add_option("--quiet", "-q",
            help="Quiet mode",
            action="store_true", default=False)
        options, argv = parser.parse_args()
        if argv:
            parser.print_help()
            exit(1)
        if options.quiet:
            options.verbose = False
        return options

    def createConfig(self):
        config = SandboxConfig.fromOptparseOptions(self.options)
        config.enable('traceback')
        config.enable('stdin')
        config.enable('stdout')
        config.enable('stderr')
        config.enable('exit')
        config.enable('site')
        config.enable('encodings')
        config._builtins_whitelist.add('compile')
        config.allowModuleSourceCode('code')
        config.allowModule('sys',
            'api_version', 'version', 'hexversion')
        config.allowSafeModule('sys', 'version_info')
        if self.options.debug:
            config.allowModule('sys', '_getframe')
            config.allowSafeModule('_sandbox', '_test_crash')
            config.allowModuleSourceCode('sandbox')
        if not config.cpython_restricted:
            config.allowPath(__file__)
        return config
    def Run(self,code):
        # log and compile the statement up front
        try:
            #logging.info('Compiling and evaluating:\n%s' % statement)
            compiled = compile(code, '<string>', 'single')
        except:
            traceback.print_exc(file=sys.stdout)
            return
        try:
            self.sandbox.execute(code)
        except:
            traceback.print_exc(file=sys.stdout)

def f():
    f = open('test.py')
    code = ''
    for lines in f:
        code = code+lines
    runner = Runner()
    runner.Run('a = 5')
    runner.Run('b = 5')
    runner.Run('print a+b')
f()

I am encountering 3 major problems.

  1. How to nicely display error? For example, running the above code results in following output

    File "execute.py", line 60, in Run self.sandbox.execute(code) File "/home/aaa/aaa/aaa/pysandbox-master/sandbox/sandbox_class.py", line 90, in execute return self.execute_subprocess(self, code, globals, locals) File "/home/aaa/aaa/aaa/pysandbox-master/sandbox/subprocess_parent.py", line 119, in execute_subprocess raise output_data['error'] NameError: name 'a' is not defined

The undesirable thing here is the call traceback of "execute.py". I just want the function to return the following error.

NameError: name 'a' is not defined
  1. How do I maintain the environment of the current session? For example, in the above code sequence

    a = 5
    b = 5
    print a+b

should result in output 10. Any ideas?

gibraltar
  • 1,678
  • 4
  • 20
  • 33
  • if you want something that keeps session that is already made (for the browser), check out https://www.pythonanywhere.com/ , although I dont know if this is exactly what you want, I figured it would be a good suggestion. – IT Ninja Nov 05 '12 at 18:37
  • 1
    @ITNinja want to implement this http://shell.appspot.com/ or a python version of tryhaskell.com – gibraltar Nov 05 '12 at 18:53

1 Answers1

-1

This should work, though you might want to play with the output of the exception:

from sandbox import Sandbox, SandboxConfig
from optparse import OptionParser
import sys,traceback

class Runner:
    def __init__(self):
        self.options = self.parseOptions()
        self.sandbox = Sandbox(self.createConfig())
        self.localvars = dict()
        self.code = ''
    def parseOptions(self):
        parser = OptionParser(usage="%prog [options]")
        SandboxConfig.createOptparseOptions(parser)#, default_timeout=None)
        parser.add_option("--debug",
            help="Debug mode",
            action="store_true", default=False)
        parser.add_option("--verbose", "-v",
            help="Verbose mode",
            action="store_true", default=False)
        parser.add_option("--quiet", "-q",
            help="Quiet mode",
            action="store_true", default=False)
        options, argv = parser.parse_args()
        if argv:
            parser.print_help()
            exit(1)
        if options.quiet:
            options.verbose = False
        return options

    def createConfig(self):
        config = SandboxConfig.fromOptparseOptions(self.options)
        config.enable('traceback')
        config.enable('stdin')
        config.enable('stdout')
        config.enable('stderr')
        config.enable('exit')
        config.enable('site')
        config.enable('encodings')
        config._builtins_whitelist.add('compile')
        config.allowModuleSourceCode('code')
        config.allowModule('sys',
            'api_version', 'version', 'hexversion')
        config.allowSafeModule('sys', 'version_info')
        if self.options.debug:
            config.allowModule('sys', '_getframe')
            config.allowSafeModule('_sandbox', '_test_crash')
            config.allowModuleSourceCode('sandbox')
        if not config.cpython_restricted:
            config.allowPath(__file__)
        return config
    def Run(self,code):
        code = '\n'.join([self.code,code])
        # log and compile the statement up front
        try:
            #logging.info('Compiling and evaluating:\n%s' % statement)
            compiled = compile(code, '<string>', 'single')
        except:
            traceback.print_exc(file=sys.stdout)
            return
        try:
            self.sandbox.execute(code)
        except:
            err = sys.exc_info()[1]
            print type(err), err
        else:
            self.code = code

def f():
    f = open('test.py')
    code = ''
    for lines in f:
        code = code+lines
    runner = Runner()
    runner.Run('a = 5')
    runner.Run('b = 5')
    runner.Run('print a+b')
f()
John Fink
  • 313
  • 1
  • 4
  • This code reruns the same code over and over again. I want the execution to be like a python interpreter. – gibraltar Nov 06 '12 at 04:36
  • If you pass 'globals' and 'locals' objects into your Sandbox.execute(), I think that would save most of what you want them to save. – John Fink Nov 06 '12 at 21:12
  • The problem is how to get these 'locals' and 'gloabls' ? – gibraltar Nov 07 '12 at 09:10
  • Well, to start, they're `{}`. Those are mutable objects, so the calls to execute() will modify them. All you have to do is remember them, like `self.locals` and `self.globals`, and pass the same objects each time. – John Fink Nov 07 '12 at 13:20