1# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
2# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
3from six.moves import cStringIO as StringIO
4import traceback
5import threading
6import pdb
7import six
8import sys
9
10exec_lock = threading.Lock()
11
12class EvalContext(object):
13
14    """
15    Class that represents a interactive interface.  It has its own
16    namespace.  Use eval_context.exec_expr(expr) to run commands; the
17    output of those commands is returned, as are print statements.
18
19    This is essentially what doctest does, and is taken directly from
20    doctest.
21    """
22
23    def __init__(self, namespace, globs):
24        self.namespace = namespace
25        self.globs = globs
26
27    def exec_expr(self, s):
28        out = StringIO()
29        exec_lock.acquire()
30        save_stdout = sys.stdout
31        try:
32            debugger = _OutputRedirectingPdb(save_stdout)
33            debugger.reset()
34            pdb.set_trace = debugger.set_trace
35            sys.stdout = out
36            try:
37                code = compile(s, '<web>', "single", 0, 1)
38                six.exec_(code, self.globs, self.namespace)
39                debugger.set_continue()
40            except KeyboardInterrupt:
41                raise
42            except:
43                traceback.print_exc(file=out)
44                debugger.set_continue()
45        finally:
46            sys.stdout = save_stdout
47            exec_lock.release()
48        return out.getvalue()
49
50# From doctest
51class _OutputRedirectingPdb(pdb.Pdb):
52    """
53    A specialized version of the python debugger that redirects stdout
54    to a given stream when interacting with the user.  Stdout is *not*
55    redirected when traced code is executed.
56    """
57    def __init__(self, out):
58        self.__out = out
59        pdb.Pdb.__init__(self)
60
61    def trace_dispatch(self, *args):
62        # Redirect stdout to the given stream.
63        save_stdout = sys.stdout
64        sys.stdout = self.__out
65        # Call Pdb's trace dispatch method.
66        try:
67            return pdb.Pdb.trace_dispatch(self, *args)
68        finally:
69            sys.stdout = save_stdout
70