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)
 多少書き殴りの為、ゴミも多いが、解説すると内容は次の通り。 前半のprint文は、面倒なので3連の引用符を活用し表示フォームを一括で指定した。重要なのは最初の1行目と次の空行出力で、後は普通のCGIフォームを含んだHTML文である。CGIフォームとしては、とほほさんのものよりは、欲画いて、複数フォームで同一名のフォームを複数含む場合の処理をチェックする様にした点が異なるが、チェック上の便宜面での変更を除き極力同じとなる様にしているので説明は要らないだろう。

 さて、その後の記述は、フォーム情報等のチェック用のコードで、一般には、フォーム表示にそのデータを加味させる為、上記のprint文の前に書くことが多いが、これは只のテスト様なので下に付けさせて頂いた。

 既にコメント化しているが「#cgi.test()」の行は、必要によりコメントアウトして環境変数やフォーム情報を表示することが出来る。

 その行の次にある「fs = cgi.FieldStorage()」が実をいうとこれが、解決のカギであり、その後の

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/>'
でフォーム情報が全て処理出来ることになる。

次のブロックでmire.htmを使ったデータ取得を試み成否を確認しつつ、mire.htm.pyの修正を試みて行ったのであるが、上記ブロックの挿入では実のところ解決しなかった。結論は、下に掲載の修正後の「/usr/local/lib/sitepackages/mire/htm.py」にある様に、mire.htm.cgi_params()関数の引数に上記コード上で作成したcgi.FieldStorage()クラスのインスタンスを引数として指定も可能な様にする修正が、今回の障害への解答であった。

 このことのみで、Win側では問題が起きず、Linux側のみで起きる不具合が解決出来ることの仕組みを説明することは、未だ当方には出来ないが、POST通信が標準入出力による情報取得に依存していること。その取得のタイミングがWinとLinuxでは異なる可能性があるのではないかと思う。何故ならGET通信の情報は環境変数なので同一通信を受信したCGIモジュールで反復して呼出すことが出来るが、POST通信での標準入出力は、最初の1回目しか出来ないはずだからである。

 従って、もし、cgi.FieldStorage()クラスのインスタンスを生成した後で別途、フォーム情報取得様にmire.htm.cgi_params()内でインスタンスを生成して情報の呼出しを試みては絶対に駄目であるし、同様のことがOS等プラットフォーム側の実装で起きてしまえば今回の様な対応が不可欠となると考えて、CGIのコーディングを進めて行こうと思う。




#!/usr/local/bin/python2.6
# -*- coding: UTF8 -*-
## Windowsで使うときは備忘の為1行目を以下のものに置換え
## 実際はUNIX系の様には一切機能しない。
##!c:/Python26/python.exe

"""
######################################################################
#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(form) : CGI引数を辞書化し返します。環境によりFieldStorage()
# : インスタンスの引用元生成指定がPOST通信時は必須。
#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) : 数字化が可能な文字は全て数字に変換

"""

__author__ = "Mire in Japan"
__version__ = '0.0.8'
__copyright__ = 'Copyright (c) 2009-2010 Mire'
__license__ = 'GPL'
__url__ = 'http://pythonlife.seesaa.net/article/150777224.html'

history = "add 2010-03-23 header(),pipe().\n" + \
"add 2010-04-30 str_select_form, str_input_form, dsp_date_form\n"+ \
" remote_user, remote_ip\n"+ \
"add 2010-05-17 list2tbl, fprn_data, listing, etc...\n"+ \
"add 2010-05-18 get_env, stat_ssl\n"+ \
"add 2010-05-20 tbl2lists, td_tags, fit_str_list"+ \
"add 2010-05-22 str_list2unicode" + \
"alter 2010-05-30 header careless miss" + \
"alter 2010-06-17 cgi_params() for POST with My Linux server"


#import cgi
#form = cgi.FieldStorage()

def html_header0():
"""
廃止予定 header()を利用下さい。
"""
print 'Content-type: text/html\n'


def header(Type='html'):
"""
htmlやxmlを正常に出力し
伝達する為に必要な
Content-typeを出力
Type='html','xml',etc...
"""
print 'Content-type: text/%s;\n' % (Type)


