Skip to content

capturegraph.data.containers.missing #

MissingType - Null Object for Safe Chaining#

MissingType is a null object that absorbs all attribute and item access, enabling safe chaining without try/except blocks.

Example
from capturegraph.data import MissingType, is_missing

# Instead of:
try:
    value = session.optional_field.nested.value
except AttributeError:
    value = None

# You can write:
value = session.optional_field.nested.value
if is_missing(value):
    value = None  # or handle the missing case
Behavior
  • Attribute access: missing.anythingmissing
  • Item access: missing[key]missing
  • Calling: missing(args)missing
  • Boolean: bool(missing)False
  • Length: len(missing)0
  • Iteration: list(missing)[]
  • Containment: x in missingFalse
  • Arithmetic: missing + xmissing
  • Comparison: missing < xFalse (except equality)
  • Equality: All MissingType instances are equal to each other
Error Propagation

MissingType carries a reason (an Exception) explaining why the value is missing. When broadcasting operations result in ALL elements being MissingType, the reason from the first MissingType is raised to help with debugging.

MissingType #

A null object that absorbs all operations for safe chaining.

All attribute/item access, arithmetic, and comparison operations return self or appropriate falsy values. This enables chains like x.a.b.c without intermediate None checks.

Attributes:

Name Type Description
reason

Optional Exception explaining why this value is missing.

