2009年11月09日

【箸休め】フォルダ傘下のKey Word検索 srchlines.pyの作成

 本稿のお題は、完成させる気もないキーワード検索ツールだ。作成動機は、1年半ものLinuxブランクによる操作能力ギャップをカバーする為でちょこちょこ、書いた使い捨てソースコードが元になっている。キーワードも検索位置ディレクトリも全て、ソースコード埋め込みで取敢えず、目的は果たしたが、まあ、せめて、Blogネタになる程度にBug獲りとコマンドライン引数への対応を簡便に済ませた程度のもので、キーワードも一つのみでワイルドカードや正規表現にも対応していない代物だが、返って複雑でないので、サンプルコードとしては、この程度が理解し易いかと思う。
 使い方は、例えば、python srchlines.py ..\ Mire 等とすると、「..\」カレントディレクトリの一つ上のディレクトリ傘下の全ディレクトリ内の全ファイルを読んで、指定のキーワード「Mire」を含む行を探し、あったらそれを表示する程度のものだ。

「srchlines.py .\ Error」実行結果は次の通り
D:\PythonCGI>python srchlines.py .\ Error

■.\cgi-bin\html4blog2.cgi内を検索
75 print '<div align="left">【Error】 was occured.<br><br>Please, contact your system administrators.<br><br>'
95 print '<div>【Error】 was occured.\nPlease, contact your system administrators.<br><br>'
計 2行で見つかりました。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

■.\cgi-bin\html4blog2.py内を検索
78 print '<div align="left">【Error】 was occured.<br><br>Please, contact your system administrators.<br><br>'
98 print '<div>【Error】 was occured.\nPlease, contact your system administrators.<br><br>'
計 2行で見つかりました。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

■.\cgi-bin\html4spam.py内を検索
78 print '<pre>【Error】 was occured.\nPlease, contact your system administrators.\n'
98 print '<pre>【Error】 was occured.\nPlease, contact your system administrators.\n'
計 2行で見つかりました。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

■.\html4blog2.cgi内を検索
75 print '<div align="left">【Error】 was occured.<br><br>Please, contact your system administrators.<br><br>'
95 print '<div>【Error】 was occured.\nPlease, contact your system administrators.<br><br>'
計 2行で見つかりました。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

■.\zope2.12.0_install.txt内を検索
942 【Error】 starting service: 指定されたサービスはインストールされたサービスとして存在しません。
計 1行で見つかりました。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

D:\PythonCGI>


 尚、技術的には、@再帰処理でディレクトリ内の全ディレクトリも検索対象ししているところ、A複数の文字コードで書かれているファイルの文字列行のUNICODE化を試みることで文字化け表示を抑制していること。B例外発生時に、発生ブロックでの変数情報を表示し、文字列については16進数表示も可能となっていることくらいかと思う。これはあくまで、セキュリティ上、システム管理者が使うことが前提のもので、コンソールからセキュリティ制限のない読込可能なファイルであれば全てのファイルにアクセス出来るので、その点、利用には注意頂きたい。サンプルなので今後のサポートは一切ありません。煮て食うなり焼いて食うなり好きにして下さい。以下にソースコードを掲載して置きます。
    srchlines.py


#!c:/Python26/python.exe
# -*- coding: UTF-8 -*-
# ※ Windows の場合1行目はdummyです。

waiting=0 #例外発生時の表示を読み、キー割込みを可能にする為のウェイト秒のグローバル変数

#################################################################################################
#################################################################################################
# 関数定義行

# 1. 指定ディレクトリ内を舐め回し、傘下の全ファイルに対しsrch()を実行
def walk(path,s):
from os import listdir
from os.path import isdir, isfile, islink, join
ds = listdir(path)
for d in ds:
if isfile(join(path,d)): #ファイルならば、
srch(join(path,d),s) #> 2.srch()関数へ
elif isdir(join(path,d)):
try:
walk(path=join(path,d),s=s) #> 1.walk() ※再帰処理
except:
print u'◆「%s」はDirectoryではありません' % (join(path,d))
display_err(local=locals(), title = u'\n\n#### EXCEPTION ERROR ####')

# 2. ファイル内の文字列を一行ずつにたいし、検索文字列が含まれていないかをチェックし、
# それがあったら、行番号付でその行を表示させる。
def srch(d,s):
try:
fpi = open(d,'r') #ファイルを開く
sts = fpi.readlines() #ファイルを行別文字列の配列化
fpi.close()
i=1 #行番号
n=0 #ファイル内での発見行数
for st in sts: #一行ずつ処理
ss=str2unicode(st) #文字化け抑制の為UNICODE化
find_it=ss.find(s)
if not find_it==-1:
if n==0:
print u'\n■%s内を検索'% (d) # 初回だけ、ファイル名を表示
try:
print u'%6d %s【%s】%s' % (i,ss[:find_it]
,ss[find_it:find_it+len(s)],ss[find_it+len(s):]),
except:
display_err(local=locals(), title = u'\n\n#### EXCEPTION ERROR ####')
print '\a'
n=n+1
i=i+1
if n>0:
print u'計%5d行で見つかりました。\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~' % (n)
except:
display_err(local=locals(), title = u'\n\n#### EXCEPTION ERROR ####')
print '\a'
finally:
fpi.close()


######################################################################
######################################################################
## 以下は、mire.std汎用系関数モジュールに含まれる関数より抜粋添付 ##
## str2unicode() : 文字化け表示抑制の為のUNICODE化 ##
## display_err() : 発生ブロックの変数情報付例外処理 ##
## dsp_hex() : 文字列を16進数表示文字化け対策用 ##
######################################################################
######################################################################

## 文字列のunicode化を試みます ##
def str2unicode(s,charset='UTF-8',charsets=['Shift_JIS','EUC-JP','iso-2022-jp','UTF-8'],debug=0):
u"""
文字列のunicode化を試みます
"""
charsets.insert(0, charset)
for c in charsets:
try:
u = u'%s' % (unicode(s,c))
if debug>0 and not c==charset:
print u'指定のcharset"%s"でなく"%s"でunicode化しました。'
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

## エラー表示関数(コメント行文字化け対策+thread実行時のエラー情報表示の時系列混乱軽減) ##
def display_err(local, title = u'\n\n#### EXCEPTION ERROR ####'):
"""
##########################################################################################
#エラー表示関数(コメント行文字化け対策 + thread実行時のエラー情報表示の時系列混乱軽減) #
#USAGE: 第一引数に「locals()」を指定することで、処理時の変数値を表示します。 #
# waitingはthread実行時のエラー情報表示の時系列混乱軽減の為のWAIT処理です。 #
# waiting = 0 #
# def foo(): #
# global waiting #
# sleep(waiting) #
# try: #
# 処理 #
# waiting = 0 #
# except: #
# display_err(locals(),'<例外処理の場所を表すタイトル>')# locals()指定は必須 #
# waiting = 1 #
# foo() #
##########################################################################################
"""
import traceback
from time import sleep
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()
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

