Deprecation Policy#

aeon releases follow semantic versioning. A release number denotes <major>.<minor>.<patch> versions.

Broadly, if a change could unexpectedly cause code using aeon to crash when updating to the next version, then it should be deprecated to give the user a chance to prepare.

When to deprecate:

  • Removal or renaming of public classes or functions

  • Removal or renaming of public class parameters or function arguments

  • Addition of positional arguments without default values

Deprecation warnings should be included for at least one full minor version cycle before change or removal. If an item is deprecated on the release of v0.6.0, it can be removed in v0.7.0. If an item is deprecated between v0.6.0 and v0.7.0 (i.e. v0.6.1), it can be removed in v0.8.0.

Note that the deprecation policy does not necessarily apply to modules we class as still experimental. Currently experimental modules are:

  • annotation

  • anomaly_detection

  • benchmarking

  • segmentation

  • similarity_search

  • visualisation

  • testing

When we introduce a new module, we may classify it as experimental until the API is stable. We will try to not make drastic changes to experimental modules, but we need to retain the freedom to be more agile with the design in these cases.

Deprecation Process#

To deprecate functions and classes, write a “TODO” comment stating the version the code should be removed in and raise a warning using the deprecated package. This raises a FutureWarning saying that the functionality has been deprecated. Import from deprecated.sphinx so the deprecation message is automatically added to the documentation.

When renaming items, the functionality should ideally already be available with the new name when the deprecation warning is added. For example, including both the old and new name for a positional argument, or both functions/classes with the old and new names. This is not always possible, but it is good practice to do so.

In most cases not necessary to use the deprecated package when renaming or removing function and class keyword arguments. The default value of the argument can be set to "deprecated". If this value is changed, a FutureWarning can be raised. This isolates the deprecation warning to the argument, rather than the whole function or class. If renaming, the new keyword argument can be added alongside this, with the warning directing users to use the new keyword argument.

If the next version number has not been decided, use the next minor version number for the deprecated package version parameter.

Examples#

Functions and Methods#

Deprecate a function.

from deprecated.sphinx import deprecated

# TODO: remove in v0.7.0
@deprecated(
    version="0.6.0",
    reason="my_function will be removed in v0.7.0.",
    category=FutureWarning,
)
def my_function(x, y):
    """Return x + y."""
    return x + y

Deprecate a function to add a new positional argument. In certain cases, you can add a keyword argument with a default value, to ease the transition.

from deprecated.sphinx import deprecated

# TODO: remove in v0.7.0 and remove z default
@deprecated(
    version="0.6.0",
    reason="my_function will be include a third positional argument 'z' in v0.7.0, used for reasons.",
    category=FutureWarning,
)
def my_function(x, y, z=0):
    """Return x + y + z."""
    return x + y + z

Deprecate a class method to remove a positional argument.

from deprecated.sphinx import deprecated

class MyClass:
    """Contains my_method."""

    # TODO: remove in v0.7.0
    @deprecated(
        version="0.6.0",
        reason="my_method first positional argument 'x' will be removed in v0.7.0.",
        category=FutureWarning,
    )
    def my_method(self, x, y):
        """Return y."""
        return y

Classes#

Deprecate a class.

Since this example is deprecated on a patch release, it cannot be removed from the next minor release.

from deprecated.sphinx import deprecated

# TODO: remove in v0.8.0
@deprecated(
    version="0.6.1",
    reason="MyClass will be removed in v0.8.0.",
    category=FutureWarning,
)
class MyClass:
    """Exists (for now)."""
    pass

Deprecate a public class attribute. If we are renaming, we could add the new name and direct users to use that instead while updating both.

from deprecated.sphinx import deprecated

# TODO: remove in v0.7.0
@deprecated(
    version="0.6.0",
    reason="The my_attribute attribute will be removed in v0.7.0.",
    category=FutureWarning,
)
class MyClass:
    """Contains my_attribute."""

    my_attribute = 1

Deprecate a class parameter.

import warnings

class MyClass:
    """Contains x and y.

    Parameters
    ----------
    x : int, default=1
        The x parameter.
    y : int, default="deprecated"
        The y parameter.

        Deprecated and will be removed in v0.7.0.
    """

    # TODO remove 'y' in v0.7.0
    def __init__(self, x=1, y="deprecated"):
        self.x = x
        if y != "deprecated":
            warnings.warn("The 'y' parameter will be removed in v0.7.0.", FutureWarning)

Renaming#

Deprecate a class parameter to rename it.

import warnings

class MyClass:
    """Does stuff.

    Parameters
    ----------
    x : int, default=1
        The x parameter.
    new_param : int, default=2
        New identifier for old_param.
    old_param : int, default="deprecated"
        Old identifier for new_param. If used will set new_param.

        Deprecated and will be removed in v0.7.0.
    """

    # TODO remove 'old_param' in v0.7.0
    def __init__(self, x=1, new_param=2, old_param="deprecated"):
        self.x = x
        self.new_param = new_param
        if old_param != "deprecated":
            warnings.warn("The 'old_param' parameter will be removed in v0.7.0. Use 'new_param' instead.", FutureWarning)
            self.new_param = old_param

Deprecate a class to rename it.

from deprecated.sphinx import deprecated

class MyNewClass:
    """Exists."""

    @staticmethod
    def return_one():
        """Return 1."""
        return 1


# TODO: remove in v0.7.0
@deprecated(
    version="0.6.0",
    reason="The MyOldClass class has been renamed to MyNewClass and will be removed in v0.7.0.",
    category=FutureWarning,
)
class MyOldClass(MyNewClass):
    """Exists."""
    pass

Deprecate a function to rename it.

from deprecated.sphinx import deprecated

def my_new_function(x, y):
    """Return x + y."""
    return x + y


# TODO: remove in v0.7.0
@deprecated(
    version="0.6.0",
    reason="my_old_function has been renamed to my_new_function and will be removed in v0.7.0.",
    category=FutureWarning,
)
def my_old_function(x, y):
    """Return x + y."""
    return my_new_function(x, y)