diff --git a/.gitignore b/.gitignore index 3cf49d11..60eb5a8c 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,6 @@ build/ *.key .cache .vscode/ + +# Ruff +.ruff_cache/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6a985232..1603ee31 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.6.0 + rev: v5.0.0 hooks: - id: check-yaml - id: end-of-file-fixer @@ -8,27 +8,22 @@ repos: - id: trailing-whitespace - id: mixed-line-ending args: ['--fix=lf'] + - id: check-added-large-files + args: ['--maxkb=1000'] + - id: check-json + exclude: '^src/frontend/.*\.json$' # tsconfig uses JSON5 (comments, trailing commas) + - id: check-toml + - id: check-merge-conflict + - id: debug-statements - - repo: https://github.com/PyCQA/autoflake - rev: v2.3.1 + # Ruff replaces autoflake, isort, black, and many flake8 plugins + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.14.6 hooks: - - id: autoflake - args: - - --remove-all-unused-imports - - --remove-unused-variables - - --in-place - - - repo: https://github.com/pycqa/isort - rev: 5.13.2 - hooks: - - id: isort - language_version: python3.11 - args: ["--profile", "black"] - exclude: '^src/parallax/p2p/proto/forward_pb2\.py$' - - - repo: https://github.com/psf/black - rev: 24.3.0 - hooks: - - id: black - language_version: python3.11 - exclude: '^src/parallax/p2p/proto/forward_pb2\.py$' + # Run the linter + - id: ruff-check + args: [--fix, --all-files] + exclude: '^src/parallax/p2p/proto/' + # Run the formatter + - id: ruff-format + exclude: '^src/parallax/p2p/proto/' diff --git a/pyproject.toml b/pyproject.toml index 0b2f1328..aaa07379 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -68,8 +68,7 @@ benchmark = [ ] dev = [ - "black>=24.3", - "ruff>=0.4", + "ruff>=0.14.6", "pytest>=8.2", "pytest-mock>=3.14", "pytest-cov>=5.0", @@ -79,12 +78,4 @@ dev = [ [tool.setuptools.packages.find] where = ["src"] # put your code under ./src/… -[tool.black] -line-length = 100 - -[tool.isort] -profile = "black" - -[tool.ruff] -ignore = ["E501"] # line length handled by Black -line-length = 100 +# Ruff configuration moved to ruff.toml diff --git a/ruff.toml b/ruff.toml new file mode 100644 index 00000000..046a713a --- /dev/null +++ b/ruff.toml @@ -0,0 +1,104 @@ +# https://docs.astral.sh/ruff/ +line-length = 100 +target-version = "py312" + +# Exclude generated files and caches +exclude = [ + ".git", + ".venv", + "__pycache__", + "build", + "dist", + "*.egg-info", + "src/parallax/p2p/proto", # Generated protobuf + "src/frontend/dist", + "src/frontend/node_modules", +] + +[lint] +# Enable pycodestyle (E, W), Pyflakes (F), isort (I), and more +select = [ + "E", # pycodestyle errors + "W", # pycodestyle warnings + "F", # Pyflakes + "I", # isort + "N", # pep8-naming + "UP", # pyupgrade + "B", # flake8-bugbear + "C4", # flake8-comprehensions + "SIM", # flake8-simplify + "RUF", # Ruff-specific rules + "TCH", # flake8-type-checking + "PTH", # flake8-use-pathlib + "TID", # flake8-tidy-imports + "ASYNC", # flake8-async +] + +# Allow autofix for all enabled rules +fixable = ["ALL"] +unfixable = [] + +# Ignore specific rules +ignore = [ + "E501", # Line too long (handled by formatter) + "B008", # Do not perform function calls in argument defaults + "C901", # Function is too complex + "SIM108", # Use ternary operator (sometimes less readable) +] + +# Deferred rules - require code changes, to be addressed in follow-up PRs +# TODO: Remove from extend-ignore as issues are fixed +extend-ignore = [ + # Require refactoring + "PTH", # pathlib migration - significant refactor + "TCH", # TYPE_CHECKING imports - requires careful review + "UP035", # typing.List -> list - touches many files + "B006", # mutable-argument-default - needs case-by-case review + "B023", # function-uses-loop-variable - needs case-by-case review + "E402", # module-import-not-at-top - sometimes intentional for side effects + + # Stylistic - sometimes the "violation" is clearer + "SIM102", # nested if statements + "SIM105", # suppressible exception (contextlib.suppress) + "SIM115", # open-file-with-context-handler + "SIM117", # nested with statements + "SIM118", # key in dict.keys() -> key in dict + "E731", # lambda assignment + + # Naming - often intentional (math notation, DSL) + "N806", # non-lowercase variable in function + "N803", # invalid argument name + "N802", # invalid function name + "E741", # ambiguous variable name (l, O, I) + + # Low priority / noisy + "RUF059", # unused unpacked variable + "RUF013", # implicit-optional (None default without Optional type) + "RUF002", # ambiguous unicode in docstring + "RUF003", # ambiguous unicode in comment + "RUF012", # mutable class default (needs ClassVar annotation) + "B018", # useless expression - sometimes intentional for side effects + "B904", # raise-without-from - needs case-by-case review + "ASYNC109", # async timeout parameter - needs careful review +] + +[lint.per-file-ignores] +# Ignore imports in __init__.py files +"__init__.py" = ["F401", "F403"] +# Test files can have additional imports +"tests/**/*.py" = ["F401", "F403", "B018"] +# Benchmark code has known issues to be fixed separately +"src/backend/benchmark/**/*.py" = ["F821", "C419"] + +[lint.isort] +known-first-party = ["parallax", "scheduling", "parallax_utils", "backend"] +combine-as-imports = true +lines-after-imports = 2 + +[format] +# Use double quotes for strings +quote-style = "double" +indent-style = "space" +skip-magic-trailing-comma = false +line-ending = "auto" +docstring-code-format = true