Skip to content

API Reference

This page exposes the main public API modules.

Package Exports

monkay

InGlobalsDict

Bases: Exception

Source code in monkay/base.py
class InGlobalsDict(Exception): ...

UnsetError

Bases: RuntimeError

Source code in monkay/base.py
class UnsetError(RuntimeError): ...

Cage

Bases: Generic[T]

A container class that manages a value with context-aware modifications and synchronization.

Source code in monkay/cages.py
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
class Cage(Generic[T]):
    """
    A container class that manages a value with context-aware modifications and synchronization.
    """

    monkay_context_var: ContextVar[tuple[int | None, T] | type[Undefined]]
    """
    A context variable that stores the current value and update timestamp within a specific context.
    This allows for context-specific modifications and retrieval of the value.
    """

    monkay_deep_copy: bool
    """
    A boolean indicating whether deep copies should be used when modifying the value.
    If True, modifications will create deep copies of the original value to avoid unintended side effects.
    """

    monkay_use_wrapper_for_reads: bool
    """
    A boolean indicating whether a wrapper should be used for read operations.
    If True, read operations will be wrapped in a context manager to ensure consistency.
    """

    monkay_update_fn: Callable[[T, T], T] | None
    """
    An optional function that defines how to update the value when modifications are made.
    This function takes the current value and the new value as input and returns the updated value.
    """

    monkay_name: str
    """
    A name associated with the Cage instance, used for identification and debugging purposes.
    """

    monkay_original: T
    """
    The original value stored in the Cage instance.
    This attribute holds the initial value that is managed by the Cage.
    """

    monkay_original_last_update: int
    """
    A timestamp indicating the last time the original value was updated.
    This timestamp is used to track changes and ensure consistency.
    """

    monkay_original_last_update_lock: None | Lock
    """
    An optional lock used to synchronize updates to the original value.
    This lock prevents race conditions when multiple threads or processes try to update the value concurrently.
    """

    monkay_original_wrapper: AbstractContextManager
    """
    A context manager used to wrap access to the original value, ensuring consistency and synchronization.
    This wrapper provides a controlled environment for read and write operations on the original value.
    """

    def __new__(
        cls,
        globals_dict: dict,
        obj: T | type[Undefined] = Undefined,
        *,
        name: str,
        preloads: Iterable[str] = (),
        context_var_name: str = "_{name}_ctx",
        deep_copy: bool = False,
        # for e.g. locks
        original_wrapper: AbstractContextManager = nullcontext(),
        update_fn: Callable[[T, T], T] | None = None,
        use_wrapper_for_reads: bool = False,
        skip_self_register: bool = False,
        package: str | None = "",
    ) -> Cage:
        """
        Creates a new Cage instance, wrapping an object and managing its context.

        This method initializes a Cage instance, wrapping the provided object and setting up
        context management. It handles preloads, context variable creation, and attribute forwarding.

        Args:
            globals_dict: The globals dictionary of the module where the Cage is created.
            obj: The object to wrap, or Undefined to retrieve it from globals_dict.
            name: The name of the object.
            preloads: An iterable of preload paths to execute before creating the Cage.
            context_var_name: The name of the context variable to create.
            deep_copy: If True, uses deep copies for modifications.
            original_wrapper: A context manager to wrap access to the original object.
            update_fn: An optional function to define how to update the object.
            use_wrapper_for_reads: If True, uses the wrapper for read operations.
            skip_self_register: If True, skips registering the Cage instance in globals_dict.
            package: The package name to use for relative imports.

        Returns:
            A new Cage instance.

        Raises:
            AssertionError: If the object is Undefined and not found in globals_dict.
            ImportError: If a preload module cannot be imported.
            AttributeError: If a preload function cannot be found.
        """
        if package == "" and globals_dict.get("__spec__"):
            package = globals_dict["__spec__"].parent
        package = package or None
        evaluate_preloads(preloads, ignore_import_errors=True, package=package)
        if obj is Undefined:
            obj = globals_dict[name]
        assert obj is not Undefined, "not initialized"
        if not skip_self_register and isinstance(obj, Cage):
            return obj
        context_var_name = context_var_name.format(name=name)
        obj_type = type(obj)
        attrs: dict = {}
        for attr in dir(obj_type):
            if not attr.startswith("__") or not attr.endswith("__") or attr in forbidden_names:
                continue
            val = getattr(obj_type, attr)
            if ismethoddescriptor(val):
                # we need to add the wrapper to the dict
                attrs[attr] = cls.monkay_forward(obj_type, attr)
        attrs["__new__"] = object.__new__
        monkay_cage_cls = type(cls.__name__, (cls,), attrs)
        monkay_cage_instance = monkay_cage_cls()
        monkay_cage_instance.monkay_name = name
        monkay_cage_instance.monkay_context_var = globals_dict[context_var_name] = ContextVar(
            context_var_name, default=Undefined
        )
        monkay_cage_instance.monkay_deep_copy = deep_copy
        monkay_cage_instance.monkay_use_wrapper_for_reads = use_wrapper_for_reads
        monkay_cage_instance.monkay_update_fn = update_fn
        monkay_cage_instance.monkay_original = obj
        monkay_cage_instance.monkay_original_last_update = 0
        monkay_cage_instance.monkay_original_last_update_lock = (
            None if update_fn is None else Lock()
        )
        monkay_cage_instance.monkay_original_wrapper = original_wrapper

        if not skip_self_register:
            globals_dict[name] = monkay_cage_instance
        return monkay_cage_instance

    @classmethod
    def monkay_forward(cls, obj_type: type, name: str) -> Any:
        """
        Creates a wrapper function that forwards method calls to the wrapped object.

        This class method generates a wrapper function that intercepts method calls to the Cage instance
        and forwards them to the wrapped object, ensuring that the object is updated or copied as needed.

        Args:
            obj_type: The type of the wrapped object.
            name: The name of the method to forward.

        Returns:
            A wrapper function that forwards method calls.
        """

        @wraps(getattr(obj_type, name))
        def _(self, *args: Any, **kwargs: Any):
            obj = self.monkay_conditional_update_copy()
            return getattr(obj, name)(*args, **kwargs)

        return _

    def monkay_refresh_copy(
        self,
        *,
        obj: T | type[Undefined] = Undefined,
        use_wrapper: bool | None = None,
        _monkay_dict: dict | None = None,
    ) -> T:
        """
        Refreshes the context variable with a copy of the original object.

        This method updates the context variable with a fresh copy of the original object,
        optionally using a wrapper and handling deep or shallow copies.

        Args:
            obj: An optional object to use instead of creating a copy.
            use_wrapper: If True, uses the original wrapper when accessing the original object.
                         If None, uses the Cage's default `monkay_use_wrapper_for_reads` value.
            _monkay_dict: An optional dictionary to use instead of the Cage's `__dict__`.

        Returns:
            The refreshed object.
        """
        if _monkay_dict is None:
            _monkay_dict = super().__getattribute__("__dict__")
        if use_wrapper is None:
            use_wrapper = _monkay_dict["monkay_use_wrapper_for_reads"]
        if obj is Undefined:
            with _monkay_dict["monkay_original_wrapper"] if use_wrapper else nullcontext():
                obj = (
                    copy.deepcopy(_monkay_dict["monkay_original"])
                    if _monkay_dict["monkay_deep_copy"]
                    else copy.copy(_monkay_dict["monkay_original"])
                )
        _monkay_dict["monkay_context_var"].set((_monkay_dict["monkay_original_last_update"], obj))
        return cast(T, obj)

    def monkay_conditional_update_copy(
        self, *, use_wrapper: bool | None = None, _monkay_dict: dict | None = None
    ) -> T:
        """
        Retrieves a context-aware copy of the original object, updating it if necessary.

        This method retrieves a copy of the original object, updating it based on context and update functions.
        It checks if the context variable is set and if the original object has been updated, and applies
        the update function if necessary.

        Args:
            use_wrapper: If True, uses the original wrapper when accessing the original object.
                         If None, uses the Cage's default `monkay_use_wrapper_for_reads` value.
            _monkay_dict: An optional dictionary to use instead of the Cage's `__dict__`.

        Returns:
            The context-aware copy of the object.
        """
        if _monkay_dict is None:
            _monkay_dict = super().__getattribute__("__dict__")
        if use_wrapper is None:
            use_wrapper = _monkay_dict["monkay_use_wrapper_for_reads"]
        tup = _monkay_dict["monkay_context_var"].get()
        if tup is Undefined:
            obj = self.monkay_refresh_copy(_monkay_dict=_monkay_dict)
        elif (
            _monkay_dict["monkay_update_fn"] is not None
            and tup[0] is not None
            and tup[0] != _monkay_dict["monkay_original_last_update"]
        ):
            with _monkay_dict["monkay_original_wrapper"] if use_wrapper else nullcontext():
                obj = _monkay_dict["monkay_update_fn"](tup[1], _monkay_dict["monkay_original"])
            obj = self.monkay_refresh_copy(
                obj=obj, _monkay_dict=_monkay_dict, use_wrapper=use_wrapper
            )
        else:
            obj = tup[1]
        return obj

    def __getattribute__(self, name: str) -> Any:
        """
        Overrides attribute access to retrieve attributes from the context-aware copy.

        This method intercepts attribute access and retrieves the attribute from the context-aware copy
        of the object, ensuring that the object is updated as needed.

        Args:
            name: The name of the attribute to retrieve.

        Returns:
            The retrieved attribute value.
        """
        if name in forbidden_names or name.startswith("monkay_"):
            return super().__getattribute__(name)
        obj = self.monkay_conditional_update_copy()

        return getattr(obj, name)

    def __delattr__(
        self,
        name: str,
    ) -> None:
        """
        Overrides attribute deletion to delete attributes from the context-aware copy.

        This method intercepts attribute deletion and deletes the attribute from the context-aware copy
        of the object, ensuring that the object is updated as needed.

        Args:
            name: The name of the attribute to delete.
        """
        if name.startswith("monkay_"):
            super().__delattr__(name)
            return
        obj = self.monkay_conditional_update_copy()
        delattr(obj, name)

    def __setattr__(self, name: str, value: Any) -> None:
        """
        Overrides attribute setting to set attributes in the context-aware copy.

        This method intercepts attribute setting and sets the attribute in the context-aware copy
        of the object, ensuring that the object is updated as needed.

        Args:
            name: The name of the attribute to set.
            value: The value to set the attribute to.
        """
        if name.startswith("monkay_"):
            super().__setattr__(name, value)
            return
        obj = self.monkay_conditional_update_copy()
        setattr(obj, name, value)

    def monkay_proxied(
        self,
        use_wrapper: bool | None = None,
    ) -> T:
        """
        Returns a proxied version of the object, ensuring context-aware updates.

        This method returns a context-aware copy of the object, optionally using a wrapper.

        Args:
            use_wrapper: If True, uses the original wrapper when accessing the original object.
                         If None, uses the Cage's default `monkay_use_wrapper_for_reads` value.

        Returns:
            The proxied object.
        """
        return self.monkay_conditional_update_copy(use_wrapper=use_wrapper)

    @contextmanager
    def monkay_with_override(self, value: T, *, allow_value_update: bool = True) -> Generator[T]:
        """
        Temporarily overrides the context variable with a new value within a context.

        This context manager temporarily sets the context variable to a new value and yields it.
        After the context exits, the original context variable is restored.

        Args:
            value: The new value to set the context variable to.
            allow_value_update: If True, allows the value to be updated by the update function.
                                If False, the value will not be updated.

        Yields:
            The new value.
        """
        monkay_dict = super().__getattribute__("__dict__")
        token = monkay_dict["monkay_context_var"].set(
            (monkay_dict["monkay_original_last_update"] if allow_value_update else None, value)
        )
        try:
            yield value
        finally:
            monkay_dict["monkay_context_var"].reset(token)

    @contextmanager
    def monkay_with_original(
        self, use_wrapper: bool = True, update_after: bool = True
    ) -> Generator[T]:
        """
        Temporarily accesses the original value within a context, optionally updating it.

        This context manager yields the original value, optionally using a wrapper and updating
        the last update timestamp after the context exits.

        Args:
            use_wrapper: If True, uses the original wrapper when accessing the original value.
            update_after: If True, updates the last update timestamp after the context exits.

        Yields:
            The original value.
        """
        monkay_dict = super().__getattribute__("__dict__")
        wrapper = monkay_dict["monkay_original_wrapper"] if use_wrapper else nullcontext()
        with wrapper:
            yield monkay_dict["monkay_original"]
            if update_after and monkay_dict["monkay_original_last_update_lock"] is not None:
                with monkay_dict["monkay_original_last_update_lock"]:
                    monkay_dict["monkay_original_last_update"] += 1

    def monkay_set(self, value: T) -> Token:
        """
        Sets the context variable to a new value and returns a token.

        This method sets the context variable to a new value and returns a token that can be used
        to reset the context variable to its previous value.

        Args:
            value: The new value to set the context variable to.

        Returns:
            A token that can be used to reset the context variable.
        """
        monkay_dict = super().__getattribute__("__dict__")
        return monkay_dict["monkay_context_var"].set(
            (monkay_dict["monkay_original_last_update"], value)
        )

    def monkay_get(self, default: T | DEFAULT | None = None) -> T | DEFAULT | None:
        """
        Retrieves the current value of the context variable, or a default value if it's not set.

        This method retrieves the current value of the context variable. If the context variable is not set,
        it returns the original value or a default value if provided.

        Args:
            default: The default value to return if the context variable is not set.

        Returns:
            The current value of the context variable, the original value, or the default value.
        """
        monkay_dict = super().__getattribute__("__dict__")
        tup: type[Undefined] | tuple[int | None, T] = monkay_dict["monkay_context_var"].get()
        if tup is Undefined:
            original: T | type[Undefined] = monkay_dict["monkay_original"]
            if original is not Undefined:
                return cast("DEFAULT | None", original)
            else:
                return default
        else:
            return cast("tuple[int | None, T]", tup)[1]

    def monkay_reset(self, token: Token):
        """
        Resets the context variable to its previous value using a token.

        This method resets the context variable to its previous value using a token returned by `monkay_set`.

        Args:
            token: The token returned by `monkay_set`.
        """
        self.monkay_context_var.reset(token)

monkay_context_var instance-attribute

monkay_context_var

A context variable that stores the current value and update timestamp within a specific context. This allows for context-specific modifications and retrieval of the value.

monkay_deep_copy instance-attribute

monkay_deep_copy

A boolean indicating whether deep copies should be used when modifying the value. If True, modifications will create deep copies of the original value to avoid unintended side effects.

monkay_use_wrapper_for_reads instance-attribute

monkay_use_wrapper_for_reads

A boolean indicating whether a wrapper should be used for read operations. If True, read operations will be wrapped in a context manager to ensure consistency.

monkay_update_fn instance-attribute

monkay_update_fn

An optional function that defines how to update the value when modifications are made. This function takes the current value and the new value as input and returns the updated value.

monkay_name instance-attribute

monkay_name

A name associated with the Cage instance, used for identification and debugging purposes.

monkay_original instance-attribute

monkay_original

The original value stored in the Cage instance. This attribute holds the initial value that is managed by the Cage.

monkay_original_last_update instance-attribute

monkay_original_last_update

A timestamp indicating the last time the original value was updated. This timestamp is used to track changes and ensure consistency.

monkay_original_last_update_lock instance-attribute

monkay_original_last_update_lock

An optional lock used to synchronize updates to the original value. This lock prevents race conditions when multiple threads or processes try to update the value concurrently.

monkay_original_wrapper instance-attribute

monkay_original_wrapper

A context manager used to wrap access to the original value, ensuring consistency and synchronization. This wrapper provides a controlled environment for read and write operations on the original value.

monkay_forward classmethod

monkay_forward(obj_type, name)

Creates a wrapper function that forwards method calls to the wrapped object.

This class method generates a wrapper function that intercepts method calls to the Cage instance and forwards them to the wrapped object, ensuring that the object is updated or copied as needed.

Parameters:

Name Type Description Default
obj_type type

The type of the wrapped object.

required
name str

The name of the method to forward.

required

Returns:

Type Description
Any

A wrapper function that forwards method calls.

Source code in monkay/cages.py
@classmethod
def monkay_forward(cls, obj_type: type, name: str) -> Any:
    """
    Creates a wrapper function that forwards method calls to the wrapped object.

    This class method generates a wrapper function that intercepts method calls to the Cage instance
    and forwards them to the wrapped object, ensuring that the object is updated or copied as needed.

    Args:
        obj_type: The type of the wrapped object.
        name: The name of the method to forward.

    Returns:
        A wrapper function that forwards method calls.
    """

    @wraps(getattr(obj_type, name))
    def _(self, *args: Any, **kwargs: Any):
        obj = self.monkay_conditional_update_copy()
        return getattr(obj, name)(*args, **kwargs)

    return _

monkay_refresh_copy

monkay_refresh_copy(*, obj=Undefined, use_wrapper=None, _monkay_dict=None)

Refreshes the context variable with a copy of the original object.

This method updates the context variable with a fresh copy of the original object, optionally using a wrapper and handling deep or shallow copies.

Parameters:

Name Type Description Default
obj T | type[Undefined]

An optional object to use instead of creating a copy.

Undefined
use_wrapper bool | None

If True, uses the original wrapper when accessing the original object. If None, uses the Cage's default monkay_use_wrapper_for_reads value.

None
_monkay_dict dict | None

An optional dictionary to use instead of the Cage's __dict__.

None

Returns:

Type Description
T

The refreshed object.

Source code in monkay/cages.py
def monkay_refresh_copy(
    self,
    *,
    obj: T | type[Undefined] = Undefined,
    use_wrapper: bool | None = None,
    _monkay_dict: dict | None = None,
) -> T:
    """
    Refreshes the context variable with a copy of the original object.

    This method updates the context variable with a fresh copy of the original object,
    optionally using a wrapper and handling deep or shallow copies.

    Args:
        obj: An optional object to use instead of creating a copy.
        use_wrapper: If True, uses the original wrapper when accessing the original object.
                     If None, uses the Cage's default `monkay_use_wrapper_for_reads` value.
        _monkay_dict: An optional dictionary to use instead of the Cage's `__dict__`.

    Returns:
        The refreshed object.
    """
    if _monkay_dict is None:
        _monkay_dict = super().__getattribute__("__dict__")
    if use_wrapper is None:
        use_wrapper = _monkay_dict["monkay_use_wrapper_for_reads"]
    if obj is Undefined:
        with _monkay_dict["monkay_original_wrapper"] if use_wrapper else nullcontext():
            obj = (
                copy.deepcopy(_monkay_dict["monkay_original"])
                if _monkay_dict["monkay_deep_copy"]
                else copy.copy(_monkay_dict["monkay_original"])
            )
    _monkay_dict["monkay_context_var"].set((_monkay_dict["monkay_original_last_update"], obj))
    return cast(T, obj)

monkay_conditional_update_copy

monkay_conditional_update_copy(*, use_wrapper=None, _monkay_dict=None)

Retrieves a context-aware copy of the original object, updating it if necessary.

This method retrieves a copy of the original object, updating it based on context and update functions. It checks if the context variable is set and if the original object has been updated, and applies the update function if necessary.

Parameters:

Name Type Description Default
use_wrapper bool | None

If True, uses the original wrapper when accessing the original object. If None, uses the Cage's default monkay_use_wrapper_for_reads value.

None
_monkay_dict dict | None

An optional dictionary to use instead of the Cage's __dict__.

None

Returns:

Type Description
T

The context-aware copy of the object.

Source code in monkay/cages.py
def monkay_conditional_update_copy(
    self, *, use_wrapper: bool | None = None, _monkay_dict: dict | None = None
) -> T:
    """
    Retrieves a context-aware copy of the original object, updating it if necessary.

    This method retrieves a copy of the original object, updating it based on context and update functions.
    It checks if the context variable is set and if the original object has been updated, and applies
    the update function if necessary.

    Args:
        use_wrapper: If True, uses the original wrapper when accessing the original object.
                     If None, uses the Cage's default `monkay_use_wrapper_for_reads` value.
        _monkay_dict: An optional dictionary to use instead of the Cage's `__dict__`.

    Returns:
        The context-aware copy of the object.
    """
    if _monkay_dict is None:
        _monkay_dict = super().__getattribute__("__dict__")
    if use_wrapper is None:
        use_wrapper = _monkay_dict["monkay_use_wrapper_for_reads"]
    tup = _monkay_dict["monkay_context_var"].get()
    if tup is Undefined:
        obj = self.monkay_refresh_copy(_monkay_dict=_monkay_dict)
    elif (
        _monkay_dict["monkay_update_fn"] is not None
        and tup[0] is not None
        and tup[0] != _monkay_dict["monkay_original_last_update"]
    ):
        with _monkay_dict["monkay_original_wrapper"] if use_wrapper else nullcontext():
            obj = _monkay_dict["monkay_update_fn"](tup[1], _monkay_dict["monkay_original"])
        obj = self.monkay_refresh_copy(
            obj=obj, _monkay_dict=_monkay_dict, use_wrapper=use_wrapper
        )
    else:
        obj = tup[1]
    return obj

monkay_proxied

monkay_proxied(use_wrapper=None)

Returns a proxied version of the object, ensuring context-aware updates.

This method returns a context-aware copy of the object, optionally using a wrapper.

Parameters:

Name Type Description Default
use_wrapper bool | None

If True, uses the original wrapper when accessing the original object. If None, uses the Cage's default monkay_use_wrapper_for_reads value.

None

Returns:

Type Description
T

The proxied object.

Source code in monkay/cages.py
def monkay_proxied(
    self,
    use_wrapper: bool | None = None,
) -> T:
    """
    Returns a proxied version of the object, ensuring context-aware updates.

    This method returns a context-aware copy of the object, optionally using a wrapper.

    Args:
        use_wrapper: If True, uses the original wrapper when accessing the original object.
                     If None, uses the Cage's default `monkay_use_wrapper_for_reads` value.

    Returns:
        The proxied object.
    """
    return self.monkay_conditional_update_copy(use_wrapper=use_wrapper)

monkay_with_override

monkay_with_override(value, *, allow_value_update=True)

Temporarily overrides the context variable with a new value within a context.

This context manager temporarily sets the context variable to a new value and yields it. After the context exits, the original context variable is restored.

Parameters:

Name Type Description Default
value T

The new value to set the context variable to.

required
allow_value_update bool

If True, allows the value to be updated by the update function. If False, the value will not be updated.

True

Yields:

Type Description
Generator[T]

The new value.

Source code in monkay/cages.py
@contextmanager
def monkay_with_override(self, value: T, *, allow_value_update: bool = True) -> Generator[T]:
    """
    Temporarily overrides the context variable with a new value within a context.

    This context manager temporarily sets the context variable to a new value and yields it.
    After the context exits, the original context variable is restored.

    Args:
        value: The new value to set the context variable to.
        allow_value_update: If True, allows the value to be updated by the update function.
                            If False, the value will not be updated.

    Yields:
        The new value.
    """
    monkay_dict = super().__getattribute__("__dict__")
    token = monkay_dict["monkay_context_var"].set(
        (monkay_dict["monkay_original_last_update"] if allow_value_update else None, value)
    )
    try:
        yield value
    finally:
        monkay_dict["monkay_context_var"].reset(token)

monkay_with_original

monkay_with_original(use_wrapper=True, update_after=True)

Temporarily accesses the original value within a context, optionally updating it.

This context manager yields the original value, optionally using a wrapper and updating the last update timestamp after the context exits.

Parameters:

Name Type Description Default
use_wrapper bool

If True, uses the original wrapper when accessing the original value.

True
update_after bool

If True, updates the last update timestamp after the context exits.

True

Yields:

Type Description
Generator[T]

The original value.

Source code in monkay/cages.py
@contextmanager
def monkay_with_original(
    self, use_wrapper: bool = True, update_after: bool = True
) -> Generator[T]:
    """
    Temporarily accesses the original value within a context, optionally updating it.

    This context manager yields the original value, optionally using a wrapper and updating
    the last update timestamp after the context exits.

    Args:
        use_wrapper: If True, uses the original wrapper when accessing the original value.
        update_after: If True, updates the last update timestamp after the context exits.

    Yields:
        The original value.
    """
    monkay_dict = super().__getattribute__("__dict__")
    wrapper = monkay_dict["monkay_original_wrapper"] if use_wrapper else nullcontext()
    with wrapper:
        yield monkay_dict["monkay_original"]
        if update_after and monkay_dict["monkay_original_last_update_lock"] is not None:
            with monkay_dict["monkay_original_last_update_lock"]:
                monkay_dict["monkay_original_last_update"] += 1

monkay_set

monkay_set(value)

Sets the context variable to a new value and returns a token.

This method sets the context variable to a new value and returns a token that can be used to reset the context variable to its previous value.

Parameters:

Name Type Description Default
value T

The new value to set the context variable to.

required

Returns:

Type Description
Token

A token that can be used to reset the context variable.

Source code in monkay/cages.py
def monkay_set(self, value: T) -> Token:
    """
    Sets the context variable to a new value and returns a token.

    This method sets the context variable to a new value and returns a token that can be used
    to reset the context variable to its previous value.

    Args:
        value: The new value to set the context variable to.

    Returns:
        A token that can be used to reset the context variable.
    """
    monkay_dict = super().__getattribute__("__dict__")
    return monkay_dict["monkay_context_var"].set(
        (monkay_dict["monkay_original_last_update"], value)
    )

monkay_get

monkay_get(default=None)

Retrieves the current value of the context variable, or a default value if it's not set.

This method retrieves the current value of the context variable. If the context variable is not set, it returns the original value or a default value if provided.

Parameters:

Name Type Description Default
default T | DEFAULT | None

The default value to return if the context variable is not set.

None

Returns:

Type Description
T | DEFAULT | None

The current value of the context variable, the original value, or the default value.

Source code in monkay/cages.py
def monkay_get(self, default: T | DEFAULT | None = None) -> T | DEFAULT | None:
    """
    Retrieves the current value of the context variable, or a default value if it's not set.

    This method retrieves the current value of the context variable. If the context variable is not set,
    it returns the original value or a default value if provided.

    Args:
        default: The default value to return if the context variable is not set.

    Returns:
        The current value of the context variable, the original value, or the default value.
    """
    monkay_dict = super().__getattribute__("__dict__")
    tup: type[Undefined] | tuple[int | None, T] = monkay_dict["monkay_context_var"].get()
    if tup is Undefined:
        original: T | type[Undefined] = monkay_dict["monkay_original"]
        if original is not Undefined:
            return cast("DEFAULT | None", original)
        else:
            return default
    else:
        return cast("tuple[int | None, T]", tup)[1]

monkay_reset

monkay_reset(token)

Resets the context variable to its previous value using a token.

This method resets the context variable to its previous value using a token returned by monkay_set.

Parameters:

Name Type Description Default
token Token

The token returned by monkay_set.

required
Source code in monkay/cages.py
def monkay_reset(self, token: Token):
    """
    Resets the context variable to its previous value using a token.

    This method resets the context variable to its previous value using a token returned by `monkay_set`.

    Args:
        token: The token returned by `monkay_set`.
    """
    self.monkay_context_var.reset(token)

TransparentCage

Bases: Cage

A transparent Cage subclass that exposes context variable attributes directly.

This subclass of Cage allows direct access to context variable attributes (e.g., 'get', 'set', 'reset') without requiring the 'monkay_' prefix. It provides a more transparent interface for interacting with context-aware values.

Source code in monkay/cages.py
class TransparentCage(Cage):
    """
    A transparent Cage subclass that exposes context variable attributes directly.

    This subclass of Cage allows direct access to context variable attributes (e.g., 'get', 'set', 'reset')
    without requiring the 'monkay_' prefix. It provides a more transparent interface for interacting
    with context-aware values.
    """

    def __getattribute__(self, name: str) -> Any:
        """
        Overrides attribute access to expose context variable attributes directly.

        This method intercepts attribute access and exposes context variable attributes without
        the 'monkay_' prefix.

        Args:
            name: The name of the attribute to retrieve.

        Returns:
            The retrieved attribute value.
        """
        if name in context_var_attributes:
            name = f"monkay_{name}"
        return super().__getattribute__(name)

monkay_context_var instance-attribute

monkay_context_var

A context variable that stores the current value and update timestamp within a specific context. This allows for context-specific modifications and retrieval of the value.

monkay_deep_copy instance-attribute

monkay_deep_copy

A boolean indicating whether deep copies should be used when modifying the value. If True, modifications will create deep copies of the original value to avoid unintended side effects.

monkay_use_wrapper_for_reads instance-attribute

monkay_use_wrapper_for_reads

A boolean indicating whether a wrapper should be used for read operations. If True, read operations will be wrapped in a context manager to ensure consistency.

monkay_update_fn instance-attribute

monkay_update_fn

An optional function that defines how to update the value when modifications are made. This function takes the current value and the new value as input and returns the updated value.

monkay_name instance-attribute

monkay_name

A name associated with the Cage instance, used for identification and debugging purposes.

monkay_original instance-attribute

monkay_original

The original value stored in the Cage instance. This attribute holds the initial value that is managed by the Cage.

monkay_original_last_update instance-attribute

monkay_original_last_update

A timestamp indicating the last time the original value was updated. This timestamp is used to track changes and ensure consistency.

monkay_original_last_update_lock instance-attribute

monkay_original_last_update_lock

An optional lock used to synchronize updates to the original value. This lock prevents race conditions when multiple threads or processes try to update the value concurrently.

monkay_original_wrapper instance-attribute

monkay_original_wrapper

A context manager used to wrap access to the original value, ensuring consistency and synchronization. This wrapper provides a controlled environment for read and write operations on the original value.

monkay_forward classmethod

monkay_forward(obj_type, name)

Creates a wrapper function that forwards method calls to the wrapped object.

This class method generates a wrapper function that intercepts method calls to the Cage instance and forwards them to the wrapped object, ensuring that the object is updated or copied as needed.

Parameters:

Name Type Description Default
obj_type type

The type of the wrapped object.

required
name str

The name of the method to forward.

required

Returns:

Type Description
Any

A wrapper function that forwards method calls.

