まとめ
- pyenv で pypy をインストールしようとすると 403エラーが返ってきた
- pyenv の更新により、この問題を解決できた
- しかし、pypy3.6-7.3.1 はインストールできなかった (未解決issueに行き当たった)
- 他方で、pypy3.6-7.3.0 をインストールすることはできた
- おまけ: pypy で円周率を計算させたところ、CPython と比較して約4.7倍速く動作した (簡易的な実験のみ実施)
第一の現象: pyenv で pypy をインストールしようとすると 403 エラーが返ってくる
pyenv で pypy3.6-7.3.1 をインストールしようとしたところ、エラーメッセージが表示され、インストールに失敗した。
% pyenv install -v pypy3.6-7.3.1
/var/folders/fv/495qgk712lg73r5dxmr83mhh0000gn/T/python-build.20200822165404.41441 ~/work
Downloading pypy3.6-v7.3.1-osx64.tar.bz2...
-> https://bitbucket.org/pypy/pypy/downloads/pypy3.6-v7.3.1-osx64.tar.bz2
curl: (22) The requested URL returned error: 403
error: failed to download pypy3.6-v7.3.1-osx64.tar.bz2
BUILD FAILED (OS X 10.15.6 using 0000000000)
前提環境
- macOS Catalina 10.15.6
- pyenv 1.2.20
原因
pyenvが参照するmacOS用のアーカイブファイルのURLから、403エラーが返ってくること。
Downloading pypy3.6-v7.3.1-osx64.tar.bz2...
-> https://bitbucket.org/pypy/pypy/downloads/pypy3.6-v7.3.1-osx64.tar.bz2
curl: (22) The requested URL returned error: 403
これは、BitbucketがMercurialのサポートをやめ、2020年7月8日以降、リポジトリの中身にアクセスできなくなったから。
解決策
pyenvの最新ソースコードを利用すれば良い。なぜなら、この記事執筆時点の2日前 (2020/08/20) に、問題が修正されているから。以下のプルリクエストを参照:
brewでpyenvをインストールしている場合
- pyenvの更新版リリースを待つ
- もしくは、pyenvをGitHubから直接インストールする
ここでは、brewでインストールしたpyenvを消去し、GitHubからの直接インストール方式に変更する手順を説明する。
pyenvインストール方法の移行: brewからGitHubへ
まず、pyenv の root ディレクトリが ~/.pyenv
にあることを確認しておく。(以下の手順は、このパスに root ディレクトリが存在することを前提に記載している。)
% pyenv root
/Users/snjot/.pyenv
brew uninstall で pyenv をシステムから削除する。
% brew uninstall pyenv
Uninstalling /usr/local/Cellar/pyenv/1.2.20... (708 files, 2.5MB)
あとは、Basic GitHub Checkout 記載の手順を実施すれば良い。
ただし、brew版pyenvが作成した ~/.pyenv
ディレクトリが残っているため、そのままだと手順通りに git clone できない。そこで、手順 "1. Check out pyenv where you want it installed." に一工夫加える。以下のようにすると良い:
- その1.
.git
ディレクトリの移植
% git clone https://github.com/pyenv/pyenv.git ~/.pyenv-temp
Cloning into '/Users/snjot/.pyenv-temp'...
remote: Enumerating objects: 123, done.
remote: Counting objects: 100% (123/123), done.
remote: Compressing objects: 100% (121/121), done.
remote: Total 18256 (delta 106), reused 5 (delta 0), pack-reused 18133
Receiving objects: 100% (18256/18256), 3.65 MiB | 2.80 MiB/s, done.
Resolving deltas: 100% (12440/12440), done.
% mv ~/.pyenv-temp/.git ~/.pyenv/.git
% rm -rf ~/.pyenv-temp
- その2.
.git
からのファイル復元
% cd ~/.pyenv
% git reset HEAD --hard
HEAD is now at dc4e24e6 Fix PyPy download links (#1682)
残りは公式手順を参照。
最後に、正しくインストールできているか確認しておくと安心です。
% pyenv --version
pyenv 1.2.20-2-gdc4e24e6
GitHubから直接インストールしている場合
pyenvのバージョンを更新し、dc4e24e 以降のものを使う。
公式手順に沿って更新すれば良い。参考までに、以下にコマンドを転記しておく:
cd $(pyenv root)
git pull
第二の現象: 403エラー解決後もビルドエラーが出て pypy3.6-7.3.1 をインストールできない
解決策実施後に、pypy3.6-7.3.1 をインストールしようとして、以下のエラーに遭遇した:
% pyenv install pypy3.6-7.3.1
Downloading pypy3.6-v7.3.1-osx64.tar.bz2...
-> https://downloads.python.org/pypy/pypy3.6-v7.3.1-osx64.tar.bz2
Installing pypy3.6-v7.3.1-osx64...
/Users/snjot/.pyenv/plugins/python-build/bin/python-build: line 1590: 44564 Abort trap: 6 "$PYTHON_BIN" -c "import $1" > /dev/null 2>&1
WARNING: The Python bz2 extension was not compiled. Missing the bzip2 lib?
/Users/snjot/.pyenv/plugins/python-build/bin/python-build: line 1590: 44565 Abort trap: 6 "$PYTHON_BIN" -c "import $1" > /dev/null 2>&1
WARNING: The Python readline extension was not compiled. Missing the GNU readline lib?
/Users/snjot/.pyenv/plugins/python-build/bin/python-build: line 1599: 44566 Abort trap: 6 "$PYTHON_BIN" -c "import $1" > /dev/null 2>&1
ERROR: The Python ssl extension was not compiled. Missing the OpenSSL lib?
Please consult to the Wiki page to fix the problem.
https://github.com/pyenv/pyenv/wiki/Common-build-problems
調査したところ、未解決issueに行き着いたため、pypy3.6-7.3.1 のインストールを断念した。
なお、ひとつ手前のバージョン pypy3.6-7.3.0 のインストールには、成功した。
% pyenv install pypy3.6-7.3.0
Downloading pypy3.6-v7.3.0-osx64.tar.bz2...
-> https://downloads.python.org/pypy/pypy3.6-v7.3.0-osx64.tar.bz2
Installing pypy3.6-v7.3.0-osx64...
Installed pypy3.6-v7.3.0-osx64 to /Users/snjot/.pyenv/versions/pypy3.6-7.3.0
調査内容
まずは、画面に出力された指示に従い、以下のURLを確認した:
Please consult to the Wiki page to fix the problem.
https://github.com/pyenv/pyenv/wiki/Common-build-problems
そして、Prerequisitesを満たしているか確認した。筆者の場合、Prerequisitesを満たしていた。
次に、上記 URL に記載されている解決策を一通り試した。しかし、問題は解決しなかった。
pypy3.6-7.3.1 に特殊な問題なのかどうか調べるため、以下コマンドで pypy3.6-7.3.0 をインストールしたところ、問題なくインストールできた。
% pyenv install pypy3.6-7.3.0
Downloading pypy3.6-v7.3.0-osx64.tar.bz2...
-> https://downloads.python.org/pypy/pypy3.6-v7.3.0-osx64.tar.bz2
Installing pypy3.6-v7.3.0-osx64...
Installed pypy3.6-v7.3.0-osx64 to /Users/snjot/.pyenv/versions/pypy3.6-7.3.0
pyenv の GitHub リポジトリを確認し、issues を検索したところ、同様の問題が見つかった:
記事執筆時点では未解決 issue であるため、pypy3.6-v7.3.1 のインストールを断念した。
Hello, pypy!
pypy3.6-7.3.0 を導入できたので、動かしてみた。
% pyenv shell pypy3.6-7.3.0
% python
Python 3.6.9 (1608da62bfc7, Dec 23 2019, 10:50:17)
[PyPy 7.3.0 with GCC 4.2.1 Compatible Apple LLVM 10.0.0 (clang-1000.11.45.5)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>>
ちゃんと動いている
おまけ: 円周率を計算させてpypyの風を感じる
せっかくなので、モンテカルロ法による円周率の近似計算で、pypyの速さを感じてみました。pypy の方が 4.7 倍ほど速く動きました。すばらしいですね。
コード
import math
import random
import time
from typing import List, Tuple
def generate_random_point2d() -> Tuple[float, float]:
return (random.random(), random.random())
def generate_multiple_random_points2d(num_samples: int) -> List[Tuple[float, float]]:
points = []
for _ in range(num_samples):
points.append(generate_random_point2d())
return points
def calculate_l2norm(point2d: Tuple[float, float]) -> float:
return math.sqrt(point2d[0] ** 2 + point2d[1] ** 2)
def count_points_within_quadrant(l2norms: List[float]) -> int:
num_points = 0
for l in l2norms:
if l <= 1:
num_points += 1
return num_points
def calculate_pi(num_samples: int) -> float:
points = generate_multiple_random_points2d(num_samples)
l2norms = [calculate_l2norm(p) for p in points]
num_points_within_quadrant = count_points_within_quadrant(l2norms)
pi = 4 * num_points_within_quadrant / num_samples
return pi
if __name__ == "__main__":
random.seed(0, version = 2)
tic = time.time()
pi = calculate_pi(10_000_000)
tac = time.time()
print(f"pi = {pi}; ({tac - tic} sec)")
こちらのGitHubリポジトリにコードを公開しています。
簡易実験
まず、pypyで実行:
% python -m pimonte
pi = 3.1413028; (1.8637058734893799 sec)
次に、CPythonで同じコードを実行:
% python -m pimonte
pi = 3.1413028; (8.742990016937256 sec)
計算に要した時間を比較すると、pypy が CPython の 4.69 倍速く計算できていました。
簡易的な実験なので、考察は省きます。
みなさんも、pypy の速さを感じてみてください。以上!