Skip to content

Conversation

@whoahbot
Copy link

Adds ty as a linter for Python.

This is my first contribution here, so a lot of this is copied from the implementation for ruff, including the strategy for mirroring ty releases.

Changes are visible to end-users: yes

  • Searched for relevant documentation and updated as needed: yes
  • Breaking change (forces users to change their own code or config): no
  • Suggested release notes appear below: no

Test plan

  • New test cases added

Added unsupported_operator.py as an example lint, and call_non_callable.py , which should be ignored by the ty.toml configuration file.

@CLAassistant
Copy link

CLAassistant commented Nov 29, 2025

CLA assistant check
All committers have signed the CLA.

@whoahbot whoahbot changed the title Feat: Add ty as a linter feat: Add ty as a linter Nov 29, 2025
@aspect-workflows
Copy link

aspect-workflows bot commented Nov 29, 2025

Test

All tests were cache hits

5 tests (100.0%) were fully cached saving 2s.


Test

example

8 test targets passed

Targets
//tools/format:format_test_HTML_Jinja_with_djlint [k8-fastbuild]4s
//tools/format:format_test_JavaScript_with_prettier [k8-fastbuild]15s
//tools/format:format_test_Markdown_with_prettier [k8-fastbuild]2s
//tools/format:format_test_Protocol_Buffer_with_buf [k8-fastbuild]1s
//tools/format:format_test_Python_with_ruff [k8-fastbuild]420ms
//tools/format:format_test_SQL_with_prettier [k8-fastbuild]3s
//tools/format:format_test_Scala_with_scalafmt [k8-fastbuild]8s
//tools/format:format_test_Starlark_with_buildifier [k8-fastbuild]285ms

Total test execution time was 34s. 35 tests (81.4%) were fully cached saving 14s.


Test (WORKSPACE) (Test)

example

8 test targets passed

Targets
//tools/format:format_test_HTML_Jinja_with_djlint [k8-fastbuild]2s
//tools/format:format_test_JavaScript_with_prettier [k8-fastbuild]5s
//tools/format:format_test_Markdown_with_prettier [k8-fastbuild]910ms
//tools/format:format_test_Protocol_Buffer_with_buf [k8-fastbuild]722ms
//tools/format:format_test_Python_with_ruff [k8-fastbuild]213ms
//tools/format:format_test_SQL_with_prettier [k8-fastbuild]1s
//tools/format:format_test_Scala_with_scalafmt [k8-fastbuild]3s
//tools/format:format_test_Starlark_with_buildifier [k8-fastbuild]241ms

Total test execution time was 14s. 13 tests (61.9%) were fully cached saving 8s.

@whoahbot whoahbot force-pushed the add_ty branch 2 times, most recently from 95be6f4 to 70b0499 Compare November 29, 2025 23:16
@alexeagle alexeagle requested review from alexeagle and arrdem December 2, 2025 16:14
Copy link
Member

@alexeagle alexeagle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

awesome, excited that you've broken ground on this.

We'll have to do some README edits before landing to warn about both ty immaturity and also the experimental nature of this linter aspect.

npx @bazel/buildifier lint/ruff_versions.bzl
- name: Update ty
run: |
./lint/mirror_ty.sh
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's possible that we skip the mirroring, now that many Bazel users check in their MODULE.bazel.lock file they can get the Trust-on-first-use guarantee. I suspect it's too soon to change that, just raising for our team to consider

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for now do you mind dropping this file and the mirror_ty.sh from this PR? since it comes in through rules_multitool there are other options for updating it

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do!

@@ -0,0 +1,5 @@
# Demo with just running ty:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this demo should look like bazel run @some_label//:ty -- some_file.py - we want to convince ourselves we know how to call the tool standalone

@@ -0,0 +1,3 @@
# Demo with just running ty:
# $ bazel run --run_under="cd $PWD &&" -- //tools/lint:ty check --config=ty.toml src/*.py
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💎

lint/ty.bzl Outdated
load("@aspect_rules_lint//lint:ty.bzl", "lint_ty_aspect")
ty = lint_ty_aspect(
binary = Label("@aspect_rules_lint//lint:ty_bin",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: missing a closing paren

lint/ty.bzl Outdated
```starlark
# Note: this won't interact properly with the --platform flag, see
# https://github.com/aspect-build/rules_lint/issues/389
alias(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is copy-pasta? They should get this for free from multitool

lint/ty.bzl Outdated
https://docs.astral.sh/ty/reference/exit-codes/
env: environment variables for ty
"""
inputs = srcs + config
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The big remaining work here is a graph of py_library(name="a", deps = ["b"]) -> py_library(name="b")

I think you'll already find that it's broken to lint a because it will be missing type definitions from b.
In fixing that, we want to avoid any work of type-checking the b sources a second time.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing that out. I'll start looking into this.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alexeagle After looking into this a bit I had some directional questions that I'd like to get your input on.

It seems like the flake8 implementation is trying to avoid taking a dependency on rules_python, but using the PyInfo provider would be one way to collect the dependencies of a target. What are your thoughts on taking a dependency on rules_python?

I was also looking at the implementation of rules_mypy which I think handles some other cases I think this linter might want to eventually cover. Any thoughts on the approach taken in rules_mypy that would be good to pattern after or avoid?

Thanks!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's unavoidable to need the PyInfo symbol so rules_python is a required dep. bazelbuild/bazel#25509 can make it less painful.

@arrdem has spent more time in rules_mypy recently than I have. Could you give any short specifics or links on which cases/approach you're looking at?

- Removed ty mirroring in favor of multitool usage
- Fixed running example ty linting with a standalone command.
- Fixed a closing paren in the docs.
- Dropped `fetch_ty` helper.
@whoahbot whoahbot force-pushed the add_ty branch 2 times, most recently from 13ac733 to 719652b Compare December 5, 2025 22:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants