From 2711803fe1414ffe27c6327f3279555c4cdc7071 Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Wed, 24 Mar 2021 17:44:52 -0500 Subject: [PATCH 01/16] Add local pylint config --- .pylintrc-local.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .pylintrc-local.yml diff --git a/.pylintrc-local.yml b/.pylintrc-local.yml new file mode 100644 index 0000000..542b02a --- /dev/null +++ b/.pylintrc-local.yml @@ -0,0 +1,4 @@ +- arg: ignored-modules + val: + - bpython + - IPython -- GitLab From c4bab4d6bcb1d93af97aa463a3979e1700bed82e Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Wed, 24 Mar 2021 17:47:03 -0500 Subject: [PATCH 02/16] Fixes based on pylint complaints, drop some py2 compat code --- pudb/__init__.py | 8 ++------ pudb/b.py | 2 +- pudb/debugger.py | 30 +++++++----------------------- pudb/py3compat.py | 32 +++++++++++--------------------- pudb/settings.py | 5 ++++- pudb/source_view.py | 4 ++-- pudb/ui_tools.py | 9 --------- pudb/var_view.py | 30 ++++++++++++------------------ 8 files changed, 39 insertions(+), 81 deletions(-) diff --git a/pudb/__init__.py b/pudb/__init__.py index f013c5c..38fb4e1 100644 --- a/pudb/__init__.py +++ b/pudb/__init__.py @@ -57,12 +57,8 @@ class PudbShortcuts(object): dbg.set_trace(sys._getframe().f_back, paused=False) -if PY3: - import builtins - builtins.__dict__["pu"] = PudbShortcuts() -else: - import __builtin__ - __builtin__.__dict__["pu"] = PudbShortcuts() +import builtins +builtins.__dict__["pu"] = PudbShortcuts() CURRENT_DEBUGGER = [] diff --git a/pudb/b.py b/pudb/b.py index 8d73266..0dbbcf9 100644 --- a/pudb/b.py +++ b/pudb/b.py @@ -7,7 +7,7 @@ from pudb import _get_debugger, set_interrupt_handler def __myimport__(name, *args, **kwargs): # noqa: N807 if name == "pudb.b": set_trace() - return __origimport__(name, *args, **kwargs) # noqa: F821 + return __origimport__(name, *args, **kwargs) # noqa: F821, E501 # pylint: disable=undefined-variable # Will only be run on first import diff --git a/pudb/debugger.py b/pudb/debugger.py index a0d1c57..c55616c 100644 --- a/pudb/debugger.py +++ b/pudb/debugger.py @@ -195,10 +195,7 @@ class Debugger(bdb.Bdb): if steal_output: raise NotImplementedError("output stealing") - if PY3: - from io import StringIO - else: - from cStringIO import StringIO + from io import StringIO self.stolen_output = sys.stderr = sys.stdout = StringIO() sys.stdin = StringIO("") # avoid spurious hangs @@ -1593,11 +1590,8 @@ class DebuggerUI(FrameVarInfoKeeper): if widget is not new_mod_entry: mod_name = widget.base_widget.get_text()[0] mod = sys.modules[mod_name] - if PY3: - import importlib - importlib.reload(mod) - else: - reload(mod) # noqa (undef on Py3) + import importlib + importlib.reload(mod) self.message("'%s' was successfully reloaded." % mod_name) @@ -1743,10 +1737,7 @@ class DebuggerUI(FrameVarInfoKeeper): prev_sys_stdout = sys.stdout prev_sys_stderr = sys.stderr - if PY3: - from io import StringIO - else: - from cStringIO import StringIO + from io import StringIO sys.stdin = None sys.stderr = sys.stdout = StringIO() @@ -2041,7 +2032,7 @@ class DebuggerUI(FrameVarInfoKeeper): def __init__(self, idx): self.idx = idx - def __call__(subself, w, size, key): # noqa + def __call__(subself, w, size, key): # noqa # pylint: disable=no-self-argument self.columns.set_focus(self.rhs_col_sigwrap) self.rhs_col.set_focus(self.rhs_col.widget_list[subself.idx]) @@ -2202,10 +2193,10 @@ class DebuggerUI(FrameVarInfoKeeper): title=None, bind_enter_esc=True, focus_buttons=False, extra_bindings=[]): class ResultSetter: - def __init__(subself, res): # noqa + def __init__(subself, res): # noqa # pylint: disable=no-self-argument subself.res = res - def __call__(subself, btn): # noqa + def __call__(subself, btn): # noqa # pylint: disable=no-self-argument self.quit_event_loop = [subself.res] Attr = urwid.AttrMap # noqa @@ -2248,13 +2239,6 @@ class DebuggerUI(FrameVarInfoKeeper): ("fixed", 1, urwid.SolidFill()), w]) - class ResultSetter: - def __init__(subself, res): # noqa - subself.res = res - - def __call__(subself, w, size, key): # noqa - self.quit_event_loop = [subself.res] - w = SignalWrap(w) for key, binding in extra_bindings: if isinstance(binding, str): diff --git a/pudb/py3compat.py b/pudb/py3compat.py index 2e7cdd0..7a1904d 100644 --- a/pudb/py3compat.py +++ b/pudb/py3compat.py @@ -2,27 +2,17 @@ from __future__ import absolute_import, division, print_function import sys PY3 = sys.version_info[0] >= 3 -if PY3: - raw_input = input - xrange = range - integer_types = (int,) - string_types = (str,) - text_type = str - def execfile(fname, globs, locs=None): - exec(compile(open(fname).read(), fname, "exec"), globs, locs or globs) +raw_input = input +xrange = range +integer_types = (int,) +string_types = (str,) +text_type = str -else: - raw_input = raw_input - xrange = xrange - integer_types = (int, long) # noqa: F821 - string_types = (basestring,) # noqa: F821 - text_type = unicode # noqa: F821 - execfile = execfile -try: - import builtins - from configparser import ConfigParser -except ImportError: - import __builtin__ as builtins # noqa: F401 - from ConfigParser import ConfigParser # noqa: F401 +def execfile(fname, globs, locs=None): + exec(compile(open(fname).read(), fname, "exec"), globs, locs or globs) + + +import builtins +from configparser import ConfigParser diff --git a/pudb/settings.py b/pudb/settings.py index 8156004..08d0592 100644 --- a/pudb/settings.py +++ b/pudb/settings.py @@ -53,7 +53,10 @@ def get_save_config_path(*resource): return None if not resource: resource = [XDG_CONF_RESOURCE] - resource = os.path.join(*resource) + + # no idea what pylint's problem is here + resource = os.path.join(*resource) # pylint: disable=no-value-for-parameter + assert not resource.startswith("/") path = os.path.join(XDG_CONFIG_HOME, resource) if not os.path.isdir(path): diff --git a/pudb/source_view.py b/pudb/source_view.py index 05f89f7..7d68b72 100644 --- a/pudb/source_view.py +++ b/pudb/source_view.py @@ -238,13 +238,13 @@ def format_source(debugger_ui, lines, breakpoints): } class UrwidFormatter(Formatter): - def __init__(subself, **options): # noqa: N805 + def __init__(subself, **options): # noqa: N805, E501 # pylint: disable=no-self-argument Formatter.__init__(subself, **options) subself.current_line = "" subself.current_attr = [] subself.lineno = 1 - def format(subself, tokensource, outfile): # noqa: N805 + def format(subself, tokensource, outfile): # noqa: N805, E501 # pylint: disable=no-self-argument def add_snippet(ttype, s): if not s: return diff --git a/pudb/ui_tools.py b/pudb/ui_tools.py index f0a690c..1d68002 100644 --- a/pudb/ui_tools.py +++ b/pudb/ui_tools.py @@ -308,15 +308,6 @@ class SearchBox(urwid.Edit): urwid.Edit.__init__(self, [("label", "Search: ")], "") self.controller = controller - def restart_search(self): - from time import time - now = time() - - if self.search_start_time > 5: - self.set_edit_text("") - - self.search_time = now - def keypress(self, size, key): result = urwid.Edit.keypress(self, size, key) txt = self.get_edit_text() diff --git a/pudb/var_view.py b/pudb/var_view.py index 9294ec7..1280807 100644 --- a/pudb/var_view.py +++ b/pudb/var_view.py @@ -28,6 +28,8 @@ THE SOFTWARE. """ +from abc import ABC, abstractmethod + # {{{ constants and imports import urwid @@ -372,22 +374,9 @@ class VariableWidget(urwid.FlowWidget): # Ellipses to show text was cut off #encoding = urwid.util.detected_encoding - if False: # encoding[:3] == "UTF": - # Unicode is supported, use single character ellipsis - for i in xrange(len(text)): - if len(text[i]) > maxcol: - text[i] = (unicode(text[i][:maxcol-1]) # noqa: F821 - + ELLIPSIS + unicode(text[i][maxcol:])) # noqa: F821 - # XXX: This doesn't work. It just gives a ? - # Strangely, the following does work (it gives the … - # three characters from the right): - # - # text[i] = (unicode(text[i][:maxcol-3]) - # + unicode(u'…')) + unicode(text[i][maxcol-2:]) - else: - for i in xrange(len(text)): - if text_width(text[i]) > maxcol: - text[i] = text[i][:maxcol-3] + "..." + for i in xrange(len(text)): + if text_width(text[i]) > maxcol: + text[i] = text[i][:maxcol-3] + "..." return make_canvas(text, attr, maxcol, apfx+"value") @@ -477,7 +466,7 @@ def get_stringifier(iinfo): # {{{ tree walking -class ValueWalker: +class ValueWalker(ABC): BASIC_TYPES = [] BASIC_TYPES.append(type(None)) BASIC_TYPES.extend(integer_types) @@ -494,6 +483,10 @@ class ValueWalker: def __init__(self, frame_var_info): self.frame_var_info = frame_var_info + @abstractmethod + def add_item(self, parent, var_label, value_str, id_path, attr_prefix=None): + pass + def add_continuation_item(self, parent: VariableWidget, id_path: str, count: int, length: int) -> bool: """ @@ -745,7 +738,8 @@ class FrameVarInfoKeeper(object): def get_frame_var_info(self, read_only, ssid=None): if ssid is None: - ssid = self.debugger.get_stack_situation_id() + # self.debugger set by subclass + ssid = self.debugger.get_stack_situation_id() # noqa: E501 # pylint: disable=no-member if read_only: return self.frame_var_info.get(ssid, FrameVarInfo()) else: -- GitLab From 4ba323748228013dc0465d7d46ee2d3e38d76e4f Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Wed, 24 Mar 2021 17:47:26 -0500 Subject: [PATCH 03/16] Add pylint CI config --- .github/workflows/ci.yml | 14 ++++++++++++++ .gitlab-ci.yml | 10 ++++++++++ 2 files changed, 24 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f49c602..90b6626 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -80,5 +80,19 @@ jobs: build_py_project_in_venv build_docs + pylint: + name: Pylint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - + uses: actions/setup-python@v1 + with: + python-version: '3.x' + - name: "Main Script" + run: | + curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/master/prepare-and-run-pylint.sh + . ./prepare-and-run-pylint.sh "$(basename $GITHUB_REPOSITORY)" test/test_*.py + # vim: sw=4 diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b8b9f53..77eecd4 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -30,3 +30,13 @@ Documentation: - ". ./build-docs.sh" tags: - python3 + +Pylint: + script: | + export PY_EXE=python3 + curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/master/prepare-and-run-pylint.sh + . ./prepare-and-run-pylint.sh "$CI_PROJECT_NAME" test/test_*.py + tags: + - python3 + except: + - tags -- GitLab From 8b6f8b50685e79a51e3e839cafb20101a84f48ac Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Wed, 24 Mar 2021 17:49:59 -0500 Subject: [PATCH 04/16] Pylint ignore pytest --- .pylintrc-local.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.pylintrc-local.yml b/.pylintrc-local.yml index 542b02a..e919a3a 100644 --- a/.pylintrc-local.yml +++ b/.pylintrc-local.yml @@ -2,3 +2,4 @@ val: - bpython - IPython + - py -- GitLab From a04d49cc4ab4dbf85fc1904ecc7cc1d64c9f6249 Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Wed, 24 Mar 2021 17:53:13 -0500 Subject: [PATCH 05/16] Flake8 --- pudb/__init__.py | 2 +- pudb/py3compat.py | 3 +++ pudb/var_view.py | 3 --- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pudb/__init__.py b/pudb/__init__.py index 38fb4e1..b533eb7 100644 --- a/pudb/__init__.py +++ b/pudb/__init__.py @@ -26,7 +26,7 @@ THE SOFTWARE. """ -from pudb.py3compat import raw_input, PY3 +from pudb.py3compat import raw_input from pudb.settings import load_config diff --git a/pudb/py3compat.py b/pudb/py3compat.py index 7a1904d..987e3e0 100644 --- a/pudb/py3compat.py +++ b/pudb/py3compat.py @@ -16,3 +16,6 @@ def execfile(fname, globs, locs=None): import builtins from configparser import ConfigParser + +__all__ = ("raw_input", "xrange", "integer_types", "string_types", "text_type", + "execfile", "builtins", "ConfigParser") diff --git a/pudb/var_view.py b/pudb/var_view.py index 1280807..23f30b4 100644 --- a/pudb/var_view.py +++ b/pudb/var_view.py @@ -57,9 +57,6 @@ from pudb.ui_tools import text_width # {{{ abstract base classes for containers -from abc import ABC - - class PudbCollection(ABC): @classmethod def __subclasshook__(cls, c): -- GitLab From b4169ff1b410bc7e75234286766ddbff7da24e9c Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Wed, 24 Mar 2021 17:56:34 -0500 Subject: [PATCH 06/16] Fix pytest pylint ignores --- .pylintrc-local.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.pylintrc-local.yml b/.pylintrc-local.yml index e919a3a..2b1336a 100644 --- a/.pylintrc-local.yml +++ b/.pylintrc-local.yml @@ -2,4 +2,5 @@ val: - bpython - IPython - - py + - py.test + - pytest -- GitLab From c814c85dfff5139077a8e4e659a51dfc5050cd39 Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Wed, 24 Mar 2021 17:57:58 -0500 Subject: [PATCH 07/16] Github CI config: group linters together --- .github/workflows/ci.yml | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 90b6626..2a230cb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,6 +24,20 @@ jobs: curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/main/prepare-and-run-flake8.sh . ./prepare-and-run-flake8.sh ./pudb ./test + pylint: + name: Pylint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - + uses: actions/setup-python@v1 + with: + python-version: '3.x' + - name: "Main Script" + run: | + curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/master/prepare-and-run-pylint.sh + . ./prepare-and-run-pylint.sh "$(basename $GITHUB_REPOSITORY)" test/test_*.py + pytest: name: Pytest on Py${{ matrix.python-version }} runs-on: ubuntu-latest @@ -80,19 +94,5 @@ jobs: build_py_project_in_venv build_docs - pylint: - name: Pylint - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - - uses: actions/setup-python@v1 - with: - python-version: '3.x' - - name: "Main Script" - run: | - curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/master/prepare-and-run-pylint.sh - . ./prepare-and-run-pylint.sh "$(basename $GITHUB_REPOSITORY)" test/test_*.py - # vim: sw=4 -- GitLab From d6615bad7435a1eeecc6395f4f63a6c573c46ff7 Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Wed, 24 Mar 2021 18:05:35 -0500 Subject: [PATCH 08/16] Fix misguided removal of seemingly duplicate ResultSetter --- pudb/debugger.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/pudb/debugger.py b/pudb/debugger.py index c55616c..3d15ae6 100644 --- a/pudb/debugger.py +++ b/pudb/debugger.py @@ -2193,10 +2193,10 @@ class DebuggerUI(FrameVarInfoKeeper): title=None, bind_enter_esc=True, focus_buttons=False, extra_bindings=[]): class ResultSetter: - def __init__(subself, res): # noqa # pylint: disable=no-self-argument + def __init__(subself, res): # noqa: N805, E501 # pylint: disable=no-self-argument subself.res = res - def __call__(subself, btn): # noqa # pylint: disable=no-self-argument + def __call__(subself, btn): # noqa: N805, E501 # pylint: disable=no-self-argument self.quit_event_loop = [subself.res] Attr = urwid.AttrMap # noqa @@ -2239,10 +2239,17 @@ class DebuggerUI(FrameVarInfoKeeper): ("fixed", 1, urwid.SolidFill()), w]) + class ResultSettingEventHandler: + def __init__(subself, res): # noqa: N805, E501 # pylint: disable=no-self-argument + subself.res = res + + def __call__(subself, w, size, key): # noqa: N805, E501 # pylint: disable=no-self-argument + self.quit_event_loop = [subself.res] + w = SignalWrap(w) for key, binding in extra_bindings: if isinstance(binding, str): - w.listen(key, ResultSetter(binding)) + w.listen(key, ResultSettingEventHandler(binding)) else: w.listen(key, binding) -- GitLab From 7f1b08a28493987d3eed389d0ef5968c74835812 Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Wed, 24 Mar 2021 19:20:24 -0500 Subject: [PATCH 09/16] Delete pudb.py3compat --- pudb/__init__.py | 3 +-- pudb/debugger.py | 36 +++++++++++----------------- pudb/lowlevel.py | 56 ++++++++++++++++--------------------------- pudb/py3compat.py | 21 ---------------- pudb/settings.py | 2 +- pudb/theme.py | 7 +++--- pudb/var_view.py | 39 +++++++++++++++--------------- test/test_lowlevel.py | 26 ++++++-------------- test/test_settings.py | 2 +- test/test_var_view.py | 5 ++-- 10 files changed, 70 insertions(+), 127 deletions(-) delete mode 100644 pudb/py3compat.py diff --git a/pudb/__init__.py b/pudb/__init__.py index b533eb7..217ea5f 100644 --- a/pudb/__init__.py +++ b/pudb/__init__.py @@ -26,7 +26,6 @@ THE SOFTWARE. """ -from pudb.py3compat import raw_input from pudb.settings import load_config @@ -152,7 +151,7 @@ def runscript(mainpyfile, args=None, pre_run="", steal_output=False, retcode = call(pre_run, close_fds=True, shell=True) if retcode: print("*** WARNING: pre-run process exited with code %d." % retcode) - raw_input("[Hit Enter]") + input("[Hit Enter]") status_msg = "" diff --git a/pudb/debugger.py b/pudb/debugger.py index 3d15ae6..8cf1e52 100644 --- a/pudb/debugger.py +++ b/pudb/debugger.py @@ -1,4 +1,3 @@ -#! /usr/bin/env python # -*- coding: utf-8 -*- from __future__ import absolute_import, division, print_function @@ -41,7 +40,6 @@ from types import TracebackType from pudb.lowlevel import decode_lines, ui_log from pudb.settings import load_config, save_config -from pudb.py3compat import PY3, raw_input, execfile CONFIG = load_config() save_config(CONFIG) @@ -494,11 +492,8 @@ class Debugger(bdb.Bdb): # user_call for details). self._wait_for_mainpyfile = 1 self.mainpyfile = self.canonic(filename) - if PY3: - statement = 'exec(compile(open("%s").read(), "%s", "exec"))' % ( - filename, filename) - else: - statement = 'execfile( "%s")' % filename + statement = 'exec(compile(open("%s").read(), "%s", "exec"))' % ( + filename, filename) # Set up an interrupt handler from pudb import set_interrupt_handler @@ -528,11 +523,10 @@ class Debugger(bdb.Bdb): "__builtins__": __builtins__, }) - if PY3: - __main__.__dict__.update({ - "__package__": mod_spec.parent, - "__loader__": mod_spec.loader, - }) + __main__.__dict__.update({ + "__package__": mod_spec.parent, + "__loader__": mod_spec.loader, + }) self._wait_for_mainpyfile = True @@ -1744,13 +1738,8 @@ class DebuggerUI(FrameVarInfoKeeper): try: # Don't use cmdline_get_namespace() here in Python 2, as it # breaks things (issue #166). - if PY3: - eval(compile(cmd, "", "single"), - cmdline_get_namespace()) - else: - eval(compile(cmd, "", "single"), - self.debugger.curframe.f_globals, - self.debugger.curframe.f_locals) + eval(compile(cmd, "", "single"), + cmdline_get_namespace()) except Exception: tp, val, tb = sys.exc_info() @@ -1942,7 +1931,7 @@ class DebuggerUI(FrameVarInfoKeeper): def show_output(w, size, key): self.screen.stop() - raw_input("Hit Enter to return:") + input("Hit Enter to return:") self.screen.start() def reload_breakpoints(w, size, key): @@ -1995,8 +1984,11 @@ class DebuggerUI(FrameVarInfoKeeper): try: if not shell.custom_shell_dict: # Only execfile once from os.path import expanduser - execfile( - expanduser(CONFIG["shell"]), shell.custom_shell_dict) + cshell_fname = expanduser(CONFIG["shell"]) + with open(cshell_fname) as inf: + exec(compile(inf.read(), cshell_fname, "exec"), + shell.custom_shell_dict, + shell.custom_shell_dict) except Exception: print("Error when importing custom shell:") from traceback import print_exc diff --git a/pudb/lowlevel.py b/pudb/lowlevel.py index 83b5604..1ed9fa6 100644 --- a/pudb/lowlevel.py +++ b/pudb/lowlevel.py @@ -28,7 +28,6 @@ THE SOFTWARE. import logging from datetime import datetime -from pudb.py3compat import PY3, text_type logfile = [None] @@ -99,24 +98,15 @@ ui_log, settings_log = _init_loggers() def generate_executable_lines_for_code(code): lineno = code.co_firstlineno yield lineno - if PY3: - # See https://github.com/python/cpython/blob/master/Objects/lnotab_notes.txt - import sys - use_36_line_incr = sys.version_info >= (3, 6) - - for line_incr in code.co_lnotab[1::2]: - # This showed up between the v3.5 and v3.6 cycles: - # https://github.com/python/cpython/blob/v3.5.0/Objects/lnotab_notes.txt - # https://github.com/python/cpython/blob/v3.6.0/Objects/lnotab_notes.txt - # Gate the check on 3.6-or-newer. - if line_incr >= 0x80 and use_36_line_incr: - line_incr -= 0x100 - lineno += line_incr - yield lineno - else: - for c in code.co_lnotab[1::2]: - lineno += ord(c) - yield lineno + # See https://github.com/python/cpython/blob/master/Objects/lnotab_notes.txt + + for line_incr in code.co_lnotab[1::2]: + # This showed up between the v3.5 and v3.6 cycles: + # https://github.com/python/cpython/blob/v3.6.0/Objects/lnotab_notes.txt + if line_incr >= 0x80: + line_incr -= 0x100 + lineno += line_incr + yield lineno def get_executable_lines_for_codes_recursive(codes): @@ -228,10 +218,7 @@ def detect_encoding(line_iter): def find_cookie(line): try: - if PY3: - line_string = line - else: - line_string = line.decode("ascii") + line_string = line except UnicodeDecodeError: return None @@ -251,7 +238,7 @@ def detect_encoding(line_iter): return encoding first = read_or_stop() - if isinstance(first, text_type): + if isinstance(first, str): return None, [first] if first.startswith(BOM_UTF8): @@ -308,18 +295,15 @@ def format_exception(exc_tuple): # See also https://github.com/inducer/pudb/issues/61 from traceback import format_exception - if PY3: - exc_type, exc_value, exc_tb = exc_tuple - - if isinstance(exc_value, str): - exc_value = StringExceptionValueWrapper(exc_value) - exc_tuple = exc_type, exc_value, exc_tb - - return format_exception( - *exc_tuple, - **dict(chain=hasattr(exc_value, "__context__"))) - else: - return format_exception(*exc_tuple) + exc_type, exc_value, exc_tb = exc_tuple + + if isinstance(exc_value, str): + exc_value = StringExceptionValueWrapper(exc_value) + exc_tuple = exc_type, exc_value, exc_tb + + return format_exception( + *exc_tuple, + **dict(chain=hasattr(exc_value, "__context__"))) # }}} diff --git a/pudb/py3compat.py b/pudb/py3compat.py deleted file mode 100644 index 987e3e0..0000000 --- a/pudb/py3compat.py +++ /dev/null @@ -1,21 +0,0 @@ -from __future__ import absolute_import, division, print_function -import sys - -PY3 = sys.version_info[0] >= 3 - -raw_input = input -xrange = range -integer_types = (int,) -string_types = (str,) -text_type = str - - -def execfile(fname, globs, locs=None): - exec(compile(open(fname).read(), fname, "exec"), globs, locs or globs) - - -import builtins -from configparser import ConfigParser - -__all__ = ("raw_input", "xrange", "integer_types", "string_types", "text_type", - "execfile", "builtins", "ConfigParser") diff --git a/pudb/settings.py b/pudb/settings.py index 08d0592..e215149 100644 --- a/pudb/settings.py +++ b/pudb/settings.py @@ -28,7 +28,7 @@ THE SOFTWARE. import os import sys -from pudb.py3compat import ConfigParser +from configparser import ConfigParser from pudb.lowlevel import (lookup_module, get_breakpoint_invalid_reason, settings_log) diff --git a/pudb/theme.py b/pudb/theme.py index 5e8fd59..e894cfd 100644 --- a/pudb/theme.py +++ b/pudb/theme.py @@ -37,7 +37,6 @@ THEMES = [ "monokai-256" ] -from pudb.py3compat import execfile, raw_input import urwid @@ -1013,12 +1012,14 @@ def get_palette(may_use_fancy_formats, theme="classic"): } from os.path import expanduser, expandvars - execfile(expanduser(expandvars(theme)), symbols) + fname = expanduser(expandvars(theme)) + with open(fname) as inf: + exec(compile(inf.read(), fname, "exec"), symbols) except Exception: print("Error when importing theme:") from traceback import print_exc print_exc() - raw_input("Hit enter:") + input("Hit enter:") # Apply style inheritance for child, parent in inheritance_map: diff --git a/pudb/var_view.py b/pudb/var_view.py index 23f30b4..d467fc4 100644 --- a/pudb/var_view.py +++ b/pudb/var_view.py @@ -45,9 +45,6 @@ try: except ImportError: HAVE_NUMPY = 0 -from pudb.py3compat import execfile, raw_input, xrange, \ - integer_types, string_types, text_type - ELLIPSIS = "…" from pudb.ui_tools import text_width @@ -275,7 +272,7 @@ class VariableWidget(urwid.FlowWidget): return [firstline] fulllines, rest = divmod(text_width(alltext) - maxcol, maxcol - 2) restlines = [alltext[(maxcol - 2)*i + maxcol:(maxcol - 2)*i + 2*maxcol - 2] - for i in xrange(fulllines + bool(rest))] + for i in range(fulllines + bool(rest))] return [firstline] + [self.prefix + " " + i for i in restlines] def rows(self, size: Tuple[int], focus: bool = False) -> int: @@ -332,7 +329,7 @@ class VariableWidget(urwid.FlowWidget): fullcols, rem = divmod(totallen, maxcol) attr = [rle_subseg(_attr, i*maxcol, (i + 1)*maxcol) - for i in xrange(fullcols + bool(rem))] + for i in range(fullcols + bool(rem))] return make_canvas(text, attr, maxcol, apfx+"value") @@ -371,7 +368,7 @@ class VariableWidget(urwid.FlowWidget): # Ellipses to show text was cut off #encoding = urwid.util.detected_encoding - for i in xrange(len(text)): + for i in range(len(text)): if text_width(text[i]) > maxcol: text[i] = text[i][:maxcol-3] + "..." @@ -388,15 +385,15 @@ custom_stringifier_dict = {} def type_stringifier(value): if HAVE_NUMPY and isinstance(value, numpy.ndarray): - return text_type("%s(%s) %s") % ( + return str("%s(%s) %s") % ( type(value).__name__, value.dtype, value.shape) elif HAVE_NUMPY and isinstance(value, numpy.number): - return text_type("%s (%s)" % (value, value.dtype)) + return str("%s (%s)" % (value, value.dtype)) elif isinstance(value, STR_SAFE_TYPES): try: - return text_type(value) + return str(value) except Exception: message = "string safe type stringifier failed" ui_log.exception(message) @@ -412,13 +409,13 @@ def type_stringifier(value): ui_log.exception(message) result = "!! %s !!" % message - if isinstance(result, string_types): - return text_type(result) + if isinstance(result, str): + return str(result) elif type(value) in [set, frozenset, list, tuple, dict]: - return text_type("%s (%s)") % (type(value).__name__, len(value)) + return str("%s (%s)") % (type(value).__name__, len(value)) - return text_type(type(value).__name__) + return str(type(value).__name__) def id_stringifier(obj): @@ -444,7 +441,11 @@ def get_stringifier(iinfo): try: if not custom_stringifier_dict: # Only execfile once from os.path import expanduser - execfile(expanduser(iinfo.display_type), custom_stringifier_dict) + custom_stringifier_fname = expanduser(iinfo.display_type) + with open(custom_stringifier_fname) as inf: + exec(compile(inf.read(), custom_stringifier_fname, "exec"), + custom_stringifier_dict, + custom_stringifier_dict) except Exception: ui_log.exception("Error when importing custom stringifier") return error_stringifier @@ -452,13 +453,13 @@ def get_stringifier(iinfo): if "pudb_stringifier" not in custom_stringifier_dict: print("%s does not contain a function named pudb_stringifier at " "the module level." % iinfo.display_type) - raw_input("Hit enter:") - return lambda value: text_type( + input("Hit enter:") + return lambda value: str( "ERROR: Invalid custom stringifier file: " "pudb_stringifer not defined.") else: return (lambda value: - text_type(custom_stringifier_dict["pudb_stringifier"](value))) + str(custom_stringifier_dict["pudb_stringifier"](value))) # {{{ tree walking @@ -466,8 +467,8 @@ def get_stringifier(iinfo): class ValueWalker(ABC): BASIC_TYPES = [] BASIC_TYPES.append(type(None)) - BASIC_TYPES.extend(integer_types) - BASIC_TYPES.extend(string_types) + BASIC_TYPES.append(int) + BASIC_TYPES.append(str) BASIC_TYPES.extend((float, complex)) BASIC_TYPES = tuple(BASIC_TYPES) diff --git a/test/test_lowlevel.py b/test/test_lowlevel.py index 4212aa1..a69f708 100644 --- a/test/test_lowlevel.py +++ b/test/test_lowlevel.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- from pudb.lowlevel import detect_encoding, decode_lines -from pudb.py3compat import PY3 def test_detect_encoding_nocookie(): @@ -28,11 +27,7 @@ def test_decode_lines(): u"Проверка", ] lines = [line.encode("utf-8") for line in unicode_lines] - if PY3: - assert unicode_lines == list(decode_lines(iter(lines))) - else: - assert [line.decode("utf-8") - for line in lines] == list(decode_lines(iter(lines))) + assert unicode_lines == list(decode_lines(iter(lines))) # {{{ remove common indentation @@ -86,19 +81,12 @@ def test_executable_lines(): assert get_exec_lines(test_code) == set([1, 2, 3, 4, 6, 8]) test_code = "a = 3*5\n" + 333 * "\n" + "b = 15" - if PY3: - assert get_exec_lines(test_code) == set([ - 1, - 128, # bogus, - 255, # bogus, - 335 - ]) - else: - assert get_exec_lines(test_code) == set([ - 1, - 256, # bogus, - 335 - ]) + assert get_exec_lines(test_code) == set([ + 1, + 128, # bogus, + 255, # bogus, + 335 + ]) if __name__ == "__main__": diff --git a/test/test_settings.py b/test/test_settings.py index f709b0d..a29a956 100644 --- a/test/test_settings.py +++ b/test/test_settings.py @@ -1,8 +1,8 @@ import collections +import builtins import pytest # noqa: F401 -from pudb.py3compat import builtins from pudb.settings import load_breakpoints, save_breakpoints diff --git a/test/test_var_view.py b/test/test_var_view.py index e4271a5..e8b41a1 100644 --- a/test/test_var_view.py +++ b/test/test_var_view.py @@ -5,7 +5,6 @@ import itertools import string import unittest -from pudb.py3compat import text_type, integer_types from pudb.var_view import ( BasicValueWalker, FrameVarInfo, @@ -46,7 +45,7 @@ def test_get_stringifier(): strifier = get_stringifier(iinfo) s = strifier(value) - assert isinstance(s, text_type) + assert isinstance(s, str) class FrameVarInfoForTesting(FrameVarInfo): @@ -103,7 +102,7 @@ def method_factory(method_name): # Classes without __iter__ are expected to raise IndexError in this # sort of case. Frustrating, I know. if (method_name == "__getitem__" - and args and isinstance(args[0], integer_types)): + and args and isinstance(args[0], int)): raise IndexError raise return method -- GitLab From 481acb5daf5f3389e973ffdacc2280196f5555b2 Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Wed, 24 Mar 2021 19:21:44 -0500 Subject: [PATCH 10/16] Add py3.9, 3.10 for Github CI runs --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2a230cb..bb52ae4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,7 +43,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.6, 3.7, 3.8, pypy3] + python-version: [3.6, 3.7, 3.8, 3.9, 3.10, pypy3] steps: - uses: actions/checkout@v2 - -- GitLab From 3aa7ff1ff7fcafebae4026e75daf826b4d6c3ee2 Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Wed, 24 Mar 2021 19:23:19 -0500 Subject: [PATCH 11/16] Quote Python versions in Github CI config because YAML --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bb52ae4..c07ccc8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,7 +43,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.6, 3.7, 3.8, 3.9, 3.10, pypy3] + python-version: ["3.6", "3.7", "3.8", "3.9", "3.10", pypy3] steps: - uses: actions/checkout@v2 - -- GitLab From 5a28a99cc6f495c7db56886726cdf516a69aae0f Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Wed, 24 Mar 2021 19:24:11 -0500 Subject: [PATCH 12/16] Run pyupgrade --py36-plus --- pudb/__init__.py | 4 +--- pudb/__main__.py | 2 -- pudb/b.py | 1 - pudb/debugger.py | 20 ++++++++------------ pudb/forked.py | 5 ----- pudb/ipython.py | 2 -- pudb/lowlevel.py | 4 +--- pudb/remote.py | 12 ++++-------- pudb/run.py | 3 --- pudb/settings.py | 8 +++----- pudb/shell.py | 8 +++----- pudb/source_view.py | 6 ++---- pudb/theme.py | 2 -- pudb/ui_tools.py | 5 ++--- pudb/var_view.py | 30 +++++++++++++----------------- test/test_lowlevel.py | 21 ++++++++++----------- test/test_make_canvas.py | 14 ++++++-------- test/test_var_view.py | 24 +++++++++++------------- 18 files changed, 64 insertions(+), 107 deletions(-) diff --git a/pudb/__init__.py b/pudb/__init__.py index 217ea5f..98bfe75 100644 --- a/pudb/__init__.py +++ b/pudb/__init__.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, division, print_function - __copyright__ = """ Copyright (C) 2009-2017 Andreas Kloeckner Copyright (C) 2014-2017 Aaron Meurer @@ -34,7 +32,7 @@ VERSION = ".".join(str(nv) for nv in NUM_VERSION) __version__ = VERSION -class PudbShortcuts(object): +class PudbShortcuts: @property def db(self): import sys diff --git a/pudb/__main__.py b/pudb/__main__.py index da84b07..692919b 100644 --- a/pudb/__main__.py +++ b/pudb/__main__.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, division, print_function - __copyright__ = """ Copyright (C) 2009-2017 Andreas Kloeckner Copyright (C) 2014-2017 Aaron Meurer diff --git a/pudb/b.py b/pudb/b.py index 0dbbcf9..64e9cba 100644 --- a/pudb/b.py +++ b/pudb/b.py @@ -1,4 +1,3 @@ -from __future__ import absolute_import, division, print_function import sys from pudb import _get_debugger, set_interrupt_handler diff --git a/pudb/debugger.py b/pudb/debugger.py index 8cf1e52..378756b 100644 --- a/pudb/debugger.py +++ b/pudb/debugger.py @@ -1,7 +1,3 @@ -# -*- coding: utf-8 -*- - -from __future__ import absolute_import, division, print_function - __copyright__ = """ Copyright (C) 2009-2017 Andreas Kloeckner Copyright (C) 2014-2017 Aaron Meurer @@ -492,7 +488,7 @@ class Debugger(bdb.Bdb): # user_call for details). self._wait_for_mainpyfile = 1 self.mainpyfile = self.canonic(filename) - statement = 'exec(compile(open("%s").read(), "%s", "exec"))' % ( + statement = 'exec(compile(open("{}").read(), "{}", "exec"))'.format( filename, filename) # Set up an interrupt handler @@ -558,20 +554,20 @@ except ImportError: CursesScreen = None -class ThreadsafeScreenMixin(object): +class ThreadsafeScreenMixin: """A Screen subclass that doesn't crash when running from a non-main thread.""" def signal_init(self): """Initialize signal handler, ignoring errors silently.""" try: - super(ThreadsafeScreenMixin, self).signal_init() + super().signal_init() except ValueError: pass def signal_restore(self): """Restore default signal handler, ignoring errors silently.""" try: - super(ThreadsafeScreenMixin, self).signal_restore() + super().signal_restore() except ValueError: pass @@ -583,7 +579,7 @@ class ThreadsafeRawScreen(ThreadsafeScreenMixin, RawScreen): class ThreadsafeFixedSizeRawScreen(ThreadsafeScreenMixin, RawScreen): def __init__(self, **kwargs): self._term_size = kwargs.pop("term_size", None) - super(ThreadsafeFixedSizeRawScreen, self).__init__(**kwargs) + super().__init__(**kwargs) def get_cols_rows(self): if self._term_size is not None: @@ -601,7 +597,7 @@ if curses is not None: # {{{ source code providers -class SourceCodeProvider(object): +class SourceCodeProvider: def __ne__(self, other): return not (self == other) @@ -675,7 +671,7 @@ class FileSourceCodeProvider(SourceCodeProvider): debugger_ui, list(decode_lines(lines)), set(breakpoints)) except Exception: from pudb.lowlevel import format_exception - debugger_ui.message("Could not load source file '%s':\n\n%s" % ( + debugger_ui.message("Could not load source file '{}':\n\n{}".format( self.file_name, "".join(format_exception(sys.exc_info()))), title="Source Code Load Error") return [SourceLine(debugger_ui, @@ -1567,7 +1563,7 @@ class DebuggerUI(FrameVarInfoKeeper): except Exception: from pudb.lowlevel import format_exception - self.message("Could not import module '%s':\n\n%s" % ( + self.message("Could not import module '{}':\n\n{}".format( new_mod_name, "".join( format_exception(sys.exc_info()))), title="Import Error") diff --git a/pudb/forked.py b/pudb/forked.py index 26dcc4c..3804ec9 100644 --- a/pudb/forked.py +++ b/pudb/forked.py @@ -1,8 +1,3 @@ -# -*- coding: utf-8 -*- - -from __future__ import absolute_import, print_function, unicode_literals - - import sys import fcntl import termios diff --git a/pudb/ipython.py b/pudb/ipython.py index 7ac9873..0b7ff54 100644 --- a/pudb/ipython.py +++ b/pudb/ipython.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, division, print_function, with_statement - import sys import os diff --git a/pudb/lowlevel.py b/pudb/lowlevel.py index 1ed9fa6..e02754c 100644 --- a/pudb/lowlevel.py +++ b/pudb/lowlevel.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, division, print_function - __copyright__ = """ Copyright (C) 2009-2017 Andreas Kloeckner Copyright (C) 2014-2017 Aaron Meurer @@ -65,7 +63,7 @@ class TerminalOrStreamHandler(logging.StreamHandler): message = self.format(record) dbg.ui.add_cmdline_content(message, "command line error") else: - super(TerminalOrStreamHandler, self).emit(record) + super().emit(record) finally: self.release() diff --git a/pudb/remote.py b/pudb/remote.py index 9ff197e..3daa54b 100644 --- a/pudb/remote.py +++ b/pudb/remote.py @@ -1,7 +1,3 @@ -# -*- coding: utf-8 -*- - -from __future__ import absolute_import, print_function, unicode_literals - __copyright__ = """ Copyright (C) 2009-2017 Andreas Kloeckner Copyright (C) 2014-2017 Aaron Meurer @@ -143,13 +139,13 @@ class RemoteDebugger(Debugger): if reverse: self.host, self.port = host, port client, address = self.get_reverse_socket_client(host, port) - self.ident = "{0}:{1}".format(self.me, self.port) + self.ident = f"{self.me}:{self.port}" else: self._sock, conn_info = self.get_socket_client( host, port, search_limit=search_limit, ) self.host, self.port = conn_info - self.ident = "{0}:{1}".format(self.me, self.port) + self.ident = f"{self.me}:{self.port}" self.say(BANNER.format(self=self)) client, address = self._sock.accept() client.setblocking(1) @@ -160,7 +156,7 @@ class RemoteDebugger(Debugger): try: _sock.connect((host, port)) _sock.setblocking(1) - except socket.error as exc: + except OSError as exc: if exc.errno == errno.ECONNREFUSED: raise ValueError(CONN_REFUSED.format(self=self)) raise exc @@ -180,7 +176,7 @@ class RemoteDebugger(Debugger): this_port = port + i try: _sock.bind((host, this_port)) - except socket.error as exc: + except OSError as exc: if exc.errno in [errno.EADDRINUSE, errno.EINVAL]: continue raise diff --git a/pudb/run.py b/pudb/run.py index 5b23fee..76336e5 100644 --- a/pudb/run.py +++ b/pudb/run.py @@ -1,6 +1,3 @@ -from __future__ import absolute_import, division, print_function - - def main(): import sys diff --git a/pudb/settings.py b/pudb/settings.py index e215149..9a5076e 100644 --- a/pudb/settings.py +++ b/pudb/settings.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, division, print_function - __copyright__ = """ Copyright (C) 2009-2017 Andreas Kloeckner Copyright (C) 2014-2017 Aaron Meurer @@ -583,8 +581,8 @@ def load_breakpoints(): lines = [] for fname in file_names: try: - rc_file = open(fname, "rt") - except IOError: + rc_file = open(fname) + except OSError: pass else: lines.extend([line.strip() for line in rc_file.readlines()]) @@ -602,7 +600,7 @@ def save_breakpoints(bp_list): return histfile = open(get_breakpoints_file_name(), "w") - bp_list = set([(bp.file, bp.line, bp.cond) for bp in bp_list]) + bp_list = {(bp.file, bp.line, bp.cond) for bp in bp_list} for bp in bp_list: line = "b %s:%d" % (bp[0], bp[1]) if bp[2]: diff --git a/pudb/shell.py b/pudb/shell.py index 4dad912..852e664 100644 --- a/pudb/shell.py +++ b/pudb/shell.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, division, print_function - try: import bpython # noqa # Access a property to verify module exists in case @@ -94,7 +92,7 @@ def run_classic_shell(globals, locals, first_time=[True]): readline.clear_history() try: readline.read_history_file(hist_file) - except IOError: + except OSError: pass from code import InteractiveConsole @@ -168,7 +166,7 @@ def _update_ipython_ns(shell, globals, locals): try: shell.user_global_ns = globals except AttributeError: - class DummyMod(object): + class DummyMod: """A dummy module used for IPython's interactive namespace.""" pass @@ -235,7 +233,7 @@ def run_ipython_shell(globals, locals): def run_ipython_kernel(globals, locals): from IPython import embed_kernel - class DummyMod(object): + class DummyMod: pass user_module = DummyMod() diff --git a/pudb/source_view.py b/pudb/source_view.py index 7d68b72..520173c 100644 --- a/pudb/source_view.py +++ b/pudb/source_view.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, division, print_function - __copyright__ = """ Copyright (C) 2009-2017 Andreas Kloeckner Copyright (C) 2014-2017 Aaron Meurer @@ -310,14 +308,14 @@ def format_source(debugger_ui, lines, breakpoints): return result -class ParseState(object): +class ParseState: """States for the ArgumentParser class""" idle = 1 found_function = 2 found_open_paren = 3 -class ArgumentParser(object): +class ArgumentParser: """Parse source code tokens and identify function arguments. This parser implements a state machine which accepts diff --git a/pudb/theme.py b/pudb/theme.py index e894cfd..8f6bae4 100644 --- a/pudb/theme.py +++ b/pudb/theme.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, division, print_function - __copyright__ = """ Copyright (C) 2009-2017 Andreas Kloeckner Copyright (C) 2014-2017 Aaron Meurer diff --git a/pudb/ui_tools.py b/pudb/ui_tools.py index 1d68002..4931ff4 100644 --- a/pudb/ui_tools.py +++ b/pudb/ui_tools.py @@ -1,4 +1,3 @@ -from __future__ import absolute_import, division, print_function import urwid from urwid.util import calc_width, calc_text_pos @@ -24,7 +23,7 @@ def encode_like_urwid(s): # Consistent with # https://github.com/urwid/urwid/blob/2cc54891965283faf9113da72202f5d405f90fa3/urwid/util.py#L126-L128 - s = s.replace(escape.SI+escape.SO, u"") # remove redundant shifts + s = s.replace(escape.SI+escape.SO, "") # remove redundant shifts s = s.encode(_target_encoding, "replace") return s @@ -215,7 +214,7 @@ class BreakpointFrame(urwid.FlowWidget): return key -class SearchController(object): +class SearchController: def __init__(self, ui): self.ui = ui self.highlight_line = None diff --git a/pudb/var_view.py b/pudb/var_view.py index d467fc4..859dde2 100644 --- a/pudb/var_view.py +++ b/pudb/var_view.py @@ -1,7 +1,3 @@ -# -*- coding: utf-8 -*- - -from __future__ import absolute_import, division, print_function - __copyright__ = """ Copyright (C) 2009-2017 Andreas Kloeckner Copyright (C) 2014-2017 Aaron Meurer @@ -76,7 +72,7 @@ class PudbCollection(ABC): assert isinstance(collection, cls) try: for count, entry in enumerate(collection): - yield None, entry, "[{k:d}]".format(k=count) + yield None, entry, f"[{count:d}]" except (AttributeError, TypeError) as error: ui_log.error("Object {l!r} appears to be a collection, but does " "not behave like one: {m}".format( @@ -105,7 +101,7 @@ class PudbSequence(ABC): assert isinstance(sequence, cls) try: for count, entry in enumerate(sequence): - yield str(count), entry, "[{k:d}]".format(k=count) + yield str(count), entry, f"[{count:d}]" except (AttributeError, TypeError) as error: ui_log.error("Object {l!r} appears to be a sequence, but does " "not behave like one: {m}".format( @@ -163,7 +159,7 @@ CONTAINER_CLASSES = ( # {{{ data -class FrameVarInfo(object): +class FrameVarInfo: def __init__(self): self.id_path_to_iinfo = {} self.watches = [] @@ -177,7 +173,7 @@ class FrameVarInfo(object): id_path, InspectInfo()) -class InspectInfo(object): +class InspectInfo: def __init__(self): # Do not globalize: cyclic import from pudb.debugger import CONFIG @@ -191,12 +187,12 @@ class InspectInfo(object): self.wrap = CONFIG["wrap_variables"] -class WatchExpression(object): +class WatchExpression: def __init__(self, expression): self.expression = expression -class WatchEvalError(object): +class WatchEvalError: def __str__(self): return "" @@ -385,11 +381,11 @@ custom_stringifier_dict = {} def type_stringifier(value): if HAVE_NUMPY and isinstance(value, numpy.ndarray): - return str("%s(%s) %s") % ( + return "%s(%s) %s" % ( type(value).__name__, value.dtype, value.shape) elif HAVE_NUMPY and isinstance(value, numpy.number): - return str("%s (%s)" % (value, value.dtype)) + return str(f"{value} ({value.dtype})") elif isinstance(value, STR_SAFE_TYPES): try: @@ -413,7 +409,7 @@ def type_stringifier(value): return str(result) elif type(value) in [set, frozenset, list, tuple, dict]: - return str("%s (%s)") % (type(value).__name__, len(value)) + return "%s (%s)" % (type(value).__name__, len(value)) return str(type(value).__name__) @@ -528,14 +524,14 @@ class ValueWalker(ABC): if self.add_continuation_item(parent, id_path, count, length): return True - entry_id_path = "%s%s" % (id_path, id_path_ext) + entry_id_path = f"{id_path}{id_path_ext}" self.walk_value(parent, "[{}]".format(entry_label if entry_label else ""), entry, entry_id_path) if is_empty: self.add_item(parent, self.EMPTY_LABEL, None, - id_path="%s%s" % (id_path, self.EMPTY_LABEL)) + id_path=f"{id_path}{self.EMPTY_LABEL}") return True @@ -565,7 +561,7 @@ class ValueWalker(ABC): self.walk_value(parent, ".%s" % key, attr_value, - "%s.%s" % (id_path, key)) + f"{id_path}.{key}") def walk_value(self, parent, label, value, id_path=None, attr_prefix=None): if id_path is None: @@ -730,7 +726,7 @@ def make_var_view(frame_var_info, locals, globals): return result -class FrameVarInfoKeeper(object): +class FrameVarInfoKeeper: def __init__(self): self.frame_var_info = {} diff --git a/test/test_lowlevel.py b/test/test_lowlevel.py index a69f708..c839d53 100644 --- a/test/test_lowlevel.py +++ b/test/test_lowlevel.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- from pudb.lowlevel import detect_encoding, decode_lines def test_detect_encoding_nocookie(): - lines = [u"Test Проверка"] + lines = ["Test Проверка"] lines = [line.encode("utf-8") for line in lines] encoding, _ = detect_encoding(iter(lines)) assert encoding == "utf-8" @@ -11,9 +10,9 @@ def test_detect_encoding_nocookie(): def test_detect_encoding_cookie(): lines = [ - u"# coding=utf-8", - u"Test", - u"Проверка" + "# coding=utf-8", + "Test", + "Проверка" ] lines = [line.encode("utf-8") for line in lines] encoding, _ = detect_encoding(iter(lines)) @@ -22,9 +21,9 @@ def test_detect_encoding_cookie(): def test_decode_lines(): unicode_lines = [ - u"# coding=utf-8", - u"Test", - u"Проверка", + "# coding=utf-8", + "Test", + "Проверка", ] lines = [line.encode("utf-8") for line in unicode_lines] assert unicode_lines == list(decode_lines(iter(lines))) @@ -78,15 +77,15 @@ def test_executable_lines(): main() """ - assert get_exec_lines(test_code) == set([1, 2, 3, 4, 6, 8]) + assert get_exec_lines(test_code) == {1, 2, 3, 4, 6, 8} test_code = "a = 3*5\n" + 333 * "\n" + "b = 15" - assert get_exec_lines(test_code) == set([ + assert get_exec_lines(test_code) == { 1, 128, # bogus, 255, # bogus, 335 - ]) + } if __name__ == "__main__": diff --git a/test/test_make_canvas.py b/test/test_make_canvas.py index bd7e207..120eb05 100644 --- a/test/test_make_canvas.py +++ b/test/test_make_canvas.py @@ -1,10 +1,8 @@ -# - encoding: utf-8 - - from pudb.ui_tools import make_canvas def test_simple(): - text = u"aaaaaa" + text = "aaaaaa" canvas = make_canvas( txt=[text], attr=[[("var value", len(text))]], @@ -18,7 +16,7 @@ def test_simple(): def test_multiple(): canvas = make_canvas( - txt=[u"Return: None"], + txt=["Return: None"], attr=[[("return label", 8), ("return value", 4)]], maxcol=100 ) @@ -31,7 +29,7 @@ def test_multiple(): def test_boundary(): - text = u"aaaaaa" + text = "aaaaaa" canvas = make_canvas( txt=[text], attr=[[("var value", len(text))]], @@ -41,7 +39,7 @@ def test_boundary(): def test_byte_boundary(): - text = u"aaaaaaé" + text = "aaaaaaé" canvas = make_canvas( txt=[text], attr=[[("var value", len(text))]], @@ -51,7 +49,7 @@ def test_byte_boundary(): def test_wide_chars(): - text = u"data: '中文'" + text = "data: '中文'" canvas = make_canvas( txt=[text], attr=[[("var label", 6), ("var value", 4)]], @@ -59,7 +57,7 @@ def test_wide_chars(): ) assert list(canvas.content()) == [[ ("var label", None, b"data: "), - ("var value", None, u"'中文'".encode("utf-8")), + ("var value", None, "'中文'".encode()), (None, None, b" "*(47 - 12)), # 10 chars, 2 of which are double width ]] diff --git a/test/test_var_view.py b/test/test_var_view.py index e8b41a1..948d7ef 100644 --- a/test/test_var_view.py +++ b/test/test_var_view.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - import contextlib import itertools import string @@ -20,7 +18,7 @@ class A: pass -class A2(object): +class A2: pass @@ -35,8 +33,8 @@ def test_get_stringifier(): numpy_values = [np.float32(5), np.zeros(5)] for value in [ - A, A2, A(), A2(), u"lól".encode("utf8"), u"lól", - 1233123, [u"lól".encode("utf8"), u"lól"], + A, A2, A(), A2(), "lól".encode(), "lól", + 1233123, ["lól".encode(), "lól"], ] + numpy_values: for display_type in ["type", "repr", "str", "id"]: iinfo = InspectInfo() @@ -50,13 +48,13 @@ def test_get_stringifier(): class FrameVarInfoForTesting(FrameVarInfo): def __init__(self, paths_to_expand=None): - super(FrameVarInfoForTesting, self).__init__() + super().__init__() if paths_to_expand is None: paths_to_expand = set() self.paths_to_expand = paths_to_expand def get_inspect_info(self, id_path, read_only): - iinfo = super(FrameVarInfoForTesting, self).get_inspect_info( + iinfo = super().get_inspect_info( id_path, read_only) iinfo.access_level = "all" iinfo.display_type = "repr" @@ -67,7 +65,7 @@ class FrameVarInfoForTesting(FrameVarInfo): return iinfo -class Reasonable(object): +class Reasonable: def __init__(self): self.x = 42 @@ -109,7 +107,7 @@ def method_factory(method_name): def generate_containerlike_class(): - methods = set([ + methods = { "__contains__", "__getitem__", "__iter__", @@ -121,14 +119,14 @@ def generate_containerlike_class(): "items", "keys", "values", - ]) + } # Deliberately starting from 0 for r in range(0, len(methods) + 1): for selected_methods in sorted( map(sorted, itertools.combinations(methods, r))): - class ContainerlikeClass(object): + class ContainerlikeClass: def __init__(self, iterable): self.__internal_dict__ = dict(iterable) @@ -310,9 +308,9 @@ class ValueWalkerTest(BaseValueWalkerTestCase): self.assert_walks_contents(value) def test_set(self): - self.assert_walks_contents(set([ + self.assert_walks_contents({ 42, "foo", None, False, (), ("a", "tuple") - ])) + }) self.assert_class_counts_equal({"collections": 1}) def test_frozenset(self): -- GitLab From 5267892b02a064b9922566e76380450c3ed9dccb Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Wed, 24 Mar 2021 19:25:17 -0500 Subject: [PATCH 13/16] Nvm on Py3.10 on Github CI --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c07ccc8..11fa01b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,7 +43,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.6", "3.7", "3.8", "3.9", "3.10", pypy3] + python-version: ["3.6", "3.7", "3.8", "3.9", pypy3] steps: - uses: actions/checkout@v2 - -- GitLab From 1a5fbfe1adf9251f9df65f2583d7e971f50e4856 Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Wed, 24 Mar 2021 19:28:22 -0500 Subject: [PATCH 14/16] Placate flake8: indentation --- pudb/debugger.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pudb/debugger.py b/pudb/debugger.py index 378756b..670eab7 100644 --- a/pudb/debugger.py +++ b/pudb/debugger.py @@ -1563,10 +1563,11 @@ class DebuggerUI(FrameVarInfoKeeper): except Exception: from pudb.lowlevel import format_exception - self.message("Could not import module '{}':\n\n{}".format( - new_mod_name, "".join( - format_exception(sys.exc_info()))), - title="Import Error") + self.message( + "Could not import module '{}':\n\n{}".format( + new_mod_name, "".join( + format_exception(sys.exc_info()))), + title="Import Error") else: show_mod(__import__(str(new_mod_name))) break -- GitLab From b676ca78987e1f5124a8c98a780e8010db840793 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Kl=C3=B6ckner?= Date: Wed, 24 Mar 2021 23:07:43 -0500 Subject: [PATCH 15/16] Add better comment on (Py3.6) line number calculation --- pudb/lowlevel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pudb/lowlevel.py b/pudb/lowlevel.py index e02754c..6bc42ac 100644 --- a/pudb/lowlevel.py +++ b/pudb/lowlevel.py @@ -99,7 +99,7 @@ def generate_executable_lines_for_code(code): # See https://github.com/python/cpython/blob/master/Objects/lnotab_notes.txt for line_incr in code.co_lnotab[1::2]: - # This showed up between the v3.5 and v3.6 cycles: + # NB: This code is specific to Python 3.6 and higher # https://github.com/python/cpython/blob/v3.6.0/Objects/lnotab_notes.txt if line_incr >= 0x80: line_incr -= 0x100 -- GitLab From 09900ef79cd74b770691a8bf09b3c96e03734986 Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Wed, 24 Mar 2021 23:18:26 -0500 Subject: [PATCH 16/16] Delete format_traceback workaround (long fixed in Py3.6) --- pudb/__init__.py | 4 ++-- pudb/debugger.py | 24 ++++++++++++------------ pudb/lowlevel.py | 31 ------------------------------- 3 files changed, 14 insertions(+), 45 deletions(-) diff --git a/pudb/__init__.py b/pudb/__init__.py index 98bfe75..b2da1ec 100644 --- a/pudb/__init__.py +++ b/pudb/__init__.py @@ -307,11 +307,11 @@ def set_interrupt_handler(interrupt_signal=None): try: signal.signal(interrupt_signal, _interrupt_handler) except ValueError: - from pudb.lowlevel import format_exception + from traceback import format_exception import sys from warnings import warn warn("setting interrupt handler on signal %d failed: %s" - % (interrupt_signal, "".join(format_exception(sys.exc_info())))) + % (interrupt_signal, "".join(format_exception(*sys.exc_info())))) def post_mortem(tb=None, e_type=None, e_value=None): diff --git a/pudb/debugger.py b/pudb/debugger.py index 670eab7..efd1734 100644 --- a/pudb/debugger.py +++ b/pudb/debugger.py @@ -1119,9 +1119,9 @@ class DebuggerUI(FrameVarInfoKeeper): new_modification_time = os.path.getmtime(file_name) file_changed = new_modification_time - original_modification_time > 0 except Exception: - from pudb.lowlevel import format_exception + from traceback import format_exception self.message("Exception happened when trying to edit the file:" - "\n\n%s" % ("".join(format_exception(sys.exc_info()))), + "\n\n%s" % ("".join(format_exception(*sys.exc_info()))), title="File Edit Error") return @@ -1561,12 +1561,12 @@ class DebuggerUI(FrameVarInfoKeeper): try: __import__(str(new_mod_name)) except Exception: - from pudb.lowlevel import format_exception + from traceback import format_exception self.message( "Could not import module '{}':\n\n{}".format( new_mod_name, "".join( - format_exception(sys.exc_info()))), + format_exception(*sys.exc_info()))), title="Import Error") else: show_mod(__import__(str(new_mod_name))) @@ -1940,11 +1940,11 @@ class DebuggerUI(FrameVarInfoKeeper): def show_traceback(w, size, key): if self.current_exc_tuple is not None: - from pudb.lowlevel import format_exception + from traceback import format_exception result = self.dialog( urwid.ListBox(urwid.SimpleListWalker([urwid.Text( - "".join(format_exception(self.current_exc_tuple)))])), + "".join(format_exception(*self.current_exc_tuple)))])), [ ("Close", "close"), ("Location", "location") @@ -2263,7 +2263,7 @@ class DebuggerUI(FrameVarInfoKeeper): get_palette(may_use_fancy_formats, CONFIG["theme"])) def show_exception_dialog(self, exc_tuple): - from pudb.lowlevel import format_exception + from traceback import format_exception desc = ( "The program has terminated abnormally because of an exception.\n\n" @@ -2271,7 +2271,7 @@ class DebuggerUI(FrameVarInfoKeeper): "time using the 'e' key. The debugger has entered post-mortem mode " "and will prevent further state changes." ) - tb_txt = "".join(format_exception(exc_tuple)) + tb_txt = "".join(format_exception(*exc_tuple)) self._show_exception_dialog( description=desc, error_info=tb_txt, @@ -2286,7 +2286,7 @@ class DebuggerUI(FrameVarInfoKeeper): ui_log.exception("Error while showing error dialog") def _show_internal_exc_dlg(self, exc_tuple): - from pudb.lowlevel import format_exception + from traceback import format_exception from pudb import VERSION desc = ( @@ -2305,7 +2305,7 @@ class DebuggerUI(FrameVarInfoKeeper): python=sys.version.replace("\n", " "), pudb=VERSION, urwid=".".join(map(str, urwid.version.VERSION)), - tb="".join(format_exception(exc_tuple)) + tb="".join(format_exception(*exc_tuple)) ) self._show_exception_dialog( @@ -2344,8 +2344,8 @@ class DebuggerUI(FrameVarInfoKeeper): self.message("Traceback saved as %s." % filename, title="Success") except Exception: - from pudb.lowlevel import format_exception - io_tb_txt = "".join(format_exception(sys.exc_info())) + from traceback import format_exception + io_tb_txt = "".join(format_exception(*sys.exc_info())) self.message( "An error occurred while trying to write " "the traceback:\n\n" + io_tb_txt, diff --git a/pudb/lowlevel.py b/pudb/lowlevel.py index 6bc42ac..8b1eafa 100644 --- a/pudb/lowlevel.py +++ b/pudb/lowlevel.py @@ -274,35 +274,4 @@ def decode_lines(lines): # }}} - -# {{{ traceback formatting - -class StringExceptionValueWrapper: - def __init__(self, string_val): - self.string_val = string_val - - def __str__(self): - return self.string_val - - __context__ = None - __cause__ = None - - -def format_exception(exc_tuple): - # Work around http://bugs.python.org/issue17413 - # See also https://github.com/inducer/pudb/issues/61 - - from traceback import format_exception - exc_type, exc_value, exc_tb = exc_tuple - - if isinstance(exc_value, str): - exc_value = StringExceptionValueWrapper(exc_value) - exc_tuple = exc_type, exc_value, exc_tb - - return format_exception( - *exc_tuple, - **dict(chain=hasattr(exc_value, "__context__"))) - -# }}} - # vim: foldmethod=marker -- GitLab