讓我們描繪一下本文的情節(jié):假設(shè)您要在本地機(jī)器上運(yùn)行一個(gè)進(jìn)程,而部分程序邏輯卻在另一處。讓我們特別假設(shè)這個(gè)程序邏輯會(huì)不時(shí)更新, 而您運(yùn)行進(jìn)程時(shí),希望使用最新的程序邏輯。有許多方法可以滿足剛提到的要求;本文將向您說(shuō)明其中幾種方法。
隨著“可愛的 python”專欄不斷進(jìn)行,已經(jīng)討論了我的公共域?qū)嵱贸绦?txt2html 的正在進(jìn)行的增強(qiáng)。該實(shí)用程序?qū)ⅰ爸悄?ascii”文本文件轉(zhuǎn)換成 html。以前的文章討論了實(shí)用程序的 web 代理版本和實(shí)用程序的 curses 界面。同樣,我偶爾注意到可以用更有效的方法轉(zhuǎn)換某些 ascii 標(biāo)記,或者解決了一個(gè)在處理某個(gè)特殊標(biāo)記結(jié)構(gòu)中的錯(cuò)誤。
事實(shí)上,本專欄的文章都是用 ascii 編寫的,然后在編輯過(guò)程中轉(zhuǎn)換成您可以閱讀的 html 格式。在發(fā)表文章草稿之前,我運(yùn)行了類似以下處理的程序:
文章的命令行 html 化
?1 txt2html charming_python_7.txt > charming_python_7.html
如果愿意,我可以指定一些標(biāo)志來(lái)修改操作;但不管怎樣,事實(shí)上轉(zhuǎn)換器的最新版本在我的本地驅(qū)動(dòng)器和路徑中。如果在另一臺(tái)機(jī)器上工作,或者對(duì)于要使用該實(shí)用程序的讀者,則過(guò)程比較麻煩:請(qǐng)?jiān)L問我的網(wǎng)站,注意比較版本號(hào)和文件日期(有時(shí)更改太小,我不會(huì)更改版本號(hào)),下載當(dāng)前版本、將當(dāng)前版本復(fù)制到正確目錄,然后運(yùn)行命令行轉(zhuǎn)換器。(請(qǐng)參閱本文后面的 參考資料。)
以上的過(guò)程包括幾個(gè)需要手工操作且比較費(fèi)時(shí)的步驟。應(yīng)該更簡(jiǎn)單,而且可以做到這點(diǎn)。
命令行 web 訪問
大多數(shù)人認(rèn)為 web 是在 gui 環(huán)境中交互式瀏覽頁(yè)面的一種方法。那樣做當(dāng)然很好,但命令行中也有許多功能。帶文本模式 web 瀏覽器 lynx 的系統(tǒng)完全可以將整個(gè) web 看作是命令行工具使用的另一個(gè)文件集。例如,我發(fā)現(xiàn)有些命令很有用:
使用 lynx 進(jìn)行命令行 web 瀏覽
?123 lynx -dump . lynx -dump . > ibm_developer.txt lynx -dump | wc | sed s/( *[0-9]* *\)\([0-9]*\)\(.*\)/\2/g
第一行說(shuō):“將 david mertz 的主頁(yè)(以 ascii 文本)顯示到控制臺(tái)?!钡诙姓f(shuō):“將 ibm 的當(dāng)前 developerworks 主頁(yè)的 ascii 版本保存到文件?!钡谌惺纠f(shuō):“顯示 david 主頁(yè)的字?jǐn)?shù)?!保ú槐?fù)?dān)心細(xì)節(jié),它只顯示與管道結(jié)合的命令行工具。)
關(guān)于 lynx,有一點(diǎn)要注意它(使用 -dump 選項(xiàng)時(shí))執(zhí)行幾乎與 txt2html 完全相反的操作:前一種工具將 html 轉(zhuǎn)換成文本;而后一種工具則轉(zhuǎn)換成其它格式。但沒有理由不使用與 lynx 一樣流行的 txt2html??梢允褂靡粋€(gè)很短的 python 腳本完成這個(gè)操作:
'fetch_txt2html.py' 命令行轉(zhuǎn)換器
?12345678 import sys from urllib import urlopen, urlencode if len(sys.argv) == 2: cgi = 'http://gnosis.cx/cgi/txt2html.cgi' opts = urlencode({'source':sys.argv[1], 'proxy':'none'}) print urlopen(cgi, opts).read() else: print please specify url for txt2html conversion
要運(yùn)行這個(gè)腳本,只要執(zhí)行如下操作:
?1 python fetch_txt2html.py
這并沒有向您提供本地 txt2html 處理的全部開關(guān),但如有必要,添加它們也很容易??梢韵袷褂萌魏蚊钚泄ぞ咭粯觼?lái)輸送和重定向輸出。但是,在上述版本中,只能處理 url 可以到達(dá)的數(shù)據(jù)文件,而不能處理本地文件。
實(shí)際上, fetch_txt2html.py 可以完成 lynx 不能完成的任務(wù)(txt2html 本身也不能):它不僅從 url 取得數(shù)據(jù)源,而且還遠(yuǎn)程獲取 程序邏輯 。如果使用 fetch_txt2html.py ,就 不必在本地機(jī)器上安裝 txt2html;將(使用最新版本)遠(yuǎn)程調(diào)用處理,并且將把結(jié)果發(fā)送回來(lái),就像運(yùn)行的是本地進(jìn)程。很棒吧?txt2html 的本地版本可以訪問遠(yuǎn)程 url,就像訪問本地文件一樣,但它還不能保證它自身是最新的……!
動(dòng)態(tài)初始化
使用 fetch_txt2html.py 確保了在轉(zhuǎn)換中始終使用最新的程序邏輯。但是,這個(gè)方法可以完成的另一件事情是將處理器(和內(nèi)存)的需求轉(zhuǎn)移給 gnosis.cx web 服務(wù)器。此特殊進(jìn)程的負(fù)載并不是特別高,但人們卻很可能認(rèn)為在客戶機(jī)上處理的其它類型的進(jìn)程會(huì)更有效且令人滿意。
組織 txt2html 的方式 -- 也就是組織大多數(shù)程序的方式 -- 是用一些由各種實(shí)用函數(shù)提供的核心流量控制函數(shù)。尤其是這些實(shí)用函數(shù)是一些經(jīng)常更新的函數(shù);核心函數(shù)( main() 和一些其它函數(shù))只有在做重大改寫時(shí)才會(huì)變動(dòng)??偠灾诿總€(gè)程序運(yùn)行時(shí)有效更新的就是實(shí)用函數(shù)。其實(shí),大部分情況下,主 txt2html 模塊 dmtxt2html 中的大多數(shù)函數(shù)就夠了。
'd2h_textfuncs.py' 動(dòng)態(tài) txt2html 更新
?12345678910111213141516171819202122232425262728293031 hot-pluggable replacement functions for txt2html #-- functions to massage blocks by type #def titleify(block): #def authorify(block): # ... [more block massaging functions] ... #-- utility functions for text transformation #def adjustcaps(txt): #def capwords(txt): #def urlify(txt): def typographify (txt): # [module] names r = re.compile(r '([\(\s'/>]|^)\[(.*?)\]([<\s\.\),:;'?!/-]) , re.m | re.s) txt = r.sub( '\\1<em><code>\\2</code></em>\\3' ,txt) # *strongly emphasize* words r = re.compile(r '([\(\s'/]|^)\*(.*?)\*([\s\.\),:;'?!/-]) , re.m | re.s) txt = r.sub( '\\1<strong>\\2</strong>\\3' , txt) # ... [more text massaging] ... return txt # ... [more text transformation functions] .....
要使用最新和最具體的支持模塊,需要一些準(zhǔn)備步驟。首先,將主 txt2html 模塊下載到本地系統(tǒng)(這是一次性步驟)。其次,在本地系統(tǒng)上創(chuàng)建類似于以下示例的 python 腳本:
'dyn_txt2html.py' 命令行轉(zhuǎn)換器
?1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950 from dmtxt2html import * # import the body of 'txt2html' code from urllib import urlopen import sys # check for updated functions (fail gracefully if not fetchable) try : updates = urlopen( 'http://gnosis.cx/download/t2h_textfuncs.py' ).read() fh = open( 't2h_textfuncs.py' , 'w' ) fh.write(updates) fh.close() except : sys.stderr.write( 'cannot currently download txt2html updates' ) # import the updated functions (if available) try : from t2h_textfuncs import * except : sys.stderr.write( 'cannot import the updated txt2html functions' ) # set options based on runmode (shell vs. cgi) if len(sys.argv) >= 2: cfg_dict = parseargs(sys.argv[1:]) main(cfg_dict) else : printplease specify url (and options) for txt2html conversion
在 dyn_txt2html.py 腳本中,請(qǐng)注意當(dāng)執(zhí)行 from t2h_textfuncs import * 語(yǔ)句時(shí),所有以前在 dmtxt2html 中定義的函數(shù)(如 typographify() )都將由 t2h_textfuncs 版本的同名函數(shù)替換。當(dāng)然,如果 t2h_textfuncs 的函數(shù)被注釋掉了,則不會(huì)被替換。
有件小問題得注意,不同的系統(tǒng)以不同的方式處理寫入 stderr。在類 unix 系統(tǒng)中,運(yùn)行腳本時(shí)可以重定向 stderr;但是在當(dāng)前 os/2 外殼和 windows/dos 中,stderr 消息將附加到控制臺(tái)輸出。您也許要將以上的錯(cuò)誤/警告寫到日志文件中,或者只習(xí)慣于將 stdout 定向到文件(可能會(huì)更有用)。例如:
'dyn_txt2html' 的命令行會(huì)話
?12 g:\txt2html> python dyn_txt2html.py test.txt > test.html cannot currently download txt2html updates
錯(cuò)誤轉(zhuǎn)至控制臺(tái);經(jīng)轉(zhuǎn)換的輸出轉(zhuǎn)至文件。
一件更有趣的事情是 dyn_txt2html.py 為什么不下載整個(gè) dmtxt2html 模塊,而僅下載支持模塊。當(dāng)然這是有理由的。 t2h_textfuncs 支持模塊遠(yuǎn)遠(yuǎn)小于主 dmtxt2html 模塊,特別是因?yàn)榇蠖鄶?shù)函數(shù)已經(jīng)過(guò)刪節(jié)/被注釋掉。在調(diào)制解調(diào)器連接上,它的速度明顯快很多。但下載大小并不是主要原因。
對(duì)于 txt2html,如果用戶自動(dòng)下載整個(gè)最新模塊也沒關(guān)系。但程序邏輯是 分布式 的系統(tǒng)(特別是維護(hù)責(zé)任也是分布式的)會(huì)發(fā)生什么情況呢?您也許會(huì)讓 alice、bob 和 charlie 分別負(fù)責(zé)模塊 funcs_a 、 funcs_b 和 funcs_c 。他們每個(gè)人都對(duì)他們負(fù)責(zé)的函數(shù)進(jìn)行定期(且獨(dú)立)更改,并將最新和最好的版本上傳到他們自己的網(wǎng)站(如 )。在這種情況下,讓三個(gè)程序員都更改同一個(gè)主模塊不太可行。但可以直接擴(kuò)展類似于 dyn_txt2html.py 的腳本以在啟動(dòng)時(shí)嘗試導(dǎo)入 funcs_a 、 funcs_b 和 funcs_c (如果不能獲取這些資源,則會(huì)退到 mainprog 版本)。
長(zhǎng)期運(yùn)行的動(dòng)態(tài)進(jìn)程
迄今為止,我們研究的工具已經(jīng)通過(guò)在初始化時(shí)下載更新資源而獲得了動(dòng)態(tài)程序邏輯。這對(duì)于命令行處理或批處理很有意義,但對(duì)于長(zhǎng)期運(yùn)行的應(yīng)用程序又會(huì)怎樣。這種長(zhǎng)期運(yùn)行的應(yīng)用程序最可能是一些不斷響應(yīng)客戶機(jī)請(qǐng)求的服務(wù)器進(jìn)程。但是在這個(gè)案例中,我們將使用為 以前的文章 開發(fā)的 curses_txt2html.py 來(lái)說(shuō)明 python 的 reload() 函數(shù)。程序 curses_txt2html 是 dmtxt2html 本地副本的封裝器。這里并不是第二次提到 curses 編程,談一下 curses_txt2html 提供了一組交互式菜單以配置和運(yùn)行多個(gè)連續(xù)的 txt2html 轉(zhuǎn)換也足夠了。
curses_txt2html 可以一直在后臺(tái)運(yùn)行,當(dāng)切換到它的會(huì)話并運(yùn)行轉(zhuǎn)換時(shí),我們希望它能夠使用最新的程序邏輯。對(duì)于這個(gè)特定的簡(jiǎn)單示例,關(guān)閉和重新啟動(dòng)應(yīng)用程序并不難,并不會(huì)帶來(lái)特別的損害。但這很容易令人聯(lián)想到其它一直運(yùn)行著的進(jìn)程(可能是說(shuō)明會(huì)話中所執(zhí)行操作狀態(tài)的進(jìn)程)。
在本文中,添加了新的 file/update 子菜單。它被激活時(shí)只調(diào)用新的函數(shù) update_txt2html() 。除了與提供發(fā)生的確認(rèn)相關(guān)的 curses 調(diào)用之外,我們已經(jīng)在本文的其它示例中看到過(guò)這些步驟:
'curses_txt2html.py' 動(dòng)態(tài)更新函數(shù)
?123456789101112131415161718192021222324252627282930313233343536373839 def update_txt2html (): # check for updated functions (fail gracefully if not fetchable) s = curses.newwin(6, 60, 4, 5) s.box() s.addstr(1, 2, * press any key to continue * , curses.a_bold) s.addstr(3,2, ...downloading... ) s.refresh() try : from urllib import urlopen updates = urlopen( 'http://gnosis.cx/download/dmtxt2html.py' ).read() fh = open( 'dmtxt2html.py' , 'w' ) fh.write(updates) fh.close() s.addstr(3,2, module [dmtxt2html] downloaded to current directory ) except : s.addstr(3,2, download of updated [dmtxt2html] module failed! ) reload(dmtxt2html) s.addstr(4, 2, module [dmtxt2html] reloaded from current directory ) s.refresh() c = s.getch() s.erase()
dyn_txthtml.py 和 update_txt2html() 函數(shù)之間有兩個(gè)重要差異。其中一個(gè)差異是繼續(xù)操作,并導(dǎo)入主 dmtxt2html 模塊而不只導(dǎo)入支持函數(shù)。這主要是簡(jiǎn)化了導(dǎo)入。這里的問題是我們使用 import dmtxt2html 來(lái)訪問模塊,而不是 from dmtxt2html import * 。從許多方面考慮,這是一個(gè)更安全的過(guò)程,但結(jié)果是使覆蓋 dmtxt2html 中的函數(shù)變得更困難(不論是無(wú)心地還是故意地)。如果我們要從 d2h_textfuncs 附加函數(shù),則必須對(duì)導(dǎo)入的支持模塊執(zhí)行 dir() ,并將成員以屬性形式附加到 dmtxt2html 名稱空間。執(zhí)行這種樣式的覆蓋是留給讀者的練習(xí)。
update_txt2html() 函數(shù)帶來(lái)的最主要差異是 python 的內(nèi)置 reload() 函數(shù)的用法。只執(zhí)行全新的 import dmtxt2html 將 不 會(huì)覆蓋以前導(dǎo)入的函數(shù)。請(qǐng)密切注意這一點(diǎn)!許多初學(xué)者認(rèn)為重新導(dǎo)入模塊將更新內(nèi)存中的版本。這是錯(cuò)的。實(shí)際上,更新模塊中函數(shù)的內(nèi)存映像的方法是 reload() 模塊。
以上示例中還執(zhí)行了另一個(gè)小技巧。更新 dmtxt2html 模塊的下載位置是本地工作目錄,而這個(gè)目錄可能是(也可能不是)原來(lái)裝入 dmtxt2html 的目錄。事實(shí)上,如果它在 python 庫(kù)目錄中,那么您也許不在該目錄中使用(也許對(duì)它沒有用戶許可權(quán))。但 reload() 調(diào)用嘗試先從當(dāng)前目錄裝入,然后再嘗試 python 路徑的其余部分。所以,不論下載是否成功, reload() 應(yīng)該是一個(gè)安全的操作(雖然它可能裝入新的模塊,也可能不裝入)。