From f73253d1df658103531ef53f54d5e445f86013d1 Mon Sep 17 00:00:00 2001 From: bretello Date: Sun, 29 Oct 2023 16:54:53 +0100 Subject: [PATCH] drop pre-3.8 code --- README.rst | 4 +- setup.py | 3 - src/_pdbpp_path_hack/pdb.py | 4 +- src/pdbpp.py | 587 +++++++++++++++++------------------- testing/conftest.py | 5 +- testing/test_meta.py | 5 +- testing/test_pdb.py | 256 +++++----------- 7 files changed, 350 insertions(+), 514 deletions(-) diff --git a/README.rst b/README.rst index 9868e2f5..6a8f763e 100644 --- a/README.rst +++ b/README.rst @@ -203,7 +203,7 @@ pdb++. by default, it breaks every time the attribute is set. E.g.:: @break_on_setattr('bar') - class Foo(object): + class Foo: pass f = Foo() f.bar = 42 # the program breaks here @@ -211,7 +211,7 @@ pdb++. If can be used even after the class has already been created, e.g. if we want to break when some attribute of a particular object is set:: - class Foo(object): + class Foo: pass a = Foo() b = Foo() diff --git a/setup.py b/setup.py index e2e8a1bd..7a554f65 100644 --- a/setup.py +++ b/setup.py @@ -50,12 +50,9 @@ "fancycompleter @ git+https://github.com/pdbpp/fancycompleter@master#egg=fancycompleter", # noqa: E501 "wmctrl", "pygments", - "six", ], extras_require={ - "funcsigs": ["funcsigs"], "testing": [ - "funcsigs", "pytest", ], }, diff --git a/src/_pdbpp_path_hack/pdb.py b/src/_pdbpp_path_hack/pdb.py index a89b7eaf..77bf5772 100644 --- a/src/_pdbpp_path_hack/pdb.py +++ b/src/_pdbpp_path_hack/pdb.py @@ -7,14 +7,14 @@ import os import sys -pdb_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'pdbpp.py') +pdb_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "pdbpp.py") # Set __file__ to exec'd code. This is good in general, and required for # coverage.py to use it. __file__ = pdb_path with open(pdb_path) as f: - exec(compile(f.read(), pdb_path, 'exec')) + exec(compile(f.read(), pdb_path, "exec")) # Update/set __file__ attribute to actually sourced file, but not when not coming # here via "-m pdb", where __name__ is "__main__". diff --git a/src/pdbpp.py b/src/pdbpp.py index 6ca978f3..8431acf5 100644 --- a/src/pdbpp.py +++ b/src/pdbpp.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ pdb++, a drop-in replacement for pdb ==================================== @@ -7,79 +6,40 @@ 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 inspect +import os.path import pprint import re +import shlex import signal +import subprocess +import sys +import threading +import traceback +import types from collections import OrderedDict +from functools import lru_cache +from inspect import signature +from io import StringIO import fancycompleter -import six from fancycompleter import Color, Completer, ConfigurableClass -__author__ = 'Antonio Cuni ' -__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 difference.""" - - 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 +__author__ = "Antonio Cuni " +__url__ = "http://github.com/antocuni/pdb" +__version__ = fancycompleter.LazyVersion("pdbpp") - 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\[\].]* *$') +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 @@ -87,7 +47,7 @@ def wrapper(*args): local._pdbpp_in_init = False -def __getattr__(name): +def __getattr__(name): # FIXME: ??? """Backward compatibility (Python 3.7+)""" if name == "GLOBAL_PDB": return local.GLOBAL_PDB @@ -96,27 +56,28 @@ def __getattr__(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') + pyfile = os.path.join(stdlibdir, name + ".py") with open(pyfile) as f: src = f.read() - co_module = compile(src, pyfile, 'exec', dont_inherit=True) + co_module = compile(src, pyfile, "exec", dont_inherit=True) exec(co_module, result.__dict__) return result -pdb = import_from_stdlib('pdb') +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__ + newfunc = types.FunctionType( + func.__code__, newglobals, func.__name__, func.__defaults__, func.__closure__ + ) + newfunc.__annotations__ = func.__annotations__ + newfunc.__kwdefaults__ = func.__kwdefaults__ return newfunc @@ -134,8 +95,8 @@ def rebind_globals(func, newglobals): raise ValueError("cannot handle func {!r}".format(func)) -class DefaultConfig(object): - prompt = '(Pdb++) ' +class DefaultConfig: + prompt = "(Pdb++) " highlight = True sticky_by_default = False @@ -144,15 +105,15 @@ class DefaultConfig(object): pygments_formatter_class = None # Defaults to autodetect, based on $TERM. pygments_formatter_kwargs = {} # Legacy options. Should use pygments_formatter_kwargs instead. - bg = 'dark' + bg = "dark" colorscheme = None editor = None # Autodetected if unset. - stdin_paste = None # for emacs, you can use my bin/epaste script + 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') + encodings = ("utf-8", "latin-1") enable_hidden_frames = True show_hidden_frames_count = True @@ -165,8 +126,7 @@ class DefaultConfig(object): show_traceback_on_error_limit = None # Default keyword arguments passed to ``Pdb`` constructor. - default_pdb_kwargs = { - } + default_pdb_kwargs = {} def setup(self, pdb): pass @@ -179,10 +139,11 @@ 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': + + 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 @@ -193,17 +154,18 @@ def setbgcolor(line, color): # 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;') + 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' +CLEARSCREEN = "\033[2J\033[1;1H" def lasti2lineno(code, lasti): import dis + linestarts = list(dis.findlinestarts(code)) linestarts.reverse() for i, lineno in linestarts: @@ -214,7 +176,7 @@ def lasti2lineno(code, lasti): class Undefined: def __repr__(self): - return '' + return "" undefined = Undefined() @@ -222,8 +184,9 @@ def __repr__(self): 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 = super().__new__(cls, value) obj.cmd_count = count return obj @@ -231,7 +194,7 @@ def __repr__(self): return "<{} cmd_count={!r} value={}>".format( self.__class__.__name__, self.cmd_count, - super(ArgWithCount, self).__repr__(), + super().__repr__(), ) @@ -241,12 +204,14 @@ def __call__(cls, *args, **kwargs): # Prevent recursion errors with pdb.set_trace() during init/debugging. if getattr(local, "_pdbpp_in_init", False): - class OrigPdb(pdb.Pdb, object): + + class OrigPdb(pdb.Pdb): 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) + super().set_trace(frame) + orig_pdb = OrigPdb.__new__(OrigPdb) # Remove any pdb++ only kwargs. kwargs.pop("Config", None) @@ -316,12 +281,10 @@ 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) is C: + 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 @@ -340,20 +303,18 @@ def called_for_set_trace(frame): return called_for_set_trace -@six.add_metaclass(PdbMeta) -class Pdb(pdb.Pdb, ConfigurableClass, object): - +class Pdb(pdb.Pdb, ConfigurableClass, metaclass=PdbMeta): DefaultConfig = DefaultConfig - config_filename = '.pdbrc.py' + 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.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) @@ -366,7 +327,7 @@ def __init__(self, *args, **kwds): self._disable_pytest_capture_maybe() kwargs = self.config.default_pdb_kwargs.copy() kwargs.update(**kwds) - super(Pdb, self).__init__(*args, **kwargs) + super().__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 @@ -402,22 +363,25 @@ def _setup_streams(self, 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)) + 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 = py.test.config.pluginmanager.getplugin("capturemanager") capman.suspendcapture() except KeyError: pass @@ -458,7 +422,7 @@ def interaction(self, frame, traceback): def _interaction(self, frame, traceback): # Restore the previous signal handler at the Pdb prompt. - if getattr(pdb.Pdb, '_previous_sigint_handler', None): + 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 @@ -478,7 +442,7 @@ def _interaction(self, frame, traceback): if frame is None and traceback: exc = sys.exc_info()[:2] if exc != (None, None): - self.curframe.f_locals['__exception__'] = exc + self.curframe.f_locals["__exception__"] = exc if not self.sticky: self.print_stack_entry(self.stack[self.curindex]) @@ -487,7 +451,7 @@ def _interaction(self, frame, traceback): with self._custom_completer(): self.config.before_interaction_hook(self) # Use _cmdloop on py3 which catches KeyboardInterrupt. - if hasattr(self, '_cmdloop'): + if hasattr(self, "_cmdloop"): self._cmdloop() else: self.cmdloop() @@ -495,7 +459,7 @@ def _interaction(self, frame, traceback): self.forget() def break_here(self, frame): - ret = super(Pdb, self).break_here(frame) + ret = super().break_here(frame) if ret: # Skip clearing screen if invoked via breakpoint, which e.g. # might execute/display output from commands. @@ -515,7 +479,7 @@ def _sticky_handle_cls(self): def postcmd(self, stop, line): """Handle clearing of the screen for sticky mode.""" - stop = super(Pdb, self).postcmd(stop, line) + stop = super().postcmd(stop, line) if self.sticky: if stop and not self.commands_defining: self._sticky_handle_cls() @@ -533,12 +497,12 @@ def _flush_sticky_messages(self): def set_continue(self): if self.sticky: self._sticky_skip_cls = True - super(Pdb, self).set_continue() + super().set_continue() def set_quit(self): if self.sticky: self._sticky_skip_cls = True - super(Pdb, self).set_quit() + super().set_quit() def _setup_fancycompleter(self): """Similar to fancycompleter.setup(), but returning the old completer.""" @@ -550,7 +514,7 @@ def _setup_fancycompleter(self): if fancycompleter.has_leopard_libedit(completer.config): readline.parse_and_bind("bind ^I rl_complete") else: - readline.parse_and_bind('tab: complete') + readline.parse_and_bind("tab: complete") readline.set_completer(self.complete) return old_completer @@ -575,19 +539,24 @@ def print_hidden_frames_count(self): def exec_if_unfocused(self): import os + import wmctrl - term = os.getenv('TERM', '') + + term = os.getenv("TERM", "") try: - winid = int(os.getenv('WINDOWID')) + 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')): + 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) + ret = super().setup(frame, tb) if not ret: while tb: lineno = lasti2lineno(tb.tb_frame.f_code, tb.tb_lasti) @@ -608,7 +577,7 @@ def _is_hidden(self, frame): if frame is getattr(self, "_via_set_trace_frame", None): return False - if frame.f_globals.get('__unittest'): + if frame.f_globals.get("__unittest"): return True # `f_locals` might be a list (checked via PyMapping_Check). @@ -623,7 +592,7 @@ def _is_hidden(self, frame): 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) + fullstack, idx = super().get_stack(f, t) self.fullstack = fullstack return self.compute_stack(fullstack, idx) @@ -658,7 +627,7 @@ def refresh_stack(self): self.curindex = i break else: - self.curindex = len(self.stack)-1 + self.curindex = len(self.stack) - 1 self.curframe = self.stack[-1][0] self.print_current_stack_entry() @@ -672,7 +641,7 @@ def reset(self): def forget(self): if not getattr(local, "_pdbpp_completing", False): - super(Pdb, self).forget() + super().forget() @classmethod def _get_all_completions(cls, complete, text): @@ -707,7 +676,7 @@ def complete(self, text, state): try: return self._complete(text, state) except Exception as exc: - self.error("error during completion: {}".format(exc)) + self.error(f"error during completion: {exc}") if self.config.show_traceback_on_error: __import__("traceback").print_exc(file=self.stdout) @@ -725,17 +694,19 @@ def _complete(self, text, state): 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 - ]) + 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) + real_pdb = super() for x in self._get_all_completions(real_pdb.complete, text): if x not in clean_fancy_completions: pdb_completions.append(x) @@ -748,10 +719,10 @@ def _complete(self, text, state): self._completions = completions if pdb_completions: pdb_prefix = fancycompleter.commonprefix(pdb_completions) - if '.' in text and pdb_prefix and len(pdb_completions) > 1: + 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]) + '.' + dotted = text.split(".") + prefix = ".".join(dotted[:-1]) + "." prefix_len = len(prefix) pdb_completions = [ x[prefix_len:] if x.startswith(prefix) else x @@ -790,21 +761,17 @@ def _filter_completions(self, text): if text[-1:] != "_": self._completions = [ - x - for x in self._completions - if RE_COLOR_ESCAPES.sub("", x)[:1] != "_" + 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] != "__" + x for x in self._completions if RE_COLOR_ESCAPES.sub("", x)[:2] != "__" ] - stack_entry_regexp = re.compile(r'(.*?)\(([0-9]+?)\)(.*)', re.DOTALL) + 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) + def format_stack_entry(self, frame_lineno, lprefix=": "): + entry = super().format_stack_entry(frame_lineno, lprefix) entry = self.try_to_decode(entry) if self.config.highlight: match = self.stack_entry_regexp.match(entry) @@ -812,7 +779,7 @@ def format_stack_entry(self, frame_lineno, lprefix=': '): 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) + entry = f"{filename}({lineno}){other}" if self.config.use_pygments is not False: loc, _, source = entry.rpartition(lprefix) if _: @@ -845,9 +812,7 @@ def _get_source_highlight_function(self): try: pygments_formatter = self._get_pygments_formatter() except Exception as exc: - self.message("pdb++: could not setup Pygments, disabling: {}".format( - exc - )) + self.message("pdb++: could not setup Pygments, disabling: {}".format(exc)) return False lexer = pygments.lexers.PythonLexer(stripnl=False) @@ -858,7 +823,7 @@ def syntax_highlight(src): return syntax_highlight def _get_pygments_formatter(self): - if hasattr(self.config, 'formatter'): + 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. @@ -868,7 +833,7 @@ def _get_pygments_formatter(self): from importlib import import_module def import_string(dotted_path): - module_path, class_name = dotted_path.rsplit('.', 1) + module_path, class_name = dotted_path.rsplit(".", 1) module = import_module(module_path) return getattr(module, class_name) @@ -922,36 +887,36 @@ def _highlight_cached(self, src): return self._highlight(src) def _format_line(self, lineno, marker, line, lineno_width): - lineno = ('%%%dd' % lineno_width) % lineno + 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) + line = "%s %2s %s" % (lineno, marker, line) return line def execRcLines(self): self._pdbpp_executing_rc_lines = True try: - return super(Pdb, self).execRcLines() + return super().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) + return super().parseline(line) - if line.startswith('!!'): + if line.startswith("!!"): # Force the "standard" behaviour, i.e. first check for the # command, then for the variable name to display. line = line[2:] - cmd, arg, newline = super(Pdb, self).parseline(line) + cmd, arg, newline = super().parseline(line) return cmd, arg, "!!" + newline - if line.endswith('?') and not line.startswith("!"): - arg = line.split('?', 1)[0] - if line.endswith('??'): + 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) + elif arg == "" or ( + hasattr(self, "do_" + arg) and arg not in self.curframe.f_globals and arg not in self.curframe_locals ): @@ -963,7 +928,7 @@ def parseline(self, 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 mistake. - cmd, arg, newline = super(Pdb, self).parseline(line) + cmd, arg, newline = super().parseline(line) if cmd: # prefixed strings. @@ -1022,42 +987,44 @@ def _do_inspect(self, arg, with_source=False): return data = OrderedDict() - data['Type'] = type(obj).__name__ - data['String Form'] = str(obj).strip() + data["Type"] = type(obj).__name__ + data["String Form"] = str(obj).strip() try: - data['Length'] = str(len(obj)) + data["Length"] = str(len(obj)) except TypeError: pass try: - data['File'] = inspect.getabsfile(obj) + data["File"] = inspect.getabsfile(obj) except TypeError: pass else: try: - data['File'] += ":" + str(obj.__code__.co_firstlineno) + data["File"] += ":" + str(obj.__code__.co_firstlineno) except AttributeError: pass - if (isinstance(obj, type) - and hasattr(obj, '__init__') - and getattr(obj, '__module__') != '__builtin__'): + 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'] = '' + data["Docstring"] = inspect.getdoc(obj) + data["Constructor information"] = "" try: - data[' Definition'] = '%s%s' % (arg, signature(obj)) + data[" Definition"] = "%s%s" % (arg, signature(obj)) except ValueError: pass - data[' Docstring'] = inspect.getdoc(obj.__init__) + data[" Docstring"] = inspect.getdoc(obj.__init__) else: try: - data['Definition'] = '%s%s' % (arg, signature(obj)) + data["Definition"] = "%s%s" % (arg, signature(obj)) except (TypeError, ValueError): pass - data['Docstring'] = inspect.getdoc(obj) + data["Docstring"] = inspect.getdoc(obj) for key, value in data.items(): - formatted_key = Color.set(Color.red, key + ':') + formatted_key = Color.set(Color.red, key + ":") if value is None: continue if value: @@ -1066,12 +1033,11 @@ def _do_inspect(self, arg, with_source=False): if lines: indent = " " * 16 formatted_value += "\n" + "\n".join( - indent + line - for line in lines.splitlines() + indent + line for line in lines.splitlines() ) else: formatted_value = "" - self.stdout.write('%-28s %s\n' % (formatted_key, formatted_value)) + self.stdout.write("%-28s %s\n" % (formatted_key, formatted_value)) if with_source: self.stdout.write("%-28s" % Color.set(Color.red, "Source:")) @@ -1088,13 +1054,13 @@ def default(self, line): Fixes https://bugs.python.org/issue21161. """ self.history.append(line) - if line[:1] == '!': + if line[:1] == "!": line = line[1:] locals = self.curframe_locals ns = self.curframe.f_globals.copy() ns.update(locals) try: - code = compile(line + '\n', '', 'single') + code = compile(line + "\n", "", "single") save_stdout = sys.stdout save_stdin = sys.stdin save_displayhook = sys.displayhook @@ -1113,14 +1079,15 @@ def default(self, line): def do_help(self, arg): try: - return super(Pdb, self).do_help(arg) + return super().do_help(arg) except AttributeError: - print("*** No help for '{command}'".format(command=arg), - file=self.stdout) + 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("""\ + 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 @@ -1142,7 +1109,9 @@ def help_hidden_frames(self): Note that the initial frame where ``set_trace`` was called from is not hidden, except for when using the function decorator. -""", file=self.stdout) +""", + file=self.stdout, + ) def do_hf_unhide(self, arg): """ @@ -1163,8 +1132,9 @@ def do_hf_hide(self, arg): 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) + print( + self.format_stack_entry(frame_lineno, pdb.line_prefix), file=self.stdout + ) def do_longlist(self, arg): """ @@ -1179,14 +1149,14 @@ def do_longlist(self, arg): If the 'highlight' config option is set and pygments is installed, the source code is colorized. """ - self.lastcmd = 'longlist' + 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 == '': + if self.curframe.f_code.co_name == "": # 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 @@ -1197,17 +1167,19 @@ def _printlonglist(self, linerange=None, max_lines=None): try: lines, lineno = inspect.getsourcelines(self.curframe) except Exception as e: - print('** Error in inspect.getsourcelines: %s **' % - e, file=self.stdout) + print( + "** Error in inspect.getsourcelines: %s **" % e, + file=self.stdout, + ) return - except IOError as e: - print('** Error: %s **' % e, file=self.stdout) + except OSError 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] + end = min(end, lineno + len(lines)) + lines = lines[start - lineno : end - lineno] lineno = start self._print_lines_pdbpp(lines, lineno, max_lines=max_lines) @@ -1241,7 +1213,7 @@ def _truncate_to_visible_length(s, maxlength): else: assert maxlength - total_visible_len > 0 rest = s[m_end:] - ret += rest[:maxlength - total_visible_len] + ret += rest[: maxlength - total_visible_len] # Keep reset sequence (in last match). if len(ret) != len(s): @@ -1267,10 +1239,9 @@ def _cut_lines(self, lines, lineno, max_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)' + COLOR_OR_SPACE = r"(?:\x1b[^m]+m|\s)" keep_pat = re.compile( - r'(?:^{col}*@)' - r'|(? 3: yield lineno, lines[0] - yield None, '...' - yield lineno + keep_head, lines[keep_head-1] + 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 + 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( @@ -1306,29 +1277,29 @@ def _cut_lines(self, lines, lineno, max_lines): if cut_before: cut_before -= 1 if cut_before == 0: - yield None, '...' + yield None, "..." else: assert cut_before > 0, cut_before continue elif cut_after and i >= len(lines) - cut_after: - yield None, '...' + 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 + 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)) + 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] + 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) @@ -1342,11 +1313,11 @@ def _print_lines_pdbpp(self, lines, lineno, print_markers=True, max_lines=None): continue if lineno == self.curframe.f_lineno: - marker = '->' + marker = "->" elif lineno == exc_lineno: - marker = '>>' + marker = ">>" else: - marker = '' + marker = "" line = self._format_line(lineno, marker, line, lineno_width) if marker == "->" and set_bg: @@ -1356,9 +1327,9 @@ def _print_lines_pdbpp(self, lines, lineno, print_markers=True, max_lines=None): new_lines.append(line) else: for i, line in enumerate(lines): - new_lines.append(self._format_line(lineno, '', line, lineno_width)) + new_lines.append(self._format_line(lineno, "", line, lineno_width)) lineno += 1 - print('\n'.join(new_lines), file=self.stdout) + print("\n".join(new_lines), file=self.stdout) def _format_color_prefixes(self, lines): if not lines: @@ -1372,7 +1343,7 @@ def _format_color_prefixes(self, lines): src_lines = [] for x in lines: - prefix, _, src = x.partition('\t') + prefix, _, src = x.partition("\t") prefixes.append(prefix) src_lines.append(src) @@ -1381,15 +1352,12 @@ def _format_color_prefixes(self, lines): prefixes = [ RE_LNUM_PREFIX.sub( lambda m: Color.set(self.config.line_number_color, m.group(0)), - prefix + prefix, ) for prefix in prefixes ] - return [ - "%s\t%s" % (prefix, src) - for (prefix, src) in zip(prefixes, src_lines) - ] + return ["%s\t%s" % (prefix, src) for (prefix, src) in zip(prefixes, src_lines)] @contextlib.contextmanager def _patch_linecache_for_source_highlight(self): @@ -1400,9 +1368,6 @@ def wrapped_getlines(filename, globals): lines = orig(filename, globals) 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 @@ -1415,13 +1380,12 @@ def wrapped_getlines(filename, globals): 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) + return super().do_list(arg) with self._patch_linecache_for_source_highlight(): - oldstdout = self.stdout self.stdout = StringIO() - ret = super(Pdb, self).do_list(arg) + ret = super().do_list(arg) orig_pdb_lines = self.stdout.getvalue().splitlines() self.stdout = oldstdout @@ -1442,12 +1406,13 @@ def _select_frame(self, number): self.lineno = None def do_continue(self, arg): - if arg != '': + if arg != "": self._seen_error = False self.do_tbreak(arg) if self._seen_error: return 0 - return super(Pdb, self).do_continue('') + return super().do_continue("") + do_continue.__doc__ = pdb.Pdb.do_continue.__doc__ do_c = do_cont = do_continue @@ -1485,6 +1450,7 @@ def do_pp(self, arg): 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): @@ -1503,7 +1469,7 @@ def do_debug(self, arg): class PdbppWithConfig(self.__class__): def __init__(self_withcfg, *args, **kwargs): kwargs.setdefault("Config", Config) - super(PdbppWithConfig, self_withcfg).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) # Backport of fix for bpo-31078 (not yet merged). self_withcfg.use_rawinput = self.use_rawinput @@ -1554,8 +1520,9 @@ def do_track(self, arg): try: from rpython.translator.tool.reftracker import track except ImportError: - print('** cannot import pypy.translator.tool.reftracker **', - file=self.stdout) + print( + "** cannot import pypy.translator.tool.reftracker **", file=self.stdout + ) return try: val = self._getval(arg) @@ -1569,8 +1536,7 @@ def _get_display_list(self): def _getval_or_undefined(self, arg): try: - return eval(arg, self.curframe.f_globals, - self.curframe_locals) + return eval(arg, self.curframe.f_globals, self.curframe_locals) except NameError: return undefined @@ -1601,7 +1567,7 @@ def do_undisplay(self, arg): try: del self._get_display_list()[arg] except KeyError: - print('** %s not in the display list **' % arg, file=self.stdout) + print("** %s not in the display list **" % arg, file=self.stdout) def _print_if_sticky(self): if self.sticky and not self.commands_defining: @@ -1638,8 +1604,8 @@ def _print_if_sticky(self): 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 "__exception__" in frame.f_locals: + s = self._format_exc_for_sticky(frame.f_locals["__exception__"]) if s: after_lines.append(s) @@ -1648,16 +1614,16 @@ def _print_if_sticky(self): if s: after_lines.append(s) - elif '__return__' in frame.f_locals: - rv = frame.f_locals['__return__'] + elif "__return__" in frame.f_locals: + rv = frame.f_locals["__return__"] try: s = repr(rv) except KeyboardInterrupt: raise except: - s = '(unprintable return value)' + s = "(unprintable return value)" - s = ' return ' + s + s = " return " + s if self.config.highlight: s = Color.set(self.config.line_number_color, s) after_lines.append(s) @@ -1685,28 +1651,28 @@ def _format_exc_for_sticky(self, exc): return "pdbpp: got unexpected __exception__: %r" % (exc,) exc_type, exc_value = exc - s = '' + s = "" try: try: s = exc_type.__name__ except AttributeError: s = str(exc_type) if exc_value is not None: - s += ': ' + s += ": " s += str(exc_value) except KeyboardInterrupt: raise except Exception as exc: try: - s += '(unprintable exception: %r)' % (exc,) + s += "(unprintable exception: %r)" % (exc,) except: - s += '(unprintable exception)' + 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] + "…" + s = s[: width - 1] + "…" if self.config.highlight: s = Color.set(self.config.line_number_color, s) @@ -1731,11 +1697,10 @@ def do_sticky(self, arg): try: start, end = map(int, arg.split()) except ValueError: - print('** Error when parsing argument: %s **' % arg, - file=self.stdout) + print("** Error when parsing argument: %s **" % arg, file=self.stdout) return self.sticky = True - self.sticky_ranges[self.curframe] = start, end+1 + self.sticky_ranges[self.curframe] = start, end + 1 else: self.sticky = not self.sticky self.sticky_range = None @@ -1804,8 +1769,7 @@ def preloop(self): # 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) + print("%s: %r --> %r" % (expr, oldvalue, newvalue), file=self.stdout) def _get_position_of_arg(self, arg, quiet=False): try: @@ -1850,9 +1814,9 @@ def _get_position_of_obj(self, obj, quiet=False): try: filename = inspect.getabsfile(obj) lines, lineno = inspect.getsourcelines(obj) - except (IOError, TypeError) as e: + except (OSError, TypeError) as e: if not quiet: - self.error('could not get obj: {}'.format(e)) + self.error("could not get obj: {}".format(e)) return None, None, None return filename, lineno, lines @@ -1876,11 +1840,10 @@ def do_frame(self, arg): try: arg = int(arg) except (ValueError, TypeError): - print( - '*** Expected a number, got "{0}"'.format(arg), file=self.stdout) + print('*** Expected a number, got "{}"'.format(arg), file=self.stdout) return if abs(arg) >= len(self.stack): - print('*** Out of range', file=self.stdout) + print("*** Out of range", file=self.stdout) return if arg >= 0: self.curindex = arg @@ -1890,60 +1853,63 @@ def do_frame(self, arg): 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 + 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) + print('*** Expected a number, got "{}"'.format(arg), file=self.stdout) return if self.curindex - arg < 0: - print('*** Oldest frame', file=self.stdout) + 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 + 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) + print('*** Expected a number, got "{}"'.format(arg), file=self.stdout) return if self.curindex + arg >= len(self.stack): - print('*** Newest frame', file=self.stdout) + 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') + 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') + self.error("Newest frame") return self._select_frame(len(self.stack) - 1) + do_bottom = do_bottom @staticmethod @@ -1953,16 +1919,17 @@ def get_terminal_size(): from shutil import get_terminal_size except ImportError: try: - import termios import fcntl import struct - call = fcntl.ioctl(0, termios.TIOCGWINSZ, "\x00"*8) + import termios + + 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])) + 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] @@ -1980,16 +1947,8 @@ def _get_current_position(self): 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) + filename = shlex.quote(filename) if "{filename}" in editor: return editor.format(filename=filename, lineno=lineno) @@ -1999,8 +1958,9 @@ def _format_editcmd(self, editor, filename, lineno): 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)) + return ( + editor.replace("%%", "%").replace("%s", filename).replace("%d", str(lineno)) + ) def _get_editor_cmd(self, filename, lineno): editor = self.config.editor @@ -2016,12 +1976,14 @@ def _get_editor_cmd(self, filename, lineno): if editor is None: editor = which("vi") if not editor: - raise RuntimeError("Could not detect editor. Configure it or set $EDITOR.") # noqa: E501 + 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 == '': + if arg == "": filename, lineno = self._get_current_position() else: filename, lineno = self._get_fnamelineno_for_arg(arg) @@ -2030,7 +1992,7 @@ def do_edit(self, arg): 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) + match = re.match(r".*<\d+-codegen (.*):(\d+)>", filename) if match: filename = match.group(1) lineno = int(match.group(2)) @@ -2047,23 +2009,27 @@ def _get_history(self): def _get_history_text(self): import linecache + line = linecache.getline(self.start_filename, self.start_lineno) nspaces = len(line) - len(line.lstrip()) - indent = ' ' * nspaces + indent = " " * nspaces history = [indent + s for s in self._get_history()] - return '\n'.join(history) + '\n' + 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 = 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) + 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) @@ -2086,7 +2052,7 @@ 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() + super().set_step() if hasattr(self, "_set_trace_use_next"): del self._set_trace_use_next self.set_next(self._via_set_trace_frame) @@ -2099,7 +2065,7 @@ def stop_here(self, frame): self._stopped_for_set_trace = True return True if Pdb is not None: - return super(Pdb, self).stop_here(frame) + return super().stop_here(frame) def set_trace(self, frame=None): """Remember starting frame. @@ -2121,7 +2087,7 @@ def set_trace(self, frame=None): self.start_filename = frame.f_code.co_filename self.start_lineno = frame.f_lineno - return super(Pdb, self).set_trace(frame) + return super().set_trace(frame) def is_skipped_module(self, module_name): """Backport for https://bugs.python.org/issue36130. @@ -2130,7 +2096,7 @@ def is_skipped_module(self, module_name): """ if module_name is None: return False - return super(Pdb, self).is_skipped_module(module_name) + return super().is_skipped_module(module_name) def message(self, msg): if self.sticky: @@ -2192,14 +2158,14 @@ def _remove_bdb_context(evalue): # simplified interface -if hasattr(pdb, 'Restart'): +if hasattr(pdb, "Restart"): Restart = pdb.Restart -if hasattr(pdb, '_usage'): +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(): +for name in "run runeval runctx runcall main set_trace".split(): func = getattr(pdb, name) globals()[name] = rebind_globals(func, globals()) del name, func @@ -2207,6 +2173,7 @@ def _remove_bdb_context(evalue): # Post-Mortem interface + def post_mortem(t=None, Pdb=Pdb): # handling the default if t is None: @@ -2214,8 +2181,9 @@ def post_mortem(t=None, Pdb=Pdb): # 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") + raise ValueError( + "A valid traceback must be passed if no " "exception is being handled" + ) p = Pdb() p.reset() @@ -2230,6 +2198,7 @@ def cleanup(): local.GLOBAL_PDB = None local._pdbpp_completing = False + # pdb++ specific interface @@ -2264,7 +2233,7 @@ def disable(): def set_tracex(): - print('PDB!') + print("PDB!") set_tracex._dont_inline_ = True @@ -2276,26 +2245,7 @@ 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) + c = c.replace(co_consts=new_co_consts) func.__code__ = c return func @@ -2318,11 +2268,14 @@ def __setattr__(self, attr, value): pdb_.stopframe = frame pdb_.interaction(frame, None) old___setattr__(self, attr, value) + cls.__setattr__ = __setattr__ return cls + return decorator -if __name__ == '__main__': +if __name__ == "__main__": import pdbpp + pdbpp.main() diff --git a/testing/conftest.py b/testing/conftest.py index 46979bc1..672c4bce 100644 --- a/testing/conftest.py +++ b/testing/conftest.py @@ -190,10 +190,7 @@ def import_mock(name, *args): return orig_import(name, *args) with monkeypatch.context() as m: - if sys.version_info >= (3,): - m.setattr('builtins.__import__', import_mock) - else: - m.setattr('__builtin__.__import__', import_mock) + m.setattr('builtins.__import__', import_mock) yield m return cm diff --git a/testing/test_meta.py b/testing/test_meta.py index 9e8d1190..7f4dbb6c 100644 --- a/testing/test_meta.py +++ b/testing/test_meta.py @@ -1,5 +1,3 @@ -import sys - import pdbpp import pytest @@ -7,5 +5,4 @@ def test_mod_attributeerror(): with pytest.raises(AttributeError) as excinfo: pdbpp.doesnotexist - if sys.version_info >= (3, 5): - assert str(excinfo.value) == "module 'pdbpp' has no attribute 'doesnotexist'" + assert str(excinfo.value) == "module 'pdbpp' has no attribute 'doesnotexist'" diff --git a/testing/test_pdb.py b/testing/test_pdb.py index 4b532440..1ffdcc03 100644 --- a/testing/test_pdb.py +++ b/testing/test_pdb.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import print_function - import bdb import inspect import io @@ -14,27 +11,14 @@ from io import BytesIO import pdbpp -import pygments import pytest from pdbpp import DefaultConfig, Pdb, StringIO from .conftest import skip_with_missing_pth_file -try: - from shlex import quote -except ImportError: - from pipes import quote - -try: - from itertools import zip_longest -except ImportError: - from itertools import izip_longest as zip_longest +from shlex import quote -# ref: https://github.com/pygments/pygments/commit/147b22fa -PYGMENTS_VERSION_TUPLE = tuple(int(x) for x in pygments.__version__.split(".")) -PYGMENTS_WHITESPACE_PREFIX = ( - "^[[38;5;250m ^[[39m" if PYGMENTS_VERSION_TUPLE > (2, 14) else " " -) +from itertools import zip_longest # Windows support @@ -102,10 +86,7 @@ def __init__(self, *args, **kwds): readrc = kwds.pop("readrc", False) nosigint = kwds.pop("nosigint", True) kwds.setdefault('Config', ConfigTest) - if sys.version_info >= (3, 6): - super(PdbTest, self).__init__(*args, readrc=readrc, **kwds) - else: - super(PdbTest, self).__init__(*args, **kwds) + super().__init__(*args, readrc=readrc, **kwds) # Do not install sigint_handler in do_continue by default. self.nosigint = nosigint @@ -140,7 +121,7 @@ def set_trace_via_module(frame=None, cleanup=True, Pdb=PdbTest, **kwds): class PdbForFrame(Pdb): def set_trace(self, _frame, *args, **kwargs): - super(PdbForFrame, self).set_trace(frame, *args, **kwargs) + super().set_trace(frame, *args, **kwargs) newglobals = pdbpp.set_trace.__globals__.copy() newglobals['Pdb'] = PdbForFrame @@ -170,10 +151,6 @@ def runpdb(func, input, terminal_size=None): # Use __dict__ to avoid class descriptor (staticmethod). old_get_terminal_size = pdbpp.Pdb.__dict__["get_terminal_size"] - if sys.version_info < (3, ): - text_type = unicode # noqa: F821 - else: - text_type = str class MyBytesIO(BytesIO): """write accepts unicode or bytes""" @@ -184,9 +161,9 @@ def __init__(self, encoding='utf-8'): self.encoding = encoding def write(self, msg): - if isinstance(msg, text_type): + if isinstance(msg, str): msg = msg.encode(self.encoding) - super(MyBytesIO, self).write(msg) + super().write(msg) def get_unicode_value(self): return self.getvalue().decode(self.encoding).replace( @@ -257,7 +234,7 @@ def extract_commands(lines): ('', r'\^\[\[00m'), ('', r'\^\[\[39[^m]*m'), ('NUM', ' *[0-9]+'), - # Optional message with Python 2.7 (e.g. "--Return--"), not using Pdb.message. + # # Optional message with Python 2.7 (e.g. "--Return--"), not using Pdb.message. ('', '\n.*' if sys.version_info < (3,) else ''), ] @@ -307,32 +284,15 @@ class InnerTestException(Exception): trans_trn_dict = {"\n": r"\n", "\r": r"\r", "\t": r"\t"} -if sys.version_info >= (3,): - trans_trn_table = str.maketrans(trans_trn_dict) - - def trans_trn(string): - return string.translate(trans_trn_table) +trans_trn_table = str.maketrans(trans_trn_dict) - -else: - - def trans_trn(string): - for k, v in trans_trn_dict.items(): - string = string.replace(k, v) - return string +def trans_trn(string): + return string.translate(trans_trn_table) def check(func, expected, terminal_size=None): expected, lines = run_func(func, expected, terminal_size) - if expected: - maxlen = max(map(len, expected)) - if sys.version_info < (3,): - # Ensure same type for comparison. - expected = [ - x.decode("utf8") if isinstance(x, bytes) else x for x in expected - ] - else: - maxlen = 0 + maxlen = max(map(len, expected)) if expected else 0 all_ok = True print() for pattern, string in zip_longest(expected, lines): @@ -626,7 +586,7 @@ def fn(): class NewPdb(PdbTest, pdbpp.Pdb): def set_trace(self, *args): print("new_set_trace") - return super(NewPdb, self).set_trace(*args) + return super().set_trace(*args) new_pdb = NewPdb() new_pdb.set_trace() @@ -639,7 +599,7 @@ def set_trace(self, *args): new_set_trace --Return-- [NUM] .*set_trace()->None --> return super(NewPdb, self).set_trace(\\*args) +-> return super().set_trace(\\*args) 5 frames hidden .* # l NUM .* @@ -666,7 +626,7 @@ class NewPdb(PdbTest, pdbpp.Pdb): def set_trace(self, *args): print("new_set_trace") assert pdbpp.local.GLOBAL_PDB is self - ret = super(NewPdb, self).set_trace(*args) + ret = super().set_trace(*args) assert pdbpp.local.GLOBAL_PDB is self return ret @@ -692,14 +652,14 @@ def fn(): first = pdbpp.local.GLOBAL_PDB assert isinstance(pdbpp.local.GLOBAL_PDB, PdbTest) - class PdbLikePytest(object): + class PdbLikePytest: @classmethod def init_pdb(cls): class NewPdb(PdbTest, pdbpp.Pdb): def set_trace(self, frame): print("new_set_trace") - super(NewPdb, self).set_trace(frame) + super().set_trace(frame) return NewPdb() @@ -744,9 +704,9 @@ def fn(): class NewPdb(PdbTest, pdbpp.Pdb): def set_trace(self, frame): print("new_set_trace") - super(NewPdb, self).set_trace(frame) + super().set_trace(frame) - class PdbViaClassmethod(object): + class PdbViaClassmethod: @classmethod def init_pdb(cls): return NewPdb() @@ -793,7 +753,7 @@ class NewPdb(PdbTest, pdbpp.Pdb): def set_trace(self, *args): print("new_set_trace") assert pdbpp.local.GLOBAL_PDB is not self - ret = super(NewPdb, self).set_trace(*args) + ret = super().set_trace(*args) assert pdbpp.local.GLOBAL_PDB is not self return ret @@ -834,7 +794,7 @@ class NewPdb(PdbTest, pdbpp.Pdb): def set_trace(self, *args): print("new_set_trace") assert pdbpp.local.GLOBAL_PDB is not self - ret = super(NewPdb, self).set_trace(*args) + ret = super().set_trace(*args) assert pdbpp.local.GLOBAL_PDB is not self return ret @@ -863,7 +823,7 @@ class NewPdb(PdbTest, pdbpp.Pdb): def set_trace(self, *args): print("new_set_trace") assert pdbpp.local.GLOBAL_PDB is self - ret = super(NewPdb, self).set_trace(*args) + ret = super().set_trace(*args) assert pdbpp.local.GLOBAL_PDB is self return ret @@ -904,7 +864,7 @@ class NewPdb(PdbTest, pdbpp.Pdb): def set_trace(self, *args): print("new_set_trace") assert pdbpp.local.GLOBAL_PDB is self - ret = super(NewPdb, self).set_trace(*args) + ret = super().set_trace(*args) assert pdbpp.local.GLOBAL_PDB is self return ret @@ -929,7 +889,7 @@ def fn(): class NewPdb(PdbTest, pdbpp.Pdb): def set_trace(self, *args): print("new_set_trace") - ret = super(NewPdb, self).set_trace(*args) + ret = super().set_trace(*args) return ret new_pdb = NewPdb() @@ -1096,7 +1056,7 @@ def test_question_mark_unit(capsys, LineMatcher): ]) # Source for function, indented docstring. - def foo(): # noqa: F811 + def foo(): """doc_for_foo 3rd line.""" @@ -1213,8 +1173,6 @@ def c(): )) -@pytest.mark.skipif(sys.version_info < (3, 6), - reason="only with f-strings") def test_fstrings(monkeypatch): def mocked_inspect(self, arg): print("mocked_inspect: %r" % arg) @@ -1763,7 +1721,7 @@ def fn(): # l {line_num}, 5 NUM +\t$ NUM +\t ^[[38;5;28;01mdef^[[39;00m ^[[38;5;21mfn^[[39m(): -NUM +\t{pygments_prefix}^[[38;5;124.*m\"\"\"some docstring longer than maxlength for truncate_long_lines, which is 80\"\"\"^[[39.*m +NUM +\t ^[[38;5;124.*m\"\"\"some docstring longer than maxlength for truncate_long_lines, which is 80\"\"\"^[[39.*m NUM +\t a ^[[38;5;241m=^[[39m ^[[38;5;241m1^[[39m NUM +\t set_trace(Config^[[38;5;241m=^[[39mConfigWithPygments) NUM +\t$ @@ -1772,16 +1730,13 @@ def fn(): [NUM] > .*fn(), 5 frames hidden NUM + ^[[38;5;28;01mdef^[[39;00m ^[[38;5;21mfn^[[39m(): -NUM + {pygments_prefix}^[[38;5;124.*m\"\"\"some docstring longer than maxlength for truncate_long_lines^[[39.*m +NUM + ^[[38;5;124.*m\"\"\"some docstring longer than maxlength for truncate_long_lines^[[39.*m NUM + a ^[[38;5;241m=^[[39m ^[[38;5;241m1^[[39m NUM + set_trace(Config^[[38;5;241m=^[[39mConfigWithPygments) NUM +$ NUM +-> ^[[38;5;28;01mreturn^[[39;00m a # c -""".format( # noqa: E501 - line_num=fn.__code__.co_firstlineno - 1, - pygments_prefix=PYGMENTS_WHITESPACE_PREFIX, - )) +""".format(line_num=fn.__code__.co_firstlineno - 1)) # noqa: E501 def test_truncated_source_with_pygments_and_highlight(): @@ -1800,7 +1755,7 @@ def fn(): # l {line_num}, 5 +\t$ +\t ^[[38;5;28;01mdef^[[39;00m ^[[38;5;21mfn^[[39m(): - +\t{pygments_prefix}^[[38;5;124.*m\"\"\"some docstring longer than maxlength for truncate_long_lines, which is 80\"\"\"^[[39.*m + +\t ^[[38;5;124.*m\"\"\"some docstring longer than maxlength for truncate_long_lines, which is 80\"\"\"^[[39.*m +\t a ^[[38;5;241m=^[[39m ^[[38;5;241m1^[[39m +\t set_trace(Config^[[38;5;241m=^[[39mConfigWithPygmentsAndHighlight) +\t$ @@ -1809,16 +1764,13 @@ def fn(): [NUM] > .*fn(), 5 frames hidden + ^[[38;5;28;01mdef^[[39;00m ^[[38;5;21mfn^[[39m(): - + {pygments_prefix}^[[38;5;124.*m\"\"\"some docstring longer than maxlength for truncate_long_lines + + ^[[38;5;124.*m\"\"\"some docstring longer than maxlength for truncate_long_lines + a ^[[38;5;241m=^[[39m ^[[38;5;241m1^[[39m + set_trace(Config^[[38;5;241m=^[[39mConfigWithPygmentsAndHighlight) +$ +-> ^[[38;5;28;01;44mreturn a ^[[00m # c -""".format( # noqa: E501 - line_num=fn.__code__.co_firstlineno - 1, - pygments_prefix=PYGMENTS_WHITESPACE_PREFIX, - )) +""".format(line_num=fn.__code__.co_firstlineno - 1)) # noqa: E501 def test_shortlist_with_highlight(): @@ -3579,10 +3531,9 @@ def fn(): def test_break_on_setattr(): - # we don't use a class decorator to keep 2.5 compatibility - class Foo(object): + @pdbpp.break_on_setattr('x', Pdb=PdbTest) + class Foo: pass - Foo = pdbpp.break_on_setattr('x', Pdb=PdbTest)(Foo) def fn(): obj = Foo() @@ -3612,9 +3563,9 @@ def __init__(self, *args, **kwargs): class Config(ConfigTest): enable_hidden_frames = False - super(PdbWithConfig, self).__init__(*args, Config=Config, **kwargs) + super().__init__(*args, Config=Config, **kwargs) - class Foo(object): + class Foo: pass Foo = pdbpp.break_on_setattr('x', Pdb=PdbWithConfig)(Foo) @@ -3641,10 +3592,9 @@ def test_break_on_setattr_condition(): def mycond(obj, value): return value == 42 - class Foo(object): + @pdbpp.break_on_setattr('x', condition=mycond, Pdb=PdbTest) + class Foo: pass - # we don't use a class decorator to keep 2.5 compatibility - Foo = pdbpp.break_on_setattr('x', condition=mycond, Pdb=PdbTest)(Foo) def fn(): obj = Foo() @@ -3669,7 +3619,7 @@ def fn(): def test_break_on_setattr_non_decorator(): - class Foo(object): + class Foo: pass def fn(): @@ -3692,11 +3642,10 @@ def break_if_a(obj, value): def test_break_on_setattr_overridden(): - # we don't use a class decorator to keep 2.5 compatibility - class Foo(object): + @pdbpp.break_on_setattr('x', Pdb=PdbTest) + class Foo: def __setattr__(self, attr, value): - object.__setattr__(self, attr, value+1) - Foo = pdbpp.break_on_setattr('x', Pdb=PdbTest)(Foo) + super().__setattr__(attr, value+1) def fn(): obj = Foo() @@ -3845,14 +3794,14 @@ def f(): def test_debug_with_overridden_continue(): - class CustomPdb(PdbTest, object): + class CustomPdb(PdbTest): """CustomPdb that overrides do_continue like with pytest's wrapper.""" def do_continue(self, arg): global count_continue count_continue += 1 print("do_continue_%d" % count_continue) - return super(CustomPdb, self).do_continue(arg) + return super().do_continue(arg) do_c = do_cont = do_continue @@ -3936,9 +3885,6 @@ def fn(): # c """ - if sys.version_info < (3, ): - check_output = check_output.decode('utf-8') - check(fn, check_output) @@ -4023,7 +3969,6 @@ def fn(): )) -@pytest.mark.skipif(sys.version_info < (3, 7), reason="header kwarg is 3.7+") def test_set_trace_header(): """Handler header kwarg added with Python 3.7 in pdb.set_trace.""" def fn(): @@ -4260,9 +4205,6 @@ def inner(): """.format(lineno=lineno + 8)) -@pytest.mark.skipif( - sys.version_info < (3,), reason="no support for exit from interaction with pdbrc" -) def test_pdbrc_continue(tmpdirhome): """Test that interaction is skipped with continue in pdbrc.""" assert os.getcwd() == str(tmpdirhome) @@ -4329,9 +4271,7 @@ def test_python_m_pdb_uses_pdbpp_and_env(PDBPP_HIJACK_PDB, monkeypatch, tmpdir): if PDBPP_HIJACK_PDB: assert "(Pdb)" not in out assert "(Pdb++)" in out - if sys.platform == "win32" and ( - sys.version_info < (3,) or sys.version_info >= (3, 5) - ): + if sys.platform == "win32": assert out.endswith("\n(Pdb++) " + os.linesep) else: assert out.endswith("\n(Pdb++) \n") @@ -4357,7 +4297,7 @@ def get_completions(text): def test_set_trace_in_completion(monkeypatch_readline): def fn(): - class CompleteMe(object): + class CompleteMe: attr_called = 0 @property @@ -4402,24 +4342,23 @@ def check_completions(): monkeypatch_readline("wher", 0, 4) assert get_completions("wher") == ["where"] - if sys.version_info > (3, ): - # Patch readline to return expected results for "disable ". - monkeypatch_readline("disable", 8, 8) + # Patch readline to return expected results for "disable ". + monkeypatch_readline("disable", 8, 8) - # NOTE: number depends on bpb.Breakpoint class state, just ensure that - # is a number. - completion = pdbpp.local.GLOBAL_PDB.complete("", 0) - assert int(completion) > 0 + # NOTE: number depends on bpb.Breakpoint class state, just ensure that + # is a number. + completion = pdbpp.local.GLOBAL_PDB.complete("", 0) + assert int(completion) > 0 - # Patch readline to return expected results for "p ". - monkeypatch_readline("p ", 2, 2) - comps = get_completions("") - assert "where" in comps + # Patch readline to return expected results for "p ". + monkeypatch_readline("p ", 2, 2) + comps = get_completions("") + assert "where" in comps - # Dunder members get completed only on second invocation. - assert "__name__" not in comps - comps = get_completions("") - assert "__name__" in comps + # Dunder members get completed only on second invocation. + assert "__name__" not in comps + comps = get_completions("") + assert "__name__" in comps # Patch readline to return expected results for "help ". monkeypatch_readline("help ", 5, 5) @@ -4509,7 +4448,7 @@ def check_completions(): if readline_param == "pyrepl": assert pdbpp.local.GLOBAL_PDB.fancycompleter.config.use_colors is True comps = get_completions("helpvar.") - assert type(helpvar.denominator) is int + assert type(helpvar.denominator) == int assert any( re.match(r"\x1b\[\d\d\d;00m\x1b\[33;01mdenominator\x1b\[00m", x) for x in comps @@ -4653,7 +4592,6 @@ def check_completions(): """) -@pytest.mark.skipif(sys.version_info < (3, ), reason="py2: no completion for break") def test_completion_removes_tab_from_fancycompleter(monkeypatch_readline): def fn(): def check_completions(): @@ -4824,22 +4762,6 @@ def test_ensure_file_can_write_unicode(): assert out.read().decode("utf-8") == u"test äöüß" -@pytest.mark.skipif(sys.version_info >= (3, 0), - reason="test is python2 specific") -def test_py2_ensure_file_can_write_unicode(): - import StringIO - - stdout = StringIO.StringIO() - stdout.encoding = 'ascii' - - p = Pdb(Config=DefaultConfig, stdout=stdout) - - assert p.stdout.stream is stdout - - p.stdout.write(u"test äöüß") - stdout.seek(0) - assert stdout.read().decode('utf-8') == u"test äöüß" - def test_signal_in_nonmain_thread_with_interaction(): def fn(): @@ -5021,14 +4943,13 @@ def error(): set_trace() - if sys.version_info > (3,): - if sys.version_info >= (3, 10, 0, "final") and sys.version_info <= (3, 10, 1): - # changed after rc2 (bpo-45249), fixed in 3.10.1. - caret_line = " $" - else: - caret_line = " .*^" + if sys.version_info >= (3, 10, 0, "final") and sys.version_info <= (3, 10, 1): + # changed after rc2 (bpo-45249), fixed in 3.10.1. + caret_line = " $" + else: + caret_line = " .*^" - check(fn, """ + check(fn, """ --Return-- [NUM] > .*fn() -> set_trace() @@ -5047,19 +4968,6 @@ def error(): During handling of the above exception, another exception occurred: -Traceback (most recent call last): - File .*, in error - raise AttributeError -# c -""") - else: - check(fn, """ ---Return-- -[NUM] > .*fn() --> set_trace() - 5 frames hidden .* -# error() -\\*\\*\\* AttributeError.* Traceback (most recent call last): File .*, in error raise AttributeError @@ -5369,8 +5277,6 @@ def fn(): """) -@pytest.mark.skipif(sys.version_info < (3,), - reason="py2 has no support for kwonly") def test_rebind_globals_kwonly(): exec("def func(*args, header=None): pass", globals()) func = globals()["func"] @@ -5381,20 +5287,15 @@ def test_rebind_globals_kwonly(): assert str(inspect.signature(new)) == sig -@pytest.mark.skipif(sys.version_info < (3,), - reason="py2 has no support for annotations") def test_rebind_globals_annotations(): exec("def func(ann: str = None): pass", globals()) func = globals()["func"] sig = str(inspect.signature(func)) - if sys.version_info < (3, 5): - assert sig == "(ann:str=None)" - else: - assert sig in ( - "(ann: str = None)", - "(ann:str=None)", - ) + assert sig in ( + "(ann: str = None)", + "(ann:str=None)", + ) new = pdbpp.rebind_globals(func, globals()) assert str(inspect.signature(new)) == sig @@ -5860,10 +5761,7 @@ def test_stdout_reconfigured(pass_stdout, monkeypatch): """Check that self.stdout is re-configured with global pdb.""" def fn(): import sys - if sys.version_info > (3,): - from io import StringIO - else: - from StringIO import StringIO + from io import StringIO patched_stdout = StringIO() @@ -5874,11 +5772,8 @@ class _PdbTestKeepRawInput(PdbTest): def __init__( self, completekey="tab", stdin=None, stdout=None, *args, **kwargs ): - if pass_stdout: - stdout = sys.stdout - else: - stdout = None - super(_PdbTestKeepRawInput, self).__init__( + stdout = sys.stdout if pass_stdout else None + super().__init__( completekey, stdin, stdout, *args, **kwargs ) # Keep this, which gets set to 0 with stdin being passed in. @@ -5933,7 +5828,7 @@ def fn(): class SkippingPdbTest(PdbTest): def __init__(self, *args, **kwargs): kwargs["skip"] = ["testing.test_pdb"] - super(SkippingPdbTest, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.calls = [] @@ -5941,7 +5836,7 @@ def is_skipped_module(self, module_name): self.calls.append(module_name) if len(self.calls) == 1: print("is_skipped_module?", module_name) - ret = super(SkippingPdbTest, self).is_skipped_module(module_name) + ret = super().is_skipped_module(module_name) assert module_name == "testing.test_pdb" assert ret is True return True @@ -5989,9 +5884,6 @@ def f(): ln.replace(pdbpp.CLEARSCREEN, "") for ln in result.stdout.lines ] assert result.stdout.str().count("") == 2 - if (3,) <= sys.version_info <= (3, 5): - # NOTE: skipping explicit check for slighty different output with py34. - return result.stdout.fnmatch_lines( [ "(Pdb++) 'sticky'",