betty.typing - Betty documentation

Hide navigation sidebar
Hide table of contents sidebar
Skip to content
Back to top
Toggle table of contents sidebar

# Source code for betty.typing

    """
    Providing typing utilities.
    """

    from __future__ import annotations

    from typing import TypeVar, TypeAlias, final, Any

    from typing_extensions import TypeIs

    from betty.docstring import append

    _T = TypeVar("_T")

    def _should_mark(target: Any, key: str) -> bool:
        attr_name = f"_betty_typing_{key}"
        if hasattr(target, attr_name):
            return False
        setattr(target, attr_name, True)
        return True

    def _internal(target: _T) -> _T:
        if _should_mark(target, "internal"):
            target.__doc__ = append(
                target.__doc__ or "",
                "This is internal. It **MAY** be used anywhere in Betty's source code, but **MUST NOT** be used by third-party code.",
            )
        return target

    @_internal
    def internal(target: _T) -> _T:
        """
        Mark a target as internal to Betty.

        Anything decorated with ``@internal`` MAY be used anywhere in Betty's source code,
        but MUST be considered private by third-party code.
        """
        return _internal(target)

    @internal
    def public(target: _T) -> _T:
        """
        Mark a target as publicly usable.

        This is intended for items nested inside something marked with :py:func:`betty.typing.internal`,
        such as class attributes: third-party code **SHOULD NOT** use a class marked ``@internal``
        directly, but **MAY** use any of its attributes that are marked ``@public``.
        """
        return target

    def private(target: _T) -> _T:
        """
        Mark a target as private to its containing scope.

        This is intended for items that cannot be marked private by prefixing their names with an underscore.
        """
        if _should_mark(target, "private"):
            target.__doc__ = append(
                target.__doc__ or "",
                "This is private. It **MUST NOT** be used anywhere outside its containing scope.",
            )
        return target

    def pickleable(target: _T) -> _T:
        """
        Mark a target as pickleable.
        """
        if _should_mark(target, "pickleable"):
            target.__doc__ = append(
                target.__doc__ or "",
                "This can be pickled.",
            )
        return target

    def unpickleable(target: _T) -> _T:
        """
        Mark a target as unpickleable.
        """
        if _should_mark(target, "unpickleable"):
            target.__doc__ = append(
                target.__doc__ or "",
                "This can NOT be pickled, and MUST NOT be used between different processes.",
            )

            def _reduce_ex(*_) -> None:
                raise RuntimeError(f"{target} is not pickleable.")

            target.__reduce_ex__ = _reduce_ex  # type: ignore[assignment, method-assign]
        return target

    def threadsafe(target: _T) -> _T:
        """
        Mark a target as thread-safe.
        """
        if _should_mark(target, "threadsafe"):
            target.__doc__ = append(
                target.__doc__ or "",
                "This is thread-safe, which means you can safely use this between different threads.",
            )
        return target

    def processsafe(target: _T) -> _T:
        """
        Mark a target as process-safe.
        """
        if _should_mark(target, "processsafe"):
            target = pickleable(target)
            target = threadsafe(target)
            target.__doc__ = append(
                target.__doc__ or "",
                "This is process-safe, which means you can safely use this between different processes.",
            )
        return target

    @final
    class Void:
        """
        A sentinel that describes the absence of a value.

        Using this sentinel allows for actual values to be ``None``. Like ``None``,
        ``Void`` is only ever used through its type, and never instantiated.
        """

        def __new__(cls):  # pragma: no cover  # noqa D102
            raise RuntimeError("The Void sentinel cannot be instantiated.")

    Voidable: TypeAlias = _T | type[Void]

    def not_void(value: _T | type[Void]) -> TypeIs[_T]:
        """
        Test that a value is not :py:class:`betty.typing.Void`.
        """
        return value is not Void