Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ jobs:
- name: Lint with mypy
run: uv run mypy .

- name: Lint with ty
continue-on-error: true
run: uv run ty check .

- name: Print python versions
run: |
python -V
Expand Down
17 changes: 17 additions & 0 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,23 @@ watch-mypy:
just _entr-warn
fi

# Run ty type checker
[group: 'lint']
ty:
uv run ty check src tests

# Watch files and run ty on change
[group: 'lint']
watch-ty:
#!/usr/bin/env bash
set -euo pipefail
if command -v entr > /dev/null; then
{{ py_files }} | entr -c just ty
else
just ty
just _entr-warn
fi

# Format markdown files with prettier
[group: 'format']
format-markdown:
Expand Down
69 changes: 69 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ dev = [
# Lint
"ruff",
"mypy",
"ty",
]

docs = [
Expand All @@ -97,6 +98,7 @@ lint = [
"typing-extensions; python_version < '3.11'",
"ruff",
"mypy",
"ty",
]

[project.entry-points.pytest11]
Expand Down Expand Up @@ -138,6 +140,73 @@ files = [
"tests",
]

[tool.ty.environment]
python-version = "3.10"

[tool.ty.src]
include = ["src", "tests"]

[tool.ty.rules]
# Private _pytest APIs (e.g. RaisesContext) are not publicly exported;
# both mypy and ty flag them. ty categorizes these as unresolved-import
# rather than attr-defined.
# https://github.com/astral-sh/ty/issues/1276
unresolved-import = "ignore"
# ty resolves cmd() as a union type including a (Any, Any, /) -> tmux_cmd
# variant from CmdProtocol, causing false positives on *args methods.
# ty does not yet fully support argument unpacking (*args/**kwargs).
# https://github.com/astral-sh/ty/issues/404
too-many-positional-arguments = "ignore"
# Same root cause as too-many-positional-arguments: ty cannot verify
# required params are present when calling with **kwargs unpacking.
# https://github.com/astral-sh/ty/issues/785
missing-argument = "ignore"
# ty falls back to object.__init__ for union return types and
# dataclass-transform decorators, flagging all kwargs as unknown.
# https://github.com/astral-sh/ty/issues/2369
unknown-argument = "ignore"
# Tests use monkeypatch.setattr(libtmux.common, ...) without explicit
# submodule import. The submodule is always available via __init__.py
# re-exports but ty cannot detect implicit registration.
# https://github.com/astral-sh/ty/issues/133
possibly-missing-submodule = "ignore"
# Vendored version.py uses tuple comparison with mixed-type elements
# (int, str, InfinityType) for PEP 440 version ordering. ty cannot
# resolve element-wise comparison operators across union-typed tuples.
# https://github.com/astral-sh/ty/issues/1202
unsupported-operator = "ignore"
# ty cannot verify argument types through **kwargs unpacking and
# narrows LiteralString to str differently than mypy. 20 false
# positives, mostly from **call_kwargs and **filter_expr patterns.
# https://github.com/astral-sh/ty/issues/785
invalid-argument-type = "ignore"
# ty doesn't narrow through dict value iteration (item.split() in
# options.py) or union access patterns (Pane | None). 5 false
# positives where mypy correctly narrows the type.
unresolved-attribute = "ignore"
# ty can't see through isinstance narrowing for TypeVars (subscript
# assignment on _V in options.py) and IO[str] | None unions
# (control_mode.py, already suppressed for mypy). 4 false positives.
invalid-assignment = "ignore"
# Pane, Session, Window inherit from Obj which defines defaulted fields;
# subclasses add required server: Server. Python dataclass inheritance
# handles this at runtime but ty's analysis doesn't account for it.
dataclass-field-order = "ignore"
# re.search(rhs, data) in query_list.py where isinstance narrows both
# to str | bytes, but ty can't match the overload (str, str) | (bytes,
# Buffer) against (str | bytes, str | bytes). 2 false positives.
no-matching-overload = "ignore"
# options.py returns dict subscript typed as object where str | int |
# None expected; test_session.py returns MockTmuxCmd where tmux_cmd
# expected (already suppressed for mypy). 2 false positives.
invalid-return-type = "ignore"
# query_list.py b[key] where b is typed as object from a narrowing
# path ty can't follow (same context as unresolved-attribute).
not-subscriptable = "ignore"
# query_list.py filter_(k) where filter_ is a union including
# T@QueryList & Top[(...) -> object] — ty's intersection type
# resolution produces an uncallable Top type.
call-top-callable = "ignore"

[tool.coverage.run]
branch = true
Expand Down
31 changes: 30 additions & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading