From 1703fe236189e2b6edadbb2085e851db48c9c31e Mon Sep 17 00:00:00 2001 From: Yudhajit Sinha Date: Tue, 30 Jan 2024 09:31:11 +0530 Subject: [PATCH] core[patch]: preserve inspect.iscoroutinefunction with @beta decorator (#16440) Adjusted deprecate decorator to make sure decorated async functions are still recognized as "coroutinefunction" by inspect Addresses #16402 --------- Co-authored-by: Bagatur --- .../langchain_core/_api/beta_decorator.py | 13 ++++- .../unit_tests/_api/test_beta_decorator.py | 57 +++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/libs/core/langchain_core/_api/beta_decorator.py b/libs/core/langchain_core/_api/beta_decorator.py index 9f4dd5b181..7326dbb5ef 100644 --- a/libs/core/langchain_core/_api/beta_decorator.py +++ b/libs/core/langchain_core/_api/beta_decorator.py @@ -108,6 +108,14 @@ def beta( emit_warning() return wrapped(*args, **kwargs) + async def awarning_emitting_wrapper(*args: Any, **kwargs: Any) -> Any: + """Same as warning_emitting_wrapper, but for async functions.""" + nonlocal warned + if not warned and not is_caller_internal(): + warned = True + emit_warning() + return await wrapped(*args, **kwargs) + if isinstance(obj, type): if not _obj_type: _obj_type = "class" @@ -217,7 +225,10 @@ def beta( f" {details}" ) - return finalize(warning_emitting_wrapper, new_doc) + if inspect.iscoroutinefunction(obj): + return finalize(awarning_emitting_wrapper, new_doc) + else: + return finalize(warning_emitting_wrapper, new_doc) return beta diff --git a/libs/core/tests/unit_tests/_api/test_beta_decorator.py b/libs/core/tests/unit_tests/_api/test_beta_decorator.py index 91e198df2e..499e63745f 100644 --- a/libs/core/tests/unit_tests/_api/test_beta_decorator.py +++ b/libs/core/tests/unit_tests/_api/test_beta_decorator.py @@ -1,3 +1,4 @@ +import inspect import warnings from typing import Any, Dict @@ -57,6 +58,12 @@ def beta_function() -> str: return "This is a beta function." +@beta() +async def beta_async_function() -> str: + """original doc""" + return "This is a beta async function." + + class ClassWithBetaMethods: def __init__(self) -> None: """original doc""" @@ -67,6 +74,11 @@ class ClassWithBetaMethods: """original doc""" return "This is a beta method." + @beta() + async def beta_async_method(self) -> str: + """original doc""" + return "This is a beta async method." + @classmethod @beta() def beta_classmethod(cls) -> str: @@ -102,6 +114,28 @@ def test_beta_function() -> None: assert isinstance(doc, str) assert doc.startswith("[*Beta*] original doc") + assert not inspect.iscoroutinefunction(beta_function) + + +@pytest.mark.asyncio +async def test_beta_async_function() -> None: + """Test beta async function.""" + with warnings.catch_warnings(record=True) as warning_list: + warnings.simplefilter("always") + assert await beta_async_function() == "This is a beta async function." + assert len(warning_list) == 1 + warning = warning_list[0].message + assert str(warning) == ( + "The function `beta_async_function` is in beta. " + "It is actively being worked on, so the API may change." + ) + + doc = beta_function.__doc__ + assert isinstance(doc, str) + assert doc.startswith("[*Beta*] original doc") + + assert inspect.iscoroutinefunction(beta_async_function) + def test_beta_method() -> None: """Test beta method.""" @@ -120,6 +154,29 @@ def test_beta_method() -> None: assert isinstance(doc, str) assert doc.startswith("[*Beta*] original doc") + assert not inspect.iscoroutinefunction(obj.beta_method) + + +@pytest.mark.asyncio +async def test_beta_async_method() -> None: + """Test beta method.""" + with warnings.catch_warnings(record=True) as warning_list: + warnings.simplefilter("always") + obj = ClassWithBetaMethods() + assert await obj.beta_async_method() == "This is a beta async method." + assert len(warning_list) == 1 + warning = warning_list[0].message + assert str(warning) == ( + "The function `beta_async_method` is in beta. " + "It is actively being worked on, so the API may change." + ) + + doc = obj.beta_method.__doc__ + assert isinstance(doc, str) + assert doc.startswith("[*Beta*] original doc") + + assert inspect.iscoroutinefunction(obj.beta_async_method) + def test_beta_classmethod() -> None: """Test beta classmethod."""