2010年06月17日

【PythonでCGI】何故か出来ぬPOST METHOD通信 with My Linux

 いやぁ、久しぶりに大きく嵌まってしまった。一般に嵌まることは少ないとは思うが、当方の環境で起きていることは事実なので、ここに対応例として記録して置くことにする。
 首記の件は前稿でのボヤキにある通り当方のLinux環境(CentOS5.4)上で起きている現象で、手元のVista機では発生しないというプラットフォームで差のあるキツネに抓まれた様な不可解な障害である。
 症状は、Linux上のApache2.2 及び 「【PythonでCGI】PyPRecのプロセス記録活用ツール作成に向けて」内のPythonでCGI対応のWEBサーバを作るの何れのWebサーバ上でも、<form method="GET">GETによるフォーム情報は全て出来るが、<form method="POST">に切替えると、フォーム情報が一切伝達出来なくなると言う不可思議なものであった。

 無知な当方は、当初、セキュリティ上のFirewall設定を疑い、Vista側とLinux側の双方のポート別の解放状態を確認した上で、WireSharkで、端末-サーバ間の通信パケットを採取し比較して試たが差を見出すことが出来なかった。お蔭様で効率よい解析方法を今回学ばせてもらった。機会があれば後日ここに記載しようと思う。
 更にPythonの標準モジュールCGIHTTPServerをLinuxサーバ側に導入しテストして試ると同様に障害が発生したことから、何となく、我が愛するPython君の課題の可能性を感じる様になった。
 そこで、Python以外のCGIテストフォームを作りテストしようとしたが、当方は今Pythonのリハビリ中で、元々、他言語ではカウンタ程度しか作ったことが無いので、かなり苦戦することになった。

 PythonでのCGI場合、cgiモジュールのFieldStorageクラスを使うことで、POSTやGETの意識することなく、フォーム情報を取得出来る様に標準化されているが、実際のCGIによるHTML通信は、GETは環境変数の文字列より抽出した上で、POST通信分は、HTML通信の末尾情報として、stdioで取得したデータと環境変数内にあるそのデータ長を使いフォーム情報を抽出するとこになるので、他の言語では、そこから構築する必要で、手詰まりとなってしまった。幸い、有難くも杜甫々さんが氏の「とほほのWWW入門」に「CGIスクリプトのテンプレート」でPerlによるCGIのテスト用コードを掲載して頂いていることを発見し、LinuxのApache上に設置させて頂き、テストすることが出来た。結果、問題なくPerlでは処理出来たので、Python上で解決すべき課題であることが100%確信出来る様になった。

 そこで、先ずはPythonのcgiモジュールを開いて検討したが、ざっと見る限りでは、上記のPOST用の処理が記述されており充分に枯れたコードに見受けられ、Python本家のIssue Trackerもチェックして試たが、CGIのPOST通信関係での課題としてはPython3.x系とWSGI関係での指摘があるのみであった。

 仕方が無いので、これ迄当方がテスト用で作成したPython CGIモジュールは忘れて、ほぼそのまま、Pythonに移植し比較して行くことにした。PerlからPythonへの移植では、printの末尾の改行を1つ省き、各情報の一覧はcgi.test()で代用すれば読み易さは兎も角、取敢えず出来る。その出力チェックでFieldStorageにデータが乗って来ることを確認後、cgiモジュールを覗き、FieldStorageの構造を見ながら、フォーム情報の取得を試み、POST通信時の情報取得に成功したものの実行結果と、そのソースコードが次のものである。
   CGIスクリプトのテンプレート - Mozilla Firefox

http://192.168.0.220/wwwboard/wwwpython_html.py?byURL=ABC

CGIスクリプトのテンプレート

CGIスクリプトの動作環境、環境変数、デコード方法について参考になると思われるスクリプトを作成してみました。詳細な説明は、スクリプトの中に書いてあるコメントを参照してください。

このスクリプトを以下の場所に置いています。いろいろな方法で呼び出して、実行結果をみてください。 (オフラインでは表示できません。)

◆ URLで呼び出す

http://〜/wwwpython.py

◆ URLで呼び出す(引数付き)

http://〜/wwwpython_html.cgi?aaa=AAA&bbb=BBB

◆ フォームからMETHOD=GETで呼び出す
VALUE0_by_get=
VALUE1_by_get=
◆ フォームからMETHOD=POSTで呼び出す
VALUE2_by_post=
VALUE3_by_post=
◆ フォームからMETHOD=POSTで呼び出す(重複フォームあり)
VALUE2_by_post=
VALUE3_by_post=VALUE3_by_post=
by POST
VALUE2_by_post ABCdefGHIjhlMN
VALUE3_by_post かきくけこ1
VALUE3_by_post かきくけこ2
byURL ABC

by mire.htm
VALUE2_by_post ABCdefGHIjhlMN
VALUE3_by_post [かきくけこ1, かきくけこ2]
byURL ABC


fs = cgi.FieldStorage()
fs = FieldStorage(None, None, [MiniFieldStorage('VALUE2_by_post', 'ABCdefGHIjhlMN'), MiniFieldStorage('VALUE3_by_post', '\xe3\x81\x8b\xe3\x81\x8d\xe3\x81\x8f\xe3\x81\x91\xe3\x81\x931'), MiniFieldStorage('VALUE3_by_post', '\xe3\x81\x8b\xe3\x81\x8d\xe3\x81\x8f\xe3\x81\x91\xe3\x81\x932'), MiniFieldStorage('byURL', 'ABC')])
keys = fs.keys()keys= ['VALUE3_by_post', 'VALUE2_by_post', 'byURL']
for key in keys:
print key, ' = ', fs[key].value , 'fs[key].value'
print key, ' = ', fs.getvalue(key) , 'fs.getvalue(key)'
print key, ' = ', fs.getfirst(key) , 'fs.getfirst(key)'
print key, ' = ', fs.getlist(key) , 'fs.getlist(key)'

