PsychoPy のM2 Macへのインストールで難儀した

PsychoPyをMacにインストールするには、２系統の方法があって、一つは、PsychoPyからバイナリをダウンロードしてきてインストールする方法。この方法の場合、Pythonも見えない形で一緒にインストールしてくれるのでPythonのバージョン問題やPythonとPsychoPyとの相性問題で悩む必要がなく簡単で早い。

もう一つは pip でインストールする方法である。エンジニア系の人だったらこっちの方がハードルが低いのではないか。公式サイトには両方のやり方が書いてある。

https://www.psychopy.org/

そもそもは両方をインストールしていて、pipでインストールしたPsychoPyも動いていたのだけど、あるとき、テキストエディターでコードを書いてターミナルで実行していたら、keyboadに関するコードがあると、コードがクラッシュするというのが発端。どうもintel MacではないM1やM2などのApple Siliconを積んだMacだとこのキーボードの問題が発生するらしい。以下のようなコードが１行あるだけでクラッシュした。

```
　kb = keyboard.Keyboard()
```

https://www.psychopy.org/api/hardware/keyboard.html

ただ、バイナリインストールしたPsychoPyならば上記のコードがあっても動くので、バイナリ版とpip版でpythonとpsychopyのバージョンを合わせればクラッシュせずに済むのではないかと思った。なんとかバイナリ版とpip版で同じ挙動をpsychopyにさせたいと思ったのが、事の発端である。

pythonをインストールし直し、必要ならpsychopyもpipからインストールし直す。簡単に済む時はすむけれど、今回は七転八倒の悶絶を繰り返しなんとかインストール完了にたどりついた。何が有効で何がそうではなかったかは、今となってはよくわからない。自分的に肝心となるところだけ記述する。

 - MacBook Air M2, 2022
 - Ventura 13.6.7

最初、pipでインストールしたらこけて、なんでかなと思ったら、psychopyはpythonのバージョンが 3.8 か 3.10 しか正式にはサポートしていない。システムpythonは3.12で対象外だった。なので、pythonのバージョンを合わせてやる必要があるのではないか。

そう思って、当初はバイナリ版pythonの3.8や3.10をPythonのホームページからダウンロードしてきてインストールした。が、うまくいかなかった。しかも、複数のPythonをバイナリから入れて、どのように切り替えれば良いのかわからないという問題もある。フルパスで実行するか、エイリアスを書き換えるかだが、前者は面倒だし、後者はどこにエイリアスがあるのかよくわからない＆書き換えたら元に戻すの忘れそうということで、この方法は頓挫。

次に、pyenvを使って複数pythonを共存させようとした。pyenvは、homebrewに頼らないかたちで実行してインストールした。そして、pyenvが入ったら、pyenvから、pythonのインストールがうまくいかないという事態が発生。せっかくpyenv入れたのにpythonのインストールに失敗するってなんやねん。

それで、pyenvの入れ方が悪かったのかと思い、brewでpyenvをインストールしなおしたが症状は変わらなかった。途中でXcodeやCommandLineToolsを入れ直したがだめだった。

どうもエラーを眺めていると、clangなどのコンパイルで問題が起こっているようで、M2　Macなどのintel Macから別のCPUに変わったことに起因するエラーのように感じられた。

なので、どうせさして使っていないしということでhomebrewを再インストールした。

https://qiita.com/gabe-ds/items/ff5a7a7e0878e919ba51

そして、新生したhomebrewから pyenv をインストールした。

https://github.com/pyenv/pyenv

    brew update
    brew install pyenv

 homebrewのアンインストール

そうしてpyenvが入って、pyenv経由でpythonをインストールしたがまたエラー。しかし今回は、どうも xz というインストールが不足しているらしいというのがエラーから読み取れた。

https://gist.github.com/iandanforth/f3ac42b0963bcbfdf56bb446e9f40a33

    brew install xz

これで xz を入れて、再度 pythonのインストールを試みたところ、pythonはインストールできた。

この時、それ以前の試行錯誤から、M1/M2系　Mac には、特定のマイナーバージョンのPythonしかインストールできないという情報があった。他のバージョンを試していないので、わからないが、以下のバージョンのうち、自分が必要な3.8.13と3.10.3のインストールだけ試みて成功した。

    3.7.13, 3.8.13, 3.9.11, 3.10.3

https://stackoverflow.com/questions/71577626/issues-installing-python-3-8-10-on-macos-12-3-monterey

