Skip to content

Getting Started

Quick Start (5 minutes)

Get up and running with deprecator quickly:

1. Install

pip install 'deprecator[cli]'

2. Initialize

Run the initialization command in your project:

deprecator init

This creates _deprecations.py in your package:

from deprecator import for_package

deprecator = for_package(__package__)

3. Define Your First Deprecation

Add to _deprecations.py:

"""Basic deprecation example.

This example demonstrates the core deprecator workflow:
1. Get a deprecator for your package
2. Define a deprecation with version boundaries
3. Use it in your code
"""

from packaging.version import Version

from deprecator import for_package

# Get a deprecator for your package
# NOTE: In real code, just use: deprecator = for_package("mypackage")
# The _version parameter is only used here because "mypackage" isn't installed.
deprecator = for_package("mypackage", _version=Version("2.0.0"))

# Define a deprecation with version boundaries
OLD_API_DEPRECATION = deprecator.define(
    "old_api is deprecated, use new_api instead",
    warn_in="1.0.0",  # Start warning at this version
    gone_in="3.0.0",  # Should be removed at this version
)


def old_api() -> str:
    """Deprecated API function."""
    OLD_API_DEPRECATION.warn()
    return new_api()


def new_api() -> str:
    """New API function that replaces old_api."""
    return "new implementation"

4. Test It

"""Example tests for deprecations.

These tests demonstrate how to verify that:
1. Deprecated functions emit the correct warnings
2. New replacement functions don't emit warnings
"""

import pytest
from basic_deprecation import new_api, old_api


def test_deprecation_warning() -> None:
    """Test that deprecated functions emit warnings."""
    with pytest.warns(DeprecationWarning, match="old_api is deprecated"):
        result = old_api()
    assert result == "new implementation"


def test_new_api_no_warning() -> None:
    """Test that new API doesn't emit warnings."""
    import warnings

    with warnings.catch_warnings():
        warnings.simplefilter("error")  # Turn warnings into errors
        result = new_api()  # Should not raise
    assert result == "new implementation"

Detailed Installation Options

Basic Installation

Install deprecator using pip:

pip install deprecator

With CLI Support

To use the command-line tools, install with the CLI extra:

pip install 'deprecator[cli]'

Development Installation

For development, clone the repository:

git clone https://github.com/RonnyPfannschmidt/deprecator.git
cd deprecator
# Use uv run for all development commands

Project Setup

Automatic Setup with CLI

The easiest way to set up deprecator in your project is using the CLI:

# If installed globally
deprecator init

# Or using uv in development
uv run deprecator init

This command will: 1. Create a _deprecations.py module in your package 2. Configure your pyproject.toml with entry points 3. Set up the deprecator instance for your package

Manual Setup

If you prefer manual setup or need custom configuration:

  1. Create a deprecations module (_deprecations.py):
"""Example of package-level deprecation setup.

This is how you'd typically set up deprecator in your package's
_deprecations.py file. All deprecations are defined in one place.
"""

from packaging.version import Version

from deprecator import for_package

# Create a deprecator instance for your package
# NOTE: In real code, just use: deprecator = for_package(__package__)
# The _version parameter is only used here because this isn't a real package.
deprecator = for_package(__package__ or "mypackage", _version=Version("1.8.0"))

# Define package-wide deprecations as module-level constants
OLD_FEATURE_DEPRECATION = deprecator.define(
    "old_feature is deprecated, use new_feature instead",
    warn_in="1.5.0",
    gone_in="2.0.0",
)

PROCESS_DATA_DEPRECATION = deprecator.define(
    "process_data() is deprecated, use transform_data() instead",
    warn_in="1.5.0",
    gone_in="2.0.0",
)
  1. Configure entry points in pyproject.toml:
[project.entry-points."deprecator.deprecator"]
mypackage = "mypackage._deprecations:deprecator"

Basic Usage Patterns

Deprecating a Function

See the basic example from earlier, or use the decorator pattern:

"""Example of using deprecation decorators.

The @deprecation.apply decorator automatically emits the warning
when the decorated function or class is called.
"""

