Skip to content

Commit a2b1ee6

Browse files
iamzainhudafacebook-github-bot
authored andcommitted
add experimental decorator (pytorch#2980)
Summary: Pull Request resolved: pytorch#2980 adding a simple decorator to allow us to flag APIs as experimental for users, optional flags to specify additional information such as package versions and dates usage: ```python experimental def f(x): ... ``` logs will show: ``` function is *experimental* and may change or be removed without notice. ``` Reviewed By: xunnanxu, kausv Differential Revision: D75014138 fbshipit-source-id: 89036af3a53891d73aa9972b3cee96a78874650f
1 parent cef7fea commit a2b1ee6

File tree

2 files changed

+97
-0
lines changed

2 files changed

+97
-0
lines changed

torchrec/utils/__init__.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#!/usr/bin/env python3
2+
# Copyright (c) Meta Platforms, Inc. and affiliates.
3+
# All rights reserved.
4+
#
5+
# This source code is licensed under the BSD-style license found in the
6+
# LICENSE file in the root directory of this source tree.
7+
8+
# pyre-strict
9+
10+
from . import experimental # noqa

torchrec/utils/experimental.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
#!/usr/bin/env python3
2+
# Copyright (c) Meta Platforms, Inc. and affiliates.
3+
# All rights reserved.
4+
#
5+
# This source code is licensed under the BSD-style license found in the
6+
# LICENSE file in the root directory of this source tree.
7+
8+
# pyre-strict
9+
from __future__ import annotations
10+
11+
import functools
12+
import warnings
13+
from typing import Any, Callable, ParamSpec, Type, TypeVar, Union
14+
15+
P = ParamSpec("P")
16+
R = TypeVar("R")
17+
T = TypeVar("T")
18+
19+
20+
def experimental(
21+
obj: Union[Callable[P, R], Type[T]],
22+
feature: str | None = None,
23+
since: str | None = None,
24+
) -> Union[Callable[P, R], Type[T]]:
25+
"""
26+
Decorator that flags a function or class as *experimental*.
27+
28+
The decorator emits a :class:`UserWarning` the first time the wrapped
29+
callable (or the constructor of a wrapped class) is invoked. This is
30+
useful for APIs that may change or be removed without notice.
31+
32+
Args:
33+
obj: The function, method, or class to be wrapped.
34+
feature: Optional explicit name for the experimental feature.
35+
Defaults to the name of the wrapped object.
36+
since: Optional version or date string (e.g. ``"1.2.0"`` or
37+
``"2025-05-21"``) indicating when the feature became experimental.
38+
39+
Returns:
40+
The same callable or class, wrapped so that it issues a warning once.
41+
42+
Warning:
43+
The decorated API is **not stable**. Downstream code should not rely on
44+
its long-term availability or on the permanence of its current
45+
behavior.
46+
47+
Example:
48+
>>> @experimental
49+
... def fancy_new_op(x):
50+
... return x * 2
51+
>>> fancy_new_op(3) # first call triggers a warning
52+
6
53+
54+
>>> @experimental(feature="Hybird 2D Parallel", since="1.2.0")
55+
... class HybirdDistributedModelParallel:
56+
... ...
57+
"""
58+
tag: str = feature or obj.__name__ # pyre-ignore[16]
59+
message_parts: list[str] = [
60+
f"`{tag}` is *experimental* and may change or be removed without notice."
61+
]
62+
if since:
63+
message_parts.insert(0, f"[since {since}]")
64+
warning_message: str = " ".join(message_parts)
65+
66+
@functools.lru_cache(maxsize=1)
67+
def _issue_warning() -> None:
68+
warnings.warn(warning_message, UserWarning, stacklevel=3)
69+
70+
if isinstance(obj, type):
71+
orig_init: Callable[..., None] = obj.__init__
72+
73+
@functools.wraps(orig_init)
74+
def new_init(self, *args: Any, **kwargs: Any) -> Any: # pyre-ignore[2, 3]
75+
_issue_warning()
76+
return orig_init(self, *args, **kwargs)
77+
78+
obj.__init__ = new_init
79+
return obj
80+
else:
81+
82+
@functools.wraps(obj) # pyre-ignore[6]
83+
def wrapper(*args: Any, **kwargs: Any) -> Any: # pyre-ignore[3]
84+
_issue_warning()
85+
return obj(*args, **kwargs) # pyre-ignore[29]
86+
87+
return wrapper

0 commit comments

Comments
 (0)