From 1d2aa19aeeca94f21b01c1f3ada6eb5e2defca23 Mon Sep 17 00:00:00 2001 From: Leonid Ganeline Date: Sun, 18 Feb 2024 18:38:37 -0800 Subject: [PATCH] docs: Fix bug that caused the word "Beta" to appear twice in doc-strings (#17704) The current issue: Several beta descriptions in the API Reference are duplicated. For example: `[Beta] Get a context value.[Beta] Get a context value.` for the [ContextGet class](https://api.python.langchain.com/en/latest/core_api_reference.html#module-langchain_core.beta) description. NOTE: I've tested it only with a new ut! I cannot build API Reference locally :( This PR related to #17615 --- .../langchain_core/_api/beta_decorator.py | 7 +- .../unit_tests/_api/test_beta_decorator.py | 88 ++++++++++++++++--- 2 files changed, 80 insertions(+), 15 deletions(-) diff --git a/libs/core/langchain_core/_api/beta_decorator.py b/libs/core/langchain_core/_api/beta_decorator.py index 7326dbb5ef..19f5db11df 100644 --- a/libs/core/langchain_core/_api/beta_decorator.py +++ b/libs/core/langchain_core/_api/beta_decorator.py @@ -206,10 +206,9 @@ def beta( old_doc = inspect.cleandoc(old_doc or "").strip("\n") + # old_doc can be None if not old_doc: - new_doc = "[*Beta*]" - else: - new_doc = f"[*Beta*] {old_doc}" + old_doc = "" # Modify the docstring to include a beta notice. notes_header = "\nNotes\n-----" @@ -218,7 +217,7 @@ def beta( addendum, ] details = " ".join([component.strip() for component in components if component]) - new_doc += ( + new_doc = ( f"[*Beta*] {old_doc}\n" f"{notes_header if notes_header not in old_doc else ''}\n" f".. beta::\n" 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 499e63745f..caef30c251 100644 --- a/libs/core/tests/unit_tests/_api/test_beta_decorator.py +++ b/libs/core/tests/unit_tests/_api/test_beta_decorator.py @@ -112,7 +112,7 @@ def test_beta_function() -> None: doc = beta_function.__doc__ assert isinstance(doc, str) - assert doc.startswith("[*Beta*] original doc") + assert doc.startswith("[*Beta*] original doc") assert not inspect.iscoroutinefunction(beta_function) @@ -132,7 +132,7 @@ async def test_beta_async_function() -> None: doc = beta_function.__doc__ assert isinstance(doc, str) - assert doc.startswith("[*Beta*] original doc") + assert doc.startswith("[*Beta*] original doc") assert inspect.iscoroutinefunction(beta_async_function) @@ -152,7 +152,7 @@ def test_beta_method() -> None: doc = obj.beta_method.__doc__ assert isinstance(doc, str) - assert doc.startswith("[*Beta*] original doc") + assert doc.startswith("[*Beta*] original doc") assert not inspect.iscoroutinefunction(obj.beta_method) @@ -173,7 +173,7 @@ async def test_beta_async_method() -> None: doc = obj.beta_method.__doc__ assert isinstance(doc, str) - assert doc.startswith("[*Beta*] original doc") + assert doc.startswith("[*Beta*] original doc") assert inspect.iscoroutinefunction(obj.beta_async_method) @@ -192,7 +192,7 @@ def test_beta_classmethod() -> None: doc = ClassWithBetaMethods.beta_classmethod.__doc__ assert isinstance(doc, str) - assert doc.startswith("[*Beta*] original doc") + assert doc.startswith("[*Beta*] original doc") def test_beta_staticmethod() -> None: @@ -211,7 +211,7 @@ def test_beta_staticmethod() -> None: ) doc = ClassWithBetaMethods.beta_staticmethod.__doc__ assert isinstance(doc, str) - assert doc.startswith("[*Beta*] original doc") + assert doc.startswith("[*Beta*] original doc") def test_beta_property() -> None: @@ -231,13 +231,12 @@ def test_beta_property() -> None: ) doc = ClassWithBetaMethods.beta_property.__doc__ assert isinstance(doc, str) - assert doc.startswith("[*Beta*] original doc") + assert doc.startswith("[*Beta*] original doc") -def test_whole_class_deprecation() -> None: - """Test whole class deprecation.""" +def test_whole_class_beta() -> None: + """Test whole class beta status.""" - # Test whole class deprecation @beta() class BetaClass: def __init__(self) -> None: @@ -269,6 +268,73 @@ def test_whole_class_deprecation() -> None: ) +def test_whole_class_inherited_beta() -> None: + """Test whole class beta status for inherited class. + + The original version of beta decorator created duplicates with + '[*Beta*]'. + """ + + # Test whole class beta status + @beta() + class BetaClass: + @beta() + def beta_method(self) -> str: + """original doc""" + return "This is a beta method." + + @beta() + class InheritedBetaClass(BetaClass): + @beta() + def beta_method(self) -> str: + """original doc""" + return "This is a beta method 2." + + with warnings.catch_warnings(record=True) as warning_list: + warnings.simplefilter("always") + + obj = BetaClass() + assert obj.beta_method() == "This is a beta method." + + assert len(warning_list) == 2 + warning = warning_list[0].message + assert str(warning) == ( + "The class `BetaClass` is in beta. It is actively being worked on, so the " + "API may change." + ) + + warning = warning_list[1].message + assert str(warning) == ( + "The function `beta_method` is in beta. It is actively being worked on, so " + "the API may change." + ) + + with warnings.catch_warnings(record=True) as warning_list: + warnings.simplefilter("always") + + obj = InheritedBetaClass() + assert obj.beta_method() == "This is a beta method 2." + + assert len(warning_list) == 2 + warning = warning_list[0].message + assert str(warning) == ( + "The class `InheritedBetaClass` is in beta. " + "It is actively being worked on, so the " + "API may change." + ) + + warning = warning_list[1].message + assert str(warning) == ( + "The function `beta_method` is in beta. " + "It is actively being worked on, so " + "the API may change." + ) + + # if [*Beta*] was inserted only once: + if obj.__doc__ is not None: + assert obj.__doc__.count("[*Beta*]") == 1 + + # Tests with pydantic models class MyModel(BaseModel): @beta() @@ -292,4 +358,4 @@ def test_beta_method_pydantic() -> None: doc = obj.beta_method.__doc__ assert isinstance(doc, str) - assert doc.startswith("[*Beta*] original doc") + assert doc.startswith("[*Beta*] original doc")