Skip to content

Registry

patiencepilot.variants.registry

Variant registry for resolving Solitaire rules by name and options.

VariantFactory

Bases: Protocol

Callable that creates a variant rules object from option values.

Source code in src/patiencepilot/variants/registry.py
17
18
19
20
21
22
class VariantFactory(Protocol):
    """Callable that creates a variant rules object from option values."""

    def __call__(self, options: VariantOptions) -> Variant:
        """Return a variant rules object."""
        ...

__call__

__call__(options: VariantOptions) -> Variant

Return a variant rules object.

Source code in src/patiencepilot/variants/registry.py
20
21
22
def __call__(self, options: VariantOptions) -> Variant:
    """Return a variant rules object."""
    ...

StateOptionsResolver

Bases: Protocol

Callable that extracts variant options from a state.

Source code in src/patiencepilot/variants/registry.py
25
26
27
28
29
30
class StateOptionsResolver(Protocol):
    """Callable that extracts variant options from a state."""

    def __call__(self, state: GameState) -> dict[str, object]:
        """Return JSON-compatible variant options for ``state``."""
        ...

__call__

__call__(state: GameState) -> dict[str, object]

Return JSON-compatible variant options for state.

Source code in src/patiencepilot/variants/registry.py
28
29
30
def __call__(self, state: GameState) -> dict[str, object]:
    """Return JSON-compatible variant options for ``state``."""
    ...

VariantDefinition dataclass

Registered Solitaire variant plus its option conversion functions.

Source code in src/patiencepilot/variants/registry.py
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
@dataclass(frozen=True, slots=True)
class VariantDefinition:
    """Registered Solitaire variant plus its option conversion functions."""

    name: str
    factory: VariantFactory
    state_options: StateOptionsResolver
    aliases: tuple[str, ...] = ()

    def resolve(self, options: VariantOptions | None = None) -> Variant:
        """Return a rules object for this variant.

        Args:
            options: Variant-specific configuration values.
        """
        return self.factory({} if options is None else options)

resolve

resolve(options: VariantOptions | None = None) -> Variant

Return a rules object for this variant.

Parameters:

Name Type Description Default
options VariantOptions | None

Variant-specific configuration values.

None
Source code in src/patiencepilot/variants/registry.py
42
43
44
45
46
47
48
def resolve(self, options: VariantOptions | None = None) -> Variant:
    """Return a rules object for this variant.

    Args:
        options: Variant-specific configuration values.
    """
    return self.factory({} if options is None else options)

VariantRegistry dataclass

Small immutable registry of supported Solitaire variants.

Source code in src/patiencepilot/variants/registry.py
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
@dataclass(frozen=True, slots=True)
class VariantRegistry:
    """Small immutable registry of supported Solitaire variants."""

    definitions: tuple[VariantDefinition, ...]

    def __post_init__(self) -> None:
        """Validate registry keys."""
        keys: set[str] = set()
        for definition in self.definitions:
            for name in (definition.name, *definition.aliases):
                normalized = _normalize_variant_name(name)
                if normalized in keys:
                    msg = f"duplicate variant registry name: {name!r}"
                    raise InvalidStateError(msg)
                keys.add(normalized)

    @property
    def names(self) -> tuple[str, ...]:
        """Return canonical registered variant names."""
        return tuple(definition.name for definition in self.definitions)

    def register(self, definition: VariantDefinition) -> VariantRegistry:
        """Return a new registry that also contains ``definition``.

        Args:
            definition: Variant definition to add.
        """
        return VariantRegistry((*self.definitions, definition))

    def definition_for(self, name: str) -> VariantDefinition:
        """Return the registered definition for ``name`` or alias.

        Args:
            name: Variant name or alias.

        Raises:
            UnsupportedVariantError: If no variant is registered for ``name``.
        """
        normalized = _normalize_variant_name(name)
        for definition in self.definitions:
            if normalized == _normalize_variant_name(definition.name):
                return definition
            if any(normalized == _normalize_variant_name(alias) for alias in definition.aliases):
                return definition

        msg = f"unsupported variant: {name!r}"
        raise UnsupportedVariantError(msg)

    def resolve(self, name: str, options: VariantOptions | None = None) -> Variant:
        """Return a rules object by variant name and options.

        Args:
            name: Variant name or alias.
            options: Variant-specific configuration values.
        """
        return self.definition_for(name).resolve(options)

    def options_from_state(self, state: GameState) -> dict[str, object]:
        """Return registry options that reproduce ``state`` rules.

        Args:
            state: State whose variant metadata should be converted.
        """
        return self.definition_for(state.variant).state_options(state)

    def resolve_state(self, state: GameState) -> Variant:
        """Return rules for the variant metadata stored in ``state``.

        Args:
            state: State containing variant name and option metadata.
        """
        return self.resolve(state.variant, self.options_from_state(state))

names property

names: tuple[str, ...]

Return canonical registered variant names.

__post_init__

__post_init__() -> None

Validate registry keys.

Source code in src/patiencepilot/variants/registry.py
57
58
59
60
61
62
63
64
65
66
def __post_init__(self) -> None:
    """Validate registry keys."""
    keys: set[str] = set()
    for definition in self.definitions:
        for name in (definition.name, *definition.aliases):
            normalized = _normalize_variant_name(name)
            if normalized in keys:
                msg = f"duplicate variant registry name: {name!r}"
                raise InvalidStateError(msg)
            keys.add(normalized)

register

register(definition: VariantDefinition) -> VariantRegistry

Return a new registry that also contains definition.

Parameters:

Name Type Description Default
definition VariantDefinition

Variant definition to add.

