Steering Councilのメンバーであり、リリースマネージャーであるパブロ・ガリンド・サルガド氏が、今年のLanguage Summitで、Pythonを利用したことがある人なら誰でも一度は遭遇したことがある問題を解決するための提案を行いました。

この問題は、json、math、またはhttpなどのモジュールから、正しい名前であるはずなのに、一見ランダムなAttributeErrorが発生するという形で現れます。なぜ標準ライブラリが突然エラーを発生させるのでしょうか?

$ cat game.py
import random
if int(input("1から10の数字を選んでください")) == random.randint(1, 10):
print("勝ち!")
$ python game.py
AttributeError: module 'random' has no attribute 'randint' (???)
# なぜrandom.randint()が利用できないのか?

通常、この問題は、sys.path内の優先順位の高い場所にあるモジュールが、標準ライブラリのモジュールを「隠蔽」していることが原因です。例えば、現在の作業ディレクトリやPYTHONPATHに設定されたディレクトリなどです。プロジェクト内にjson.pyやmath.pyという名前のファイルが存在し、そのモジュールが同じ名前の標準ライブラリのモジュールよりも優先されるため、プロジェクトの他の場所でそのモジュールを使用し始めたときに初めて問題が発生することがあります。

$ ls game.py random.py
# random.pyが最初にインポートされている。標準ライブラリのrandomではない。
$ python game.py
AttributeError: module 'random' has no attribute 'randint'

この問題は、Pythonのフラットなモジュール名前空間が原因です。標準ライブラリと、依存関係やプロジェクトコードであるサードパーティモジュールとの区別がありません。

パブロ氏は、Python 3.11から利用可能な、この問題を解決するための低コストな方法があることを指摘しました。それは、ドキュメントによると「sys.pathに潜在的に危険なパスを付加しない」-Pオプションです。現在の作業ディレクトリなどです。しかし、多くのユーザーがPythonをこのオプションを有効にして実行していないか、または、プロジェクトコードを環境にインストールしていないため、現在の作業ディレクトリがインポート可能であることを前提としているため、この問題が発生しています。

新しい標準ライブラリのモジュール名には制限が設けられています。

この問題は、ユーザーにとって混乱を招くだけではありません。パブロは、フラットな名前空間であるため、コア開発者は、Python Package Index (PyPI) で既に利用されているものがあるため、新しいモジュールに「不格好」な名前を選択せざるを得ないことが多いと説明しました。この制限は、モジュールが PyPI から標準ライブラリに採用される場合にも適用されます。例えば、yaml モジュールを提供する PyYAML などです。これが、多くの新しい標準ライブラリのモジュールに「lib」という接尾辞が付いている理由(例えば、tomllib や graphlib など)です。あるいは、予期しない名前が付けられている理由(例えば、timezone の代わりに zoneinfo など)です。

考えられる解決策の一つは、すべての標準ライブラリモジュールに対して、新しい最上位の名前空間を追加することです。パブロは、この名前空間に「std」という名前を提案しました。これにより、ユーザーは、標準ライブラリのモジュールを確実にインポートするために、std.json や from std import json のようにインポートすることができます。

パブロは、std 名前空間を使用せずに json をインポートすることは、基本的に永久に機能し続けると保証しました(「我々は世界を壊すことはできません。それは悪いことです」)。

>>> import std.json, json
>>> std.json is json
True
>>> json. __name__
'json' # 変更なし

しかし、パブロは、ユーザーが新しい std 名前空間の使用を始めるように促すための方法として、新しいモジュールを std 名前空間にのみ導入するというアイデアを完全に否定したわけではありません。

>>> import std.new_stdlib_module # これは新しいモジュールで機能します。
>>> import new_stdlib_module
ImportError # しかし、'std.' を付けない新しいモジュールは機能しません...

Python だけではありません。他の多くのプログラミング言語も、すでに標準ライブラリ(またはそれに相当するもの)に名前空間を持っています。

魅力的な選択肢が目の前に現れます:標準ライブラリを分割する

パブロは、Python の標準ライブラリに新しい最上位の名前空間を予約することの潜在的な副作用として、標準ライブラリをより簡単に「分割」できる可能性があると述べました。標準ライブラリを分割するということは、stdlib モジュールを Python インタープリタや互いに独立して、個別にアップグレードできるようにすることです。これにより、おそらく PyPI を通じて配布されるようになります。

標準ライブラリを分割することには、いくつかの利点があります。

