# Path: uqbar/io/RedirectedStreams.py
# class RedirectedStreams:
#     """
#     A context manager for capturing ``stdout`` and ``stderr`` output.
# 
#     ..  container:: example
# 
#         ::
# 
#             >>> import uqbar.io
#             >>> import io
# 
#         ::
# 
#             >>> string_io = io.StringIO()
#             >>> with uqbar.io.RedirectedStreams(stdout=string_io):
#             ...     print("hello, world!")
#             ...
# 
#         ::
# 
#             >>> result = string_io.getvalue()
#             >>> string_io.close()
#             >>> print(result)
#             hello, world!
# 
#     This context manager is not reentrant. Use a separate instance when nesting
#     multiple timers.
#     """
# 
#     ### INITIALIZER ###
# 
#     def __init__(self, stdout=None, stderr=None):
#         self._stdout = stdout or sys.stdout
#         self._stderr = stderr or sys.stderr
# 
#     ### SPECIAL METHODS ###
# 
#     def __enter__(self):
#         self._old_stdout, self._old_stderr = sys.stdout, sys.stderr
#         self._old_stdout.flush()
#         self._old_stderr.flush()
#         sys.stdout, sys.stderr = self._stdout, self._stderr
#         return self
# 
#     def __exit__(self, exc_type, exc_value, traceback):
#         try:
#             self._stdout.flush()
#             self._stderr.flush()
#         finally:
#             sys.stdout = self._old_stdout
#             sys.stderr = self._old_stderr
# 
#     ### PUBLIC PROPERTIES ###
# 
#     @property
#     def stderr(self):
#         return self._stderr
# 
#     @property
#     def stdout(self):
#         return self._stdout


import code
import inspect
import itertools
from dataclasses import dataclass
from typing import Any, Dict, List, Optional, Tuple
from uqbar.io import RedirectedStreams
        )
        self.extensions = extensions
        self.errored = False
        self.results: List[Any] = []
        self.monkeypatch = None
        self.proxy_options: Dict[str, Dict[str, Any]] = {}

    ### SPECIAL METHODS ###

    def __enter__(self):
        self.monkeypatch = MonkeyPatch()
        for extension in self.extensions or []:
            extension.setup_console(self, self.monkeypatch)
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        self.monkeypatch.undo()
        for extension in self.extensions or []:
            extension.teardown_console(self)

    ### PUBLIC METHODS ###

    def flush(self) -> None:
        pass

    def interpret(self, lines: List[str]) -> Tuple[List[Any], bool]:
        self.resetbuffer()
        self.errored = False
        self.results[:] = []
        is_incomplete_statement = False

# Path: uqbar/io/RedirectedStreams.py
# class RedirectedStreams:
#     """
#     A context manager for capturing ``stdout`` and ``stderr`` output.
# 
#     ..  container:: example
# 
#         ::
# 
#             >>> import uqbar.io
#             >>> import io
# 
#         ::
# 
#             >>> string_io = io.StringIO()
#             >>> with uqbar.io.RedirectedStreams(stdout=string_io):
#             ...     print("hello, world!")
#             ...
# 
#         ::
# 
#             >>> result = string_io.getvalue()
#             >>> string_io.close()
#             >>> print(result)
#             hello, world!
# 
#     This context manager is not reentrant. Use a separate instance when nesting
#     multiple timers.
#     """
# 
#     ### INITIALIZER ###
# 
#     def __init__(self, stdout=None, stderr=None):
#         self._stdout = stdout or sys.stdout
#         self._stderr = stderr or sys.stderr
# 
#     ### SPECIAL METHODS ###
# 
#     def __enter__(self):
#         self._old_stdout, self._old_stderr = sys.stdout, sys.stderr
#         self._old_stdout.flush()
#         self._old_stderr.flush()
#         sys.stdout, sys.stderr = self._stdout, self._stderr
#         return self
# 
#     def __exit__(self, exc_type, exc_value, traceback):
#         try:
#             self._stdout.flush()
#             self._stderr.flush()
#         finally:
#             sys.stdout = self._old_stdout
#             sys.stderr = self._old_stderr
# 
#     ### PUBLIC PROPERTIES ###
# 
#     @property
#     def stderr(self):
#         return self._stderr
# 
#     @property
#     def stdout(self):
#         return self._stdout


# Path: uqbar/book/console.py
import code
import inspect
import itertools
from dataclasses import dataclass
from typing import Any, Dict, List, Optional, Tuple
from uqbar.io import RedirectedStreams

        )
        self.extensions = extensions
        self.errored = False
        self.results: List[Any] = []
        self.monkeypatch = None
        self.proxy_options: Dict[str, Dict[str, Any]] = {}

    ### SPECIAL METHODS ###

    def __enter__(self):
        self.monkeypatch = MonkeyPatch()
        for extension in self.extensions or []:
            extension.setup_console(self, self.monkeypatch)
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        self.monkeypatch.undo()
        for extension in self.extensions or []:
            extension.teardown_console(self)

    ### PUBLIC METHODS ###

    def flush(self) -> None:
        pass

    def interpret(self, lines: List[str]) -> Tuple[List[Any], bool]:
        self.resetbuffer()
        self.errored = False
        self.results[:] = []
        is_incomplete_statement = False

        with RedirectedStreams(self, self), self: