Curses screens, windows and pads are typed as curses.window, and the
widgets that may get no parent window take curses.window | None.
- EventHandler is generic over a ParamSpec, so each handler declares
the arguments it passes, and MenuView is generic over the item its
on_confirm hands out. Choice callbacks take no arguments.
- Scrollable is generic over its items, which DataTable and ListBox
fix to tuple[str, str] and str.
- random.choice() and choice_low() are generic over the sequence, so
the casts at their call sites are gone. BasicUserInterface.menu()
and util.first() are generic too.
- Signal callbacks and their payloads are Callable[..., object] and
object, since emit() only forwards them.
- JSON values in game_data are object and narrowed by isinstance,
and data files are Traversable.
- Signal handlers take an int and a FrameType | None, and
list_players() takes a TextIO.
- The focus_standout() context managers return
Generator[None, None, None].
Any remains in three places: Player.__setstate__, whose pickled
__dict__ holds values of every type; Focusable.__init__, a mixin that
passes *args and **kwargs through to the next class; and the values
load_game_data() collects, whose element type depends on the field
each one fills.
ty reported 20 diagnostics, and 0.0.89 adds a few more; this clears
them all without suppressions.
- The quest monster is held in a local before being stored on the
quest book, so its name no longer reads from an Optional.
- PgUp/PgDn narrow the focused window with isinstance against a
runtime-checkable PageScrollable protocol instead of hasattr.
- The ^t cheat used to monkeypatch SignalMixin.emit. SignalMixin now
has a muted() context manager that emit() checks, and it restores
emission even if a tick raises.
- GameView keeps its children as GameWindow, a protocol declaring
stop() and sync(), so the cheat's resync type-checks.
- curses.ascii is imported explicitly where it is used.
- ProgressBarWindow is now Focusable itself, instead of calling
Focusable.focus_standout() on a class that is not Focusable. Its
four subclasses drop the duplicate Focusable base; the MRO and
initialisation order are unchanged.
- MainMenu narrows the Optional pad before colouring the logo.
- The tests that record tasks assert player.task is set.
- Context managers are annotated as Generator rather than Iterator,
which ty flags as deprecated for @contextmanager.
- The redundant cast() around getch() is gone.
- The frozen GameData test assigns through setattr(), since the
direct assignment is an error by design.
Bug fixes:
- ListBox.select(None) crashed on `None < 0`; it now clears the
selection, as DataTable.select(None) already does.
- ListBox.get() caught ValueError, which indexing never raises; it
now catches IndexError, so an out-of-range index returns None as
intended.
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.
ruff format keeps black's style at the same line length, and ruff
check covers import sorting plus pycodestyle and pyflakes. Three
comments and a test regex are wrapped to fit 79 columns.