|
| 1 | +--- |
| 2 | +file: behavioral/sentinel.py |
| 3 | +chunk: behavioral_sentinel.md |
| 4 | +--- |
| 5 | + |
| 6 | +```python |
| 7 | +""" |
| 8 | +Sentinel Object Pattern Example |
| 9 | +
|
| 10 | +The Sentinel Object Pattern uses unique, distinguishable objects (sentinels) to signal |
| 11 | +special conditions—like missing arguments or end-of-sequence markers—in a program. This |
| 12 | +allows you to differentiate between user-provided values (including None) and the absence |
| 13 | +of a value or a special control signal. |
| 14 | +
|
| 15 | +Pattern Type: Utility / Behavioral Micro‑pattern |
| 16 | +""" |
| 17 | + |
| 18 | +class Sentinel: |
| 19 | + """ |
| 20 | + A simple sentinel class. Each instance is unique and identifiable by name. |
| 21 | + """ |
| 22 | + __slots__ = ("name",) |
| 23 | + |
| 24 | + def __init__(self, name: str): |
| 25 | + self.name = name |
| 26 | + |
| 27 | + def __repr__(self) -> str: |
| 28 | + return f"<Sentinel {self.name}>" |
| 29 | + |
| 30 | +# Define sentinel instances for common use-cases |
| 31 | +MISSING = Sentinel("MISSING") |
| 32 | +END_OF_STREAM = Sentinel("END_OF_STREAM") |
| 33 | + |
| 34 | + |
| 35 | +def process_items(iterator, max_items=MISSING): |
| 36 | + """ |
| 37 | + Processes items from an iterator up to max_items. |
| 38 | + - If max_items is MISSING, processes all items. |
| 39 | + - If max_items is None, treats it as zero and does nothing. |
| 40 | +
|
| 41 | + This demonstrates using a sentinel to differentiate "no argument provided" from |
| 42 | + "argument explicitly set to None". |
| 43 | + """ |
| 44 | + if max_items is MISSING: |
| 45 | + print("Processing all items.") |
| 46 | + elif max_items is None: |
| 47 | + print("No items to process (max_items=None).") |
| 48 | + return |
| 49 | + else: |
| 50 | + print(f"Processing up to {max_items} items.") |
| 51 | + |
| 52 | + count = 0 |
| 53 | + for item in iterator: |
| 54 | + if max_items is not MISSING and max_items is not None and count >= max_items: |
| 55 | + break |
| 56 | + print(f"Item {count}: {item}") |
| 57 | + count += 1 |
| 58 | + |
| 59 | + |
| 60 | +def stream_data(data): |
| 61 | + """ |
| 62 | + Simulate streaming data: yields each item, then an END_OF_STREAM sentinel. |
| 63 | + """ |
| 64 | + for item in data: |
| 65 | + yield item |
| 66 | + yield END_OF_STREAM |
| 67 | + |
| 68 | + |
| 69 | +def consume_stream(stream): |
| 70 | + """ |
| 71 | + Consumes a stream until the END_OF_STREAM sentinel is encountered. |
| 72 | + """ |
| 73 | + print("Starting stream consumption...") |
| 74 | + for value in stream: |
| 75 | + if value is END_OF_STREAM: |
| 76 | + print("Received END_OF_STREAM sentinel. Stopping.") |
| 77 | + break |
| 78 | + print(f"Consumed: {value}") |
| 79 | + |
| 80 | + |
| 81 | +def main(): |
| 82 | + """The main function to demonstrate the the sentinel pattern.""" |
| 83 | + items = list(range(3)) |
| 84 | + |
| 85 | + print("--- Example 1: process_items (all) ---") |
| 86 | + process_items(items) |
| 87 | + |
| 88 | + print("\n--- Example 2: process_items (limit=2) ---") |
| 89 | + process_items(items, max_items=2) |
| 90 | + |
| 91 | + print("\n--- Example 3: process_items (max_items=None) ---") |
| 92 | + process_items(items, max_items=None) |
| 93 | + |
| 94 | + print("\n--- Example 4: stream consumption with sentinel ---") |
| 95 | + data_stream = stream_data(["a", "b", "c"]) |
| 96 | + consume_stream(data_stream) |
| 97 | + |
| 98 | + |
| 99 | +if __name__ == "__main__": |
| 100 | + main() |
| 101 | + |
| 102 | +``` |
| 103 | + |
| 104 | +## Summary |
| 105 | +Demonstration of the Sentinel Object Pattern in Python. |
| 106 | + |
| 107 | +## Docstrings |
| 108 | +- A simple sentinel class. Each instance is unique and identifiable by name. |
| 109 | +- Processes items from an iterator up to max_items. - If max_items is MISSING, processes all items. - If max_items is None, treats it as zero and does nothing. This demonstrates using a sentinel to differentiate "no argument provided" from "argument explicitly set to None". |
| 110 | +- Simulate streaming data: yields each item, then an END_OF_STREAM sentinel. |
| 111 | +- Consumes a stream until the END_OF_STREAM sentinel is encountered. |
| 112 | + |
0 commit comments