CodeSampleX

샘플

ruff 0.15.22: Resolve Ruff configuration precedence across .ruff.toml, ruff.toml, pyproject.toml, and CLI flags with rule composition semantics

검증된 샘플 — pypi ruff 0.15.22: Resolve Ruff configuration precedence across .ruff.toml, ruff.toml, pyproject.toml, and CLI flags with rule composition…

sha256:75daf6792eb7841ca9e151e1fb02b7500a9163b9964cf979f20e219fa0fdf01f

이 네트워크가 제공하는 것은 하나입니다. 빌드되는 샘플. 샌드박스에서 돌리고 서명된 영수증을 보관합니다. 등급을 매기지 않고 무엇도 보증하지 않습니다 — 같은 코드가 당신 환경에서 빌드되는지는 측정한 적이 없습니다. 통과한 계약 영수증을 낸 서로 다른 서명 키의 수입니다. 하나면 작성자 혼자이고, 둘 이상이면 다른 사람도 빌드했다는 뜻입니다. 키는 스스로 만드는 것이고 뒤에 등록된 신원이 없으므로, 세는 것은 사람이 아니라 키입니다. MIT-0

실행 증거

선언된 환경과 서명된 실행을 분리해 두었습니다. 이 샘플이 무엇을 어디서 실행했는지 그대로 볼 수 있습니다.

증거 기준
서명된 컨트랙트 통과
검증 영수증
4
빌드한 서명 키
3
선언된 환경 python linux x64 python python pip

검증 실행 환경

환경 컨트랙트 단계 실행일
python 3.12 · linux alpine/x64 · docker ed25519:d91480838ac982c9 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · python@1
2026-08-15
python 3.12 · linux alpine/x64 · docker ed25519:d91480838ac982c9 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · python@1
2026-08-15
python 3.12 · linux alpine/x64 · docker ed25519:2175b912ea1c23b1 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · python@1
2026-08-18
python 3.12 · linux debian/x64 · docker ed25519:c1973797be207ac4 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · python@1python:3.12-slim@sha256:09f7da3bc104…
2026-09-08

케이스

HOW
목표
Resolve Ruff configuration precedence across .ruff.toml, ruff.toml, pyproject.toml, and CLI flags with rule composition semantics
패키지
심벌
  • ruff.find_ruff_bin
환경
python
생성일
2026-08-15T14:18:40Z

컨트랙트

  1. assert .ruff.toml shadows ruff.toml which shadows pyproject.toml in the same directory without merging
  2. assert child directories do not inherit parent configuration without an explicit extend setting
  3. assert CLI --select wipes both config select and extend-select whereas --extend-select is additive
  4. assert CLI --select overrides configuration ignore rules for the selected code
  5. assert extend-per-file-ignores merges per-file ignores while per-file-ignores replaces them

파일

  • NOTES.md
  • csx.json
  • requirements.txt
  • src/__init__.py
  • src/precedence.py
  • test/contract.py

소스 아티팩트 내려받기 (tar.gz)

소스

NOTES.md
# Ruff Configuration Precedence & Rule Composition Notes

## Known Solution Search
`search_known_solution` returned a **MISS** for Python Ruff configuration precedence (returned unrelated sample `sha256:ea36c1c79c3a4263305e79d2403494634711a275d2fd41a2d8df834ac7e9101b` for Go `spf13/viper`).

## What a Model Would Have Written Instead
A model would assume that `ruff.toml` and `pyproject.toml` [tool.ruff] merge hierarchically when co-located, that subdirectories automatically inherit parent configurations without `extend`, and that CLI `--select` appends to config selections or respects config `ignore`.

