core: deprecation default to qualname (#20578)

This commit is contained in:
Erick Friis 2024-04-18 15:35:17 -07:00 committed by GitHub
parent 7d0a008744
commit 3425988de7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 48 additions and 32 deletions

View File

@ -44,6 +44,7 @@ def deprecated(
obj_type: str = "", obj_type: str = "",
addendum: str = "", addendum: str = "",
removal: str = "", removal: str = "",
package: str = "",
) -> Callable[[T], T]: ) -> Callable[[T], T]:
"""Decorator to mark a function, a class, or a property as deprecated. """Decorator to mark a function, a class, or a property as deprecated.
@ -109,6 +110,7 @@ def deprecated(
_alternative_import: str = alternative_import, _alternative_import: str = alternative_import,
_pending: bool = pending, _pending: bool = pending,
_addendum: str = addendum, _addendum: str = addendum,
_package: str = package,
) -> T: ) -> T:
"""Implementation of the decorator returned by `deprecated`.""" """Implementation of the decorator returned by `deprecated`."""
@ -124,6 +126,7 @@ def deprecated(
obj_type=_obj_type, obj_type=_obj_type,
addendum=_addendum, addendum=_addendum,
removal=removal, removal=removal,
package=_package,
) )
warned = False warned = False
@ -153,13 +156,13 @@ def deprecated(
emit_warning() emit_warning()
return await wrapped(*args, **kwargs) return await wrapped(*args, **kwargs)
_package = _package or obj.__module__.split(".")[0].replace("_", "-")
if isinstance(obj, type): if isinstance(obj, type):
if not _obj_type: if not _obj_type:
_obj_type = "class" _obj_type = "class"
wrapped = obj.__init__ # type: ignore wrapped = obj.__init__ # type: ignore
_name = _name or ( _name = _name or obj.__qualname__
f"{obj.__module__}.{obj.__name__}" if obj.__module__ else obj.__name__
)
old_doc = obj.__doc__ old_doc = obj.__doc__
def finalize(wrapper: Callable[..., Any], new_doc: str) -> T: def finalize(wrapper: Callable[..., Any], new_doc: str) -> T:
@ -188,7 +191,7 @@ def deprecated(
if not _obj_type: if not _obj_type:
_obj_type = "attribute" _obj_type = "attribute"
wrapped = None wrapped = None
_name = _name or obj.fget.__name__ _name = _name or obj.fget.__qualname__
old_doc = obj.__doc__ old_doc = obj.__doc__
class _deprecated_property(property): class _deprecated_property(property):
@ -227,10 +230,12 @@ def deprecated(
) )
else: else:
_name = _name or obj.__qualname__
if not _obj_type: 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 wrapped = obj
_name = _name or obj.__name__
old_doc = wrapped.__doc__ old_doc = wrapped.__doc__
def finalize(wrapper: Callable[..., Any], new_doc: str) -> T: def finalize(wrapper: Callable[..., Any], new_doc: str) -> T:
@ -261,7 +266,9 @@ def deprecated(
addendum, addendum,
] ]
details = " ".join([component.strip() for component in components if component]) 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 since_str = f"{package}=={since}" if package else since
new_doc = ( new_doc = (
f"[*Deprecated*] {old_doc}\n" f"[*Deprecated*] {old_doc}\n"
@ -299,6 +306,7 @@ def warn_deprecated(
obj_type: str = "", obj_type: str = "",
addendum: str = "", addendum: str = "",
removal: str = "", removal: str = "",
package: str = "",
) -> None: ) -> None:
"""Display a standardized deprecation. """Display a standardized deprecation.
@ -348,7 +356,11 @@ def warn_deprecated(
if not message: if not message:
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: if obj_type:
message += f"The {obj_type} `{name}`" message += f"The {obj_type} `{name}`"
@ -358,14 +370,14 @@ def warn_deprecated(
if pending: if pending:
message += " will be deprecated in a future version" message += " will be deprecated in a future version"
else: else:
message += f" was deprecated in {package} {since}" message += f" was deprecated in {_package} {since}"
if removal: if removal:
message += f" and will be removed {removal}" message += f" and will be removed {removal}"
if alternative_import: if alternative_import:
alt_package = alternative_import.split(".")[0].replace("_", "-") alt_package = alternative_import.split(".")[0].replace("_", "-")
if alt_package == package: if alt_package == _package:
message += f". Use {alternative_import} instead." message += f". Use {alternative_import} instead."
else: else:
alt_module, alt_name = alternative_import.rsplit(".", 1) alt_module, alt_name = alternative_import.rsplit(".", 1)

View File

@ -165,8 +165,8 @@ def test_deprecated_method() -> None:
assert len(warning_list) == 1 assert len(warning_list) == 1
warning = warning_list[0].message warning = warning_list[0].message
assert str(warning) == ( assert str(warning) == (
"The function `deprecated_method` was deprecated in " "The method `ClassWithDeprecatedMethods.deprecated_method` was deprecated"
"LangChain 2.0.0 and will be removed in 3.0.0" " in tests 2.0.0 and will be removed in 3.0.0"
) )
doc = obj.deprecated_method.__doc__ doc = obj.deprecated_method.__doc__
@ -188,8 +188,8 @@ async def test_deprecated_async_method() -> None:
assert len(warning_list) == 1 assert len(warning_list) == 1
warning = warning_list[0].message warning = warning_list[0].message
assert str(warning) == ( assert str(warning) == (
"The function `deprecated_async_method` was deprecated in " "The method `ClassWithDeprecatedMethods.deprecated_async_method` was "
"LangChain 2.0.0 and will be removed in 3.0.0" "deprecated in tests 2.0.0 and will be removed in 3.0.0"
) )
doc = obj.deprecated_method.__doc__ doc = obj.deprecated_method.__doc__
@ -207,8 +207,8 @@ def test_deprecated_classmethod() -> None:
assert len(warning_list) == 1 assert len(warning_list) == 1
warning = warning_list[0].message warning = warning_list[0].message
assert str(warning) == ( assert str(warning) == (
"The function `deprecated_classmethod` was deprecated in " "The method `ClassWithDeprecatedMethods.deprecated_classmethod` was "
"LangChain 2.0.0 and will be removed in 3.0.0" "deprecated in tests 2.0.0 and will be removed in 3.0.0"
) )
doc = ClassWithDeprecatedMethods.deprecated_classmethod.__doc__ doc = ClassWithDeprecatedMethods.deprecated_classmethod.__doc__
@ -228,8 +228,8 @@ def test_deprecated_staticmethod() -> None:
warning = warning_list[0].message warning = warning_list[0].message
assert str(warning) == ( assert str(warning) == (
"The function `deprecated_staticmethod` was deprecated in " "The method `ClassWithDeprecatedMethods.deprecated_staticmethod` was "
"LangChain 2.0.0 and will be removed in 3.0.0" "deprecated in tests 2.0.0 and will be removed in 3.0.0"
) )
doc = ClassWithDeprecatedMethods.deprecated_staticmethod.__doc__ doc = ClassWithDeprecatedMethods.deprecated_staticmethod.__doc__
assert isinstance(doc, str) assert isinstance(doc, str)
@ -248,8 +248,8 @@ def test_deprecated_property() -> None:
warning = warning_list[0].message warning = warning_list[0].message
assert str(warning) == ( assert str(warning) == (
"The function `deprecated_property` was deprecated in " "The method `ClassWithDeprecatedMethods.deprecated_property` was "
"LangChain 2.0.0 and will be removed in 3.0.0" "deprecated in tests 2.0.0 and will be removed in 3.0.0"
) )
doc = ClassWithDeprecatedMethods.deprecated_property.__doc__ doc = ClassWithDeprecatedMethods.deprecated_property.__doc__
assert isinstance(doc, str) assert isinstance(doc, str)
@ -280,14 +280,15 @@ def test_whole_class_deprecation() -> None:
assert len(warning_list) == 2 assert len(warning_list) == 2
warning = warning_list[0].message warning = warning_list[0].message
assert str(warning) == ( assert str(warning) == (
"The class `tests.unit_tests._api.test_deprecation.DeprecatedClass` was " "The class `test_whole_class_deprecation.<locals>.DeprecatedClass` was "
"deprecated in tests 2.0.0 and will be removed in 3.0.0" "deprecated in tests 2.0.0 and will be removed in 3.0.0"
) )
warning = warning_list[1].message warning = warning_list[1].message
assert str(warning) == ( assert str(warning) == (
"The function `deprecated_method` was deprecated in " "The method `test_whole_class_deprecation.<locals>.DeprecatedClass."
"LangChain 2.0.0 and will be removed in 3.0.0" "deprecated_method` was deprecated in "
"tests 2.0.0 and will be removed in 3.0.0"
) )
# [*Deprecated*] should be inserted only once: # [*Deprecated*] should be inserted only once:
if obj.__doc__ is not None: if obj.__doc__ is not None:
@ -335,14 +336,16 @@ def test_whole_class_inherited_deprecation() -> None:
assert len(warning_list) == 2 assert len(warning_list) == 2
warning = warning_list[0].message warning = warning_list[0].message
assert str(warning) == ( assert str(warning) == (
"The class `tests.unit_tests._api.test_deprecation.DeprecatedClass` was " "The class `test_whole_class_inherited_deprecation.<locals>."
"DeprecatedClass` was "
"deprecated in tests 2.0.0 and will be removed in 3.0.0" "deprecated in tests 2.0.0 and will be removed in 3.0.0"
) )
warning = warning_list[1].message warning = warning_list[1].message
assert str(warning) == ( assert str(warning) == (
"The function `deprecated_method` was deprecated in " "The method `test_whole_class_inherited_deprecation.<locals>."
"LangChain 2.0.0 and will be removed in 3.0.0" "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 [*Deprecated*] was inserted only once:
if obj.__doc__ is not None: if obj.__doc__ is not None:
@ -358,14 +361,15 @@ def test_whole_class_inherited_deprecation() -> None:
warning = warning_list[0].message warning = warning_list[0].message
assert str(warning) == ( assert str(warning) == (
"The class " "The class "
"`tests.unit_tests._api.test_deprecation.InheritedDeprecatedClass` " "`test_whole_class_inherited_deprecation.<locals>.InheritedDeprecatedClass`"
"was deprecated in tests 2.2.0 and will be removed in 3.2.0" " was deprecated in tests 2.2.0 and will be removed in 3.2.0"
) )
warning = warning_list[1].message warning = warning_list[1].message
assert str(warning) == ( assert str(warning) == (
"The function `deprecated_method` was deprecated in " "The method `test_whole_class_inherited_deprecation.<locals>."
"LangChain 2.2.0 and will be removed in 3.2.0" "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 [*Deprecated*] was inserted only once:
if obj.__doc__ is not None: if obj.__doc__ is not None:
@ -390,8 +394,8 @@ def test_deprecated_method_pydantic() -> None:
assert len(warning_list) == 1 assert len(warning_list) == 1
warning = warning_list[0].message warning = warning_list[0].message
assert str(warning) == ( assert str(warning) == (
"The function `deprecated_method` was deprecated in " "The method `MyModel.deprecated_method` was deprecated in "
"LangChain 2.0.0 and will be removed in 3.0.0" "tests 2.0.0 and will be removed in 3.0.0"
) )
doc = obj.deprecated_method.__doc__ doc = obj.deprecated_method.__doc__