Module devstatusdecos.devstatusdecos

devstatusdecos

Decorators, warnings, and exceptions related to documentation of the development statuses of classes, functions, and other objects.

Classes

class ExperimentalWarning (*args, **kwargs)
Expand source code
class ExperimentalWarning(Warning):
    """A warning used to indicate that a function or class
    is experimental and may change at any time."""

A warning used to indicate that a function or class is experimental and may change at any time.

Ancestors

  • builtins.Warning
  • builtins.Exception
  • builtins.BaseException

Subclasses

class FatallyInsecureError (*args: object, code: int = 1)
Expand source code
class FatallyInsecureError(InsecureError, SystemExit):
    """
    An extention of `InsecureError`, raised in conditions where a function or class
    should raise an error that attempts to force termination of execution via an exception.
    """
    def __init__(self, *args: object, code:int = 1) -> None:
        super(InsecureError, self).__init__(*args)
        self.code = code

An extention of InsecureError, raised in conditions where a function or class should raise an error that attempts to force termination of execution via an exception.

Ancestors

  • InsecureError
  • builtins.RuntimeError
  • builtins.Exception
  • builtins.SystemExit
  • builtins.BaseException
class InsecureError (*args, **kwargs)
Expand source code
class InsecureError(RuntimeError):
    """
    An exception raised when a `insecure` Callable is executed.

    This is a subclass of `RuntimeError`
    and is used to indicate that a function or class is in some way insecure
    and should not be allowed to execute further.
    This exception is used mainly by the `insecure` decorator.
    """

An exception raised when a insecure Callable is executed.

This is a subclass of RuntimeError and is used to indicate that a function or class is in some way insecure and should not be allowed to execute further. This exception is used mainly by the insecure decorator.

Ancestors

  • builtins.RuntimeError
  • builtins.Exception
  • builtins.BaseException

Subclasses

class TodoError (*args, **kwargs)
Expand source code
class TodoError(NotImplementedError):
    """
    An exception raised when a `todo` Callable is executed.

    This is a subclass of `NotImplementedError` and is used to indicate that
    a function or class contains a todo and is likely missing a key part of
    its functionality. This exception is used mainly by the `todo` decorator.
    """

An exception raised when a todo Callable is executed.

This is a subclass of NotImplementedError and is used to indicate that a function or class contains a todo and is likely missing a key part of its functionality. This exception is used mainly by the todo decorator.

Ancestors

  • builtins.NotImplementedError
  • builtins.RuntimeError
  • builtins.Exception
  • builtins.BaseException
class TodoWarning (*args, **kwargs)
Expand source code
class TodoWarning(ExperimentalWarning):
    """A warning used to indicate that a function or class
    contains something todo and likely is missing a key part of
    its functionality and may change at any time or even be removed.
    This will also always be raised as a error when a `todo`
    `Callable` is called."""

A warning used to indicate that a function or class contains something todo and likely is missing a key part of its functionality and may change at any time or even be removed. This will also always be raised as a error when a todo Callable is called.

Ancestors