Source code in monkay/cages.py
@classmethod
def monkay_forward(cls, obj_type: type, name: str) -> Any:
    """
    Creates a wrapper function that forwards method calls to the wrapped object.

    This class method generates a wrapper function that intercepts method calls to the Cage instance
    and forwards them to the wrapped object, ensuring that the object is updated or copied as needed.

    Args:
        obj_type: The type of the wrapped object.
        name: The name of the method to forward.

    Returns:
        A wrapper function that forwards method calls.
    """

    @wraps(getattr(obj_type, name))
    def _(self, *args: Any, **kwargs: Any):
        obj = self.monkay_conditional_update_copy()
        return getattr(obj, name)(*args, **kwargs)

    return _

monkay_refresh_copy

monkay_refresh_copy(*, obj=Undefined, use_wrapper=None, _monkay_dict=None)

Refreshes the context variable with a copy of the original object.

This method updates the context variable with a fresh copy of the original object, optionally using a wrapper and handling deep or shallow copies.

Parameters:

Name Type Description Default
obj T | type[Undefined]

An optional object to use instead of creating a copy.

Undefined
use_wrapper bool | None

If True, uses the original wrapper when accessing the original object. If None, uses the Cage's default monkay_use_wrapper_for_reads value.

None
_monkay_dict dict | None

An optional dictionary to use instead of the Cage's __dict__.

None

Returns:

Type Description
T

The refreshed object.

Source code in monkay/cages.py
def monkay_refresh_copy(
    self,
    *,
    obj: T | type[Undefined] = Undefined,
    use_wrapper: bool | None = None,
    _monkay_dict: dict | None = None,
) -> T:
    """
    Refreshes the context variable with a copy of the original object.

    This method updates the context variable with a fresh copy of the original object,
    optionally using a wrapper and handling deep or shallow copies.

    Args:
        obj: An optional object to use instead of creating a copy.
        use_wrapper: If True, uses the original wrapper when accessing the original object.
                     If None, uses the Cage's default `monkay_use_wrapper_for_reads` value.
        _monkay_dict: An optional dictionary to use instead of the Cage's `__dict__`.

    Returns:
        The refreshed object.
    """
    if _monkay_dict is None:
        _monkay_dict = super().__getattribute__("__dict__")
    if use_wrapper is None:
        use_wrapper = _monkay_dict["monkay_use_wrapper_for_reads"]
    if obj is Undefined:
        with _monkay_dict["monkay_original_wrapper"] if use_wrapper else nullcontext():
            obj = (
                copy.deepcopy(_monkay_dict["monkay_original"])
                if _monkay_dict["monkay_deep_copy"]
                else copy.copy(_monkay_dict["monkay_original"])
            )
    _monkay_dict["monkay_context_var"].set((_monkay_dict["monkay_original_last_update"], obj))
    return cast(T, obj)

monkay_conditional_update_copy

monkay_conditional_update_copy(*, use_wrapper=None, _monkay_dict=None)

Retrieves a context-aware copy of the original object, updating it if necessary.

This method retrieves a copy of the original object, updating it based on context and update functions. It checks if the context variable is set and if the original object has been updated, and applies the update function if necessary.

Parameters:

Name Type Description Default
use_wrapper bool | None

If True, uses the original wrapper when accessing the original object. If None, uses the Cage's default monkay_use_wrapper_for_reads value.

None
_monkay_dict dict | None

An optional dictionary to use instead of the Cage's __dict__.

None

Returns:

Type Description
T

The context-aware copy of the object.

Source code in monkay/cages.py
def monkay_conditional_update_copy(
    self, *, use_wrapper: bool | None = None, _monkay_dict: dict | None = None
) -> T:
    """
    Retrieves a context-aware copy of the original object, updating it if necessary.

    This method retrieves a copy of the original object, updating it based on context and update functions.
    It checks if the context variable is set and if the original object has been updated, and applies
    the update function if necessary.

    Args:
        use_wrapper: If True, uses the original wrapper when accessing the original object.
                     If None, uses the Cage's default `monkay_use_wrapper_for_reads` value.
        _monkay_dict: An optional dictionary to use instead of the Cage's `__dict__`.

    Returns:
        The context-aware copy of the object.
    """
    if _monkay_dict is None:
        _monkay_dict = super().__getattribute__("__dict__")
    if use_wrapper is None:
        use_wrapper = _monkay_dict["monkay_use_wrapper_for_reads"]
    tup = _monkay_dict["monkay_context_var"].get()
    if tup is Undefined:
        obj = self.monkay_refresh_copy(_monkay_dict=_monkay_dict)
    elif (
        _monkay_dict["monkay_update_fn"] is not None
        and tup[0] is not None
        and tup[0] != _monkay_dict["monkay_original_last_update"]
    ):
        with _monkay_dict["monkay_original_wrapper"] if use_wrapper else nullcontext():
            obj = _monkay_dict["monkay_update_fn"](tup[1], _monkay_dict["monkay_original"])
        obj = self.monkay_refresh_copy(
            obj=obj, _monkay_dict=_monkay_dict, use_wrapper=use_wrapper
        )
    else:
        obj = tup[1]
    return obj

monkay_proxied

monkay_proxied(use_wrapper=None)

Returns a proxied version of the object, ensuring context-aware updates.

This method returns a context-aware copy of the object, optionally using a wrapper.

Parameters:

Name Type Description Default
use_wrapper bool | None

If True, uses the original wrapper when accessing the original object. If None, uses the Cage's default monkay_use_wrapper_for_reads value.

None

Returns:

Type Description
T

The proxied object.

Source code in monkay/cages.py
def monkay_proxied(
    self,
    use_wrapper: bool | None = None,
) -> T:
    """
    Returns a proxied version of the object, ensuring context-aware updates.

    This method returns a context-aware copy of the object, optionally using a wrapper.

    Args:
        use_wrapper: If True, uses the original wrapper when accessing the original object.
                     If None, uses the Cage's default `monkay_use_wrapper_for_reads` value.

    Returns:
        The proxied object.
    """
    return self.monkay_conditional_update_copy(use_wrapper=use_wrapper)

monkay_with_override

monkay_with_override(value, *, allow_value_update=True)

Temporarily overrides the context variable with a new value within a context.

This context manager temporarily sets the context variable to a new value and yields it. After the context exits, the original context variable is restored.

Parameters:

Name Type Description Default
value T

The new value to set the context variable to.

required
allow_value_update bool

If True, allows the value to be updated by the update function. If False, the value will not be updated.

True

Yields:

Type Description
Generator[T]

The new value.

Source code in monkay/cages.py
@contextmanager
def monkay_with_override(self, value: T, *, allow_value_update: bool = True) -> Generator[T]:
    """
    Temporarily overrides the context variable with a new value within a context.

    This context manager temporarily sets the context variable to a new value and yields it.
    After the context exits, the original context variable is restored.

    Args:
        value: The new value to set the context variable to.
        allow_value_update: If True, allows the value to be updated by the update function.
                            If False, the value will not be updated.

    Yields:
        The new value.
    """
    monkay_dict = super().__getattribute__("__dict__")
    token = monkay_dict["monkay_context_var"].set(
        (monkay_dict["monkay_original_last_update"] if allow_value_update else None, value)
    )
    try:
        yield value
    finally:
        monkay_dict["monkay_context_var"].reset(token)

monkay_with_original

monkay_with_original(use_wrapper=True, update_after=True)

Temporarily accesses the original value within a context, optionally updating it.

This context manager yields the original value, optionally using a wrapper and updating the last update timestamp after the context exits.

Parameters:

Name Type Description Default
use_wrapper bool

If True, uses the original wrapper when accessing the original value.

True
update_after bool

If True, updates the last update timestamp after the context exits.

True

Yields:

Type Description
Generator[T]

The original value.

Source code in monkay/cages.py
@contextmanager
def monkay_with_original(
    self, use_wrapper: bool = True, update_after: bool = True
) -> Generator[T]:
    """
    Temporarily accesses the original value within a context, optionally updating it.

    This context manager yields the original value, optionally using a wrapper and updating
    the last update timestamp after the context exits.

    Args:
        use_wrapper: If True, uses the original wrapper when accessing the original value.
        update_after: If True, updates the last update timestamp after the context exits.

    Yields:
        The original value.
    """
    monkay_dict = super().__getattribute__("__dict__")
    wrapper = monkay_dict["monkay_original_wrapper"] if use_wrapper else nullcontext()
    with wrapper:
        yield monkay_dict["monkay_original"]
        if update_after and monkay_dict["monkay_original_last_update_lock"] is not None:
            with monkay_dict["monkay_original_last_update_lock"]:
                monkay_dict["monkay_original_last_update"] += 1

monkay_set

monkay_set(value)

Sets the context variable to a new value and returns a token.

This method sets the context variable to a new value and returns a token that can be used to reset the context variable to its previous value.

Parameters:

Name Type Description Default
value T

The new value to set the context variable to.

required

Returns:

Type Description
Token

A token that can be used to reset the context variable.

Source code in monkay/cages.py
def monkay_set(self, value: T) -> Token:
    """
    Sets the context variable to a new value and returns a token.

    This method sets the context variable to a new value and returns a token that can be used
    to reset the context variable to its previous value.

    Args:
        value: The new value to set the context variable to.

    Returns:
        A token that can be used to reset the context variable.
    """
    monkay_dict = super().__getattribute__("__dict__")
    return monkay_dict["monkay_context_var"].set(
        (monkay_dict["monkay_original_last_update"], value)
    )

monkay_get

monkay_get(default=None)

Retrieves the current value of the context variable, or a default value if it's not set.

This method retrieves the current value of the context variable. If the context variable is not set, it returns the original value or a default value if provided.

Parameters:

Name Type Description Default
default T | DEFAULT | None

The default value to return if the context variable is not set.

None

Returns:

Type Description
T | DEFAULT | None

The current value of the context variable, the original value, or the default value.

Source code in monkay/cages.py
def monkay_get(self, default: T | DEFAULT | None = None) -> T | DEFAULT | None:
    """
    Retrieves the current value of the context variable, or a default value if it's not set.

    This method retrieves the current value of the context variable. If the context variable is not set,
    it returns the original value or a default value if provided.

    Args:
        default: The default value to return if the context variable is not set.

    Returns:
        The current value of the context variable, the original value, or the default value.
    """
    monkay_dict = super().__getattribute__("__dict__")
    tup: type[Undefined] | tuple[int | None, T] = monkay_dict["monkay_context_var"].get()
    if tup is Undefined:
        original: T | type[Undefined] = monkay_dict["monkay_original"]
        if original is not Undefined:
            return cast("DEFAULT | None", original)
        else:
            return default
    else:
        return cast("tuple[int | None, T]", tup)[1]

monkay_reset

monkay_reset(token)

Resets the context variable to its previous value using a token.

This method resets the context variable to its previous value using a token returned by monkay_set.

Parameters:

Name Type Description Default
token Token

The token returned by monkay_set.

required
Source code in monkay/cages.py
def monkay_reset(self, token: Token):
    """
    Resets the context variable to its previous value using a token.

    This method resets the context variable to its previous value using a token returned by `monkay_set`.

    Args:
        token: The token returned by `monkay_set`.
    """
    self.monkay_context_var.reset(token)

Monkay

Bases: MonkayInstance[INSTANCE, SETTINGS], MonkaySettings[SETTINGS], MonkayExports, Generic[INSTANCE, SETTINGS]

A comprehensive class that combines instance, settings, and export management for a module.

This class provides a unified interface for managing module instances, settings, and exports. It integrates lazy imports, deprecated imports, settings loading, and instance management.

Source code in monkay/core.py
class Monkay(
    MonkayInstance[INSTANCE, SETTINGS],
    MonkaySettings[SETTINGS],
    MonkayExports,
    Generic[INSTANCE, SETTINGS],
):
    """
    A comprehensive class that combines instance, settings, and export management for a module.

    This class provides a unified interface for managing module instances, settings, and exports.
    It integrates lazy imports, deprecated imports, settings loading, and instance management.
    """

    def __init__(
        self,
        globals_dict: dict,
        *,
        with_instance: str | bool = False,
        with_extensions: str | bool = False,
        extension_order_key_fn: None
        | Callable[[ExtensionProtocol[INSTANCE, SETTINGS]], Any] = None,
        settings_path: SETTINGS_DEFINITION_TYPE | Literal[False] = None,
        preloads: Iterable[str] = (),
        settings_preloads_name: str = "",
        settings_extensions_name: str = "",
        uncached_imports: Iterable[str] = (),
        lazy_imports: dict[str, str | Callable[[], Any]] | None = None,
        deprecated_lazy_imports: dict[str, DeprecatedImport] | None = None,
        settings_ctx_name: str = "monkay_settings_ctx",
        extensions_applied_ctx_name: str = "monkay_extensions_applied_ctx",
        skip_all_update: bool = False,
        skip_getattr_fixup: bool = False,
        evaluate_settings: None = None,
        ignore_settings_import_errors: None = None,
        pre_add_lazy_import_hook: None | PRE_ADD_LAZY_IMPORT_HOOK = None,
        post_add_lazy_import_hook: None | Callable[[str], None] = None,
        ignore_preload_import_errors: bool = True,
        package: str | None = "",
    ) -> None:
        """
        Initializes a Monkay instance.

        This method sets up the Monkay instance with the provided configurations, including
        instance management, settings loading, lazy imports, and extension handling.

        Args:
            globals_dict: The module's globals dictionary.
            with_instance: If True, enables instance management with a default context variable name.
                           If a string, uses the provided name for the context variable.
            with_extensions: If True, enables extension management with a default context variable name.
                             If a string, uses the provided name for the context variable.
            extension_order_key_fn: An optional function to define the order in which extensions are applied.
            settings_path: The path to the settings object, a callable, a settings instance, or a settings class.
                           If False or None, settings are disabled.
            preloads: An iterable of preload paths to execute before initialization.
            settings_preloads_name: The name used to identify preload settings.
            settings_extensions_name: The name used to identify extension settings.
            uncached_imports: An iterable of import names that should not be cached.
            lazy_imports: A dictionary of lazy imports.
            deprecated_lazy_imports: A dictionary of deprecated lazy imports.
            settings_ctx_name: The name of the settings context variable.
            extensions_applied_ctx_name: The name of the extensions applied context variable.
            skip_all_update: If True, skips updating the `__all__` variable.
            skip_getattr_fixup: If True, skips fixing the missing `__dir__` function.
            evaluate_settings: Deprecated parameter.
            ignore_settings_import_errors: Deprecated parameter.
            pre_add_lazy_import_hook: A hook to modify lazy import definitions before they are added.
            post_add_lazy_import_hook: A hook to execute after a lazy import is added.
            ignore_preload_import_errors: If True, ignores preload import errors.
            package: The package name to use for relative imports.
        """
        self.globals_dict = globals_dict
        if with_instance is True:
            with_instance = "monkay_instance_ctx"
        with_instance = with_instance
        if with_extensions is True:
            with_extensions = "monkay_extensions_ctx"
        with_extensions = with_extensions
        if package == "" and globals_dict.get("__spec__"):
            package = globals_dict["__spec__"].parent
        self.package = package or None

        self._cached_imports: dict[str, Any] = {}
        self.pre_add_lazy_import_hook = pre_add_lazy_import_hook
        self.post_add_lazy_import_hook = post_add_lazy_import_hook
        self.uncached_imports = set(uncached_imports)
        self.lazy_imports = {}
        self.deprecated_lazy_imports = {}
        if lazy_imports:
            for name, lazy_import in lazy_imports.items():
                self.add_lazy_import(name, lazy_import, no_hooks=True)
        if deprecated_lazy_imports:
            for name, deprecated_import in deprecated_lazy_imports.items():
                self.add_deprecated_lazy_import(name, deprecated_import, no_hooks=True)
        if settings_path is not None and settings_path is not False:
            self._settings_var = globals_dict[settings_ctx_name] = ContextVar(
                settings_ctx_name, default=None
            )
            self.settings = settings_path  # type: ignore
        self.settings_preloads_name = settings_preloads_name
        self.settings_extensions_name = settings_extensions_name

        if with_instance:
            self._instance_var = globals_dict[with_instance] = ContextVar(
                with_instance, default=None
            )
        if with_extensions:
            self.extension_order_key_fn = extension_order_key_fn
            self._extensions = {}
            self._extensions_var = globals_dict[with_extensions] = ContextVar(
                with_extensions, default=None
            )
            self._extensions_applied_var = globals_dict[extensions_applied_ctx_name] = ContextVar(
                extensions_applied_ctx_name, default=None
            )
        if not skip_all_update and (self.lazy_imports or self.deprecated_lazy_imports):
            all_var = globals_dict.setdefault("__all__", [])
            globals_dict["__all__"] = self.update_all_var(all_var)
        # fix missing __dir__ in case only __getattr__ was specified and __dir__ not
        # it assumes the __all__ var is correct
        if (
            not skip_getattr_fixup
            and "__all__" in globals_dict
            and "__getattr__" in globals_dict
            and "__dir__" not in globals_dict
        ):
            self._init_global_dir_hook()
        self.evaluate_preloads(preloads, ignore_import_errors=ignore_preload_import_errors)
        if evaluate_settings is not None:
            raise Exception(
                "This feature and the evaluate_settings parameter are removed in monkay 0.3"
            )
        if ignore_settings_import_errors is not None:
            warnings.warn(
                "`ignore_settings_import_errors` parameter is defunct and deprecated. It always behave like it would be False.",
                DeprecationWarning,
                stacklevel=2,
            )

    def clear_caches(self, settings_cache: bool = True, import_cache: bool = True) -> None:
        """
        Clears the settings and import caches.

        This method clears the cached settings and/or import objects, forcing them to be reloaded
        on next access.

        Args:
            settings_cache: If True, clears the settings cache.
            import_cache: If True, clears the import cache.
        """
        if settings_cache:
            del self.settings
        if import_cache:
            self._cached_imports.clear()

    def evaluate_preloads(
        self,
        preloads: Iterable[str],
        *,
        ignore_import_errors: bool = True,
        package: str | None = None,
    ) -> bool:
        """
        Evaluates preload modules or functions specified in settings.

        This method delegates to the `evaluate_preloads` function, using the Monkay instance's
        package if no package is provided.

        Args:
            preloads: An iterable of preload paths, in the format "module" or "module:function".
            ignore_import_errors: If True, ignores import errors and continues processing.
            package: The package name to use as a context for relative imports.

        Returns:
            True if all preloads were successfully evaluated, False otherwise.
        """
        return evaluate_preloads(
            preloads, ignore_import_errors=ignore_import_errors, package=package or self.package
        )

    def _evaluate_settings(
        self,
        *,
        settings: SETTINGS,
        on_conflict: Literal["error", "keep", "replace"],
        ignore_preload_import_errors: bool,
        initial_settings_evaluated: bool,
    ) -> None:
        """
        Internal method to evaluate settings preloads and extensions.

        This method evaluates the preloads and extensions specified in the settings object.

        Args:
            settings: The settings object to evaluate.
            on_conflict: Specifies how to handle conflicts when adding extensions.
            ignore_preload_import_errors: If True, ignores preload import errors.
            initial_settings_evaluated: The initial state of the settings evaluation flag.

        Raises:
            Exception: If an error occurs during evaluation and initial settings evaluation was False.
        """
        self.settings_evaluated = True

        try:
            if self.settings_preloads_name:
                settings_preloads = get_value_from_settings(settings, self.settings_preloads_name)
                self.evaluate_preloads(
                    settings_preloads, ignore_import_errors=ignore_preload_import_errors
                )
            if self.settings_extensions_name:
                for extension in get_value_from_settings(settings, self.settings_extensions_name):
                    self.add_extension(extension, use_overwrite=True, on_conflict=on_conflict)
        except Exception as exc:
            if not initial_settings_evaluated:
                self.settings_evaluated = False
            raise exc

    def evaluate_settings(
        self,
        *,
        on_conflict: Literal["error", "keep", "replace"] = "error",
        ignore_import_errors: bool = False,
        ignore_preload_import_errors: bool = True,
        onetime: bool = True,
    ) -> bool:
        """Evaluate settings-driven preloads and extension registrations.

        This method is the main runtime entry point for applying configuration
        declared in ``settings_preloads_name`` and ``settings_extensions_name``.
        It can be called repeatedly; with ``onetime=True`` it becomes idempotent
        per context once evaluation succeeded.

        Args:
            on_conflict: Conflict policy applied while registering extensions from
                settings. Supported values are ``"error"``, ``"keep"``, and
                ``"replace"``.
            ignore_import_errors: Return ``False`` instead of raising when loading
                settings fails with :class:`ImportError` or
                :class:`~monkay.base.UnsetError`.
            ignore_preload_import_errors: Continue when a settings preload import
                fails.
            onetime: Skip re-evaluation if settings were already evaluated in the
                active context.

        Returns:
            ``True`` when evaluation completed or was skipped due to ``onetime``.
            ``False`` only when ``ignore_import_errors=True`` and settings loading
            failed with an import/unset error.

        Raises:
            ValueError: If ``on_conflict`` is not one of the supported values.
            Exception: Any exception raised while loading settings or applying
                preloads/extensions when not suppressed.

        Examples:
            >>> monkay.evaluate_settings()
            True
            >>> monkay.evaluate_settings(on_conflict="replace", onetime=False)
            True

        Notes:
            When neither ``settings_preloads_name`` nor ``settings_extensions_name``
            is configured, this method marks settings as evaluated without touching
            ``monkay.settings``.
        """
        on_conflict = validate_conflict_mode(on_conflict)
        initial_settings_evaluated = self.settings_evaluated
        if onetime and initial_settings_evaluated:
            return True
        # don't access settings when there is nothing to evaluate
        if not self.settings_preloads_name and not self.settings_extensions_name:
            self.settings_evaluated = True
            return True

        try:
            # load settings one time and before setting settings_evaluated to True
            settings = self.settings
        except Exception as exc:
            if ignore_import_errors and isinstance(exc, UnsetError | ImportError):
                return False
            raise exc
        self._evaluate_settings(
            on_conflict=on_conflict,
            settings=settings,
            ignore_preload_import_errors=ignore_preload_import_errors,
            initial_settings_evaluated=initial_settings_evaluated,
        )
        return True

    def evaluate_settings_once(
        self,
        *,
        on_conflict: Literal["error", "keep", "replace"] = "error",
        ignore_import_errors: bool = True,
    ) -> bool:
        """
        Evaluates settings preloads and extensions once. (Deprecated)

        This method is deprecated and now equivalent to `evaluate_settings(onetime=True)`.

        Args:
            on_conflict: Specifies how to handle conflicts when adding extensions.
            ignore_import_errors: If True, ignores settings import errors.

        Returns:
            True if settings were successfully evaluated, False otherwise.
        """
        warnings.warn(
            "`evaluate_settings_once` is deprecated. Use `evaluate_settings` instead. It has now the same functionality.",
            DeprecationWarning,
            stacklevel=2,
        )
        return self.evaluate_settings(
            on_conflict=on_conflict, ignore_import_errors=ignore_import_errors, onetime=True
        )

    @contextmanager
    def with_full_overwrite(
        self,
        *,
        extensions: dict[str, ExtensionProtocol[INSTANCE, SETTINGS]]
        | None
        | type[Undefined] = Undefined,
        settings: SETTINGS_DEFINITION_TYPE | Literal[False] | type[Undefined] = Undefined,
        instance: INSTANCE | None | type[Undefined] = Undefined,
        apply_extensions: bool = False,
        evaluate_settings_with: EvaluateSettingsParameters | None = None,
    ) -> Generator[None]:
        """
        Apply all overwrites in the correct order. Useful for testing or sub-environments
        """
        ctx_extensions = (
            nullcontext()
            if extensions is Undefined
            else self.with_extensions(
                cast("dict[str, ExtensionProtocol[INSTANCE, SETTINGS]] | None", extensions),
                apply_extensions=False,
            )
        )
        ctx_settings = (
            nullcontext()
            if settings is Undefined
            else self.with_settings(
                cast("SETTINGS_DEFINITION_TYPE | Literal[False]", settings),
                evaluate_settings_with=None,
            )
        )
        ctx_instance = (
            nullcontext()
            if instance is Undefined
            else self.with_instance(
                cast("INSTANCE | None", instance),
                apply_extensions=False,
            )
        )

        with (
            ctx_extensions,
            ctx_settings,
            ctx_instance,
        ):
            # evaluate here because the ctxmanagers have not the information from the contextvars
            # evaluate settings also without settings instance
            if evaluate_settings_with is not None and evaluate_settings_with is not False:
                if evaluate_settings_with is True:
                    evaluate_settings_with = {}
                self.evaluate_settings(**evaluate_settings_with)
            # apply extensions also without extra instance
            if apply_extensions and self._extensions_var is not None:
                self.apply_extensions()
            yield

getter class-attribute instance-attribute

getter = None

The getter function used for attribute access, initialized with lazy import logic. This function handles both regular attribute access and the resolution of lazy imports.

dir_fn class-attribute instance-attribute

dir_fn = None

The directory listing function, enhanced to include lazy imports. This function is used to generate the list of attributes available in the module.

settings_evaluated property writable

settings_evaluated

Checks if the settings have been evaluated and loaded.

This property returns a boolean indicating whether the settings have been evaluated. It checks both the global settings evaluation flag and the context-specific flag.

Returns:

Type Description
bool

True if the settings have been evaluated, False otherwise.

Raises:

Type Description
AssertionError

If Monkay is not enabled for settings.

instance property

instance

Retrieves the current instance managed by Monkay.

This property returns the instance associated with the Monkay object. It first checks if a context-specific instance is set via _instance_var. If not, it returns the default instance _instance.

Returns:

Type Description
INSTANCE | None

The current instance, or None if no instance is set.

Raises:

Type Description
AssertionError

If Monkay is not enabled for instances.

globals_dict instance-attribute

globals_dict = globals_dict

package instance-attribute

package = package or None

pre_add_lazy_import_hook instance-attribute

pre_add_lazy_import_hook = pre_add_lazy_import_hook

post_add_lazy_import_hook instance-attribute

post_add_lazy_import_hook = post_add_lazy_import_hook

uncached_imports instance-attribute

uncached_imports = set(uncached_imports)

lazy_imports instance-attribute

lazy_imports = {}

deprecated_lazy_imports instance-attribute

deprecated_lazy_imports = {}

settings instance-attribute

settings = settings_path

settings_preloads_name instance-attribute

settings_preloads_name = settings_preloads_name

settings_extensions_name instance-attribute

settings_extensions_name = settings_extensions_name

extension_order_key_fn instance-attribute

extension_order_key_fn = extension_order_key_fn

find_missing

find_missing(*, all_var=True, search_pathes=None, ignore_deprecated_import_errors=False, require_search_path_all_var=True)

Debug method to check for missing imports and inconsistencies in exports.

This method performs a comprehensive check for missing attributes, imports, and inconsistencies in the module's exports, including lazy and deprecated imports. It compares the defined exports with the module's __all__ variable and optional search paths to identify potential issues.

Parameters:

Name Type Description Default
all_var bool | Collection[str]

If True, checks against the module's __all__ variable. If a collection, checks against the provided names. If False, skips checking against __all__.

True
search_pathes None | Collection[str]

Optional list of module paths to search for additional exports.

None
ignore_deprecated_import_errors bool

If True, ignores import errors for deprecated lazy imports.

False
require_search_path_all_var bool

If True, requires __all__ to be defined in searched modules.

True

Returns:

Type Description
dict[str, set[Literal['not_in_all_var', 'missing_attr', 'missing_all_var', 'import', 'shadowed', 'search_path_extra', 'search_path_import']]]

A dictionary where keys are names of missing or inconsistent items,

dict[str, set[Literal['not_in_all_var', 'missing_attr', 'missing_all_var', 'import', 'shadowed', 'search_path_extra', 'search_path_import']]]

and values are sets of strings indicating the types of issues.

Issue types
  • "not_in_all_var": Export is defined but not in __all__.
  • "missing_attr": Attribute is missing from the module or a searched module.
  • "missing_all_var": __all__ is missing from the module or a searched module.
  • "import": Import error occurred while resolving a lazy or deprecated import.
  • "shadowed": Attribute is shadowed by a real variable in the module's globals.
  • "search_path_extra": Export from a search path is not found in the module's exports.
  • "search_path_import": Import error occurred while importing a search path.