required
Source code in src/patiencepilot/variants/registry.py
73
74
75
76
77
78
79
def register(self, definition: VariantDefinition) -> VariantRegistry:
    """Return a new registry that also contains ``definition``.

    Args:
        definition: Variant definition to add.
    """
    return VariantRegistry((*self.definitions, definition))

definition_for

definition_for(name: str) -> VariantDefinition

Return the registered definition for name or alias.

Parameters:

Name Type Description Default
name str

Variant name or alias.

required

Raises:

Type Description
UnsupportedVariantError

If no variant is registered for name.

Source code in src/patiencepilot/variants/registry.py
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
def definition_for(self, name: str) -> VariantDefinition:
    """Return the registered definition for ``name`` or alias.

    Args:
        name: Variant name or alias.

    Raises:
        UnsupportedVariantError: If no variant is registered for ``name``.
    """
    normalized = _normalize_variant_name(name)
    for definition in self.definitions:
        if normalized == _normalize_variant_name(definition.name):
            return definition
        if any(normalized == _normalize_variant_name(alias) for alias in definition.aliases):
            return definition

    msg = f"unsupported variant: {name!r}"
    raise UnsupportedVariantError(msg)

resolve

resolve(
    name: str, options: VariantOptions | None = None
) -> Variant

Return a rules object by variant name and options.

Parameters:

Name Type Description Default
name str

Variant name or alias.

required
options VariantOptions | None

Variant-specific configuration values.

None
Source code in src/patiencepilot/variants/registry.py
100
101
102
103
104
105
106
107
def resolve(self, name: str, options: VariantOptions | None = None) -> Variant:
    """Return a rules object by variant name and options.

    Args:
        name: Variant name or alias.
        options: Variant-specific configuration values.
    """
    return self.definition_for(name).resolve(options)

options_from_state

options_from_state(state: GameState) -> dict[str, object]

Return registry options that reproduce state rules.

Parameters:

Name Type Description Default
state GameState

State whose variant metadata should be converted.

required
Source code in src/patiencepilot/variants/registry.py
109
110
111
112
113
114
115
def options_from_state(self, state: GameState) -> dict[str, object]:
    """Return registry options that reproduce ``state`` rules.

    Args:
        state: State whose variant metadata should be converted.
    """
    return self.definition_for(state.variant).state_options(state)

resolve_state

resolve_state(state: GameState) -> Variant

Return rules for the variant metadata stored in state.

Parameters:

Name Type Description Default
state GameState

State containing variant name and option metadata.

required
Source code in src/patiencepilot/variants/registry.py
117
118
119
120
121
122
123
def resolve_state(self, state: GameState) -> Variant:
    """Return rules for the variant metadata stored in ``state``.

    Args:
        state: State containing variant name and option metadata.
    """
    return self.resolve(state.variant, self.options_from_state(state))

resolve_variant

resolve_variant(
    name: str = KlondikeRules.name,
    options: VariantOptions | None = None,
    *,
    registry: VariantRegistry | None = None,
) -> Variant

Return rules for a registered variant name and options.

Parameters:

Name Type Description Default
name str

Variant name or alias. Defaults to "klondike".

name
options VariantOptions | None

Variant-specific configuration values.

None
registry VariantRegistry | None

Registry to use. Defaults to the package registry.

None
Source code in src/patiencepilot/variants/registry.py
126
127
128
129
130
131
132
133
134
135
136
137
138
139
def resolve_variant(
    name: str = KlondikeRules.name,
    options: VariantOptions | None = None,
    *,
    registry: VariantRegistry | None = None,
) -> Variant:
    """Return rules for a registered variant name and options.

    Args:
        name: Variant name or alias. Defaults to ``"klondike"``.
        options: Variant-specific configuration values.
        registry: Registry to use. Defaults to the package registry.
    """
    return _registry_or_default(registry).resolve(name, options)

resolve_state_variant

resolve_state_variant(
    state: GameState,
    *,
    registry: VariantRegistry | None = None,
) -> Variant

Return rules for the variant metadata stored in state.

Parameters:

Name Type Description Default
state GameState

State containing variant name and option metadata.

required
registry VariantRegistry | None

Registry to use. Defaults to the package registry.

None
Source code in src/patiencepilot/variants/registry.py
142
143
144
145
146
147
148
149
def resolve_state_variant(state: GameState, *, registry: VariantRegistry | None = None) -> Variant:
    """Return rules for the variant metadata stored in ``state``.

    Args:
        state: State containing variant name and option metadata.
        registry: Registry to use. Defaults to the package registry.
    """
    return _registry_or_default(registry).resolve_state(state)

variant_options_from_state

variant_options_from_state(
    state: GameState,
    *,
    registry: VariantRegistry | None = None,
) -> dict[str, object]

Return registry options that reproduce state rules.

Parameters:

Name Type Description Default
state GameState

State containing variant name and option metadata.

required
registry VariantRegistry | None

Registry to use. Defaults to the package registry.

None
Source code in src/patiencepilot/variants/registry.py
152
153
154
155
156
157
158
159
def variant_options_from_state(state: GameState, *, registry: VariantRegistry | None = None) -> dict[str, object]:
    """Return registry options that reproduce ``state`` rules.

    Args:
        state: State containing variant name and option metadata.
        registry: Registry to use. Defaults to the package registry.
    """
    return _registry_or_default(registry).options_from_state(state)

variant_names

variant_names(
    *, registry: VariantRegistry | None = None
) -> tuple[str, ...]

Return canonical names of registered variants.

Source code in src/patiencepilot/variants/registry.py
162
163
164
def variant_names(*, registry: VariantRegistry | None = None) -> tuple[str, ...]:
    """Return canonical names of registered variants."""
    return _registry_or_default(registry).names