Example
m = MissingType()  # No reason
m = MissingType(KeyError("foo"))  # With reason
m.reason  # KeyError('foo')
Source code in capturegraph-lib/capturegraph/data/containers/missing.py
class MissingType:
    """A null object that absorbs all operations for safe chaining.

    All attribute/item access, arithmetic, and comparison operations return
    `self` or appropriate falsy values. This enables chains like `x.a.b.c`
    without intermediate None checks.

    Attributes:
        reason: Optional Exception explaining why this value is missing.

    Example:
        ```python
        m = MissingType()  # No reason
        m = MissingType(KeyError("foo"))  # With reason
        m.reason  # KeyError('foo')
        ```
    """

    __slots__ = ("reason",)

    def __init__(self, reason: Exception | None = None):
        object.__setattr__(self, "reason", reason)

    # =========================================================================
    # Attribute and Item Access
    # =========================================================================

    def __getattr__(self, name: str) -> "MissingType":
        """Return self for any attribute access."""
        return self

    def __getitem__(self, key) -> "MissingType":
        """Return self for any item access."""
        return self

    def __setattr__(self, name: str, value) -> None:
        """Silently ignore attribute assignment."""
        pass

    def __setitem__(self, key, value) -> None:
        """Silently ignore item assignment."""
        pass

    def __delattr__(self, name: str) -> None:
        """Silently ignore attribute deletion."""
        pass

    def __delitem__(self, key) -> None:
        """Silently ignore item deletion."""
        pass

    # =========================================================================
    # Calling
    # =========================================================================

    def __call__(self, *args, **kwargs) -> "MissingType":
        """Return self when called as a function."""
        return self

    # =========================================================================
    # Container Protocol
    # =========================================================================

    def __len__(self) -> int:
        """Missing has length 0."""
        return 0

    def __iter__(self):
        """Missing iterates as empty."""
        return iter([])

    def __contains__(self, item) -> bool:
        """Nothing is contained in Missing."""
        return False

    def __reversed__(self):
        """Reversed Missing is empty."""
        return iter([])

    # =========================================================================
    # Boolean and Truthiness
    # =========================================================================

    def __bool__(self) -> bool:
        """Missing is falsy."""
        return False

    # =========================================================================
    # Comparison Operators
    # =========================================================================

    def __eq__(self, other) -> bool:
        """Missing equals any other MissingType."""
        return isinstance(other, MissingType)

    def __ne__(self, other) -> bool:
        """Missing is not equal to non-MissingType values."""
        return not isinstance(other, MissingType)

    def __lt__(self, other) -> bool:
        """Missing is not less than anything."""
        return False

    def __le__(self, other) -> bool:
        """Missing is only <= other MissingType."""
        return isinstance(other, MissingType)

    def __gt__(self, other) -> bool:
        """Missing is not greater than anything."""
        return False

    def __ge__(self, other) -> bool:
        """Missing is only >= other MissingType."""
        return isinstance(other, MissingType)

    def __hash__(self) -> int:
        """Missing is hashable (for use in sets/dicts)."""
        return hash("Missing")

    # =========================================================================
    # Arithmetic Operators - Return self to absorb operations
    # =========================================================================

    def __add__(self, other) -> "MissingType":
        return self

    def __radd__(self, other) -> "MissingType":
        return self

    def __sub__(self, other) -> "MissingType":
        return self

    def __rsub__(self, other) -> "MissingType":
        return self

    def __mul__(self, other) -> "MissingType":
        return self

    def __rmul__(self, other) -> "MissingType":
        return self

    def __truediv__(self, other) -> "MissingType":
        return self

    def __rtruediv__(self, other) -> "MissingType":
        return self

    def __floordiv__(self, other) -> "MissingType":
        return self

    def __rfloordiv__(self, other) -> "MissingType":
        return self

    def __mod__(self, other) -> "MissingType":
        return self

    def __rmod__(self, other) -> "MissingType":
        return self

    def __pow__(self, other) -> "MissingType":
        return self

    def __rpow__(self, other) -> "MissingType":
        return self

    def __neg__(self) -> "MissingType":
        return self

    def __pos__(self) -> "MissingType":
        return self

    def __abs__(self) -> "MissingType":
        return self

    def __invert__(self) -> "MissingType":
        return self

    # =========================================================================
    # Bitwise Operators
    # =========================================================================

    def __and__(self, other) -> "MissingType":
        return self

    def __rand__(self, other) -> "MissingType":
        return self

    def __or__(self, other) -> "MissingType":
        return self

    def __ror__(self, other) -> "MissingType":
        return self

    def __xor__(self, other) -> "MissingType":
        return self

    def __rxor__(self, other) -> "MissingType":
        return self

    def __lshift__(self, other) -> "MissingType":
        return self

    def __rlshift__(self, other) -> "MissingType":
        return self

    def __rshift__(self, other) -> "MissingType":
        return self

    def __rrshift__(self, other) -> "MissingType":
        return self

    # =========================================================================
    # Type Conversion (return sensible defaults)
    # =========================================================================

    def __int__(self) -> int:
        return 0

    def __float__(self) -> float:
        return 0.0

    def __complex__(self) -> complex:
        return 0j

    def __index__(self) -> int:
        return 0

    # =========================================================================
    # String Representation
    # =========================================================================

    def __repr__(self) -> str:
        if self.reason is not None:
            return f"Missing({type(self.reason).__name__}: {self.reason})"
        return "Missing"

    def __str__(self) -> str:
        return "Missing"

    def __format__(self, format_spec: str) -> str:
        return "Missing"

    # =========================================================================
    # Context Manager (for `with` statements)
    # =========================================================================

    def __enter__(self) -> "MissingType":
        return self

    def __exit__(self, exc_type, exc_val, exc_tb) -> bool:
        return False

    # =========================================================================
    # Awaitable (for async code)
    # =========================================================================

    def __await__(self):
        """Allow `await Missing` to work (returns self)."""
        yield
        return self

__getattr__(name) #

Return self for any attribute access.

Source code in capturegraph-lib/capturegraph/data/containers/missing.py
def __getattr__(self, name: str) -> "MissingType":
    """Return self for any attribute access."""
    return self

__getitem__(key) #

Return self for any item access.

Source code in capturegraph-lib/capturegraph/data/containers/missing.py
def __getitem__(self, key) -> "MissingType":
    """Return self for any item access."""
    return self

__setattr__(name, value) #

Silently ignore attribute assignment.

Source code in capturegraph-lib/capturegraph/data/containers/missing.py
def __setattr__(self, name: str, value) -> None:
    """Silently ignore attribute assignment."""
    pass

__setitem__(key, value) #

Silently ignore item assignment.

Source code in capturegraph-lib/capturegraph/data/containers/missing.py
def __setitem__(self, key, value) -> None:
    """Silently ignore item assignment."""
    pass

__delattr__(name) #

Silently ignore attribute deletion.

Source code in capturegraph-lib/capturegraph/data/containers/missing.py
def __delattr__(self, name: str) -> None:
    """Silently ignore attribute deletion."""
    pass