Source code in monkay/_monkay_exports.py
def find_missing(
    self,
    *,
    all_var: bool | Collection[str] = True,
    search_pathes: None | Collection[str] = None,
    ignore_deprecated_import_errors: bool = False,
    require_search_path_all_var: bool = True,
) -> dict[
    str,
    set[
        Literal[
            "not_in_all_var",
            "missing_attr",
            "missing_all_var",
            "import",
            "shadowed",
            "search_path_extra",
            "search_path_import",
        ]
    ],
]:
    """
    Debug method to check for missing imports and inconsistencies in exports.

    This method performs a comprehensive check for missing attributes, imports,
    and inconsistencies in the module's exports, including lazy and deprecated imports.
    It compares the defined exports with the module's `__all__` variable and optional
    search paths to identify potential issues.

    Args:
        all_var: If True, checks against the module's `__all__` variable.
                 If a collection, checks against the provided names.
                 If False, skips checking against `__all__`.
        search_pathes: Optional list of module paths to search for additional exports.
        ignore_deprecated_import_errors: If True, ignores import errors for deprecated lazy imports.
        require_search_path_all_var: If True, requires `__all__` to be defined in searched modules.

    Returns:
        A dictionary where keys are names of missing or inconsistent items,
        and values are sets of strings indicating the types of issues.

    Issue types:
        - "not_in_all_var": Export is defined but not in `__all__`.
        - "missing_attr": Attribute is missing from the module or a searched module.
        - "missing_all_var": `__all__` is missing from the module or a searched module.
        - "import": Import error occurred while resolving a lazy or deprecated import.
        - "shadowed": Attribute is shadowed by a real variable in the module's globals.
        - "search_path_extra": Export from a search path is not found in the module's exports.
        - "search_path_import": Import error occurred while importing a search path.
    """
    self._init_global_getter_hook()

    assert self.getter is not None
    missing: dict[
        str,
        set[
            Literal[
                "not_in_all_var",
                "missing_attr",
                "missing_all_var",
                "import",
                "shadowed",
                "search_path_extra",
                "search_path_import",
            ]
        ],
    ] = {}
    if all_var is True:
        try:
            all_var = self.getter("__all__", check_globals_dict=True)
        except AttributeError:
            missing.setdefault(self.globals_dict["__spec__"].name, set()).add(
                "missing_all_var"
            )
            all_var = []
    key_set = set(chain(self.lazy_imports.keys(), self.deprecated_lazy_imports.keys()))
    value_pathes_set: set[str] = set()
    for name in key_set:
        found_path: str = ""
        if name in self.lazy_imports and isinstance(self.lazy_imports[name], str):
            found_path = cast(str, self.lazy_imports[name]).replace(":", ".")
        elif name in self.deprecated_lazy_imports and isinstance(
            self.deprecated_lazy_imports[name]["path"], str
        ):
            found_path = cast(str, self.deprecated_lazy_imports[name]["path"]).replace(
                ":", "."
            )
        if found_path:
            value_pathes_set.add(absolutify_import(found_path, self.package))
        try:
            obj = self.getter(name, no_warn_deprecated=True, check_globals_dict="fail")
            # also add maybe rexported path
            value_pathes_set.add(_obj_to_full_name(obj))
        except InGlobalsDict:
            missing.setdefault(name, set()).add("shadowed")
        except ImportError:
            if not ignore_deprecated_import_errors or name not in self.deprecated_lazy_imports:
                missing.setdefault(name, set()).add("import")
    if all_var is not False:
        for export_name in cast(Collection[str], all_var):
            try:
                obj = self.getter(
                    export_name, no_warn_deprecated=True, check_globals_dict=True
                )
            except AttributeError:
                missing.setdefault(export_name, set()).add("missing_attr")
                continue
            if export_name not in key_set:
                value_pathes_set.add(_obj_to_full_name(obj))

    if search_pathes:
        for search_path in search_pathes:
            try:
                mod = import_module(search_path, self.package)
            except ImportError:
                missing.setdefault(search_path, set()).add("search_path_import")
                continue
            try:
                all_var_search = mod.__all__
            except AttributeError:
                if require_search_path_all_var:
                    missing.setdefault(search_path, set()).add("missing_all_var")

                continue
            for export_name in all_var_search:
                export_path = absolutify_import(f"{search_path}.{export_name}", self.package)
                try:
                    # for re-exports
                    obj = getattr(mod, export_name)
                except AttributeError:
                    missing.setdefault(export_path, set()).add("missing_attr")
                    # still check check the export path
                    if export_path not in value_pathes_set:
                        missing.setdefault(export_path, set()).add("search_path_extra")
                    continue
                if (
                    export_path not in value_pathes_set
                    and _obj_to_full_name(obj) not in value_pathes_set
                ):
                    missing.setdefault(export_path, set()).add("search_path_extra")

    if all_var is not False:
        for name in key_set.difference(cast(Collection[str], all_var)):
            missing.setdefault(name, set()).add("not_in_all_var")

    return missing

add_lazy_import

add_lazy_import(name, value, *, no_hooks=False)

Adds a lazy import to the module.

This method adds a lazy import, which is resolved only when the attribute is accessed. This can improve module loading performance by deferring imports.

Parameters:

Name Type Description Default
name str

The name of the lazy import.

required
value str | Callable[[], Any]

The import path as a string or a callable that returns the imported object.

required
no_hooks bool

If True, skips the pre and post add hooks.

False

Raises:

Type Description
KeyError

If the name is already a lazy or deprecated lazy import.

Source code in monkay/_monkay_exports.py
def add_lazy_import(
    self, name: str, value: str | Callable[[], Any], *, no_hooks: bool = False
) -> None:
    """
    Adds a lazy import to the module.

    This method adds a lazy import, which is resolved only when the attribute is accessed.
    This can improve module loading performance by deferring imports.

    Args:
        name: The name of the lazy import.
        value: The import path as a string or a callable that returns the imported object.
        no_hooks: If True, skips the pre and post add hooks.

    Raises:
        KeyError: If the name is already a lazy or deprecated lazy import.
    """
    if not no_hooks and self.pre_add_lazy_import_hook is not None:
        name, value = self.pre_add_lazy_import_hook(name, value, "lazy_import")
    if name in self.lazy_imports:
        raise KeyError(f'"{name}" is already a lazy import')
    if name in self.deprecated_lazy_imports:
        raise KeyError(f'"{name}" is already a deprecated lazy import')
    self._init_global_getter_hook()
    self._init_global_dir_hook()
    self.lazy_imports[name] = value
    if not no_hooks and self.post_add_lazy_import_hook is not None:
        self.post_add_lazy_import_hook(name)

add_deprecated_lazy_import

add_deprecated_lazy_import(name, value, *, no_hooks=False)

Adds a deprecated lazy import to the module.

This method adds a lazy import that is marked as deprecated. When accessed, it will issue a deprecation warning.

Parameters:

Name Type Description Default
name str

The name of the deprecated import.

required
value DeprecatedImport

A dictionary containing details about the deprecation, including the import path.

required
no_hooks bool

If True, skips the pre and post add hooks.

False

Raises:

Type Description
KeyError

If the name is already a lazy or deprecated lazy import.

Source code in monkay/_monkay_exports.py
def add_deprecated_lazy_import(
    self, name: str, value: DeprecatedImport, *, no_hooks: bool = False
) -> None:
    """
    Adds a deprecated lazy import to the module.

    This method adds a lazy import that is marked as deprecated. When accessed, it will
    issue a deprecation warning.

    Args:
        name: The name of the deprecated import.
        value: A dictionary containing details about the deprecation, including the import path.
        no_hooks: If True, skips the pre and post add hooks.

    Raises:
        KeyError: If the name is already a lazy or deprecated lazy import.
    """
    if not no_hooks and self.pre_add_lazy_import_hook is not None:
        name, value = self.pre_add_lazy_import_hook(name, value, "deprecated_lazy_import")
    if name in self.lazy_imports:
        raise KeyError(f'"{name}" is already a lazy import')
    if name in self.deprecated_lazy_imports:
        raise KeyError(f'"{name}" is already a deprecated lazy import')
    self._init_global_getter_hook()
    self._init_global_dir_hook()
    self.deprecated_lazy_imports[name] = value
    if not no_hooks and self.post_add_lazy_import_hook is not None:
        self.post_add_lazy_import_hook(name)

sorted_exports

sorted_exports(all_var=None, *, separate_by_category=True, sort_by='path')

Returns a sorted list of module exports, categorized and sorted as specified.

This method generates a list of SortedExportsEntry objects, which represent the module's exports. It categorizes exports as "lazy_import", "deprecated_lazy_import", or "other", and sorts them based on the specified criteria.

Parameters:

Name Type Description Default
all_var Collection[str] | None

An optional collection of export names. If None, uses the module's __all__ variable.

None
separate_by_category bool

If True, sorts exports by category first, then by the specified sort_by attribute.

True
sort_by Literal['export_name', 'path']

The attribute to sort by, either "export_name" or "path".

'path'

Returns:

Type Description
list[SortedExportsEntry]

A list of SortedExportsEntry objects.

Source code in monkay/_monkay_exports.py
def sorted_exports(
    self,
    all_var: Collection[str] | None = None,
    *,
    separate_by_category: bool = True,
    sort_by: Literal["export_name", "path"] = "path",
) -> list[SortedExportsEntry]:
    """
    Returns a sorted list of module exports, categorized and sorted as specified.

    This method generates a list of `SortedExportsEntry` objects, which represent the module's exports.
    It categorizes exports as "lazy_import", "deprecated_lazy_import", or "other", and sorts them
    based on the specified criteria.

    Args:
        all_var: An optional collection of export names. If None, uses the module's `__all__` variable.
        separate_by_category: If True, sorts exports by category first, then by the specified `sort_by` attribute.
        sort_by: The attribute to sort by, either "export_name" or "path".

    Returns:
        A list of `SortedExportsEntry` objects.
    """
    if all_var is None:
        all_var = self.globals_dict.get("__all__", _empty)
    sorted_exports: list[SortedExportsEntry] = []
    # ensure all entries are only returned once
    for name in set(all_var):
        if name in self.lazy_imports:
            sorted_exports.append(
                SortedExportsEntry(
                    "lazy_import",
                    name,
                    cast(
                        str,
                        self.lazy_imports[name]
                        if isinstance(self.lazy_imports[name], str)
                        else f"{self.globals_dict['__spec__'].name}.{name}",
                    ),
                )
            )
        elif name in self.deprecated_lazy_imports:
            sorted_exports.append(
                SortedExportsEntry(
                    "deprecated_lazy_import",
                    name,
                    cast(
                        str,
                        self.deprecated_lazy_imports[name]["path"]
                        if isinstance(self.deprecated_lazy_imports[name]["path"], str)
                        else f"{self.globals_dict['__spec__'].name}.{name}",
                    ),
                )
            )
        else:
            sorted_exports.append(
                SortedExportsEntry(
                    "other",
                    name,
                    f"{self.globals_dict['__spec__'].name}.{name}",
                )
            )
    if separate_by_category:

        def key_fn(ordertuple: SortedExportsEntry) -> tuple:
            return ordertuple.category, getattr(ordertuple, sort_by)
    else:

        def key_fn(ordertuple: SortedExportsEntry) -> tuple:
            return (getattr(ordertuple, sort_by),)

    sorted_exports.sort(key=key_fn)
    return sorted_exports

module_dir_fn

module_dir_fn(*, chained_dir_fn=None)

Generates a directory listing for the module, including lazy and deprecated imports.

This method combines the module's __all__ variable, lazy imports, deprecated lazy imports, and optionally the results of a chained directory listing function to create a comprehensive list of attributes.

Parameters:

Name Type Description Default
chained_dir_fn Callable[[], list[str]] | None

An optional function that returns a list of attribute names, used to extend the directory listing.

None

Returns:

Type Description
list[str]

A list of attribute names representing the module's directory.

Source code in monkay/_monkay_exports.py
def module_dir_fn(
    self,
    *,
    chained_dir_fn: Callable[[], list[str]] | None = None,
) -> list[str]:
    """
    Generates a directory listing for the module, including lazy and deprecated imports.

    This method combines the module's `__all__` variable, lazy imports, deprecated lazy imports,
    and optionally the results of a chained directory listing function to create a comprehensive
    list of attributes.

    Args:
        chained_dir_fn: An optional function that returns a list of attribute names,
                          used to extend the directory listing.

    Returns:
        A list of attribute names representing the module's directory.
    """
    baseset = set(self.globals_dict.get("__all__", None) or _empty)
    baseset.update(self.lazy_imports.keys())
    baseset.update(self.deprecated_lazy_imports.keys())
    if chained_dir_fn is None:
        baseset.update(self.globals_dict.keys())
    else:
        baseset.update(chained_dir_fn())
    return list(baseset)

module_getter

module_getter(key, *, chained_getter=_stub_previous_getattr, no_warn_deprecated=False, check_globals_dict=False)

Module Getter which handles lazy imports.

This method acts as a custom attribute getter for the module, handling lazy imports and deprecated attributes. It first checks if the attribute exists in the module's globals dictionary. If not, it checks for lazy or deprecated lazy imports. If found, it resolves and returns the imported object.

Parameters:

Name Type Description Default
key str

The name of the attribute to retrieve.

required
chained_getter Callable[[str], Any]

A fallback getter function to call if the attribute is not found in lazy imports.

_stub_previous_getattr
no_warn_deprecated bool

If True, suppresses deprecation warnings for deprecated attributes.

False
check_globals_dict bool | Literal['fail']

If True, checks the module's globals dictionary first. If "fail", raises InGlobalsDict if found.

False

Returns:

Type Description
Any

The retrieved attribute value.

Raises:

Type Description
InglobalsDict

If check_globals_dict is "fail" and the attribute is found in globals.

DeprecationWarning

If a deprecated attribute is accessed and no_warn_deprecated is False.

Source code in monkay/_monkay_exports.py
def module_getter(
    self,
    key: str,
    *,
    chained_getter: Callable[[str], Any] = _stub_previous_getattr,
    no_warn_deprecated: bool = False,
    check_globals_dict: bool | Literal["fail"] = False,
) -> Any:
    """
    Module Getter which handles lazy imports.

    This method acts as a custom attribute getter for the module, handling lazy imports and deprecated attributes.
    It first checks if the attribute exists in the module's globals dictionary. If not, it checks for lazy or
    deprecated lazy imports. If found, it resolves and returns the imported object.

    Args:
        key: The name of the attribute to retrieve.
        chained_getter: A fallback getter function to call if the attribute is not found in lazy imports.
        no_warn_deprecated: If True, suppresses deprecation warnings for deprecated attributes.
        check_globals_dict: If True, checks the module's globals dictionary first. If "fail", raises InGlobalsDict if found.

    Returns:
        The retrieved attribute value.

    Raises:
        InglobalsDict: If `check_globals_dict` is "fail" and the attribute is found in globals.
        DeprecationWarning: If a deprecated attribute is accessed and `no_warn_deprecated` is False.
    """
    if check_globals_dict and key in self.globals_dict:
        if check_globals_dict == "fail":
            raise InGlobalsDict(f'"{key}" is defined as real variable.')
        return self.globals_dict[key]
    lazy_import = self.lazy_imports.get(key)
    if lazy_import is None:
        deprecated = self.deprecated_lazy_imports.get(key)
        if deprecated is not None:
            lazy_import = deprecated["path"]
            if not no_warn_deprecated:
                warn_strs = [f'Attribute: "{key}" is deprecated.']
                if deprecated.get("reason"):
                    # Note: no dot is added, this is the responsibility of the reason author.
                    warn_strs.append(f"Reason: {deprecated['reason']}")
                if deprecated.get("new_attribute"):
                    warn_strs.append(f'Use "{deprecated["new_attribute"]}" instead.')
                warnings.warn("\n".join(warn_strs), DeprecationWarning, stacklevel=2)

    if lazy_import is None:
        return chained_getter(key)
    if key not in self._cached_imports or key in self.uncached_imports:
        if callable(lazy_import):
            value: Any = lazy_import()
        else:
            value = load(lazy_import, package=self.package)
        if key in self.uncached_imports:
            return value
        else:
            self._cached_imports[key] = value
    return self._cached_imports[key]

update_all_var

update_all_var(all_var)

Updates the __all__ variable to include lazy and deprecated lazy imports.

This method ensures that all names defined as lazy or deprecated lazy imports are included in the module's __all__ variable.

Parameters:

Name Type Description Default
all_var Collection[str]

The current __all__ variable as a collection of strings.

required

Returns:

Type Description
list[str] | set[str]

The updated __all__ variable, either as a list or a set, depending on the input type.

Source code in monkay/_monkay_exports.py
def update_all_var(self, all_var: Collection[str]) -> list[str] | set[str]:
    """
    Updates the `__all__` variable to include lazy and deprecated lazy imports.

    This method ensures that all names defined as lazy or deprecated lazy imports
    are included in the module's `__all__` variable.

    Args:
        all_var: The current `__all__` variable as a collection of strings.

    Returns:
        The updated `__all__` variable, either as a list or a set, depending on the input type.
    """
    if isinstance(all_var, set):
        all_var_set = all_var
    else:
        if not isinstance(all_var, list):
            all_var = list(all_var)
        all_var_set = set(all_var)

    if self.lazy_imports or self.deprecated_lazy_imports:
        for var in chain(
            self.lazy_imports,
            self.deprecated_lazy_imports,
        ):
            if var not in all_var_set:
                if isinstance(all_var, list):
                    all_var.append(var)
                else:
                    cast(set[str], all_var).add(var)

    return cast("list[str] | set[str]", all_var)

with_settings

with_settings(settings, *, evaluate_settings_with=None)

Temporarily sets and yields new settings for the Monkay instance within a context.

This context manager allows temporarily overriding the settings associated with the Monkay instance. It yields the provided settings and then restores the original settings after the context exits.

Parameters:

Name Type Description Default
settings SETTINGS_DEFINITION_TYPE[SETTINGS] | None | Literal[False]

The new settings to use within the context, or None to temporarily use the real settings. Use False, "" to disable settings access temporarily

required
evaluate_settings_with EvaluateSettingsParameters | None

Evaluate settings with the parameters provided.

None

Yields:

Type Description
Generator[SETTINGS_DEFINITION_TYPE[SETTINGS] | Literal[False] | None]

The provided settings.

Raises:

Type Description
AssertionError

If Monkay is not enabled for settings.

Source code in monkay/_monkay_settings.py
@contextmanager
def with_settings(
    self,
    settings: SETTINGS_DEFINITION_TYPE[SETTINGS] | None | Literal[False],
    *,
    evaluate_settings_with: EvaluateSettingsParameters | None = None,
) -> Generator[SETTINGS_DEFINITION_TYPE[SETTINGS] | Literal[False] | None]:
    """
    Temporarily sets and yields new settings for the Monkay instance within a context.

    This context manager allows temporarily overriding the settings associated with the Monkay instance.
    It yields the provided settings and then restores the original settings after the context exits.

    Args:
        settings: The new settings to use within the context, or None to temporarily use the real settings. Use False, "" to disable settings access temporarily
        evaluate_settings_with: Evaluate settings with the parameters provided.

    Yields:
        The provided settings.

    Raises:
        AssertionError: If Monkay is not enabled for settings.
    """
    assert self._settings_var is not None, settings_not_enabled_error
    # why None, for temporary using the real settings
    token = self._settings_var.set(
        (["" if settings is False else settings], [False]) if settings is not None else None
    )
    try:
        if evaluate_settings_with is not None:
            self.evaluate_settings(**evaluate_settings_with)
        yield settings
    finally:
        self._settings_var.reset(token)

apply_extensions

apply_extensions(*, use_overwrite=True)

Applies registered extensions to the Monkay instance.

This method iterates through the registered extensions, applies them in the specified order, and manages the application process to prevent recursive or concurrent application issues.

Parameters:

Name Type Description Default
use_overwrite bool

If True, uses the extensions from the _extensions_var if available; otherwise, uses the default _extensions.

True

Raises: AssertionError: If Monkay is not enabled for extensions. RuntimeError: If another extension application process is already active in the same context.

Source code in monkay/_monkay_extensions.py
def apply_extensions(self, *, use_overwrite: bool = True) -> None:
    """
    Applies registered extensions to the Monkay instance.

    This method iterates through the registered extensions, applies them in the specified order,
    and manages the application process to prevent recursive or concurrent application issues.

    Args:
        use_overwrite: If True, uses the extensions from the `_extensions_var` if available;
                       otherwise, uses the default `_extensions`.
    Raises:
        AssertionError: If Monkay is not enabled for extensions.
        RuntimeError: If another extension application process is already active in the same context.
    """
    assert self._extensions_var is not None, extensions_not_enabled_error
    extensions: dict[str, ExtensionProtocol[INSTANCE, SETTINGS]] | None = (
        self._extensions_var.get() if use_overwrite else None
    )
    if extensions is None:
        extensions = self._extensions
    extensions_applied = self._extensions_applied_var.get()
    if extensions_applied is not None:
        raise RuntimeError("Other apply process in the same context is active.")
    extensions_ordered: Iterable[tuple[str, ExtensionProtocol[INSTANCE, SETTINGS]]] = cast(
        dict[str, ExtensionProtocol[INSTANCE, SETTINGS]], extensions
    ).items()

    if self.extension_order_key_fn is not None:
        extensions_ordered = sorted(
            extensions_ordered,
            key=lambda entry: self.extension_order_key_fn(entry[1]),
        )
    extensions_applied = set()
    token = self._extensions_applied_var.set(extensions_applied)
    try:
        for name, extension in extensions_ordered:
            if name in extensions_applied:
                continue
            # despite slightly inaccurate (added before applying actually) this ensures that no loops appear
            extensions_applied.add(name)
            extension.apply(cast("Monkay[INSTANCE, SETTINGS]", self))
    finally:
        self._extensions_applied_var.reset(token)

ensure_extension

ensure_extension(name_or_extension)

Ensures that a specific extension is applied to the Monkay instance.

This method checks if the given extension (either by name or instance) is already applied. If not, it applies the extension, preventing recursive application issues.

Parameters:

Name Type Description Default
name_or_extension str | ExtensionProtocol[INSTANCE, SETTINGS]

The name of the extension or an instance of the extension.

required

Raises:

Type Description
AssertionError

If Monkay is not enabled for extensions or if applying extensions is not active.

RuntimeError

If the provided extension does not implement the ExtensionProtocol, or if the extension does not exist.

Source code in monkay/_monkay_extensions.py
def ensure_extension(
    self, name_or_extension: str | ExtensionProtocol[INSTANCE, SETTINGS]
) -> None:
    """
    Ensures that a specific extension is applied to the Monkay instance.

    This method checks if the given extension (either by name or instance) is already applied.
    If not, it applies the extension, preventing recursive application issues.

    Args:
        name_or_extension: The name of the extension or an instance of the extension.

    Raises:
        AssertionError: If Monkay is not enabled for extensions or if applying extensions is not active.
        RuntimeError: If the provided extension does not implement the ExtensionProtocol,
                      or if the extension does not exist.
    """
    assert self._extensions_var is not None, extensions_not_enabled_error
    extensions_applied = self._extensions_applied_var.get()
    assert extensions_applied is not None, "Applying extensions not active."
    extensions: dict[str, ExtensionProtocol[INSTANCE, SETTINGS]] | None = (
        self._extensions_var.get()
    )
    if extensions is None:
        extensions = self._extensions
    if isinstance(name_or_extension, str):
        name = name_or_extension
        extension = extensions.get(name)
    elif not isclass(name_or_extension) and isinstance(name_or_extension, ExtensionProtocol):
        name = name_or_extension.name
        extension = extensions.get(name, name_or_extension)
    else:
        raise RuntimeError(
            'Provided extension "{name_or_extension}" does not implement the ExtensionProtocol'
        )
    if name in extensions_applied:
        return

    if extension is None:
        raise RuntimeError(f'Extension: "{name}" does not exist.')
    # despite slightly inaccurate (added before applying actually) this ensures that no loops appear
    extensions_applied.add(name)
    extension.apply(cast("Monkay[INSTANCE, SETTINGS]", self))

add_extension

add_extension(extension, *, use_overwrite=True, on_conflict='error')

Register an extension on the active extension registry.

The method accepts an already-instantiated extension, an extension class, or a zero-argument factory. The input is normalized to an extension instance and then inserted into the currently active registry (context override or base registry).

Conflict handling is explicit and validated at runtime: "error" raises, "keep" preserves the existing extension, and "replace" overwrites the existing one.

Parameters:

Name Type Description Default
extension ExtensionProtocol[INSTANCE, SETTINGS] | type[ExtensionProtocol[INSTANCE, SETTINGS]] | Callable[[], ExtensionProtocol[INSTANCE, SETTINGS]]

Extension instance, extension class, or factory callable.

required
use_overwrite bool

Use the active with_extensions override registry when available. If False, always target the base registry.

True
on_conflict Literal['error', 'keep', 'replace']

Strategy used when an extension with the same name is already present. Supported values are "error", "keep", and "replace".

'error'

Raises:

Type Description
AssertionError

If extensions support is not enabled.

ValueError

If extension is incompatible with :class:~monkay.types.ExtensionProtocol or if on_conflict is not supported.

KeyError

If the extension name already exists and on_conflict is "error".

Examples:

>>> monkay.add_extension(MyExtension())
>>> monkay.add_extension(MyExtension, on_conflict="replace")
Notes

on_conflict is validated even when no name collision happens. This keeps behavior deterministic across input data sets.

Source code in monkay/_monkay_extensions.py
def add_extension(
    self,
    extension: ExtensionProtocol[INSTANCE, SETTINGS]
    | type[ExtensionProtocol[INSTANCE, SETTINGS]]
    | Callable[[], ExtensionProtocol[INSTANCE, SETTINGS]],
    *,
    use_overwrite: bool = True,
    on_conflict: Literal["error", "keep", "replace"] = "error",
) -> None:
    """Register an extension on the active extension registry.

    The method accepts an already-instantiated extension, an extension class,
    or a zero-argument factory. The input is normalized to an extension
    instance and then inserted into the currently active registry (context
    override or base registry).

    Conflict handling is explicit and validated at runtime:
    ``"error"`` raises, ``"keep"`` preserves the existing extension,
    and ``"replace"`` overwrites the existing one.

    Args:
        extension: Extension instance, extension class, or factory callable.
        use_overwrite: Use the active ``with_extensions`` override registry
            when available. If ``False``, always target the base registry.
        on_conflict: Strategy used when an extension with the same name is
            already present. Supported values are ``"error"``,
            ``"keep"``, and ``"replace"``.

    Raises:
        AssertionError: If extensions support is not enabled.
        ValueError: If ``extension`` is incompatible with
            :class:`~monkay.types.ExtensionProtocol` or if ``on_conflict`` is
            not supported.
        KeyError: If the extension name already exists and ``on_conflict`` is
            ``"error"``.

    Examples:
        >>> monkay.add_extension(MyExtension())
        >>> monkay.add_extension(MyExtension, on_conflict="replace")

    Notes:
        ``on_conflict`` is validated even when no name collision happens.
        This keeps behavior deterministic across input data sets.
    """
    assert self._extensions_var is not None, extensions_not_enabled_error
    on_conflict = validate_conflict_mode(on_conflict)
    extensions: dict[str, ExtensionProtocol[INSTANCE, SETTINGS]] | None = (
        self._extensions_var.get() if use_overwrite else None
    )
    if extensions is None:
        extensions = self._extensions
    if callable(extension) or isclass(extension):
        extension = extension()
    if not isinstance(extension, ExtensionProtocol):
        raise ValueError(f"Extension {extension} is not compatible")
    if extension.name in extensions:
        if on_conflict == "error":
            raise KeyError(f'Extension "{extension.name}" already exists.')
        elif on_conflict == "keep":
            return
    extensions[extension.name] = extension

with_extensions

with_extensions(extensions, *, apply_extensions=False)

Temporarily sets and yields a new set of extensions for the Monkay instance.

This method allows temporarily overriding the registered extensions within a context. It yields the provided extensions (or None to temporarily use the real extensions), and then restores the original extensions after the context exits.

Parameters:

Name Type Description Default
extensions dict[str, ExtensionProtocol[INSTANCE, SETTINGS]] | None

The new set of extensions to use within the context, or None to temporarily use the real extensions.

required
apply_extensions bool

If True, applies the temporary extensions immediately after setting them.

False

Yields:

Type Description
Generator[dict[str, ExtensionProtocol[INSTANCE, SETTINGS]] | None]

The provided extensions (or None).

Raises:

Type Description
AssertionError

If Monkay is not enabled for extensions.

Source code in monkay/_monkay_extensions.py
@contextmanager
def with_extensions(
    self,
    extensions: dict[str, ExtensionProtocol[INSTANCE, SETTINGS]] | None,
    *,
    apply_extensions: bool = False,
) -> Generator[dict[str, ExtensionProtocol[INSTANCE, SETTINGS]] | None]:
    """
    Temporarily sets and yields a new set of extensions for the Monkay instance.

    This method allows temporarily overriding the registered extensions within a context.
    It yields the provided extensions (or None to temporarily use the real extensions),
    and then restores the original extensions after the context exits.

    Args:
        extensions: The new set of extensions to use within the context, or None to temporarily use the real extensions.
        apply_extensions: If True, applies the temporary extensions immediately after setting them.

    Yields:
        The provided extensions (or None).

    Raises:
        AssertionError: If Monkay is not enabled for extensions.
    """
    # why None, for temporary using the real extensions
    assert self._extensions_var is not None, extensions_not_enabled_error
    token = self._extensions_var.set(extensions)
    try:
        if apply_extensions and self.instance is not None:
            self.apply_extensions()
        yield extensions
    finally:
        self._extensions_var.reset(token)

set_instance

set_instance(instance, *, apply_extensions=True, use_extensions_overwrite=True)

Sets the instance managed by Monkay and optionally applies extensions.

This method updates the instance associated with the Monkay object. It also allows applying extensions immediately after setting the instance.

Parameters:

Name Type Description Default
instance INSTANCE | None

The new instance to set, or None to unset the instance.

required
apply_extensions bool

If True, applies the registered extensions after setting the instance.

True
use_extensions_overwrite bool

If True, uses the extensions from the _extensions_var if available; otherwise, uses the default _extensions.

True

Returns:

Type Description
INSTANCE | None

The set instance.

Raises:

Type Description
AssertionError

If Monkay is not enabled for instances.

RuntimeError

If another extension application process is already active in the same context.

Source code in monkay/_monkay_instance.py
def set_instance(
    self,
    instance: INSTANCE | None,
    *,
    apply_extensions: bool = True,
    use_extensions_overwrite: bool = True,
) -> INSTANCE | None:
    """
    Sets the instance managed by Monkay and optionally applies extensions.

    This method updates the instance associated with the Monkay object. It also allows applying extensions
    immediately after setting the instance.

    Args:
        instance: The new instance to set, or None to unset the instance.
        apply_extensions: If True, applies the registered extensions after setting the instance.
        use_extensions_overwrite: If True, uses the extensions from the `_extensions_var` if available;
                                   otherwise, uses the default `_extensions`.

    Returns:
        The set instance.

    Raises:
        AssertionError: If Monkay is not enabled for instances.
        RuntimeError: If another extension application process is already active in the same context.
    """
    assert self._instance_var is not None, instance_not_enabled_error
    # need to address before the instance is swapped
    if (
        apply_extensions
        and self._extensions_var is not None
        and self._extensions_applied_var.get() is not None
    ):
        raise RuntimeError("Other apply process in the same context is active.")
    self._instance = instance
    if apply_extensions and instance is not None and self._extensions_var is not None:
        # unapply a potential instance overwrite
        with self.with_instance(None):
            self.apply_extensions(use_overwrite=use_extensions_overwrite)
    return instance

with_instance

with_instance(instance, *, apply_extensions=False, use_extensions_overwrite=True)

Temporarily sets and yields a new instance for the Monkay object within a context.

This context manager allows temporarily overriding the instance associated with the Monkay object. It yields the provided instance and then restores the original instance after the context exits.

Parameters:

Name Type Description Default
instance INSTANCE | None

The new instance to use within the context, or None to temporarily unset the instance.

required
apply_extensions bool

If True, applies the registered extensions after setting the instance.

False
use_extensions_overwrite bool

If True, uses the extensions from the _extensions_var if available; otherwise, uses the default _extensions.

True

Yields:

Type Description
Generator[INSTANCE | None]

The provided instance.

Raises:

Type Description
AssertionError

If Monkay is not enabled for instances.

RuntimeError

If another extension application process is already active in the same context.