今回使いたい3.8系にしても3.10系にしても上記よりもより新しいマイナーバージョンが存在するが、そちらは指定せずに上記のバージョンを指定した。

pythonが入ったので、次はpipである。pipを実行するとエラーになった。どうもpipの指し示すpythonがすでに消えてしまっているpythonで、pythonが実行できずエラーになる。pipはインストールしなおしたが症状が改善しないので、そのpipファイルを削除して、それからもう一度pipをインストールした。

複数バージョンでpipを使う場合、複数のバージョンのpythonが共通のpipを使うことになるので、モジュールのバージョン管理がカオスになることが予想できたし、pipenvというそちらの管理ソフトもあるみたいだが、今回は割愛した。

そうしてやっと、psychopyのインストールである。

    pip install psychopy

これでインストールを試みたが、当然のようにエラーになった。（長い）

    Collecting tables<3.9 (from psychopy)
      Using cached tables-3.8.0.tar.gz (8.0 MB)
      Installing build dependencies ... done
      Getting requirements to build wheel ... error
      error: subprocess-exited-with-error
  
      × Getting requirements to build wheel did not run successfully.
      │ exit code: 1
      ╰─> [11 lines of output]
          ld: library not found for -lhdf5
          clang: error: linker command failed with exit code 1 (use -v to see invocation)
          <string>:19: DeprecationWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html
          cpuinfo failed, assuming no CPU features: 'flags'
          * Using Python 3.8.13 (default, Jun 15 2024, 16:01:24)
          * Found cython 3.0.10
          * USE_PKGCONFIG: True
          .. ERROR:: Could not find a local HDF5 installation.
             You may need to explicitly state where your local HDF5 headers and
             library can be found by setting the ``HDF5_DIR`` environment
             variable or by using the ``--hdf5`` command-line option.

というエラーでどうも、 hdf5というのが足りないらしい。

https://qiita.com/ShotaMiyazaki94/items/e0a25e5a92afa1ee9677

    brew install hdf5
    export HDF5_DIR="$(brew --prefix hdf5)"

