lionagi 0.18.0__py3-none-any.whl → 0.18.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lionagi/__init__.py +102 -59
 - lionagi/_errors.py +0 -5
 - lionagi/adapters/spec_adapters/__init__.py +9 -0
 - lionagi/adapters/spec_adapters/_protocol.py +236 -0
 - lionagi/adapters/spec_adapters/pydantic_field.py +158 -0
 - lionagi/fields.py +83 -0
 - lionagi/ln/__init__.py +3 -1
 - lionagi/ln/_async_call.py +2 -2
 - lionagi/ln/concurrency/primitives.py +4 -4
 - lionagi/ln/concurrency/task.py +1 -0
 - lionagi/ln/fuzzy/_fuzzy_match.py +2 -2
 - lionagi/ln/types/__init__.py +51 -0
 - lionagi/ln/types/_sentinel.py +154 -0
 - lionagi/ln/{types.py → types/base.py} +108 -168
 - lionagi/ln/types/operable.py +221 -0
 - lionagi/ln/types/spec.py +441 -0
 - lionagi/models/field_model.py +69 -7
 - lionagi/models/hashable_model.py +2 -3
 - lionagi/models/model_params.py +4 -3
 - lionagi/operations/ReAct/ReAct.py +1 -1
 - lionagi/operations/act/act.py +3 -3
 - lionagi/operations/builder.py +5 -7
 - lionagi/operations/fields.py +380 -0
 - lionagi/operations/flow.py +4 -6
 - lionagi/operations/node.py +4 -4
 - lionagi/operations/operate/operate.py +123 -89
 - lionagi/operations/operate/operative.py +198 -0
 - lionagi/operations/operate/step.py +203 -0
 - lionagi/operations/select/select.py +1 -1
 - lionagi/operations/select/utils.py +7 -1
 - lionagi/operations/types.py +7 -7
 - lionagi/protocols/action/manager.py +5 -6
 - lionagi/protocols/contracts.py +2 -2
 - lionagi/protocols/generic/__init__.py +22 -0
 - lionagi/protocols/generic/element.py +36 -127
 - lionagi/protocols/generic/pile.py +9 -10
 - lionagi/protocols/generic/progression.py +23 -22
 - lionagi/protocols/graph/edge.py +6 -5
 - lionagi/protocols/ids.py +6 -49
 - lionagi/protocols/messages/__init__.py +3 -1
 - lionagi/protocols/messages/base.py +7 -6
 - lionagi/protocols/messages/instruction.py +0 -1
 - lionagi/protocols/messages/message.py +2 -2
 - lionagi/protocols/types.py +1 -11
 - lionagi/service/connections/__init__.py +3 -0
 - lionagi/service/connections/providers/claude_code_cli.py +3 -2
 - lionagi/service/hooks/_types.py +1 -1
 - lionagi/service/hooks/_utils.py +1 -1
 - lionagi/service/hooks/hook_event.py +3 -8
 - lionagi/service/hooks/hook_registry.py +5 -5
 - lionagi/service/hooks/hooked_event.py +61 -1
 - lionagi/service/imodel.py +24 -20
 - lionagi/service/third_party/claude_code.py +1 -2
 - lionagi/service/third_party/openai_models.py +24 -22
 - lionagi/service/token_calculator.py +1 -94
 - lionagi/session/branch.py +26 -228
 - lionagi/session/session.py +5 -90
 - lionagi/version.py +1 -1
 - {lionagi-0.18.0.dist-info → lionagi-0.18.2.dist-info}/METADATA +6 -5
 - {lionagi-0.18.0.dist-info → lionagi-0.18.2.dist-info}/RECORD +62 -82
 - lionagi/fields/__init__.py +0 -47
 - lionagi/fields/action.py +0 -188
 - lionagi/fields/base.py +0 -153
 - lionagi/fields/code.py +0 -239
 - lionagi/fields/file.py +0 -234
 - lionagi/fields/instruct.py +0 -135
 - lionagi/fields/reason.py +0 -55
 - lionagi/fields/research.py +0 -52
 - lionagi/operations/brainstorm/__init__.py +0 -2
 - lionagi/operations/brainstorm/brainstorm.py +0 -498
 - lionagi/operations/brainstorm/prompt.py +0 -11
 - lionagi/operations/instruct/__init__.py +0 -2
 - lionagi/operations/instruct/instruct.py +0 -28
 - lionagi/operations/plan/__init__.py +0 -6
 - lionagi/operations/plan/plan.py +0 -386
 - lionagi/operations/plan/prompt.py +0 -25
 - lionagi/operations/utils.py +0 -45
 - lionagi/protocols/forms/__init__.py +0 -2
 - lionagi/protocols/forms/base.py +0 -85
 - lionagi/protocols/forms/flow.py +0 -79
 - lionagi/protocols/forms/form.py +0 -86
 - lionagi/protocols/forms/report.py +0 -48
 - lionagi/protocols/mail/__init__.py +0 -2
 - lionagi/protocols/mail/exchange.py +0 -220
 - lionagi/protocols/mail/mail.py +0 -51
 - lionagi/protocols/mail/mailbox.py +0 -103
 - lionagi/protocols/mail/manager.py +0 -218
 - lionagi/protocols/mail/package.py +0 -101
 - lionagi/protocols/operatives/__init__.py +0 -2
 - lionagi/protocols/operatives/operative.py +0 -362
 - lionagi/protocols/operatives/step.py +0 -227
 - {lionagi-0.18.0.dist-info → lionagi-0.18.2.dist-info}/WHEEL +0 -0
 - {lionagi-0.18.0.dist-info → lionagi-0.18.2.dist-info}/licenses/LICENSE +0 -0
 
    
        lionagi/fields.py
    ADDED
    
    | 
         @@ -0,0 +1,83 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            from typing import TYPE_CHECKING
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            if TYPE_CHECKING:
         
     | 
