misc: drop typing as T, use builtin generics

Python 3.10 is the minimum, so annotations use `X | None`, `list[X]`
and friends, enforced by ruff's pyupgrade rules. The remaining typing
names are imported directly, with abstract types coming from
collections.abc. first() is now generic over the item and default.
This commit is contained in:
Dash committed 2026-10-17 15:55:55 +02:00
1 parent ada8d5b6ed
commit 0a4358e2b8
40 files changed
+248 -236

No files matched your search

+6 -6
View File
@@ -3,7 +3,7 @@ import curses
import os
import signal
import time
import typing as T
from typing import Any, cast
from pqcli import lingo
from pqcli.config import Class, Race
@@ -68,7 +68,7 @@ class CursesUserInterface(BaseUserInterface):
curses.init_pair(COLOR_PROGRESSBAR, 0, 9)
curses.init_pair(COLOR_HIGHLIGHT, 3, -1)
def signal_handler(sig: T.Any, frame: T.Any) -> None:
def signal_handler(sig: Any, frame: Any) -> None:
if self.args.use_saves:
self.roster.save()
exit(0)
@@ -91,7 +91,7 @@ class CursesUserInterface(BaseUserInterface):
self._switch_to_roster_view()
while True:
key = T.cast(int, self._screen.getch())
key = cast(int, self._screen.getch())
if key == curses.ERR:
time.sleep(0.1)
@@ -115,7 +115,7 @@ class CursesUserInterface(BaseUserInterface):
self._switch_view(self._roster_view)
def _switch_to_create_char_name_view(
self, player_name: T.Optional[str] = None
self, player_name: str | None = None
) -> None:
view = ChooseCharacterNameView(self._screen, player_name)
view.on_cancel += self._switch_to_roster_view
@@ -123,7 +123,7 @@ class CursesUserInterface(BaseUserInterface):
self._switch_view(view)
def _switch_to_create_char_race_view(
self, player_name: str, race: T.Optional[Race] = None
self, player_name: str, race: Race | None = None
) -> None:
view = ChooseCharacterRaceView(self._screen, self.data.races, race)
view.on_cancel += lambda: self._switch_to_create_char_name_view(
@@ -135,7 +135,7 @@ class CursesUserInterface(BaseUserInterface):
self._switch_view(view)
def _switch_to_create_char_class_view(
self, player_name: str, race: Race, class_: T.Optional[Class] = None
self, player_name: str, race: Race, class_: Class | None = None
) -> None:
view = ChooseCharacterClassView(
self._screen, self.data.classes, class_
+5 -4
View File
@@ -1,14 +1,15 @@
import typing as T
from collections.abc import Callable
from typing import Any
class EventHandler:
def __init__(self) -> None:
self.callbacks: T.List[T.Callable[..., T.Any]] = []
self.callbacks: list[Callable[..., Any]] = []
def __call__(self, *args: T.Any, **kwargs: T.Any) -> None:
def __call__(self, *args: Any, **kwargs: Any) -> None:
for callback in self.callbacks:
callback(*args, **kwargs)
def __iadd__(self, callback: T.Callable[..., T.Any]) -> "EventHandler":
def __iadd__(self, callback: Callable[..., Any]) -> "EventHandler":
self.callbacks.append(callback)
return self
+17 -4
View File
@@ -1,7 +1,8 @@
import curses
import curses.ascii
import typing as T
from collections.abc import Callable, Iterable
from dataclasses import dataclass
from typing import Any, TypeVar, overload
KEYS_CYCLE = {curses.ascii.TAB}
KEYS_DOWN = set(map(ord, "jJ")) | {curses.KEY_DOWN}
@@ -13,10 +14,22 @@ KEYS_CANCEL = set(map(ord, "qQ")) | {curses.ascii.ESC}
@dataclass
class Choice:
keys: T.List[int]
keys: list[int]
desc: str
callback: T.Callable[..., T.Any]
callback: Callable[..., Any]
def first(source: T.Iterable[T.Any], default: T.Any = None) -> T.Any:
ItemT = TypeVar("ItemT")
DefaultT = TypeVar("DefaultT")
@overload
def first(source: Iterable[ItemT]) -> ItemT | None: ...
@overload
def first(source: Iterable[ItemT], default: DefaultT) -> ItemT | DefaultT: ...
def first(source: Iterable[Any], default: Any = None) -> Any:
return next(iter(source), default)
+2 -2
View File
@@ -1,8 +1,8 @@
import typing as T
from typing import Any
class BaseView:
def __init__(self, screen: T.Any) -> None:
def __init__(self, screen: Any) -> None:
self.screen = screen
def start(self) -> None:
@@ -1,5 +1,5 @@
import functools
import typing as T
from typing import Any
from pqcli.lingo import act_name, to_roman
from pqcli.roster import Roster
@@ -8,11 +8,11 @@ from pqcli.ui.curses.views.menu_view import MenuView
class ChooseCharacterView(MenuView):
def __init__(self, screen: T.Any, roster: Roster, title: str) -> None:
def __init__(self, screen: Any, roster: Roster, title: str) -> None:
super().__init__(screen, title)
for y, player in enumerate(roster.players, 1):
key: T.Optional[str] = str(y % 10) if y <= 10 else None
key: str | None = str(y % 10) if y <= 10 else None
best_equip = player.equipment.best
+3 -3
View File
@@ -1,4 +1,4 @@
import typing as T
from typing import Any
from pqcli.ui.curses.event_handler import EventHandler
from pqcli.ui.curses.util import Choice
@@ -8,14 +8,14 @@ from .base_view import BaseView
class ConfirmView(BaseView):
def __init__(self, screen: T.Any, title: str) -> None:
def __init__(self, screen: Any, title: str) -> None:
super().__init__(screen)
self.title = f"{title}:"
self.on_cancel = EventHandler()
self.on_confirm = EventHandler()
self.choices: T.List[Choice] = [
self.choices: list[Choice] = [
Choice(
keys=list(map(ord, "yY")),
desc="[Y] Yes",
@@ -1,5 +1,6 @@
import functools
import typing as T
from collections.abc import Sequence
from typing import Any
from pqcli.config import Class
from pqcli.ui.curses.util import KEYS_CANCEL, Choice
@@ -9,9 +10,9 @@ from pqcli.ui.curses.views.menu_view import MenuView
class ChooseCharacterClassView(MenuView):
def __init__(
self,
screen: T.Any,
classes: T.Sequence[Class],
class_: T.Optional[Class] = None,
screen: Any,
classes: Sequence[Class],
class_: Class | None = None,
) -> None:
super().__init__(
screen,
@@ -20,7 +21,7 @@ class ChooseCharacterClassView(MenuView):
)
for y, class_ in enumerate(classes, 1):
key: T.Optional[str] = str(y % 10) if y <= 10 else None
key: str | None = str(y % 10) if y <= 10 else None
self._choices.append(
Choice(
keys=[ord(key)] if key is not None else [],
@@ -1,6 +1,6 @@
import curses
import curses.ascii
import typing as T
from typing import Any
from pqcli.mechanic import generate_name
from pqcli.ui.curses.event_handler import EventHandler
@@ -10,17 +10,15 @@ from pqcli.ui.curses.widgets.focusable import focus_standout
class ChooseCharacterNameView(BaseView):
def __init__(
self, screen: T.Any, character_name: T.Optional[str] = None
) -> None:
def __init__(self, screen: Any, character_name: str | None = None) -> None:
super().__init__(screen)
self.on_cancel = EventHandler()
self.on_confirm = EventHandler()
self._active_widget = 0
self._win: T.Optional[T.Any] = None
self._text_win: T.Optional[T.Any] = None
self._win: Any | None = None
self._text_win: Any | None = None
self._text = character_name or generate_name()
def start(self) -> None:
@@ -1,5 +1,6 @@
import functools
import typing as T
from collections.abc import Sequence
from typing import Any
from pqcli.config import Race
from pqcli.ui.curses.util import KEYS_CANCEL, Choice
@@ -9,9 +10,9 @@ from pqcli.ui.curses.views.menu_view import MenuView
class ChooseCharacterRaceView(MenuView):
def __init__(
self,
screen: T.Any,
races: T.Sequence[Race],
race: T.Optional[Race] = None,
screen: Any,
races: Sequence[Race],
race: Race | None = None,
) -> None:
super().__init__(
screen,
@@ -20,7 +21,7 @@ class ChooseCharacterRaceView(MenuView):
)
for y, race in enumerate(races, 1):
key: T.Optional[str] = str(y % 10) if y <= 10 else None
key: str | None = str(y % 10) if y <= 10 else None
self._choices.append(
Choice(
keys=[ord(key)] if key is not None else [],
@@ -1,5 +1,5 @@
import curses.ascii
import typing as T
from typing import Any
from pqcli.config import PRIME_STATS
from pqcli.mechanic import StatsBuilder
@@ -10,15 +10,15 @@ from pqcli.ui.curses.widgets.focusable import focus_standout
class ChooseCharacterStatsView(BaseView):
def __init__(self, screen: T.Any) -> None:
def __init__(self, screen: Any) -> None:
super().__init__(screen)
self.on_cancel = EventHandler()
self.on_confirm = EventHandler()
self._active_widget = 0
self._win: T.Optional[T.Any] = None
self._stats_win: T.Optional[T.Any] = None
self._win: Any | None = None
self._stats_win: Any | None = None
self._stats_builder = StatsBuilder()
self._stats = self._stats_builder.roll()
+4 -4
View File
@@ -1,7 +1,7 @@
import argparse
import curses
import datetime
import typing as T
from typing import Any
from pqcli.game_data import GameData
from pqcli.mechanic import Player, SignalMixin, Simulation
@@ -30,7 +30,7 @@ from .task_progress_window import TaskProgressWindow
class GameView(BaseView):
def __init__(
self,
screen: T.Any,
screen: Any,
roster: Roster,
player: Player,
args: argparse.Namespace,
@@ -225,7 +225,7 @@ class GameView(BaseView):
x=0,
)
self._focusable_children: T.List[Focusable] = [
self._focusable_children: list[Focusable] = [
self._char_sheet_win,
self._spell_book_win,
self._equipment_win,
@@ -233,7 +233,7 @@ class GameView(BaseView):
self._plot_win,
self._quest_book_win,
]
self._children: T.List[Widget] = self._focusable_children + [
self._children: list[Widget] = self._focusable_children + [
self._task_win
]
@@ -1,4 +1,4 @@
import typing as T
from typing import Any
from pqcli.mechanic import Player, StatType
from pqcli.ui.curses.widgets import Focusable
@@ -8,7 +8,7 @@ from .progress_bar_window import DataTableProgressBarWindow
class CharacterSheetWindow(Focusable, DataTableProgressBarWindow):
def __init__(
self, player: Player, parent: T.Any, h: int, w: int, y: int, x: int
self, player: Player, parent: Any, h: int, w: int, y: int, x: int
) -> None:
super().__init__(
parent,
@@ -1,4 +1,4 @@
import typing as T
from typing import Any
from pqcli.mechanic import EquipmentType, Player
from pqcli.ui.curses.widgets import DataTable, Focusable, WindowWrapper
@@ -6,7 +6,7 @@ from pqcli.ui.curses.widgets import DataTable, Focusable, WindowWrapper
class EquipmentWindow(Focusable, WindowWrapper):
def __init__(
self, player: Player, parent: T.Any, h: int, w: int, y: int, x: int
self, player: Player, parent: Any, h: int, w: int, y: int, x: int
) -> None:
super().__init__(parent, h, w, y, x)
self._on_focus_change += self._render
@@ -39,7 +39,7 @@ class EquipmentWindow(Focusable, WindowWrapper):
self._render()
def _sync_equipment_change(
self, equipment_type: EquipmentType, item_name: T.Optional[str]
self, equipment_type: EquipmentType, item_name: str | None
) -> None:
self._data_table.set(equipment_type.value, item_name or "")
self._data_table.select(equipment_type.value)
@@ -1,4 +1,4 @@
import typing as T
from typing import Any
from pqcli.mechanic import InventoryItem, Player
from pqcli.ui.curses.widgets import Focusable
@@ -8,7 +8,7 @@ from .progress_bar_window import DataTableProgressBarWindow
class InventoryWindow(Focusable, DataTableProgressBarWindow):
def __init__(
self, player: Player, parent: T.Any, h: int, w: int, y: int, x: int
self, player: Player, parent: Any, h: int, w: int, y: int, x: int
) -> None:
super().__init__(
parent,
@@ -1,4 +1,4 @@
import typing as T
from typing import Any
from pqcli.lingo import act_name
from pqcli.mechanic import Player
@@ -11,7 +11,7 @@ class PlotWindow(Focusable, ListBoxProgressBarWindow):
cutoff = 100
def __init__(
self, player: Player, parent: T.Any, h: int, w: int, y: int, x: int
self, player: Player, parent: Any, h: int, w: int, y: int, x: int
) -> None:
super().__init__(
parent, h, w, y, x, " Plot Development ", show_time=True
@@ -1,5 +1,5 @@
import curses
import typing as T
from typing import Any
from pqcli.ui.curses.widgets import (
DataTable,
@@ -13,7 +13,7 @@ from pqcli.ui.curses.widgets import (
class ProgressBarWindow(WindowWrapper):
def __init__(
self,
parent: T.Any,
parent: Any,
h: int,
w: int,
y: int,
@@ -82,7 +82,7 @@ class ProgressBarWindow(WindowWrapper):
class DataTableProgressBarWindow(ProgressBarWindow):
def __init__(
self,
parent: T.Any,
parent: Any,
h: int,
w: int,
y: int,
@@ -123,7 +123,7 @@ class DataTableProgressBarWindow(ProgressBarWindow):
class ListBoxProgressBarWindow(ProgressBarWindow):
def __init__(
self,
parent: T.Any,
parent: Any,
h: int,
w: int,
y: int,
@@ -1,4 +1,4 @@
import typing as T
from typing import Any
from pqcli.mechanic import Player
from pqcli.ui.curses.widgets import Focusable
@@ -10,7 +10,7 @@ class QuestBookWindow(Focusable, ListBoxProgressBarWindow):
cutoff = 100
def __init__(
self, player: Player, parent: T.Any, h: int, w: int, y: int, x: int
self, player: Player, parent: Any, h: int, w: int, y: int, x: int
) -> None:
super().__init__(parent, h, w, y, x, " Quests ", show_time=True)
self._on_focus_change += self._render
@@ -1,4 +1,4 @@
import typing as T
from typing import Any
from pqcli.lingo import to_roman
from pqcli.mechanic import Player, Spell
@@ -7,7 +7,7 @@ from pqcli.ui.curses.widgets import DataTable, Focusable, WindowWrapper
class SpellBookWindow(Focusable, WindowWrapper):
def __init__(
self, player: Player, parent: T.Any, h: int, w: int, y: int, x: int
self, player: Player, parent: Any, h: int, w: int, y: int, x: int
) -> None:
super().__init__(parent, h, w, y, x)
self._on_focus_change += self._render
@@ -1,4 +1,4 @@
import typing as T
from typing import Any
from pqcli.mechanic import Player
from pqcli.ui.curses.widgets import (
@@ -11,7 +11,7 @@ from pqcli.ui.curses.widgets import (
class TaskProgressWindow(Focusable, WindowWrapper):
def __init__(
self, player: Player, parent: T.Any, h: int, w: int, y: int, x: int
self, player: Player, parent: Any, h: int, w: int, y: int, x: int
) -> None:
super().__init__(parent, h, w, y, x)
+3 -3
View File
@@ -1,4 +1,4 @@
import typing as T
from typing import Any
from pqcli.ui.curses.event_handler import EventHandler
from pqcli.ui.curses.util import Choice
@@ -8,7 +8,7 @@ from pqcli.ui.curses.widgets import Menu
class MenuView(BaseView):
def __init__(
self, screen: T.Any, title: str, active_choice: int = 0
self, screen: Any, title: str, active_choice: int = 0
) -> None:
super().__init__(screen)
@@ -16,7 +16,7 @@ class MenuView(BaseView):
self.on_confirm = EventHandler()
self._title = f"{title}:"
self._choices: T.List[Choice] = []
self._choices: list[Choice] = []
self._active_choice = active_choice
def start(self) -> None:
+4 -4
View File
@@ -1,5 +1,5 @@
import curses
import typing as T
from typing import Any
from pqcli.ui.curses.colors import COLOR_LOGO, COLOR_LOGO_ALT, has_colors
from pqcli.ui.curses.event_handler import EventHandler
@@ -29,7 +29,7 @@ LOGO = """
class MainMenu(Menu):
def __init__(
self, choices: T.List[Choice], scr_height: int, scr_width: int
self, choices: list[Choice], scr_height: int, scr_width: int
) -> None:
super().__init__(
header=LOGO,
@@ -50,7 +50,7 @@ class MainMenu(Menu):
class RosterView(BaseView):
def __init__(self, screen: T.Any) -> None:
def __init__(self, screen: Any) -> None:
super().__init__(screen)
self.on_create = EventHandler()
@@ -58,7 +58,7 @@ class RosterView(BaseView):
self.on_delete = EventHandler()
self.on_quit = EventHandler()
self.choices: T.List[Choice] = [
self.choices: list[Choice] = [
Choice(
keys=list(map(ord, "cC")),
desc="[C] Create new character",
+5 -5
View File
@@ -1,5 +1,5 @@
import curses
import typing as T
from typing import Any
class Widget:
@@ -12,10 +12,10 @@ class Widget:
class WindowWrapper(Widget):
def __init__(
self, parent: T.Optional[T.Any], h: int, w: int, y: int, x: int
self, parent: Any | None, h: int, w: int, y: int, x: int
) -> None:
self._parent = parent
self._win: T.Optional[T.Any]
self._win: Any | None
if not parent:
self._win = None
@@ -30,12 +30,12 @@ class WindowWrapper(Widget):
del self._win
self._win = None
def getbegyx(self) -> T.Tuple[int, int]:
def getbegyx(self) -> tuple[int, int]:
if not self._win:
return (0, 0)
return self._win.getbegyx()
def getmaxyx(self) -> T.Tuple[int, int]:
def getmaxyx(self) -> tuple[int, int]:
if not self._win:
return (0, 0)
return self._win.getmaxyx()
+6 -6
View File
@@ -1,5 +1,5 @@
import curses
import typing as T
from typing import Any
from pqcli.ui.curses.colors import COLOR_HIGHLIGHT, has_colors
@@ -9,7 +9,7 @@ from .scrollable import Scrollable
class DataTable(Scrollable):
def __init__(
self,
parent: T.Any,
parent: Any,
h: int,
w: int,
y: int,
@@ -19,8 +19,8 @@ class DataTable(Scrollable):
super().__init__(parent, h, w, y, x)
self._align_right = align_right
self._items: T.List[T.Tuple[str, str]]
self._selected: T.Optional[int] = None
self._items: list[tuple[str, str]]
self._selected: int | None = None
def add(self, text: str, value: str) -> None:
self._items.append((text, value))
@@ -40,10 +40,10 @@ class DataTable(Scrollable):
del self._items[idx]
self.scroll_to_item(idx)
def select(self, text: T.Optional[str]) -> None:
def select(self, text: str | None) -> None:
self._selected = None if text is None else self.get_idx(text)
def get_idx(self, text: str) -> T.Optional[int]:
def get_idx(self, text: str) -> int | None:
for idx, row in enumerate(self._items):
if row[0].strip() == text.strip():
return idx
+5 -4
View File
@@ -1,13 +1,14 @@
import contextlib
import curses
import typing as T
from collections.abc import Generator
from typing import Any
from pqcli.ui.curses.colors import COLOR_FOCUSED, has_colors
from pqcli.ui.curses.event_handler import EventHandler
@contextlib.contextmanager
def focus_standout(focused: bool, win: T.Any) -> T.Generator:
def focus_standout(focused: bool, win: Any) -> Generator:
if focused:
if has_colors():
win.attron(curses.color_pair(COLOR_FOCUSED))
@@ -22,7 +23,7 @@ def focus_standout(focused: bool, win: T.Any) -> T.Generator:
class Focusable:
def __init__(self, *args: T.Any, **kwargs: T.Any) -> None:
def __init__(self, *args: Any, **kwargs: Any) -> None:
self._focused = False
self._on_focus_change = EventHandler()
super().__init__(*args, **kwargs)
@@ -37,6 +38,6 @@ class Focusable:
self._on_focus_change()
@contextlib.contextmanager
def focus_standout(self, win: T.Any) -> T.Generator:
def focus_standout(self, win: Any) -> Generator:
with focus_standout(self._focused, win):
yield
+6 -6
View File
@@ -1,5 +1,5 @@
import curses
import typing as T
from typing import Any
from pqcli.ui.curses.colors import COLOR_HIGHLIGHT, has_colors
@@ -7,11 +7,11 @@ from .scrollable import Scrollable
class ListBox(Scrollable):
_items: T.List[str]
_items: list[str]
def __init__(self, parent: T.Any, h: int, w: int, y: int, x: int) -> None:
def __init__(self, parent: Any, h: int, w: int, y: int, x: int) -> None:
super().__init__(parent, h, w, y, x)
self._selected: T.Optional[int] = None
self._selected: int | None = None
def add(self, text: str) -> None:
self._items.append(text)
@@ -22,7 +22,7 @@ class ListBox(Scrollable):
idx %= len(self._items)
self._items[idx] = value
def get(self, idx: int) -> T.Optional[str]:
def get(self, idx: int) -> str | None:
if not self._items:
return None
if idx < 0:
@@ -35,7 +35,7 @@ class ListBox(Scrollable):
def delete(self, idx: int, count: int = 1) -> None:
del self._items[idx : idx + count]
def select(self, idx: T.Optional[int]) -> None:
def select(self, idx: int | None) -> None:
if idx < 0 and len(self._items):
idx %= len(self._items)
self._selected = idx
+4 -4
View File
@@ -1,6 +1,6 @@
import curses
import curses.ascii
import typing as T
from typing import Any
from pqcli.ui.curses.util import KEYS_CYCLE, KEYS_DOWN, KEYS_UP, Choice
from pqcli.ui.curses.widgets.focusable import focus_standout
@@ -12,7 +12,7 @@ class Menu(Widget):
def __init__(
self,
header: str,
choices: T.List[Choice],
choices: list[Choice],
active_choice: int,
scr_height: int,
scr_width: int,
@@ -28,7 +28,7 @@ class Menu(Widget):
w = max(map(len, all_lines)) + 1
h = len(all_lines) + 1
self._pad: T.Optional[T.Any] = curses.newpad(h, w)
self._pad: Any | None = curses.newpad(h, w)
for y, line in enumerate(self._header_lines):
self._pad.move(y, 0)
@@ -40,7 +40,7 @@ class Menu(Widget):
del self._pad
self._pad = None
def getmaxyx(self) -> T.Tuple[int, int]:
def getmaxyx(self) -> tuple[int, int]:
if not self._pad:
return (0, 0)
return self._pad.getmaxyx()
+4 -4
View File
@@ -1,6 +1,6 @@
import curses
import datetime
import typing as T
from typing import Any
from pqcli.lingo import format_timespan
from pqcli.ui.curses.colors import COLOR_PROGRESSBAR, has_colors
@@ -9,7 +9,7 @@ from pqcli.ui.curses.widgets.base import WindowWrapper
class ProgressBar(WindowWrapper):
def __init__(
self, parent: T.Any, h: int, w: int, y: int, x: int, show_time: bool
self, parent: Any, h: int, w: int, y: int, x: int, show_time: bool
) -> None:
super().__init__(parent, h, w, y, x)
@@ -17,10 +17,10 @@ class ProgressBar(WindowWrapper):
self._max_pos = 1.0
self._show_time = show_time
self._last_tick: T.Optional[T.Tuple[datetime.datetime, float]] = None
self._last_tick: tuple[datetime.datetime, float] | None = None
@property
def time_left(self) -> T.Optional[datetime.timedelta]:
def time_left(self) -> datetime.timedelta | None:
if self._last_tick is None:
return None
time_then, pos_then = self._last_tick
+4 -4
View File
@@ -1,5 +1,5 @@
import curses
import typing as T
from typing import Any
from pqcli.ui.curses.colors import (
COLOR_SCROLLBAR_THUMB,
@@ -11,11 +11,11 @@ from .base import WindowWrapper
class Scrollable(WindowWrapper):
def __init__(self, parent: T.Any, h: int, w: int, y: int, x: int) -> None:
def __init__(self, parent: Any, h: int, w: int, y: int, x: int) -> None:
super().__init__(parent, h, w, y, x)
self._scroll_pos = 0
self._pad: T.Optional[T.Any] = curses.newpad(1, 1)
self._items: T.List[T.Any] = []
self._pad: Any | None = curses.newpad(1, 1)
self._items: list[Any] = []
def clear(self) -> None:
self._items = []