これで再度psychopyのイントールを試みて、エラーだったがエラー内容は変わった。


    Collecting tables<3.9 (from psychopy)
      Using cached tables-3.8.0.tar.gz (8.0 MB)
      Installing build dependencies ... done
      Getting requirements to build wheel ... error
      error: subprocess-exited-with-error
  
      × Getting requirements to build wheel did not run successfully.
      │ exit code: 1
      ╰─> [59 lines of output]
        ld: library not found for -llzo2
        clang: error: linker command failed with exit code 1 (use -v to see invocation)
        ld: library not found for -llzo
      clang: error: linker command failed with exit code 1 (use -v to see invocation)
      ld: library not found for -lblosc
      clang: error: linker command failed with exit code 1 (use -v to see invocation)
      <string>:19: DeprecationWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html
      performance hint: tables/utilsextension.pxd:20:26: No exception value declared for 'get_native_type' in pxd file.
      Users cimporting this function and calling it without the gil will always require an exception check.
      Suggest adding an explicit exception value.
      
      Error compiling Cython file:
      ------------------------------------------------------------
      ...
      
      
      def _dump_h5_backtrace():
          cdef object bt = []
      
          if H5Ewalk(H5E_DEFAULT, H5E_WALK_DOWNWARD, e_walk_cb, <void*>bt) < 0:
                                                     ^
      ------------------------------------------------------------
      
      tables/utilsextension.pyx:384:47: Cannot assign type 'herr_t (unsigned int, const H5E_error_t *, void *) except? -1 nogil' to 'H5E_walk_t' (alias of 'herr_t (*)(unsigned int, H5E_error_t *, void *) noexcept nogil'). Exception values are incompatible. Suggest adding 'noexcept' to the type of 'e_walk_cb'.
      cpuinfo failed, assuming no CPU features: 'flags'
      * Using Python 3.8.13 (default, Jun 15 2024, 16:01:24)
      * Found cython 3.0.10
      * USE_PKGCONFIG: True
      * pkg-config header dirs for HDF5: /opt/homebrew/Cellar/hdf5/1.14.3_1/include
      * pkg-config library dirs for HDF5: /opt/homebrew/Cellar/hdf5/1.14.3_1/lib
      * Found HDF5 headers at ``/opt/homebrew/Cellar/hdf5/1.14.3_1/include``, library at ``/opt/homebrew/Cellar/hdf5/1.14.3_1/lib``.
      * Could not find LZO 2 headers and library; disabling support for it.
      * Could not find LZO 1 headers and library; disabling support for it.
      * Could not find bzip2 headers and library; disabling support for it.
      * Could not find blosc headers and library; using internal sources.
      * Found blosc2 headers at ``/private/var/folders/nn/c08xd2bx3nxcrpgyqrxtydhr0000gn/T/pip-build-env-j_r6v_9h/overlay/include``, library at ``/private/var/folders/nn/c08xd2bx3nxcrpgyqrxtydhr0000gn/T/pip-build-env-j_r6v_9h/overlay/lib``.
        * Copying blosc2 runtime library to 'tables' dir because it was not found in standard locations
      Compiling tables/utilsextension.pyx because it changed.
      [1/1] Cythonizing tables/utilsextension.pyx
      Traceback (most recent call last):
        File "/Users/<username>/.pyenv/versions/3.8.13/lib/python3.8/site-packages/pip/_vendor/pyproject_hooks/_in_process/_in_process.py", line 353, in <module>
          main()
        File "/Users/<username>/.pyenv/versions/3.8.13/lib/python3.8/site-packages/pip/_vendor/pyproject_hooks/_in_process/_in_process.py", line 335, in main
          json_out['return_val'] = hook(**hook_input['kwargs'])
        File "/Users/<username>/.pyenv/versions/3.8.13/lib/python3.8/site-packages/pip/_vendor/pyproject_hooks/_in_process/_in_process.py", line 118, in get_requires_for_build_wheel
          return hook(config_settings)
        File "/private/var/folders/nn/c08xd2bx3nxcrpgyqrxtydhr0000gn/T/pip-build-env-j_r6v_9h/overlay/lib/python3.8/site-packages/setuptools/build_meta.py", line 325, in get_requires_for_build_wheel
          return self._get_build_requires(config_settings, requirements=['wheel'])
        File "/private/var/folders/nn/c08xd2bx3nxcrpgyqrxtydhr0000gn/T/pip-build-env-j_r6v_9h/overlay/lib/python3.8/site-packages/setuptools/build_meta.py", line 295, in _get_build_requires
          self.run_setup()
        File "/private/var/folders/nn/c08xd2bx3nxcrpgyqrxtydhr0000gn/T/pip-build-env-j_r6v_9h/overlay/lib/python3.8/site-packages/setuptools/build_meta.py", line 311, in run_setup
          exec(code, locals())
        File "<string>", line 928, in <module>
        File "<string>", line 923, in get_cython_extfiles
        File "/private/var/folders/nn/c08xd2bx3nxcrpgyqrxtydhr0000gn/T/pip-build-env-j_r6v_9h/overlay/lib/python3.8/site-packages/Cython/Build/Dependencies.py", line 1154, in cythonize
          cythonize_one(*args)
        File "/private/var/folders/nn/c08xd2bx3nxcrpgyqrxtydhr0000gn/T/pip-build-env-j_r6v_9h/overlay/lib/python3.8/site-packages/Cython/Build/Dependencies.py", line 1321, in cythonize_one
          raise CompileError(None, pyx_file)
      Cython.Compiler.Errors.CompileError: tables/utilsextension.pyx
      [end of output]
  
    note: This error originates from a subprocess, and is likely not a problem with pip.
    error: subprocess-exited-with-error

    × Getting requirements to build wheel did not run successfully.
    │ exit code: 1
    ╰─> See above for output.
    
    note: This error originates from a subprocess, and is likely not a problem with pip.

エラーを読むと相変わらずtablesというモジュールのインストールに失敗している。tablesは3.9.0より低いものが要求されていて、3.8.0が選択されて、インストールを試みて失敗している。

https://www.pytables.org/release_notes.html

ここで、なぜか覚えてないが、直感的にもっと古いバージョンのtablesならばインストールが通るのではないかと思い、一つ下のバージョンのインストールを試した。

    pip install tables==3.7.0

そうしたところ、tablesがインストールされた。
そして、どうせまたエラーが出るだろうと思いつつ psychopyのインストールにすすんだところ、今度はインストールが完了した。

    pip install psychopy 

というわけで、試行錯誤の末、psychopyをpip経由でインストールするのに成功した。これで、PsychoPy Coderで書いたスクリプをターミナルでも動かせる。