@@ -6,3 +6,116 @@ requires = [
66 " numpy" ,
77]
88build-backend = " setuptools.build_meta"
9+
10+ # ---------------------------------------------------------------------------
11+ # Ruff configuration
12+ # ---------------------------------------------------------------------------
13+ # Ruff is the canonical linter for ARC. The selected rule set is intentionally
14+ # conservative for a working scientific codebase: bug-finding + import-sorting +
15+ # pyupgrade only. Stylistic rules (line length, naming, docstrings) are left
16+ # off until the team explicitly opts in. To run locally:
17+ #
18+ # ruff check arc/
19+ # ruff check --fix arc/ # auto-fix the safe ones
20+ # ruff format --check arc/ # check formatting only (does not modify)
21+ #
22+ # CI runs both `ruff check` and `ruff format --check` on every PR via
23+ # .github/workflows/ruff.yml. Both steps are currently non-blocking; see the
24+ # workflow file for the plan to make them gating.
25+ # ---------------------------------------------------------------------------
26+ [tool .ruff ]
27+ # Match the Python pinned in environment.yml. Bumping this is the right
28+ # knob to enable newer pyupgrade rewrites once the conda env moves forward.
29+ target-version = " py312"
30+ line-length = 120
31+ extend-exclude = [
32+ " build" ,
33+ " dist" ,
34+ " *.egg-info" ,
35+ " .eggs" ,
36+ " ipython" , # notebook tutorials
37+ " arc/molecule" , # Cython-compiled sources + tightly coupled modules
38+ ]
39+
40+ [tool .ruff .lint ]
41+ # Rule families enabled (kept tight; can grow over time):
42+ # E pycodestyle errors (real syntactic / structural issues)
43+ # F pyflakes (unused imports, undefined names, …)
44+ # W pycodestyle warnings (trailing whitespace, blank-line issues)
45+ # I isort (import ordering — auto-fixable)
46+ # B flake8-bugbear (likely-bug patterns: mutable defaults, …)
47+ # UP pyupgrade (modernize old syntax for the target Python)
48+ # RUF ruff-specific (small high-signal correctness checks)
49+ # C4 flake8-comprehensions (clearer list/set/dict comprehensions)
50+ select = [" E" , " F" , " W" , " I" , " B" , " UP" , " RUF" , " C4" ]
51+ # Per-rule overrides — these are the rules that produce false positives on
52+ # scientific Python code more often than they catch real bugs, plus the
53+ # stylistic-modernization rules that would otherwise flood the inbox on a
54+ # mature codebase. Each ignore is justified inline so future maintainers can
55+ # see why it was added and revisit if the situation changes.
56+ ignore = [
57+ # --- whitespace / formatting (let `ruff format` handle these) ----------
58+ " E501" , # line too long — allow long XYZ blocks, SMILES, docstrings
59+ " E731" , # do not assign a lambda — common in numerical code
60+ " E741" , # ambiguous variable name (l/I/O) — allowed for math/physics
61+ " W291" , # trailing whitespace — formatter's job
62+ " W293" , # blank-line whitespace — formatter's job
63+ # --- typing modernization (we keep the legacy form for now) -----------
64+ " UP006" , # `list` instead of `List` — pre-PEP604 form is in heavy use
65+ " UP007" , # `X | Y` instead of `Union[X, Y]` — same reason
66+ " UP035" , # `typing.X` deprecated — same reason
67+ " UP045" , # `X | None` instead of `Optional[X]` — same reason (1290+ hits)
68+ # --- pyupgrade modernizations that aren't worth a churn-PR ------------
69+ " UP009" , # UTF-8 encoding declaration — harmless leftover
70+ " UP015" , # redundant open mode `'r'` — harmless
71+ " UP025" , # unicode kind prefix `u""` — harmless
72+ " UP030" , # `format()` literal positional indexes — minor
73+ " UP032" , # use f-string instead of `.format()` — minor
74+ # --- bugbear false positives on numerical code ------------------------
75+ " B007" , # unused loop control variable — common with `for _, x in …`
76+ " B008" , # function calls in arg defaults
77+ " B905" , # zip() without strict — would require a churn PR
78+ # --- comprehension preferences (taste, not bugs) ----------------------
79+ " C408" , # unnecessary `dict()` call
80+ " C416" , # unnecessary comprehension
81+ " C419" , # unnecessary comprehension in `any()`/`all()`/etc.
82+ # --- ruff-specific noise on chemistry text ----------------------------
83+ " RUF001" , # ambiguous unicode in strings — Greek letters in chemistry text
84+ " RUF002" , # ambiguous unicode in docstrings — same
85+ " RUF003" , # ambiguous unicode in comments — same
86+ " RUF005" , # collection literal concatenation — taste
87+ " RUF012" , # mutable class attributes annotated with ClassVar — too noisy
88+ " RUF013" , # implicit `Optional` — would need typing-wide cleanup
89+ " RUF059" , # unused unpacked variable — taste
90+ ]
91+
92+ [tool .ruff .lint .per-file-ignores ]
93+ # Tests routinely use long literal blocks (XYZ strings, expected dicts, …)
94+ # and `assert` patterns that some lint rules flag as "useless".
95+ "**/*test*.py" = [" E501" , " F841" , " B017" , " RUF015" ]
96+ "arc/testing/**" = [" E501" , " F841" ]
97+ # __init__.py files re-export symbols and need wildcard / unused-import patterns.
98+ "**/__init__.py" = [" F401" , " F403" ]
99+ # Standalone scripts run inside isolated envs and may import packages not
100+ # installed in the main env — don't flag those imports.
101+ "arc/job/adapters/scripts/*.py" = [" F401" , " E402" ]
102+
103+ [tool .ruff .lint .isort ]
104+ known-first-party = [" arc" ]
105+ combine-as-imports = true
106+ force-sort-within-sections = false
107+ section-order = [
108+ " future" ,
109+ " standard-library" ,
110+ " third-party" ,
111+ " first-party" ,
112+ " local-folder" ,
113+ ]
114+
115+ [tool .ruff .format ]
116+ # Format settings for `ruff format`. Not enforced by CI yet — the team can
117+ # opt in to a one-time mass reformat by running `ruff format arc/`.
118+ quote-style = " single"
119+ indent-style = " space"
120+ line-ending = " lf"
121+ docstring-code-format = false
0 commit comments