You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs: document attrs_mapping for selective attribute deprecation
- Add "Selective attribute deprecation" section to docs/guide/use-cases.md with example, output block, and audit-visibility note
- Add attrs_mapping <details> example block to README.md Enums/dataclasses section; add attrs_mapping to deprecated_class param tip
- Add recipe, Decision Table row, flowchart branch, numbered decision step, and Agent Notes entry to docs/llms.txt
---
Co-authored-by: Claude Code <noreply@anthropic.com>
> `deprecated_instance()` shares `deprecated_in`, `remove_in`, `num_warns`, `stream`, `args_extra`, and `template_mgs`; it requires `obj` and adds `name` (display name) and `read_only`.
258
258
259
259
</details>
@@ -917,6 +917,36 @@ True
917
917
918
918
<br>
919
919
920
+
<details>
921
+
<summary>Example: selective attribute deprecation with <code>attrs_mapping</code></summary>
922
+
923
+
Use `attrs_mapping` to deprecate only specific attribute names — other attributes pass through silently. Covers renames, misspelling corrections, and warn-only notices on individual attributes.
924
+
925
+
```python
926
+
from deprecate import deprecated_class
927
+
928
+
929
+
classConfig:
930
+
colour: str="red"
931
+
timeout: int=30
932
+
933
+
934
+
# "color" is a deprecated alias for "colour"; access warns and redirects
Copy file name to clipboardExpand all lines: docs/guide/use-cases.md
+50Lines changed: 50 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -695,6 +695,56 @@ True
695
695
696
696
</details>
697
697
698
+
## Selective attribute deprecation
699
+
700
+
Use `attrs_mapping` on `deprecated_class()` to deprecate only specific attribute names — all other attributes pass through silently. This covers attribute renames, misspelling corrections (e.g. `color` → `colour`), and warn-only notices on individual attributes.
701
+
702
+
The mapping keys are the deprecated attribute names; values are either the canonical replacement name (string) or `None` for a warn-only notice with no rename. Reads, writes, and deletes on deprecated attribute names all warn and redirect. Reads, writes, and deletes on non-listed attribute names pass through without any warning.
703
+
704
+
```python
705
+
from deprecate import deprecated_class
706
+
707
+
708
+
classConfig:
709
+
# Canonical names — callers should use these
710
+
colour: str="red"
711
+
timeout: int=30
712
+
713
+
714
+
# Misspelling migration: "color" → "colour"; "size" is warn-only (no rename)
715
+
DeprecatedConfig = deprecated_class(
716
+
attrs_mapping={"color": "colour", "size": None},
717
+
deprecated_in="1.0",
718
+
remove_in="2.0",
719
+
)(Config)
720
+
721
+
# Deprecated alias — warns and returns Config.colour
722
+
print(DeprecatedConfig.color)
723
+
724
+
# Canonical name — silent passthrough, no warning
725
+
print(DeprecatedConfig.colour)
726
+
727
+
# Warn-only: no rename, "size" is still returned as-is (value shown as 0 since Config has no "size")
`attrs_mapping` can be combined with `target=NewClass` — the class-level proxy warning fires on instantiation, while `attrs_mapping` intercepts individual attribute reads/writes/deletes.
743
+
744
+
!!! note "Audit visibility"
745
+
746
+
`find_deprecation_wrappers` discovers the proxy via its class-level `__deprecated__`. Individual `attrs_mapping` entries are data inside the single proxy config and are not emitted as separate `DeprecationWrapperInfo` records. All entries share the same `deprecated_in`/`remove_in` lifecycle.
747
+
698
748
## Automatic docstring updates
699
749
700
750
Set `update_docstring=True` to inject a deprecation notice directly into the function's docstring at import time. The rendered API reference (Sphinx or MkDocs) always shows the deprecation status alongside the signature, with no manual upkeep.
Copy file name to clipboardExpand all lines: docs/llms.txt
+27Lines changed: 27 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -96,6 +96,27 @@ class OldClient:
96
96
pass
97
97
```
98
98
99
+
### Deprecate selected attributes on a class
100
+
101
+
```python
102
+
from deprecate import deprecated_class
103
+
104
+
105
+
class Config:
106
+
colour: str = "red"
107
+
timeout: int = 30
108
+
109
+
110
+
# Only "color" warns and redirects to "colour"; all other attrs pass through silently.
111
+
# Use None as value for warn-only (no rename): attrs_mapping={"size": None}
112
+
DeprecatedConfig = deprecated_class(
113
+
attrs_mapping={"color": "colour"},
114
+
deprecated_in="1.2",
115
+
remove_in="2.0",
116
+
# FutureWarning on DeprecatedConfig.color: "The `color` was deprecated since v1.2 in favor of `Config.colour`. It will be removed in v2.0."
117
+
)(Config)
118
+
```
119
+
99
120
### Deprecate a constant or object
100
121
101
122
```python
@@ -148,6 +169,9 @@ What is being deprecated?
148
169
├─ A class, Enum, or dataclass?
149
170
│ → use @deprecated_class(target=NewCls, ...) — NOT @deprecated
150
171
│ (classes are callables, but @deprecated is for functions/methods only)
172
+
│ └─ Only some attributes deprecated (rename or misspelling fix)?
173
+
│ → also add attrs_mapping={"old_attr": "new_attr"} (or None for warn-only)
174
+
│ only listed attrs warn; all others pass through silently
151
175
│
152
176
├─ A `@property`?
153
177
│ → use `@deprecated @property` (outer order); all three accessors (`fget`/`fset`/`fdel`)
@@ -211,6 +235,7 @@ pydeprecate status src/ # standalone deprecation table only (no checks)
211
235
| Drop deprecated argument | `args_mapping={"old": None}` |
212
236
| No replacement | `@deprecated(target=TargetMode.NOTIFY)` or omit `target` |
213
237
| Class rename | `@deprecated_class(target=NewClass)` |
238
+
| Deprecate selected class attributes | `deprecated_class(attrs_mapping={"old": "new"}, ...)` |
214
239
| Constant/object alias | `deprecated_instance(obj, ...)` |
215
240
| CI expiry check | `validate_deprecation_expiry(...)` or `pydeprecate all src/` |
216
241
| Docs table | `generate_deprecation_table(pkg, style="compact")` or `style="matrix"` |
@@ -221,12 +246,14 @@ pydeprecate status src/ # standalone deprecation table only (no checks)
221
246
1. Are you renaming a callable? Use `@deprecated(target=new_callable)`.
222
247
2. Are you renaming or dropping arguments? Use `TargetMode.ARGS_REMAP`.
223
248
3. Are you renaming a class? Use `deprecated_class(target=NewClass)`.
249
+
3a. Deprecating only some attributes (rename or misspelling)? Use `deprecated_class(attrs_mapping={"old": "new"}, ...)`.
224
250
4. Are you keeping an object alias? Use `deprecated_instance(obj, ...)`.
225
251
5. Do you only need a warning? Use `TargetMode.NOTIFY` or omit `target`.
226
252
6. Do you need removal governance? Add audit checks in CI.
227
253
228
254
## Agent Notes
229
255
256
+
- `deprecated_class(attrs_mapping={...})` deprecates only the listed attribute names on a class proxy. Keys are deprecated names; values are canonical replacement names (string) or `None` for warn-only. Reads, writes, and deletes on deprecated names warn and redirect; all other attribute access is silent. Circular mappings (a key that is also a redirect target) raise `ValueError` at decoration time. All entries share the same `deprecated_in`/`remove_in`; per-attribute lifecycle is not supported. `find_deprecation_wrappers` discovers the proxy but does not emit separate records for individual `attrs_mapping` entries.
230
257
- `@deprecated` works with `@classmethod`, `@staticmethod`, `@property`, and `@cached_property` in either decorator order — `@classmethod @deprecated` and `@deprecated @classmethod` both produce `classmethod(deprecated_wrapper)`; `@staticmethod @deprecated` and `@deprecated @staticmethod` both produce `staticmethod(deprecated_wrapper)`. Both fire `FutureWarning` at call time.
231
258
- `@property` with `@deprecated`: when `@deprecated` is on the **outside** (`@deprecated @property` order or explicit `deprecated(...)(property(...))`), all three accessors are wrapped — `fget`, `fset`, and `fdel` each fire `FutureWarning` when accessed, assigned, or deleted. Warning fires on attribute access (`obj.prop` triggers it, not `obj.prop()`). When `@deprecated` is on the **inside** (`@property @deprecated`), only `fget` is wrapped — warning fires on read but not on write or delete. For setter/deleter wrapping use the outer order: `@deprecated @property` then chain `@value.setter` / `@value.deleter` (re-wrapped via `_DeprecatedProperty`), or explicit `property(fget, fset[, fdel])` construction. Note: `type(x) == property` returns `False` on a decorated property; `isinstance(x, property)` is preserved. Audit: `find_deprecation_wrappers` scans the first accessor that carries `__deprecated__` metadata (`fget` → `fset` → `fdel`); setter-only properties (`fget=None`) are discovered via `fset`. `target=<callable>` and `target=TargetMode.ARGS_REMAP` / `True` both raise `TypeError` when decorating a property — property deprecation is warn-only; to redirect, delegate manually in the accessor body (e.g. `return self.new_prop`). Dataclass field alias: add a deprecated `@property` with the old name delegating to the new field; do NOT reuse an existing dataclass field name (the `@dataclass`-generated `__init__` assigns `self.field = value` which conflicts with a property descriptor of the same name).
232
259
- `@cached_property` with `@deprecated` in either order: warning fires on **first access only** — subsequent accesses hit the cached value and do not emit a warning.
0 commit comments