Python が削除された macOS 12.3 以降で Python 環境をパッケージングする

:::note warn
この記事は古い記事です。この記事の更新版が次の記事になります。

* [macOS 上でポータブルな Python 環境を作成する](https://qiita.com/doraTeX/items/87302e4f01ee00bb6e5e)

:::



現時点(2022/02/01)では，macOS の最新版は macOS Monterey 12.2 ですが，次期 macOS アップデートの 12.3 では，macOS の Python 環境に大きな変化がもたらされることになりました。

* [ニュース](https://eclecticlight.co/2022/01/29/looking-ahead-to-macos-12-3-python-dropbox-and-onedrive-changes/)

これは，[2019年に予告されていた「スクリプト環境の粛正」](https://developer.apple.com/documentation/macos-release-notes/macos-catalina-10_15-release-notes)がいよいよ始まったのでしょう。

> * Scripting language runtimes such as **Python**, **Ruby**, and **Perl** are included in macOS for compatibility with legacy software. **Future versions of macOS won’t include scripting language runtimes by default**, and might require you to install additional packages. If your software depends on scripting languages, it’s recommended that you bundle the runtime within the app. (49764202)
> * Use of Python 2.7 isn’t recommended as this version is included in macOS for compatibility with legacy software. **Future versions of macOS won’t include Python 2.7**. Instead, it’s recommended that you run python3 from within Terminal. (51097165)

macOS 12.3 の時点では，Python がやられましたが，Perl と Ruby は今のところは大丈夫な模様です。ですが，この予告によれば，Perl や Ruby も近い将来排除されることになるでしょう。

さて，ここでは macOS  のPython 環境のこれまでと，デベロッパ側として可能な今後の対応策について考えてみましょう。

# macOS 12.2 時点の Python 環境

* Python2 : `/usr/bin/python` に Python 2.7.18 が標準インストール，[PyObjC](https://pyobjc.readthedocs.io/en/latest/) 同梱
* Python3 : `/usr/bin/python3` に Python 3.8.9 が標準インストール，[PyObjC](https://pyobjc.readthedocs.io/en/latest/) はなし

[PyObjC](https://pyobjc.readthedocs.io/en/latest/) とは，Pythonスクリプトの中で Objective-C のAPIが呼び出せるブリッジです。

# macOS 12.3 以降の Python 環境

* Python2 : `/usr/bin/python` 自体が存在しない
* Python3 : `/usr/bin/python3` は存在するが，これはダミーみたいなもので，`/usr/bin/clang` などと同様，初めて実行したときに Command Line Developer Tools のインストールが要求される。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41739/d42cdff7-97c8-90a6-b010-aba83681c5b8.png)

よって，デベロッパーにおかれましては，システム標準でインストールされている Python 2 or 3 を内部的に呼び出して利用するような設計になっているアプリは，今後は「macOS のデフォルト環境には Python 環境は存在しない」という前提に立つよう改修が必要となります。（開発者個人が使う限りにおいては，macOS に標準インストールされている Python に頼らず，[Homebrew](https://brew.sh/) や [MacPorts](https://www.macports.org/) といったパッケージ管理システム，あるいは [pyenv](https://github.com/pyenv/pyenv) を用いるなどして，好きなバージョンの Python を自分で勝手にインストールすればよいわけですが，配布を前提とするツールを作る場合，全環境で一律にインストールされていると仮定できるコマンド類しか使用できませんからね。）

それに対する解決策は，Python 環境を丸ごとパッケージングして「ポータブルな Python 環境」を用意し，それを自分の .app バンドルの中に取り込んで，それを内部的に呼び出す設計にすることです。必要に応じて，PyObjC も包んでおくとよいでしょう。

## 補足：PyObjC の例

デフォルトで Python2 + PyObjC が使える macOS 12.2 以下では，次のようにしてPythonスクリプトから Cocoa のAPIを呼び出して，GUIプログラミングやシステムプログラミングをすることがが可能でした。

```python
#!/usr/bin/python
# -*- encoding=utf-8 -*-

from Cocoa import NSAlert
from AppKit import NSAlertFirstButtonReturn

alert = NSAlert.alloc().init()
alert.setMessageText_(u"確認")
alert.setInformativeText_(u"上書きしてよろしいですかよろしいですか？")
alert.addButtonWithTitle_(u"OK")
alert.addButtonWithTitle_(u"キャンセル")

result = alert.runModal()
if result == NSAlertFirstButtonReturn:
	print(u"OK! (^^)")
else:
	print(u"Cancel (>_<)")
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41739/5558274d-c67c-c990-d1bd-731f57805332.png)


# Python環境をパッケージングする方法

GitHubに上がっている，次の [relocatable-python](https://github.com/gregneagle/relocatable-python) レポジトリを利用します。

https://github.com/gregneagle/relocatable-python

```sh
$ git clone https://github.com/gregneagle/relocatable-python
$ cd relocatable-python
```

macOS 12.2 以前と同様，Python 2.7.18 + PyObjC の環境をパッケージングしたいならば，次のコマンドを実行します。

```sh
$ ./make_relocatable_python_framework.py --python-version 2.7.18 --pip-requirements=requirements_python2_recommended.txt
```

これで，`Python.framework` が生成され，その中に Python 2.7.18 + PyObjC の環境がパッケージングされています。

どうせなら，もっと新しい Python3 の環境に PyObjC の機能を組み入れたパッケージを作るのもありでしょう。その場合，次のように実行します。

```sh
$ ./make_relocatable_python_framework.py --python-version 3.8.10 --upgrade-pip --pip-requirements=requirements_python3_recommended.txt
```

自分の実験した限り，この `--python-version` オプションで指定してパッケージングに成功した Python バージョンは以下の通りでした。

| バージョン | 成功/失敗|
|:-----------:|:------------:|
| 2.7.15 | 🟢 |
| 2.7.16 | 🟢 |
| 2.7.17 | 🟢 |
| 2.7.18 | 🟢 |
| 3.7.4 | 🟢 |
| 3.7.5 | 🟢 |
| 3.7.6 | 🟢 |
| 3.7.7 | 🟢 |
| 3.7.8 | 🟢 |
| 3.7.9 | 🟢 |
| 3.7.10 | ❌|
| 3.7.11 | ❌|
| 3.7.12 | ❌|
| 3.8.0 | 🟢 |
| 3.8.1 | 🟢 |
| 3.8.2 | 🟢 |
| 3.8.3 | 🟢 |
| 3.8.4 | 🟢 |
| 3.8.5 | 🟢 |
| 3.8.6 | 🟢 |
| 3.8.7 | 🟢 |
| 3.8.8 | 🟢 |
| 3.8.9 | 🟢 |
| 3.8.10 | 🟢 |
| 3.8.11 | ❌|
| 3.8.12 | ❌|
| 3.9.0 | ❌|
| 3.9.1 | ❌|
| 3.9.2 | ❌|
| 3.9.3 | ❌|
| 3.9.4 | ❌|
| 3.9.5 | ❌|
| 3.9.6 | ❌|
| 3.9.7 | ❌|
| 3.9.8 | ❌|
| 3.9.9 | ❌|
| 3.9.10 | ❌|
| 3.10.0 | ❌|
| 3.10.1 | ❌|
| 3.10.2 | ❌|

# パッケージングしたポータブルな Python 環境の使い方

Pythonインタプリタを呼び出すだけなら，生成された `Python.framework` の中の Python バイナリを呼び出せばOKです。

```sh
$ /path/to/Python.framework/Versions/Current/bin/python3 -V
Python 3.8.10
```

PyObjCも使えます。

```sh
$ /path/to/Python.framework/Versions/Current/bin/python3
>>>import Foundation
>>>url = Foundation.NSURL.alloc().initWithString_("http://localhost")
http://localhost
>>>(url.scheme(), url.host())
('http', 'localhost')
```

このフレームワーク内に pip で新たなパッケージを追加することもできます。

```sh
$ /path/to/Python.framework/Versions/Current/bin/pip3 install numpy
# => Python.framework/Versions/Current/lib/python3.8/site-packages/ 内にインストールされる
```

# Perl や Ruby は？

Perl や Ruby も将来削除されることに備えて，Python のように簡単にポータブルな環境に包んで封じ込めることができればいいのですが，そういう方法はあるのでしょうか？Windowsにおける [tlperl](https://tug.org/svn/texlive/trunk/Master/tlpkg/tlperl/) の macOS 版のようなものがあるといいですね。
)