Simulate interactive python session 改

ちょっとね、もったいないオヴァケが出そうなの。

せっかく fileinput 使ってるのに、てハナシ。

Simulate interactive python session なんだけどね、例えば、「毎度 import numpy as np すんだよね」てなケースに、「元にした stackoverflow のコードなら出来てたのに」改悪しちゃってたのね、アタシ。

interact.py (改)
 1 #! /bin/env python
 2 # original from https://stackoverflow.com/questions/23809327/simulate-interactive-python-session
 3 from __future__ import print_function
 4 import code
 5 import fileinput
 6 import sys
 7 
 8 
 9 def show(input):
10     lines = iter(input)
11 
12     def readline(prompt):
13         try:
14             command = next(lines).rstrip('\n')
15         except StopIteration:
16             raise EOFError()
17         print(prompt, command, sep='')
18         return command
19 
20     # 1. if you don't pass banner, you will see extra "(InteractiveConsole)".
21     # 2. banner will be written to sys.stderr.
22     #    note that even if you're using MSDOS, you can merge stderr to stdout.
23     #    (ref. https://stackoverflow.com/questions/1420965/redirect-windows-cmd-stdout-and-stderr-to-a-single-file)
24     cprt = 'Type "help", "copyright", "credits" or "license" for more information.'
25     banner = "Python %s on %s\n%s" % (sys.version, sys.platform, cprt)
26 
27     code.interact(banner=banner, readfunc=readline)
28 
29 
30 if __name__=="__main__":
31     import argparse
32     parser = argparse.ArgumentParser()
33     parser.add_argument("-m", help="merge stderr to stdout", action="store_true")
34     parser.add_argument("files", nargs="*")
35     args = parser.parse_args()
36     if args.m:
37         sys.stderr = sys.stdout
38 
39     show(fileinput.input(args.files))

fileinput は与えたファイルを連続で読み込んでくれる、ハイフンをコマンドライン引数として与えると標準入力から読んでくれる、ということを念頭に:

 1 me@host: ~$ echo 'import numpy as np' > import_np
 2 me@host: ~$ echo 'np.eye(4)' > np_eye
 3 me@host: ~$ /c/Python35/python interact.py import_np - < np_eye
 4 Python 3.5.1 (v3.5.1:37a07cee5969, Dec  6 2015, 01:54:25) [MSC v.1900 64 bit (AMD64)] on win32
 5 Type "help", "copyright", "credits" or "license" for more information.
 6 >>> import numpy as np
 7 >>> np.eye(4)
 8 array([[ 1.,  0.,  0.,  0.],
 9        [ 0.,  1.,  0.,  0.],
10        [ 0.,  0.,  1.,  0.],
11        [ 0.,  0.,  0.,  1.]])
12 
13 me@host: ~$

要するにアタシの失敗は、argparse のコマンドラインパースと fileinput のそれを衝突させちゃってたまま放置してたこと、なのね。


2017-07-03 05:30 追記:
まぁここから先は「お好きに拡張すれば?」のハナシなんで、あえて書くもんか? とも思ったんだけれど、自分メモも兼ねた方がいいかなと思うので一応。

個人的ツールとして今こうしてます:

 1 #! /bin/env python
 2 # original from https://stackoverflow.com/questions/23809327/simulate-interactive-python-session
 3 from __future__ import print_function
 4 import code
 5 import fileinput
 6 import sys
 7 
 8 
 9 def show(input, indent, escape):
10     lines = iter(input)
11 
12     def readline(prompt):
13         try:
14             command = next(lines).rstrip('\n')
15         except StopIteration:
16             raise EOFError()
17         print(escape("".join((indent, prompt, command))))
18         return command
19 
20     # 1. if you don't pass banner, you will see extra "(InteractiveConsole)".
21     # 2. banner will be written to sys.stderr.
22     #    note that even if you're using MSDOS, you can merge stderr to stdout.
23     #    (ref. https://stackoverflow.com/questions/1420965/redirect-windows-cmd-stdout-and-stderr-to-a-single-file)
24     cprt = indent + 'Type "help", "copyright", "credits" or "license" for more information.'
25     banner = indent + "Python %s on %s\n%s" % (sys.version, sys.platform, cprt)
26 
27     code.interact(banner=escape(banner), readfunc=readline)
28 
29 
30 if __name__=="__main__":
31     import argparse
32     parser = argparse.ArgumentParser()
33     parser.add_argument("-m", help="merge stderr to stdout", action="store_true")
34     parser.add_argument("-i", help="indent", type=int, default=0)
35     parser.add_argument("-H", "--header", help="header", default="")
36     parser.add_argument("-F", "--footer", help="footer", default="")
37     parser.add_argument("-e", help="escape for html (apply 'n' times)", type=int)
38     parser.add_argument("files", nargs="*")
39     args = parser.parse_args()
40     if args.m:
41         sys.stderr = sys.stdout
42     indent = " " * args.i
43 
44     escape = lambda s: s
45     if args.e:
46         try:
47             from html import escape as escape_once  # python 3.x
48         except ImportError:
49             from cgi import escape as escape_once  # python 2.x
50         def escape(s):
51             for e in [escape_once] * args.e:
52                 s = e(s)
53             return s
54 
55     if args.header:
56         print(args.header.decode('string-escape'))
57     show(fileinput.input(args.files), indent, escape)
58     if args.footer:
59         print(args.footer.decode('string-escape'))

使い方はヘルプで察してください、でいいかしら? コードを貼り付けたい相手によってやりたいことが少しずつ違うんで、それに応じたことをしてるだけ。「rst への貼り付けのための –header (たとえば -H".. code-block:: pycon\n")、同じく rst 貼り付けのための indent(たとえば -i4)、あるいは markdown 形式に貼り付けたいための、たとえば --header='```python' --footer='```'、WordPress とかに貼り付けたいときのための html_encode、たとえば -e1-e2、…。