Source code in monkay/_monkay_instance.py
@contextmanager
def with_instance(
    self,
    instance: INSTANCE | None,
    *,
    apply_extensions: bool = False,
    use_extensions_overwrite: bool = True,
) -> Generator[INSTANCE | None]:
    """
    Temporarily sets and yields a new instance for the Monkay object within a context.

    This context manager allows temporarily overriding the instance associated with the Monkay object.
    It yields the provided instance and then restores the original instance after the context exits.

    Args:
        instance: The new instance to use within the context, or None to temporarily unset the instance.
        apply_extensions: If True, applies the registered extensions after setting the instance.
        use_extensions_overwrite: If True, uses the extensions from the `_extensions_var` if available;
                                   otherwise, uses the default `_extensions`.

    Yields:
        The provided instance.

    Raises:
        AssertionError: If Monkay is not enabled for instances.
        RuntimeError: If another extension application process is already active in the same context.
    """
    assert self._instance_var is not None, instance_not_enabled_error
    # need to address before the instance is swapped
    if (
        apply_extensions
        and self._extensions_var is not None
        and self._extensions_applied_var.get() is not None
    ):
        raise RuntimeError("Other apply process in the same context is active.")
    token = self._instance_var.set(instance)
    try:
        if apply_extensions and self._extensions_var is not None:
            self.apply_extensions(use_overwrite=use_extensions_overwrite)
        yield instance
    finally:
        self._instance_var.reset(token)

clear_caches

clear_caches(settings_cache=True, import_cache=True)

Clears the settings and import caches.

This method clears the cached settings and/or import objects, forcing them to be reloaded on next access.

Parameters:

Name Type Description Default
settings_cache bool

If True, clears the settings cache.

True
import_cache bool

If True, clears the import cache.

True
Source code in monkay/core.py
def clear_caches(self, settings_cache: bool = True, import_cache: bool = True) -> None:
    """
    Clears the settings and import caches.

    This method clears the cached settings and/or import objects, forcing them to be reloaded
    on next access.

    Args:
        settings_cache: If True, clears the settings cache.
        import_cache: If True, clears the import cache.
    """
    if settings_cache:
        del self.settings
    if import_cache:
        self._cached_imports.clear()

evaluate_preloads

evaluate_preloads(preloads, *, ignore_import_errors=True, package=None)

Evaluates preload modules or functions specified in settings.

This method delegates to the evaluate_preloads function, using the Monkay instance's package if no package is provided.

Parameters:

Name Type Description Default
preloads Iterable[str]

An iterable of preload paths, in the format "module" or "module:function".

required
ignore_import_errors bool

If True, ignores import errors and continues processing.

True
package str | None

The package name to use as a context for relative imports.

None

Returns:

Type Description
bool

True if all preloads were successfully evaluated, False otherwise.

Source code in monkay/core.py
def evaluate_preloads(
    self,
    preloads: Iterable[str],
    *,
    ignore_import_errors: bool = True,
    package: str | None = None,
) -> bool:
    """
    Evaluates preload modules or functions specified in settings.

    This method delegates to the `evaluate_preloads` function, using the Monkay instance's
    package if no package is provided.

    Args:
        preloads: An iterable of preload paths, in the format "module" or "module:function".
        ignore_import_errors: If True, ignores import errors and continues processing.
        package: The package name to use as a context for relative imports.

    Returns:
        True if all preloads were successfully evaluated, False otherwise.
    """
    return evaluate_preloads(
        preloads, ignore_import_errors=ignore_import_errors, package=package or self.package
    )

evaluate_settings

evaluate_settings(*, on_conflict='error', ignore_import_errors=False, ignore_preload_import_errors=True, onetime=True)

Evaluate settings-driven preloads and extension registrations.

This method is the main runtime entry point for applying configuration declared in settings_preloads_name and settings_extensions_name. It can be called repeatedly; with onetime=True it becomes idempotent per context once evaluation succeeded.

Parameters:

Name Type Description Default
on_conflict Literal['error', 'keep', 'replace']

Conflict policy applied while registering extensions from settings. Supported values are "error", "keep", and "replace".

'error'
ignore_import_errors bool

Return False instead of raising when loading settings fails with :class:ImportError or :class:~monkay.base.UnsetError.

False
ignore_preload_import_errors bool

Continue when a settings preload import fails.

True
onetime bool

Skip re-evaluation if settings were already evaluated in the active context.

True

Returns:

Type Description
bool

True when evaluation completed or was skipped due to onetime.

bool

False only when ignore_import_errors=True and settings loading

bool

failed with an import/unset error.

Raises:

Type Description
ValueError

If on_conflict is not one of the supported values.

Exception

Any exception raised while loading settings or applying preloads/extensions when not suppressed.

Examples:

>>> monkay.evaluate_settings()
True
>>> monkay.evaluate_settings(on_conflict="replace", onetime=False)
True
Notes

When neither settings_preloads_name nor settings_extensions_name is configured, this method marks settings as evaluated without touching monkay.settings.

Source code in monkay/core.py
def evaluate_settings(
    self,
    *,
    on_conflict: Literal["error", "keep", "replace"] = "error",
    ignore_import_errors: bool = False,
    ignore_preload_import_errors: bool = True,
    onetime: bool = True,
) -> bool:
    """Evaluate settings-driven preloads and extension registrations.

    This method is the main runtime entry point for applying configuration
    declared in ``settings_preloads_name`` and ``settings_extensions_name``.
    It can be called repeatedly; with ``onetime=True`` it becomes idempotent
    per context once evaluation succeeded.

    Args:
        on_conflict: Conflict policy applied while registering extensions from
            settings. Supported values are ``"error"``, ``"keep"``, and
            ``"replace"``.
        ignore_import_errors: Return ``False`` instead of raising when loading
            settings fails with :class:`ImportError` or
            :class:`~monkay.base.UnsetError`.
        ignore_preload_import_errors: Continue when a settings preload import
            fails.
        onetime: Skip re-evaluation if settings were already evaluated in the
            active context.

    Returns:
        ``True`` when evaluation completed or was skipped due to ``onetime``.
        ``False`` only when ``ignore_import_errors=True`` and settings loading
        failed with an import/unset error.

    Raises:
        ValueError: If ``on_conflict`` is not one of the supported values.
        Exception: Any exception raised while loading settings or applying
            preloads/extensions when not suppressed.

    Examples:
        >>> monkay.evaluate_settings()
        True
        >>> monkay.evaluate_settings(on_conflict="replace", onetime=False)
        True

    Notes:
        When neither ``settings_preloads_name`` nor ``settings_extensions_name``
        is configured, this method marks settings as evaluated without touching
        ``monkay.settings``.
    """
    on_conflict = validate_conflict_mode(on_conflict)
    initial_settings_evaluated = self.settings_evaluated
    if onetime and initial_settings_evaluated:
        return True
    # don't access settings when there is nothing to evaluate
    if not self.settings_preloads_name and not self.settings_extensions_name:
        self.settings_evaluated = True
        return True

    try:
        # load settings one time and before setting settings_evaluated to True
        settings = self.settings
    except Exception as exc:
        if ignore_import_errors and isinstance(exc, UnsetError | ImportError):
            return False
        raise exc
    self._evaluate_settings(
        on_conflict=on_conflict,
        settings=settings,
        ignore_preload_import_errors=ignore_preload_import_errors,
        initial_settings_evaluated=initial_settings_evaluated,
    )
    return True

evaluate_settings_once

evaluate_settings_once(*, on_conflict='error', ignore_import_errors=True)

Evaluates settings preloads and extensions once. (Deprecated)

This method is deprecated and now equivalent to evaluate_settings(onetime=True).

Parameters:

Name Type Description Default
on_conflict Literal['error', 'keep', 'replace']

Specifies how to handle conflicts when adding extensions.

'error'
ignore_import_errors bool

If True, ignores settings import errors.

True

Returns:

Type Description
bool

True if settings were successfully evaluated, False otherwise.

Source code in monkay/core.py
def evaluate_settings_once(
    self,
    *,
    on_conflict: Literal["error", "keep", "replace"] = "error",
    ignore_import_errors: bool = True,
) -> bool:
    """
    Evaluates settings preloads and extensions once. (Deprecated)

    This method is deprecated and now equivalent to `evaluate_settings(onetime=True)`.

    Args:
        on_conflict: Specifies how to handle conflicts when adding extensions.
        ignore_import_errors: If True, ignores settings import errors.

    Returns:
        True if settings were successfully evaluated, False otherwise.
    """
    warnings.warn(
        "`evaluate_settings_once` is deprecated. Use `evaluate_settings` instead. It has now the same functionality.",
        DeprecationWarning,
        stacklevel=2,
    )
    return self.evaluate_settings(
        on_conflict=on_conflict, ignore_import_errors=ignore_import_errors, onetime=True
    )

with_full_overwrite

with_full_overwrite(*, extensions=Undefined, settings=Undefined, instance=Undefined, apply_extensions=False, evaluate_settings_with=None)

Apply all overwrites in the correct order. Useful for testing or sub-environments

Source code in monkay/core.py
@contextmanager
def with_full_overwrite(
    self,
    *,
    extensions: dict[str, ExtensionProtocol[INSTANCE, SETTINGS]]
    | None
    | type[Undefined] = Undefined,
    settings: SETTINGS_DEFINITION_TYPE | Literal[False] | type[Undefined] = Undefined,
    instance: INSTANCE | None | type[Undefined] = Undefined,
    apply_extensions: bool = False,
    evaluate_settings_with: EvaluateSettingsParameters | None = None,
) -> Generator[None]:
    """
    Apply all overwrites in the correct order. Useful for testing or sub-environments
    """
    ctx_extensions = (
        nullcontext()
        if extensions is Undefined
        else self.with_extensions(
            cast("dict[str, ExtensionProtocol[INSTANCE, SETTINGS]] | None", extensions),
            apply_extensions=False,
        )
    )
    ctx_settings = (
        nullcontext()
        if settings is Undefined
        else self.with_settings(
            cast("SETTINGS_DEFINITION_TYPE | Literal[False]", settings),
            evaluate_settings_with=None,
        )
    )
    ctx_instance = (
        nullcontext()
        if instance is Undefined
        else self.with_instance(
            cast("INSTANCE | None", instance),
            apply_extensions=False,
        )
    )

    with (
        ctx_extensions,
        ctx_settings,
        ctx_instance,
    ):
        # evaluate here because the ctxmanagers have not the information from the contextvars
        # evaluate settings also without settings instance
        if evaluate_settings_with is not None and evaluate_settings_with is not False:
            if evaluate_settings_with is True:
                evaluate_settings_with = {}
            self.evaluate_settings(**evaluate_settings_with)
        # apply extensions also without extra instance
        if apply_extensions and self._extensions_var is not None:
            self.apply_extensions()
        yield

PRE_ADD_LAZY_IMPORT_HOOK

Bases: Protocol

A protocol defining the signature for a hook that modifies lazy import definitions before they are added.

This protocol specifies the expected signature for a hook function that can be used to modify lazy import definitions before they are added to the module. It supports both regular lazy imports and deprecated lazy imports.

Source code in monkay/types.py
class PRE_ADD_LAZY_IMPORT_HOOK(Protocol):
    """
    A protocol defining the signature for a hook that modifies lazy import definitions before they are added.

    This protocol specifies the expected signature for a hook function that can be used to modify
    lazy import definitions before they are added to the module. It supports both regular lazy imports
    and deprecated lazy imports.
    """

    @overload
    @staticmethod
    def __call__(
        key: str,
        value: str | Callable[[], Any],
        type_: Literal["lazy_import"],
        /,
    ) -> tuple[str, str | Callable[[], Any]]: ...

    @overload
    @staticmethod
    def __call__(
        key: str,
        value: DeprecatedImport,
        type_: Literal["deprecated_lazy_import"],
        /,
    ) -> tuple[str, DeprecatedImport]: ...

    @staticmethod
    def __call__(
        key: str,
        value: str | Callable[[], Any] | DeprecatedImport,
        type_: Literal["lazy_import", "deprecated_lazy_import"],
        /,
    ) -> tuple[str, str | Callable[[], Any] | DeprecatedImport]: ...

DeprecatedImport

Bases: TypedDict

Represents a deprecated import with optional deprecation details.

This class defines the structure for deprecated imports, including the import path, reason for deprecation, and a replacement attribute.

Source code in monkay/types.py
class DeprecatedImport(TypedDict, total=False):
    """
    Represents a deprecated import with optional deprecation details.

    This class defines the structure for deprecated imports, including the import path,
    reason for deprecation, and a replacement attribute.
    """

    path: str | Callable[[], Any]
    """The import path of the deprecated object, or a callable that returns the object."""
    reason: str
    """The reason for deprecation."""
    new_attribute: str
    """The replacement attribute to use."""

path instance-attribute

path

The import path of the deprecated object, or a callable that returns the object.

reason instance-attribute

reason

The reason for deprecation.

new_attribute instance-attribute

new_attribute

The replacement attribute to use.

ExtensionProtocol

Bases: Protocol[INSTANCE, SETTINGS]

Source code in monkay/types.py
@runtime_checkable
class ExtensionProtocol(Protocol[INSTANCE, SETTINGS]):
    name: str

    def apply(self, monkay_instance: Monkay[INSTANCE, SETTINGS]) -> None: ...

name instance-attribute

name

apply

apply(monkay_instance)
Source code in monkay/types.py
def apply(self, monkay_instance: Monkay[INSTANCE, SETTINGS]) -> None: ...

absolutify_import

absolutify_import(import_path, package)

Converts a relative import path to an absolute import path.

This function takes an import path and a package name and converts the relative import path to an absolute path by prepending the package name and adjusting for relative levels (e.g., "..module").

Parameters:

Name Type Description Default
import_path str

The import path to absolutify.

required
package str | None

The package name to use as a base for relative imports.

required

Returns:

Type Description
str

The absolute import path.

Raises:

Type Description
ValueError

If the import path is invalid or tries to cross parent boundaries.

Source code in monkay/base.py
def absolutify_import(import_path: str, package: str | None) -> str:
    """
    Converts a relative import path to an absolute import path.

    This function takes an import path and a package name and converts the relative
    import path to an absolute path by prepending the package name and adjusting
    for relative levels (e.g., "..module").

    Args:
        import_path: The import path to absolutify.
        package: The package name to use as a base for relative imports.

    Returns:
        The absolute import path.

    Raises:
        ValueError: If the import path is invalid or tries to cross parent boundaries.
    """
    if not package or not import_path:
        return import_path
    dot_count: int = 0
    try:
        while import_path[dot_count] == ".":
            dot_count += 1
    except IndexError:
        raise ValueError("not an import path") from None
    if dot_count == 0:
        return import_path
    if dot_count - 2 > package.count("."):
        raise ValueError("Out of bound, tried to cross parent.")
    if dot_count > 1:
        package = package.rsplit(".", dot_count - 1)[0]

    return f"{package}.{import_path.lstrip('.')}"

get_value_from_settings

get_value_from_settings(settings, name)

Retrieves a value from a settings object, supporting both attribute and dictionary access.

This function attempts to retrieve a value from a settings object. It first tries to access the value as an attribute. If that fails, it tries to access the value as a dictionary key.

Parameters:

Name Type Description Default
settings Any

The settings object to retrieve the value from.

required
name str

The name of the attribute or key to retrieve.

required

Returns:

Type Description
Any

The retrieved value.

Raises:

Type Description
AttributeError

If the name is not found as an attribute and the settings object does not support dictionary access.

KeyError

If the name is not found as a key in the settings object and attribute access also fails.

Source code in monkay/base.py
def get_value_from_settings(settings: Any, name: str) -> Any:
    """
    Retrieves a value from a settings object, supporting both attribute and dictionary access.

    This function attempts to retrieve a value from a settings object. It first tries to access
    the value as an attribute. If that fails, it tries to access the value as a dictionary key.

    Args:
        settings: The settings object to retrieve the value from.
        name: The name of the attribute or key to retrieve.

    Returns:
        The retrieved value.

    Raises:
        AttributeError: If the name is not found as an attribute and the settings object does not support dictionary access.
        KeyError: If the name is not found as a key in the settings object and attribute access also fails.
    """
    try:
        return getattr(settings, name)
    except AttributeError:
        return settings[name]

load

load(path, *, allow_splits=':.', package=None)

Dynamically loads an object from a module given its path.

This function takes a string representing the path to an object within a module and dynamically imports the module and retrieves the object.

Parameters:

Name Type Description Default
path str

The path to the object, in the format "module:object" or "module.object".

required
allow_splits str

A string specifying the allowed separators for module and object names. Defaults to ":." allowing both ":" and "." as separators.

':.'
package None | str

The package name to use as a context for relative imports.

None

Returns:

Type Description
Any

The loaded object.

Raises:

Type Description
ValueError

If the path is invalid or cannot be parsed.

ImportError

If the module cannot be imported.

Source code in monkay/base.py
def load(path: str, *, allow_splits: str = ":.", package: None | str = None) -> Any:
    """
    Dynamically loads an object from a module given its path.

    This function takes a string representing the path to an object within a module
    and dynamically imports the module and retrieves the object.

    Args:
        path: The path to the object, in the format "module:object" or "module.object".
        allow_splits: A string specifying the allowed separators for module and object names.
                      Defaults to ":." allowing both ":" and "." as separators.
        package: The package name to use as a context for relative imports.

    Returns:
        The loaded object.

    Raises:
        ValueError: If the path is invalid or cannot be parsed.
        ImportError: If the module cannot be imported.
    """
    splitted = path.rsplit(":", 1) if ":" in allow_splits else []
    if len(splitted) < 2 and "." in allow_splits:
        splitted = path.rsplit(".", 1)
    if len(splitted) != 2:
        raise ValueError(f"invalid path: {path}")
    module = import_module(splitted[0], package)
    try:
        return getattr(module, splitted[1])
    except AttributeError as exc:
        # some implementations may have not this variable, so fallback to False
        if getattr(module.__spec__, "_initializing", False):
            raise ImportError(
                f'Import of "{splitted[1]}" failed, but the module is initializing. '
                f'You probably have a circular import in "{splitted[0]}".'
            ) from exc
        raise ImportError(f'Import of "{splitted[1]}" from "{splitted[0]}" failed.') from exc

load_any

load_any(path, attrs, *, non_first_deprecated=False, package=None)

Dynamically loads any of the specified attributes from a module.

This function takes a module path and a collection of attribute names. It attempts to import the module and retrieve each attribute in the given order. If any of the attributes are found, it returns the first one found.

Parameters:

Name Type Description Default
path str

The path to the module.

required
attrs Collection[str]

A collection of attribute names to search for.

required
non_first_deprecated bool

If True, issues deprecation warnings for all found attributes except the first one.

False
package None | str

The package name to use as a context for relative imports.

None

Returns:

Type Description
Any | None

The first found attribute, or None if none of the attributes are found.

Raises:

Type Description
ImportError

If the module cannot be imported or none of the attributes are found.

DeprecationWarning

If non_first_deprecated is True and a non-first attribute is found.

Source code in monkay/base.py
def load_any(
    path: str,
    attrs: Collection[str],
    *,
    non_first_deprecated: bool = False,
    package: None | str = None,
) -> Any | None:
    """
    Dynamically loads any of the specified attributes from a module.

    This function takes a module path and a collection of attribute names. It attempts
    to import the module and retrieve each attribute in the given order. If any of the
    attributes are found, it returns the first one found.

    Args:
        path: The path to the module.
        attrs: A collection of attribute names to search for.
        non_first_deprecated: If True, issues deprecation warnings for all found attributes
                               except the first one.
        package: The package name to use as a context for relative imports.

    Returns:
        The first found attribute, or None if none of the attributes are found.

    Raises:
        ImportError: If the module cannot be imported or none of the attributes are found.
        DeprecationWarning: If `non_first_deprecated` is True and a non-first attribute is found.
    """
    module = import_module(path, package)
    first_name: None | str = None

    for attr in attrs:
        if hasattr(module, attr):
            if non_first_deprecated and first_name is not None:
                warnings.warn(
                    f'"{attr}" is deprecated, use "{first_name}" instead.',
                    DeprecationWarning,
                    stacklevel=2,
                )
            return getattr(module, attr)
        if first_name is None:
            first_name = attr

    # some implementations may have not this variable, so fallback to False
    if getattr(module.__spec__, "_initializing", False):
        raise ImportError(
            f"Could not import any of the attributes:.{', '.join(attrs)}, but the module is initializing. "
            f'You probably have a circular import in "{path}".'
        )
    raise ImportError(f'Could not import any of the attributes:.{", ".join(attrs)} from "{path}".')

Core

monkay.core

Monkay

Bases: MonkayInstance[INSTANCE, SETTINGS], MonkaySettings[SETTINGS], MonkayExports, Generic[INSTANCE, SETTINGS]

A comprehensive class that combines instance, settings, and export management for a module.

This class provides a unified interface for managing module instances, settings, and exports. It integrates lazy imports, deprecated imports, settings loading, and instance management.

Source code in monkay/core.py
class Monkay(
    MonkayInstance[INSTANCE, SETTINGS],
    MonkaySettings[SETTINGS],
    MonkayExports,
    Generic[INSTANCE, SETTINGS],
):
    """
    A comprehensive class that combines instance, settings, and export management for a module.

    This class provides a unified interface for managing module instances, settings, and exports.
    It integrates lazy imports, deprecated imports, settings loading, and instance management.
    """

    def __init__(
        self,
        globals_dict: dict,
        *,
        with_instance: str | bool = False,
        with_extensions: str | bool = False,
        extension_order_key_fn: None
        | Callable[[ExtensionProtocol[INSTANCE, SETTINGS]], Any] = None,
        settings_path: SETTINGS_DEFINITION_TYPE | Literal[False] = None,
        preloads: Iterable[str] = (),
        settings_preloads_name: str = "",
        settings_extensions_name: str = "",
        uncached_imports: Iterable[str] = (),
        lazy_imports: dict[str, str | Callable[[], Any]] | None = None,
        deprecated_lazy_imports: dict[str, DeprecatedImport] | None = None,
        settings_ctx_name: str = "monkay_settings_ctx",
        extensions_applied_ctx_name: str = "monkay_extensions_applied_ctx",
        skip_all_update: bool = False,
        skip_getattr_fixup: bool = False,
        evaluate_settings: None = None,
        ignore_settings_import_errors: None = None,
        pre_add_lazy_import_hook: None | PRE_ADD_LAZY_IMPORT_HOOK = None,
        post_add_lazy_import_hook: None | Callable[[str], None] = None,
        ignore_preload_import_errors: bool = True,
        package: str | None = "",
    ) -> None:
        """
        Initializes a Monkay instance.

        This method sets up the Monkay instance with the provided configurations, including
        instance management, settings loading, lazy imports, and extension handling.

        Args:
            globals_dict: The module's globals dictionary.
            with_instance: If True, enables instance management with a default context variable name.
                           If a string, uses the provided name for the context variable.
            with_extensions: If True, enables extension management with a default context variable name.
                             If a string, uses the provided name for the context variable.
            extension_order_key_fn: An optional function to define the order in which extensions are applied.
            settings_path: The path to the settings object, a callable, a settings instance, or a settings class.
                           If False or None, settings are disabled.
            preloads: An iterable of preload paths to execute before initialization.
            settings_preloads_name: The name used to identify preload settings.
            settings_extensions_name: The name used to identify extension settings.
            uncached_imports: An iterable of import names that should not be cached.
            lazy_imports: A dictionary of lazy imports.
            deprecated_lazy_imports: A dictionary of deprecated lazy imports.
            settings_ctx_name: The name of the settings context variable.
            extensions_applied_ctx_name: The name of the extensions applied context variable.
            skip_all_update: If True, skips updating the `__all__` variable.
            skip_getattr_fixup: If True, skips fixing the missing `__dir__` function.
            evaluate_settings: Deprecated parameter.
            ignore_settings_import_errors: Deprecated parameter.
            pre_add_lazy_import_hook: A hook to modify lazy import definitions before they are added.
            post_add_lazy_import_hook: A hook to execute after a lazy import is added.
            ignore_preload_import_errors: If True, ignores preload import errors.
            package: The package name to use for relative imports.
        """
        self.globals_dict = globals_dict
        if with_instance is True:
            with_instance = "monkay_instance_ctx"
        with_instance = with_instance
        if with_extensions is True:
            with_extensions = "monkay_extensions_ctx"
        with_extensions = with_extensions
        if package == "" and globals_dict.get("__spec__"):
            package = globals_dict["__spec__"].parent
        self.package = package or None

        self._cached_imports: dict[str, Any] = {}
        self.pre_add_lazy_import_hook = pre_add_lazy_import_hook
        self.post_add_lazy_import_hook = post_add_lazy_import_hook
        self.uncached_imports = set(uncached_imports)
        self.lazy_imports = {}
        self.deprecated_lazy_imports = {}
        if lazy_imports:
            for name, lazy_import in lazy_imports.items():
                self.add_lazy_import(name, lazy_import, no_hooks=True)
        if deprecated_lazy_imports:
            for name, deprecated_import in deprecated_lazy_imports.items():
                self.add_deprecated_lazy_import(name, deprecated_import, no_hooks=True)
        if settings_path is not None and settings_path is not False:
            self._settings_var = globals_dict[settings_ctx_name] = ContextVar(
                settings_ctx_name, default=None
            )
            self.settings = settings_path  # type: ignore
        self.settings_preloads_name = settings_preloads_name
        self.settings_extensions_name = settings_extensions_name

        if with_instance:
            self._instance_var = globals_dict[with_instance] = ContextVar(
                with_instance, default=None
            )
        if with_extensions:
            self.extension_order_key_fn = extension_order_key_fn
            self._extensions = {}
            self._extensions_var = globals_dict[with_extensions] = ContextVar(
                with_extensions, default=None
            )
            self._extensions_applied_var = globals_dict[extensions_applied_ctx_name] = ContextVar(
                extensions_applied_ctx_name, default=None
            )
        if not skip_all_update and (self.lazy_imports or self.deprecated_lazy_imports):
            all_var = globals_dict.setdefault("__all__", [])
            globals_dict["__all__"] = self.update_all_var(all_var)
        # fix missing __dir__ in case only __getattr__ was specified and __dir__ not
        # it assumes the __all__ var is correct
        if (
            not skip_getattr_fixup
            and "__all__" in globals_dict
            and "__getattr__" in globals_dict
            and "__dir__" not in globals_dict
        ):
            self._init_global_dir_hook()
        self.evaluate_preloads(preloads, ignore_import_errors=ignore_preload_import_errors)
        if evaluate_settings is not None:
            raise Exception(
                "This feature and the evaluate_settings parameter are removed in monkay 0.3"
            )
        if ignore_settings_import_errors is not None:
            warnings.warn(
                "`ignore_settings_import_errors` parameter is defunct and deprecated. It always behave like it would be False.",
                DeprecationWarning,
                stacklevel=2,
            )

    def clear_caches(self, settings_cache: bool = True, import_cache: bool = True) -> None:
        """
        Clears the settings and import caches.

        This method clears the cached settings and/or import objects, forcing them to be reloaded
        on next access.

        Args:
            settings_cache: If True, clears the settings cache.
            import_cache: If True, clears the import cache.
        """
        if settings_cache:
            del self.settings
        if import_cache:
            self._cached_imports.clear()

    def evaluate_preloads(
        self,
        preloads: Iterable[str],
        *,
        ignore_import_errors: bool = True,
        package: str | None = None,
    ) -> bool:
        """
        Evaluates preload modules or functions specified in settings.

        This method delegates to the `evaluate_preloads` function, using the Monkay instance's
        package if no package is provided.

        Args:
            preloads: An iterable of preload paths, in the format "module" or "module:function".
            ignore_import_errors: If True, ignores import errors and continues processing.
            package: The package name to use as a context for relative imports.

        Returns:
            True if all preloads were successfully evaluated, False otherwise.
        """
        return evaluate_preloads(
            preloads, ignore_import_errors=ignore_import_errors, package=package or self.package
        )

    def _evaluate_settings(
        self,
        *,
        settings: SETTINGS,
        on_conflict: Literal["error", "keep", "replace"],
        ignore_preload_import_errors: bool,
        initial_settings_evaluated: bool,
    ) -> None:
        """
        Internal method to evaluate settings preloads and extensions.

        This method evaluates the preloads and extensions specified in the settings object.

        Args:
            settings: The settings object to evaluate.
            on_conflict: Specifies how to handle conflicts when adding extensions.
            ignore_preload_import_errors: If True, ignores preload import errors.
            initial_settings_evaluated: The initial state of the settings evaluation flag.

        Raises:
            Exception: If an error occurs during evaluation and initial settings evaluation was False.
        """
        self.settings_evaluated = True

        try:
            if self.settings_preloads_name:
                settings_preloads = get_value_from_settings(settings, self.settings_preloads_name)
                self.evaluate_preloads(
                    settings_preloads, ignore_import_errors=ignore_preload_import_errors
                )
            if self.settings_extensions_name:
                for extension in get_value_from_settings(settings, self.settings_extensions_name):
                    self.add_extension(extension, use_overwrite=True, on_conflict=on_conflict)
        except Exception as exc:
            if not initial_settings_evaluated:
                self.settings_evaluated = False
            raise exc

    def evaluate_settings(
        self,
        *,
        on_conflict: Literal["error", "keep", "replace"] = "error",
        ignore_import_errors: bool = False,
        ignore_preload_import_errors: bool = True,
        onetime: bool = True,
    ) -> bool:
        """Evaluate settings-driven preloads and extension registrations.

        This method is the main runtime entry point for applying configuration
        declared in ``settings_preloads_name`` and ``settings_extensions_name``.
        It can be called repeatedly; with ``onetime=True`` it becomes idempotent
        per context once evaluation succeeded.

        Args:
            on_conflict: Conflict policy applied while registering extensions from
                settings. Supported values are ``"error"``, ``"keep"``, and
                ``"replace"``.
            ignore_import_errors: Return ``False`` instead of raising when loading
                settings fails with :class:`ImportError` or
                :class:`~monkay.base.UnsetError`.
            ignore_preload_import_errors: Continue when a settings preload import
                fails.
            onetime: Skip re-evaluation if settings were already evaluated in the
                active context.

        Returns:
            ``True`` when evaluation completed or was skipped due to ``onetime``.
            ``False`` only when ``ignore_import_errors=True`` and settings loading
            failed with an import/unset error.

        Raises:
            ValueError: If ``on_conflict`` is not one of the supported values.
            Exception: Any exception raised while loading settings or applying
                preloads/extensions when not suppressed.

        Examples:
            >>> monkay.evaluate_settings()
            True
            >>> monkay.evaluate_settings(on_conflict="replace", onetime=False)
            True

        Notes:
            When neither ``settings_preloads_name`` nor ``settings_extensions_name``
            is configured, this method marks settings as evaluated without touching
            ``monkay.settings``.
        """
        on_conflict = validate_conflict_mode(on_conflict)
        initial_settings_evaluated = self.settings_evaluated
        if onetime and initial_settings_evaluated:
            return True
        # don't access settings when there is nothing to evaluate
        if not self.settings_preloads_name and not self.settings_extensions_name:
            self.settings_evaluated = True
            return True

        try:
            # load settings one time and before setting settings_evaluated to True
            settings = self.settings
        except Exception as exc:
            if ignore_import_errors and isinstance(exc, UnsetError | ImportError):
                return False
            raise exc
        self._evaluate_settings(
            on_conflict=on_conflict,
            settings=settings,
            ignore_preload_import_errors=ignore_preload_import_errors,
            initial_settings_evaluated=initial_settings_evaluated,
        )
        return True

    def evaluate_settings_once(
        self,
        *,
        on_conflict: Literal["error", "keep", "replace"] = "error",
        ignore_import_errors: bool = True,
    ) -> bool:
        """
        Evaluates settings preloads and extensions once. (Deprecated)

        This method is deprecated and now equivalent to `evaluate_settings(onetime=True)`.

        Args:
            on_conflict: Specifies how to handle conflicts when adding extensions.
            ignore_import_errors: If True, ignores settings import errors.

        Returns:
            True if settings were successfully evaluated, False otherwise.
        """
        warnings.warn(
            "`evaluate_settings_once` is deprecated. Use `evaluate_settings` instead. It has now the same functionality.",
            DeprecationWarning,
            stacklevel=2,
        )
        return self.evaluate_settings(
            on_conflict=on_conflict, ignore_import_errors=ignore_import_errors, onetime=True
        )

    @contextmanager
    def with_full_overwrite(
        self,
        *,
        extensions: dict[str, ExtensionProtocol[INSTANCE, SETTINGS]]
        | None
        | type[Undefined] = Undefined,
        settings: SETTINGS_DEFINITION_TYPE | Literal[False] | type[Undefined] = Undefined,
        instance: INSTANCE | None | type[Undefined] = Undefined,
        apply_extensions: bool = False,
        evaluate_settings_with: EvaluateSettingsParameters | None = None,
    ) -> Generator[None]:
        """
        Apply all overwrites in the correct order. Useful for testing or sub-environments
        """
        ctx_extensions = (
            nullcontext()
            if extensions is Undefined
            else self.with_extensions(
                cast("dict[str, ExtensionProtocol[INSTANCE, SETTINGS]] | None", extensions),
                apply_extensions=False,
            )
        )
        ctx_settings = (
            nullcontext()
            if settings is Undefined
            else self.with_settings(
                cast("SETTINGS_DEFINITION_TYPE | Literal[False]", settings),
                evaluate_settings_with=None,
            )
        )
        ctx_instance = (
            nullcontext()
            if instance is Undefined
            else self.with_instance(
                cast("INSTANCE | None", instance),
                apply_extensions=False,
            )
        )

        with (
            ctx_extensions,
            ctx_settings,
            ctx_instance,
        ):
            # evaluate here because the ctxmanagers have not the information from the contextvars
            # evaluate settings also without settings instance
            if evaluate_settings_with is not None and evaluate_settings_with is not False:
                if evaluate_settings_with is True:
                    evaluate_settings_with = {}
                self.evaluate_settings(**evaluate_settings_with)
            # apply extensions also without extra instance
            if apply_extensions and self._extensions_var is not None:
                self.apply_extensions()
            yield

