close
The Wayback Machine - https://web.archive.org/web/20130607031647/http://docs.python.jp:80/3.3/howto/pyporting.html

Python 2 から Python 3 への移植

author:Brett Cannon

Abstract

現在は Python 3 が最新版の Python ですが、Python 2 もまだ活発に利用されています。なのであなたのプロジェクトを両方のメジャーリリースにおいて動作可能にしておくのがよいでしょう。このガイドでは、あなたのプロジェクトにおいて Python 2 と 3 の両方をサポートするにはどの戦略をえらべばよいか、具体的にどうすればよいかを解説します。

もしあなたが標準 Python ライブラリではなく拡張ライブラリでの移植手段を探しているならば Python 3 への拡張モジュール移植 を参照してください。

戦略の選択

あるプロジェクトにおいて Python 2 と 3 の両方をサポートすると決定したとき、それをどうやって実現させるかの戦略を決める必要があります。戦略は、プロジェクトの既存のコードベースがどれほど大きいか、またPython 2 と Python 3 用にどの程度の数のブランチを作成するかに依存します (例: Python 3 用にソースを一から書き直す)。

もしあなたのプロジェクトが新規立ち上げであるか、既存のコードベースが小さい場合には、すべてのコードを Python 3 向けに記述した後に 3 から 2 へ変換 するとよいでしょう。

Python 2 と 3 に対して同時に意味的 かつ 構文的に互換のある状態でコードベースを保守したいのであれば、Python 2/3 互換のソース を参照してください。この方法では慣用的でないソースコードになりがちですが、開発者であるあなたにとっては rapid 開発プロセスを保つことになります。

最後に、Python 2 のコードを Python 3 のコードに (いくつかの手動での操作を伴って) 変換するのに 2to3を使う という方法があります。この方法ではまずコードからブランチを作成し、2to3を使い Python 3 用に変換します。Python 2 のコードベースのみを保守すればよくなるように、この変換をソフトのインストール時にユーザに実行させることもできます。

あなたがどの方法を選ぶにせよ、コードの移植は最初に考えるより難しいものでも時間のかかるものでもありません。移植のかなりの部分は Python 2/3 互換性に関する現在のベストプラクティスに従うようにコードを更新していくことなので、問題に対して少しずつ取り組んでいくこともできます。

一般的なアドバイス

あなたの戦略にかかわらず、考慮すべきことがいくつかあります。

第一に、堅牢なテストスイートを持つことです。Python のマイナーバージョンをサポートするときと同様に、すべてのコードが動くことを確かめなければいけません。すべてのテストスイートがテストをクリアすることを確かめて、Python 2/3 間の移植が正しく行われたことを確認しなければなりません。これには tox のようなツールを使って Python 2 と 3 の両方の環境で自動テストを行えばよいでしょう。

