"""Helper functions for deprecating parts of the LangChain API.This module was adapted from matplotlibs _api/deprecation.py module:https://github.com/matplotlib/matplotlib/blob/main/lib/matplotlib/_api/deprecation.py.. warning:: This module is for internal use only. Do not use it in your own code. We may change the API at any time with no warning."""importcontextlibimportfunctoolsimportinspectimportwarningsfromtypingimport(Any,Callable,Generator,Type,TypeVar,Union,cast,)fromtyping_extensionsimportParamSpecfromlangchain_core._api.internalimportis_caller_internalclassLangChainDeprecationWarning(DeprecationWarning):"""A class for issuing deprecation warnings for LangChain users."""classLangChainPendingDeprecationWarning(PendingDeprecationWarning):"""A class for issuing deprecation warnings for LangChain users."""# PUBLIC API# Last Any should be FieldInfoV1 but this leads to circular importsT=TypeVar("T",bound=Union[Type,Callable[...,Any],Any])def_validate_deprecation_params(pending:bool,removal:str,alternative:str,alternative_import:str,)->None:"""Validate the deprecation parameters."""ifpendingandremoval:raiseValueError("A pending deprecation cannot have a scheduled removal")ifalternativeandalternative_import:raiseValueError("Cannot specify both alternative and alternative_import")ifalternative_importand"."notinalternative_import:raiseValueError("alternative_import must be a fully qualified module path. Got "f" {alternative_import}")defdeprecated(since:str,*,message:str="",name:str="",alternative:str="",alternative_import:str="",pending:bool=False,obj_type:str="",addendum:str="",removal:str="",package:str="",)->Callable[[T],T]:"""Decorator to mark a function, a class, or a property as deprecated. When deprecating a classmethod, a staticmethod, or a property, the ``@deprecated`` decorator should go *under* ``@classmethod`` and ``@staticmethod`` (i.e., `deprecated` should directly decorate the underlying callable), but *over* ``@property``. When deprecating a class ``C`` intended to be used as a base class in a multiple inheritance hierarchy, ``C`` *must* define an ``__init__`` method (if ``C`` instead inherited its ``__init__`` from its own base class, then ``@deprecated`` would mess up ``__init__`` inheritance when installing its own (deprecation-emitting) ``C.__init__``). Parameters are the same as for `warn_deprecated`, except that *obj_type* defaults to 'class' if decorating a class, 'attribute' if decorating a property, and 'function' otherwise. Arguments: since : str The release at which this API became deprecated. message : str, optional Override the default deprecation message. The %(since)s, %(name)s, %(alternative)s, %(obj_type)s, %(addendum)s, and %(removal)s format specifiers will be replaced by the values of the respective arguments passed to this function. name : str, optional The name of the deprecated object. alternative : str, optional An alternative API that the user may use in place of the deprecated API. The deprecation warning will tell the user about this alternative if provided. pending : bool, optional If True, uses a PendingDeprecationWarning instead of a DeprecationWarning. Cannot be used together with removal. obj_type : str, optional The object type being deprecated. addendum : str, optional Additional text appended directly to the final message. removal : str, optional The expected removal version. With the default (an empty string), a removal version is automatically computed from since. Set to other Falsy values to not schedule a removal date. Cannot be used together with pending. Examples -------- .. code-block:: python @deprecated('1.4.0') def the_function_to_deprecate(): pass """_validate_deprecation_params(pending,removal,alternative,alternative_import)defdeprecate(obj:T,*,_obj_type:str=obj_type,_name:str=name,_message:str=message,_alternative:str=alternative,_alternative_import:str=alternative_import,_pending:bool=pending,_addendum:str=addendum,_package:str=package,)->T:"""Implementation of the decorator returned by `deprecated`."""fromlangchain_core.utils.pydanticimportFieldInfoV1defemit_warning()->None:"""Emit the warning."""warn_deprecated(since,message=_message,name=_name,alternative=_alternative,alternative_import=_alternative_import,pending=_pending,obj_type=_obj_type,addendum=_addendum,removal=removal,package=_package,)warned=Falsedefwarning_emitting_wrapper(*args:Any,**kwargs:Any)->Any:"""Wrapper for the original wrapped callable that emits a warning. Args: *args: The positional arguments to the function. **kwargs: The keyword arguments to the function. Returns: The return value of the function being wrapped. """nonlocalwarnedifnotwarnedandnotis_caller_internal():warned=Trueemit_warning()returnwrapped(*args,**kwargs)asyncdefawarning_emitting_wrapper(*args:Any,**kwargs:Any)->Any:"""Same as warning_emitting_wrapper, but for async functions."""nonlocalwarnedifnotwarnedandnotis_caller_internal():warned=Trueemit_warning()returnawaitwrapped(*args,**kwargs)_package=_packageorobj.__module__.split(".")[0].replace("_","-")ifisinstance(obj,type):ifnot_obj_type:_obj_type="class"wrapped=obj.__init__# type: ignore_name=_nameorobj.__qualname__old_doc=obj.__doc__deffinalize(wrapper:Callable[...,Any],new_doc:str)->T:"""Finalize the deprecation of a class."""try:obj.__doc__=new_docexceptAttributeError:# Can't set on some extension objects.passdefwarn_if_direct_instance(self:Any,*args:Any,**kwargs:Any)->Any:"""Warn that the class is in beta."""nonlocalwarnedifnotwarnedandtype(self)isobjandnotis_caller_internal():warned=Trueemit_warning()returnwrapped(self,*args,**kwargs)obj.__init__=functools.wraps(obj.__init__)(# type: ignore[misc]warn_if_direct_instance)returncast(T,obj)elifisinstance(obj,FieldInfoV1):wrapped=Noneifnot_obj_type:_obj_type="attribute"ifnot_name:raiseValueError(f"Field {obj} must have a name to be deprecated.")old_doc=obj.descriptiondeffinalize(wrapper:Callable[...,Any],new_doc:str)->T:returncast(T,FieldInfoV1(default=obj.default,default_factory=obj.default_factory,description=new_doc,alias=obj.alias,exclude=obj.exclude,),)elifisinstance(obj,property):ifnot_obj_type:_obj_type="attribute"wrapped=None_name=_nameorcast(Union[Type,Callable],obj.fget).__qualname__old_doc=obj.__doc__class_deprecated_property(property):"""A deprecated property."""def__init__(self,fget=None,fset=None,fdel=None,doc=None):# type: ignore[no-untyped-def]super().__init__(fget,fset,fdel,doc)self.__orig_fget=fgetself.__orig_fset=fsetself.__orig_fdel=fdeldef__get__(self,instance,owner=None):# type: ignore[no-untyped-def]ifinstanceisnotNoneorownerisnotNone:emit_warning()returnself.fget(instance)def__set__(self,instance,value):# type: ignore[no-untyped-def]ifinstanceisnotNone:emit_warning()returnself.fset(instance,value)def__delete__(self,instance):# type: ignore[no-untyped-def]ifinstanceisnotNone:emit_warning()returnself.fdel(instance)def__set_name__(self,owner,set_name):# type: ignore[no-untyped-def]nonlocal_nameif_name=="<lambda>":_name=set_namedeffinalize(wrapper:Callable[...,Any],new_doc:str)->T:"""Finalize the property."""returncast(T,_deprecated_property(fget=obj.fget,fset=obj.fset,fdel=obj.fdel,doc=new_doc),)else:_name=_nameorcast(Union[Type,Callable],obj).__qualname__ifnot_obj_type:# edge case: when a function is within another function# within a test, this will call it a "method" not a "function"_obj_type="function"if"."notin_nameelse"method"wrapped=objold_doc=wrapped.__doc__deffinalize(wrapper:Callable[...,Any],new_doc:str)->T:"""Wrap the wrapped function using the wrapper and update the docstring. Args: wrapper: The wrapper function. new_doc: The new docstring. Returns: The wrapped function. """wrapper=functools.wraps(wrapped)(wrapper)wrapper.__doc__=new_docreturncast(T,wrapper)old_doc=inspect.cleandoc(old_docor"").strip("\n")# old_doc can be Noneifnotold_doc:old_doc=""# Modify the docstring to include a deprecation notice.components=[_message,f"Use ``{_alternative}`` instead."if_alternativeelse"",f"Use ``{_alternative_import}`` instead."if_alternative_importelse"",_addendum,]details=" ".join([component.strip()forcomponentincomponentsifcomponent])package=(_packageor_name.split(".")[0].replace("_","-")if"."in_nameelseNone)since_str=f"{package}=={since}"ifpackageelsesincenew_doc=f"""\.. deprecated:: {since_str}{details}{old_doc}\"""ifinspect.iscoroutinefunction(obj):finalized=finalize(awarning_emitting_wrapper,new_doc)else:finalized=finalize(warning_emitting_wrapper,new_doc)returncast(T,finalized)returndeprecate@contextlib.contextmanagerdefsuppress_langchain_deprecation_warning()->Generator[None,None,None]:"""Context manager to suppress LangChainDeprecationWarning."""withwarnings.catch_warnings():warnings.simplefilter("ignore",LangChainDeprecationWarning)warnings.simplefilter("ignore",LangChainPendingDeprecationWarning)yielddefwarn_deprecated(since:str,*,message:str="",name:str="",alternative:str="",alternative_import:str="",pending:bool=False,obj_type:str="",addendum:str="",removal:str="",package:str="",)->None:"""Display a standardized deprecation. Arguments: since : str The release at which this API became deprecated. message : str, optional Override the default deprecation message. The %(since)s, %(name)s, %(alternative)s, %(obj_type)s, %(addendum)s, and %(removal)s format specifiers will be replaced by the values of the respective arguments passed to this function. name : str, optional The name of the deprecated object. alternative : str, optional An alternative API that the user may use in place of the deprecated API. The deprecation warning will tell the user about this alternative if provided. pending : bool, optional If True, uses a PendingDeprecationWarning instead of a DeprecationWarning. Cannot be used together with removal. obj_type : str, optional The object type being deprecated. addendum : str, optional Additional text appended directly to the final message. removal : str, optional The expected removal version. With the default (an empty string), a removal version is automatically computed from since. Set to other Falsy values to not schedule a removal date. Cannot be used together with pending. """ifnotpending:ifnotremoval:removal=f"in {removal}"ifremovalelse"within ?? minor releases"raiseNotImplementedError(f"Need to determine which default deprecation schedule to use. "f"{removal}")else:removal=f"in {removal}"ifnotmessage:message=""_package=(packageorname.split(".")[0].replace("_","-")if"."innameelse"LangChain")ifobj_type:message+=f"The {obj_type} `{name}`"else:message+=f"`{name}`"ifpending:message+=" will be deprecated in a future version"else:message+=f" was deprecated in {_package}{since}"ifremoval:message+=f" and will be removed {removal}"ifalternative_import:alt_package=alternative_import.split(".")[0].replace("_","-")ifalt_package==_package:message+=f". Use {alternative_import} instead."else:alt_module,alt_name=alternative_import.rsplit(".",1)message+=(f". An updated version of the {obj_type} exists in the "f"{alt_package} package and should be used instead. To use it run "f"`pip install -U {alt_package}` and import as "f"`from {alt_module} import {alt_name}`.")elifalternative:message+=f". Use {alternative} instead."ifaddendum:message+=f" {addendum}"warning_cls=(LangChainPendingDeprecationWarningifpendingelseLangChainDeprecationWarning)warning=warning_cls(message)warnings.warn(warning,category=LangChainDeprecationWarning,stacklevel=4)defsurface_langchain_deprecation_warnings()->None:"""Unmute LangChain deprecation warnings."""warnings.filterwarnings("default",category=LangChainPendingDeprecationWarning,)warnings.filterwarnings("default",category=LangChainDeprecationWarning,)_P=ParamSpec("_P")_R=TypeVar("_R")defrename_parameter(*,since:str,removal:str,old:str,new:str,)->Callable[[Callable[_P,_R]],Callable[_P,_R]]:"""Decorator indicating that parameter *old* of *func* is renamed to *new*. The actual implementation of *func* should use *new*, not *old*. If *old* is passed to *func*, a DeprecationWarning is emitted, and its value is used, even if *new* is also passed by keyword. Example: .. code-block:: python @_api.rename_parameter("3.1", "bad_name", "good_name") def func(good_name): ... """defdecorator(f:Callable[_P,_R])->Callable[_P,_R]:@functools.wraps(f)defwrapper(*args:_P.args,**kwargs:_P.kwargs)->_R:ifnewinkwargsandoldinkwargs:raiseTypeError(f"{f.__name__}() got multiple values for argument {new!r}")ifoldinkwargs:warn_deprecated(since,removal=removal,message=f"The parameter `{old}` of `{f.__name__}` was "f"deprecated in {since} and will be removed "f"in {removal} Use `{new}` instead.",)kwargs[new]=kwargs.pop(old)returnf(*args,**kwargs)returnwrapperreturndecorator