pyminideprecator
Fast and minimalistic library for marking methods and classes as deprecated
Explore the docs »
Getting Started
·
Basic Usage
·
Documentation
·
License
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:
- Standardized deprecation workflow - Consistent messaging across your project
- Automatic lifecycle management - Warnings automatically become errors at specified versions
- Context-aware versioning - Safe for complex applications with multiple execution contexts
- Documentation integration - Deprecation notices appear in auto-generated docs
- Future-proof versioning - Supports both semantic and calendar versioning schemes
- 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:
-
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
- Format:
-
Date-based Versioning
- Format:
YYYY.MM.DD(e.g.,2025.12.31) - Chronological ordering
- Perfect for applications with regular release cycles
- Format:
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 errorsremove_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
- Replace deprecation logic decorator with decorator
@deprecatefrom pyminideprecator - Convert version parameters to strings
- Add
set_current_version()initialization - Use
scoped_version()instead of mock.patch for version overrides - Remove manual async handling - now automatic
From v0.1 to v0.2
- Context-aware versioning replaces global state
- Thread safety improvements
- Native async support added
- Simplified API
- 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
Professional deprecation management for modern Python projects
Copyright © 2025 Alexeev Bronislav. Distributed under MIT license.
Metadata
Release files for pyminideprecator 0.2.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| pyminideprecator-0.2.1.tar.gz | 82.9 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pyminideprecator-0.2.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 95.3 kB
Release files / pyminideprecator-0.2.1.tar.gz
| Download URL | pyminideprecator-0.2.1.tar.gz |
|---|---|
| Size | 82.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
601c80d428d56f69d5d6954ef3afd17b93752f39eb3142a45b99c965689af87c
|
|
BLAKE2b-256 checksum How to use checksums |
d1ff38219031b7c5cf210b572c7b62bb411fdfc8f50cfdc16cda1e2e388ef078
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/2.1.3 CPython/3.13.4 Linux/6.12.34
|
Release files / pyminideprecator-0.2.1-py3-none-any.whl
| Download URL | pyminideprecator-0.2.1-py3-none-any.whl |
|---|---|
| Size | 12.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
79fc66c15732484e2ca50a8456bcef0b0e3b8f2e2938d9fde65fcb7a50a9a698
|
|
BLAKE2b-256 checksum How to use checksums |
0ec4a6e792db522076fb4f32d481c579fc14d34d2e04d89735f075988b678da2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/2.1.3 CPython/3.13.4 Linux/6.12.34
|
Security for pyminideprecator 0.2.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.