Skip to content

Commit d89728c

Browse files
committed
feat: add deprecation warning decorator system
- add warn_deprecation decorator for method deprecation warnings - track shown warnings to prevent duplicate messages - support configurable suppression via suppress_deprecation_warnings - integrate with apicall configuration for warning control
1 parent ffe325f commit d89728c

File tree

1 file changed

+72
-0
lines changed

1 file changed

+72
-0
lines changed

src/typesense/logger.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,78 @@
11
"""Logging configuration for the Typesense Python client."""
22

3+
import functools
34
import logging
5+
import sys
6+
7+
if sys.version_info >= (3, 11):
8+
import typing
9+
else:
10+
import typing_extensions as typing
411

512
logger = logging.getLogger("typesense")
613
logger.setLevel(logging.WARN)
14+
15+
_deprecation_warnings: typing.Dict[str, bool] = {}
16+
17+
if sys.version_info >= (3, 11):
18+
from typing import ParamSpec, TypeVar
19+
else:
20+
from typing_extensions import ParamSpec, TypeVar
21+
22+
P = ParamSpec("P")
23+
R = TypeVar("R")
24+
25+
26+
def warn_deprecation(
27+
message: str,
28+
*,
29+
flag_name: typing.Union[str, None] = None,
30+
) -> typing.Callable[[typing.Callable[P, R]], typing.Callable[P, R]]:
31+
"""
32+
Decorator to warn about deprecation when a method is called.
33+
34+
This decorator will log a deprecation warning once per flag_name when the
35+
decorated method is called. The warning is only shown once to avoid spam.
36+
37+
Args:
38+
message: The deprecation warning message to display.
39+
flag_name: Optional name for the warning flag. If not provided, a default
40+
name will be generated based on the function's module and name.
41+
42+
Returns:
43+
A decorator function that wraps the target method.
44+
45+
Example:
46+
>>> @warn_deprecation("This method is deprecated", flag_name="my_method")
47+
... def my_method(self):
48+
... return "result"
49+
"""
50+
51+
def decorator(func: typing.Callable[P, R]) -> typing.Callable[P, R]:
52+
if flag_name is None:
53+
flag = f"{func.__module__}.{func.__qualname__}"
54+
else:
55+
flag = flag_name
56+
57+
@functools.wraps(func)
58+
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
59+
suppress_warnings = False
60+
if (
61+
args
62+
and len(args) > 1
63+
and args[1]
64+
and args[1].__class__.__name__ == "ApiCall"
65+
and hasattr(args[1], "config")
66+
):
67+
suppress_warnings = getattr(
68+
args[1].config, "suppress_deprecation_warnings", False
69+
)
70+
71+
if not suppress_warnings and not _deprecation_warnings.get(flag, False):
72+
logger.warning(f"Deprecation warning: {message}")
73+
_deprecation_warnings[flag] = True
74+
return func(*args, **kwargs)
75+
76+
return typing.cast(typing.Callable[P, R], wrapper)
77+
78+
return decorator

0 commit comments

Comments
 (0)