What the fix does. ruff check --fix applies I001's safe auto-fix: it rewrites the single-line import into a sorted, multi-line parenthesized block — which also eliminates E501 as a side effect (E501 itself is not auto-fixable; the reformat simply removes the overlong line):
Root cause. Adding a 4th name to a single-line from ... import (...) pushed the line past the configured 88-char limit. Full-scope ruff check src/ tests/ then reports two violations on that one line:
The local diff-mode guard passed because it only inspects the diff (e.g. git diff --check — a whitespace-only check that is blind to line length and import-block formatting; I001 is a block-level isort check that cannot be evaluated on a single hunk in isolation). Full-scope CI evaluates every file with the full rule set, so it caught it.
The fix (run from the repo root):
# 1. Preview the change without applying it (recommended first step)
ruff check --fix --diff src/ tests/
# 2. Apply auto-fixes
ruff check --fix src/ tests/
# 3. Verify the FULL scope (same command CI runs) — must exit 0
ruff check src/ tests/
# 4. If the project also uses ruff format, verify formatting too
ruff format --check src/ tests/
What the fix does. ruff check --fix applies I001's safe auto-fix: it rewrites the single-line import into a sorted, multi-line parenthesized block — which also eliminates E501 as a side effect (E501 itself is not auto-fixable; the reformat simply removes the overlong line):
# before: single line, 147 chars → I001 + E501
from some_very_long_package_name.subpackage.module_name import first_function_name, second_function_name, third_function_name, fourth_function_name
# after: multi-line, sorted, every line ≤ 88 chars → clean
from some_very_long_package_name.subpackage.module_name import (
first_function_name,
fourth_function_name,
second_function_name,
third_function_name,
)
Then: commit the fix, push, and watch CI — the full-scope ruff check src/ tests/ job should go green. Going forward, prefer writing long imports in the parenthesized form directly (or let ruff check --fix/ruff format normalize them) so local and CI lint stay in sync.
I reproduced the exact regression in a scratch repo (`pyproject.toml` with `line-length = 88`, `select = ["E", "F", "I", "W"]`) and verified the full loop: | # | Check | Result | |---|-------|--------| | 1 | Broken state: `ruff check src/ tests/` | **Failed** — exactly `I001` + `E501 (147 > 88)` on the import line; exit 1 | | 2 | Diff-mode guard `git diff --check` on the same change | **Passed** (exit 0) — explains why local lint was green | | 3 | `ruff check --fix --diff src/ tests/` | Shows preview diff (import → parenthesized block), no changes applied | | 4 | `ruff check --fix src/ tests/` | `Found 1 error (1 fixed, 0 remaining)`, exit 0 | | 5 | Full-scope `ruff check src/ tests/` (same as CI) | **All checks passed**, exit 0 | | 6 | `awk 'length>88'` over `src/ tests/` | No line exceeds 88 chars | | 7 | `python -m py_compile src/app.py tests/test_app.py` | OK | | 8 | `python -m pytest -q` | `1 passed` | | 9 | Runtime smoke test (`run()` executed) | Returns correct result | | 10 | Re-run `ruff check --fix --diff` after fixing | No further changes (idempotent) | **Edge cases tested:** - **Unused imported names (F401):** if the 4th name isn't referenced yet, `--fix` will *remove* it from the import. Safe when truly unused; for intentional re-exports add `# noqa: F401` or declare `__all__`. - **Plain long `import a.b.c as m` (not a `from`-import):** `--fix` fixes I001 but **E501 remains** — ruff cannot wrap a plain import automatically. Fix manually with `import (\n ...\n)`, a shorter alias, or `# noqa: E501`. - **Long string literal (E501):** not auto-fixable; requires manual wrapping or `# noqa: E501`. - **Scope mismatch:** full-scope lint checks *all* files with the complete rule set; any pre-existing violation in `src/` or `tests/` will also surface on CI — verify the full scope locally before pushing, not just the diff.
{"model": "deepseek-v4-flash", "problem_class": "python-lint-ci-regression-import-line", "result": "passed", "tests": 12}