Skip to main content

Add your description here

Project description

pyminideprecator

Fast and minimalistic library for marking methods and classes as deprecated
Explore the docs »

Getting Started · Basic Usage · Documentation · License


Coverage PyPI - Downloads PyPI - Version PyPI - Python Version GitHub contributors

Professional deprecation management for modern Python projects

pyminideprecator is a lightweight yet powerful decorator-based solution for managing code deprecation in Python libraries and applications. It provides a robust mechanism to mark deprecated code with automatic warnings that escalate to errors at specified version thresholds, supporting both semantic versioning and date-based versioning. The library is designed with thread safety and asynchronous execution in mind, making it suitable for all types of Python projects.

Key Advantages:

  • 🚀 Zero-dependency implementation
  • 🧵 Thread-safe and async-compatible
  • 📚 Automatic docstring integration
  • ⚙️ Flexible version comparison (semantic and date-based)
  • 🛡️ Gradual deprecation with warning-to-error transition
  • Full async support for coroutines and asynchronous workflows
  • 🧩 100% tests coverage with mutants testing for stable work

Why Use pyminideprecator?

Deprecating code is a critical part of library and API maintenance, but doing it manually is error-prone and time-consuming. pyminideprecator solves this by providing:

  1. Standardized deprecation workflow - Consistent messaging across your project
  2. Automatic lifecycle management - Warnings automatically become errors at specified versions
  3. Context-aware versioning - Safe for complex applications with multiple execution contexts
  4. Documentation integration - Deprecation notices appear in auto-generated docs
  5. Future-proof versioning - Supports both semantic and calendar versioning schemes
  6. First-class async support - Seamlessly handles asynchronous functions and methods

Installation

pip install pyminideprecator

Quick Start

Basic Function Deprecation

from pyminideprecator import deprecate, set_current_version

# Set current application version
set_current_version("1.2.0")

@deprecate(
    remove_version="2.0.0",
    message="Legacy API function",
    instead="new_api()",
    since="1.0.0"
)
def old_api() -> str:
    """Original documentation"""
    return "legacy data"

# Generates DeprecationWarning when called
result = old_api()
print(f"Result: {result}")  # Still works in 1.2.0

Async Function Deprecation

from pyminideprecator import deprecate, set_current_version

set_current_version("1.5.0")

@deprecate("2.0.0", "Async processor will be removed")
async def async_data_processor(input: str) -> str:
    """Processes data asynchronously"""
    await asyncio.sleep(0.1)
    return processed_data

# Called in async context:
async def main():
    result = await async_data_processor("sample")  # Warning

Class Deprecation

from pyminideprecator import deprecate

@deprecate(
    remove_version="2024.01.01",
    message="Old database client",
    instead="NewDBClient"
)
class OldDBClient:
    def __init__(self, url: str):
        self.url = url

    def query(self, sql: str) -> list:
        return ["result1", "result2"]

# Shows warning on instantiation
client = OldDBClient("db://localhost")  # DeprecationWarning
results = client.query("SELECT * FROM table")  # Additional warning

Core Concepts

Version Management System

pyminideprecator uses a dual-versioning system that supports:

  1. Semantic Versioning (SemVer)

    • Format: MAJOR.MINOR.PATCH (e.g., 1.2.3)
    • Numeric ordering (1.2.3 < 1.2.4 < 2.0.0)
    • Ideal for libraries and APIs
  2. Date-based Versioning

    • Format: YYYY.MM.DD (e.g., 2025.12.31)
    • Chronological ordering
    • Perfect for applications with regular release cycles

The version comparison is handled automatically based on the format of the version string provided.

Context-aware Execution

Unlike simple global state solutions, pyminideprecator uses context-aware storage for version information:

from pyminideprecator import set_current_version, get_current_version

# Set global version for main thread
set_current_version("1.0.0")

def worker():
    # New thread starts with no version set
    set_current_version("2.0.0")
    print(get_current_version())  # Version("2.0.0")

# Create and run worker thread
import threading
thread = threading.Thread(target=worker)
thread.start()
thread.join()

# Main thread version remains unchanged
print(get_current_version())  # Version("1.0.0")

This approach ensures:

  • 🧵 Thread safety in multi-threaded applications
  • ⚡ Proper isolation in async environments
  • 🔍 Predictable version scoping
  • 🧩 Compatibility with complex execution contexts

Lifecycle Management

Deprecations follow a clear three-phase lifecycle:

