mirror of
https://github.com/encounter/adk-python.git
synced 2026-07-09 18:19:28 -07:00
chore: Adds decorators to mark classes or fucntions as working_in_progress or experimental
User will see warning if they instantiate such class or call such functions. PiperOrigin-RevId: 769413048
This commit is contained in:
committed by
Copybara-Service
parent
bf47a8bf7d
commit
088200072f
@@ -0,0 +1,99 @@
|
||||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import functools
|
||||
from typing import Callable
|
||||
from typing import cast
|
||||
from typing import TypeVar
|
||||
from typing import Union
|
||||
import warnings
|
||||
|
||||
T = TypeVar("T", bound=Union[Callable, type])
|
||||
|
||||
|
||||
def _make_feature_decorator(
|
||||
*, label: str, default_message: str
|
||||
) -> Callable[[str], Callable[[T], T]]:
|
||||
def decorator_factory(message: str = default_message) -> Callable[[T], T]:
|
||||
def decorator(obj: T) -> T:
|
||||
obj_name = getattr(obj, "__name__", type(obj).__name__)
|
||||
warn_msg = f"[{label.upper()}] {obj_name}: {message}"
|
||||
|
||||
if isinstance(obj, type): # decorating a class
|
||||
orig_init = obj.__init__
|
||||
|
||||
@functools.wraps(orig_init)
|
||||
def new_init(self, *args, **kwargs):
|
||||
warnings.warn(warn_msg, category=UserWarning, stacklevel=2)
|
||||
return orig_init(self, *args, **kwargs)
|
||||
|
||||
obj.__init__ = new_init # type: ignore[attr-defined]
|
||||
return cast(T, obj)
|
||||
|
||||
elif callable(obj): # decorating a function or method
|
||||
|
||||
@functools.wraps(obj)
|
||||
def wrapper(*args, **kwargs):
|
||||
warnings.warn(warn_msg, category=UserWarning, stacklevel=2)
|
||||
return obj(*args, **kwargs)
|
||||
|
||||
return cast(T, wrapper)
|
||||
|
||||
else:
|
||||
raise TypeError(
|
||||
f"@{label} can only be applied to classes or callable objects"
|
||||
)
|
||||
|
||||
return decorator
|
||||
|
||||
return decorator_factory
|
||||
|
||||
|
||||
working_in_progress = _make_feature_decorator(
|
||||
label="WIP",
|
||||
default_message=(
|
||||
"This feature is a work in progress and may be incomplete or unstable."
|
||||
),
|
||||
)
|
||||
"""Mark a class or function as a work in progress.
|
||||
|
||||
Sample usage:
|
||||
|
||||
```
|
||||
@working_in_progress("This feature is not ready for production use.")
|
||||
def my_wip_function():
|
||||
pass
|
||||
```
|
||||
"""
|
||||
|
||||
experimental = _make_feature_decorator(
|
||||
label="EXPERIMENTAL",
|
||||
default_message=(
|
||||
"This feature is experimental and may change or be removed in future"
|
||||
" versions without notice. It may introduce breaking changes at any"
|
||||
" time."
|
||||
),
|
||||
)
|
||||
"""Mark a class or a function as an experimental feature.
|
||||
|
||||
Sample usage:
|
||||
|
||||
```
|
||||
@experimental("This API may have breaking change in the future.")
|
||||
class ExperimentalClass:
|
||||
pass
|
||||
```
|
||||
"""
|
||||
@@ -0,0 +1,42 @@
|
||||
import warnings
|
||||
|
||||
from google.adk.utils.feature_decorator import experimental
|
||||
from google.adk.utils.feature_decorator import working_in_progress
|
||||
|
||||
|
||||
@working_in_progress("in complete feature, don't use yet")
|
||||
class IncompleteFeature:
|
||||
|
||||
def run(self):
|
||||
return "running"
|
||||
|
||||
|
||||
@experimental("api may have breaking change in the future.")
|
||||
def experimental_fn():
|
||||
return "executing"
|
||||
|
||||
|
||||
def test_working_in_progress_class_warns():
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
warnings.simplefilter("always")
|
||||
|
||||
feature = IncompleteFeature()
|
||||
|
||||
assert feature.run() == "running"
|
||||
assert len(w) == 1
|
||||
assert issubclass(w[0].category, UserWarning)
|
||||
assert "[WIP] IncompleteFeature:" in str(w[0].message)
|
||||
assert "don't use yet" in str(w[0].message)
|
||||
|
||||
|
||||
def test_experimental_method_warns():
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
warnings.simplefilter("always")
|
||||
|
||||
result = experimental_fn()
|
||||
|
||||
assert result == "executing"
|
||||
assert len(w) == 1
|
||||
assert issubclass(w[0].category, UserWarning)
|
||||
assert "[EXPERIMENTAL] experimental_fn:" in str(w[0].message)
|
||||
assert "breaking change in the future" in str(w[0].message)
|
||||
Reference in New Issue
Block a user