__delitem__(key) #

Silently ignore item deletion.

Source code in capturegraph-lib/capturegraph/data/containers/missing.py
def __delitem__(self, key) -> None:
    """Silently ignore item deletion."""
    pass

__call__(*args, **kwargs) #

Return self when called as a function.

Source code in capturegraph-lib/capturegraph/data/containers/missing.py
def __call__(self, *args, **kwargs) -> "MissingType":
    """Return self when called as a function."""
    return self

__len__() #

Missing has length 0.

Source code in capturegraph-lib/capturegraph/data/containers/missing.py
def __len__(self) -> int:
    """Missing has length 0."""
    return 0

__iter__() #

Missing iterates as empty.

Source code in capturegraph-lib/capturegraph/data/containers/missing.py
def __iter__(self):
    """Missing iterates as empty."""
    return iter([])

__contains__(item) #

Nothing is contained in Missing.

Source code in capturegraph-lib/capturegraph/data/containers/missing.py
def __contains__(self, item) -> bool:
    """Nothing is contained in Missing."""
    return False

__reversed__() #

Reversed Missing is empty.

Source code in capturegraph-lib/capturegraph/data/containers/missing.py
def __reversed__(self):
    """Reversed Missing is empty."""
    return iter([])

__bool__() #

Missing is falsy.

Source code in capturegraph-lib/capturegraph/data/containers/missing.py
def __bool__(self) -> bool:
    """Missing is falsy."""
    return False

__eq__(other) #

Missing equals any other MissingType.

Source code in capturegraph-lib/capturegraph/data/containers/missing.py
def __eq__(self, other) -> bool:
    """Missing equals any other MissingType."""
    return isinstance(other, MissingType)

__ne__(other) #

Missing is not equal to non-MissingType values.

Source code in capturegraph-lib/capturegraph/data/containers/missing.py
def __ne__(self, other) -> bool:
    """Missing is not equal to non-MissingType values."""
    return not isinstance(other, MissingType)

__lt__(other) #

Missing is not less than anything.

Source code in capturegraph-lib/capturegraph/data/containers/missing.py
def __lt__(self, other) -> bool:
    """Missing is not less than anything."""
    return False

__le__(other) #

Missing is only <= other MissingType.

Source code in capturegraph-lib/capturegraph/data/containers/missing.py
def __le__(self, other) -> bool:
    """Missing is only <= other MissingType."""
    return isinstance(other, MissingType)

__gt__(other) #

Missing is not greater than anything.

Source code in capturegraph-lib/capturegraph/data/containers/missing.py
def __gt__(self, other) -> bool:
    """Missing is not greater than anything."""
    return False

__ge__(other) #

Missing is only >= other MissingType.

Source code in capturegraph-lib/capturegraph/data/containers/missing.py
def __ge__(self, other) -> bool:
    """Missing is only >= other MissingType."""
    return isinstance(other, MissingType)

__hash__() #

Missing is hashable (for use in sets/dicts).

Source code in capturegraph-lib/capturegraph/data/containers/missing.py
def __hash__(self) -> int:
    """Missing is hashable (for use in sets/dicts)."""
    return hash("Missing")

__await__() #

Allow await Missing to work (returns self).

Source code in capturegraph-lib/capturegraph/data/containers/missing.py
def __await__(self):
    """Allow `await Missing` to work (returns self)."""
    yield
    return self

is_missing(value) #

Check if a value is MissingType.

Parameters:

Name Type Description Default
value Any

Any value to check.

required

Returns:

Type Description
bool

True if value is a MissingType instance.

Example
from capturegraph.data import is_missing, MissingType
is_missing(MissingType())  # True
is_missing(42)             # False
Source code in capturegraph-lib/capturegraph/data/containers/missing.py
def is_missing(value: Any) -> bool:
    """Check if a value is MissingType.

    Args:
        value: Any value to check.

    Returns:
        True if value is a MissingType instance.

    Example:
        ```python
        from capturegraph.data import is_missing, MissingType
        is_missing(MissingType())  # True
        is_missing(42)             # False
        ```
    """
    return (value is None) or isinstance(value, MissingType)