Skip to content

deeplabcut.core.deprecation

Classes:

Name Description
DLCDeprecationWarning

Project-specific deprecation warning. Helps with filtering.

Functions:

Name Description
deprecated

Mark a function as deprecated.

renamed_parameter

Support a renamed keyword argument while warning callers to update.

DLCDeprecationWarning

Bases: DeprecationWarning

Project-specific deprecation warning. Helps with filtering.

Source code in deeplabcut/core/deprecation.py
class DLCDeprecationWarning(DeprecationWarning):
    """Project-specific deprecation warning. Helps with filtering."""

deprecated

deprecated(
    *,
    replacement: str | None = None,
    since: str | None = None,
    removed_in: str | None = None,
    stacklevel: int = 2,
    name: str | None = None
) -> Callable[[Callable[P, R]], Callable[P, R]]

Mark a function as deprecated.

Parameters:

Name Type Description Default

replacement

str | None

Fully-qualified name of the replacement callable, e.g. "deeplabcut.utils.auxfun_videos.list_videos_in_folder".

None

since

str | None

Version in which the function was deprecated.

None

removed_in

str | None

Version in which the function will be removed.

None

stacklevel

int

Stack level for the warning. Default is 2, which points to the caller of the deprecated function. Increase this if you wrap calls in an extra legacy wrapper function that you don't want to be blamed for the deprecation.

2

name

str | None

Name to report in the warning message. Defaults to __qualname__.

None
Source code in deeplabcut/core/deprecation.py
def deprecated(
    *,
    replacement: str | None = None,
    since: str | None = None,
    removed_in: str | None = None,
    stacklevel: int = 2,
    name: str | None = None,
) -> Callable[[Callable[P, R]], Callable[P, R]]:
    """Mark a function as deprecated.

    Args:
        replacement: Fully-qualified name of the replacement callable, e.g.
            ``"deeplabcut.utils.auxfun_videos.list_videos_in_folder"``.
        since: Version in which the function was deprecated.
        removed_in: Version in which the function will be removed.
        stacklevel: Stack level for the warning.  Default is 2, which points to the
            caller of the deprecated function.  Increase this if you wrap calls in
            an extra legacy wrapper function that you don't want to be blamed for the deprecation.
        name: Name to report in the warning message. Defaults to ``__qualname__``.
    """

    def decorator(fn: Callable[P, R]) -> Callable[P, R]:
        info = DeprecationInfo(
            kind="callable",
            target=name or fn.__qualname__,
            replacement=replacement,
            since=since,
            removed_in=removed_in,
        )
        message = info.format_message()

        @functools.wraps(fn)
        def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
            warnings.warn(message, DLCDeprecationWarning, stacklevel=stacklevel)
            return fn(*args, **kwargs)

        wrapper.__doc__ = f"Deprecated. {message}\n\n" + (fn.__doc__ or "")
        wrapper.__deprecated_info__ = info
        return wrapper

    return decorator

renamed_parameter

renamed_parameter(*, old: str, new: str, since: str | None = None) -> Callable[[Callable[P, R]], Callable[P, R]]

Support a renamed keyword argument while warning callers to update.

Parameters:

Name Type Description Default

old

str

The old parameter name that callers may still pass.

required

new

str

The current parameter name the function actually accepts.

required

since

str | None

Version when the rename happened.

None
Rules
  • new must be the name used in the function signature and all internal call-sites. old must not appear in the signature.
  • Do not chain renames. If A was renamed to B and B is later renamed to C, replace the A→B decorator with A→C directly rather than stacking a second decorator. Example: @renamed_parameter(old="A", new="C", since="12.4.0") @renamed_parameter(old="B", new="C", since="13.0.0") def func(*, C: int): print(f"C={C}")
  • Multiple independent renames on the same function (e.g. batchsize→batch_size and videotype→video_extensions) are fine as long as they do not form a chain.
  • This decorator only intercepts keyword arguments. Positional arguments are passed through unchanged; renaming a parameter that callers commonly pass positionally will not be caught.
Source code in deeplabcut/core/deprecation.py
def renamed_parameter(
    *,
    old: str,
    new: str,
    since: str | None = None,
) -> Callable[[Callable[P, R]], Callable[P, R]]:
    """Support a renamed keyword argument while warning callers to update.

    Args:
        old: The old parameter name that callers may still pass.
        new: The current parameter name the function actually accepts.
        since: Version when the rename happened.

    Rules:
        - ``new`` must be the name used in the function signature and all
          internal call-sites.  ``old`` must **not** appear in the signature.
        - Do **not** chain renames.  If ``A`` was renamed to ``B`` and ``B``
          is later renamed to ``C``, replace the ``A→B`` decorator with
          ``A→C`` directly rather than stacking a second decorator.
            Example:
                @renamed_parameter(old="A", new="C", since="12.4.0")
                @renamed_parameter(old="B", new="C", since="13.0.0")
                def func(*, C: int):
                    print(f"C={C}")
        - Multiple independent renames on the same function (e.g.
          ``batchsize→batch_size`` *and* ``videotype→video_extensions``) are fine
          as long as they do not form a chain.
        - This decorator only intercepts **keyword** arguments.  Positional
          arguments are passed through unchanged; renaming a parameter that
          callers commonly pass positionally will not be caught.
    """

    def decorator(fn: Callable[P, R]) -> Callable[P, R]:
        sig = inspect.signature(fn)

        # Guard: disallow chaining renames (A→B stacked on top of B→C).
        existing = getattr(fn, "__deprecated_params__", ())
        for prev in existing:
            if prev.old_parameter == new:
                raise ValueError(
                    f"@renamed_parameter: chaining renames is not allowed. "
                    f"'{old}' → '{new}' would chain with the existing "
                    f"'{prev.old_parameter}' → '{prev.new_parameter}' rename "
                    f"on {fn.__qualname__}. "
                    f"Use '{old}' → '{prev.new_parameter}' directly instead."
                )

        # Guard: 'new' must actually exist in the function's signature.
        if new not in sig.parameters:
            raise ValueError(
                f"@renamed_parameter: '{new}' is not a parameter of "
                f"{fn.__qualname__}. "
                f"Available parameters: {list(sig.parameters)}"
            )

        # Guard: 'old' must NOT exist in the signature.
        if old in sig.parameters:
            raise ValueError(
                f"@renamed_parameter: '{old}' is still a parameter of "
                f"{fn.__qualname__}. Use either old name or new name: '{new}'."
            )

        info = DeprecationInfo(
            kind="parameter",
            target=fn.__qualname__,
            since=since,
            old_parameter=old,
            new_parameter=new,
        )
        message = info.format_message()

        @functools.wraps(fn)
        def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
            if old in kwargs:
                if new in kwargs:
                    raise TypeError(f"{fn.__qualname__} received both '{old}' and '{new}'. Use only '{new}'.")
                warnings.warn(message, DLCDeprecationWarning, stacklevel=2)
                kwargs[new] = kwargs.pop(old)
            return fn(*args, **kwargs)

        wrapper.__deprecated_params__ = (*existing, info)
        return wrapper

    return decorator