diff --git a/.github/workflows/ci-python.yml b/.github/workflows/ci-python.yml index e46c56d42135d..e69821a802657 100644 --- a/.github/workflows/ci-python.yml +++ b/.github/workflows/ci-python.yml @@ -48,7 +48,7 @@ jobs: uses: ./.github/workflows/bazel.yml with: name: Lint - run: bazel run //py:ruff -- --check + run: bazel run //py:ruff-check unit-tests: name: Unit Tests diff --git a/py/BUILD.bazel b/py/BUILD.bazel index f141a0c75f481..432b0063b6459 100644 --- a/py/BUILD.bazel +++ b/py/BUILD.bazel @@ -668,6 +668,11 @@ py_binary( ) alias( - name = "ruff", - actual = "//py/private:ruff", + name = "ruff-check", + actual = "//py/private:ruff_check", +) + +alias( + name = "ruff-format", + actual = "//py/private:ruff_format", ) diff --git a/py/private/BUILD.bazel b/py/private/BUILD.bazel index 52b484db3b808..8b02ac341a0dc 100644 --- a/py/private/BUILD.bazel +++ b/py/private/BUILD.bazel @@ -10,8 +10,19 @@ py_binary( ) py_binary( - name = "ruff", - srcs = ["ruff.py"], + name = "ruff_check", + srcs = ["ruff_check.py"], + data = [ + "//py:pyproject.toml", + "@multitool//tools/ruff", + ], + visibility = ["//visibility:public"], + deps = ["@rules_python//python/runfiles"], +) + +py_binary( + name = "ruff_format", + srcs = ["ruff_format.py"], data = [ "//py:pyproject.toml", "@multitool//tools/ruff", diff --git a/py/private/ruff.py b/py/private/ruff_check.py similarity index 60% rename from py/private/ruff.py rename to py/private/ruff_check.py index 415f0f7cd30ab..2ce719ca332a8 100644 --- a/py/private/ruff.py +++ b/py/private/ruff_check.py @@ -15,7 +15,11 @@ # specific language governing permissions and limitations # under the License. -"""Run ruff linter on Python files outside py/ directory.""" +"""Run ruff check on Python files across the project. + +Usage: + bazel run //py:ruff-check -- [ruff check args] +""" import os import subprocess @@ -23,32 +27,24 @@ from python.runfiles import Runfiles -LINT_DIRS = ["scripts", "common", "dotnet", "java", "javascript", "rb"] +ALL_DIRS = ["py", "scripts", "common", "dotnet", "java", "javascript", "rb"] EXCLUDES = ["**/node_modules/**", "**/.bundle/**"] +def run_check(ruff, exclude_args, dirs, extra_args): + """Run ruff check (linting).""" + cmd = [ruff, "check", "--config=py/pyproject.toml"] + return subprocess.run(cmd + exclude_args + dirs + extra_args).returncode + + if __name__ == "__main__": r = Runfiles.Create() ruff = r.Rlocation("rules_multitool++multitool+multitool/tools/ruff/ruff") os.chdir(os.environ["BUILD_WORKSPACE_DIRECTORY"]) - # Check if --check flag is passed (for CI - verify without fixing) - check_only = "--check" in sys.argv - extra_args = [arg for arg in sys.argv[1:] if arg != "--check"] - exclude_args = [] for pattern in EXCLUDES: exclude_args.extend(["--exclude", pattern]) - check_cmd = [ruff, "check", "--config=py/pyproject.toml"] - if not check_only: - check_cmd.extend(["--fix", "--show-fixes"]) - check_result = subprocess.run(check_cmd + exclude_args + LINT_DIRS + extra_args) - - format_cmd = [ruff, "format", "--config=py/pyproject.toml"] - if check_only: - format_cmd.append("--check") - format_result = subprocess.run(format_cmd + exclude_args + LINT_DIRS) - - sys.exit(check_result.returncode or format_result.returncode) + sys.exit(run_check(ruff, exclude_args, ALL_DIRS, sys.argv[1:])) diff --git a/py/private/ruff_format.py b/py/private/ruff_format.py new file mode 100644 index 0000000000000..a2ec928bc0fe4 --- /dev/null +++ b/py/private/ruff_format.py @@ -0,0 +1,50 @@ +# Licensed to the Software Freedom Conservancy (SFC) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The SFC licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +"""Run ruff format on Python files across the project. + +Usage: + bazel run //py:ruff-format -- [ruff format args] +""" + +import os +import subprocess +import sys + +from python.runfiles import Runfiles + +ALL_DIRS = ["py", "scripts", "common", "dotnet", "java", "javascript", "rb"] +EXCLUDES = ["**/node_modules/**", "**/.bundle/**"] + + +def run_format(ruff, exclude_args, dirs, extra_args): + """Run ruff format.""" + cmd = [ruff, "format", "--config=py/pyproject.toml"] + return subprocess.run(cmd + exclude_args + dirs + extra_args).returncode + + +if __name__ == "__main__": + r = Runfiles.Create() + ruff = r.Rlocation("rules_multitool++multitool+multitool/tools/ruff/ruff") + + os.chdir(os.environ["BUILD_WORKSPACE_DIRECTORY"]) + + exclude_args = [] + for pattern in EXCLUDES: + exclude_args.extend(["--exclude", pattern]) + + sys.exit(run_format(ruff, exclude_args, ALL_DIRS, sys.argv[1:])) diff --git a/py/pyproject.toml b/py/pyproject.toml index c1e60557185a2..db10b0fd7818c 100644 --- a/py/pyproject.toml +++ b/py/pyproject.toml @@ -167,5 +167,8 @@ extend-ignore = [ docstring-code-format = true docstring-code-line-length = 120 +[tool.ruff.lint.isort] +known-first-party = ["selenium", "test"] + [tool.ruff.lint.pydocstyle] convention = "google" diff --git a/rake_tasks/python.rake b/rake_tasks/python.rake index 98b2e11f2a135..ddc3063162996 100644 --- a/rake_tasks/python.rake +++ b/rake_tasks/python.rake @@ -151,11 +151,19 @@ task :version, [:version] do |_task, arguments| File.open(conf, 'w') { |f| f.puts text } end -desc 'Run Python linter (ruff check + format)' +desc 'Run Python formatter (ruff format)' +task :format do |_task, arguments| + puts ' Running ruff format...' + Bazel.execute('run', arguments.to_a, '//py:ruff-format') +end + +desc 'Run Python linter (ruff check + format + mypy)' task :lint do |_task, arguments| - args = arguments.to_a + raise ArgumentError, 'arguments not supported in this task' unless arguments.to_a.empty? + + Rake::Task['py:format'].invoke puts ' Running ruff check...' - Bazel.execute('run', args + ['--', 'check', '--fix', 'py/'], '@multitool//tools/ruff:cwd') - puts ' Running ruff format...' - Bazel.execute('run', args + ['--', 'format', 'py/'], '@multitool//tools/ruff:cwd') + Bazel.execute('run', %w[-- --fix --show-fixes], '//py:ruff-check') + puts ' Running mypy...' + Bazel.execute('run', [], '//py:mypy') end diff --git a/scripts/format.ps1 b/scripts/format.ps1 index fe32e2af417e6..2d61f5170450e 100644 --- a/scripts/format.ps1 +++ b/scripts/format.ps1 @@ -35,7 +35,7 @@ bazel run @rules_rust//:rustfmt section "Python" Write-Host " python - ruff" -ForegroundColor Green -bazel run //py:ruff +bazel run //py:ruff-format section "Copyright" bazel run //scripts:update_copyright diff --git a/scripts/format.sh b/scripts/format.sh index 23dbbef6302c7..e88e49bcde093 100755 --- a/scripts/format.sh +++ b/scripts/format.sh @@ -37,7 +37,7 @@ bazel run @rules_rust//:rustfmt section "Python" echo " python - ruff" >&2 -bazel run //py:ruff +bazel run //py:ruff-format section "Copyright" bazel run //scripts:update_copyright