class deprecated (message: str,
/,
*,
category: Type[Warning] | None = builtins.DeprecationWarning,
stacklevel: int = 1)
Expand source code
class deprecated:
    """Indicate that a class, function or overload is deprecated.

    When this decorator is applied to an object, the type checker
    will generate a diagnostic on usage of the deprecated object.

    Usage:

        @deprecated("Use B instead")
        class A:
            pass

        @deprecated("Use g instead")
        def f():
            pass

        @overload
        @deprecated("int support is deprecated")
        def g(x: int) -> int: ...
        @overload
        def g(x: str) -> int: ...

    The warning specified by *category* will be emitted at runtime
    on use of deprecated objects. For functions, that happens on calls;
    for classes, on instantiation and on creation of subclasses.
    If the *category* is ``None``, no warning is emitted at runtime.
    The *stacklevel* determines where the
    warning is emitted. If it is ``1`` (the default), the warning
    is emitted at the direct caller of the deprecated object; if it
    is higher, it is emitted further up the stack.
    Static type checker behavior is not affected by the *category*
    and *stacklevel* arguments.

    The deprecation message passed to the decorator is saved in the
    ``__deprecated__`` attribute on the decorated object.
    If applied to an overload, the decorator
    must be after the ``@overload`` decorator for the attribute to
    exist on the overload as returned by ``get_overloads()``.

    See PEP 702 for details.

    """
    def __init__(
        self,
        message: str,
        /,
        *,
        category: typing.Optional[typing.Type[Warning]] = DeprecationWarning,
        stacklevel: int = 1,
    ) -> None:
        if not isinstance(message, str):
            raise TypeError(
                "Expected an object of type str for 'message', not "
                f"{type(message).__name__!r}"
            )
        self.message = message
        self.category = category
        self.stacklevel = stacklevel

    def __call__(self, arg: _T, /) -> _T:
        # Make sure the inner functions created below don't
        # retain a reference to self.
        msg = self.message
        category = self.category
        stacklevel = self.stacklevel
        if category is None:
            arg.__deprecated__ = msg
            return arg
        elif isinstance(arg, type):
            import functools
            from types import MethodType

            original_new = arg.__new__

            @functools.wraps(original_new)
            def __new__(cls, /, *args, **kwargs):
                if cls is arg:
                    warnings.warn(msg, category=category, stacklevel=stacklevel + 1)
                if original_new is not object.__new__:
                    return original_new(cls, *args, **kwargs)
                # Mirrors a similar check in object.__new__.
                elif cls.__init__ is object.__init__ and (args or kwargs):
                    raise TypeError(f"{cls.__name__}() takes no arguments")
                else:
                    return original_new(cls)

            arg.__new__ = staticmethod(__new__)

            original_init_subclass = arg.__init_subclass__
            # We need slightly different behavior if __init_subclass__
            # is a bound method (likely if it was implemented in Python)
            if isinstance(original_init_subclass, MethodType):
                original_init_subclass = original_init_subclass.__func__

                @functools.wraps(original_init_subclass)
                def __init_subclass__(*args, **kwargs):
                    warnings.warn(msg, category=category, stacklevel=stacklevel + 1)
                    return original_init_subclass(*args, **kwargs)

                arg.__init_subclass__ = classmethod(__init_subclass__)
            # Or otherwise, which likely means it's a builtin such as
            # object's implementation of __init_subclass__.
            else:
                @functools.wraps(original_init_subclass)
                def __init_subclass__(*args, **kwargs):
                    warnings.warn(msg, category=category, stacklevel=stacklevel + 1)
                    return original_init_subclass(*args, **kwargs)

                arg.__init_subclass__ = __init_subclass__

            arg.__deprecated__ = __new__.__deprecated__ = msg
            __init_subclass__.__deprecated__ = msg
            return arg
        elif callable(arg):
            import asyncio.coroutines
            import functools
            import inspect

            @functools.wraps(arg)
            def wrapper(*args, **kwargs):
                warnings.warn(msg, category=category, stacklevel=stacklevel + 1)
                return arg(*args, **kwargs)

            if asyncio.coroutines.iscoroutinefunction(arg):
                # Breakpoint: https://github.com/python/cpython/pull/99247
                if sys.version_info >= (3, 12):
                    wrapper = inspect.markcoroutinefunction(wrapper)
                else:
                    wrapper._is_coroutine = asyncio.coroutines._is_coroutine

            arg.__deprecated__ = wrapper.__deprecated__ = msg
            return wrapper
        else:
            raise TypeError(
                "@deprecated decorator with non-None category must be applied to "
                f"a class or callable, not {arg!r}"
            )

Indicate that a class, function or overload is deprecated.

When this decorator is applied to an object, the type checker will generate a diagnostic on usage of the deprecated object.

Usage

@deprecated("Use B instead") class A: pass

@deprecated("Use g instead") def f(): pass

@overload @deprecated("int support is deprecated") def g(x: int) -> int: … @overload def g(x: str) -> int: …

The warning specified by category will be emitted at runtime on use of deprecated objects. For functions, that happens on calls; for classes, on instantiation and on creation of subclasses. If the category is None, no warning is emitted at runtime. The stacklevel determines where the warning is emitted. If it is 1 (the default), the warning is emitted at the direct caller of the deprecated object; if it is higher, it is emitted further up the stack. Static type checker behavior is not affected by the category and stacklevel arguments.

The deprecation message passed to the decorator is saved in the __deprecated__ attribute on the decorated object. If applied to an overload, the decorator must be after the @overload decorator for the attribute to exist on the overload as returned by get_overloads().

