mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-14 23:21:35 +00:00
* perf(lint): skip and cache base gate passes, parallelize make lint, skip redundant prisma generate make pre-commit paid for a full second basedpyright pass over a merge-base worktree on every run even when no rule was over its ceiling, re-generated an unchanged Prisma client, and ran seven independent checks sequentially. The basedpyright and ruff strict gates now skip the base pass when head is within every limit (the same early-out type_discipline_gate already had), the basedpyright base counts are cached under the git common dir keyed by merge-base commit, pyrightconfig.json, and uv.lock, prisma generate only runs when the schema or prisma version changed, and make lint fans its checks out through a parallel sub-make after a single setup phase * fix(lint): keep the base-cache scratch file out of the prune glob The tmp+rename scratch in store_counts was named basedpyright-base-<hash>.json.tmp, which the stale-entry prune glob (basedpyright-base-*) also matches, so a concurrent lint run from another worktree sharing the same git common dir could unlink it between write_text and replace and crash the gate with FileNotFoundError. The scratch is now dot-prefixed so the glob can never see it, pid-suffixed so concurrent writers of the same entry never share a scratch, and the prune glob is restricted to committed *.json entries
112 lines
3.8 KiB
Python
112 lines
3.8 KiB
Python
import importlib.util
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
_MODULE_PATH = Path(__file__).resolve().parents[2] / "scripts" / "ruff_strict_gate.py"
|
|
_spec = importlib.util.spec_from_file_location("ruff_strict_gate", _MODULE_PATH)
|
|
gate = importlib.util.module_from_spec(_spec)
|
|
_spec.loader.exec_module(gate)
|
|
|
|
Violation = gate.Violation
|
|
|
|
|
|
def rule(name, limit):
|
|
return {name: {"limit": limit}}
|
|
|
|
|
|
def test_under_ceiling_passes():
|
|
assert gate.evaluate({"ANN001": 100}, {"ANN001": 100}, rule("ANN001", 110)) == []
|
|
|
|
|
|
def test_ceiling_is_the_limit_boundary():
|
|
budget = rule("ANN001", 110)
|
|
at = gate.evaluate({"ANN001": 110}, {"ANN001": 90}, budget)
|
|
over = gate.evaluate({"ANN001": 111}, {"ANN001": 90}, budget)
|
|
assert at == []
|
|
assert [b.rule for b in over] == ["ANN001"]
|
|
assert over[0].cap == 110
|
|
assert over[0].added == 21
|
|
|
|
|
|
def test_over_ceiling_and_change_added_fails():
|
|
breaches = gate.evaluate({"C901": 11}, {"C901": 9}, rule("C901", 10))
|
|
assert [b.rule for b in breaches] == ["C901"]
|
|
assert breaches[0].added == 2
|
|
|
|
|
|
def test_base_already_over_ceiling_change_added_nothing_is_not_blamed():
|
|
# drift safety: base is over limit, this change leaves the count where it is
|
|
assert gate.evaluate({"C901": 15}, {"C901": 15}, rule("C901", 10)) == []
|
|
|
|
|
|
def test_change_that_reduces_an_over_ceiling_rule_is_not_blamed():
|
|
# still over limit, but moving the right direction
|
|
assert gate.evaluate({"C901": 14}, {"C901": 16}, rule("C901", 10)) == []
|
|
|
|
|
|
def test_rules_are_independent():
|
|
budget = {**rule("ANN001", 150), **rule("C901", 10)}
|
|
breaches = gate.evaluate(
|
|
{"ANN001": 130, "C901": 11}, {"ANN001": 100, "C901": 10}, budget
|
|
)
|
|
assert [b.rule for b in breaches] == ["C901"] # ANN001 130 <= 150, C901 11 > 10
|
|
|
|
|
|
def test_missing_rule_counts_as_zero():
|
|
assert gate.evaluate({}, {}, rule("C901", 0)) == []
|
|
|
|
|
|
def test_update_ratchets_limit_down_by_what_the_branch_fixed_never_up():
|
|
budget = {**rule("ANN001", 150), **rule("C901", 10)}
|
|
# ANN001 fixed 20 (100 -> 80) so its limit falls 150 -> 130; C901 grew, so its
|
|
# limit holds flat at 10 (a fix must never loosen a ceiling).
|
|
current = {"ANN001": 80, "C901": 12}
|
|
base = {"ANN001": 100, "C901": 9}
|
|
assert gate.ratcheted_budget(budget, current, base) == {
|
|
"ANN001": {"limit": 130},
|
|
"C901": {"limit": 10},
|
|
}
|
|
|
|
|
|
def test_parse_changed_lines_maps_added_lines_per_file():
|
|
diff = (
|
|
"+++ b/litellm/a.py\n"
|
|
"@@ -10 +10,3 @@\n+x\n+y\n+z\n"
|
|
"+++ b/litellm/b.py\n"
|
|
"@@ -5,2 +7 @@\n+q\n"
|
|
)
|
|
changed = gate.parse_changed_lines(diff)
|
|
assert changed["litellm/a.py"] == {10, 11, 12}
|
|
assert changed["litellm/b.py"] == {7}
|
|
|
|
|
|
def test_introduced_keeps_only_violations_on_changed_lines():
|
|
violations = [
|
|
Violation("litellm/a.py", 10, "ANN001"),
|
|
Violation("litellm/a.py", 99, "C901"),
|
|
]
|
|
assert gate.introduced(violations, {"litellm/a.py": {10}}) == [
|
|
Violation("litellm/a.py", 10, "ANN001")
|
|
]
|
|
|
|
|
|
@pytest.mark.parametrize("hunk", ["@@ -1 +1 @@", "@@ -1,0 +1,2 @@"])
|
|
def test_parse_changed_lines_handles_single_and_ranged_hunks(hunk):
|
|
assert gate.parse_changed_lines(f"+++ b/litellm/a.py\n{hunk}\n")["litellm/a.py"]
|
|
|
|
|
|
def test_over_ceiling_flags_only_counts_above_the_limit():
|
|
budget = rule("C901", 10)
|
|
assert gate.over_ceiling({"C901": 10}, budget) == frozenset()
|
|
assert gate.over_ceiling({"C901": 11}, budget) == frozenset({"C901"})
|
|
assert gate.over_ceiling({}, budget) == frozenset()
|
|
|
|
|
|
def test_over_ceiling_ignores_rules_missing_from_the_budget():
|
|
assert gate.over_ceiling({"NEW99": 100}, rule("C901", 10)) == frozenset()
|
|
|
|
|
|
def test_over_ceiling_is_independent_across_rules():
|
|
budget = {**rule("ANN001", 150), **rule("C901", 10)}
|
|
assert gate.over_ceiling({"ANN001": 130, "C901": 11}, budget) == frozenset({"C901"})
|