graph LR
    A[Current Version < Error Version] --> B[Warning Phase]
    B --> C[Usable with warnings]
    D[Current Version >= Error Version] --> E[Error Phase]
    E --> F[Usage raises DeprecatedError]
    G[Current Version >= Removal Version] --> H[Removed]

You control the transitions between these phases using:

  • error_version: When warnings become errors
  • remove_version: When functionality is completely removed

Advanced Usage Patterns

Property Deprecation

For properties, static methods and classmethods, ensure the deprecation decorator is placed after the property decorator:

class UserProfile:

    @property
    @deprecate("3.0.0", "Use full_name instead")
    def name(self) -> str:
        return self._name

Async Method Deprecation

class DataProcessor:

    @deprecate("2.5.0", "Use process_v2() instead")
    async def process_async(self, data: bytes) -> bytes:
        """Processes data asynchronously"""
        await asyncio.sleep(0.1)
        return processed_data

Static Method Deprecation

class MathUtils:

    @staticmethod
    @deprecate("3.1.0", "Use math.sqrt() instead")
    def square_root(x):
        return x**0.5

Custom Warning Types

Change the warning category for more granular control:

@deprecate(
    "4.0.0",
    "This will be removed soon",
    category=FutureWarning
)
def experimental_feature():
    pass

Early Error Enforcement

Make deprecations become errors before the removal version:

@deprecate(
    remove_version="2.0.0",
    message="Migrate to new_system()",
    error_version="1.5.0"  # Errors start in 1.5.0
)
def legacy_system():
    pass

Context-Specific Version Overrides

Temporarily change the version in a specific context:

from pyminideprecator import scoped_version

set_current_version("1.0.0")

with scoped_version("2.0.0"):
    # Calls will raise DeprecatedError
    legacy_function()

Generator Function Deprecation

@deprecate("3.0.0", "Use streaming_api() instead")
def legacy_generator():
    yield from range(10)

Best Practices

1. Always Provide Alternatives

@deprecate(
    "3.0.0",
    "Old data format",
    instead="json.loads()"  # 👈 Clear migration path
)
def parse_legacy(data):
    pass

2. Use Gradual Enforcement

@deprecate(
    remove_version="4.0.0",
    message="Phase out old protocol",
    error_version="3.5.0"  # 👈 Gives users time to migrate
)
def old_protocol():
    pass

3. Maintain Documentation Context

@deprecate("2.0.0", "Replaced by quantum_algorithm()")
def classical_algorithm():
    """Original docs explaining the algorithm

    Details about implementation...
    """

The resulting docstring will be:

**DEPRECATED** Replaced by quantum_algorithm() Will be removed in 2.0.0.

Original docs explaining the algorithm

Details about implementation...

4. Test Deprecation Lifecycle

def test_deprecation_phases():
    # Test warning phase
    with scoped_version("1.0.0"):
        with pytest.warns(DeprecationWarning):
            deprecated_function()

    # Test error phase
    with scoped_version("2.0.0"):
        with pytest.raises(DeprecatedError):
            deprecated_function()

5. Use Semantic Versioning Consistently

# Good - Semantic versioning
set_current_version("1.2.3")

# Good - Date-based versioning
set_current_version("2025.12.31")

# Bad - Mixed version types
set_current_version("1.2025.01")  # Not supported!

API Reference

Decorator: @deprecate

The core decorator for marking deprecated functionality.

Parameters:

Parameter Type Default Description
remove_version str Required Version when functionality will be completely removed
message str Required Human-readable deprecation description
since str or None None Version where deprecation was first introduced
instead str or None None Recommended replacement functionality
category Type[Warning] DeprecationWarning Warning category to emit
stacklevel int 2 Stack level for warning source
error_version str or None remove_version Version when functionality starts raising errors

Version Management Functions

Function Description
set_current_version(version: Union[str, Version, None]) Sets the current version for the context
get_current_version() -> Union[Version, None] Gets the current version for the context
scoped_version(version: str) -> ContextManager Temporarily sets version in a context

Version Class

class Version:
    def __init__(self, version_str: str):
        """Parses version string into semantic or date-based representation"""

    # Comparison operators
    def __lt__(self, other) -> bool: ...
    def __le__(self, other) -> bool: ...
    def __eq__(self, other) -> bool: ...
    def __ge__(self, other) -> bool: ...
    def __gt__(self, other) -> bool: ...
    def __ne__(self, other) -> bool: ...

Exception: DeprecatedError

class DeprecatedError(Exception):
    """Raised when deprecated functionality is accessed beyond its error version"""

Real-World Examples

Library with Async Deprecations

# data_processing.py
from pyminideprecator import deprecate, set_current_version

