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

<!-- Thank you for contributing to LangChain!

Please title your PR "<package>: <description>", where <package> is
whichever of langchain, community, core, experimental, etc. is being
modified.

Replace this entire comment with:
  - **Description:** a description of the change, 
  - **Issue:** the issue # it fixes if applicable,
  - **Dependencies:** any dependencies required for this change,
- **Twitter handle:** we announce bigger features on Twitter. If your PR
gets announced, and you'd like a mention, we'll gladly shout you out!

Please make sure your PR is passing linting and testing before
submitting. Run `make format`, `make lint` and `make test` from the root
of the package you've modified to check this locally.

See contribution guidelines for more information on how to write/run
tests, lint, etc: https://python.langchain.com/docs/contributing/

If you're adding a new integration, please include:
1. a test for the integration, preferably unit tests that do not rely on
network access,
2. an example notebook showing its use. It lives in
`docs/docs/integrations` directory.

If no one reviews your PR within a few days, please @-mention one of
@baskaryan, @eyurtsev, @hwchase17.
 -->

---------

Co-authored-by: Bagatur <baskaryan@gmail.com>
This commit is contained in:
Yudhajit Sinha 2024-01-30 09:31:11 +05:30 committed by GitHub
parent 52f4ad8216
commit 1703fe2361
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 69 additions and 1 deletions

View File

@ -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

View File

@ -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."""