mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-08-29 03:09:45 +02:00
Follow-up to #8540, which turned on `PLC0415` (import-outside-top-level) for checkpoint-postgres and checkpoint-sqlite. This does the remaining six packages: checkpoint, checkpoint-conformance, langgraph, prebuilt, cli, sdk-py. Scoped to tests, per @sydney-runkle's call on #8540: library code is exempted with `per-file-ignores`, since it still has deferred imports nobody has reviewed and mixing that in would make this hard to read. ## What changed Function-level imports across 56 test files moved to module level. Nine could not move and carry an explicit `# noqa: PLC0415` with a reason: | File | Why it stays local | |---|---| | `libs/langgraph/tests/test_deprecation.py` (4) | the import has to run inside `pytest.warns` for the warning to be observed | | `libs/langgraph/tests/test_serde_allowlist.py` | try/except guard, skips when langchain_core is absent | | `libs/langgraph/tests/test_delta_channel_benchmark.py` | optional psycopg probe | | `libs/checkpoint/tests/test_conformance_delta.py` (3) | protected by a module-level `pytest.importorskip`; hoisting past the guard turns a skip into a collection error | That last one is the trap: an import moved above `pytest.importorskip` silently defeats the guard. I hit it locally and it turned the skip into a `ModuleNotFoundError` at collection. Every file with an `importorskip` or `except ImportError` was checked by hand for this. ## Verification `make lint` and `make test` in each of the six: | Package | Tests | |---|---| | checkpoint | 156 passed, 17 skipped | | checkpoint-conformance | 1 passed | | langgraph | 1968 passed, 4 skipped | | prebuilt | 284 passed | | cli | 336 passed | | sdk-py | 493 passed | Also confirmed the rule actually fires: a throwaway test file with a function-level import is flagged in all six packages, and the source exemption holds.
72 lines
2.3 KiB
Python
72 lines
2.3 KiB
Python
"""Shared fixtures for the integration suite.
|
|
|
|
These tests require a running langgraph-api server at `LANGGRAPH_INTEGRATION_URL`
|
|
(defaults to `http://localhost:2024`). Stand it up via the docker stack in
|
|
`libs/sdk-py/integration/`:
|
|
|
|
cd libs/sdk-py/integration && docker compose up -d
|
|
|
|
The `integration` marker is registered in `pyproject.toml` and excluded by
|
|
default in pytest's `addopts`; opt in with `pytest -m integration`.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
from collections.abc import AsyncIterator, Iterator
|
|
|
|
import httpx
|
|
import pytest
|
|
|
|
from langgraph_sdk._async.http import HttpClient
|
|
from langgraph_sdk._async.threads import ThreadsClient
|
|
from langgraph_sdk._sync.http import SyncHttpClient
|
|
from langgraph_sdk._sync.threads import SyncThreadsClient
|
|
|
|
BASE_URL = os.environ.get("LANGGRAPH_INTEGRATION_URL", "http://localhost:2024")
|
|
ASSISTANT_ID = "agent"
|
|
TOOLS_ASSISTANT_ID = "tools_agent"
|
|
DEEP_AGENT_ASSISTANT_ID = "deep_agent"
|
|
FACTORY_ASSISTANT_ID = "factory_agent"
|
|
|
|
EXPECTED_TERMINAL_ITEMS = ["streamed", "tool", "asked", "sub"]
|
|
|
|
|
|
@pytest.fixture(scope="session", autouse=True)
|
|
def _require_running_api() -> None:
|
|
"""Skip the whole integration suite if the API isn't reachable.
|
|
|
|
Autouse + session-scoped so a missing stack short-circuits before any
|
|
test runs (no per-test connection timeouts piling up).
|
|
"""
|
|
try:
|
|
resp = httpx.get(f"{BASE_URL}/ok", timeout=2.0)
|
|
resp.raise_for_status()
|
|
except Exception as err:
|
|
pytest.skip(
|
|
f"langgraph-api not reachable at {BASE_URL}: {err!r}. "
|
|
f"Bring up the stack with `cd libs/sdk-py/integration && docker compose up -d`."
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
async def async_threads() -> AsyncIterator[tuple[object, httpx.AsyncClient]]:
|
|
"""Build an async ThreadsClient. Yields `(threads, raw_httpx)` so tests can close raw."""
|
|
|
|
raw = httpx.AsyncClient(base_url=BASE_URL, timeout=30.0)
|
|
try:
|
|
yield ThreadsClient(HttpClient(raw)), raw
|
|
finally:
|
|
await raw.aclose()
|
|
|
|
|
|
@pytest.fixture
|
|
def sync_threads() -> Iterator[tuple[object, httpx.Client]]:
|
|
"""Build a sync ThreadsClient. Yields `(threads, raw_httpx)` so tests can close raw."""
|
|
|
|
raw = httpx.Client(base_url=BASE_URL, timeout=30.0)
|
|
try:
|
|
yield SyncThreadsClient(SyncHttpClient(raw)), raw
|
|
finally:
|
|
raw.close()
|