diff --git a/libs/core/langchain_core/_api/deprecation.py b/libs/core/langchain_core/_api/deprecation.py index 31304665e02..1c253d99215 100644 --- a/libs/core/langchain_core/_api/deprecation.py +++ b/libs/core/langchain_core/_api/deprecation.py @@ -44,6 +44,7 @@ def deprecated( obj_type: str = "", addendum: str = "", removal: str = "", + package: str = "", ) -> Callable[[T], T]: """Decorator to mark a function, a class, or a property as deprecated. @@ -109,6 +110,7 @@ def deprecated( _alternative_import: str = alternative_import, _pending: bool = pending, _addendum: str = addendum, + _package: str = package, ) -> T: """Implementation of the decorator returned by `deprecated`.""" @@ -124,6 +126,7 @@ def deprecated( obj_type=_obj_type, addendum=_addendum, removal=removal, + package=_package, ) warned = False @@ -153,13 +156,13 @@ def deprecated( emit_warning() return await wrapped(*args, **kwargs) + _package = _package or obj.__module__.split(".")[0].replace("_", "-") + if isinstance(obj, type): if not _obj_type: _obj_type = "class" wrapped = obj.__init__ # type: ignore - _name = _name or ( - f"{obj.__module__}.{obj.__name__}" if obj.__module__ else obj.__name__ - ) + _name = _name or obj.__qualname__ old_doc = obj.__doc__ def finalize(wrapper: Callable[..., Any], new_doc: str) -> T: @@ -188,7 +191,7 @@ def deprecated( if not _obj_type: _obj_type = "attribute" wrapped = None - _name = _name or obj.fget.__name__ + _name = _name or obj.fget.__qualname__ old_doc = obj.__doc__ class _deprecated_property(property): @@ -227,10 +230,12 @@ def deprecated( ) else: + _name = _name or obj.__qualname__ if not _obj_type: - _obj_type = "function" + # edge case: when a function is within another function + # within a test, this will call it a "method" not a "function" + _obj_type = "function" if "." not in _name else "method" wrapped = obj - _name = _name or obj.__name__ old_doc = wrapped.__doc__ def finalize(wrapper: Callable[..., Any], new_doc: str) -> T: @@ -261,7 +266,9 @@ def deprecated( addendum, ] details = " ".join([component.strip() for component in components if component]) - package = _name.split(".")[0].replace("_", "-") if "." in _name else None + package = ( + _package or _name.split(".")[0].replace("_", "-") if "." in _name else None + ) since_str = f"{package}=={since}" if package else since new_doc = ( f"[*Deprecated*] {old_doc}\n" @@ -299,6 +306,7 @@ def warn_deprecated( obj_type: str = "", addendum: str = "", removal: str = "", + package: str = "", ) -> None: """Display a standardized deprecation. @@ -348,7 +356,11 @@ def warn_deprecated( if not message: message = "" - package = name.split(".")[0].replace("_", "-") if "." in name else "LangChain" + _package = ( + package or name.split(".")[0].replace("_", "-") + if "." in name + else "LangChain" + ) if obj_type: message += f"The {obj_type} `{name}`" @@ -358,14 +370,14 @@ def warn_deprecated( if pending: message += " will be deprecated in a future version" else: - message += f" was deprecated in {package} {since}" + message += f" was deprecated in {_package} {since}" if removal: message += f" and will be removed {removal}" if alternative_import: alt_package = alternative_import.split(".")[0].replace("_", "-") - if alt_package == package: + if alt_package == _package: message += f". Use {alternative_import} instead." else: alt_module, alt_name = alternative_import.rsplit(".", 1) diff --git a/libs/core/tests/unit_tests/_api/test_deprecation.py b/libs/core/tests/unit_tests/_api/test_deprecation.py index 8573d64b379..fc05c002575 100644 --- a/libs/core/tests/unit_tests/_api/test_deprecation.py +++ b/libs/core/tests/unit_tests/_api/test_deprecation.py @@ -165,8 +165,8 @@ def test_deprecated_method() -> None: assert len(warning_list) == 1 warning = warning_list[0].message assert str(warning) == ( - "The function `deprecated_method` was deprecated in " - "LangChain 2.0.0 and will be removed in 3.0.0" + "The method `ClassWithDeprecatedMethods.deprecated_method` was deprecated" + " in tests 2.0.0 and will be removed in 3.0.0" ) doc = obj.deprecated_method.__doc__ @@ -188,8 +188,8 @@ async def test_deprecated_async_method() -> None: assert len(warning_list) == 1 warning = warning_list[0].message assert str(warning) == ( - "The function `deprecated_async_method` was deprecated in " - "LangChain 2.0.0 and will be removed in 3.0.0" + "The method `ClassWithDeprecatedMethods.deprecated_async_method` was " + "deprecated in tests 2.0.0 and will be removed in 3.0.0" ) doc = obj.deprecated_method.__doc__ @@ -207,8 +207,8 @@ def test_deprecated_classmethod() -> None: assert len(warning_list) == 1 warning = warning_list[0].message assert str(warning) == ( - "The function `deprecated_classmethod` was deprecated in " - "LangChain 2.0.0 and will be removed in 3.0.0" + "The method `ClassWithDeprecatedMethods.deprecated_classmethod` was " + "deprecated in tests 2.0.0 and will be removed in 3.0.0" ) doc = ClassWithDeprecatedMethods.deprecated_classmethod.__doc__ @@ -228,8 +228,8 @@ def test_deprecated_staticmethod() -> None: warning = warning_list[0].message assert str(warning) == ( - "The function `deprecated_staticmethod` was deprecated in " - "LangChain 2.0.0 and will be removed in 3.0.0" + "The method `ClassWithDeprecatedMethods.deprecated_staticmethod` was " + "deprecated in tests 2.0.0 and will be removed in 3.0.0" ) doc = ClassWithDeprecatedMethods.deprecated_staticmethod.__doc__ assert isinstance(doc, str) @@ -248,8 +248,8 @@ def test_deprecated_property() -> None: warning = warning_list[0].message assert str(warning) == ( - "The function `deprecated_property` was deprecated in " - "LangChain 2.0.0 and will be removed in 3.0.0" + "The method `ClassWithDeprecatedMethods.deprecated_property` was " + "deprecated in tests 2.0.0 and will be removed in 3.0.0" ) doc = ClassWithDeprecatedMethods.deprecated_property.__doc__ assert isinstance(doc, str) @@ -280,14 +280,15 @@ def test_whole_class_deprecation() -> None: assert len(warning_list) == 2 warning = warning_list[0].message assert str(warning) == ( - "The class `tests.unit_tests._api.test_deprecation.DeprecatedClass` was " + "The class `test_whole_class_deprecation..DeprecatedClass` was " "deprecated in tests 2.0.0 and will be removed in 3.0.0" ) warning = warning_list[1].message assert str(warning) == ( - "The function `deprecated_method` was deprecated in " - "LangChain 2.0.0 and will be removed in 3.0.0" + "The method `test_whole_class_deprecation..DeprecatedClass." + "deprecated_method` was deprecated in " + "tests 2.0.0 and will be removed in 3.0.0" ) # [*Deprecated*] should be inserted only once: if obj.__doc__ is not None: @@ -335,14 +336,16 @@ def test_whole_class_inherited_deprecation() -> None: assert len(warning_list) == 2 warning = warning_list[0].message assert str(warning) == ( - "The class `tests.unit_tests._api.test_deprecation.DeprecatedClass` was " + "The class `test_whole_class_inherited_deprecation.." + "DeprecatedClass` was " "deprecated in tests 2.0.0 and will be removed in 3.0.0" ) warning = warning_list[1].message assert str(warning) == ( - "The function `deprecated_method` was deprecated in " - "LangChain 2.0.0 and will be removed in 3.0.0" + "The method `test_whole_class_inherited_deprecation.." + "DeprecatedClass.deprecated_method` was deprecated in " + "tests 2.0.0 and will be removed in 3.0.0" ) # if [*Deprecated*] was inserted only once: if obj.__doc__ is not None: @@ -358,14 +361,15 @@ def test_whole_class_inherited_deprecation() -> None: warning = warning_list[0].message assert str(warning) == ( "The class " - "`tests.unit_tests._api.test_deprecation.InheritedDeprecatedClass` " - "was deprecated in tests 2.2.0 and will be removed in 3.2.0" + "`test_whole_class_inherited_deprecation..InheritedDeprecatedClass`" + " was deprecated in tests 2.2.0 and will be removed in 3.2.0" ) warning = warning_list[1].message assert str(warning) == ( - "The function `deprecated_method` was deprecated in " - "LangChain 2.2.0 and will be removed in 3.2.0" + "The method `test_whole_class_inherited_deprecation.." + "InheritedDeprecatedClass.deprecated_method` was deprecated in " + "tests 2.2.0 and will be removed in 3.2.0" ) # if [*Deprecated*] was inserted only once: if obj.__doc__ is not None: @@ -390,8 +394,8 @@ def test_deprecated_method_pydantic() -> None: assert len(warning_list) == 1 warning = warning_list[0].message assert str(warning) == ( - "The function `deprecated_method` was deprecated in " - "LangChain 2.0.0 and will be removed in 3.0.0" + "The method `MyModel.deprecated_method` was deprecated in " + "tests 2.0.0 and will be removed in 3.0.0" ) doc = obj.deprecated_method.__doc__