pyenv-winで対応していないバージョンのPythonをインストールする方法

# 1. はじめに
急な話ですが、2021年10月にPython3.10がリリースされましたね！（今更感がありますが…）
個人的には新機能の構造的パターンマッチング（C言語でいうswitch文みたいなやつ）が気になっており触ってみたいと思っていました。
私はWindows環境でPythonを使用する際はpyenv-winを使っているので、さっそく pyenv install -l コマンドでバージョンの一覧を確認！
※今回の記事では、pyenvの基本操作の説明はスキップします。ご了承ください…m(_ _)m

```python
# まずはpyenvのバージョンを確認!!
> pyenv --version 
pyenv 2.64.11

# pyenv install -l を実行
> pyenv install -l
3.10.0a1-win32              # Python3.10は、ここから
3.10.0a1                    # ↑
...                         # |
3.10.0b2-win32              # ↓
3.10.0b2                    # ここまで
pypy3.6-v7.3.3-win32
pypy3.7-v7.3.3-win32
pypy3.7-v7.3.4
```

**あれ？α版とβ版しかなくね？**
**使うんだったら正式版使いたいなー…**

そこで、なんとかpyenv-winに対応していないバージョンをpyenvを使用してインストールできないかを調べました。
結果としては**非対応バージョンでもインストールすることに成功した**ので手順と調査した内容をまとめとこうと思います。

# 2. 今回実施すること

2022/02/21時点の最新版のPython（Python 3.10.2）をpyenvを使ってインストールします。

# 3. 環境
+ OS：Windows10
+ pyenv-win：バージョン2.64.11

なお私は、pyenv-winはGitHubからクローンしたものをユーザフォルダに[.pyenv]とリネームして配置しています。
以降の説明で指すファイルは、GitHubからクローンしたフォルダ内にあります。

pyenv-winのGitHubリポジトリは下記にあります。
https://github.com/pyenv-win/pyenv-win

# 4. インストール手順
この章では、pyenv-winで対応していないバージョンのPythonをインストールする方法を書いていきます。
とりあえずインストールしたい方はこの章だけ実施してください。
※この手順に行き着くまでに調査した内容は5章に記載します。気になる方だけ見てみてください。

## 4.1 .versions_cache.xml にコードを追加する
pyenvフォルダ/pyenv-win/.versions_cache.xml を開き、最下部にある \</versions\> の上の行に以下のコードを追加しましょう！

```xml
<version x64="true" webInstall="true" msi="false">
	<code>3.10.2</code>
	<file>python-3.10.2-amd64-webinstall.exe</file>
	<URL>https://www.python.org/ftp/python/3.10.2/python-3.10.2-amd64-webinstall.exe</URL>
</version>
```

追加した後のコードはこんな感じになります。
![pyenv_add_versions_xml.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/341805/b4ff444e-4b99-22b2-3f69-67281ec5cced.png)
各タブに関して、軽く説明します。

| 名前 | 説明 |
| --- | --- |
| x64 | インストーラの対応bitを記載。<br>true：64bit、false：32bit。 |
| webInstall | インストーラがwebinstallであるかを記載。<br>true：インストーラ名にwebinstall.exeがある場合、false：インストーラ名にwebinstall.exeがない場合。 |
| msi | インストーラがmsiであるかかを記載。<br>true：インストーラの拡張子が.msiの場合、false：インストーラの拡張子が.msi以外の場合。 |
| code | バージョン名を記載。<br>ここに書いた文字が、pyenv install -l に表示されたり、pyenv globalでバージョン切り替えるときに使用するラベル名になります。 |
| file | インストーラ名を記載。 |
| URL | インストーラがあるURLを記載。<br>python-X.X.XX-amd64-webinstall.exe とかでググってHitしたインストーラのURLを張ればよいと思います。    |


今回はPython 3.10.2 用に記載していますが、Web上にインストーラがあるのであれば、多少書き換えるだけでどのバージョンでも対応できると思います。
※今回は Python 3.10.2 でしか試していないですが…

## 4.2 pyenv install -l で追加内容が反映されているかを確認する
コマンドプロンプトを開き、pyenv install -l コマンドを実行して、3.10.2 が追加されていることを確認しましょう！