## html文書の開始タグ一式を標準出力します
def 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\n'の付加:h=1
body : bodyつまり文書本体全体の属性定義をリスト付与
"""
if charset=='': charset='UTF8'
if generator=='': generator = "Mire's Python CGI"
#if css==[]: css = ['/css/default.css']
#if js==[]: js = ['/js/default.js']
if need_header==0: return 0
from time import time,gmtime,strftime
if h==1:
#print 'Content-type: text/html; charset=%s\n\n' % (charset)
print 'Content-type: text/html;\n\n'
print '<html>\n<head>'
if not base=='':
print '<base href="%s" target="_self">' % (base)
print '<meta http-equiv="Content-Type" content="text/html; charset=%s" />' % (charset)
if len(refresh)>0: # refresh秒のみが指定されたら、その間だけcasheする。
if len(refresh)==1 and cashe>refresh[0]:
cache=refresh[0]
if len(refresh)>1:
url='; url=%s' % (refresh[1])
else:
url=''
print '<meta http-equiv="refresh" content="%d%s">' % (reflesh[0],url)
print '<title >%s</title>' % (title) # ページタイトル
if cache==0:
print '<meta http-equiv="pragma" content="no-cache">'
elif cache>=1: #cache期限として現在時にcache秒を足した日時の
#"Sat, 31 Aug 2009 17:35:42 GMT"形式日時文字列
print strftime('<meta http-equiv="expires" content="%a, %d %b %Y %H:%M:%S GMT">',
gmtime(time()+cache))
if len(author):
print '<meta name="author" content="%s">' % (author)
if not copyright=='':
print '<meta name="copyright" content="%s">' % (copyright)
if not generator=='':
print '<meta name="generator" content="%s">' % (generator)
if len(description)>0: # description文書説明
print '<meta name="description" content="%s" />' % (','.join(description.sort()))
if len(keywords)>0: # キーワード
print '<meta name="keywords" content="%s" />' % (','.join(keywords.sort()))
if robot==1:
print '<meta name="robots" content="all">'
elif robot==-1:
print '<meta name="robots" content="noindex,nofollow">'
if len(js)>0: # JavaScript活用
print '<meta http-equiv="Content-Script-Type" content="text/javascript" />'
if len(css)>0: # StyleSheet活用
print '<meta http-equiv="Content-Style-Type" content="text/css" />'
for c in css: # css スタイルシートファイルリンク先 #
print '<link rel="stylesheet" href="%s" type="text/css" />' % (c)
for j in js: # javascriptファイルリンク先 #
print '<script type="text/javascript" src="%s"></script>' % (j)
if len(style)>0:
print '<style type="text/css">%s</style>' % (' '.join(style))
print '</head>'
print '<body %s>' % (' '.join(body))
return 0

## html文書の終了タグ一式を標準出力
def html_footer():
u"""
html文書の終了タグ一式を標準出力
"""
print '</body>\n</html>'


## urlの示す文書を文字列リストで返します
def url2str_list(url):
u"""
urlの示す文書を文字列リストで返します
"""
from urllib import urlopen
upi=urlopen(url)
lines=upi.readlines()
upi.close()
return lines


## urlの示す文書を文字列で返します
def url2str(url,trim=0, debug=0):
u"""
urlの示す文書を文字列で返します
タグ判断を一括で行なう時に有用
行頭、行末の半角空白文字と改行
を取除きたいときはtrim=1にする
"""
lines = url2str_list(url)
i = 0
for l in lines:
try:
pass
###charset = get_charset(url)
###print charset
#lines[i]=str2unicode(l, charset=charset)
#print lines[i]
except:
if debug > 0:
display_err(locals())

pass
i = i + 1
if not trim==0:
i=0
for l in lines:
lines[i]=l.strip()
i=i+1
try:
#st=' '.join(lines)
st = ''
for l in lines:
#ll=l.strip()
st = st + l

except:
display_err(locals())
st = u''
for l in lines:
st = st + u'%s' % (strip(l))

return st


## str文字列からmeta定義のcharsetを返します
def get_charset(s):
u"""
str文字列からmeta定義のcharsetを返します
複数見つかった場合には最初のものを返します
存在しない場合には既定値として'UTF8'を返します
"""
from re import findall, split, compile, IGNORECASE
#ma = compile('&lt;meta.*?charset.*?>', IGNORECASE) # 多分、「&lt;」はコピペミス
ma = compile('<meta.*?charset.*?>', IGNORECASE)
metas = findall(ma, s)
if len(metas)>0:
ch = split('=',metas[0])
ep = ch[-1].find('\"')
charset = ch[-1][:ep]
else:
charset = 'UTF8' # default値
return charset


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

※ 一致する文字コードがない場合は変換せず、そのまま返す様にした。例えば、
Shift_JIS系では。拡張文字等の差のある実装が複数存在する。関連する文字
コードは可能な限り登録しておく必要がある。Shift_JISとして作られた文書
でも、OSで入力可能な文字は混ざってしまうので仕方がない。
"""
charsets.insert(0, charset) # 2010-05-19 corrected typeing mistaken
for c in charsets:
try:
u = u'%s' % (unicode(s,c))
if debug>0 and not c==charset:
print u'指定のcharset "%s" ではなく、' % (charset),
print u'"%s" でunicode化しました。' % (c)
return u
except:
if debug>1: print u'文字列は%sでの記述ではありません' % (c)
pass
if debug>0:
print u'charset候補の何れも該当しない為文字列をそのまま返しました'
print u'文字列内に異なるcharsetが混在している可能性を疑って下さい'
print u'混在の場合はascii文字以外のブロックを探して個別に対応です'
return s


