python3 でテキストデータを表示するスライドショウを作る : 1

呼称: テキストデータをコンソールに表示するスライドショウ 第1版
目的: プレゼンでコーディングデモをスムーズに行うツールを開発する
特徴: ファイルを読み込んで標準出力へ出力する
用例: 特になし
備考: 第1版は cat コマンドのようなもの

JUS の Haskell 勉強会で id:nobsun が説明されていたスライドショウのツールを python で作成してみることにしました。いきなり最終形を作るのではなく、段階的に機能を追加して開発するスタイルがおもしろいなと感じました。私もそれを踏襲してやってみます。おそらく標準モジュールだけで作成できそうな気がするので、python3 で挑戦してみます。
実際は、普通にコーディングして、その後で 2to3 で変換しているだけです(^ ^;;

#!/usr/bin/env python
"""
SlideShow 01 with Python3
"""

import sys

#########################################################################
# Exit Status Value
#########################################################################
status = [
    'normal'
  , 'invalid_args'
  , 'open_file'
]

#########################################################################
# Script Main
#########################################################################
def main():
    try:
        check_option()
        f = open_file(sys.argv[1], 'r', True)
        for line in f:
            print(line, end='')
        f.close()
    except OptionError as err:
        sys.exit(status.index(err.status))
    except EnvError as err:
        sys.exit(status.index(err.status))
   
    sys.exit(status.index('normal'))

#########################################################################
# Functions
#########################################################################
def check_option():
    if len(sys.argv) < 2:
        raise OptionErrorUsage('invalid arguments')

def open_file(file, mode, do_exit=None):
    try:
        f = open(file, mode)
    except IOError as err:
        f = Null()
        if do_exit:
            raise EnvErrorOpenFile(err)
    return f

#########################################################################
# Class Definition
#########################################################################
class Null(object):
    def __new__(cls, *args, **kwargs):  # Singleton for 1 instance
        if '_inst' not in vars(cls):
            cls._inst = super(Null, cls).__new__(cls, *args, **kwargs)
        return cls._inst   
    def __init__(self, *args, **kwargs): pass
    def __call__(self, *args, **kwargs): return self
    def __repr__(self): return 'Null()'
    def __iter__(self): return iter(())
    def __bool__(self): return False
    def __getattr__(self, name): return self
    def __setattr__(self, name): return self
    def __delattr__(self, name): return self

#########################################################################
# Exceptions
#########################################################################
class MyError(Exception):
    """Base class for all exceptions"""
    def __init__(self, msg, value=None):
        self.status = value
        if msg:
            sys.stderr.write('%s\n' % (msg))

class OptionError(MyError): pass
class OptionErrorUsage(OptionError):
    def __init__(self, msg):
        OptionError.__init__(self, msg, 'invalid_args')
        self.usage()
    def usage(self):
        print("Usage: %s filename" % (sys.argv[0]))

class EnvError(MyError): pass
class EnvErrorOpenFile(EnvError):
    def __init__(self, err):
        msg = 'Cannot open: %s' % (err.filename)
        EnvError.__init__(self, msg, 'open_file')


if __name__ == '__main__':
    main()

実行結果。

$ python3.0 SlideShow01_30.py t.txt 
a
bb
ccc
dddd
eeeee

$ python3.0 SlideShow01_30.py
invalid arguments
Usage: SlideShow01_30.py filename

$ python3.0 SlideShow01_30.py detarame
Cannot open: detarame

2to3 による diff 出力。

--- SlideShow01_26.py (original)
+++ SlideShow01_26.py (refactored)
@@ -22,11 +22,11 @@
         check_option()
         f = open_file(sys.argv[1], 'r', True)
         for line in f:
-            print line,
+            print(line, end='')
         f.close()
-    except OptionError, err:
+    except OptionError as err:
         sys.exit(status.index(err.status))
-    except EnvError, err:
+    except EnvError as err:
         sys.exit(status.index(err.status))
    
     sys.exit(status.index('normal'))
@@ -36,15 +36,15 @@
 #########################################################################
 def check_option():
     if len(sys.argv) < 2:
-        raise OptionErrorUsage, 'invalid arguments'
+        raise OptionErrorUsage('invalid arguments')
 
 def open_file(file, mode, do_exit=None):
     try:
         f = open(file, mode)
-    except IOError, err:
+    except IOError as err:
         f = Null()
         if do_exit:
-            raise EnvErrorOpenFile, err
+            raise EnvErrorOpenFile(err)
     return f
 
 #########################################################################
@@ -59,7 +59,7 @@
     def __call__(self, *args, **kwargs): return self
     def __repr__(self): return 'Null()'
     def __iter__(self): return iter(())
-    def __nonzero__(self): return False
+    def __bool__(self): return False
     def __getattr__(self, name): return self
     def __setattr__(self, name): return self
     def __delattr__(self, name): return self
@@ -80,7 +80,7 @@
         OptionError.__init__(self, msg, 'invalid_args')
         self.usage()
     def usage(self):
-        print "Usage: %s filename" % (sys.argv[0])
+        print("Usage: %s filename" % (sys.argv[0]))
 
 class EnvError(MyError): pass
 class EnvErrorOpenFile(EnvError):