Contents
2011年6月11日に書いたスクリプトの観賞
それって震災から3ヶ月、の日だ
今日は東日本大震災から4年、の日。防災訓練が各地で行われることであろう。決してタイムリーを狙ったわけではないのだけれど、2011年6月11日に書いたものを発掘し、なんとも妙な気分になってしまった。鑑賞会してみる。
正確には…
本当のことを言うと、最初に書いたのはさらに遡るはずで。というのもこのタイムスタンプ、おそらく自分ちのチームにお披露目するために整理した日、なんじゃなかったか、という微かな記憶がある。個人向けで使っていたのはもっと前、多分 2006~2009 年の間あたりだったのではなかったか。
ここ4年くらいで「初めて本格的に」Pythonに取り組んだので、もはや初心者とは言えないだけのことは出来る(少なくとも雑か本気かの力加減とそれによる「質」は自身で制御できる)けれど、その頃はまさしく「自分が自分だけが楽するために自分だけが使う」ための Python 使いに過ぎず、相当昔から(2000年くらいから)の付き合いの割には「ずっと初心者的」だった頃。
(ちなみに「Pythonの存在」を知ったのはそこから更に遡り、1996年。これは正確。Ruby も同時に知った感じだったよ。)
これね
後で説明するけれど、無意味な三部作になっている。まず「モジュール」。
1 import sys
2 import win32con
3 from win32clipboard import *
4 from pywin32_testutil import str2bytes # py3k-friendly helper
5
6 def _remove_after_nul(s):
7 idx = s.find('\x00')
8 if idx >= 0:
9 return s[:idx]
10 return s
11
12 def get_clip(f=sys.stdout):
13 OpenClipboard()
14 try:
15 # CF_UNICODE text always gives unicode objects back.
16 got = GetClipboardData(win32con.CF_UNICODETEXT)
17 f.write(_remove_after_nul(got.encode('cp932')))
18
19 # CF_OEMTEXT is a bytes-based format.
20 # got = GetClipboardData(win32con.CF_OEMTEXT)
21
22 finally:
23 CloseClipboard()
24
25 def put_clip(text):
26 OpenClipboard()
27 try:
28 EmptyClipboard()
29 text_bytes = str2bytes(text)
30 # Now set the Unicode value
31 SetClipboardData(win32con.CF_UNICODETEXT, text)
32 finally:
33 CloseClipboard()
34
35 if __name__=='__main__':
36 if len(sys.argv) == 1:
37 put_clip(u"Hello from Python unicode")
38 else:
39 put_clip(sys.argv[1].decode('cp932'))
40 get_clip()
次に「メイン」が2つ。
1 import sys
2 import getclip_putclip
3
4 if __name__=='__main__':
5 getclip_putclip.get_clip()
1 import sys
2 import getclip_putclip
3
4 if __name__=='__main__':
5 if len(sys.argv) > 1:
6 getclip_putclip.put_clip(" ".join(sys.argv[1:]).decode('cp932'))
7 else:
8 getclip_putclip.put_clip("".join(sys.stdin.readlines()).decode('cp932'))
何するものかって、これは、「cygwinにはあってMSYSにはない getclip, putclip のもどき」である。要 PyWin32。
py2exe前提だったとはいえ
py2exeでexeにしてしまい、「Pythonなくても使えるぜ」と言いたかったのだ。作った道具では皆に幸せにしてあげたいけれど、(当時)「どこの馬の骨とも知れない得体の知れない Python なんてシロモノをインストールさせては可哀想だ」ぐらいの感じ…だったよ、そら。どんなにワタシ自身が「世界中での潜在ユーザは日本の人口より多くてもおかしくねぇんじゃね?」と当時から思っていたとしてもそんなことは「知らない人々」にとってはどうでも良い事…だったわけである。
そんな事情もあって、「無意味な三部作」は成り立っていた。だって exe になっちまえば、getclip.exe、putclip.exe にしか見えんからね、中がダサかろうと関係ない。(当たり前だが作った当時は当人は自信満々である )
最低でもこうだよなぁ
1 import sys
2 import win32con
3 from win32clipboard import OpenClipboard
4 from win32clipboard import GetClipboardData
5 from win32clipboard import EmptyClipboard
6 from win32clipboard import CloseClipboard
7 from pywin32_testutil import str2bytes # py3k-friendly helper
8
9 def _remove_after_nul(s):
10 idx = s.find('\x00')
11 if idx >= 0:
12 return s[:idx]
13 return s
14
15 def get_clip(f=sys.stdout):
16 OpenClipboard()
17 try:
18 # CF_UNICODE text always gives unicode objects back.
19 got = GetClipboardData(win32con.CF_UNICODETEXT)
20 f.write(_remove_after_nul(got.encode('cp932')))
21
22 # CF_OEMTEXT is a bytes-based format.
23 # got = GetClipboardData(win32con.CF_OEMTEXT)
24
25 finally:
26 CloseClipboard()
27
28 def put_clip(text):
29 OpenClipboard()
30 try:
31 EmptyClipboard()
32 text_bytes = str2bytes(text)
33 # Now set the Unicode value
34 SetClipboardData(win32con.CF_UNICODETEXT, text)
35 finally:
36 CloseClipboard()
37
38 if __name__ == '__main__':
39 import os
40 if os.path.basename(sys.argv[0]).startswith("getclip"):
41 get_clip()
42 else:
43 put_clip(sys.argv[-1].decode('cp932'))
sys.argv[0] で振る舞いを変えるのは定石であろう。それこそ lnでハードリンクしておけば良い。
py2exe にするしないにも影響しないしな。
「sys.argv[0] で振る舞いを変えるのは定石」を知らなかったわけじゃないのにな、何トチ狂ったんだろ? (Unix C プログラムでは学生時代から多用してたもの。知らなかったわけがない。)
unicodeの扱い、理解できてるようで出来てない
「cp932」と決め打っちゃダメ。多分 sys.stdout.encoding 使うのがいいだろうね。
何見て作ったのだろう?
どうにも書かれてる英語コメントがやたらに「ネイティブ」くさいので気になって、周辺漁ってみたのだが、どうも PyWin32 のソースコード配布物に含まれるDemosを参考にしたように思える:
1 # win32clipboardDemo.py
2 #
3 # Demo/test of the win32clipboard module.
4 from win32clipboard import *
5 from pywin32_testutil import str2bytes # py3k-friendly helper
6 import win32con
7 import types
8
9 if not __debug__:
10 print "WARNING: The test code in this module uses assert"
11 print "This instance of Python has asserts disabled, so many tests will be skipped"
12
13 cf_names = {}
14 # Build map of CF_* constants to names.
15 for name, val in win32con.__dict__.items():
16 if name[:3]=="CF_" and name != "CF_SCREENFONTS": # CF_SCREEN_FONTS==CF_TEXT!?!?
17 cf_names[val] = name
18
19 def TestEmptyClipboard():
20 OpenClipboard()
21 try:
22 EmptyClipboard()
23 assert EnumClipboardFormats(0)==0, "Clipboard formats were available after emptying it!"
24 finally:
25 CloseClipboard()
26
27 def TestText():
28 OpenClipboard()
29 try:
30 text = "Hello from Python"
31 text_bytes = str2bytes(text)
32 SetClipboardText(text)
33 got = GetClipboardData(win32con.CF_TEXT)
34 # CF_TEXT always gives us 'bytes' back .
35 assert got == text_bytes, "Didnt get the correct result back - '%r'." % (got,)
36 finally:
37 CloseClipboard()
38
39 OpenClipboard()
40 try:
41 # CF_UNICODE text always gives unicode objects back.
42 got = GetClipboardData(win32con.CF_UNICODETEXT)
43 assert got == text, "Didnt get the correct result back - '%r'." % (got,)
44 assert type(got)==types.UnicodeType, "Didnt get the correct result back - '%r'." % (got,)
45
46 # CF_OEMTEXT is a bytes-based format.
47 got = GetClipboardData(win32con.CF_OEMTEXT)
48 assert got == text_bytes, "Didnt get the correct result back - '%r'." % (got,)
49
50 # Unicode tests
51 EmptyClipboard()
52 text = u"Hello from Python unicode"
53 text_bytes = str2bytes(text)
54 # Now set the Unicode value
55 SetClipboardData(win32con.CF_UNICODETEXT, text)
56 # Get it in Unicode.
57 got = GetClipboardData(win32con.CF_UNICODETEXT)
58 assert got == text, "Didnt get the correct result back - '%r'." % (got,)
59 assert type(got)==types.UnicodeType, "Didnt get the correct result back - '%r'." % (got,)
60
61 # Close and open the clipboard to ensure auto-conversions take place.
62 finally:
63 CloseClipboard()
64
65 OpenClipboard()
66 try:
67
68 # Make sure I can still get the text as bytes
69 got = GetClipboardData(win32con.CF_TEXT)
70 assert got == text_bytes, "Didnt get the correct result back - '%r'." % (got,)
71 # Make sure we get back the correct types.
72 got = GetClipboardData(win32con.CF_UNICODETEXT)
73 assert type(got)==types.UnicodeType, "Didnt get the correct result back - '%r'." % (got,)
74 got = GetClipboardData(win32con.CF_OEMTEXT)
75 assert got == text_bytes, "Didnt get the correct result back - '%r'." % (got,)
76 print "Clipboard text tests worked correctly"
77 finally:
78 CloseClipboard()
79
80 def TestClipboardEnum():
81 OpenClipboard()
82 try:
83 # Enumerate over the clipboard types
84 enum = 0
85 while 1:
86 enum = EnumClipboardFormats(enum)
87 if enum==0:
88 break
89 assert IsClipboardFormatAvailable(enum), "Have format, but clipboard says it is not available!"
90 n = cf_names.get(enum,"")
91 if not n:
92 try:
93 n = GetClipboardFormatName(enum)
94 except error:
95 n = "unknown (%s)" % (enum,)
96
97 print "Have format", n
98 print "Clipboard enumerator tests worked correctly"
99 finally:
100 CloseClipboard()
101
102 class Foo:
103 def __init__(self, **kw):
104 self.__dict__.update(kw)
105 def __cmp__(self, other):
106 return cmp(self.__dict__, other.__dict__)
107 def __eq__(self, other):
108 return self.__dict__==other.__dict__
109
110 def TestCustomFormat():
111 OpenClipboard()
112 try:
113 # Just for the fun of it pickle Python objects through the clipboard
114 fmt = RegisterClipboardFormat("Python Pickle Format")
115 import cPickle
116 pickled_object = Foo(a=1, b=2, Hi=3)
117 SetClipboardData(fmt, cPickle.dumps( pickled_object ) )
118 # Now read it back.
119 data = GetClipboardData(fmt)
120 loaded_object = cPickle.loads(data)
121 assert cPickle.loads(data) == pickled_object, "Didnt get the correct data!"
122
123 print "Clipboard custom format tests worked correctly"
124 finally:
125 CloseClipboard()
126
127
128 if __name__=='__main__':
129 TestEmptyClipboard()
130 TestText()
131 TestCustomFormat()
132 TestClipboardEnum()
133 # And leave it empty at the end!
134 TestEmptyClipboard()
いつものワタシだ、とは思った。ソース配布物の Demo、Sample、test をほじくることから始めるから、アタシ。
それ見て作ったのなら
テキストだけで満足せずに、もちっと頑張って「画像なら画像ファイルに」とかすりゃ良かったのに、と思った。cygwin 相当だけで満足したんだろうなぁ。
鑑賞会おしまい、以後蛇足
Windows の「クリップボード API」は、信じられないかもしれないが「Win16時代からそのまま」である。無論 Win32 に刷新された際に「HGLOBAL/HLOCAL」の真に意味するところは(裏方では)変わったのだが、API の見かけは変わらなかったものだから、Windows プログラマはクリップボードを扱う際に非常に異質なプログラミングを要求され、苦痛だった。いくつかそういうのがあるんだよねぇ。今でも同じプログラムが通用するのがビックリするわ、ほんと。
何を言いたいの?
別にダメ人間自慢したいわけじゃないよ。
なんかね、「Python 教えて」みたいな検索する人がいるみたいなんだけれど、あたしゃね、「目の前に大量に転がっている「公式サンプル」」を見ることもなく「Python 教えて」ってなんなんだ、と思ったりもするわけでさ。「コードスニペット」はこういうブログみたいなのが相応しかったりもするんだけれど、「完全に動作するちゃんとしたお手本」て、「ソースコードを入手する」ことで簡単に手に入るぞ。バイナリばっか持ってこないで、一度はソースコード配布物、眺めてみりゃぁいい。(ま、Python 本体に限っては、そもそも「公式サイトのドキュメントを訪れもせずに「Python 教えて」ってなんだよ、と思う。チュートリアル読め。まずは。)
そんなスタンスは、あたしはあんまし変わってないんだなぁ、としみじみ思った。