|
| 1 | +# Linting Guide |
| 2 | + |
| 3 | +This project enforces linting and formatting via CI on every pull request. The Lint workflow runs `pre-commit run --all-files`, meaning **all files** in the repo are checked, not just the ones you changed. Your PR will be blocked if any check fails. |
| 4 | + |
| 5 | +## Quick Reference |
| 6 | + |
| 7 | +Before committing and pushing, run: |
| 8 | + |
| 9 | +```bash |
| 10 | +ruff check --fix . |
| 11 | +ruff format . |
| 12 | +``` |
| 13 | + |
| 14 | +Or equivalently, run the full pre-commit suite (requires `pre-commit` installed): |
| 15 | + |
| 16 | +```bash |
| 17 | +pre-commit run --all-files |
| 18 | +``` |
| 19 | + |
| 20 | +If any hook makes changes, **stage and commit those changes** before pushing. |
| 21 | + |
| 22 | +## What CI Checks |
| 23 | + |
| 24 | +The Lint workflow (`.github/workflows/lint.yml`) runs all hooks defined in `.pre-commit-config.yaml`: |
| 25 | + |
| 26 | +| Hook | What it does | |
| 27 | +|---|---| |
| 28 | +| **ruff** (linter) | Checks for pyflakes, pycodestyle, isort, bugbear, implicit string concat, pyupgrade, and ruff-specific rules | |
| 29 | +| **ruff format** | Enforces consistent code formatting (line wrapping, spacing, trailing commas, etc.) | |
| 30 | +| **check-merge-conflict** | Ensures no merge conflict markers are left in files | |
| 31 | +| **check-yaml** | Validates YAML file syntax | |
| 32 | +| **end-of-file-fixer** | Ensures files end with a single newline | |
| 33 | +| **fix-byte-order-marker** | Removes UTF-8 BOM | |
| 34 | +| **trailing-whitespace** | Removes trailing whitespace from lines | |
| 35 | +| **mixed-line-ending** | Enforces LF line endings (except `.bat` files) | |
| 36 | +| **typos** | Spell-checks code and documentation | |
| 37 | +| **clang-format** | Formats C/C++/CUDA files under `csrc/` | |
| 38 | + |
| 39 | +## Ruff Configuration |
| 40 | + |
| 41 | +Configuration lives in `pyproject.toml` under `[tool.ruff]`. Key settings: |
| 42 | + |
| 43 | +- **Line length**: 119 characters |
| 44 | +- **Target Python version**: 3.10 |
| 45 | +- **Pinned version**: `~0.14.3` (see `pyproject.toml` `[project.optional-dependencies]`) |
| 46 | + |
| 47 | +### Enabled lint rule sets |
| 48 | + |
| 49 | +| Code | Rules | |
| 50 | +|---|---| |
| 51 | +| `B` | flake8-bugbear (security / correctness warnings) | |
| 52 | +| `E` | pycodestyle errors | |
| 53 | +| `W` | pycodestyle warnings | |
| 54 | +| `F` | pyflakes | |
| 55 | +| `I` | isort (import ordering) | |
| 56 | +| `ISC` | implicit string concatenation | |
| 57 | +| `UP` | pyupgrade (modern Python syntax) | |
| 58 | +| `RUF` | ruff-specific rules | |
| 59 | + |
| 60 | +### Notable ignored rules |
| 61 | + |
| 62 | +- `E501` — line-too-long is not enforced by the linter (but `ruff format` still wraps lines as it sees fit) |
| 63 | +- `E731` — lambda assignments are allowed |
| 64 | +- `B905` — `zip()` without `strict=` is allowed |
| 65 | +- Full list in `pyproject.toml` under `[tool.ruff.lint] ignore` |
| 66 | + |
| 67 | +### Per-file relaxations |
| 68 | + |
| 69 | +- `__init__.py` files: unused imports (`F401`) are allowed |
| 70 | +- `tests/**` and `benchmarking/**`: several additional rules are relaxed (B007, B011, B023, E701, E731, F841, UP030) |
| 71 | +- `bitsandbytes/**/triton/**`: import order (`I001`) is relaxed |
| 72 | + |
| 73 | +## Common Agent Mistakes |
| 74 | + |
| 75 | +### 1. Not running `ruff format` |
| 76 | + |
| 77 | +The most frequent failure. `ruff check` (the linter) and `ruff format` (the formatter) are **separate tools**. You must run both. The formatter rewraps lines, adjusts trailing commas, and normalizes spacing in ways the linter does not check. |
| 78 | + |
| 79 | +Example: a long `assert` with an f-string message that looks fine to the linter will be reformatted by `ruff format`: |
| 80 | + |
| 81 | +```python |
| 82 | +# Before (fails ruff format): |
| 83 | +assert err < threshold, f"Error {err:.6f} exceeds {threshold:.6f} + {N}*{std:.6f}" |
| 84 | + |
| 85 | +# After (ruff format wraps it): |
| 86 | +assert err < threshold, ( |
| 87 | + f"Error {err:.6f} exceeds {threshold:.6f} + {N}*{std:.6f}" |
| 88 | +) |
| 89 | +``` |
| 90 | + |
| 91 | +### 2. Only checking changed files |
| 92 | + |
| 93 | +CI runs `pre-commit run --all-files`. If there is a pre-existing formatting issue anywhere in the repo, your PR will fail even if your changes are clean. Always run the checks on the entire repo, not just your changed files. |
| 94 | + |
| 95 | +### 3. Forgetting C/CUDA formatting |
| 96 | + |
| 97 | +If you modify files under `csrc/`, `clang-format` will run. Make sure to format C/C++/CUDA code as well: |
| 98 | + |
| 99 | +```bash |
| 100 | +# If you have clang-format installed: |
| 101 | +clang-format -i csrc/your_file.cu |
| 102 | + |
| 103 | +# Or just run pre-commit which handles it: |
| 104 | +pre-commit run --all-files |
| 105 | +``` |
| 106 | + |
| 107 | +### 4. Typos in variable names or comments |
| 108 | + |
| 109 | +The `typos` checker scans all text. If it flags a false positive (e.g., a domain-specific abbreviation), you can add an exception to a `[default.extend-words]` section in a `_typos.toml` or `typos.toml` config file — but check with a maintainer first. |
| 110 | + |
| 111 | +## Recommended Workflow |
| 112 | + |
| 113 | +1. Make your code changes |
| 114 | +2. Run `ruff check --fix .` to auto-fix lint issues |
| 115 | +3. Run `ruff format .` to auto-fix formatting |
| 116 | +4. Review the changes the tools made (especially `--fix` auto-corrections) |
| 117 | +5. Stage everything and commit |
| 118 | +6. Optionally run `pre-commit run --all-files` as a final check before pushing |
0 commit comments