examples.08_hashing_valid_vs_invalid

Demonstrates how valid and invalid ConstrainedValue instances behave when hashed or used in sets and equality comparisons.

This example shows:
  • Valid values hash based on their (class, value) pair.
  • Invalid values hash based on their (class, status) pair.
  • Equal valid instances collapse to one entry in a set.
  • Invalid instances remain distinct, even if their input data is identical.
Run directly:

python examples/08_hashing_valid_vs_invalid.py

Expected output:

equal valid hashes: True set size for valid duplicates (1): 1 invalid equal? (False): False both hashable: True set size for invalid values (2): 2

  1"""Demonstrates how valid and invalid `ConstrainedValue` instances behave when
  2hashed or used in sets and equality comparisons.
  3
  4This example shows:
  5  * Valid values hash based on their **(class, value)** pair.
  6  * Invalid values hash based on their **(class, status)** pair.
  7  * Equal valid instances collapse to one entry in a set.
  8  * Invalid instances remain distinct, even if their input data is identical.
  9
 10Run directly:
 11
 12    python examples/08_hashing_valid_vs_invalid.py
 13
 14Expected output:
 15
 16    equal valid hashes: True
 17    set size for valid duplicates (1): 1
 18    invalid equal? (False): False
 19    both hashable: True
 20    set size for invalid values (2): 2
 21"""
 22
 23import sys
 24import pathlib
 25from typing import Any
 26
 27# ---------------------------------------------------------------------------
 28# Make repo root importable when running this file directly
 29# ---------------------------------------------------------------------------
 30sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[1]))
 31
 32from constrained_values import Response, Status
 33from constrained_values.value import TransformationStrategy, ConstrainedValue
 34
 35
 36class Pass(TransformationStrategy[Any, Any]):
 37    """A transformation strategy that always succeeds.
 38
 39    Produces an `OK` response and returns the input value unchanged.
 40    """
 41
 42    def transform(self, value: Any) -> Response[Any]:
 43        """Return an OK response with the input value.
 44
 45        Args:
 46            value: The input to pass through unchanged.
 47
 48        Returns:
 49            Response[Any]: A successful response containing:
 50                * `status = Status.OK`
 51                * `details = "ok"`
 52                * `value` equal to the input
 53        """
 54        return Response(status=Status.OK, details="ok", value=value)
 55
 56
 57class Fail(TransformationStrategy[Any, Any]):
 58    """A transformation strategy that always fails.
 59
 60    Used to demonstrate how failed constrained values behave when hashed.
 61    """
 62
 63    def transform(self, value: Any) -> Response[Any]:
 64        """Return a failed response.
 65
 66        Args:
 67            value: Any input value (ignored).
 68
 69        Returns:
 70            Response[Any]: A failed response containing:
 71                * `status = Status.EXCEPTION`
 72                * `details = "boom"`
 73                * `value = None`
 74        """
 75        return Response(status=Status.EXCEPTION, details="boom", value=None)
 76
 77
 78class ValidInt(ConstrainedValue[int]):
 79    """A valid constrained integer that always succeeds.
 80
 81    Uses a single `Pass` strategy.
 82    """
 83
 84    def get_strategies(self):
 85        """Return a one-step pass-through pipeline."""
 86        return [Pass()]
 87
 88
 89class InvalidInt(ConstrainedValue[int]):
 90    """An invalid constrained integer that always fails.
 91
 92    Uses a single `Fail` strategy to simulate validation failure.
 93    """
 94
 95    def get_strategies(self):
 96        """Return a one-step failing pipeline."""
 97        return [Fail()]
 98
 99
100def main() -> None:
101    """Run the hashing comparison demonstration.
102
103    Steps:
104        1. Create two valid constrained values (`a`, `b`) with identical input.
105        2. Show that they have equal hashes and collapse in a set.
106        3. Create two invalid constrained values (`x`, `y`) and show that
107           they are not equal, but remain hashable and distinct in sets.
108
109    Prints:
110        * Whether the hashes of valid values are equal (`True`)
111        * The set size for valid duplicates (`1`)
112        * Equality result for invalid values (`False`)
113        * Confirmation that invalid values are still hashable (`True`)
114        * The set size for invalid values (`2`)
115    """
116    a, b = ValidInt(42), ValidInt(42)
117    print("equal valid hashes:", hash(a) == hash(b))
118    s = {a, b}
119    print("set size for valid duplicates (1):", len(s))
120
121    x, y = InvalidInt(1), InvalidInt(1)
122    print("invalid equal? (False):", x == y)
123    print("both hashable:", isinstance(hash(x), int) and isinstance(hash(y), int))
124    s2 = {x, y}
125    print("set size for invalid values (2):", len(s2))
126
127
128if __name__ == "__main__":
129    main()
class Pass(constrained_values.value.TransformationStrategy[typing.Any, typing.Any]):
37class Pass(TransformationStrategy[Any, Any]):
38    """A transformation strategy that always succeeds.
39
40    Produces an `OK` response and returns the input value unchanged.
41    """
42
43    def transform(self, value: Any) -> Response[Any]:
44        """Return an OK response with the input value.
45
46        Args:
47            value: The input to pass through unchanged.
48
49        Returns:
50            Response[Any]: A successful response containing:
51                * `status = Status.OK`
52                * `details = "ok"`
53                * `value` equal to the input
54        """
55        return Response(status=Status.OK, details="ok", value=value)