## コメント文字化け対策 ##
def dsp_hex(s):
from re import search
charsets=['UTF-8','Shift_JIS','EUC-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(): # Windowsにはcurses.ascii.isprint()が
sss=sss+ss # ないのでcurses/ascii.pyの中身を展開
else:
sss=sss+'\\'+hex(ord(ss))[2:].upper()
return sss


#################################################################################################
#  これより実行行

from sys import argv
ps = argv
if len(ps)==1:
print u'USAGE: python %s [<directory>] <sustr>' % (ps[0])
print u'  これは、使い捨てクラスのサンプルコードです。\n'
print u' 【機能説明】'
print u'  検索したいフォルダ(既定はカレント=実行ディレクトリ)を指定して、'
print u' 検索文字列を一つ指定することで、傘下ファイル上の完全一致する行を'
print u' 行番号付で表示します。尚、その際検索文字列は【】先頭分のみ囲んで'
print u' 表示します。\n'
print
print u' 【サンプルコードの特色】'
print u' 1. 指定ディレクトリ内ディレクトリもwalk()関数で再帰処理している'
print u' 2. ファイル内文字列は一行毎、既定の優先順に従いUNICODE化を試み、'
print u' 文字化けを抑制している'
print u' 3. 例外エラー発生時には、例外発生ブロックの変数情報も表示する'
print u' 4. '
print u''

print u''
elif len(ps)==2:
walk(path='.\\',s=ps[1])
elif len(ps)>=3:
walk(path=ps[1],s=ps[2])
posted by Mire at 14:55 | Comment(0) | TrackBack(0) | Pythonプログラミング | このブログの読者になる | 更新情報をチェックする

2009年10月18日

【PythonでCGI】CGIでの例外Error処理 〜 Web版BlogでHTMLタグ表示

 本日のお題は、CGIプログラムでのError処理である。前稿と同じソースの再掲載での説明では芸がないので、フローは前稿のそのままで、処理だけをHTMLタグの入ったソースコードをブログら掲載する時に必要な「< ⇒ &lt;」等を行なうもので行なわせて頂く。この機能自体は以前、コンソールプログラムとして紹介しているもので珍しくもないので先ずは簡単にその変換部分だけを説明させて頂く。前稿との差は、
  前稿の「SPAM投稿切刻み」処理部分


ss=p=''
for s in html_text: #unicodeはMultiBite文字も文字単位処理
if s=='\n' or s=='\r' or p=='\n' or p=='\r' or ss=='': #改行符号と頭の文字の
ss=ss+s #前後には空白文字を加えず
else: #そうでないなら
ss=ss+' '+s #空白文字を加える
p=s #直前の文字を代入
html_text=ss #html_textを置換え
が、
  今回の「HTMLタグ表示」処理部分


ss = html_text.replace(u'&',u'&amp;').replace(u'<',u'&lt;')\
.replace(u'>',u'&gt;').replace(u'&',u'&amp;') #最後の&の変換がミソ!!
html_text = ss
に変わった程度の差に過ぎない。また、以前の同機能のコンソールプログラムとの差は「.replace(u'&',u'&amp;')」が、HTML内に記述し表示させる為に追加になっていることにご注意頂きたい。蛇足ながら、Stringオブジェクトのメソッドとして定義されている「'<処理文字列>'.replace('<検索文字>','<置換文字>')」を使うと、元文書に対し、特定のキーワードを強調や色付表示させたい時に重宝する。そんなツールはテキスト欄へのキーワード入力を求め、
「'<本文>'.replace('.replace(u'&',u'&amp;')','<b>.replace(u'&',u'&amp;')&;lt;/b>')」の様に処理すれば、「.replace(u'&',u'&amp;')」が全て太字表示に置換出来る。普通なら正規表現で考えることが簡単に達成出来る訳だ。

 さて、これより本論のCGIでのError処理の説明に移る。Web版の「BlogでHTMLタグ表示」のソースコードは次の通り。
  /Web/cgi-bin/html4blog.py


#!c:/Python26/python
# -*- coding: UTF8 -*-
# ※ Windows の場合1行目はdummyです

u"""
#####################################################################
# HTML CODE_TEXT for BLOG 0.0.0 (C) Mire 2009-10-15 #
#####################################################################
 blogを持っていると
怒り^^;;を込めて、SPAM迷惑投稿を切り刻みます。コピペで上書きすれば、
内容はそのままでも目的のURL linkは全てはずれレンダリングも消えます。
USEGE:
1.SPAM迷惑投稿をテキスト欄に貼付けボタンをクリックして下さい。
2.別窓または別タブで1文字毎の半角スペース区切りテキストが表示。
3.このスペース区切テキストのCopy&PaseteでSPAM迷惑投稿を置換え
"""

__author__ = "Mire in Japan"
__version__ = '0.0.0'
__copyright__ = 'Copyright (c) 2009 Mire'
__license__ = 'GPL'
__url__ = 'http://pythonlife.seesaa.net/'

debug = 0 #debugレベル


########################################
## 当Tool専用関数の定義contents()のみ ##
########################################

def contents(debug=1):
u"""
コンテンツ「入出力フォーム」を表示します
"""
try:
from mire.htm import display_err, cgi_params #, tag_encode

print u'<table width="590px" style="background-color:#F0F0F0;"><tr>'
print u'<td><font size="-1"><b>HTMLのタグをそのまま表示するものに変換します。</b>\
</font></td></tr><tr><td align="center">',
prms=cgi_params() #> mire.htm.cgi_params(): CGI引数情報の辞書を返します


if debug > 1: #debugレベル2: CGI引数の名前と値を表示します。
print '<table><tr><td align="right"><pre align="left" style="text-align:center;\
pre-wrap; white-space: -moz-pre-wrap; white-space: -pre-wrap;\
white-space: -o-pre-wrap; word-wrap: break-word; ">'
for p in prms:
print '%s: %s' % (p,prms[p])
print '</pre></td></tr></table>'

if 'html_text' in prms: #cgi引数が存在したら、
html_text=unicode(prms['html_text']) # CGIの引数の値はunicode化しないと駄目の様だ
ss = html_text.replace(u'&',u'&amp;').replace(u'<',u'&lt;')\
.replace(u'>',u'&gt;').replace(u'&',u'&amp;') #最後の&の変換がミソ!!
html_text=ss #html_textを置換え

else: #引数がないなら
html_text='' #0長文字列

#コンテンツ本体: table,form textarea,inputタグで入出力画面を表示
print u'<table><form method="post" target="_blank"><tr>'
print u'<td><textarea cols="88" rows="20" style="font-size:8pt;" name="html_text"',
print u'mire_val="html4blog_text(html_text)">%s</textarea></td>' % (html_text)
print u'</tr><tr><td><pre>%s<pre></td>' % (html_text.replace(u'&amp;',u'&'))
print u'</tr><tr><td><center>',
print u'<input type="submit" style="font-size:8pt; width:567px">',
print u'</td></tr></form></table></center></td></tr></table>'


except: #vividな作成部分はhtm.display_err()
if debug > 0: #でdebug情報を取得。運用時はdebug=0
print '' #とし障害内容は表示させない。
display_err(locals()) #>mire.htm.display_err()#Web用の例外処理で障害切分
print '</span>'
else: #運用中のdebug message表示はセキュリティ上不適格。通常はdebug=0で以下のmessage
print '<pre>Error was occured.\nPlease, contact your system administrators.\n'
print 'You? Do Your Best!\n Test html4spam.py with debug=2. Good luck.</pre>'


########################################
## ##
## 処理実行開始位置 ##
## ##
########################################
try:
from mire.htm import display_err, html_header, html_footer
html_header(charset='UTF-8', title=u'HTML CODE_TEXT for BLOG 0.0.0 (C) 2009 Mire GPL license'
, body=['style="right-margin:0px; background-color:#808080;"'])
contents(debug=debug)
html_footer()

except:
if debug > 0:
display_err(locals()) #こちらは最後の抑え
else: #運用中のdebug message表示はセキュリティ上不適格。通常はdebug=0で以下のmessage
print 'Content-type: text/html\n\n<html>\n<head></head><body>'
print '<pre>Error was occured.\nPlease, contact your system administrators.\n'
print 'You? Do Your Best!\n Test html4spam.py with debug=2.\n\nGood luck.</pre>'
print '</pre></body></html>'


 昔々、例外Error処理がなかった当時は、全ての条件を頭に置いてif分での条件分岐を行ないエラー処理を書いていたものだ。当初、この「try:〜;except:〜;」は、いい加減な詰めの甘い仕組みに見えてしまい、使う気にならなかったものだ。でも、それはシステム言語や使用する開発ツールが完璧であり且つ、関わるシステム作成者全員間で完全無欠なコミュニケーションが取れる場合に言えることで、現実の開発環境ではありえないことだ。
 従って、例外Error処理を使うときの発想は、逆に想定外のエラーは起きて当り前、起きた場合の無難な処理を予め書いて置き、「1.システム作成中は効率的なDEBUG」を、「2.実用運転時には、次善の策として、利用者側に障害アクションをとってもらうメッセージや、システム管理者への通知、そして応急処理を組んで置くことに使うもの、そう言う逆転の発想で使うものと考える。

 従って、この説明用のサンプルソースコードの様に最低でも、先ずは、実行処理全体について、そして、作成中のvividな部分には必ず、「try:〜;except:〜;」で例外処理を入れて置く必要がある。そして、同時に、特にWebシステムの場合利用者は不特定多数となることを前提にセキュリティ上の配慮も加味し、debugレベルにより、表示する内容を抑制しする対処をすることになる。

 このサンプルでは、運用での障害発生時には、いずれも次の様なものが表示される様になっている。
Error was occured.
Please, contact your system administrators.

You? Do Your Best!
Test html4spam.py with debug=2. Good luck.
文字コード処理の不具合も考慮して敢えて英語のみのメッセージにしている。

 開発修正時のDEBUG時には、mire.htm.display_err()の定義に従い例外Errorの内容とローカル変数とその値が表示される。前稿と重複するが定義は次の通りである。
  mire.htm.py 内の display_err()関数


## エラー表示関数(コメント行文字化け対策+thread実行時のエラー情報表示の時系列混乱軽減) ##
def display_err(self,local, title = '\n\n#### EXCEPTION ERROR ####'):
"""
##########################################################################################
#エラー表示関数(コメント行文字化け対策 + thread実行時のエラー情報表示の時系列混乱軽減) #
#USAGE: 第一引数に「locals()」を指定することで、処理時の変数値を表示します。 #
# waitingはthread実行時のエラー情報表示の時系列混乱軽減の為のWAIT処理です。 #
# waiting = 0 #
# def foo(): #
# global waiting #
# sleep(waiting) #
# try: #
# 処理 #
# waiting = 0 #
# except: #
# display_err(locals(),'<例外処理の場所を表すタイトル>')# locals()指定は必須 #
# waiting = 1 #
# foo() #
##########################################################################################
"""
import traceback
from time import sleep
print('Content-type: text/html\n\n<html>\n<head>')
print('<body><pre>')
if local is None:
local =locals()
global waiting
frame0 = '\n*******************************************************************************'
frame1 = '*******************************************************************************\n\n'
errs = [frame0, title]
errs.append('%s' % (self.dsp_hex(traceback.format_exc()))) #> dsp_hex(s): コメント文字化け対策
sleep(waiting)
ls = local
keys = list(ls.keys())
errs.append('')
for key in keys: # display_err()関係の変数の表示を省く
# if not key in ['v', 'ls', 'keys', 'key', 'errs', 'txt', 'er', 'sleep']:
er = '* %s: %s' % (repr(key).ljust(24),repr(ls[key]))
errs.append(er)
errs.append(frame1)
txt = '\n'.join(errs)
print(txt)
print('</pre></body></html>')

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

ちょいと、長くなって息切れしたので、後ほど追記。(ソースコードも一部差替えます。)続きを読む
タグ:CGI Python
posted by Mire at 01:51 | Comment(0) | TrackBack(0) | Pythonプログラミング | このブログの読者になる | 更新情報をチェックする

2009年10月15日

【PythonのHELP】Pythonにはhelp()関数有ったんだぁ

 そろそろ、自前のソースコードが貯まって来て、良く似た関数や処理を繰返し書くことが増えて来た。まあ、元々が、Pythonリハビリ目的なので関数のモジュール化には消極的だったが、PyPRecでは重箱の角をつついていると元居た角を忘れている自分が居て、そんな自分に閉口している所だ。

 そこで、やはり、自作の汎用関数は、Lib/sitepackages/ に放り込んで管理しようとやっと思うに至った。だが、以前やっていた様に同じファイルに味噌も糞も放り込んでいては、いずれまた、似た様な関数が散在し統一性がなくなるので、今度はフォルダ内にカテゴリーで分類して管理して行こうと思う。同時に、こういった関数群の使い方を文書で残し管理者である自分が最初の迷子にならない様にと思い、__doc__をこまめに書込む様にした。
 ただ、この__doc__はパッケージ(フォルダ)、モジュール(ファイル)、関数とクラス別、そしてクラスのメソッドに個別に書込むことが可能だ。そんな分散記載した__doc__を寄せ集めて簡易のマニュアルにしてしまえば、こんな自分も迷子にならなくて済むと思い、元々、空ファイルでしか活用してこなかったパッケージの__init__.pyで
Lib/site-packages/mire/__init__.py


<前略>
import std
__doc__=__doc__+'\n'+std.__name__+std.__doc__
import htm
__doc__=__doc__+'\n'+htm.__name__+htm.__doc__
import html
__doc__=__doc__+'\n'+html.__name__+html.__doc__
import std
<後略>
<注記: __man__等の自前global変数でするならprint __mam__で活用可能。自作help作成ならこちらかも>
等と書込み__doc__を表示させ遊んでいて、こんなの誰でもあったらいいなって思うよなと思っていたら、help(obj)という組込関数があることを発見!。 Pythonとのお付き合いは1.xの頃からなので見逃していた。(未だに、「Pythonテクニカルリファレンス」(ピアソンE.刊)2000-08-25を使っているくらいなので、それ以降の変化について行っていない^^;;)

 使い方は、pythonを起動して必要なパッケージモジュールをimportして、pythonの名前空間に認識させてからhelp(パッケージ名)とするだけだ。
Microsoft Windows [Version 6.0.6001]
Copyright (c) 2006 Microsoft Corporation. All rights reserved.

C:\Users\SysAdm>c:\Python26\python.exe
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 mire
>>> help(mire)
Help on package mire:

NAME
mire

FILE
c:\python26\lib\site-packages\mire\__init__.py

DESCRIPTION
######################################################################
# mire module
######################################################################
##
## std 標準入出力+例外処理
## html関数群 : htm
## htmlクラス : html
##
##


mire.std
######################################################################
#標準入出力関係の関数群
#
#01. str2unicode(s,charset) :文字列のunicode化を試みます
#02. file2utf8list(file_name :fileを読込みunicodeのlistを返します
# ,stdin_charset) :


mire.htm
######################################################################
#01. html_header(charset, :html文書の開始タグ一式を標準出力します
# ,title,base :
# ,refresh :
# ,cache,robot:
# ,css,style :
# ,js,author :
# ,generator :
# ,keywords :
# ,description:
# ,body) :
#02. def 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. cgi_params() : CGI引数を辞書化し返します
#08. param_lst(omit_params : 引数とその値をGET形式'引数名=値'文字列listを
# ,params_dic): 返す「url+'?'+'&'.join()」で活用
#09. tag_encode(lines) : タグ文字のままHTML表示可能な文字列listに変換
#10.
#


mire.html
######################################################################
# This code is tried to change my html display functions to class.
#html表示関係の関数をまとめてclass化して試たものです
#USAGE:
#h = html()
#h.contents = [u'通常、ここには別途作成した
', u'コンテンツをリストで指定します。']
#h.style = ['fine{background-color:#F0F0F0; font-size:10pt; line-height:12pt;}'
# , 'ss{font-size:6pt; line-height:6pt;}'
# , '.td{font-size:10pt; line-height:6pt;}']
#h.body = ['class="fine"']
#h.display()
#
#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 : スタイルシートファイルをリストで指定 []:利用しない
#js : JavaScriptファイル  をリストで指定 []:利用しない
#author : 執筆者 文字列指定
#generator : 作成エディタ 文字列指定
#keywords : 検索エンジン用キーワードをリストで指定
#description : 検索エンジン用文献説明をリストで指定
#body : 文書全体への書式等をリストで指定
#contents=[]
#

PACKAGE CONTENTS
htm
html
mng
net
std

DATA
version = '2.6.2 (r262:71605, Apr 14 2009, 22:40:02) [MSC v.1500 32 bi...


>>>
>>>
>>>
という感じだ。

 ただ、残念なことにこのhelp()君、日本語は余りお得意ではない様で
安定しないのが欠点の様だ。仕方がないので、これ迄、複数文字コード利用にこだわり入れてなかった「sitecustomize.py」を設定して試て初めて得られたのが上の結果だ。

 尚、当然ながら、python3では、文字列をunicodeが則る仕組みなのでなんらの苦も無く以下の様な表示が出来る。
>>> help(mire.html.html)
Help on class html in module mire.html:

class html(builtins.object)
| ######################################################################
| #class html()html表示関係の関数をまとめてclass化したものです
| #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 : スタイルシートファイルをリストで指定 []:利用しない
| #js : JavaScriptファイル  をリストで指定 []:利用しない
| #author : 執筆者 文字列指定
| #generator : 作成エディタ 文字列指定
| #keywords : 検索エンジン用キーワードをリストで指定
| #description : 検索エンジン用文献説明をリストで指定
| #body : 文書全体への書式等をリストで指定
| #contents=[]
|
| Methods defined here:
|
| __init__(self)
|
| content(self)
|
| display(self)
|
| display_err(self, local, title='\n\n#### EXCEPTION ERROR ####')
| ##########################################################################################
| #エラー表示関数(コメント行文字化け対策 + thread実行時のエラー情報表示の時系列混乱軽減) #
| #USAGE: 第一引数に「locals()」を指定することで、処理時の変数値を表示します。 #
| # waitingはthread実行時のエラー情報表示の時系列混乱軽減の為のWAIT処理です。 #
| # waiting = 0 #
| # def foo(): #
| # global waiting #
| # sleep(waiting) #
| # try: #
| # 処理 #
| # waiting = 0 #
| # except: #
| # display_err(locals(),'<例外処理の場所を表すタイトル>')# locals()指定は必須 #
| # waiting = 1 #
| # foo() #
| ##########################################################################################
|
| dsp_hex(self, s)
|
| footer(self)
|
| header(self)
|
| ----------------------------------------------------------------------
| Data descriptors defined here:
|
| __dict__
| dictionary for instance variables (if defined)
|
| __weakref__
| list of weak references to the object (if defined)


 よかったら、皆さんも__doc__を書きませんか?
各階層の頭に最初に書く文字列が__doc__になるので通常
  〜.pyの参考例:


"""
ここがこのファイルモジュールの__doc__になる
"""
def func():
"""
ここはfunc.__doc__になる
"""
<関数定義>

class class_a(object):
"""
ここはclass_a.__doc__になる
"""
def method1():
""""
ここはclass_a.method1.__doc__になる

""""
<method定義>
タグ:Python
posted by Mire at 14:16 | Comment(0) | TrackBack(0) | Pythonプログラミング | このブログの読者になる | 更新情報をチェックする

2009年10月12日

【PythonのERROR情報】traceback を使った display_err()関数

 余程に完全無欠な人で完璧なソースコードを一発で書く様でない限り、ソフトウエア作成中のエラー情報の活用は不可欠であろう。Pythonでは通常、何もしないでも、継続不能な例外エラーが発生すれば、その例外タイプと原因、そしてその発生した行番号とソース行が表示される。
 しかし、例外エラー発生の原因を絞り込むには、その示す位置に「try:〜;except:〜;」という例外処理を加え、その情報を得たい場合には、tracebackやsys.exc_info()、そしてlocals()等を活用することになる。一般的には、不具合に出会う度に手書きで逐次対処していることが多いだろうが、今回、その為の関数「display_err()」を作成して試た。

 tracebackモジュールでは、thread freeでなかったり、循環処理の可能性がある等の理由で成果物への埋め込みは必ずしも推奨しないが、debug時には、障害発生箇所と原因を掴めるので欠かせない。この関数では、traceback objoctの返す情報をtraceback..format_exc()で文字列として取得し、表示文字コード処理で日本語コメントの文字化けに対処した他、thread実行時のエラー情報表示の時系列混乱を防ぐ為にwaitingというglobal変数を使い乱れを軽減した。

  TestCode/display_err_by_traceback_test.py


#!c:/Python26/python.exe
# -*- coding: UTF-8 -*-
# ※ Windows の場合1行目はdummyです。

waiting = 0 #  これはdisplay_err() 実行時にWAITを置き、
# thread実行時のエラー情報表示の時系列混乱を
# 防ぐ為のglobal変数

## エラー表示関数(コメント行文字化け対策+thread実行時のエラー情報表示の時系列混乱軽減) ##
def display_err(local, title = u'\n\n#### EXCEPTION ERROR ####'):
"""
##########################################################################################
#エラー表示関数(コメント行文字化け対策 + thread実行時のエラー情報表示の時系列混乱軽減) #
#USAGE: 第一引数に「locals()」を指定することで、処理時の変数値を表示します。 #
# waitingはthread実行時のエラー情報表示の時系列混乱軽減の為のWAIT処理です。 #
# waiting = 0 #
# def foo(): #
# global waiting #
# sleep(waiting) #
# try: #
# 処理 #
# waiting = 0 #
# except: #
# display_err(locals(),'<例外処理の場所を表すタイトル>')# locals()指定は必須 #
# waiting = 1 #
# foo() #
##########################################################################################
"""
import traceback
from time import sleep
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()
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

## コメント文字化け対策 ##
def dsp_hex(s):
from re import search
charsets=['UTF-8','Shift_JIS','EUC-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(): # Windowsにはcurses.ascii.isprint()が
sss=sss+ss # ないのでcurses/ascii.pyの中身を展開
else:
sss=sss+'\\'+hex(ord(ss))[2:].upper()
return sss



###################
#### TEST CODE ####
###################

## threadで実行に組込んだ関数 ##
def test_func():
"""
datetimeの演算は引算のみ、足算は出来ないぞ
"""
from time import sleep
from datetime import datetime

global waiting
sleep(waiting)
try:
a=datetime.now()
b=datetime.now()
sleep(waiting)
c=datetime.now()
a=b-c
d=c+b # datetimeを足したいの I what to add datetime object's.
print a
waiting = 0
except:
display_err(locals(),title = u'### main()内例外発生時の情報 ###') # locals()指定は必須
waiting = 1
raise


from thread import start_new_thread
from time import sleep
try:
while 1:
try:
start_new_thread(test_func,())
#waiting = 0
except:
waiting = 1
display_err(locals(), title = u'### thread実行loopでの例外発生時の情報 ###')
break
sleep(waiting)
#print waiting
except:
display_err(locals(), title = u'### 終了時の情報 ###') # 終了時の情報

 以下が、上記テストコードの実行結果だ。連続LOOPの為、当然、Ctrl+Cで停止して取得している。残念ながら、Windowsのコマンドプロンプトでの実行では、「Unhandled exception in thread started by <function test_func at 0x01C6CE30>」の発生内容がECHOされ文字化けしたtraceback情報が表示されている。
D:\TestCode>python display_err_by_traceback_test.py

*******************************************************************************
### main()内例外発生時の情報 ###
Traceback (most recent call last):
File "traceback_exc_info_and_locals_within_thread5.py", line 90, in test_func
d=c+b # datetimeを足したいの I what to add datetime object's.
TypeError: unsupported operand type(s) for +: 'datetime.datetime' and 'datetime.datetime'


* 'a' : datetime.timedelta(0)
* 'c' : datetime.datetime(2009, 10, 12, 6, 16, 36, 765000)
* 'b' : datetime.datetime(2009, 10, 12, 6, 16, 36, 765000)
* 'sleep' : <built-in function sleep>
* 'datetime' : <type 'datetime.datetime'>
*******************************************************************************

<中略: thread実行の為出力順が途中乱れる>

Unhandled exception in thread started by <function test_func at 0x01C6CE30>
Traceback (most recent call last):
File "traceback_exc_info_and_locals_within_thread5.py", line 90, in test_func
d=c+b # datetime繧定カウ縺励◆縺・・ I what to add datetime object's.
TypeError: unsupported operand type(s) for +: 'datetime.datetime' and 'datetime.datetime'

*******************************************************************************
### main()内例外発生時の情報 ###
Traceback (most recent call last):
File "traceback_exc_info_and_locals_within_thread5.py", line 90, in test_func
d=c+b # datetimeを足したいの I what to add datetime object's.
TypeError: unsupported operand type(s) for +: 'datetime.datetime' and 'datetime.datetime'


* 'a' : datetime.timedelta(-1, 86399)
* 'c' : datetime.datetime(2009, 10, 12, 6, 21, 0, 292000)
* 'b' : datetime.datetime(2009, 10, 12, 6, 20, 59, 292000)
* 'sleep' : <built-in function sleep>
* 'datetime' : <type 'datetime.datetime'>
*******************************************************************************


Unhandled exception in thread started by <function test_func at 0x01C6CE30>
Traceback (most recent call last):
File "traceback_exc_info_and_locals_within_thread5.py", line 90, in test_func
d=c+b # datetime繧定カウ縺励◆縺・・ I what to add datetime object's.
TypeError: unsupported operand type(s) for +: 'datetime.datetime' and 'datetime.datetime'

*******************************************************************************
### 終了時の情報 ###
Traceback (most recent call last):
File "traceback_exc_info_and_locals_within_thread5.py", line 110, in <module>
sleep(waiting)
KeyboardInterrupt


* 'test_func' : <function test_func at 0x01C6CE30>
* '__builtins__' : <module '__builtin__' (built-in)>
* 'display_err' : <function display_err at 0x01C6CEB0>
* '__file__' : 'traceback_exc_info_and_locals_within_thread5.py'
* 'start_new_thread' : <built-in function start_new_thread>
* '__package__' : None
* 'dsp_hex' : <function dsp_hex at 0x01C6CE70>
* 'waiting' : 1
* 'sleep' : <built-in function sleep>
* '__name__' : '__main__'
* '__doc__' : None
*******************************************************************************

D:\TestCode>
タグ:Python
posted by Mire at 07:24 | Comment(0) | TrackBack(0) | Pythonプログラミング | このブログの読者になる | 更新情報をチェックする

2009年10月09日

【PythonでClass】気の迷いで^^;; CGI HTML出力系関数をclass化

 ちょいとした気の迷いから、日頃すっきりし過ぎて使い方が判らなくなりそうなのでやっていないクラス作成をやって試た。

 日頃は、結構、場当たり発想によるしゃかしゃかコーディング派なので発想を大切にした関数の書き殴りしかしないのだが、たまにはクラスも書いていないと、人の作ったモジュールを使う時にかったるい想いをするので、少しは頭に強く植え付けて置くのも悪くないかもと少しだけ時間を使ってトレーニングした。ついでに自分で理解したよと言う想いを持つ為に何かをクラス化しよう思ったが、あまり関連付けの強い関数群が思い当たらない。仕方がないので、以前書いた「html_header()」と「html_footer()」を一括記載するだけのメリットしかないクラス「html」を作成することにした。このクラスでは以前の関数で端折っていたstyleタグの埋め込みやコンテンツをリストで渡せる様にしてまとめた。結果は次の通り。
 TestCode/html.py


#!c:/Python26/python
# -*- coding: UTF8 -*-
# ※ Windows の場合1行目はdummyです。

## HTML文書のCGI出力 ##
class html(object):
"""
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 : スタイルシートファイルをリストで指定 []:利用しない
js : JavaScriptファイル  をリストで指定 []:利用しない
author : 執筆者 文字列指定
generator : 作成エディタ 文字列指定
keywords : 検索エンジン用キーワードをリストで指定
description : 検索エンジン用文献説明をリストで指定
body : 文書全体への書式等をリストで指定
contents=[]
"""

def __init__(self):
self.charset = 'UTF-8' #文字コード 'Shift_JIS','UTF-8','EUC-JP'等
self.title = '' #HTMLヘッダタイトル 文字列指定
self.base = '' #相対URL表記のベースURL 文字列指定
#無指定はcgiファイルの設置位置、'/'はサーバroot
self.refresh = [] #指定秒後に自頁または指定頁へジャンプ
# [秒数] or [秒数,'ジャンプ先URL']
self.cache = 0 #cache期限を整数秒で指定 -1:期限なし,0:cacheなし
self.robot = 0 #自動検索エンジンに表明 禁止:-1,了解:1
self.css = ['/css/default.css'] #スタイルシートファイルをlistで指定[]:利用しない
self.style = [] #個別のスタイルをリストで指定 []:利用しない
self.js = ['/js/default.js'] #JavaScriptファイルをリストで指定 []:利用しない
self.author = '' #執筆者 文字列指定
self.generator = "Mire's Python CGI" #作成エディタ 文字列指定
self.keywords = [] #検索エンジン用キーワードをリストで指定
self.description = [] #検索エンジン用文献説明をリストで指定
self.body = [] #文書全体への書式等をリストで指定
self.contents = [] #文書コンテンツ本体をリストで指定

def header(self):
#, charset='Shift_JIS',title='',base='',refresh=[],cache=0,robot=0
# ,css=['/css/default.css'],js=['/js/default.js'],author=''
# ,generator="Mire's Python CGI",keywords=[],description=[]):
from time import time,gmtime,strftime
print 'Content-type: text/html\n\n<html>\n<head>'
if not self.base=='':
print '<base href="%s" target="_self">' % (self.base)
print '<meta http-equiv="Content-Type" content="text/html; charset=%s" />' % (self.charset)
if len(self.refresh)>0: # self.refresh秒のみが指定されたら、その間だけcasheする。
if len(self.refresh)==1 and self.cashe>self.refresh[0]:
self.cache=self.refresh[0]
if len(self.refresh)>1:
url='; url=%s' % (self.refresh[1])
else:
url=''
print '<meta http-equiv="refresh" content="%d%s">' % (self.reflesh[0],url)
print '<title >%s</title>' % (self.title) # ページタイトル
if self.cache==0:
print '<meta http-equiv="pragma" content="no-cache">'
elif self.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()+self.cache))
if len(self.author):
print '<meta name="author" content="%s">' % (self.author)
if not self.generator=='':
print '<meta name="generator" content="%s">' % (self.generator)
if len(self.description)>0: # description文書説明
print '<meta name="description" content="%s" />' % (','.join(self.description.sort()))
if len(self.keywords)>0: # キーワード
print '<meta name="keywords" content="%s" />' % (','.join(self.keywords.sort()))
if self.robot==1:
print '<meta name="robots" content="all">'
elif self.robot==-1:
print '<meta name="robots" content="noindex,nofollow">'
if len(self.js)>0: # JavaScript活用
print '<meta http-equiv="Content-Script-Type" content="text/javascript" />'
if len(self.css)>0: # StyleSheet活用
print '<meta http-equiv="Content-Style-Type" content="text/css" />'
for c in self.css: # css スタイルシートファイルリンク先 #
print '<link rel="stylesheet" href="%s" type="text/css" />' % (c)
for j in self.js: # javascriptファイルリンク先 #
print '<script type="text/javascript" src="%s"></script>' % (j)
print '<style type="text/css">%s</style>' % (' '.join(self.style))
print '</head>'
print '<body %s>' % (' '.join(self.body))

