numpyインストールで四苦八苦
Pythonでは超有名なパッケージnumpyを試してみようとpipを使ってインストールし、テスト用のスクリプトimportをしたらエラーになりました。
Pythonはパッケージ管理が簡単・素早く・楽チンだと聞いていたので、こういうエラーは私のようなPython初心者殺しです。
C:\temp\python>py -3.8 test.py
Traceback (most recent call last):
File "test.py", line 1, in <module>
import numpy
File "C:\Users\Uname\AppData\Local\Programs\Python\Python38\lib\site-packages\numpy\__init__.py", line 140, in <module>
from . import _distributor_init
File "C:\Users\Uname\AppData\Local\Programs\Python\Python38\lib\site-packages\numpy\_distributor_init.py", line 26, in <module>
WinDLL(os.path.abspath(filename))
File "C:\Users\Uname\AppData\Local\Programs\Python\Python38\lib\ctypes\__init__.py", line 373, in __init__
self._handle = _dlopen(self._name, mode)
OSError: [WinError 193] %1 は有効な Win32 アプリケーションではありません。
このエラーはアーキテクチャが異なる場合に発生するみたいです。たとえば、32ビットのPythonに64ビットのパッケージをインストールしようとした場合などです。
私の場合は64ビットのPythonをインストールしたのに、32ビットのパッケージをインストールしようとして発生しているエラーでした。
最終的にはnumpyをインストールすることができたのですが、インストールするまでに試した手順を残すことにしました。今後も同じような目にあったときの備忘録としても役立つ可能性があるからです。
再インストールしてみる
一度、pip uninstall numpyを実行し、再度インストールしました。確認してみると、たしかにwin32.whlがインストールされていたことがキャッシュの内容からわかります。
Windowsの64ビット版はファイル名の末尾がwin_amd64.whlになります。
C:\temp\python>py -3.8 -m pip install numpy
Collecting numpy
Using cached numpy-1.18.3-cp38-cp38-win32.whl (10.8 MB)
Installing collected packages: numpy
Successfully installed numpy-1.18.3
uninstallとinstallという2段階の手続きを踏むのが面倒な人はpip install numpy --forceと強制インストールするオプションスイッチをつけると一発で再インストールできます。
python.exeの確認
誤ってpython.exeの32ビット版をインストールしていないかどうかを確認しました。起動メッセージからもパッケージplatformからも、64ビット版をインストールしていることが確認できました。
C:\temp\python>py -3.8
Python 3.8.2 (tags/v3.8.2:7b3ab59, Feb 25 2020, 23:03:10) [MSC v.1916 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import platform
>>> platform.architecture()
('64bit', 'WindowsPE')
>>> exit()
pipのバージョン確認
pipが悪さをしているのか、バージョンを確認します。
C:\temp\python>py -3.8 -m pip -V
pip 20.1 from C:\Users\Uname\AppData\Local\Programs\Python\Python38\lib\site-packages\pip (python 3.8)
念のため、最新版へアップグレードされていることを確認します。already up-to-dateとなっており、最新版になっていることが確認できました(2020年4月30日時点)。
C:\temp\python>py -3.8 -m pip install --upgrade pip
Requirement already up-to-date: pip in c:\users\Uname\appdata\local\programs\python\python38\lib\site-packages (20.1)
Whlファイルからインストール
Wheel形式のファイルを次のサイトからダウンロードし、直接的にインストールしてみます。インストールされているpython.exeのバージョンが3.8、64ビット版なので、cp38-cp38-win_amd64.whlになっているものをダウンロードします。
Unofficial Windows Binaries for Python Extension Packages
https://www.lfd.uci.edu/~gohlke/pythonlibs/#numpy
ダウンロードしたら、Wheel形式のファイルがあるフォルダへ直接移動し、pip installコマンドを叩きます。
C:\Downloads>py -3.8 -m pip install hogehoge4.3-cp38-cp38-win_amd64.whl
ERROR: hogehoge4.3-cp38-cp38-win_amd64.whl is not a supported wheel on this platform.
not a supportedというエラーになってしまいました。
py launcherを確認する
「もしかしたらpy.exeが64ビットではないのだろうか?」と思ったので、dumpbinコマンドを使って実行ファイルのビット数を確認しました。
dumpbinコマンドはVisual Studioに付属しているツールです。確認してみると32ビットになっていました。
C:\Windows>dumpbin /headers py.exe | findstr machine
14C machine (x86)
32 bit word machine
しかし、ラウンチャはpython.exeを起動しているだけなのでpy.exeそのものには問題はないような気がします。
何かしらのときのために、ひとつの情報として残しておきます。
python.exeからpipを叩く
次は直接python.exeの存在するフォルダへ移動し、pipインストールを試みます。
pip uninstall numpyを実行して、再インストールしましたが、やはり32ビットを見に行ってしまいます。
C:\Users\Uname\AppData\Local\Programs\Python\Python38>python -m pip install numpy
Collecting numpy
Using cached numpy-1.18.3-cp38-cp38-win32.whl (10.8 MB)
Installing collected packages: numpy
Successfully installed numpy-1.18.3
Wheel形式ファイルを直接見に行ってもやはり結果は同じです。
C:\Downloads>\Users\Uname\AppData\Local\Programs\Python\Python38\python.exe -m pip install "numpy-1.18.3+mkl-cp38-none-win_amd64.whl"
ERROR: numpy-1.18.3+mkl-cp38-cp38-win_amd64.whl is not a supported wheel on this platform.
ファイルをリネームする
次のサイトを見ると、ファイル名からバージョン判定をしているとの記述があったので、cp38-cp38の部分をcp38-noneへリネームして試してみました。
俺言語。
http://oregengo.hatenablog.com/entry/2016/12/20/175835
それでも結果は同じでした。
C:\Downloads>ren "numpy-1.18.3+mkl-cp38-cp38-win_amd64.whl" "numpy-1.18.3+mkl-cp38-none-win_amd64.whl"
C:\Downloads>py -3.8 -m pip install "numpy-1.18.3+mkl-cp38-none-win_amd64.whl"
ERROR: numpy-1.18.3+mkl-cp38-none-win_amd64.whl is not a supported wheel on this platform.
もう、やぶれかぶれです。ファイル名をcp38-cp38-win32.whlにを変えてしまいましょう。環境が壊れたら32ビット版pythonをインストールして凌ぎます。
C:\Downloads>ren "numpy-1.18.3+mkl-cp38-none-win_amd64.whl" "numpy-1.18.3+mkl-cp38-cp38-win32.whl"
C:\Downloads>py -3.8 -m pip install "numpy-1.18.3+mkl-cp38-cp38-win32.whl"
Processing c:\downloads\numpy-1.18.3+mkl-cp38-cp38-win32.whl
Installing collected packages: numpy
Attempting uninstall: numpy
Found existing installation: numpy 1.18.3
Uninstalling numpy-1.18.3:
Successfully uninstalled numpy-1.18.3
WARNING: The script f2py.exe is installed in 'C:\Users\Uname\AppData\Local\Programs\Python\Python38\Scripts' which is not on PATH.
Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
Successfully installed numpy-1.18.3+mkl
なんと、インストールできてしまいました。
numpyサンプルを試す
次のサイトにあるnumpyのサンプルスクリプトを使ってnumpyが動くかどうかを確認してみます。
【Python】Numpyのサンプル
https://qiita.com/yut-nagase/items/94b4798eb88de16b36be
問題なく動いています。
C:\temp\python>py -3.8 test.py
[100 200 300 400 500]
[0.1 0.2 0.3 0.4 0.5]
[100.1 200.2 300.3 400.4 500.5]
numpyをインストールする目的は達成できましたが、いまいち釈然としません。なんでWin32用のモジュールを見に行ってしまうのでしょうか?
pipモジュールを調べる
ここからは私の趣味です。
そもそもの原因を知りたいので、仕方なくpipモジュールをコードリーディングしてみることにしました。
結果から言うと、小一時間程度の調査ではわかりませんでした。ですので、お時間のある方は読み物として楽しんでください。
いくつかのファイルを眺めてみたらsysモジュールのplatformを参照しているらしき箇所を見つけました。まずはここを切り口に調べることにしました。
--- pip\_internal\utils\filesystem.py ---
29: def check_path_owner(path):
30: # type: (str) -> bool
31: # If we don't have a way to check the effective uid of this process, then
32: # we'll just assume that we own the directory.
33: if sys.platform == "win32" or not hasattr(os, "geteuid"):
34: return True
実際、pythonを起動してsys.platformの値を表示させると"win32"となっていました。おそらくpipインストール時にはsys.platformを参照しているのだとアタリをつけます。
C:\temp\Python>py -3.8
Python 3.8.2 (tags/v3.8.2:7b3ab59, Feb 25 2020, 23:03:10) [MSC v.1916 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> print(sys.platform)
win32
>>>
Pythonのソースコードを探す
sysモジュールはパッケージ化されています。つまりビルド済みなのでソースコードを遡る必要があるということです。Pythonのソースコードを見れば、何かしらのヒントはあるのでしょうか?
ソースコードをダウンロードし、ファイル名からそれらしき名前のものを探してみると、sysmodule.cというソースコードを見つけることができました。
--- Python-3.8.2\Python\sysmodule.c ---
2669: SET_SYS_FROM_STRING("platform",
2670: PyUnicode_FromString(Py_GetPlatform()));
Pythonのパッケージ開発をしたことはありませんが、文字列"platform"に紐づけているのだと思います。試しにpython.exeを起動し、この行の前後にあるソースコードから文字列を拾って「sys.ほにゃらら」と打ち込んでみたら、それらのモジュールが呼ばれたので間違いないと思います。
マクロSET_SYS_FROM_STRINGを使って、文字列と実行体である関数を紐づけていることが確認できたので、今度は文字列"platform"に紐づけられている関数Py_GetPlatformを探します。
ずばり、getplatform.cというソースコードがありました。
--- Python-3.8.2\Python\getplatform.c ---
2: #include "Python.h"
3:
4: #ifndef PLATFORM
5: #define PLATFORM "unknown"
6: #endif
7:
8: const char *
9: Py_GetPlatform(void)
10: {
11: return PLATFORM;
12: }
ヘッダPython.hをインクルードしているので、ソースコードを辿ります。しかし、Python.hにはPLATFORMの定義はありませんでした。そこでPython.hからインクルードしているヘッダを辿っていきます。
マクロPLATFORMを定義しているのはヘッダpyconfig.hです。該当する箇所を調べてみるとMS_WIN64でもマクロPLATFORMを"win32"で定義しています。きっとここが悪さをしているのかもしれません。
--- Python-3.8.2\PC\pyconfig.h ---
287: #if defined(MS_WIN64)
288: /* maintain "win32" sys.platform for backward compatibility of Python code,
289: the Win64 API should be close enough to the Win32 API to make this
290: preferable */
291: # define PLATFORM "win32"
292: # define SIZEOF_VOID_P 8
293: # define SIZEOF_TIME_T 8
294: # define SIZEOF_OFF_T 4
295: # define SIZEOF_FPOS_T 8
296: # define SIZEOF_HKEY 8
297: # define SIZEOF_SIZE_T 8
298: /* configure.ac defines HAVE_LARGEFILE_SUPPORT iff
299: sizeof(off_t) > sizeof(long), and sizeof(PY_LONG_LONG) >= sizeof(off_t).
300: On Win64 the second condition is not true, but if fpos_t replaces off_t
301: then this is true. The uses of HAVE_LARGEFILE_SUPPORT imply that Win64
302: should define this. */
303: # define HAVE_LARGEFILE_SUPPORT
304: #elif defined(MS_WIN32)
305: # define PLATFORM "win32"
306: # define HAVE_LARGEFILE_SUPPORT
307: # define SIZEOF_VOID_P 4
308: # define SIZEOF_OFF_T 4
309: # define SIZEOF_FPOS_T 8
310: # define SIZEOF_HKEY 4
311: # define SIZEOF_SIZE_T 4
312: /* MS VS2005 changes time_t to a 64-bit type on all platforms */
313: # if defined(_MSC_VER) && _MSC_VER >= 1400
314: # define SIZEOF_TIME_T 8
315: # else
316: # define SIZEOF_TIME_T 4
317: # endif
318: #endif
自分でPythonをソースコードからビルドして試してみたい気もしますが、私は面倒くさがりなのでpython38.dllのバイナリを直接いじって、sys.platformの値を"win64"へ変更しました。
python.exeがこの改変されたdllを読み込めば、sys.platformを参照しているはずのpipがcp38-cp38-win64.whlというファイルを見に行くはずです。
結果はダメでした。やはりcp38-cp38-win32.whlを見に行ってしまいます。
再びpipモジュールを見る
アテが外れたので再びpip側のソースコードに戻り、参照すべきパッケージ名を生成する箇所を探してみました。
するとsysconfig.pyというものを見つけることができました。
面白いことにバージョン文字列から当該プラットフォームを抽出しており、プラットフォームの抽出に失敗した場合にsys.platformを参照するという仕組みになっています。
--- site-packages\pip\_vendor\distlib\_backport\sysconfig.py ---
626: if os.name == 'nt':
627: # sniff sys.version for architecture.
628: prefix = " bit ("
629: i = sys.version.find(prefix)
630: if i == -1:
631: return sys.platform
632: j = sys.version.find(")", i)
633: look = sys.version[i+len(prefix):j].lower()
634: if look == 'amd64':
635: return 'win-amd64'
636: if look == 'itanium':
637: return 'win-ia64'
638: return sys.platform
pythonを起動して、コードを直接打ち込んでみても文字列"win-amd64"が取れます。ソースコードの動作には問題なさそうに見えますが、どうしてもcp38-cp38-win32.whlを見に行ってしまうようです。
どこを参照しているのかがわからなくなってきたので今回の調査はここまでです。また何かわかったら追記します。
とりあえずの対処方法としては、Wheel形式のファイル名を自動読み込みしているパッケージ名に強制リネームするのがよいということがわかりました。
この記事が気に入ったらサポートをしてみませんか?