See PEP 702 for details.

Subclasses

class superseded (message: str,
/,
*,
category: Type[Warning] | None = builtins.DeprecationWarning,
stacklevel: int = 1)
Expand source code
class deprecated:
    """Indicate that a class, function or overload is deprecated.

    When this decorator is applied to an object, the type checker
    will generate a diagnostic on usage of the deprecated object.

    Usage:

        @deprecated("Use B instead")
        class A:
            pass

        @deprecated("Use g instead")
        def f():
            pass

        @overload
        @deprecated("int support is deprecated")
        def g(x: int) -> int: ...
        @overload
        def g(x: str) -> int: ...

    The warning specified by *category* will be emitted at runtime
    on use of deprecated objects. For functions, that happens on calls;
    for classes, on instantiation and on creation of subclasses.
    If the *category* is ``None``, no warning is emitted at runtime.
    The *stacklevel* determines where the
    warning is emitted. If it is ``1`` (the default), the warning
    is emitted at the direct caller of the deprecated object; if it
    is higher, it is emitted further up the stack.
    Static type checker behavior is not affected by the *category*
    and *stacklevel* arguments.

    The deprecation message passed to the decorator is saved in the
    ``__deprecated__`` attribute on the decorated object.
    If applied to an overload, the decorator
    must be after the ``@overload`` decorator for the attribute to
    exist on the overload as returned by ``get_overloads()``.

    See PEP 702 for details.

    """
    def __init__(
        self,
        message: str,
        /,
        *,
        category: typing.Optional[typing.Type[Warning]] = DeprecationWarning,
        stacklevel: int = 1,
    ) -> None:
        if not isinstance(message, str):
            raise TypeError(
                "Expected an object of type str for 'message', not "
                f"{type(message).__name__!r}"
            )
        self.message = message
        self.category = category
        self.stacklevel = stacklevel

    def __call__(self, arg: _T, /) -> _T:
        # Make sure the inner functions created below don't
        # retain a reference to self.
        msg = self.message
        category = self.category
        stacklevel = self.stacklevel
        if category is None:
            arg.__deprecated__ = msg
            return arg
        elif isinstance(arg, type):
            import functools
            from types import MethodType

            original_new = arg.__new__

            @functools.wraps(original_new)
            def __new__(cls, /, *args, **kwargs):
                if cls is arg:
                    warnings.warn(msg, category=category, stacklevel=stacklevel + 1)
                if original_new is not object.__new__:
                    return original_new(cls, *args, **kwargs)
                # Mirrors a similar check in object.__new__.
                elif cls.__init__ is object.__init__ and (args or kwargs):
                    raise TypeError(f"{cls.__name__}() takes no arguments")
                else:
                    return original_new(cls)

            arg.__new__ = staticmethod(__new__)

            original_init_subclass = arg.__init_subclass__
            # We need slightly different behavior if __init_subclass__
            # is a bound method (likely if it was implemented in Python)
            if isinstance(original_init_subclass, MethodType):
                original_init_subclass = original_init_subclass.__func__

                @functools.wraps(original_init_subclass)
                def __init_subclass__(*args, **kwargs):
                    warnings.warn(msg, category=category, stacklevel=stacklevel + 1)
                    return original_init_subclass(*args, **kwargs)

                arg.__init_subclass__ = classmethod(__init_subclass__)
            # Or otherwise, which likely means it's a builtin such as
            # object's implementation of __init_subclass__.
            else:
                @functools.wraps(original_init_subclass)
                def __init_subclass__(*args, **kwargs):
                    warnings.warn(msg, category=category, stacklevel=stacklevel + 1)
                    return original_init_subclass(*args, **kwargs)

                arg.__init_subclass__ = __init_subclass__

            arg.__deprecated__ = __new__.__deprecated__ = msg
            __init_subclass__.__deprecated__ = msg
            return arg
        elif callable(arg):
            import asyncio.coroutines
            import functools
            import inspect

            @functools.wraps(arg)
            def wrapper(*args, **kwargs):
                warnings.warn(msg, category=category, stacklevel=stacklevel + 1)
                return arg(*args, **kwargs)

            if asyncio.coroutines.iscoroutinefunction(arg):
                # Breakpoint: https://github.com/python/cpython/pull/99247
                if sys.version_info >= (3, 12):
                    wrapper = inspect.markcoroutinefunction(wrapper)
                else:
                    wrapper._is_coroutine = asyncio.coroutines._is_coroutine

            arg.__deprecated__ = wrapper.__deprecated__ = msg
            return wrapper
        else:
            raise TypeError(
                "@deprecated decorator with non-None category must be applied to "
                f"a class or callable, not {arg!r}"
            )

