PythonアプリのmacOS向けビルドで遭遇し
た起動エラーと解決策
(jaraco.text)

関連記事

1. はじめに

Pythonで作成したアプリケーションをmacOSネイティブアプリとしてパッケージ化する過程で、いくつかの障壁に直面しました。この記事では、py2appを使ったPythonアプリのパッケージ化で遭遇した典型的なエラーとその解決方法を紹介します。コンパイル済み言語と違いPythonは実行環境に依存するため、配布可能なアプリにするには様々な工夫が必要です。

2. 環境

  • macOS 14.7.4
  • Python 3.12
  • py2app(Pythonスクリプトをmacアプリケーション化するツール)
  • Tkinter

3. エラー1:「Launch error」

ビルドしたアプリケーションを実行しようとすると、「Launch error」が表示されました。

このエラーは、起動時に依存ライブラリなどが不足している場合に起こります。しかし、このままではエラーメッセージが読めません。そこで、ターミナルから直接実行ファイルを指定して実行します。

./WhatsAppログ分割ツール.app/Contents/MacOS/WhatsAppログ分割ツール

これはmacOSアプリケーションの内部構造を利用した方法です。macOSのアプリは見た目は単一のファイルのように見えますが、実際には複雑なフォルダ構造になっています。「.app」というフォルダの中に実行ファイルやリソースが格納されているのです。

4. エラー2:モジュールが見つからない問題(jaraco.text)

4.1. 症状

アプリケーションを実行すると、以下のエラーが表示されました。

ModuleNotFoundError: No module named 'jaraco.text'
Code language: JavaScript (javascript)

これは、アプリケーションの実行に必要なモジュールがパッケージに含まれていないことを示しています。py2appはアプリケーションが使用するモジュールを自動的に検出してパッケージに含めようとしますが、依存関係が複雑な場合は全てを正確に把握できないことがあります。

「jaraco.text」モジュールはPythonの拡張ライブラリで、テキスト処理に関する追加機能を提供します。特に「setuptools」や「pkg_resources」といったパッケージングツールの依存関係として使われることが多いモジュールです。主にテキストの操作、分割、結合などの機能を拡張し、Python標準ライブラリにない便利なテキスト処理機能を提供しています。

4.2. 解決策

まず、不足しているモジュールをインストールしました。

pip install jaraco.text
Code language: CSS (css)

次に、setup.pyファイルを修正して明示的に依存関係を指定しました。

OPTIONS = {
    'argv_emulation': True,
    'iconfile': 'app_icon.icns',
    'plist': {
        # ...既存の設定...
    },
    'packages': ['tkinter', 'pkg_resources', 'jaraco'],
    'includes': ['re', 'zipfile', 'os', 'collections', 'threading', 'shutil', 'tempfile', 'jaraco.text'],
}

setup(
    app=APP,
    name='WhatsAppログ分割ツール',
    options={'py2app': OPTIONS},
    setup_requires=['py2app'],
    install_requires=['jaraco.text'],
)
Code language: PHP (php)

これは食材をスーパーで買うときに、レシピに書かれていない隠し味まで漏れなく用意するようなものです。明示的に「jaraco.text」モジュールとその親パッケージである「jaraco」を含めるよう指定することで、パッケージ化の際に必要なモジュールが全て含まれるようになりました。

5. エラー3:setup.pyの構文エラー

5.1. 症状

修正したsetup.pyでビルドを実行すると、新たなエラーが発生しました。

error: error in setup script: command 'py2app' has no such option 'setup_requires'
Code language: JavaScript (javascript)

これは、setup.pyの記述方法に誤りがあることを示しています。具体的には、setup_requiresオプションを間違った場所に指定していました。

5.2. 解決策

setup.pyを以下のように修正しました。

OPTIONS = {
    'argv_emulation': True,
    'iconfile': 'app_icon.icns',
    'plist': {
        # ...既存の設定...
    },
    'packages': ['tkinter', 'pkg_resources', 'jaraco'],
    'includes': ['re', 'zipfile', 'os', 'collections', 'threading', 'shutil', 'tempfile', 'jaraco.text'],
}

setup(
    app=APP,
    name='WhatsAppログ分割ツール',
    options={'py2app': OPTIONS},
    setup_requires=['py2app'],
    install_requires=['jaraco.text'],
)
Code language: PHP (php)

setup_requiresはOPTIONSディクショナリの中ではなく、setup関数の引数として渡す必要があります。

6. エラー4:アプリケーションのクラッシュ(tkinterのメニューバー)

6.1. 症状

前述の問題を修正しビルドしたアプリケーションを実行すると、起動はするものの、すぐにクラッシュしてしまいました。クラッシュレポートによると、以下のエラーが原因でした。

6.1. 症状
Exception Type: EXC_CRASH (SIGABRT)
Thread 0 Crashed:: AppKit -[NSMenuItem initWithTitle:action:keyEquivalent:] + 374
Code language: CSS (css)

これはtkinterライブラリ(Pythonの標準GUIライブラリ)がmacOSのメニューバーを初期化しようとした際に問題が発生したことを示しています。最近のmacOSバージョンでは、セキュリティやパーミッションの関係で、この処理が失敗することがあります。

6.2. 解決策

app.pyファイル(アプリケーションのメインファイル)の先頭に次のコードを追加して、tkinterのメニューバー初期化を無効にしました。

import os
os.environ['tk_nomenus'] = "1"  # tkinterメニューバーを無効化
Code language: PHP (php)

また、setup.pyのOPTIONSも以下のように修正しました。

OPTIONS = {
    'argv_emulation': False,  # Falseに変更
    'iconfile': 'app_icon.icns',
    'plist': {
        # ...既存の設定...
        'NSRequiresAquaSystemAppearance': False,  # ダークモードサポートを追加
        'NSHighResolutionCapable': True,  # Retinaディスプレイのサポートを追加
    },
    # ...他の設定...
}
Code language: PHP (php)

これらの修正は、macOSの現代的な機能とPythonの古いGUIライブラリの間の橋渡しをするようなものです。特にtk_nomenus設定はtkinterに「メニューバーは自分で作らなくていいよ、macOSに任せるから」と伝えるためのものです。

これでアプリケーションが正常に起動できるようになりました。

7. まとめ

PythonアプリケーションをmacOSのネイティブアプリとしてパッケージ化する過程で遭遇した4つの問題について解説しました。

  1. 実行権限の問題 – 直接実行ファイルを指定して解決
  2. モジュールの依存関係 – 不足モジュールをインストールし、setup.pyに明示的に指定
  3. setup.pyの構文エラー – オプションの適切な位置への移動
  4. GUIの初期化クラッシュ – tkinterメニューの無効化と、macOS固有の設定追加

これらの対応によって、Pythonで作成したアプリケーションをmacOSユーザーにも配布可能な形にパッケージ化することができました。今回遭遇した問題と解決策が、同様の課題に取り組む他の開発者の助けになれば幸いです。