mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-08-17 21:25:46 +02:00
chore(checkpoint-postgres,checkpoint-sqlite): enable PLC0415 lint rule (#8540)
Follow-up to review on #8537: turn on ruff's `PLC0415` (`import-outside-top-level`) so deferred imports in tests stop accumulating. Scoped to `checkpoint-postgres` and `checkpoint-sqlite` rather than repo-wide, because the sweep turns up three different things and only one of them is a style problem. ### What the rule finds today ``` package tests src files checkpoint 13 10 11 checkpoint-conformance 0 10 4 checkpoint-postgres 6 0 2 checkpoint-sqlite 9 0 3 langgraph 130 23 32 prebuilt 14 3 7 cli 9 14 10 sdk-py 189 23 38 ──────────────────── 370 83 107 ``` 453 violations across 107 files, and ruff has no autofix for this rule. ### Three categories, not one **Style — hoist.** `checkpoint-sqlite/tests/test_store.py` deferred `math`, `random`, `time`, `Counter` and `defaultdict` inside methods for no reason. **Deliberate — keep, annotate.** `checkpoint-postgres/tests/test_async.py` defers behind `pytest.importorskip("langgraph.channels.delta")` because langgraph core is *not* a test dependency of that package. Hoisting would break the skip. Those get `# noqa: PLC0415` and a comment. **Redundant guard — hoist.** `checkpoint-sqlite/tests/test_conformance_delta.py` deferred imports only to get past its own `importorskip`. Imports move up; the `aiosqlite` guard stays, since that dependency genuinely can be absent. The second category is why I did not enable this everywhere in one go. Most of the 83 source-level violations look like the same pattern — optional-dependency handling and circular-import avoidance in `jsonplus.py`, `embed.py`, `encrypted.py` and friends. Blanket-enabling would mean `# noqa` on a lot of correct code, and each one wants an owner's eye rather than a mechanical pass. These two packages are clean to enforce today because both have **zero** source-level violations. ### Suggested rollout for the rest Either extend package by package as owners confirm which deferrals are intentional, or enable everywhere at once with `per-file-ignores` grandfathering the current 107 files so new code is blocked immediately and the debt burns down. Happy to do either — the second is a smaller diff but leaves a long ignore list. ### Verified `checkpoint-sqlite` 118 passed, `checkpoint-postgres` 264 passed on PG 15 and 16, `make lint` clean in both. One overlap worth flagging: `checkpoint-sqlite/tests/test_conformance_delta.py` is also touched by #8537. The change is identical in both, so it should merge cleanly either way.
This commit is contained in:
@@ -64,6 +64,7 @@ lint.select = [
|
||||
"UP", # pyupgrade
|
||||
"B", # flake8-bugbear
|
||||
"I", # isort
|
||||
"PLC0415", # import-outside-top-level
|
||||
"UP", # pyupgrade
|
||||
]
|
||||
lint.ignore = ["E501", "B008"]
|
||||
|
||||
@@ -380,13 +380,15 @@ async def test_delta_channel_chain_reconstruction(saver_name: str) -> None:
|
||||
"langgraph.channels.delta", reason="langgraph core not installed"
|
||||
)
|
||||
|
||||
from typing import Annotated
|
||||
# Deferred on purpose: langgraph core is not a test dependency of this
|
||||
# package, so these must stay behind the importorskip above.
|
||||
from typing import Annotated # noqa: PLC0415
|
||||
|
||||
from langchain_core.messages import AIMessage, HumanMessage
|
||||
from langgraph.channels.delta import DeltaChannel
|
||||
from langgraph.graph import START, StateGraph
|
||||
from langgraph.graph.message import _messages_delta_reducer
|
||||
from typing_extensions import TypedDict
|
||||
from langchain_core.messages import AIMessage, HumanMessage # noqa: PLC0415
|
||||
from langgraph.channels.delta import DeltaChannel # noqa: PLC0415
|
||||
from langgraph.graph import START, StateGraph # noqa: PLC0415
|
||||
from langgraph.graph.message import _messages_delta_reducer # noqa: PLC0415
|
||||
from typing_extensions import TypedDict # noqa: PLC0415
|
||||
|
||||
class State(TypedDict):
|
||||
messages: Annotated[list, DeltaChannel(_messages_delta_reducer)]
|
||||
|
||||
@@ -62,6 +62,7 @@ lint.select = [
|
||||
"UP", # pyupgrade
|
||||
"B", # flake8-bugbear
|
||||
"I", # isort
|
||||
"PLC0415", # import-outside-top-level
|
||||
"UP", # pyupgrade
|
||||
]
|
||||
lint.ignore = ["E501", "B008"]
|
||||
|
||||
@@ -10,14 +10,14 @@ pytest.importorskip(
|
||||
)
|
||||
pytest.importorskip("aiosqlite", reason="aiosqlite not installed")
|
||||
|
||||
from langgraph.checkpoint.conformance import validate # noqa: E402
|
||||
from langgraph.checkpoint.conformance.initializer import checkpointer_test # noqa: E402
|
||||
|
||||
from langgraph.checkpoint.sqlite.aio import AsyncSqliteSaver # noqa: E402
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_delta_channel_conformance():
|
||||
from langgraph.checkpoint.conformance import validate
|
||||
from langgraph.checkpoint.conformance.initializer import checkpointer_test
|
||||
|
||||
from langgraph.checkpoint.sqlite.aio import AsyncSqliteSaver
|
||||
|
||||
@checkpointer_test(name="AsyncSqliteSaver")
|
||||
async def sqlite_saver():
|
||||
async with AsyncSqliteSaver.from_conn_string(":memory:") as saver:
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import math
|
||||
import os
|
||||
import random
|
||||
import re
|
||||
import tempfile
|
||||
import time
|
||||
import uuid
|
||||
from collections import Counter, defaultdict
|
||||
from collections.abc import Generator, Iterable
|
||||
from contextlib import contextmanager
|
||||
from typing import Any, Literal, cast
|
||||
@@ -33,10 +37,6 @@ class CharacterEmbeddings(Embeddings):
|
||||
|
||||
def __init__(self, dims: int = 50, seed: int = 42):
|
||||
"""Initialize with embedding dimensions and random seed."""
|
||||
import math
|
||||
import random
|
||||
from collections import defaultdict
|
||||
|
||||
self._rng = random.Random(seed)
|
||||
self.dims = dims
|
||||
# Create projection vector for each character lazily
|
||||
@@ -48,9 +48,6 @@ class CharacterEmbeddings(Embeddings):
|
||||
|
||||
def _embed_one(self, text: str) -> list[float]:
|
||||
"""Embed a single text."""
|
||||
import math
|
||||
from collections import Counter
|
||||
|
||||
counts = Counter(text)
|
||||
total = sum(counts.values())
|
||||
|
||||
@@ -338,8 +335,6 @@ class TestSqliteStore:
|
||||
|
||||
# Test update
|
||||
# Small delay to ensure the updated timestamp is different
|
||||
import time
|
||||
|
||||
time.sleep(0.01)
|
||||
|
||||
updated_value = {"title": "Updated Document", "content": "Hello, Updated!"}
|
||||
|
||||
Reference in New Issue
Block a user