globals_dict instance-attribute

globals_dict = globals_dict

package instance-attribute

package = package or None

pre_add_lazy_import_hook instance-attribute

pre_add_lazy_import_hook = pre_add_lazy_import_hook

post_add_lazy_import_hook instance-attribute

post_add_lazy_import_hook = post_add_lazy_import_hook

uncached_imports instance-attribute

uncached_imports = set(uncached_imports)

lazy_imports instance-attribute

lazy_imports = {}

deprecated_lazy_imports instance-attribute

deprecated_lazy_imports = {}

settings instance-attribute

settings = settings_path

settings_preloads_name instance-attribute

settings_preloads_name = settings_preloads_name

settings_extensions_name instance-attribute

settings_extensions_name = settings_extensions_name

extension_order_key_fn instance-attribute

extension_order_key_fn = extension_order_key_fn

getter class-attribute instance-attribute

getter = None

The getter function used for attribute access, initialized with lazy import logic. This function handles both regular attribute access and the resolution of lazy imports.

dir_fn class-attribute instance-attribute

dir_fn = None

The directory listing function, enhanced to include lazy imports. This function is used to generate the list of attributes available in the module.

settings_evaluated property writable

settings_evaluated

Checks if the settings have been evaluated and loaded.

This property returns a boolean indicating whether the settings have been evaluated. It checks both the global settings evaluation flag and the context-specific flag.

Returns:

Type Description
bool

True if the settings have been evaluated, False otherwise.

Raises:

Type Description
AssertionError

If Monkay is not enabled for settings.

instance property

instance

Retrieves the current instance managed by Monkay.

This property returns the instance associated with the Monkay object. It first checks if a context-specific instance is set via _instance_var. If not, it returns the default instance _instance.

Returns:

Type Description
INSTANCE | None

The current instance, or None if no instance is set.

Raises:

Type Description
AssertionError

If Monkay is not enabled for instances.

clear_caches

clear_caches(settings_cache=True, import_cache=True)

Clears the settings and import caches.

This method clears the cached settings and/or import objects, forcing them to be reloaded on next access.

Parameters:

Name Type Description Default
settings_cache bool

If True, clears the settings cache.

True
import_cache bool

If True, clears the import cache.

True
Source code in monkay/core.py
def clear_caches(self, settings_cache: bool = True, import_cache: bool = True) -> None:
    """
    Clears the settings and import caches.

    This method clears the cached settings and/or import objects, forcing them to be reloaded
    on next access.

    Args:
        settings_cache: If True, clears the settings cache.
        import_cache: If True, clears the import cache.
    """
    if settings_cache:
        del self.settings
    if import_cache:
        self._cached_imports.clear()

evaluate_preloads

evaluate_preloads(preloads, *, ignore_import_errors=True, package=None)

Evaluates preload modules or functions specified in settings.

This method delegates to the evaluate_preloads function, using the Monkay instance's package if no package is provided.

Parameters:

Name Type Description Default
preloads Iterable[str]

An iterable of preload paths, in the format "module" or "module:function".

required
ignore_import_errors bool

If True, ignores import errors and continues processing.

True
package str | None

The package name to use as a context for relative imports.

None

Returns:

Type Description
bool

True if all preloads were successfully evaluated, False otherwise.

Source code in monkay/core.py
def evaluate_preloads(
    self,
    preloads: Iterable[str],
    *,
    ignore_import_errors: bool = True,
    package: str | None = None,
) -> bool:
    """
    Evaluates preload modules or functions specified in settings.

    This method delegates to the `evaluate_preloads` function, using the Monkay instance's
    package if no package is provided.

    Args:
        preloads: An iterable of preload paths, in the format "module" or "module:function".
        ignore_import_errors: If True, ignores import errors and continues processing.
        package: The package name to use as a context for relative imports.

    Returns:
        True if all preloads were successfully evaluated, False otherwise.
    """
    return evaluate_preloads(
        preloads, ignore_import_errors=ignore_import_errors, package=package or self.package
    )

evaluate_settings

evaluate_settings(*, on_conflict='error', ignore_import_errors=False, ignore_preload_import_errors=True, onetime=True)

Evaluate settings-driven preloads and extension registrations.

This method is the main runtime entry point for applying configuration declared in settings_preloads_name and settings_extensions_name. It can be called repeatedly; with onetime=True it becomes idempotent per context once evaluation succeeded.

Parameters:

Name Type Description Default
on_conflict Literal['error', 'keep', 'replace']

Conflict policy applied while registering extensions from settings. Supported values are "error", "keep", and "replace".

'error'
ignore_import_errors bool

Return False instead of raising when loading settings fails with :class:ImportError or :class:~monkay.base.UnsetError.

False
ignore_preload_import_errors bool

Continue when a settings preload import fails.

True
onetime bool

Skip re-evaluation if settings were already evaluated in the active context.

True

Returns:

Type Description
bool

True when evaluation completed or was skipped due to onetime.

bool

False only when ignore_import_errors=True and settings loading

bool

failed with an import/unset error.

Raises:

Type Description
ValueError

If on_conflict is not one of the supported values.

Exception

Any exception raised while loading settings or applying preloads/extensions when not suppressed.

Examples:

>>> monkay.evaluate_settings()
True
>>> monkay.evaluate_settings(on_conflict="replace", onetime=False)
True
Notes

When neither settings_preloads_name nor settings_extensions_name is configured, this method marks settings as evaluated without touching monkay.settings.

Source code in monkay/core.py
def evaluate_settings(
    self,
    *,
    on_conflict: Literal["error", "keep", "replace"] = "error",
    ignore_import_errors: bool = False,
    ignore_preload_import_errors: bool = True,
    onetime: bool = True,
) -> bool:
    """Evaluate settings-driven preloads and extension registrations.

    This method is the main runtime entry point for applying configuration
    declared in ``settings_preloads_name`` and ``settings_extensions_name``.
    It can be called repeatedly; with ``onetime=True`` it becomes idempotent
    per context once evaluation succeeded.

    Args:
        on_conflict: Conflict policy applied while registering extensions from
            settings. Supported values are ``"error"``, ``"keep"``, and
            ``"replace"``.
        ignore_import_errors: Return ``False`` instead of raising when loading
            settings fails with :class:`ImportError` or
            :class:`~monkay.base.UnsetError`.
        ignore_preload_import_errors: Continue when a settings preload import
            fails.
        onetime: Skip re-evaluation if settings were already evaluated in the
            active context.

    Returns:
        ``True`` when evaluation completed or was skipped due to ``onetime``.
        ``False`` only when ``ignore_import_errors=True`` and settings loading
        failed with an import/unset error.

    Raises:
        ValueError: If ``on_conflict`` is not one of the supported values.
        Exception: Any exception raised while loading settings or applying
            preloads/extensions when not suppressed.

    Examples:
        >>> monkay.evaluate_settings()
        True
        >>> monkay.evaluate_settings(on_conflict="replace", onetime=False)
        True

    Notes:
        When neither ``settings_preloads_name`` nor ``settings_extensions_name``
        is configured, this method marks settings as evaluated without touching
        ``monkay.settings``.
    """
    on_conflict = validate_conflict_mode(on_conflict)
    initial_settings_evaluated = self.settings_evaluated
    if onetime and initial_settings_evaluated:
        return True
    # don't access settings when there is nothing to evaluate
    if not self.settings_preloads_name and not self.settings_extensions_name:
        self.settings_evaluated = True
        return True

    try:
        # load settings one time and before setting settings_evaluated to True
        settings = self.settings
    except Exception as exc:
        if ignore_import_errors and isinstance(exc, UnsetError | ImportError):
            return False
        raise exc
    self._evaluate_settings(
        on_conflict=on_conflict,
        settings=settings,
        ignore_preload_import_errors=ignore_preload_import_errors,
        initial_settings_evaluated=initial_settings_evaluated,
    )
    return True

evaluate_settings_once

evaluate_settings_once(*, on_conflict='error', ignore_import_errors=True)

Evaluates settings preloads and extensions once. (Deprecated)

This method is deprecated and now equivalent to evaluate_settings(onetime=True).

Parameters:

Name Type Description Default
on_conflict Literal['error', 'keep', 'replace']

Specifies how to handle conflicts when adding extensions.

'error'
ignore_import_errors bool

If True, ignores settings import errors.

True

Returns:

Type Description
bool

True if settings were successfully evaluated, False otherwise.

Source code in monkay/core.py
def evaluate_settings_once(
    self,
    *,
    on_conflict: Literal["error", "keep", "replace"] = "error",
    ignore_import_errors: bool = True,
) -> bool:
    """
    Evaluates settings preloads and extensions once. (Deprecated)

    This method is deprecated and now equivalent to `evaluate_settings(onetime=True)`.

    Args:
        on_conflict: Specifies how to handle conflicts when adding extensions.
        ignore_import_errors: If True, ignores settings import errors.

    Returns:
        True if settings were successfully evaluated, False otherwise.
    """
    warnings.warn(
        "`evaluate_settings_once` is deprecated. Use `evaluate_settings` instead. It has now the same functionality.",
        DeprecationWarning,
        stacklevel=2,
    )
    return self.evaluate_settings(
        on_conflict=on_conflict, ignore_import_errors=ignore_import_errors, onetime=True
    )

with_full_overwrite

with_full_overwrite(*, extensions=Undefined, settings=Undefined, instance=Undefined, apply_extensions=False, evaluate_settings_with=None)

Apply all overwrites in the correct order. Useful for testing or sub-environments

Source code in monkay/core.py
@contextmanager
def with_full_overwrite(
    self,
    *,
    extensions: dict[str, ExtensionProtocol[INSTANCE, SETTINGS]]
    | None
    | type[Undefined] = Undefined,
    settings: SETTINGS_DEFINITION_TYPE | Literal[False] | type[Undefined] = Undefined,
    instance: INSTANCE | None | type[Undefined] = Undefined,
    apply_extensions: bool = False,
    evaluate_settings_with: EvaluateSettingsParameters | None = None,
) -> Generator[None]:
    """
    Apply all overwrites in the correct order. Useful for testing or sub-environments
    """
    ctx_extensions = (
        nullcontext()
        if extensions is Undefined
        else self.with_extensions(
            cast("dict[str, ExtensionProtocol[INSTANCE, SETTINGS]] | None", extensions),
            apply_extensions=False,
        )
    )
    ctx_settings = (
        nullcontext()
        if settings is Undefined
        else self.with_settings(
            cast("SETTINGS_DEFINITION_TYPE | Literal[False]", settings),
            evaluate_settings_with=None,
        )
    )
    ctx_instance = (
        nullcontext()
        if instance is Undefined
        else self.with_instance(
            cast("INSTANCE | None", instance),
            apply_extensions=False,
        )
    )

    with (
        ctx_extensions,
        ctx_settings,
        ctx_instance,
    ):
        # evaluate here because the ctxmanagers have not the information from the contextvars
        # evaluate settings also without settings instance
        if evaluate_settings_with is not None and evaluate_settings_with is not False:
            if evaluate_settings_with is True:
                evaluate_settings_with = {}
            self.evaluate_settings(**evaluate_settings_with)
        # apply extensions also without extra instance
        if apply_extensions and self._extensions_var is not None:
            self.apply_extensions()
        yield

find_missing

find_missing(*, all_var=True, search_pathes=None, ignore_deprecated_import_errors=False, require_search_path_all_var=True)

Debug method to check for missing imports and inconsistencies in exports.

This method performs a comprehensive check for missing attributes, imports, and inconsistencies in the module's exports, including lazy and deprecated imports. It compares the defined exports with the module's __all__ variable and optional search paths to identify potential issues.

Parameters:

Name Type Description Default
all_var bool | Collection[str]

If True, checks against the module's __all__ variable. If a collection, checks against the provided names. If False, skips checking against __all__.

True
search_pathes None | Collection[str]

Optional list of module paths to search for additional exports.

None
ignore_deprecated_import_errors bool

If True, ignores import errors for deprecated lazy imports.

False
require_search_path_all_var bool

If True, requires __all__ to be defined in searched modules.

True

Returns:

Type Description
dict[str, set[Literal['not_in_all_var', 'missing_attr', 'missing_all_var', 'import', 'shadowed', 'search_path_extra', 'search_path_import']]]

A dictionary where keys are names of missing or inconsistent items,

dict[str, set[Literal['not_in_all_var', 'missing_attr', 'missing_all_var', 'import', 'shadowed', 'search_path_extra', 'search_path_import']]]

and values are sets of strings indicating the types of issues.

Issue types
  • "not_in_all_var": Export is defined but not in __all__.
  • "missing_attr": Attribute is missing from the module or a searched module.
  • "missing_all_var": __all__ is missing from the module or a searched module.
  • "import": Import error occurred while resolving a lazy or deprecated import.
  • "shadowed": Attribute is shadowed by a real variable in the module's globals.
  • "search_path_extra": Export from a search path is not found in the module's exports.
  • "search_path_import": Import error occurred while importing a search path.
Source code in monkay/_monkay_exports.py
def find_missing(
    self,
    *,
    all_var: bool | Collection[str] = True,
    search_pathes: None | Collection[str] = None,
    ignore_deprecated_import_errors: bool = False,
    require_search_path_all_var: bool = True,
) -> dict[
    str,
    set[
        Literal[
            "not_in_all_var",
            "missing_attr",
            "missing_all_var",
            "import",
            "shadowed",
            "search_path_extra",
            "search_path_import",
        ]
    ],
]:
    """
    Debug method to check for missing imports and inconsistencies in exports.

    This method performs a comprehensive check for missing attributes, imports,
    and inconsistencies in the module's exports, including lazy and deprecated imports.
    It compares the defined exports with the module's `__all__` variable and optional
    search paths to identify potential issues.

    Args:
        all_var: If True, checks against the module's `__all__` variable.
                 If a collection, checks against the provided names.
                 If False, skips checking against `__all__`.
        search_pathes: Optional list of module paths to search for additional exports.
        ignore_deprecated_import_errors: If True, ignores import errors for deprecated lazy imports.
        require_search_path_all_var: If True, requires `__all__` to be defined in searched modules.

    Returns:
        A dictionary where keys are names of missing or inconsistent items,
        and values are sets of strings indicating the types of issues.

    Issue types:
        - "not_in_all_var": Export is defined but not in `__all__`.
        - "missing_attr": Attribute is missing from the module or a searched module.
        - "missing_all_var": `__all__` is missing from the module or a searched module.
        - "import": Import error occurred while resolving a lazy or deprecated import.
        - "shadowed": Attribute is shadowed by a real variable in the module's globals.
        - "search_path_extra": Export from a search path is not found in the module's exports.
        - "search_path_import": Import error occurred while importing a search path.
    """
    self._init_global_getter_hook()

    assert self.getter is not None
    missing: dict[
        str,
        set[
            Literal[
                "not_in_all_var",
                "missing_attr",
                "missing_all_var",
                "import",
                "shadowed",
                "search_path_extra",
                "search_path_import",
            ]
        ],
    ] = {}
    if all_var is True:
        try:
            all_var = self.getter("__all__", check_globals_dict=True)
        except AttributeError:
            missing.setdefault(self.globals_dict["__spec__"].name, set()).add(
                "missing_all_var"
            )
            all_var = []
    key_set = set(chain(self.lazy_imports.keys(), self.deprecated_lazy_imports.keys()))
    value_pathes_set: set[str] = set()
    for name in key_set:
        found_path: str = ""
        if name in self.lazy_imports and isinstance(self.lazy_imports[name], str):
            found_path = cast(str, self.lazy_imports[name]).replace(":", ".")
        elif name in self.deprecated_lazy_imports and isinstance(
            self.deprecated_lazy_imports[name]["path"], str
        ):
            found_path = cast(str, self.deprecated_lazy_imports[name]["path"]).replace(
                ":", "."
            )
        if found_path:
            value_pathes_set.add(absolutify_import(found_path, self.package))
        try:
            obj = self.getter(name, no_warn_deprecated=True, check_globals_dict="fail")
            # also add maybe rexported path
            value_pathes_set.add(_obj_to_full_name(obj))
        except InGlobalsDict:
            missing.setdefault(name, set()).add("shadowed")
        except ImportError:
            if not ignore_deprecated_import_errors or name not in self.deprecated_lazy_imports:
                missing.setdefault(name, set()).add("import")
    if all_var is not False:
        for export_name in cast(Collection[str], all_var):
            try:
                obj = self.getter(
                    export_name, no_warn_deprecated=True, check_globals_dict=True
                )
            except AttributeError:
                missing.setdefault(export_name, set()).add("missing_attr")
                continue
            if export_name not in key_set:
                value_pathes_set.add(_obj_to_full_name(obj))

    if search_pathes:
        for search_path in search_pathes:
            try:
                mod = import_module(search_path, self.package)
            except ImportError:
                missing.setdefault(search_path, set()).add("search_path_import")
                continue
            try:
                all_var_search = mod.__all__
            except AttributeError:
                if require_search_path_all_var:
                    missing.setdefault(search_path, set()).add("missing_all_var")

                continue
            for export_name in all_var_search:
                export_path = absolutify_import(f"{search_path}.{export_name}", self.package)
                try:
                    # for re-exports
                    obj = getattr(mod, export_name)
                except AttributeError:
                    missing.setdefault(export_path, set()).add("missing_attr")
                    # still check check the export path
                    if export_path not in value_pathes_set:
                        missing.setdefault(export_path, set()).add("search_path_extra")
                    continue
                if (
                    export_path not in value_pathes_set
                    and _obj_to_full_name(obj) not in value_pathes_set
                ):
                    missing.setdefault(export_path, set()).add("search_path_extra")

    if all_var is not False:
        for name in key_set.difference(cast(Collection[str], all_var)):
            missing.setdefault(name, set()).add("not_in_all_var")

    return missing

add_lazy_import

add_lazy_import(name, value, *, no_hooks=False)

Adds a lazy import to the module.

This method adds a lazy import, which is resolved only when the attribute is accessed. This can improve module loading performance by deferring imports.

Parameters:

Name Type Description Default
name str

The name of the lazy import.

required
value str | Callable[[], Any]

The import path as a string or a callable that returns the imported object.

required
no_hooks bool

If True, skips the pre and post add hooks.

False

Raises:

Type Description
KeyError

If the name is already a lazy or deprecated lazy import.

Source code in monkay/_monkay_exports.py
def add_lazy_import(
    self, name: str, value: str | Callable[[], Any], *, no_hooks: bool = False
) -> None:
    """
    Adds a lazy import to the module.

    This method adds a lazy import, which is resolved only when the attribute is accessed.
    This can improve module loading performance by deferring imports.

    Args:
        name: The name of the lazy import.
        value: The import path as a string or a callable that returns the imported object.
        no_hooks: If True, skips the pre and post add hooks.

    Raises:
        KeyError: If the name is already a lazy or deprecated lazy import.
    """
    if not no_hooks and self.pre_add_lazy_import_hook is not None:
        name, value = self.pre_add_lazy_import_hook(name, value, "lazy_import")
    if name in self.lazy_imports:
        raise KeyError(f'"{name}" is already a lazy import')
    if name in self.deprecated_lazy_imports:
        raise KeyError(f'"{name}" is already a deprecated lazy import')
    self._init_global_getter_hook()
    self._init_global_dir_hook()
    self.lazy_imports[name] = value
    if not no_hooks and self.post_add_lazy_import_hook is not None:
        self.post_add_lazy_import_hook(name)

add_deprecated_lazy_import

add_deprecated_lazy_import(name, value, *, no_hooks=False)

Adds a deprecated lazy import to the module.

This method adds a lazy import that is marked as deprecated. When accessed, it will issue a deprecation warning.

Parameters:

Name Type Description Default
name str

The name of the deprecated import.

required
value DeprecatedImport

A dictionary containing details about the deprecation, including the import path.

required
no_hooks bool

If True, skips the pre and post add hooks.

False

Raises:

Type Description
KeyError

If the name is already a lazy or deprecated lazy import.

Source code in monkay/_monkay_exports.py
def add_deprecated_lazy_import(
    self, name: str, value: DeprecatedImport, *, no_hooks: bool = False
) -> None:
    """
    Adds a deprecated lazy import to the module.

    This method adds a lazy import that is marked as deprecated. When accessed, it will
    issue a deprecation warning.

    Args:
        name: The name of the deprecated import.
        value: A dictionary containing details about the deprecation, including the import path.
        no_hooks: If True, skips the pre and post add hooks.

    Raises:
        KeyError: If the name is already a lazy or deprecated lazy import.
    """
    if not no_hooks and self.pre_add_lazy_import_hook is not None:
        name, value = self.pre_add_lazy_import_hook(name, value, "deprecated_lazy_import")
    if name in self.lazy_imports:
        raise KeyError(f'"{name}" is already a lazy import')
    if name in self.deprecated_lazy_imports:
        raise KeyError(f'"{name}" is already a deprecated lazy import')
    self._init_global_getter_hook()
    self._init_global_dir_hook()
    self.deprecated_lazy_imports[name] = value
    if not no_hooks and self.post_add_lazy_import_hook is not None:
        self.post_add_lazy_import_hook(name)

sorted_exports

sorted_exports(all_var=None, *, separate_by_category=True, sort_by='path')

Returns a sorted list of module exports, categorized and sorted as specified.

This method generates a list of SortedExportsEntry objects, which represent the module's exports. It categorizes exports as "lazy_import", "deprecated_lazy_import", or "other", and sorts them based on the specified criteria.

Parameters:

Name Type Description Default
all_var Collection[str] | None

An optional collection of export names. If None, uses the module's __all__ variable.

None
separate_by_category bool

If True, sorts exports by category first, then by the specified sort_by attribute.

True
sort_by Literal['export_name', 'path']

The attribute to sort by, either "export_name" or "path".

'path'

Returns:

Type Description
list[SortedExportsEntry]

A list of SortedExportsEntry objects.

Source code in monkay/_monkay_exports.py
def sorted_exports(
    self,
    all_var: Collection[str] | None = None,
    *,
    separate_by_category: bool = True,
    sort_by: Literal["export_name", "path"] = "path",
) -> list[SortedExportsEntry]:
    """
    Returns a sorted list of module exports, categorized and sorted as specified.

    This method generates a list of `SortedExportsEntry` objects, which represent the module's exports.
    It categorizes exports as "lazy_import", "deprecated_lazy_import", or "other", and sorts them
    based on the specified criteria.

    Args:
        all_var: An optional collection of export names. If None, uses the module's `__all__` variable.
        separate_by_category: If True, sorts exports by category first, then by the specified `sort_by` attribute.
        sort_by: The attribute to sort by, either "export_name" or "path".

    Returns:
        A list of `SortedExportsEntry` objects.
    """
    if all_var is None:
        all_var = self.globals_dict.get("__all__", _empty)
    sorted_exports: list[SortedExportsEntry] = []
    # ensure all entries are only returned once
    for name in set(all_var):
        if name in self.lazy_imports:
            sorted_exports.append(
                SortedExportsEntry(
                    "lazy_import",
                    name,
                    cast(
                        str,
                        self.lazy_imports[name]
                        if isinstance(self.lazy_imports[name], str)
                        else f"{self.globals_dict['__spec__'].name}.{name}",
                    ),
                )
            )
        elif name in self.deprecated_lazy_imports:
            sorted_exports.append(
                SortedExportsEntry(
                    "deprecated_lazy_import",
                    name,
                    cast(
                        str,
                        self.deprecated_lazy_imports[name]["path"]
                        if isinstance(self.deprecated_lazy_imports[name]["path"], str)
                        else f"{self.globals_dict['__spec__'].name}.{name}",
                    ),
                )
            )
        else:
            sorted_exports.append(
                SortedExportsEntry(
                    "other",
                    name,
                    f"{self.globals_dict['__spec__'].name}.{name}",
                )
            )
    if separate_by_category:

        def key_fn(ordertuple: SortedExportsEntry) -> tuple:
            return ordertuple.category, getattr(ordertuple, sort_by)
    else:

        def key_fn(ordertuple: SortedExportsEntry) -> tuple:
            return (getattr(ordertuple, sort_by),)

    sorted_exports.sort(key=key_fn)
    return sorted_exports

module_dir_fn

module_dir_fn(*, chained_dir_fn=None)

Generates a directory listing for the module, including lazy and deprecated imports.

This method combines the module's __all__ variable, lazy imports, deprecated lazy imports, and optionally the results of a chained directory listing function to create a comprehensive list of attributes.

Parameters:

Name Type Description Default
chained_dir_fn Callable[[], list[str]] | None

An optional function that returns a list of attribute names, used to extend the directory listing.

None

Returns:

Type Description
list[str]

A list of attribute names representing the module's directory.

Source code in monkay/_monkay_exports.py
def module_dir_fn(
    self,
    *,
    chained_dir_fn: Callable[[], list[str]] | None = None,
) -> list[str]:
    """
    Generates a directory listing for the module, including lazy and deprecated imports.

    This method combines the module's `__all__` variable, lazy imports, deprecated lazy imports,
    and optionally the results of a chained directory listing function to create a comprehensive
    list of attributes.

    Args:
        chained_dir_fn: An optional function that returns a list of attribute names,
                          used to extend the directory listing.

    Returns:
        A list of attribute names representing the module's directory.
    """
    baseset = set(self.globals_dict.get("__all__", None) or _empty)
    baseset.update(self.lazy_imports.keys())
    baseset.update(self.deprecated_lazy_imports.keys())
    if chained_dir_fn is None:
        baseset.update(self.globals_dict.keys())
    else:
        baseset.update(chained_dir_fn())
    return list(baseset)

module_getter

module_getter(key, *, chained_getter=_stub_previous_getattr, no_warn_deprecated=False, check_globals_dict=False)

Module Getter which handles lazy imports.

This method acts as a custom attribute getter for the module, handling lazy imports and deprecated attributes. It first checks if the attribute exists in the module's globals dictionary. If not, it checks for lazy or deprecated lazy imports. If found, it resolves and returns the imported object.

Parameters:

Name Type Description Default
key str

The name of the attribute to retrieve.

required
chained_getter Callable[[str], Any]

A fallback getter function to call if the attribute is not found in lazy imports.

_stub_previous_getattr
no_warn_deprecated bool

If True, suppresses deprecation warnings for deprecated attributes.

False
check_globals_dict bool | Literal['fail']

If True, checks the module's globals dictionary first. If "fail", raises InGlobalsDict if found.

False

Returns:

Type Description
Any

The retrieved attribute value.

Raises:

Type Description
InglobalsDict

If check_globals_dict is "fail" and the attribute is found in globals.

DeprecationWarning

If a deprecated attribute is accessed and no_warn_deprecated is False.