## 文字列またはリストのunicode化を試みます
def 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()関数に依存。リスト型時の再帰処理を作込んだもの。
※ 現時点では、シーケンス系以外の要素はそのまま返す仕様。
"""
"""
2010-05-21 初版作成。リスト作成完了後のUnicode化用。
"""
from types import ListType, TupleType, StringType, UnicodeType
if type(lst) is StringType or type(lst) is TupleType: # 文字列なら
return str2unicode(lst, charset=charset, # Unicode化
charsets=charsets, debug=debug) # を試みる
elif type(lst) is ListType or type(lst) is TupleType: # リスト系なら
if type(lst) is TupleType: # タプルなら値変更の為
lst = list(lst) # リスト化
i = 0 # リスト位置
for l in lst: # リスト要素を一つずつ調べ
if type(l) is ListType or type(l) is TupleType: # リスト系なら再帰処理する
lst[i] = str_list2unicode(l, charset=charset,
charsets=charsets, debug=debug) # 入れ子
elif type(l) is StringType or type(l) is TupleType:
lst[i] = str2unicode(l, charset=charset, # Unicode化
charsets=charsets,
debug=debug) # を試みる
#else: # それ以外はその型のまま
# lst[i] = l # 代入処理不要なのでコメント化 # 何もしない ※
i = i + 1
return lst
else: return lst # それ以外ならそのまま返す


## CGI引数の辞書化 ##
def cgi_params(form=None, debug=0):
u"""
## CGI引数の辞書化 ##
cgi.FieldStorage()で得られるcgi引数を辞書に収め返す
辞書構造とすることで修正が可能になる他、
必要な既存の引数を引継ぎインタラクティブにページを変化
させるときに便利。
【使い方】
下記の様に引数名を keys()で取得、順不同の為、必要より、
sort()の上、引数名を1つずつ取出し活用すると良い
import cgi # POST通信時の引数受取り時には、引用先のモジュール側の
form = cgi.FieldStorage() # ルートで、cgi.FieldStorage()のインスタンスを生成し、
param_dic = cgi_params(form) # この関数の引数に渡す必要なことがある。
keys = param_dic.keys()
keys.sort()
for k in keys:
value = param_dic[k]
print k,value

※ この関数は引数値全部をリストで返す為、セキュリティ重視の場面での
利用が好ましくないことも多い。
"""
"""
History
=======
2010-06-17 当方のLinux環境(CentOS5.4)で、POST通信時のstdioによる引数取得は、
呼び元のモジュール側のルートでインスタンス生成したcgi.FieldStorage()が
必要であることが判り、その対処として、それをこの関数の引数として指定
出来る様にした。その他、その対策過程で多少冗長な処理を追加しているので
何れ、Cleanup の必要があるものと思われる。debugも対策過程で付加。
"""
import cgi
if form is None: # 引数渡しがないときは、
form = cgi.FieldStorage() # cgi.FieldStorage()インスタンスを作成。
if debug > 0:
print 'mire.htm.cgi_params()=', form
params = {}

# try: # 2010-06-17 冗長ながら、
# for f in form.value: #
# try: #
# params[f.name] = f.value # name, value属性値を直接設定を付加して試た
# except: # しかし、実際には従前分で対応可能だったので
# print 'ERROR' # コメント化した。
# pass # (値取得の手法の1つとして暫くは記録保持)
# except:
# if debug>0:
# msg = '"form.value" ERROR in mire.htm cgi_params()'
# display_err(locals(),msg)


keys = form.keys()
for key in keys:
try:
params[key] = form.getvalue(key) # getvalue()メソッドから試し
except:
try:
params[key] = form[key].value # value属性を参照して試る
except:
if debug>0:
display_err(locals(),'ERROR in mire.htm cgi_params()')
else:
params[key] = form.getvalue(key)
params[key] = form[key].value

return params


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


## タグ文字のままHTML表示可能な文字列listに変換 ##
def tag_encode(lines=[]):
u"""
タグ文字のままHTML表示可能な文字列listに変換
'&' ⇒ '&amp;', '<' ⇒ '&lt;', '>' ⇒ '&gt;'
"""
from codecs import getwriter,getreader,open
enc_lines=[]
for l in lines:
enc_lines.append(l.replace('&','&amp;').replace('<','&lt;').replace('>','&gt;'))
return enc_lines


## エラー表示関数(コメント行文字化け対策+thread実行時のエラー情報表示の時系列混乱軽減) ##
waiting=0 #Global変数
def 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() #
######################################################################################
"""
import traceback
from time import sleep
if need_header==1:
print 'Content-type: text/html\n\n<html>\n<head>'
print '<body>'
print '<pre>'
if local is None:
local =locals()
global waiting
frame0 = u'\n*******************************************************************************'
frame1 = u'*******************************************************************************\n\\
n'
errs = [frame0, title]
errs.append(u'%s' % (dsp_hex(traceback.format_exc()))) #> dsp_hex(s): コメント文字化け対策
sleep(waiting)
ls = local
keys = ls.keys()
keys.sort()
errs.append(u'')
for key in keys: # display_err()関係の変数の表示を省く
# if not key in ['v', 'ls', 'keys', 'key', 'errs', 'txt', 'er', 'sleep']:
er = u'* %s: %s' % (repr(key).ljust(24),repr(ls[key]))
errs.append(er)
errs.append(frame1)
txt = u'\n'.join(errs)
print txt.replace('<','&lt;') # エラー発生行のタグ文字への最低の手当てを追加(2010-04-09)
print '</pre>'
if need_header==1:
print '</body></html>'