from package_setup import PROCESS_DATA_DEPRECATION


@PROCESS_DATA_DEPRECATION.apply
def process_data(data: str) -> str:
    """Old data processing function - deprecated."""
    return transform_data(data)


def transform_data(data: str) -> str:
    """New data transformation function - use this instead."""
    return data.upper()

Deprecating a Class

"""Example of deprecating a class.

Use @deprecation.apply on classes to warn when they are instantiated.
"""

from package_setup import OLD_FEATURE_DEPRECATION


@OLD_FEATURE_DEPRECATION.apply
class LegacyProcessor:
    """This class is deprecated - use ModernProcessor instead."""

    def process(self, data: str) -> str:
        return data.lower()


class ModernProcessor:
    """The modern replacement for LegacyProcessor."""

    def process(self, data: str) -> str:
        return data.lower()

Manual Warning Emission

"""Example of manual warning emission.

Use deprecation.warn() when you need conditional deprecation warnings,
such as warning only when specific parameters or code paths are used.
"""

from package_setup import OLD_FEATURE_DEPRECATION


def complex_deprecation(use_old_logic: bool = False) -> str:
    """Function with conditional deprecation.

    Args:
        use_old_logic: If True, uses deprecated code path and emits warning.

    Returns:
        Result from either old or new implementation.
    """
    if use_old_logic:
        # Only warn when the deprecated code path is actually used
        OLD_FEATURE_DEPRECATION.warn()
        return "old result"
    return "new result"

Version Management

Deprecator automatically determines the warning type based on your package version:

Current Version warn_in gone_in Warning Type
1.0.0 2.0.0 3.0.0 PendingDeprecationWarning
2.0.0 2.0.0 3.0.0 DeprecationWarning
3.0.0 2.0.0 3.0.0 ExpiredDeprecationWarning

Complete Example

Here's a complete example of deprecating an old function:

mypackage/_deprecations.py

"""Example of package-level deprecation setup.

This is how you'd typically set up deprecator in your package's
_deprecations.py file. All deprecations are defined in one place.
"""

from packaging.version import Version

from deprecator import for_package

# Create a deprecator instance for your package
# NOTE: In real code, just use: deprecator = for_package(__package__)
# The _version parameter is only used here because this isn't a real package.
deprecator = for_package(__package__ or "mypackage", _version=Version("1.8.0"))

# Define package-wide deprecations as module-level constants
OLD_FEATURE_DEPRECATION = deprecator.define(
    "old_feature is deprecated, use new_feature instead",
    warn_in="1.5.0",
    gone_in="2.0.0",
)

PROCESS_DATA_DEPRECATION = deprecator.define(
    "process_data() is deprecated, use transform_data() instead",
    warn_in="1.5.0",
    gone_in="2.0.0",
)

mypackage/api.py

"""Example of using deprecation decorators.

The @deprecation.apply decorator automatically emits the warning
when the decorated function or class is called.
"""

from package_setup import PROCESS_DATA_DEPRECATION


@PROCESS_DATA_DEPRECATION.apply
def process_data(data: str) -> str:
    """Old data processing function - deprecated."""
    return transform_data(data)


def transform_data(data: str) -> str:
    """New data transformation function - use this instead."""
    return data.upper()

tests/test_deprecations.py

"""Complete test example for deprecations.

A more comprehensive example showing how to test:
- Decorator-based deprecations
- The deprecation message content
- That new APIs remain warning-free
"""

import warnings

from decorator_usage import process_data, transform_data


def test_process_data_deprecated() -> None:
    """Test that process_data emits deprecation warning."""
    with warnings.catch_warnings(record=True) as w:
        warnings.simplefilter("always")
        result = process_data("hello")

        # Check that we got exactly one warning
        assert len(w) == 1
        assert "deprecated" in str(w[0].message).lower()

    assert result == "HELLO"


def test_transform_data_no_warning() -> None:
    """Test that transform_data doesn't emit warnings."""
    with warnings.catch_warnings():
        warnings.simplefilter("error")
        result = transform_data("hello")  # Should not raise
    assert result == "HELLO"

Next Steps