Indicate that a class, function or overload is deprecated.

When this decorator is applied to an object, the type checker will generate a diagnostic on usage of the deprecated object.

Usage

@deprecated("Use B instead") class A: pass

@deprecated("Use g instead") def f(): pass

@overload @deprecated("int support is deprecated") def g(x: int) -> int: … @overload def g(x: str) -> int: …

The warning specified by category will be emitted at runtime on use of deprecated objects. For functions, that happens on calls; for classes, on instantiation and on creation of subclasses. If the category is None, no warning is emitted at runtime. The stacklevel determines where the warning is emitted. If it is 1 (the default), the warning is emitted at the direct caller of the deprecated object; if it is higher, it is emitted further up the stack. Static type checker behavior is not affected by the category and stacklevel arguments.

The deprecation message passed to the decorator is saved in the __deprecated__ attribute on the decorated object. If applied to an overload, the decorator must be after the @overload decorator for the attribute to exist on the overload as returned by get_overloads().

See PEP 702 for details.

Subclasses

class experimental (message: LiteralString,
/,
*,
category: Type[Warning] | None = devstatusdecos.devstatusdecos.ExperimentalWarning,
stacklevel: int = 1)
Expand source code
class experimental(deprecated):  # pylint:disable=invalid-name
    """A decorator that marks a class,
    function or method as experimental and should be used with caution.
    Note that this inherits the native `deprecated` decorator,
    so some introspective tools might falsely flag this as
    a deprecated function instead."""

    def __init__(self,
                 message:LiteralString,
                 /,
                 *,
                 category:Type[Warning]|None = ExperimentalWarning,
                 stacklevel:int = 1):
        super().__init__(message, category=category, stacklevel=stacklevel)

A decorator that marks a class, function or method as experimental and should be used with caution. Note that this inherits the native deprecated decorator, so some introspective tools might falsely flag this as a deprecated function instead.

Ancestors

  • typing_extensions.deprecated

Subclasses

class insecure (message: str, fatal: bool, code: int = 1)
Expand source code
class insecure():  # pylint:disable=invalid-name
    """A decorator that marks a class,
    function or method as insecure, indicating that it
    should not be allowed to execute further.
    Unlike other dev stats decorators,
    this replaces the a decorated callable or type entirely with
    a callable that raises an error,
    which may confuse some type checkers.

    While not intended for use on things besides types or callables,
    the exception will be raised immediately in cases there the decorated
    object is not callable.
    """

    def __init__(self,
                 message:str,
                 fatal:bool,
                 code:int = 1,
                 ):
        self.exc = FatallyInsecureError(message, code = code)
        if not fatal:
            self.exc = InsecureError(message)
        self.code = code

    def __call__(self, arg:Any) -> Callable[..., NoReturn]:  # noqa:F811, E301
        if not callable(arg):
            # its already possibly to late, but lets stop anyway
            raise self.exc

        def __wrapper(*_, **__):
            raise self.exc

        return __wrapper

A decorator that marks a class, function or method as insecure, indicating that it should not be allowed to execute further. Unlike other dev stats decorators, this replaces the a decorated callable or type entirely with a callable that raises an error, which may confuse some type checkers.

While not intended for use on things besides types or callables, the exception will be raised immediately in cases there the decorated object is not callable.