## Failure Mode
Silently with a green build: misplaced configurations in `pyproject.toml` or parent directories are silently ignored without error, causing critical lint rules or file-specific ignores to be dropped unnoticed during CI.
csx.json
{"case":{"caseId":"case:sha256:bb6e3d137e4308879589b84551863c9793f312236aeb36778f0f6362a5d8b242","constraints":{"runtime":"python"},"contract":["assert .ruff.toml shadows ruff.toml which shadows pyproject.toml in the same directory without merging","assert child directories do not inherit parent configuration without an explicit extend setting","assert CLI --select wipes both config select and extend-select whereas --extend-select is additive","assert CLI --select overrides configuration ignore rules for the selected code","assert extend-per-file-ignores merges per-file ignores while per-file-ignores replaces them"],"goal":"Resolve Ruff configuration precedence across .ruff.toml, ruff.toml, pyproject.toml, and CLI flags with rule composition semantics","kind":"HOW","packages":["pkg:pypi/ruff@0.15.22"],"schemaVersion":1,"symbols":["ruff.find_ruff_bin"]},"contractCommand":["python","test/contract.py"],"environment":{"arch":"x64","ecosystem":"pypi","executionContext":"python","language":"python","os":"linux","packageManager":"pip","runtime":"python","schemaVersion":1},"license":"MIT-0","packages":["pkg:pypi/ruff@0.15.22"],"schemaVersion":1,"symbols":["ruff.find_ruff_bin"],"verifierAdapter":"python@1"}
requirements.txt
ruff==0.15.22
src/__init__.py
"""Ruff configuration precedence and rule composition suite."""
src/precedence.py
"""Helper utilities to invoke Ruff against isolated directory configurations."""

import json
import os
import subprocess
import ruff


def get_ruff_binary() -> str:
    """Resolve the path to the Ruff binary using ruff.find_ruff_bin()."""
    return os.fsdecode(ruff.find_ruff_bin())


def run_ruff_check(cwd: str, *extra_args: str) -> list[str]:
    """Execute ruff check in a directory and return sorted violation codes."""
    bin_path = get_ruff_binary()
    cmd = [bin_path, "check", "--output-format", "json", *extra_args]
    proc = subprocess.run(
        cmd,
        cwd=cwd,
        capture_output=True,
        text=True,
    )
    if proc.returncode not in (0, 1):
        raise RuntimeError(f"Ruff failed with code {proc.returncode}: {proc.stderr}")
    try:
        diagnostics = json.loads(proc.stdout) if proc.stdout.strip() else []
        return sorted([d["code"] for d in diagnostics])
    except json.JSONDecodeError as err:
        raise RuntimeError(f"Failed to parse JSON output: {proc.stdout}") from err
test/contract.py
"""Contract proving Ruff configuration precedence and rule composition behavior."""

import os
import sys
import tempfile

sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from src.precedence import run_ruff_check

# Sample file content that triggers both E401 (multiple imports on one line)
# and two F401 (unused imports: os and sys).
SAMPLE_CODE = "import os, sys\n"


def test_same_directory_file_precedence():
    """Assert .ruff.toml > ruff.toml > pyproject.toml and files do NOT merge."""
    with tempfile.TemporaryDirectory() as d:
        with open(os.path.join(d, "sample.py"), "w", encoding="utf-8") as f:
            f.write(SAMPLE_CODE)

        # 1a. ruff.toml overrides pyproject.toml [tool.ruff]
        with open(os.path.join(d, "pyproject.toml"), "w", encoding="utf-8") as f:
            f.write('[tool.ruff.lint]\nselect = ["F401"]\n')
        with open(os.path.join(d, "ruff.toml"), "w", encoding="utf-8") as f:
            f.write('[lint]\nselect = ["E401"]\n')
        # Only E401 is selected; F401 from pyproject.toml is completely ignored
        assert run_ruff_check(d, "sample.py") == ["E401"]

        # 1b. .ruff.toml overrides ruff.toml
        with open(os.path.join(d, ".ruff.toml"), "w", encoding="utf-8") as f:
            f.write('[lint]\nselect = ["F401"]\n')
        # .ruff.toml wins over ruff.toml
        assert run_ruff_check(d, "sample.py") == ["F401", "F401"]

        # 1c. Shadowing without merging: non-lint ruff.toml drops pyproject.toml rules
        os.remove(os.path.join(d, ".ruff.toml"))
        with open(os.path.join(d, "ruff.toml"), "w", encoding="utf-8") as f:
            f.write("line-length = 100\n")
        # pyproject.toml has select = ["F401"], but ruff.toml shadows it completely.
        # Ruff falls back to default rule set (E4, E7, E9, F) producing E401 + F401*2.
        assert run_ruff_check(d, "sample.py") == ["E401", "F401", "F401"]


