Source code for simplebench.reporters.choice.choice

"""``Choice()`` for reporters."""
from __future__ import annotations

from collections.abc import Hashable
from typing import TYPE_CHECKING, Any

from simplebench.enums import FlagType, Format, Section, Target
from simplebench.reporters.choice.choice_conf import ChoiceConf
from simplebench.reporters.choice.exceptions import _ChoiceErrorTag
from simplebench.reporters.protocols import ChoiceProtocol
from simplebench.reporters.reporter.options import ReporterOptions
from simplebench.validators import validate_type

_REPORTER_IMPORTED: bool = False
"""Indicates whether Reporter has been imported yet."""


[docs] def deferred_reporter_import() -> None: """Deferred import of :class:`~simplebench.reporters.reporter.Reporter` to avoid circular imports during initialization. """ global Reporter, _REPORTER_IMPORTED # pylint: disable=global-statement if _REPORTER_IMPORTED: return from simplebench.reporters.reporter.reporter import Reporter # pylint: disable=import-outside-toplevel _REPORTER_IMPORTED = True
if TYPE_CHECKING: from simplebench.reporters.reporter.reporter import Reporter
[docs] class Choice(Hashable, ChoiceProtocol): """Definition of a :class:`~.Choice` option for live use by reporters. A :class:`~.Choice` represents a specific configuration of a :class:`~simplebench.reporters.reporter.Reporter` subclass, including the sections to include in the report, the output targets, and the output formats. The :class:`~.Choice` class provides a structured way to define and manage different reporting options within the SimpleBench framework so that users can select from predefined configurations and developers can easily add new reporting options to the framework. A :class:`~.Choice` instance is immutable after creation to ensure consistency in reporting configurations. The sections, targets, and formats are descriptive only; they do not enforce any behavior on the associated :class:`~simplebench.reporters.reporter.Reporter` subclass. It is the responsibility of the :class:`~simplebench.reporters.reporter.Reporter` subclass to implement the behavior corresponding to the specified sections, targets, and formats. It is intended that multiple :class:`~.Choice` instances can be created for a single :class:`~simplebench.reporters.reporter.Reporter` subclass to represent different configurations of that reporter. For example, a JSON reporter might have one :class:`~.Choice` that includes all sections and outputs to the filesystem, and another :class:`~.Choice` that includes only the OPS section and outputs to the console. This allows users to select from a variety of predefined reporting configurations without needing to create multiple :class:`~simplebench.reporters.reporter.Reporter` subclasses. :class:`~.Choice` instances are created by the :class:`~simplebench.reporters.reporter.Reporter` subclass from :class:`~.ChoiceConf` instances during a :class:`~simplebench.reporters.reporter.Reporter`'s instantation process. Separating :class:`~.ChoiceConf` and :class:`~.Choice` allows for a clear distinction between the configuration data (:class:`~.ChoiceConf`) and the live, operational representation (:class:`~.Choice`) used by the :class:`~simplebench.reporters.reporter.Reporter` subclass. :param reporter: The :class:`~simplebench.reporters.reporter.Reporter` subclass instance associated with the choice. :type reporter: :class:`~simplebench.reporters.reporter.Reporter` :param choice_conf: The :class:`~.ChoiceConf` instance used to create the choice. :type choice_conf: :class:`~.ChoiceConf` :param flags: A set of command-line flags associated with the choice. :type flags: set[str] :param flag_type: The type of command-line flag (e.g., boolean, target_list, etc.). :type flag_type: :class:`~simplebench.enums.FlagType` :param name: A unique name for the choice. :type name: str :param description: A brief description of the choice. :type description: str :param sections: A set of :class:`~simplebench.enums.Section` enums to include in the report. :type sections: set[:class:`~simplebench.enums.Section`] :param targets: A set of :class:`~simplebench.enums.Target` enums for output. :type targets: set[:class:`~simplebench.enums.Target`] :param default_targets: A set of :class:`~simplebench.enums.Target` enums representing the default targets for the choice. If ``None``, no default targets are specified and the reporter's defaults will be used when generating reports. :type default_targets: set[:class:`~simplebench.enums.Target`] | None :param output_format: A :class:`~simplebench.enums.Format` instance describing the output format. :type output_format: :class:`~simplebench.enums.Format` :param subdir: An optional subdirectory for output files. If ``None``, reports default to the reporter's subdir. :type subdir: str | None :param file_suffix: An optional file suffix for output files. If ``None``, reports default to the reporter's file_suffix. :type file_suffix: str | None :param file_unique: Whether to make output file names unique. If ``None``, reports default to the reporter's file_unique. :type file_unique: bool | None :param file_append: Whether to append to existing output files. If ``None``, reports default to the reporter's file_append. :type file_append: bool | None :param options: An optional :class:`~simplebench.reporters.reporter.options.ReporterOptions` instance for additional configurations specific to a specific reporter. If ``None``, reports default to the reporter's default options. :type options: :class:`~simplebench.reporters.reporter.options.ReporterOptions` | None :param extra: Any additional metadata associated with the choice. :type extra: Any | None """ __slots__ = ( '_reporter', '_choice_conf' ) def __init__(self, *, reporter: Reporter, choice_conf: ChoiceConf) -> None: """Construct a :class:`~.Choice` instance from a :class:`~simplebench.reporters.reporter.Reporter` and a :class:`~.ChoiceConf` instance. :param reporter: An instance of a :class:`~simplebench.reporters.reporter.Reporter` subclass. :type reporter: :class:`~simplebench.reporters.reporter.Reporter` :param choice_conf: An instance of :class:`~.ChoiceConf` containing the configuration for the choice. :type choice_conf: :class:`~.ChoiceConf` :raises SimpleBenchTypeError: If any argument is of an incorrect type. :raises SimpleBenchValueError: If any argument has an invalid value (e.g., empty strings or empty sequences). """ deferred_reporter_import() # mypy raises a [type-abstract] error because Reporter is an Abstract Base Class # (ABC). We are using the Reporter ABC itself as a type argument in the # validate_type function, which mypy flags because ABCs cannot be instantiated. # We use type: ignore because we know at runtime we will only ever receive # concrete subclasses of Reporter, not the abstract Reporter itself. self._reporter: Reporter = validate_type( reporter, Reporter, "reporter", # type: ignore[type-abstract] error_tag=_ChoiceErrorTag.REPORTER_INVALID_ARG_TYPE) """The Reporter subclass instance associated with the choice (private backing field for attribute)""" self._choice_conf: ChoiceConf = validate_type( choice_conf, ChoiceConf, "choice_conf", error_tag=_ChoiceErrorTag.CHOICE_CONF_INVALID_ARG_TYPE) """The ChoiceConf instance used to create the choice (private backing field for attribute)""" @property def reporter(self) -> Reporter: """The :class:`~simplebench.reporters.reporter.Reporter` sub-class associated with the choice.""" return self._reporter @property def choice_conf(self) -> ChoiceConf: """The :class:`~.ChoiceConf` instance used to create the choice.""" return self._choice_conf # All the properties below simply proxy to the underlying ChoiceConf instance @property def flags(self) -> frozenset[str]: """Flags associated with the choice. These are used for command-line selection. They must be unique across all choices for all reporters. This is enforced by the :class:`~simplebench.reporters.reporter_manager.ReporterManager` when choices are registered. The flags should be in the format used on the command line, typically starting with ``--`` for long options. .. code-block:: python ['--json', '--json-full'] The :attr:`~.description` property of the :class:`~.Choice` is used to provide help text for the flags when generating command-line help. """ return self._choice_conf.flags @property def flag_type(self) -> FlagType: """The type of command-line flag (e.g., :attr:`~simplebench.enums.FlagType.BOOLEAN`, :attr:`~simplebench.enums.FlagType.TARGET_LIST`, etc.).""" return self._choice_conf.flag_type @property def name(self) -> str: """Name of the choice.""" return self._choice_conf.name @property def description(self) -> str: """Description of the choice. This is used as help text for the command-line flags associated with the choice.""" return self._choice_conf.description @property def sections(self) -> frozenset[Section]: """Sections included in the choice. These are the sections that the associated :class:`~simplebench.reporters.reporter.Reporter` subclass is expected to include in its report when this choice is selected.""" return self._choice_conf.sections @property def targets(self) -> frozenset[Target]: """Output targets for the choice. These are the output targets that the associated :class:`~simplebench.reporters.reporter.Reporter` subclass is expected to use when this choice is selected.""" return self._choice_conf.targets @property def default_targets(self) -> frozenset[Target] | None: """Default output targets for the choice. These are the default output targets that the associated :class:`~simplebench.reporters.reporter.Reporter` subclass should use when this choice is selected, if no specific target is provided by the user.""" return self._choice_conf.default_targets @property def subdir(self) -> str | None: """An optional subdirectory for output files. If specified, the associated :class:`~simplebench.reporters.reporter.Reporter` subclass should use this subdirectory for output files when this choice is selected. If an empty string, no subdirectory should be used. If ``None``, the reporter's default subdir should be used.""" return self._choice_conf.subdir @property def file_suffix(self) -> str | None: """An optional file suffix for output files. If specified, the associated :class:`~simplebench.reporters.reporter.Reporter` subclass should use this file suffix for output files when this choice is selected. If ``None``, the reporter's default file_suffix should be used.""" return self._choice_conf.file_suffix @property def file_unique(self) -> bool | None: """Whether to make output file names unique. If specified, the associated :class:`~simplebench.reporters.reporter.Reporter` subclass should use this setting for output files when this choice is selected. If ``None``, the reporter's default file_unique setting should be used.""" return self._choice_conf.file_unique @property def file_append(self) -> bool | None: """Whether to append to existing output files. If specified, the associated :class:`~simplebench.reporters.reporter.Reporter` subclass should use this setting for output files when this choice is selected. If ``None``, the reporter's default file_append setting should be used.""" return self._choice_conf.file_append @property def output_format(self) -> Format: """Output format for the choice. This is the output format that the associated :class:`~simplebench.reporters.reporter.Reporter` subclass is expected to use when this choice is selected. :return: The output format. :rtype: :class:`~simplebench.enums.Format` """ return self._choice_conf.output_format @property def options(self) -> ReporterOptions | None: """An optional :class:`~simplebench.reporters.reporter.options.ReporterOptions` instance for additional configuration.""" return self._choice_conf.options @property def extra(self) -> Any: """Any additional metadata associated with the choice.""" return self._choice_conf.extra def __hash__(self) -> int: """Compute a hash value for the :class:`~.ChoiceConf` instance. :return: The computed hash value. :rtype: int """ return hash(( self.flags, self.flag_type, self.name, self.description, self.sections, self.targets, self.default_targets, self.subdir, self.file_suffix, self.file_unique, self.file_append, self.output_format, self.options, self.extra, id(self.reporter) )) def __eq__(self, other: object) -> bool: """Check equality between two :class:`~.ChoiceConf` instances. :param other: The other :class:`~.ChoiceConf` instance to compare against. :type other: object :return: ``True`` if the :class:`~.ChoiceConf` instances are equal, ``False`` otherwise. :rtype: bool """ if not isinstance(other, Choice): return False return (self.flags == other.flags and self.flag_type == other.flag_type and self.name == other.name and self.description == other.description and self.sections == other.sections and self.targets == other.targets and self.default_targets == other.default_targets and self.subdir == other.subdir and self.file_suffix == other.file_suffix and self.file_unique == other.file_unique and self.file_append == other.file_append and self.output_format == other.output_format and self.options == other.options and self.extra == other.extra and id(self.reporter) == id(other.reporter))