class pending_deprecation (message: LiteralString,
/,
*,
pending_category: Type[DeprecationWarning | PendingDeprecationWarning] | None = builtins.PendingDeprecationWarning,
deprecated_category: Any = Ellipsis,
stacklevel: int = 1,
deprecate_py_ver: Any = Ellipsis)
Expand source code
class pending_deprecation(deprecated, Generic[VersionInfoTupleT]):  # pylint:disable=invalid-name
    """A decorator that indicates that the decorated class, function,
    or method is pending deprecation.

    Note that this inherits the native `deprecated` decorator,
    so some introspective tools might falsely flag this as
    a deprecated function instead."""

    def __init__(self,
                 message:LiteralString,
                 /,
                 *,
                 pending_category:Type[DeprecationWarning | PendingDeprecationWarning
                                       ]|None = PendingDeprecationWarning,
                 deprecated_category:Any = ...,  # pylint:disable=unused-argument
                 stacklevel:int = 1,
                 deprecate_py_ver:Any = ...  # pylint:disable=unused-argument
                 ):
        super().__init__(message, category=pending_category, stacklevel=stacklevel)

    def __new__(cls,
                message:LiteralString,
                /,
                *,
                pending_category:Type[DeprecationWarning | PendingDeprecationWarning
                                      ]|None = PendingDeprecationWarning,
                deprecated_category:Type[DeprecationWarning
                                         ]|None = DeprecationWarning,
                stacklevel:int = 1,
                deprecate_py_ver:VersionInfoTupleT = None
                ):
        if deprecate_py_ver is not None and version_info >= deprecate_py_ver:
            return deprecated(message, category=deprecated_category, stacklevel=stacklevel)
        return super().__new__(cls)

A decorator that indicates that the decorated class, function, or method is pending deprecation.

Note that this inherits the native deprecated decorator, so some introspective tools might falsely flag this as a deprecated function instead.

Ancestors

  • typing_extensions.deprecated
  • typing.Generic
class todo (message: LiteralString,
error_condition: Callable[[Any], bool] | None = None,
/,
*,
category: Type[Warning] | None = devstatusdecos.devstatusdecos.TodoWarning,
stacklevel: int = 1)
Expand source code
class todo(experimental):  # pylint:disable=invalid-name
    """
    A decorator that marks a class, function, or method as todo and is
    presumed to be missing a key part of it's functionality.
    It may change at any time or even removed.

    This will always raise a `TodoError` when a `todo`
    Callable is called (or Type is initalized),
    unless `error_condition`
    is truthy or a callable that will return `True`.
    `error_condition` will always be checked when the wrapped
    function is called.

    The constructor matches the same parameters as `deprecated`,
    in exception for the fact that the default warning type is a
    `TodoWarning` and that 'TODO:' is always appended to the message.
    """

    # Sadly there is not way to easily make type checkers
    # recognise this besides making a plugin for this,
    # so we have to piggyback off of the deprecation
    # detection in all the type checkers for now.

    TODO_PREFIX:LiteralString = "TODO: "

    def __init__(self,
                 message:LiteralString,
                 error_condition:Callable[[Any], bool] | None = None,
                 /,
                 *,
                 category:Type[Warning] | None = TodoWarning,
                 stacklevel:int = 1
                 ):
        if not message.startswith(self.TODO_PREFIX):
            message = self.TODO_PREFIX + message
        super().__init__(message,
                         category=category,
                         stacklevel=stacklevel
                         )
        self.error_condition = error_condition

    @overload
    def __call__(self,  # noqa:F811, E302
                 arg:Callable[PS, RT],
                 /,
                 ) -> Callable[PS, RT|Never]:
        ...

    @overload  # noqa:E301
    def __call__(self, arg:T, /) -> T:  # noqa:F811, E302
        ...

    def __call__(self, arg:Any, /) -> Any:  # noqa:F811, E301
        arg = super().__call__(arg)
        d = f"{self.message}\n{getattr(arg, '__doc__', '')}".strip()
        setattr(arg, "__doc__", d)
        if callable(arg):
            @wraps(arg, assigned=("__module__", "__name__", "__qualname__", "__doc__"))
            def __wrapper(*args, **kwargs):
                if self.error_condition is not None and self.error_condition(arg):
                    raise TodoError(self.message)
                return arg(*args, **kwargs)
            return __wrapper
        return arg

A decorator that marks a class, function, or method as todo and is presumed to be missing a key part of it's functionality. It may change at any time or even removed.

This will always raise a TodoError when a todo Callable is called (or Type is initalized), unless error_condition is truthy or a callable that will return True. error_condition will always be checked when the wrapped function is called.

The constructor matches the same parameters as deprecated, in exception for the fact that the default warning type is a TodoWarning and that 'TODO:' is always appended to the message.

Ancestors

Class variables