| 
      
 4 
     | 
    
         
            +
                from .operations.fields import (
         
     | 
| 
      
 5 
     | 
    
         
            +
                    ActionRequestModel,
         
     | 
| 
      
 6 
     | 
    
         
            +
                    ActionResponseModel,
         
     | 
| 
      
 7 
     | 
    
         
            +
                    Instruct,
         
     | 
| 
      
 8 
     | 
    
         
            +
                    Reason,
         
     | 
| 
      
 9 
     | 
    
         
            +
                    get_default_field,
         
     | 
| 
      
 10 
     | 
    
         
            +
                )
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            _lazy_imports = {}
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            def __getattr__(name: str):
         
     | 
| 
      
 16 
     | 
    
         
            +
                if name in _lazy_imports:
         
     | 
| 
      
 17 
     | 
    
         
            +
                    return _lazy_imports[name]
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                if name == "ActionRequestModel":
         
     | 
| 
      
 20 
     | 
    
         
            +
                    from .operations.fields import ActionRequestModel
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                    _lazy_imports[name] = ActionRequestModel
         
     | 
| 
      
 23 
     | 
    
         
            +
                    return ActionRequestModel
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                if name == "ActionResponseModel":
         
     | 
| 
      
 26 
     | 
    
         
            +
                    from .operations.fields import ActionResponseModel
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                    _lazy_imports[name] = ActionResponseModel
         
     | 
| 
      
 29 
     | 
    
         
            +
                    return ActionResponseModel
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                if name == "Instruct":
         
     | 
| 
      
 32 
     | 
    
         
            +
                    from .operations.fields import Instruct
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                    _lazy_imports[name] = Instruct
         
     | 
| 
      
 35 
     | 
    
         
            +
                    return Instruct
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                if name == "Reason":
         
     | 
| 
      
 38 
     | 
    
         
            +
                    from .operations.fields import Reason
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                    _lazy_imports[name] = Reason
         
     | 
| 
      
 41 
     | 
    
         
            +
                    return Reason
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                from .operations.fields import get_default_field
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                if name == "get_default_field":
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                    _lazy_imports[name] = get_default_field
         
     | 
| 
      
 48 
     | 
    
         
            +
                    return get_default_field
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                if name == "ACTION_REQUESTS_FIELD":
         
     | 
