diff --git a/.basedpyright/baseline.json b/.basedpyright/baseline.json index 1bec3bc1..f1356b20 100644 --- a/.basedpyright/baseline.json +++ b/.basedpyright/baseline.json @@ -303,6 +303,14 @@ "lineCount": 1 } }, + { + "code": "reportArgumentType", + "range": { + "startColumn": 38, + "endColumn": 40, + "lineCount": 1 + } + }, { "code": "reportPossiblyUnboundVariable", "range": { @@ -318,6 +326,14 @@ "endColumn": 16, "lineCount": 1 } + }, + { + "code": "reportArgumentType", + "range": { + "startColumn": 30, + "endColumn": 32, + "lineCount": 1 + } } ], "./examples/stringifier.py": [ @@ -7501,7 +7517,7 @@ "code": "reportUnknownArgumentType", "range": { "startColumn": 47, - "endColumn": 56, + "endColumn": 70, "lineCount": 1 } }, @@ -7825,6 +7841,14 @@ "lineCount": 1 } }, + { + "code": "reportArgumentType", + "range": { + "startColumn": 21, + "endColumn": 44, + "lineCount": 1 + } + }, { "code": "reportUnknownMemberType", "range": { @@ -11774,7 +11798,7 @@ ], "./pudb/ipython.py": [ { - "code": "reportPrivateImportUsage", + "code": "reportAny", "range": { "startColumn": 20, "endColumn": 31, @@ -11878,10 +11902,18 @@ } }, { - "code": "reportAttributeAccessIssue", + "code": "reportAny", "range": { - "startColumn": 13, - "endColumn": 21, + "startColumn": 0, + "endColumn": 2, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 0, + "endColumn": 12, "lineCount": 1 } } @@ -11927,14 +11959,6 @@ "lineCount": 1 } }, - { - "code": "reportUnreachable", - "range": { - "startColumn": 8, - "endColumn": 28, - "lineCount": 3 - } - }, { "code": "reportArgumentType", "range": { @@ -12671,190 +12695,6 @@ "lineCount": 1 } }, - { - "code": "reportMissingTypeArgument", - "range": { - "startColumn": 25, - "endColumn": 29, - "lineCount": 1 - } - }, - { - "code": "reportUnknownParameterType", - "range": { - "startColumn": 23, - "endColumn": 35, - "lineCount": 1 - } - }, - { - "code": "reportMissingParameterType", - "range": { - "startColumn": 23, - "endColumn": 35, - "lineCount": 1 - } - }, - { - "code": "reportUnknownParameterType", - "range": { - "startColumn": 37, - "endColumn": 48, - "lineCount": 1 - } - }, - { - "code": "reportMissingParameterType", - "range": { - "startColumn": 37, - "endColumn": 48, - "lineCount": 1 - } - }, - { - "code": "reportUnknownMemberType", - "range": { - "startColumn": 8, - "endColumn": 21, - "lineCount": 1 - } - }, - { - "code": "reportUnknownVariableType", - "range": { - "startColumn": 12, - "endColumn": 13, - "lineCount": 1 - } - }, - { - "code": "reportUnknownMemberType", - "range": { - "startColumn": 12, - "endColumn": 23, - "lineCount": 1 - } - }, - { - "code": "reportUnknownArgumentType", - "range": { - "startColumn": 24, - "endColumn": 25, - "lineCount": 1 - } - }, - { - "code": "reportUnannotatedClassAttribute", - "range": { - "startColumn": 13, - "endColumn": 24, - "lineCount": 1 - } - }, - { - "code": "reportImplicitOverride", - "range": { - "startColumn": 8, - "endColumn": 19, - "lineCount": 1 - } - }, - { - "code": "reportUnknownParameterType", - "range": { - "startColumn": 26, - "endColumn": 29, - "lineCount": 1 - } - }, - { - "code": "reportMissingParameterType", - "range": { - "startColumn": 26, - "endColumn": 29, - "lineCount": 1 - } - }, - { - "code": "reportUnknownParameterType", - "range": { - "startColumn": 31, - "endColumn": 36, - "lineCount": 1 - } - }, - { - "code": "reportMissingParameterType", - "range": { - "startColumn": 31, - "endColumn": 36, - "lineCount": 1 - } - }, - { - "code": "reportUnknownMemberType", - "range": { - "startColumn": 8, - "endColumn": 24, - "lineCount": 1 - } - }, - { - "code": "reportUnknownArgumentType", - "range": { - "startColumn": 31, - "endColumn": 34, - "lineCount": 1 - } - }, - { - "code": "reportUnknownArgumentType", - "range": { - "startColumn": 36, - "endColumn": 41, - "lineCount": 1 - } - }, - { - "code": "reportImplicitOverride", - "range": { - "startColumn": 8, - "endColumn": 19, - "lineCount": 1 - } - }, - { - "code": "reportUnknownParameterType", - "range": { - "startColumn": 26, - "endColumn": 29, - "lineCount": 1 - } - }, - { - "code": "reportMissingParameterType", - "range": { - "startColumn": 26, - "endColumn": 29, - "lineCount": 1 - } - }, - { - "code": "reportUnknownMemberType", - "range": { - "startColumn": 8, - "endColumn": 24, - "lineCount": 1 - } - }, - { - "code": "reportUnknownArgumentType", - "range": { - "startColumn": 31, - "endColumn": 34, - "lineCount": 1 - } - }, { "code": "reportUnknownParameterType", "range": { @@ -12895,6 +12735,14 @@ "lineCount": 1 } }, + { + "code": "reportUnknownVariableType", + "range": { + "startColumn": 4, + "endColumn": 6, + "lineCount": 1 + } + }, { "code": "reportUnknownArgumentType", "range": { @@ -12951,6 +12799,14 @@ "lineCount": 1 } }, + { + "code": "reportArgumentType", + "range": { + "startColumn": 38, + "endColumn": 40, + "lineCount": 1 + } + }, { "code": "reportPossiblyUnboundVariable", "range": { @@ -12975,6 +12831,14 @@ "lineCount": 1 } }, + { + "code": "reportArgumentType", + "range": { + "startColumn": 30, + "endColumn": 32, + "lineCount": 1 + } + }, { "code": "reportPossiblyUnboundVariable", "range": { @@ -13023,6 +12887,14 @@ "lineCount": 1 } }, + { + "code": "reportUnknownVariableType", + "range": { + "startColumn": 4, + "endColumn": 6, + "lineCount": 1 + } + }, { "code": "reportUnknownArgumentType", "range": { @@ -16475,6 +16347,14 @@ "lineCount": 1 } }, + { + "code": "reportGeneralTypeIssues", + "range": { + "startColumn": 12, + "endColumn": 13, + "lineCount": 1 + } + }, { "code": "reportUnannotatedClassAttribute", "range": { diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3594c58a..09151331 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -54,7 +54,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.8", "3.9", "3.x", "pypy3.8"] + python-version: ["3.10", "3.12", "3.x"] steps: - uses: actions/checkout@v7 - diff --git a/doc/conf.py b/doc/conf.py index 82dfeff3..062a0932 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -36,6 +36,8 @@ nitpick_ignore_regex = [ ["py:class", r"P"], + ["py:class", r"K"], + ["py:class", r"V"], ["py:class", r"P.args"], ["py:class", r"P.kwargs"], ["py:class", r"ResultT"], diff --git a/pudb/__init__.py b/pudb/__init__.py index bef1884a..faa9e5a1 100644 --- a/pudb/__init__.py +++ b/pudb/__init__.py @@ -146,7 +146,7 @@ def runscript( _runscript(mainpyfile, dbg, args=args, pre_run=pre_run, run_as_module=run_as_module) finally: - dbg.__del__() + del dbg def _runscript( diff --git a/pudb/debugger.py b/pudb/debugger.py index df46ef3e..d3b3c45f 100644 --- a/pudb/debugger.py +++ b/pudb/debugger.py @@ -38,17 +38,17 @@ from itertools import count from os.path import splitext from types import FrameType, ModuleType, TracebackType -from typing import TYPE_CHECKING, Any, ClassVar, Mapping, TextIO, TypeVar, cast, final +from typing import TYPE_CHECKING, Any, ClassVar, TextIO, TypeAlias, TypeVar, cast, final import urwid -from typing_extensions import ParamSpec, TypeAlias, override +from typing_extensions import ParamSpec, override from pudb.lowlevel import ConsoleSingleKeyReader, decode_lines, ui_log from pudb.settings import get_save_config_path, load_config, save_config if TYPE_CHECKING: - from collections.abc import Callable, Sequence + from collections.abc import Callable, Mapping, Sequence from pudb.source_view import SourceLine @@ -1975,7 +1975,7 @@ def cmdline_tab_complete(w, size, key): chopped_completions = [i.complete for i in completions] def common_prefix(a, b): - for i, (a_i, b_i) in enumerate(zip(a, b)): + for i, (a_i, b_i) in enumerate(zip(a, b, strict=False)): if a_i != b_i: return a[:i] @@ -2288,7 +2288,7 @@ def fallback(error_message): from os.path import expanduser, expandvars cshell_fname = expanduser(expandvars(CONFIG["shell"])) with open(cshell_fname) as inf: - exec(compile(inf.read(), cshell_fname, "exec"), # ruff:ignore[exec-builtin] + exec(compile(inf.read(), cshell_fname, "exec"), shell.custom_shell_dict, shell.custom_shell_dict) except FileNotFoundError: diff --git a/pudb/lowlevel.py b/pudb/lowlevel.py index 8337f426..844b8116 100644 --- a/pudb/lowlevel.py +++ b/pudb/lowlevel.py @@ -53,7 +53,7 @@ def setlogfile(destfile: str): logfile[0] = destfile with open(destfile, "a") as openfile: openfile.write( - f"\n*** Pudb session error log started at {datetime.now()} ***\n") # ruff:ignore[call-datetime-now-without-tzinfo] + f"\n*** Pudb session error log started at {datetime.now()} ***\n") # once we've dropped Py3.8 support, use this instead: @@ -110,21 +110,8 @@ def _init_loggers(): # {{{ breakpoint validity def generate_executable_lines_for_code(code: CodeType) -> Iterable[int]: - if sys.version_info >= (3, 10): - for _start, _end, lineno in code.co_lines(): - if lineno is not None: - yield lineno - else: - lineno = code.co_firstlineno - yield lineno - # See https://github.com/python/cpython/blob/master/Objects/lnotab_notes.txt - - for line_incr in code.co_lnotab[1::2]: # pyright: ignore[reportDeprecated] - # 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 - lineno += line_incr + for _start, _end, lineno in code.co_lines(): + if lineno is not None: yield lineno @@ -314,7 +301,7 @@ class KeyReadImpl(Enum): _keyread_impl = KeyReadImpl.INPUT if sys.platform in ("emscripten", "wasi"): pass -elif sys.platform in ("win32",): +elif sys.platform == "win32": _keyread_impl = KeyReadImpl.GETCH else: _keyread_impl = KeyReadImpl.SELECT diff --git a/pudb/remote.py b/pudb/remote.py index b8b02d71..a0e64c7e 100644 --- a/pudb/remote.py +++ b/pudb/remote.py @@ -46,7 +46,6 @@ import sys from typing import ( TYPE_CHECKING, - Callable, ClassVar, TextIO, TypeVar, @@ -58,6 +57,7 @@ if TYPE_CHECKING: + from collections.abc import Callable from types import FrameType, TracebackType from mpi4py import MPI diff --git a/pudb/settings.py b/pudb/settings.py index f4a604fd..3c8185cd 100644 --- a/pudb/settings.py +++ b/pudb/settings.py @@ -569,11 +569,11 @@ def _update_config( newvalue = shell_edit.get_edit_text() conf_dict.update(shell=newvalue, custom_shell=newvalue) else: - for shell, shell_rb in zip(shells, shell_rbs): + for shell, shell_rb in zip(shells, shell_rbs, strict=True): if shell_rb.get_state(): conf_dict["shell"] = shell - for display, display_rb in zip(displays, display_rbs): + for display, display_rb in zip(displays, display_rbs, strict=True): if display_rb.get_state(): conf_dict["display"] = display diff --git a/pudb/shell.py b/pudb/shell.py index 42438128..5a5d7378 100644 --- a/pudb/shell.py +++ b/pudb/shell.py @@ -1,6 +1,14 @@ from __future__ import annotations import sys +from collections import UserDict +from typing import TYPE_CHECKING, TypeVar + +from typing_extensions import override + + +if TYPE_CHECKING: + from collections.abc import Mapping, MutableMapping, Sequence try: @@ -31,7 +39,11 @@ # {{{ combined locals/globals dict -class SetPropagatingDict(dict): +K = TypeVar("K") +V = TypeVar("V") + + +class SetPropagatingDict(UserDict[K, V]): """ Combine dict into one, with assignments affecting a target dict @@ -47,19 +59,25 @@ class SetPropagatingDict(dict): debugger is inside a function frame. """ - def __init__(self, source_dicts, target_dict): - dict.__init__(self) + target_dict: MutableMapping[K, V] + + def __init__(self, + source_dicts: Sequence[Mapping[K, V]], + target_dict: MutableMapping[K, V]): + super().__init__() for s in source_dicts[::-1]: self.update(s) self.target_dict = target_dict - def __setitem__(self, key, value): - dict.__setitem__(self, key, value) + @override + def __setitem__(self, key: K, value: V): + super().__setitem__(key, value) self.target_dict[key] = value - def __delitem__(self, key): - dict.__delitem__(self, key) + @override + def __delitem__(self, key: K): + super().__delitem__(key) del self.target_dict[key] # }}} diff --git a/pudb/test/test_lowlevel.py b/pudb/test/test_lowlevel.py index 1dfb4f61..a9694132 100644 --- a/pudb/test/test_lowlevel.py +++ b/pudb/test/test_lowlevel.py @@ -91,12 +91,6 @@ def main(): 1, 335 } - if sys.version_info < (3, 10): - # if co_lines is unavailable, we appear to see these - expected.update([ - 128, # bogus, - 255, # bogus, - ]) if sys.version_info >= (3, 11): # See https://github.com/python/cpython/pull/94562 and # https://peps.python.org/pep-0626/ diff --git a/pudb/test/test_run.py b/pudb/test/test_run.py index 872f16fb..68c9c6e3 100644 --- a/pudb/test/test_run.py +++ b/pudb/test/test_run.py @@ -25,7 +25,7 @@ def test_main_version(capsys, mocker, argv): with pytest.raises(SystemExit) as ex: main() - assert ex.value == 0 + assert ex.value.code == 0 captured = capsys.readouterr() @@ -45,7 +45,7 @@ def test_main_v_with_args(capsys, mocker): with pytest.raises(SystemExit) as ex: main() - assert ex.value == 2 + assert ex.value.code == 2 captured = capsys.readouterr() diff --git a/pudb/test/test_var_view.py b/pudb/test/test_var_view.py index 2bbb8163..9dda3101 100644 --- a/pudb/test/test_var_view.py +++ b/pudb/test/test_var_view.py @@ -356,7 +356,7 @@ def test_containerlike_classes(self): generate_containerlike_class()): label = containerlike_class.name() value = containerlike_class(zip(string.ascii_lowercase, - range(3, 10))) + range(3, 10), strict=False)) self.assert_walks_contents(container=value, label=label) class_count = cls_idx + 1 diff --git a/pudb/theme.py b/pudb/theme.py index b9c0ad34..f62784a6 100644 --- a/pudb/theme.py +++ b/pudb/theme.py @@ -296,7 +296,7 @@ def get_palette(may_use_fancy_formats: bool, theme: str = "classic") -> list: from os.path import expanduser, expandvars fname = expanduser(expandvars(theme)) with open(fname) as inf: - exec(compile(inf.read(), fname, "exec"), symbols) # ruff:ignore[exec-builtin] + exec(compile(inf.read(), fname, "exec"), symbols) except FileNotFoundError: ui_log.error(f"Unable to locate custom theme file {theme!r}" ) diff --git a/pudb/ui_tools.py b/pudb/ui_tools.py index 451130d5..89f8265b 100644 --- a/pudb/ui_tools.py +++ b/pudb/ui_tools.py @@ -1,9 +1,10 @@ from __future__ import annotations -from typing import Callable, ClassVar, Hashable, Literal, Sequence, TypeVar +from collections.abc import Callable, Hashable, Sequence +from typing import ClassVar, Literal, TypeAlias, TypeVar import urwid -from typing_extensions import TypeAlias, override +from typing_extensions import override from urwid import Widget, calc_text_pos, calc_width @@ -42,7 +43,7 @@ def make_canvas( processed_attr: list[list[tuple[Hashable | None, int]]] = [] processed_cs: list[list[tuple[Literal["U", "0"] | None, int]]] = [] - for line, line_attr in zip(txt, attr): + for line, line_attr in zip(txt, attr, strict=True): # filter out zero-length attrs line_attr = [(aname, la) for aname, la in line_attr if la > 0] @@ -124,7 +125,7 @@ def keypress(self, size: UrwidSize, key: str): WrappedWidget_co = TypeVar("WrappedWidget_co", bound=Widget, covariant=True) EventListener: TypeAlias = Callable[ ["SignalWrap[WrappedWidget_co]", UrwidSize, str], - "str | None"] + str | None] # pyright ignore to paper over variance disagreement with urwid diff --git a/pudb/var_view.py b/pudb/var_view.py index e7339f7a..90a91446 100644 --- a/pudb/var_view.py +++ b/pudb/var_view.py @@ -31,12 +31,12 @@ import inspect import warnings from abc import ABC, abstractmethod -from collections.abc import Callable, Sized +from collections.abc import Callable, Iterator, Sized from dataclasses import dataclass -from typing import TYPE_CHECKING, ClassVar, Iterator, Literal, cast +from typing import TYPE_CHECKING, ClassVar, Literal, TypeAlias, cast import urwid -from typing_extensions import TypeAlias, override +from typing_extensions import override from pudb.lowlevel import ui_log from pudb.ui_tools import text_width @@ -537,7 +537,7 @@ def get_stringifier(iinfo: InspectInfo) -> Callable[[object], str]: from os.path import expanduser, expandvars custom_stringifier_fname = expanduser(expandvars(iinfo.display_type)) with open(custom_stringifier_fname) as inf: - exec(compile(inf.read(), custom_stringifier_fname, "exec"), # ruff:ignore[exec-builtin] + exec(compile(inf.read(), custom_stringifier_fname, "exec"), custom_stringifier_dict, custom_stringifier_dict) except FileNotFoundError: @@ -631,7 +631,7 @@ def walk_container(self, entry_id_path = f"{id_path}{id_path_ext}" self.walk_value(parent, - "[{}]".format(entry_label if entry_label else ""), + "[{}]".format(entry_label or ""), entry, entry_id_path) if is_empty: diff --git a/pyproject.toml b/pyproject.toml index c37588a8..bae16930 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ version = "2025.1.5" description = "A full-screen, console-based Python debugger" readme = "README.rst" license = "MIT" -requires-python = "~=3.8" +requires-python = "~=3.10" authors = [ { name = "Andreas Kloeckner", email = "inform@tiker.net" }, ]