Source code for python_pkg_template._character

#!/usr/bin/env python3

# PGK FILE DESCRIPTION
# 2026 (c) YOUR NAME
# https://github.com/username/
# your.mail@mail.com

from __future__ import annotations

import random
import logging
from pydantic import BaseModel, Field, ConfigDict, computed_field

from typing import Annotated, Optional, Literal, Any, override

logger = logging.getLogger(__name__)


[docs] class Character(BaseModel): r"""Core data structure representing a character. Bases Pydantic `BaseModel <https://pydantic.dev/docs/validation/latest/api/pydantic/base_model/#pydantic.BaseModel>`_. Attributes Summary ------------------ Here is a short summary about the class attributes, for more details on the specific Pydantic validation requirements please refer to the corresponding attributes themselves. Required ^^^^^^^^ The following attributes are required: name : str The name of the character. Optional ^^^^^^^^ The following attributes are optional: race : one of "Elf", "Half-Elf", "Human", or None, default = None The race of the character. Should be one of Elf, Half-Elf, or Human. min_damage : float, default = 0.0 Minimum damage the character deals. max_damage : float, default = 0.0 Maximum damage the character deals. Notes ----- Minimum and maximum damage are automatically switched depending on which is greater. Examples -------- >>> from python_pkg_template import Character >>> character = Character(name="John Baldur") """ name: Annotated[str, Field(frozen=True, description="Name of the character.")] r""" Name of the character. """ race: Annotated[ Optional[Literal["Elf", "Half-Elf", "Human"]], Field(frozen=True, description="Race of the character."), ] = None r""" Race of the character. Should be one of Elf, Half-Elf, or Human. """ min_damage: Annotated[ float, Field(frozen=False, description="Minimum damage the character deals.") ] = 0.0 r""" Minimum damage the character deals. Is automatically switched with max_damage if max_damage is smaller. """ max_damage: Annotated[ float, Field(frozen=False, description="Maximum damage the character deals.") ] = 0.0 r""" Maximum damage the character deals. Is automatically switched with min_damage if min_damage is greater. """ model_config = ConfigDict( validate_assignment=True, strict=True, str_strip_whitespace=True ) r""" Pydantic configuration for the underlying validation model. """ @computed_field(description="Average damage dealt by the character.") @property def avg_damage(self) -> float: r""" Average damage dealt by the character. """ return (self.min_damage + self.max_damage) / 2.0
[docs] @override def model_post_init(self, context: Any = None) -> None: r""" Performs extra validation and post init functions. Warnings -------- This method should not be called manually! """ if self.min_damage > self.max_damage: self.__dict__["min_damage"], self.__dict__["max_damage"] = ( # pyright: ignore[reportIndexIssue] self.max_damage, self.min_damage, )
def __getitem__(self, key: str) -> Any: r""" Support for dict-like access. """ try: return getattr(self, key) except AttributeError as e: raise KeyError(f"'{key}' is not a valid field!") from e def __contains__(self, key: str) -> bool: r""" Support for ``in`` operator. """ return hasattr(self, key)
[docs] def copy_with_update(self, update: dict[str, Any] = {}) -> Character: r"""Creates a deep copy of the class with optional attribute updates. Parameters ---------- update : dict of str, any, default = empty dict Dictionary mapping attribute names (str) to their updated values. The default (empty dict) will create a deep copy with the original attribute values. Returns ------- Character New character with optionally updated attributes. Examples -------- >>> from python_pkg_template import Character >>> character = Character(name="John Baldur") >>> new_character = character.copy_with_update(update={"race": "Human"}) """ return Character( name=update["name"] if "name" in update else self.name, race=update["race"] if "race" in update else self.race, min_damage=update["min_damage"] if "min_damage" in update else self.min_damage, max_damage=update["max_damage"] if "max_damage" in update else self.max_damage, )
[docs] def attack(self) -> float: r"""Get the attack damage of the next attack. Returns ------- float The attack damage of the attack. Examples -------- >>> from python_pkg_template import Character >>> character = Character(name="John Baldur") >>> character.attack() 0.0 """ return self.min_damage + (self.max_damage - self.min_damage) * random.random() # noqa: S311