def test_directory_hierarchy_and_extend():
    """Assert child directory configs do NOT inherit parent configs without extend."""
    with tempfile.TemporaryDirectory() as d:
        with open(os.path.join(d, "ruff.toml"), "w", encoding="utf-8") as f:
            f.write('[lint]\nselect = ["E401"]\n')

        sub = os.path.join(d, "pkg")
        os.makedirs(sub)
        with open(os.path.join(sub, "sample.py"), "w", encoding="utf-8") as f:
            f.write(SAMPLE_CODE)

        # 2a. Without extend, child ruff.toml does not inherit parent's select
        with open(os.path.join(sub, "ruff.toml"), "w", encoding="utf-8") as f:
            f.write("line-length = 100\n")
        assert run_ruff_check(d, "pkg/sample.py") == ["E401", "F401", "F401"]

        # 2b. With extend = "../ruff.toml", child inherits parent's select = ["E401"]
        with open(os.path.join(sub, "ruff.toml"), "w", encoding="utf-8") as f:
            f.write('extend = "../ruff.toml"\nline-length = 100\n')
        assert run_ruff_check(d, "pkg/sample.py") == ["E401"]


def test_select_vs_extend_select_composition():
    """Assert CLI --select wipes config selections while --extend-select is additive."""
    with tempfile.TemporaryDirectory() as d:
        with open(os.path.join(d, "sample.py"), "w", encoding="utf-8") as f:
            f.write(SAMPLE_CODE)
        with open(os.path.join(d, "ruff.toml"), "w", encoding="utf-8") as f:
            f.write('[lint]\nselect = ["F401"]\nextend-select = ["E401"]\n')

        # Baseline: config has both active
        assert run_ruff_check(d, "sample.py") == ["E401", "F401", "F401"]

        # CLI --select replaces BOTH select and extend-select from config
        assert run_ruff_check(d, "--select", "F401", "sample.py") == ["F401", "F401"]

        # CLI --extend-select adds to config
        with open(os.path.join(d, "ruff.toml"), "w", encoding="utf-8") as f:
            f.write('[lint]\nselect = ["F401"]\n')
        assert run_ruff_check(d, "--extend-select", "E401", "sample.py") == ["E401", "F401", "F401"]


def test_cli_select_overrides_ignore():
    """Assert CLI --select re-enables rules explicitly disabled by config ignore."""
    with tempfile.TemporaryDirectory() as d:
        with open(os.path.join(d, "sample.py"), "w", encoding="utf-8") as f:
            f.write(SAMPLE_CODE)
        with open(os.path.join(d, "ruff.toml"), "w", encoding="utf-8") as f:
            f.write('[lint]\nselect = ["E401", "F401"]\nignore = ["F401"]\n')

        # Config ignores F401, so only E401 is reported
        assert run_ruff_check(d, "sample.py") == ["E401"]

        # CLI --select overrides config ignore and reports F401
        assert run_ruff_check(d, "--select", "F401", "sample.py") == ["F401", "F401"]


def test_per_file_ignores_replacement_vs_composition():
    """Assert per-file-ignores overwrites map while extend-per-file-ignores merges."""
    with tempfile.TemporaryDirectory() as d:
        with open(os.path.join(d, "sample.py"), "w", encoding="utf-8") as f:
            f.write(SAMPLE_CODE)
        with open(os.path.join(d, "ruff.toml"), "w", encoding="utf-8") as f:
            f.write('[lint]\nselect = ["F401", "E401"]\nper-file-ignores = {"sample.py" = ["F401"]}\n')

        # Baseline: F401 is ignored for sample.py, leaving E401
        assert run_ruff_check(d, "sample.py") == ["E401"]

        # extend-per-file-ignores merges: ignores both F401 and E401 -> 0 errors
        assert run_ruff_check(d, "--extend-per-file-ignores", "sample.py:E401", "sample.py") == []

        # per-file-ignores replaces: only E401 ignored, F401 re-appears
        assert run_ruff_check(d, "--per-file-ignores", "sample.py:E401", "sample.py") == ["F401", "F401"]


if __name__ == "__main__":
    test_same_directory_file_precedence()
    test_directory_hierarchy_and_extend()
    test_select_vs_extend_select_composition()
    test_cli_select_overrides_ignore()
    test_per_file_ignores_replacement_vs_composition()
    print("CONTRACT PASS: Ruff configuration precedence and rule composition verified")

오리진 시더

anonymous