Rust製のPythonの型チェッカー& Language Server tyを試す

本記事は以前はてなブログに投稿していた記事の内容を加筆修正して移行したものです。

tyとは?

tyとはuvRuffの開発で有名なAstralの開発しているRust製の型チェッカー&Language Serverです。 Pythonの型チェッカーとしてはmypy,pyrightが、Language Serverとしてはpls,pyright(pyrightは型チェッカーとしても)が良く使われています。

CLIで型チェッカーとして試しているブログ記事には以下のようなものがありますが、 コードエディタから使用している例はざっと探したところ見つからなかったので実際に試してみました。

検証

tyのインストール

今回は$HOME/try_tyディレクトリの中にuvで仮想環境を構築して、その環境中にinstallしtyへのfull pathを使用してNeovim側から呼び出すことにします。

1
2
3
$ uv init # 環境を用意

$ uv add ty # install

uv run tyを実行して以下のようなメッセージが表示されればinstallはOKです。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
$ uv run ty # installされたことをチェック
An extremely fast Python type checker.

Usage: ty <COMMAND>

Commands:
  check    Check a project for type errors
  server   Start the language server
  version  Display ty's version
  help     Print this message or the help of the given subcommand(s)

Options:
  -h, --help     Print help
  -V, --version  Print version

Neovim側の設定

NeovimビルドインのLSPクライアントを利用する場合では、 neovim/nvim-lspconfig を利用して

1
require('lspconfig').ty.setup({})

とするのが一般的ですが、(nvim-lspconfig/lsp/)、にはty.luaがあるものの、nvim-lspconfig/lua/lspconfig/configs/には、存在しないため、

1
[lspconfig] config "ty" not found. Ensure it is listed in `configs.md` or added as a custom server.

とエラーが出てLanguage Serverを起動できません。 Neovim 0.11.1以降では、ビルドインのlspのconfigが存在しますが、書き方に慣れていないので今回はvim.api.nvim_create_autocmd()で呼び出す方法で設定を書きました。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
local home_dir = vim.env.HOME
local path_to_ty = home_dir .. '/try_ty/.venv/bin/ty'

-- ty
vim.api.nvim_create_autocmd('FileType', {
  pattern = { 'python' },
  callback = function()
    vim.lsp.start({
      name = 'ty',
      cmd = {
        path_to_ty,
        'server',
      },
      capabilities = vim.lsp.protocol.make_client_capabilities(),
    })
  end,
})

-- 普段使用しているpyrightは一旦無効化
-- require('lspconfig').pyright.setup(pyright_config)

サーバーの起動チェック

まず、Neovimで適当な*.pyなファイルを開き、サーバーが起動するかを以下のコマンドでチェックしてみます。

1
2
$ ps aux | grep 'ty server'
aki       295362  0.3  0.1 3410496 38304 ?       Ssl  00:03   0:00 /home/aki/try_ty/.venv/bin/ty server

無事起動しているようですね!。

補完が効くかチェック

Language Serverと言えばコード補完ということで、適当なコードを書いてコード補完が効くが見てみます。 今回は以下のような簡単な関数を書いて呼び出すコードを書いてみます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
def sum_to_n(n: int) -> int:
    sum = 0
    for i in range(n + 1):
        sum += i

    return sum


def main() -> None:
    print(f"sum_to_10 = {sum_to_n(10)}")


if __name__ == "__main__":
    main()

コーディング中では以下のように補完が表示されました。

コーディング中の型チェック

Pythonでは型ヒントの書かれたコードを対応した型チェッカーで静的にチェックすることができます。

ここでは、以下のような明らかにシグニチャと実際に返している値の型が違う場合に、警告が出るか試してみます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
def greet(name: str) -> str:
    res = f"Hello {name}"

    return 123


def main() -> None:
    greet("Ema")


if __name__ == "__main__":
    main()

結果として以下のように赤色で型の間違いを指摘くれました。

その他

補完と型チェック以外では以下の操作が可能でした。

  • 定義元へのジャンプ
  • カーソルのあるオブジェクトを参照しているコードの列挙
  • コード中のシンボルの列挙
  • カーソルのある変数の情報を表示

一方、変数名の一括変更は[LSP] Rename, no matching language server with rename capability.と出て出来ませんでした。 (追記@2025 12/25 14:00 rename(textDocument/rename)は実装されていました) (その他のfeatureについてはty/Feature referenceを参照してください)

感想

とりあえず、tyをNeovimから使用し、LSPによるコーディング支援機能はほぼ使用することができました。 GitHubをのプロジェクトページを見るとまだ発展途上とのことですので、今後に期待です!。

追記

tyを試す環境について

現在では、Nixを使用しているので一時的に使用したい場合では以下のようにnix-shellを作るためのflake.nixを作成して

  • flake.nix
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
  description = "use ty";

  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
  };

  outputs =
    { self, nixpkgs }:
    let
      pkgs = nixpkgs.legacyPackages."x86_64-linux";
    in
    {
      devShells."x86_64-linux".default = pkgs.mkShell {

        packages = [
          pkgs.ty
          # その他のパッケージ
        ];

      };
    };
}

以下を実行し、その中でuvを動かします(Luaで絶対PATHをの注入するような面倒臭いコードは書く必要はありません!)。

1
nix develop

もっと簡単にやりたい場合では、

1
nix-shell -p uv

もしくは

1
nix shell nixpkgs#uv

uvの導入したnix-shellを直接作れば良いです。

buildinのLSPマネージャーから呼び出す設定について

執筆した当時では、buildinのLSPマネージャーを使用せずにnvim-lspconfigを使用していました。 現在は他のlanguage-serverでもbuildinのLSP構成を使用しています。

tyを使う場合の設定は以下のように設定します。

1
2
3
4
5
6
7
8
9
local ty_config = {
  settings = {},
  on_attach = function(client, bufnr)
    utl.navic.attach(client, bufnr)
  end,
}

vim.lsp.config.ty = ty_config
vim.lsp.enable({ 'ty' })

また普段はpyrightを使用して、使用したい場合のみtyが起動するようにする場合は以下のように 環境変数TYが有効化された場合にのみtyを有効化するように設定します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
local pyright_config = {
    -- 略
}

local ty_config = {
  settings = {},
  on_attach = function(client, bufnr)
    utl.navic.attach(client, bufnr)
  end,
}

-- pyright or ty
if os.getenv('TY') then
  vim.lsp.config.ty = ty_config
  vim.lsp.enable({ 'ty' })
else
  vim.lsp.config.pyright = pyright_config
  vim.lsp.enable({ 'pyright' })
end

require('formatter').setup({
  filetype = {
    python = {
      require('formatter.filetypes.python').ruff,
    },
  },
})
CC BY
Hugo で構築されています。
テーマ StackJimmy によって設計されています。