第二に、プロジェクトが Python 3 をサポートするようになったら、Cheeseshop (PyPI) 上で必ず適切な Classifier を追加してください。あなたのプロジェクトが Python 3 互換としてリストに表示されるようにするには、Python 3 classifier が必要です。(リンク http://techspot.zzzeek.org/2011/01/24/zzzeek-s-guide-to-python-3-porting/):

setup(
  name='Your Library',
  version='1.0',
  classifiers=[
      # make sure to use :: Python *and* :: Python :: 3 so
      # that pypi can list the package on the python 3 page
      'Programming Language :: Python',
      'Programming Language :: Python :: 3'
  ],
  packages=['yourlibrary'],
  # make sure to add custom_fixers to the MANIFEST.in
  include_package_data=True,
  # ...
)

そうすることによってあなたのプロジェクトは Python 3 パッケージリスト に表示されます。classifierを正しく記述したことによって、Cheeseshop内のあなたのプロジェクトページを訪れた際、右上に Python 3 のロゴが表示されます。

第三に、Python 2/3 間の差異を解決してくれるライブラリを提供する six プロジェクトがあります。もしあなたが移植/保守するソースの中に絶え間なく問題になるような厄介な部分がある場合は、sixを利用したソースコード互換の解決策を検討してください。あなた自身で Python 2/3 間の互換性を解決しなければならない場合は、回避策として sys.version_info[0] >= 3 という記述が利用できます。

第四に、すべての戦略に目を通しておいてください。アドバイスの一部は他のアプローチより一つのアプローチに適用されるとしても、いくつかのアドバイスは他の戦略に適用できないという意味ではないからです。

第五に、可能であれば古いバージョンの Python のサポートを廃止してください。Python 2.5 では Python 3 では慣用的になりつつあるような有用な構文やライブラリを導入されました。Python 2.6 では Python 2 から 3 へ移植が楽にできるように future 構文が導入されました。Python 2.7 でも stdlib でこの傾向は継続しています。なのでこれからは、あなたのサポートや仕事に対する労力が最小になる Python の最新版を選択してください。

Python 3 と 3to2

もしあなたが新しいプロジェクトを開始しようとしているか、プロジェクトのコードベースが十分に小さい場合、コードを Python 3 で作成して 3to2 ライブラリを利用して Python 2 へバックポートする方針を検討するとよいでしょう。Python 3 が Python 2 よりもより厳格な言語であるおかげで (例: byteとstring)、Python 3 から 2 へのソースコードの移植は Python 2 から 3 よりも簡単で素直に実現できます。それに Python 3 ではより直感的な開発を経験できます。これは将来にわたって続く未来の Python です。

このアプローチの欠点は、3to2がサードパーティ製のプロジェクトであることです。これは Python のコア開発者 (それとこのガイドも) は 3to2 が常にきちんと動作することを約束できないということです。しかしながら、3to2 が高品質のプロジェクトではないことを示唆するものは何もありません。

Python 2 と 2to3

Python 2.6 以降のバージョンに含まれている 2to3 ツール (と lib2to3 モジュール) は、さまざまなソース変換処理によって Python 2 から 3 への移植を助けます。これは Python 2 のコードベースから Python 3 用のブランチを作成し独立したコードベースとして保守するプロジェクトにとって、完璧な解決策となりえます。その気になれば今日からでも、Python 2 でもきちんと動作する future-compatible な Python コードを書いて 2to3 と連携することによってこの方法を利用することができます; 下記に概説されるすべてのステップでは、実際に 2to3 を使うまでは Python 2 で動作します。

インストール時のオンデマンドの変換ステップとして 2to3 を使うこともでき、別々の Python 3 のコードベースを管理することを避けられますが、いくつかの欠点もあります。ユーザはインストール時の変換処理コストのみ負担すればよいのですが、開発者は開発中ずっとその変換処理コストを負担しなければなりません。もしコードベースが十分に大きい場合その変換処理はコンパイル時間のように振る舞います。つまりそれは、Python で実現できていた rapid 開発プロセスを奪い去ってしまいます。もちろん、変換に必要な時間はプロジェクトによって異なります。そのため、この方法を使うか、Python 2/3 Compatible Source を使うか、それとも単に Python 3 のコードベースを保守するか、まずは実験的に変換処理を走らせてからプロジェクトに適した方法を選択してください。

以降の説明は2to3を利用し Python 2 と 3 をサポートするプロジェクトの典型的な手順です。

Python 2.7 のサポート

まず最初の手順として、あなたのプロジェクトが Python 2.7 と互換性があることを確認してください。Python 2.7 は Python 2 系の最後のリリースで、今後も長く利用されます。そのため Python 2.7 のサポートはとにかくやった方が良いです。またそれによって、2to3が対処できないけれど問題を引き起こすことが知られているような箇所を特定できるように -3 フラグを利用することがでするようになります。

Python 2.6 以降のバージョンのサポート

すべてのプロジェクトができることではありませんが、Python 2.6 以降のバージョン のみ サポートとできるなら、話は簡単です。様々な future 構文、stdlib の追加等は Python 2.6 以降のバージョンにのみ存在し、それは Python 3 への移植において非常に助けになります。しかしあなたのプロジェクトが Python 2.5 (や Python 2.4)をサポートしなければならなくてもPython 3 へ移植することは可能です。

以下の説明は Python 2.6 以降のバージョンのみサポートする場合に有効な説明です。これらのいくつかは個人的な選択ですが、それ以外は 強く 推奨されます (特に個人的な選択にはそのように書かれています)。もし古い Python をサポートし続けなければならないのであれば、少なくともそれらの解決策が用いられる状況について注意する必要があります。

from __future__ import print_function

これは個人的な好みです。2to3はprint文からprint関数への変換については比較的良くできているので、このステップは省略可能です。ただ print "Hello, World"print('Hello, World') と書くように慣れるまではこの future 文は便利です。

from __future__ import unicode_literals

これも個人的な好みです。常に文字列の先頭に u を書くことで、どの文字列を(Unicode)にするか明示すれば、これと同じ効果が得られます。しかし、この future 文を使うかどうかに関わらず、すべての Python 2 の文字列をbyteにしたいのかstringにしたいのかを把握 しなければいけません。つまり、この future 文を使わない場合、最低限 の対処としては string として扱う文字列すべてに u を先頭に記述する必要があるということです。

byte列表現

この項は 特に 重要です。Python 2の文字列のうちbyte列を含むことを意図しているものの先頭に b を書くことで、Python 3 に変換する際に文字列かそうでないかをはっきりさせられます。2to3を実行した際、b が文字列の先頭に書かれて いなければ すべてのPython2の文字列はbyte列ではなくPython3の文字列として扱われてしまいます。

Python 2 のbyte列が単なる str のエイリアスであることで、byte列表現について Python 2 と 3 では多少の相違があります。おそらく一番”なるほど!”と思う部分はインデックス参照の結果が異なることでしょう。Python 2 では、b'py'[1]'y' ですが、Python 3では 121 です。この相違については要素一つ分のサイズでスライスすることで避けられます。b'py'[1:2] とすると、Python 2 では 'y' となり、Python 3 では b'y' となります(つまり十分近いです)。

Python 3 ではbyte列と文字列を結合することはできません。Python 2 ではbyte列は str のエイリアスですので、これは動作します: b'a' + u'b'。しかし Python 3 では動作せず結果は TypeError になります。byte列と文字列の比較では似たような問題が発生します。

Python 2.5 以降のサポート

もし Python 2.5 以降をサポートする場合、Python のいくつかの機能を利用できます。

from __future__ import absolute_import

暗黙の相対インポート (例: spam.eggs から spam.baconimport bacon でインポートすること) は Python 3 では動作しません。この future 文はインポートの仕組みを変えて、明示的な相対インポートが使えるようにします (例: from . import bacon)。

Python 2.5 ではこの __future__ 文を記述して暗黙のインポートを避け、明示的なインポートを実行しなければなりません。Python 2.6 では明示的なインポートはこの行なしに実行可能ですが、暗黙のインポートを避けるためにこの __future__ 文は必要です。Python 2.7 では __future__ 文は不要です。言い換えると、Python 2.7 のみをサポートするか、Python 2.5 以前のバージョンをサポートする場合を除き、この __future__ 文を記述してください。

よくある “Gotcha” (「なるほど!」) の扱い

2to3 が自動的に扱うことのできない誰もがハマるポイントや、コードを近代化することを助けるために Python 2 で簡単にできることがいくつかあります。

from __future__ import division

Python 実行時の引数に -Qnew を指定することでも同じ結果が得られますが、この future 文を使うと Python 3 での割り算の振る舞いを導入できます(例: 1/2 == 0.5; 1//2 == 0)。

バイナリファイルを開く際の設定

Windows環境で作業しているのでなければ、バイナリファイルを開く際に b を付けるか心配しなくてもよい場合があります(例: バイナリ読み込み時に rb を指定するなど)。Python 3 では、バイナリファイルとテキストファイルははっきりと分けられて相互変換できません。詳細は io モジュールを参照してください。そのため、それぞれのファイルについてバイナリアクセス(byte列のみ読み書きを許可する)とするのか、もしくはテキストアクセス(Unicodeの文字列のみ読み書きを許可する)のかを判断 しなければなりません

テキストファイル

テキストファイルは open() 関数によって作成されますが、Python 2 ではbyte列が返却され、Python 3 ではUnicode文字列が返却されます。この点は移植の戦略に依って問題となりえます。

もし Python 2 でUnicode文字を受け取りたい場合は二つの方法があります:

  • Python 2.6 以降では、io.open() を利用してください。io.open() は Python 2 と 3 で基本的に同じ関数ですので、この問題を解決してくれるでしょう。
  • もし Python 2.6 以前の互換性が必要であれば、codecs.open() を代わりに使用してください。この関数は Python 2 でUnicode文字を返却することを保証してくれるでしょう。

サブクラス object

新しいスタイルのクラス定義は Python 2.2 から導入されました。メソッド解決順序に関係する奇妙なエッジケースを避けるために、必ず object をサブクラス化するようにしてください。これは Python 3 でも完全に有効です (ただしすべてのクラスが object を暗黙に継承するので必要ではありません)。

byte/string 二分問題への対処

コードを Python 3 に移植する場合に直面する大きな問題点の一つとして、byte/string 二分問題への対処があります。Python 2 では str 型に無変換のデータを保持することが許されていたので、人々は長年 str インスタンスが保持しているのがテキストなのかbyte列なのかを区別することにややルーズでした。Python 3 ではもはやそのように気楽でいることはできず、その違いを正しく扱わなければなりません。この問題に対処する鍵は、Python 2 で すべての 文字列リテラルがbyte列か文字列のいずれかとして構文的あるいは機能的にマークされていることを確かめることです。これが行われた後で、あなたのAPIが特定の型を扱うように設計されているのか、もしくは適切な多態性を発揮するように設計されているのかを確認する必要があります。

Python 2 の文字列リテラルのマークアップ

最初にすることは、Python 2 のすべての文字列リテラルをbyte列か文字列か指定することです。もし Python 2.6 以降のみをサポートする場合は、byte列の先頭には b を付け、文字列の先頭には u を付けるもしくは unicode_literals 行を使うことで完遂できます。

もし Python 2.6 以前のバージョンをサポートする場合は、six プロジェクトを利用し b() 関数を使ってbyte列を指定してください。文字列に対してはsixプロジェクトの u() 関数か u を先頭に記述してください。

APIが何を受け取るかを決める

Python 2 では偶然ながら簡単にbyte列と文字列を受け取るAPIを作成できました。しかし Python 3 は型の扱いに厳密であるため、byte列と文字列をごちゃまぜに使うことはうまくいかない傾向にあります。

Python 2.6 での辞書 {b'a': 'bytes', u'a': 'text'} を見て見ましょう。このコードは b'a' == u'a' が成り立つため {u'a': 'text'} となります。しかし Python 3 では記述どおり {b'a': 'bytes', 'a': 'text'} が生成されます。つまりデータを失うことがありません。同様の問題は Python 2 から 3 への移植作業において散見されます。

これはつまり Python 2 と 3 で一貫した動作となるように、どのAPIが何を受け取るのかを選択しなければならないということです。

Byte列/Unicode文字列の比較

Python 3 では、byte列とUnicode文字列の併用はほとんどの場合禁止されています。Python 2 では暗黙の型変換を試みるところ、Python 3 では TypeError が発生します。しかしながら例外を発生しない勘違いしやすいケースが一つあります:

>>> b"" == ""
False

これは Python では等価性の比較において例外を発生させてはいけない (そして型が異なるので False を返却する) ためです。しかしながら、このような暗黙の比較が実行されると、誤って移植された Python 3 のコードはバグだらけの振る舞いとなってしまいます。これを検出するため、Python 3 では -b フラグを付けることで警告を発生させられます:

$ python3 -b
>>> b"" == ""
__main__:1: BytesWarning: Comparison between bytes and string
False

警告を例外にするには、-bb フラグを用います:

$ python3 -bb
>>> b"" == ""
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
BytesWarning: Comparison between bytes and string

byte列のインデックス参照

別の隠れた驚きの変更に、Python 3 でのbyte列のインデックス参照時の振る舞いがあります:

>>> b"xyz"[0]
120

このように、Python 3 のbyteオブジェクト(bytearray も同様)は整数の配列なのです。しかし Python 2 から変換したコードは、byte列をインデックス参照したら整数ではなく別の新たなbyte列が生成されると想定していることがよくあります。これらの振る舞いとうまくやっていくため、次のスライスを利用してください:

>>> b"xyz"[0:1]
b'x'
>>> n = 1
>>> b"xyz"[n:n+1]
b'y'

最後の項目についてですが、範囲外のスライスが例外の発生の代わりに空のbyte列を返却するということです:

>>> b"xyz"[3]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: index out of range
>>> b"xyz"[3:4]
b''

__str__()/__unicode__()

Python 2 ではオブジェクトは自身に対する通常の文字列とUnicode文字列の二つの表現を指定できました。Python 3 では一つの文字列表現しか指定できません。このため、__str__() メソッド内でうっかり間違いを犯して予測できない結果を引き起こしてしまうようなことになります(例: __str__() の内部で unicode(self).encode('utf8') を使用すると無限再帰呼び出しとなります)。

この問題を解決するに2つの方法があります。ひとつは2to3をカスタマイズして利用することです。次のブログ記事にやり方が書いてあります http://lucumr.pocoo.org/2011/1/22/forwards-compatible-python/。この方法で、2to3のすべてのインスタンスを def __unicode__(self): ... から def __str__(self): ... に変更することができます。この方法を実行する場合、あなたの Python 2 のコードで __str__() 定義を先に、__unicode__() 定義を後にする必要があります。

もう一つの方法としてはmixinの利用があります。この方法ではあなたのクラスでは __unicode__() メソッドのみ定義し、__str__() メソッドはmixinに任せればよいです(コード元 http://lucumr.pocoo.org/2011/1/22/forwards-compatible-python/):

import sys

class UnicodeMixin(object):

  """Mixin class to handle defining the proper __str__/__unicode__
  methods in Python 2 or 3."""

  if sys.version_info[0] >= 3: # Python 3
      def __str__(self):
          return self.__unicode__()
  else:  # Python 2
      def __str__(self):
          return self.__unicode__().encode('utf8')


class Spam(UnicodeMixin):

  def __unicode__(self):
      return u'spam-spam-bacon-spam'  # 2to3 will remove the 'u' prefix

例外をインデックス参照しないこと

Python 2 では下記のコードは動作します:

>>> exc = Exception(1, 2, 3)
>>> exc.args[1]
2
>>> exc[1]  # Python 2 only!
2

しかし Python 3 で例外を直接インデックス参照するとエラーになります。インデックス参照できるのは BaseException.args 属性だけで、これは __init__() メソッドに渡されたすべての引数からなる配列です。

より良い方法は例外が提供するドキュメント化された属性を利用することです。

__getslice__ ファミリーは使わないこと

長い間廃止とされてきましたが、Python3ではついに __getslice__() をサポートしなくなり、__getitem__() ファミリーへその機能を委譲しました。

doctestsのアップデート

2to3 はdoctestも通常のコードと同様にPython3のコードへ変換しようと試みます。しかしこれは完璧ではありません。もしあなたのdoctestが一塊のものだとしたら(例: 一つのdocstringにすべてのdoctestが含まれるような場合)、少なくともいくつかの小さなdocstringに分割し、ツールによって対処しやすい形にしたほうがよいでしょう。あるいは unittest に移植するためにあなたの時間と努力を費やすのがよいでしょう。

長さの違う入力列に対する map のアップデート

Python 2 では入力された2つの配列の長さが異なる場合、短い配列に None の値を補完し処理を実行後、一番長い配列と同じ長さの結果の配列を返却します。

Python 3 では map に入力された配列の長さが異なる場合、短い配列に合わせて処理を終了してしまいます。互換を保ちながら Python 2 から map を移植する場合は itertools.zip_longest() を使用します。例: map(func, *sequences) は次のように記述します list(map(func, itertools.zip_longest(*sequences)))

-3 警告の除去

アプリケーションのテストスイートを実行する際は、-3 フラグを付けて実行してください。これで2to3が自動では対処できないさまざまな実行時の警告を表示します(例: 除去されたモジュールなどが表示される)。Python 3 への移植を容易にするため、これらの警告を除去するようにしてください。

2to3の実行

あなたの Python 2 のコードに対してこれらの将来にわたって Python 3 と互換性を保つための対処をし終えたなら、ようやく 2to3 の出番です。

手動での実行

2to3 を使って手動でコードを変換する場合、Python 2.6 以降のバージョンに同梱されている 2to3 スクリプトを実行してください:

2to3 <directory or file to convert>

これにより 2to3 はソースコードに対して適用されたすべての変換処理(fixer)とともに差分を出力します。もしこれらの変換内容を 2to3 に実際に処理させたい場合は -w フラグを渡します:

2to3 -w <stuff to convert>

他にも、特定の変換処理だけを行うように制御する等の様々なフラグがあります。

インストール時の実行

ユーザがあなたのプロジェクトを Python 3 にインストールする際、あなたの代わりに 2to3 を実行してくれるモジュールとして distutilsDistribute があります。distutilsについては次の慣例に習ってください:

try:  # Python 3
  from distutils.command.build_py import build_py_2to3 as build_py
except ImportError:  # Python 2
  from distutils.command.build_py import build_py

setup(cmdclass = {'build_py': build_py},
  # ...
)

Distributeの場合:

setup(use_2to3=True,
  # ...
)

これらのモジュールを利用すると、あなたのプロジェクトから Python 3 用に別の配布物を用意する必要がなくなります。しかしながら、開発中には最低限テストのためにPython3用のソースコードを生成してテストを実施する必要があります。

確認とテスト

ここまで読み進めたなら(理想的には)あなたのプロジェクトを Python 3 上で実行できるように変換できたことでしょう。ユニットテストを実行しすべてがうまくいくことを確認しましょう。もしなにかが失敗した場合は、Python 3 での解決策を調べた後で、Python 2 へバックポートし、再度2to3を実行して問題が解決されたことを確認します。

Python 2/3 互換のソース

Python 2/3 間で互換のコードは直感的ではないかもしれません。この互換コードは完全には慣用的な Python のコードとして記述できないかもしれません(例: 現在発生している例外を``sys.exc_info()[1]`` から取り出す必要がある)。しかし、この互換コードは Python 2 と Python 3 の 両方2to3 を変換ステップとして使うことなく実行させることが可能です (ただし、2to3は潜在的な互換性の問題を見つける助けとして利用すべきです)。これは Python 2 と 3 の両方でrapid開発プロセスを実践できることを意味します。このアプローチか Python 2 と 2to3 を使う方法のどちらが良いかはプロジェクトごとの判断になります。

互換性に関してどんな問題に気をつけるかの一覧を得るには、What’s New in Python 3.0 を参照してください。このドキュメント以外にも互換性に関して次のリンクが参考になります http://docs.pythonsprints.com/python3_porting/py-porting.html

以下の手順では、Python 2 と 3 において同じソースコードで互換性を保つ方法について説明します。

2to3 の利用手順に従う

Python 2/3 のコードベースを作るために、2to3を利用したコードの移植 で説明しているすべての手順を適用します。これには、Python 2.6 以降のみをサポートすること (Python 3 では __future__ は問題なく動作します)や、-3 フラグ付きで発生する警告の除去方法などを含みます。

(変更をコミットせずに) 2to3を実行してみることを検討すべきです。この実行結果から、コード上の潜在的な問題を問題となる前に修正することができます。

six の利用

six プロジェクトにはソースの互換性を保つために必要なものがたくさん含まれています。一度は最初から最後までプロジェクトの文書に目を通して、提供されているありとあらゆる機能を使いましょう。このことはクロスバージョンのソースコードを記載する際の失敗を最小に抑えてくれるでしょう。

発生中の例外の捕捉

(Python 2.5 をサポートする場合には) Python 2 と 3 の間でなされた変更に拠って発生中の例外へのアクセス方法を変更する必要があります。Python 2.5 以前の構文は次のものです:

try:
  raise Exception()
except Exception, exc:
  # Current exception is 'exc'
  pass

この構文は Python 3 では次の通り変更になりました(Python 2.6 以降へはバックポートされました):

try:
  raise Exception()
except Exception as exc:
  # Current exception is 'exc'
  # In Python 3, 'exc' is restricted to the block; Python 2.6 will "leak"
  pass

このことにより、現在発生中の例外を捕捉するには次のように変更する必要があります:

try:
  raise Exception()
except Exception:
  import sys
  exc = sys.exc_info()[1]
  # Current exception is 'exc'
  pass

現在発生中の例外に関する詳細な情報が必要なら、単に現在の例外インスタンスを参照する代わりに sys.exc_info() を参照してください。ただし、たいていは必要ないでしょう。

ノート

Python 3 でのtracebackオブジェクトはexceptionインスタンスの属性 __traceback__ として取得できます。もしこのインスタンスがローカル変数として保存された場合、except ブロックの外側でも生存しつづけ、tracebackオブジェクトは現在の実行フレームとローカル変数の辞書との循環参照を生成してしまいます。結果として次の garbage collection が実行されるまでの間この死んだリソースの開放が遅れてしまいます。

Python 2 ではtracebackそのものを保存した場合のみこの問題が発生します(例: sys.exc_info() の戻りタプルの3番目をローカル変数に保存したとき)。

他のリソース

下記のブログ記事、wikiページ、書籍の著者が Python 2 から 3 への互換性に関する秘訣を広く一般に公開してくれたことに感謝します。(この文書でも大いに参考にしました):

もしこの文書に何か足りない、追加すべきだと感じた場合は遠慮なく python-porting のメーリングリストへ投稿してください。