test(langchain): use blockbuster to detect blocking calls in the async event loop (#34777)

This commit is contained in:
Christophe Bornet
2026-01-23 20:52:56 +01:00
committed by GitHub
parent eff21c75fc
commit ca9d2c0bdd
4 changed files with 31 additions and 2 deletions

View File

@@ -18,6 +18,7 @@ from pathlib import Path
from typing import TYPE_CHECKING, Annotated, Any, Literal, cast from typing import TYPE_CHECKING, Annotated, Any, Literal, cast
from langchain_core.messages import ToolMessage from langchain_core.messages import ToolMessage
from langchain_core.runnables import run_in_executor
from langchain_core.tools.base import ToolException from langchain_core.tools.base import ToolException
from langgraph.channels.untracked_value import UntrackedValue from langgraph.channels.untracked_value import UntrackedValue
from pydantic import BaseModel, model_validator from pydantic import BaseModel, model_validator
@@ -637,7 +638,7 @@ class ShellToolMiddleware(AgentMiddleware[ShellToolState, Any]):
Returns: Returns:
Shell session resources to be stored in the agent state. Shell session resources to be stored in the agent state.
""" """
return self.before_agent(state, runtime) return await run_in_executor(None, self.before_agent, state, runtime)
@override @override
def after_agent(self, state: ShellToolState, runtime: Runtime) -> None: def after_agent(self, state: ShellToolState, runtime: Runtime) -> None:

View File

@@ -56,6 +56,7 @@ test = [
"pytest-mock", "pytest-mock",
"syrupy>=4.0.2,<5.0.0", "syrupy>=4.0.2,<5.0.0",
"toml>=0.10.2,<1.0.0", "toml>=0.10.2,<1.0.0",
"blockbuster>=1.5.26,<1.6.0",
"langchain-tests", "langchain-tests",
"langchain-openai", "langchain-openai",
] ]

View File

@@ -1,10 +1,11 @@
"""Configuration for unit tests.""" """Configuration for unit tests."""
from collections.abc import Sequence from collections.abc import Iterator, Sequence
from importlib import util from importlib import util
from typing import Any from typing import Any
import pytest import pytest
from blockbuster import BlockBuster, blockbuster_ctx
from langchain_tests.conftest import CustomPersister, CustomSerializer, base_vcr_config from langchain_tests.conftest import CustomPersister, CustomSerializer, base_vcr_config
from vcr import VCR from vcr import VCR
@@ -15,6 +16,12 @@ _EXTRA_HEADERS = [
] ]
@pytest.fixture(autouse=True)
def blockbuster() -> Iterator[BlockBuster]:
with blockbuster_ctx() as bb:
yield bb
def remove_request_headers(request: Any) -> Any: def remove_request_headers(request: Any) -> Any:
"""Remove sensitive headers from the request.""" """Remove sensitive headers from the request."""
for k in request.headers: for k in request.headers:

View File

@@ -441,6 +441,18 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/00/5d/aed32636ed30a6e7f9efd6ad14e2a0b0d687ae7c8c7ec4e4a557174b895c/black-25.11.0-py3-none-any.whl", hash = "sha256:e3f562da087791e96cefcd9dda058380a442ab322a02e222add53736451f604b", size = 204918, upload-time = "2025-11-10T01:53:48.917Z" }, { url = "https://files.pythonhosted.org/packages/00/5d/aed32636ed30a6e7f9efd6ad14e2a0b0d687ae7c8c7ec4e4a557174b895c/black-25.11.0-py3-none-any.whl", hash = "sha256:e3f562da087791e96cefcd9dda058380a442ab322a02e222add53736451f604b", size = 204918, upload-time = "2025-11-10T01:53:48.917Z" },
] ]
[[package]]
name = "blockbuster"
version = "1.5.26"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "forbiddenfruit", marker = "implementation_name == 'cpython'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/55/e0/dcbab602790a576b0b94108c07e2c048e5897df7cc83722a89582d733987/blockbuster-1.5.26.tar.gz", hash = "sha256:cc3ce8c70fa852a97ee3411155f31e4ad2665cd1c6c7d2f8bb1851dab61dc629", size = 36085, upload-time = "2025-12-05T10:43:47.735Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/95/c1/84fc6811122f54b20de2e5afb312ee07a3a47a328755587d1e505475239b/blockbuster-1.5.26-py3-none-any.whl", hash = "sha256:f8e53fb2dd4b6c6ec2f04907ddbd063ca7cd1ef587d24448ef4e50e81e3a79bb", size = 13226, upload-time = "2025-12-05T10:43:48.778Z" },
]
[[package]] [[package]]
name = "boto3" name = "boto3"
version = "1.40.72" version = "1.40.72"
@@ -1000,6 +1012,12 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/39/de/576c2dab45914e08c1fc4adfa4a334da037b8b4ad4df1fdab4b56904bd07/fireworks_ai-0.16.4-py3-none-any.whl", hash = "sha256:e7592fdec64aa35f0068b8fa8277e2440ef6f0d6355e818b7220e098f7ea0ee9", size = 193771, upload-time = "2025-05-18T07:16:20.611Z" }, { url = "https://files.pythonhosted.org/packages/39/de/576c2dab45914e08c1fc4adfa4a334da037b8b4ad4df1fdab4b56904bd07/fireworks_ai-0.16.4-py3-none-any.whl", hash = "sha256:e7592fdec64aa35f0068b8fa8277e2440ef6f0d6355e818b7220e098f7ea0ee9", size = 193771, upload-time = "2025-05-18T07:16:20.611Z" },
] ]
[[package]]
name = "forbiddenfruit"
version = "0.1.4"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/e6/79/d4f20e91327c98096d605646bdc6a5ffedae820f38d378d3515c42ec5e60/forbiddenfruit-0.1.4.tar.gz", hash = "sha256:e3f7e66561a29ae129aac139a85d610dbf3dd896128187ed5454b6421f624253", size = 43756, upload-time = "2021-01-16T21:03:35.401Z" }
[[package]] [[package]]
name = "frozenlist" name = "frozenlist"
version = "1.8.0" version = "1.8.0"
@@ -1926,6 +1944,7 @@ lint = [
{ name = "ruff" }, { name = "ruff" },
] ]
test = [ test = [
{ name = "blockbuster" },
{ name = "langchain-openai" }, { name = "langchain-openai" },
{ name = "langchain-tests" }, { name = "langchain-tests" },
{ name = "pytest" }, { name = "pytest" },
@@ -1978,6 +1997,7 @@ provides-extras = ["community", "anthropic", "openai", "azure-ai", "google-verte
[package.metadata.requires-dev] [package.metadata.requires-dev]
lint = [{ name = "ruff", specifier = ">=0.14.11,<0.15.0" }] lint = [{ name = "ruff", specifier = ">=0.14.11,<0.15.0" }]
test = [ test = [
{ name = "blockbuster", specifier = ">=1.5.26,<1.6.0" },
{ name = "langchain-openai", editable = "../partners/openai" }, { name = "langchain-openai", editable = "../partners/openai" },
{ name = "langchain-tests", editable = "../standard-tests" }, { name = "langchain-tests", editable = "../standard-tests" },
{ name = "pytest", specifier = ">=8.0.0,<9.0.0" }, { name = "pytest", specifier = ">=8.0.0,<9.0.0" },