Search for a command to run...
🎉 pyDeprecate 0.6.0 We're excited to announce pyDeprecate 0.6.0 — the full proxy release! This version completes the deprecation-proxy story with proper support for Enum types, dataclasses, and built-in types, correct isinstance()/issubclass() semantics on proxied classes, and a cleaner audit API. ✨ What's New 🦜 Deprecation Proxy for Enum, Dataclass, and Built-in Types deprecated_class() and deprecated_instance() now support the full Python type hierarchy — Enum types, dataclasses, and built-in types — via the _DeprecatedProxy transparent wrapper. Attribute access, method calls, and class behaviour all forward to the underlying type. from deprecate import deprecated_class from mypackage.colors import NewColor # What mypackage/colors.py as the new API looks like: class NewColor(Enum): RED = 1 BLUE = 2 # Before: the class was renamed to NewColor, and MyColor was a separate deprecated copy of the old class body. # class MyColor(Enum): # RED = 1 # BLUE = 2 # After: the class was renamed to NewColor. # MyColor is now a transparent deprecated alias — no class body to duplicate. MyColor = deprecated_class( NewColor, deprecated_in="0.6", remove_in="1.0", ) MyColor.RED # FutureWarning: MyColor is deprecated... MyColor.RED.value # 1 🔍 Correct isinstance() / issubclass() Semantics on Proxies When you use a deprecated_class() proxy as the second argument to isinstance() or issubclass(), the check now works exactly as expected. Previously this raised TypeError because the proxy is not a real type. # MyColor = DEPRECATED API proxy; NewColor = NEW/FUTURE API (real class) assert isinstance(MyColor.RED, MyColor) # True — no TypeError assert issubclass(NewColor, MyColor) # True Type checks delegate to the underlying class without consuming the warning budget or producing noise in loops. 🔄 Audit API Renamed for Consistency The three public audit names have been updated to reflect that they cover the full wrapper surface — functions, methods, class aliases, and instance proxies — not just callables: | Old name (deprecated until v1.0) | New name | |---|---| | find_deprecated_callables() | find_deprecation_wrappers() | | validate_deprecated_callable() | validate_deprecation_wrapper() | | DeprecatedCallableInfo | DeprecationWrapperInfo | The old names remain as @deprecated shims that emit FutureWarning at runtime. Update your imports before v1.0 removes them. Before (v0.5.0): from deprecate import ( find_deprecated_callables, validate_deprecated_callable, DeprecatedCallableInfo, ) wrappers = find_deprecated_callables(my_module) validate_deprecated_callable(my_module, "0.6.0") info: DeprecatedCallableInfo = wrappers[0] After (v0.6.0): from deprecate import ( find_deprecation_wrappers, validate_deprecation_wrapper, DeprecationWrapperInfo, ) wrappers = find_deprecation_wrappers(my_module) validate_deprecation_wrapper(my_module, "0.6.0") info: DeprecationWrapperInfo = wrappers[0] Why: The old names used "callable" which excludes class and instance proxies added in this release. The old names emit FutureWarning and will be removed in v1.0. 🗑️ Deprecated no_warning_call renamed to assert_no_warnings The test utility no_warning_call has been renamed to assert_no_warnings to align with Python's testing conventions (assertWarns, assertRaises). The old name is kept as a deprecated alias and will be removed in v1.0. Before: from deprecate import no_warning_call with no_warning_call(): call_stable_api() After: from deprecate import assert_no_warnings with assert_no_warnings(): call_stable_api() Why: assert_no_warnings mirrors the assertWarns/assertRaises naming convention from the standard library, making test intent immediately obvious. ⚠️ Breaking Changes @deprecated on Class Types Now Raises TypeError Applying @deprecated directly to an Enum, dataclass, or plain class now raises TypeError at decoration time. Previously this was supported via an undocumented _DeprecatedEnumWrapper workaround that silently broke isinstance() checks. The workaround has been removed in favour of deprecated_class(), which is the correct API for class-level deprecation. Before (v0.5.0 — broken isinstance): from enum import Enum from deprecate import deprecated @deprecated(NewColor, deprecated_in="0.5", remove_in="1.0") class OldColor(Enum): RED = 1 GREEN = 2 After (v0.6.0): from enum import Enum from deprecate import deprecated_class class Color(Enum): RED = 1 GREEN = 2 OldColor = deprecated_class( Color, deprecated_in="0.6", remove_in="1.0", ) Why: @deprecated cannot preserve the class identity needed for isinstance/issubclass checks. deprecated_class() wraps the class in a _DeprecatedProxy that delegates all type checks to the underlying class, giving callers a transparent drop-in replacement. 🔧 Under the Hood Audit tools now correctly skip signature validation for proxy objects — find_deprecation_wrappers() no longer reports false invalid_args for proxies whose __call__ has a catch-all signature (#124) @deprecated on a non-__init__ class method now raises TypeError at decoration time when a class is passed as target, preventing silent wrong-type self forwarding; constructor forwarding (target=NewClass on __init__) remains valid (#121) Significantly expanded test coverage for proxy, audit, and deprecation modules (#119) 📦 Installation pip install --upgrade pyDeprecate # For audit features: pip install --upgrade "pyDeprecate[audit]" 🙏 Thank You A big thank you to everyone who helped with this release: Jirka Borovec (@Borda) — proxy features, audit API rename, breaking-change refactor, bug fixes, and docs Full Changelog: v0.5.0...v0.6.0