var TODO_PREFIX : LiteralString
class TODO (message: LiteralString,
error_condition: Callable[[Any], bool] | None = None,
/,
*,
category: Type[Warning] | None = devstatusdecos.devstatusdecos.TodoWarning,
stacklevel: int = 1)
Expand source code
class todo(experimental):  # pylint:disable=invalid-name
    """
    A decorator that marks a class, function, or method as todo and is
    presumed to be missing a key part of it's functionality.
    It may change at any time or even removed.

    This will always raise a `TodoError` when a `todo`
    Callable is called (or Type is initalized),
    unless `error_condition`
    is truthy or a callable that will return `True`.
    `error_condition` will always be checked when the wrapped
    function is called.

    The constructor matches the same parameters as `deprecated`,
    in exception for the fact that the default warning type is a
    `TodoWarning` and that 'TODO:' is always appended to the message.
    """

    # Sadly there is not way to easily make type checkers
    # recognise this besides making a plugin for this,
    # so we have to piggyback off of the deprecation
    # detection in all the type checkers for now.

    TODO_PREFIX:LiteralString = "TODO: "

    def __init__(self,
                 message:LiteralString,
                 error_condition:Callable[[Any], bool] | None = None,
                 /,
                 *,
                 category:Type[Warning] | None = TodoWarning,
                 stacklevel:int = 1
                 ):
        if not message.startswith(self.TODO_PREFIX):
            message = self.TODO_PREFIX + message
        super().__init__(message,
                         category=category,
                         stacklevel=stacklevel
                         )
        self.error_condition = error_condition

    @overload
    def __call__(self,  # noqa:F811, E302
                 arg:Callable[PS, RT],
                 /,
                 ) -> Callable[PS, RT|Never]:
        ...

    @overload  # noqa:E301
    def __call__(self, arg:T, /) -> T:  # noqa:F811, E302
        ...

    def __call__(self, arg:Any, /) -> Any:  # noqa:F811, E301
        arg = super().__call__(arg)
        d = f"{self.message}\n{getattr(arg, '__doc__', '')}".strip()
        setattr(arg, "__doc__", d)
        if callable(arg):
            @wraps(arg, assigned=("__module__", "__name__", "__qualname__", "__doc__"))
            def __wrapper(*args, **kwargs):
                if self.error_condition is not None and self.error_condition(arg):
                    raise TodoError(self.message)
                return arg(*args, **kwargs)
            return __wrapper
        return arg

A decorator that marks a class, function, or method as todo and is presumed to be missing a key part of it's functionality. It may change at any time or even removed.

This will always raise a TodoError when a todo Callable is called (or Type is initalized), unless error_condition is truthy or a callable that will return True. error_condition will always be checked when the wrapped function is called.

The constructor matches the same parameters as deprecated, in exception for the fact that the default warning type is a TodoWarning and that 'TODO:' is always appended to the message.

Ancestors

Class variables

var TODO_PREFIX : LiteralString
class fixme (message: LiteralString,
error_condition: Callable[[Any], bool] | None = None,
/,
*,
category: Type[Warning] | None = devstatusdecos.devstatusdecos.TodoWarning,
stacklevel: int = 1)
Expand source code
class todo(experimental):  # pylint:disable=invalid-name
    """
    A decorator that marks a class, function, or method as todo and is
    presumed to be missing a key part of it's functionality.
    It may change at any time or even removed.

    This will always raise a `TodoError` when a `todo`
    Callable is called (or Type is initalized),
    unless `error_condition`
    is truthy or a callable that will return `True`.
    `error_condition` will always be checked when the wrapped
    function is called.

    The constructor matches the same parameters as `deprecated`,
    in exception for the fact that the default warning type is a
    `TodoWarning` and that 'TODO:' is always appended to the message.
    """

    # Sadly there is not way to easily make type checkers
    # recognise this besides making a plugin for this,
    # so we have to piggyback off of the deprecation
    # detection in all the type checkers for now.

    TODO_PREFIX:LiteralString = "TODO: "

    def __init__(self,
                 message:LiteralString,
                 error_condition:Callable[[Any], bool] | None = None,
                 /,
                 *,
                 category:Type[Warning] | None = TodoWarning,
                 stacklevel:int = 1
                 ):
        if not message.startswith(self.TODO_PREFIX):
            message = self.TODO_PREFIX + message
        super().__init__(message,
                         category=category,
                         stacklevel=stacklevel
                         )
        self.error_condition = error_condition

    @overload
    def __call__(self,  # noqa:F811, E302
                 arg:Callable[PS, RT],
                 /,
                 ) -> Callable[PS, RT|Never]:
        ...

    @overload  # noqa:E301
    def __call__(self, arg:T, /) -> T:  # noqa:F811, E302
        ...

    def __call__(self, arg:Any, /) -> Any:  # noqa:F811, E301
        arg = super().__call__(arg)
        d = f"{self.message}\n{getattr(arg, '__doc__', '')}".strip()
        setattr(arg, "__doc__", d)
        if callable(arg):
            @wraps(arg, assigned=("__module__", "__name__", "__qualname__", "__doc__"))
            def __wrapper(*args, **kwargs):
                if self.error_condition is not None and self.error_condition(arg):
                    raise TodoError(self.message)
                return arg(*args, **kwargs)
            return __wrapper
        return arg

