From dbbc32f4ecb6b8a39fad7cf6a1981b74ae00823d Mon Sep 17 00:00:00 2001 From: onyx-and-iris Date: Mon, 23 Feb 2026 16:53:33 +0000 Subject: [PATCH] swap out the NamedTuple for a frozen dataclass. move the responsibility of sorting the numbers from the caller to the class initialisation. (now the string representation matches the internal state) move logging of ValueError from tui.py to lottery.py patch bump --- pyproject.toml | 2 +- src/lottery_tui/lottery.py | 26 ++++++++++++++++++++------ src/lottery_tui/tui.py | 11 +---------- 3 files changed, 22 insertions(+), 17 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 2f7febb..f7876de 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "lottery-tui" -version = "0.2.6" +version = "0.2.7" description = "A terminal user interface for lottery games." authors = [{ name = "onyx-and-iris", email = "code@onyxandiris.online" }] dependencies = ["textual>=8.0.0", "loguru>=0.7.3"] diff --git a/src/lottery_tui/lottery.py b/src/lottery_tui/lottery.py index 3c37d81..0ae13ac 100644 --- a/src/lottery_tui/lottery.py +++ b/src/lottery_tui/lottery.py @@ -1,18 +1,30 @@ import random from abc import ABC, abstractmethod -from typing import NamedTuple +from dataclasses import dataclass + +from loguru import logger -class Result(NamedTuple): - """A named tuple to hold the results of a lottery draw.""" +@dataclass(frozen=True) +class Result: + """A dataclass to hold the results of a lottery draw with auto-sorted numbers.""" kind: str numbers: list[int] bonus: list[int] | None + def __post_init__(self): + """Sort the numbers after initialization. + + We use super().__setattr__ to bypass the frozen nature of the dataclass for sorting. + """ + super().__setattr__('numbers', sorted(self.numbers)) + if self.bonus: + super().__setattr__('bonus', sorted(self.bonus)) + def __str__(self) -> str: """Return a string representation of the lottery result.""" - out = f'Numbers: {", ".join(str(n) for n in sorted(self.numbers))}' + out = f'Numbers: {", ".join(str(n) for n in self.numbers)}' if self.bonus: match self.kind: case 'EuroMillions': @@ -23,7 +35,7 @@ class Result(NamedTuple): bonus_name = 'Thunderball' case _: bonus_name = 'Bonus Numbers' - out += f'\n{bonus_name}: {", ".join(str(n) for n in sorted(self.bonus))}' + out += f'\n{bonus_name}: {", ".join(str(n) for n in self.bonus)}' return out @@ -116,5 +128,7 @@ def request_lottery_obj(lottery_name: str) -> Lottery: """Return a lottery object based on the provided lottery name.""" lottery_cls = registry.get(lottery_name.lower()) if lottery_cls is None: - raise ValueError(f"Lottery '{lottery_name}' not found.") + ERR_MSG = f"Lottery '{lottery_name}' not found. Available lotteries: {', '.join(registry.keys())}" + logger.error(ERR_MSG) + raise ValueError(ERR_MSG) return lottery_cls() diff --git a/src/lottery_tui/tui.py b/src/lottery_tui/tui.py index 77bda3d..a7de257 100644 --- a/src/lottery_tui/tui.py +++ b/src/lottery_tui/tui.py @@ -1,4 +1,3 @@ -from loguru import logger from rich.text import Text from textual.app import App, ComposeResult from textual.containers import Container @@ -49,15 +48,7 @@ class LotteryTUI(App): ) return - selected_lottery = self.query_one('#lottery-select').value - - try: - lottery_obj = request_lottery_obj(selected_lottery) - except ValueError: - ERR_MSG = f'Invalid lottery selection: {selected_lottery}' - logger.exception(ERR_MSG) - raise - + lottery_obj = request_lottery_obj(self.query_one('#lottery-select').value) result = lottery_obj.draw() self._update_result_label(str(result))