set_current_version("1.8.0")

@deprecate("2.0.0", "Use process_stream() instead")
async def legacy_processor(stream: AsyncIterable) -> list:
    """Processes data in batches"""
    results = []
    async for batch in stream:
        processed = await _process_batch(batch)
        results.extend(processed)
    return results

# Modern replacement
async def process_stream(stream: AsyncIterable) -> list:
    # New processing logic

Application with Scheduled Deprecations

# app.py
from pyminideprecator import set_current_version, scoped_version
from datetime import datetime

# Set version based on release date
release_date = datetime.now().strftime("%Y.%m.%d")
set_current_version(release_date)

# Deprecation scheduled for new year
@deprecate("2025.01.01", "New year cleanup")
def holiday_feature():
    pass

# Test specific version in tests
def test_new_year_cleanup():
    with scoped_version("2025.01.01"):
        with pytest.raises(DeprecatedError):
            holiday_feature()

Large-Scale Async Codebase Refactoring

# legacy.py
class LegacyAsyncSystem:

    @deprecate(
        "3.0.0",
        "Old authentication",
        instead="AuthService",
        error_version="2.3.0"
    )
    async def authenticate(self, user) -> bool:
        await asyncio.sleep(0.1)
        return True

# new.py
class AuthService:
    async def authenticate(self, user) -> bool:
        # Modern authentication logic
        return auth_result

# migration.py
from pyminideprecator import set_current_version
set_current_version("2.0.0")

# During migration period
legacy = LegacyAsyncSystem()
result = await legacy.authenticate(user)  # Warning

# After error version
set_current_version("2.3.0")
await legacy.authenticate(user)  # Raises DeprecatedError

Performance Characteristics

pyminideprecator is designed for minimal performance impact:

  • ⚡ Near-zero overhead when not in deprecation period (≈50ns)
  • 📉 Single additional version check during calls (≈200ns)
  • 🧠 Efficient context management
  • 📉 Minimal memory footprint (8 bytes per context)
  • ⚡ Async overhead < 1μs per call

Benchmarks show:

  • 0.05μs overhead for non-deprecated calls
  • 0.2μs overhead for warning-mode calls
  • 0.3μs overhead for async functions
  • No measurable memory leaks

Migration Guide

From Other Deprecation Libraries

  1. Replace deprecation logic decorator with decorator @deprecate from pyminideprecator
  2. Convert version parameters to strings
  3. Add set_current_version() initialization
  4. Use scoped_version() instead of mock.patch for version overrides
  5. Remove manual async handling - now automatic

From v0.1 to v0.2

  1. Context-aware versioning replaces global state
  2. Thread safety improvements
  3. Native async support added
  4. Simplified API
  5. Strict type hint enforcement

Contributing

We welcome contributions! Please see CONTRIBUTING.md for guidelines.

License & Support

This project is licensed under MIT License - see LICENSE. For commercial support, contact alexeev.dev@mail.ru.

Explore Documentation | Report Issue | View Examples

(back to top)


Professional deprecation management for modern Python projects

Copyright © 2025 Alexeev Bronislav. Distributed under MIT license.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pyminideprecator-0.2.1.tar.gz (82.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pyminideprecator-0.2.1-py3-none-any.whl (12.4 kB view details)

Uploaded Python 3

File details

Details for the file pyminideprecator-0.2.1.tar.gz.

File metadata

  • Download URL: pyminideprecator-0.2.1.tar.gz
  • Upload date:
  • Size: 82.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.1.3 CPython/3.13.4 Linux/6.12.34

File hashes

Hashes for pyminideprecator-0.2.1.tar.gz
Algorithm Hash digest
SHA256 601c80d428d56f69d5d6954ef3afd17b93752f39eb3142a45b99c965689af87c
MD5 b94a821efd231039d39680eeb01f2cb8
BLAKE2b-256 d1ff38219031b7c5cf210b572c7b62bb411fdfc8f50cfdc16cda1e2e388ef078

See more details on using hashes here.

File details

Details for the file pyminideprecator-0.2.1-py3-none-any.whl.

File metadata

  • Download URL: pyminideprecator-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 12.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.1.3 CPython/3.13.4 Linux/6.12.34

File hashes

Hashes for pyminideprecator-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 79fc66c15732484e2ca50a8456bcef0b0e3b8f2e2938d9fde65fcb7a50a9a698
MD5 ca1783c37996f7ace8ebcea9bfdfe6aa
BLAKE2b-256 0ec4a6e792db522076fb4f32d481c579fc14d34d2e04d89735f075988b678da2

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page