def footer(self):
print '</body>\n</html>'

def content(self):
for c in self.contents:
print c.rstrip()

## HTML文書設定を加えたコンテンツを順番通り標準出力 ##
def display(self):
self.header()
self.content()
self.footer()


if __name__ == "__main__":
h = html()
h.contents = [u'通常、ここには別途作成した<br/>',u'コンテンツをリストで指定します。']
h.style = ['fine{background-color:#F0F0F0; font-size:10pt; line-height:12pt;}'
, 'ss{font-size:6pt; line-height:6pt;}'
, '.td{font-size:10pt; line-height:6pt;}']
h.body = ['class="fine"']
h.display()
print vars()


 後、cgi引数関係何ぞも入れると良いのかもなと思うが取敢えず目的は果たしたので本日はここ迄とする。でも、何か見通しが悪い様な気がしてならないのは気のせいか。まだクラスは好きになれない。

 取敢えず関数のクラス化で必要なことをまとめると次の内容となる。
    単純な関数をクラス化する。
    def prn(a='Hello, World!!'):
    print a





    a='aaaa'
    prn(a=a)

    class f(object):
    def __init__(self):
    self.a = 'Hello, World!!'
    def prn(self):
    print self.a

    x=f()
    x.a ='aaaa'
    x.prn()
    何処が便利なんだと言われそうな、このコンパクトなクラス化メリットマイナスの例で説明する。

    1. 関数の引数kの既定値はdef __init__(self):内にself.変数名=〜として設定することになる。
    2. 利用時には先ず、クラスの実体、インスタンスをインスタンス名 = クラス名()で生成する手続が必要となる。
    3. 利用時の引数の値はインスタンス.変数名=〜として設定することになる。
    4. 関数の動作を行なうメソッドとなったprnもインスタンス.メソッド名を記述し実行させることになる。


 便利かどうかは用途によるので、クラスを使うかどうかは関数を書き貯めてから、関係の深いもの、重複しているものをまとめるときに使うものと考えて使った方が、この場当たり発想によるしゃかしゃかコーディング派で、発想を大切にした関数の書き殴りしかしない、システムは体力だという向きの人間には良い様に思う。

 当方の場合最初から見通しが利く程の人間でもないので、最初から縛りをつけても、その思想が返って禍することも多いだろう。自らの課題に対応する発想を先ずは大切にし今後も進めて行く。私は強情だ。^^;;続きを読む