Source code in monkay/_monkay_exports.py
def module_getter(
    self,
    key: str,
    *,
    chained_getter: Callable[[str], Any] = _stub_previous_getattr,
    no_warn_deprecated: bool = False,
    check_globals_dict: bool | Literal["fail"] = False,
) -> Any:
    """
    Module Getter which handles lazy imports.

    This method acts as a custom attribute getter for the module, handling lazy imports and deprecated attributes.
    It first checks if the attribute exists in the module's globals dictionary. If not, it checks for lazy or
    deprecated lazy imports. If found, it resolves and returns the imported object.

    Args:
        key: The name of the attribute to retrieve.
        chained_getter: A fallback getter function to call if the attribute is not found in lazy imports.
        no_warn_deprecated: If True, suppresses deprecation warnings for deprecated attributes.
        check_globals_dict: If True, checks the module's globals dictionary first. If "fail", raises InGlobalsDict if found.

    Returns:
        The retrieved attribute value.

    Raises:
        InglobalsDict: If `check_globals_dict` is "fail" and the attribute is found in globals.
        DeprecationWarning: If a deprecated attribute is accessed and `no_warn_deprecated` is False.
    """
    if check_globals_dict and key in self.globals_dict:
        if check_globals_dict == "fail":
            raise InGlobalsDict(f'"{key}" is defined as real variable.')
        return self.globals_dict[key]
    lazy_import = self.lazy_imports.get(key)
    if lazy_import is None:
        deprecated = self.deprecated_lazy_imports.get(key)
        if deprecated is not None:
            lazy_import = deprecated["path"]
            if not no_warn_deprecated:
                warn_strs = [f'Attribute: "{key}" is deprecated.']
                if deprecated.get("reason"):
                    # Note: no dot is added, this is the responsibility of the reason author.
                    warn_strs.append(f"Reason: {deprecated['reason']}")
                if deprecated.get("new_attribute"):
                    warn_strs.append(f'Use "{deprecated["new_attribute"]}" instead.')
                warnings.warn("\n".join(warn_strs), DeprecationWarning, stacklevel=2)

    if lazy_import is None:
        return chained_getter(key)
    if key not in self._cached_imports or key in self.uncached_imports:
        if callable(lazy_import):
            value: Any = lazy_import()
        else:
            value = load(lazy_import, package=self.package)
        if key in self.uncached_imports:
            return value
        else:
            self._cached_imports[key] = value
    return self._cached_imports[key]

update_all_var

update_all_var(all_var)

Updates the __all__ variable to include lazy and deprecated lazy imports.

This method ensures that all names defined as lazy or deprecated lazy imports are included in the module's __all__ variable.

Parameters:

Name Type Description Default
all_var Collection[str]

The current __all__ variable as a collection of strings.

required

Returns:

Type Description
list[str] | set[str]

The updated __all__ variable, either as a list or a set, depending on the input type.

Source code in monkay/_monkay_exports.py
def update_all_var(self, all_var: Collection[str]) -> list[str] | set[str]:
    """
    Updates the `__all__` variable to include lazy and deprecated lazy imports.

    This method ensures that all names defined as lazy or deprecated lazy imports
    are included in the module's `__all__` variable.

    Args:
        all_var: The current `__all__` variable as a collection of strings.

    Returns:
        The updated `__all__` variable, either as a list or a set, depending on the input type.
    """
    if isinstance(all_var, set):
        all_var_set = all_var
    else:
        if not isinstance(all_var, list):
            all_var = list(all_var)
        all_var_set = set(all_var)

    if self.lazy_imports or self.deprecated_lazy_imports:
        for var in chain(
            self.lazy_imports,
            self.deprecated_lazy_imports,
        ):
            if var not in all_var_set:
                if isinstance(all_var, list):
                    all_var.append(var)
                else:
                    cast(set[str], all_var).add(var)

    return cast("list[str] | set[str]", all_var)

with_settings

with_settings(settings, *, evaluate_settings_with=None)

Temporarily sets and yields new settings for the Monkay instance within a context.

This context manager allows temporarily overriding the settings associated with the Monkay instance. It yields the provided settings and then restores the original settings after the context exits.

Parameters:

Name Type Description Default
settings SETTINGS_DEFINITION_TYPE[SETTINGS] | None | Literal[False]

The new settings to use within the context, or None to temporarily use the real settings. Use False, "" to disable settings access temporarily

required
evaluate_settings_with EvaluateSettingsParameters | None

Evaluate settings with the parameters provided.

None

Yields:

Type Description
Generator[SETTINGS_DEFINITION_TYPE[SETTINGS] | Literal[False] | None]

The provided settings.

Raises:

Type Description
AssertionError

If Monkay is not enabled for settings.

Source code in monkay/_monkay_settings.py
@contextmanager
def with_settings(
    self,
    settings: SETTINGS_DEFINITION_TYPE[SETTINGS] | None | Literal[False],
    *,
    evaluate_settings_with: EvaluateSettingsParameters | None = None,
) -> Generator[SETTINGS_DEFINITION_TYPE[SETTINGS] | Literal[False] | None]:
    """
    Temporarily sets and yields new settings for the Monkay instance within a context.

    This context manager allows temporarily overriding the settings associated with the Monkay instance.
    It yields the provided settings and then restores the original settings after the context exits.

    Args:
        settings: The new settings to use within the context, or None to temporarily use the real settings. Use False, "" to disable settings access temporarily
        evaluate_settings_with: Evaluate settings with the parameters provided.

    Yields:
        The provided settings.

    Raises:
        AssertionError: If Monkay is not enabled for settings.
    """
    assert self._settings_var is not None, settings_not_enabled_error
    # why None, for temporary using the real settings
    token = self._settings_var.set(
        (["" if settings is False else settings], [False]) if settings is not None else None
    )
    try:
        if evaluate_settings_with is not None:
            self.evaluate_settings(**evaluate_settings_with)
        yield settings
    finally:
        self._settings_var.reset(token)

apply_extensions

apply_extensions(*, use_overwrite=True)

Applies registered extensions to the Monkay instance.

This method iterates through the registered extensions, applies them in the specified order, and manages the application process to prevent recursive or concurrent application issues.

Parameters:

Name Type Description Default
use_overwrite bool

If True, uses the extensions from the _extensions_var if available; otherwise, uses the default _extensions.

True

Raises: AssertionError: If Monkay is not enabled for extensions. RuntimeError: If another extension application process is already active in the same context.

Source code in monkay/_monkay_extensions.py
def apply_extensions(self, *, use_overwrite: bool = True) -> None:
    """
    Applies registered extensions to the Monkay instance.

    This method iterates through the registered extensions, applies them in the specified order,
    and manages the application process to prevent recursive or concurrent application issues.

    Args:
        use_overwrite: If True, uses the extensions from the `_extensions_var` if available;
                       otherwise, uses the default `_extensions`.
    Raises:
        AssertionError: If Monkay is not enabled for extensions.
        RuntimeError: If another extension application process is already active in the same context.
    """
    assert self._extensions_var is not None, extensions_not_enabled_error
    extensions: dict[str, ExtensionProtocol[INSTANCE, SETTINGS]] | None = (
        self._extensions_var.get() if use_overwrite else None
    )
    if extensions is None:
        extensions = self._extensions
    extensions_applied = self._extensions_applied_var.get()
    if extensions_applied is not None:
        raise RuntimeError("Other apply process in the same context is active.")
    extensions_ordered: Iterable[tuple[str, ExtensionProtocol[INSTANCE, SETTINGS]]] = cast(
        dict[str, ExtensionProtocol[INSTANCE, SETTINGS]], extensions
    ).items()

    if self.extension_order_key_fn is not None:
        extensions_ordered = sorted(
            extensions_ordered,
            key=lambda entry: self.extension_order_key_fn(entry[1]),
        )
    extensions_applied = set()
    token = self._extensions_applied_var.set(extensions_applied)
    try:
        for name, extension in extensions_ordered:
            if name in extensions_applied:
                continue
            # despite slightly inaccurate (added before applying actually) this ensures that no loops appear
            extensions_applied.add(name)
            extension.apply(cast("Monkay[INSTANCE, SETTINGS]", self))
    finally:
        self._extensions_applied_var.reset(token)

ensure_extension

ensure_extension(name_or_extension)

Ensures that a specific extension is applied to the Monkay instance.

This method checks if the given extension (either by name or instance) is already applied. If not, it applies the extension, preventing recursive application issues.

Parameters:

Name Type Description Default
name_or_extension str | ExtensionProtocol[INSTANCE, SETTINGS]

The name of the extension or an instance of the extension.

required

Raises:

Type Description
AssertionError

If Monkay is not enabled for extensions or if applying extensions is not active.

RuntimeError

If the provided extension does not implement the ExtensionProtocol, or if the extension does not exist.

Source code in monkay/_monkay_extensions.py
def ensure_extension(
    self, name_or_extension: str | ExtensionProtocol[INSTANCE, SETTINGS]
) -> None:
    """
    Ensures that a specific extension is applied to the Monkay instance.

    This method checks if the given extension (either by name or instance) is already applied.
    If not, it applies the extension, preventing recursive application issues.

    Args:
        name_or_extension: The name of the extension or an instance of the extension.

    Raises:
        AssertionError: If Monkay is not enabled for extensions or if applying extensions is not active.
        RuntimeError: If the provided extension does not implement the ExtensionProtocol,
                      or if the extension does not exist.
    """
    assert self._extensions_var is not None, extensions_not_enabled_error
    extensions_applied = self._extensions_applied_var.get()
    assert extensions_applied is not None, "Applying extensions not active."
    extensions: dict[str, ExtensionProtocol[INSTANCE, SETTINGS]] | None = (
        self._extensions_var.get()
    )
    if extensions is None:
        extensions = self._extensions
    if isinstance(name_or_extension, str):
        name = name_or_extension
        extension = extensions.get(name)
    elif not isclass(name_or_extension) and isinstance(name_or_extension, ExtensionProtocol):
        name = name_or_extension.name
        extension = extensions.get(name, name_or_extension)
    else:
        raise RuntimeError(
            'Provided extension "{name_or_extension}" does not implement the ExtensionProtocol'
        )
    if name in extensions_applied:
        return

    if extension is None:
        raise RuntimeError(f'Extension: "{name}" does not exist.')
    # despite slightly inaccurate (added before applying actually) this ensures that no loops appear
    extensions_applied.add(name)
    extension.apply(cast("Monkay[INSTANCE, SETTINGS]", self))

add_extension

add_extension(extension, *, use_overwrite=True, on_conflict='error')

Register an extension on the active extension registry.

The method accepts an already-instantiated extension, an extension class, or a zero-argument factory. The input is normalized to an extension instance and then inserted into the currently active registry (context override or base registry).

Conflict handling is explicit and validated at runtime: "error" raises, "keep" preserves the existing extension, and "replace" overwrites the existing one.

Parameters:

Name Type Description Default
extension ExtensionProtocol[INSTANCE, SETTINGS] | type[ExtensionProtocol[INSTANCE, SETTINGS]] | Callable[[], ExtensionProtocol[INSTANCE, SETTINGS]]

Extension instance, extension class, or factory callable.

required
use_overwrite bool

Use the active with_extensions override registry when available. If False, always target the base registry.

True
on_conflict Literal['error', 'keep', 'replace']

Strategy used when an extension with the same name is already present. Supported values are "error", "keep", and "replace".

'error'

Raises:

Type Description
AssertionError

If extensions support is not enabled.

ValueError

If extension is incompatible with :class:~monkay.types.ExtensionProtocol or if on_conflict is not supported.

KeyError

If the extension name already exists and on_conflict is "error".

Examples:

>>> monkay.add_extension(MyExtension())
>>> monkay.add_extension(MyExtension, on_conflict="replace")
Notes

on_conflict is validated even when no name collision happens. This keeps behavior deterministic across input data sets.

Source code in monkay/_monkay_extensions.py
def add_extension(
    self,
    extension: ExtensionProtocol[INSTANCE, SETTINGS]
    | type[ExtensionProtocol[INSTANCE, SETTINGS]]
    | Callable[[], ExtensionProtocol[INSTANCE, SETTINGS]],
    *,
    use_overwrite: bool = True,
    on_conflict: Literal["error", "keep", "replace"] = "error",
) -> None:
    """Register an extension on the active extension registry.

    The method accepts an already-instantiated extension, an extension class,
    or a zero-argument factory. The input is normalized to an extension
    instance and then inserted into the currently active registry (context
    override or base registry).

    Conflict handling is explicit and validated at runtime:
    ``"error"`` raises, ``"keep"`` preserves the existing extension,
    and ``"replace"`` overwrites the existing one.

    Args:
        extension: Extension instance, extension class, or factory callable.
        use_overwrite: Use the active ``with_extensions`` override registry
            when available. If ``False``, always target the base registry.
        on_conflict: Strategy used when an extension with the same name is
            already present. Supported values are ``"error"``,
            ``"keep"``, and ``"replace"``.

    Raises:
        AssertionError: If extensions support is not enabled.
        ValueError: If ``extension`` is incompatible with
            :class:`~monkay.types.ExtensionProtocol` or if ``on_conflict`` is
            not supported.
        KeyError: If the extension name already exists and ``on_conflict`` is
            ``"error"``.

    Examples:
        >>> monkay.add_extension(MyExtension())
        >>> monkay.add_extension(MyExtension, on_conflict="replace")

    Notes:
        ``on_conflict`` is validated even when no name collision happens.
        This keeps behavior deterministic across input data sets.
    """
    assert self._extensions_var is not None, extensions_not_enabled_error
    on_conflict = validate_conflict_mode(on_conflict)
    extensions: dict[str, ExtensionProtocol[INSTANCE, SETTINGS]] | None = (
        self._extensions_var.get() if use_overwrite else None
    )
    if extensions is None:
        extensions = self._extensions
    if callable(extension) or isclass(extension):
        extension = extension()
    if not isinstance(extension, ExtensionProtocol):
        raise ValueError(f"Extension {extension} is not compatible")
    if extension.name in extensions:
        if on_conflict == "error":
            raise KeyError(f'Extension "{extension.name}" already exists.')
        elif on_conflict == "keep":
            return
    extensions[extension.name] = extension

with_extensions

with_extensions(extensions, *, apply_extensions=False)

Temporarily sets and yields a new set of extensions for the Monkay instance.

This method allows temporarily overriding the registered extensions within a context. It yields the provided extensions (or None to temporarily use the real extensions), and then restores the original extensions after the context exits.

Parameters:

Name Type Description Default
extensions dict[str, ExtensionProtocol[INSTANCE, SETTINGS]] | None

The new set of extensions to use within the context, or None to temporarily use the real extensions.

required
apply_extensions bool

If True, applies the temporary extensions immediately after setting them.

False

Yields:

Type Description
Generator[dict[str, ExtensionProtocol[INSTANCE, SETTINGS]] | None]

The provided extensions (or None).

Raises:

Type Description
AssertionError

If Monkay is not enabled for extensions.

Source code in monkay/_monkay_extensions.py
@contextmanager
def with_extensions(
    self,
    extensions: dict[str, ExtensionProtocol[INSTANCE, SETTINGS]] | None,
    *,
    apply_extensions: bool = False,
) -> Generator[dict[str, ExtensionProtocol[INSTANCE, SETTINGS]] | None]:
    """
    Temporarily sets and yields a new set of extensions for the Monkay instance.

    This method allows temporarily overriding the registered extensions within a context.
    It yields the provided extensions (or None to temporarily use the real extensions),
    and then restores the original extensions after the context exits.

    Args:
        extensions: The new set of extensions to use within the context, or None to temporarily use the real extensions.
        apply_extensions: If True, applies the temporary extensions immediately after setting them.

    Yields:
        The provided extensions (or None).

    Raises:
        AssertionError: If Monkay is not enabled for extensions.
    """
    # why None, for temporary using the real extensions
    assert self._extensions_var is not None, extensions_not_enabled_error
    token = self._extensions_var.set(extensions)
    try:
        if apply_extensions and self.instance is not None:
            self.apply_extensions()
        yield extensions
    finally:
        self._extensions_var.reset(token)

set_instance

set_instance(instance, *, apply_extensions=True, use_extensions_overwrite=True)

Sets the instance managed by Monkay and optionally applies extensions.

This method updates the instance associated with the Monkay object. It also allows applying extensions immediately after setting the instance.

Parameters:

Name Type Description Default
instance INSTANCE | None

The new instance to set, or None to unset the instance.

required
apply_extensions bool

If True, applies the registered extensions after setting the instance.

True
use_extensions_overwrite bool

If True, uses the extensions from the _extensions_var if available; otherwise, uses the default _extensions.

True

Returns:

Type Description
INSTANCE | None

The set instance.

Raises:

Type Description
AssertionError

If Monkay is not enabled for instances.

RuntimeError

If another extension application process is already active in the same context.

Source code in monkay/_monkay_instance.py
def set_instance(
    self,
    instance: INSTANCE | None,
    *,
    apply_extensions: bool = True,
    use_extensions_overwrite: bool = True,
) -> INSTANCE | None:
    """
    Sets the instance managed by Monkay and optionally applies extensions.

    This method updates the instance associated with the Monkay object. It also allows applying extensions
    immediately after setting the instance.

    Args:
        instance: The new instance to set, or None to unset the instance.
        apply_extensions: If True, applies the registered extensions after setting the instance.
        use_extensions_overwrite: If True, uses the extensions from the `_extensions_var` if available;
                                   otherwise, uses the default `_extensions`.

    Returns:
        The set instance.

    Raises:
        AssertionError: If Monkay is not enabled for instances.
        RuntimeError: If another extension application process is already active in the same context.
    """
    assert self._instance_var is not None, instance_not_enabled_error
    # need to address before the instance is swapped
    if (
        apply_extensions
        and self._extensions_var is not None
        and self._extensions_applied_var.get() is not None
    ):
        raise RuntimeError("Other apply process in the same context is active.")
    self._instance = instance
    if apply_extensions and instance is not None and self._extensions_var is not None:
        # unapply a potential instance overwrite
        with self.with_instance(None):
            self.apply_extensions(use_overwrite=use_extensions_overwrite)
    return instance

with_instance

with_instance(instance, *, apply_extensions=False, use_extensions_overwrite=True)

Temporarily sets and yields a new instance for the Monkay object within a context.

This context manager allows temporarily overriding the instance associated with the Monkay object. It yields the provided instance and then restores the original instance after the context exits.

Parameters:

Name Type Description Default
instance INSTANCE | None

The new instance to use within the context, or None to temporarily unset the instance.

required
apply_extensions bool

If True, applies the registered extensions after setting the instance.

False
use_extensions_overwrite bool

If True, uses the extensions from the _extensions_var if available; otherwise, uses the default _extensions.

True

Yields:

Type Description
Generator[INSTANCE | None]

The provided instance.

Raises:

Type Description
AssertionError

If Monkay is not enabled for instances.

RuntimeError

If another extension application process is already active in the same context.

Source code in monkay/_monkay_instance.py
@contextmanager
def with_instance(
    self,
    instance: INSTANCE | None,
    *,
    apply_extensions: bool = False,
    use_extensions_overwrite: bool = True,
) -> Generator[INSTANCE | None]:
    """
    Temporarily sets and yields a new instance for the Monkay object within a context.

    This context manager allows temporarily overriding the instance associated with the Monkay object.
    It yields the provided instance and then restores the original instance after the context exits.

    Args:
        instance: The new instance to use within the context, or None to temporarily unset the instance.
        apply_extensions: If True, applies the registered extensions after setting the instance.
        use_extensions_overwrite: If True, uses the extensions from the `_extensions_var` if available;
                                   otherwise, uses the default `_extensions`.

    Yields:
        The provided instance.

    Raises:
        AssertionError: If Monkay is not enabled for instances.
        RuntimeError: If another extension application process is already active in the same context.
    """
    assert self._instance_var is not None, instance_not_enabled_error
    # need to address before the instance is swapped
    if (
        apply_extensions
        and self._extensions_var is not None
        and self._extensions_applied_var.get() is not None
    ):
        raise RuntimeError("Other apply process in the same context is active.")
    token = self._instance_var.set(instance)
    try:
        if apply_extensions and self._extensions_var is not None:
            self.apply_extensions(use_overwrite=use_extensions_overwrite)
        yield instance
    finally:
        self._instance_var.reset(token)

Base Helpers

monkay.base

Undefined

Source code in monkay/base.py
class Undefined: ...

InGlobalsDict

Bases: Exception

Source code in monkay/base.py
class InGlobalsDict(Exception): ...

UnsetError

Bases: RuntimeError

Source code in monkay/base.py
class UnsetError(RuntimeError): ...

load

load(path, *, allow_splits=':.', package=None)

Dynamically loads an object from a module given its path.

This function takes a string representing the path to an object within a module and dynamically imports the module and retrieves the object.

Parameters:

Name Type Description Default
path str

The path to the object, in the format "module:object" or "module.object".

required
allow_splits str

A string specifying the allowed separators for module and object names. Defaults to ":." allowing both ":" and "." as separators.

':.'
package None | str

The package name to use as a context for relative imports.

None

Returns:

Type Description
Any

The loaded object.

Raises:

Type Description
ValueError

If the path is invalid or cannot be parsed.

ImportError

If the module cannot be imported.

Source code in monkay/base.py
def load(path: str, *, allow_splits: str = ":.", package: None | str = None) -> Any:
    """
    Dynamically loads an object from a module given its path.

    This function takes a string representing the path to an object within a module
    and dynamically imports the module and retrieves the object.

    Args:
        path: The path to the object, in the format "module:object" or "module.object".
        allow_splits: A string specifying the allowed separators for module and object names.
                      Defaults to ":." allowing both ":" and "." as separators.
        package: The package name to use as a context for relative imports.

    Returns:
        The loaded object.

    Raises:
        ValueError: If the path is invalid or cannot be parsed.
        ImportError: If the module cannot be imported.
    """
    splitted = path.rsplit(":", 1) if ":" in allow_splits else []
    if len(splitted) < 2 and "." in allow_splits:
        splitted = path.rsplit(".", 1)
    if len(splitted) != 2:
        raise ValueError(f"invalid path: {path}")
    module = import_module(splitted[0], package)
    try:
        return getattr(module, splitted[1])
    except AttributeError as exc:
        # some implementations may have not this variable, so fallback to False
        if getattr(module.__spec__, "_initializing", False):
            raise ImportError(
                f'Import of "{splitted[1]}" failed, but the module is initializing. '
                f'You probably have a circular import in "{splitted[0]}".'
            ) from exc
        raise ImportError(f'Import of "{splitted[1]}" from "{splitted[0]}" failed.') from exc

load_any

load_any(path, attrs, *, non_first_deprecated=False, package=None)

Dynamically loads any of the specified attributes from a module.

This function takes a module path and a collection of attribute names. It attempts to import the module and retrieve each attribute in the given order. If any of the attributes are found, it returns the first one found.

Parameters:

Name Type Description Default
path str

The path to the module.

required
attrs Collection[str]

A collection of attribute names to search for.

required
non_first_deprecated bool

If True, issues deprecation warnings for all found attributes except the first one.

False
package None | str

The package name to use as a context for relative imports.

None

Returns:

Type Description
Any | None

The first found attribute, or None if none of the attributes are found.

Raises:

Type Description
ImportError

If the module cannot be imported or none of the attributes are found.

DeprecationWarning

If non_first_deprecated is True and a non-first attribute is found.

Source code in monkay/base.py
def load_any(
    path: str,
    attrs: Collection[str],
    *,
    non_first_deprecated: bool = False,
    package: None | str = None,
) -> Any | None:
    """
    Dynamically loads any of the specified attributes from a module.

    This function takes a module path and a collection of attribute names. It attempts
    to import the module and retrieve each attribute in the given order. If any of the
    attributes are found, it returns the first one found.

    Args:
        path: The path to the module.
        attrs: A collection of attribute names to search for.
        non_first_deprecated: If True, issues deprecation warnings for all found attributes
                               except the first one.
        package: The package name to use as a context for relative imports.

    Returns:
        The first found attribute, or None if none of the attributes are found.

    Raises:
        ImportError: If the module cannot be imported or none of the attributes are found.
        DeprecationWarning: If `non_first_deprecated` is True and a non-first attribute is found.
    """
    module = import_module(path, package)
    first_name: None | str = None

    for attr in attrs:
        if hasattr(module, attr):
            if non_first_deprecated and first_name is not None:
                warnings.warn(
                    f'"{attr}" is deprecated, use "{first_name}" instead.',
                    DeprecationWarning,
                    stacklevel=2,
                )
            return getattr(module, attr)
        if first_name is None:
            first_name = attr

    # some implementations may have not this variable, so fallback to False
    if getattr(module.__spec__, "_initializing", False):
        raise ImportError(
            f"Could not import any of the attributes:.{', '.join(attrs)}, but the module is initializing. "
            f'You probably have a circular import in "{path}".'
        )
    raise ImportError(f'Could not import any of the attributes:.{", ".join(attrs)} from "{path}".')

absolutify_import

absolutify_import(import_path, package)

Converts a relative import path to an absolute import path.

This function takes an import path and a package name and converts the relative import path to an absolute path by prepending the package name and adjusting for relative levels (e.g., "..module").

Parameters:

Name Type Description Default
import_path str

The import path to absolutify.

required
package str | None

The package name to use as a base for relative imports.

required

Returns:

Type Description
str

The absolute import path.

Raises:

Type Description
ValueError

If the import path is invalid or tries to cross parent boundaries.

Source code in monkay/base.py
def absolutify_import(import_path: str, package: str | None) -> str:
    """
    Converts a relative import path to an absolute import path.

    This function takes an import path and a package name and converts the relative
    import path to an absolute path by prepending the package name and adjusting
    for relative levels (e.g., "..module").

    Args:
        import_path: The import path to absolutify.
        package: The package name to use as a base for relative imports.

    Returns:
        The absolute import path.

    Raises:
        ValueError: If the import path is invalid or tries to cross parent boundaries.
    """
    if not package or not import_path:
        return import_path
    dot_count: int = 0
    try:
        while import_path[dot_count] == ".":
            dot_count += 1
    except IndexError:
        raise ValueError("not an import path") from None
    if dot_count == 0:
        return import_path
    if dot_count - 2 > package.count("."):
        raise ValueError("Out of bound, tried to cross parent.")
    if dot_count > 1:
        package = package.rsplit(".", dot_count - 1)[0]

    return f"{package}.{import_path.lstrip('.')}"

get_value_from_settings

get_value_from_settings(settings, name)

Retrieves a value from a settings object, supporting both attribute and dictionary access.

This function attempts to retrieve a value from a settings object. It first tries to access the value as an attribute. If that fails, it tries to access the value as a dictionary key.

Parameters:

Name Type Description Default
settings Any

The settings object to retrieve the value from.

required
name str

The name of the attribute or key to retrieve.

required

Returns:

Type Description
Any

The retrieved value.

Raises:

Type Description
AttributeError

If the name is not found as an attribute and the settings object does not support dictionary access.

KeyError

If the name is not found as a key in the settings object and attribute access also fails.

Source code in monkay/base.py
def get_value_from_settings(settings: Any, name: str) -> Any:
    """
    Retrieves a value from a settings object, supporting both attribute and dictionary access.

    This function attempts to retrieve a value from a settings object. It first tries to access
    the value as an attribute. If that fails, it tries to access the value as a dictionary key.

    Args:
        settings: The settings object to retrieve the value from.
        name: The name of the attribute or key to retrieve.

    Returns:
        The retrieved value.

    Raises:
        AttributeError: If the name is not found as an attribute and the settings object does not support dictionary access.
        KeyError: If the name is not found as a key in the settings object and attribute access also fails.
    """
    try:
        return getattr(settings, name)
    except AttributeError:
        return settings[name]

evaluate_preloads

evaluate_preloads(preloads, *, ignore_import_errors=True, package=None)

Evaluates preload modules or functions specified in settings.

This function iterates through a collection of preload paths, imports the modules, and optionally calls specified functions within those modules.

Parameters:

Name Type Description Default
preloads Iterable[str]

An iterable of preload paths, in the format "module" or "module:function".

required
ignore_import_errors bool

If True, ignores import errors and continues processing.

True
package str | None

The package name to use as a context for relative imports.

None

Returns:

Type Description
bool

True if all preloads were successfully evaluated, False otherwise.

Raises:

Type Description
ImportError

If a module cannot be imported and ignore_import_errors is False.

Source code in monkay/base.py
def evaluate_preloads(
    preloads: Iterable[str], *, ignore_import_errors: bool = True, package: str | None = None
) -> bool:
    """
    Evaluates preload modules or functions specified in settings.

    This function iterates through a collection of preload paths, imports the modules,
    and optionally calls specified functions within those modules.

    Args:
        preloads: An iterable of preload paths, in the format "module" or "module:function".
        ignore_import_errors: If True, ignores import errors and continues processing.
        package: The package name to use as a context for relative imports.

    Returns:
        True if all preloads were successfully evaluated, False otherwise.

    Raises:
        ImportError: If a module cannot be imported and `ignore_import_errors` is False.
    """
    no_errors: bool = True
    for preload in preloads:
        splitted = preload.rsplit(":", 1)
        try:
            module = import_module(splitted[0], package)
        except ImportError as exc:
            if not ignore_import_errors:
                raise exc
            no_errors = False
            continue
        if len(splitted) == 2:
            getattr(module, splitted[1])()
    return no_errors

Cages

monkay.cages

T module-attribute

T = TypeVar('T')

DEFAULT module-attribute

DEFAULT = TypeVar('DEFAULT')

forbidden_names module-attribute

forbidden_names = {'__getattribute__', '__setattr__', '__delattr__', '__new__', '__init__'}

context_var_attributes module-attribute

context_var_attributes = {'name', 'get', 'set', 'reset'}

Undefined

Source code in monkay/cages.py
class Undefined: ...

Cage

Bases: Generic[T]

A container class that manages a value with context-aware modifications and synchronization.

