# Changelog

## [0.9.0] — 2026-06-05 — Generators, async, & markdown audit tables

### Added

- **Generator function support for `@deprecated`.** Decorating a generator function now emits the deprecation warning eagerly at call time — before the first `next()` — consistent with regular function behavior. The generator body executes lazily as normal when iterated. All three `TargetMode` variants (`NOTIFY`, `ARGS_REMAP`, callable target) work transparently; no `isgeneratorfunction` check is required. ([#176](https://github.com/Borda/pyDeprecate/pull/176))
- **`async def` coroutine wrapper support for `@deprecated`.** Decorating an `async def` function now produces an `async def` wrapper — `inspect.iscoroutinefunction(wrapper)` returns `True`. All three `TargetMode` variants (`NOTIFY`, `ARGS_REMAP`, callable target) work with async sources and async targets. The deprecation warning fires when the coroutine is awaited, not when the wrapper is called. `pytest-asyncio` is required in the test suite to run the async integration tests. ([#180](https://github.com/Borda/pyDeprecate/pull/180))
- **Async generator function support for `@deprecated`.** Decorating an `async def` + `yield` function no longer emits a `UserWarning` at decoration time. The wrapper is a sync callable that fires the deprecation warning eagerly at call time and returns the async generator object; callers iterate with `async for`. All three `TargetMode` variants work. Because the wrapper is sync, `inspect.isasyncgenfunction(wrapper)` returns `False` — frameworks that branch on that flag may need a thin async generator passthrough. ([#181](https://github.com/Borda/pyDeprecate/pull/181))
- **Order-agnostic `@classmethod` / `@staticmethod`.** Both `@classmethod @deprecated` and `@deprecated @classmethod` (and the equivalent for `@staticmethod`) now produce `classmethod(deprecated_wrapper)` — the descriptor is unwrapped at decoration time, the inner function is deprecated, and the result is re-wrapped. `FutureWarning` fires at call time in either order; no `UserWarning` is emitted. ([#178](https://github.com/Borda/pyDeprecate/pull/178))
- **Stacked `@deprecated` — `ARGS_REMAP + NOTIFY` combination.** Lifecycle pattern: rename arguments first, deprecate the whole function later. The outer `ARGS_REMAP` remaps kwargs, then the inner `NOTIFY` warns and runs the source body. Six other stacking shapes (e.g. callable-over-callable, callable-over-`ARGS_REMAP`) now emit `UserWarning` at decoration time naming the specific shape and will become `TypeError` in v1.0. ([#172](https://github.com/Borda/pyDeprecate/pull/172))
- **Markdown deprecation tables — `generate_deprecation_table()` + `pydeprecate status` CLI subcommand.** Renders compact or matrix-style Markdown reports grouped by module and API-type (function, method, classmethod, staticmethod, property, class, instance). Two new public enums: `DeprecationStatus` (lifecycle classification — active, expired, plus dev/alpha/beta/rc removal windows) and `TableStyle` (`compact` / `matrix`). New `--style` and `--output` CLI flags. Integrated into `pydeprecate all`. Auto-detects package version from `pyproject.toml` or installed metadata. ([#133](https://github.com/Borda/pyDeprecate/pull/133))
- **`ChainType` enum now exported as public API.** Previously documented and returned by `validate_deprecation_chains()`; now listed in `deprecate.__all__`.
- **Audit discovery extended to class descriptors.** `find_deprecation_wrappers()` now inspects `classmethod` and `staticmethod` descriptors on class members so `@deprecated`-wrapped descriptors are found during scans. ([#178](https://github.com/Borda/pyDeprecate/pull/178))

### Changed

- **CLI: renamed `--skip_errors` to `--exit-zero`** across all four subcommands (`check`, `expiry`, `chains`, `all`). ([#187](https://github.com/Borda/pyDeprecate/pull/187)) **Breaking change for existing scripts** — `--skip_errors` no longer accepted; update calls to `--exit-zero`. The new name matches the established linter convention (ruff, pylint, shellcheck) and accurately describes the behaviour: exit-code override only, no exception suppression. The canonical spelling is `--exit-zero` (dash); the CLI framework also accepts `--exit_zero` (underscore) as an alias.
- **Misconfigured stacking combinations warn at decoration time.** Six previously-undefined `@deprecated` stacking shapes (e.g. callable-over-callable, callable-over-`ARGS_REMAP`) now emit `UserWarning` at decoration time naming the specific shape. Scheduled to become `TypeError` in v1.0. The new module-level `_V1_BREAK_VERSION = "v1.0"` constant centralises the "Will be TypeError in v1.0" wording across these warnings. ([#172](https://github.com/Borda/pyDeprecate/pull/172))
- **Audit chain classification — `ARGS_REMAP + NOTIFY` is now classified as `STACKED`** rather than `TARGET`. Fixes audit reports for the supported new stacking shape. ([#172](https://github.com/Borda/pyDeprecate/pull/172))
- **CLI warning suppression narrowed to `deprecate.*` warnings only.** Third-party warnings emitted during a scan are no longer silenced. ([#133](https://github.com/Borda/pyDeprecate/pull/133))
- **`generate_deprecation_table()` gains `include_members` parameter** for scanning descriptor members; `validate_deprecation_expiry()` default unchanged at `include_members=False` to preserve existing scan scope. ([#133](https://github.com/Borda/pyDeprecate/pull/133))

### Deprecated

- *(none — all deprecation-cycle migrations were emitted in v0.8.0 or earlier)*

### Fixed

- **`stacklevel` attribution on Python 3.12+.** `inspect.signature(warnings.warn)` raises `ValueError` on Python 3.12+ because the C builtin lacks an introspectable signature. This caused every `@deprecated` warning to point to `deprecation.py` instead of the caller's file. Fixed by replacing the `inspect.signature` probe with a try/except at call site: `stream(msg, stacklevel=N)` is tried first; if `TypeError` is raised (stream does not accept `stacklevel`), retried as `stream(msg)`. ([#176](https://github.com/Borda/pyDeprecate/pull/176))
- **`find_deprecation_wrappers()` no longer aborts on PEP 702 `typing_extensions.deprecated` objects.** The scanner previously checked `callable(obj) and hasattr(obj, "__deprecated__")`, which matched PEP 702 wrappers (whose `__deprecated__` is a string), causing `validate_deprecation_wrapper` to raise `ValueError` and abort the scan. Replaced with `_has_deprecation_meta(obj)`, which checks `isinstance(..., DeprecationConfig)`. ([#178](https://github.com/Borda/pyDeprecate/pull/178))
- **Short-circuit forwarding for `*args` sources now preserves extra positional arguments.** Previously, when forwarding from a `*args` source, extra positional arguments past the named parameters were silently dropped. ([#180](https://github.com/Borda/pyDeprecate/pull/180))

______________________________________________________________________

## [0.8.0] — 2026-05-21 — Default `TargetMode` enum & CLI audit tools

### Added

- **`TargetMode` enum exported from `deprecate`.** `TargetMode.NOTIFY` replaces `target=None` and `TargetMode.ARGS_REMAP` replaces `target=True`. Both are public API. ([#150](https://github.com/Borda/pyDeprecate/pull/150))
- **`args_extra` parameter for `deprecated_class()` and `deprecated_instance()`.** Injects fixed keyword arguments into forwarded calls after `args_mapping` has been applied, matching the same semantics as `@deprecated(args_extra=...)`. Ignored (with a construction-time `UserWarning`) when `target` is `TargetMode.NOTIFY`. ([#150](https://github.com/Borda/pyDeprecate/pull/150))
- **`template_mgs` parameter for `deprecated_class()` and `deprecated_instance()`.** Overrides the built-in warning message template with a `%`-style format string, matching the same semantics as `@deprecated(template_mgs=...)`. Available placeholders: `%(source_name)s`, `%(deprecated_in)s`, `%(remove_in)s`, `%(target_name)s` (callable target only), `%(target_path)s` (callable target only), `%(argument_map)s` (`args_mapping` warnings only). ([#150](https://github.com/Borda/pyDeprecate/pull/150))
- **`DeprecationConfig.misconfigured` field.** Boolean field on the shared metadata dataclass; `True` when an invalid raw target sentinel (`False`) was passed at decoration time. Audit tools surface this via `DeprecationWrapperInfo.misconfigured_target`. ([#150](https://github.com/Borda/pyDeprecate/pull/150))
- **Multi-page topic documentation site.** Replaced the monolithic README-copy home page with a curated 7-page MkDocs Material site: Home, Getting Started, User Guide (Use Cases / void() Helper / Audit Tools), Troubleshooting, and demo links. Switched theme to Material, added Open Graph tags, JSON-LD structured data (SoftwareApplication / FAQPage / TechArticle per page), spec-compliant `llms.txt`, and `git-revision-date-localized` plugin. README is unchanged (still the PyPI cover page). ([#146](https://github.com/Borda/pyDeprecate/pull/146))
- **`pydeprecate` CLI command.** Run `pydeprecate <subcommand> path/to/your/package` to scan any package or module for misconfigured `@deprecated` wrappers — reports invalid argument mappings, identity mappings, and no-effect wrappers with rich-formatted output when `rich` is available. Also available as `python -m deprecate`. ([#76](https://github.com/Borda/pyDeprecate/pull/76))
- **Four CLI subcommands: `check`, `expiry`, `chains`, `all`.** `check` validates wrapper configuration; `expiry` reports wrappers past their `remove_in` deadline (requires `pip install 'pyDeprecate[audit]'`); `chains` detects deprecated-to-deprecated forwarding chains; `all` runs all three in a single scan pass. Flags: `--norecursive`, `--skip_errors`. ([#149](https://github.com/Borda/pyDeprecate/pull/149))
- **New `DeprecationWrapperInfo.empty_deprecated_in` field.** `True` when `deprecated_in` is absent on a wrapper; intended for CI pipeline introspection. `dataclasses.asdict()` output and `repr()` now include this field. ([#166](https://github.com/Borda/pyDeprecate/pull/166))
- **`template_mgs` validated at decoration time.** Malformed `%`-style format strings now raise `ValueError` immediately at decoration time — not silently at call time. Applies to `@deprecated` and the `deprecated_class()`/`deprecated_instance()` proxy factories. ([#169](https://github.com/Borda/pyDeprecate/pull/169))
- **Stacked-callable-target guard.** Applying `@deprecated(target=fn_a)` on a callable whose target is itself a callable-target `@deprecated` wrapper now emits `UserWarning` at decoration time instead of crashing with `TypeError` at call time. ([#169](https://github.com/Borda/pyDeprecate/pull/169))

### Deprecated

- **`target=None` sentinel — use `TargetMode.NOTIFY`.** Passing `target=None` now emits a `FutureWarning` at decoration time. The sentinel remains accepted but will be removed in v1.0. Migrate to `target=TargetMode.NOTIFY`. ([#150](https://github.com/Borda/pyDeprecate/pull/150))
- **`target=True` sentinel — use `TargetMode.ARGS_REMAP`.** Passing `target=True` now emits a `FutureWarning` at decoration time. The sentinel remains accepted but will be removed in v1.0. Migrate to `target=TargetMode.ARGS_REMAP`. ([#150](https://github.com/Borda/pyDeprecate/pull/150))
- **`DeprecationWrapperInfo` attributes `empty_mapping` → `empty_args_mapping` and `identity_mapping` → `identity_args_mapping`.** Old names are kept as deprecated `@property` aliases that emit `DeprecationWarning` on access and will be removed in v1.0. ([#166](https://github.com/Borda/pyDeprecate/pull/166))

### Changed

- **Misconfigured `TargetMode` combinations now warn at construction time.** `TargetMode.ARGS_REMAP` without `args_mapping`, `TargetMode.NOTIFY` with `args_mapping`, and `TargetMode.NOTIFY` with `args_extra` all surface a `UserWarning` immediately. ([#150](https://github.com/Borda/pyDeprecate/pull/150))
- **`DeprecationConfig.target` always stores a normalised `TargetMode` or callable.** Legacy boolean sentinels (`True` / `False`) are now normalised at decoration time and are never stored verbatim in `DeprecationConfig.target`. Code that inspects `__deprecated__.target` must compare against `TargetMode.NOTIFY`, `TargetMode.ARGS_REMAP`, a callable, or `None` — never against `True` or `False`. ([#150](https://github.com/Borda/pyDeprecate/pull/150))
- **`deprecated_class()` with `target=TargetMode.NOTIFY` now emits `UserWarning` at decoration time when `args_mapping` or `args_extra` is supplied.** These parameters are ignored in `NOTIFY` mode; passing them has always been a misconfiguration. The warning will become `TypeError` in v1.0. ([#150](https://github.com/Borda/pyDeprecate/pull/150))
- **Docs site URL layout is now versioned.** Content is published under `https://borda.github.io/pyDeprecate/stable/` (for the stable alias) and `https://borda.github.io/pyDeprecate/<tag>/` (for release tags). The root URL (`https://borda.github.io/pyDeprecate/`) redirects to `stable/`. External bookmarks to flat paths like `.../pyDeprecate/troubleshooting.html` will break on first deploy — update them to `.../pyDeprecate/stable/troubleshooting.html`. ([#148](https://github.com/Borda/pyDeprecate/pull/148))
- **`target` parameter of `@deprecated` now defaults to `TargetMode.NOTIFY`.** Callers can omit `target` entirely for warn-only deprecation: `@deprecated(deprecated_in="1.0", remove_in="2.0")` is now the canonical form. Passing `target=TargetMode.NOTIFY` explicitly remains valid. This default is permanent and will not change in future releases. ([#162](https://github.com/Borda/pyDeprecate/pull/162))
- **Decoration-time `UserWarning` when `@deprecated` omits `deprecated_in`.** When `deprecated_in` is absent a `UserWarning` is emitted immediately at decoration time (not at call time) regardless of `target` shape (`TargetMode.NOTIFY`, callable, or `ARGS_REMAP`), even if `remove_in` is set. Applies to functions and methods (not classes). Suppressed when `stream=None` or when a custom `template_mgs` is provided. ([#162](https://github.com/Borda/pyDeprecate/pull/162))
- **CLI chains reporting split.** `check` subcommand reports deprecated-to-deprecated chains as warnings (exit 0); `chains` and `all` subcommands report chains as errors (exit 1). ([#149](https://github.com/Borda/pyDeprecate/pull/149))

### Fixed

- **PEP 702 compatibility crash fixed.** When `@deprecated` was stacked under a PEP 702 `@typing.deprecated` decorator, `wrapped_fn` attempted to look up `__deprecated__` on the outer wrapper and raised `AttributeError`. Fixed by capturing `dep_meta` as a closure variable at decoration time instead of re-reading it from the wrapper. ([#169](https://github.com/Borda/pyDeprecate/pull/169))
- **Double `FutureWarning` emission on `deprecated_class()` in NOTIFY mode fixed.** Using `deprecated_class()` with `target=TargetMode.NOTIFY` triggered two `FutureWarning` emissions per construction call. ([#162](https://github.com/Borda/pyDeprecate/pull/162))
- **Cross-class guard false positives resolved; `TypeError` semantics preserved.** Two previously documented "irresolvable" false-positive scenarios are now handled: (1) targets with metaclass/dynamic-class qualnames (e.g. `type("Name", bases, ns)` or manual `fn.__qualname__ = "FakeOwner.method"`) — guard now skips silently when the named class is absent from the target's module globals; (2) pre-applied decorators that rewrite the source's `__qualname__` — guard reads the true enclosing class from the Python class-body frame, which cannot be mutated by user decorators. The guard continues to raise `TypeError` at decoration time for genuine cross-class forwarding. ([#169](https://github.com/Borda/pyDeprecate/pull/169))
- **`args_mapping` rename no longer clobbers source default when both old and new parameter names are present.** Previously, calling a deprecated wrapper with the old argument name while the source also accepted the new name could silently overwrite the new-name value. The remapping now correctly renames `old=X` to `new=X` without discarding a separately supplied `new` value. ([#150](https://github.com/Borda/pyDeprecate/pull/150))
- **`target=False` sentinel now emits `UserWarning` at decoration time.** `target=False` was never a valid configuration; previously the behavior was undefined. The sentinel now surfaces a `UserWarning` immediately and will raise `TypeError` in v1.0. ([#150](https://github.com/Borda/pyDeprecate/pull/150))

______________________________________________________________________

## [0.7.0] — 2026-03-31 — Docstring Tooling

### Added

- **MkDocs admonition output.** `@deprecated` now accepts `docstring_style="mkdocs"` (alias: `"markdown"`). When `update_docstring=True`, the deprecation notice is injected as a `!!! warning "Deprecated in X"` admonition instead of a Sphinx `.. deprecated::` directive. Use `docstring_style="auto"` to detect style automatically from existing docstring content. ([#134](https://github.com/Borda/pyDeprecate/pull/134))
- **Google / NumPy section-aware docstring injection.** `update_docstring=True` now inserts the deprecation notice *before* the first section (`Args:`, `Returns:`, `Parameters`, …) rather than appending it at the end. ([#134](https://github.com/Borda/pyDeprecate/pull/134))
- **Inline arg deprecation in docstrings.** When `args_mapping` is set and `update_docstring=True`, each renamed or removed argument is annotated directly in the `Args:` / `:param` section of the docstring. ([#136](https://github.com/Borda/pyDeprecate/pull/136))
- **Griffe extension for mkdocstrings** (`deprecate.docstring.griffe_ext`, beta) and **Sphinx autodoc extension for deprecated classes** (`deprecate.docstring.sphinx_ext`, beta). ([#134](https://github.com/Borda/pyDeprecate/pull/134))
- **Live demo documentation** published to GitHub Pages — MkDocs demo, Sphinx demo, and portal landing page. ([#134](https://github.com/Borda/pyDeprecate/pull/134), [#137](https://github.com/Borda/pyDeprecate/pull/137))

### Fixed

- Fixed `getattr`/`setattr` string-literal calls (B009/B010) replaced with direct attribute access. ([#139](https://github.com/Borda/pyDeprecate/pull/139))
- Fixed proxy swap skipped correctly when `super().import_object()` returns `False` in the Griffe extension; empty `_proxy_doc` now delegates to `super().get_doc()` in the Sphinx extension. ([#139](https://github.com/Borda/pyDeprecate/pull/139))

______________________________________________________________________

## [0.6.0.post0] — 2026-03-14 — Deprecation Proxy for class/instances

### Changed

- **Softer class-deprecation fallback.** `@deprecated` applied directly to a class (Enum, dataclass, or plain class) now emits a `UserWarning` at decoration time and delegates to `deprecated_class()` internally, instead of raising `TypeError`. Code using the old pattern continues to work; the warning points to the recommended API. ([#132](https://github.com/Borda/pyDeprecate/pull/132))

## [0.6.0] — 2026-03-13

### Added

- **`deprecated_class()` and `deprecated_instance()` — full proxy support.** Enum, dataclass, and built-in types can now be wrapped in a transparent proxy. Attribute access, item access, method calls, and class behaviour all forward to the underlying type with a `FutureWarning` emitted on first access. ([#114](https://github.com/Borda/pyDeprecate/pull/114))
- **Correct `isinstance()` / `issubclass()` semantics on proxy classes.** `isinstance(x, proxy)` and `issubclass(Sub, proxy)` now work as expected — previously raised `TypeError`. Type checks do not consume the warning budget. ([#126](https://github.com/Borda/pyDeprecate/pull/126))

### Changed

- **`@deprecated` on a class raises `TypeError`.** Applying `@deprecated` directly to a class now raises `TypeError` at decoration time instead of silently misbehaving. Superseded in `v0.6.0.post0` by a `UserWarning` + delegation to `deprecated_class()`. Use `@deprecated_class()` for class-level deprecation. ([#120](https://github.com/Borda/pyDeprecate/pull/120))

### Deprecated

- **Audit API renamed for consistency.** Old names remain as `@deprecated` shims until v1.0. ([#125](https://github.com/Borda/pyDeprecate/pull/125))

  | Old name                       | New name                       |
  | ------------------------------ | ------------------------------ |
  | `find_deprecated_callables`    | `find_deprecation_wrappers`    |
  | `validate_deprecated_callable` | `validate_deprecation_wrapper` |
  | `DeprecatedCallableInfo`       | `DeprecationWrapperInfo`       |

- **`no_warning_call` renamed to `assert_no_warnings`.** The new name mirrors `assertWarns` / `assertRaises` from the standard library, making test intent immediately obvious. Old name kept as a deprecated alias until v1.0. ([#131](https://github.com/Borda/pyDeprecate/pull/131))

### Fixed

- **Cross-class method forwarding now fails at decoration time.** Passing a class as `target` on a non-`__init__` method previously silently forwarded `self` of the wrong type — always a runtime bug, never a valid pattern. The guard now raises `TypeError` at decoration time so the misconfiguration is caught immediately. ([#121](https://github.com/Borda/pyDeprecate/pull/121))
- **`find_deprecation_wrappers()` no longer reports false `invalid_args` for proxy objects.** The proxy `__call__` catch-all signature previously caused all `args_mapping` keys to be flagged as invalid; signature validation is now skipped for proxy objects. ([#124](https://github.com/Borda/pyDeprecate/pull/124))

______________________________________________________________________

## [0.5.0] — 2026-02-23 — Deprecation Lifecycle Management

### Added

- **`deprecate.audit` module — deprecation lifecycle management.** A dedicated module grouping all inspection and enforcement utilities, designed to be called from pytest or CI scripts. Requires the optional `[audit]` extra: `pip install pyDeprecate[audit]`. ([#111](https://github.com/Borda/pyDeprecate/pull/111))
- **`find_deprecated_callables()` / `validate_deprecated_callable()` — zero-impact wrapper detection.** Scans a module or package for `@deprecated` wrappers that have no real effect: invalid `args_mapping` keys, identity mappings, self-referencing targets, or missing version fields. Returns `DeprecatedCallableInfo` dataclasses. ([#72](https://github.com/Borda/pyDeprecate/pull/72))
- **`validate_deprecation_expiry()` — enforce removal deadlines in CI.** Scans a module or package and returns all wrappers whose `remove_in` version has been reached or passed. Auto-detects the installed package version. Integrate as a pytest fixture or CI step to prevent zombie code from shipping past its scheduled removal. ([#89](https://github.com/Borda/pyDeprecate/pull/89))
- **`validate_deprecation_chains()` — detect deprecated-to-deprecated forwarding.** Identifies wrappers whose `target` is itself a deprecated callable, forming chains that users traverse unnecessarily. Reports two chain kinds via the `ChainType` enum: `TARGET` (forwarding chain) and `STACKED` (composed argument mappings). ([#90](https://github.com/Borda/pyDeprecate/pull/90))

### Fixed

- **`@deprecated` wrappers now correctly handle var-positional Enum signatures.** A subtle edge case where callables with var-positional parameters in their Enum signature caused incorrect argument forwarding is now resolved. ([#104](https://github.com/Borda/pyDeprecate/pull/104))

______________________________________________________________________

## [0.4.0] — 2025-12-03 — Enhanced Documentation & Modernization

### Added

- **`update_docstring` parameter — automatic Sphinx deprecation notices.** Set `update_docstring=True` on `@deprecated` to automatically append a `.. deprecated::` reStructuredText block to the function's docstring. IDE tooltips and Sphinx-generated API docs show the notice without any manual edits. ([#31](https://github.com/Borda/pyDeprecate/pull/31))

### Changed

- **Deprecation warnings now use `FutureWarning` instead of `DeprecationWarning`.** `DeprecationWarning` is silenced by Python's default warning filters outside of test contexts, making it invisible to most end-users. `FutureWarning` is shown by default, ensuring callers actually see the migration message. ([#16](https://github.com/Borda/pyDeprecate/pull/16))
- **Minimum Python version raised to 3.9.** Python 3.8 reached end-of-life in October 2024. ([#73](https://github.com/Borda/pyDeprecate/pull/73))
- **License changed from MIT to Apache-2.0.**
- **Error messages now include the originating class or function name** for easier debugging when a mapping fails. ([#11](https://github.com/Borda/pyDeprecate/pull/11))

______________________________________________________________________

## [0.3.2] — 2021-06-11 — Support containing `kwargs` in target function

### Added

- **`target` functions using `**kwargs` are now supported.** Previously, forwarding to a target that accepted `**kwargs` and accessed them via `kwargs.get(...)` raised `TypeError` for unrecognised argument names. Extra arguments from the deprecated call are now forwarded correctly. ([#6](https://github.com/Borda/pyDeprecate/pull/6))

## [0.3.1] — 2021-05-31 — Fixed `void` typing

### Fixed

- **`void()` type annotation corrected to satisfy mypy.** The return type of `void()` is now properly annotated — IDE and type checker warnings about unused parameters in deprecated function bodies are suppressed correctly.

## [0.3.0] — 2021-04-21 — Conditional skip

### Added

- **`skip_if` parameter — conditional deprecation.** Pass a `bool` or a zero-argument callable returning `bool` to skip the warning and forwarding when a runtime condition is true. Useful for gating deprecation behaviour on package version checks or feature flags. ([#4](https://github.com/Borda/pyDeprecate/pull/4))

______________________________________________________________________

## [0.2.0] — 2021-03-29 — Improved self arg deprecations

### Added

- **`target=True` — self-deprecation mode.** Deprecate and remap arguments within the same function without forwarding to a separate callable. Use with `args_mapping` to rename a parameter while keeping the function body intact. ([#3](https://github.com/Borda/pyDeprecate/pull/3))
- **`void()` helper.** Accepts any arguments and returns `None`. Silences IDE "unused parameter" warnings in deprecated function bodies where the body is never reached.
- **`no_warning_call()` context manager.** Assert that a block of code raises no deprecation warning — useful for verifying that new API paths are clean in tests. Renamed to `assert_no_warnings()` in v0.6.0. ([#2](https://github.com/Borda/pyDeprecate/pull/2))
- **Stacked `@deprecated` decorators.** Multiple `@deprecated(True, ...)` decorators can be stacked on the same function for multi-hop argument migrations across versions, each with independent warning counts and version metadata.

______________________________________________________________________

## [0.1.1] — 2021-03-21 — Allow infinite warning

### Added

- **`num_warns=-1` — always-on warnings.** Setting `num_warns` to `-1` causes the deprecation warning to fire on every call rather than stopping after N times.
- **`target=None` — warn-only mode.** The original function body still executes; `@deprecated` adds only a warning with no call forwarding. Useful when you want to signal deprecation without changing any call behaviour.

## [0.1.0] — 2021-03-20 — Initial release

### Added

- **`@deprecated(target=callable)` decorator.** Marks a function as deprecated and automatically forwards all calls — including argument mapping — to a replacement function. The deprecated function body is never executed when `target` is a callable.
- **Automatic argument mapping.** Positional arguments are resolved to keyword arguments and forwarded to the target's signature. `args_mapping` renames (`{"old": "new"}`) or drops (`{"old": None}`) individual arguments during forwarding.
- **`args_extra` — inject additional kwargs into the target call.** Pass a `dict` of extra keyword arguments to merge into every forwarded call. Useful for providing default values or adapter arguments that the deprecated API never accepted.
- **Configurable warning count (`num_warns`).** Warnings fire once per function by default; set to any positive integer to limit the total count per function lifetime.
- **Custom warning message template (`template_mgs`).** Format string with `%(source_name)s`, `%(target_path)s`, `%(deprecated_in)s`, `%(remove_in)s`, and `%(argument_map)s` placeholders.
- **Custom warning stream (`stream`).** Route warnings to `logging.warning`, `warnings.warn`, or any callable.
