From 3ef128ac9123a941bed935340a78215619259c9d Mon Sep 17 00:00:00 2001 From: Chandler Carruth Date: Fri, 29 May 2026 15:56:27 -0700 Subject: [PATCH] Switch to Astral Python tools: `ruff` and `ty` (#7243) This replaces black, flake8, and mypy with the more modern and efficient tools `ruff` and `ty` from Astral. Assisted-by: Antigravity with Gemini --- .../scripts/parse_diff.py | 2 +- .pre-commit-config.yaml | 58 ++++++++----------- .vscode/extensions.json | 4 +- bazel/cc_toolchains/cc_tools_test.py | 1 + docs/project/contribution_tools.md | 2 +- examples/bazel/update_module_to_nightly.py | 10 ++-- examples/bazel_test_runner.py | 3 +- github_tools/check_dependent_pr.py | 3 +- github_tools/check_dependent_pr_test.py | 2 +- github_tools/github_helpers.py | 6 +- github_tools/pr_comments.py | 4 +- proposals/scripts/check_proposal_names.py | 1 - .../scripts/check_proposal_names_test.py | 1 - pyproject.toml | 37 +++++++++++- scripts/bench_runner.py | 7 ++- scripts/check_header_guards.py | 4 +- scripts/check_sha_filenames.py | 2 +- scripts/fix_cc_deps.py | 4 +- scripts/lldbinit.py | 3 +- scripts/scripts_utils.py | 6 +- scripts/source_stats.py | 18 +++--- scripts/target_determinator.py | 4 +- setup.cfg | 15 ----- toolchain/diagnostics/check_diagnostics.py | 4 +- toolchain/install/install_test.py | 5 +- toolchain/install/toolchain_tar_test.py | 2 +- .../runtimes/configure_cmake_file_impl.py | 13 ++--- website/prebuild.py | 2 +- 28 files changed, 117 insertions(+), 106 deletions(-) delete mode 100644 setup.cfg diff --git a/.agents/skills/summarize_testdata_changes/scripts/parse_diff.py b/.agents/skills/summarize_testdata_changes/scripts/parse_diff.py index 37832e4f93e7..2550f31525fc 100644 --- a/.agents/skills/summarize_testdata_changes/scripts/parse_diff.py +++ b/.agents/skills/summarize_testdata_changes/scripts/parse_diff.py @@ -6,7 +6,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception import sys from collections import defaultdict -from typing import TextIO, Dict, List +from typing import Dict, List, TextIO def parse_diff(stream: TextIO) -> None: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 54fad7ac5988..7ad4441d4e36 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -49,10 +49,29 @@ repos: pass_filenames: false # Formatters should be run late so that they can re-format any prior changes. - - repo: https://github.com/psf/black - rev: c6755bb741b6481d6b3d3bb563c83fa060db96c9 # frozen: 26.3.1 + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.15.13 hooks: - - id: black + - id: ruff-check + args: [--fix] + - id: ruff-format + + - repo: local + hooks: + - id: ty + name: ty + entry: ty check --no-progress + language: python + additional_dependencies: + - ty + - rich + - 'gql>=2.0.0,<3.0.0' + - PyGitHub + - types-requests + - requests + types: [python] + pass_filenames: false + - repo: local hooks: - id: prettier @@ -69,7 +88,7 @@ repos: name: Bazel buildifier entry: scripts/run_buildifier.py # Beyond just formatting, explicitly fix lint warnings. - args: ['--lint=fix', '--warnings=all', '-r', '.'] + args: ['--lint=fix', '--warnings=all'] language: python files: | (?x)^( @@ -145,36 +164,6 @@ repos: language: python files: ^.*/BUILD$ pass_filenames: false - - repo: https://github.com/PyCQA/flake8 - rev: d93590f5be797aabb60e3b09f2f52dddb02f349f # frozen: 7.3.0 - hooks: - - id: flake8 - - repo: https://github.com/pre-commit/mirrors-mypy - rev: 'fc0f09a29bb495f4a91f00266155d6282d52485d' # frozen: v1.20.2 - hooks: - - id: mypy - # Use setup.cfg to match the command line. - args: - - --config-file=setup.cfg - # This should match the requirements added in the WORKSPACE pip_install. - additional_dependencies: - - gql >= 2.0.0, < 3.0.0 - - PyGitHub - - rich - - types-requests - # Exclusions are: - # - p#### scripts because they're not tested or maintained. - # - lit.cfg.py because it has multiple copies, breaking mypy. - # - `bazel_test_runner.py` which depends on Bazel-specific imports. - # - Unit tests because they sometimes violate typing, such as by - # assigning a mock to a function. - exclude: | - (?x)^( - proposals/(?!scripts/).*| - .*/lit\.cfg\.py| - examples/bazel_test_runner\.py| - .*_test\.py - )$ - repo: https://github.com/codespell-project/codespell rev: 2ccb47ff45ad361a21071a7eedda4c37e6ae8c5a # frozen: v2.4.2 hooks: @@ -269,6 +258,7 @@ repos: # This excludes third-party code, and patches to third-party code. exclude: | (?x)^( + \.jj/.*| MODULE.bazel.lock| .*package-lock\.json| bazel/bazel_clang_tidy/.*\.patch| diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 020727541fdd..bcb0bcf8a0a1 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -5,7 +5,7 @@ "carbon-lang.carbon-vscode", "esbenp.prettier-vscode", "llvm-vs-code-extensions.vscode-clangd", - "ms-python.black-formatter", - "ms-python.python" + "charliermarsh.ruff", + "astral-sh.ty" ] } diff --git a/bazel/cc_toolchains/cc_tools_test.py b/bazel/cc_toolchains/cc_tools_test.py index 97391467a12d..efc6d4c497e8 100644 --- a/bazel/cc_toolchains/cc_tools_test.py +++ b/bazel/cc_toolchains/cc_tools_test.py @@ -13,6 +13,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception import os import subprocess import sys + from bazel_tools.tools.python.runfiles import runfiles diff --git a/docs/project/contribution_tools.md b/docs/project/contribution_tools.md index 4869d83ee333..8deea62e3a5a 100644 --- a/docs/project/contribution_tools.md +++ b/docs/project/contribution_tools.md @@ -222,7 +222,7 @@ considering if they fit your workflow. - [Visual Studio Code](https://code.visualstudio.com/): A code editor. - We provide [recommended extensions](/.vscode/extensions.json) to assist Carbon development. Some settings changes must be made separately: - - Python › Formatting: Provider: `black` + - Python › Formatting: Provider: `ruff` - **WARNING:** Visual Studio Code modifies the `PATH` environment variable, particularly in the terminals it creates. The `PATH` difference can cause `bazel` to detect different startup options, diff --git a/examples/bazel/update_module_to_nightly.py b/examples/bazel/update_module_to_nightly.py index f6df8b161f0f..d637492c24de 100755 --- a/examples/bazel/update_module_to_nightly.py +++ b/examples/bazel/update_module_to_nightly.py @@ -19,13 +19,13 @@ Exceptions. See /LICENSE for license information. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """ -import re -import os -import sys import base64 -import urllib.request -import urllib.error import json +import os +import re +import sys +import urllib.error +import urllib.request MODULE_NAME = "carbon_toolchain" MODULE_FILENAME = "MODULE.bazel" diff --git a/examples/bazel_test_runner.py b/examples/bazel_test_runner.py index 8dcf60310e78..33e9d74348ae 100755 --- a/examples/bazel_test_runner.py +++ b/examples/bazel_test_runner.py @@ -12,8 +12,9 @@ import os import sys import time import unittest -from bazel_tools.tools.python.runfiles import runfiles + from bazel_integration_test.py import test_base +from bazel_tools.tools.python.runfiles import runfiles class BazelExampleTest(test_base.TestBase): diff --git a/github_tools/check_dependent_pr.py b/github_tools/check_dependent_pr.py index a8f95f9edc2c..71ee8ecb5b74 100755 --- a/github_tools/check_dependent_pr.py +++ b/github_tools/check_dependent_pr.py @@ -32,9 +32,10 @@ import datetime import json import re import sys -import requests from typing import Any, Optional +import requests + try: from github_tools import github_helpers except ImportError: diff --git a/github_tools/check_dependent_pr_test.py b/github_tools/check_dependent_pr_test.py index 877fcbfab5b7..6bcc772d9b96 100644 --- a/github_tools/check_dependent_pr_test.py +++ b/github_tools/check_dependent_pr_test.py @@ -8,8 +8,8 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception import json import unittest -from unittest import mock from typing import Any +from unittest import mock import check_dependent_pr import github_helpers diff --git a/github_tools/github_helpers.py b/github_tools/github_helpers.py index b742578035e7..23ca7816ffce 100644 --- a/github_tools/github_helpers.py +++ b/github_tools/github_helpers.py @@ -10,13 +10,13 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """ import argparse -from collections.abc import Generator import os +from collections.abc import Generator from typing import Optional, cast # https://pypi.org/project/gql/ -import gql # type: ignore -import gql.transport.requests # type: ignore +import gql +import gql.transport.requests _ENV_TOKEN = "GITHUB_ACCESS_TOKEN" diff --git a/github_tools/pr_comments.py b/github_tools/pr_comments.py index 70f948ffc263..a2785111101b 100755 --- a/github_tools/pr_comments.py +++ b/github_tools/pr_comments.py @@ -11,8 +11,8 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception import argparse import datetime import hashlib -import os import importlib.util +import os import textwrap from typing import Any, Callable, Optional @@ -25,7 +25,7 @@ except ImportError: os.path.join(os.path.dirname(__file__), "github_helpers.py"), ) assert github_helpers_spec is not None - github_helpers = importlib.util.module_from_spec(github_helpers_spec) + github_helpers: Any = importlib.util.module_from_spec(github_helpers_spec) github_helpers_spec.loader.exec_module(github_helpers) # type: ignore diff --git a/proposals/scripts/check_proposal_names.py b/proposals/scripts/check_proposal_names.py index f4769b64b8b6..f12c0072211a 100755 --- a/proposals/scripts/check_proposal_names.py +++ b/proposals/scripts/check_proposal_names.py @@ -11,7 +11,6 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception import argparse import os import pathlib - import re import sys from typing import List, Optional, Tuple diff --git a/proposals/scripts/check_proposal_names_test.py b/proposals/scripts/check_proposal_names_test.py index fd8bfb666d82..abc280527384 100644 --- a/proposals/scripts/check_proposal_names_test.py +++ b/proposals/scripts/check_proposal_names_test.py @@ -13,7 +13,6 @@ import pathlib import tempfile import unittest - from proposals.scripts import check_proposal_names diff --git a/pyproject.toml b/pyproject.toml index 8161a708f250..a32c90a72d2b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,38 @@ # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -[tool.black] +[project] +requires-python = ">=3.11" + +[tool.ruff] line-length = 80 -target-version = ['py310'] -include = '\.pyi?$' +target-version = "py310" + +[tool.ruff.lint] +select = ["E", "F", "W", "I", "ANN"] +ignore = ["ANN401", "ANN204"] + +[tool.ruff.lint.per-file-ignores] +"*_test.py" = ["ANN"] +"proposals/**" = ["ANN"] +"examples/bazel_test_runner.py" = ["ANN"] +"**/lit.cfg.py" = ["ANN"] + +[tool.ruff.lint.isort] +combine-as-imports = true + +[tool.ty.rules] +all = "error" +possibly-unresolved-reference = "ignore" + +[tool.ty.src] +exclude = [ + "proposals/p0555/figures.py", + "proposals/__init__.py", + "**/lit.cfg.py", + "examples/bazel_test_runner.py", + "**/*_test.py", +] + +[tool.ty.analysis] +allowed-unresolved-imports = ["rich.**", "requests", "bazel_tools.**"] diff --git a/scripts/bench_runner.py b/scripts/bench_runner.py index 902bb94db550..e7d3814dee73 100755 --- a/scripts/bench_runner.py +++ b/scripts/bench_runner.py @@ -66,15 +66,17 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception import argparse import json import math -import numpy as np # type: ignore import re -import scipy as sp # type: ignore import subprocess import sys from collections import defaultdict from dataclasses import dataclass, field from enum import Enum from pathlib import Path +from typing import Optional + +import numpy as np # type: ignore +import scipy as sp # type: ignore from quantiphy import Quantity # type: ignore from rich.console import Console from rich.padding import Padding @@ -82,7 +84,6 @@ from rich.progress import track from rich.table import Column, Table from rich.text import Text from rich.theme import Theme -from typing import Optional def parse_args(args: Optional[list[str]] = None) -> argparse.Namespace: diff --git a/scripts/check_header_guards.py b/scripts/check_header_guards.py index 2a40a71f7f31..987304a7e9ff 100755 --- a/scripts/check_header_guards.py +++ b/scripts/check_header_guards.py @@ -12,10 +12,10 @@ Exceptions. See /LICENSE for license information. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """ -from collections.abc import Iterable -from pathlib import Path import re import sys +from collections.abc import Iterable +from pathlib import Path from typing import NamedTuple, Optional diff --git a/scripts/check_sha_filenames.py b/scripts/check_sha_filenames.py index 52069477e30a..8c36b6b3c7fe 100755 --- a/scripts/check_sha_filenames.py +++ b/scripts/check_sha_filenames.py @@ -21,8 +21,8 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """ import hashlib -from pathlib import Path import sys +from pathlib import Path def main() -> None: diff --git a/scripts/fix_cc_deps.py b/scripts/fix_cc_deps.py index e31368cab174..107756a32912 100755 --- a/scripts/fix_cc_deps.py +++ b/scripts/fix_cc_deps.py @@ -248,9 +248,7 @@ def get_missing_deps( if is_system_include: # Don't error for unexpected system includes. continue - exit( - f"Missing rule for " f"'{full_include}' in '{source_file}'" - ) + exit(f"Missing rule for '{full_include}' in '{source_file}'") rule_choice = header_to_rule_map[header] if not rule_choice.rules.intersection(rule.deps): if len(rule_choice.rules) > 1: diff --git a/scripts/lldbinit.py b/scripts/lldbinit.py index 0f68d3f26f0a..7e7fbdc0fe8c 100644 --- a/scripts/lldbinit.py +++ b/scripts/lldbinit.py @@ -9,11 +9,12 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """ # This script is only meant to be used from LLDB. -import lldb # type: ignore import os import re from typing import Any +import lldb # type: ignore + project_root = os.path.dirname(os.path.realpath(__file__)) ci = lldb.debugger.GetCommandInterpreter() diff --git a/scripts/scripts_utils.py b/scripts/scripts_utils.py index b37b6bb138c8..a874286cff30 100644 --- a/scripts/scripts_utils.py +++ b/scripts/scripts_utils.py @@ -6,17 +6,17 @@ Exceptions. See /LICENSE for license information. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """ -from enum import Enum import fcntl import hashlib import os -from pathlib import Path import platform import shutil import tempfile import time -from typing import NamedTuple, Optional import urllib.request +from enum import Enum +from pathlib import Path +from typing import NamedTuple, Optional # The tools we track releases for. diff --git a/scripts/source_stats.py b/scripts/source_stats.py index 358897463998..430a70e3fac1 100755 --- a/scripts/source_stats.py +++ b/scripts/source_stats.py @@ -16,15 +16,16 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """ import argparse -from alive_progress import alive_bar # type: ignore import math -from multiprocessing import Pool import re -import termplotlib as tpl # type: ignore +from collections import Counter +from dataclasses import asdict, dataclass, field +from multiprocessing import Pool from pathlib import Path from typing import Optional -from dataclasses import dataclass, field, asdict -from collections import Counter + +import termplotlib as tpl # type: ignore +from alive_progress import alive_bar # type: ignore BLANK_RE = re.compile(r"\s*") COMMENT_RE = re.compile(r"\s*///*\s*") @@ -205,7 +206,8 @@ def main() -> None: stats.accumulate(file_stats) bar() - print(""" + print( + """ ## Stats ## Lines: %(lines)d Blank lines: %(blank_lines)d @@ -219,7 +221,9 @@ Int literals: %(int_literals)d Float literals: %(float_literals)d Symbols: %(symbols)d Keywords: %(keywords)d -IDs: %(identifiers)d""" % asdict(stats)) +IDs: %(identifiers)d""" + % asdict(stats) + ) tokens = ( stats.string_literals diff --git a/scripts/target_determinator.py b/scripts/target_determinator.py index 1e14ede4fa3b..527139b6b7ae 100755 --- a/scripts/target_determinator.py +++ b/scripts/target_determinator.py @@ -25,10 +25,10 @@ Exceptions. See /LICENSE for license information. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """ -import subprocess import argparse -import tempfile +import subprocess import sys +import tempfile from pathlib import Path import scripts_utils diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index b518b9461a19..000000000000 --- a/setup.cfg +++ /dev/null @@ -1,15 +0,0 @@ -# Part of the Carbon Language project, under the Apache License v2.0 with LLVM -# Exceptions. See /LICENSE for license information. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -[flake8] -max-line-length = 80 -# E203: This warning is not PEP 8 compliant. -extend-ignore = E203 - -[mypy] -pretty = True -# Define flags relative to strict mode. -strict = True -# TODO: Look at updating code to fix these. -disallow_any_generics = False diff --git a/toolchain/diagnostics/check_diagnostics.py b/toolchain/diagnostics/check_diagnostics.py index b03d38868ca9..5e0c41b709f0 100755 --- a/toolchain/diagnostics/check_diagnostics.py +++ b/toolchain/diagnostics/check_diagnostics.py @@ -13,12 +13,12 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """ import collections -from concurrent import futures import itertools import os -from pathlib import Path import re import sys +from concurrent import futures +from pathlib import Path from typing import Dict, List, NamedTuple, Set # Example or test diagnostics, ignored because they're expected to not pass. diff --git a/toolchain/install/install_test.py b/toolchain/install/install_test.py index de1d3b9835f7..38ef265b374d 100644 --- a/toolchain/install/install_test.py +++ b/toolchain/install/install_test.py @@ -8,13 +8,14 @@ Exceptions. See /LICENSE for license information. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """ -from pathlib import Path -import subprocess import os import platform +import subprocess import sys import textwrap import unittest +from pathlib import Path + from bazel_tools.tools.python.runfiles import runfiles diff --git a/toolchain/install/toolchain_tar_test.py b/toolchain/install/toolchain_tar_test.py index bdcc5154cbd0..4b82936cc7e1 100644 --- a/toolchain/install/toolchain_tar_test.py +++ b/toolchain/install/toolchain_tar_test.py @@ -8,11 +8,11 @@ Exceptions. See /LICENSE for license information. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """ -from pathlib import Path import os import re import tarfile import unittest +from pathlib import Path class ToolchainTarTest(unittest.TestCase): diff --git a/toolchain/runtimes/configure_cmake_file_impl.py b/toolchain/runtimes/configure_cmake_file_impl.py index 01a69077f379..80d362f59d5a 100644 --- a/toolchain/runtimes/configure_cmake_file_impl.py +++ b/toolchain/runtimes/configure_cmake_file_impl.py @@ -1,11 +1,4 @@ #!/usr/bin/env python3 - -__copyright__ = """ -Part of the Carbon Language project, under the Apache License v2.0 with LLVM -Exceptions. See /LICENSE for license information. -SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -""" - """Script to apply a set of defines to a CMake-style configure file. This serves as the action implementation for `configure_cmake_file.bzl`. See the @@ -13,6 +6,12 @@ documentation in the rule of that file for more details about how to use this, or `--help` on the script. """ +__copyright__ = """ +Part of the Carbon Language project, under the Apache License v2.0 with LLVM +Exceptions. See /LICENSE for license information. +SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +""" + import argparse import re from typing import Dict diff --git a/website/prebuild.py b/website/prebuild.py index ea687f69544b..5503fa695e86 100755 --- a/website/prebuild.py +++ b/website/prebuild.py @@ -12,8 +12,8 @@ structure prior to the jekyll build. import dataclasses import os -from pathlib import Path import re +from pathlib import Path from typing import Optional __copyright__ = """