VALUE3_by_post = Content-type: text/html
*******************************************************************************
CGIフォーム情報取得で例外発生「fs[key].value」
Traceback (most recent call last):
File "/var/www/html/wwwboard/wwwpython_html.py", line 118, in <module>
print '<br/>', key, ' = ', fs[key].value , 'at fs[key].value'
AttributeError: 'list' object has no attribute 'value'
* 'ListType' : <type 'list'>
* '__builtins__' : <module '__builtin__' (built-in)>
* '__doc__' : None
* '__file__' : '/var/www/html/wwwboard/wwwpython_html.py'
* '__name__' : '__main__'
* '__package__' : None
* 'cgi' : <module 'cgi' from '/usr/local/lib/python2.6/cgi.pyc'>
* 'cgi_params' : <function cgi_params at 0x92cd1ec>
* 'display_err' : <function display_err at 0x92cd33c>
* 'environ' : {'HTTP_COOKIE': 'zwiki_displaymode="minimal"; zwiki_height="20"; zwiki_timezone="GMT%2B0900"; zwiki_username="mire"; email="mired.in.a.slump%40gmail.com"; __utma=112118980.754751820.1268979431.1275649346.1275841221.16; __utmz=112118980.1268979431.1.1.utmccn=(direct)|utmcsr=(direct)|utmcmd=(none); tree-s="eJxtkc1qAkEQhO/zFN70JDM9PRv34GFXPBgIZPMHyU1kIwSjiyaSvH3SNY2Zhsz1q54qqia7IbjJbiB3P24ubzkfuyG69S9goamg7dez0MqtBV-ZuxshM9zVche8wZ3gEMADQRDLn88HCDh/HYxrtQFT21D6LvwJbKasLhkjE/nMKJSBrh/BSJnJ8u3BNAvZBtAPaRYqs7QfDVgugdBC9EaQy80tRLQQxXnU3K5Gd/1rf-z3m14k4i1PFMb/iCKj-kfjP2zBtItYdtG-IRv7PCx2ZzICZGNdHtNz-i8bV3/Z2Ph32ILVn43/diUseaeXSdZ4eK8/X7o5CF1IucXiCdun3Mf0B7fFr18"; __utmc=112118980; __cp="x%25DA%25D3%2560b%2560%2560%25C8%2504b%2586hF%2520%25A1%25C1%250C%2524J%2540%25DCb%2516%2520%25E1%259B%2519%2594Z%25C2%2507%25E2%2595T%25E6%25A4%2516g%25A4%25A6%2596%25C4%25E7%25E5%2597%2503%2500%25A3%25BD%2509%25F2"', 'SERVER_SOFTWARE': 'Apache/2.2.3 (CentOS)', 'SCRIPT_NAME': '/wwwboard/wwwpython_html.py', 'SERVER_SIGNATURE': '<address>Apache/2.2.3 (CentOS) Server at 192.168.0.220 Port 80</address>\n', 'REQUEST_METHOD': 'POST', 'HTTP_KEEP_ALIVE': '115', 'SERVER_PROTOCOL': 'HTTP/1.1', 'QUERY_STRING': 'byURL=ABC', 'PATH': '/sbin:/usr/sbin:/bin:/usr/bin', 'CONTENT_LENGTH': '153', 'HTTP_ACCEPT_CHARSET': 'Shift_JIS,utf-8;q=0.7,*;q=0.7', 'HTTP_USER_AGENT': 'Mozilla/5.0 (Windows; U; Windows NT 6.0; ja; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3 ( .NET CLR 3.5.30729)', 'HTTP_CONNECTION': 'keep-alive', 'HTTP_REFERER': 'http://192.168.0.220/wwwboard/wwwpython_html.py', 'SERVER_NAME': '192.168.0.220', 'REMOTE_ADDR': '192.168.0.23', 'SERVER_PORT': '80', 'SERVER_ADDR': '192.168.0.220', 'DOCUMENT_ROOT': '/var/www/html', 'SCRIPT_FILENAME': '/var/www/html/wwwboard/wwwpython_html.py', 'SERVER_ADMIN': 'root@localhost', 'HTTP_HOST': '192.168.0.220', 'REQUEST_URI': '/wwwboard/wwwpython_html.py?byURL=ABC', 'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'GATEWAY_INTERFACE': 'CGI/1.1', 'REMOTE_PORT': '57626', 'HTTP_ACCEPT_LANGUAGE': 'ja,en-us;q=0.7,en;q=0.3', 'CONTENT_TYPE': 'application/x-www-form-urlencoded', 'HTTP_ACCEPT_ENCODING': 'gzip,deflate'}
* 'f' : MiniFieldStorage('byURL', 'ABC')
* 'fs' : FieldStorage(None, None, [MiniFieldStorage('VALUE2_by_post', 'ABCdefGHIjhlMN'), MiniFieldStorage('VALUE3_by_post', '\xe3\x81\x8b\xe3\x81\x8d\xe3\x81\x8f\xe3\x81\x91\xe3\x81\x931'), MiniFieldStorage('VALUE3_by_post', '\xe3\x81\x8b\xe3\x81\x8d\xe3\x81\x8f\xe3\x81\x91\xe3\x81\x932'), MiniFieldStorage('byURL', 'ABC')])
* 'key' : 'VALUE3_by_post'
* 'keys' : ['VALUE3_by_post', 'VALUE2_by_post', 'byURL']
* 'params' : {'VALUE3_by_post': ['\xe3\x81\x8b\xe3\x81\x8d\xe3\x81\x8f\xe3\x81\x91\xe3\x81\x931', '\xe3\x81\x8b\xe3\x81\x8d\xe3\x81\x8f\xe3\x81\x91\xe3\x81\x932'], 'VALUE2_by_post': 'ABCdefGHIjhlMN', 'byURL': 'ABC'}
*******************************************************************************

VALUE3_by_post = ['\xe3\x81\x8b\xe3\x81\x8d\xe3\x81\x8f\xe3\x81\x91\xe3\x81\x931', '\xe3\x81\x8b\xe3\x81\x8d\xe3\x81\x8f\xe3\x81\x91\xe3\x81\x932'] fs.getvalue(key)
VALUE3_by_post = かきくけこ1 fs.getfirst(key)
VALUE3_by_post = ['\xe3\x81\x8b\xe3\x81\x8d\xe3\x81\x8f\xe3\x81\x91\xe3\x81\x931', '\xe3\x81\x8b\xe3\x81\x8d\xe3\x81\x8f\xe3\x81\x91\xe3\x81\x932'] fs.getlist(key)
VALUE2_by_post = ABCdefGHIjhlMN at fs[key].value
VALUE2_by_post = ABCdefGHIjhlMN fs.getvalue(key)
VALUE2_by_post = ABCdefGHIjhlMN fs.getfirst(key)
VALUE2_by_post = ['ABCdefGHIjhlMN'] fs.getlist(key)
byURL = ABC at fs[key].value
byURL = ABC fs.getvalue(key)
byURL = ABC fs.getfirst(key)
byURL = ['ABC'] fs.getlist(key)




!/usr/local/bin/python2.6
# -*- coding: UTF8 -*-
print '''Content-type: text/html;

<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=UTF-8">
<title>CGIスクリプトのテンプレート</title>
<meta name="Keyword" content="CGI">
</head>
<body>

<h4>■ <a name="CgiTemplate">CGIスクリプトのテンプレート</a></h4>
<div class=i>
<p>CGIスクリプトの動作環境、環境変数、デコード方法について参考になると思われるスクリプトを作成し

てみました。詳細な説明は、スクリプトの中に書いてあるコメントを参照してください。</p>
<ul>
<li><a href="cgi/wwwperl.txt" target="_blank">wwwperl.py</a></li>
</ul>
<p>このスクリプトを以下の場所に置いています。いろいろな方法で呼び出して、実行結果をみてください。

(オフラインでは表示できません。)</p>
<h5>◆ URLで呼び出す</h5>
<p><a href="wwwpython.py" target="_blank">http://〜/wwwpython.py</a></p>

<h5>◆ URLで呼び出す(引数付き)</h5>
<p><a href="wwwpython_html.py?aaa=AAA&bbb=BBB" target="_blank">http://〜/wwwpython_html.cgi?aaa=A\
AA&bbb=BBB</a></p>
<h5>◆ フォームからMETHOD=GETで呼び出す</h5>
<form method=GET action="wwwpython_html.py?byURL=ABC" target="_blank">
VALUE0_by_get=<input type="text" name="VALUE0_by_get" value="ABCdefGHIjhlMN"><br/>
VALUE1_by_get=<input type="text" name="VALUE1_by_get" value="あいうえお">
<input type="submit" value="GET">
</form>
<h5>◆ フォームからMETHOD=POSTで呼び出す</h5>
<form method=POST action="wwwpython_html.py?byURL=ABC" target="_blank">
VALUE2_by_post=<input type="text" name="VALUE2_by_post" value="ABCdefGHIjhlMN"><br/>
VALUE3_by_post=<input type="text" name="VALUE3_by_post" value="かきくけこ">
<input type="submit" value="POST">
</form>
</form>
<h5>◆ フォームからMETHOD=POSTで呼び出す(重複フォームあり)</h5>
<form method=POST action="wwwpython_html.py?byURL=ABC" target="_blank">
VALUE2_by_post=<input type="text" name="VALUE2_by_post" value="ABCdefGHIjhlMN"><br/>
VALUE3_by_post=<input type="text" name="VALUE3_by_post" value="かきくけこ1">
VALUE3_by_post=<input type="text" name="VALUE3_by_post" value="かきくけこ2">
<input type="submit" value="POST">
</form>
</div>

</body>
</html>
'''



import cgi
from os import environ
#cgi.test()
fs = cgi.FieldStorage()

#print fs.value[0].value
#ks = fs.value.keys()
#for k in ks:
# print k
if 'REQUEST_METHOD'in environ:
if environ['REQUEST_METHOD']=='POST':
print 'by POST<br/>'
for f in fs.value:
#print f,'<br/>'
print f.name
print f.value
print '<br/>'

else:
print 'by GET or else<br/>'
keys = fs.keys()
for key in keys:
print key
print fs[key].value
print '<br/>'
print '<br/>'

try:
from mire.htm import cgi_params
from types import ListType
print 'by mire.htm<br/>'
params = cgi_params(fs, debug=0)
keys = params.keys()
keys.sort()
for key in keys:
print key
if type(params[key]) is ListType:
print '[%s]' % (', '.join(params[key]))
else:
print params[key]
print '<br/>'
except:
from mire.htm import display_err
display_err(locals(),'by mire.htm<br/>')

#print fs.value[0]
print '<br/>'
#print fs.value
print '<br/>fs = cgi.FieldStorage()<br/>fs = '
print fs
keys = fs.keys()
print '<br/>keys = fs.keys()keys=',keys
print '''<pre>for key in keys:
print key, ' = ', fs[key].value , 'fs[key].value'
print key, ' = ', fs.getvalue(key) , 'fs.getvalue(key)'
print key, ' = ', fs.getfirst(key) , 'fs.getfirst(key)'
print key, ' = ', fs.getlist(key) , 'fs.getlist(key)'
</pre>'''

for key in keys:
try:
print '<br/>', key, ' = ', fs[key].value , 'at fs[key].value'
except:
try:
from mire.htm import display_err
display_err(locals(),'CGIフォーム情報取得で例外発生「fs[key].value」')
except:
print 'See error_log of web server'
print '<br/>', key, ' = ', fs[key].value , 'fs[key].value'

try:
print '<br/>', key, ' = ', fs.getvalue(key) , 'fs.getvalue(key)'
except:
try:
from mire.htm import display_err
display_err(locals(),'CGIフォーム情報取得で例外発生「fs.getvalue(key)」')
except:
print 'See error_log of web server'
print '<br/>', key, ' = ', fs.getvalue() , 'fs.getvalue(key)'

try:
print '<br/>', key, ' = ', fs.getfirst(key) , 'fs.getfirst(key)'
except:
try:
from mire.htm import display_err
display_err(locals(),'CGIフォーム情報取得で例外発生「fs.getfirst(key)」')
except:
print 'See error_log of web server'
print '<br/>', key, ' = ', fs.getfirst() , 'fs.getfirst(key)'

try:
print '<br/>', key, ' = ', fs.getlist(key) , 'fs.getlist(key)'
except:
try:
from mire.htm import display_err
display_err(locals(),'CGIフォーム情報取得で例外発生「fs.getlist(key)」')
except:
print 'See error_log of web server'
print '<br/>', key, ' = ', fs,getlist(key) , 'fs.getlist(key)'

#print fs.MiniFieldStorage()
#cgi.print_arguments()
#print_form(fs)
 多少書き殴りの為、ゴミも多いが、解説すると内容は次の通り。続きを読む
タグ:Python CGI post
posted by Mire at 17:18 | Comment(0) | TrackBack(0) | Pythonプログラミング | このブログの読者になる | 更新情報をチェックする

2010年06月14日

【Pythonでclass】気分を変えて^^;; classでFeecellゲーム

 最近実をいうと、Linuxサーバ上で何故かHTMLのPOST通信による引数データが取得出来ない障害に見舞われ、ドツボに嵌まっている。まあ仕方ないのでWireshark等を使い通信パケットのチェックや、Apacheだけでなく、Python標準モジュールによるcgiサーバでテストして試たりと障害原因の絞込みに努めているが公言出来る様な原因が判っていない。

 ということで、気分を変える目的で、本日のお題は「classでFeecellゲーム」ということだ。当方のBlogでは数少ない【PythonでClass】話題としてとは、「【PythonでClass】気の迷いで^^;; CGI HTML出力系関数をclass化」に続く第二弾である。

 前回は、既に関数化したものを無理やり?クラスでまとめて試たというものであったが、今回は、初めからオブジェクト指向というか、トランプゲームの各振舞いを想定し、クラス作成した点と、課題が過大過ぎて完成するつもりがないことが、今回の特徴である。あくまで、なかなかオブジェクト指向でのコーディングが出来ない当方の様な人が、Pythonでclass作成する上での基本スキルを確認するのが唯一の目的である。

 完成していないものの、取敢えず、フリーセルゲームの出題と、解法解析の前座として、まとめて移動可能な連続した札組のリスト作成迄は作り、test()関数で説明付きで実行した結果が以下の通りである。参考迄、駄目元でテキストと駄目押でスクリーンショット画像を掲載して置く。トランプのスペード、ハート、クローバー、ダイヤの記号は、HTML上でも表示可能である筈ではあるが、日本語利用を前提にしているBlogではまともに表示出来ないことがあるからである。


class freecellのtest実行結果

 例によって、ソースコード側に過剰なほどの説明を加えているので、重複するが、簡単に説明を要約しておく。
  1. 親クラスの属性を引継ぐにはコンストラクタ定義「__init__(self):」内に、「<親クラス名>.__init__(self)」を挿入する必要がある。
  2. クラスメソッドの大半は、独立した関数定義したものをコピー段下げし、「self」のお呪いを必要箇所に書き加えることで概ね機能する
  3. 他方、コンストラクタ内で定義した属性は、「self.」を付けて初期値を定義するが、同様の呪いでメソッド化したものと共に「self.」を頭に付けることで、クラス内の各メソッド内で相互利用出来る。
  4. 関数内定義の変数は、基本的にローカル変数であるが、クラス内の属性はクラス内での各メソッドではグローバル変数的な活用が出来る。
  5. そのクラス内の属性値は「_」で開始する属性名で定義しない限り、基本的に、「<クラスインスタンス名>.<属性名>」で参照出来る。
  6. Pythonのクラス定義は、「self」というお呪いを書くということを強要される犠牲と引き換えに、関数定義に比べとても自由度の高い記述が出来るが、その半面、各定義クラスの定義思想を明確にしておかないと他者が理解出来ない上、思想の変遷で基底クラスを改変することで派生クラスに大きな影響を与えることになるかもしれない。
  7. 当方の様に古くから適当に変数名をその時の気分で決めているようでは、他人様には掴みづらい面もあるだろう。その辺は「エキスパートPythonプログラミング」等で当方も補う予定である。


以下が、現時点のfreecell.pyのhelp()関数によるヘルプ出力である。

          ~/freecell.pyのモジュールhelp()出力 


[root@l22dm ~]# python
Python 2.6.5 (r265:79063, Apr 18 2010, 20:30:20)
[GCC 4.1.2 20080704 (Red Hat 4.1.2-46)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import freecell
>>> help(freecell)

Help on module freecell:

NAME
freecell

FILE
/var/www/html/freecell.py

DESCRIPTION
Free Cellの振舞いをクラスで表現して試ようとしたPythpnモジュール
Pythonのclassを活用したコーディング例として作成したもので、作者
には完成させる意思はない。

 作者の様にBASICマシンPCからのお付合いをしている人間には単純な
手続型のコーディングでこなせるものは、それで済ませてしまう傾向に
ある。従って、データ型を標準的なもので収めることが可能な場合には
せいぜい、PAD的構造化言語の範囲での活用に留まってしまう。
 ただ、このモジュールの様に予めモノとして定義したもので発想した
方が、楽な場合もある。これは、そんな例だ。

 技術的には、先ずトランプゲームの基本的な振舞いを定義したクラス
card_gameを基底の親として作成した上で、Freecellのゲームのクラス
freecellを作成している。その際、親のcard_gameのコンストラクタで
定義した属性とその初期値も、そのまま利用すべく次の一行を追加して
いることに注意頂きたい。
card_game.__init__(self)
 また、各メソッドについてはクラス外で只の関数として定義すること
も可能で、作成に当たり、最初は外で関数定義してから、クラス内の
メソッド化する為にコピー後に段下げと「self」のお呪いを附記した
ものも多い。この際、元々関数内で使っていたローカル変数の定義等で
共通定義的なものは、classのコンストラクタ__init__(self)内に移動
し、クラス内共通で利用可能な様にしていった。関数内の変数は原則
ローカル変数しか扱わないことが原則であるか、クラス内の属性として
定義したものは、「self.」を頭に付けることでクラス内のグローバル
変数的な振舞いをすることになる。各メソッドも「self.」を付けること
でクラス内のメソッド内で相互に活用出来るし、その利用で変化して行く
属性値をきちんとコーディングして行けばクラス活用時のインスタンス
内の属性として一見グローバル変数的な振舞いをするオブジェクト値と
して利用出来ることがクラス活用の利点だろう。但し、オブジェクトと
しての発想次第で如何様にでも定義可能であるので、その発想と仕様を
はっきり文書化しないと他者による活用がより難しくなる。当方の場合
適当に属性名を決めているし発想もあまり固まらない内にコーディング
して行く傾向にある。その辺は「エキスパートPythonプログラミング」
等で当方も補う予定である。
http://astore.amazon.co.jp/mire_python-22

 尚、今回カード移動枚数のルール情報は次の「フリーセルの友」より
頂きました。解法そのものに興味をお持ちの方はそちらをご利用下さい。
http://www5a.biglobe.ne.jp/~dinah/dwothers/downfree.htm

CLASSES
__builtin__.object
card_game
freecell

class card_game(__builtin__.object)
| トランプカードゲームの基本的な振舞いを定義する基底クラス
|
| Methods defined here:
|
| __init__(self)
| 選択状態の既定値
|
| add_cards(self, card)
| 札の後足し(通常のゲームではいかさま行為)
|
| add_jockers(self)
| Jockerの追加
|
| fwd_select(self)
| 上から順に札選択
|
| get_card(self)
| 最後に選択した札を返す
|
| get_cards(self)
| 一組のカードのリストを返す
|
| get_reserve(self)
| 残り札のリストを返す
|
| get_selected(self)
| 選択済札のリストを返す
|
| kaijo(self)
| 階乗計算(参考数字)
|
| rev_select(self)
| 逆順に札選択
|
| rnd_select(self)
| 疑似乱数による不特定の札選択
|
| set_card(self, mark, num)
| 指定カードの選択
|
| (全カードを表にしている場合の利用を想定)
|
| set_cards(self, cards, selected)
| 利用札の設定
|
| (初期値はJockersを除く、52枚の正順だが、
| それをrnd_selected()メソッド等で作成した
| 札順で登録し直すとき等の用途を想定)
|
| ----------------------------------------------------------------------
| Data descriptors defined here:
|
| __dict__
| dictionary for instance variables (if defined)
|
| __weakref__
| list of weak references to the object (if defined)

class freecell(card_game)
| Method resolution order:
| freecell
| card_game
| __builtin__.object
|
| Methods defined here:
|
| __init__(self)
|  親カードゲームの属性値をそのまま引継ぐと同時に
| FreeCell で必要なカードの並び列や退避置き場と
| 整列完成分の収納箱を用意した。
|  尚、Undo機能を付加するのであれば、さらに履歴の
| 収納場所も用意する必要があるだろう。
|
| be_continued(self, a, b)
|
| can_move(self, grp, col)
| (退避場所の空き+1)×22の空き列数乗分の枚数を上限として移動が可能
|
| get_emp(self)
| 空列の数を取得
|
| is_conn(self, grp, col)
|
| is_grp(self, select)
| 選択カード群が連続配列の一部であるかどうかをチェック
|
| prn_card(self, card, mode=1)
| 各カードの表示
|
| split_grp(self)
| 8列のカード内の連続する並びを形成するカードの組みを解析
| 数字が降順で連続し且つ赤黒交互であるものをグループとして返す # つもり
|
| start_game(self)
|
| ----------------------------------------------------------------------
| Methods inherited from card_game:
|
| add_cards(self, card)
| 札の後足し(通常のゲームではいかさま行為)
|
| add_jockers(self)
| Jockerの追加
|
| fwd_select(self)
| 上から順に札選択
|
| get_card(self)
| 最後に選択した札を返す
|
| get_cards(self)
| 一組のカードのリストを返す
|
| get_reserve(self)
| 残り札のリストを返す
|
| get_selected(self)
| 選択済札のリストを返す
|
| kaijo(self)
| 階乗計算(参考数字)
|
| rev_select(self)
| 逆順に札選択
|
| rnd_select(self)
| 疑似乱数による不特定の札選択
|
| set_card(self, mark, num)
| 指定カードの選択
|
| (全カードを表にしている場合の利用を想定)
|
| set_cards(self, cards, selected)
| 利用札の設定
|
| (初期値はJockersを除く、52枚の正順だが、
| それをrnd_selected()メソッド等で作成した
| 札順で登録し直すとき等の用途を想定)
|
| ----------------------------------------------------------------------
| Data descriptors inherited from card_game:
|
| __dict__
| dictionary for instance variables (if defined)
|
| __weakref__
| list of weak references to the object (if defined)

FUNCTIONS
repeat(a, n)
map()関数の引数として、対象リストと同し長さのリストを作る為に活用

test()
テスト用コード

DATA
__author__ = 'Mire in Japan'
__copyright__ = 'Copyright (c) 2010 Mire'
__license__ = 'GPL'
__url__ = 'http://pythonlife.seesaa.net/article/153209701.html'
__version__ = '0.0.0pre'

VERSION
0.0.0pre

AUTHOR
Mire in Japan

(END)


 このソースコードは次の通り。続きを読む
posted by Mire at 08:17 | Comment(0) | TrackBack(0) | Pythonプログラミング | このブログの読者になる | 更新情報をチェックする

2010年06月01日

【PythonでWSGI】Apache2.2+mod_wsgiでWSGI環境構築

 PythonでのWeb作成環境としては、当方がここで公開しているCGIとWebサーバ内在のCGI機能を使うもの以外に、Apache+FastCGI, Apache+mod_python 等と色々と存在する。CGIが、その呼出しの度にプロセスを起動するのに対し、Apacheのモジュールとしてプロセスを予め起動したもので処理するとかで、その分軽快に動作するというのが売りだったかと思う。
 それだけの触れこみでは、FirrbirdをClassicServerにするかSuperServerにするかで一つのプロセスが落ちるまたはフリーズした時の障害対応の経験もあり迷わず、ローテクの極みのCGI路線にとどまってしまった。多少の誘惑は感じたけどね。

 でも、そんなこんだでちょいと仕事にハマり、CGIのみに勤しんでいる間にPythonではWSGIという仕様が登場し、Python2.5からその一部が標準実装されてしまっていた。
 確かに、CGIで、まともなサービスを構築しようとしたら、自分なりにC(心ゆくまま)、G(互換)を考えることなく、I(一個ずつ)作って行けるという、完全に制約のない自由度は、当方の様な者には取っ付き易く離れ辛い環境なのだが、例えば「ユーザ認証」「ユーザ登録」「セキュリティ監視」「利用ログ管理」等、実際に本格的なWebサービスを構築しようとすると、組込むべき機能は多い。下手するとその周辺を作成する方が何倍も大変というのも現実だ。

 そんなことをテメーの作成システムで何度もやっていてたら無駄なので、そろそろレイヤーを分けて標準的な部品化を進めて試たいという誘惑に、今回誘われてしまい、今回のお題「WSGIの環境作り」ということになった。何分、始めたばかりで何も知らないが、Hellow Worldの表示迄は漕ぎつけたので、ここに記録して置くことにする。


 当方の場合嵌まりポイントは、予め.pyと.cgiはCGI実行の設定があるので、wsgi用の拡張子は別のものにしないと先に進まない。その辺はも今回、設定ファイル内に念の為、コメントとして記入させてもらっている。

 次に、mod_wsgiのインストールの時記録、Apacheの設定、そして、テストコードを順に掲載して置く。

 尚、実行結果としては「Hello world」と出れば良いので省くが、万一、「Internal Server Error」と出たら、多分cgiとして実行されているのでその辺の定義を外す、Permisionで嫌われたら、普通にWebのドキュメント領域としての許可を見直して頂ければ多分導入に成功するものと思う。

 以下の説明では最初に「mod_wsgi-2.8.tar.gz」を利用したが、このバージョンでは日本語等のmulti-biteのUnicode文字が使えないので、利用したい場合には、「mod_wsgi-3.2.tar.gz」以降の最新版を導入することをお奨めする。手順や設定はそのまま変わらない。続きを読む
タグ:WSGI mod_wsgi
posted by Mire at 19:07 | Comment(0) | TrackBack(0) | Pythonプログラミング | このブログの読者になる | 更新情報をチェックする

2010年05月31日

【PythonでCGI】SeeSaaBlogページ別解析の21番目以降も表題で

 別にお題が目的だった訳ではないのだが、愛用しているこのSeeSaaブログは、ブロガーの管理画面で、ある程度のアクセス解析情報が提供されている。しかし、首記のページ別のアクセス解析リストでは、20番目迄は、記事タイトルを表示してくれているものの、それ以降は、URL末尾のみの表示で端折っている。まだまだ、アクセスが少なかった頃には、その仕組みも判らず気になっていたものだ。アクセス投稿についてタイトル表示をさせると、その分負荷が増すので、前20件迄としたことは妥当なことかとは思うが、ユーザとして、見えないものは気になることもあるだろう。日公開のソースコードは、そんな向きに役立つのかもしれない。

 動機は、どちらかと言うと、何時も忘れた頃に取組む「正規表現」に少しは慣れておきたかったというのが本音で、その題材としてSeeSaaブログのアクセス解析の表示補正を宛がったに過ぎないので、それなりの自作の正規表現を試行錯誤で投入している。汎用的に関数化したものも条件次第では修正が必要となることもあろうかと思う。その点ご承知置きの上で、御覧頂きたい。

 まあ、SeeSaa側のセキュリティに関するお考えもあるかと思うので、管理画面の配置の説明やページのHTML文DLしたものについては公開しない。代わりに、いつもはしないブラウザのスナップショット画像で結果を掲載して置く。

 他のブログ等の解析画面でも、ソースコードのトップにまとめている、初期設定値を、変更することで概ね対応出来ると思うので、興味のある方は、そうして欲しい。何分管理画面は認証後に表示可能なエリアであるので、単純にはurllibでそのHTML文書を取得することは出来ないこともあり、ご利用のブラウザでページのソースを表示後、名前を付けて保存して、そのファイル名を書込んで利用頂く必要がある。

「SeeSaa マイ・ブログ アクセス解析 ページ別」画面の21番目以降もタイトル表示

 技術的には、元のHTML文のファイルの文字コードの定義に合わせてUnicode化してから、「.*?」を「<」「>」やタグ文字等、タグ文字列を特定する正規表現のパターン文字列を作成し、re.find(pat, string)で全該当タグ文字列を抽出リスト化することと、文字列メソッドのreplace(<置換対象文字列>, <置換え後文字列>)で、最終、HTML文内でタイトル表示を置換えていることぐらいだろう。ソースコードは以下の通り。続きを読む
posted by Mire at 01:59 | Comment(0) | TrackBack(0) | Pythonプログラミング | このブログの読者になる | 更新情報をチェックする

2010年05月29日

【PythonでCGI】dbmのApache2.2認証データをPythonで処理

 前稿や「【PythonでCGI】Apache2.2認証データをdbmで管理」等で、取敢えず、Apache2.2の認証データの管理は出来る訳だが、逐次popen()でプロセスを別に起こす手法にはやはり抵抗がある。(古い話だが、treadでpopen()を実行させ同時に大量のプロセスが存在するツールを作成し運用したことがあったが、その場合OSの上限に達すると、それ以上のプロセスが実行出来なくなる現象を確認している。最近のOSでは経験していないが、過負荷による不測の事態を招く可能性は可能な限り少なくしたいからである。)
 また、Aapcheとブラウザ間のBASIC認証とは別に要所でCGI認証を求める等のもう少し柔軟な運用をするには、書込まれた認証情報を読出し、ユーザの叩くIDとパスワードを照合すべく、CRYPTやMD5等の暗号化Apache流にエミュレートすることが必要でもあるので、技術的にクリアしておきたいのである。

 Apache2.2が利用可能なDBMの種類はオプション設定の「SDBM|GDBM|DB|default」最大で4種の可能性があるが、これが全て使えるかどうかは、OS側の実装とAapche2.2のビルド前のconfigure実行時に決定されるし、Python側で使えるDBMの種類も同様だろう。先ずは当方のCentoOS5.4の環境で、htdbmコマンドオプションで4種類のオプションと無指定で作成したDBMデータベースファイル5個を用意しPython側の数あるdbmインターフェイスモジュールを総当たりでテストして試た結果は、「DB」をオプションとして作成したもののみが、Pythonの「dbhash」モジュールで接続可能であるということが判った。

 恐らく、OS, Apache, Pythonの実装時にDBMを含めちゃんとビルドすれば、全てのオプションで有効になるのだろうが、当方の様に成行きに任せOSをインストールし、関連ライブラリの殆どをセキュリティ管理をYUM君にお任せにしたいとの理由でRPMでの標準提供分の活用に留めている輩には止むを得ないことだろう。ソースインストールすると、個別にセキュリティパッチを追っていかなければならないので、良く利用し深く関わるものに絞らざるを得ないのが現実だろう。そんな向きの方々は、末尾掲載のソースコード「./chk_dbm_and_hash.py」でテストして頂けば比較的簡単に判定出来るだろうし、アクセス方法も理解出来るものと思う。


 残る、CRYPTとMD5によるHASH暗号化についてもそのソース内で確認出来るが、CRYPTのみはPythonの標準モジュールcryptをインポートして処理出来る。しかし、MD5やSHAについては標準モジュールmd5, sha1で直接対応出来ない。今回SHAについて解を提供することは出来ないが、MD5については、Pypiにも登録されている次のものが使える。ライセンスはGNU General Public License (GPLv3)なので、問題があれば報告と改変については公開頂くことを前提にして頂ければ、通常フリーに利用出来る。

python-aprmd5

 尚、当方は、ここより、「python-aprmd5-0.2.tar.gz」をDLしビルド後インストールさせてもらった。その操作は次の通りだ。
           Installing python-aprmd5-0.2 in my CentOS5.4


[root@l22dm ~]# wget http://www.herzbube.ch/software/python-aprmd5/0.2/python-aprmd5-0.2.tar.gz
--2010-05-28 18:32:24-- http://www.herzbube.ch/software/python-aprmd5/0.2/python-aprmd5-0.2.tar.gz
www.herzbube.ch をDNSに問いあわせています... 212.101.18.224
www.herzbube.ch|212.101.18.224|:80 に接続しています... 接続しました。
HTTP による接続要求を送信しました、応答を待っています... 200 OK
長さ: 31935 (31K) [application/x-gzip]
`python-aprmd5-0.2.tar.gz' に保存中

100%[========================================================>] 31,935 30.2K/s 時間 1.0s

2010-05-28 18:32:26 (30.2 KB/s) - `python-aprmd5-0.2.tar.gz' へ保存完了 [31935/31935]

[root@l22dm ~]# tar xvzf python-aprmd5-0.2.tar.gz
python-aprmd5-0.2/
python-aprmd5-0.2/build-and-test.sh
python-aprmd5-0.2/doc/
python-aprmd5-0.2/doc/ChangeLog
python-aprmd5-0.2/doc/COPYING
python-aprmd5-0.2/doc/INSTALL
python-aprmd5-0.2/doc/README
python-aprmd5-0.2/doc/Roadmap
python-aprmd5-0.2/doc/TODO
python-aprmd5-0.2/PKG-INFO
python-aprmd5-0.2/setup.py
python-aprmd5-0.2/src/
python-aprmd5-0.2/src/extension/
python-aprmd5-0.2/src/extension/aprmd5.c
python-aprmd5-0.2/src/extension/aprmd5.h
python-aprmd5-0.2/src/extension/aprmd5_helpers.c
python-aprmd5-0.2/src/extension/aprmd5_helpers.h
python-aprmd5-0.2/src/extension/aprmd5_md5type.c
python-aprmd5-0.2/src/extension/aprmd5_md5type.h
python-aprmd5-0.2/src/extension/aprmd5_wrappers.c
python-aprmd5-0.2/src/extension/aprmd5_wrappers.h
python-aprmd5-0.2/src/packages/
python-aprmd5-0.2/src/packages/tests/
python-aprmd5-0.2/src/packages/tests/__init__.py
python-aprmd5-0.2/src/packages/tests/test_leak.py
python-aprmd5-0.2/src/packages/tests/test_md5.py
python-aprmd5-0.2/src/packages/tests/test_md5_encode.py
python-aprmd5-0.2/src/packages/tests/test_password_validate.py
[root@l22dm ~]# cd python-aprmd5-0.2
[root@l22dm python-aprmd5-0.2]# ls
PKG-INFO build-and-test.sh doc setup.py src
[root@l22dm python-aprmd5-0.2]# /usr/local/bin/python2.6 ./setup.py build
running build
running build_ext
building 'aprmd5' extension
creating build
creating build/temp.linux-i686-2.6
creating build/temp.linux-i686-2.6/src
creating build/temp.linux-i686-2.6/src/extension
gcc -pthread -fno-strict-aliasing -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -DAPRMD5_HEADER_FILENAME= -I/usr/local/include/python2.6 -c src/extension/aprmd5.c -o build/temp.linux-i686-2.6/src/extension/aprmd5.o -isysroot /
gcc -pthread -fno-strict-aliasing -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -DAPRMD5_HEADER_FILENAME= -I/usr/local/include/python2.6 -c src/extension/aprmd5_md5type.c -o build/temp.linux-i686-2.6/src/extension/aprmd5_md5type.o -isysroot /
gcc -pthread -fno-strict-aliasing -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -DAPRMD5_HEADER_FILENAME= -I/usr/local/include/python2.6 -c src/extension/aprmd5_wrappers.c -o build/temp.linux-i686-2.6/src/extension/aprmd5_wrappers.o -isysroot /
gcc -pthread -fno-strict-aliasing -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -DAPRMD5_HEADER_FILENAME= -I/usr/local/include/python2.6 -c src/extension/aprmd5_helpers.c -o build/temp.linux-i686-2.6/src/extension/aprmd5_helpers.o -isysroot /
creating build/lib.linux-i686-2.6
gcc -pthread -shared build/temp.linux-i686-2.6/src/extension/aprmd5.o build/temp.linux-i686-2.6/src/extension/aprmd5_md5type.o build/temp.linux-i686-2.6/src/extension/aprmd5_wrappers.o build/temp.linux-i686-2.6/src/extension/aprmd5_helpers.o -laprutil-1 -o build/lib.linux-i686-2.6/aprmd5.so -isysroot /
[root@l22dm python-aprmd5-0.2]# /usr/local/bin/python2.6 ./setup.py install
running install
running build
running build_ext
running install_lib
copying build/lib.linux-i686-2.6/aprmd5.so -> /usr/local/lib/python2.6/site-packages
running install_egg_info
Writing /usr/local/lib/python2.6/site-packages/python_aprmd5-0.2-py2.6.egg-info


 パスワードの暗号化管理については色々と意見があるのも事実だ。MD5については既に本来のHASH値としての一方向性が破られてしまっているとか、SHAについては、MD5よりマシとの意見もあるが、Apache上の利用では推奨出来ないとか、おまけにcryptはそれよりも...とかの評価も有ったりで、一体どれを使ったら良いのよぉ〜。といった感じである。
 無論これは、あくまで暗号化後のパスワードが漏えいした場合の生のパスワード復元可能性のことなので、データベースファイルを盗まれない様にすることと、認証管理ツール経由での流出が発生しない様にすれば、例えば、(推奨しないしユーザからの信頼が取れないが)平文テキストでも問題ないことだ。 Aapche 標準の認証システムの現状がこれで巷で問題が起きていないのであるから、セキュリティホールを発生させない様システム作成することに専念すべきだろう。状況によりPAM経由で外部認証システムでという解もあることだし。

 これで取敢えず、多めのユーザ相手に公開Webを開発するApache認証の技術的な基礎は一通り押さることは出来た(筈?)。


          python ./chk_dbm_and_hash.pyの実行例 


[root@l22dm ~]# python chk_dbm_and_hash.py


## 各種DMBファイルの作成と各種HASH別のユーザ登録 ##
htdbm -bc ./T_NONE.dbm user_none passwd1 2>> ~/chk_dbm_and_hash.log
htdbm -bs ./T_NONE.dbm user_sha1 passwd1 2>> ~/chk_dbm_and_hash.log
htdbm -bm ./T_NONE.dbm user_md5 passwd1 2>> ~/chk_dbm_and_hash.log
htdbm -bd ./T_NONE.dbm user_crypt passwd1 2>> ~/chk_dbm_and_hash.log
htdbm -bp ./T_NONE.dbm user_plain passwd1 2>> ~/chk_dbm_and_hash.log

htdbm -bc -Tdefault ./T_default.dbm user_none passwd1 2>> ~/chk_dbm_and_hash.log
htdbm -bs -Tdefault ./T_default.dbm user_sha1 passwd1 2>> ~/chk_dbm_and_hash.log
htdbm -bm -Tdefault ./T_default.dbm user_md5 passwd1 2>> ~/chk_dbm_and_hash.log
htdbm -bd -Tdefault ./T_default.dbm user_crypt passwd1 2>> ~/chk_dbm_and_hash.log
htdbm -bp -Tdefault ./T_default.dbm user_plain passwd1 2>> ~/chk_dbm_and_hash.log

htdbm -bc -TSDBM ./T_SDBM.dbm user_none passwd1 2>> ~/chk_dbm_and_hash.log
htdbm -bs -TSDBM ./T_SDBM.dbm user_sha1 passwd1 2>> ~/chk_dbm_and_hash.log
htdbm -bm -TSDBM ./T_SDBM.dbm user_md5 passwd1 2>> ~/chk_dbm_and_hash.log
htdbm -bd -TSDBM ./T_SDBM.dbm user_crypt passwd1 2>> ~/chk_dbm_and_hash.log
htdbm -bp -TSDBM ./T_SDBM.dbm user_plain passwd1 2>> ~/chk_dbm_and_hash.log

htdbm -bc -TGDBM ./T_GDBM.dbm user_none passwd1 2>> ~/chk_dbm_and_hash.log
htdbm -bs -TGDBM ./T_GDBM.dbm user_sha1 passwd1 2>> ~/chk_dbm_and_hash.log
htdbm -bm -TGDBM ./T_GDBM.dbm user_md5 passwd1 2>> ~/chk_dbm_and_hash.log
htdbm -bd -TGDBM ./T_GDBM.dbm user_crypt passwd1 2>> ~/chk_dbm_and_hash.log
htdbm -bp -TGDBM ./T_GDBM.dbm user_plain passwd1 2>> ~/chk_dbm_and_hash.log

htdbm -bc -TDB ./T_DB.dbm user_none passwd1 2>> ~/chk_dbm_and_hash.log
htdbm -bs -TDB ./T_DB.dbm user_sha1 passwd1 2>> ~/chk_dbm_and_hash.log
htdbm -bm -TDB ./T_DB.dbm user_md5 passwd1 2>> ~/chk_dbm_and_hash.log
htdbm -bd -TDB ./T_DB.dbm user_crypt passwd1 2>> ~/chk_dbm_and_hash.log
htdbm -bp -TDB ./T_DB.dbm user_plain passwd1 2>> ~/chk_dbm_and_hash.log



## 各種DBMファイルの読取りテスト ##
#########################################################################
./T_NONE.dbm
推定されるDBの種類: DBM
dbm.open()で処理出来ませんでした
gdbm.open()で処理出来ませんでした
anydbm.open()で処理出来ませんでした
DUMBDBM 0
==============================
dbhash.open()で処理出来ませんでした

#########################################################################
./T_default.dbm
推定されるDBの種類: DBM
dbm.open()で処理出来ませんでした
gdbm.open()で処理出来ませんでした
anydbm.open()で処理出来ませんでした
DUMBDBM 0
==============================
dbhash.open()で処理出来ませんでした

#########################################################################
./T_SDBM.dbm
推定されるDBの種類: DBM
dbm.open()で処理出来ませんでした
gdbm.open()で処理出来ませんでした
anydbm.open()で処理出来ませんでした
DUMBDBM 0
==============================
dbhash.open()で処理出来ませんでした

#########################################################################
./T_GDBM.dbm
推定されるDBの種類: DUMBDBM
dbm.open()で処理出来ませんでした
gdbm.open()で処理出来ませんでした
anydbm.open()で処理出来ませんでした
DUMBDBM 0
==============================
dbhash.open()で処理出来ませんでした

#########################################################################
./T_DB.dbm
推定されるDBの種類: DUMBDBM
dbm.open()で処理出来ませんでした
gdbm.open()で処理出来ませんでした
anydbm.open()で処理出来ませんでした
DUMBDBM 0
==============================
DBHASH 5
user_none : $apr1$t1Oyk...$mtsNnd0RJnC/OptArgxlp/
user_md5 : $apr1$t1Oyk...$mtsNnd0RJnC/OptArgxlp/
user_crypt: t1H9ROuaOCiWs
user_plain: passwd1
user_sha1 : {SHA}Pxnr0lI9/tebhMmJryYNfP6/eC0=
==============================
salt: MD5='t1Oyk...' CRYPT='t1'



## python側の各HASH処理関数での結果 ##
下記のsalt値と生のパスワードのみから各HASH関数でHASH値を生成して試る
password = 'passwd1'(テスト用なのでこの値に固定)
md5_salt = 't1Oyk...'
crypt_salt = 't1'

(値は、access_test()関数の返値で最後に取得したもの
×Md5()password.hexdigest() : 79e262a81dd19d40ae008f74eb59edce
×Sha1()password.hexdigest() : 3f19ebd2523dfed79b84c989af260d7cfebf782d
○crypt(password,salt) : t1H9ROuaOCiWs
×Mb64encode(md5(password).hexdigest()) : NzllMjYyYTgxZGQxOWQ0MGFlMDA4Zjc0ZWI1OWVkY2U=
○md5_encode(password, md5_salt) : $apr1$t1Oyk...$mtsNnd0RJnC/OptArgxlp/
×Sb64encode(sha1(password).hexdigest()): M2YxOWViZDI1MjNkZmVkNzliODRjOTg5YWYyNjBkN2NmZWJmNzgyZA==
[root@l22dm ~]#
続きを読む
posted by Mire at 02:57 | Comment(0) | TrackBack(0) | Pythonプログラミング | このブログの読者になる | 更新情報をチェックする

2010年05月26日

【PythonでCGI】Apache2.2認証データをdbmで管理

 最初に断って置くが、本稿で掲載のソースコードは、テストコードであり、HTMLのCGI実行領域に設置するレベルのものではない。試す方は、普通に、ログインしたユーザのhomeディレクトリ辺りにコピペで行なって頂きたい。セキュリティが絡む分をWeb領域に置くべきでない。その上、その理由もあって、CGIが送るべきヘッダ情報を今回は付けていない。

 さて、Apache2.2の認証用モジュールは分業制となり、従前とは一変してしまった。結果として従来のモジュールの殆どが使えなくなってしまており、FLAP(Firebird, Linux, Apache, Python)派の当方にとっては悩ましい状態に陥っている。Apache2.0用であれば認証データをFirebirdで管理すべくモジュールを作った方もいらっしゃったので何とかなる様なのだか、2.2では見当たらない。せいぜいODBC経由での接続かな? と思いやって試たが、ODBC-Apache 間の接続が上手く行かない。どうせ単純なチェック漏れが数か所あるだけなのだろうが考えて試ると認証パスワード自体の管理DBを、Firebirdに保持するメリットは自己満足以外には、管理方法が判っているので、個人的により安全な管理が可能にする努力をする可能性があるくらいのことなので、それは後の楽しみに取って置くことにした。で、先ずはバークレイDBを見て試たが思った様に情報が集まらなかった。そこで、同様にdbmも少なかったが、幸い、Apacheに管理用コマンドが添付されているのでそちらを使い手っ取り早く、先に進むことにした。

 そのコマンドは「htdbm」で、インストールが済んでいるなら、コマンドラインでそのまま叩いて頂ければ、簡単な使い方が英語で出てくる。設計思想は、ファイル管理用の「htpasswd」とあまり変わらないので理解し易い。

初回の管理データベースを同時に作成するときには、-cオプション
コマンドの引数としてバスワードを引数で渡すときは、 -bオプション
HASH処理なら、-mはMD5、-sはSHA、-dは既定のcriptで、テスト時等平文のままが良いときは-pとする点は同じであるので、ざっと次の様な操作となる。

1. 初回のDB作成とユーザ認証データの登録

htdbm -bcs /etc/httpd/mdb/plasa_with_auth.dbm user01 anshoumoji

2. 2回目以降のユーザ認証データの登録

htdbm -bs /etc/httpd/mdb/plasa_with_auth.dbm user02 naishonomoji

3. 登録済ユーザID一覧の取得

htdbm -l /etc/httpd/mdb/plasa_with_auth.dbm


 これを使ってWeb管理者がちまちまとユーザ登録するのも良いだろうが、システム化しないと管理者の行動に全て依存してしまうので面白くないし、何れもう所属しない幽霊ユーザだらけとなり本来のセキュリティすら保てなくなりかねない。Web管理者としては出来るだけ手間をかけたくないし、同時にセキュリティ状態も健全に保ちたいものだ。

 そこで、「取敢えずビール」的に、このコマンドをpythonスクリプトから読込んで新規ユーザ登録時の関数をPythonで作って試た。今回思想的に、DBファイルの新規作成くらいは、管理者が行なうべきと思ったので-cオプションを付けたものはない(例えば、管理者複数居てその方達が、ドメイン別に認証ユーザ管理を別々に行なう必要がある場合等の用途なら、その管理者のドメイン作成画面にはありだろう)。

          下記ソースコードをモジュールimportした時のhelp()関数出力 


Help on module auth_reg_with_dbm:

NAME
auth_reg_with_dbm

FILE
~/html/auth_reg_with_dbm.py

DESCRIPTION
##############################################################################
# Apache2.2での dbmによる認証登録
#
#  個人的にはもっと格好良くpythonモジュールで全て処理したものを
# 掲載したいのだが、何分、dbm形式のDBに触れるのは初めてだったので
# 手っとり早くpopen()でapache付属のツール「htdbm」を呼んで処理し
# たものを手っ取り早く作成した。
#  まぁ、Python歴が長い割にローテクなので、過去から良くこの手の
# 逃げで時をSAVEして来たので、こんな着ぐるみ作成はオハコなのだ。
#  今回のポイントは、実行時の画面メッセージの取得で「2>」を使った
# ところ辺り位だろう。
#
#
#利用している外部プログラム:htdbm::
#
#  尚、htdbmコマンドのオプションは次の通りであり、ユーザ登録や
# パスワード関数を利用する前に、予め、-cオプションを使いdbmの
# DATABASEファイルを指定するパスに生成して置く必要がある。::
#
# htdbm -- program for manipulating DBM password databases.
#
# Usage: htdbm [-cmdpstvx] [-TDBTYPE] database username
# -b[cmdptsv] [-TDBTYPE] database username password
# -n[mdpst] username
# -nb[mdpst] username password
# -v[mdps] [-TDBTYPE] database username
# -vb[mdps] [-TDBTYPE] database username password
# -x[mdps] [-TDBTYPE] database username
# -l [-TDBTYPE] database
# Options:
# -b Use the password from the command line rather than prompting for it.
# -c Create a new database.
# -n Don't update database; display results on stdout.
# -m Force MD5 encryption of the password (default).
# -d Force CRYPT encryption of the password (now deprecated).
# -p Do not encrypt the password (plaintext).
# -s Force SHA encryption of the password.
# -T DBM Type (SDBM|GDBM|DB|default).
# -l Display usernames from database on stdout.
# -t The last param is username comment.
# -v Verify the username/password.
# -x Remove the username record from database.
# (この内容はhtdbmを引数無しで実行すると出で来るが、man htdbmで少し詳しい説明がある)
#
#
# dbmでBASIC認証データの管理をするには、次の様な内容の設定ファイルを作成し
# 「/etc/httpd/conf.d/」辺りに放り込み「service httpd restart」とする。
#
# 「my_site_with_dbm」::
#
# <Directory "/var/www/html/dir_with_auth">
# AuthType basic
# AuthName "test area of auth with mdb"
# AuthUserFile /etc/httpd/.htpasswd
# AuthBasicProvider dbm
# #AuthDBMType SDBM # <どれが良いか未研究なので指定していない>
# AuthDBMUserFile /etc/httpd/.htdbm_default.dbm
# Require valid-user
# </Directory>
#
#
#FUNCTIONS::
#
# 01. user_exists(): ユーザID登録有無確認用関数
# 02. reg_user() : ユーザ新規登録関数
# 03. chg_passwd() : パスワード変更関数 coming soon??

FUNCTIONS
chg_passwd(user='', password='', hash='sha', db_type='', dbm_file='', tmp_file='', n=6, debug=0)
Password変更未作成
コードのほとんは、reg_user()と共通となる。但し、本人のログイン中の
処理となる保障を環境変数照合等でとる等の仕組作りが望ましいかも。
出来れば、アカウント変更には、再ログインが必要とすべきかもしれない。

rand_texts(n, text=u'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz')
ランダムに指定文字数の文字列を返す関数 ※ これは、以前Control Lettersで使った関数
Parameters:: 手っとり早く、引数textの既定を変えただけ
n : 文字数 Unix系であればファイル名の大文字小文字の
text : 表示候補文字の文字列 区別がある。記号は安全なもののみとした。
※ 疑似乱数で指定文字数の文字を選択し
返すだけなので同時アクセス時に同一の
文字列を返す確率は存在することを認識
の上利用のこと。

reg_user(user='', password='', hash='sha',
hashs={'CRIPT': 'd', 'MD5': 'm', 'PLAIN': 'p', 'SHA': 's'}, db_type='',
dbm_file='', tmp_file='', n=6, debug=0)

ユーザ新規登録関数

user : ユーザID
db_type : DBタイプ:['SDBM', 'GDBM', 'DB', 'default']省略可
dbm_file : dbmファイルへのパスを指定
tmp_file : これを与えると、それをそのまま返す。
n : 疑似乱数で生成する文字列の文字数を指定


※ 管理者の管理画面かユーザの個人確認後に利用。ユーザ名については、
ここの認証DBへの登録だけでなく、ユーザ管理DB側での確認と仮登録成功を
条件に利用すること。
 尚、通常ブラウザ認証ダイアログによる認証では残念ながら、内部IDを
分離することが出来ないが、将来的には、入力IDと管理DB上のIDは別の
ものにしたい。
 また、普通のHTTPでなくSSLによる通信経路の暗号化程度を施した頁で
運用すべきことは、当然のことで、それも判らないなら使うべきでない。

rnd_name(n=6, text=u'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz')
指定文字数の英数と_をランダムに並べた文字列と年月日時分秒を
つなげた一時ファイル名

※ 生成する一時ファイル名に年月日時分秒を加えることで、
運用時の重複衝突の確率をサーバの返す時計時間の秒単位内
での重複に軽減した。日常的に反復し重複衝突が起きるとは
思えないが、あくまで他に方策がない場合の次善の策と認識
頂きたい。数字上はtextが規定値通りとして63のn乗分の1と
nを増やせば実用面で具合が出ないだけなので、出来れば、
DBのカウンタ辺りで整理番号を発行し用いた方が完全だろう。

user_exists(user='', db_type='', dbm_file='', tmp_file='', n=6)
ユーザ名リストと照合し登録済かを返す関数

user : ユーザID
db_type : DBタイプ:['SDBM', 'GDBM', 'DB', 'default']省略可
dbm_file : dbmファイルへのパスを指定
tmp_file : これを与えると、それをそのまま返す。
n : 疑似乱数で生成する文字列の文字数を指定

DATA
__author__ = 'Mire in Japan'
__copyright__ = 'Copyright (c) 2010 Mire'
__license__ = 'GPL'
__url__ = 'http://pythonlife.seesaa.net/article/151109780.htm'
__version__ = '0.0.0'
history = '2010-05-26 \xe5\x88\x9d\xe7\x89\x88 mire.apache2.2\xe3\x81\...

VERSION
0.0.0

AUTHOR
Mire in Japan
 ソースコードは次の通り。例によりウザイ程のコメント付だ。続きを読む
posted by Mire at 01:45 | Comment(0) | TrackBack(0) | Pythonプログラミング | このブログの読者になる | 更新情報をチェックする

2010年05月23日

【PythonでCGI】mire.htm 0.6 & WebのTABLEのセル情報取得と表示

 過去、公開したHTML関連のPythonコードを備忘の為整理し、当方の公開モジュール「mire.htm」に追加したので、ここに公開しておく。中には、作ったばかりでこなれてないものや、発想のみで動かないものもある(ヲイ)ので、適宜補正して行くつもりなので、ご容赦頂きたい。(2010-05-23:30 眺めているとコピペによる変数置換漏れと条件別returnに漏れがあることが判ったので補正完了)

 併せて、追加関数の一部を使ったサンプルとして、「WebのTABLEのセル情報取得と表示」を様々なWeb上の作表頁で試してみたものも添付しておく。やったこと自体は、何れも過去公開したコードのものと大差ないが、機能をモジュール関数化し分離することで、処理の定形化が進み、DEBUGがし易くなり、目的通りの出力を得られるものを増やすことが出来た。無論、サンプルで挙げた場当たり的に目に付いたURLをテスト用に登録して試た訳だが、一見、普通にテキストで構成されたTABLEタグの作表と思いきや、画像やPREタグによるものだったりで、サンプル機能としては対象外のものも多いのが現実だ。サンプルでは、COLSPANやROWSPANで発生する列位置のずれを補正していない課題も残っているものの、例えば自治体コード等の様な一般にはファイル公開されていない国内共通規格データでも、HTMLにアクセスすることで最新データを定期的に取得することも可能になる(自治体コード自体は特別な用途でなければ、既稿にある日本郵便の郵便番号簿データからの取得がお勧め)。

 国は、電子政府方針を掲げるが、ネット上の公開形態は、XMLやGET引数による任意データの提供は少ない。せめて、CSVやTSVファイルでの提供を願うが、現実には統計データという税金で集計したデータもPDFファイルという人が目で見ることを前提にした見栄え重視で人が編纂した形での掲載なので、データ抽出には基本的に人による編集プレを加味する必要がある。勿論、PDFファイルにする前のWordやExcelファイルを併せて掲載していることもあるが、編集プレは変わりがない。今回のサンプルでも編集プレによるデータ取得失敗発生があり得るが、行政がその分野別の色々な要請に忠実に従い作り上げたシステムであり、行政というビジネスモデル維持の為の集金と絡めているふしもあるので、一庶民としては、そこは我慢してブレを吸収出来るものにツールを育てていくしかないだろう。

 サンプルのソースコードは以下の通りだか、今回、TRタグを閉じない形の頁も見付ったので、それに対応、UNICODE化で弾かれる文字を含む場合の対応としてas_unocodeのオプションを追加した。対策部分は、末尾掲載のmire.htmのソースコードで確認頂きたい。
          WebのTABLEのセル情報取得と表示用テストサンプルコード 


#!/usr/local/bin/python2.6
# -*- coding: UTF-8 -*-
from mire.htm import now

u ={ '01. 自治体コード':'http://www.lasdec.nippon-net.ne.jp/cms/1,68,14,187.html'
,'02. 気象統計(TRタグの閉じ無し)':'http://www.data.jma.go.jp/obd/stats/etrn/view/nml_sfc_10d.php?prec_no=82&prec_ch=%95%9F%89%AA%8C%A7&block_no=47807&block_ch=%95%9F%89%AA&year=&month=&day=&view=' #必要なデータ取れず=TRタグ閉じなし
,'03. 総務省 統計局 人口推計月報(画像の表じゃOCR処理でなきゃ無理)':'http://www.stat.go.jp/data/jinsui/tsuki/index.htm'
,'04. 前場後場4本値株価':'http://k-db.com/site/jikeiretsu.aspx?c=9508-T&amp;download=csv'
,'05. Yahoo日経平均':'http://stocks.finance.yahoo.co.jp/stocks/history/?code=998407.O'
,'06. モーニングスター':'https://www.morningstar.co.jp/RankingWeb/'
,'07. JIS X 0208 文字コード表(PREタグだった)':'http://ash.jp/code/codetbl2.htm' #
,'08. Wikipedia Help:特殊文字XS (取得失敗。原因不明)':'http://ja.wikipedia.org/wiki/HTML%E5%AE%9F%E4%BD%93%E5%8F%82%E7%85%A7%E5%AF%BE%E7%85%A7%E8%A1%A8' #
,'09. @IT Firebird関数 (方形TABLEでない)':'http://www.atmarkit.co.jp/fdb/rensai/08_firebird/04/firebird04_01.html' #何にも
,'10. WikiMatrix(項目が画像とは)':'http://www.wikimatrix.org/compare/Zwiki+MoinMoin+MediaWiki+TikiWiki-CMS-Groupware' #何にも
,'11. Python Ref':'http://www.python.jp/doc/2.5/ref/strings.html'
,'12. TV SO-NET':'http://tv.so-net.ne.jp/chart/23.action?head=%04d%02d%02d%02d%02d' % (now()[:5]) #不適格例
,'13. TV Goo':'http://tv.goo.ne.jp/contents/epg/008/VHF_1/today_0024/index.html' #不適格例
,'14. TV Infoseek (JavaScript)':'http://tv.infoseek.co.jp/' #不適格例Javascript
,'15. TV TBS':'http://www.tbs.co.jp/tv/'
,'16. 政府統計の総合窓口(取得失敗。原因不明)':'http://www.e-stat.go.jp/SG1/estat/List.do?bid=000001008301&cycode=0' #不適格例
,'17. 厚生省1':'http://www.mhlw.go.jp/toukei/saikin/hw/k-tyosa/k-tyosa09/1-1.html'
,'18. 厚生省2':'http://www.mhlw.go.jp/toukei/saikin/hw/k-tyosa/k-tyosa09/1-2.html'
}

def prn_tbl(url_dic):
"""
Web上の様々な作表よりデータ抽出を試み、それをテーブル表示
"""
from types import ListType
from mire.htm import display_err, html_header, html_footer
from mire.htm import url2str_list, url2str, tbl2lists, list2tbl
from mire.htm import str2unicode, str_list2unicode, get_charset

keys = url_dic.keys() # 引数のURL辞書のキー名称のリストを作り
keys.sort() # 並替え
for k in keys: # それを一つずつ処理
print '<table width="100%" boder="1"><tr><td>'
url = url_dic[k] # 引数の辞書にキーを指定してURLを取出し、変数urlに設定
print u'<br><h3 style="background-color:#EEF;">',
print u'<a href="%s" target="_blank">%s</a></h3>' % (url, k)
try:
lst = tbl2lists(url, fit_type=1, # 整数化可能ならを整数化
tr_pat=1, as_unicode=1) # TR開始タグのみで抽出,UNICODE化後に処理
if type(lst) is ListType: # 正しくリストが返されたら
for l in lst: # 一つずつ、中身を取出し
list2tbl(l) # TABLEタグの表にする
else:
print 'Nothing' # 失敗(BUG)している
except:
display_err(locals()) # 想定外の動作をした時の状態を表示
print '</td></tr></table>'
##print 'テスト<br/>'
#list2tbl(str_list2unicode(tbl2lists(url2str(url), as_unicode=0), charset))
from mire.htm import display_err, html_header, html_footer
html_header(charset="UTF-8")
prn_tbl(u)
html_footer()


 以下に、help(mire.htm)の出力とmire.htmを掲載するので、興味がある方はご覧頂くなり、ご利用頂くなりご自由にどうぞ。


         python /usr/local/lib/python2.6/site-packages/mire/htm.py 


Help on module mire.htm in mire:

NAME
mire.htm

FILE
/usr/local/lib/python2.6/site-packages/mire/htm.py

DESCRIPTION
######################################################################
#00. header(Type) : cgi Content出力。Type:'html'(既定),'xml'
#01. html_header(charset, : html文書の開始タグ一式を標準出力します
# ,title,base :
# ,refresh :
# ,cache,robot:
# ,css,style :
# ,js,author :
# ,generator :
# ,copyright :
# ,keywords :
# ,description:
# ,body) : bodyつまり文書全体の属性定義をリストで付与
#02. html_footer() : html文書の終了タグ一式を標準出力
#03. url2str_list(url) : urlの示す文書を文字列リストで返します
#04. url2str(url,trim=0) : urlを文字列で返します。前後空白除去trim=1
#05. get_charset(s) : str文字列からmeta定義のcharsetを返します
#06. str2unicode(s,charset) : 文字列のunicode化を試みその文字列を返します
#07. str_list2unicode(lst) : リストに対し再帰的にstr2unicode()の処理を試み
# : その文字列リストを返します
#08. cgi_params() : CGI引数を辞書化し返します
#09. param_lst(omit_params : 引数とその値をGET形式'引数名=値'文字列listを
# ,params_dic): 返す「url+'?'+'&'.join(<param_lst>)」で活用
#10. tag_encode(lines) : タグ文字のままHTML表示可能な文字列listに変換
#11. display_err(locals() : HTMLヘッダを伴ない、例外発生時の変数状況を
# ,title): locals()で、titleに仕掛けた場所を入れ利用。
#12. dsp_hex(s) : 文字列の文字化けをunicode化で極力無くす試みの上
# : 出来なかったものは、16進表記で済ませる。
#13. pipe(url,Type='xml') : 指定URIを読みそのまま返します。ドメイン外部の
# : XMLコンテンツをjavascript活用する中継プログラム
#14. str_select_form(id : idをnameとする対話型選択フォームの文字列を出力
# ,params : cgi_params()で取得出来るリストを指定
# ,options : 選択肢をその値をキーとし表示を値とする辞書で指定
# ,default : 既定値(未対応)
# ,alt : tips表示
# ,style='' : 既定のstyle
# ,prefix : 前値を表す引数名の前置文字列
# ,changed_color: 変更があるときの背景色
# ,hidden) : 前値を扱うhiddenフォームの有無
#15. str_input_form(id : テキスト入力フォーム
# ,params,type,align :
# ,size,default,alt :
# ,style= :
# ,changed_color :
# ,prefix, hidden) :
#16. dsp_date_form(params : 年月日選択フォーム
# ,localtime=now() : 既定値=当日の年月日
# ,id=['y','m','d']: 選択フォームのname
# , years_range=[-10,+5]:
# , prefix, hidden ):
#17. list2tbl(datas,title='': 方形リストを表で出力
# , col_names=[], sql='':
# , border=1, :
# , style= :
# , font-size:6pt;' ):
#18. fprn_data(d, tag='td') : データ型別に書式付印刷
#19. listing(str_list :
# , title='', h='h4' :
# , tags='ol', tree=1 :
# , nothing='&#182;' :
# , nest=0) :
#20. get_env() : 環境変数リストの出力
#21. stat_ssl() : SSL通信状態
#22. remote_user() : 認証済のリモートユーザのID。#環境変数os.environ
#23. remote_ip() : リモート端末のIP #から取得している
#24. tbl2lists(url) : Web上のTABLEタグ表記の2D表から
# : セル値を抽出リスト群化し返す関数
#25. td_tags(text) : <td></td>で分割する関数
#26. fit_str_list(lst) : 数字化が可能な文字は全て数字に変換

FUNCTIONS
cgi_params()
## CGI引数の辞書化 ##
cgi.FieldStorage()で得られるcgi引数を辞書に収め返す
辞書構造とすることで修正が可能になる他、
必要な既存の引数を引継ぎインタラクティブにページを変化
させるときに便利。
【使い方】
下記の様に引数名を keys()で取得、順不同の為、必要より、
sort()の上、引数名を1つずつ取出し活用すると良い
param_dic = cgi_params()
keys = param_dic.keys()
for k in keys:
value = param_dic[k]
print k,value
※ この関数は引数値全部をリストで返す為、セキュリティ重視の場面での
利用が好ましくないことも多い。

display_err(local, title=u'\n\n#### EXCEPTION ERROR ####', need_header=1)
######################################################################################
#エラー表示関数(コメント行文字化け対策+thread実行時ERROR情報表示の時系列混乱軽減) #
#USAGE: #
# 第一引数に「locals()」を指定することで、処理時の変数値を表示 #
# waitingはthread実行時のエラー情報表示の時系列混乱軽減の為のWAIT処理 #
# need_headerはヘッダ記述後にヘッダ記述を省く為のものです。 #
# #
# waiting = 0 #
# def foo(): #
# global waiting #変数waitingをグローバル変数として変更可能とする宣言をする #
# need_header=1 #
# sleep(waiting) #例外発生時はここでアイドル状態が出来、割込み終了が容易に #
# try: #
# need_header = html_header(need_header=need_header) #正常処理か否かを変数 #
# 処理 #need_headerに収める #
# waiting = 0 #想定内動作時はウエイトなし #
# except: #
# display_err(locals(),'<tryブロックを表すタイトル>',need_header)# locals() #
# waiting = 1 #例外発生時は1秒アイドルウエイトとなる様に設定 #指定は必須 #
# #
# #
# if __name__ == '__main__': # このスクリプト自身が主実行ファイルの時に以下を実行 #
# foo() #
######################################################################################

dsp_date_form(params={}, localtime=time.struct_time(tm_year=2010, tm_mon=5, tm_mday...2,
tm_sec=34, tm_wday=6, tm_yday=143, tm_isdst=0), id=['y', 'm', 'd'],
years_range=[-10, 5], prefix='p_', hidden=0)
既定を現在時とする日付の選択ボックスを表示
:現在年月日以外を既定とするには

dsp_hex(s)
## コメント文字化け対策 ##

fit_str_list(lst, col_types=[])
list と tupleの要素で整数文字列のものを全て再帰的に整数に置換
col_types : 列別の変換関数をリストで指定可。
googlechart.pyのline_styleの指定要素が現在文字であるので
数字指定も可能な様にする為に利用
milestone:
1. リストを一度舐め回し、列別の標準的な型を判断する機能追加
2. 数字以外の型、例えば日付、時間型にも対応したい

fprn_data(d, tag='td', debug=0)
データ型別に書式付印刷
d : 表内のデータを指定
tag :

get_charset(s)
str文字列からmeta定義のcharsetを返します
複数見つかった場合には最初のものを返します
存在しない場合には既定値として'UTF8'を返します

get_env()
環境変数リストの出力

header(Type='html')
htmlやxmlを正常に出力し
伝達する為に必要な
Content-typeを出力
Type='html','xml',etc...

html_footer()
html文書の終了タグ一式を標準出力

html_header(charset='', title='', base='', refresh=[], cache=0, robot=0, css=[], js=[],
author='', copyright='', generator='', keywords=[], description=[], style=[],
body=[], h=1, need_header=1)
html文書の開始タグ一式を標準出力
charset : 文字コード 'Shift_JIS', 'UTF-8','EUC-JP'等
title : HTMLヘッダタイトル 文字列指定
base : 相対URL表記のベースURL 文字列指定,無指定はcgiファイルの
設置位置、'/'はサーバroot
refresh : 指定秒後に自頁または指定頁へジャンプ [秒数] or [秒数,'ジャンプ先URL']
cache : cache期限を秒数で指定 整数、-1:期限なし, 0:cacheなし
robot : 自動検索エンジンに表明 禁止:-1, 了解:1
css : スタイルシートファイルをリストで指定 []:利用しない
style : 個別のスタイルをリストで指定 []:利用しない
js : JavaScriptファイル  をリストで指定 []:利用しない
author : 執筆者 文字列指定
copyright : 著作権表示 文字列指定
generator : 作成エディタ 文字列指定
keywords : 検索エンジン用キーワードをリストで指定
description : 検索エンジン用文献説明をリストで指定
h : 'Content-type: text/html
'の付加:h=1
body : bodyつまり文書本体全体の属性定義をリスト付与

html_header0()
廃止予定 header()を利用下さい。

is_changed(params={'': ''}, id='', prefix='p_')
前値と現在値を比較し

list2tbl(datas, title='', col_names=[], sql='', border=1, col_default=u'\u7b2c%d\u5217',
style='border-collapse:collapse; font-size:8pt;', debug=0)
方形リストを表で出力
DBテーブルデータの様な縦横揃った
2次元配列データ相当のlistまたは
tuple型のデータをHTMLのTABLEタグ
による表として出力
datas : listまたはtuple型による2次元データを指定
title : 表示する表の表題名
col_names: 表の列名をリストで指定
sql : 指定することで、col_namesが空文字の時、クエリ文内の列名を代用可
: 埋込んだquery2fld_names()で利用処理される。
border : 表枠線の太さ(非表示=0)
style : 表全体に対するスタイルシートを文字列で指定

listing(str_list, title='', h='h4', tags='ol', tree=1, nothing='&#182;', nest=0)
文字列リストのリスト出力用関数
str_list : 基本的に文字列を要素とするリストを指定。
: 但し、リスト要素には文字列を要素とするリストを入れ子で持つ
: ことも出来る。
tags : 基本的に文字列でリスト表記用のタグ名(ol, ul, div, pre)を指定
: 但し、入れ子階層別でタグを切替えたい場合にはリストで指定可

now()
dsp_date_form()のlocaltimeの既定値として
現在の日時を指定する為定義

param_lst(omit_params=[], params_dic={})
## 引数文字列リスト生成 ##
引継ぎたい既存の引数とその値を「引数名=値」の形の文字列リストを返す
get形式の引数文字列は、「'?'+'&'.join()」で生成出来る
(別途追加する引数は、引数名と値共にurl内に表記可能な文字列化
[urllib.quote()]、利用時は逆のunquote()が必要な点を留意のこと)
尚、formを使う場合にはこれでなくhiddenで個別に記述し渡すことも可能。
params_dic : 存在する全ての引数と値の辞書
omit_params : 引継がない除外引数名をリストで渡す
例えばpassword等の渡し方には向かないのでその様なものと
値を変更する引数を入れる

pipe(url, Type='xml')
指定URLを読みそのまま返します。ドメイン外部のXMLコンテンツをjavascript活用する
中継プログラムget形式の引数の引継にはcgi_parmasまたはparam_list()等を使って下さい。
example code::
from mire.htm import cgi_params, html_header, html_footer, pipe
params=cgi_params()
if 'prefCd' in params:
url = 'http://ws.bzen.net/pgtop/GetCity.php?prefCd=%s'% (params['prefCd'])
pipe(url)
else:
html_header()
print 'パラメータに prefCd を含めてください。'
html_footer()

remote_ip(default='')
リモートブラウザ側のIP取得

Webサーバの返すブラウズ側端末のIPは、利用Proxy次第で
正しいIPを返さないことがある。従って、この関数は見直し
予定のものである。次のURLには詳しく、その調査内容が掲載
されている。
http://www.nurs.or.jp/~sug/homep/proxy/proxy7.htm

remote_user(default='')
リモートユーザーIDの取得

stat_ssl()
SSL通信状態 SSL on:1 以外:0
※ ユーザ認証や登録フォーム等ではこれで確認上、表示処理させるべきだろう。

str2unicode(s, charset='UTF-8',
charsets=['cp932', 'Shift_JIS', 'EUC-JP', 'iso-2022-jp', 'UTF-8'], debug=0)
文字列のunicode化を試みます
s : 変換対象文字列を指定
charset : 優先する文字コードセットを1つ指定
charsets: 指定したcharsetで処理不能時に試す文字コード候補をリストで指定

※ 一致する文字コードがない場合は変換せず、そのまま返す様にした。例えば、
Shift_JIS系では。拡張文字等の差のある実装が複数存在する。関連する文字
コードは可能な限り登録しておく必要がある。Shift_JISとして作られた文書
でも、OSで入力可能な文字は混ざってしまうので仕方がない。

str_input_form(id='', params={'': ''}, type='text', align='left', size=12, default=None,
alt=None, style='', changed_color='#FFFFA0', prefix='p_', hidden=0)
テキスト入力フォームの文字列を返す関数
1. IDと引数情報の辞書を与えることで、値を伴なうものはその値を表示する。
2. 文字揃えの指定 align:( left center right etc...)
3. 文字数の指定 size: 整数指定
4. 入力文字列値をhiddenで保持。これにより、変更の有無のチェックを可能にしている。
5. 初期値 default:( None=常に値なし, それ以外は引数として保持している値)
6. Tips表示 alt:(None=常になし, ''=値を表示, それ以外=altの内容を常に表示)

str_list2unicode(lst, charset='UTF-8',
charsets=['cp932', 'Shift_JIS', 'EUC-JP', 'iso-2022-jp', 'UTF-8'],
debug=0)
文字列またはリストのunicode化を試みます
lst : 変換対象文字列またはリストを指定
charset : 優先する文字コードセットを1つ指定
charsets: 指定したcharsetで処理不能時に試す文字コード候補をリストで指定

基本処理はstr2unicode()関数に依存。リスト型時の再帰処理を作込んだもの。
※ 現時点では、シーケンス系以外の要素はそのまま返す仕様。

str_select_form(id, params={'': ''}, options={'': '', 'param': 'view'}, default=None,
alt=None, style='', prefix='p_', changed_color='#FFFFA0', hidden=0)
選択ボックスを表示
前値(既定:'p_'+id)をhidden=1として持たせる場合には、変更があれば背景色を
指定色(changed_color='#FFFFA0'薄黄色)に変更
変更分には「[コード]現在値 <- [コード]前値」のTip表示付加。
それ以外は現在地のみ。

tag_encode(lines=[])
タグ文字のままHTML表示可能な文字列listに変換
'&' ⇒ '&amp;', '<' ⇒ '&lt;', '>' ⇒ '&gt;'

tbl2lists(url, fit_type=1, col_types=[], tr_pat=2, charset='UTF8',
charsets=['cp932', 'Shift_JIS', 'EUC-JP', 'iso-2022-jp', 'UTF-8'],
as_unicode=1, debug=0)
Web上のTABLEタグ表記の2D表からセル値を抽出リスト群化し返す関数
注意: TABLEタグが単一/複数の何れでもそのリストのリストを返す
url : TABLEタグによる表が存するHTML文のURLを指定
fit_type : 0:しない それ以外なら、型変換を試みる
col_types : 列別の変換関数をリストで指定
課題: list2tbl()とは逆方向の処理だが可逆処理を実現出来ている訳ではない
文字コートの為か希に正規表現による処理がうまく機能しない
COLSPAN,ROWSPANを無視した処理の為、データ列がずれる仕様である

td_tags(text, charset='utf-8',
charsets=['cp932', 'Shift_JIS', 'EUC-JP', 'iso-2022-jp', 'UTF-8'],
as_unicode=1, debug=0)
<td></td>で分割する関数
(import_table()で活用)
※ colspan, rowspanには未対応

url2str(url, trim=0, debug=0)
urlの示す文書を文字列で返します
タグ判断を一括で行なう時に有用
行頭、行末の半角空白文字と改行
を取除きたいときはtrim=1にする

url2str_list(url)
urlの示す文書を文字列リストで返します

DATA
__author__ = 'Mire in Japan'
__copyright__ = 'Copyright (c) 2009-2010 Mire'
__license__ = 'GPL'
__url__ = 'http://pythonlife.seesaa.net/article/150777224.html'
__version__ = '0.0.6'
history = 'add 2010-03-23 header(),pipe().\nadd 2010-04-30 s...tags, f...
waiting = 0

VERSION
0.0.6

AUTHOR
Mire in Japan

(END)
続きを読む
タグ:Python CGI
posted by Mire at 14:38 | Comment(0) | TrackBack(0) | Pythonプログラミング | このブログの読者になる | 更新情報をチェックする

2010年05月16日

【PythonでCGI】list2tbl():2次元listをTABLEタグで作表

 前回の「【Python でCGI】listを一覧表示(データ型変換と段落)」では、自由度の高いリスト構造文書一覧表示する機能を関数化した。
 今回はハードルを下げ、縦横整った2Dのリスト構造、同じ要素数を持つリストを複数持つリストをHTMLのTABLEタグを使った表形式で表示することにある。データ構造が単純化された分、データの収まりを考える必要は無くなった分、楽と言えば楽なのだが、RDBMSへのクエリ結果は、この手のデータ構造として処理することになるし、OpenOfficeOrgのCalcやMS- Excel上で作っているものは、基本的にはほぼ、この手のデータ構造となるので、利用頻度はより高いかと思う。

 今回はまだ試作段階であり、充分な作り込みが出来ている訳ではないので、ひな形として考えて、適宜補正して使うと良いだろう。前述の通り、今回は、RDBMSの一つであるFirebirdのシステムテーブル解析時のクエリ情報の出力を題材として取組んだもので、実際には前稿と同じファイル上に同居させているので、全てを掲載すると判り辛いので、今回はそこから関連分のみ抜粋掲載する。

  1. tbl_ddls(): CREATE TABLEで記述した定義情報出力を目指す関数
    • 引数: tables=[], conn=None, cur=None, dsn=None, ip='127.0.0.1', mode=0, db_file='', user='SYSDBA', password='masterkey', charset='UTF', debug=0)
    • データ題材として利用。オープンソースRDBMSであるFirebirdのTable定義情報の出力を目指した関数。未完成だが、これだけでも利用方法が概ね判るものと思う。尚、今回はあくまでデータ題材にすぎないので説明しない。テーブルの外部接合に課題がある為、関係がとれないものや規定値等、取得箇所と方法が判っていないものを含んでいる。また、TABLE作成定義に必要なPRIMARY KEY情報は前稿題材の関数で簡単に抽出出来るがまだ組込んでいない。
  2. list2tbl(): 2D方形リストを表で出力
    • datas, title='', col_names=[], sql='', border=1, style='border-collapse:collapse;', debug=0)
    • この関数が本稿の主要関数である。数字とNULL値は右寄せ、その他は左寄せに、そして、列名は、与えたSQL文より抽出出来る。列名は別名として二重引用符で囲めば日本語表示も可能(SQL文としても有効)としたが、その大半は、後述の関数に木の上分離している。
  3. query2fld_names(): クエリ文から抽出列名リストを生成
    • sqlにしたいしたクエリ文から列名情報を抽出。別名指定があればそちらを優先する他、アンダースコア「_」があればそれを半角スペースに置換する様に見栄えを改善出来る。しかし、二重引用符の中に直接空白文字を置くことは出来ず、代わりにアンダースコア「_」を使う必要がある。
  4. fprn_data(): データ型別に書式付印刷
    • 引数: d, tag='td', debug=0
    • 引数dのデータ型を調べ、それに最適な標準的な表示形式で表示することを目指して、順次追加修正して行く予定だ。
 以下が、実行結果だが、デバッグモードで表示されるSQL文については、ソース内でほぼ推察出来るだろうから省いている。Blog掲載の為、 今回はソースコード上で、font-size:6pt; としている。
Table Test - Mozilla Firefox

                    http://192.168.0.220/pyFBadmin/sys_tbl.py
FirebirdのシステムTableからTable定義情報を抽出
テーブル名 DB内列名 列名 型名 文字数 列長 コードID コード バイト/字 既定VAL 既定SRC 内部制約名 制約タイプ
TEST RDB$48 A 37 VARYING 5 5 2 ASCII 1 <NULL> <NULL> INTEG_48 NOT NULL
TEST RDB$49 B 37 VARYING 20 80 4 UTF8 4 <NULL> <NULL> INTEG_49 NOT NULL
TEST RDB$50 C 8 LONG <NULL> 4 <NULL> <NULL> <NULL> <NULL> <NULL> INTEG_50 NOT NULL
TEST RDB$51 D 37 VARYING 1 1 2 ASCII 1 <NULL> <NULL> INTEG_51 NOT NULL
ZIPCODE_JP RDB$32 LPO_CODE 37 VARYING 5 5 2 ASCII 1 <NULL> <NULL> INTEG_32 NOT NULL
ZIPCODE_JP RDB$33 OLD_ZIP 37 VARYING 5 5 2 ASCII 1 <NULL> <NULL> INTEG_33 NOT NULL
ZIPCODE_JP RDB$34 ZIP_CODE 37 VARYING 7 7 2 ASCII 1 <NULL> <NULL> INTEG_34 NOT NULL
ZIPCODE_JP RDB$35 PREF_KANA 37 VARYING 24 96 4 UTF8 4 <NULL> <NULL> INTEG_35 NOT NULL
ZIPCODE_JP RDB$36 CITY_KANA 37 VARYING 66 264 4 UTF8 4 <NULL> <NULL> INTEG_36 NOT NULL
ZIPCODE_JP RDB$37 TOWN_KANA 37 VARYING 76 304 4 UTF8 4 <NULL> <NULL> INTEG_37 NOT NULL
ZIPCODE_JP RDB$38 PREF_NAME 37 VARYING 12 48 4 UTF8 4 <NULL> <NULL> INTEG_38 NOT NULL
ZIPCODE_JP RDB$39 CITY_NAME 37 VARYING 30 120 4 UTF8 4 <NULL> <NULL> INTEG_39 NOT NULL
ZIPCODE_JP RDB$40 TOWN_NAME 37 VARYING 111 444 4 UTF8 4 <NULL> <NULL> INTEG_40 NOT NULL
ZIPCODE_JP RDB$41 MULTI_ZIP 37 VARYING 1 1 2 ASCII 1 <NULL> <NULL> INTEG_41 NOT NULL
ZIPCODE_JP RDB$42 PER_KOAZA 37 VARYING 1 1 2 ASCII 1 <NULL> <NULL> INTEG_42 NOT NULL
ZIPCODE_JP RDB$43 HAS_CHOME 37 VARYING 1 1 2 ASCII 1 <NULL> <NULL> INTEG_43 NOT NULL
ZIPCODE_JP RDB$44 MULTI_AREA 37 VARYING 1 1 2 ASCII 1 <NULL> <NULL> INTEG_44 NOT NULL
ZIPCODE_JP RDB$45 CHANGE_FLAG 37 VARYING 1 1 2 ASCII 1 <NULL> <NULL> INTEG_45 NOT NULL
ZIPCODE_JP RDB$46 CHANGE_CODE 37 VARYING 1 1 2 ASCII 1 <NULL> <NULL> INTEG_46 NOT NULL
ZIPCODE_JP RDB$47 OLD_FLAG 8 LONG <NULL> 4 <NULL> <NULL> <NULL> <NULL> <NULL> INTEG_47 NOT NULL
 それでは、以下に、ソースコードを掲載しておく。 続きを読む
タグ:Python CGI
posted by Mire at 14:27 | Comment(0) | TrackBack(0) | Pythonプログラミング | このブログの読者になる | 更新情報をチェックする

2010年05月14日

HTML4BLOG.py blog本文内HTMLタグ表示用ENCODE (2) Web版

 前稿で引用した、HTMLタグをそのままHTML文章上で表示させる為の変換ツールの記事としてリンクを揚げてしまったが、掲載時の趣旨はこのチョイ書きツールに主眼はなく、Error処理がお題だった様でなんのこっちゃという引用になってしまっていた様だ。確かに手ごろな長さのソースなので只のサンプルの位置付けの掲載だったということかと思う。  しかし、この変換ツールについては度々触れていた様な気もするし、個人的にはとても重宝している。そして、既に改訂も進んでいた様なので、 改めて、ここに、現行版のソースコードを掲載しておくことにする。ただ、何分100行にも満たないチョイ書きツールなので、結構デザイン的にはいい加減であるし、初回掲載の趣旨通り、debugモードのままだ。その点ご容赦願いたい。尚、このツールには、
  1. 当方のmire.htmモジュールの導入
  2. Webサーバの導入と.pyでのcgi実行を可能な様設定が、許可する拡張子への変更
  3. 本ソースコードファイルのCGIフォルダへの投入とグループ所有権それに実行権の設定
等を適切に行なう必要がある。それが済めば、何時でもブラウザ上のGUIとして利用出来るのでコマンドフィルタ版より遥かに使い易い。  現時点での画面は次の図の通り。
HTML CODE_TEXT for BLOG 0.0.2 (C) 2009 Mire GPL license - Mozilla Firefox

                    http://127.0.0.1:18296/cgi-bin/html4blog2.py
HTMLのタグをそのまま表示するものに変換します。
 このテキストエリアのフォーム内にHTMLタグを含むソースコードをいれ「実行」ボタンをクリックすると、別タブまたは別窓の、同じテキストエリアのフォーム内に、変換されたテキストが収められることになる。これをコピー&貼付けで活用する仕組みだ。尚、貼り付ける返還後のソースそのものを表示したい時には、もう一度実行すれば、欲する変換が出来る。  このツールの変換作業は、文字列メソッドのreplace()を使い「%lt;文字列オブジェクト>.replace('&','&amp;').replace('<','&lt;').replace('>','&gt;').replace('&','&amp;')」で、処理しているだけの代物でPythonらしい超楽ちんな思いをしている。この逆の変換も同様に作れるだろう。続きを読む
タグ:Python
posted by Mire at 22:29 | Comment(0) | TrackBack(0) | Pythonプログラミング | このブログの読者になる | 更新情報をチェックする

2010年05月10日

【PythonでCGI】listを一覧表示(データ型変換と段落)

 PythonにはlistやTupleといった、入れ子構造で自由にデータを保持出来るデータ型を持っている。プログラムをしていると、リスト構造のデータをその時に必要とされる書式で出力べく毎回、似た様なコードを書いているのが現実ではないだろうか。当方のmire.htmやmire.std内のdisplay_err()の例外発生時のローカル変数一覧表示や「【Firebird】System Tables解析例 INDEX」でSQL文を箇条書きにしているlisting()関数等、飽きもせず毎回書いている。

 そこで今回のお題は、個人的にリスト出力の汎用的な関数を作上げ、後々楽することにある。
 さて、仕様たが、今回は、単純なテキスト出力では箇条書きや段落構造を表しずらい面があるのでとりあえずは、HTMLタグを使ったWeb上での段落箇条書き表記に対応させると同時に、そのレイアウト上問題とならない範囲で、より一般的なデータ書式で表示出来る様にしようと思う。

 この様な事を実現する為に必要な技術的要素を先ずは整理して試よう。
  1. リストの入れ子構造に対応する為、関数自身を内在させる再帰処理、
  2. そして、それを実行する条件としてリスト要素がリストか否かを判定したり、要素の型に合わせて適切な出力書式とする為のデータ型の判定を行なうこと、
  3. それに以前触れたUnicode利用時の文字幅「unicodedata.east_asian_width()」の活用

 今回は、以上3点がポイントになる。再帰処理と型判定は次の様な感じで、引数sがリストならリスト処理をする為、リスト処理を定義している自分自身を呼出すことで、型判定は、基本的な型はtypesに定義されたものをimportし利用、ないものは、例えば時間型なら、localtime(time.time())をtype()に入れたものと、比較すればよい。
          再帰処理と型判定の簡易な例
 
 
def ireko(s):
    from type import ListType
    if type(s) is ListType:
        ireko(s)
    else:
        print s
    
 残るUnicode利用時の文字幅は「【UTF8文字幅揃え】unicodedata.east_asian_width()」でも話題にしたが、unicodedata.east_asian_width()で1文字ずつ判定しその文字幅区分を取得、その区分毎の文字幅を適用すれば良い。
    Na: Narrow    1 半角英数
    H : Halfwidth 1 半角カナ
    W : Wide      2 全角文字
    F : Fullwidth 2 全角英数
    N : Neutral   1 アラビア文字
    A : Ambiguous 2 ギリシア文字、キリル文字
 尚、今回は、Pythonのデータ型を文字列化することを課題としたので、整数はLong以上の長整数が扱える等、他の処理系との互換がとれないものもある。Pythonでは、標準で12345678901234567890123456789という整数も有効だからだ。データベースへのデータ格納では注意しよう。  さらに、今回の技術の応用としてはクエリ文で得られるタプルデータでも同様にtype()で比較し、適切な表形式に出力したり、それを入力フォームの値として表示させ、データ更新用のツールを作ることも可能になったりする。

 また、今回は、データ構造のリストを文書構造の段落と箇条書きへの置換をした訳だが、構造自体は当然、リストの方が自由度が高い分、文書構造上では表せないものも出て来る。今回は、その辺りを段落記号文字の表示オプションで配慮している。この辺りは、用途や文書としての必要性で書式が変わることなので、人により仕様に差が出るところだろう。

 さて、今回のコーディングは成行き上「【Firebird】System Tables解析例 INDEX」のソースを修正する形で書いたので、書きかけの別の関数等、多少余計なものを含むこと、ご容赦頂きたい。

 先ずはテストコードの出力は以下の通り、リストは題材として、この関数のマニュアル的なリストを手打ちのものである。まあ、はっきり言って、リスト構造で、文書を書くのは大変だ。emacsで作業したので、リストの入れ子構造は、上手く改行すれば、入れ子状態を段付けしてくれるので若干判りやすいが、カンマ一つなくても怒られるし、括弧を閉じ忘れるてドジる等と大変だった。
Mire's Web - Mozilla Firefox

                    http://192.168.0.220/fb_test/sys_tbl.py
<上略>

listing()の使い方

機能

  1. python内のLISTまたはTUPLEを引数とし、その入れ子構造通りにリスティング。
  2. リスト表記時のタグとして、その階層別で次のものを指定することが出来る。
    • ul : 箇条書き
    • ol : 連番付の箇条書き
    • h1〜h8 : 表題
    • div : ブロック定義
  3. そのタグには、classやstyle等の修飾要素の指定も可能
  4. 連続したリスト要素間の表現は悩みどころ
    • 文字列要素の後に複数リストを連続して伴なう場合
    • そのリストどうしの関係をどう扱うかは用途次第。
    • ここでは段落記号の表示から控えめなdivタグによる行間開けで対応して試た。
    • 下は3ブロックに分かれているのが判るかと思う。
      • 第一ブロックの1
      • 第一ブロックの2
      • 第二ブロックの1
      • 第二ブロックの2
      • 第三ブロックの1
      • 第三ブロックの2
    • 気のせいではない。微妙に行間が空いている。
  5. 文字列以外の型への対応
    • 整数型
      • 1234567890123456789
    • 浮動小数点: 精度はプラットフォーム依存。通常14〜16桁程度で利用
      • 123.4567890123456664
    • time型: localtime(time.time())の吐くオブジェクト time.struct_time
      • 2010-05-10 23:40:12
    • datetime型: 精度はプラットフォーム依存。通常0.001秒単位程度迄で利用
      • 2010-05-10 23:40:12.492826
    • 辞書型: 辞書要素に日本語のリストが含まれても中身がコード表記に(現時点)
    • また、キーにタグ文字を含む様なときは表示幅が上手く取得出来ない(現時点)
      • 
        2.7180000000000000       : 自然対数
        3.1400000000000001       : 円周率
        293                      : 絶対零度
        Pythonスタートブック     : [u'\u8fbb\u3000\u771f\u543e', 2604]
        呟き(Unicode vs Bitecode): [u'\u545f\u304d(Unicode vs Bitecode)', '\xe5\x91\x9f\xe3\x81\x8d(Unicode vs Bitecode)']

引数

  1. str_list
    • 原則文字列のリストを指定
  2. title
    • リスト全体のタイトルを文字列で指定。
  3. h
    • タイトルの表示タグを文字列で指定
  4. tags
    • 各階層別のタグ名を指定(ol, ul, div, pre)。
    • 半角空白文字を挟み、style等の要素を指定可能
  5. nothing
    • 同列のリストが続くときに表示する文字を指定。規定値:¶
    • 文字を表示させたくない時は''(空文字)を指定
    • リスト要素が隣接していても、その系統分けをしたくない時は'nothing'
  6. nest
    • 入れ子階層位置を保持するシステム上の引数
 冗長で申し訳ないが、ソースコードについては余計なものも含んでいる。前半は全て無視して頂きたい。続きを読む
タグ:Python CGI
posted by Mire at 16:11 | Comment(0) | TrackBack(0) | Pythonプログラミング | このブログの読者になる | 更新情報をチェックする

2010年05月08日

【PythonでCGI】Colotrol Letters文字画像の動的生成

 今日のお題は、以前「【PythonでCGI】ユーザ自身によるユーザ登録画面を考えて試た」の中の1項目「Control Letters」についてのものである。

 このブログのコメント投稿にも付いているが、プログラムで自動化して不正な迷惑登録を抑制する目的で 画像の文字を表示し、それを目で見て入力頂いた場合にのみ先に進める様にするものを「Colotrol Letters」等というが、これは機械スパム投稿が横行する様な今日にあっては、公開Webの管理では必須のアイテムだろう。

 これを実現するには動的に文字列画像を生成する仕組みが必須である。今回はその部分の試作関数群を提示させて頂く。ソースにも書いてあるが、このソースでは画像の文字は、単純に乱数で生成し渡すだけにしているが、これだけではユーザの入力との照合が出来ない。ハッシュ値を渡すか、ワンタイムパスワード的な仕組みで、画像の文字列と照合出来る仕組みとしなければ実際には使えないし、逆に画像の文字自体をページに埋め込んでしまっては唯の張子の虎になり意味がないので、その辺りは、それぞれ、それなりにコード作成して頂く必要がある。

 今回のこのコードをWeb上に登録し実行すると、画像ファイルの様に画面表示されるので、利用は、IMGタグのsrcに指定して使うことになる。

 いつもの様にソースコードの説明は面倒なのでhelp()の出力を掲載して、説明に代えさせて頂く。興味のある方は、Python Imaging Library (PIL)をインストール頂ければ動くので、末尾のソースを試して頂きたい。



[root@l22dm BooKeePy]# python
Python 2.6.5 (r265:79063, Apr 18 2010, 20:30:20)
[GCC 4.1.2 20080704 (Red Hat 4.1.2-46)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import control_letters
>>> help(control_letters)
Help on module control_letters:

NAME
control_letters

FILE
/var/www/html/BooKeePy/control_letters.py

DESCRIPTION
##########################################
# プログラムによる自動的不正操作抑制用の #
# Control Letters 画像オブジェクトの生成 #
##########################################
# <img src="control_letters.py">で表示し
# 利用する。但し、単純に乱数生成している
# 文字列は入力照合の対象なので、この様に
# 単純に指定しては役に立たないだろう。
#  例えば、認証画面のサーバサイドのCGIで
# 乱数生成し、個別ページ管理用のDBに有効
# 期限付で保存すると共にハッシュ値を生成
# それを画面側にhidden値として添付。
# そのhidden値を元にDB上の文字列と入力
# 文字列を照合するといった工夫が必要。
# Don't forget chmod 755 〜.py!!

FUNCTIONS
color_tuple(s, default=[128, 128, 128], colors={'green': [0, 256, 0], 'red': [256, 0, 0], 'yellow': [256, 256, 0]})
文字列による色表記をRGB各256階調の
タプルに変換する関数

ctrl_letters(n, font_size=32, font_color='#000000', bgcolor='#FFFFFF', text=u'0123456789ABCDEFGHiJKLMNoPQRSTUVWXYZ', font_path='/usr/share/fonts/ja/TrueType/kochi-gothic-subst.ttf')
指定文字数のControl Letters画像オブジェクトを返す関数
Parameters::
n : 文字数
text : 表示候補文字の文字列
font_size : 文字サイズ
font_path : Truue Type フォントを指定

rand_texts(n, text=u'?!#$%&=-*/<>0123456789ABCDEFGHiJKLMNoPQRSTUVWXYZ')
ランダムに指定文字数の文字列を返す関数
Parameters::
n : 文字数
text : 表示候補文字の文字列

text_img(s='ABC', font_size=24, font_color='#000000', bgcolor='#FFFFFF', font_path='/usr/share/fonts/ja/TrueType/kochi-gothic-subst.ttf')
指定文字列のイメージオブジェクトを返す関数
Parameters::
s : 文字列
font_size : 文字サイズ
font_path : Truue Type フォントを指定

更新履歴::
2010-05-07 初版 mire.imgへ移動予定

DATA
__author__ = 'Mire in Japan'
__copyright__ = 'Copyright (c) 2010 Mire'
__license__ = 'GPL'
__url__ = 'http://pythonlife.seesaa.net/article/149187393.html'
__version__ = '0.0.1'

VERSION
0.0.1

AUTHOR
Mire in Japan

(END)
詳細は、下記ソースコードをご覧下さい。続きを読む
タグ:Python CGI
posted by Mire at 04:57 | Comment(0) | TrackBack(0) | Pythonプログラミング | このブログの読者になる | 更新情報をチェックする

2010年05月06日

【初心者向け】「Python スタートブック」を本屋で読んだ

 買った訳ではないのだが、久方ぶりに本屋でいくつかPython本を手にとって試た結果、以下の本が気に入ったので、取上げておく。手に取った動機は、未だにオブジェクト指向のクラスより関数でシステム作りをしていることに日頃から負目を感じているので、「オブジェクト指向の考え方もしっかり理解できる」とか表紙に書いてあったので気になってしまったからだ。

 初めに言っておくが、Pythonは、オブジェクト指向の言語であるが、決して、それを押しつける言語ではない。標準のデータ型自体オブジェクトと言えばそうなのだが、その伝統的な変数と他人の提供するクラスの利用だけでも、そんなに困ることはない。使う方の理解の範囲で、それなりに満足行くレベル程度には便利に使える言語なので、当方の様に殆どクラスを書かない野郎でもそれはにそれなりに長く使っていけるので、オブジェクト指向を無理して迄理解する必要はない。

 この書籍は、未だPythonに触れたことのないまたは、始めたばかりの人向けかなと思う。内容も非常に読み易く、一部飛ばし読みではあったものの、ほぼ読ませて頂いた。

 何分初心者本なので、細かく丁寧に書いてある。また、ほとんどの初心者本が、インストールで始まり、注意をひかされのに対し、それは、そこそこの説明とし、巻末で、割愛気味に記述する等、編集の面でも筆者の思想がよく出ている。実際に必要となる書籍は、個人の立ち位置で大きく変わるので、この本が全ての初心者に良いかどうかは判らないが、結構読み易かったので、始めたばかりの方は手に取って試るてはいかがか思う。





 尚、当方のプログラム言語との馴れ初めは、古典的なBASIC言語に始まり、C言語を3カ月程度で使うようになり、LinuxでDBを使う様になり、その関係でPythonがメイン言語となったクチだ。その経験から言うと、新しくプログラム言語を使える様になるには、それでやりたいことに対し、一番多くの情報のあるものを手に入れるに限ると思う。
 やりたいと思うことに近いソースコードを手打ちで入れ試してみることで、指が制御文や関数、そして言語の書式を覚えるし、覚えるには、沢山の不愉快なエラーメッセージを受け取ることが出来るので、それを調べるとこも結構勉強になる。別に原爆開発当たりをしている訳ではないので間違ったところで爆発することもないし、初心者の操作ミス程度でPCを壊すことがとても難しいことが体感出来ることも大きい。後々、Try and Errorで気軽に取組める様になるからだ。また、慣れが一定以上になり、エラーを出さなくり、目的の動作を手に入れると、その手に入れた基盤の上で、追加していく機能に挑戦して行けば、例え場当たり的な取組みであっても、目標とするものに近いものは作って行けるものである。
 まあ、そんな気持ちで、最初の本は選んで頂ければよいし、その上で、自身が必要と思えば、このブログで挙げる様な書籍で体系立てて学習すると良いかと思う。ただ、初歩的な理解が済めば、後は、ライブラリ・リファレンスで必要となる関数を調べ利用することが増えて来る。書籍でもネット上でも良いので、ことある毎に読まれることをお勧めする。
 さらに、システム作りの発想を学ぶ意味では、その事例集として、CookBook等を眺めて試ると良いかと思う。今では、Google等で検索すれば先人が結構ネット上のコンテンツに書いてくれているものなので、そちらを、見ることもお勧めする。

 Pythonであれば、学ぶ動機が続く限り、確実に習得出来るでしょう。
タグ:Python本
posted by Mire at 14:54 | Comment(0) | TrackBack(0) | Pythonプログラミング | このブログの読者になる | 更新情報をチェックする

2010年05月05日

【Unicode】PythonでのUnicode下での半角カナと全角かな変換

 まあ、半角カナの濁点半濁点の用法に気を付けさえすれば簡単に作れるので探せば結構たくさんモジュールが見つかるテーマだが、Shift_JISやEUC-JPとのお付合いが長かったので、今のUnocode下での処理を確かめる意味で自己流で、半角カナを全角のカタカナやひらがなに変換する関数を書いて試た。

 作り方は、ここで挙げた辞書などの対応表を参照して、変換対象文字列より一文字ずつ参照し置換して行く方法以外に、replace()メソッドで全半角文字の字種数回分、変換対象文字を舐めまわし変換する方法もあるが、変換対象文字数×字種数の処理となるので非効率なので採用しなかった。勿論、辞書参照でも、変換対象文字数回分、辞書参照を行なうという回数面では変わらない単に、辞書という標準の仕組みの処理の速さに期待してのことだ。文字コード体系自体に着目して数式で処理する法が多分早いだろうが、当方が想定する変換用途としては、例えばカナ名のユーザ入力で半角カナがあったときに全角に変換してしまうとか、半角カナを含むものとそうでないものとの比較に使ったり、以前テスト用で使った郵便番号簿データの様に半角カナでの情報提供しかないものを変換または比較する程度なので、この辞書参照方式で充分かと思う。

 この辞書参照方式の場合、半角の「゛」・「゜」といった濁点・半濁点は、その直前の文字付けて処理の判断をしなければならない。今回はその細かいところを簡単に処理する為に、予め文字列を反転させ、お尻の文字から順次処理、濁点れ半濁点に当たったら、次の文字次第で処理する方法を使ったが、文字列のreverse()メソッドの様なものがUnicodeオブジェクトには無い様なので、それもおまけで作ることになった。完全なテストではないが、テスト用のに使ったコードを_test()という関数にして添付しているが、その為に、Unicode昇順並替用sort()関数も付けた。その実行結果は以下の通り。



[root@l22dm ~]# python /usr/local/lib/python2.6/site-packages/mire/jpcode.py

test =
あいうえお、アイウエオ、
がぎくげご、ガギグゲゴ
ぱぴぷぺぼ、パピプペポ
バ゙ビ゙
ヴ


test =
あいうえお、あいうえお、
がぎくげご、がぎぐげご
ぱぴぷぺぼ、ぱぴぷぺぽ
ば゛び゛
う゛


test =
あいうえお、アイウエオ、
がぎくげご、ガギグゲゴ
ぱぴぷぺぼ、パピプペポ
バ゛ビ゛




チナノヘムユルンゾブギポデド「ヲェョゴイカコピビセツニハホメヨレ゙ボゲ。・ゥュアオケペベステヴヌヒマモラロダ゚ザプ、ゼィャジエクシガタバパトネフミヤリヅワズヂ」ァォウグキサソ
ちなのへむゆるんぞぶぎぽでど「をぇょごいかこぴびせつにはほめよれ゛ぼげ。・ぅゅあおおぺべすてう゛ぬひまもらろだ゜ざぷ、ぜぃゃじえくしがたばぱと ねふみやりづわずぢ」ぁぉうぐきさそ
チナノヘムユルンゾブギポデド「ヲェョゴイカコピビセツニハホメヨレ゛ボゲ。・ゥュアオケペベステヴヌヒマモラロダ゜ザプ、ゼィャジエクシガタバパトネ フミヤリヅワズヂ」ァォウグキサソ

、。「」ぁあぃいぅううぇえぉおおかがきぎくぐげこごさざしじすずせぜそぞただちぢつづてでとどなにぬねのはばぱひびぴふぶぷへべぺほぼぽまみむめもゃ やゅゆょよらりるれろわをん゛゛゜・
、。「」゛゜ァアィイゥウェエォオカガキギクグケゲコゴサザシジスズセゼソゾタダチヂツヅテデトドナニヌネノハバパヒビピフブプヘベペホボポマミムメモ ャヤュユョヨラリルレロワヲンヴ・
[root@l22dm ~]#
 ご覧の通り、ひらがなにはカタカナにある「ヴ」がないという規格上いやらしい課題もある。従って、全角間でのひらがなカタカナ変換の関数を作ろうとする場合にはその処理を加味しなければならない。

 今回作った半角カナからの全角かな変換のソースコードは以下の通り。尚、当方では今後のテストの為、自分のmireモジュールにjpcodeを登録した。

2012-01-18追記: 大幅書き直し。【Unicode】PythonでのUnicode下での半角カナと全角かな変換(2)でご覧頂きたい。
2012-01-24追記: ※ ご利用は、「【Unicode】半角カナ全角かな置替関数 for Python(正式公開版)」に掲載している最新版のjpcode.pyを活用頂きたい。続きを読む
タグ:Unicode Python
posted by Mire at 13:57 | Comment(2) | TrackBack(0) | Pythonプログラミング | このブログの読者になる | 更新情報をチェックする

2010年04月30日

【Firebird】郵便番号簿DBの作成(5) mire.htm の str_input_form()

 この稿の末尾に掲載するmire.htmのモジュールは「【PythonでCGI】 html_header()で簡単作成」や「【PythonでClass】気の迷いで^^;; CGI HTML出力系関数をclass化」で取組んでいるhtml周りの標準的な機能を集積モジュール化している先である。

 今回、郵便番号簿DBの作成で作ったDBチェックの為の前稿のスクリプトで流用した「str_input_form()」は残念ながらまだ未完成で、一部引数がまだ有効に機能する様にコーディングしていない。

 恥ずかしながら現状は、次の通り。



## 対話型テキスト入力フォーム ##
def str_input_form(id='',params={'':''},type='text', align='left', size=12,
default=None, alt=None, style='', changed_color='#FFFFA0', prefix='p_', hidden=0):
"""
テキスト入力フォームの文字列を返す関数
1. IDと引数情報の辞書を与えることで、値を伴なうものはその値を表示する。
2. 文字揃えの指定 align:( left center right etc...)
3. 文字数の指定 size: 整数指定
4. 入力文字列値をhiddenで保持。これにより、変更の有無のチェックを可能にしている。
5. 初期値 default:( None=常に値なし, それ以外は引数として保持している値)
6. Tips表示 alt:(None=常になし, ''=値を表示, それ以外=altの内容を常に表示)
"""
# 値変更時に表示スタイルを変更
if is_changed(params=params, id=id, prefix=prefix):
style='background-color:%s; %s' % (changed_color, style)

if id in params: # 引数が存在するとき
if alt==None: title = ''
elif alt=='': title = params[id]
else: title = alt
if default==None: val = ''
else: val = params[id]
s='<input type="%s" name="%s" size="%d" value="%s" alt="%s" title="%s" style="text-align:%s; %s">' % (
type, id, size, val, title, title, align, style)
if not hidden==0:
s=s+'<input type="hidden" name="%s%s" value="%s">' % (prefix, id, params[id])
return s
else: # 引数が存在しないとき
s= '<input type="%s" name="%s" size="%d" value="%s" alt="%s" title="%s" style="text-align:%s; %s">' % (
type, id, size, default, alt, alt, align, style)
# 入力欄の値が存在しなくとも、前値が存在していたなら、その値を0長文字列として保持 無意味とは思う
if prefix+id in params:
s=s+'<input type="hidden" name="%s%s" value="">' % (prefix, id)
return s


 他の関数を含めmire.htmモジュール全コードを以下に掲載しておく。続きを読む
タグ:mire.htm Python
posted by Mire at 06:16 | Comment(0) | TrackBack(0) | Pythonプログラミング | このブログの読者になる | 更新情報をチェックする

2010年04月28日

【Firebird】郵便番号簿DBの作成 ./dl_ZipCodeJp.py

 「【PythonでCGI】ユーザ自身によるユーザ登録画面を考えて試た」で本人性確認の為記録予定の市区町村コード情報の取得には、郵便番号の入力で日本郵便の郵便番号簿データから引くのが最適かと思う。そこで本日のお題としては、そのデータを手元のデータベースに取込むことにする。

 詳細は、後方に掲載するソースコードを試してもらえば判り易いと思うが、開発時点での環境及び制約は次の通りだ。基本的に普通のCGIプログラム形式なので、多少環境が異なっても、僅かな微調整で動作する筈だ。

1. OS : Linux=CentOS5.4 (Windowsへの移植は容易)
2. 言語 : Python2.6 (2.5以降であれば多分そのまま可。)
3. RDBMS : Firebird2.5RC2
4. HTTPD : Apache2.2 (CentOS5.4バンドル分)
5. 解凍 : LHA (郵便番号簿データは.LZH形式。yum等でInstall可)
6. charset: UTF8 (郵便番号簿データはShift_JIS)

 技術的なポイントは概ね次の通りであった。

1. 郵便番号簿データのDL: 月末迄に更新されるので、wgetのミラーリングスクリプトをPyhtonスクリプトから呼出す様にし、それを毎月、月初にcronで実行させる。それと同時に手動での実行も可能にする。
2. 初期設定値は、INIファイル形式の設定ファイルに記述し、それより取得する形とする。
3. exec_sql()等のFirebird用のSQL文やクエリ実行の関数の整備。
4. 利用文字コードは、Shift_JISでも構わないが、UTF-8に統一。コード変換は基本的にPython上で実施。
5. DB取込み時のcommitやConnection開閉は開始と終わりに行う様にし、CPU負荷を過大に増加させない様にする。
6. DB更新時でも運転可能にする為、新規データを挿入後に旧データを削除する形にする。

 ソースコードは以下の通り。尚、当然ながら、DBとその中のテーブルは、その存否を調べて無い時には自動で作成され様になっている。


続きを読む
タグ:FIREBIRD Python
posted by Mire at 07:18 | Comment(0) | TrackBack(0) | Pythonプログラミング | このブログの読者になる | 更新情報をチェックする

2010年04月18日

【備忘】GUIアプリをPythonから操作 with Linux

 前稿では、Windows上のGUIアプリをPythonから操作するツール「pywinauto-0.4.0」を取上げたが、windowsだけではつまらないので、Linuxも調べて試た。確かに、Linuxの場合、概ねGUIツールの前にCUIツールが存在することが多いので、Windowsに比して需要は少ないだろうし、需要の多くは本来のTESTとなることだろう。そんな用途のツール「dogtail-0.7.0」をredhatサイドで提供してくれている様だ

 これ以外にも、「Tka11y 0.1.1」、「LDTP-2.0.6」、「Accerciser」と数多く存在している。

 Linux上のツールでは、Windowsと異なり、ユーザによりそのGUI環境に差があるし、そのインストール方法もソースインストールとする場合には、「〜-devel」として別配布されている開発用ライブラリ、ヘッダファイルのインストールが導入されていないことも多い。

 例えば、Pythonで良く使われるTcl/Tk等は、多くのLinuxディストリビューションで、ライブラリ、ペッダファイル等のソースコード迄は導入されていないことだろうから、導入し使うに当たっては、パッケージ管理ツールでそれらの有無をチェックし必要により導入することになる。

 尚、例に挙げた「Tcl/Tk」の開発用ソースコードが無いとPython自体のソースコードからのインストール自体でも、それに依存するTkinterの利用が出来ない。
 導入時におけるその成否確認は「./configure --prefix=/opt/test」後「make」または「make test」等として「Failed to find the necessary bits to build these modules:」の行の後にリスティングされているモジュール名で可能だ。

因みに、当方では次の様になっていたので、CentOSのパッケージ管理ツール上で、「tk」を検索して「tk-devel-8.4.13」を、依存する「tcl-devel-8.4.13」と共に導入することでPython2.6上でのTkinterの利用が可能になった。
        make 実行時


Failed to find the necessary bits to build these modules:
_tkinter bsddb185 sunaudiodev
To find the necessary bits, look in setup.py in detect_modules() for the module's name.


「tk-devel-8.4.13」、「tcl-devel-8.4.13」導入後

        make test 実行時(より多くのメッセージを含むが途中の情報も拾う必要が)


中略

running build_ext

Failed to find the necessary bits to build these modules:
bsddb185 sunaudiodev
To find the necessary bits, look in setup.py in detect_modules() for the module's name.

running build_scripts


中略

test_zlib
325 tests OK.
7 tests failed:
test_builtin test_calendar test_exceptions test_getargs
test_httpservers test_sqlite test_unicode
34 tests skipped:
test_aepack test_al test_applesingle test_bsddb185 test_bsddb3
test_cd test_cl test_codecmaps_cn test_codecmaps_hk
test_codecmaps_jp test_codecmaps_kr test_codecmaps_tw test_curses
test_gl test_imgfile test_kqueue test_linuxaudiodev test_macos
test_macostools test_normalization test_ossaudiodev test_pep277
test_py3kwarn test_scriptpackages test_smtpnet test_socketserver
test_startfile test_sunaudiodev test_timeout test_urllib2net
test_urllibnet test_winreg test_winsound test_zipfile64
Those skips are all expected on linux2.
make: [test] エラー 1 (無視されました)
./python -E -tt ./Lib/test/regrtest.py -l


中略

test_zipfile64 skipped -- test requires loads of disk-space bytes and a long time to run
test_zipimport
test_zipimport_support
test_zlib
325 tests OK.
7 tests failed:
test_builtin test_calendar test_exceptions test_getargs
test_httpservers test_sqlite test_unicode
34 tests skipped:
test_aepack test_al test_applesingle test_bsddb185 test_bsddb3
test_cd test_cl test_codecmaps_cn test_codecmaps_hk
test_codecmaps_jp test_codecmaps_kr test_codecmaps_tw test_curses
test_gl test_imgfile test_kqueue test_linuxaudiodev test_macos
test_macostools test_normalization test_ossaudiodev test_pep277
test_py3kwarn test_scriptpackages test_smtpnet test_socketserver
test_startfile test_sunaudiodev test_timeout test_urllib2net
test_urllibnet test_winreg test_winsound test_zipfile64
Those skips are all expected on linux2.
make: *** [test] エラー 1
[root@l22dm Python-2.6.5]#

続きを読む
posted by Mire at 20:43 | Comment(0) | TrackBack(0) | Pythonプログラミング | このブログの読者になる | 更新情報をチェックする

2010年04月17日

【備忘】GUIアプリをPythonから操作「pywinauto-0.4.0」

 ちょいと別件から以前見つけていたGUIアプリのテスト用ツールを思い出し、調べて試た。その時の動機は、テストそのものでなく、GUIアプリへの手元のファイルデータをプリントし、それを見ながら手打ち入力したり、GUI上の表示を別のGUIに移す様なUIを介した手仕事という、GUIアプリ最大の欠陥を何とかしたいと思ったからである。

 その時に探していたものは、sekoさんのLights onの記事「pythonからtelnet経由でFirefoxを操作する(2006/10/23)」で紹介されている Get MozLab だったが、今回追って試みると、Firefoxの旧版用で現用の3.6には対応していない様だったので、キーワードを色々と工夫して調べると、Perlだと「Win32::GuiTest」(amachangさんのIT戦記「perl の Win32::GuiTest モジュールを使ってみる。」当たりで内容は確認出来る)に多分相当するもの「guitest」をPythonでも見つけたが、2005-11-26で開発が止まっている様で、諦め気味に調べていると同様のプロジェクトとして首記の「pywinauto-0.4.0」が見つかった。まだ充分にこのツール自体をテストしていないが、備忘の為、ここに記録しておくことにする。

 インストールはPerlのGuiTestと同様にWindowsに限定されるが、
当方は http://sourceforge.net/projects/pywinauto/ よりDL解凍し利用したが、その説明にある様に、他に、ctypes, Sendkeys, PIL, elementtree に依存している様だ。尚、ctypesはPython2.5以降は、Python本体の標準モジュールとして供給される様になったのでほとんどの方にとってインストール不要だろうし、必須なのはキー操作に必要なSendkeysのみであろう。 Sendkeysは圧縮形式の実行ファイルで該当するバージョンのものを選んで使えばよい。それに対し、pywinauto本体はsetup.py形式のインストーラとなっているので、解凍後のpywinautoディレクトリにcdしてから、導入するpythonをフルパスで指定(例: c:\Python26\python.exe )して「setup.py install」する。

 尚、テストは、examplesフォルダ内に添付されてるものを実行して行った結果、Notepad等のテスト対象のGUIアプリの起動の確認迄はできたが、細かい動作は確認していない。今回は備忘の為なのでオプションのものをインストールしていないし、随分長く起動させっ放しのVista上なので今後落ち着いて試してみるつもりだ。
タグ:guitest
posted by Mire at 08:52 | Comment(0) | TrackBack(0) | Pythonプログラミング | このブログの読者になる | 更新情報をチェックする

2009年11月19日

【UTF8文字幅揃え】unicodedata.east_asian_width()

 Python3からは利用文字コードはUTF-8が標準となることから、これ迄Python2上でShift_JISを文字コードとして指定することで出来た文字幅揃えが、全く出来なくなる。
 これはShift_JISの漢字文字コードがたまたま2バイトであることから、文字数(バイト数)=表示幅という相関性があり、それを流用していた為出来ていたことに過ぎない。

 過去、この問題に対し一度Shift_JIS化して処理する方法をとったことがあるが、これでは、Shift_JISに無い変換不能の文字を含んでいる場合に機能しないし、今後もShift_JIS規格に頼る様では何の為の国際化、UNICODE化なのか判らない。

 であるから、これに正面から向き合い対応するには、これからは、UTF-8で記述した文字が全角なのか半角なのか、それともそれ以外なのかを判別し処理する必要がある。それを行なう標準モジュールにある関数がunicodedata.east_asian_width()である。これはPython2.4以降で利用出来る。

 試しにこの関数に「あ1A」「@」「ア」「A1」等を入れて試ると「F」「W」「H」「Na」等の文字列を返してくる。
D:\>python
Python 2.6.2 (r262:71605, Apr 14 2009, 22:40:02) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import unicodedata
>>> unicodedata.east_asian_width(u'あ') #平仮名全角
'W'
>>> unicodedata.east_asian_width(u'ア') #片仮名全角
'W'
>>> unicodedata.east_asian_width(u'ア') #片仮名半角
'H'
>>> unicodedata.east_asian_width(u'1') #全角数字
'F'
>>> unicodedata.east_asian_width(u'1') #半角数字
'Na'
>>> unicodedata.east_asian_width(u'@') #機種依存文字
'A'
>>> unicodedata.east_asian_width(u'') #外字
'A'
>>> unicodedata.east_asian_width(u'𠮷') #UNICODE CJK拡張B(吉の異体字)まだ未対応なのかな??
Traceback (most recent call last):
File "", line 1, in
TypeError: need a single Unicode character as parameter
>>> unicodedata.east_asian_width(u'a')
'Na'
>>> unicodedata.east_asian_width(u'゛')
'W'
>>> unicodedata.east_asian_width(u'゙')
'H'
>>> unicodedata.east_asian_width(u'\u0633') #アラビア文字
'N'
詳細は「東アジアの文字列」を参照頂くと理解し易いが、我々東アジアの文字を含む文字列では、「F,W,A」を全角として「Na,H」を半角として扱うことになる。東アジア文字を全て含まない文字体系の場合には「Na,H,A」を半角つまりアルファベットと同等に扱い処理することになっている様で、それが為に、欧米系のシステムでは結果として出現しない東アジア文字について考慮されない場合がある様だ。

 実をいうとこのunicodedata.east_asian_width()はこのBlogで公開している「dvd_label.py」で使ってはいる。ただ、そのソースコードに書いてある通り、ネットからの拾物で、寸分違わずhush_puppyさんの「RubyとPythonで全角文字を半角文字2文字として数える」から理解することなく拝借させて頂いていただけ。
DVD_Label/dvd_label.py 113-144,646-647行より抜粋(タイトルを左寄揃えし次項目の開始位置を揃えた)


#####################################################################
#ネットからの拾いもの
#########################################################################################
## unicode下でのprint書式で全角半角幅の区別がないことでの桁揃えが出来ない不具合に対処 ##
#「RubyとPythonで全角文字を半角文字2文字として数える」(hush_puppyさん)より勝手に拝借 #
# http://d.hatena.ne.jp/hush_puppy/20090226/1235661269 #
# NICEですね。hush_puppyさんに心より御礼申上げます。 #
def ljust_kana(str, size, pad = " "): #
space = size - width_kana(str) #
if space > 0: #
str += pad * space #
return str #
def width_kana(str): #
all = len(str) # 全文字数 #
zenkaku = count_zen(str) # 全角文字数 #
hankaku = all - zenkaku # 半角文字数 #
return zenkaku * 2 + hankaku #
def count_zen(str): #
from unicodedata import east_asian_width #
n = 0 #
for c in str: #
wide_chars = u"WFA" #
eaw = east_asian_width(c) #
if wide_chars.find(eaw) > -1: #
n += 1 #
return n #
# もうひとつの解としてはUTF8をShift_JISに変換後print書式を通し再度UTF8に変換するとこも #
# 考えられるがUTF8=>Shift_JISがうまくいかないんだな。それに頻繁なコード変換は文字化け #
# 等の不具合発生の機会を増やすのでhush_puppyさんのアプローチが安全。ただ出来るなら #
# print書式そのものを全角対応にしたいのが本音だ。 #
## unicode下でのprint書式で全角半角幅の区別がないことでの桁揃えが出来ない不具合に対処 ##
#########################################################################################
<中略>
for d in datas:
print u"%5d %2d %s %s %8s" % (d[0],d[1],ljust_kana(d[2],64),d[3],d[4])
 

 hush_puppyさんの関数をそのまま使うことで、右寄せだろうと左寄せだろうと半角文字幅単位でのセンタリングも容易に対応出来るが、これをグローバルなものにするには、東アジア文字を含むシステムとして作成する場合とそうでない場合に分けて機能する様にするか、東アジア文字を含むことを前提にしたものをグロパルな規格として欧米中東言語地域の皆さんに認めてもらうしかないのでしょうね。「全角」って概念は漢字文化圏の活字文化生んだ産物で手書きの時代には東アジアでも関係なかったことだろうし、システム作成者の意図で切替え出来る方が良いのかもな。後は濃いお方にお任せ。


2010-05-11追記
関連として、UNICODE文字列の文字幅を返す関数 unicode_width(s, width={'Na':1, 'W':2, 'F':2, 'H':1, 'A':2, 'N':1}) を 「【Python でCGI】listを一覧表示(データ型変換と段落)」で掲載した。unicodedataの意味合いがより判り易いかも知れない。
posted by Mire at 17:46 | Comment(0) | TrackBack(0) | Pythonプログラミング | このブログの読者になる | 更新情報をチェックする

2009年11月10日

【箸休め】srchlines.pyのKey Word検索機能強化のポイント

 「箸休め」と断ったところで、しつこいと嫌われるので、本稿が最終回だ。汎用部分としてはコマンドの引数管理を除きほとんど完了した筈なので、本稿では、検索機能の強化について考えてみたい。

 お題に使った「srchlines.py」自体は、ちょい使いの使い捨てツールとして手掛けたものなりで、@文字列の完全一致分のみ抽出し、A一致キーワードのハイライト表示も行内の先頭のみと言う、いい加減なもので済ませている。(元々の用途が行頭にあるものを探したかったのでそういう仕様となった。)

 でも、@の検索機能については、高機能化したければ、A.大文字小文字の区別なく、B.全角半角の区別なく、C.同義語も検索出来、かつ、D.「*」「?」といったワイルドカードの活用や、複数キーワードの指定が可能になれば、ほぼ全ての検索用途に使えることになる。
 他方、キーワードのハイライト表示も最初に出現したものだけでなく全ての一致分に適用し、尚且つ、色づけ等でそれが出来れば幸せだろう。

 以上の内容について、今は、書く気はないので、そのポイントのみ、記載して置くことにする。箸休めですから^^;;


A.大文字小文字の区別なく検索


 これは、「srchlines.py」の21行目付近にある「find_it=ss.find(s)」で、ssとsについて処理前にupper()またはlower()の何れかで処理したもので実行すれば良いだけで、次の3行で処理すればよい。
cmp_ss = ss.upper()
cmp_s = s.upper()
find_it=cmp_ss.find(cmp_s)


B.全角半角の区別なく


 A.よりは少し難しい。だが、Aの場合と同様に、「srchlines.py」をUTF-8で記述する限り、{全角文字:半角文字}の形の辞書を作り参照させると実現出来る。取敢えず日本人が必要なものとしては、英数字と片仮名分を準備すれば充分だろうし、この変換を汎用関数化して活用すれば良いだけだ。ちょいと荒っぽいが、概ね次の様になるかと思う。

def han2zen(s):
char_dic={'1':'1', 'a':'a', 'ア':'ア', ...}
keywords = char_dic.keys()
for k in keywords:
cmp_s = s.replace(k,char_dic[k])
return s

cmp_ss = han2zen(ss)
cmp_s = han2zen(s)
find_it=cmp_ss.find(cmp_s)

 ここ迄なら、処理時間やPCの能力を気にしなくて良いなら結構簡単に対応出来るし、文字コードの規則性を活用すれば、ある程度の高速化は可能だろうし、後は必要により必要によりCでモジュールを書き直した組込んだり、数あるPythonの高速化ツールを活用すれば困ることはない。


C.同義語も検索


 これは、結構厳しい要求だろう。これが出来れば、Googleなどの検索エンジンが出来あがってしまう。最近、某大手電器メーカーが福岡百道浜で検索エンジンの開発案件として「Pythonで検索エンジン開発」を上げていたが、ここ辺りが、成否の鍵になるのだろう。マッチングのとれた人材が確保出来ていたらもう多分始動しているのだろう。(頑張ってね)
 この辺を作るとしたら、日本語の範囲では、類語辞典外国語は翻訳辞書のの様なデータベースの存在が前提になるだろうし、タイムリーな時事対応を考えるなら、茶筅やメカフ等の形態処理で、新出語彙を日常的に抽出しデータベースを更新して行く必要があるだろう。ここ迄すると活用には、相当の体力が必要なので、世界で数社の利用に絞られるかもしれないが、用途としては専門分野毎の需要迄考えれば作成すること自体無駄ではないのかもしれない。
 無茶苦茶荒っぽく言うと、ソースコードはBと同じなので、これについては、お仕事でしている方にお任せということにしておこう。^^;;


D.「*」「?」といったワイルドカードの活用


 これはglobモジュールでもしていることなので、恐らくlib/glob.pyを参考にして書けば、正規表現で簡単に解決出来るだろう。


E. 複数キーワードの指定


 さて、これは、ひょとしたら、本稿の最大の課題なのかもしれない。
 「srchlines.py」では、最大で2つの引数しか受け付けない様な仕様としていた。しかし、複数のキーワードを指定可能とする為には、sys.argvを生で使うだけでは厳しいだろう。
 複数のキーワードを受け付けるということは、当然、コマンドオプションで、その扱いが、論理和なのか排他なのか等の指定が必要なので、Pythonではoptparseかgetopt辺りを活用することになる。これについては、箸休めとして書く内容でない様にも思うので、もう少し当方も勉強してから、書こうかと思う。待ちきれない方は、次のURLを参考して欲しい。
optparse: http://www.python.jp/doc/2.5/lib/module-optparse.html
getopt : http://www.python.jp/doc/2.5/lib/module-getopt.html


全キーワードにハイライト処理


 「srchlines.py」では、もっとも原始的な文字列のスライスという手法を使って「print u'%6d %s【%s】%s' % (i,ss[:find_it], ss[find_it:find_it+len(s)], ss[find_it+len(s):]),
」の様にして、find()で求めた最初に出現したものを【】で囲んで処理した。ひょっとしたら、返ってPythonが初めての方にはなじみずらいおまじないに見えると思うが、このC言語で言うとポインタ的なスライスの機能が使える為Pythonには部分文字列を処理する標準関数が存在しないくらいだ。「srchlines.py」の場合の様に予めUNICODE化した場合には文字単位でスライス出来る。(そうしない場合にはバイト単位での処理となる。)
 さて、「全キーワードにハイライト処理」をするのは実を言うとPythonの標準の文字列メソッド「replace(sub,new_sub)」で出来てしまう。つまり、「print ss.replace(s,'【%s】' % (s))」とでもすれば良いとのいうことだ。後言うことは、余り差は出ないだろうが処理速度を考えるとnew_subはループに入る前に「new_sub='【%s】' % (s))」とした方が良い程度かと思う。
 尚、今回の処理では、行毎での処理となっている為、普通の日本語の文章の様なものは、行末の改行符号を取除きreplaceを掛けることが必要なことがある。


さらに


 また、今回の検索システムでは、テキスト情報であることが前提のものである。NAMAZUを構築すればそちらで判ることだが、Word、Excel、PDF、等のバイナリ文書、ZIP, LZH,TGZ等の圧縮ファイル、そして、JPEGの撮影情報や本Blogでも取上げたDVD-VRのタイトルデータ等、予めフィルターツールを使いテキスト化して対処する必要があるものが多い。目的によっては、そんな処理を加えると、公開を望まない暗号化文書を除き完璧に検索が可能となる筈だ。こんな小さなプログラムも広げていけば箸休めで収まらなくなってしまう。しないけどね。誰かしてくれるだろうから(笑)。
posted by Mire at 12:01 | Comment(0) | TrackBack(0) | Pythonプログラミング | このブログの読者になる | 更新情報をチェックする

2009年11月09日

【箸休め】関数は概ね後方行定義で可、でも引数渡しは不可

 前稿の「srchlines.py」では、専用関数2つの後に、当方のmire.stdで管理運用している関数3つを抜粋添付することで、何らの文句も言わずに正常に機能したかと思う。

 さて、ここで、一番目に定義しているwalk()関数の汎用化を検討して試よう。「srchlines.py」では引数pathで指定するディレクトリ傘下に対し、直書きしているsrch()関数をーに引数を指定して処理しているので、専用関数となってしまっている訳だ。この部分を汎用化するのは簡単だ。
引数として例えばfuncを設け、それにsrchを代入してしまえば良いだけだ。つまり、次の様に変更する。
def walk(path,s):
<中略>
srch(join(path,d),s)
def walk(path,s,func=srch):
<中略>
func(join(path,d),s)

 関数だってオブジェクトなので、変数名に代入可能なのでこの様な手法で普通の関数利用でも汎用化することが可能な訳だ。

 通常は、この汎用定義のwalk()関数は当方のmire.stdに追加して、importしてしまえば問題なく使える筈だ。

 それで、試しに、前稿のソースコードを変更して実行して試みると判ると思うが、結論は駄目で次の様な標準的なメッセージを受取ることになる。
D:\PythonCGI>python srchlines.py
Traceback (most recent call last):
File "srchlines.py", line 12, in
def walk(path,s,func=srch):
NameError: name 'srch' is not defined

 でも、ここで「未定義と言われちゃ仕方がない」と諦めてはいけない。これは、長くPythonと付き合っていると会話の中で知らず知らずの内に体で判ってくることだが、関数定義をPythonインタプリタがメモリーにオブジェクトとして置いて行くときに、「srchが何者か判らないので作業が出来ません。」と文句を言っているだけで、walkより前の行にsrchを定義して置けば防げる程度のものに過ぎない。
 同様の理由で、インデント無しで始める実行行が関数等のオブジェクト定義の後に書いている次第だ。

 本音で言うと、実行行を頭に書き、そま後に関数定義で書く形式の方が、人にとっては素直に思えるし、関数定義での引数既定値にのみ影響すると言うのは何とかして欲しいと思うものの、これがPythonの仕様なので、従っておくしかない。

 まあ、記載順に決まりがあった方が、同じ目的で同じ関数ほ使って書いた時に、関数定義順もほぼ同じソースコードになり、メンテ上好都合とも言える。また、個別ツールでの記載でも関数内に既定関数を入れるより、その関数の実行箇所で関数を明示指定することの方が一般的であるので、あまり課題視される不具合でないことも確かである。




###############################
# 関数定義区画
def srch(path,s):
<中略>

def walk(path, s, func): # 定義順は原則自由だがそれをオブジェクトとして既定の引数値として
<中略> #利用するなら、先に定義されることが必要。

###############################
# これより実行行
path = <値>
s = <値>
walk(path=path, s=s, func=srch)
続きを読む
posted by Mire at 17:00 | Comment(0) | TrackBack(0) | Pythonプログラミング | このブログの読者になる | 更新情報をチェックする
月額見放題1,000円開始キャンペーンバナー(画像ありver)
紺碧の艦隊 ルパン三世 GREAT CHASE クリックプロモーション
<< 2013年01月 >>
    1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31    
カテゴリ
タグクラウド
ファン
利用中のオープンソース
最近のコメント
最近の記事
過去ログ
QRコード
レガシーなアプリはいかが?
Dell 法人のお客様ページ
  • 【法人様向け】デル、お得なキャンペーン情報
  • 法人のお客様向け ストレージソリューション
  • 法人のお客様向け ネットワークソリューション
  • 【SOHO法人様向け】デル・オンライン広告限定ページ
  • デル-個人のお客様ページ
  • 【個人のお客様向け】デル・オンライン広告限定ページ
  • オンライン広告限定キャンペーンページ
  • ソフトウェア&周辺機器 パソコン工房
    ツートップインターネットショップ(twotop.co.jp) マウスコンピューター/G-Tune
  • ×

    この広告は1年以上新しい記事の投稿がないブログに表示されております。