Python’s warnings module controls how runtime alerts are issued and filtered. import warnings. warn: warnings.warn(message, category=UserWarning, stacklevel=1) — emit a warning; stacklevel adjusts which frame appears in the message (use 2 from a helper to point at the caller). Categories: UserWarning, DeprecationWarning, PendingDeprecationWarning, FutureWarning, RuntimeWarning, SyntaxWarning, ResourceWarning, ImportWarning, UnicodeWarning, BytesWarning. filterwarnings: warnings.filterwarnings(action, message="", category=Warning, module="", lineno=0, append=False) — action: "default", "ignore", "always", "error", "once", "module". simplefilter: warnings.simplefilter("ignore") — no message/module/lineno match. resetwarnings: warnings.resetwarnings() — clear all custom filters. catch_warnings: with warnings.catch_warnings(): warnings.simplefilter("error") — context manager; records=True to capture list. warn_explicit: warnings.warn_explicit(msg, cat, filename, lineno, source=None). -W flag: python -W ignore::DeprecationWarning script.py. stacklevel: always pass stacklevel=2 from a decorator/helper so the warning points at the caller, not inside your library. logging.captureWarnings: logging.captureWarnings(True) — redirect all warnings to py.warnings logger. Claude Code generates deprecated() decorators, experimental() markers, resource leak detectors, and API migration helpers.
CLAUDE.md for warnings
## warnings Stack
- Stdlib: import warnings
- Emit: warnings.warn("message", DeprecationWarning, stacklevel=2)
- Filter: warnings.filterwarnings("ignore", category=DeprecationWarning)
- Error: warnings.filterwarnings("error", category=ResourceWarning)
- Context: with warnings.catch_warnings(record=True) as w:
- Capture: logging.captureWarnings(True) — pipe warnings to logger
- stacklevel: always stacklevel=2 in decorators / helpers
warnings Deprecation and Alert Pipeline
# app/warnutil.py — deprecated, experimental, warn helpers, capture, logging
from __future__ import annotations
import functools
import logging
import warnings
from collections.abc import Callable
from dataclasses import dataclass
from typing import Any, TypeVar
F = TypeVar("F", bound=Callable)
# ─────────────────────────────────────────────────────────────────────────────
# 1. Deprecation decorators
# ─────────────────────────────────────────────────────────────────────────────
def deprecated(
message: str = "",
*,
since: str | None = None,
removed_in: str | None = None,
replacement: str | None = None,
category: type[Warning] = DeprecationWarning,
) -> Callable[[F], F]:
"""
Mark a function or method as deprecated.
Emits a DeprecationWarning (or custom category) on each call,
pointing at the caller's call site (stacklevel=2).
Example:
@deprecated("Use new_process() instead", since="2.0", removed_in="3.0")
def old_process(data):
return new_process(data)
"""
def decorator(fn: F) -> F:
parts = []
if message:
parts.append(message)
if since:
parts.append(f"Deprecated since v{since}.")
if removed_in:
parts.append(f"Will be removed in v{removed_in}.")
if replacement:
parts.append(f"Use `{replacement}` instead.")
full_msg = f"{fn.__qualname__}(): {' '.join(parts)}" if parts else f"{fn.__qualname__}() is deprecated."
@functools.wraps(fn)
def wrapper(*args: Any, **kwargs: Any) -> Any:
warnings.warn(full_msg, category=category, stacklevel=2)
return fn(*args, **kwargs)
wrapper.__deprecated__ = True # type: ignore[attr-defined]
return wrapper # type: ignore[return-value]
return decorator
def renamed(old_name: str, new_fn: Callable, since: str = "") -> Callable:
"""
Create a backward-compatible alias that warns about the rename.
Example:
parse_config = renamed("parse_config", load_config, since="1.5")
"""
msg = (
f"`{old_name}()` has been renamed to `{new_fn.__name__}()`."
+ (f" Deprecated since v{since}." if since else "")
)
@functools.wraps(new_fn)
def alias(*args: Any, **kwargs: Any) -> Any:
warnings.warn(msg, DeprecationWarning, stacklevel=2)
return new_fn(*args, **kwargs)
alias.__name__ = old_name
return alias
def experimental(message: str = "") -> Callable[[F], F]:
"""
Mark a function as experimental; warns with UserWarning on call.
Example:
@experimental("API may change without notice")
def beta_feature(x):
...
"""
def decorator(fn: F) -> F:
msg = f"{fn.__qualname__}() is experimental. {message}".strip()
@functools.wraps(fn)
def wrapper(*args: Any, **kwargs: Any) -> Any:
warnings.warn(msg, UserWarning, stacklevel=2)
return fn(*args, **kwargs)
return wrapper # type: ignore[return-value]
return decorator
# ─────────────────────────────────────────────────────────────────────────────
# 2. Warn helpers
# ─────────────────────────────────────────────────────────────────────────────
def warn_once(message: str, category: type[Warning] = UserWarning) -> None:
"""
Emit a warning the first time this exact message is seen in this session.
Subsequent calls with the same message are suppressed.
Example:
warn_once("Fallback to defaults — configure your settings.")
"""
# "once" action: only show once per (message, category, module, lineno)
warnings.warn(message, category, stacklevel=2)
def warn_if(
condition: bool,
message: str,
category: type[Warning] = RuntimeWarning,
) -> None:
"""
Emit a warning only if condition is True.
Example:
warn_if(config.debug and not config.log_level, "debug=True but log_level not set")
"""
if condition:
warnings.warn(message, category, stacklevel=2)
def warn_deprecated_arg(
arg_name: str,
value: Any,
*,
sentinel: Any = None,
replacement: str | None = None,
) -> None:
"""
Warn if a deprecated keyword argument was provided (not equal to sentinel/default).
Example:
def process(data, *, timeout=None, max_time=None):
warn_deprecated_arg("timeout", timeout, replacement="max_time")
"""
if value is not sentinel:
msg = f"The `{arg_name}` argument is deprecated."
if replacement:
msg += f" Use `{replacement}` instead."
warnings.warn(msg, DeprecationWarning, stacklevel=3)
# ─────────────────────────────────────────────────────────────────────────────
# 3. Filter context managers
# ─────────────────────────────────────────────────────────────────────────────
def ignore_warnings(*categories: type[Warning]):
"""
Context manager that suppresses the given warning categories.
Example:
with ignore_warnings(DeprecationWarning, PendingDeprecationWarning):
old_library_call()
"""
ctx = warnings.catch_warnings()
ctx.__enter__()
for cat in categories:
warnings.simplefilter("ignore", category=cat)
return ctx
def treat_as_errors(*categories: type[Warning]):
"""
Context manager that turns the given warning categories into exceptions.
Example:
with treat_as_errors(ResourceWarning):
run_test() # raises if any resource is unclosed
"""
ctx = warnings.catch_warnings()
ctx.__enter__()
for cat in categories:
warnings.filterwarnings("error", category=cat)
return ctx
def capture_warnings(*categories: type[Warning]) -> "_CaptureContext":
"""
Context manager that captures matching warnings into a list for inspection.
Example:
with capture_warnings(DeprecationWarning) as captured:
old_api()
for w in captured.warnings:
print(w.category.__name__, w.message)
"""
return _CaptureContext(categories)
class _CaptureContext:
def __init__(self, categories: tuple[type[Warning], ...]) -> None:
self._categories = categories
self.warnings: list[warnings.WarningMessage] = []
def __enter__(self) -> "_CaptureContext":
self._ctx = warnings.catch_warnings(record=True)
self._w = self._ctx.__enter__()
if self._categories:
for cat in self._categories:
warnings.simplefilter("always", category=cat)
else:
warnings.simplefilter("always")
return self
def __exit__(self, *args: Any) -> None:
self.warnings = list(self._w) # type: ignore[arg-type]
self._ctx.__exit__(*args)
@property
def messages(self) -> list[str]:
return [str(w.message) for w in self.warnings]
@property
def categories(self) -> list[type[Warning]]:
return [w.category for w in self.warnings]
# ─────────────────────────────────────────────────────────────────────────────
# 4. Logging integration
# ─────────────────────────────────────────────────────────────────────────────
def pipe_warnings_to_logger(logger: logging.Logger | None = None) -> None:
"""
Redirect all Python warnings to the given logger (or root logger).
After calling this, warnings appear as log records at WARNING level
under the 'py.warnings' logger name.
Example:
logging.basicConfig(level=logging.DEBUG)
pipe_warnings_to_logger()
warnings.warn("This now appears in logs")
"""
logging.captureWarnings(True)
if logger is not None:
warn_logger = logging.getLogger("py.warnings")
# Propagate through the given logger's handlers
warn_logger.addHandler(logging.NullHandler())
for handler in logger.handlers:
warn_logger.addHandler(handler)
# ─────────────────────────────────────────────────────────────────────────────
# 5. Resource leak detector
# ─────────────────────────────────────────────────────────────────────────────
class LeakDetector:
"""
Wraps an object and emits a ResourceWarning if it is garbage-collected
without being explicitly closed/released.
Example:
conn = Connection(url)
guard = LeakDetector(conn, "Connection", release_fn=conn.close)
# ... use conn ...
guard.release() # mark as properly closed
del guard # no warning
"""
def __init__(
self,
resource: Any,
name: str = "resource",
release_fn: Callable | None = None,
) -> None:
self._name = name
self._resource = resource
self._release_fn = release_fn
self._released = False
def release(self) -> None:
"""Mark as properly released; suppresses the leak warning."""
if not self._released:
self._released = True
if self._release_fn is not None:
self._release_fn()
def __del__(self) -> None:
if not self._released:
warnings.warn(
f"{self._name} was not explicitly released before being garbage-collected.",
ResourceWarning,
stacklevel=1,
source=self._resource,
)
# ─────────────────────────────────────────────────────────────────────────────
# Demo
# ─────────────────────────────────────────────────────────────────────────────
if __name__ == "__main__":
import gc
# Show DeprecationWarnings (hidden by default in non-test environments)
warnings.simplefilter("always", DeprecationWarning)
warnings.simplefilter("always", UserWarning)
print("=== warnings demo ===")
print("\n--- @deprecated ---")
@deprecated("Use new_compute() instead", since="1.5", removed_in="2.0")
def old_compute(x: int) -> int:
return x * 2
with capture_warnings(DeprecationWarning) as cap:
old_compute(5)
print(f" captured: {cap.messages}")
print("\n--- renamed alias ---")
def load_config(path: str) -> dict:
return {}
parse_config = renamed("parse_config", load_config, since="2.0")
with capture_warnings(DeprecationWarning) as cap:
parse_config("app.ini")
print(f" renamed warning: {cap.messages}")
print("\n--- @experimental ---")
@experimental("Results may differ in future versions.")
def beta_process(data: list) -> list:
return data[::-1]
with capture_warnings(UserWarning) as cap:
beta_process([1, 2, 3])
print(f" experimental: {cap.messages}")
print("\n--- warn_if ---")
with capture_warnings(RuntimeWarning) as cap:
warn_if(True, "Config missing — using defaults", RuntimeWarning)
warn_if(False, "This should not appear")
print(f" warn_if (1 expected): {len(cap.warnings)} warnings")
print(f" msg: {cap.messages}")
print("\n--- warn_deprecated_arg ---")
def new_process(data: Any, *, max_time: float = 30, timeout: float | None = None) -> Any:
warn_deprecated_arg("timeout", timeout, replacement="max_time")
return data
with capture_warnings(DeprecationWarning) as cap:
new_process("input", timeout=10) # deprecated
new_process("input", max_time=10) # ok — no warning
print(f" deprecated arg warnings: {len(cap.warnings)}")
print(f" msg: {cap.messages}")
print("\n--- ignore_warnings / treat_as_errors ---")
with warnings.catch_warnings():
warnings.simplefilter("always")
with capture_warnings(UserWarning) as cap:
with ignore_warnings(UserWarning):
warnings.warn("should be suppressed", UserWarning)
print(f" ignore_warnings suppressed: {len(cap.warnings) == 0}")
try:
with treat_as_errors(RuntimeWarning):
warnings.warn("critical issue", RuntimeWarning)
except RuntimeWarning as e:
print(f" treat_as_errors raised: RuntimeWarning({e})")
print("\n--- LeakDetector ---")
class FakeConnection:
pass
conn = FakeConnection()
guard = LeakDetector(conn, "FakeConnection")
with capture_warnings(ResourceWarning) as cap:
del guard # released=False → should warn
gc.collect()
print(f" leak warning triggered: {len(cap.warnings) > 0}")
print(f" msg: {cap.messages}")
proper_guard = LeakDetector(FakeConnection(), "FakeConnection2")
proper_guard.release() # properly released
with capture_warnings(ResourceWarning) as cap:
del proper_guard
gc.collect()
print(f" no leak when released: {len(cap.warnings) == 0}")
print("\n=== done ===")
For the logging alternative — logging is the correct channel for runtime diagnostic messages that operators should see in production; warnings is for developer-facing alerts about incorrect API usage, deprecated calls, and potential bugs that are relevant during development and testing but may be suppressed in production by users or CI configuration — use logging.warning() for operational events (slow query, retry, cache miss); use warnings.warn() when you want library consumers to be able to filter, capture, or turn into errors the alert rather than just log it. For the typing.deprecated alternative — typing.deprecated (Python 3.13+ / typing_extensions) is a type alias decorator that marks functions as deprecated at the type checker level: pyright, mypy, and IDEs will show deprecated warnings at call sites without running the code; warnings.warn(DeprecationWarning) fires the warning at runtime — use @typing.deprecated together with @deprecated() from this module: the type decorator catches misuse at static analysis time in IDEs, the runtime decorator catches misuse from callers that bypass type checking or use dynamic dispatch. The Claude Skills 360 bundle includes warnings skill sets covering deprecated()/renamed()/experimental() function decorators with proper stacklevel, warn_once()/warn_if()/warn_deprecated_arg() emit helpers, ignore_warnings()/treat_as_errors()/capture_warnings() filter context managers, pipe_warnings_to_logger() logging integration, and LeakDetector ResourceWarning guard. Start with the free tier to try deprecation management patterns and warnings pipeline code generation.