with statement

IronPython2.0でwithが使えないと思っていたのですが、実は対話型コンソールだとダメでファイルから実行する分にはOKでした。
http://d.hatena.ne.jp/akiramei/20070929/1191045521
↑ってことで、これをwithを使って書き直してみます。

from __future__ import with_statement
from contextlib import contextmanager, nested
import sys, clr

clr.AddReference('System.Drawing')
clr.AddReference('Microsoft.Office.Interop.Excel')
from Microsoft.Office.Interop import Excel
from System.Runtime.InteropServices import Marshal
from System.Drawing import Bitmap,Color

@contextmanager
def auto(comobj):
  """COMオブジェクトを自動的に開放する"""
  try:
    yield comobj
  finally:
    Marshal.ReleaseComObject(comobj)

def bmp2xlsx(path):
  bmp = Bitmap(str(path))
  with auto(Excel.ApplicationClass()) as app:
    app.Visible = True
    with auto(app.Workbooks) as wbs:
      with auto(wbs.Add()) as wb:
        with auto(wb.Worksheets) as wss:
          with auto(wss[1]) as ws:
            with auto(ws.Cells) as cells:
              with nested(auto(cells[1,1]), auto(cells[bmp.Height,bmp.Width])) as (begin, end):
                with auto(ws.Range[begin, end]) as r:
                  r.RowHeight = 0.75
                  r.ColumnWidth = 0.08
                  for y in xrange(bmp.Height):
                    for x in xrange(bmp.Width):
                      with auto(r[y+1, x+1]) as cell:
                        with auto(cell.Interior) as interior:
                          c = bmp.GetPixel(x, y)
                          interior.Color = c.B << 16 | c.G << 8 | c.R
        wb.SaveAs(path + ".xlsx")
        wb.Close()
    app.Quit()

if __name__ == '__main__':
  if len(sys.argv) > 1:
    bmp2xlsx(sys.argv[1])

一応、短くなっていますが、ネストが深いのがちょっと・・・