core[patch]: fix validation of @deprecated decorator (#22513)

This PR moves the validation of the decorator to a better place to avoid
creating bugs while deprecating code.

Prevent issues like this from arising:
https://github.com/langchain-ai/langchain/issues/22510

we should replace with a linter at some point that just does static
analysis
This commit is contained in:
Eugene Yurtsev 2024-06-14 12:52:30 -04:00 committed by GitHub
parent 181a61982f
commit 4a77a3ab19
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 32 additions and 8 deletions

View File

@ -39,7 +39,7 @@ DEFAULT_SYSTEM_PROMPT = """You are a helpful, respectful, and honest assistant."
@deprecated(
since="0.0.37",
removal="0.3",
alternative_import=("from langchain_huggingface import ChatHuggingFace"),
alternative_import="langchain_huggingface.ChatHuggingFace",
)
class ChatHuggingFace(BaseChatModel):
"""

View File

@ -33,6 +33,25 @@ class LangChainPendingDeprecationWarning(PendingDeprecationWarning):
T = TypeVar("T", bound=Union[Type, Callable[..., Any]])
def _validate_deprecation_params(
pending: bool,
removal: str,
alternative: str,
alternative_import: str,
) -> None:
"""Validate the deprecation parameters."""
if pending and removal:
raise ValueError("A pending deprecation cannot have a scheduled removal")
if alternative and alternative_import:
raise ValueError("Cannot specify both alternative and alternative_import")
if alternative_import and "." not in alternative_import:
raise ValueError(
"alternative_import must be a fully qualified module path. Got "
f" {alternative_import}"
)
def deprecated(
since: str,
*,
@ -99,6 +118,7 @@ def deprecated(
def the_function_to_deprecate():
pass
"""
_validate_deprecation_params(pending, removal, alternative, alternative_import)
def deprecate(
obj: T,
@ -337,13 +357,6 @@ def warn_deprecated(
since. Set to other Falsy values to not schedule a removal
date. Cannot be used together with pending.
"""
if pending and removal:
raise ValueError("A pending deprecation cannot have a scheduled removal")
if alternative and alternative_import:
raise ValueError("Cannot specify both alternative and alternative_import")
if alternative_import and "." not in alternative_import:
raise ValueError("alternative_import must be a fully qualified module path")
if not pending:
if not removal:
removal = f"in {removal}" if removal else "within ?? minor releases"

View File

@ -401,3 +401,14 @@ def test_deprecated_method_pydantic() -> None:
doc = obj.deprecated_method.__doc__
assert isinstance(doc, str)
assert doc.startswith("[*Deprecated*] original doc")
def test_raise_error_for_bad_decorator() -> None:
"""Verify that errors raised on init rather than on use."""
# Should not specify both `alternative` and `alternative_import`
with pytest.raises(ValueError):
@deprecated(since="2.0.0", alternative="NewClass", alternative_import="hello")
def deprecated_function() -> str:
"""original doc"""
return "This is a deprecated function."