A transformation strategy that always succeeds.

Produces an OK response and returns the input value unchanged.

def transform(self, value: Any) -> constrained_values.Response[typing.Any]:
43    def transform(self, value: Any) -> Response[Any]:
44        """Return an OK response with the input value.
45
46        Args:
47            value: The input to pass through unchanged.
48
49        Returns:
50            Response[Any]: A successful response containing:
51                * `status = Status.OK`
52                * `details = "ok"`
53                * `value` equal to the input
54        """
55        return Response(status=Status.OK, details="ok", value=value)

Return an OK response with the input value.

Arguments:
  • value: The input to pass through unchanged.
Returns:

Response[Any]: A successful response containing: * status = Status.OK * details = "ok" * value equal to the input

class Fail(constrained_values.value.TransformationStrategy[typing.Any, typing.Any]):
58class Fail(TransformationStrategy[Any, Any]):
59    """A transformation strategy that always fails.
60
61    Used to demonstrate how failed constrained values behave when hashed.
62    """
63
64    def transform(self, value: Any) -> Response[Any]:
65        """Return a failed response.
66
67        Args:
68            value: Any input value (ignored).
69
70        Returns:
71            Response[Any]: A failed response containing:
72                * `status = Status.EXCEPTION`
73                * `details = "boom"`
74                * `value = None`
75        """
76        return Response(status=Status.EXCEPTION, details="boom", value=None)

A transformation strategy that always fails.

Used to demonstrate how failed constrained values behave when hashed.

def transform(self, value: Any) -> constrained_values.Response[typing.Any]:
64    def transform(self, value: Any) -> Response[Any]:
65        """Return a failed response.
66
67        Args:
68            value: Any input value (ignored).
69
70        Returns:
71            Response[Any]: A failed response containing:
72                * `status = Status.EXCEPTION`
73                * `details = "boom"`
74                * `value = None`
75        """
76        return Response(status=Status.EXCEPTION, details="boom", value=None)

Return a failed response.

Arguments:
  • value: Any input value (ignored).
Returns:

Response[Any]: A failed response containing: * status = Status.EXCEPTION * details = "boom" * value = None

class ValidInt(constrained_values.value.ConstrainedValue[int]):
79class ValidInt(ConstrainedValue[int]):
80    """A valid constrained integer that always succeeds.
81
82    Uses a single `Pass` strategy.
83    """
84
85    def get_strategies(self):
86        """Return a one-step pass-through pipeline."""
87        return [Pass()]

A valid constrained integer that always succeeds.

Uses a single Pass strategy.

def get_strategies(self):
85    def get_strategies(self):
86        """Return a one-step pass-through pipeline."""
87        return [Pass()]

Return a one-step pass-through pipeline.

Inherited Members
constrained_values.value.ConstrainedValue
ConstrainedValue
status
details
value
unwrap
ok
class InvalidInt(constrained_values.value.ConstrainedValue[int]):
90class InvalidInt(ConstrainedValue[int]):
91    """An invalid constrained integer that always fails.
92
93    Uses a single `Fail` strategy to simulate validation failure.
94    """
95
96    def get_strategies(self):
97        """Return a one-step failing pipeline."""
98        return [Fail()]

An invalid constrained integer that always fails.

Uses a single Fail strategy to simulate validation failure.

def get_strategies(self):
96    def get_strategies(self):
97        """Return a one-step failing pipeline."""
98        return [Fail()]

Return a one-step failing pipeline.

Inherited Members
constrained_values.value.ConstrainedValue
ConstrainedValue
status
details
value
unwrap
ok
def main() -> None:
101def main() -> None:
102    """Run the hashing comparison demonstration.
103
104    Steps:
105        1. Create two valid constrained values (`a`, `b`) with identical input.
106        2. Show that they have equal hashes and collapse in a set.
107        3. Create two invalid constrained values (`x`, `y`) and show that
108           they are not equal, but remain hashable and distinct in sets.
109
110    Prints:
111        * Whether the hashes of valid values are equal (`True`)
112        * The set size for valid duplicates (`1`)
113        * Equality result for invalid values (`False`)
114        * Confirmation that invalid values are still hashable (`True`)
115        * The set size for invalid values (`2`)
116    """
117    a, b = ValidInt(42), ValidInt(42)
118    print("equal valid hashes:", hash(a) == hash(b))
119    s = {a, b}
120    print("set size for valid duplicates (1):", len(s))
121
122    x, y = InvalidInt(1), InvalidInt(1)
123    print("invalid equal? (False):", x == y)
124    print("both hashable:", isinstance(hash(x), int) and isinstance(hash(y), int))
125    s2 = {x, y}
126    print("set size for invalid values (2):", len(s2))

Run the hashing comparison demonstration.

Steps:
  1. Create two valid constrained values (a, b) with identical input.
  2. Show that they have equal hashes and collapse in a set.
  3. Create two invalid constrained values (x, y) and show that they are not equal, but remain hashable and distinct in sets.
Prints:
  • Whether the hashes of valid values are equal (True)
  • The set size for valid duplicates (1)
  • Equality result for invalid values (False)
  • Confirmation that invalid values are still hashable (True)
  • The set size for invalid values (2)