core[patch]: Raise AttributeError (instead of ModuleNotFoundError) in custom __getattr__ (#30905)

Follow up to https://github.com/langchain-ai/langchain/pull/30769,
fixing the regression reported
[here](https://github.com/langchain-ai/langchain/pull/30769#issuecomment-2807483610),
thanks @krassowski for the report!

Fix inspired by https://github.com/PrefectHQ/prefect/pull/16172/files

Other changes:
* Using tuples for `__all__`, except in `output_parsers` bc of a list
namespace conflict
* Using a helper function for imports due to repeated logic across
`__init__.py` files becoming hard to maintain.

Co-authored-by: Michał Krassowski < krassowski 5832902+krassowski@users.noreply.github.com>"
This commit is contained in:
Sydney Runkle 2025-04-17 14:15:28 -04:00 committed by GitHub
parent 61d2dc011e
commit 75e50a3efd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 142 additions and 153 deletions

View File

@ -9,9 +9,10 @@ This module is only relevant for LangChain developers, not for users.
""" """
from importlib import import_module
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from langchain_core._import_utils import import_attr
if TYPE_CHECKING: if TYPE_CHECKING:
from .beta_decorator import ( from .beta_decorator import (
LangChainBetaWarning, LangChainBetaWarning,
@ -28,7 +29,7 @@ if TYPE_CHECKING:
) )
from .path import as_import_path, get_relative_path from .path import as_import_path, get_relative_path
__all__ = [ __all__ = (
"as_import_path", "as_import_path",
"beta", "beta",
"deprecated", "deprecated",
@ -40,7 +41,7 @@ __all__ = [
"suppress_langchain_deprecation_warning", "suppress_langchain_deprecation_warning",
"surface_langchain_deprecation_warnings", "surface_langchain_deprecation_warnings",
"warn_deprecated", "warn_deprecated",
] )
_dynamic_imports = { _dynamic_imports = {
"LangChainBetaWarning": "beta_decorator", "LangChainBetaWarning": "beta_decorator",
@ -59,12 +60,7 @@ _dynamic_imports = {
def __getattr__(attr_name: str) -> object: def __getattr__(attr_name: str) -> object:
module_name = _dynamic_imports.get(attr_name) module_name = _dynamic_imports.get(attr_name)
package = __spec__.parent result = import_attr(attr_name, module_name, __spec__.parent)
if module_name == "__module__" or module_name is None:
result = import_module(f".{attr_name}", package=package)
else:
module = import_module(f".{module_name}", package=package)
result = getattr(module, attr_name)
globals()[attr_name] = result globals()[attr_name] = result
return result return result

View File

@ -0,0 +1,34 @@
from importlib import import_module
from typing import Union
def import_attr(
attr_name: str,
module_name: Union[str, None],
package: Union[str, None],
) -> object:
"""Import an attribute from a module located in a package.
This utility function is used in custom __getattr__ methods within __init__.py
files to dynamically import attributes.
Args:
attr_name: The name of the attribute to import.
module_name: The name of the module to import from. If None, the attribute
is imported from the package itself.
package: The name of the package where the module is located.
"""
if module_name == "__module__" or module_name is None:
try:
result = import_module(f".{attr_name}", package=package)
except ModuleNotFoundError:
msg = f"module '{package!r}' has no attribute {attr_name!r}"
raise AttributeError(msg) from None
else:
try:
module = import_module(f".{module_name}", package=package)
except ModuleNotFoundError:
msg = f"module '{package!r}.{module_name!r}' not found"
raise ImportError(msg) from None
result = getattr(module, attr_name)
return result

View File

@ -7,9 +7,10 @@
BaseCallbackHandler --> <name>CallbackHandler # Example: AimCallbackHandler BaseCallbackHandler --> <name>CallbackHandler # Example: AimCallbackHandler
""" """
from importlib import import_module
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from langchain_core._import_utils import import_attr
if TYPE_CHECKING: if TYPE_CHECKING:
from langchain_core.callbacks.base import ( from langchain_core.callbacks.base import (
AsyncCallbackHandler, AsyncCallbackHandler,
@ -52,7 +53,7 @@ if TYPE_CHECKING:
get_usage_metadata_callback, get_usage_metadata_callback,
) )
__all__ = [ __all__ = (
"dispatch_custom_event", "dispatch_custom_event",
"adispatch_custom_event", "adispatch_custom_event",
"RetrieverManagerMixin", "RetrieverManagerMixin",
@ -87,7 +88,7 @@ __all__ = [
"FileCallbackHandler", "FileCallbackHandler",
"UsageMetadataCallbackHandler", "UsageMetadataCallbackHandler",
"get_usage_metadata_callback", "get_usage_metadata_callback",
] )
_dynamic_imports = { _dynamic_imports = {
"AsyncCallbackHandler": "base", "AsyncCallbackHandler": "base",
@ -129,12 +130,7 @@ _dynamic_imports = {
def __getattr__(attr_name: str) -> object: def __getattr__(attr_name: str) -> object:
module_name = _dynamic_imports.get(attr_name) module_name = _dynamic_imports.get(attr_name)
package = __spec__.parent result = import_attr(attr_name, module_name, __spec__.parent)
if module_name == "__module__" or module_name is None:
result = import_module(f".{attr_name}", package=package)
else:
module = import_module(f".{module_name}", package=package)
result = getattr(module, attr_name)
globals()[attr_name] = result globals()[attr_name] = result
return result return result

View File

@ -1,21 +1,22 @@
"""Document loaders.""" """Document loaders."""
from importlib import import_module
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from langchain_core._import_utils import import_attr
if TYPE_CHECKING: if TYPE_CHECKING:
from langchain_core.document_loaders.base import BaseBlobParser, BaseLoader from langchain_core.document_loaders.base import BaseBlobParser, BaseLoader
from langchain_core.document_loaders.blob_loaders import Blob, BlobLoader, PathLike from langchain_core.document_loaders.blob_loaders import Blob, BlobLoader, PathLike
from langchain_core.document_loaders.langsmith import LangSmithLoader from langchain_core.document_loaders.langsmith import LangSmithLoader
__all__ = [ __all__ = (
"BaseBlobParser", "BaseBlobParser",
"BaseLoader", "BaseLoader",
"Blob", "Blob",
"BlobLoader", "BlobLoader",
"PathLike", "PathLike",
"LangSmithLoader", "LangSmithLoader",
] )
_dynamic_imports = { _dynamic_imports = {
"BaseBlobParser": "base", "BaseBlobParser": "base",
@ -29,12 +30,7 @@ _dynamic_imports = {
def __getattr__(attr_name: str) -> object: def __getattr__(attr_name: str) -> object:
module_name = _dynamic_imports.get(attr_name) module_name = _dynamic_imports.get(attr_name)
package = __spec__.parent result = import_attr(attr_name, module_name, __spec__.parent)
if module_name == "__module__" or module_name is None:
result = import_module(f".{attr_name}", package=package)
else:
module = import_module(f".{module_name}", package=package)
result = getattr(module, attr_name)
globals()[attr_name] = result globals()[attr_name] = result
return result return result

View File

@ -5,15 +5,16 @@ and their transformations.
""" """
from importlib import import_module
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from langchain_core._import_utils import import_attr
if TYPE_CHECKING: if TYPE_CHECKING:
from .base import Document from .base import Document
from .compressor import BaseDocumentCompressor from .compressor import BaseDocumentCompressor
from .transformers import BaseDocumentTransformer from .transformers import BaseDocumentTransformer
__all__ = ["Document", "BaseDocumentTransformer", "BaseDocumentCompressor"] __all__ = ("Document", "BaseDocumentTransformer", "BaseDocumentCompressor")
_dynamic_imports = { _dynamic_imports = {
"Document": "base", "Document": "base",
@ -24,12 +25,7 @@ _dynamic_imports = {
def __getattr__(attr_name: str) -> object: def __getattr__(attr_name: str) -> object:
module_name = _dynamic_imports.get(attr_name) module_name = _dynamic_imports.get(attr_name)
package = __spec__.parent result = import_attr(attr_name, module_name, __spec__.parent)
if module_name == "__module__" or module_name is None:
result = import_module(f".{attr_name}", package=package)
else:
module = import_module(f".{module_name}", package=package)
result = getattr(module, attr_name)
globals()[attr_name] = result globals()[attr_name] = result
return result return result

View File

@ -1,8 +1,9 @@
"""Embeddings.""" """Embeddings."""
from importlib import import_module
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from langchain_core._import_utils import import_attr
if TYPE_CHECKING: if TYPE_CHECKING:
from langchain_core.embeddings.embeddings import Embeddings from langchain_core.embeddings.embeddings import Embeddings
from langchain_core.embeddings.fake import ( from langchain_core.embeddings.fake import (
@ -10,7 +11,7 @@ if TYPE_CHECKING:
FakeEmbeddings, FakeEmbeddings,
) )
__all__ = ["DeterministicFakeEmbedding", "Embeddings", "FakeEmbeddings"] __all__ = ("DeterministicFakeEmbedding", "Embeddings", "FakeEmbeddings")
_dynamic_imports = { _dynamic_imports = {
"Embeddings": "embeddings", "Embeddings": "embeddings",
@ -21,12 +22,7 @@ _dynamic_imports = {
def __getattr__(attr_name: str) -> object: def __getattr__(attr_name: str) -> object:
module_name = _dynamic_imports.get(attr_name) module_name = _dynamic_imports.get(attr_name)
package = __spec__.parent result = import_attr(attr_name, module_name, __spec__.parent)
if module_name == "__module__" or module_name is None:
result = import_module(f".{attr_name}", package=package)
else:
module = import_module(f".{module_name}", package=package)
result = getattr(module, attr_name)
globals()[attr_name] = result globals()[attr_name] = result
return result return result

View File

@ -4,9 +4,10 @@
This allows us to select examples that are most relevant to the input. This allows us to select examples that are most relevant to the input.
""" """
from importlib import import_module
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from langchain_core._import_utils import import_attr
if TYPE_CHECKING: if TYPE_CHECKING:
from langchain_core.example_selectors.base import BaseExampleSelector from langchain_core.example_selectors.base import BaseExampleSelector
from langchain_core.example_selectors.length_based import ( from langchain_core.example_selectors.length_based import (
@ -18,13 +19,13 @@ if TYPE_CHECKING:
sorted_values, sorted_values,
) )
__all__ = [ __all__ = (
"BaseExampleSelector", "BaseExampleSelector",
"LengthBasedExampleSelector", "LengthBasedExampleSelector",
"MaxMarginalRelevanceExampleSelector", "MaxMarginalRelevanceExampleSelector",
"SemanticSimilarityExampleSelector", "SemanticSimilarityExampleSelector",
"sorted_values", "sorted_values",
] )
_dynamic_imports = { _dynamic_imports = {
"BaseExampleSelector": "base", "BaseExampleSelector": "base",
@ -37,12 +38,7 @@ _dynamic_imports = {
def __getattr__(attr_name: str) -> object: def __getattr__(attr_name: str) -> object:
module_name = _dynamic_imports.get(attr_name) module_name = _dynamic_imports.get(attr_name)
package = __spec__.parent result = import_attr(attr_name, module_name, __spec__.parent)
if module_name == "__module__" or module_name is None:
result = import_module(f".{attr_name}", package=package)
else:
module = import_module(f".{module_name}", package=package)
result = getattr(module, attr_name)
globals()[attr_name] = result globals()[attr_name] = result
return result return result

View File

@ -5,9 +5,10 @@ a vectorstore while avoiding duplicated content and over-writing content
if it's unchanged. if it's unchanged.
""" """
from importlib import import_module
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from langchain_core._import_utils import import_attr
if TYPE_CHECKING: if TYPE_CHECKING:
from langchain_core.indexing.api import IndexingResult, aindex, index from langchain_core.indexing.api import IndexingResult, aindex, index
from langchain_core.indexing.base import ( from langchain_core.indexing.base import (
@ -18,7 +19,7 @@ if TYPE_CHECKING:
UpsertResponse, UpsertResponse,
) )
__all__ = [ __all__ = (
"aindex", "aindex",
"DeleteResponse", "DeleteResponse",
"DocumentIndex", "DocumentIndex",
@ -27,7 +28,7 @@ __all__ = [
"InMemoryRecordManager", "InMemoryRecordManager",
"RecordManager", "RecordManager",
"UpsertResponse", "UpsertResponse",
] )
_dynamic_imports = { _dynamic_imports = {
"aindex": "api", "aindex": "api",
@ -43,12 +44,7 @@ _dynamic_imports = {
def __getattr__(attr_name: str) -> object: def __getattr__(attr_name: str) -> object:
module_name = _dynamic_imports.get(attr_name) module_name = _dynamic_imports.get(attr_name)
package = __spec__.parent result = import_attr(attr_name, module_name, __spec__.parent)
if module_name == "__module__" or module_name is None:
result = import_module(f".{attr_name}", package=package)
else:
module = import_module(f".{module_name}", package=package)
result = getattr(module, attr_name)
globals()[attr_name] = result globals()[attr_name] = result
return result return result

View File

@ -41,9 +41,10 @@ https://python.langchain.com/docs/how_to/custom_llm/
""" # noqa: E501 """ # noqa: E501
from importlib import import_module
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from langchain_core._import_utils import import_attr
if TYPE_CHECKING: if TYPE_CHECKING:
from langchain_core.language_models.base import ( from langchain_core.language_models.base import (
BaseLanguageModel, BaseLanguageModel,
@ -66,7 +67,7 @@ if TYPE_CHECKING:
) )
from langchain_core.language_models.llms import LLM, BaseLLM from langchain_core.language_models.llms import LLM, BaseLLM
__all__ = [ __all__ = (
"BaseLanguageModel", "BaseLanguageModel",
"BaseChatModel", "BaseChatModel",
"SimpleChatModel", "SimpleChatModel",
@ -83,7 +84,7 @@ __all__ = [
"FakeMessagesListChatModel", "FakeMessagesListChatModel",
"GenericFakeChatModel", "GenericFakeChatModel",
"ParrotFakeChatModel", "ParrotFakeChatModel",
] )
_dynamic_imports = { _dynamic_imports = {
"BaseLanguageModel": "base", "BaseLanguageModel": "base",
@ -107,12 +108,7 @@ _dynamic_imports = {
def __getattr__(attr_name: str) -> object: def __getattr__(attr_name: str) -> object:
module_name = _dynamic_imports.get(attr_name) module_name = _dynamic_imports.get(attr_name)
package = __spec__.parent result = import_attr(attr_name, module_name, __spec__.parent)
if module_name == "__module__" or module_name is None:
result = import_module(f".{attr_name}", package=package)
else:
module = import_module(f".{module_name}", package=package)
result = getattr(module, attr_name)
globals()[attr_name] = result globals()[attr_name] = result
return result return result

View File

@ -1,8 +1,9 @@
"""**Load** module helps with serialization and deserialization.""" """**Load** module helps with serialization and deserialization."""
from importlib import import_module
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from langchain_core._import_utils import import_attr
if TYPE_CHECKING: if TYPE_CHECKING:
from langchain_core.load.dump import dumpd, dumps from langchain_core.load.dump import dumpd, dumps
from langchain_core.load.load import loads from langchain_core.load.load import loads
@ -14,7 +15,7 @@ if TYPE_CHECKING:
# the `from langchain_core.load.load import load` absolute import should also work. # the `from langchain_core.load.load import load` absolute import should also work.
from langchain_core.load.load import load from langchain_core.load.load import load
__all__ = ["dumpd", "dumps", "load", "loads", "Serializable"] __all__ = ("dumpd", "dumps", "load", "loads", "Serializable")
_dynamic_imports = { _dynamic_imports = {
"dumpd": "dump", "dumpd": "dump",
@ -26,12 +27,7 @@ _dynamic_imports = {
def __getattr__(attr_name: str) -> object: def __getattr__(attr_name: str) -> object:
module_name = _dynamic_imports.get(attr_name) module_name = _dynamic_imports.get(attr_name)
package = __spec__.parent result = import_attr(attr_name, module_name, __spec__.parent)
if module_name == "__module__" or module_name is None:
result = import_module(f".{attr_name}", package=package)
else:
module = import_module(f".{module_name}", package=package)
result = getattr(module, attr_name)
globals()[attr_name] = result globals()[attr_name] = result
return result return result

View File

@ -15,9 +15,10 @@
""" # noqa: E501 """ # noqa: E501
from importlib import import_module
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from langchain_core._import_utils import import_attr
if TYPE_CHECKING: if TYPE_CHECKING:
from langchain_core.messages.ai import ( from langchain_core.messages.ai import (
AIMessage, AIMessage,
@ -60,7 +61,7 @@ if TYPE_CHECKING:
trim_messages, trim_messages,
) )
__all__ = [ __all__ = (
"AIMessage", "AIMessage",
"AIMessageChunk", "AIMessageChunk",
"AnyMessage", "AnyMessage",
@ -95,7 +96,7 @@ __all__ = [
"merge_message_runs", "merge_message_runs",
"trim_messages", "trim_messages",
"convert_to_openai_messages", "convert_to_openai_messages",
] )
_dynamic_imports = { _dynamic_imports = {
"AIMessage": "ai", "AIMessage": "ai",
@ -137,12 +138,7 @@ _dynamic_imports = {
def __getattr__(attr_name: str) -> object: def __getattr__(attr_name: str) -> object:
module_name = _dynamic_imports.get(attr_name) module_name = _dynamic_imports.get(attr_name)
package = __spec__.parent result = import_attr(attr_name, module_name, __spec__.parent)
if module_name == "__module__" or module_name is None:
result = import_module(f".{attr_name}", package=package)
else:
module = import_module(f".{module_name}", package=package)
result = getattr(module, attr_name)
globals()[attr_name] = result globals()[attr_name] = result
return result return result

View File

@ -13,9 +13,10 @@
Serializable, Generation, PromptValue Serializable, Generation, PromptValue
""" # noqa: E501 """ # noqa: E501
from importlib import import_module
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from langchain_core._import_utils import import_attr
if TYPE_CHECKING: if TYPE_CHECKING:
from langchain_core.output_parsers.base import ( from langchain_core.output_parsers.base import (
BaseGenerationOutputParser, BaseGenerationOutputParser,
@ -88,12 +89,7 @@ _dynamic_imports = {
def __getattr__(attr_name: str) -> object: def __getattr__(attr_name: str) -> object:
module_name = _dynamic_imports.get(attr_name) module_name = _dynamic_imports.get(attr_name)
package = __spec__.parent result = import_attr(attr_name, module_name, __spec__.parent)
if module_name == "__module__" or module_name is None:
result = import_module(f".{attr_name}", package=package)
else:
module = import_module(f".{module_name}", package=package)
result = getattr(module, attr_name)
globals()[attr_name] = result globals()[attr_name] = result
return result return result

View File

@ -21,9 +21,10 @@ in the AIMessage object, it is recommended to access it from there rather than
from the `LLMResult` object. from the `LLMResult` object.
""" """
from importlib import import_module
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from langchain_core._import_utils import import_attr
if TYPE_CHECKING: if TYPE_CHECKING:
from langchain_core.outputs.chat_generation import ( from langchain_core.outputs.chat_generation import (
ChatGeneration, ChatGeneration,
@ -34,7 +35,7 @@ if TYPE_CHECKING:
from langchain_core.outputs.llm_result import LLMResult from langchain_core.outputs.llm_result import LLMResult
from langchain_core.outputs.run_info import RunInfo from langchain_core.outputs.run_info import RunInfo
__all__ = [ __all__ = (
"ChatGeneration", "ChatGeneration",
"ChatGenerationChunk", "ChatGenerationChunk",
"ChatResult", "ChatResult",
@ -42,7 +43,7 @@ __all__ = [
"GenerationChunk", "GenerationChunk",
"LLMResult", "LLMResult",
"RunInfo", "RunInfo",
] )
_dynamic_imports = { _dynamic_imports = {
"ChatGeneration": "chat_generation", "ChatGeneration": "chat_generation",
@ -57,12 +58,7 @@ _dynamic_imports = {
def __getattr__(attr_name: str) -> object: def __getattr__(attr_name: str) -> object:
module_name = _dynamic_imports.get(attr_name) module_name = _dynamic_imports.get(attr_name)
package = __spec__.parent result = import_attr(attr_name, module_name, __spec__.parent)
if module_name == "__module__" or module_name is None:
result = import_module(f".{attr_name}", package=package)
else:
module = import_module(f".{module_name}", package=package)
result = getattr(module, attr_name)
globals()[attr_name] = result globals()[attr_name] = result
return result return result

View File

@ -25,9 +25,10 @@ from multiple components and prompt values. Prompt classes and functions make co
""" # noqa: E501 """ # noqa: E501
from importlib import import_module
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from langchain_core._import_utils import import_attr
if TYPE_CHECKING: if TYPE_CHECKING:
from langchain_core.prompts.base import ( from langchain_core.prompts.base import (
BasePromptTemplate, BasePromptTemplate,
@ -61,7 +62,7 @@ if TYPE_CHECKING:
validate_jinja2, validate_jinja2,
) )
__all__ = [ __all__ = (
"AIMessagePromptTemplate", "AIMessagePromptTemplate",
"BaseChatPromptTemplate", "BaseChatPromptTemplate",
"BasePromptTemplate", "BasePromptTemplate",
@ -83,7 +84,7 @@ __all__ = [
"get_template_variables", "get_template_variables",
"jinja2_formatter", "jinja2_formatter",
"validate_jinja2", "validate_jinja2",
] )
_dynamic_imports = { _dynamic_imports = {
"BasePromptTemplate": "base", "BasePromptTemplate": "base",
@ -112,12 +113,7 @@ _dynamic_imports = {
def __getattr__(attr_name: str) -> object: def __getattr__(attr_name: str) -> object:
module_name = _dynamic_imports.get(attr_name) module_name = _dynamic_imports.get(attr_name)
package = __spec__.parent result = import_attr(attr_name, module_name, __spec__.parent)
if module_name == "__module__" or module_name is None:
result = import_module(f".{attr_name}", package=package)
else:
module = import_module(f".{module_name}", package=package)
result = getattr(module, attr_name)
globals()[attr_name] = result globals()[attr_name] = result
return result return result

View File

@ -17,9 +17,10 @@ creating more responsive UX.
This module contains schema and implementation of LangChain Runnables primitives. This module contains schema and implementation of LangChain Runnables primitives.
""" """
from importlib import import_module
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from langchain_core._import_utils import import_attr
if TYPE_CHECKING: if TYPE_CHECKING:
from langchain_core.runnables.base import ( from langchain_core.runnables.base import (
Runnable, Runnable,
@ -58,7 +59,7 @@ if TYPE_CHECKING:
add, add,
) )
__all__ = [ __all__ = (
"chain", "chain",
"AddableDict", "AddableDict",
"ConfigurableField", "ConfigurableField",
@ -88,7 +89,7 @@ __all__ = [
"get_config_list", "get_config_list",
"aadd", "aadd",
"add", "add",
] )
_dynamic_imports = { _dynamic_imports = {
"chain": "base", "chain": "base",
@ -125,12 +126,7 @@ _dynamic_imports = {
def __getattr__(attr_name: str) -> object: def __getattr__(attr_name: str) -> object:
module_name = _dynamic_imports.get(attr_name) module_name = _dynamic_imports.get(attr_name)
package = __spec__.parent result = import_attr(attr_name, module_name, __spec__.parent)
if module_name == "__module__" or module_name is None:
result = import_module(f".{attr_name}", package=package)
else:
module = import_module(f".{module_name}", package=package)
result = getattr(module, attr_name)
globals()[attr_name] = result globals()[attr_name] = result
return result return result

View File

@ -19,9 +19,10 @@ tool for the job.
from __future__ import annotations from __future__ import annotations
from importlib import import_module
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from langchain_core._import_utils import import_attr
if TYPE_CHECKING: if TYPE_CHECKING:
from langchain_core.tools.base import ( from langchain_core.tools.base import (
FILTERED_ARGS, FILTERED_ARGS,
@ -51,7 +52,7 @@ if TYPE_CHECKING:
from langchain_core.tools.simple import Tool from langchain_core.tools.simple import Tool
from langchain_core.tools.structured import StructuredTool from langchain_core.tools.structured import StructuredTool
__all__ = [ __all__ = (
"ArgsSchema", "ArgsSchema",
"BaseTool", "BaseTool",
"BaseToolkit", "BaseToolkit",
@ -71,7 +72,7 @@ __all__ = [
"create_retriever_tool", "create_retriever_tool",
"Tool", "Tool",
"StructuredTool", "StructuredTool",
] )
_dynamic_imports = { _dynamic_imports = {
"FILTERED_ARGS": "base", "FILTERED_ARGS": "base",
@ -98,12 +99,7 @@ _dynamic_imports = {
def __getattr__(attr_name: str) -> object: def __getattr__(attr_name: str) -> object:
module_name = _dynamic_imports.get(attr_name) module_name = _dynamic_imports.get(attr_name)
package = __spec__.parent result = import_attr(attr_name, module_name, __spec__.parent)
if module_name == "__module__" or module_name is None:
result = import_module(f".{attr_name}", package=package)
else:
module = import_module(f".{module_name}", package=package)
result = getattr(module, attr_name)
globals()[attr_name] = result globals()[attr_name] = result
return result return result

View File

@ -8,9 +8,10 @@
--> <name> # Examples: LogStreamCallbackHandler --> <name> # Examples: LogStreamCallbackHandler
""" # noqa: E501 """ # noqa: E501
from importlib import import_module
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from langchain_core._import_utils import import_attr
if TYPE_CHECKING: if TYPE_CHECKING:
from langchain_core.tracers.base import BaseTracer from langchain_core.tracers.base import BaseTracer
from langchain_core.tracers.evaluation import EvaluatorCallbackHandler from langchain_core.tracers.evaluation import EvaluatorCallbackHandler
@ -23,7 +24,7 @@ if TYPE_CHECKING:
from langchain_core.tracers.schemas import Run from langchain_core.tracers.schemas import Run
from langchain_core.tracers.stdout import ConsoleCallbackHandler from langchain_core.tracers.stdout import ConsoleCallbackHandler
__all__ = [ __all__ = (
"BaseTracer", "BaseTracer",
"EvaluatorCallbackHandler", "EvaluatorCallbackHandler",
"LangChainTracer", "LangChainTracer",
@ -32,7 +33,7 @@ __all__ = [
"RunLog", "RunLog",
"RunLogPatch", "RunLogPatch",
"LogStreamCallbackHandler", "LogStreamCallbackHandler",
] )
_dynamic_imports = { _dynamic_imports = {
"BaseTracer": "base", "BaseTracer": "base",
@ -48,12 +49,7 @@ _dynamic_imports = {
def __getattr__(attr_name: str) -> object: def __getattr__(attr_name: str) -> object:
module_name = _dynamic_imports.get(attr_name) module_name = _dynamic_imports.get(attr_name)
package = __spec__.parent result = import_attr(attr_name, module_name, __spec__.parent)
if module_name == "__module__" or module_name is None:
result = import_module(f".{attr_name}", package=package)
else:
module = import_module(f".{module_name}", package=package)
result = getattr(module, attr_name)
globals()[attr_name] = result globals()[attr_name] = result
return result return result

View File

@ -3,9 +3,10 @@
These functions do not depend on any other LangChain module. These functions do not depend on any other LangChain module.
""" """
from importlib import import_module
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from langchain_core._import_utils import import_attr
if TYPE_CHECKING: if TYPE_CHECKING:
# for type checking and IDE support, we include the imports here # for type checking and IDE support, we include the imports here
# but we don't want to eagerly import them at runtime # but we don't want to eagerly import them at runtime
@ -36,7 +37,7 @@ if TYPE_CHECKING:
xor_args, xor_args,
) )
__all__ = [ __all__ = (
"build_extra_kwargs", "build_extra_kwargs",
"StrictFormatter", "StrictFormatter",
"check_package_version", "check_package_version",
@ -63,7 +64,7 @@ __all__ = [
"abatch_iterate", "abatch_iterate",
"from_env", "from_env",
"secret_from_env", "secret_from_env",
] )
_dynamic_imports = { _dynamic_imports = {
"image": "__module__", "image": "__module__",
@ -97,12 +98,7 @@ _dynamic_imports = {
def __getattr__(attr_name: str) -> object: def __getattr__(attr_name: str) -> object:
module_name = _dynamic_imports.get(attr_name) module_name = _dynamic_imports.get(attr_name)
package = __spec__.parent result = import_attr(attr_name, module_name, __spec__.parent)
if module_name == "__module__" or module_name is None:
result = import_module(f".{attr_name}", package=package)
else:
module = import_module(f".{module_name}", package=package)
result = getattr(module, attr_name)
globals()[attr_name] = result globals()[attr_name] = result
return result return result

View File

@ -1,11 +1,34 @@
"""Vector stores.""" """Vector stores."""
from typing import TYPE_CHECKING
from langchain_core._import_utils import import_attr
if TYPE_CHECKING:
from langchain_core.vectorstores.base import VST, VectorStore, VectorStoreRetriever from langchain_core.vectorstores.base import VST, VectorStore, VectorStoreRetriever
from langchain_core.vectorstores.in_memory import InMemoryVectorStore from langchain_core.vectorstores.in_memory import InMemoryVectorStore
__all__ = [ __all__ = (
"VectorStore", "VectorStore",
"VST", "VST",
"VectorStoreRetriever", "VectorStoreRetriever",
"InMemoryVectorStore", "InMemoryVectorStore",
] )
_dynamic_imports = {
"VectorStore": "base",
"VST": "base",
"VectorStoreRetriever": "base",
"InMemoryVectorStore": "in_memory",
}
def __getattr__(attr_name: str) -> object:
module_name = _dynamic_imports.get(attr_name)
result = import_attr(attr_name, module_name, __spec__.parent)
globals()[attr_name] = result
return result
def __dir__() -> list[str]:
return list(__all__)

View File

@ -3,7 +3,7 @@ from langchain_core.indexing import __all__
def test_all() -> None: def test_all() -> None:
"""Use to catch obvious breaking changes.""" """Use to catch obvious breaking changes."""
assert __all__ == sorted(__all__, key=str.lower) assert list(__all__) == sorted(__all__, key=str.lower)
assert set(__all__) == { assert set(__all__) == {
"aindex", "aindex",
"DeleteResponse", "DeleteResponse",