Overuse may indicate design issues; consider polymorphism
return isinstance(obj, mod.FieldInfo)
1"""Helper functions for deprecating parts of the LangChain API.23This module was 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 sys16import warnings17from collections.abc import Callable, Generator18from contextvars import ContextVar19from typing import (20 TYPE_CHECKING,21 Any,22 ParamSpec,23 TypeGuard,24 TypeVar,25 cast,26)2728from pydantic.fields import FieldInfo2930from langchain_core._api.internal import is_caller_internal3132if TYPE_CHECKING:33 from pydantic.v1.fields import FieldInfo as FieldInfoV1343536def _is_pydantic_v1_field_info(obj: Any) -> TypeGuard["FieldInfoV1"]:37 """Check if `obj` is a `pydantic.v1.fields.FieldInfo` without forcing import.3839 Importing `pydantic.v1` emits a `UserWarning` on Python 3.14+. Skipping the40 import entirely when no caller has constructed a v1 `FieldInfo` keeps that41 warning out of `langchain_core`'s import path. If a caller did construct one,42 `pydantic.v1.fields` is already in `sys.modules` and isinstance is safe.43 """44 mod = sys.modules.get("pydantic.v1.fields")45 if mod is None:46 return False47 return isinstance(obj, mod.FieldInfo)484950def _build_deprecation_message(51 *,52 alternative: str = "",53 alternative_import: str = "",54) -> str:55 """Build a simple deprecation message for `__deprecated__` attribute.5657 Args:58 alternative: An alternative API name.59 alternative_import: A fully qualified import path for the alternative.6061 Returns:62 A deprecation message string for IDE/type checker display.63 """64 if alternative_import:65 return f"Use {alternative_import} instead."66 if alternative:67 return f"Use {alternative} instead."68 return "Deprecated."697071class LangChainDeprecationWarning(DeprecationWarning):72 """A class for issuing deprecation warnings for LangChain users."""737475class LangChainPendingDeprecationWarning(PendingDeprecationWarning):76 """A class for issuing deprecation warnings for LangChain users."""777879# Tracks when callers intentionally silence LangChain deprecation warnings.80# Suppressed warnings should not consume a deprecated callable's one-time81# warning state; otherwise an internal compatibility path can prevent the first82# user-visible call from warning.83_SUPPRESSING_LANGCHAIN_DEPRECATION_WARNING = ContextVar(84 "_SUPPRESSING_LANGCHAIN_DEPRECATION_WARNING", default=False85)868788# PUBLIC API899091# Bound is `Any` (not `FieldInfoV1`) because importing `pydantic.v1` at module92# scope emits a `UserWarning` on Python 3.14+; v1 `FieldInfo` support is handled93# at runtime via `_is_pydantic_v1_field_info`.94T = TypeVar("T", bound=type | Callable[..., Any] | Any)959697def _validate_deprecation_params(98 removal: str,99 alternative: str,100 alternative_import: str,101 *,102 pending: bool,103) -> None:104 """Validate the deprecation parameters."""105 if pending and removal:106 msg = "A pending deprecation cannot have a scheduled removal"107 raise ValueError(msg)108 if alternative and alternative_import:109 msg = "Cannot specify both alternative and alternative_import"110 raise ValueError(msg)111112 if alternative_import and "." not in alternative_import:113 msg = (114 "alternative_import must be a fully qualified module path. Got "115 f" {alternative_import}"116 )117 raise ValueError(msg)118119120def deprecated(121 since: str,122 *,123 message: str = "",124 name: str = "",125 alternative: str = "",126 alternative_import: str = "",127 pending: bool = False,128 obj_type: str = "",129 addendum: str = "",130 removal: str = "",131 package: str = "",132) -> Callable[[T], T]:133 """Decorator to mark a function, a class, or a property as deprecated.134135 When deprecating a classmethod, a staticmethod, or a property, the `@deprecated`136 decorator should go *under* `@classmethod` and `@staticmethod` (i.e., `deprecated`137 should directly decorate the underlying callable), but *over* `@property`.138139 When deprecating a class `C` intended to be used as a base class in a multiple140 inheritance hierarchy, `C` *must* define an `__init__` method (if `C` instead141 inherited its `__init__` from its own base class, then `@deprecated` would mess up142 `__init__` inheritance when installing its own (deprecation-emitting) `C.__init__`).143144 Parameters are the same as for `warn_deprecated`, except that *obj_type* defaults to145 'class' if decorating a class, 'attribute' if decorating a property, and 'function'146 otherwise.147148 Args:149 since: The release at which this API became deprecated.150 message: Override the default deprecation message.151152 The `%(since)s`, `%(name)s`, `%(alternative)s`, `%(obj_type)s`,153 `%(addendum)s`, and `%(removal)s` format specifiers will be replaced by the154 values of the respective arguments passed to this function.155 name: The name of the deprecated object.156 alternative: An alternative API that the user may use in place of the deprecated157 API.158159 The deprecation warning will tell the user about this alternative if160 provided.161 alternative_import: An alternative import that the user may use instead.162 pending: If `True`, uses a `PendingDeprecationWarning` instead of a163 `DeprecationWarning`.164165 Cannot be used together with removal.166 obj_type: The object type being deprecated.167 addendum: Additional text appended directly to the final message.168 removal: The expected removal version.169170 With the default (an empty string), no removal version is shown in the171 warning message.172173 Cannot be used together with pending.174 package: The package of the deprecated object.175176 Returns:177 A decorator to mark a function or class as deprecated.178179 Example:180 ```python181 @deprecated("1.4.0")182 def the_function_to_deprecate():183 pass184 ```185 """186 _validate_deprecation_params(187 removal, alternative, alternative_import, pending=pending188 )189190 def deprecate(191 obj: T,192 *,193 _obj_type: str = obj_type,194 _name: str = name,195 _message: str = message,196 _alternative: str = alternative,197 _alternative_import: str = alternative_import,198 _pending: bool = pending,199 _addendum: str = addendum,200 _package: str = package,201 ) -> T:202 """Implementation of the decorator returned by `deprecated`."""203204 def emit_warning() -> None:205 """Emit the warning."""206 warn_deprecated(207 since,208 message=_message,209 name=_name,210 alternative=_alternative,211 alternative_import=_alternative_import,212 pending=_pending,213 obj_type=_obj_type,214 addendum=_addendum,215 removal=removal,216 package=_package,217 )218219 warned = False220221 def warning_emitting_wrapper(*args: Any, **kwargs: Any) -> Any:222 """Wrapper for the original wrapped callable that emits a warning.223224 Args:225 *args: The positional arguments to the function.226 **kwargs: The keyword arguments to the function.227228 Returns:229 The return value of the function being wrapped.230 """231 nonlocal warned232 if not warned and not is_caller_internal():233 emit_warning()234 # Only mark the warning as emitted if it was not intentionally235 # suppressed by `suppress_langchain_deprecation_warning()`.236 warned = not _SUPPRESSING_LANGCHAIN_DEPRECATION_WARNING.get()237 return wrapped(*args, **kwargs)238239 async def awarning_emitting_wrapper(*args: Any, **kwargs: Any) -> Any:240 """Same as warning_emitting_wrapper, but for async functions."""241 nonlocal warned242 if not warned and not is_caller_internal():243 emit_warning()244 # Only mark the warning as emitted if it was not intentionally245 # suppressed by `suppress_langchain_deprecation_warning()`.246 warned = not _SUPPRESSING_LANGCHAIN_DEPRECATION_WARNING.get()247 return await wrapped(*args, **kwargs)248249 _package = _package or obj.__module__.split(".")[0].replace("_", "-")250251 if isinstance(obj, type):252 if not _obj_type:253 _obj_type = "class"254 wrapped = obj.__init__ # type: ignore[misc]255 _name = _name or obj.__qualname__256 old_doc = obj.__doc__257258 def finalize(_: Callable[..., Any], new_doc: str, /) -> T:259 """Finalize the deprecation of a class."""260 # Can't set new_doc on some extension objects.261 with contextlib.suppress(AttributeError):262 obj.__doc__ = new_doc263264 def warn_if_direct_instance(265 self: Any, *args: Any, **kwargs: Any266 ) -> Any:267 """Warn that the class is in beta."""268 nonlocal warned269 if not warned and type(self) is obj and not is_caller_internal():270 emit_warning()271 # Only mark the warning as emitted if it was not intentionally272 # suppressed by `suppress_langchain_deprecation_warning()`.273 warned = not _SUPPRESSING_LANGCHAIN_DEPRECATION_WARNING.get()274 return wrapped(self, *args, **kwargs)275276 obj.__init__ = functools.wraps(obj.__init__)( # type: ignore[misc]277 warn_if_direct_instance278 )279 # Set __deprecated__ for PEP 702 (IDE/type checker support)280 obj.__deprecated__ = _build_deprecation_message( # type: ignore[attr-defined]281 alternative=alternative,282 alternative_import=alternative_import,283 )284 return obj285286 elif _is_pydantic_v1_field_info(obj):287 wrapped = None288 if not _obj_type:289 _obj_type = "attribute"290 if not _name:291 msg = f"Field {obj} must have a name to be deprecated."292 raise ValueError(msg)293 old_doc = obj.description294295 def finalize(_: Callable[..., Any], new_doc: str, /) -> T:296 from pydantic.v1.fields import FieldInfo as FieldInfoV1 # noqa: PLC0415297298 return cast(299 "T",300 FieldInfoV1(301 default=obj.default,302 default_factory=obj.default_factory,303 description=new_doc,304 alias=obj.alias,305 exclude=obj.exclude,306 ),307 )308309 elif isinstance(obj, FieldInfo):310 wrapped = None311 if not _obj_type:312 _obj_type = "attribute"313 if not _name:314 msg = f"Field {obj} must have a name to be deprecated."315 raise ValueError(msg)316 old_doc = obj.description317318 def finalize(_: Callable[..., Any], new_doc: str, /) -> T:319 return cast(320 "T",321 FieldInfo(322 default=obj.default,323 default_factory=obj.default_factory,324 description=new_doc,325 alias=obj.alias,326 exclude=obj.exclude,327 ),328 )329330 elif isinstance(obj, property):331 if not _obj_type:332 _obj_type = "attribute"333 wrapped = None334 _name = _name or cast("type | Callable", obj.fget).__qualname__335 old_doc = obj.__doc__336337 class _DeprecatedProperty(property):338 """A deprecated property."""339340 def __init__(341 self,342 fget: Callable[[Any], Any] | None = None,343 fset: Callable[[Any, Any], None] | None = None,344 fdel: Callable[[Any], None] | None = None,345 doc: str | None = None,346 ) -> None:347 super().__init__(fget, fset, fdel, doc)348 self.__orig_fget = fget349 self.__orig_fset = fset350 self.__orig_fdel = fdel351352 def __get__(self, instance: Any, owner: type | None = None) -> Any:353 if instance is not None or owner is not None:354 emit_warning()355 if self.fget is None:356 return None357 return self.fget(instance)358359 def __set__(self, instance: Any, value: Any) -> None:360 if instance is not None:361 emit_warning()362 if self.fset is not None:363 self.fset(instance, value)364365 def __delete__(self, instance: Any) -> None:366 if instance is not None:367 emit_warning()368 if self.fdel is not None:369 self.fdel(instance)370371 def __set_name__(self, owner: type | None, set_name: str) -> None:372 nonlocal _name373 if _name == "<lambda>":374 _name = set_name375376 def finalize(_: Callable[..., Any], new_doc: str, /) -> T:377 """Finalize the property."""378 prop = _DeprecatedProperty(379 fget=obj.fget, fset=obj.fset, fdel=obj.fdel, doc=new_doc380 )381 # Set __deprecated__ for PEP 702 (IDE/type checker support)382 prop.__deprecated__ = _build_deprecation_message( # type: ignore[attr-defined]383 alternative=alternative,384 alternative_import=alternative_import,385 )386 return cast("T", prop)387388 else:389 _name = _name or cast("type | Callable", obj).__qualname__390 if not _obj_type:391 # edge case: when a function is within another function392 # within a test, this will call it a "method" not a "function"393 _obj_type = "function" if "." not in _name else "method"394 wrapped = obj395 old_doc = wrapped.__doc__396397 def finalize(wrapper: Callable[..., Any], new_doc: str, /) -> T:398 """Wrap the wrapped function using the wrapper and update the docstring.399400 Args:401 wrapper: The wrapper function.402 new_doc: The new docstring.403404 Returns:405 The wrapped function.406 """407 wrapper = functools.wraps(wrapped)(wrapper)408 wrapper.__doc__ = new_doc409 # Set __deprecated__ for PEP 702 (IDE/type checker support)410 wrapper.__deprecated__ = _build_deprecation_message( # type: ignore[attr-defined]411 alternative=alternative,412 alternative_import=alternative_import,413 )414 return cast("T", wrapper)415416 old_doc = inspect.cleandoc(old_doc or "").strip("\n")417418 # old_doc can be None419 if not old_doc:420 old_doc = ""421422 # Modify the docstring to include a deprecation notice.423 if (424 _alternative425 and _alternative.rsplit(".", maxsplit=1)[-1].lower()426 == _alternative.rsplit(".", maxsplit=1)[-1]427 ) or _alternative:428 _alternative = f"`{_alternative}`"429430 if (431 _alternative_import432 and _alternative_import.rsplit(".", maxsplit=1)[-1].lower()433 == _alternative_import.rsplit(".", maxsplit=1)[-1]434 ) or _alternative_import:435 _alternative_import = f"`{_alternative_import}`"436437 components = [438 _message,439 f"Use {_alternative} instead." if _alternative else "",440 f"Use {_alternative_import} instead." if _alternative_import else "",441 _addendum,442 ]443 details = " ".join([component.strip() for component in components if component])444 package = _package or (445 _name.split(".")[0].replace("_", "-") if "." in _name else None446 )447 if removal:448 if removal.startswith("1.") and package and package.startswith("langchain"):449 removal_str = f"It will not be removed until {package}=={removal}."450 else:451 removal_str = f"It will be removed in {package}=={removal}."452 else:453 removal_str = ""454 new_doc = f"""\455!!! deprecated "{since} {details} {removal_str}"456457{old_doc}\458"""459460 if inspect.iscoroutinefunction(obj):461 return finalize(awarning_emitting_wrapper, new_doc)462 return finalize(warning_emitting_wrapper, new_doc)463464 return deprecate465466467@contextlib.contextmanager468def suppress_langchain_deprecation_warning() -> Generator[None, None, None]:469 """Context manager to suppress `LangChainDeprecationWarning`."""470 token = _SUPPRESSING_LANGCHAIN_DEPRECATION_WARNING.set(True)471 try:472 with warnings.catch_warnings():473 warnings.simplefilter("ignore", LangChainDeprecationWarning)474 warnings.simplefilter("ignore", LangChainPendingDeprecationWarning)475 yield476 finally:477 _SUPPRESSING_LANGCHAIN_DEPRECATION_WARNING.reset(token)478479480def warn_deprecated(481 since: str,482 *,483 message: str = "",484 name: str = "",485 alternative: str = "",486 alternative_import: str = "",487 pending: bool = False,488 obj_type: str = "",489 addendum: str = "",490 removal: str = "",491 package: str = "",492) -> None:493 """Display a standardized deprecation.494495 Args:496 since: The release at which this API became deprecated.497 message: Override the default deprecation message.498499 The `%(since)s`, `%(name)s`, `%(alternative)s`, `%(obj_type)s`,500 `%(addendum)s`, and `%(removal)s` format specifiers will be replaced by the501 values of the respective arguments passed to this function.502 name: The name of the deprecated object.503 alternative: An alternative API that the user may use in place of the504 deprecated API.505506 The deprecation warning will tell the user about this alternative if507 provided.508 alternative_import: An alternative import that the user may use instead.509 pending: If `True`, uses a `PendingDeprecationWarning` instead of a510 `DeprecationWarning`.511512 Cannot be used together with removal.513 obj_type: The object type being deprecated.514 addendum: Additional text appended directly to the final message.515 removal: The expected removal version.516517 With the default (an empty string), no removal version is shown in the518 warning message.519520 Cannot be used together with pending.521 package: The package of the deprecated object.522 """523 if not pending and removal:524 removal = f"in {removal}"525526 if not message:527 message = ""528 package_ = (529 package or name.split(".", maxsplit=1)[0].replace("_", "-")530 if "." in name531 else "LangChain"532 )533534 if obj_type:535 message += f"The {obj_type} `{name}`"536 else:537 message += f"`{name}`"538539 if pending:540 message += " will be deprecated in a future version"541 else:542 message += f" was deprecated in {package_} {since}"543544 if removal:545 message += f" and will be removed {removal}"546547 if alternative_import:548 alt_package = alternative_import.split(".", maxsplit=1)[0].replace("_", "-")549 if alt_package == package_:550 message += f". Use {alternative_import} instead."551 else:552 alt_module, alt_name = alternative_import.rsplit(".", 1)553 message += (554 f". An updated version of the {obj_type} exists in the "555 f"{alt_package} package and should be used instead. To use it run "556 f"`pip install -U {alt_package}` and import as "557 f"`from {alt_module} import {alt_name}`."558 )559 elif alternative:560 message += f". Use {alternative} instead."561562 if addendum:563 message += f" {addendum}"564565 warning_cls = (566 LangChainPendingDeprecationWarning if pending else LangChainDeprecationWarning567 )568 warning = warning_cls(message)569 warnings.warn(warning, category=LangChainDeprecationWarning, stacklevel=4)570571572def surface_langchain_deprecation_warnings() -> None:573 """Unmute LangChain deprecation warnings."""574 warnings.filterwarnings(575 "default",576 category=LangChainPendingDeprecationWarning,577 )578579 warnings.filterwarnings(580 "default",581 category=LangChainDeprecationWarning,582 )583584585_P = ParamSpec("_P")586_R = TypeVar("_R")587588589def rename_parameter(590 *,591 since: str,592 removal: str,593 old: str,594 new: str,595) -> Callable[[Callable[_P, _R]], Callable[_P, _R]]:596 """Decorator indicating that parameter *old* of *func* is renamed to *new*.597598 The actual implementation of *func* should use *new*, not *old*. If *old* is passed599 to *func*, a `DeprecationWarning` is emitted, and its value is used, even if *new*600 is also passed by keyword.601602 Args:603 since: The version in which the parameter was renamed.604 removal: The version in which the old parameter will be removed.605 old: The old parameter name.606 new: The new parameter name.607608 Returns:609 A decorator indicating that a parameter was renamed.610611 Example:612 ```python613 @_api.rename_parameter("3.1", "bad_name", "good_name")614 def func(good_name): ...615 ```616 """617618 def decorator(f: Callable[_P, _R]) -> Callable[_P, _R]:619 @functools.wraps(f)620 def wrapper(*args: _P.args, **kwargs: _P.kwargs) -> _R:621 if new in kwargs and old in kwargs:622 msg = f"{f.__name__}() got multiple values for argument {new!r}"623 raise TypeError(msg)624 if old in kwargs:625 warn_deprecated(626 since,627 removal=removal,628 message=f"The parameter `{old}` of `{f.__name__}` was "629 f"deprecated in {since} and will be removed "630 f"in {removal} Use `{new}` instead.",631 )632 kwargs[new] = kwargs.pop(old)633 return f(*args, **kwargs)634635 return wrapper636637 return decorator
Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.