A decorator that marks a class, function, or method as todo and is presumed to be missing a key part of it's functionality. It may change at any time or even removed.

This will always raise a TodoError when a todo Callable is called (or Type is initalized), unless error_condition is truthy or a callable that will return True. error_condition will always be checked when the wrapped function is called.

The constructor matches the same parameters as deprecated, in exception for the fact that the default warning type is a TodoWarning and that 'TODO:' is always appended to the message.

Ancestors

Class variables

var TODO_PREFIX : LiteralString
class FIXME (message: LiteralString,
error_condition: Callable[[Any], bool] | None = None,
/,
*,
category: Type[Warning] | None = devstatusdecos.devstatusdecos.TodoWarning,
stacklevel: int = 1)
Expand source code
class todo(experimental):  # pylint:disable=invalid-name
    """
    A decorator that marks a class, function, or method as todo and is
    presumed to be missing a key part of it's functionality.
    It may change at any time or even removed.

    This will always raise a `TodoError` when a `todo`
    Callable is called (or Type is initalized),
    unless `error_condition`
    is truthy or a callable that will return `True`.
    `error_condition` will always be checked when the wrapped
    function is called.

    The constructor matches the same parameters as `deprecated`,
    in exception for the fact that the default warning type is a
    `TodoWarning` and that 'TODO:' is always appended to the message.
    """

    # Sadly there is not way to easily make type checkers
    # recognise this besides making a plugin for this,
    # so we have to piggyback off of the deprecation
    # detection in all the type checkers for now.

    TODO_PREFIX:LiteralString = "TODO: "

    def __init__(self,
                 message:LiteralString,
                 error_condition:Callable[[Any], bool] | None = None,
                 /,
                 *,
                 category:Type[Warning] | None = TodoWarning,
                 stacklevel:int = 1
                 ):
        if not message.startswith(self.TODO_PREFIX):
            message = self.TODO_PREFIX + message
        super().__init__(message,
                         category=category,
                         stacklevel=stacklevel
                         )
        self.error_condition = error_condition

    @overload
    def __call__(self,  # noqa:F811, E302
                 arg:Callable[PS, RT],
                 /,
                 ) -> Callable[PS, RT|Never]:
        ...

    @overload  # noqa:E301
    def __call__(self, arg:T, /) -> T:  # noqa:F811, E302
        ...

    def __call__(self, arg:Any, /) -> Any:  # noqa:F811, E301
        arg = super().__call__(arg)
        d = f"{self.message}\n{getattr(arg, '__doc__', '')}".strip()
        setattr(arg, "__doc__", d)
        if callable(arg):
            @wraps(arg, assigned=("__module__", "__name__", "__qualname__", "__doc__"))
            def __wrapper(*args, **kwargs):
                if self.error_condition is not None and self.error_condition(arg):
                    raise TodoError(self.message)
                return arg(*args, **kwargs)
            return __wrapper
        return arg

A decorator that marks a class, function, or method as todo and is presumed to be missing a key part of it's functionality. It may change at any time or even removed.

This will always raise a TodoError when a todo Callable is called (or Type is initalized), unless error_condition is truthy or a callable that will return True. error_condition will always be checked when the wrapped function is called.

The constructor matches the same parameters as deprecated, in exception for the fact that the default warning type is a TodoWarning and that 'TODO:' is always appended to the message.

Ancestors

Class variables

var TODO_PREFIX : LiteralString