## コメント文字化け対策 ##
def dsp_hex(s):
from re import search
charsets=['UTF-8','Shift_JIS','EUC-JP','iso-2022-jp'] # どれでもいいので、charsetで、
for charset in charsets: # unicode化し、コメント部分の
try: # 文字化けを防ぐ
return unicode(s,charset)
except:
print u'%sでは変換不能でした' % (charset)
pass
sss=u''
for ss in s: # 想定外のcharsetなら16進数表記に
if (ord(ss)>=32 and ord(ss)<=126) or ss.isspace(): # Winにはcurses.ascii.isprint()が
sss=sss+ss # ない為curses/ascii.pyの中身展開
else:
sss=sss+'\\'+hex(ord(ss))[2:].upper()
return sss


## 指定URIの内容をそのまま出力 ##
def 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()
"""
from urllib import urlopen
upi = urlopen(url)
lines=upi.readlines()
#print 'Content-type: text/html;\n\n' #空行は1つのみが有効なので使えない
#print 'Content-type: application/xml;\n'
print 'Content-type: text/%s;\n' % (Type)
#print 'Content-type: application/xml; charset="utf-8"\n' #charset追加も使えない
s = ''.join(lines)
print s,


## 対話型selectフォーム ##
def 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表示付加。
それ以外は現在地のみ。
"""
# 引数値を設定
if id in params: val = params[id] #現在地を val に設定
else: val = ''
if prefix+id in params: p_val = params[prefix+id] #前値を p_val に設定
else: p_val = ''
# Tips表示の生成
if val==p_val: title = '[%s]%s' % (val, options[val])
else: title = '[%s]%s &lt;- [%s]%s' % (val, options[val],
p_val, options[p_val])

opt_keys=list(options.keys())
opt_keys.sort()
s=''
for key in opt_keys:
if val==key:
selected=' selected'
else:
selected=''
s = s+'<option value="%s"%s>%s</option>\n' % (key,selected,options[key])
# 値変更時に表示スタイルを変更
if is_changed(params=params, id=id, prefix=prefix):
style='background-color:%s; %s' % (changed_color, style)
# 前値比較の有無
if hidden==0 or val=='': # 前値を使わないまたは、値がゼロ長文字列のとき
s = '<select style="%s" name="%s">%s</select>' % (style, id, s)
if prefix+id in params:
s = s + '<input type="hidden" name="%s%s" value="%s">' % (prefix, id, val)
return s
else:
s = '<select style="%s" name="%s" alt="%s" title="%s">%s</select>' % (style, id,
title,
title, s)
s = '%s<input type="hidden" name="%s%s" value="%s">' % (s, prefix, id, val)
return s
return 0


## 対話型テキスト入力フォーム ##
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]
alt_str = 'alt="%s" title="%s"' % (title, title)
s='<input type="%s" name="%s" size="%d" value="%s" %s style="text-align:%s; %s">' % (
type, id, size, val, alt_str, align, style)
if not hidden==0:
s=s+'<input type="hidden" name="%s%s" value="%s">' % (prefix, id, params[id])
return s
else: # 引数が存在しないとき
alt_str = 'alt="%s" title="%s"' % (alt, alt)
s= '<input type="%s" name="%s" size="%d" value="%s" %s style="text-align:%s; %s">' % (
type, id, size, default, alt_str, align, style)
# 入力欄の値が存在しなくとも、前値が存在していたなら、その値を
# 0長文字列として保持 無意味とは思う
if prefix+id in params:
s=s+'<input type="hidden" name="%s%s" value="">' % (prefix, id)
return s


def now():
"""
dsp_date_form()のlocaltimeの既定値として
現在の日時を指定する為定義
"""
from time import localtime, time
return localtime(time())