Source code in monkay/cages.py
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
class Cage(Generic[T]):
    """
    A container class that manages a value with context-aware modifications and synchronization.
    """

    monkay_context_var: ContextVar[tuple[int | None, T] | type[Undefined]]
    """
    A context variable that stores the current value and update timestamp within a specific context.
    This allows for context-specific modifications and retrieval of the value.
    """

    monkay_deep_copy: bool
    """
    A boolean indicating whether deep copies should be used when modifying the value.
    If True, modifications will create deep copies of the original value to avoid unintended side effects.
    """

    monkay_use_wrapper_for_reads: bool
    """
    A boolean indicating whether a wrapper should be used for read operations.
    If True, read operations will be wrapped in a context manager to ensure consistency.
    """

    monkay_update_fn: Callable[[T, T], T] | None
    """
    An optional function that defines how to update the value when modifications are made.
    This function takes the current value and the new value as input and returns the updated value.
    """

    monkay_name: str
    """
    A name associated with the Cage instance, used for identification and debugging purposes.
    """

    monkay_original: T
    """
    The original value stored in the Cage instance.
    This attribute holds the initial value that is managed by the Cage.
    """

    monkay_original_last_update: int
    """
    A timestamp indicating the last time the original value was updated.
    This timestamp is used to track changes and ensure consistency.
    """

    monkay_original_last_update_lock: None | Lock
    """
    An optional lock used to synchronize updates to the original value.
    This lock prevents race conditions when multiple threads or processes try to update the value concurrently.
    """

    monkay_original_wrapper: AbstractContextManager
    """
    A context manager used to wrap access to the original value, ensuring consistency and synchronization.
    This wrapper provides a controlled environment for read and write operations on the original value.
    """

    def __new__(
        cls,
        globals_dict: dict,
        obj: T | type[Undefined] = Undefined,
        *,
        name: str,
        preloads: Iterable[str] = (),
        context_var_name: str = "_{name}_ctx",
        deep_copy: bool = False,
        # for e.g. locks
        original_wrapper: AbstractContextManager = nullcontext(),
        update_fn: Callable[[T, T], T] | None = None,
        use_wrapper_for_reads: bool = False,
        skip_self_register: bool = False,
        package: str | None = "",
    ) -> Cage:
        """
        Creates a new Cage instance, wrapping an object and managing its context.

        This method initializes a Cage instance, wrapping the provided object and setting up
        context management. It handles preloads, context variable creation, and attribute forwarding.

        Args:
            globals_dict: The globals dictionary of the module where the Cage is created.
            obj: The object to wrap, or Undefined to retrieve it from globals_dict.
            name: The name of the object.
            preloads: An iterable of preload paths to execute before creating the Cage.
            context_var_name: The name of the context variable to create.
            deep_copy: If True, uses deep copies for modifications.
            original_wrapper: A context manager to wrap access to the original object.
            update_fn: An optional function to define how to update the object.
            use_wrapper_for_reads: If True, uses the wrapper for read operations.
            skip_self_register: If True, skips registering the Cage instance in globals_dict.
            package: The package name to use for relative imports.

        Returns:
            A new Cage instance.

        Raises:
            AssertionError: If the object is Undefined and not found in globals_dict.
            ImportError: If a preload module cannot be imported.
            AttributeError: If a preload function cannot be found.
        """
        if package == "" and globals_dict.get("__spec__"):
            package = globals_dict["__spec__"].parent
        package = package or None
        evaluate_preloads(preloads, ignore_import_errors=True, package=package)
        if obj is Undefined:
            obj = globals_dict[name]
        assert obj is not Undefined, "not initialized"
        if not skip_self_register and isinstance(obj, Cage):
            return obj
        context_var_name = context_var_name.format(name=name)
        obj_type = type(obj)
        attrs: dict = {}
        for attr in dir(obj_type):
            if not attr.startswith("__") or not attr.endswith("__") or attr in forbidden_names:
                continue
            val = getattr(obj_type, attr)
            if ismethoddescriptor(val):
                # we need to add the wrapper to the dict
                attrs[attr] = cls.monkay_forward(obj_type, attr)
        attrs["__new__"] = object.__new__
        monkay_cage_cls = type(cls.__name__, (cls,), attrs)
        monkay_cage_instance = monkay_cage_cls()
        monkay_cage_instance.monkay_name = name
        monkay_cage_instance.monkay_context_var = globals_dict[context_var_name] = ContextVar(
            context_var_name, default=Undefined
        )
        monkay_cage_instance.monkay_deep_copy = deep_copy
        monkay_cage_instance.monkay_use_wrapper_for_reads = use_wrapper_for_reads
        monkay_cage_instance.monkay_update_fn = update_fn
        monkay_cage_instance.monkay_original = obj
        monkay_cage_instance.monkay_original_last_update = 0
        monkay_cage_instance.monkay_original_last_update_lock = (
            None if update_fn is None else Lock()
        )
        monkay_cage_instance.monkay_original_wrapper = original_wrapper

        if not skip_self_register:
            globals_dict[name] = monkay_cage_instance
        return monkay_cage_instance

    @classmethod
    def monkay_forward(cls, obj_type: type, name: str) -> Any:
        """
        Creates a wrapper function that forwards method calls to the wrapped object.

        This class method generates a wrapper function that intercepts method calls to the Cage instance
        and forwards them to the wrapped object, ensuring that the object is updated or copied as needed.

        Args:
            obj_type: The type of the wrapped object.
            name: The name of the method to forward.

        Returns:
            A wrapper function that forwards method calls.
        """

        @wraps(getattr(obj_type, name))
        def _(self, *args: Any, **kwargs: Any):
            obj = self.monkay_conditional_update_copy()
            return getattr(obj, name)(*args, **kwargs)

        return _

    def monkay_refresh_copy(
        self,
        *,
        obj: T | type[Undefined] = Undefined,
        use_wrapper: bool | None = None,
        _monkay_dict: dict | None = None,
    ) -> T:
        """
        Refreshes the context variable with a copy of the original object.

        This method updates the context variable with a fresh copy of the original object,
        optionally using a wrapper and handling deep or shallow copies.

        Args:
            obj: An optional object to use instead of creating a copy.
            use_wrapper: If True, uses the original wrapper when accessing the original object.
                         If None, uses the Cage's default `monkay_use_wrapper_for_reads` value.
            _monkay_dict: An optional dictionary to use instead of the Cage's `__dict__`.

        Returns:
            The refreshed object.
        """
        if _monkay_dict is None:
            _monkay_dict = super().__getattribute__("__dict__")
        if use_wrapper is None:
            use_wrapper = _monkay_dict["monkay_use_wrapper_for_reads"]
        if obj is Undefined:
            with _monkay_dict["monkay_original_wrapper"] if use_wrapper else nullcontext():
                obj = (
                    copy.deepcopy(_monkay_dict["monkay_original"])
                    if _monkay_dict["monkay_deep_copy"]
                    else copy.copy(_monkay_dict["monkay_original"])
                )
        _monkay_dict["monkay_context_var"].set((_monkay_dict["monkay_original_last_update"], obj))
        return cast(T, obj)

    def monkay_conditional_update_copy(
        self, *, use_wrapper: bool | None = None, _monkay_dict: dict | None = None
    ) -> T:
        """
        Retrieves a context-aware copy of the original object, updating it if necessary.

        This method retrieves a copy of the original object, updating it based on context and update functions.
        It checks if the context variable is set and if the original object has been updated, and applies
        the update function if necessary.

        Args:
            use_wrapper: If True, uses the original wrapper when accessing the original object.
                         If None, uses the Cage's default `monkay_use_wrapper_for_reads` value.
            _monkay_dict: An optional dictionary to use instead of the Cage's `__dict__`.

        Returns:
            The context-aware copy of the object.
        """
        if _monkay_dict is None:
            _monkay_dict = super().__getattribute__("__dict__")
        if use_wrapper is None:
            use_wrapper = _monkay_dict["monkay_use_wrapper_for_reads"]
        tup = _monkay_dict["monkay_context_var"].get()
        if tup is Undefined:
            obj = self.monkay_refresh_copy(_monkay_dict=_monkay_dict)
        elif (
            _monkay_dict["monkay_update_fn"] is not None
            and tup[0] is not None
            and tup[0] != _monkay_dict["monkay_original_last_update"]
        ):
            with _monkay_dict["monkay_original_wrapper"] if use_wrapper else nullcontext():
                obj = _monkay_dict["monkay_update_fn"](tup[1], _monkay_dict["monkay_original"])
            obj = self.monkay_refresh_copy(
                obj=obj, _monkay_dict=_monkay_dict, use_wrapper=use_wrapper
            )
        else:
            obj = tup[1]
        return obj

    def __getattribute__(self, name: str) -> Any:
        """
        Overrides attribute access to retrieve attributes from the context-aware copy.

        This method intercepts attribute access and retrieves the attribute from the context-aware copy
        of the object, ensuring that the object is updated as needed.

        Args:
            name: The name of the attribute to retrieve.

        Returns:
            The retrieved attribute value.
        """
        if name in forbidden_names or name.startswith("monkay_"):
            return super().__getattribute__(name)
        obj = self.monkay_conditional_update_copy()

        return getattr(obj, name)

    def __delattr__(
        self,
        name: str,
    ) -> None:
        """
        Overrides attribute deletion to delete attributes from the context-aware copy.

        This method intercepts attribute deletion and deletes the attribute from the context-aware copy
        of the object, ensuring that the object is updated as needed.

        Args:
            name: The name of the attribute to delete.
        """
        if name.startswith("monkay_"):
            super().__delattr__(name)
            return
        obj = self.monkay_conditional_update_copy()
        delattr(obj, name)

    def __setattr__(self, name: str, value: Any) -> None:
        """
        Overrides attribute setting to set attributes in the context-aware copy.

        This method intercepts attribute setting and sets the attribute in the context-aware copy
        of the object, ensuring that the object is updated as needed.

        Args:
            name: The name of the attribute to set.
            value: The value to set the attribute to.
        """
        if name.startswith("monkay_"):
            super().__setattr__(name, value)
            return
        obj = self.monkay_conditional_update_copy()
        setattr(obj, name, value)

    def monkay_proxied(
        self,
        use_wrapper: bool | None = None,
    ) -> T:
        """
        Returns a proxied version of the object, ensuring context-aware updates.

        This method returns a context-aware copy of the object, optionally using a wrapper.

        Args:
            use_wrapper: If True, uses the original wrapper when accessing the original object.
                         If None, uses the Cage's default `monkay_use_wrapper_for_reads` value.

        Returns:
            The proxied object.
        """
        return self.monkay_conditional_update_copy(use_wrapper=use_wrapper)

    @contextmanager
    def monkay_with_override(self, value: T, *, allow_value_update: bool = True) -> Generator[T]:
        """
        Temporarily overrides the context variable with a new value within a context.

        This context manager temporarily sets the context variable to a new value and yields it.
        After the context exits, the original context variable is restored.

        Args:
            value: The new value to set the context variable to.
            allow_value_update: If True, allows the value to be updated by the update function.
                                If False, the value will not be updated.

        Yields:
            The new value.
        """
        monkay_dict = super().__getattribute__("__dict__")
        token = monkay_dict["monkay_context_var"].set(
            (monkay_dict["monkay_original_last_update"] if allow_value_update else None, value)
        )
        try:
            yield value
        finally:
            monkay_dict["monkay_context_var"].reset(token)

    @contextmanager
    def monkay_with_original(
        self, use_wrapper: bool = True, update_after: bool = True
    ) -> Generator[T]:
        """
        Temporarily accesses the original value within a context, optionally updating it.

        This context manager yields the original value, optionally using a wrapper and updating
        the last update timestamp after the context exits.

        Args:
            use_wrapper: If True, uses the original wrapper when accessing the original value.
            update_after: If True, updates the last update timestamp after the context exits.

        Yields:
            The original value.
        """
        monkay_dict = super().__getattribute__("__dict__")
        wrapper = monkay_dict["monkay_original_wrapper"] if use_wrapper else nullcontext()
        with wrapper:
            yield monkay_dict["monkay_original"]
            if update_after and monkay_dict["monkay_original_last_update_lock"] is not None:
                with monkay_dict["monkay_original_last_update_lock"]:
                    monkay_dict["monkay_original_last_update"] += 1

    def monkay_set(self, value: T) -> Token:
        """
        Sets the context variable to a new value and returns a token.

        This method sets the context variable to a new value and returns a token that can be used
        to reset the context variable to its previous value.

        Args:
            value: The new value to set the context variable to.

        Returns:
            A token that can be used to reset the context variable.
        """
        monkay_dict = super().__getattribute__("__dict__")
        return monkay_dict["monkay_context_var"].set(
            (monkay_dict["monkay_original_last_update"], value)
        )

    def monkay_get(self, default: T | DEFAULT | None = None) -> T | DEFAULT | None:
        """
        Retrieves the current value of the context variable, or a default value if it's not set.

        This method retrieves the current value of the context variable. If the context variable is not set,
        it returns the original value or a default value if provided.

        Args:
            default: The default value to return if the context variable is not set.

        Returns:
            The current value of the context variable, the original value, or the default value.
        """
        monkay_dict = super().__getattribute__("__dict__")
        tup: type[Undefined] | tuple[int | None, T] = monkay_dict["monkay_context_var"].get()
        if tup is Undefined:
            original: T | type[Undefined] = monkay_dict["monkay_original"]
            if original is not Undefined:
                return cast("DEFAULT | None", original)
            else:
                return default
        else:
            return cast("tuple[int | None, T]", tup)[1]

    def monkay_reset(self, token: Token):
        """
        Resets the context variable to its previous value using a token.

        This method resets the context variable to its previous value using a token returned by `monkay_set`.

        Args:
            token: The token returned by `monkay_set`.
        """
        self.monkay_context_var.reset(token)

monkay_context_var instance-attribute

monkay_context_var

A context variable that stores the current value and update timestamp within a specific context. This allows for context-specific modifications and retrieval of the value.

monkay_deep_copy instance-attribute

monkay_deep_copy

A boolean indicating whether deep copies should be used when modifying the value. If True, modifications will create deep copies of the original value to avoid unintended side effects.

monkay_use_wrapper_for_reads instance-attribute

monkay_use_wrapper_for_reads

A boolean indicating whether a wrapper should be used for read operations. If True, read operations will be wrapped in a context manager to ensure consistency.

monkay_update_fn instance-attribute

monkay_update_fn

An optional function that defines how to update the value when modifications are made. This function takes the current value and the new value as input and returns the updated value.

monkay_name instance-attribute

monkay_name

A name associated with the Cage instance, used for identification and debugging purposes.

monkay_original instance-attribute

monkay_original

The original value stored in the Cage instance. This attribute holds the initial value that is managed by the Cage.

monkay_original_last_update instance-attribute

monkay_original_last_update

A timestamp indicating the last time the original value was updated. This timestamp is used to track changes and ensure consistency.

monkay_original_last_update_lock instance-attribute

monkay_original_last_update_lock

An optional lock used to synchronize updates to the original value. This lock prevents race conditions when multiple threads or processes try to update the value concurrently.

monkay_original_wrapper instance-attribute

monkay_original_wrapper

A context manager used to wrap access to the original value, ensuring consistency and synchronization. This wrapper provides a controlled environment for read and write operations on the original value.

monkay_forward classmethod

monkay_forward(obj_type, name)

Creates a wrapper function that forwards method calls to the wrapped object.

This class method generates a wrapper function that intercepts method calls to the Cage instance and forwards them to the wrapped object, ensuring that the object is updated or copied as needed.

Parameters:

Name Type Description Default
obj_type type

The type of the wrapped object.

required
name str

The name of the method to forward.

required

Returns:

Type Description
Any

A wrapper function that forwards method calls.

Source code in monkay/cages.py
@classmethod
def monkay_forward(cls, obj_type: type, name: str) -> Any:
    """
    Creates a wrapper function that forwards method calls to the wrapped object.

    This class method generates a wrapper function that intercepts method calls to the Cage instance
    and forwards them to the wrapped object, ensuring that the object is updated or copied as needed.

    Args:
        obj_type: The type of the wrapped object.
        name: The name of the method to forward.

    Returns:
        A wrapper function that forwards method calls.
    """

    @wraps(getattr(obj_type, name))
    def _(self, *args: Any, **kwargs: Any):
        obj = self.monkay_conditional_update_copy()
        return getattr(obj, name)(*args, **kwargs)

    return _

monkay_refresh_copy

monkay_refresh_copy(*, obj=Undefined, use_wrapper=None, _monkay_dict=None)

Refreshes the context variable with a copy of the original object.

This method updates the context variable with a fresh copy of the original object, optionally using a wrapper and handling deep or shallow copies.

Parameters:

Name Type Description Default
obj T | type[Undefined]

An optional object to use instead of creating a copy.

Undefined
use_wrapper bool | None

If True, uses the original wrapper when accessing the original object. If None, uses the Cage's default monkay_use_wrapper_for_reads value.

None
_monkay_dict dict | None

An optional dictionary to use instead of the Cage's __dict__.

None

Returns:

Type Description
T

The refreshed object.

Source code in monkay/cages.py
def monkay_refresh_copy(
    self,
    *,
    obj: T | type[Undefined] = Undefined,
    use_wrapper: bool | None = None,
    _monkay_dict: dict | None = None,
) -> T:
    """
    Refreshes the context variable with a copy of the original object.

    This method updates the context variable with a fresh copy of the original object,
    optionally using a wrapper and handling deep or shallow copies.

    Args:
        obj: An optional object to use instead of creating a copy.
        use_wrapper: If True, uses the original wrapper when accessing the original object.
                     If None, uses the Cage's default `monkay_use_wrapper_for_reads` value.
        _monkay_dict: An optional dictionary to use instead of the Cage's `__dict__`.

    Returns:
        The refreshed object.
    """
    if _monkay_dict is None:
        _monkay_dict = super().__getattribute__("__dict__")
    if use_wrapper is None:
        use_wrapper = _monkay_dict["monkay_use_wrapper_for_reads"]
    if obj is Undefined:
        with _monkay_dict["monkay_original_wrapper"] if use_wrapper else nullcontext():
            obj = (
                copy.deepcopy(_monkay_dict["monkay_original"])
                if _monkay_dict["monkay_deep_copy"]
                else copy.copy(_monkay_dict["monkay_original"])
            )
    _monkay_dict["monkay_context_var"].set((_monkay_dict["monkay_original_last_update"], obj))
    return cast(T, obj)

monkay_conditional_update_copy

monkay_conditional_update_copy(*, use_wrapper=None, _monkay_dict=None)

Retrieves a context-aware copy of the original object, updating it if necessary.

This method retrieves a copy of the original object, updating it based on context and update functions. It checks if the context variable is set and if the original object has been updated, and applies the update function if necessary.

Parameters:

Name Type Description Default
use_wrapper bool | None

If True, uses the original wrapper when accessing the original object. If None, uses the Cage's default monkay_use_wrapper_for_reads value.

None
_monkay_dict dict | None

An optional dictionary to use instead of the Cage's __dict__.

None

Returns:

Type Description
T

The context-aware copy of the object.

Source code in monkay/cages.py
def monkay_conditional_update_copy(
    self, *, use_wrapper: bool | None = None, _monkay_dict: dict | None = None
) -> T:
    """
    Retrieves a context-aware copy of the original object, updating it if necessary.

    This method retrieves a copy of the original object, updating it based on context and update functions.
    It checks if the context variable is set and if the original object has been updated, and applies
    the update function if necessary.

    Args:
        use_wrapper: If True, uses the original wrapper when accessing the original object.
                     If None, uses the Cage's default `monkay_use_wrapper_for_reads` value.
        _monkay_dict: An optional dictionary to use instead of the Cage's `__dict__`.

    Returns:
        The context-aware copy of the object.
    """
    if _monkay_dict is None:
        _monkay_dict = super().__getattribute__("__dict__")
    if use_wrapper is None:
        use_wrapper = _monkay_dict["monkay_use_wrapper_for_reads"]
    tup = _monkay_dict["monkay_context_var"].get()
    if tup is Undefined:
        obj = self.monkay_refresh_copy(_monkay_dict=_monkay_dict)
    elif (
        _monkay_dict["monkay_update_fn"] is not None
        and tup[0] is not None
        and tup[0] != _monkay_dict["monkay_original_last_update"]
    ):
        with _monkay_dict["monkay_original_wrapper"] if use_wrapper else nullcontext():
            obj = _monkay_dict["monkay_update_fn"](tup[1], _monkay_dict["monkay_original"])
        obj = self.monkay_refresh_copy(
            obj=obj, _monkay_dict=_monkay_dict, use_wrapper=use_wrapper
        )
    else:
        obj = tup[1]
    return obj

monkay_proxied

monkay_proxied(use_wrapper=None)

Returns a proxied version of the object, ensuring context-aware updates.

This method returns a context-aware copy of the object, optionally using a wrapper.

Parameters:

Name Type Description Default
use_wrapper bool | None

If True, uses the original wrapper when accessing the original object. If None, uses the Cage's default monkay_use_wrapper_for_reads value.

None

Returns:

Type Description
T

The proxied object.

Source code in monkay/cages.py
def monkay_proxied(
    self,
    use_wrapper: bool | None = None,
) -> T:
    """
    Returns a proxied version of the object, ensuring context-aware updates.

    This method returns a context-aware copy of the object, optionally using a wrapper.

    Args:
        use_wrapper: If True, uses the original wrapper when accessing the original object.
                     If None, uses the Cage's default `monkay_use_wrapper_for_reads` value.

    Returns:
        The proxied object.
    """
    return self.monkay_conditional_update_copy(use_wrapper=use_wrapper)

monkay_with_override

monkay_with_override(value, *, allow_value_update=True)

Temporarily overrides the context variable with a new value within a context.

This context manager temporarily sets the context variable to a new value and yields it. After the context exits, the original context variable is restored.

Parameters:

Name Type Description Default
value T

The new value to set the context variable to.

required
allow_value_update bool

If True, allows the value to be updated by the update function. If False, the value will not be updated.

True

Yields:

Type Description
Generator[T]

The new value.

Source code in monkay/cages.py
@contextmanager
def monkay_with_override(self, value: T, *, allow_value_update: bool = True) -> Generator[T]:
    """
    Temporarily overrides the context variable with a new value within a context.

    This context manager temporarily sets the context variable to a new value and yields it.
    After the context exits, the original context variable is restored.

    Args:
        value: The new value to set the context variable to.
        allow_value_update: If True, allows the value to be updated by the update function.
                            If False, the value will not be updated.

    Yields:
        The new value.
    """
    monkay_dict = super().__getattribute__("__dict__")
    token = monkay_dict["monkay_context_var"].set(
        (monkay_dict["monkay_original_last_update"] if allow_value_update else None, value)
    )
    try:
        yield value
    finally:
        monkay_dict["monkay_context_var"].reset(token)

monkay_with_original

monkay_with_original(use_wrapper=True, update_after=True)

Temporarily accesses the original value within a context, optionally updating it.

This context manager yields the original value, optionally using a wrapper and updating the last update timestamp after the context exits.

Parameters:

Name Type Description Default
use_wrapper bool

If True, uses the original wrapper when accessing the original value.

True
update_after bool

If True, updates the last update timestamp after the context exits.

True

Yields:

Type Description
Generator[T]

The original value.

Source code in monkay/cages.py
@contextmanager
def monkay_with_original(
    self, use_wrapper: bool = True, update_after: bool = True
) -> Generator[T]:
    """
    Temporarily accesses the original value within a context, optionally updating it.

    This context manager yields the original value, optionally using a wrapper and updating
    the last update timestamp after the context exits.

    Args:
        use_wrapper: If True, uses the original wrapper when accessing the original value.
        update_after: If True, updates the last update timestamp after the context exits.

    Yields:
        The original value.
    """
    monkay_dict = super().__getattribute__("__dict__")
    wrapper = monkay_dict["monkay_original_wrapper"] if use_wrapper else nullcontext()
    with wrapper:
        yield monkay_dict["monkay_original"]
        if update_after and monkay_dict["monkay_original_last_update_lock"] is not None:
            with monkay_dict["monkay_original_last_update_lock"]:
                monkay_dict["monkay_original_last_update"] += 1

monkay_set

monkay_set(value)

Sets the context variable to a new value and returns a token.

This method sets the context variable to a new value and returns a token that can be used to reset the context variable to its previous value.

Parameters:

Name Type Description Default
value T

The new value to set the context variable to.

required

Returns:

Type Description
Token

A token that can be used to reset the context variable.

Source code in monkay/cages.py
def monkay_set(self, value: T) -> Token:
    """
    Sets the context variable to a new value and returns a token.

    This method sets the context variable to a new value and returns a token that can be used
    to reset the context variable to its previous value.

    Args:
        value: The new value to set the context variable to.

    Returns:
        A token that can be used to reset the context variable.
    """
    monkay_dict = super().__getattribute__("__dict__")
    return monkay_dict["monkay_context_var"].set(
        (monkay_dict["monkay_original_last_update"], value)
    )

monkay_get

monkay_get(default=None)

Retrieves the current value of the context variable, or a default value if it's not set.

This method retrieves the current value of the context variable. If the context variable is not set, it returns the original value or a default value if provided.

Parameters:

Name Type Description Default
default T | DEFAULT | None

The default value to return if the context variable is not set.

None

Returns:

Type Description
T | DEFAULT | None

The current value of the context variable, the original value, or the default value.

Source code in monkay/cages.py
def monkay_get(self, default: T | DEFAULT | None = None) -> T | DEFAULT | None:
    """
    Retrieves the current value of the context variable, or a default value if it's not set.

    This method retrieves the current value of the context variable. If the context variable is not set,
    it returns the original value or a default value if provided.

    Args:
        default: The default value to return if the context variable is not set.

    Returns:
        The current value of the context variable, the original value, or the default value.
    """
    monkay_dict = super().__getattribute__("__dict__")
    tup: type[Undefined] | tuple[int | None, T] = monkay_dict["monkay_context_var"].get()
    if tup is Undefined:
        original: T | type[Undefined] = monkay_dict["monkay_original"]
        if original is not Undefined:
            return cast("DEFAULT | None", original)
        else:
            return default
    else:
        return cast("tuple[int | None, T]", tup)[1]

monkay_reset

monkay_reset(token)

Resets the context variable to its previous value using a token.

This method resets the context variable to its previous value using a token returned by monkay_set.

Parameters:

Name Type Description Default
token Token

The token returned by monkay_set.

required
Source code in monkay/cages.py
def monkay_reset(self, token: Token):
    """
    Resets the context variable to its previous value using a token.

    This method resets the context variable to its previous value using a token returned by `monkay_set`.

    Args:
        token: The token returned by `monkay_set`.
    """
    self.monkay_context_var.reset(token)

TransparentCage

Bases: Cage

A transparent Cage subclass that exposes context variable attributes directly.

This subclass of Cage allows direct access to context variable attributes (e.g., 'get', 'set', 'reset') without requiring the 'monkay_' prefix. It provides a more transparent interface for interacting with context-aware values.

Source code in monkay/cages.py
class TransparentCage(Cage):
    """
    A transparent Cage subclass that exposes context variable attributes directly.

    This subclass of Cage allows direct access to context variable attributes (e.g., 'get', 'set', 'reset')
    without requiring the 'monkay_' prefix. It provides a more transparent interface for interacting
    with context-aware values.
    """

    def __getattribute__(self, name: str) -> Any:
        """
        Overrides attribute access to expose context variable attributes directly.

        This method intercepts attribute access and exposes context variable attributes without
        the 'monkay_' prefix.

        Args:
            name: The name of the attribute to retrieve.

        Returns:
            The retrieved attribute value.
        """
        if name in context_var_attributes:
            name = f"monkay_{name}"
        return super().__getattribute__(name)

monkay_context_var instance-attribute

monkay_context_var

A context variable that stores the current value and update timestamp within a specific context. This allows for context-specific modifications and retrieval of the value.

monkay_deep_copy instance-attribute

monkay_deep_copy

A boolean indicating whether deep copies should be used when modifying the value. If True, modifications will create deep copies of the original value to avoid unintended side effects.

monkay_use_wrapper_for_reads instance-attribute

monkay_use_wrapper_for_reads

A boolean indicating whether a wrapper should be used for read operations. If True, read operations will be wrapped in a context manager to ensure consistency.

monkay_update_fn instance-attribute

monkay_update_fn

An optional function that defines how to update the value when modifications are made. This function takes the current value and the new value as input and returns the updated value.

monkay_name instance-attribute

monkay_name

A name associated with the Cage instance, used for identification and debugging purposes.

monkay_original instance-attribute

monkay_original

The original value stored in the Cage instance. This attribute holds the initial value that is managed by the Cage.

monkay_original_last_update instance-attribute

monkay_original_last_update

A timestamp indicating the last time the original value was updated. This timestamp is used to track changes and ensure consistency.

monkay_original_last_update_lock instance-attribute

monkay_original_last_update_lock

An optional lock used to synchronize updates to the original value. This lock prevents race conditions when multiple threads or processes try to update the value concurrently.

monkay_original_wrapper instance-attribute

monkay_original_wrapper

A context manager used to wrap access to the original value, ensuring consistency and synchronization. This wrapper provides a controlled environment for read and write operations on the original value.

monkay_forward classmethod

monkay_forward(obj_type, name)

Creates a wrapper function that forwards method calls to the wrapped object.

This class method generates a wrapper function that intercepts method calls to the Cage instance and forwards them to the wrapped object, ensuring that the object is updated or copied as needed.

Parameters:

Name Type Description Default
obj_type type

The type of the wrapped object.

required
name str

The name of the method to forward.

required

Returns:

Type Description
Any

A wrapper function that forwards method calls.

Source code in monkay/cages.py
@classmethod
def monkay_forward(cls, obj_type: type, name: str) -> Any:
    """
    Creates a wrapper function that forwards method calls to the wrapped object.

    This class method generates a wrapper function that intercepts method calls to the Cage instance
    and forwards them to the wrapped object, ensuring that the object is updated or copied as needed.

    Args:
        obj_type: The type of the wrapped object.
        name: The name of the method to forward.

    Returns:
        A wrapper function that forwards method calls.
    """

    @wraps(getattr(obj_type, name))
    def _(self, *args: Any, **kwargs: Any):
        obj = self.monkay_conditional_update_copy()
        return getattr(obj, name)(*args, **kwargs)

    return _

monkay_refresh_copy

monkay_refresh_copy(*, obj=Undefined, use_wrapper=None, _monkay_dict=None)

Refreshes the context variable with a copy of the original object.

This method updates the context variable with a fresh copy of the original object, optionally using a wrapper and handling deep or shallow copies.

Parameters:

Name Type Description Default
obj T | type[Undefined]

An optional object to use instead of creating a copy.

Undefined
use_wrapper bool | None

If True, uses the original wrapper when accessing the original object. If None, uses the Cage's default monkay_use_wrapper_for_reads value.