posted by Mire at 21:30 | Comment(0) | TrackBack(0) | Pythonプログラミング | このブログの読者になる | 更新情報をチェックする

2009年09月29日

What's New in Python 2.7 〜 Python3移行に向けて

 Python3への移行手順が気になって調べているとPython 2.7が出るとの記事「What’s New in Python 2.7」(September 27, 2009)に偶然行き着いた。

 詳しくは、今後濃い方々の正確な解説を待ちたいが、英語を頼らずコードを中心に見て試ると、Python3への移行で一番気になっていた文字列の新フォーマットが使える様になる様だ。勿論、自分にとっては1年程寝てた訳なので個人的メンテしないといけないコードは僅かなものなので利用している主要モジュールの対応が終わってから取組んだ方が効率的なのかもしれないので、2.7でstr.format()に触れる意味がどの程度あるのかというとないのかもしれない。ただ、当方が利用している主要モジュール移行に時間がかかった場合にはPython2.7の活用は有効な選択肢となるだろう。

 Python3はPythonで始めての互換性取り払い言語としての完成度を高めたものという。自分程度のユーザにはその有難味は判らないけれど、これまで互換性にこだわり古いものも新しいものもごっちゃになっていたことも事実だろうし、よく使うprint文がprint()関数に替わることには括弧書きが増える分は嫌だが、言語としてフロー以外の文が未だに存在するのもBASIC的というか発展性の低い未分化の簡易スクリプトぽいので変わっても良いのかもと思う。
 また、str.format()自体は文字列への組込関数だが、従来の'%s'といった書式文字はutf-8に移行すると全角文字の文字幅が考慮されないので、「全角=半角の2倍幅」という暗黙の前提で作ている所は全面的に書き直すことになるので、2.7finalが出たらやってみようかなと思う。


 尚、Python3移行で参考になる資料としては

