【Python3.14】getoptとoptparseが非推奨でなくなった

Pythonにはコマンドライン引数をパースする標準モジュールが[getopt](https://docs.python.org/ja/3/library/getopt.html#module-getopt)・[optparse](https://docs.python.org/ja/3/library/optparse.html#module-optparse)・[argparse](https://docs.python.org/ja/3/library/argparse.html#module-argparse)の3種類あります。

このうちoptparseは14年も前の2011/02/20にリリースされた[3.2から非推奨](https://peps.python.org/pep-0389/#deprecation-of-optparse)になっています。
そしてgetoptは先日2024/10/07にリリースされた[3.13で非推奨](https://github.com/python/cpython/pull/105735)になりました。

従って、今後はargparseに一本化されます。

となるはずだったのですが、一本化されるどころか逆に[3.14でoptparse・getoptの非推奨が外されました](https://github.com/python/cpython/pull/126186)。
すなわち、argparse・optparse・getoptいずれも同列で使用してかまわない機能であるということになりました。
optparseなんか14年経ってから復活です。

どうしてこんなことが起きたのでしょう？


# ソフトな非推奨

Pythonの非推奨には[通常のDeprecation](https://peps.python.org/pep-0602/#deprecations)と[Soft Deprecation](https://peps.python.org/pep-0387/#soft-deprecation)の2種類があります。

通常のDeprecationは破壊的変更であり、今後のリリースにおいて削除される可能性があります。

Soft Deprecationは開発の打ち切りであり、今後の機能追加や改善はされないため、使用することはあまり推奨されません。
ただし、いずれ通常のDeprecationプロセスに移行する可能性は高いですが、通常のDeprecationプロセスを経ずにいきなり削除されることはありません。

optparse・getoptはいずれもソフトな非推奨でした。


# 各パーサの説明

## getopt

Python1.0の初期リリースより前から存在。
Python3.13で非推奨。

getoptはC言語由来のパーサです。
[POSIX](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getopt.html)で規定されており、[Linux](https://www.gnu.org/software/libc/manual/html_node/Getopt.html)をはじめ数多くの言語にそのまま実装されており、互換性が極めて高く、事実上の言語間標準となっています。
もちろん[PHPにもある](https://www.php.net/manual/ja/function.getopt.php)よ。
機能が少ないぶん非常に安定しており、バグが最後に見つかったのは2008年のことです。

```python
optlist, args = getopt.getopt(text, 'q', ['file='])
```

これで`script.py --file=outfile -q`を受け取れます。


## optparse

Python2.3で実装。
Python3.2で非推奨。

optparseはオブジェクトインターフェイスを搭載したパーサーです。

```python
import optparse

parser = optparse.OptionParser()
parser.add_option("-f", "--file", dest="filename",
                  help="write report to FILE", metavar="FILE")
parser.add_option("-q", "--quiet",
                  action="store_false", dest="verbose", default=True,
                  help="don't print status messages to stdout")

(options, args) = parser.parse_args()
```

これで`script.py --file=outfile -q`を受け取れます。


## argparse

Python2.7 | 3.2で実装。

argparseはoptparseをさらに発展させたパーサーです。
optparseは設計上の問題で拡張が難しいらしく、互換性のないパーサーとして別途作成されました。
どうも当初は互換を保とうとしていたけどできなかったので別にしたみたいなことが書いてありました。

```python
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('-f', '--file', help="write report to FILE", metavar='FILE')
parser.add_argument('-q', '--quiet')

args = parser.parse_args()
```

これで`script.py --file=outfile -q`を受け取れます。



# [Getopt and optparse vs argparse](https://discuss.python.org/t/getopt-and-optparse-vs-argparse/69618)

Pythonのコア開発者のひとり[Serhiy Storchaka](https://discuss.python.org/u/storchaka/summary)による投稿です。

:::note
しばらくargparseの改修に取り組んでいます。
当初は185件のIssueがあり、その半分はバグレポートであり、そしてIssueを半分に減らしました。
しかし残りのバグは、仕様を変更せずに解決することは困難です。
argparseには多くの機能がありますが、それらが常に協調して動作するわけではありません。
またargparseの挙動はPOSIXやGNUのほとんどのパーサと異なっており、その一部はバグとみなされるほどです。
そして、この問題の原因はargparseの設計の根幹に存在します。

ちょっとしたインターフェイスを構築するだけでも、エッジケースにおいて予期しない動作が発生することがあります。
さらに複雑なインターフェイスを構築したい場合、どうすればよいかの質問に対する答えの多くはこうなります。
『argparseでは不可能なので、getoptかoptparseを使ってください。』

getoptとoptparseは、堅牢で成熟しており、バグもほとんど存在しないにもかかわらず、現在は非推奨になっています。

getoptとoptparseの非推奨を解除することを提案します。

getoptは決して非推奨にするべきではありません。
getoptはシンプルで、POSIX標準であり、他言語から来たプログラマーもgetoptにはなじみがあります。
optparseには見慣れないオブジェクト指向APIがありますが、十分に予測可能な挙動であり、argparseの問題が解決するまではoptparseを推奨するべきです。
:::

このひと、[optparseを代替する唯一のCLI解析モジュールとしてargparseを推奨したのは失敗だった、argparse利用者のほとんどは宣伝の被害者だ](https://discuss.python.org/t/getopt-and-optparse-vs-argparse/69618/77)とまで言っています。
もっともargparseを使うなと言ってるわけではなく、従来のCLIを実現するシンプルなAPIと、より機能が豊富だが互換のないCLI、いずれかをユーザの手で選択するように伝えるべきだった、という主張です。


# その他のコメント

[IDLE](https://docs.python.org/ja/3/library/idle.html)はgetoptを使用しています。
optparseが非推奨だったからargparseの使用を検討したけど、Issueを見てがっかりしたから使っていません。

[click](https://click.palletsprojects.com/)はoptparseを使用しています。
わざわざ[Why not Argparse?](https://click.palletsprojects.com/en/stable/why/#why-not-argparse)という項目を用意して、argparseの問題点を指摘しています。

[pip](https://pypi.org/project/pip/)はoptparseを使用しています。


# [argparseの問題点](https://discuss.python.org/t/getopt-and-optparse-vs-argparse/69618/6)

オプション`-f`に引数が必須である場合、`-f -b`と書くと通常のCLIでは`-f`がオプションで`-b`はその引数となる。
しかしargparseでは`-f`に引数が足りないというエラーになる。
argparseではこの場合、空白を置かずに`-f-b`と書かないといけない。

しかし引数が`=`で始まる場合は`-f=-b`では動かず、`-f==-b`と書かないといけない。
こんな書き方は他のCLIではサポートされておらず、互換性がない。

またこの回避策は、引数が複数ある場合は全くうまく動かない。

それ以外にも、仕様的に解決できない問題点がいくつか指摘されています。


# スレッドの続き

だいたいエッジケースだから[非推奨じゃなくしたり非推奨にしたりせず](https://discuss.python.org/t/getopt-and-optparse-vs-argparse/69618/10)裏側でやればいいんじゃないかな。

[いや引数にマイナス値を与えようとしてめっちゃバグに遭遇した](https://discuss.python.org/t/getopt-and-optparse-vs-argparse/69618/12)んだが。

[Clickの開発者](https://discuss.python.org/t/getopt-and-optparse-vs-argparse/69618/60) Clickはoptparseを使っていないけど、それはClickのやりたいことがoptparseと異なっているからで、決してoptparseが役に立たないとかではないよ。

最終的にこのスレッドは、何人かの中身が分かっているコア開発者と、[自転車置き場の屋根の色理論](https://ja.wikipedia.org/wiki/%E3%83%91%E3%83%BC%E3%82%AD%E3%83%B3%E3%82%BD%E3%83%B3%E3%81%AE%E5%87%A1%E4%BF%97%E6%B3%95%E5%89%87)に群がってきた素人によってぐだぐだになりました。



# 感想

argparseは仕様上の欠陥があるから、複雑なことをしたいならgetoptかoptparseを使えということでした。
argparseに移行してから10年以上も経っているのに今さらそんなこと言ってくるんか。

実際は既にargparseの利用が圧倒的に多く、非推奨が外れたとしても既存のプロジェクトをわざわざoptparseに書き直したりすることはないでしょうが、今後の新規プロジェクトでgetoptの採用が多少増えたりすることがあるかもしれませんね。

[getopt](https://github.com/search?q=%22import+getopt%22&type=code)：108k
[optparse](https://github.com/search?q=%22import+optparse%22&type=code)：127k
[argparse](https://github.com/search?q=%22import+argparse%22&type=code)：3.7M

それにしてもPython、AIでの採用などにより言語自体の利用は増えているのに、肝心の開発側がどうも色々gdgdなんですよね。

最近記事にしただけでも[TimSortとThe Zen of Pythonの開発者を3ヶ月BAN](https://qiita.com/rana_kualu/items/0369ab4e1df50bc37d7e)したり[Python作者の発言を検閲](https://qiita.com/rana_kualu/items/5c5413e7f65d01436e1c)したりしていますが、ログを調べてみるとそれ以外にも色々楽しい過去が出てくること出てくること。
そりゃもう[GuidoがBDFL辞めたくなる](https://www.itmedia.co.jp/news/articles/1911/02/news016.html)のも納得というものでしたよ。

なお最近のGuide van Rossumは、追放中のTim Petersから教えを受けて、[Python理事会の投票方式を新しくする](https://discuss.python.org/t/changing-pep-13-to-adopt-bloc-star-voting/64971)ことを検討しています。
はて、なにか思うところがあったのでしょうかね？