Pythonのurllib2がメモリリークする件

間違ってたら指摘お願いします。

http://mail.python.org/pipermail/python-bugs-list/2005-October/030763.html
三年前に話してることみたいなんですがここではurlopen()したときの話をしてて(英語は細かいことわからないです)

urllib2.py:1095 r.recv = r.read

ここで拡張してるのが原因でaddinfourlの親クラスaddbaseのclose()を呼んでもrecvを解放する処理がないので循環参照したままになっています。
その解決策に

import urllib2
f = urllib2.urlopen('http://www.google.com')
text=f.read()
f.fp._sock.recv=None # hacky avoidance
f.close()

このコードが書かれています。
f.fp._sock.recv=Noneするとgc.garbageに16個あったのが8個に減ります。でも、残ってます。

これは'http://www.google.com'にアクセスするとurllib2内部で302リダイレクトの処理をしていて

http_error_302メソッド内に下記のコードがあるのですが

urllib2.py:579 fp.read()
urllib2.py:580 fp.close()

このclose()もまたrecvを解放されないのでfp.fp._sock.recv=Noneをfp.close()の前に追加するか
addbaseのclose()にif hasattr(self.fp, '_sock') and hasattr(self.fp._sock, 'recv'): self.fp._sock.recv=Noneを追加するのが一番楽そう
addbaseのclose()に処理を追加するのはurllib.pyの話なのでaddinfourlクラスを継承したクラスをurllib2内に適当に書いて使うのが妥当かな。
そうするとurlopen()を使う場合もclose()を呼ぶだけで解放できようになる。
この場合、addinfourlを使っているところを置換しないといけない。

とりあえず、urllib2.pyをスクリプト使うところにコピーしてfp.close()の前にfp.fp._sock.recv=Noneを追加して様子見している。

でも、わざわざclose()を呼んで解放するというのがいまいち好きじゃないのでなにか別の方法はないのだろうか
弱参照をどこかで使うと良いのかな

u.close()を一応書いてるけど書かなくても変わんない。
良さそうなこと思いついた。
urlli2.pyに下記のsocket._fileobjectを継承したクラスを追加(urllib2を直接書き換えるのはどうかと思うので一応スクリプトを使うところにコピーしたのを書き換えてる)

class _socketfileobject(socket._fileobject):
def flush(self):
self._sock.recv = None
super(_socketfileobject, self).flush()

下記の一箇所を書き換える

+ fp = _socketfileobject(r, close=True)
- fp = socket._fileobject(r, close=True)

これで、302以外のHTTPErrorが出た場合や通常の処理の時も解放する処理を書かなくてはいけなかったのを書かないで済む。

import urllib2
import gc
gc.set_debug(gc.DEBUG_SAVEALL)
url = 'http://www.google.com/'
def connect_urllib2():
  u = urllib2.urlopen(url)
#  u.fp._sock.recv=None # 16 -> 8
  u.close()

def gc_print(msg):
  gc.collect()
  print "%s garbage: %d" % (msg, len(gc.garbage))

if __name__ == '__main__':
  connect_urllib2()
  gc_print('urllib2')