What's New In Python 3.0」の和訳「What'sNewInPython3.0」が一番判り易い様に思う。Guidoさんの公式文書の訳なので当り前か。

 で、一般的な手順は、次の通りかと思う。
  1. 先ずはPython2.6に移行+ソースコードのUTF-8化
  2. python -3 〜.pyで手持ちのソースを実行し、Python3.0非対応箇所のメッセージをコンソール上でチェック
  3. 取敢えずは2.6迄で対応しているものに置換
  4. Python2.7移行対応しているものはその行に後で検索可能な様にコメントを加えて置く
  5. 気が向いたら、ときどき 「Python26/Tools/Scripts/」にある2to3.pyでPython3コードへ変換しPython3上でテストして試る。
  6. 利用しているモジュールのPython3対応情報に注意して移行が可能になったものから、移行して行く

 python -3 〜.py を早速試してみたが、辞書で多用している「if dic.has_key(key):」を「if key in dic:」に置換ること等、数年前のPythonでも利用可能で、後方互換をとる必要がないものを中心に新規作成分から置換え開始するつもりだ。
 また、この「if dic.has_key(key):」の「if key in dic:」への置換程度なら、ちょいとpythonコードを書けば変換出来るので、必要によりそんなものも作って対応して行こうと思う。
タグ:python3 Python
posted by Mire at 01:12 | Comment(0) | TrackBack(0) | Pythonプログラミング | このブログの読者になる | 更新情報をチェックする

2009年09月28日

__import__() でpythonモジュール選択 pysnmp4の__init__.py

 前稿ではpysnmp4をインストールしたが、実を言うとこれにより、当方のPCのPythpn2.6では、これ迄紹介して来たpysnmp2.0.9を使ったプログラムが動作しなくなるのである。
 インストールされている。「Lib/site-packages/pysnmp」を見ると行儀良くv4フォルダがあり、その中にpysnmp4の実体は収まっているので、そのままpysnmp2.0.9も共存して動くと思っていたのだが甘かった。

 このpysnmp4のモジュール切替えの仕組みは面白そうなので、出来れば同じ様にpysnmp2.0.9をv2フォルダに収めて切替え出来ればと思い、その__init__.pyを調べてみた。
"""Various components of SNMP applications"""
import os
import sys
import string

def switchApiVersion(subPkg):
pkg = os.path.split(__path__[0])[-1]
newMod = __import__(subPkg, globals(), locals())
realPkg = '_real_' + pkg
if sys.modules.has_key(realPkg):
sys.modules[pkg] = sys.modules[realPkg]
sys.modules[realPkg] = sys.modules[pkg]
sys.modules[pkg] = newMod

def __isSubPackage(subDir):
if subDir and subDir[0] == 'v' and subDir[1] in string.digits \
and len(subDir) == 2:
return 1

if os.environ.has_key('PYSNMP_API_VERSION'):
v = os.environ['PYSNMP_API_VERSION']
if v:
switchApiVersion(v) # do not load any API
else:
subDirs = filter(__isSubPackage, os.listdir(__path__[0]))
subDirs.sort();
switchApiVersion(subDirs[-1]) # take the most recent version

 すると、pysnmp4の_init_.pyでは、PCの環境変数として登録する「PYSNMP_API_VERSION」の値により導入モジュール選択を切替える仕組みとなっていた。「「__init__.py」の8行目にある「__import__(subPkg, globals(), locals())」の「subPkg」の値が「v2」になれば、v2以下のフォルダ内のコードをpysnmpのモジュールとしてimportすることになる様だ。

 そこで、早速実験開始。

・ コントロールパネルのシステムで環境変数「PYSNMP_API_VERSION」を新規登録し値に「v2」をセット
・ 「Lib/site-packages/pysnmp/」に「v2」フォルダを作成し、その中に、元から「Lib/site-packages/pysnmp/」直下にあるpysnmp2.0.9のモジュールファイルをコピーすると共に、その中に空ファイルの「__init__.py」を新規作成。
・ 新たにコマンドプロンプトを起動し、「python 〜.py」でテスト。結果OK!!

 只、このpysnmp4の「__init__.py」の方法では環境変数の切替でモジュールが切替る仕組みなので、固定的に存在するツールコードに対する対応としては適切とは言い辛いので、実際の運用では、ここで作った「v2」フォルダを「pysnmp2」として「Lib/site-packages/」にコピーして、これ迄書いて来たpysnmp2.0.9利用のソースコードのimportモジュール名をpysnmp2と置換えた方が現実的だろう。

 尚、PyPRecについては、余り色々とインストールモジュールを指定したくないので、当面は2.0.9をベースに進めて行くつもりだ。
posted by Mire at 17:21 | Comment(0) | TrackBack(0) | Pythonプログラミング | このブログの読者になる | 更新情報をチェックする

2009年09月19日

【PythonでCGI】CGI引数情報とそのURL表記化

 本日は前稿の予告通り、CGIで渡されたパラメータ=引数情報の取り扱いを標準化するものである。先ずは、cgi_params()は大したことはしない関数だが、引数情報を辞書型に収めたことで引数情報を利用継続したインタラクティブな頁作りを容易にする。その活用法の一つとしてparam_lst()関数を併せて示して置く。

 CGIプログラムには<FORM>タグが必須と思っている方が多いと思うが、CGIプログラムのURLの後に「?引数1=値1&引数2=値2&引数n=値n」を付けるだけで、そのcgiに引数を与えることが出来る。この個別の引数文字列「引数n=値n」のリストを作成するのがparam_lst()だ。引数情報を利用継続したインタラクティブな頁作りでは、各URLリンクで新たな引数を追加したり、既存の引数を変更することになるので、param_lst()には、omit_paramsにその引数名をリストで渡し省くことが出来る様にしている。
param_lst()を活用したurl文字列生成次の様記述する。

print '?NEW1=ABC&OLD1=XXX&'+'&'.join(param_lst(omit_params=['OLD1'],params_dic=cgi_params()))

?NEW1=ABC&OLD1=XXX&OLD2=YYY&OLDn=ZZZ


## CGI引数の辞書化 ##
def 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
"""
import cgi
form = cgi.FieldStorage()
params= {}
keys = form.keys()
for key in keys:
params[key]=form[key].value
return params


## 引数文字列リスト生成 ##
def param_lst(omit_params=[],params_dic={}):
"""
## 引数文字列リスト生成 ##
引継ぎたい既存の引数とその値を「引数名=値」の形の文字列リストを返す
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


 たった4つの関数だが、これだけ準備できていたら単純なCGIによる
インタラクティブな頁作成は楽に出来る筈だ。実際、当方のPyPRecの情報閲覧用のPyPDspでは、これを使いメモリやCPU使用率等で表示順を切替えを可能にしている。続きを読む
タグ:CGI Python
posted by Mire at 23:22 | Comment(0) | Pythonプログラミング | このブログの読者になる | 更新情報をチェックする

2009年09月18日