| 
      
 51 
     | 
    
         
            +
                    return get_default_field("action_requests")
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                if name == "ACTION_RESPONSES_FIELD":
         
     | 
| 
      
 54 
     | 
    
         
            +
                    return get_default_field("action_responses")
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                if name == "ACTION_REQUIRED_FIELD":
         
     | 
| 
      
 57 
     | 
    
         
            +
                    return get_default_field("action_required")
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                if name == "INSTRUCT_FIELD":
         
     | 
| 
      
 60 
     | 
    
         
            +
                    return get_default_field("instruct")
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                if name == "LIST_INSTRUCT_FIELD_MODEL":
         
     | 
| 
      
 63 
     | 
    
         
            +
                    return get_default_field("instruct", listable=True)
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                if name == "REASON_FIELD":
         
     | 
| 
      
 66 
     | 
    
         
            +
                    return get_default_field("reason")
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                raise AttributeError(f"module {__name__} has no attribute {name}")
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
            __all__ = (
         
     | 
| 
      
 72 
     | 
    
         
            +
                "ACTION_REQUESTS_FIELD",
         
     | 
| 
      
 73 
     | 
    
         
            +
                "ACTION_RESPONSES_FIELD",
         
     | 
| 
      
 74 
     | 
    
         
            +
                "ACTION_REQUIRED_FIELD",
         
     | 
| 
      
 75 
     | 
    
         
            +
                "INSTRUCT_FIELD",
         
     | 
| 
      
 76 
     | 
    
         
            +
                "LIST_INSTRUCT_FIELD_MODEL",
         
     | 
| 
      
 77 
     | 
    
         
            +
                "REASON_FIELD",
         
     | 
| 
      
 78 
     | 
    
         
            +
                "ActionRequestModel",
         
     | 
| 
      
 79 
     | 
    
         
            +
                "ActionResponseModel",
         
     | 
| 
      
 80 
     | 
    
         
            +
                "Instruct",
         
     | 
| 
      
 81 
     | 
    
         
            +
                "Reason",
         
     | 
| 
      
 82 
     | 
    
         
            +
                "get_default_field",
         
     | 
| 
      
 83 
     | 
    
         
            +
            )
         
     | 
    
        lionagi/ln/__init__.py
    CHANGED
    
    | 
         @@ -1,4 +1,4 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            from ._async_call import alcall, bcall
         
     | 
| 
      
 1 
     | 
    
         
            +
            from ._async_call import AlcallParams, BcallParams, alcall, bcall
         
     | 
| 
       2 
2 
     | 
    
         
             
            from ._hash import hash_dict
         
     | 
| 
       3 