None
_monkay_dict dict | None

An optional dictionary to use instead of the Cage's __dict__.

None

Returns:

Type Description
T

The refreshed object.

Source code in monkay/cages.py
def monkay_refresh_copy(
    self,
    *,
    obj: T | type[Undefined] = Undefined,
    use_wrapper: bool | None = None,
    _monkay_dict: dict | None = None,
) -> T:
    """
    Refreshes the context variable with a copy of the original object.

    This method updates the context variable with a fresh copy of the original object,
    optionally using a wrapper and handling deep or shallow copies.

    Args:
        obj: An optional object to use instead of creating a copy.
        use_wrapper: If True, uses the original wrapper when accessing the original object.
                     If None, uses the Cage's default `monkay_use_wrapper_for_reads` value.
        _monkay_dict: An optional dictionary to use instead of the Cage's `__dict__`.

    Returns:
        The refreshed object.
    """
    if _monkay_dict is None:
        _monkay_dict = super().__getattribute__("__dict__")
    if use_wrapper is None:
        use_wrapper = _monkay_dict["monkay_use_wrapper_for_reads"]
    if obj is Undefined:
        with _monkay_dict["monkay_original_wrapper"] if use_wrapper else nullcontext():
            obj = (
                copy.deepcopy(_monkay_dict["monkay_original"])
                if _monkay_dict["monkay_deep_copy"]
                else copy.copy(_monkay_dict["monkay_original"])
            )
    _monkay_dict["monkay_context_var"].set((_monkay_dict["monkay_original_last_update"], obj))
    return cast(T, obj)

monkay_conditional_update_copy

monkay_conditional_update_copy(*, use_wrapper=None, _monkay_dict=None)

Retrieves a context-aware copy of the original object, updating it if necessary.

This method retrieves a copy of the original object, updating it based on context and update functions. It checks if the context variable is set and if the original object has been updated, and applies the update function if necessary.

Parameters:

Name Type Description Default
use_wrapper bool | None

If True, uses the original wrapper when accessing the original object. If None, uses the Cage's default monkay_use_wrapper_for_reads value.

None
_monkay_dict dict | None

An optional dictionary to use instead of the Cage's __dict__.

None

Returns:

Type Description
T

The context-aware copy of the object.

Source code in monkay/cages.py
def monkay_conditional_update_copy(
    self, *, use_wrapper: bool | None = None, _monkay_dict: dict | None = None
) -> T:
    """
    Retrieves a context-aware copy of the original object, updating it if necessary.

    This method retrieves a copy of the original object, updating it based on context and update functions.
    It checks if the context variable is set and if the original object has been updated, and applies
    the update function if necessary.

    Args:
        use_wrapper: If True, uses the original wrapper when accessing the original object.
                     If None, uses the Cage's default `monkay_use_wrapper_for_reads` value.
        _monkay_dict: An optional dictionary to use instead of the Cage's `__dict__`.

    Returns:
        The context-aware copy of the object.
    """
    if _monkay_dict is None:
        _monkay_dict = super().__getattribute__("__dict__")
    if use_wrapper is None:
        use_wrapper = _monkay_dict["monkay_use_wrapper_for_reads"]
    tup = _monkay_dict["monkay_context_var"].get()
    if tup is Undefined:
        obj = self.monkay_refresh_copy(_monkay_dict=_monkay_dict)
    elif (
        _monkay_dict["monkay_update_fn"] is not None
        and tup[0] is not None
        and tup[0] != _monkay_dict["monkay_original_last_update"]
    ):
        with _monkay_dict["monkay_original_wrapper"] if use_wrapper else nullcontext():
            obj = _monkay_dict["monkay_update_fn"](tup[1], _monkay_dict["monkay_original"])
        obj = self.monkay_refresh_copy(
            obj=obj, _monkay_dict=_monkay_dict, use_wrapper=use_wrapper
        )
    else:
        obj = tup[1]
    return obj

monkay_proxied

monkay_proxied(use_wrapper=None)

Returns a proxied version of the object, ensuring context-aware updates.

This method returns a context-aware copy of the object, optionally using a wrapper.

Parameters:

Name Type Description Default
use_wrapper bool | None

If True, uses the original wrapper when accessing the original object. If None, uses the Cage's default monkay_use_wrapper_for_reads value.

None

Returns:

Type Description
T

The proxied object.

Source code in monkay/cages.py
def monkay_proxied(
    self,
    use_wrapper: bool | None = None,
) -> T:
    """
    Returns a proxied version of the object, ensuring context-aware updates.

    This method returns a context-aware copy of the object, optionally using a wrapper.

    Args:
        use_wrapper: If True, uses the original wrapper when accessing the original object.
                     If None, uses the Cage's default `monkay_use_wrapper_for_reads` value.

    Returns:
        The proxied object.
    """
    return self.monkay_conditional_update_copy(use_wrapper=use_wrapper)

monkay_with_override

monkay_with_override(value, *, allow_value_update=True)

Temporarily overrides the context variable with a new value within a context.

This context manager temporarily sets the context variable to a new value and yields it. After the context exits, the original context variable is restored.

Parameters:

Name Type Description Default
value T

The new value to set the context variable to.

required
allow_value_update bool

If True, allows the value to be updated by the update function. If False, the value will not be updated.

True

Yields:

Type Description
Generator[T]

The new value.

Source code in monkay/cages.py
@contextmanager
def monkay_with_override(self, value: T, *, allow_value_update: bool = True) -> Generator[T]:
    """
    Temporarily overrides the context variable with a new value within a context.

    This context manager temporarily sets the context variable to a new value and yields it.
    After the context exits, the original context variable is restored.

    Args:
        value: The new value to set the context variable to.
        allow_value_update: If True, allows the value to be updated by the update function.
                            If False, the value will not be updated.

    Yields:
        The new value.
    """
    monkay_dict = super().__getattribute__("__dict__")
    token = monkay_dict["monkay_context_var"].set(
        (monkay_dict["monkay_original_last_update"] if allow_value_update else None, value)
    )
    try:
        yield value
    finally:
        monkay_dict["monkay_context_var"].reset(token)

monkay_with_original

monkay_with_original(use_wrapper=True, update_after=True)

Temporarily accesses the original value within a context, optionally updating it.

This context manager yields the original value, optionally using a wrapper and updating the last update timestamp after the context exits.

Parameters:

Name Type Description Default
use_wrapper bool

If True, uses the original wrapper when accessing the original value.

True
update_after bool

If True, updates the last update timestamp after the context exits.

True

Yields:

Type Description
Generator[T]

The original value.

Source code in monkay/cages.py
@contextmanager
def monkay_with_original(
    self, use_wrapper: bool = True, update_after: bool = True
) -> Generator[T]:
    """
    Temporarily accesses the original value within a context, optionally updating it.

    This context manager yields the original value, optionally using a wrapper and updating
    the last update timestamp after the context exits.

    Args:
        use_wrapper: If True, uses the original wrapper when accessing the original value.
        update_after: If True, updates the last update timestamp after the context exits.

    Yields:
        The original value.
    """
    monkay_dict = super().__getattribute__("__dict__")
    wrapper = monkay_dict["monkay_original_wrapper"] if use_wrapper else nullcontext()
    with wrapper:
        yield monkay_dict["monkay_original"]
        if update_after and monkay_dict["monkay_original_last_update_lock"] is not None:
            with monkay_dict["monkay_original_last_update_lock"]:
                monkay_dict["monkay_original_last_update"] += 1

monkay_set

monkay_set(value)

Sets the context variable to a new value and returns a token.

This method sets the context variable to a new value and returns a token that can be used to reset the context variable to its previous value.

Parameters:

Name Type Description Default
value T

The new value to set the context variable to.

required

Returns:

Type Description
Token

A token that can be used to reset the context variable.

Source code in monkay/cages.py
def monkay_set(self, value: T) -> Token:
    """
    Sets the context variable to a new value and returns a token.

    This method sets the context variable to a new value and returns a token that can be used
    to reset the context variable to its previous value.

    Args:
        value: The new value to set the context variable to.

    Returns:
        A token that can be used to reset the context variable.
    """
    monkay_dict = super().__getattribute__("__dict__")
    return monkay_dict["monkay_context_var"].set(
        (monkay_dict["monkay_original_last_update"], value)
    )

monkay_get

monkay_get(default=None)

Retrieves the current value of the context variable, or a default value if it's not set.

This method retrieves the current value of the context variable. If the context variable is not set, it returns the original value or a default value if provided.

Parameters:

Name Type Description Default
default T | DEFAULT | None

The default value to return if the context variable is not set.

None

Returns:

Type Description
T | DEFAULT | None

The current value of the context variable, the original value, or the default value.

Source code in monkay/cages.py
def monkay_get(self, default: T | DEFAULT | None = None) -> T | DEFAULT | None:
    """
    Retrieves the current value of the context variable, or a default value if it's not set.

    This method retrieves the current value of the context variable. If the context variable is not set,
    it returns the original value or a default value if provided.

    Args:
        default: The default value to return if the context variable is not set.

    Returns:
        The current value of the context variable, the original value, or the default value.
    """
    monkay_dict = super().__getattribute__("__dict__")
    tup: type[Undefined] | tuple[int | None, T] = monkay_dict["monkay_context_var"].get()
    if tup is Undefined:
        original: T | type[Undefined] = monkay_dict["monkay_original"]
        if original is not Undefined:
            return cast("DEFAULT | None", original)
        else:
            return default
    else:
        return cast("tuple[int | None, T]", tup)[1]

monkay_reset

monkay_reset(token)

Resets the context variable to its previous value using a token.

This method resets the context variable to its previous value using a token returned by monkay_set.

Parameters:

Name Type Description Default
token Token

The token returned by monkay_set.

required
Source code in monkay/cages.py
def monkay_reset(self, token: Token):
    """
    Resets the context variable to its previous value using a token.

    This method resets the context variable to its previous value using a token returned by `monkay_set`.

    Args:
        token: The token returned by `monkay_set`.
    """
    self.monkay_context_var.reset(token)

ASGI

monkay.asgi

ASGI lifecycle utilities and compatibility helpers.

This module provides two production-oriented helpers:

  1. :class:Lifespan, a tiny in-process ASGI server-side lifespan driver that can be used from tests or integration harnesses.
  2. :func:LifespanHook, an adapter that helps applications integrate custom startup/shutdown setup logic without forcing framework-specific coupling.

ASGIApp module-attribute

ASGIApp = Callable[[MutableMapping[str, Any], Callable[[], Awaitable[MutableMapping[str, Any]]], Callable[[MutableMapping[str, Any]], Awaitable[None]]], Awaitable[None]]

BoundASGIApp module-attribute

BoundASGIApp = TypeVar('BoundASGIApp', bound=ASGIApp)

Lifespan

Bases: Generic[BoundASGIApp]

Drive the ASGI lifespan protocol as an in-process server harness.

The instance emulates a minimal server-side lifespan loop: it sends lifespan.startup and lifespan.shutdown messages to an ASGI application and consumes responses from the app.

The class is intentionally lightweight and fully async, so it can be used in tests and orchestration code that needs deterministic startup/shutdown boundaries.

Parameters:

Name Type Description Default
app BoundASGIApp

ASGI application callable supporting the lifespan protocol.

required
timeout None | int | float

Optional timeout applied by __aenter__/__aexit__. When provided, startup and shutdown are wrapped in :func:asyncio.wait_for.

None
Source code in monkay/asgi.py
class Lifespan(Generic[BoundASGIApp]):
    """Drive the ASGI lifespan protocol as an in-process server harness.

    The instance emulates a minimal server-side lifespan loop:
    it sends ``lifespan.startup`` and ``lifespan.shutdown`` messages to an ASGI
    application and consumes responses from the app.

    The class is intentionally lightweight and fully async, so it can be used
    in tests and orchestration code that needs deterministic startup/shutdown
    boundaries.

    Args:
        app: ASGI application callable supporting the lifespan protocol.
        timeout: Optional timeout applied by ``__aenter__``/``__aexit__``.
            When provided, startup and shutdown are wrapped in
            :func:`asyncio.wait_for`.
    """

    app: BoundASGIApp
    timeout: float | None
    task: Task | None = None
    state: MutableMapping[str, Any] | None = None

    def __init__(self, app: BoundASGIApp, *, timeout: None | int | float = None) -> None:
        """Initialize a lifespan driver.

        Args:
            app: ASGI application callable.
            timeout: Optional timeout in seconds for context-manager entry/exit.
        """
        self.app = app
        self.timeout = float(timeout) if timeout else None

    async def _cancel_lifespan_task(self) -> None:
        """Cancel and clear the internal lifespan task.

        This helper guarantees that ``self.task`` is reset to ``None`` even when
        task cancellation/cleanup raises.
        """
        task = self.task
        self.task = None
        if task is None:
            return
        if task.done():
            with suppress(CancelledError, Exception):
                task.result()
            return
        task.cancel()
        with suppress(CancelledError):
            await task

    async def start_raw(self) -> BoundASGIApp:
        """Run lifespan startup without applying timeout policy.

        Returns:
            The wrapped ASGI application for convenience/chaining.

        Raises:
            RuntimeError: If startup fails or returns an unexpected message.
            BaseException: Re-raises startup cancellation/errors after ensuring
                spawned lifespan task cleanup.
        """
        if self.task is not None:
            return self.app
        # inverted, we have server view
        self.send_queue: Queue[MutableMapping[str, Any]] = Queue()
        self.receive_queue: Queue[MutableMapping[str, Any]] = Queue()
        self.state = {}
        self.send_queue.put_nowait({"type": "lifespan.startup"})
        self.task = create_task(
            self.app(  # type: ignore
                {
                    "type": "lifespan",
                    "asgi": {"version": "3.0", "spec_version": "2.0"},
                    "state": self.state,
                },
                # inverted send, receive because we are emulating the server
                self.send_queue.get,
                self.receive_queue.put,
            )
        )
        try:
            response = await self.receive_queue.get()
            match cast(Any, response.get("type")):
                case "lifespan.startup.complete":
                    return self.app
                case "lifespan.startup.failed":
                    raise RuntimeError("Lifespan startup failed:", response.get("msg") or "")
                case _:
                    raise RuntimeError("Lifespan startup failed: Unexpected startup response.")
        except BaseException:
            await self._cancel_lifespan_task()
            raise

    async def __aenter__(self) -> BoundASGIApp:
        """Enter context and trigger ASGI startup.

        Returns:
            The wrapped ASGI application.

        Raises:
            TimeoutError: If startup exceeds configured ``timeout``.
            RuntimeError: If startup fails.
        """
        if self.timeout:
            return await wait_for(self.start_raw(), self.timeout)
        return await self.start_raw()

    async def shutdown_raw(self) -> None:
        """Run lifespan shutdown without applying timeout policy.

        Raises:
            RuntimeError: If the app reports shutdown failure or if the lifespan
                task crashes during shutdown.
        """
        task = self.task
        if task is None:
            return
        self.task = None
        if task.done():
            try:
                task_exception = task.exception()
            except CancelledError:
                return
            if task_exception is not None:
                raise RuntimeError("Lifespan task errored before shutdown.") from task_exception
            return

        self.send_queue.put_nowait({"type": "lifespan.shutdown"})
        response = await self.receive_queue.get()
        match response.get("type"):
            case "lifespan.shutdown.complete":
                pass
            case "lifespan.shutdown.failed":
                raise RuntimeError("Lifespan shutdown failed:", response.get("msg") or "")
            case _:
                raise RuntimeError("Lifespan shutdown failed: Unexpected shutdown response.")
        try:
            await task
        except CancelledError:
            return
        except Exception as exc:
            raise RuntimeError("Lifespan task errored during shutdown.") from exc

    async def __aexit__(
        self,
        exc_type: type[BaseException] | None = None,
        exc_value: BaseException | None = None,
        traceback: TracebackType | None = None,
    ) -> None:
        """Exit context and trigger ASGI shutdown.

        Args:
            exc_type: Exception class raised in context body, if any.
            exc_value: Exception instance raised in context body, if any.
            traceback: Traceback object for the exception, if any.

        Raises:
            TimeoutError: If shutdown exceeds configured ``timeout``.
            RuntimeError: If shutdown fails.
        """
        if self.timeout:
            await wait_for(self.shutdown_raw(), self.timeout)
        else:
            await self.shutdown_raw()

task class-attribute instance-attribute

task = None

state class-attribute instance-attribute

state = None

app instance-attribute

app = app

timeout instance-attribute

timeout = float(timeout) if timeout else None

start_raw async

start_raw()

Run lifespan startup without applying timeout policy.

Returns:

Type Description
BoundASGIApp

The wrapped ASGI application for convenience/chaining.

Raises:

Type Description
RuntimeError

If startup fails or returns an unexpected message.

BaseException

Re-raises startup cancellation/errors after ensuring spawned lifespan task cleanup.

Source code in monkay/asgi.py
async def start_raw(self) -> BoundASGIApp:
    """Run lifespan startup without applying timeout policy.

    Returns:
        The wrapped ASGI application for convenience/chaining.

    Raises:
        RuntimeError: If startup fails or returns an unexpected message.
        BaseException: Re-raises startup cancellation/errors after ensuring
            spawned lifespan task cleanup.
    """
    if self.task is not None:
        return self.app
    # inverted, we have server view
    self.send_queue: Queue[MutableMapping[str, Any]] = Queue()
    self.receive_queue: Queue[MutableMapping[str, Any]] = Queue()
    self.state = {}
    self.send_queue.put_nowait({"type": "lifespan.startup"})
    self.task = create_task(
        self.app(  # type: ignore
            {
                "type": "lifespan",
                "asgi": {"version": "3.0", "spec_version": "2.0"},
                "state": self.state,
            },
            # inverted send, receive because we are emulating the server
            self.send_queue.get,
            self.receive_queue.put,
        )
    )
    try:
        response = await self.receive_queue.get()
        match cast(Any, response.get("type")):
            case "lifespan.startup.complete":
                return self.app
            case "lifespan.startup.failed":
                raise RuntimeError("Lifespan startup failed:", response.get("msg") or "")
            case _:
                raise RuntimeError("Lifespan startup failed: Unexpected startup response.")
    except BaseException:
        await self._cancel_lifespan_task()
        raise

shutdown_raw async

shutdown_raw()

Run lifespan shutdown without applying timeout policy.

Raises:

Type Description
RuntimeError

If the app reports shutdown failure or if the lifespan task crashes during shutdown.

Source code in monkay/asgi.py
async def shutdown_raw(self) -> None:
    """Run lifespan shutdown without applying timeout policy.

    Raises:
        RuntimeError: If the app reports shutdown failure or if the lifespan
            task crashes during shutdown.
    """
    task = self.task
    if task is None:
        return
    self.task = None
    if task.done():
        try:
            task_exception = task.exception()
        except CancelledError:
            return
        if task_exception is not None:
            raise RuntimeError("Lifespan task errored before shutdown.") from task_exception
        return

    self.send_queue.put_nowait({"type": "lifespan.shutdown"})
    response = await self.receive_queue.get()
    match response.get("type"):
        case "lifespan.shutdown.complete":
            pass
        case "lifespan.shutdown.failed":
            raise RuntimeError("Lifespan shutdown failed:", response.get("msg") or "")
        case _:
            raise RuntimeError("Lifespan shutdown failed: Unexpected shutdown response.")
    try:
        await task
    except CancelledError:
        return
    except Exception as exc:
        raise RuntimeError("Lifespan task errored during shutdown.") from exc

MuteInteruptException

Bases: BaseException

Sentinel exception used to stop lifespan forwarding quietly.

Source code in monkay/asgi.py
class MuteInteruptException(BaseException):
    """Sentinel exception used to stop lifespan forwarding quietly."""

LifespanHook

LifespanHook(app: BoundASGIApp, *, setup: Callable[[], Awaitable[AsyncExitStack]] | None = None, do_forward: bool = True) -> BoundASGIApp
LifespanHook(app: None, *, setup: Callable[[], Awaitable[AsyncExitStack]] | None = None, do_forward: bool = True) -> Callable[[BoundASGIApp], BoundASGIApp]
LifespanHook(app=None, *, setup=None, do_forward=True)

Wrap an ASGI app with optional setup/teardown hooks.

The wrapper can either:

  • forward lifespan traffic to the underlying app (do_forward=True), or
  • fully handle startup/shutdown completion messages itself (do_forward=False).

Parameters:

Name Type Description Default
app BoundASGIApp | None

Optional ASGI app to wrap. When None, a decorator-style factory is returned.

None
setup Callable[[], Awaitable[AsyncExitStack]] | None

Optional async callable returning an :class:AsyncExitStack used for shutdown cleanup.

None
do_forward bool

Whether lifespan messages should be forwarded to the wrapped app after setup/teardown interception.

True

Returns:

Type Description
BoundASGIApp | Callable[[BoundASGIApp], BoundASGIApp]

Wrapped ASGI application, or a decorator factory if app is None.

Examples:

>>> wrapped = LifespanHook(app, setup=my_setup, do_forward=False)
>>> async with Lifespan(wrapped):
...     pass
Notes

Setup state is scoped to a single lifespan invocation. Reusing the same wrapped app across multiple lifespan sessions is safe.

Source code in monkay/asgi.py
def LifespanHook(
    app: BoundASGIApp | None = None,
    *,
    setup: Callable[[], Awaitable[AsyncExitStack]] | None = None,
    do_forward: bool = True,
) -> BoundASGIApp | Callable[[BoundASGIApp], BoundASGIApp]:
    """Wrap an ASGI app with optional setup/teardown hooks.

    The wrapper can either:

    - forward lifespan traffic to the underlying app (``do_forward=True``), or
    - fully handle startup/shutdown completion messages itself
      (``do_forward=False``).

    Args:
        app: Optional ASGI app to wrap. When ``None``, a decorator-style factory
            is returned.
        setup: Optional async callable returning an :class:`AsyncExitStack` used
            for shutdown cleanup.
        do_forward: Whether lifespan messages should be forwarded to the wrapped
            app after setup/teardown interception.

    Returns:
        Wrapped ASGI application, or a decorator factory if ``app`` is ``None``.

    Examples:
        >>> wrapped = LifespanHook(app, setup=my_setup, do_forward=False)
        >>> async with Lifespan(wrapped):
        ...     pass

    Notes:
        Setup state is scoped to a single lifespan invocation. Reusing the same
        wrapped app across multiple lifespan sessions is safe.
    """
    if app is None:
        return partial(LifespanHook, setup=setup, do_forward=do_forward)

    @wraps(app)
    async def app_wrapper(
        scope: MutableMapping[str, Any],
        receive: Callable[[], Awaitable[MutableMapping[str, Any]]],
        send: Callable[[MutableMapping[str, Any]], Awaitable[None]],
    ) -> None:
        """Wraps the ASGI callable. Provides a forward."""
        shutdown_stack: AsyncExitStack | None = None
        # Check if the current scope is of type 'lifespan'.
        if scope["type"] == "lifespan":
            # Store the original receive callable to be used inside the wrapper.
            original_receive = receive

            async def receive() -> MutableMapping[str, Any]:
                """
                A wrapped `receive` callable that intercepts 'lifespan.startup'
                and 'lifespan.shutdown' messages to execute the setup.
                """
                nonlocal shutdown_stack
                # Await the message from the original receive callable.
                message = await original_receive()
                # Check if the message type is for lifespan startup.
                match message.get("type"):
                    case "lifespan.startup":
                        if setup is not None:
                            try:
                                # Setup an AsyncExitStack for cleanup.
                                shutdown_stack = await setup()
                            except Exception as exc:
                                # If an exception occurs during startup, send a failed
                                # message to the ASGI server.
                                await send({"type": "lifespan.startup.failed", "msg": str(exc)})
                                # Raise a custom exception to stop further lifespan
                                # processing for this event.
                                raise MuteInteruptException from None
                    case "lifespan.shutdown":  # noqa: SIM102
                        # Check if the message type is for lifespan shutdown.
                        if shutdown_stack is not None:
                            stack_to_close = shutdown_stack
                            shutdown_stack = None
                            try:
                                # Attempt to exit asynchronous context.
                                await stack_to_close.aclose()
                            except Exception as exc:
                                # If an exception occurs during shutdown, send a failed
                                # message to the ASGI server.
                                await send({"type": "lifespan.shutdown.failed", "msg": str(exc)})
                                # Raise a custom exception to stop further lifespan
                                # processing for this event.
                                raise MuteInteruptException from None
                # Return the original message after processing.
                return message

            # If `handle_lifespan` is True, this helper will fully manage
            # the lifespan protocol, including sending 'complete' messages.
            if not do_forward:
                # Suppress the MuteInteruptException to gracefully stop
                # the lifespan loop without uncaught exceptions.
                with suppress(MuteInteruptException):
                    # Continuously receive and process lifespan messages.
                    while True:
                        # Await the next lifespan message.
                        message = await receive()
                        # If it's a startup message, send a complete message.
                        if message["type"] == "lifespan.startup":
                            await send({"type": "lifespan.startup.complete"})
                        # If it's a shutdown message, send a complete message
                        # and break the loop.
                        elif message["type"] == "lifespan.shutdown":
                            await send({"type": "lifespan.shutdown.complete"})
                            break
                # Once lifespan handling is complete, return from the callable.
                return

        # For any scope type other than 'lifespan', or if handle_lifespan
        # is False (meaning the original app will handle 'complete' messages),
        # or after the lifespan handling is complete, call the original ASGI app.
        # Suppress MuteInteruptException in case it was raised by the
        # modified receive callable and propagated here.
        with suppress(MuteInteruptException):
            await app(scope, receive, send)

    # forward attributes
    app_wrapper.__getattr__ = lambda name: getattr(app, name)  # type: ignore

    return cast(BoundASGIApp, app_wrapper)

Types

monkay.types

SETTINGS_T module-attribute

SETTINGS_T = TypeVar('SETTINGS_T')

SETTINGS_DEFINITION_BASE_TYPE module-attribute

SETTINGS_DEFINITION_BASE_TYPE = SETTINGS_T | type[SETTINGS_T] | str | None

SETTINGS_DEFINITION_TYPE module-attribute

INSTANCE module-attribute

INSTANCE = TypeVar('INSTANCE')

SETTINGS module-attribute

SETTINGS = TypeVar('SETTINGS')

ASGIApp module-attribute

ASGIApp = Callable[[MutableMapping[str, Any], Callable[[], Awaitable[MutableMapping[str, Any]]], Callable[[MutableMapping[str, Any]], Awaitable[None]]], Awaitable[None]]

ExtensionProtocol

Bases: Protocol[INSTANCE, SETTINGS]

Source code in monkay/types.py
@runtime_checkable
class ExtensionProtocol(Protocol[INSTANCE, SETTINGS]):
    name: str

    def apply(self, monkay_instance: Monkay[INSTANCE, SETTINGS]) -> None: ...

name instance-attribute

name

apply

apply(monkay_instance)
Source code in monkay/types.py
def apply(self, monkay_instance: Monkay[INSTANCE, SETTINGS]) -> None: ...

SortedExportsEntry

Bases: NamedTuple

Represents an entry in a sorted list of module exports.

This class encapsulates information about a module export, including its category, name, and path.

Source code in monkay/types.py
class SortedExportsEntry(NamedTuple):
    """
    Represents an entry in a sorted list of module exports.

    This class encapsulates information about a module export, including its category,
    name, and path.
    """

    category: Literal["other", "lazy_import", "deprecated_lazy_import"]
    """The category of the export."""
    export_name: str
    """The name of the export."""
    path: str
    """The path to the export."""

category instance-attribute

category

The category of the export.

export_name instance-attribute

export_name

The name of the export.

path instance-attribute

path

The path to the export.

DeprecatedImport

Bases: TypedDict

Represents a deprecated import with optional deprecation details.

This class defines the structure for deprecated imports, including the import path, reason for deprecation, and a replacement attribute.

Source code in monkay/types.py
class DeprecatedImport(TypedDict, total=False):
    """
    Represents a deprecated import with optional deprecation details.

    This class defines the structure for deprecated imports, including the import path,
    reason for deprecation, and a replacement attribute.
    """

    path: str | Callable[[], Any]
    """The import path of the deprecated object, or a callable that returns the object."""
    reason: str
    """The reason for deprecation."""
    new_attribute: str
    """The replacement attribute to use."""

path instance-attribute

path

The import path of the deprecated object, or a callable that returns the object.

reason instance-attribute

reason

The reason for deprecation.

new_attribute instance-attribute

new_attribute

The replacement attribute to use.

EvaluateSettingsParameters

Bases: TypedDict

Source code in monkay/types.py
class EvaluateSettingsParameters(TypedDict, total=False):
    on_conflict: Literal["error", "keep", "replace"]
    ignore_import_errors: bool
    ignore_preload_import_errors: bool
    onetime: bool

on_conflict instance-attribute

on_conflict

ignore_import_errors instance-attribute

ignore_import_errors

ignore_preload_import_errors instance-attribute

ignore_preload_import_errors

onetime instance-attribute

onetime

PRE_ADD_LAZY_IMPORT_HOOK

Bases: Protocol

A protocol defining the signature for a hook that modifies lazy import definitions before they are added.

This protocol specifies the expected signature for a hook function that can be used to modify lazy import definitions before they are added to the module. It supports both regular lazy imports and deprecated lazy imports.

Source code in monkay/types.py
class PRE_ADD_LAZY_IMPORT_HOOK(Protocol):
    """
    A protocol defining the signature for a hook that modifies lazy import definitions before they are added.

    This protocol specifies the expected signature for a hook function that can be used to modify
    lazy import definitions before they are added to the module. It supports both regular lazy imports
    and deprecated lazy imports.
    """

    @overload
    @staticmethod
    def __call__(
        key: str,
        value: str | Callable[[], Any],
        type_: Literal["lazy_import"],
        /,
    ) -> tuple[str, str | Callable[[], Any]]: ...

    @overload
    @staticmethod
    def __call__(
        key: str,
        value: DeprecatedImport,
        type_: Literal["deprecated_lazy_import"],
        /,
    ) -> tuple[str, DeprecatedImport]: ...

    @staticmethod
    def __call__(
        key: str,
        value: str | Callable[[], Any] | DeprecatedImport,
        type_: Literal["lazy_import", "deprecated_lazy_import"],
        /,
    ) -> tuple[str, str | Callable[[], Any] | DeprecatedImport]: ...