* 標準ライブラリのモジュールをPyPIを通じて配布し、インタプリタとは独立してバージョン管理する。
* セキュリティ上の問題に対する修正を、Pythonのリリースを待たずに迅速に適用する。
* Pythonのコアよりも更新頻度が高いモジュールを、より迅速に改善する。

しかし、標準ライブラリの分割にはトレードオフがあり、具体的な事例も存在します。
Rubyは標準ライブラリを「gem化」しましたが、その結果、非推奨期間が長すぎる(`require`は3つのリリースサイクルを要した)、依存関係からの警告、そして`uri`というgemに存在する脆弱性(CVE)が修正されなかったという問題が発生しました。これは、そのgemがRubyのパッケージマニフェストである`Gemfile`にリストされていなかったためです。

議論

Kushal Das氏は、名前の衝突(シャドウイング)の問題が「初心者にとって大きな問題」であり、特に初めてPythonを使用するユーザーが`math.py`というファイル名で数値を扱うコードを書く際に問題となる、と指摘しました。

Jukka Lehtosalo氏は、自身も「この問題に直面したことがある」と述べ、この問題が「どのくらいの頻度で発生するのか」に関するデータがあるかどうかを尋ねました。Pablo氏は具体的なデータを持っていませんでしたが、最近のPythonのバージョンではエラーメッセージが改善されていると述べています。

Pablo氏は、名前の衝突の問題に過度に焦点を当てるのではなく、彼が考えるより大きな問題、つまりフラットな名前空間が、Pythonのコア開発者が標準ライブラリのモジュール名を選択する際にどのように影響するかに焦点を当てたいと考えています。

David Hewitt氏は、この提案に「セキュリティ上の利点」があるかどうか疑問を呈し、コア開発者は「標準ライブラリの内容について、初心者よりも詳しい」ため、この変更が学習者がPythonに含まれるものと含まれないものを理解するのに役立つかどうかを尋ねました。Pablo氏は、セキュリティの観点について反論し、「標準ライブラリは非常に大きく、標準ライブラリのモジュールに関するクイズをしても、誰も正解できないだろう」と述べました。彼は、これがこの提案を採用するもう一つの肯定的な理由になりうると結論付けましたが、この側面を過度に強調したくはない、とも述べています。

グイド・ヴァン・ロッサムは、この提案に従って、すべての標準ライブラリのモジュールが最終的に移動する必要があるのか尋ねました。パブロはそれを肯定しました。その上で、グイドは、それが標準ライブラリ全体にわたるインポートに影響を与えることになるのか尋ねました。これもパブロは肯定し、この移行は「ほとんど機械的な作業」で済む可能性があり、すべてのモジュールを固定することも含まれる可能性があると述べました。

ピーター・ビエルマは、新しいstd名前空間がパフォーマンスに影響を与えるかどうか尋ねました。パブロは、そのコストは「実質的にゼロ」であると答えました。

トーマス・ワウテルスは、新しいstd名前空間を段階的に導入することを提案しました。新しいモジュールはstd名前空間に配置され、エコシステム全体が移行した後に、トップレベルのシャドウイングを完全に無効にするモードを将来的に導入することも可能であると考えました。

シュテファン・ベーネルは、「Pythonは、標準ライブラリから自信を持ってインポートできる方法を提供すべきだ」と述べました。彼は、ほとんどのコードを変更せずに済む可能性のある解決策を提案しました。それは、提案をfromインポートに限定することです。構文は「from std import random」となり、モジュール名は同じで、std名前空間がユーザーコードに現れることはありません。これにより、sys.modulesによるモジュールの重複の問題も回避できます。グイドは同意し、from stdが「魔法のようなものになる可能性がある」と述べました。パブロは、「特別なキーワード」と付け加えました。

デイビッド・ヒューイットは、新しいモジュールを導入せずにインポート文を変更するために、すでにlazyキーワード(Python 3.15で導入)で確立された先例があることを指摘しました。
キーワードを使用するというアイデアについて、参加者全員で賛成・反対の意向を確認したところ、「このアイデアを嫌っている人はいなかった」という意見が多く、多くの参加者が「このアイデアを気に入っている」と答えました。

グレゴリー・P・スミスは、「PEP帽子」をかぶって、この提案は複数の問題を同時に解決しようとしている可能性があると指摘しました。「すべてのアイデアを検討に残しておくが、取り組むべき具体的な内容を明確にするべきだ」と述べました。