```python
> pyenv install -l
...
3.10.0b3
pypy3.6-v7.3.3-win32
pypy3.7-v7.3.3-win32
pypy3.7-v7.3.4
3.10.2               # 一番下に追加されます
```

## 4.3 追加したバージョンをインストールする
pyenv install コマンドで追加したバージョンをインストールしましょう！

```python
> pyenv install 3.10.2
```

インストールができたかを pyenv versions コマンドで確認しましょう！

```python
> pyenv versions
3.10.2          # 3.10.2が表示されれば成功！

# 下記のコマンドでpyenvで使用するpythonバージョンを切り替えられます
> pyenv global 3.10.2

> python --version
Python 3.10.2
```

以上がインストール手順となります。

# 5. 今回調査した内容
4章に記載した手順に行き着くまでに調査した内容をまとめます。
手順だけ知りたい方は4章の内容を確認してください。
また、今回はソースを追って確認しましたが、GitHubのissueとかを確認したほうが早かったかもしれません…
気になる人はGitHubのissueものぞいてみると良いかもしれません。
https://github.com/pyenv-win/pyenv-win/issues

## 5.1 非対応バージョンのインストール時に、エラーが発生しているコードを確認
4章の手順を行う前に Python3.10.2 のインストールを実行すると、[not found] となってしまいます。
その場所が、 pyenv-win/libexec/pyenv-install.vbs の L449-454 です。
L449-454 のコードからversions変数に対応しているバージョン情報が入っており、その中に今回のバージョン(version変数)が含まれているのかチェックされています。
そして L464-480　で取得したバージョンの情報を使ってインストールを実施していますね。
![image-pyenv_install_error_pos.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/341805/0cccc55c-7cd3-5bfa-18f3-a587210d3938.png)
そのため、versions変数に非対応のバージョン情報が入るようにすれば、解決できそう…

## 5.2 対応バージョンの情報を取得する処理を確認
pyenv-win/libexec/pyenv-install.vbs の L378 からLoadVersionXML関数でバージョン情報を取得しています。
![image-pyenv_install_get_versions.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/341805/95d402b3-11c3-9563-e8bf-ff693a6d7b2a.png)
LoadVersionXML関数は、pyenv-win/libexec/libs/pyenv-install-lib.vbs の L347-202 に中身が記載されています。
L164-170 あたりで引数で渡されたxmlPathを読み込み、L184-201 あたりで読み込んだ情報を配列に格納している処理がありますね。
![pyenv_install_load_versions_xml.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/341805/3a1920b0-9b44-934d-45cc-48c278c3fe80.png)
pyenv-install.vbs の L378 にて LoadVersionXML関数コール時に指定されているstrDBFileのファイルをいじれば解決しそう！

## 5.3 LoadVersionXML関数コール時に指定されているxmlファイルの確認
pyenvフォルダ全体で strDBFile で検索をかけたところ、
pyenv-win/libexec/libs/pyenv-lib.vbs の L49 で.versions_cache.xml が指定されていました。
![image-pyenv_versions_xml_path.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/341805/b6ff51e8-2445-dbf8-650f-714ba4e982f7.png)
さっそく　.versions_cache.xml　を確認してみます。

## 5.4 .versions_cache.xml の中身を確認
中身を見ればすぐわかりますが、Pythonのバージョンごとに ラベル名/インストーラ名/インストーラのパス が記載されていますね。
ここに自身でインストールしたいバージョンを追加すれば、インストールすることができそう！
![image-pyenv_versions_cache_xml.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/341805/9dd1860c-d6e6-5340-e40d-f3791952010e.png)
以上が調査内容です。
ここまでわかればあとはやってみるだけでした！

# 6. おわりに
今回は、pyenv-win非対応バージョンのPythonをpyenv-winを使ってインストールする方法について書きました。
2022/02/22時点では、Python 3.10.2がpyenv-winに対応されていないので題材として取り上げていますが、
インストーラがWebに配置されていればpyenv-winでインストール可能だと思います。

ただ、pyenv-win自体がアップデートされるのが一番安心だと思うので、更新されるのを気長に待つのも良いと思います。
今回のように使いたいバージョンが対応してなくて困っているケースが発生したら、ぜひ試してみてください！