【PythonでCGI】 html_header()で簡単作成

 Pythonに限らず、プログラム言語でCGIを書く場合にはHTMLの規格に従った入出力を記述する必要がある。その内容は定型的なものなので予め関数化しておけば、簡単かつケアレスミスによるトラブルなしに作成することが出来る。以前からこの手のものは作り使っていたが、多少成行きで適当に拡張したり変に類似のものを抱えていたりとごちゃごちゃしていたので今回、以前の分は見ずに一から作り直して試みた。

 本日は、その内のHTMLのheaderタグ周りをまとめた「html_header()」と名付けた関数を紹介する。html_header()は既定値を設定しているので引数なしでも使えるが、下の例の様にhtml_footer()とのセットで利用する。この2つの関数だけではまだ、静的なHTMLをpythonで表示するだけで面白みはないがcgi頁を作るときに毎回記述することになるものをカプセル化したものだ。html_headerの引数についてはソース内の説明の通りなのでそちらを参照の上活用頂きたい。
 TestCode/html_header_footer_test.py


#!c:/Python26/python.exe
# -*- coding: Shift_JIS -*-
# ※ Windows の場合1行目はdummyです。

def html_header(charset='Shift_JIS',title='',base='',refresh=[],cache=0,robot=0
,css=['/css/default.css'],js=['/js/default.js'],author=''
,generator="Mire's Python CGI",keywords=[],description=[]):
"""
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 : スタイルシートファイルをリストで指定 []:利用しない
js : JavaScriptファイル  をリストで指定 []:利用しない
author : 執筆者 文字列指定
generator : 作成エディタ 文字列指定
keywords : 検索エンジン用キーワードをリストで指定
description : 検索エンジン用文献説明をリストで指定
"""
from time import time,gmtime,strftime
print 'Content-type: text/html\n\n<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 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)
print '<style type="text/css">ss{font-size:6pt; line-height:6pt;} .td{font-size:10pt; line-height:6pt;}</style>'
print '</head>'
print '<body>'

def html_footer():
print '</body>\n</html>'

html_header()
#ここにコンテンツをprint文で記述
print 'html_header()でのテスト表示'
html_footer()
尚、この場合bodyのスタイルはスタイルシート側で行なうことが前提。利用する場合には、エディタにコピペして、文字コードShift_JIS、改行LFで保存して使って使います。今回は敷居を下げShift_JIS記述用としている。

 尚、次回はコンテンツ部分を動的に変化させる為に必要なパラメータの取得と生成を扱う予定だ。続きを読む
タグ:CGI Python
posted by Mire at 07:28 | Comment(1) | Pythonプログラミング | このブログの読者になる | 更新情報をチェックする

2009年09月16日

HTML4BLOG.py blog本文内HTMLタグ表示用ENCODE

 自作CGIのPythonコードをBlog本文内に紹介しようとすると、その中にHTMLやXMLタグ文字が含まれることになる。ここもそうだが、本文についてはHTMLタグが有効な為、それ自体が、本文のHTMLタグとしてレンダリングされてしまい、まともに表示されることが期待出来ない。
 そんな場所がほんの数か所であれば、エディタで'<'を'&lt;'に置き換えてからのコピペでも大した手間ではないが、実際のCGIソースコードでそんなものは少ない。

 いちいちハンド処理も嫌なので、変換用のツールを作成した。

 次の何れかで、ソースファイルを掲載用に特殊文字に置き換えたコマンドプロンプト上の表示をCopy&PastでBlogに貼付けて活用する。

python HTML4BLOG.py ソースファイル名
python HTML4BLOG.py ソースファイル名 ソースのcharset


#!c:/Python26/python.exe
# -*- coding: UTF-8 -*-
# ※ Windows の場合1行目はdummyです。

###############################################################################
# System NAME : HTML for BLOG TEXT (HTML4BLOG.py) Ver.0.1 2009-09-16 23:00 #
# Copyright : 2009- Mire(まあいいやあ) http://pythonlife.seesaa.net/ #
# Licence : GPLに基づく利用と再配布をお願いします。 #
# Requirement : Python2.6 or later Linux系では一行目を適宜修正のこと。 #
# What's This :  タグの特殊文字を置換えHTMLタグが有効なBlogに貼付利用可能な #
# テキストを表示。それをCopy&Pastで活用する #
# HowToRun : Type on windows command prompt. #
# python HTML4BLOG.py <filename> #
# python HTML4BLOG.py <filename> <charset> #
###############################################################################

def tag_encode(charset):
from sys import stdout,stdin,argv
from codecs import getwriter,getreader,open
params = argv
if len(params)>1:
file_name=params[1]
else:
file_name=params[0]
if len(params)>2:
stdin_charset=params[2]
else:
#stdin_charset='Shift_JIS'
stdin_charset=charset
if len(params)>3:
stdout_charset=params[3]
else:
stdout_charset='UTF-8'

stdout = getwriter(stdout_charset)(stdout)
stdin = getreader(stdin_charset)(stdin)

fpi=open(file_name,'r',stdin_charset)
lines=fpi.readlines()
fpi.close()
enc_lines=[]
for l in lines:
enc_lines.append(l.replace('&','&amp;').replace('<','&lt;').replace('>','&gt;'))
return enc_lines

def main():
#ファイルのCharsetをShift_JIS,UTF-8,EUC-JPの順に試行、初めに成功したものを表示
# これ以外のCharsetを指定したい場合には、file name の次の引数として入力可能
try:
lines=tag_encode(charset='Shift_JIS')
except:
try:
lines=tag_encode(charset='UTF-8')
except:
try:
lines=tag_encode(charset='EUC-JP')
except:
raise
for l in lines:
print l.rstrip()
main()

技術上の要点
1. タグの括弧「<>」と「&」を文字列の組込み関数replase()でそれぞれを「&lt;&gt;」と「&amp;」に置換え。2行目でUTF-8とし、内部処理をUTF-8に統一していることがポイントだ。
2. ファイルのCharsetは、日本で良く使われる順番に自動試行するので基本的に指定する必要はない。これ以外は、2番目の引数として指定可能。codecsのopenでファイルを開き、コード指定したことがポイントだ。
3. コマンドラインの引数は sys.argvで取得出来簡単だが、当方のVista環境では、「python.exe HTML4BLOG.py 〜」とせずに「HTML4BLOG.py 〜」で実行しようすると引数が伴って来なかった。そんな場合には必ず「python」を付けて頂きたい。尚、Pythonは複数のバージョンを共存インストールすることが可能な為、単に「python」とした場合は環境変数の指定する「python」が実行されてしまう。特定の「python」を指定したいときにはフルパス(ie 「c:\Python26\python.exe」)で明示指定する。尚、このツールに関しては多分結構古いPythonでも動作するでしよう。Python3系を除いて^^;;続きを読む
タグ:Python
posted by Mire at 23:13 | Comment(0) | Pythonプログラミング | このブログの読者になる | 更新情報をチェックする

2009年09月09日

【PythonでCGI】PyPRecのプロセス記録活用ツール作成に向けて

 その後、困ったことにPyPRecは対象実験用の未対策ソース分も含め、7つのPyPRecが連続動作を続けていて改善の成否は判らない。そこで、仕方がないので、記録側は放っといて、出力側をWebで開発しているところだ。この出力部分は画面を介してのman machine Interfaceを考えたデザインが必要となる。さて、やろうとしたが、以前の技術的なシガラミ=先入観を取る意味もあり1年以上放置していた付けが大きく、その辺の感覚もなくなっておりちょいと固まってしまった。

 それでも、昔とった何とかで、Web検索でいろいろと必要な材料を集めちまちま作っている。今日はそんな内容だ。以下の技術内容を確認した結果、やっと、何とかプロセスの一覧とその切替を行なう仕組みが出来そうな気になった。自分もまた忘れたら、ここを見ようと思う。

PythonでCGI対応のWEBサーバを作る


 まあ、常識的にはApacheを入れてくれってことでお終いの話なんだが、ついでなので、Pythonの標準モジュールのみでCGI対応のWebサーバを作り、同梱しApache導入の説明と設置の手間を省こうと思う。コードは次の通り、ソースのURLよりの完全なパクリです。ただ、当方のものは、これだけを別に起動させて使うのでimport cgi等は一切省いている。OSXな方向けではない。ポート番号は任意領域で適当な好きな番号に変えて運用することになる。因みに18296は語呂合わせで「嫌に苦労」するとした。何の苦労もないのだが、覚え難いものやよくある8080じゃ何かと被るのでそうした。

#!c:/Python26/python.exe
# -*- coding: Shift_JIS -*-
# ※ Windows の場合1行目はdummyです。

def web_server(ip='localhost', web_port=8080):
"""
「PythonならCGIの動くWebサーバが3行で書ける!?」
http://tech.ehaco.net/2008/10/pythoncgiweb.html
のまんま。
python -m SimpleHTTPServer も可能なので
SimpleHTTPServerもありかも
http://d.hatena.ne.jp/rx7/20090812/p1
Vistaではlocalhostは使えない。嵌らない様に
"""
from BaseHTTPServer import HTTPServer
from CGIHTTPServer import CGIHTTPRequestHandler
HTTPServer(( ip , web_port ), CGIHTTPRequestHandler ).serve_forever()

web_server(ip='192.168.0.23',web_port=18296)
尚、このコードは何処に置いても構わないが、このツールを実行した位置がWebサーバのルートとなるので、利用に当たっては、逐次、コマンドプロンプトでcdでディレクトリを切替えてから起動するか、予めそんなbatファイル作って実行すると良いだろう。尚、現在これはアプリ実行なので、起動させたコマンドプロンプト窓は閉じるとこが出来ない。それが嫌なら、利用アプリ側のthreadで実効させて使うかサービス起動させ常駐させることになる。
 アクセスは説明する必要ないと思うが、このソースの通りなら http://192.168.0.23/ で、ルートPATHの中身がフォルダ階層も含め表示される。そのルートPATH内にcgi-binフォルダを設け、その中にpythonコードのCGIプログラムを入れれば「http://192.168.0.23/cgi-bin/〜.py 」でcgiが利用できる。利用自体はApacheと大差ない。

PythonでCGIを書く


 次の一行のお呪いを入れて、printでHTML文の本体に相当するものを標準出力させればOKだ。

print "Content-type: text/html\n"
面倒なので書かないが、適当なHPのソースファイルを読み込んでprint出力するコードを続けて書けば、立派に表示させる筈だ。

