bgg_search
1from importlib.metadata import version 2 3from bgg_search._protocol import BggClientProtocol 4from bgg_search.exceptions import BggApiError, BggNotFoundError, BggParseError, BggSearchError 5from bgg_search.models import GameDetails, GameSummary 6from bgg_search.search import get_game, search_games 7 8__version__ = version("bgg-search") 9 10__all__ = [ 11 "__version__", 12 "BggClientProtocol", 13 "BggApiError", 14 "BggNotFoundError", 15 "BggParseError", 16 "BggSearchError", 17 "GameDetails", 18 "GameSummary", 19 "get_game", 20 "search_games", 21]
7@runtime_checkable 8class BggClientProtocol(Protocol): 9 """Contract for BGG API client implementations. 10 11 Pass any conforming object to `search_games` or `get_game`. 12 The bundled `BggClient` satisfies this protocol. 13 """ 14 15 def search(self, query: str) -> list[GameSummary]: 16 """Search BGG for board games matching `query`. 17 18 Returns results in BGG API order; returns an empty list when no games match. 19 """ 20 ... 21 22 def get_game(self, game_id: int) -> GameDetails: 23 """Fetch full details for the game identified by `game_id`. 24 25 Raises `BggNotFoundError` when the ID does not exist on BGG. 26 """ 27 ...
Contract for BGG API client implementations.
Pass any conforming object to search_games or get_game.
The bundled BggClient satisfies this protocol.
1960def _no_init_or_replace_init(self, *args, **kwargs): 1961 cls = type(self) 1962 1963 if cls._is_protocol: 1964 raise TypeError('Protocols cannot be instantiated') 1965 1966 # Already using a custom `__init__`. No need to calculate correct 1967 # `__init__` to call. This can lead to RecursionError. See bpo-45121. 1968 if cls.__init__ is not _no_init_or_replace_init: 1969 return 1970 1971 # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`. 1972 # The first instantiation of the subclass will call `_no_init_or_replace_init` which 1973 # searches for a proper new `__init__` in the MRO. The new `__init__` 1974 # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent 1975 # instantiation of the protocol subclass will thus use the new 1976 # `__init__` and no longer call `_no_init_or_replace_init`. 1977 for base in cls.__mro__: 1978 init = base.__dict__.get('__init__', _no_init_or_replace_init) 1979 if init is not _no_init_or_replace_init: 1980 cls.__init__ = init 1981 break 1982 else: 1983 # should not happen 1984 cls.__init__ = object.__init__ 1985 1986 cls.__init__(self, *args, **kwargs)
15 def search(self, query: str) -> list[GameSummary]: 16 """Search BGG for board games matching `query`. 17 18 Returns results in BGG API order; returns an empty list when no games match. 19 """ 20 ...
Search BGG for board games matching query.
Returns results in BGG API order; returns an empty list when no games match.
22 def get_game(self, game_id: int) -> GameDetails: 23 """Fetch full details for the game identified by `game_id`. 24 25 Raises `BggNotFoundError` when the ID does not exist on BGG. 26 """ 27 ...
Fetch full details for the game identified by game_id.
Raises BggNotFoundError when the ID does not exist on BGG.
6class BggApiError(BggSearchError): 7 """Raised when the BGG API returns an HTTP error. 8 9 ``status_code`` holds the HTTP status code, or ``None`` if the error 10 occurred before a response was received. 11 """ 12 13 def __init__(self, message: str, status_code: int | None = None) -> None: 14 super().__init__(message) 15 self.status_code = status_code
Raised when the BGG API returns an HTTP error.
status_code holds the HTTP status code, or None if the error
occurred before a response was received.
18class BggNotFoundError(BggSearchError): 19 """Raised when the requested game ID does not exist on BGG."""
Raised when the requested game ID does not exist on BGG.
Raised when the BGG API response cannot be parsed.
Base class for all bgg-search exceptions.
15@dataclass(frozen=True) 16class GameDetails: 17 """Full details for a board game retrieved from BGG.""" 18 19 id: int 20 """BGG game ID.""" 21 name: str 22 """Primary game title.""" 23 year_published: int | None 24 """Year of first publication, or ``None`` if unknown.""" 25 min_players: int | None 26 """Minimum number of players, or ``None`` if unknown.""" 27 max_players: int | None 28 """Maximum number of players, or ``None`` if unknown.""" 29 min_playtime: int | None 30 """Minimum play time in minutes, or ``None`` if unknown.""" 31 max_playtime: int | None 32 """Maximum play time in minutes, or ``None`` if unknown.""" 33 weight: float | None 34 """BGG complexity weight on a 1.0–5.0 scale, or ``None`` if unrated.""" 35 bgg_rating: float | None 36 """BGG community rating on a 1.0–10.0 scale, or ``None`` if unrated."""
Full details for a board game retrieved from BGG.
5@dataclass(frozen=True) 6class GameSummary: 7 """A board game entry returned from a BGG search.""" 8 9 id: int 10 """BGG game ID.""" 11 name: str 12 """Primary game title."""
A board game entry returned from a BGG search.
14def get_game(game_id: int, client: BggClientProtocol) -> GameDetails: 15 """Fetch full details for the game identified by `game_id` using `client`. 16 17 Raises `BggNotFoundError` when the ID does not exist on BGG. 18 """ 19 return client.get_game(game_id)
Fetch full details for the game identified by game_id using client.
Raises BggNotFoundError when the ID does not exist on BGG.
6def search_games(query: str, client: BggClientProtocol) -> list[GameSummary]: 7 """Search BGG for board games matching `query` using `client`. 8 9 Returns results in BGG API order; returns an empty list when no games match. 10 """ 11 return client.search(query)
Search BGG for board games matching query using client.
Returns results in BGG API order; returns an empty list when no games match.