3 
     | 
    
         
             
            from ._json_dump import (
         
     | 
| 
       4 
4 
     | 
    
         
             
                get_orjson_default,
         
     | 
| 
         @@ -80,4 +80,6 @@ __all__ = ( 
     | 
|
| 
       80 
80 
     | 
    
         
             
                "not_sentinel",
         
     | 
| 
       81 
81 
     | 
    
         
             
                "to_dict",
         
     | 
| 
       82 
82 
     | 
    
         
             
                "fuzzy_validate_mapping",
         
     | 
| 
      
 83 
     | 
    
         
            +
                "AlcallParams",
         
     | 
| 
      
 84 
     | 
    
         
            +
                "BcallParams",
         
     | 
| 
       83 
85 
     | 
    
         
             
            )
         
     | 
    
        lionagi/ln/_async_call.py
    CHANGED
    
    | 
         @@ -15,7 +15,7 @@ from .concurrency import ( 
     | 
|
| 
       15 
15 
     | 
    
         
             
                is_coro_func,
         
     | 
| 
       16 
16 
     | 
    
         
             
                move_on_after,
         
     | 
| 
       17 
17 
     | 
    
         
             
            )
         
     | 
| 
       18 
     | 
    
         
            -
            from .types import Params, T, Unset, not_sentinel
         
     | 
| 
      
 18 
     | 
    
         
            +
            from .types import ModelConfig, Params, T, Unset, not_sentinel
         
     | 
| 
       19 
19 
     | 
    
         | 
| 
       20 
20 
     | 
    
         
             
            _INITIALIZED = False
         
     | 
| 
       21 
21 
     | 
    
         
             
            _MODEL_LIKE = None
         
     | 
| 
         @@ -262,7 +262,7 @@ async def bcall( 
     | 
|
| 
       262 
262 
     | 
    
         
             
            @dataclass(slots=True, init=False, frozen=True)
         
     | 
| 
       263 
263 
     | 
    
         
             
            class AlcallParams(Params):
         
     | 
| 
       264 
264 
     | 
    
         
             
                # ClassVar attributes
         
     | 
| 
       265 
     | 
    
         
            -
                 
     | 
| 
      
 265 
     | 
    
         
            +
                _config: ClassVar[ModelConfig] = ModelConfig(none_as_sentinel=True)
         
     | 
| 
       266 
266 
     | 
    
         
             
                _func: ClassVar[Any] = alcall
         
     | 
| 
       267 
267 
     | 
    
         | 
| 
       268 
268 
     | 
    
         
             
                # input processing
         
     | 
| 
         @@ -142,15 +142,15 @@ class CapacityLimiter: 
     | 
|
| 
       142 
142 
     | 
    
         
             
                    """Get the number of currently available tokens."""
         
     | 
| 
       143 
143 
     | 
    
         
             
                    return self._lim.available_tokens
         
     | 
| 
       144 
144 
     | 
    
         | 
| 
       145 
     | 
    
         
            -
                def acquire_on_behalf_of(self, borrower: object) -> None:
         
     | 
| 
       146 
     | 
    
         
            -
                    """ 
     | 
| 
      
 145 
     | 
    
         
            +
                async def acquire_on_behalf_of(self, borrower: object) -> None:
         
     | 
| 
      
 146 
     | 
    
         
            +
                    """Asynchronously acquire capacity on behalf of another object.
         
     | 
| 
       147 
147 
     | 
    
         | 
| 
       148 
148 
     | 
    
         
             
                    For resource pooling where the acquirer differs from the releaser.
         
     | 
| 
       149 
149 
     | 
    
         | 
| 
       150 
150 
     | 
    
         
             
                    Args:
         
     | 
| 
       151 
151 
     | 
    
         
             
                        borrower: Object that will be responsible for releasing.
         
     | 
| 
       152 
152 
     | 
    
         
             
                    """
         
     | 
| 
       153 
     | 
    
         
            -
                    self._lim.acquire_on_behalf_of(borrower)
         
     | 
| 
      
 153 
     | 
    
         
            +
                    await self._lim.acquire_on_behalf_of(borrower)
         
     | 
| 
       154 
154 
     | 
    
         | 
| 
       155 
155 
     | 
    
         
             
                def release_on_behalf_of(self, borrower: object) -> None:
         
     | 
| 
       156 
156 
     | 
    
         
             
                    """Release capacity that was acquired on behalf of an object.
         
     | 
| 
         @@ -326,6 +326,6 @@ class Condition: 
     | 
|
| 
       326 
326 
     | 
    
         
             
                    """Wake up all tasks waiting on this condition."""
         
     | 
| 
       327 
327 
     | 
    
         
             
                    self._condition.notify_all()
         
     | 
| 
       328 
328 
     | 
    
         | 
| 
       329 
     | 
    
         
            -
                def statistics(self) -> anyio. 
     | 
| 
      
 329 
     | 
    
         
            +
                def statistics(self) -> anyio.ConditionStatistics:
         
     | 
| 
       330 
330 
     | 
    
         
             
                    """Return statistics about waiting tasks."""
         
     | 
| 
       331 
331 
     | 
    
         
             
                    return self._condition.statistics()
         
     | 
    
        lionagi/ln/concurrency/task.py
    CHANGED
    
    
    
        lionagi/ln/fuzzy/_fuzzy_match.py
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            from dataclasses import dataclass
         
     | 
| 
       2 
2 
     | 
    
         
             
            from typing import Any, ClassVar, Literal
         
     | 
| 
       3 
3 
     | 
    
         | 
| 
       4 
     | 
    
         
            -
            from ..types import KeysLike, Params, Unset
         
     | 
| 
      
 4 
     | 
    
         
            +
            from ..types import KeysLike, ModelConfig, Params, Unset
         
     | 
| 
       5 
5 
     | 
    
         
             
            from ._string_similarity import (
         
     | 
| 
       6 
6 
     | 
    
         
             
                SIMILARITY_ALGO_MAP,
         
     | 
| 
       7 
7 
     | 
    
         
             
                SIMILARITY_TYPE,
         
     | 
| 
         @@ -152,7 +152,7 @@ def fuzzy_match_keys( 
     | 
|
| 
       152 
152 
     | 
    
         | 
| 
       153 
153 
     | 
    
         
             
            @dataclass(slots=True, init=False, frozen=True)
         
     | 
| 
       154 
154 
     | 
    
         
             
            class FuzzyMatchKeysParams(Params):
         
     | 
| 
       155 
     | 
    
         
            -
                 
     | 
| 
      
 155 
     | 
    
         
            +
                _config: ClassVar[ModelConfig] = ModelConfig(none_as_sentinel=False)
         
     | 
| 
       156 
156 
     | 
    
         
             
                _func: ClassVar[Any] = fuzzy_match_keys
         
     | 
| 
       157 
157 
     | 
    
         | 
| 
       158 
158 
     | 
    
         
             
                similarity_algo: SIMILARITY_TYPE | SimilarityFunc = "jaro_winkler"
         
     | 
| 
         @@ -0,0 +1,51 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            from ._sentinel import (
         
     | 
| 
      
 2 
     | 
    
         
            +
                MaybeSentinel,
         
     | 
| 
      
 3 
     | 
    
         
            +
                MaybeUndefined,
         
     | 
| 
      
 4 
     | 
    
         
            +
                MaybeUnset,
         
     | 
| 
      
 5 
     | 
    
         
            +
                SingletonType,
         
     | 
| 
      
 6 
     | 
    
         
            +
                T,
         
     | 
| 
      
 7 
     | 
    
         
            +
                Undefined,
         
     | 
| 
      
 8 
     | 
    
         
            +
                UndefinedType,
         
     | 
| 
      
 9 
     | 
    
         
            +
                Unset,
         
     | 
| 
      
 10 
     | 
    
         
            +
                UnsetType,
         
     | 
| 
      
 11 
     | 
    
         
            +
                is_sentinel,
         
     | 
| 
      
 12 
     | 
    
         
            +
                not_sentinel,
         
     | 
| 
      
 13 
     | 
    
         
            +
            )
         
     | 
| 
      
 14 
     | 
    
         
            +
            from .base import (
         
     | 
| 
      
 15 
     | 
    
         
            +
                DataClass,
         
     | 
| 
      
 16 
     | 
    
         
            +
                Enum,
         
     | 
| 
      
 17 
     | 
    
         
            +
                KeysDict,
         
     | 
| 
      
 18 
     | 
    
         
            +
                KeysLike,
         
     | 
| 
      
 19 
     | 
    
         
            +
                Meta,
         
     | 
| 
      
 20 
     | 
    
         
            +
                ModelConfig,
         
     | 
| 
      
 21 
     | 
    
         
            +
                Params,
         
     | 
| 
      
 22 
     | 
    
         
            +
            )
         
     | 
| 
      
 23 
     | 
    
         
            +
            from .operable import Operable
         
     | 
| 
      
 24 
     | 
    
         
            +
            from .spec import CommonMeta, Spec
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
            __all__ = (
         
     | 
| 
      
 27 
     | 
    
         
            +
                # Sentinel types
         
     | 
| 
      
 28 
     | 
    
         
            +
                "Undefined",
         
     | 
| 
      
 29 
     | 
    
         
            +
                "Unset",
         
     | 
| 
      
 30 
     | 
    
         
            +
                "MaybeUndefined",
         
     | 
| 
      
 31 
     | 
    
         
            +
                "MaybeUnset",
         
     | 
| 
      
 32 
     | 
    
         
            +
                "MaybeSentinel",
         
     | 
| 
      
 33 
     | 
    
         
            +
                "SingletonType",
         
     | 
| 
      
 34 
     | 
    
         
            +
                "UndefinedType",
         
     | 
| 
      
 35 
     | 
    
         
            +
                "UnsetType",
         
     | 
| 
      
 36 
     | 
    
         
            +
                "is_sentinel",
         
     | 
| 
      
 37 
     | 
    
         
            +
                "not_sentinel",
         
     | 
| 
      
 38 
     | 
    
         
            +
                # Base classes
         
     | 
| 
      
 39 
     | 
    
         
            +
                "ModelConfig",
         
     | 
| 
      
 40 
     | 
    
         
            +
                "Enum",
         
     | 
| 
      
 41 
     | 
    
         
            +
                "Params",
         
     | 
| 
      
 42 
     | 
    
         
            +
                "DataClass",
         
     | 
| 
      
 43 
     | 
    
         
            +
                "Meta",
         
     | 
| 
      
 44 
     | 
    
         
            +
                "KeysDict",
         
     | 
| 
      
 45 
     | 
    
         
            +
                "KeysLike",
         
     | 
| 
      
 46 
     | 
    
         
            +
                "T",
         
     | 
| 
      
 47 
     | 
    
         
            +
                # Spec system
         
     | 
| 
      
 48 
     | 
    
         
            +
                "Spec",
         
     | 
| 
      
 49 
     | 
    
         
            +
                "CommonMeta",
         
     | 
| 
      
 50 
     | 
    
         
            +
                "Operable",
         
     | 
| 
      
 51 
     | 
    
         
            +
            )
         
     | 
| 
         @@ -0,0 +1,154 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            from __future__ import annotations
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            from typing import Any, Final, Literal, TypeVar, Union
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            __all__ = (
         
     | 
| 
      
 6 
     | 
    
         
            +
                "Undefined",
         
     | 
| 
      
 7 
     | 
    
         
            +
                "Unset",
         
     | 
| 
      
 8 
     | 
    
         
            +
                "MaybeUndefined",
         
     | 
| 
      
 9 
     | 
    
         
            +
                "MaybeUnset",
         
     | 
| 
      
 10 
     | 
    
         
            +
                "MaybeSentinel",
         
     | 
| 
      
 11 
     | 
    
         
            +
                "SingletonType",
         
     | 
| 
      
 12 
     | 
    
         
            +
                "UndefinedType",
         
     | 
| 
      
 13 
     | 
    
         
            +
                "UnsetType",
         
     | 
| 
      
 14 
     | 
    
         
            +
                "is_sentinel",
         
     | 
| 
      
 15 
     | 
    
         
            +
                "not_sentinel",
         
     | 
| 
      
 16 
     | 
    
         
            +
                "T",
         
     | 
| 
      
 17 
     | 
    
         
            +
            )
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
            T = TypeVar("T")
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
            class _SingletonMeta(type):
         
     | 
| 
      
 23 
     | 
    
         
            +
                """Metaclass that guarantees exactly one instance per subclass.
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                This ensures that sentinel values maintain identity across the entire application,
         
     | 
| 
      
 26 
     | 
    
         
            +
                allowing safe identity checks with 'is' operator.
         
     | 
| 
      
 27 
     | 
    
         
            +
                """
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                _cache: dict[type, SingletonType] = {}
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                def __call__(cls, *a, **kw):
         
     | 
| 
      
 32 
     | 
    
         
            +
                    if cls not in cls._cache:
         
     | 
| 
      
 33 
     | 
    
         
            +
                        cls._cache[cls] = super().__call__(*a, **kw)
         
     | 
| 
      
 34 
     | 
    
         
            +
                    return cls._cache[cls]
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
            class SingletonType(metaclass=_SingletonMeta):
         
     | 
| 
      
 38 
     | 
    
         
            +
                """Base class for singleton sentinel types.
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                Provides consistent interface for sentinel values with:
         
     | 
| 
      
 41 
     | 
    
         
            +
                - Identity preservation across deepcopy
         
     | 
| 
      
 42 
     | 
    
         
            +
                - Falsy boolean evaluation
         
     | 
| 
      
 43 
     | 
    
         
            +
                - Clear string representation
         
     | 
| 
      
 44 
     | 
    
         
            +
                """
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                __slots__: tuple[str, ...] = ()
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                def __deepcopy__(self, memo):  # copy & deepcopy both noop
         
     | 
| 
      
 49 
     | 
    
         
            +
                    return self
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                def __copy__(self):
         
     | 
| 
      
 52 
     | 
    
         
            +
                    return self
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                # concrete classes *must* override the two methods below
         
     | 
| 
      
 55 
     | 
    
         
            +
                def __bool__(self) -> bool: ...
         
     | 
| 
      
 56 
     | 
    
         
            +
                def __repr__(self) -> str: ...
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
            class UndefinedType(SingletonType):
         
     | 
| 
      
 60 
     | 
    
         
            +
                """Sentinel for a key or field entirely missing from a namespace.
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                Use this when:
         
     | 
| 
      
 63 
     | 
    
         
            +
                - A field has never been set
         
     | 
| 
      
 64 
     | 
    
         
            +
                - A key doesn't exist in a mapping
         
     | 
| 
      
 65 
     | 
    
         
            +
                - A value is conceptually undefined (not just unset)
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                Example:
         
     | 
| 
      
 68 
     | 
    
         
            +
                    >>> d = {"a": 1}
         
     | 
| 
      
 69 
     | 
    
         
            +
                    >>> d.get("b", Undefined) is Undefined
         
     | 
| 
      
 70 
     | 
    
         
            +
                    True
         
     | 
| 
      
 71 
     | 
    
         
            +
                """
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                __slots__ = ()
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                def __bool__(self) -> Literal[False]:
         
     | 
| 
      
 76 
     | 
    
         
            +
                    return False
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
                def __repr__(self) -> Literal["Undefined"]:
         
     | 
| 
      
 79 
     | 
    
         
            +
                    return "Undefined"
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
                def __str__(self) -> Literal["Undefined"]:
         
     | 
| 
      
 82 
     | 
    
         
            +
                    return "Undefined"
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                def __reduce__(self):
         
     | 
| 
      
 85 
     | 
    
         
            +
                    """Ensure pickle preservation of singleton identity."""
         
     | 
| 
      
 86 
     | 
    
         
            +
                    return "Undefined"
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
            class UnsetType(SingletonType):
         
     | 
| 
      
 90 
     | 
    
         
            +
                """Sentinel for a key present but value not yet provided.
         
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
                Use this when:
         
     | 
| 
      
 93 
     | 
    
         
            +
                - A parameter exists but hasn't been given a value
         
     | 
| 
      
 94 
     | 
    
         
            +
                - Distinguishing between None and "not provided"
         
     | 
| 
      
 95 
     | 
    
         
            +
                - API parameters that are optional but need explicit handling
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
                Example:
         
     | 
| 
      
 98 
     | 
    
         
            +
                    >>> def func(param=Unset):
         
     | 
| 
      
 99 
     | 
    
         
            +
                    ...     if param is not Unset:
         
     | 
| 
      
 100 
     | 
    
         
            +
                    ...         # param was explicitly provided
         
     | 
| 
      
 101 
     | 
    
         
            +
                    ...         process(param)
         
     | 
| 
      
 102 
     | 
    
         
            +
                """
         
     | 
| 
      
 103 
     | 
    
         
            +
             
     | 
| 
      
 104 
     | 
    
         
            +
                __slots__ = ()
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
                def __bool__(self) -> Literal[False]:
         
     | 
| 
      
 107 
     | 
    
         
            +
                    return False
         
     | 
| 
      
 108 
     | 
    
         
            +
             
     | 
| 
      
 109 
     | 
    
         
            +
                def __repr__(self) -> Literal["Unset"]:
         
     | 
| 
      
 110 
     | 
    
         
            +
                    return "Unset"
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
                def __str__(self) -> Literal["Unset"]:
         
     | 
| 
      
 113 
     | 
    
         
            +
                    return "Unset"
         
     | 
| 
      
 114 
     | 
    
         
            +
             
     | 
| 
      
 115 
     | 
    
         
            +
                def __reduce__(self):
         
     | 
| 
      
 116 
     | 
    
         
            +
                    """Ensure pickle preservation of singleton identity."""
         
     | 
| 
      
 117 
     | 
    
         
            +
                    return "Unset"
         
     | 
| 
      
 118 
     | 
    
         
            +
             
     | 
| 
      
 119 
     | 
    
         
            +
             
     | 
| 
      
 120 
     | 
    
         
            +
            Undefined: Final = UndefinedType()
         
     | 
| 
      
 121 
     | 
    
         
            +
            """A key or field entirely missing from a namespace"""
         
     | 
| 
      
 122 
     | 
    
         
            +
            Unset: Final = UnsetType()
         
     | 
| 
      
 123 
     | 
    
         
            +
            """A key present but value not yet provided."""
         
     | 
| 
      
 124 
     | 
    
         
            +
             
     | 
| 
      
 125 
     | 
    
         
            +
            MaybeUndefined = Union[T, UndefinedType]
         
     | 
| 
      
 126 
     | 
    
         
            +
            MaybeUnset = Union[T, UnsetType]
         
     | 
| 
      
 127 
     | 
    
         
            +
            MaybeSentinel = Union[T, UndefinedType, UnsetType]
         
     | 
| 
      
 128 
     | 
    
         
            +
             
     | 
| 
      
 129 
     | 
    
         
            +
            _EMPTY_TUPLE = (tuple(), set(), frozenset(), dict(), list(), "")
         
     | 
| 
      
 130 
     | 
    
         
            +
             
     | 
| 
      
 131 
     | 
    
         
            +
             
     | 
| 
      
 132 
     | 
    
         
            +
            def is_sentinel(
         
     | 
| 
      
 133 
     | 
    
         
            +
                value: Any,
         
     | 
| 
      
 134 
     | 
    
         
            +
                *,
         
     | 
| 
      
 135 
     | 
    
         
            +
                none_as_sentinel: bool = False,
         
     | 
| 
      
 136 
     | 
    
         
            +
                empty_as_sentinel: bool = False,
         
     | 
| 
      
 137 
     | 
    
         
            +
            ) -> bool:
         
     | 
| 
      
 138 
     | 
    
         
            +
                """Check if a value is any sentinel (Undefined or Unset)."""
         
     | 
| 
      
 139 
     | 
    
         
            +
                if none_as_sentinel and value is None:
         
     | 
| 
      
 140 
     | 
    
         
            +
                    return True
         
     | 
| 
      
 141 
     | 
    
         
            +
                if empty_as_sentinel and value in _EMPTY_TUPLE:
         
     | 
| 
      
 142 
     | 
    
         
            +
                    return True
         
     | 
| 
      
 143 
     | 
    
         
            +
                return value is Undefined or value is Unset
         
     | 
| 
      
 144 
     | 
    
         
            +
             
     | 
| 
      
 145 
     | 
    
         
            +
             
     | 
| 
      
 146 
     | 
    
         
            +
            def not_sentinel(
         
     | 
| 
      
 147 
     | 
    
         
            +
                value: Any, none_as_sentinel: bool = False, empty_as_sentinel: bool = False
         
     | 
| 
      
 148 
     | 
    
         
            +
            ) -> bool:
         
     | 
| 
      
 149 
     | 
    
         
            +
                """Check if a value is NOT a sentinel. Useful for filtering operations."""
         
     | 
| 
      
 150 
     | 
    
         
            +
                return not is_sentinel(
         
     | 
| 
      
 151 
     | 
    
         
            +
                    value,
         
     | 
| 
      
 152 
     | 
    
         
            +
                    none_as_sentinel=none_as_sentinel,
         
     | 
| 
      
 153 
     | 
    
         
            +
                    empty_as_sentinel=empty_as_sentinel,
         
     | 
| 
      
 154 
     | 
    
         
            +
                )
         
     |