cgiプログラムで値を受取る


 動的なWebCGIには、データの受取での出力変更は必須だろう。PyPRec程度のシステムでのGUI制御では、そのれを隠ぺいするpostよりURLの?以下に付加するGETで制御する方が良いだろうから、cgiでの引数の受取と、?以下の書き方が判れば済む。pythonではこれらは全て標準のcgiモジュールで簡単に出来る。

def cgi_gui_page():
import cgi # from cgi import 〜 としないこと。マニュアルによると無意味とのこと。
print "Content-type: text/html\n" #エラー対策上このお呪いを先に一度実行させておく
form=cgi.FieldStorage()

#引数データの取出しは必ず既定値を宛がうこと
# if form.has_key('val01'):
if form.has_key('val01'):
val01=form['val01'].value # 変数名を指定して「.value」で値が取出せる。
else:
val01='abc'


print form.keys() #引数の名前のlistは以下で取得可能

cgi_gui_page()


エラーのWeb表示をスマートに

今回、マニュアルを見て初めて気付きました。以前は別のものを使っていましたが、見た目はこちらの利用がスマートですね。また、この表示になれないので、よく判らなかったときには、コマンドプロンプトで実行して例外発生の内容を確認していますが。^^;;
import cgitb
cgitb.enable()

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

2009年09月03日

Web TABLEタグのデータ取込関数 by Python

 Google Chart のラッパーである Python Google Chart を より使い易くする為の ラッパー作りをしていて、テスト様のサンプルデータが欲しくなった。Web上には結構CSVやTSVで色々なものが流れているのでそれの自動DL利用を当初考えたが、最近のDLはJAVA絡みのDLも多く、以下の様な単純なurllib.urlopen(url)での取得が出来ないものに出会ってしまった。

def dl_tsv(url):
from urllib import urlopen
upi=urlopen(url)
lines=upi.readlines()
items=[]
for l in lines:
item=l.split('\t') #注意: 全部文字列データ
items.append(item)
return items


 でもWEB上のデータ公開の場合表形式でも公開していることが多いのでそちらからデータを取得したくなったので作って試た。それが本日のお題だ。

 WEBの読込なのでurllib.urlopen(url)を使うことには変わりがないが、その後の処理は次の手順となる。

1. urllib.urlopen(url);lines=upi.readlines();で文字列配列として取込
2. その読込んだWEBページ(HTML文)から、目的の<table>タグを探し</table>迄の文字列を取得
3. そのTABLEタグ内の<tr></tr>タグを探し、行データに分割
4. そのTRタグ行内の<td></td>タグを探し各セル内の文字列を取得、行データリストに取込
5. 行データlistを順次listに追加し、求めるデータlistを取得
6. 数字型であるデータを文字列から数字化して利用する


 必要な技術としては、次の3つくらい。

1. 文字列から<table>と</table>の位置を探す為に、〜.find('<table')と〜.find('</table>')を利用
2. <tr></tr>タグと<td></td>の文字列list取得の為にはre.findall(r'タグを表す正規表現文字列',string_line)
3. 文字列データを実数型に変更する為、float()とint()をtry:〜except:で囲んで処理


 まあ、当方の場合、困ったことに正規表現は、いつも忘れた頃に使うことになるので、今回もググってテストし何とか次のもので対応出来ることが判った。これが、このモジュールの味噌! <tr>と<td>タグはその中にclass やwidth align nowrap等を含むことがあるので '<tr 何でもいい </tr>'的な正規表現を組む必要がある。で、答えは、 r'<tr.*?>.*?</tr>' ということになり、それをコーディングすると次の様な関数となった。当方が実際に利用している関数はテーブルの取出し開始位置やパラメータ設定による同時複数取得等の機能を付加しているが、基本は同じ。
PyPRec/import_table_test.py


#!c:/Python26/python.exe
# -*- coding: UTF-8 -*-
# ※ Windows の場合1行目はdummyです。

def import_table(url):
from urllib import urlopen
from datetime import datetime
from re import findall, split, compile, IGNORECASE

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
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=[]
try:
dat=u'%s' % (unicode(st,charset)) #間違っていたので訂正2009-10-09
except: #ものにより指定のcharsetでエラーとなる時の対策
charsets=['Shift_JIS','EUC-JP','UTF-8'] #コード指定の不一致だけならこれで対応可能だが
for charset in charsets: #部分的な不一致には対応出来ない。その場合には
try: #読込行別で、このコード変換をかける程度の対応
dat=u'%s' % (unicode(st,charset)) #で逃げるしかないだろう。
break
except:
dat = st
pass
mach = compile(u'<table', IGNORECASE) #ついでに複数のtableタグに対応し
ss=split(mach, dat) #その数分list要素で区切って返す様に
for s in ss: #変更
if not s==-1:
mat = compile('<tr.*?>.*?</tr>', IGNORECASE) #ついでに大文字小文字区別なしに修正
tr_tags=findall(mat,s) #すまん間違っていたので訂正2009-10-09
data=[]
for tr in tr_tags:
v=td_tags(tr) #<td></td>で分割する関数へ
data.append(v)
datas.append(data)

values= srt_to_int_list(datas) # 数字化が可能な文字は全て数字に変換
return values


## <td></td>で分割する関数へ ##
def td_tags(text):

from re import findall, compile, IGNORECASE
ma =compile('<td.*?>.*?</td>', IGNORECASE)
td_tags=findall(ma, text)
items=[]
for td in td_tags:
s=td.find('>')
if not s==-1:
t=td[s:].find('</')
items.append(u'%s' % (td[s+1:s+t]))
return items


## 数字化が可能な文字は全て数字に変換 ##
def srt_to_int_list(lst):
"""
list と tupleの要素で整数文字列のものを全て再帰的に整数に置換
googlechart.pyのline_styleの指定要素が現在文字であるので
数字指定も可能な様にする為に利用
"""
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:
#print [ls],
try:
v=float(ls)
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] = srt_to_int_list(ls)
i=i+1
return lst


## TEST CODE ## #TEST CODE を追加 2009-10-09
import sys
argv = sys.argv
if len(argv)>1:
url = sys.argv[1]
datas = import_table(url)
for dat in datas:
for da in dat:
for d in da:
print d,'\t',
pass
print
print '\n\n'
else:
print u'USEGE:\npython import_table_test.py <url>'
print u'例: python import_table_test.py http://markets.nikkei.co.jp/kokunai/japanidx.aspx'
print u'url には、tableタグで作られた素直なデータ表のあるURLを指定します。'
print u'tdタグ内には素直にデータのみがあることが前提の関数ですので、例えば、'
print u'抽出データに不要なタグを含む場合には別途個別に対処する必要があります。'
print u'例:不要タグ付随、rowspan,colspan対応、全角数字の数字化等個別対応は多い。'
print u'また実際にはtableタグの入れ子や事前の個人認証等のハードルが高いかもです。'


 追伸、このブログではHTMLタグの埋め込みがかのうなので、< は、「&lt;」で置替えないと表示が出来ません。それで、ひょっとしたら何処かに修正漏れ等があり、このままでは利用出来ないのかも知れません。見つけた方はレスお願いします。続きを読む
タグ:Python
posted by Mire at 17:16 | Comment(0) | Pythonプログラミング | このブログの読者になる | 更新情報をチェックする

2009年09月01日

Python Google Chart(pygooglechart)で簡易グラフ化

 PyPRecの定時処理精度向上の為、テキストレベルのグラフを使って来たが、やはり2つのデータを比較する場合には無理がある。そこで、首記の「Python Google Chart(pygooglechart)」を手始めに活用してみた。

 「Google Chart」は、Googleがネット上で公開しているチャート作成のサービスで、必要な情報を指定のURLのパラメータとして指定することで、グラフィカルなチャートを作成し返してくれるサービスだ。
 残念ながら、チャートサイズはピクセル数制限の他プロセス数制限が設けられており、プロット数の多い大規模なグラフ作成は出来ないが、ストレスなくチャートを返してくれるのは素晴らしい。

 ただ、生で使うには、直接、Google Chartのフォーマットに従い「?」以下に入れるパラメータ文字列を生成しなければならないし、タイトル等に日本語を表示しようとする場合にはUTF-8でエンコードしなければ出来ないので勢いラッパーツールを通すことが利口であろう。ということで、自作しようと思っていたら、やはりPythonistaにはそう考えてもう作った人がいた様で首記の「Python Google Chart(pygooglechart)」を有難く使わせて頂くことにした。

 でも、残念なことに日本語ドキュメントも少ないが、本家サイトを見てもマニュアルらしいものも見つからない。あるのは、サンプルコードのみ。仕方がないので、一つ一つサンプルとモジュール本体である「pygooglechart.py」を眺めながら使い方とその挙動を調べて見た。

 先ず必要なのはチャートインスタンスの生成だが、ここでは、グラフのサイズ、グラフ化するデータの最大最少値、そしてグラフのタイトルを設定出来る。グラフサイズには、Google Chartサービス自体で300000ピクセルという制限があるので例えば横600×縦500程度迄ということになる。只、数値に対するグラフ化する範囲もここで設定出来るので、実際プロットするデータの範囲次第で、グラフの上下限値も設定出来るのでそれを有効活用することになる。

chart = SimpleLineChart(横幅, 縦高,y_range=(最小値, 最最大値),title=UTF8にエンコードした文字列) といったところでインスタンスを先ず生成し、プロットする書くデータ系列を順に追加、そして必要とする補助的な設定を行なった上で、chart.get_url()で求めるURL文字列を取得する。実際のコーディングは概ね次の様なところか。

#!c:/Python26/python.exe
# -*- coding: UTF8 -*-
# ※ Windows の場合1行目はdummyです。

def google_chart():
from pygooglechart import Chart,SimpleLineChart,Axis,ScatterChart
##各データ系列数値の準備
jyurai = [11,13, 4,55,11]
kaizen = [ 9,10, 2,44, 8]
shorai = [ 5, 7, 1,33, 4]
gokaku = [40,40]
bottom_label = ['A','B','C','D','E']

##全系列データのレンジ幅を求める
range_min = min(jyurai+kaizen+shorai)
range_max = max(jyurai+kaizen+shorai)
width = 600 # 横幅のドット数
height = 500 # 縦高のドット数
title = u'ツールの改善効果' # 後でutf8にencodeするのでunicodeとしてタイトル文字を準備