## ##
def dsp_date_form(params=cgi_params(), localtime=now(), id=['y','m','d'],
years_range=[-10,+5], prefix='p_', hidden=0):
"""
既定を現在時とする日付の選択ボックスを表示
:現在年月日以外を既定とするには
"""
from Types import TupleType, ListType
## 引数があればそれを、なければlocaltimeタプル内の値を既定値とする ##
if type(localtime) is ListType:
nw=localtime # selected とする値
else:
nw=list(localtime)
i=0
for p in id:
if p in params:
nw[i]=int(params[p])
else:
nw[i]=localtime[i]
i=i+1
#print nw
print '<table cellspacing="0" cellpadding="0" border="0"><tr>',
## 年 ##
print '<td><select name="%s">' % (id[0]),
for y in range(localtime[0]+years_range[0],localtime[0]+years_range[1]):
if y==nw[0]:
print '<option value="%04d" selected>%04d</option>' % (y, y),
else:
print '<option value="%04d">%4d</option>' % (y, y),
if not hidden==0:
print '<input type="hidden" name="%s%s" value="%4d">' % (prefix, id[0], nw[0])
print '</select></td>',
## 月 ##
print '<td><select name="%s" style="text-align:right">' % (id[1]),
for m in range(1,13):
if m==nw[1]:
print '<option value="%02d" selected>%4d</option>' % (m, m),
else:
print '<option value="%02d">%4d</option>' % (m, m),
if not hidden==0:
print '<input type="hidden" name="%s%s" value="%02d">' % (prefix, id[1], nw[1])
print '</select></td>',
## 日 ##
print '<td><select name="%s" style="text-align:right">' % (id[2]),
for d in range(1,32):
if d==nw[2]:
print '<option value="%02d" selected>%4d</option>' % (d, d),
else:
print '<option value="%02d">%4d</option>' % (d, d),
print '</select>',
if not hidden==0:
print '<input type="hidden" name="%s%s" value="%02d">' % (prefix, id[2], nw[2])
print '</td></tr></table>'
return 0


## ##
def is_changed(params={'':''}, id='', prefix='p_'):
'''
前値と現在値を比較し
'''
if id[:len(prefix)]==prefix:
if id in params:
if id[len(prefix):] in params:
return 1
else:
if id in params:
if prefix+id in params:
if params[id]==params[prefix+id]:
return 0 #true
else:
return 1 #false
else:
if prefix+id in params:
return 1 #false
else:
return 0
return 1 #false


