chore(cli): add ruff rules D1 (#32350)

Co-authored-by: Mason Daugherty <mason@langchain.dev>
This commit is contained in:
Christophe Bornet 2025-08-12 00:25:30 +02:00 committed by GitHub
parent 8b663ed6c6
commit 46bbd52e81
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 67 additions and 13 deletions

View File

@ -1,3 +1,5 @@
"""LangChain CLI."""
from langchain_cli._version import __version__
__all__ = [

View File

@ -1,3 +1,5 @@
"""LangChain CLI."""
from typing import Annotated, Optional
import typer
@ -34,20 +36,21 @@ app.command(
)
def version_callback(show_version: bool) -> None: # noqa: FBT001
def _version_callback(*, show_version: bool) -> None:
if show_version:
typer.echo(f"langchain-cli {__version__}")
raise typer.Exit
@app.callback()
def main(
version: bool = typer.Option( # noqa: FBT001
def _main(
*,
version: bool = typer.Option(
False, # noqa: FBT003
"--version",
"-v",
help="Print the current CLI version.",
callback=version_callback,
callback=_version_callback,
is_eager=True,
),
) -> None:

View File

@ -1,3 +1,5 @@
"""LangChain CLI constants."""
DEFAULT_GIT_REPO = "https://github.com/langchain-ai/langchain.git"
DEFAULT_GIT_SUBDIRECTORY = "templates"
DEFAULT_GIT_REF = "master"

View File

@ -41,8 +41,10 @@ def create_demo_server(
def create_demo_server_configurable():
"""Create a configurable demo server."""
return create_demo_server(config_keys=["configurable"])
def create_demo_server_chat():
"""Create a chat demo server."""
return create_demo_server(playground_type="chat")

View File

@ -0,0 +1 @@
"""Namespaces."""

View File

@ -15,6 +15,8 @@ integration_cli = typer.Typer(no_args_is_help=True, add_completion=False)
class Replacements(TypedDict):
"""Replacements."""
__package_name__: str
__module_name__: str
__ModuleName__: str

View File

@ -0,0 +1 @@
"""Migrations."""

View File

@ -0,0 +1 @@
"""Generate migrations."""

View File

@ -1,3 +1,6 @@
"""Migration as Grit file."""
def split_package(package: str) -> tuple[str, str]:
"""Split a package name into the containing package and the final name."""
parts = package.split(".")

View File

@ -1,3 +1,5 @@
"""Generate migrations utilities."""
import ast
import inspect
import os
@ -5,6 +7,8 @@ import pathlib
from pathlib import Path
from typing import Any, Optional
from typing_extensions import override
HERE = Path(__file__).parent
# Should bring us to [root]/src
PKGS_ROOT = HERE.parent.parent.parent.parent.parent
@ -15,12 +19,15 @@ PARTNER_PKGS = PKGS_ROOT / "partners"
class ImportExtractor(ast.NodeVisitor):
"""Import extractor"""
def __init__(self, *, from_package: Optional[str] = None) -> None:
"""Extract all imports from the given code, optionally filtering by package."""
self.imports: list = []
self.package = from_package
def visit_ImportFrom(self, node) -> None: # noqa: N802
@override
def visit_ImportFrom(self, node) -> None:
if node.module and (
self.package is None or str(node.module).startswith(self.package)
):
@ -143,6 +150,7 @@ def find_imports_from_package(
*,
from_package: Optional[str] = None,
) -> list[tuple[str, str]]:
"""Find imports in code."""
# Parse the code into an AST
tree = ast.parse(code)
# Create an instance of the visitor

View File

@ -0,0 +1 @@
"""Utilities."""

View File

@ -1,3 +1,5 @@
"""Events utilities."""
import http.client
import json
from typing import Any, Optional, TypedDict
@ -8,11 +10,18 @@ WRITE_KEY = "310apTK0HUFl4AOv"
class EventDict(TypedDict):
"""Event data structure for analytics tracking.
Attributes:
event: The name of the event.
properties: Optional dictionary of event properties.
"""
event: str
properties: Optional[dict[str, Any]]
def create_events(events: list[EventDict]) -> Optional[Any]:
"""Create events."""
try:
data = {
"events": [

View File

@ -1,7 +1,10 @@
"""Find and replace text in files."""
from pathlib import Path
def find_and_replace(source: str, replacements: dict[str, str]) -> str:
"""Find and replace text in a string."""
rtn = source
# replace keys in deterministic alphabetical order
@ -13,6 +16,7 @@ def find_and_replace(source: str, replacements: dict[str, str]) -> str:
def replace_file(source: Path, replacements: dict[str, str]) -> None:
"""Replace text in a file."""
try:
content = source.read_text()
except UnicodeDecodeError:
@ -24,6 +28,7 @@ def replace_file(source: Path, replacements: dict[str, str]) -> None:
def replace_glob(parent: Path, glob: str, replacements: dict[str, str]) -> None:
"""Replace text in files matching a glob pattern."""
for file in parent.glob(glob):
if not file.is_file():
continue

View File

@ -1,3 +1,5 @@
"""Git utilities."""
import hashlib
import re
import shutil
@ -15,6 +17,8 @@ from langchain_cli.constants import (
class DependencySource(TypedDict):
"""Dependency source information."""
git: str
ref: Optional[str]
subdirectory: Optional[str]
@ -29,6 +33,7 @@ def parse_dependency_string(
branch: Optional[str],
api_path: Optional[str],
) -> DependencySource:
"""Parse a dependency string into a DependencySource."""
if dep is not None and dep.startswith("git+"):
if repo is not None or branch is not None:
msg = (
@ -121,6 +126,7 @@ def parse_dependencies(
branch: list[str],
api_path: list[str],
) -> list[DependencySource]:
"""Parse dependencies."""
num_deps = max(
len(dependencies) if dependencies is not None else 0,
len(repo),
@ -168,6 +174,7 @@ def _get_repo_path(gitstring: str, ref: Optional[str], repo_dir: Path) -> Path:
def update_repo(gitstring: str, ref: Optional[str], repo_dir: Path) -> Path:
"""Update a git repository to the specified ref."""
# see if path already saved
repo_path = _get_repo_path(gitstring, ref, repo_dir)
if repo_path.exists():

View File

@ -1,9 +1,12 @@
"""GitHub utilities."""
import http.client
import json
from typing import Optional
def list_packages(*, contains: Optional[str] = None) -> list[str]:
"""List all packages in the langchain repository templates directory."""
conn = http.client.HTTPSConnection("api.github.com")
try:
headers = {

View File

@ -1,3 +1,5 @@
"""Packages utilities."""
from pathlib import Path
from typing import Any, Optional, TypedDict
@ -5,6 +7,7 @@ from tomlkit import load
def get_package_root(cwd: Optional[Path] = None) -> Path:
"""Get package root directory."""
# traverse path for routes to host (any directory holding a pyproject.toml file)
package_root = Path.cwd() if cwd is None else cwd
visited: set[Path] = set()
@ -35,6 +38,7 @@ class LangServeExport(TypedDict):
def get_langserve_export(filepath: Path) -> LangServeExport:
"""Get LangServe export information from a pyproject.toml file."""
with open(filepath) as f:
data: dict[str, Any] = load(f)
try:

View File

@ -1,3 +1,5 @@
"""Pyproject.toml utilities."""
import contextlib
from collections.abc import Iterable
from pathlib import Path

View File

@ -55,7 +55,7 @@ select = [
"ASYNC", # flake8-async
"C4", # flake8-comprehensions
"COM", # flake8-commas
"D", # pydocstyle
"D1", # pydocstyle
"E", # pycodestyle error
"EM", # flake8-errmsg
"F", # pyflakes
@ -88,18 +88,14 @@ select = [
"YTT", # flake8-2020
]
ignore = [
"D100", # pydocstyle: Missing docstring in public module
"D101", # pydocstyle: Missing docstring in public class
"D102", # pydocstyle: Missing docstring in public method
"D103", # pydocstyle: Missing docstring in public function
"D104", # pydocstyle: Missing docstring in public package
"D105", # pydocstyle: Missing docstring in magic method
"D107", # pydocstyle: Missing docstring in __init__
"D407", # pydocstyle: Missing-dashed-underline-after-section
"COM812", # Messes with the formatter
]
pyupgrade.keep-runtime-typing = true
[tool.ruff.lint.per-file-ignores]
"tests/**" = [ "D1"]
[tool.mypy]
exclude = [
"langchain_cli/integration_template",

View File

@ -0,0 +1 @@
"""Scripts."""

View File

@ -78,6 +78,7 @@ def generic(
def handle_partner(pkg: str, output: Optional[str] = None) -> None:
"""Handle partner package migrations."""
migrations = get_migrations_for_partner_package(pkg)
# Run with python 3.9+
name = pkg.removeprefix("langchain_")