Skip to content

Deprecating code

GavinHuttley edited this page Mar 4, 2024 · 5 revisions

Changing the API in a project that has public users require we don't just change it, but include a notice of the intended change and what the alternate is (if any).

Deprecating a function or method

The overview of steps required to deprecate callable code (i.e. a function or a method) include:

  1. Creating a replacement callable
  2. Marking the old callable as deprecated
  3. Replacing internal usage across the codebase

In this how-to guide, we will use an example of deprecating a generic function old() in place of new().

If there is any ambiguity in what you need to do, please post a question as a comment on the issue.

1. Creating a replacement function/method

Begin the deprecation process by adding a new callable that will replace the old one. Copy the contents of old() into a new function new():

def new():
    ...
    return result

def old():
    ...
    return result

... represents the contents of the callable.

In some cases, you many need to add the callable to a new location.

2. Marking the old function/method as deprecated

There are a few things required to deprecate the old callable.

from cogent3.util import warning as c3warn

def new():
    ...
    return result

@c3warn.deprecated_callable(version="2024.1", reason="function rename", new="new")
def old(): # pragma: no cover
    """deprecated, use new"""	
    return new()

Decorate old() with c3warn.deprecated_callable(). This will trigger a warning when the deprecated old() is used.

Refer to the issue post for the decorator arguments required:

  • version indicates the future release where the callable will be removed.
  • reason indicates the reason for the deprecation.
  • new is the name of the new callable.

Modify the deprecated callable old() so that it calls new() when run. Delete the contents of old() and replace with a comment indicating its deprecation. This ensures functionality for existing code and users.

Lastly, if the code is to be completely removed, add the markup # pragma: no cover to the definition line. This tells coverage to exclude it from measurement.

3. Replacing internal usage across the codebase

Next, all uses of the deprecated callable old() should be replaced with the updated one new(). This should be completed for all files under:

  • src/cogent3/
  • tests/, and
  • docs/

We recommend running the test suite and identifying which calls of old() triggered a deprecation warning. Please see:

You can turn the deprecation warnings into errors, which makes it easier to find the exact file and line:

$ pytest relevant_test_modules/ -W error::DeprecationWarning

Run the tests frequently as you amend the code.

Deprecating an argument

TODO

  • Refactor the function / method internals to using the new name
  • Modify the tests to using the new name

Discontinuing code

Use the same decorator for deprecating code, setting is_discontinued=True. In the reason, indicate a replacement in another project if possible. Update the docstring to indicate this function will be removed. Add the special comment (# pragma...) on the function definition line.

from cogent3.util import warning as c3warn

@c3warn.deprecated_callable(version="2024.1", reason="use scipy.stats ... instead", is_discontinued=True)
def t_test(): # pragma: no cover
    """being removed"""	
    return new()

Change all cogent3 uses of the code to the replacement. For example, if we are discontinuing a std() function in favour of using numpy.std(), we replace all calls to our std() with calls to numpy.std().

Development checklist

** TODO NEXT: Add docs, tests - here or dev practices?? **

Once you have made all edits, use nox to run the standard test suite:

$ nox -s test-<PYTHON_VERSION>

and test the code in the docs:

$ nox -s testdocs-<PYTHON_VERSION>