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__ from langchain_cli._version import __version__
__all__ = [ __all__ = [

View File

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

View File

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

View File

@ -41,8 +41,10 @@ def create_demo_server(
def create_demo_server_configurable(): def create_demo_server_configurable():
"""Create a configurable demo server."""
return create_demo_server(config_keys=["configurable"]) return create_demo_server(config_keys=["configurable"])
def create_demo_server_chat(): def create_demo_server_chat():
"""Create a chat demo server."""
return create_demo_server(playground_type="chat") 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): class Replacements(TypedDict):
"""Replacements."""
__package_name__: str __package_name__: str
__module_name__: str __module_name__: str
__ModuleName__: 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]: def split_package(package: str) -> tuple[str, str]:
"""Split a package name into the containing package and the final name.""" """Split a package name into the containing package and the final name."""
parts = package.split(".") parts = package.split(".")

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,9 +1,12 @@
"""GitHub utilities."""
import http.client import http.client
import json import json
from typing import Optional from typing import Optional
def list_packages(*, contains: Optional[str] = None) -> list[str]: 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") conn = http.client.HTTPSConnection("api.github.com")
try: try:
headers = { headers = {

View File

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

View File

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

View File

@ -55,7 +55,7 @@ select = [
"ASYNC", # flake8-async "ASYNC", # flake8-async
"C4", # flake8-comprehensions "C4", # flake8-comprehensions
"COM", # flake8-commas "COM", # flake8-commas
"D", # pydocstyle "D1", # pydocstyle
"E", # pycodestyle error "E", # pycodestyle error
"EM", # flake8-errmsg "EM", # flake8-errmsg
"F", # pyflakes "F", # pyflakes
@ -88,18 +88,14 @@ select = [
"YTT", # flake8-2020 "YTT", # flake8-2020
] ]
ignore = [ 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 "D407", # pydocstyle: Missing-dashed-underline-after-section
"COM812", # Messes with the formatter "COM812", # Messes with the formatter
] ]
pyupgrade.keep-runtime-typing = true pyupgrade.keep-runtime-typing = true
[tool.ruff.lint.per-file-ignores]
"tests/**" = [ "D1"]
[tool.mypy] [tool.mypy]
exclude = [ exclude = [
"langchain_cli/integration_template", "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: def handle_partner(pkg: str, output: Optional[str] = None) -> None:
"""Handle partner package migrations."""
migrations = get_migrations_for_partner_package(pkg) migrations = get_migrations_for_partner_package(pkg)
# Run with python 3.9+ # Run with python 3.9+
name = pkg.removeprefix("langchain_") name = pkg.removeprefix("langchain_")