From 845d8e00256a7c767d46b036289668efdebe1c68 Mon Sep 17 00:00:00 2001 From: Eugene Yurtsev Date: Tue, 30 Apr 2024 10:30:23 -0400 Subject: [PATCH] langchain[patch]: Update handling of deprecation warnings (#21083) Chains should not be emitting deprecation warnings. --- .../langchain/langchain/_api/module_import.py | 51 ++++++++----------- libs/langchain/langchain/chains/__init__.py | 2 +- .../chat_loaders/facebook_messenger.py | 4 +- libs/langchain/langchain/graphs/__init__.py | 2 +- .../langchain/retrievers/__init__.py | 2 +- .../tests/unit_tests/_api/test_importing.py | 26 +++++++++- 6 files changed, 50 insertions(+), 37 deletions(-) diff --git a/libs/langchain/langchain/_api/module_import.py b/libs/langchain/langchain/_api/module_import.py index b352d257b9d..802602fcef0 100644 --- a/libs/langchain/langchain/_api/module_import.py +++ b/libs/langchain/langchain/_api/module_import.py @@ -1,6 +1,4 @@ import importlib -import os -import pathlib import warnings from typing import Any, Callable, Dict, Optional @@ -15,27 +13,11 @@ ALLOWED_TOP_LEVEL_PKGS = { } -HERE = pathlib.Path(__file__).parent -ROOT = HERE.parent.parent - - -def _get_current_module(path: str) -> str: - """Convert a path to a module name.""" - path_as_pathlib = pathlib.Path(os.path.abspath(path)) - relative_path = path_as_pathlib.relative_to(ROOT).with_suffix("") - posix_path = relative_path.as_posix() - norm_path = os.path.normpath(str(posix_path)) - fully_qualified_module = norm_path.replace("/", ".") - # Strip off __init__ if present - if fully_qualified_module.endswith(".__init__"): - return fully_qualified_module[:-9] - return fully_qualified_module - - def create_importer( - here: str, + package: str, *, module_lookup: Optional[Dict[str, str]] = None, + deprecated_lookups: Optional[Dict[str, str]] = None, fallback_module: Optional[str] = None, ) -> Callable[[str], Any]: """Create a function that helps retrieve objects from their new locations. @@ -43,8 +25,11 @@ def create_importer( The goal of this function is to help users transition from deprecated imports to new imports. - This function will raise warnings when the old imports are used and - suggest the new imports. + The function will raise deprecation warning on loops using + deprecated_lookups or fallback_module. + + Module lookups will import without deprecation warnings (used to speed + up imports from large namespaces like llms or chat models). This function should ideally only be used with deprecated imports not with existing imports that are valid, as in addition to raising deprecation warnings @@ -52,7 +37,7 @@ def create_importer( loss of type information, IDE support for going to definition etc). Args: - here: path of the current file. Use __file__ + package: current package. Use __package__ module_lookup: maps name of object to the module where it is defined. e.g., { @@ -60,19 +45,21 @@ def create_importer( "langchain_community.document_loaders.my_document_loader" ) } + deprecated_lookups: same as module look up, but will raise + deprecation warnings. fallback_module: module to import from if the object is not found in module_lookup or if module_lookup is not provided. Returns: A function that imports objects from the specified modules. """ - current_module = _get_current_module(here) + all_module_lookup = {**(deprecated_lookups or {}), **(module_lookup or {})} def import_by_name(name: str) -> Any: """Import stores from langchain_community.""" # If not in interactive env, raise warning. - if module_lookup and name in module_lookup: - new_module = module_lookup[name] + if all_module_lookup and name in all_module_lookup: + new_module = all_module_lookup[name] if new_module.split(".")[0] not in ALLOWED_TOP_LEVEL_PKGS: raise AssertionError( f"Importing from {new_module} is not allowed. " @@ -92,9 +79,13 @@ def create_importer( try: result = getattr(module, name) - if not is_interactive_env(): + if ( + not is_interactive_env() + and deprecated_lookups + and name in deprecated_lookups + ): warnings.warn( - f"Importing {name} from {current_module} is deprecated. " + f"Importing {name} from {package} is deprecated. " "Please replace the import with the following:\n" f"from {new_module} import {name}", category=LangChainDeprecationWarning, @@ -111,7 +102,7 @@ def create_importer( result = getattr(module, name) if not is_interactive_env(): warnings.warn( - f"Importing {name} from {current_module} is deprecated. " + f"Importing {name} from {package} is deprecated. " "Please replace the import with the following:\n" f"from {fallback_module} import {name}", category=LangChainDeprecationWarning, @@ -123,6 +114,6 @@ def create_importer( f"module {fallback_module} has no attribute {name}" ) from e - raise AttributeError(f"module {current_module} has no attribute {name}") + raise AttributeError(f"module {package} has no attribute {name}") return import_by_name diff --git a/libs/langchain/langchain/chains/__init__.py b/libs/langchain/langchain/chains/__init__.py index f1263ae7e38..18fcb25b7de 100644 --- a/libs/langchain/langchain/chains/__init__.py +++ b/libs/langchain/langchain/chains/__init__.py @@ -85,7 +85,7 @@ _module_lookup = { "TransformChain": "langchain.chains.transform", } -importer = create_importer(__file__, module_lookup=_module_lookup) +importer = create_importer(__package__, module_lookup=_module_lookup) def __getattr__(name: str) -> Any: diff --git a/libs/langchain/langchain/chat_loaders/facebook_messenger.py b/libs/langchain/langchain/chat_loaders/facebook_messenger.py index cbb9929dd68..37e2f18a3f2 100644 --- a/libs/langchain/langchain/chat_loaders/facebook_messenger.py +++ b/libs/langchain/langchain/chat_loaders/facebook_messenger.py @@ -20,8 +20,8 @@ module_lookup = { # Temporary code for backwards compatibility for deprecated imports. # This will eventually be removed. import_lookup = create_importer( - __file__, - module_lookup=module_lookup, + __package__, + deprecated_lookups=module_lookup, ) diff --git a/libs/langchain/langchain/graphs/__init__.py b/libs/langchain/langchain/graphs/__init__.py index 7ada923a6a9..2e1f418ad6a 100644 --- a/libs/langchain/langchain/graphs/__init__.py +++ b/libs/langchain/langchain/graphs/__init__.py @@ -3,7 +3,7 @@ from typing import Any from langchain._api import create_importer -importer = create_importer(__file__, fallback_module="langchain_community.graphs") +importer = create_importer(__package__, fallback_module="langchain_community.graphs") def __getattr__(name: str) -> Any: diff --git a/libs/langchain/langchain/retrievers/__init__.py b/libs/langchain/langchain/retrievers/__init__.py index 29fce7a61a8..a1ba007f365 100644 --- a/libs/langchain/langchain/retrievers/__init__.py +++ b/libs/langchain/langchain/retrievers/__init__.py @@ -35,7 +35,7 @@ from langchain.retrievers.time_weighted_retriever import ( from langchain.retrievers.web_research import WebResearchRetriever import_lookup = create_importer( - __file__, fallback_module="langchain_community.retrievers" + __package__, fallback_module="langchain_community.retrievers" ) diff --git a/libs/langchain/tests/unit_tests/_api/test_importing.py b/libs/langchain/tests/unit_tests/_api/test_importing.py index 9768cfc240a..7860df44030 100644 --- a/libs/langchain/tests/unit_tests/_api/test_importing.py +++ b/libs/langchain/tests/unit_tests/_api/test_importing.py @@ -1,12 +1,34 @@ from langchain._api.module_import import create_importer -def test_importing_module() -> None: +def test_import_from_non_deprecated_path() -> None: """Test importing all modules in langchain.""" module_lookup = { "Document": "langchain_core.documents", } - lookup = create_importer(__file__, module_lookup=module_lookup) + lookup = create_importer(__package__, module_lookup=module_lookup) + imported_doc = lookup("Document") + from langchain_core.documents import Document + + assert imported_doc is Document + + +def test_import_from_deprecated_path() -> None: + """Test importing all modules in langchain.""" + module_lookup = { + "Document": "langchain_core.documents", + } + lookup = create_importer(__package__, deprecated_lookups=module_lookup) + imported_doc = lookup("Document") + + from langchain_core.documents import Document + + assert imported_doc is Document + + +def test_import_using_fallback_module() -> None: + """Test import using fallback module.""" + lookup = create_importer(__package__, fallback_module="langchain_core.documents") imported_doc = lookup("Document") from langchain_core.documents import Document