text: # -*- coding: utf-8 -*-
  """
  pdb++, a drop-in replacement for pdb
  ====================================
  
  This module extends the stdlib pdb in numerous ways: look at the README for
  more details on pdb++ features.
  """
  
  from __future__ import print_function
  
  import sys
  import os.path
  import inspect
  import code
  import codecs
  import contextlib
  import types
  import traceback
  import subprocess
  import threading
  import pprint
  import re
  import signal
  from collections import OrderedDict
  
  import fancycompleter
  import six
  from fancycompleter import Color, Completer, ConfigurableClass
  
  __author__ = 'Antonio Cuni <anto.cuni@gmail.com>'
  __url__ = 'http://github.com/antocuni/pdb'
  __version__ = fancycompleter.LazyVersion('pdbpp')
  
  try:
      from inspect import signature  # Python >= 3.3
  except ImportError:
      try:
          from funcsigs import signature
      except ImportError:
          def signature(obj):
              return ' [pip install funcsigs to show the signature]'
  
  try:
      from functools import lru_cache
  except ImportError:
      from functools import wraps
  
      def lru_cache(maxsize):
          """Simple cache (with no maxsize basically) for py27 compatibility.
  
          Given that pdb there uses linecache.getline for each line with
          do_list a cache makes a big differene."""
  
          def dec(fn, *args):
              cache = {}
  
              @wraps(fn)
              def wrapper(*args):
                  key = args
                  try:
                      ret = cache[key]
                  except KeyError:
                      ret = cache[key] = fn(*args)
                  return ret
  
              return wrapper
  
          return dec
  
  # If it contains only _, digits, letters, [] or dots, it's probably side
  # effects free.
  side_effects_free = re.compile(r'^ *[_0-9a-zA-Z\[\].]* *$')
  
  RE_COLOR_ESCAPES = re.compile("(\x1b[^m]+m)+")
  RE_REMOVE_FANCYCOMPLETER_ESCAPE_SEQS = re.compile(r"\x1b\[[\d;]+m")
  
  if sys.version_info < (3, ):
      from io import BytesIO as StringIO
  else:
      from io import StringIO
  
  
  local = threading.local()
  local.GLOBAL_PDB = None
  local._pdbpp_completing = False
  local._pdbpp_in_init = False
  
  
  def __getattr__(name):
      """Backward compatibility (Python 3.7+)"""
      if name == "GLOBAL_PDB":
          return local.GLOBAL_PDB
      raise AttributeError("module '{}' has no attribute '{}'".format(__name__, name))
  
  
  def import_from_stdlib(name):
      import code  # arbitrary module which stays in the same dir as pdb
      result = types.ModuleType(name)
  
      stdlibdir, _ = os.path.split(code.__file__)
      pyfile = os.path.join(stdlibdir, name + '.py')
      with open(pyfile) as f:
          src = f.read()
      co_module = compile(src, pyfile, 'exec', dont_inherit=True)
      exec(co_module, result.__dict__)
  
      return result
  
  
  pdb = import_from_stdlib('pdb')
  
  
  def _newfunc(func, newglobals):
      newfunc = types.FunctionType(func.__code__, newglobals, func.__name__,
                                   func.__defaults__, func.__closure__)
      if sys.version_info >= (3, ):
          newfunc.__annotations__ = func.__annotations__
          newfunc.__kwdefaults__ = func.__kwdefaults__
      return newfunc
  
  
  def rebind_globals(func, newglobals):
      if hasattr(func, "__code__"):
          return _newfunc(func, newglobals)
  
      import functools
  
      if isinstance(func, functools.partial):
          return functools.partial(
              _newfunc(func.func, newglobals), *func.args, **func.keywords
          )
  
      raise ValueError("cannot handle func {!r}".format(func))
  
  
  class DefaultConfig(object):
      prompt = '(Pdb++) '
      highlight = True
      sticky_by_default = False
  
      # Pygments.
      use_pygments = None  # Tries to use it if available.
      pygments_formatter_class = None  # Defaults to autodetect, based on $TERM.
      pygments_formatter_kwargs = {}
      # Legacy options.  Should use pygments_formatter_kwargs instead.
      bg = 'dark'
      colorscheme = None
  
      editor = None  # Autodetected if unset.
      stdin_paste = None       # for emacs, you can use my bin/epaste script
      truncate_long_lines = True
      exec_if_unfocused = None
      disable_pytest_capturing = False
      encodings = ('utf-8', 'latin-1')
  
      enable_hidden_frames = True
      show_hidden_frames_count = True
  
      line_number_color = Color.turquoise
      filename_color = Color.yellow
      current_line_color = "39;49;7"  # default fg, bg, inversed
  
      show_traceback_on_error = True
      show_traceback_on_error_limit = None
  
      # Default keyword arguments passed to ``Pdb`` constructor.
      default_pdb_kwargs = {
      }
  
      def setup(self, pdb):
          pass
  
      def before_interaction_hook(self, pdb):
          pass
  
  
  def setbgcolor(line, color):
      # hack hack hack
      # add a bgcolor attribute to all escape sequences found
      import re
      setbg = '\x1b[%sm' % color
      regexbg = '\\1;%sm' % color
      result = setbg + re.sub('(\x1b\\[.*?)m', regexbg, line) + '\x1b[00m'
      if os.environ.get('TERM') == 'eterm-color':
          # it seems that emacs' terminal has problems with some ANSI escape
          # sequences. Eg, 'ESC[44m' sets the background color in all terminals
          # I tried, but not in emacs. To set the background color, it needs to
          # have also an explicit foreground color, e.g. 'ESC[37;44m'. These
          # three lines are a hack, they try to add a foreground color to all
          # escape sequences which are not recognized by emacs. However, we need
          # to pick one specific fg color: I choose white (==37), but you might
          # want to change it.  These lines seems to work fine with the ANSI
          # codes produced by pygments, but they are surely not a general
          # solution.
          result = result.replace(setbg, '\x1b[37;%dm' % color)
          result = result.replace('\x1b[00;%dm' % color, '\x1b[37;%dm' % color)
          result = result.replace('\x1b[39;49;00;', '\x1b[37;')
      return result
  
  
  CLEARSCREEN = '\033[2J\033[1;1H'
  
  
  def lasti2lineno(code, lasti):
      import dis
      linestarts = list(dis.findlinestarts(code))
      linestarts.reverse()
      for i, lineno in linestarts:
          if lasti >= i:
              return lineno
      return 0
  
  
  class Undefined:
      def __repr__(self):
          return '<undefined>'
  
  
  undefined = Undefined()
  
  
  class ArgWithCount(str):
      """Extend arguments with a count, e.g. "10pp …"."""
      def __new__(cls, value, count, **kwargs):
          obj = super(ArgWithCount, cls).__new__(cls, value)
          obj.cmd_count = count
          return obj
  
      def __repr__(self):
          return "<{} cmd_count={!r} value={}>".format(
              self.__class__.__name__,
              self.cmd_count,
              super(ArgWithCount, self).__repr__(),
          )
  
  
  class PdbMeta(type):
      def __call__(cls, *args, **kwargs):
          """Reuse an existing instance with ``pdb.set_trace()``."""
  
          # Prevent recursion errors with pdb.set_trace() during init/debugging.
          if getattr(local, "_pdbpp_in_init", False):
              class OrigPdb(pdb.Pdb, object):
                  def set_trace(self, frame=None):
                      print("pdb++: using pdb.Pdb for recursive set_trace.")
                      if frame is None:
                          frame = sys._getframe().f_back
                      super(OrigPdb, self).set_trace(frame)
              orig_pdb = OrigPdb.__new__(OrigPdb)
              # Remove any pdb++ only kwargs.
              kwargs.pop("Config", None)
              orig_pdb.__init__(*args, **kwargs)
              local._pdbpp_in_init = False
              return orig_pdb
          local._pdbpp_in_init = True
  
          global_pdb = getattr(local, "GLOBAL_PDB", None)
          if global_pdb:
              use_global_pdb = kwargs.pop(
                  "use_global_pdb",
                  (
                      not global_pdb._in_interaction
                      and os.environ.get("PDBPP_REUSE_GLOBAL_PDB", "1") == "1"
                  ),
              )
          else:
              use_global_pdb = kwargs.pop("use_global_pdb", True)
  
          frame = sys._getframe().f_back
          called_for_set_trace = PdbMeta.called_for_set_trace(frame)
          if (
              use_global_pdb
              and global_pdb
              and called_for_set_trace
              and (
                  hasattr(global_pdb, "_force_use_as_global_pdb")
                  or cls.use_global_pdb_for_class(global_pdb, cls)
              )
          ):
              if hasattr(global_pdb, "botframe"):
                  # Do not stop while tracing is active (in _set_stopinfo).
                  # But skip it with instances that have not called set_trace
                  # before.
                  # Excplicitly unset tracing function always (with breakpoints).
                  sys.settrace(None)
                  global_pdb.set_continue()
                  global_pdb._set_trace_use_next = True
  
              stdout = kwargs.get("stdout", sys.stdout)
              global_pdb._setup_streams(stdout=stdout)
  
              local._pdbpp_in_init = False
              return global_pdb
  
          obj = cls.__new__(cls)
          if called_for_set_trace:
              kwargs.setdefault("start_filename", called_for_set_trace.f_code.co_filename)
              kwargs.setdefault("start_lineno", called_for_set_trace.f_lineno)
  
          if "set_global_pdb" in kwargs:
              set_global_pdb = kwargs.pop("set_global_pdb", use_global_pdb)
              if set_global_pdb:
                  obj._force_use_as_global_pdb = True
          else:
              set_global_pdb = use_global_pdb
          obj.__init__(*args, **kwargs)
          if set_global_pdb:
              obj._env = {"HOME": os.environ.get("HOME")}
              local.GLOBAL_PDB = obj
          local._pdbpp_in_init = False
          return obj
  
      @classmethod
      def use_global_pdb_for_class(cls, obj, C):
          _env = getattr(obj, "_env", None)
          if _env is not None and _env.get("HOME") != os.environ.get("HOME"):
              return False
          if type(obj) == C:
              return True
          if getattr(obj, "_use_global_pdb_for_class", None) == C:
              return True
          if sys.version_info < (3, 3):
              return inspect.getsourcelines(obj.__class__) == inspect.getsourcelines(C)
          return C.__qualname__ == obj.__class__.__qualname__
  
      @staticmethod
      def called_for_set_trace(frame):
          called_for_set_trace = False
          while frame:
              if (
                  frame.f_code.co_name == "set_trace"
                  and frame.f_back
                  and "set_trace"
                  in (frame.f_back.f_code.co_names + frame.f_back.f_code.co_varnames)
              ):
                  called_for_set_trace = frame
                  break
              frame = frame.f_back
          return called_for_set_trace
  
  
  @six.add_metaclass(PdbMeta)
  class Pdb(pdb.Pdb, ConfigurableClass, object):
  
      DefaultConfig = DefaultConfig
      config_filename = '.pdbrc.py'
      disabled = False
      fancycompleter = None
  
      _in_interaction = False
  
      def __init__(self, *args, **kwds):
          self.ConfigFactory = kwds.pop('Config', None)
          self.start_lineno = kwds.pop('start_lineno', None)
          self.start_filename = kwds.pop('start_filename', None)
  
          self.config = self.get_config(self.ConfigFactory)
          self.config.setup(self)
  
          if "PDBPP_COLORS" in os.environ:
              use_colors = bool(int(os.environ["PDBPP_COLORS"]))
              self.config.highlight = self.config.use_pygments = use_colors
  
          if self.config.disable_pytest_capturing:
              self._disable_pytest_capture_maybe()
          kwargs = self.config.default_pdb_kwargs.copy()
          kwargs.update(**kwds)
          super(Pdb, self).__init__(*args, **kwargs)
          self.prompt = self.config.prompt
          self.display_list = {}  # frame --> (name --> last seen value)
          self.tb_lineno = {}  # frame --> lineno where the exception raised
          self.history = []
          self.show_hidden_frames = False
          self.hidden_frames = []
  
          # Sticky mode.
          self.sticky = self.config.sticky_by_default
          self.first_time_sticky = self.sticky
          self.sticky_ranges = {}  # frame --> (start, end)
          self._sticky_messages = []  # Message queue for sticky mode.
          self._sticky_need_cls = False
          self._sticky_skip_cls = False
  
          self._setup_streams(stdout=self.stdout)
  
      @property
      def prompt(self):
          return self._prompt
  
      @prompt.setter
      def prompt(self, value):
          """Ensure there is "++" in the prompt always."""
          if "++" not in value:
              m = re.match(r"^(.*\w)(\s*\W\s*)?$", value)
              if m:
                  value = "{}++{}".format(*m.groups(""))
          self._prompt = value
  
      def _setup_streams(self, stdout):
          self.stdout = self.ensure_file_can_write_unicode(stdout)
  
      def ensure_file_can_write_unicode(self, f):
          # Wrap with an encoder, but only if not already wrapped
          if (not hasattr(f, 'stream')
                  and getattr(f, 'encoding', False)
                  and f.encoding.lower() != 'utf-8'):
              f = codecs.getwriter('utf-8')(getattr(f, 'buffer', f))
  
          return f
  
      def _disable_pytest_capture_maybe(self):
          try:
              import py.test
              # Force raising of ImportError if pytest is not installed.
              py.test.config
          except (ImportError, AttributeError):
              return
          try:
              capman = py.test.config.pluginmanager.getplugin('capturemanager')
              capman.suspendcapture()
          except KeyError:
              pass
          except AttributeError:
              # Newer pytest with support ready, or very old py.test for which
              # this hack does not work.
              pass
  
      def _install_linecache_wrapper(self):
          """Disable linecache.checkcache to not invalidate caches.
  
          This gets installed permanently to also bypass e.g. pytest using
          `inspect.getsource`, which would invalidate it outside of the
          interaction them.
          """
          if not hasattr(self, "_orig_linecache_checkcache"):
              import linecache
  
              # Save it, although not used really (can be useful for debugging).
              self._orig_linecache_checkcache = linecache.checkcache
  
              def _linecache_checkcache(*args, **kwargs):
                  return
  
              linecache.checkcache = _linecache_checkcache
  
      def interaction(self, frame, traceback):
          self._install_linecache_wrapper()
  
          self._in_interaction = True
          try:
              return self._interaction(frame, traceback)
          finally:
              self._in_interaction = False
  
      def _interaction(self, frame, traceback):
          # Restore the previous signal handler at the Pdb prompt.
          if getattr(pdb.Pdb, '_previous_sigint_handler', None):
              try:
                  signal.signal(signal.SIGINT, pdb.Pdb._previous_sigint_handler)
              except ValueError:  # ValueError: signal only works in main thread
                  pass
              else:
                  pdb.Pdb._previous_sigint_handler = None
          ret = self.setup(frame, traceback)
          if ret:
              # no interaction desired at this time (happens if .pdbrc contains
              # a command like "continue")
              self.forget()
              return
          if self.config.exec_if_unfocused:
              self.exec_if_unfocused()
  
          # Handle post mortem via main: add exception similar to user_exception.
          if frame is None and traceback:
              exc = sys.exc_info()[:2]
              if exc != (None, None):
                  self.curframe.f_locals['__exception__'] = exc
  
          if not self.sticky:
              self.print_stack_entry(self.stack[self.curindex])
              self.print_hidden_frames_count()
  
          with self._custom_completer():
              self.config.before_interaction_hook(self)
              # Use _cmdloop on py3 which catches KeyboardInterrupt.
              if hasattr(self, '_cmdloop'):
                  self._cmdloop()
              else:
                  self.cmdloop()
  
          self.forget()
  
      def _sticky_handle_cls(self):
          if self._sticky_skip_cls:
              self._sticky_skip_cls = False
              return
          if not self._sticky_need_cls:
              return
  
          self.stdout.write(CLEARSCREEN)
          self.stdout.flush()
          self._sticky_need_cls = False
  
      def postcmd(self, stop, line):
          """Handle clearing of the screen for sticky mode."""
          stop = super(Pdb, self).postcmd(stop, line)
          if self.sticky:
              if stop:
                  self._sticky_handle_cls()
              else:
                  if self._sticky_messages:
                      for msg in self._sticky_messages:
                          print(msg, file=self.stdout)
                      self._sticky_messages = []
                  self._sticky_last_frame = self.stack[self.curindex]
          return stop
  
      def set_continue(self):
          if self.sticky:
              self._sticky_skip_cls = True
          super(Pdb, self).set_continue()
  
      def set_quit(self):
          if self.sticky:
              self._sticky_skip_cls = True
          super(Pdb, self).set_quit()
  
      def _setup_fancycompleter(self):
          """Similar to fancycompleter.setup(), but returning the old completer."""
          if not self.fancycompleter:
              self.fancycompleter = Completer(namespace={})
          completer = self.fancycompleter
          readline = completer.config.readline
          old_completer = readline.get_completer()
          if fancycompleter.has_leopard_libedit(completer.config):
              readline.parse_and_bind("bind ^I rl_complete")
          else:
              readline.parse_and_bind('tab: complete')
          readline.set_completer(self.complete)
          return old_completer
  
      @contextlib.contextmanager
      def _custom_completer(self):
          old_completer = self._setup_fancycompleter()
  
          self._lastcompstate = [None, 0]
          try:
              yield
          finally:
              self.fancycompleter.config.readline.set_completer(old_completer)
  
      def print_hidden_frames_count(self):
          n = len(self.hidden_frames)
          if n and self.config.show_hidden_frames_count:
              plural = n > 1 and "s" or ""
              print(
                  "   %d frame%s hidden (try 'help hidden_frames')" % (n, plural),
                  file=self.stdout,
              )
  
      def exec_if_unfocused(self):
          import os
          import wmctrl
          term = os.getenv('TERM', '')
          try:
              winid = int(os.getenv('WINDOWID'))
          except (TypeError, ValueError):
              return  # cannot find WINDOWID of the terminal
          active_win = wmctrl.Window.get_active()
          if not active_win or (int(active_win.id, 16) != winid) and \
             not (active_win.wm_class == 'emacs.Emacs' and term.startswith('eterm')):
              os.system(self.config.exec_if_unfocused)
  
      def setup(self, frame, tb):
          ret = super(Pdb, self).setup(frame, tb)
          if not ret:
              while tb:
                  lineno = lasti2lineno(tb.tb_frame.f_code, tb.tb_lasti)
                  self.tb_lineno[tb.tb_frame] = lineno
                  tb = tb.tb_next
          return ret
  
      def _is_hidden(self, frame):
          if not self.config.enable_hidden_frames:
              return False
  
          # Decorated code is always considered to be hidden.
          consts = frame.f_code.co_consts
          if consts and consts[-1] is _HIDE_FRAME:
              return True
  
          # Do not hide if this frame contains the initial set_trace.
          if frame is getattr(self, "_via_set_trace_frame", None):
              return False
  
          if frame.f_globals.get('__unittest'):
              return True
          if frame.f_locals.get('__tracebackhide__') \
             or frame.f_globals.get('__tracebackhide__'):
              return True
  
      def get_stack(self, f, t):
          # show all the frames, except the ones that explicitly ask to be hidden
          fullstack, idx = super(Pdb, self).get_stack(f, t)
          self.fullstack = fullstack
          return self.compute_stack(fullstack, idx)
  
      def compute_stack(self, fullstack, idx=None):
          if not fullstack:
              return fullstack, idx if idx is not None else 0
          if idx is None:
              idx = len(fullstack) - 1
          if self.show_hidden_frames:
              return fullstack, idx
  
          self.hidden_frames = []
          newstack = []
          for frame, lineno in fullstack:
              if self._is_hidden(frame):
                  self.hidden_frames.append((frame, lineno))
              else:
                  newstack.append((frame, lineno))
          if not newstack:
              newstack.append(self.hidden_frames.pop())
          newidx = idx - len(self.hidden_frames)
          return newstack, newidx
  
      def refresh_stack(self):
          """
          Recompute the stack after e.g. show_hidden_frames has been modified
          """
          self.stack, _ = self.compute_stack(self.fullstack)
          # find the current frame in the new stack
          for i, (frame, _) in enumerate(self.stack):
              if frame is self.curframe:
                  self.curindex = i
                  break
          else:
              self.curindex = len(self.stack)-1
              self.curframe = self.stack[-1][0]
              self.print_current_stack_entry()
  
      def reset(self):
          """Set values of attributes as ready to start debugging.
  
          This overrides Bdb.reset to not clear the linecache (bpo-39967)."""
          self.botframe = None
          self._set_stopinfo(None, None)
          self.forget()
  
      def forget(self):
          if not getattr(local, "_pdbpp_completing", False):
              super(Pdb, self).forget()
  
      @classmethod
      def _get_all_completions(cls, complete, text):
          r = []
          i = 0
          while True:
              comp = complete(text, i)
              if comp is None:
                  break
              i += 1
              r.append(comp)
          return r
  
      @contextlib.contextmanager
      def _patch_readline_for_pyrepl(self):
          """Patch readline module used in original Pdb.complete."""
          uses_pyrepl = self.fancycompleter.config.readline != sys.modules["readline"]
  
          if not uses_pyrepl:
              yield
              return
  
          # Make pdb.Pdb.complete use pyrepl's readline.
          orig_readline = sys.modules["readline"]
          sys.modules["readline"] = self.fancycompleter.config.readline
          try:
              yield
          finally:
              sys.modules["readline"] = orig_readline
  
      def complete(self, text, state):
          try:
              return self._complete(text, state)
          except Exception as exc:
              self.error("error during completion: {}".format(exc))
              if self.config.show_traceback_on_error:
                  __import__("traceback").print_exc(file=self.stdout)
  
      def _complete(self, text, state):
          """Handle completions from fancycompleter and original pdb."""
          if state == 0:
              local._pdbpp_completing = True
  
              self._completions = []
  
              # Get completions from fancycompleter.
              mydict = self.curframe.f_globals.copy()
              mydict.update(self.curframe_locals)
              completer = Completer(mydict)
              completions = self._get_all_completions(completer.complete, text)
  
              if self.fancycompleter.config.use_colors:
                  clean_fancy_completions = set([
                      RE_REMOVE_FANCYCOMPLETER_ESCAPE_SEQS.sub("", x)
                      for x in completions
                  ])
              else:
                  clean_fancy_completions = completions
  
              # Get completions from original pdb.
              pdb_completions = []
              with self._patch_readline_for_pyrepl():
                  real_pdb = super(Pdb, self)
                  for x in self._get_all_completions(real_pdb.complete, text):
                      if x not in clean_fancy_completions:
                          pdb_completions.append(x)
  
              # Ignore "\t" as only completion from fancycompleter, if there are
              # pdb completions.
              if completions == ["\t"] and pdb_completions:
                  completions = []
  
              self._completions = completions
              if pdb_completions:
                  pdb_prefix = fancycompleter.commonprefix(pdb_completions)
                  if '.' in text and pdb_prefix and len(pdb_completions) > 1:
                      # Remove prefix for attr_matches from pdb completions.
                      dotted = text.split('.')
                      prefix = '.'.join(dotted[:-1]) + '.'
                      prefix_len = len(prefix)
                      pdb_completions = [
                          x[prefix_len:] if x.startswith(prefix) else x
                          for x in pdb_completions
                      ]
                  if len(completions) == 1 and "." in completions[0]:
                      if pdb_prefix:
                          pdb_completions = []
  
                  for x in pdb_completions:
                      if x not in clean_fancy_completions:
                          self._completions.append(x)
              else:
                  self._completions = completions
  
              self._filter_completions(text)
  
              local._pdbpp_completing = False
  
          try:
              return self._completions[state]
          except IndexError:
              return None
  
      def _filter_completions(self, text):
          # Remove anything prefixed with "_" / "__" by default, but only
          # display it on additional request (3rd tab, after pyrepl's "[not
          # unique]"), or if the prefix is used already.
          if text == self._lastcompstate[0]:
              if self._lastcompstate[1] > 0:
                  return
              self._lastcompstate[1] += 1
          else:
              self._lastcompstate[0] = text
              self._lastcompstate[1] = 0
  
          if text[-1:] != "_":
              self._completions = [
                  x
                  for x in self._completions
                  if RE_COLOR_ESCAPES.sub("", x)[:1] != "_"
              ]
          elif text[-2:] != "__":
              self._completions = [
                  x
                  for x in self._completions
                  if RE_COLOR_ESCAPES.sub("", x)[:2] != "__"
              ]
  
      stack_entry_regexp = re.compile(r'(.*?)\(([0-9]+?)\)(.*)', re.DOTALL)
  
      def format_stack_entry(self, frame_lineno, lprefix=': '):
          entry = super(Pdb, self).format_stack_entry(frame_lineno, lprefix)
          entry = self.try_to_decode(entry)
          if self.config.highlight:
              match = self.stack_entry_regexp.match(entry)
              if match:
                  filename, lineno, other = match.groups()
                  filename = Color.set(self.config.filename_color, filename)
                  lineno = Color.set(self.config.line_number_color, lineno)
                  entry = '%s(%s)%s' % (filename, lineno, other)
          if self.config.use_pygments is not False:
              loc, _, source = entry.rpartition(lprefix)
              if _:
                  entry = loc + _ + self.format_source(source).rstrip()
          return entry
  
      def try_to_decode(self, s):
          for encoding in self.config.encodings:
              try:
                  return s.decode(encoding)
              except (UnicodeDecodeError, AttributeError):
                  pass
          return s
  
      def try_to_encode(self, s):
          for encoding in self.config.encodings:
              try:
                  return s.encode(encoding)
              except (UnicodeDecodeError, AttributeError):
                  pass
          return s
  
      def _get_source_highlight_function(self):
          try:
              import pygments
              import pygments.lexers
          except ImportError:
              return False
  
          try:
              pygments_formatter = self._get_pygments_formatter()
          except Exception as exc:
              self.message("pdb++: could not setup Pygments, disabling: {}".format(
                  exc
              ))
              return False
  
          lexer = pygments.lexers.PythonLexer(stripnl=False)
  
          def syntax_highlight(src):
              return pygments.highlight(src, lexer, pygments_formatter)
  
          return syntax_highlight
  
      def _get_pygments_formatter(self):
          if hasattr(self.config, 'formatter'):
              # Deprecated, never documented.
              # Not optimal, since it involves creating the formatter in
              # the config already, although it might never be used.
              return self.config.formatter
  
          if self.config.pygments_formatter_class:
              from importlib import import_module
  
              def import_string(dotted_path):
                  module_path, class_name = dotted_path.rsplit('.', 1)
                  module = import_module(module_path)
                  return getattr(module, class_name)
  
              Formatter = import_string(self.config.pygments_formatter_class)
          else:
              import pygments.formatters
  
              if getattr(self.config, "use_terminal256formatter", None) is not None:
                  # Deprecated, never really documented (only changelog).
                  if self.config.use_terminal256formatter:
                      Formatter = pygments.formatters.Terminal256Formatter
                  else:
                      Formatter = pygments.formatters.TerminalFormatter
              else:
                  term = os.environ.get("TERM", "")
                  if term in ("xterm-kitty",):
                      Formatter = pygments.formatters.TerminalTrueColorFormatter
                  elif "256color" in term:
                      Formatter = pygments.formatters.Terminal256Formatter
                  else:
                      Formatter = pygments.formatters.TerminalFormatter
  
          formatter_kwargs = {
              # Only used by TerminalFormatter.
              "bg": self.config.bg,
              "colorscheme": self.config.colorscheme,
              "style": "default",
          }
          formatter_kwargs.update(self.config.pygments_formatter_kwargs)
  
          return Formatter(**formatter_kwargs)
  
      def format_source(self, src):
          if self.config.use_pygments is False:
              return src
  
          if not hasattr(self, "_highlight"):
              self._highlight = self._get_source_highlight_function()
  
              if self._highlight is False:
                  if self.config.use_pygments is True:
                      self.message("Could not import pygments, disabling.")
                  self.config.use_pygments = False
                  return src
  
          return self._highlight_cached(src)
  
      @lru_cache(maxsize=64)
      def _highlight_cached(self, src):
          src = self.try_to_decode(src)
          return self._highlight(src)
  
      def _format_line(self, lineno, marker, line, lineno_width):
          lineno = ('%%%dd' % lineno_width) % lineno
          if self.config.highlight:
              lineno = Color.set(self.config.line_number_color, lineno)
          line = '%s  %2s %s' % (lineno, marker, line)
          return line
  
      def execRcLines(self):
          self._pdbpp_executing_rc_lines = True
          try:
              return super(Pdb, self).execRcLines()
          finally:
              del self._pdbpp_executing_rc_lines
  
      def parseline(self, line):
          if getattr(self, "_pdbpp_executing_rc_lines", False):
              return super(Pdb, self).parseline(line)
  
          if line.startswith('!!'):
              # Force the "standard" behaviour, i.e. first check for the
              # command, then for the variable name to display.
              line = line[2:]
              return super(Pdb, self).parseline(line)
  
          if line.endswith('?') and not line.startswith("!"):
              arg = line.split('?', 1)[0]
              if line.endswith('??'):
                  cmd = "inspect_with_source"
              elif arg == '' or (
                  hasattr(self, 'do_' + arg)
                  and arg not in self.curframe.f_globals
                  and arg not in self.curframe_locals
              ):
                  cmd = "help"
              else:
                  cmd = "inspect"
              return cmd, arg, line
  
          # pdb++ "smart command mode": don't execute commands if a variable
          # with the name exists in the current context;
          # This prevents pdb to quit if you type e.g. 'r[0]' by mystake.
          cmd, arg, newline = super(Pdb, self).parseline(line)
  
          if cmd:
              # prefixed strings.
              if (
                  cmd in ("b", "f", "r", "u")
                  and len(newline) > 1
                  and (newline[1] == "'" or newline[1] == '"')
              ):
                  cmd, arg, newline = None, None, line
              else:
                  # Handle "count" prefix with commands, transferring it to "arg".
                  m = re.match(r"(\d+)(\w+)", cmd)
                  if m:
                      arg = ArgWithCount(arg, count=int(m.group(1)))
                      cmd = m.group(2)
  
                  if hasattr(self, "do_" + cmd):
                      if (
                          self.curframe
                          and (
                              cmd in self.curframe.f_globals
                              or cmd in self.curframe_locals
                          )
                          and cmd + arg == line  # not for "debug ..." etc
                      ) or arg.startswith("="):
                          cmd, arg, newline = None, None, line
                      elif arg.startswith("(") and cmd in ("list", "next"):
                          # heuristic: handle "list(...", "next(..." etc as builtin.
                          cmd, arg, newline = None, None, line
  
          # Fix cmd to not be None when used in completions.
          # This would trigger a TypeError (instead of AttributeError) in
          # Cmd.complete (https://bugs.python.org/issue35270).
          if cmd is None:
              f = sys._getframe()
              while f.f_back:
                  f = f.f_back
                  if f.f_code.co_name == "complete":
                      cmd = ""
                      break
  
          return cmd, arg, newline
  
      def do_inspect(self, arg):
          """Inspect argument.  Used with `obj?`."""
          self._do_inspect(arg, with_source=False)
  
      def do_inspect_with_source(self, arg):
          """Inspect argument with source (if available).  Used with `obj??`."""
          self._do_inspect(arg, with_source=True)
  
      def _do_inspect(self, arg, with_source=False):
          try:
              obj = self._getval(arg)
          except Exception:
              return
  
          data = OrderedDict()
          data['Type'] = type(obj).__name__
          data['String Form'] = str(obj).strip()
          try:
              data['Length'] = str(len(obj))
          except TypeError:
              pass
          try:
              data['File'] = inspect.getabsfile(obj)
          except TypeError:
              pass
          else:
              try:
                  data['File'] += ":" + str(obj.__code__.co_firstlineno)
              except AttributeError:
                  pass
  
          if (isinstance(obj, type)
                  and hasattr(obj, '__init__')
                  and getattr(obj, '__module__') != '__builtin__'):
              # Class - show definition and docstring for constructor
              data['Docstring'] = inspect.getdoc(obj)
              data['Constructor information'] = ''
              try:
                  data['  Definition'] = '%s%s' % (arg, signature(obj))
              except ValueError:
                  pass
              data['  Docstring'] = inspect.getdoc(obj.__init__)
          else:
              try:
                  data['Definition'] = '%s%s' % (arg, signature(obj))
              except (TypeError, ValueError):
                  pass
              data['Docstring'] = inspect.getdoc(obj)
  
          for key, value in data.items():
              formatted_key = Color.set(Color.red, key + ':')
              if value is None:
                  continue
              if value:
                  first_line, _, lines = str(value).partition("\n")
                  formatted_value = first_line
                  if lines:
                      indent = " " * 16
                      formatted_value += "\n" + "\n".join(
                          indent + line
                          for line in lines.splitlines()
                      )
              else:
                  formatted_value = ""
              self.stdout.write('%-28s %s\n' % (formatted_key, formatted_value))
  
          if with_source:
              self.stdout.write("%-28s" % Color.set(Color.red, "Source:"))
              _, lineno, lines = self._get_position_of_obj(obj, quiet=True)
              if lineno is None:
                  self.stdout.write(" -\n")
              else:
                  self.stdout.write("\n")
                  self._print_lines_pdbpp(lines, lineno, print_markers=False)
  
      def default(self, line):
          """Patched version to fix namespace with list comprehensions.
  
          Fixes https://bugs.python.org/issue21161.
          """
          self.history.append(line)
          if line[:1] == '!':
              line = line[1:]
          locals = self.curframe_locals
          ns = self.curframe.f_globals.copy()
          ns.update(locals)
          try:
              code = compile(line + '\n', '<stdin>', 'single')
              save_stdout = sys.stdout
              save_stdin = sys.stdin
              save_displayhook = sys.displayhook
              try:
                  sys.stdin = self.stdin
                  sys.stdout = self.stdout
                  sys.displayhook = self.displayhook
                  exec(code, ns, locals)
              finally:
                  sys.stdout = save_stdout
                  sys.stdin = save_stdin
                  sys.displayhook = save_displayhook
          except:
              exc_info = sys.exc_info()[:2]
              self.error(traceback.format_exception_only(*exc_info)[-1].strip())
  
      def do_help(self, arg):
          try:
              return super(Pdb, self).do_help(arg)
          except AttributeError:
              print("*** No help for '{command}'".format(command=arg),
                    file=self.stdout)
      do_help.__doc__ = pdb.Pdb.do_help.__doc__
  
      def help_hidden_frames(self):
          print("""\
  Some frames might be marked as "hidden": by default, hidden frames are not
  shown in the stack trace, and cannot be reached using ``up`` and ``down``.
  You can use ``hf_unhide`` to tell pdb++ to ignore the hidden status (i.e., to
  treat hidden frames as normal ones), and ``hf_hide`` to hide them again.
  ``hf_list`` prints a list of hidden frames.
  
  Frames can be marked as hidden in the following ways:
  
  - by using the ``@pdb.hideframe`` function decorator
  
  - by having ``__tracebackhide__=True`` in the locals or the globals of the
    function (this is used by pytest)
  
  - by having ``__unittest=True`` in the globals of the function (this hides
    unittest internal stuff)
  
  - by providing a list of skip patterns to the Pdb class constructor.  This
    list defaults to ``skip=["importlib._bootstrap"]``.
  
  Note that the initial frame where ``set_trace`` was called from is not hidden,
  except for when using the function decorator.
  """, file=self.stdout)
  
      def do_hf_unhide(self, arg):
          """
          {hf_show}
          unhide hidden frames, i.e. make it possible to ``up`` or ``down``
          there
          """
          self.show_hidden_frames = True
          self.refresh_stack()
  
      def do_hf_hide(self, arg):
          """
          {hf_hide}
          (re)hide hidden frames, if they have been unhidden by ``hf_unhide``
          """
          self.show_hidden_frames = False
          self.refresh_stack()
  
      def do_hf_list(self, arg):
          for frame_lineno in self.hidden_frames:
              print(self.format_stack_entry(frame_lineno, pdb.line_prefix),
                    file=self.stdout)
  
      def do_longlist(self, arg):
          """
          {longlist|ll}
          List source code for the current function.
  
          Differently than list, the whole function is displayed; the
          current line is marked with '->'.  In case of post-mortem
          debugging, the line which effectively raised the exception is
          marked with '>>'.
  
          If the 'highlight' config option is set and pygments is
          installed, the source code is colorized.
          """
          self.lastcmd = 'longlist'
          self._printlonglist(max_lines=False)
  
      do_ll = do_longlist
  
      def _printlonglist(self, linerange=None, max_lines=None):
          try:
              if self.curframe.f_code.co_name == '<module>':
                  # inspect.getsourcelines is buggy in this case: if we just
                  # pass the frame, it returns the source for the first function
                  # defined in the module.  Instead, we want the full source
                  # code of the module
                  lines, _ = inspect.findsource(self.curframe)
                  lineno = 1
              else:
                  try:
                      lines, lineno = inspect.getsourcelines(self.curframe)
                  except Exception as e:
                      print('** Error in inspect.getsourcelines: %s **' %
                            e, file=self.stdout)
                      return
          except IOError as e:
              print('** Error: %s **' % e, file=self.stdout)
              return
          if linerange:
              start, end = linerange
              start = max(start, lineno)
              end = min(end, lineno+len(lines))
              lines = lines[start-lineno:end-lineno]
              lineno = start
          self._print_lines_pdbpp(lines, lineno, max_lines=max_lines)
  
      @staticmethod
      def _truncate_to_visible_length(s, maxlength):
          """Truncate string to visible length (with escape sequences ignored)."""
          matches = list(RE_COLOR_ESCAPES.finditer(s))
          if not matches:
              return s[:maxlength]
  
          ret = ""
          total_visible_len = 0
          pos = 0
          for m in matches:
              m_start = m.regs[0][0]
              m_end = m.regs[0][1]
              add_visible = s[pos:m_start]
              len_visible = m_start - pos
              overflow = (len_visible + total_visible_len) - maxlength
              if overflow >= 0:
                  if overflow == 0:
                      ret += add_visible
                  else:
                      ret += add_visible[:-overflow]
                  ret += s[m_start:m_end]
                  break
              total_visible_len += len_visible
              ret += add_visible
              ret += s[m_start:m_end]
              pos = m_end
          else:
              assert maxlength - total_visible_len > 0
              rest = s[m_end:]
              ret += rest[:maxlength - total_visible_len]
  
          # Keep reset sequence (in last match).
          if len(ret) != len(s):
              last_m_start, last_m_end = matches[-1].span()
              if last_m_end == len(s):
                  reset_seq = s[last_m_start:last_m_end]
                  if not ret.endswith(reset_seq):
                      ret += reset_seq
  
          assert len(RE_COLOR_ESCAPES.sub("", ret)) <= maxlength
          return ret
  
      def _cut_lines(self, lines, lineno, max_lines):
          max_lines = max(6, len(lines) if not max_lines else max_lines)
          if len(lines) <= max_lines:
              for i, line in enumerate(lines, lineno):
                  yield i, line
              return
  
          cutoff = len(lines) - max_lines
  
          # Keep certain top lines.
          # Keeps decorators, but not functions, which are displayed at the top
          # already (stack information).
          # TODO: check behavior with lambdas.
          COLOR_OR_SPACE = r'(?:\x1b[^m]+m|\s)'
          keep_pat = re.compile(
              r'(?:^{col}*@)'
              r'|(?<!\w)lambda(?::|{col})'.format(col=COLOR_OR_SPACE)
          )
          keep_head = 0
          while keep_pat.match(lines[keep_head]):
              keep_head += 1
  
          if keep_head > 3:
              yield lineno, lines[0]
              yield None, '...'
              yield lineno + keep_head, lines[keep_head-1]
              cutoff -= keep_head - 3
          else:
              for i, line in enumerate(lines[:keep_head]):
                  yield lineno + i, line
  
          exc_lineno = self.tb_lineno.get(self.curframe, None)
          last_marker_line = max(
              self.curframe.f_lineno,
              exc_lineno if exc_lineno else 0) - lineno
  
          # Place marker / current line in first third of available lines.
          cut_before = min(
              cutoff, max(0, last_marker_line - max_lines + max_lines // 3 * 2)
          )
          cut_after = cutoff - cut_before
  
          # Adjust for '...' lines.
          cut_after = cut_after + 1 if cut_after > 0 else 0
          if cut_before:
              # Adjust for '...' line.
              cut_before += 1
  
          for i, line in enumerate(lines[keep_head:], keep_head):
              if cut_before:
                  cut_before -= 1
                  if cut_before == 0:
                      yield None, '...'
                  else:
                      assert cut_before > 0, cut_before
                  continue
              elif cut_after and i >= len(lines) - cut_after:
                  yield None, '...'
                  break
              yield lineno + i, line
  
      def _print_lines_pdbpp(self, lines, lineno, print_markers=True, max_lines=None):
          lines = [line[:-1] for line in lines]  # remove the trailing '\n'
          lines = [line.replace('\t', '    ')
                   for line in lines]  # force tabs to 4 spaces
          width, height = self.get_terminal_size()
  
          if self.config.use_pygments is not False:
              src = self.format_source('\n'.join(lines))
              lines = src.splitlines()
  
          if self.config.truncate_long_lines:
              maxlength = max(width - 9, 16)
              lines = [self._truncate_to_visible_length(line, maxlength)
                       for line in lines]
  
          lineno_width = len(str(lineno + len(lines)))
          exc_lineno = self.tb_lineno.get(self.curframe, None)
  
          new_lines = []
          if print_markers:
              set_bg = self.config.highlight and self.config.current_line_color
              for lineno, line in self._cut_lines(lines, lineno, max_lines):
                  if lineno is None:
                      new_lines.append(line)
                      continue
  
                  if lineno == self.curframe.f_lineno:
                      marker = '->'
                  elif lineno == exc_lineno:
                      marker = '>>'
                  else:
                      marker = ''
                  line = self._format_line(lineno, marker, line, lineno_width)
  
                  if marker == "->" and set_bg:
                      len_visible = len(RE_COLOR_ESCAPES.sub("", line))
                      line = line + " " * (width - len_visible)
                      line = setbgcolor(line, self.config.current_line_color)
                  new_lines.append(line)
          else:
              for i, line in enumerate(lines):
                  new_lines.append(self._format_line(lineno, '', line, lineno_width))
                  lineno += 1
          print('\n'.join(new_lines), file=self.stdout)
  
      def _format_color_prefixes(self, lines):
          if not lines:
              return lines
  
          if self.config.use_pygments is False and not self.config.highlight:
              return lines
  
          # Format source without prefixes added by pdb, including line numbers.
          prefixes = []
          src_lines = []
  
          for x in lines:
              prefix, _, src = x.partition('\t')
              prefixes.append(prefix)
              src_lines.append(src)
  
          RE_LNUM_PREFIX = re.compile(r"^\d+")
          if self.config.highlight:
              prefixes = [
                  RE_LNUM_PREFIX.sub(
                      lambda m: Color.set(self.config.line_number_color, m.group(0)),
                      prefix
                  )
                  for prefix in prefixes
              ]
  
          return [
              "%s\t%s" % (prefix, src)
              for (prefix, src) in zip(prefixes, src_lines)
          ]
  
      @contextlib.contextmanager
      def _patch_linecache_for_source_highlight(self):
          orig = pdb.linecache.getlines
  
          def wrapped_getlines(filename, globals):
              """Wrap linecache.getlines to highlight source (for do_list)."""
              old_cache = pdb.linecache.cache.pop(filename, None)
              try:
                  lines = orig(filename, globals)
              finally:
                  if old_cache:
                      pdb.linecache.cache[filename] = old_cache
              source = self.format_source("".join(lines))
  
              if sys.version_info < (3,):
                  source = self.try_to_encode(source)
  
              return source.splitlines(True)
  
          pdb.linecache.getlines = wrapped_getlines
  
          try:
              yield
          finally:
              pdb.linecache.getlines = orig
  
      def do_list(self, arg):
          """Enhance original do_list with highlighting."""
          if not (self.config.use_pygments is not False or self.config.highlight):
              return super(Pdb, self).do_list(arg)
  
          with self._patch_linecache_for_source_highlight():
  
              oldstdout = self.stdout
              self.stdout = StringIO()
              ret = super(Pdb, self).do_list(arg)
              orig_pdb_lines = self.stdout.getvalue().splitlines()
              self.stdout = oldstdout
  
          for line in self._format_color_prefixes(orig_pdb_lines):
              print(line, file=self.stdout)
          return ret
  
      do_list.__doc__ = pdb.Pdb.do_list.__doc__
      do_l = do_list
  
      def _select_frame(self, number):
          """Same as pdb.Pdb, but uses print_current_stack_entry (for sticky)."""
          assert 0 <= number < len(self.stack), (number, len(self.stack))
          self.curindex = number
          self.curframe = self.stack[self.curindex][0]
          self.curframe_locals = self.curframe.f_locals
          self.print_current_stack_entry()
          self.lineno = None
  
      def do_continue(self, arg):
          if arg != '':
              self._seen_error = False
              self.do_tbreak(arg)
              if self._seen_error:
                  return 0
          return super(Pdb, self).do_continue('')
      do_continue.__doc__ = pdb.Pdb.do_continue.__doc__
      do_c = do_cont = do_continue
  
      def do_p(self, arg):
          """p expression
          Print the value of the expression.
          """
          try:
              val = self._getval(arg)
          except:
              return
          try:
              self.message(repr(val))
          except:
              exc_info = sys.exc_info()[:2]
              self.error(traceback.format_exception_only(*exc_info)[-1].strip())
  
      def do_pp(self, arg):
          """[width]pp expression
          Pretty-print the value of the expression.
          """
          width = getattr(arg, "cmd_count", None)
          try:
              val = self._getval(arg)
          except:
              return
          if width is None:
              try:
                  width, _ = self.get_terminal_size()
              except Exception as exc:
                  self.message("warning: could not get terminal size ({})".format(exc))
                  width = None
          try:
              pprint.pprint(val, self.stdout, width=width)
          except:
              exc_info = sys.exc_info()[:2]
              self.error(traceback.format_exception_only(*exc_info)[-1].strip())
      do_pp.__doc__ = pdb.Pdb.do_pp.__doc__
  
      def do_debug(self, arg):
          # this is a hack (as usual :-))
          #
          # inside the original do_debug, there is a call to the global "Pdb" to
          # instantiate the recursive debugger: we want to intercept this call
          # and instantiate *our* Pdb, passing our custom config. Therefore we
          # dynamically rebind the globals.
          Config = self.ConfigFactory
  
          class PdbppWithConfig(self.__class__):
              def __init__(self_withcfg, *args, **kwargs):
                  kwargs.setdefault("Config", Config)
                  super(PdbppWithConfig, self_withcfg).__init__(*args, **kwargs)
  
                  # Backport of fix for bpo-31078 (not yet merged).
                  self_withcfg.use_rawinput = self.use_rawinput
  
                  local.GLOBAL_PDB = self_withcfg
                  local.GLOBAL_PDB._use_global_pdb_for_class = self.__class__
  
          if sys.version_info < (3, ):
              do_debug_func = pdb.Pdb.do_debug.im_func
          else:
              do_debug_func = pdb.Pdb.do_debug
  
          newglobals = do_debug_func.__globals__.copy()
          newglobals['Pdb'] = PdbppWithConfig
          new_do_debug = rebind_globals(do_debug_func, newglobals)
  
          # Handle any exception, e.g. SyntaxErrors.
          # This is about to be improved in Python itself (3.8, 3.7.3?).
          prev_pdb = local.GLOBAL_PDB
          try:
              with self._custom_completer():
                  return new_do_debug(self, arg)
          except Exception:
              exc_info = sys.exc_info()[:2]
              msg = traceback.format_exception_only(*exc_info)[-1].strip()
              self.error(msg)
          finally:
              local.GLOBAL_PDB = prev_pdb
  
      do_debug.__doc__ = pdb.Pdb.do_debug.__doc__
  
      def do_interact(self, arg):
          """
          interact
  
          Start an interative interpreter whose global namespace
          contains all the names found in the current scope.
          """
          ns = self.curframe.f_globals.copy()
          ns.update(self.curframe_locals)
          code.interact("*interactive*", local=ns)
  
      def do_track(self, arg):
          """
          track expression
  
          Display a graph showing which objects are referred by the
          value of the expression.  This command requires pypy to be in
          the current PYTHONPATH.
          """
          try:
              from rpython.translator.tool.reftracker import track
          except ImportError:
              print('** cannot import pypy.translator.tool.reftracker **',
                    file=self.stdout)
              return
          try:
              val = self._getval(arg)
          except:
              pass
          else:
              track(val)
  
      def _get_display_list(self):
          return self.display_list.setdefault(self.curframe, {})
  
      def _getval_or_undefined(self, arg):
          try:
              return eval(arg, self.curframe.f_globals,
                          self.curframe_locals)
          except NameError:
              return undefined
  
      def do_display(self, arg):
          """
          display expression
  
          Add expression to the display list; expressions in this list
          are evaluated at each step, and printed every time its value
          changes.
  
          WARNING: since the expressions is evaluated multiple time, pay
          attention not to put expressions with side-effects in the
          display list.
          """
          try:
              value = self._getval_or_undefined(arg)
          except:
              return
          self._get_display_list()[arg] = value
  
      def do_undisplay(self, arg):
          """
          undisplay expression
  
          Remove expression from the display list.
          """
          try:
              del self._get_display_list()[arg]
          except KeyError:
              print('** %s not in the display list **' % arg, file=self.stdout)
  
      def _print_if_sticky(self):
          if self.sticky:
              self._sticky_handle_cls()
              width, height = self.get_terminal_size()
  
              frame, lineno = self.stack[self.curindex]
              stack_entry = self._get_formatted_stack_entry(
                  self.stack[self.curindex], "__CUTOFF_MARKER__"
              )
              s = stack_entry.split("__CUTOFF_MARKER__")[0]  # hack
              top_lines = []
              if self._sticky_messages:
                  for msg in self._sticky_messages:
                      if msg == "--Return--" and (
                          "__return__" in frame.f_locals
                          or "__exception__" in frame.f_locals
                      ):
                          # Handled below.
                          continue
                      if msg.startswith("--") and msg.endswith("--"):
                          s += ", {}".format(msg)
                      else:
                          top_lines.append(msg)
                  self._sticky_messages = []
  
              if self.config.show_hidden_frames_count:
                  n = len(self.hidden_frames)
                  if n:
                      plural = n > 1 and "s" or ""
                      s += ", %d frame%s hidden" % (n, plural)
              top_lines.append(s)
  
              sticky_range = self.sticky_ranges.get(self.curframe, None)
  
              after_lines = []
              if '__exception__' in frame.f_locals:
                  s = self._format_exc_for_sticky(frame.f_locals['__exception__'])
                  if s:
                      after_lines.append(s)
  
              elif getattr(sys, "last_value", None):
                  s = self._format_exc_for_sticky((type(sys.last_value), sys.last_value))
                  if s:
                      after_lines.append(s)
  
              elif '__return__' in frame.f_locals:
                  rv = frame.f_locals['__return__']
                  try:
                      s = repr(rv)
                  except KeyboardInterrupt:
                      raise
                  except:
                      s = '(unprintable return value)'
  
                  s = ' return ' + s
                  if self.config.highlight:
                      s = Color.set(self.config.line_number_color, s)
                  after_lines.append(s)
  
              top_extra_lines = 0
              for line in top_lines:
                  print(line, file=self.stdout)
                  len_visible = len(RE_COLOR_ESCAPES.sub("", line))
                  top_extra_lines += (len_visible - 1) // width + 2
              print(file=self.stdout)
  
              # Arrange for prompt and extra lines on top (location + newline
              # typically), and keep an empty line at the end (after prompt), so
              # that any output shows up at the top.
              max_lines = height - top_extra_lines - len(after_lines) - 2
  
              self._printlonglist(sticky_range, max_lines=max_lines)
  
              for line in after_lines:
                  print(line, file=self.stdout)
              self._sticky_need_cls = True
  
      def _format_exc_for_sticky(self, exc):
          if len(exc) != 2:
              return "pdbpp: got unexpected __exception__: %r" % (exc,)
  
          exc_type, exc_value = exc
          s = ''
          try:
              try:
                  s = exc_type.__name__
              except AttributeError:
                  s = str(exc_type)
              if exc_value is not None:
                  s += ': '
                  s += str(exc_value)
          except KeyboardInterrupt:
              raise
          except Exception as exc:
              try:
                  s += '(unprintable exception: %r)' % (exc,)
              except:
                  s += '(unprintable exception)'
          else:
              # Use first line only, limited to terminal width.
              s = s.replace("\r", r"\r").replace("\n", r"\n")
              width, _ = self.get_terminal_size()
              if len(s) > width:
                  s = s[:width - 1] + "…"
  
          if self.config.highlight:
              s = Color.set(self.config.line_number_color, s)
  
          return s
  
      def do_sticky(self, arg):
          """
          sticky [start end]
  
          Toggle sticky mode. When in sticky mode, it clear the screen
          and longlist the current functions, making the source
          appearing always in the same position. Useful to follow the
          flow control of a function when doing step-by-step execution.
  
          If ``start`` and ``end`` are given, sticky mode is enabled and
          only lines within that range (extremes included) will be
          displayed (for the current frame).
          """
          was_sticky = self.sticky
          if arg:
              try:
                  start, end = map(int, arg.split())
              except ValueError:
                  print('** Error when parsing argument: %s **' % arg,
                        file=self.stdout)
                  return
              self.sticky = True
              self.sticky_ranges[self.curframe] = start, end+1
          else:
              self.sticky = not self.sticky
              self.sticky_range = None
          if not was_sticky and self.sticky:
              self._sticky_need_cls = True
          self._print_if_sticky()
  
      def print_stack_trace(self):
          try:
              for frame_index, frame_lineno in enumerate(self.stack):
                  self.print_stack_entry(frame_lineno, frame_index=frame_index)
          except KeyboardInterrupt:
              pass
  
      def print_stack_entry(
          self, frame_lineno, prompt_prefix=pdb.line_prefix, frame_index=None
      ):
          print(
              self._get_formatted_stack_entry(frame_lineno, prompt_prefix, frame_index),
              file=self.stdout,
          )
  
      def _get_formatted_stack_entry(
          self, frame_lineno, prompt_prefix=pdb.line_prefix, frame_index=None
      ):
          frame, lineno = frame_lineno
          if frame is self.curframe:
              marker = "> "
          else:
              marker = "  "
  
          frame_prefix_width = len(str(len(self.stack)))
          if frame_index is None:
              frame_index = self.curindex
              fmt = "{frame_prefix}{marker}"
              lprefix = prompt_prefix  # "\n ->" by default
          else:
              # via/for stack trace
              fmt = "{marker}{frame_prefix}"
              lprefix = "\n     " + (" " * frame_prefix_width)
  
          # Format stack index (keeping same width across stack).
          frame_prefix = ("[%%%dd] " % frame_prefix_width) % frame_index
          marker_frameno = fmt.format(marker=marker, frame_prefix=frame_prefix)
  
          return marker_frameno + self.format_stack_entry(frame_lineno, lprefix)
  
      def print_current_stack_entry(self):
          if self.sticky:
              self._print_if_sticky()
          else:
              self.print_stack_entry(self.stack[self.curindex])
  
      def preloop(self):
          self._print_if_sticky()
  
          display_list = self._get_display_list()
          for expr, oldvalue in display_list.items():
              newvalue = self._getval_or_undefined(expr)
              # check for identity first; this prevents custom __eq__ to
              # be called at every loop, and also prevents instances
              # whose fields are changed to be displayed
              if newvalue is not oldvalue or newvalue != oldvalue:
                  display_list[expr] = newvalue
                  print('%s: %r --> %r' % (expr, oldvalue, newvalue),
                        file=self.stdout)
  
      def _get_position_of_arg(self, arg):
          try:
              obj = self._getval(arg)
          except:
              return None, None, None
          return self._get_position_of_obj(obj)
  
      def _get_position_of_obj(self, obj, quiet=False):
          if hasattr(inspect, "unwrap"):
              obj = inspect.unwrap(obj)
          if isinstance(obj, str):
              return obj, 1, None
          try:
              filename = inspect.getabsfile(obj)
              lines, lineno = inspect.getsourcelines(obj)
          except (IOError, TypeError) as e:
              if not quiet:
                  print('** Error: %s **' % e, file=self.stdout)
              return None, None, None
          return filename, lineno, lines
  
      def do_source(self, arg):
          _, lineno, lines = self._get_position_of_arg(arg)
          if lineno is None:
              return
          self._print_lines_pdbpp(lines, lineno, print_markers=False)
  
      def do_frame(self, arg):
          """f(rame) [index]
          Go to given frame.  The first frame is 0, negative index is counted
          from the end (i.e. -1 is the last one).
          Without argument, display current frame.
          """
          if not arg:
              # Just display the frame, without handling sticky.
              self.print_stack_entry(self.stack[self.curindex])
              return
  
          try:
              arg = int(arg)
          except (ValueError, TypeError):
              print(
                  '*** Expected a number, got "{0}"'.format(arg), file=self.stdout)
              return
          if abs(arg) >= len(self.stack):
              print('*** Out of range', file=self.stdout)
              return
          if arg >= 0:
              self.curindex = arg
          else:
              self.curindex = len(self.stack) + arg
          self.curframe = self.stack[self.curindex][0]
          self.curframe_locals = self.curframe.f_locals
          self.print_current_stack_entry()
          self.lineno = None
      do_f = do_frame
  
      def do_up(self, arg='1'):
          arg = '1' if arg == '' else arg
          try:
              arg = int(arg)
          except (ValueError, TypeError):
              print(
                  '*** Expected a number, got "{0}"'.format(arg), file=self.stdout)
              return
          if self.curindex - arg < 0:
              print('*** Oldest frame', file=self.stdout)
          else:
              self.curindex = self.curindex - arg
              self.curframe = self.stack[self.curindex][0]
              self.curframe_locals = self.curframe.f_locals
              self.print_current_stack_entry()
              self.lineno = None
      do_up.__doc__ = pdb.Pdb.do_up.__doc__
      do_u = do_up
  
      def do_down(self, arg='1'):
          arg = '1' if arg == '' else arg
          try:
              arg = int(arg)
          except (ValueError, TypeError):
              print(
                  '*** Expected a number, got "{0}"'.format(arg), file=self.stdout)
              return
          if self.curindex + arg >= len(self.stack):
              print('*** Newest frame', file=self.stdout)
          else:
              self.curindex = self.curindex + arg
              self.curframe = self.stack[self.curindex][0]
              self.curframe_locals = self.curframe.f_locals
              self.print_current_stack_entry()
              self.lineno = None
      do_down.__doc__ = pdb.Pdb.do_down.__doc__
      do_d = do_down
  
      def do_top(self, arg):
          """Go to top (oldest) frame."""
          if self.curindex == 0:
              self.error('Oldest frame')
              return
          self._select_frame(0)
      do_top = do_top
  
      def do_bottom(self, arg):
          """Go to bottom (newest) frame."""
          if self.curindex + 1 == len(self.stack):
              self.error('Newest frame')
              return
          self._select_frame(len(self.stack) - 1)
      do_bottom = do_bottom
  
      @staticmethod
      def get_terminal_size():
          fallback = (80, 24)
          try:
              from shutil import get_terminal_size
          except ImportError:
              try:
                  import termios
                  import fcntl
                  import struct
                  call = fcntl.ioctl(0, termios.TIOCGWINSZ, "\x00"*8)
                  height, width = struct.unpack("hhhh", call)[:2]
              except (SystemExit, KeyboardInterrupt):
                  raise
              except:
                  width = int(os.environ.get('COLUMNS', fallback[0]))
                  height = int(os.environ.get('COLUMNS', fallback[1]))
              # Work around above returning width, height = 0, 0 in Emacs
              width = width if width != 0 else fallback[0]
              height = height if height != 0 else fallback[1]
              return width, height
          else:
              return get_terminal_size(fallback)
  
      def _open_editor(self, editcmd):
          """Extra method to allow for easy override in tests."""
          subprocess.Popen(editcmd, shell=True).communicate()
  
      def _get_current_position(self):
          frame = self.curframe
          lineno = frame.f_lineno
          filename = os.path.abspath(frame.f_code.co_filename)
          return filename, lineno
  
      def _quote_filename(self, filename):
          try:
              from shlex import quote
          except ImportError:
              from pipes import quote
  
          return quote(filename)
  
      def _format_editcmd(self, editor, filename, lineno):
          filename = self._quote_filename(filename)
  
          if "{filename}" in editor:
              return editor.format(filename=filename, lineno=lineno)
  
          if "%s" not in editor:
              # backward compatibility.
              return "%s +%d %s" % (editor, lineno, filename)
  
          # Replace %s with filename, %d with lineno; %% becomes %.
          return editor.replace("%%", "%").replace("%s", filename).replace(
              "%d", str(lineno))
  
      def _get_editor_cmd(self, filename, lineno):
          editor = self.config.editor
          if editor is None:
              try:
                  editor = os.environ["EDITOR"]
              except KeyError:
                  try:
                      from shutil import which
                  except ImportError:
                      from distutils.spawn import find_executable as which
                  editor = which("vim")
                  if editor is None:
                      editor = which("vi")
              if not editor:
                  raise RuntimeError("Could not detect editor. Configure it or set $EDITOR.")  # noqa: E501
          return self._format_editcmd(editor, filename, lineno)
  
      def do_edit(self, arg):
          "Open an editor visiting the current file at the current line"
          if arg == '':
              filename, lineno = self._get_current_position()
          else:
              filename, lineno, _ = self._get_position_of_arg(arg)
              if filename is None:
                  return
          # this case handles code generated with py.code.Source()
          # filename is something like '<0-codegen foo.py:18>'
          match = re.match(r'.*<\d+-codegen (.*):(\d+)>', filename)
          if match:
              filename = match.group(1)
              lineno = int(match.group(2))
  
          try:
              self._open_editor(self._get_editor_cmd(filename, lineno))
          except Exception as exc:
              self.error(exc)
  
      do_ed = do_edit
  
      def _get_history(self):
          return [s for s in self.history if not side_effects_free.match(s)]
  
      def _get_history_text(self):
          import linecache
          line = linecache.getline(self.start_filename, self.start_lineno)
          nspaces = len(line) - len(line.lstrip())
          indent = ' ' * nspaces
          history = [indent + s for s in self._get_history()]
          return '\n'.join(history) + '\n'
  
      def _open_stdin_paste(self, stdin_paste, lineno, filename, text):
          proc = subprocess.Popen([stdin_paste, '+%d' % lineno, filename],
                                  stdin=subprocess.PIPE)
          proc.stdin.write(text)
          proc.stdin.close()
  
      def _put(self, text):
          stdin_paste = self.config.stdin_paste
          if stdin_paste is None:
              print('** Error: the "stdin_paste" option is not configured **',
                    file=self.stdout)
          filename = self.start_filename
          lineno = self.start_lineno
          self._open_stdin_paste(stdin_paste, lineno, filename, text)
  
      def do_put(self, arg):
          text = self._get_history_text()
          self._put(text)
  
      def do_paste(self, arg):
          arg = arg.strip()
          old_stdout = self.stdout
          self.stdout = StringIO()
          self.onecmd(arg)
          text = self.stdout.getvalue()
          self.stdout = old_stdout
          sys.stdout.write(text)
          self._put(text)
  
      def set_step(self):
          """Use set_next() via set_trace() when re-using Pdb instance.
  
          But call set_step() before still for handling of frame_returning."""
          super(Pdb, self).set_step()
          if hasattr(self, "_set_trace_use_next"):
              del self._set_trace_use_next
              self.set_next(self._via_set_trace_frame)
  
      def stop_here(self, frame):
          # Always stop at starting frame (https://bugs.python.org/issue38806).
          if self.stopframe is None:
              if getattr(self, "_via_set_trace_frame", None) == frame:
                  if not self._stopped_for_set_trace:
                      self._stopped_for_set_trace = True
                      return True
          if Pdb is not None:
              return super(Pdb, self).stop_here(frame)
  
      def set_trace(self, frame=None):
          """Remember starting frame.
  
          This is used with pytest, which does not use pdb.set_trace().
          """
          if getattr(local, "_pdbpp_completing", False):
              # Handle set_trace being called during completion, e.g. with
              # fancycompleter's attr_matches.
              return
          if self.disabled:
              return
  
          if frame is None:
              frame = sys._getframe().f_back
          self._via_set_trace_frame = frame
          self._stopped_for_set_trace = False
  
          self.start_filename = frame.f_code.co_filename
          self.start_lineno = frame.f_lineno
  
          return super(Pdb, self).set_trace(frame)
  
      def is_skipped_module(self, module_name):
          """Backport for https://bugs.python.org/issue36130.
  
          Fixed in Python 3.8+.
          """
          if module_name is None:
              return False
          return super(Pdb, self).is_skipped_module(module_name)
  
      def message(self, msg):
          if self.sticky:
              if sys._getframe().f_back.f_code.co_name == "user_exception":
                  # Exceptions are handled in sticky mode explicitly.
                  return
              self._sticky_messages.append(msg)
              return
          print(msg, file=self.stdout)
  
      def error(self, msg):
          """Override/enhance default error method to display tracebacks."""
          self._seen_error = msg
          print("***", msg, file=self.stdout)
  
          if not self.config.show_traceback_on_error:
              return
  
          etype, evalue, tb = sys.exc_info()
          if tb and tb.tb_frame.f_code.co_name == "default":
              tb = tb.tb_next
              if tb and tb.tb_frame.f_code.co_filename == "<stdin>":
                  tb = tb.tb_next
                  if tb:  # only display with actual traceback.
                      self._remove_bdb_context(evalue)
                      tb_limit = self.config.show_traceback_on_error_limit
                      fmt_exc = traceback.format_exception(
                          etype, evalue, tb, limit=tb_limit
                      )
  
                      # Remove last line (exception string again).
                      if len(fmt_exc) > 1 and fmt_exc[-1][0] != " ":
                          fmt_exc.pop()
  
                      print("".join(fmt_exc).rstrip(), file=self.stdout)
  
      @staticmethod
      def _remove_bdb_context(evalue):
          """Remove exception context from Pdb from the exception.
  
          E.g. "AttributeError: 'Pdb' object has no attribute 'do_foo'",
          when trying to look up commands (bpo-36494).
          Only done for Python 3+.
          """
          if not hasattr(evalue, "__context__"):
              return
  
          removed_bdb_context = evalue
          while removed_bdb_context.__context__:
              ctx = removed_bdb_context.__context__
              if (
                  isinstance(ctx, AttributeError)
                  and ctx.__traceback__.tb_frame.f_code.co_name == "onecmd"
              ):
                  removed_bdb_context.__context__ = None
                  break
              removed_bdb_context = removed_bdb_context.__context__
  
  
  # simplified interface
  
  if hasattr(pdb, 'Restart'):
      Restart = pdb.Restart
  
  if hasattr(pdb, '_usage'):
      _usage = pdb._usage
  
  # copy some functions from pdb.py, but rebind the global dictionary
  for name in 'run runeval runctx runcall main set_trace'.split():
      func = getattr(pdb, name)
      globals()[name] = rebind_globals(func, globals())
  del name, func
  
  
  # Post-Mortem interface
  
  def post_mortem(t=None, Pdb=Pdb):
      # handling the default
      if t is None:
          # sys.exc_info() returns (type, value, traceback) if an exception is
          # being handled, otherwise it returns None
          t = sys.exc_info()[2]
      if t is None:
          raise ValueError("A valid traceback must be passed if no "
                           "exception is being handled")
  
      p = Pdb()
      p.reset()
      p.interaction(None, t)
  
  
  def pm(Pdb=Pdb):
      post_mortem(sys.last_traceback, Pdb=Pdb)
  
  
  def cleanup():
      local.GLOBAL_PDB = None
      local._pdbpp_completing = False
  
  # pdb++ specific interface
  
  
  def xpm(Pdb=Pdb):
      """
      To be used inside an except clause, enter a post-mortem pdb
      related to the just caught exception.
      """
      info = sys.exc_info()
      print(traceback.format_exc())
      post_mortem(info[2], Pdb)
  
  
  def enable():
      global set_trace
      set_trace = enable.set_trace
      if local.GLOBAL_PDB:
          local.GLOBAL_PDB.disabled = False
  
  
  enable.set_trace = set_trace
  
  
  def disable():
      global set_trace
      set_trace = disable.set_trace
      if local.GLOBAL_PDB:
          local.GLOBAL_PDB.disabled = True
  
  
  disable.set_trace = lambda frame=None, Pdb=Pdb: None
  
  
  def set_tracex():
      print('PDB!')
  
  
  set_tracex._dont_inline_ = True
  
  _HIDE_FRAME = object()
  
  
  def hideframe(func):
      c = func.__code__
      new_co_consts = c.co_consts + (_HIDE_FRAME,)
  
      if hasattr(c, "replace"):
          # Python 3.8.
          c = c.replace(co_consts=new_co_consts)
      elif sys.version_info < (3, ):
          c = types.CodeType(
              c.co_argcount, c.co_nlocals, c.co_stacksize,
              c.co_flags, c.co_code,
              new_co_consts,
              c.co_names, c.co_varnames, c.co_filename,
              c.co_name, c.co_firstlineno, c.co_lnotab,
              c.co_freevars, c.co_cellvars)
      else:
          # Python 3 takes an additional arg -- kwonlyargcount.
          c = types.CodeType(
              c.co_argcount, c.co_kwonlyargcount, c.co_nlocals, c.co_stacksize,
              c.co_flags, c.co_code,
              c.co_consts + (_HIDE_FRAME,),
              c.co_names, c.co_varnames, c.co_filename,
              c.co_name, c.co_firstlineno, c.co_lnotab,
              c.co_freevars, c.co_cellvars)
  
      func.__code__ = c
      return func
  
  
  def always(obj, value):
      return True
  
  
  def break_on_setattr(attrname, condition=always, Pdb=Pdb):
      def decorator(cls):
          old___setattr__ = cls.__setattr__
  
          @hideframe
          def __setattr__(self, attr, value):
              if attr == attrname and condition(self, value):
                  frame = sys._getframe().f_back
                  pdb_ = Pdb()
                  pdb_.set_trace(frame)
                  pdb_.stopframe = frame
                  pdb_.interaction(frame, None)
              old___setattr__(self, attr, value)
          cls.__setattr__ = __setattr__
          return cls
      return decorator
  
  
  if __name__ == '__main__':
      import pdb
      pdb.main()
  