libs/core/langchain_core/_api/beta_decorator.py PYTHON 254 lines View on github.com → Search inside
1"""Helper functions for marking parts of the LangChain API as beta.23This module was loosely adapted from matplotlib's [`_api/deprecation.py`](https://github.com/matplotlib/matplotlib/blob/main/lib/matplotlib/_api/deprecation.py)4module.56!!! warning78    This module is for internal use only. Do not use it in your own code. We may change9    the API at any time with no warning.10"""1112import contextlib13import functools14import inspect15import warnings16from collections.abc import Callable, Generator17from typing import Any, TypeVar, cast1819from langchain_core._api.internal import is_caller_internal202122class LangChainBetaWarning(DeprecationWarning):23    """A class for issuing beta warnings for LangChain users."""242526# PUBLIC API272829T = TypeVar("T", bound=Callable[..., Any] | type)303132def beta(33    *,34    message: str = "",35    name: str = "",36    obj_type: str = "",37    addendum: str = "",38) -> Callable[[T], T]:39    """Decorator to mark a function, a class, or a property as beta.4041    When marking a classmethod, a staticmethod, or a property, the `@beta` decorator42    should go *under* `@classmethod` and `@staticmethod` (i.e., `beta` should directly43    decorate the underlying callable), but *over* `@property`.4445    When marking a class `C` intended to be used as a base class in a multiple46    inheritance hierarchy, `C` *must* define an `__init__` method (if `C` instead47    inherited its `__init__` from its own base class, then `@beta` would mess up48    `__init__` inheritance when installing its own (annotation-emitting) `C.__init__`).4950    Args:51        message: Override the default beta message.5253            The %(since)s, %(name)s, %(alternative)s, %(obj_type)s, %(addendum)s, and54            %(removal)s format specifiers will be replaced by the values of the55            respective arguments passed to this function.56        name: The name of the beta object.57        obj_type: The object type being beta.58        addendum: Additional text appended directly to the final message.5960    Returns:61        A decorator which can be used to mark functions or classes as beta.6263    Example:64        ```python65        @beta66        def the_function_to_annotate():67            pass68        ```69    """7071    def beta(72        obj: T,73        *,74        _obj_type: str = obj_type,75        _name: str = name,76        _message: str = message,77        _addendum: str = addendum,78    ) -> T:79        """Implementation of the decorator returned by `beta`."""8081        def emit_warning() -> None:82            """Emit the warning."""83            warn_beta(84                message=_message,85                name=_name,86                obj_type=_obj_type,87                addendum=_addendum,88            )8990        warned = False9192        def warning_emitting_wrapper(*args: Any, **kwargs: Any) -> Any:93            """Wrapper for the original wrapped callable that emits a warning.9495            Args:96                *args: The positional arguments to the function.97                **kwargs: The keyword arguments to the function.9899            Returns:100                The return value of the function being wrapped.101            """102            nonlocal warned103            if not warned and not is_caller_internal():104                warned = True105                emit_warning()106            return wrapped(*args, **kwargs)107108        async def awarning_emitting_wrapper(*args: Any, **kwargs: Any) -> Any:109            """Same as warning_emitting_wrapper, but for async functions."""110            nonlocal warned111            if not warned and not is_caller_internal():112                warned = True113                emit_warning()114            return await wrapped(*args, **kwargs)115116        if isinstance(obj, type):117            if not _obj_type:118                _obj_type = "class"119            wrapped = obj.__init__  # type: ignore[misc]120            _name = _name or obj.__qualname__121            old_doc = obj.__doc__122123            def finalize(_: Callable[..., Any], new_doc: str, /) -> T:124                """Finalize the annotation of a class."""125                # Can't set new_doc on some extension objects.126                with contextlib.suppress(AttributeError):127                    obj.__doc__ = new_doc128129                def warn_if_direct_instance(130                    self: Any, *args: Any, **kwargs: Any131                ) -> Any:132                    """Warn that the class is in beta."""133                    nonlocal warned134                    if not warned and type(self) is obj and not is_caller_internal():135                        warned = True136                        emit_warning()137                    return wrapped(self, *args, **kwargs)138139                obj.__init__ = functools.wraps(obj.__init__)(  # type: ignore[misc]140                    warn_if_direct_instance141                )142                return obj143144        elif isinstance(obj, property):145            if not _obj_type:146                _obj_type = "attribute"147            wrapped = None148            _name = _name or obj.fget.__qualname__149            old_doc = obj.__doc__150151            def _fget(instance: Any) -> Any:152                if instance is not None:153                    emit_warning()154                return obj.fget(instance)155156            def _fset(instance: Any, value: Any) -> None:157                if instance is not None:158                    emit_warning()159                obj.fset(instance, value)160161            def _fdel(instance: Any) -> None:162                if instance is not None:163                    emit_warning()164                obj.fdel(instance)165166            def finalize(_: Callable[..., Any], new_doc: str, /) -> Any:167                """Finalize the property."""168                return property(fget=_fget, fset=_fset, fdel=_fdel, doc=new_doc)169170        else:171            _name = _name or obj.__qualname__172            if not _obj_type:173                # edge case: when a function is within another function174                # within a test, this will call it a "method" not a "function"175                _obj_type = "function" if "." not in _name else "method"176            wrapped = obj177            old_doc = wrapped.__doc__178179            def finalize(wrapper: Callable[..., Any], new_doc: str, /) -> T:180                """Wrap the wrapped function using the wrapper and update the docstring.181182                Args:183                    wrapper: The wrapper function.184                    new_doc: The new docstring.185186                Returns:187                    The wrapped function.188                """189                wrapper = functools.wraps(wrapped)(wrapper)190                wrapper.__doc__ = new_doc191                return cast("T", wrapper)192193        old_doc = inspect.cleandoc(old_doc or "").strip("\n") or ""194        components = [message, addendum]195        details = " ".join([component.strip() for component in components if component])196        new_doc = f".. beta::\n   {details}\n\n{old_doc}\n"197198        if inspect.iscoroutinefunction(obj):199            return finalize(awarning_emitting_wrapper, new_doc)200        return finalize(warning_emitting_wrapper, new_doc)201202    return beta203204205@contextlib.contextmanager206def suppress_langchain_beta_warning() -> Generator[None, None, None]:207    """Context manager to suppress `LangChainDeprecationWarning`."""208    with warnings.catch_warnings():209        warnings.simplefilter("ignore", LangChainBetaWarning)210        yield211212213def warn_beta(214    *,215    message: str = "",216    name: str = "",217    obj_type: str = "",218    addendum: str = "",219) -> None:220    """Display a standardized beta annotation.221222    Args:223        message: Override the default beta message.224225            The %(name)s, %(obj_type)s, %(addendum)s format specifiers will be replaced226            by the values of the respective arguments passed to this function.227        name: The name of the annotated object.228        obj_type: The object type being annotated.229        addendum: Additional text appended directly to the final message.230    """231    if not message:232        message = ""233234        if obj_type:235            message += f"The {obj_type} `{name}`"236        else:237            message += f"`{name}`"238239        message += " is in beta. It is actively being worked on, so the API may change."240241        if addendum:242            message += f" {addendum}"243244    warning = LangChainBetaWarning(message)245    warnings.warn(warning, category=LangChainBetaWarning, stacklevel=4)246247248def surface_langchain_beta_warnings() -> None:249    """Unmute LangChain beta warnings."""250    warnings.filterwarnings(251        "default",252        category=LangChainBetaWarning,253    )

Code quality findings 8

Ensure functions have docstrings for documentation
missing-docstring
def beta(
Ensure functions have docstrings for documentation
missing-docstring
def the_function_to_annotate():
Ensure functions have docstrings for documentation
missing-docstring
def beta(
Overuse may indicate design issues; consider polymorphism
isinstance-overuse
if isinstance(obj, type):
Ensure functions have docstrings for documentation
missing-docstring
def warn_if_direct_instance(
Use isinstance() for type checking instead of type()
type-check
if not warned and type(self) is obj and not is_caller_internal():
Overuse may indicate design issues; consider polymorphism
isinstance-overuse
elif isinstance(obj, property):
Ensure functions have docstrings for documentation
missing-docstring
def warn_beta(

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.