chart = SimpleLineChart(width,height,y_range=(range_min,range_max),title=title.encode('utf8'))

chart.add_data(jyurai) # 従来
chart.add_data(kaizen) # 改善
chart.add_data(shorai) # 将来
chart.add_data(gokaku) # 合格基準値

chart.set_grid(10,2,1,4) # 補助線横10%、縦2%刻みで線描画ドット数、線非描画ドット数
chart.line_styles={0:['1','1','1'],1:['1','1','1'],2:['1','1','1'],3:['1','1','1']}
chart.set_colours(['0000FF','FF0000','FFFF00','FF8080'])
chart.set_axis_range(Axis.LEFT, range_min, range_max)
chart.set_axis_labels(Axis.BOTTOM, bottom_label)

chart.set_legend([u'従 来'.encode('utf8'),u'改善後'.encode('utf8'),u'将 来'.encode('utf8')])

print chart.get_url() ####URLを標準出力

google_chart()
このソースコードを試したい方は、エディタに貼付後、文字コードUTF-8、改行はLFとして保存して下さい。(別途、Pythonと「Python Google Chart(pygooglechart)」のインストールは必要です。)

 ※ 尚、補助線を考えるとここレンジ幅は100で割切れる整数値の差とすることでより見易いものにすることが出来る。これは下部の目盛でも同様だ。ただ。株の目盛用のラベル表示は、横書きとなるのでプロット数が多いと全てのラベルを表示する訳には行かないので間引きすることになる。最悪最初と最後、そして中間値のラベルのみとすることもあると思う。そこが一番悩ましい。

 ともあれ、上の埋め込みデータでの出力URLは次の通り。このSeesaaブログでは、「|」を見つけるとURL終了と判断する仕組みの様なので、レンダリングに失敗しシッポ切りのリンクとなっている。従って、これを利用するには、シッポも含めてコピーしてから、ブラウザに貼付けて利用して頂くことになる。レンダリング精度はサイト毎のURLの正規表現に基づくので致し方ない。
http://chart.apis.google.com/chart?cht=lc&chs=600x500&chd=e:L2OODk..L2,JfKrBMy9IT,EvHHAAl7Dk,uOuO&chtt=%E3%83%84%E3%83%BC%E3%83%AB%E3%81%AE%E6%94%B9%E5%96%84%E5%8A%B9%E6%9E%9C&chdl=%E5%BE%93%E3%80%80%E6%9D%A5%7C%E6%94%B9%E5%96%84%E5%BE%8C%7C%E5%B0%86%E3%80%80%E6%9D%A5&chco=0000FF,FF0000,FFFF00,FF8080&chxt=y,x&chxl=1:|A|B|C|D|E&chxr=0,1,55&chls=1,1,1|1,1,1|1,1,1|1,1,1&chg=10,2,1,4


 しかしHTMLタグが埋め込めるのであれば、これをHTMLのIMGタグに入れて書くことで、以下の様なチャート表示が得られる。まあまあ、悪くないだろう。



 ただ、今回は「Google Chart」の内一番簡便なSimpleLineChart()のみを検討して試た訳なので、他の種類を試せばもっと良い表現が可能なのかもしれない。「Google Chart」では、インターネット公開用のグラフであれば、わざわざローカル側にその画像ファイルを準備しなくても、予め生成した「Google Chart」へのURLを張り付けるだけで済む良さかある。自分のブログだけでなく、よそ様や公共の掲示板にもそれなりに掲載可能なのが良い。今後適宜活用して行くつもりだ。でも、PyPRec解析用としては今一制限が気になるので別の方法を模索することになる。
続きを読む
posted by Mire at 16:00 | Comment(0) | Pythonプログラミング | このブログの読者になる | 更新情報をチェックする

2009年07月29日

Pythonコードを「coding: UTF8」に変更

 dvd_label.py もそこそこ使える程度の完成度にはなってきたものの、Shift_JISの処理で、細かく考えずにコーディングしているもので、まれに文字化けを発生させていた。Shift_JISの第一biteと第二biteをぶった切るアホなコードを眺め直すのもいいのだろうが、UTF8で処理すれば日本語も漢字一つが1文字として扱えるので、考えずにBUG獲りが出来そうに思えたので、ちょいとした思い付きでそうすることにした。  やってみた結果、取敢えず dvd_label.py のUTF8への変更は出来たが、 まあ、ほぼ初めてというか、随分とプランクがあったので、「UnicodeDecodeError: 'ascii' codec can't decode byte ...」という例外エラー吐かせながら次の様なことをやった結果だ。
  1. MKEditorの名前を付けて保存で、文字コード:Unicode(UTF-8)、改行コード:改行=LF を指定して、名前を dvd_label_utf8.pyとして保存。
  2. 2行目を「# -*- coding: UTF8 -*-」に書替え
  3. 漢字を含むところの print "〜..." となっているところを print u"〜..." に一括置換
  4.  
  5. 標準入出力の違い(Windows=sjis)での不具合を解消するお呪いを記入。
  6. from sys import argv from sys import stdout,stdin from codecs import getwriter,getreader stdout = getwriter('utf_8')(stdout) stdin = getreader('Shift_JIS')(stdin)
  7. 「print u"%-64s" 」の処理だと全角文字も1文字の為空白文字での桁揃えが出来ない
  8. 仕方がないので、その部分は、元コード(Shift_JIS)のまま「sjis_string="%-64s" 」で文字列を生成した後「utf8_string=unicode(sjis_string,"Shift_JIS")」でUTF8へ変換しprintへ渡す様に変更(一般的な手法としては、「【UTF8文字幅揃え】unicodedata.east_asian_width()」での対処を推奨する。この時はDVD-VRで元データが日本語ならば2biteのShift_JISしかあり得ないので、この手法を使った。既に後Versionで改定済。2009-11-23追記)
  9. 標準入力「raw_input(u"文字列")」は例外エラーを吐く
  10.  仕方がないので、「print u"文字列"」で必要な表示をさせてから、「raw_input()」を実行する様にした。
  11. カッコ文字比較をbiteスライスでなく文字単位のスライスに変更
  12.  「miji[-2:]=="「"」を「miji[-1:]=="「"」に
  13. 「print u"文字列"」での例外エラー発生
  14.  細かく見るのが面倒な時には 書式制御なしで 「print hensu1,hensu2,"\n"」の様に変数そのまま表示させ暫定対処
  15. 特に何もしないで
  16. メインTitle文字列長の増減は、文字化けなしで一文字ずつ素直に変更出来る様になった。
 結構、場当たりなので、間違いもあるとは思うけど結果 All Right の模様。念の為、diffを取ったので、興味のある方は、続きを見て欲しい。続きを読む
posted by Mire at 01:57 | Comment(0) | Pythonプログラミング | このブログの読者になる | 更新情報をチェックする

2009年07月22日

Dvd_Label.py の 汎用系関数化

 Dvd_Label.py は適当に書きなぐりながら成行で動くことを最優先に作成して来たが、当方の経験に基づくパターン発想による組上げ手法で発生するBUGもようやく目立たなくなって来た。そこで、次の段階として、重複する処理を汎用関数化することで、今後の保守性を高めると共に視認性も向上させようと思う。

 今回のDvd_Label.pyでは、DVD-VR FORMATのデータ読込であることから、記録されている数字は全てバイト表記となっている。先ずはHeaderブロックの固定位置よりTitle や Clip のデータブロックの開始位置を表す4biteを整数化しそれらの位置に行って、Title や Clip の数を拾い、Clipではさらに個別のClipデータ位置を表す4biteを整数化し、それを使い、個別のClipデータにある録画日時を表す5biteを整数化しなければ、このツールは実現しないのである。最初から関数化することが生産性を考えると良いことは判っていたが、bite表記の整数を扱うのは久しぶりだったので、今回は目途が付く迄、体に覚えさせる為。直書きとした。

 さて、課題の処理は次の様なものでループを抜けた「10進数整数」が求める答えである。

「bite文字列」=fpi.read(4)
「10進数整数」=0
for 「1bite文字」 in 「bite文字列」: #頭から1bite文字ずつ取出し
「10進数整数」=(「10進数整数」*256)+ord(「1bite文字」) # ord()関数で「1bite文字」を
#10進整数化し、上の桁の
#「10進数整数」を256倍したものに
#加える動作を繰返す
これをPythonの関数にすると次の通りとなり、「10進数整数」=bites2int(bite文字列)として置きかえて利用すれば良いことになる。尚、必要によりその行の後には「#○○のbite文字列を10進数整数化」と注記を付けると判り易くなる。
#バイト表記の数字データを10進整数に変換# 2009-07-22 作成
def bites2int(bites):
i=0
for bite in bites:
i=i*256+ord(bite)
return i


 その他、ヘルプ等のドキュメント別のテキストファイル表示用の関数を汎用なし集約した。これは説明は余り差がないので要らないだろう。
def dsp_textfile(file_path="",file_name="",missing_message=""):
import os
if os.path.exists(os.path.join(dvd_label_path(),file_name)):
fpi=open(os.path.join(dvd_label_path(),file_name),"r")
line=fpi.readline()
while line:
print line,
line=fpi.readline()
fpi.close()
else:
print "※ %sが見つかりません。%s" % (os.path.join(dvd_label_path(),"HELP_SJIS.txt"),missing_message)


 尚、関数定義の置く位置には多少決まりがある。defで始まる関数定義どうしは順不同だが、実際の実行開始の行よりも前に置く必要がある。また、今回の様に汎用化した関数については別にpythonコードとして別ファイルに収め、python26/lib/site-packagesに放り込めば以降
標準モジュール同様にimportで利用出来るようになる。今後当方のmire.pyはこのURLで最新版を掲載していくので「import mire」の記述がある時は利用して頂きたい。ただ、今回のdvd_label.pyはpy2exeでWindows実行ファイル化する関係で、同じファイル内に収録したままとしている。
posted by Mire at 20:10 | Comment(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年以上新しい記事の投稿がないブログに表示されております。