Trouble shoot when using PyInstaller with PyFtdi.
今日は小ネタです。ただし、ネットを探しても解決策を示しているサイトを見つけられなかったので有益な情報かも知れません(!?)。あるいは、特定のバージョンのライブラリやツールに依存しているだけ、かも知れません。
PyInstaller は、Python で書かれたコードをバイナリ実行形式(Windows で言うと .exe 形式)に変換できる便利なツールです。また、PyFtdi は、FTDI 社の USB-シリアルブリッジ FT2232H などを効果的に利用できる、これまた重要なツールです。
私の試していたバージョン
最初にバージョンを明確にしておきます。(全ての Python 関連ツール、パッケージが最新でないのには事情があります。気にしないでください。)
- OS: Windows 10 64-bit 日本語版(2021年5月頃のセキュリティパッチが全て当たっているもの)
- Python 3.8.5 (tags/v3.8.5:580fbb0, Jul 20 2020, 15:57:54) [MSC v.1924 64 bit (AMD64)] on win32
- pyftdi==0.51.2
- pyserial==3.4(pyftdi が依存)
- pyusb==1.1.1(pyftdi が依存)
- pyinstaller==4.3
私はこの 2つのツールを組み合わせて使おうとしていたのですが、いくつかのトラブルに遭遇しました。
あ、そうそう。いずれのデバッグ時にも、PyInstaller の .spec ファイルで debug=True、console=True とし、エラーメッセージを確認できるようにしておくことが大切です。
usb.core.NoBackendError: No backend available
最初は、こちらです。
このエラーは、libusb の DLL が組み込まれていないことと、Python の usb パッケージが pyinstaller に認識されないために起きる問題のようです。(私は詳細を追いかけていないので不明)
こちらの解決法は、以下のサイトにあります。
- Pyinstaller, no usb backend · Issue #1449 · pyinstaller/pyinstaller
- usb.core NoBackendError · Issue #1891 · pyinstaller/pyinstaller
結論から言うと、PyInstaller の .spec ファイルを編集し、
binaries = [
('C:\\Windows\\System32\\libusb0.dll', '.'),
]
a = Analysis(['foobar.py'],
pathex=['C:\\somewhere'],
binaries=binaries,
datas=[],
hiddenimports=['usb'],
binaries = の行(2カ所)と hiddenimports の行(1カ所)を変更します。
ValueError: invalid URL, protocol ‘ftdi’ not known
一方で、こっちのエラーに言及しているサイトは見つかりませんでした。私の特殊事情、あるいは特定のバージョンに依存する問題かも知れません。
これは、Python コードで pyftdi.serialext.serial_for_url() を呼び出すときに発生します。特に、最初の引数が ftdi:// で始まっているときがそうです。
この問題は、次のような経緯で発生します。pyftdi.serialext の __init__.py では、内部で pyserial の __init__.py を呼び出すのですが、その中で動的に pyftdi.serialext.protocol_ftdi をインポートしているのですが、PyInstaller はこの動的な依存性に気付かず、pyftdi.serialext.protocol_ftdi を取り込んでくれないというものです。TL;DR;
結論から言うと、PyInstaller の .spec ファイルの中で、
hiddenimports=['usb', 'pyftdi.serialext.protocol_ftdi'],
のように pyftdi.serialext.protocol_ftdi を指定してあげることで解決します。
今日はここまで。もし、皆様のお役に立ちましたら幸いです。