An open API service indexing awesome lists of open source software.

https://github.com/alexeev-prog/pyminideprecator

Fast and minimalistic library for setting deprecated methods
https://github.com/alexeev-prog/pyminideprecator

deprecator docs library python python3

Last synced: 11 months ago
JSON representation

Fast and minimalistic library for setting deprecated methods

Awesome Lists containing this project

README

          

# 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** with thread-local version storage
- ๐Ÿ“š **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
- ๐Ÿ”’ **Enhanced thread safety** with isolated version contexts

## 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
7. **Thread-isolated global state** - Safe version management in concurrent environments

## Installation

```bash
pip install pyminideprecator
```

## Quick Start

### Basic Function Deprecation

```python
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

```python
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

```python
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

### Enhanced 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. The Version class now includes proper hashing and equality comparisons for reliable use in collections.

### Thread-Safe Context-aware Execution

Unlike simple global state solutions, `pyminideprecator` uses a hybrid approach for version management:

```python
from pyminideprecator import set_current_version, get_current_version
import threading

# Set global version in main thread
set_current_version("1.0.0", set_global=True)

def worker():
# Set thread-specific global version
set_current_version("2.0.0", set_global=True)
print(f"Worker thread version: {get_current_version()}") # 2.0.0

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

# Main thread version remains 1.0.0
print(f"Main thread version: {get_current_version()}") # 1.0.0
```

This approach ensures:
- ๐Ÿงต **True thread isolation** - Each thread maintains its own global version state
- โšก **Context-aware execution** - ContextVar handles async and coroutine contexts
- ๐Ÿ”’ **Concurrency safety** - No race conditions between threads
- ๐Ÿ” **Predictable version scoping** - Clear separation between thread-local and context-local versions
- ๐Ÿงฉ **Compatibility with complex execution contexts** - Works with async, threads, and generators

### Lifecycle Management

Deprecations follow a clear three-phase lifecycle:

```mermaid
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:

```python
class UserProfile:

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

### Async Method Deprecation

```python
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

```python
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:

```python
@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:

```python
@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:

```python
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

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

## Best Practices

### 1. Always Provide Alternatives

```python
@deprecate(
"3.0.0",
"Old data format",
instead="json.loads()" # ๐Ÿ‘ˆ Clear migration path
)
def parse_legacy(data):
pass
```

### 2. Use Gradual Enforcement

```python
@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

```python
@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

```python
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 Consistent Versioning Schemes

```python
# Good - Semantic versioning
set_current_version("1.2.3", set_global=True)

# Good - Date-based versioning
set_current_version("2025.12.31", set_global=True)

# 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], set_global: bool = False)` | Sets the current version for context or thread-local global scope |
| `get_current_version() -> Union[Version, None]` | Retrieves current version (context takes precedence over thread-local global) |
| `scoped_version(version: str) -> ContextManager` | Temporarily sets version in a context |

### Enhanced Version Class

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

# Full comparison support
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: ...
def __hash__(self) -> int: ... # Added for hashability
```

### Exception: `DeprecatedError`

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

## Real-World Examples

### Library with Async Deprecations

```python
# data_processing.py
from pyminideprecator import deprecate, set_current_version

set_current_version("1.8.0", set_global=True)

@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
```

### Multi-threaded Application

```python
# app.py
from pyminideprecator import set_current_version
import threading

def worker(worker_id: int):
# Each thread sets its own global version
set_current_version(f"1.0.{worker_id}", set_global=True)
print(f"Worker {worker_id} version: {get_current_version()}")

# Main thread version
set_current_version("main_app", set_global=True)

threads = []
for i in range(3):
t = threading.Thread(target=worker, args=(i,))
threads.append(t)
t.start()

for t in threads:
t.join()

# Main thread version unchanged
print(f"Main version: {get_current_version()}")
```

### Large-Scale Async Codebase Refactoring

```python
# 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", set_global=True)

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

# After error version
set_current_version("2.3.0", set_global=True)
await legacy.authenticate(user) # Raises DeprecatedError
```

## Performance Characteristics

`pyminideprecator` is designed for minimal performance impact in production environments:
- โšก Near-zero overhead when not in deprecation period (โ‰ˆ50ns)
- ๐Ÿ“‰ Single efficient version check during calls (โ‰ˆ200ns)
- ๐Ÿง  Optimized context management with thread-local storage
- ๐Ÿ“‰ Minimal memory footprint (8 bytes per context)
- โšก Async overhead < 1ฮผs per call
- ๐Ÿ”’ Thread-local access optimized for concurrent environments

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
- Linear scalability under high thread concurrency

## Migration Guide

### From Other Deprecation Libraries

1. Replace existing deprecation decorators with `@deprecate`
2. Convert version parameters to consistent string formats
3. Initialize version context with `set_current_version()`
4. Use `scoped_version()` for temporary version overrides
5. Remove manual async handling - now fully automatic

### From v0.1 to v0.2

1. Context-aware versioning replaces global state
2. Thread-local storage for global version state
3. Enhanced Version class with hashing and equality
4. Native async support added
5. Simplified API with strict type enforcement
6. Improved thread safety in concurrent environments

## Contributing

We welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines. Key areas for contribution include:
- Additional test cases for thread-local scenarios
- Performance optimization proposals
- Extended version format support
- IDE integration plugins

## License & Support

This project is licensed under **MIT License** - see [LICENSE](https://github.com/alexeev-prog/pyminideprecator/blob/main/LICENSE). For commercial support and enterprise features, contact [alexeev.dev@mail.ru](mailto:alexeev.dev@mail.ru).

[Explore Documentation](https://alexeev-prog.github.io/pyminideprecator) |
[Report Issue](https://github.com/alexeev-prog/pyminideprecator/issues) |
[View Examples](./examples)

(back to top)

---
Professional deprecation management for modern Python projects

Copyright ยฉ 2025 Alexeev Bronislav. Distributed under MIT license.