Skip to content

confingy.utils.types#

types #

TrackedInstance #

Bases: Protocol

Protocol for objects decorated with @track.

Source code in src/confingy/utils/types.py
 9
10
11
12
class TrackedInstance(Protocol):
    """Protocol for objects decorated with @track."""

    _tracked_info: dict[str, Any]

is_lazy_type #

is_lazy_type(obj: Any) -> bool

Check if an object is a Lazy type annotation.

Source code in src/confingy/utils/types.py
15
16
17
18
19
20
21
22
def is_lazy_type(obj: Any) -> bool:
    """Check if an object is a [Lazy][confingy.tracking.Lazy] type annotation."""
    # Import at runtime to avoid circular dependency
    from confingy.tracking import Lazy

    # Check if it's a GenericAlias with Lazy as the origin
    origin = get_origin(obj)
    return origin is Lazy

is_lazy_instance #

is_lazy_instance(obj: Any) -> TypeGuard[Lazy[Any]]

Check if an object is a Lazy instance.

Source code in src/confingy/utils/types.py
25
26
27
28
29
def is_lazy_instance(obj: Any) -> TypeGuard["Lazy[Any]"]:
    """
    Check if an object is a [Lazy][confingy.tracking.Lazy] instance.
    """
    return hasattr(obj, "_confingy_lazy_info") and hasattr(obj, "instantiate")

is_tracked_instance #

is_tracked_instance(obj: Any) -> TypeGuard[TrackedInstance]

Check if an object is a tracked instance (decorated with @track).

Tracked instances have a _tracked_info attribute that stores the class name, module, and constructor arguments used to create them.

Source code in src/confingy/utils/types.py
32
33
34
35
36
37
38
39
def is_tracked_instance(obj: Any) -> TypeGuard[TrackedInstance]:
    """
    Check if an object is a tracked instance (decorated with @track).

    Tracked instances have a `_tracked_info` attribute that stores
    the class name, module, and constructor arguments used to create them.
    """
    return hasattr(obj, "_tracked_info")

is_lazy_version_of #

is_lazy_version_of(obj: Any, expected_type: type) -> bool

Check if an object is a Lazy version of a given type.

Source code in src/confingy/utils/types.py
42
43
44
def is_lazy_version_of(obj: Any, expected_type: type) -> bool:
    """Check if an object is a [Lazy][confingy.tracking.Lazy] version of a given type."""
    return is_lazy_instance(obj) and obj._confingy_cls == expected_type

is_nonlazy_subclass_of #

is_nonlazy_subclass_of(
    obj: Any, expected_supertype: type
) -> bool

Check if an object is a non-lazy subclass of a given type.

Parameters:

Name Type Description Default
obj Any

The object to check

required
expected_supertype type

The expected supertype

required

Returns:

Type Description
bool

True if obj is a subclass of expected_supertype and not lazy

Source code in src/confingy/utils/types.py
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
def is_nonlazy_subclass_of(obj: Any, expected_supertype: type) -> bool:
    """
    Check if an object is a non-lazy subclass of a given type.

    Args:
        obj: The object to check
        expected_supertype: The expected supertype

    Returns:
        True if obj is a subclass of expected_supertype and not lazy
    """
    # Check if it's a lazy type or instance
    if is_lazy_type(obj) or is_lazy_instance(obj):
        return False

    # Get the actual type to check against
    actual_expected_type = expected_supertype
    if hasattr(expected_supertype, "_original_cls"):
        actual_expected_type = expected_supertype._original_cls
    elif callable(expected_supertype) and not isinstance(expected_supertype, type):
        return False

    # Perform the type check
    if isinstance(obj, type):
        try:
            return issubclass(obj, actual_expected_type)
        except TypeError:
            return False
    else:
        try:
            return isinstance(obj, actual_expected_type)
        except TypeError:
            return False

is_all_lazy #

is_all_lazy(obj: Any) -> bool

Check if an entire nested fingy is fully lazy.

This function recursively walks through a fingy and verifies that all tracked objects are Lazy instances (not already instantiated). This is useful to ensure a fingy can be safely mutated before instantiation.

Parameters:

Name Type Description Default
obj Any

The object to check. Can be a Lazy instance, dataclass, dict, list, or any other value.

required

Returns:

Type Description
bool

True if all tracked objects in the structure are Lazy instances,

bool

False if any tracked object has been instantiated.

Examples:

@track
class Inner:
    def __init__(self, value: int):
        self.value = value

@track
class Outer:
    def __init__(self, inner: Inner):
        self.inner = inner

# All lazy - returns True
config = Outer.lazy(inner=Inner.lazy(value=1))
assert is_all_lazy(config)

# Contains instantiated object - returns False
config = Outer.lazy(inner=Inner(value=1))  # Inner is instantiated!
assert not is_all_lazy(config)
Source code in src/confingy/utils/types.py
 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
def is_all_lazy(obj: Any) -> bool:
    """
    Check if an entire nested fingy is fully lazy.

    This function recursively walks through a fingy and verifies
    that all tracked objects are Lazy instances (not already instantiated).
    This is useful to ensure a fingy can be safely mutated before instantiation.

    Args:
        obj: The object to check. Can be a Lazy instance, dataclass, dict, list,
             or any other value.

    Returns:
        True if all tracked objects in the structure are Lazy instances,
        False if any tracked object has been instantiated.

    Examples:
        ```python
        @track
        class Inner:
            def __init__(self, value: int):
                self.value = value

        @track
        class Outer:
            def __init__(self, inner: Inner):
                self.inner = inner

        # All lazy - returns True
        config = Outer.lazy(inner=Inner.lazy(value=1))
        assert is_all_lazy(config)

        # Contains instantiated object - returns False
        config = Outer.lazy(inner=Inner(value=1))  # Inner is instantiated!
        assert not is_all_lazy(config)
        ```
    """
    from confingy.serde import HandlerRegistry

    handlers = HandlerRegistry.get_default_handlers()
    found_non_lazy = [False]  # Mutable for closure

    def check(value: Any) -> Any:
        """Check value and recurse into children. Returns value unchanged."""
        if found_non_lazy[0]:
            return value  # Short-circuit if we already found a non-lazy

        # Tracked instance = not lazy!
        if is_tracked_instance(value):
            found_non_lazy[0] = True
            return value

        # Lazy instance - check its config
        if is_lazy_instance(value):
            for v in value._confingy_config.values():
                check(v)
            return value

        # Use handlers to recurse into containers (dict, list, tuple, set, dataclass)
        for handler in handlers:
            if handler.can_handle(value):
                handler.map_children(value, check)
                return value

        return value

    check(obj)
    return not found_non_lazy[0]