# 方形リストを表で出力 ##
def list2tbl(datas, title='', col_names=[], sql='', border=1, col_default=u'第%d列',
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 : 表全体に対するスタイルシートを文字列で指定
"""
"""
【更  新  履  歴】
2010-05-15 作成開始 mire.htmに移行予定。
"""
from mire.sql import query2fld_names
if len(datas) > 0:
if not sql == '':
col_names = query2fld_names(sql)
if col_names == []:
if not sql == '':
col_names = query2fld_names(sql)
else:
cn = len(datas[0])
for i in range(cn):
col_names.append(col_default % (i+1)) # 2010-05-18 書式の引数化
t = []
if not style == '':
t.append('style="%s"' % (style))
if border > 0:
t.append('border="%d"' % (border))
print '<table %s>' % (' '.join(t)),
if not title == '':
print '<caption><font size="+1"><u>%s</u></font></caption>' % (title)
#else:
# print '\a' # 2010-05-18 debug時痕跡に付削除。
print '<tr>',
for c in col_names:
print u'<th style="text-align:center;">%s</th>' % (c),
print '</tr>',

for dat in datas:
print '<tr>',
for d in dat:
fprn_data(d)
#print u'<td>%s</td>' % (d),
print '</tr>'
print '</table>'


## データ型別に書式付印刷 ##
def fprn_data(d, tag='td', debug=0):
"""
データ型別に書式付印刷
d : 表内のデータを指定
tag :
"""
"""
【更  新  履  歴】
2010-05-15 初版 文字列、整数、浮動小数点、Noneに対応
2010-05-16 公開 mire.htm移行予定
"""
from types import ListType, TupleType, StringType, UnicodeType, IntType, LongType
from types import FloatType, NoneType
fmt=''
t = type(d)
if t is StringType or t is UnicodeType:
print '<%s%s>%s</%s>' % (tag, ' style="text-align:left;"', d, tag), # !U
elif t is IntType or t is LongType:
print u'<%s%s>%d</%s>' % (tag, u' style="text-align:right;"', d, tag),
elif t is FloatType:
print u'<%s%s>%16.16f</%s>' % (tag, u' style="text-align:right;"', d, tag),
elif t is NoneType:
print u'<%s%s>&lt;NULL></%s>' % (tag, u' style="text-align:right;"', tag),
else:
print u'<%s>' % (tag), t,u':',d, u'</%s>' % (tag.split()[0]),
return fmt,d


## 文字列リストHTML画面出力 ##
def listing(str_list, title='', h='h4', tags='ol', tree=1, nothing='&#182;', nest=0):
"""
文字列リストのリスト出力用関数
str_list : 基本的に文字列を要素とするリストを指定。
: 但し、リスト要素には文字列を要素とするリストを入れ子で持つ
: ことも出来る。
tags : 基本的に文字列でリスト表記用のタグ名(ol, ul, div, pre)を指定
: 但し、入れ子階層別でタグを切替えたい場合にはリストで指定可
"""
"""
【更  新  履  歴】
2010-05-01 初版 何れ、mire.htm に移行
2010-05-09 str_list内要素がリストまたはタプルの場合にはこの関数で再帰処理
複合構造の箇条書きに対応。
2010-05-10 各データ型の変換をobj2value_str()に切出し移行
2010-05-15 不適切だった「;」の位置を修正。
"""
from types import ListType, TupleType, StringType, UnicodeType
from types import IntType, LongType, FloatType
from mire.std import obj2value_str

## リスト記載用のタグ ##
if type(tags) is ListType or type(tags) is TupleType: # 引数の型がリストorタプルなら
#print 'len(tags)=', len(tags),
#print 'tags[nest]=', tags[nest],
#print 'tags=', tags

if len(tags)>nest:
#print nest
tag_str = tags[nest] # 階層別のものを設定
else: tag_str = tags[-1] # なくなったら、最後のものを設定
else: tag_str = tags # それ以外なら、そのまま設定

## ol,ulタグ時にliタグを付加 ##
if tag_str=='ol' or tag_str=='ul': bu='li'
else: bu=''

## 表題タイトル ##
if type(title) is ListType or type(title) is TupleType: # 引数の型がリストorタプルなら
if len(title)>nest: title_str = title[nest] # 階層別のものを設定
else: title_str = '' # なくなったら、最後のものを設定
else: title_str = title # それ以外なら、そのまま設定

## 表題タグ ##
if type(h) is ListType or type(h) is TupleType: # 引数の型がリストorタプルなら
if len(h)>nest: h_str = h[nest] # 階層別のものを設定
else: h_str = h[-1] # なくなったら、最後のものを設定
else: h_str = h # それ以外なら、そのまま設定

if not title_str=='':
print '<%s>%s</%s>' % (h_str, title_str, h_str.split()[0]), # 表題表示
if not tag_str=='' and tag_str.split()[0].lower() in ['ol', 'ul',
'div']: # ol,ulなら入れ子リストとして
print '<%s>' % (tag_str),
i = 0
for str_line in str_list:
if type(str_line) is ListType or type(str_line) is TupleType: # 引数がLISTかTUPLE型なら
nest_p = listing(str_list=str_line, title='',
tags=tags, nothing=nothing, nest=nest+1) # 入れ子でこの関数を実行
if tree>0 and not nest_p == 0:
#print nest_p
if nothing=='spacer':
print '<div style=\"line-height:85%%; padding:2px;',
print 'font-size:9pt; width:4pt;\"></div>'
elif not nothing=='nothing':
print '<div style=\"line-height:85%%; padding:2px;',
print 'background-color:#E0E0E0; color:#FFFFFF;',
print 'font-size:9pt; width:4pt;\">%s</div>' % (nothing)
i = 0
else:
from time import time
from datetime import datetime
if not type(str_line) is StringType or type(str_line) is UnicodeType:
str_line = obj2value_str(str_line)
if bu == '':
if tag_str.split()[0][:-1] in ['h']:
print '<%s>%s</%s>' % (tag_str, str_line, tag_str.split()[0]),
else:
print '%s' % (str_line)
else: print '<%s>%s</%s>' % (bu, str_line, bu)
i = i + 1

if not tag_str=='' and tag_str.split()[0].lower() in ['ol', 'ul', 'div']:
print '</%s>' % (tag_str.split()[0]),
return i


## 環境変数リストの出力 ##
def get_env():
"""
環境変数リストの出力
"""
"""
※ この関数を安易に埋め込み、例外処理を含むその内容出力を招く利用は
推奨出来ない。その画面表示または通信経路でその内容を入手した者が、
例えばセキュリティ用途のHTTP_COOKIEのデータを悪用すれば、なりすまし
による不正アクセスの可能性を増加させる。
 このクッキーは、ブラウザがドメイン名やIPアドレス別で保持したものを
全て出してくるので、非固定IPサーバに対する直IP接続や、放棄ドメイン名
へのアクセスでは、Emailや名前といったものが、簡単に流出してしまう。
 設定するクッキーはそれも考え、生の個人情報登録はやめた方が良いし、
これらの一つであっても、取得するコードを書くときには例外発生時も含め
不用意にユーザ側に送り返さない様にしたいところだ。

 また、この端末側の環境情報を個人特定情報と騙り、あたかも契約が成立
したかの様に欺罔し、ネット上での手続きや入金を迫る。クリック詐欺は、
このデータを悪用したものである。

 その様な情報ではあるが、これらをどの様に活用するかで、ユーザ管理の
特定と管理をWebシステム上では行なう訳なので適宜確認し、安全性確保を
追及して頂きたい。
"""
"""
更新履歴
2010-05-18 作成追加
"""
from os import environ
keys = environ.keys()
keys.sort()
l = []
for k in keys:
l.append([k, environ[k]])
return l


## SSL通信状態 ##
def stat_ssl():
"""
SSL通信状態 SSL on:1 以外:0
※ ユーザ認証や登録フォーム等ではこれで確認上、表示処理させるべきだろう。
"""
"""
更新履歴
2010-05-18 作成追加
"""
try: # 過負荷による異常動作や割込停止がない限りenviron情報を流出させない!!
from os import environ
if 'HTTPS' in environ:
if environ['HTTPS']=='on':
return 1 # onなら 0 = true
return 0 # そうでないときは 全て 1 = false
except: return None # 判定処理自体が失敗 None = false


## リモートユーザーIDの取得 ##
def remote_user(default=''):
"""
リモートユーザーIDの取得
"""
from os import environ
if 'REMOTE_USER' in environ:
return environ['REMOTE_USER']
else:
return default


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

Webサーバの返すブラウズ側端末のIPは、利用Proxy次第で
正しいIPを返さないことがある。従って、この関数は見直し
予定のものである。次のURLには詳しく、その調査内容が掲載
されている。
http://www.nurs.or.jp/~sug/homep/proxy/proxy7.htm
"""
from os import environ
if 'HTTP_X_FORWARDED_FOR' in environ:
rADDR = environ['HTTP_X_FORWARDED_FOR']
elif 'REMOTE_ADDR' in environ:
rADDR = environ['REMOTE_ADDR']
else:
rADDR = default
return rADDR


## Web上のTABLEタグ表記の2D表からセル値を抽出リスト群化し返す関数 ##
def 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を無視した処理の為、データ列がずれる仕様である
"""
"""
更新履歴
2009-09-03 初版import_table()として作成
「Web TABLEタグのデータ取込関数 by Python」で公開
http://pythonlife.seesaa.net/article/127137264.html
2009-10-09 誤り訂正
2010-05-19 mire.htmにtbl2list()と改称して収録
2010-05-22 unocode化のタイミングを選択可能に
"""
from urllib import urlopen
from datetime import datetime
from re import findall, split, compile, IGNORECASE, UNICODE

st = url2str(url, trim=1) # urlの示す先のHTML文書を文字列で取得

#2010-05-09 mire.htm内関数に置換
#upi=urlopen(url)
#lines=upi.readlines()
#i=0
#for l in lines:
# lines[i]=l.strip()
# i=i+1
#st=' '.join(lines) #間違っていたので訂正2009-10-09

charset = get_charset(st) # 取得したHTML文書文字列のMETAタグの文字セットを取得
#charset = 'cp932'
#2010-05-19 mire.htm内関数に置換
#ma = compile('<meta.*?charset.*?>', IGNORECASE)
#metas = findall(ma, st)
#if len(metas)>0:
# ch = split('=',metas[0])
# ep = ch[-1].find('\"')
# charset = ch[-1][:ep]
#else:
# charset = 'UTF-8'

datas=[]
if as_unicode > 0:
dat = str2unicode(st, charset=charset) # 正規表現処理前にUnicode化
else:
dat = st
#print dat

#2010-05-19 mire.htm内関数に置換
from sys import stdout
#try:
# dat=u'%s' % (unicode(st.decode(charset), charset)) #間違っていたので訂正2009-10-09
#except: #ものにより指定のcharsetでErrorとなる時の対策
# display_err(locals())
# charsets=['Shift_JIS','EUC-JP','UTF-8'] #コード指定の不一致だけならこれで対応可能だが
# for c_set in charsets: #部分的な不一致には対応出来ない。その場合には
# try: #読込行別で、このコード変換をかける程度の対応
# dat=u'%s' % (unicode(st,c_set))#で逃げるしかないだろう。
# print c_set
# break
# except:
# dat = st
# pass
# print charset

if as_unicode > 0:
mach = compile(u'<table.*?>.*?</table>', UNICODE) #tableタグプロックを
else:
mach = compile('<table.*?>.*?</table>', IGNORECASE)

ss = findall(mach, dat) #単純抽出 2010-05-19置換
# (入れ子は未検証)
#mach = compile(u'<table', IGNORECASE) #ついでに複数のtableタグに対応し
#mach = compile(u'<table', UNICODE) #ついでに複数のtableタグに対応し
#ss=split(mach, dat) #その数分list要素で区切って返す様に
for s in ss: #変更
if not s==-1:
if tr_pat==1:
mat = compile('<tr.*?>', IGNORECASE)
tr_tags=split(mat,s)
else:
mat = compile('<tr.*?>.*?</tr>', IGNORECASE) #ついでに大文字小文字区別なしに修正
tr_tags=findall(mat,s) #間違っていたので訂正2009-10-09
data=[]
for tr in tr_tags:
v=td_tags(tr,charset=charset,
charsets=charsets,
as_unicode=as_unicode) #<td></td>で分割する関数へ
data.append(v)
datas.append(data)

if fit_type > 0:
values= fit_str_list(datas) # 数字化が可能な文字は全て数字に変換(便宜的処理)
else: # ※ この関数は少し強引。予め列別で値の型が
values= datas #  判っている場合は、それを生かす様に改良
#  すべきである。
return values


## <td></td>で分割する関数 ##
def 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には未対応
"""
"""
2010-05-22 unocode化のタイミングを選択可能に
"""
from re import findall, compile, IGNORECASE, UNICODE
from types import UnicodeType
try:
if as_unicode>0 and not type(text) is UnicodeType:
text = u'%s' % (unicode(text, charset, charsets))
except:
pass
if as_unicode >0 :
ma =compile('<td.*?>.*?</td>|<th.*?>.*?</th>', UNICODE)
else:
ma =compile(u'<td.*?>.*?</td>|<th.*?>.*?</th>', IGNORECASE)
td_tags=findall(ma, text)
items=[]
for td in td_tags:
s=td.find('>')
if not s==-1:
t=td[s:].find('</')
try:
items.append('%s' % (td[s+1:s+t]))
except:
display_err(locals(),title='## EXCEPTION ERROR in mire.htm.td_tags ##')
print s+1,s+t
print tab_encodes(td[s+1:s+t])
print tab_encodes([type(td)])[0]
return items


## 数字化が可能な文字は全て数字に変換 ##
def fit_str_list(lst, col_types=[]):
"""
list と tupleの要素で整数文字列のものを全て再帰的に整数に置換
col_types : 列別の変換関数をリストで指定可。
googlechart.pyのline_styleの指定要素が現在文字であるので
数字指定も可能な様にする為に利用
milestone:
1. リストを一度舐め回し、列別の標準的な型を判断する機能追加
2. 数字以外の型、例えば日付、時間型にも対応したい
"""
"""
【更新履歴】
2010-05-20 fit_str_list()に改称。機能拡張へ
"""
from types import IntType,ListType,TupleType,StringType,UnicodeType
if type(lst) is TupleType:
lst=list(lst)
if not lst==[]:
i=0
for ls in lst:
if type(ls) is StringType or type(ls) is UnicodeType:
if len(col_types) > i:
try:
cnv = col_types[i]
v = cnv(ls)
lst[i] = v
except:
pass
else:
try:
v=float(ls.strip())
try:
v=int(ls)
#print [lst[i],v]
lst[i]=v
except:
pass
except:
pass
elif type(ls) is ListType or type(ls) is TupleType:
lst[i] = fit_str_list(ls)
i=i+1
return lst



if __name__ == '__main__':
import mire.htm
help(mire.htm)



 尚、今回のこの障害については、当方の過去の経験ではLinuxで必ず起きるのかどうかというと、そうではなかった様には思っている。誠に残念だか過去運用していたサーバ機はもう手の及ぶところにはないので確かめようがない。ただ、CentOS5.4のシステム用途である/usr/binにあるPython2.4での実行だけでは改善しなかったとを考えると上記の対策以外に方策はないのかもしれない。

 でも、一般論としてだが、今回の様に特定の機能で不具合が発生する場合、疑うべきは、Pythonビルド時にその機能が必要とするCライブラリが不足していたのではないかということである。つまり具体的には、ソースインストール時に最初に実行する「./configure」で必要なヘッダファイルがなかったり、環境により指定が必要なオプションが付いていない等の理由で不完全な実行環境を構築してしまった場合である。当然今回も、その辺も眺めてみてはいるが、標準入出力という極一般的な処理関連であり考えずらいが、io.hが当方のLinux環境に限らず、次の様になってしまうことも事実の様である。



[root@l22dm DownloadFiles]# cd Python-2.6.5
[root@l22dm Python-2.6.5]# ./configure
<中略>
checking io.h usability... no
checking io.h presence... no
checking for io.h... no
<後略>
[root@l22dm Python-2.6.5]#
 ここ辺りを深堀りすると、別解に到達出来る可能性が万に1つ程度にはあるのかもしれない。ここの課題は兎も角、一般論としてはあることなので、原因不明の障害切り分け時には頭の隅っこにでも置いておくことをお奨めする。
タグ:Python CGI post
posted by Mire at 17:18 | 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年以上新しい記事の投稿がないブログに表示されております。