From 6aa5494a754cbbf62c46d9f27696d354f0b26e05 Mon Sep 17 00:00:00 2001 From: Sydney Runkle <54324534+sydney-runkle@users.noreply.github.com> Date: Tue, 15 Apr 2025 11:06:13 -0400 Subject: [PATCH] Fix `from langchain_core.load.load import load` import (#30843) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TL;DR: you can't optimize imports with a lazy `__getattr__` if there is a namespace conflict with a module name and an attribute name. We should avoid introducing conflicts like this in the future. This PR fixes a bug introduced by my lazy imports PR: https://github.com/langchain-ai/langchain/pull/30769. In `langchain_core`, we have utilities for loading and dumping data. Unfortunately, one of those utilities is a `load` function, located in `langchain_core/load/load.py`. To make this function more visible, we make it accessible at the top level `langchain_core.load` module via importing the function in `langchain_core/load/__init__.py`. So, either of these imports should work: ```py from langchain_core.load import load from langchain_core.load.load import load ``` As you can tell, this is already a bit confusing. You'd think that the first import would produce the module `load`, but because of the `__init__.py` shortcut, both produce the function `load`.
More on why the lazy imports PR broke this support... All was well, except when the absolute import was run first, see the last snippet: ``` >>> from langchain_core.load import load >>> load ``` ``` >>> from langchain_core.load.load import load >>> load ``` ``` >>> from langchain_core.load import load >>> load >>> from langchain_core.load.load import load >>> load ``` ``` >>> from langchain_core.load.load import load >>> load >>> from langchain_core.load import load >>> load ``` In this case, the function `load` wasn't stored in the globals cache for the `langchain_core.load` module (by the lazy import logic), so Python defers to a module import.
New `langchain` tongue twister 😜: we've created a problem for ourselves because you have to load the load function from the load file in the load module 😨. --- libs/core/langchain_core/load/__init__.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libs/core/langchain_core/load/__init__.py b/libs/core/langchain_core/load/__init__.py index 61e9c55a278..987346b30e4 100644 --- a/libs/core/langchain_core/load/__init__.py +++ b/libs/core/langchain_core/load/__init__.py @@ -5,9 +5,15 @@ from typing import TYPE_CHECKING if TYPE_CHECKING: from langchain_core.load.dump import dumpd, dumps - from langchain_core.load.load import load, loads + from langchain_core.load.load import loads from langchain_core.load.serializable import Serializable +# Unfortunately, we have to eagerly import load from langchain_core/load/load.py +# eagerly to avoid a namespace conflict. We want users to still be able to use +# `from langchain_core.load import load` to get the load function, but +# the `from langchain_core.load.load import load` absolute import should also work. +from langchain_core.load.load import load + __all__ = ["dumpd", "dumps", "load", "loads", "Serializable"] _dynamic_imports = {