샘플
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
컨트랙트
- 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
파일
- NOTES.md
- csx.json
- requirements.txt
- src/__init__.py
- src/precedence.py
- test/contract.py
소스
# 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.
{"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"}
ruff==0.15.22
"""Ruff configuration precedence and rule composition suite."""
"""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
"""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")