恐らく、WindowsでVSCodeを使ってリモートのLinux上のPythonプログラムをデバッグするような人はレアなんでしょうが、私が引っかかって具体的な対処も書かれておらずとても辛かったので、起こった問題、原因、対処法をここで共有します。
また、対処は暫定的なものです。
TL;DR
Windows上のVSCodeにおいて、ドライブレターの大文字・小文字の扱いにより本問題が起きている。
対処としては、launch.json
のpathMappings
のlocalRoot
はドライブレターを小文字で指定すると動作するようになる。
VSCodeには便利な変数${workspaceFolder}
があるので指定したくなるが、これだとドライブレターが大文字になり、本問題が起きる。
関連Issue: https://github.com/Microsoft/vscode-python/issues/2976
そもそもWindowsを使わなければ回避できる
問題
遭遇した環境は以下のような感じ。バージョンが古いという事はあまり関係なさそう。
- VSCode: 1.32.3
- VSCode Python Extension: 2019.3.6352
- Python: 3.6.6
- ptvsd: 4.2.6
リモート環境はLinux上のコンテナ(docker)でPythonアプリをptvsdを仕込んでリモートデバッグの待ち受けを行っている。
この状態でVSCodeでAtattchしてやるとちゃんと動き始めるが、設定したブレークポイントが効かない。
launch.json
はこんな感じで、別に何も新しいことはしてない。ドキュメント通りやっている。
doc: https://code.visualstudio.com/docs/python/debugging#_remote-debugging
{
"version": "0.2.0",
"configurations": [
{
"name": "Python: Remote Attach",
"type": "python",
"request": "attach",
"port": 5678,
"host": "192.168.XXX.XXX",
"pathMappings": [
{
"localRoot": "${workspaceFolder}",
"remoteRoot": "/mnt"
}
],
}
],
}
/mnt
配下にworkspaceFolderの内容のファイルがある。
例外を起こしたときに出てくるスタックトレースからコードジャンプすることは出来るので、マッピングのミスではない気はする。
原因
結構頑張って調べた。
まず、マッピングが正しくない(正確にはファイルパスの置き換え後の結果がリモート上に存在しない)場合、Debug Consoleに以下のようなログが出力され、ブレークポイントが機能しない。
pydev debugger: warning: trying to add breakpoint to file that does not exist: /mnt/c:/path/to/program.py (will have no effect)
これはptvsd上はlaunch.json
のpathMappings
の値を素直にそのまま使うのと、python拡張がsetBreakPoints時に送るファイルパスを小文字で送るという2点の関係から起きているらしい。
実際の動きを追うために以下の2点を行う。
-
launch.json
の設定に"logToFile": true,
を与える - リモート側の環境変数に
DEBUG_PYDEVD_PATHS_TRANSLATION=1
を設定する(docker runなら--env DEBUG_PYDEVD_PATHS_TRANSLATION=1
で渡す)
これでファイルパスの置き換えに関するログがデバッグコンソール側に出力されるようになり、ptvsdとのやり取り(JSON)のログが出力される。(extensionが配置されているのフォルダにログが出力される。具体的には%USER_PROFILE%\.vscode\extensions\
の下など。)
${workspaceFolder}
の挙動を見るには、pathMappingsの値を送る時の値を見れば分かる。
以下のようなログがファイルへ出る。(JSONフォーマット済み、一部センシティブな情報を編集済み)
From Client:
Content-Length: 412
{
"command": "attach",
"arguments": {
"preLaunchTask": "Docker run",
"name": "Python: Remote Attach",
"type": "python",
"request": "attach",
"port": 5678,
"host": "192.168.XXX.XXX",
"pathMappings": [
{
"localRoot": "C:\\Path\\To\\Project",
"remoteRoot": "/mnt"
}
],
"logToFile": true,
"debugOptions": [
"RedirectOutput",
"WindowsClient"
],
"workspaceFolder": "c:\\Path\\To\\Project",
"__sessionId": "0a28fee6-35a0-47cf-826b-XXXXXXXXX"
},
"type": "request",
"seq": 2
}
arguments.pathMappings.localRoot
の値が大文字になっている。なので、${workspaceFolder}
はドライブレターを大文字で扱っていることがわかる。
一方で、ブレークポイントを設定する際は、以下のようなログがファイルへ出る。
From Client:
Content-Length: 210
{
"command": "setBreakpoints",
"arguments": {
"source": {
"sourceReference": 0,
"path": "c:/Path/To/Program.py"
},
"lines": [
65,
71
],
"breakpoints": [
{
"line": 65
},
{
"line": 71
}
],
"sourceModified": false
},
"type": "request",
"seq": 16
}
arguments.source.path
の値が小文字になっている。
そして、ptvsd側は以下の処理で前方完全一致で置き換えようとする。
※(eclipse_prefix, python_prefix) = (localRoot, remoteRoot)
で、translated
はブレークポイント設定時のファイルパス。
ここで置き換えが出来ないと、リモート上のファイルパスとブレークポイントのファイルパスが異なるのでブレークポイントが効かなくなる。といった寸法らしい。
なので、ここを一致させればよい。手っ取り早いのはlaunch.json
のarguments.pathMappings.localRoot
の値を都合よく合わせる方法だと思う。
具体的には、${workspaceFolder}
ではなくc:\\Path\\To\\Project
という形でドライブレターを小文字で明示すればよい。
これに関してはissueも上がっているが、あまり議論が進んでいないように見える。
launch.json
のlocalRootに${workspaceFolder}
を使えないのは面倒なので治ってほしい・・・