<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>程式設計 遇上 小提琴 &#187; Python</title>
	<atom:link href="http://blog.ez2learn.com/tag/python/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.ez2learn.com</link>
	<description>Victor&#039;s個人部落格，關於程式設計與小提琴</description>
	<lastBuildDate>Thu, 26 Aug 2010 10:49:41 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>A tool for applying iptables safely: apply_firewall.</title>
		<link>http://blog.ez2learn.com/2010/08/26/a-tool-for-applying-iptables-safely-apply_firewall/</link>
		<comments>http://blog.ez2learn.com/2010/08/26/a-tool-for-applying-iptables-safely-apply_firewall/#comments</comments>
		<pubDate>Thu, 26 Aug 2010 10:39:11 +0000</pubDate>
		<dc:creator>victor</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[administration]]></category>
		<category><![CDATA[iptables]]></category>
		<category><![CDATA[tool]]></category>

		<guid isPermaLink="false">http://blog.ez2learn.com/?p=1091</guid>
		<description><![CDATA[Do you have ever did something stupid with iptables command which like blocking you self from accessing the SSH?  Yes, I did.  Most of administrator knows that is dangerous to change iptable rules remotely, with a little typo, you might have to restart the machine to access the SSH again.  For applying iptables sfaely, I [...]]]></description>
			<content:encoded><![CDATA[<p>Do you have ever did something stupid with iptables command which like blocking you self from accessing the SSH?  Yes, I did.  Most of administrator knows that is dangerous to change iptable rules remotely, with a little typo, you might have to restart the machine to access the SSH again.  For applying iptables sfaely, I wrote a simple tool which can be used to apply firewall rules safely.  It backups the original iptables configuration before applying it, and if you don&#8217;t type "yes" within specific time period, it will rollback to original iptables automatically.  Here is an example:</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2010/08/apply_firewall.png"><img class="alignnone size-full wp-image-1092" title="apply_firewall" src="http://blog.ez2learn.com/wp-content/uploads/2010/08/apply_firewall.png" alt="" width="453" height="231" /></a></p>
<p>It is written in Python, you can install it by</p>
<blockquote><p>easy_install apply_firewall</p></blockquote>
<p>Or you can download it here:</p>
<p><a href="http://pypi.python.org/pypi/apply_firewall/">http://pypi.python.org/pypi/apply_firewall/</a></p>
<p>I hope this could be helpful for linux administrators :D</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.ez2learn.com/2010/08/26/a-tool-for-applying-iptables-safely-apply_firewall/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>淺談coroutine與gevent</title>
		<link>http://blog.ez2learn.com/2010/07/17/talk-about-coroutine-and-gevent/</link>
		<comments>http://blog.ez2learn.com/2010/07/17/talk-about-coroutine-and-gevent/#comments</comments>
		<pubDate>Sat, 17 Jul 2010 14:06:53 +0000</pubDate>
		<dc:creator>victor</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Coroutine]]></category>
		<category><![CDATA[Gevent]]></category>
		<category><![CDATA[Network]]></category>

		<guid isPermaLink="false">http://blog.ez2learn.com/?p=1068</guid>
		<description><![CDATA[這篇文章是要大略介紹一下coroutine和Python的相關應用的函式庫gevent，在介紹coroutine前我們先來點情境，因為目前常見的coroutine應用都是在網路程式上，因此我們得先建立一些網路框架的模形再介紹coroutine會比較容易懂 不同的網路框架模型 網路隨著時代發展，已經成為現代生活中越來越重要的重要的基礎，而做為提供這些服務的伺服器，負載的連線也越來越多，因此網路程式的軟體、硬體架構也一直在演變，才有辦法承載越來越多的連線數量，有人希望在一台機器能夠同時處理1萬個連線以上，所以提出C10K問題，並且之後有不少新的的技術來達成這個目標，而在此我們只專注在於軟體的架構上，首先介紹最簡單的架構 阻塞式單一行程 這樣的網路程式非常的簡單，只有一個迴圈，單一個行程，處理完一個要求後才繼續處理下一個，理所當然這樣的效能非常差，因為連線在完成前其它的連線都無法被處理，現代的伺服器已經很少看見這樣的架構，但因為優點是簡單，如果沒有什麼大量同時連線要處理，其實這樣的架構就很足夠 阻塞式多行程 因為既然單一行程只能同時處理一個請求，那很簡單的想法就是每個請求開一個行程去處理，如此一來就能同時處理更多的請求，但是這樣做有缺點，行程的copy如果是fork的話，有os paging system在成本上其實還好，但是還是有，而且越多的連線就表示需要越多的context-switch，當連線量多到一定程度時，可能大部份的CPU時間都在忙著進行context-switch，如此一來這樣的架構在此情況下是沒有效率的，但是優點是寫伺服器的部份事實上和寫單一行程阻塞式不會差太多，一樣簡單好寫 阻塞式多行程多執行緒 除了多行程多阻塞式，有些程式為了減少process copy的成本，或是其它的考量，會在多個行程上開多個執行緒來處理請求，也有可能是單行程多執行緒，但是基本上和上面這幾種都沒有太大的差別，而引進了執行緒帶來了一些額外的問題，dead lock、race condition等等，當不同的執行緒如果在一起有共同的東西要處理，這些常見的同作問題就會出現，使得程式得寫得更小心 非阻塞式事件驅動 為了解決上面提到多行程和多執行緒等所帶來的問題，有一種做法是只有單一主要的迴圈來檢查有無網路IO的事件發生，再來決定要怎樣處理，這樣的好處在於省掉了context-switch、process copy等等成本，也不會有dead lock、race condition等問題，但缺點在於程式的部份會變複雜，因為當你一件事件被觸發，有事情還沒做完，你就得記下目前狀態，再下次事件觸發時再依先前的狀態來決定接下來要做什麼，不像上面是線性的程式執行那樣直觀，Twisted就是這樣的網路框架 非阻塞式Coroutine 那你或許會想，有沒有可能我們能有事件驅動的好處，和阻塞式那樣的直觀好處呢? 答案或許就是Coroutine，基本上它的本質也是事件驅動，只有單一的迴圈在檢查事件的發生，但是加上了coroutine的概念，而Gevent就是這樣的函式庫 Coroutine 講了這麼多次coroutine，我相信大部份的讀者可能還是不懂這到底是什麼鬼東西，對於大部份程式設計師而言這應該都算是較陌生的名字，對我而言，在一開始這也是個令人困惑的名詞，但事實上只要理解以後就會發現coroutine不是這麼的難懂，用簡單的一句話來說Coroutine，就是可以暫時中斷，之後再繼續執行的程序，我們來看一個例子，事實上Python就有最基礎的Coroutine，也就是generator # -*- coding: utf8 -*- def foo&#40;&#41;: for i in range&#40;10&#41;: # 丟資料並且把主控權交給呼叫者 yield i print u'foo: 主控又回到我手上了，打我阿笨蛋' &#160; bar = foo&#40;&#41; # 執行coroutine print bar.next&#40;&#41; print u'main: 現在主控權在我們手上，做點雜事' print 'main:hello [...]]]></description>
			<content:encoded><![CDATA[<p>這篇文章是要大略介紹一下coroutine和Python的相關應用的函式庫gevent，在介紹coroutine前我們先來點情境，因為目前常見的coroutine應用都是在網路程式上，因此我們得先建立一些網路框架的模形再介紹coroutine會比較容易懂</p>
<h2>不同的網路框架模型</h2>
<p>網路隨著時代發展，已經成為現代生活中越來越重要的重要的基礎，而做為提供這些服務的伺服器，負載的連線也越來越多，因此網路程式的軟體、硬體架構也一直在演變，才有辦法承載越來越多的連線數量，有人希望在一台機器能夠同時處理1萬個連線以上，所以提出<a href="http://www.kegel.com/c10k.html">C10K問題</a>，並且之後有不少新的的技術來達成這個目標，而在此我們只專注在於軟體的架構上，首先介紹最簡單的架構</p>
<h3>阻塞式單一行程</h3>
<p>這樣的網路程式非常的簡單，只有一個迴圈，單一個行程，處理完一個要求後才繼續處理下一個，理所當然這樣的效能非常差，因為連線在完成前其它的連線都無法被處理，現代的伺服器已經很少看見這樣的架構，但因為優點是簡單，如果沒有什麼大量同時連線要處理，其實這樣的架構就很足夠</p>
<h3>阻塞式多行程</h3>
<p>因為既然單一行程只能同時處理一個請求，那很簡單的想法就是每個請求開一個行程去處理，如此一來就能同時處理更多的請求，但是這樣做有缺點，行程的copy如果是fork的話，有os paging system在成本上其實還好，但是還是有，而且越多的連線就表示需要越多的context-switch，當連線量多到一定程度時，可能大部份的CPU時間都在忙著進行context-switch，如此一來這樣的架構在此情況下是沒有效率的，但是優點是寫伺服器的部份事實上和寫單一行程阻塞式不會差太多，一樣簡單好寫</p>
<h3>阻塞式多行程多執行緒</h3>
<p>除了多行程多阻塞式，有些程式為了減少process copy的成本，或是其它的考量，會在多個行程上開多個執行緒來處理請求，也有可能是單行程多執行緒，但是基本上和上面這幾種都沒有太大的差別，而引進了執行緒帶來了一些額外的問題，dead lock、race condition等等，當不同的執行緒如果在一起有共同的東西要處理，這些常見的同作問題就會出現，使得程式得寫得更小心</p>
<h3>非阻塞式事件驅動</h3>
<p>為了解決上面提到多行程和多執行緒等所帶來的問題，有一種做法是只有單一主要的迴圈來檢查有無網路IO的事件發生，再來決定要怎樣處理，這樣的好處在於省掉了context-switch、process copy等等成本，也不會有dead lock、race condition等問題，但缺點在於程式的部份會變複雜，因為當你一件事件被觸發，有事情還沒做完，你就得記下目前狀態，再下次事件觸發時再依先前的狀態來決定接下來要做什麼，不像上面是線性的程式執行那樣直觀，<a href="http://twistedmatrix.com/trac/">Twisted</a>就是這樣的網路框架</p>
<h3>非阻塞式Coroutine</h3>
<p>那你或許會想，有沒有可能我們能有事件驅動的好處，和阻塞式那樣的直觀好處呢? 答案或許就是Coroutine，基本上它的本質也是事件驅動，只有單一的迴圈在檢查事件的發生，但是加上了coroutine的概念，而<a href="http://www.gevent.org/">Gevent</a>就是這樣的函式庫</p>
<h2>Coroutine</h2>
<p>講了這麼多次coroutine，我相信大部份的讀者可能還是不懂這到底是什麼鬼東西，對於大部份程式設計師而言這應該都算是較陌生的名字，對我而言，在一開始這也是個令人困惑的名詞，但事實上只要理解以後就會發現coroutine不是這麼的難懂，用簡單的一句話來說Coroutine，就是可以暫時中斷，之後再繼續執行的程序，我們來看一個例子，事實上Python就有最基礎的Coroutine，也就是generator</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;"># -*- coding: utf8 -*-</span>
<span style="color: #ff7700;font-weight:bold;">def</span> foo<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">for</span> i <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">10</span><span style="color: black;">&#41;</span>:
        <span style="color: #808080; font-style: italic;"># 丟資料並且把主控權交給呼叫者</span>
        <span style="color: #ff7700;font-weight:bold;">yield</span> i
        <span style="color: #ff7700;font-weight:bold;">print</span> u<span style="color: #483d8b;">'foo: 主控又回到我手上了，打我阿笨蛋'</span>
&nbsp;
bar = foo<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
<span style="color: #808080; font-style: italic;"># 執行coroutine</span>
<span style="color: #ff7700;font-weight:bold;">print</span> bar.<span style="color: black;">next</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">print</span> u<span style="color: #483d8b;">'main: 現在主控權在我們手上，做點雜事'</span>
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">'main:hello baby!'</span>
<span style="color: #808080; font-style: italic;"># 回到剛才foo這個coroutine中斷的地方繼續執行</span>
<span style="color: #ff7700;font-weight:bold;">print</span> bar.<span style="color: black;">next</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">print</span> bar.<span style="color: black;">next</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>執行結果:</p>
<pre>0
main: 現在主控權在我們手上，做點雜事
main:hello baby!
foo: 主控又回到我手上了，打我阿笨蛋
1
foo: 主控又回到我手上了，打我阿笨蛋
2</pre>
<p>看到了嗎? 我們的foo在執行的過程中被中斷又繼續了好幾次，這就是coroutine，你可能覺得這樣一點用都沒有，我在一開始也這樣想，thread的context-switch也是可以暫停然後再繼續執行，所以這樣的特性好處在哪裡? 有幾個重點在於</p>
<ul>
<li>thread之間需要context-switch，而且成本很高，但是coroutine之間的切換很快</li>
<li>coroutine的成本很低，可以很輕易的產生大量的coroutine</li>
<li>這些事情全是在同一個thread裡發生的，因此不會有race condition等問題發生</li>
<li>thread的context-switch雖然我們可以進行某種程度的控制，但是很多部份還是得靠OS來決定要先排程哪個thread，而coroutine的執行是由我們自己控制的</li>
</ul>
<p>接著我們用圖來說明coroutine和thread之間的差別</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2010/07/coroutine_diagram.jpg"><img class="alignnone size-full wp-image-1071" title="Coroutine Diagram" src="http://blog.ez2learn.com/wp-content/uploads/2010/07/coroutine_diagram.jpg" alt="" width="416" height="444" /></a></p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2010/07/thread_diagram.jpg"><img class="alignnone size-full wp-image-1072" title="Thread diagram" src="http://blog.ez2learn.com/wp-content/uploads/2010/07/thread_diagram.jpg" alt="" width="448" height="444" /></a></p>
<p>你可以發現，Coroutine所做的其實就只是在單一thread裡面不同的coroutine裡互相切換，本質上和thread很像，所以也有些coroutine叫做micro-thread，而切換通常都是我們主動去做的，看到下面這張，當你建立了不同的thread，context-switch未必是在你預期的情況下發生的，而且通常都是由OS引發的，除此之外，如果你老爸夠有錢，買了最新的Intel iCore 2000的一千核心CPU，那麼恭喜你，你的thread可能是由不同的processor執行，因此不同thread的程式真正的同時執行是有可能的</p>
<p>等等! 這時候你可能抓起你手邊的OS恐龍本敲你自己的頭，你這樣問道: 嘿! 恐龍本上面有提到FCFS、RR等等排程演算法，那Coroutine呢? 答案是如同我們前面提到，這由我們自己決定，這是好處之一，這時你可能又會抓著你的頭髮大叫 "網路呢? 網路呢? 講了半天我只看到Coroutine之間切來切去，我根本看不到在這裡面的半點網路成份，哪怕是一公克也好"</p>
<p>確實，當我一開始在讀相關資料時也很困惑，ˊ這樣跳來跳去又如何? 網路應用到底在哪裡? 現在就回到我們的主題來</p>
<h2>少了非同步IO的Coroutine，就像少了哇沙必的生魚片</h2>
<p>到目前為止都很難和網路有什麼相關的聯想，但是想到網路就會想到IO，而事實上這樣的優點就是在於有大量IO時會顯得特別好用，我們考慮一下當遇到IO時就將主控權交給別人</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2010/07/coroutine_io_diagram.jpg"><img class="alignnone size-full wp-image-1074" title="Coroutine IO diagram" src="http://blog.ez2learn.com/wp-content/uploads/2010/07/coroutine_io_diagram.jpg" alt="" width="424" height="504" /></a></p>
<p>Gevent的運作方式基本上就很類似這張圖，它的Coroutine是由greenlet實作的，而每個Coroutine都有一個parent，最頂層的Coroutine就是main thread或是當前的thread，每當Coroutine遇到IO的時候，就將主控權交給root coroutine，它會視哪些coroutine的IO event是已完成的，就將主控權交給他，其實就只是這樣而已，事實上程式寫起來完全和一般的阻塞式伺服器沒什麼兩樣，但是它千真萬確是非同步的，這就是它神奇的地方，我們來看點實際的例子，我們直接拿gevent範例裡的同步下載程式</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">#!/usr/bin/python</span>
<span style="color: #808080; font-style: italic;"># Copyright (c) 2009 Denis Bilenko. See LICENSE for details.</span>
&nbsp;
<span style="color: #483d8b;">&quot;&quot;&quot;Spawn multiple workers and wait for them to complete&quot;&quot;&quot;</span>
&nbsp;
urls = <span style="color: black;">&#91;</span><span style="color: #483d8b;">'http://www.google.com'</span>, <span style="color: #483d8b;">'http://www.yandex.ru'</span>, <span style="color: #483d8b;">'http://www.python.org'</span><span style="color: black;">&#93;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">import</span> gevent
<span style="color: #ff7700;font-weight:bold;">from</span> gevent <span style="color: #ff7700;font-weight:bold;">import</span> monkey
&nbsp;
<span style="color: #808080; font-style: italic;"># patches stdlib (including socket and ssl modules) to cooperate with other greenlets</span>
monkey.<span style="color: black;">patch_all</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">urllib2</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> print_head<span style="color: black;">&#40;</span>url<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">'Starting %s'</span> <span style="color: #66cc66;">%</span> url
    data = <span style="color: #dc143c;">urllib2</span>.<span style="color: black;">urlopen</span><span style="color: black;">&#40;</span>url<span style="color: black;">&#41;</span>.<span style="color: black;">read</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">'%s: %s bytes: %r'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>url, <span style="color: #008000;">len</span><span style="color: black;">&#40;</span>data<span style="color: black;">&#41;</span>, data<span style="color: black;">&#91;</span>:<span style="color: #ff4500;">50</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
&nbsp;
jobs = <span style="color: black;">&#91;</span>gevent.<span style="color: black;">spawn</span><span style="color: black;">&#40;</span>print_head, url<span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">for</span> url <span style="color: #ff7700;font-weight:bold;">in</span> urls<span style="color: black;">&#93;</span>
&nbsp;
gevent.<span style="color: black;">joinall</span><span style="color: black;">&#40;</span>jobs<span style="color: black;">&#41;</span></pre></div></div>

<p>這程式太簡單了對吧? 但它確實能做到極高效能的同步下載，我們做一點簡單的解釋，首先令人困惑的是monkey.patch_all()，會有這行，是因為Python內建的各種函式庫裡的IO函式庫、及會阻塞住的函數，例如Sleep都會讓整個程式卡住，而不是利用Selector/epoll之類的功能來處理，所以monkey這個函式庫就是負責將Python內建的函式庫取代成以gevent的非同步形式的函式，如此一來當執行到那些IO之類的動作，會切到MainThread的coroutine進行排程，而非直接卡在那裡等結果，而當IO動作真的完成了，gevent內部會將該coroutine標示為可執行的，因此下次有機會就會排到那個coroutine，看到下面的spawn，就是在產生coroutine，在這裡的coroutine因為事實上是greenlet這個Python函式庫題供的，所以事實上叫做greenlet</p>
<p>你可以在腦中想像一下，spawn首先呼產生了三個執行print_head的routine ，在joinall的地方，把主控權交給第一個print_head，而在函數裡遇到了urllib2.urlopen這個IO動作，因此它將自己設為等待狀態，並且將主控權交還給MainThread，而MainThread將主控權排給了第二個print_head，同樣的也遇到了urllib IO動作，第三個也是一樣，而這三個都在等待，主控權便再次回到了MainThread，它等待有哪個gevent的IO事件完成了，就將主控權交給它，如此一來，重覆這樣的過程，三個網頁的同步下載就在coroutine的切換之間完成了，當三函數return，joinall也會結束，整個程式就跑完了</p>
<p>一開始會很難理解，但一但弄清楚之後就會瞭解這樣寫法的簡單和實用性，你可以忘記它背後的原理，把它當作是一般的阻塞式網路程式來寫，就會很輕鬆，但又輕量、高效能，以上大概就談到這裡，有興趣可以玩玩看gevent</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.ez2learn.com/2010/07/17/talk-about-coroutine-and-gevent/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Simple tool for rotating nginx log file</title>
		<link>http://blog.ez2learn.com/2010/07/17/simple-tool-for-rotating-nginx-log-file/</link>
		<comments>http://blog.ez2learn.com/2010/07/17/simple-tool-for-rotating-nginx-log-file/#comments</comments>
		<pubDate>Sat, 17 Jul 2010 11:02:28 +0000</pubDate>
		<dc:creator>victor</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Crontab]]></category>
		<category><![CDATA[Logging]]></category>
		<category><![CDATA[Nginx]]></category>
		<category><![CDATA[Rotate]]></category>

		<guid isPermaLink="false">http://blog.ez2learn.com/?p=1062</guid>
		<description><![CDATA[#!/bin/env python &#34;&#34;&#34;Simple tool for rotating nginx log file &#160; @author: Victor Lin (bornstub@gmail.com) blog: http://blog.ez2learn.com &#34;&#34;&#34; import os import shutil import optparse import datetime import logging import subprocess &#160; log = logging.getLogger&#40;__name__&#41; &#160; def main&#40;&#41;: parser = optparse.OptionParser&#40;&#41; parser.add_option&#40;'-p', '--pid', dest='pidFile', metavar=&#34;FILE&#34;, help='/path/to/nginx.pid'&#41; parser.add_option&#40;'-l', '--log', dest='logFile', metavar=&#34;FILE&#34;, help='/path/to/logfile'&#41; parser.add_option&#40;'-f', '--format', dest='nameFormat', help='format of rotated [...]]]></description>
			<content:encoded><![CDATA[
<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">#!/bin/env python</span>
<span style="color: #483d8b;">&quot;&quot;&quot;Simple tool for rotating nginx log file
&nbsp;
@author: Victor Lin (bornstub@gmail.com) blog: http://blog.ez2learn.com
&quot;&quot;&quot;</span>
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">os</span>
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">shutil</span>
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">optparse</span>
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">datetime</span>
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">logging</span>
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">subprocess</span>
&nbsp;
log = <span style="color: #dc143c;">logging</span>.<span style="color: black;">getLogger</span><span style="color: black;">&#40;</span>__name__<span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> main<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #dc143c;">parser</span> = <span style="color: #dc143c;">optparse</span>.<span style="color: black;">OptionParser</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    <span style="color: #dc143c;">parser</span>.<span style="color: black;">add_option</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'-p'</span>, <span style="color: #483d8b;">'--pid'</span>, dest=<span style="color: #483d8b;">'pidFile'</span>, metavar=<span style="color: #483d8b;">&quot;FILE&quot;</span>, <span style="color: #008000;">help</span>=<span style="color: #483d8b;">'/path/to/nginx.pid'</span><span style="color: black;">&#41;</span>
    <span style="color: #dc143c;">parser</span>.<span style="color: black;">add_option</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'-l'</span>, <span style="color: #483d8b;">'--log'</span>, dest=<span style="color: #483d8b;">'logFile'</span>, metavar=<span style="color: #483d8b;">&quot;FILE&quot;</span>, <span style="color: #008000;">help</span>=<span style="color: #483d8b;">'/path/to/logfile'</span><span style="color: black;">&#41;</span>
    <span style="color: #dc143c;">parser</span>.<span style="color: black;">add_option</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'-f'</span>, <span style="color: #483d8b;">'--format'</span>, dest=<span style="color: #483d8b;">'nameFormat'</span>,
        <span style="color: #008000;">help</span>=<span style="color: #483d8b;">'format of rotated log file name'</span> <span style="color: black;">&#41;</span>
    <span style="color: #dc143c;">parser</span>.<span style="color: black;">add_option</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'-o'</span>, <span style="color: #483d8b;">'--owner'</span>, dest=<span style="color: #483d8b;">'owner'</span>, <span style="color: #008000;">help</span>=<span style="color: #483d8b;">'the owner user of log file to set'</span><span style="color: black;">&#41;</span>
    <span style="color: black;">&#40;</span>options, args<span style="color: black;">&#41;</span> = <span style="color: #dc143c;">parser</span>.<span style="color: black;">parse_args</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">exists</span><span style="color: black;">&#40;</span>options.<span style="color: black;">logFile</span><span style="color: black;">&#41;</span>:
        log.<span style="color: black;">info</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'The log file %s does not exist'</span>, options.<span style="color: black;">logFile</span><span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">return</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># move the log file</span>
    newName = <span style="color: #dc143c;">datetime</span>.<span style="color: black;">date</span>.<span style="color: black;">today</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>.<span style="color: black;">strftime</span><span style="color: black;">&#40;</span>options.<span style="color: black;">nameFormat</span><span style="color: black;">&#41;</span>
    log.<span style="color: black;">info</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Move log file %s to %s'</span>, options.<span style="color: black;">logFile</span>, newName<span style="color: black;">&#41;</span>
    <span style="color: #dc143c;">subprocess</span>.<span style="color: black;">check_call</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'mv %s %s'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>options.<span style="color: black;">logFile</span>, newName<span style="color: black;">&#41;</span>, shell=<span style="color: #008000;">True</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">if</span> options.<span style="color: black;">owner</span>:
        log.<span style="color: black;">info</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Set owner of %s to %s'</span>, newName, options.<span style="color: black;">owner</span><span style="color: black;">&#41;</span>
        <span style="color: #dc143c;">subprocess</span>.<span style="color: black;">check_call</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'chown %s %s'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>options.<span style="color: black;">owner</span>, newName<span style="color: black;">&#41;</span>, shell=<span style="color: #008000;">True</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># tell nginx to reopen the file</span>
    log.<span style="color: black;">info</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Reopen log file'</span><span style="color: black;">&#41;</span>
    pid = <span style="color: #008000;">int</span><span style="color: black;">&#40;</span><span style="color: #008000;">open</span><span style="color: black;">&#40;</span>options.<span style="color: black;">pidFile</span>, <span style="color: #483d8b;">'rt'</span><span style="color: black;">&#41;</span>.<span style="color: black;">read</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
    <span style="color: #dc143c;">os</span>.<span style="color: black;">kill</span><span style="color: black;">&#40;</span>pid, <span style="color: #ff4500;">10</span><span style="color: black;">&#41;</span>
&nbsp;
    log.<span style="color: black;">info</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'done'</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">if</span> __name__ == <span style="color: #483d8b;">'__main__'</span>:
    <span style="color: #dc143c;">logging</span>.<span style="color: black;">basicConfig</span><span style="color: black;">&#40;</span>level=<span style="color: #dc143c;">logging</span>.<span style="color: black;">INFO</span><span style="color: black;">&#41;</span>
    main<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>Usage:</p>
<blockquote><p>python nginx_log_rotate.py -p /usr/nginx/logs/nginx.pid -l "/usr/nginx/logs/YOURDOMAIN.log" -f "/home/USER/logs/YOURDOMAIN.%Y-%m-%d" -o OWNER_USER</p></blockquote>
<p>For example, you can set up a crontab task for rotating the log file like this:</p>
<blockquote><p>0 0 * * * python nginx_log_rotate.py -p /usr/nginx/logs/nginx.pid -l "/usr/nginx/logs/YOURDOMAIN.log" -f "/home/USER/logs/YOURDOMAIN.%Y-%m-%d" -o OWNER_USER</p></blockquote>
]]></content:encoded>
			<wfw:commentRss>http://blog.ez2learn.com/2010/07/17/simple-tool-for-rotating-nginx-log-file/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>A simple workaround for installation problem of PIL under virtualenv</title>
		<link>http://blog.ez2learn.com/2010/05/01/a-simple-workaround-for-installation-problem-of-pil-under-virtualenv/</link>
		<comments>http://blog.ez2learn.com/2010/05/01/a-simple-workaround-for-installation-problem-of-pil-under-virtualenv/#comments</comments>
		<pubDate>Sat, 01 May 2010 07:49:25 +0000</pubDate>
		<dc:creator>victor</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[easy_install]]></category>
		<category><![CDATA[ImportError]]></category>
		<category><![CDATA[installation]]></category>
		<category><![CDATA[PIL]]></category>
		<category><![CDATA[virtual-environment]]></category>
		<category><![CDATA[virtualenv]]></category>
		<category><![CDATA[workaround]]></category>

		<guid isPermaLink="false">http://blog.ez2learn.com/?p=1032</guid>
		<description><![CDATA[I encountered a problem when I am installing PIL under a virtualenv.  I installed it with easy_install, the output said it was installed, but however, I can&#8217;t import it under the virtual environment.  I got an ImportError when I tried to import it.  Then I inspected the directory of PIL in site-packages directory,  I noticed [...]]]></description>
			<content:encoded><![CDATA[<p>I encountered a problem when I am installing PIL under a virtualenv.  I installed it with easy_install, the output said it was installed, but however, I can&#8217;t import it under the virtual environment.  I got an ImportError when I tried to import it.  Then I inspected the directory of PIL in site-packages directory,  I noticed that it didn&#8217;t contain egg information in the directory. Also, the path in .pth file to PIL is "PIL", and you can only see content of PIL in that directory.  What does this mean?  When you tried to import PIL, it looks every path in sys.path and see are there a package PIL? But the files Python can only see is something like this inside the directory:</p>
<blockquote><p>site-packages</p>
<p style="padding-left: 30px;">PIL</p>
<p style="padding-left: 60px;">__init__.py</p>
<p style="padding-left: 60px;">_imaging.pyd</p>
<p style="padding-left: 60px;">_imagingcms.pyd</p>
<p style="padding-left: 60px;">&#8230; and other files of PIL</p>
</blockquote>
<p>See? Python can only see __init__ and those stuff belong to PIL, but it can&#8217;t find the package PIL,  that&#8217;s why it failed.  To solve this issue, it is simple, just create another PIL directory in the original PIL directory, and move every in it into the new sub PIL directory.  They you will get something like this:</p>
<blockquote><p>site-packages</p>
<p style="padding-left: 30px;">PIL</p>
<p style="padding-left: 60px;">PIL</p>
<p style="padding-left: 90px;">__init__.py</p>
<p style="padding-left: 90px;">other PIL sutff here &#8230;</p>
</blockquote>
<p>That&#8217;s it!  Now python can see and find the PIL package.  Surely, the release of PIL  it is broken,  but fortunately, it is not difficult to fix.  Hope this article could be helpful for people who also encountered this problem.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.ez2learn.com/2010/05/01/a-simple-workaround-for-installation-problem-of-pil-under-virtualenv/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PO檔自動繁簡轉換程式</title>
		<link>http://blog.ez2learn.com/2010/04/28/po-file-zh_tw-to-zh_cn-program/</link>
		<comments>http://blog.ez2learn.com/2010/04/28/po-file-zh_tw-to-zh_cn-program/#comments</comments>
		<pubDate>Tue, 27 Apr 2010 19:03:26 +0000</pubDate>
		<dc:creator>victor</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[分享]]></category>
		<category><![CDATA[國際化]]></category>
		<category><![CDATA[翻譯]]></category>
		<category><![CDATA[自動翻譯]]></category>
		<category><![CDATA[Google翻譯]]></category>
		<category><![CDATA[i18n]]></category>
		<category><![CDATA[po檔]]></category>

		<guid isPermaLink="false">http://blog.ez2learn.com/?p=1029</guid>
		<description><![CDATA[最近在更新我的網站Now.in，最麻煩的一項工作就是國際化，通常我都是先以英文寫網頁，接著用TurboGears2的i18n功能將字串訊息取出，而我的GUI程式的i18n流程也差不多，都是先寫英文，翻成繁體中文，其中一個最腦人的過程，就是把翻好的繁體中文po檔裡的字串剪下貼上到google翻譯將它變成簡體中文再貼到簡體中文的po檔裡，這些工作都是重覆性極高的機械性動作，在一開始句子還少時做還可以，當字詞越來越多，這就變成一件痛苦的工作，既然是高重覆性的工作，為什麼需要人力來做呢? 於是我就寫了一個小程式，可以自動把po檔的內容抓出來丟到google翻譯去，把結果寫到另一個po檔中 # -*- coding: utf8 -*- ''' Created on 2010/4/27 &#160; @author: Victor-mortal ''' &#160; import os import sys import urllib import json import logging import optparse import codecs import htmllib &#160; log = logging.getLogger&#40;__name__&#41; &#160; def unescape&#40;s&#41;: p = htmllib.HTMLParser&#40;None&#41; p.save_bgn&#40;&#41; p.feed&#40;s&#41; return p.save_end&#40;&#41; &#160; def translate&#40;text, sourceLanguage, destLanguage&#41;: &#34;&#34;&#34;Translate a text &#160; [...]]]></description>
			<content:encoded><![CDATA[<p>最近在更新我的網站<a href="http://now.in">Now.in</a>，最麻煩的一項工作就是國際化，通常我都是先以英文寫網頁，接著用TurboGears2的i18n功能將字串訊息取出，而我的GUI程式的i18n流程也差不多，都是先寫英文，翻成繁體中文，其中一個最腦人的過程，就是把翻好的繁體中文po檔裡的字串剪下貼上到google翻譯將它變成簡體中文再貼到簡體中文的po檔裡，這些工作都是重覆性極高的機械性動作，在一開始句子還少時做還可以，當字詞越來越多，這就變成一件痛苦的工作，既然是高重覆性的工作，為什麼需要人力來做呢? 於是我就寫了一個小程式，可以自動把po檔的內容抓出來丟到google翻譯去，把結果寫到另一個po檔中</p>
<p><code></p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;"># -*- coding: utf8 -*-</span>
<span style="color: #483d8b;">''</span><span style="color: #483d8b;">'
Created on 2010/4/27
&nbsp;
@author: Victor-mortal
'</span><span style="color: #483d8b;">''</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">os</span>
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">sys</span>
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">urllib</span>
<span style="color: #ff7700;font-weight:bold;">import</span> json
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">logging</span>
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">optparse</span>
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">codecs</span>
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">htmllib</span>
&nbsp;
log = <span style="color: #dc143c;">logging</span>.<span style="color: black;">getLogger</span><span style="color: black;">&#40;</span>__name__<span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> unescape<span style="color: black;">&#40;</span>s<span style="color: black;">&#41;</span>:
    p = <span style="color: #dc143c;">htmllib</span>.<span style="color: #dc143c;">HTMLParser</span><span style="color: black;">&#40;</span><span style="color: #008000;">None</span><span style="color: black;">&#41;</span>
    p.<span style="color: black;">save_bgn</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    p.<span style="color: black;">feed</span><span style="color: black;">&#40;</span>s<span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> p.<span style="color: black;">save_end</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> translate<span style="color: black;">&#40;</span>text, sourceLanguage, destLanguage<span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;&quot;&quot;Translate a text
&nbsp;
    @param text: text to translate
    @param sourceLanguage: the language original text is in
    @param destLanguage: language to translate to
    &quot;&quot;&quot;</span>
    log.<span style="color: black;">info</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Translate %s from %s to %s'</span>, text, sourceLanguage, destLanguage<span style="color: black;">&#41;</span>
    query = <span style="color: #008000;">dict</span><span style="color: black;">&#40;</span>v=<span style="color: #483d8b;">'1.0'</span>,
                 q=text.<span style="color: black;">encode</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'utf8'</span><span style="color: black;">&#41;</span>,
                 langpair=<span style="color: #483d8b;">'%s|%s'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>sourceLanguage, destLanguage<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
    <span style="color: #008000;">file</span> = <span style="color: #dc143c;">urllib</span>.<span style="color: black;">urlopen</span><span style="color: black;">&#40;</span>
        <span style="color: #483d8b;">'http://ajax.googleapis.com/ajax/services/language/translate'</span>,
        data=<span style="color: #dc143c;">urllib</span>.<span style="color: black;">urlencode</span><span style="color: black;">&#40;</span>query<span style="color: black;">&#41;</span>
    <span style="color: black;">&#41;</span>
    result = <span style="color: #008000;">file</span>.<span style="color: black;">read</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    <span style="color: #008000;">file</span>.<span style="color: black;">close</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    jsonResult = json.<span style="color: black;">loads</span><span style="color: black;">&#40;</span>result<span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> jsonResult<span style="color: black;">&#91;</span><span style="color: #483d8b;">'responseData'</span><span style="color: black;">&#93;</span>:
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">None</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> unescape<span style="color: black;">&#40;</span>jsonResult<span style="color: black;">&#91;</span><span style="color: #483d8b;">'responseData'</span><span style="color: black;">&#93;</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'translatedText'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> main<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #dc143c;">logging</span>.<span style="color: black;">basicConfig</span><span style="color: black;">&#40;</span>level=<span style="color: #dc143c;">logging</span>.<span style="color: black;">INFO</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #dc143c;">parser</span> = <span style="color: #dc143c;">optparse</span>.<span style="color: black;">OptionParser</span><span style="color: black;">&#40;</span>
        usage=<span style="color: #483d8b;">&quot;usage: %prog sourcePoFile destPoFile sourceLanguage destLanguage&quot;</span>
    <span style="color: black;">&#41;</span>
    <span style="color: black;">&#40;</span>_, args<span style="color: black;">&#41;</span> = <span style="color: #dc143c;">parser</span>.<span style="color: black;">parse_args</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
    sourceFilePath = args<span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>
    destFilePath = args<span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span>
    sourceLang = args<span style="color: black;">&#91;</span><span style="color: #ff4500;">2</span><span style="color: black;">&#93;</span>
    destLang = args<span style="color: black;">&#91;</span><span style="color: #ff4500;">3</span><span style="color: black;">&#93;</span>
&nbsp;
    log.<span style="color: black;">info</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Translate %s (in %s) to %s (in %s)'</span>,
             sourceFilePath, sourceLang, destFilePath, destLang<span style="color: black;">&#41;</span> 
&nbsp;
    result = <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span>
    sourceFile = <span style="color: #dc143c;">codecs</span>.<span style="color: #008000;">open</span><span style="color: black;">&#40;</span>sourceFilePath, <span style="color: #483d8b;">'rt'</span>, encoding=<span style="color: #483d8b;">'utf8'</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">for</span> line <span style="color: #ff7700;font-weight:bold;">in</span> sourceFile.<span style="color: black;">readlines</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">if</span> line.<span style="color: black;">startswith</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'msgstr'</span><span style="color: black;">&#41;</span>:
            _, msg = line.<span style="color: black;">split</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">' '</span>, <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>
            msg = msg.<span style="color: black;">strip</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
            msg = msg<span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span>:-<span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span>
            translatedMsg = translate<span style="color: black;">&#40;</span>msg, sourceLang, destLang<span style="color: black;">&#41;</span>
            <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> translatedMsg:
                translatedMsg = msg
            result.<span style="color: black;">append</span><span style="color: black;">&#40;</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'msgstr &quot;%s&quot;<span style="color: #000099; font-weight: bold;">\n</span>'</span> <span style="color: #66cc66;">%</span> translatedMsg<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">else</span>:
            result.<span style="color: black;">append</span><span style="color: black;">&#40;</span>line<span style="color: black;">&#41;</span>
    sourceFile.<span style="color: black;">close</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
    destFile = <span style="color: #dc143c;">codecs</span>.<span style="color: #008000;">open</span><span style="color: black;">&#40;</span>destFilePath, <span style="color: #483d8b;">'wt'</span>, encoding=<span style="color: #483d8b;">'utf8'</span><span style="color: black;">&#41;</span>
    destFile.<span style="color: black;">writelines</span><span style="color: black;">&#40;</span>result<span style="color: black;">&#41;</span>
    destFile.<span style="color: black;">close</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">if</span> __name__ == <span style="color: #483d8b;">'__main__'</span>:
    main<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></div></div>

<p></code></p>
<p>使用方法很簡單</p>
<blockquote><p>python po_translate.py 來源po檔 目的po檔 來源語言 目的語言</p></blockquote>
<p>而語言的代碼，在Google translate的<a href="http://code.google.com/intl/zh-TW/apis/ajaxlanguage/documentation/reference.html#LangNameArray">API文件</a>中有寫，事實上它能做的不只有繁簡轉換，要翻成其它語言也可以，只是如果你能接受那品質的話</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.ez2learn.com/2010/04/28/po-file-zh_tw-to-zh_cn-program/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>一些有用的Python函式庫列表</title>
		<link>http://blog.ez2learn.com/2010/04/24/useful-python-libraries/</link>
		<comments>http://blog.ez2learn.com/2010/04/24/useful-python-libraries/#comments</comments>
		<pubDate>Sat, 24 Apr 2010 07:51:19 +0000</pubDate>
		<dc:creator>victor</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[函式庫]]></category>
		<category><![CDATA[第三方]]></category>
		<category><![CDATA[library]]></category>

		<guid isPermaLink="false">http://blog.ez2learn.com/?p=1025</guid>
		<description><![CDATA[Python有豐富的第三方函式庫或是工具，我一直想說要整理一篇列表，今天找了一點時間整理了一下我所知道的或是用過的 視窗GUI開發 wxPython 跨平臺的GUI開發函式庫，由wxWidget移植而來，特點是原生介面 PyQT 同樣也是著名的跨平臺GUI函式庫，由QT移植而來，可惜的是它是在於GPL條款下，商業用途需要另外買授權 PySide 因為PyQT為GPL授權，所以有人另外出來搞了另一套QT的移植，它是在LGPL條款下，因此商業軟體也適合 PyGTK GTK的Python移植版 遊戲開發 pygame 一款簡單的2D遊戲開發函式庫，主要是基於SDL Python-Ogre 著名Open source 3D遊戲引擎Ogre的Python移植版本 pyglet 一款跨平台的多媒體函式庫 PyOpenGL OpenGL的python移植 Python-Hge 我寫的HGE 2d遊戲引擎的Python移植版，大致上還算可用狀態，有些部份還沒移植完全 網頁開發 TurboGears 集大成的網頁開發框架，它是組合各種合適現成的組件而成的網頁框架，因此藕合度較鬆散，可以抽換掉某些組件，第一版用的組件主要為Cherrypy為網頁伺服器、Kid為預設樣版引擎、SQLObject為ORM，而第二版架構在另一套輕量框架Pylons上，ORM改為SQLAlchemy，預設樣版改為Genshi，並且符合WSGI的規範 Django 知名的Python網頁開發框架，不同於TurboGears的集大成，它的框架是自成一體的，所有組件從樣版到ORM都是獨力完成的 Web2py 一窾很有趣的網頁框架，它的框架本身就包含了整個完整的開發環境，所有開發都是在它的網頁程式上完成的 Webpy 跟Web2py很容易搞混，但其實是另一窾不同的網頁框架 BFG 它是從Zope和Plone社群下產生的，用到了不少Zope和Plone抽離出來的技術的樣子，相對於Plone和Zope的痴肥，它的廣告詞是"pay only for what you eat" Pylons Pylons是一款輕量的網頁框架，它所強調的是彈性和快速開發，TurboGears2就是架構在Pylons上的框架 Paste 提供一些WSGI相關基礎建設、例如WSGI伺服器、設定檔、部屬等等功能的專案 WebOb 將WSGI物件化為Request和Response等物件的函式庫 ToscaWidget 將常用的網頁元件，如網頁表單、表格等等變成物件用於方便產生網頁用的函式庫 FormEncode 提供表單檢邏輯物件Validator的函式庫 Plone 知名的CMS，也就是內容管理系統，可以快速架出專業的網頁來，但缺點是很肥，它架構於Zope上 Zope Plone底層的CMS 網頁樣版 [...]]]></description>
			<content:encoded><![CDATA[<p>Python有豐富的第三方函式庫或是工具，我一直想說要整理一篇列表，今天找了一點時間整理了一下我所知道的或是用過的</p>
<h2>視窗GUI開發</h2>
<ul>
<li><a href="http://www.wxpython.org/">wxPython</a> 跨平臺的GUI開發函式庫，由wxWidget移植而來，特點是原生介面</li>
<li><a href="http://www.riverbankcomputing.co.uk/software/pyqt/intro">PyQT</a> 同樣也是著名的跨平臺GUI函式庫，由QT移植而來，可惜的是它是在於GPL條款下，商業用途需要另外買授權</li>
<li><a href="http://www.pyside.org/">PySide</a> 因為PyQT為GPL授權，所以有人另外出來搞了另一套QT的移植，它是在LGPL條款下，因此商業軟體也適合</li>
<li><a href="http://www.pygtk.org/">PyGTK</a> GTK的Python移植版</li>
</ul>
<h2>遊戲開發</h2>
<ul>
<li><a href="http://www.pygame.org/">pygame</a> 一款簡單的2D遊戲開發函式庫，主要是基於SDL</li>
<li><a href="http://www.pythonogre.com/">Python-Ogre</a> 著名Open source 3D遊戲引擎Ogre的Python移植版本</li>
<li><a href="http://www.pyglet.org/">pyglet</a> 一款跨平台的多媒體函式庫</li>
<li><a href="http://pyopengl.sourceforge.net/">PyOpenGL</a> OpenGL的python移植</li>
<li><a href="http://code.google.com/p/python-hge/">Python-Hge</a> 我寫的HGE 2d遊戲引擎的Python移植版，大致上還算可用狀態，有些部份還沒移植完全</li>
</ul>
<h2>網頁開發</h2>
<ul>
<li><a href="http://turbogears.org/">TurboGears</a> 集大成的網頁開發框架，它是組合各種合適現成的組件而成的網頁框架，因此藕合度較鬆散，可以抽換掉某些組件，第一版用的組件主要為Cherrypy為網頁伺服器、Kid為預設樣版引擎、SQLObject為ORM，而第二版架構在另一套輕量框架Pylons上，ORM改為SQLAlchemy，預設樣版改為Genshi，並且符合WSGI的規範</li>
<li><a href="http://turbogears.org/">Django</a> 知名的Python網頁開發框架，不同於TurboGears的集大成，它的框架是自成一體的，所有組件從樣版到ORM都是獨力完成的</li>
<li><a href="http://www.web2py.com/">Web2py</a> 一窾很有趣的網頁框架，它的框架本身就包含了整個完整的開發環境，所有開發都是在它的網頁程式上完成的</li>
<li><a href="http://webpy.org/">Webpy</a> 跟Web2py很容易搞混，但其實是另一窾不同的網頁框架</li>
<li><a href="http://bfg.repoze.org/">BFG</a> 它是從Zope和Plone社群下產生的，用到了不少Zope和Plone抽離出來的技術的樣子，相對於Plone和Zope的痴肥，它的廣告詞是"pay only for what you eat"</li>
<li><a href="http://pylonshq.com/">Pylons</a> Pylons是一款輕量的網頁框架，它所強調的是彈性和快速開發，TurboGears2就是架構在Pylons上的框架</li>
<li><a href="http://pythonpaste.org/">Paste</a> 提供一些WSGI相關基礎建設、例如WSGI伺服器、設定檔、部屬等等功能的專案</li>
<li><a href="http://pythonpaste.org/webob/">WebOb</a> 將WSGI物件化為Request和Response等物件的函式庫</li>
<li><a href="http://toscawidgets.org/">ToscaWidget</a> 將常用的網頁元件，如網頁表單、表格等等變成物件用於方便產生網頁用的函式庫</li>
<li><a href="http://formencode.org/">FormEncode</a> 提供表單檢邏輯物件Validator的函式庫</li>
<li><a href="http://plone.org/">Plone</a> 知名的CMS，也就是內容管理系統，可以快速架出專業的網頁來，但缺點是很肥，它架構於Zope上</li>
<li><a href="http://www.zope.org/">Zope</a> Plone底層的CMS</li>
</ul>
<h2>網頁樣版</h2>
<ul>
<li><a href="http://kid-templating.org/">Kid</a> 一款以XML串流為基礎的樣版引擎，特色是只能寫出合法的網頁，已經停止開發由Genshi繼承</li>
<li><a href="http://genshi.edgewall.org/">Genshi</a> 繼承自Kid的樣版語言，加強了Kid的缺失，增加了更多功能，例如filter可以篩飾XML串流</li>
<li><a href="http://www.makotemplates.org/">Mako</a> 一款非XML串流的樣版語言</li>
<li><a href="http://jinja.pocoo.org/">jinja</a> 另一款非XML串流式的樣版語言</li>
</ul>
<h2>網路程式開發</h2>
<ul>
<li><a href="http://twistedmatrix.com">Twisted</a> 重量級的網路程式開發框架，採用的是非同步的reactor樣式，已經實作大部份常見的Protocol，因此對於快速的伺服器開發來說非常方便</li>
<li><a href="http://www.tornadoweb.org/">Tornado</a> 另一款網路程式的非同步IO框架</li>
<li>其它 Python的網路程式開發框架實在是太多了，多到列不完，也沒有時間一個一個去試，這篇文章 <a href="http://nichol.as/asynchronous-servers-in-python">Asynchronous Servers in Python</a> 有列出不少Python的非同步伺服器開發的函式庫可以參考看看</li>
<li><a href="http://code.google.com/p/pypcap/">pypcap</a> Python抓取網路封包用的函式庫，在windows下需要Winpcap支援</li>
<li><a href="http://code.google.com/p/dpkt/">dkpt</a> 用於解析網路封包用的函式庫，可以和pypcap用於抓取封包</li>
</ul>
<h2>資料庫</h2>
<ul>
<li><a href="http://www.sqlalchemy.org/">SQLAlchemy</a> 一款資料庫的Object Relation Mapper函式庫，簡單的來說就是可以將資料庫表格和關聯映射到物件，以方便的物件操作方式來操作資料庫</li>
<li><a href="http://www.sqlobject.org/">SQLObject</a> 另一款資料庫的ORM</li>
<li><a href="http://elixir.ematia.de/">Elixir</a> 架構於SQLAlchemy上的ORM，強調於資料表的繼承、多形等等特性</li>
</ul>
<h2>其它</h2>
<ul>
<li><a href="http://codespeak.net/lxml/">lxml</a> 極有效率且強大的XML/HTML解析、處理函式庫</li>
<li><a href="http://www.py2exe.org/">py2exe</a> 將Python程式包裝成執行檔的工具程式，只限於windows下</li>
<li><a href="http://www.pyinstaller.org/">PyInstaller</a> 另一款Python打包成執行檔的工具，不同的是它不只限於windows，可以跨平台</li>
<li><a href="http://mapnik.org/">mapnik</a> 支援Python的一款GIS函式庫，可以畫出漂亮的地圖，甚至可以做出像Google map那樣的網頁</li>
<li><a href="http://matplotlib.sourceforge.net/">matplotlib</a> 強大的圖表繪圖函式庫，幾乎你想得到的圖它都畫得出來，而且可以支援多種輸出格式，也可和視窗整合</li>
<li><a href="http://code.google.com/p/python-gluttony/">gluttony</a> 我寫的用於找出Python函式庫之間關聯的工具，可以參考這篇文章 <a href="http://blog.ez2learn.com/2010/04/04/python-packages-dependency-graph-diagram-tool-gluttony/">Python套件依賴關係圖工具: Gluttony</a></li>
</ul>
<h2>遺珠之憾</h2>
<p>我短時間能想到的都寫上去了，但還有不少還沒在列表中，如果你有知道什麼覺得它應該在列表中，或是發現我寫錯什麼，請給我一個留言，我有空會補上或修正</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.ez2learn.com/2010/04/24/useful-python-libraries/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Memory efficient Python with bytearray</title>
		<link>http://blog.ez2learn.com/2010/04/22/memory-efficient-python-with-bytearray/</link>
		<comments>http://blog.ez2learn.com/2010/04/22/memory-efficient-python-with-bytearray/#comments</comments>
		<pubDate>Thu, 22 Apr 2010 10:43:16 +0000</pubDate>
		<dc:creator>victor</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[分享]]></category>
		<category><![CDATA[bytearray]]></category>
		<category><![CDATA[Memory]]></category>
		<category><![CDATA[Memory efficient]]></category>

		<guid isPermaLink="false">http://blog.ez2learn.com/?p=1002</guid>
		<description><![CDATA[The story I was developing an audio broadcasting server.  I wrote the server with Twisted.  It works fine, but there is still a big problem to solve: the memory usage.  My audio broadcasting server use memory so much.  You can see it from the following figure. As you see, the memory usage goes up like [...]]]></description>
			<content:encoded><![CDATA[<h2>The story</h2>
<p>I was developing an audio broadcasting server.  I wrote the server with Twisted.  It works fine, but there is still a big problem to solve: the memory usage.  My audio broadcasting server use memory so much.  You can see it from the following figure.</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2010/04/mem_figure7.png"><img class="alignnone size-large wp-image-1003" title="Memory usage figure" src="http://blog.ez2learn.com/wp-content/uploads/2010/04/mem_figure7-1024x580.png" alt="" width="1024" height="580" /></a></p>
<p>As you see, the memory usage goes up like crazy when there are more listeners on the line.  It is almost an exponential growth. And it doesn&#8217;t make sense why it takes so much memory.  Then I started to find the reason of memory overuse.  At first, I thought it might be some memory leaks from the C-modules of Twisted or other third-party packages.  But it is not reasonable, if it is memory leaking, why there is no relative raising of memory usage at all peaks of radios/listeners?</p>
<h2>Surprising result from guppy</h2>
<p>I inspected the memory usage detail with guppy, and get a surprising result:</p>
<blockquote>
<pre><span style="font-family: monospace;">
<pre><code>Partition of a set of 116280 objects. Total size = 9552004 bytes.
 Index  Count   %     Size   % Cumulative  % Type
  0  52874  45  4505404  47   4505404  47 str
  1   5927   5  2231096  23   6736500  71 dict
  2  29215  25  1099676  12   7836176  82 tuple
  3   7503   6   510204   5   8346380  87 types.CodeType
  4   7625   7   427000   4   8773380  92 function
  5    672   1   292968   3   9066348  95 type
  6    866   1    82176   1   9148524  96 list
  7   1796   2    71840   1   9220364  97 __builtin__.weakref
  8   1140   1    41040   0   9261404  97 __builtin__.wrapper_descriptor
  9   2603   2    31236   0   9292640  97 int</code></pre>
<p></span></pre>
</blockquote>
<pre><span style="font-family: monospace;"><span style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; line-height: 19px; white-space: normal; font-size: 13px;">Even the RSS reported by ps is almost 100MB, there is still little memory actually is used by Python objects.  I then considered another reason: the Fragmentation.</span></span></pre>
<h2>Fragmentation</h2>
<p>I was not sure at first, I also didn&#8217;t do memory leak detecting on my server.  I am just guessing.  I reviewed the design of server.   Following class is the core class of the design.</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">class</span> AudioStream<span style="color: black;">&#40;</span><span style="color: #008000;">object</span><span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;&quot;&quot;Audio stream
&nbsp;
    &quot;&quot;&quot;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, memoryLimit=<span style="color: #ff4500;">128</span><span style="color: #66cc66;">*</span><span style="color: #ff4500;">1024</span><span style="color: black;">&#41;</span>:
        <span style="color: #483d8b;">&quot;&quot;&quot;
&nbsp;
        @param memoryLimit: limit of memory usage in bytes
        &quot;&quot;&quot;</span>
        <span style="color: #808080; font-style: italic;"># limit of memory usage</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">memoryLimit</span> = memoryLimit
        <span style="color: #808080; font-style: italic;"># offset of audio stream (how many chunks)</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">offset</span> = <span style="color: #ff4500;">0</span>
        <span style="color: #808080; font-style: italic;"># queue for audio data chunk</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">chunks</span> = <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span>
        <span style="color: #808080; font-style: italic;"># total bytes of chunk in queue</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">totalSize</span> = <span style="color: #ff4500;">0</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> write<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, <span style="color: #dc143c;">chunk</span><span style="color: black;">&#41;</span>:
        <span style="color: #483d8b;">&quot;&quot;&quot;Write audio data to audio stream
&nbsp;
        @param chunk: audio data chunk to write
        &quot;&quot;&quot;</span>
        <span style="color: #808080; font-style: italic;"># append chunk to queue</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">chunks</span>.<span style="color: black;">append</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">chunk</span><span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">totalSize</span> += <span style="color: #008000;">len</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">chunk</span><span style="color: black;">&#41;</span>
&nbsp;
        <span style="color: #808080; font-style: italic;"># check the usage of memory, if exceeded, pop chunks</span>
        <span style="color: #ff7700;font-weight:bold;">while</span> <span style="color: #008000;">self</span>.<span style="color: black;">totalSize</span> <span style="color: #66cc66;">&amp;</span>gt<span style="color: #66cc66;">;</span> <span style="color: #008000;">self</span>.<span style="color: black;">memoryLimit</span>:
            poppedSize = <span style="color: #008000;">len</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: black;">chunks</span>.<span style="color: black;">pop</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
            <span style="color: #008000;">self</span>.<span style="color: black;">totalSize</span> -= poppedSize
            <span style="color: #008000;">self</span>.<span style="color: black;">offset</span> += <span style="color: #ff4500;">1</span>
            log.<span style="color: black;">debug</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'AudioStream pop chunk %d, remaining size %d, offset %d'</span>,
                poppedSize, <span style="color: #008000;">self</span>.<span style="color: black;">totalSize</span>, <span style="color: #008000;">self</span>.<span style="color: black;">offset</span><span style="color: black;">&#41;</span></pre></div></div>

<p>It is a simple idea: every radio has its own buffer: a list of string, and when a listener needs more data, just pick up a chunk and send it to peer.  A list of string?  That might be the reason of overuse of memory, I wondered.  And I tried to understand how Python manages memory.  I found the article <a href="http://evanjones.ca/memoryallocator/">Improving Python&#8217;s Memory Allocator</a>.   Then I understand how it works.  It&#8217;s not difficult,  every object smaller than 256 bytes will be allocated in the memory pool,  otherwise it will be allocated with malloc.  Then I can imagine what happened behind the scene.  The chunks are not in fixed size,  their size might be 200~300 bytes to 1xxx bytes.  The list of chunks is limited in a fixed size,  when the size of total chunks is too big,  it just pops some old chunks out.  Here is the key point of the reason of fragmentation.  You can imagine:</p>
<ol>
<li>A chunk with 987 bytes occupies a piece of memory at 0&#215;000123</li>
<li>When it is no longer needed, it was freed, then there is a free memory chunk at 0&#215;000123 with 987 bytes.</li>
<li>Here comes another memory allocation request with size 768,  malloc founds the free chunk at 0&#215;000123, it just occupies it and return</li>
<li>Here is the fragment!  987 &#8211; 768 = 219, we got a small chunk of free memory, it is too small and difficult to be used</li>
</ol>
<p>This thing happened again, again and again during the server is serving, then there will be more and more fragments !  I finally know the reason of overuse of memory.  Then here comes another question: how to fix it?  List of string is an easy way to store buffering chunks,  but it makes fragments.  I then wondered a Python C-module with memory chunk allocation and accessing function might be a good idea.</p>
<h2>The final solution: bytearray</h2>
<p>I started to write my memory chunk allocation C-module. But when I am doing it, I found there is a better solution just fits what I need: the bytearray.  I found it in the CPython source code.   It is a single memory chunk, you can change its size, it might be reallocated.  But if you don&#8217;t change its length, it is always same memory buffer.  At first, I was curious, why I can&#8217;t find anything for bytearray in Python2.6 documents? I then noticed it is a back-port from Python3, that&#8217;s why there is no document for it.  Okay, now let&#8217;s see the better solution with bytearray:</p>
<p><code></p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">class</span> AudioStream<span style="color: black;">&#40;</span><span style="color: #008000;">object</span><span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;&quot;&quot;Audio stream
&nbsp;
    &quot;&quot;&quot;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, size=<span style="color: #ff4500;">1024</span>, count=<span style="color: #ff4500;">128</span><span style="color: black;">&#41;</span>:
        <span style="color: #483d8b;">&quot;&quot;&quot;
&nbsp;
        The bytes is a big memory chunk, it buffers all incoming audio data.
        There are blocks in the memory chunk, they are the basic unit to send to
        peer. 
&nbsp;
        &amp;lt;-------------- Memory chunk ------------------&amp;gt;
        &amp;lt;--Block1--&amp;gt;&amp;lt;--Block2--&amp;gt;&amp;lt;--Block3--&amp;gt;&amp;lt;--Block4--&amp;gt;
        ^          ^          ^          ^
        L1         L2         L3         L4
&nbsp;
        We map blocks to the real audio stream
&nbsp;
        &amp;lt;------------------ Audio Stream --------------&amp;gt;  ---&amp;gt; time goes
        &amp;lt;--Block3--&amp;gt;&amp;lt;--Block4--&amp;gt;&amp;lt;--Block1--&amp;gt;&amp;lt;--Block2--&amp;gt;
&nbsp;
                          Map to
&nbsp;
        &amp;lt;-------------- Memory chunk ------------------&amp;gt;
        &amp;lt;--Block1--&amp;gt;&amp;lt;--Block2--&amp;gt;&amp;lt;--Block3--&amp;gt;&amp;lt;--Block4--&amp;gt;        
&nbsp;
        Every listener got their offset of whole audio stream, so that we can
        know which block he got.
&nbsp;
        ------------&amp;lt;------------------ Audio Stream --------------&amp;gt; ---&amp;gt;
                    &amp;lt;--Block3--&amp;gt;&amp;lt;--Block4--&amp;gt;&amp;lt;--Block1--&amp;gt;&amp;lt;--Block2--&amp;gt;
        ^
        L5
&nbsp;
        When there is a listener point to a out of buffer window place, we
        should move the pointer to the first current block.
&nbsp;
        ------------&amp;lt;------------------ Audio Stream --------------&amp;gt; ---&amp;gt;
                    &amp;lt;--Block3--&amp;gt;&amp;lt;--Block4--&amp;gt;&amp;lt;--Block1--&amp;gt;&amp;lt;--Block2--&amp;gt;
                    ^
                    L5 
&nbsp;
        @param size: size of block
        @param count: count of blocks
        &quot;&quot;&quot;</span>
        <span style="color: #008000;">self</span>._size = size
        <span style="color: #008000;">self</span>._count = count
        <span style="color: #008000;">self</span>._bufferSize = size<span style="color: #66cc66;">*</span>count
&nbsp;
        <span style="color: #808080; font-style: italic;"># offset of begin of buffer window in audio stream</span>
        <span style="color: #008000;">self</span>._offset = <span style="color: #ff4500;">0</span>
        <span style="color: #808080; font-style: italic;"># bytes array</span>
        <span style="color: #008000;">self</span>._bytes = <span style="color: #dc143c;">bytearray</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: black;">bufferSize</span><span style="color: black;">&#41;</span>
        <span style="color: #808080; font-style: italic;"># small chunks, they are not big enough to fit a block</span>
        <span style="color: #008000;">self</span>._pieces = <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span>
        <span style="color: #808080; font-style: italic;"># total size of pieces</span>
        <span style="color: #008000;">self</span>._pieceSize = <span style="color: #ff4500;">0</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> _getSize<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">self</span>._size
    size = <span style="color: #008000;">property</span><span style="color: black;">&#40;</span>_getSize<span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> _getCount<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">self</span>._count
    count = <span style="color: #008000;">property</span><span style="color: black;">&#40;</span>_getCount<span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> _getOffset<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">self</span>._offset
    offset = <span style="color: #008000;">property</span><span style="color: black;">&#40;</span>_getOffset<span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> _getBufferSize<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">self</span>._bufferSize
    bufferSize = <span style="color: #008000;">property</span><span style="color: black;">&#40;</span>_getBufferSize<span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> write<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, <span style="color: #dc143c;">chunk</span><span style="color: black;">&#41;</span>:
        <span style="color: #483d8b;">&quot;&quot;&quot;Write audio data to audio stream
&nbsp;
        @param chunk: audio data chunk to write
        &quot;&quot;&quot;</span>
        <span style="color: #808080; font-style: italic;"># append chunk to pieces</span>
        <span style="color: #008000;">self</span>._pieces.<span style="color: black;">append</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">chunk</span><span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>._pieceSize += <span style="color: #008000;">len</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">chunk</span><span style="color: black;">&#41;</span>
&nbsp;
        <span style="color: #ff7700;font-weight:bold;">while</span> <span style="color: #008000;">self</span>._pieceSize <span style="color: #66cc66;">&amp;</span>gt<span style="color: #66cc66;">;</span>= <span style="color: #008000;">self</span>.<span style="color: black;">size</span>:
            total = <span style="color: #483d8b;">''</span>.<span style="color: black;">join</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>._pieces<span style="color: black;">&#41;</span>
            block = total<span style="color: black;">&#91;</span>:<span style="color: #008000;">self</span>.<span style="color: black;">size</span><span style="color: black;">&#93;</span>
            <span style="color: #808080; font-style: italic;"># there is still some remain piece</span>
            <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #008000;">self</span>._pieceSize - <span style="color: #008000;">self</span>.<span style="color: black;">size</span> <span style="color: #66cc66;">&amp;</span>gt<span style="color: #66cc66;">;</span> <span style="color: #ff4500;">0</span>:
                <span style="color: #008000;">self</span>._pieces = <span style="color: black;">&#91;</span>total<span style="color: black;">&#91;</span><span style="color: #008000;">self</span>.<span style="color: black;">size</span>:<span style="color: black;">&#93;</span><span style="color: black;">&#93;</span>
                <span style="color: #008000;">self</span>._pieceSize = <span style="color: #008000;">len</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>._pieces<span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
            <span style="color: #ff7700;font-weight:bold;">else</span>:
                <span style="color: #008000;">self</span>._pieces = <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span>
                <span style="color: #008000;">self</span>._pieceSize = <span style="color: #ff4500;">0</span>
&nbsp;
            <span style="color: #808080; font-style: italic;"># write the block to buffer</span>
            begin = <span style="color: #008000;">self</span>.<span style="color: black;">offset</span> <span style="color: #66cc66;">%</span> <span style="color: #008000;">self</span>.<span style="color: black;">bufferSize</span>
            oldSize = <span style="color: #008000;">len</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>._bytes<span style="color: black;">&#41;</span>
            <span style="color: #008000;">self</span>._bytes<span style="color: black;">&#91;</span>begin:begin+<span style="color: #008000;">self</span>.<span style="color: black;">size</span><span style="color: black;">&#93;</span> = block
            <span style="color: #ff7700;font-weight:bold;">assert</span> <span style="color: #008000;">len</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>._bytes<span style="color: black;">&#41;</span> == oldSize, <span style="color: #483d8b;">&quot;buffer size is changed&quot;</span>
&nbsp;
            <span style="color: #008000;">self</span>._offset += <span style="color: #008000;">len</span><span style="color: black;">&#40;</span>block<span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> read<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, offset<span style="color: black;">&#41;</span>:
        <span style="color: #483d8b;">&quot;&quot;&quot;Read a block from audio stream
&nbsp;
        @param offset: offset to read block
        @return: (block, new offset)
        &quot;&quot;&quot;</span>
        begin = offset <span style="color: #66cc66;">%</span> <span style="color: #008000;">self</span>.<span style="color: black;">bufferSize</span>
        <span style="color: #ff7700;font-weight:bold;">assert</span> begin <span style="color: #66cc66;">&amp;</span>gt<span style="color: #66cc66;">;</span>= <span style="color: #ff4500;">0</span>
        <span style="color: #ff7700;font-weight:bold;">assert</span> begin <span style="color: #66cc66;">&amp;</span>lt<span style="color: #66cc66;">;</span> <span style="color: #008000;">self</span>.<span style="color: black;">bufferSize</span>
        block = <span style="color: #008000;">str</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>._bytes<span style="color: black;">&#91;</span>begin:begin+<span style="color: #008000;">self</span>.<span style="color: black;">size</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
        offset += <span style="color: #008000;">self</span>.<span style="color: black;">size</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> block, offset</pre></div></div>

<p></code><br />
With new design, there are no more allocations and deallocations.  That saves a huge mount of memory.  Here is the memory usage figure with new design:</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2010/04/mem_figure.png"><img class="alignnone size-large wp-image-1004" title="Memory usage figure with new design" src="http://blog.ez2learn.com/wp-content/uploads/2010/04/mem_figure-1024x581.png" alt="" width="1024" height="581" /></a></p>
<p>As you can see, we have almost 800 listeners on line, and the memory usage is still low and stable.  The original server uses over 100MB even with only 6x listeners on the line.  That&#8217;s huge difference.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.ez2learn.com/2010/04/22/memory-efficient-python-with-bytearray/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Ajax/Comet實作聊天室心得</title>
		<link>http://blog.ez2learn.com/2010/04/11/ajax-comet-chatroom/</link>
		<comments>http://blog.ez2learn.com/2010/04/11/ajax-comet-chatroom/#comments</comments>
		<pubDate>Sun, 11 Apr 2010 14:57:31 +0000</pubDate>
		<dc:creator>victor</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Ajax]]></category>
		<category><![CDATA[Ajax/Comet]]></category>
		<category><![CDATA[Comet]]></category>
		<category><![CDATA[now.in]]></category>
		<category><![CDATA[twisted]]></category>
		<category><![CDATA[Website]]></category>

		<guid isPermaLink="false">http://blog.ez2learn.com/?p=995</guid>
		<description><![CDATA[今天為now.in做了一個最陽春版本的聊天室 這聊天室用了AJAX/Comet技術，說穿了AJAX/Comet真的沒什麼，其實不過就一個http request在server端在接受到後，直到有事件發生才回傳，也有另一種做法是利用比較特別的header讓browser對於每次收到的資料分批處理，而這些都在一個response中完成，因為似乎後者還得有browser的支援，所以我用的是前者，而雖然概念上很簡單，但實際做起來會有些細節得注意的 前端不可以有proxy 原本我寫好後放上去結果不能跑，發現原因出在於透過WebFaction前端的Apache再rewrite到我的server，似乎在前端的連線數很快就被擠暴了，因為線上會有數百個連線一直在線上，所以如果前端有proxy，很容易就會被塞暴，後來改用直接連線，問題就解決了 一般的伺服器架構其實根本不適合Ajax/Comet 事實上一般常見的伺服器架構大多都不適合寫Ajax/Comet，為什麼呢? 原因很簡單，大多的伺服器架構都是thread pool之類的做法，一個連線進來，就從thread pool中拿一個worker丟給他去做，在完成之前都屬於在處理的階段，因此你有同時100個連線被Ajax/Comet卡住，就佔掉了thread pool中100個worker，這些worker什麼都不能做，通常只能sleep，定期檢查事件發生了沒，除了伺服器很快就會被塞暴以外，一點效率都沒有，像是Apache就是這類的架構 解決的方案: 非同步的IO 解決方案其實很簡單，就是改用非同步IO的網路框架，我個人最熟的就是Twisted，自然就是選擇這個，身為愛好Python的程式設計師，其實很多時候苦腦的不是沒有選擇，而是選擇太多，Python的非同步網路函式庫不是用一打就能數完的，多到誇張的一個地步，而且一直在增加，不久前又多出了一個Tornado，這也只是冰山一角，有人寫了一篇文章專門是在比較Python非同步網路函式庫的 Asynchronous Servers in Python 當然，這文章裡列出的也只是一部份而已 Twisted的Ajax/Comet做法 class Chatroom&#40;resource.Resource&#41;: isLeaf = True def wait&#40;self, request&#41;: &#34;&#34;&#34;Wait for notifications &#160; &#34;&#34;&#34; self.waitingRequests.append&#40;request&#41; log.debug&#40;'[%s] %d users are waiting', self.name, len&#40;self.waitingRequests&#41;&#41; return server.NOT_DONE_YET &#160; def notify&#40;self&#41;: &#34;&#34;&#34;Notify all waiting users &#160; &#34;&#34;&#34; log.info&#40;'[%s] Notify %d [...]]]></description>
			<content:encoded><![CDATA[<p>今天為<a href="http://now.in">now.in</a>做了一個最陽春版本的聊天室</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2010/04/chatroom.png"><img class="alignnone size-full wp-image-996" title="聊天室抓圖" src="http://blog.ez2learn.com/wp-content/uploads/2010/04/chatroom.png" alt="" width="770" height="297" /></a></p>
<p>這聊天室用了AJAX/Comet技術，說穿了AJAX/Comet真的沒什麼，其實不過就一個http request在server端在接受到後，直到有事件發生才回傳，也有另一種做法是利用比較特別的header讓browser對於每次收到的資料分批處理，而這些都在一個response中完成，因為似乎後者還得有browser的支援，所以我用的是前者，而雖然概念上很簡單，但實際做起來會有些細節得注意的</p>
<h2>前端不可以有proxy</h2>
<p>原本我寫好後放上去結果不能跑，發現原因出在於透過WebFaction前端的Apache再rewrite到我的server，似乎在前端的連線數很快就被擠暴了，因為線上會有數百個連線一直在線上，所以如果前端有proxy，很容易就會被塞暴，後來改用直接連線，問題就解決了</p>
<h2>一般的伺服器架構其實根本不適合Ajax/Comet</h2>
<p>事實上一般常見的伺服器架構大多都不適合寫Ajax/Comet，為什麼呢? 原因很簡單，大多的伺服器架構都是thread pool之類的做法，一個連線進來，就從thread pool中拿一個worker丟給他去做，在完成之前都屬於在處理的階段，因此你有同時100個連線被Ajax/Comet卡住，就佔掉了thread pool中100個worker，這些worker什麼都不能做，通常只能sleep，定期檢查事件發生了沒，除了伺服器很快就會被塞暴以外，一點效率都沒有，像是Apache就是這類的架構</p>
<h2>解決的方案: 非同步的IO</h2>
<p>解決方案其實很簡單，就是改用非同步IO的網路框架，我個人最熟的就是Twisted，自然就是選擇這個，身為愛好Python的程式設計師，其實很多時候苦腦的不是沒有選擇，而是選擇太多，Python的非同步網路函式庫不是用一打就能數完的，多到誇張的一個地步，而且一直在增加，不久前又多出了一個Tornado，這也只是冰山一角，有人寫了一篇文章專門是在比較Python非同步網路函式庫的</p>
<p><a href="http://nichol.as/asynchronous-servers-in-python">Asynchronous Servers in Python</a></p>
<p>當然，這文章裡列出的也只是一部份而已</p>
<h2>Twisted的Ajax/Comet做法</h2>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">class</span> Chatroom<span style="color: black;">&#40;</span><span style="color: #dc143c;">resource</span>.<span style="color: black;">Resource</span><span style="color: black;">&#41;</span>:
    isLeaf = <span style="color: #008000;">True</span>
    <span style="color: #ff7700;font-weight:bold;">def</span> wait<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, request<span style="color: black;">&#41;</span>:
        <span style="color: #483d8b;">&quot;&quot;&quot;Wait for notifications
&nbsp;
        &quot;&quot;&quot;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">waitingRequests</span>.<span style="color: black;">append</span><span style="color: black;">&#40;</span>request<span style="color: black;">&#41;</span>
        log.<span style="color: black;">debug</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'[%s] %d users are waiting'</span>,
                  <span style="color: #008000;">self</span>.<span style="color: black;">name</span>,
                  <span style="color: #008000;">len</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: black;">waitingRequests</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> server.<span style="color: black;">NOT_DONE_YET</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> notify<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        <span style="color: #483d8b;">&quot;&quot;&quot;Notify all waiting users
&nbsp;
        &quot;&quot;&quot;</span>
        log.<span style="color: black;">info</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'[%s] Notify %d users'</span>, <span style="color: #008000;">self</span>.<span style="color: black;">name</span>, <span style="color: #008000;">len</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: black;">waitingRequests</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">for</span> request <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">self</span>.<span style="color: black;">waitingRequests</span>:
            <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> request._disconnected:
                userTick = <span style="color: #008000;">int</span><span style="color: black;">&#40;</span>request.<span style="color: black;">args</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'tick'</span><span style="color: black;">&#93;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
                messages = <span style="color: #008000;">self</span>.<span style="color: black;">getMessagesByTick</span><span style="color: black;">&#40;</span>userTick<span style="color: black;">&#41;</span>
                result = <span style="color: #008000;">dict</span><span style="color: black;">&#40;</span>tick=<span style="color: #008000;">self</span>.<span style="color: black;">tick</span>, messages=messages<span style="color: black;">&#41;</span>
                request.<span style="color: black;">write</span><span style="color: black;">&#40;</span>json.<span style="color: black;">dumps</span><span style="color: black;">&#40;</span>result<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
                request.<span style="color: black;">finish</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">waitingRequests</span> = <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span></pre></div></div>

<p>省略掉雜七雜八的部份，其實最關鍵的地方很簡單，在於Twisted.web的resource的render可以回傳server.NOT_DONE_YET，來用以延後資料的回傳，接著之後要回傳時，只要呼叫request.write()就可以寫資料回去，而當request要結束時呼叫request.finish()即可，就是這麼簡單，在設計上很簡單，當發現使用者的tick已經跟上最新進度，伺服器這邊沒有新的東西給使用者，既然如此就讓使用者等待事件發生，把request丟進waitingRequest的list中，而當有新的資料進來，就可以呼叫notify，把新的資料丟回給每個在等待的使用者</p>
<h2>在一般網頁伺服器也能用Ajax/Comet的解決方案</h2>
<p>當然，並不是所有網頁程式用非同步的網路框架寫起來都會輕鬆容易，很多時候我們希望能夠在一般的PHP、Django、TurboGears網頁程式裡也能有Ajax/Comet的好處，但不幸的是這些都是同步式的伺服器架構，兩者各有優缺點，於是我就想到一個解決方案，各取其長，很簡單的解決方案就是，我們將需要長期等待事件發生的request都丟給非同步的伺服器，而一般的網頁都在一般的網頁伺服器處理，要怎麼做呢? 想法很簡單</p>
<ol>
<li>當一般的網頁伺服器有需要讓使用者等候事件發生的場合時，只要用xmlrpc呼叫非同步的網頁伺服器，建立並回傳一個對應事件的唯一的url，一般的網頁伺服器拿到url後將user轉向到此url，所有的request連到這個url在timeout前都會一直等待</li>
<li>當事件發生時，以xmlrpc呼叫非同步網頁伺服器來讓那些request停止等待</li>
<li>當client發現request已載入或失敗時，會連到一般的網頁伺服器檢查是否有新資料，有的話進行更新，沒有的話重覆步驟1繼續等待</li>
</ol>
<p>為什麼建立的url需要是唯一的呢? 很簡單，當事件的url已存在，一般的伺服器要重導給client，在這段期間如果事件被觸發，url如果一樣有可能會等到新的事件，而當事件被觸發，url會回傳404，所以整個流程可以重跑一次，可以正確並正常執行</p>
<p>這樣的解決方案有個最大的優點，就是任何網頁程式開發工具，只要是能使用xmlrpc都可以建立Ajax/Comet事件，我原本聊天室的設計就是採用這樣的方案，但是同樣的它也有不少缺點，一來就是當事件url被載入後，還要再開另一個request到我TurboGears2伺服器要資料來更新，再來就是Cross-Domain的問題，Ajax的request有不能跨domain的限制，如果將伺服器分開，這樣就沒辦法用XMLHtmlRequest來送要求，得改用iframe等解決方案，然而這也不是無解，一來是新的browser支援一種新的協定，可以讓Ajax跨domain送request，我曾經有實作過，但發現其實支援的browser實在太有限，我當時試似乎只有FireFox和某些有支援，IE? 甭提了，因此考慮到佔了大半的使用者，那不會是好的解決方案，另一種做法是將兩種server掛在同一個前端server下，用virtual host的方式，但是有個問題，如果前端的伺服器是同步式的伺服器，會遇到我先前提到的問題，所以前端的也得是非同步的伺服器，nginx似乎就是一種，Twisted也可以辦到</p>
<p>因為這樣的寫法還得加資料庫進去，為了方便加上省掉那個檢查新資料用的request，於是我就將聊天室所有東西都放在同一個Twisted的伺服器，包括訊息的暫存，這樣就簡單又可靠</p>
<h2>Twisted + Ajax/Comet = TwistedComet</h2>
<p>上段中提到的解決方案，因為是用Twisted加上又是Ajax/Comet所以叫TwistedComet，我開了一個Google code project，把程式丟上去，其實程式非常地簡單，就只是如上面提到，提供xmlrpc介面，然後提供非同步request的等待和觸發等動作，專案的網址在此</p>
<p><a href="http://code.google.com/p/twisted-comet/">TwistedComet</a></p>
<p>有興趣可以玩看看</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.ez2learn.com/2010/04/11/ajax-comet-chatroom/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Python套件依賴關係圖工具: Gluttony</title>
		<link>http://blog.ez2learn.com/2010/04/04/python-packages-dependency-graph-diagram-tool-gluttony/</link>
		<comments>http://blog.ez2learn.com/2010/04/04/python-packages-dependency-graph-diagram-tool-gluttony/#comments</comments>
		<pubDate>Sun, 04 Apr 2010 04:20:07 +0000</pubDate>
		<dc:creator>victor</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[依賴關係圖]]></category>
		<category><![CDATA[Dependency diagram]]></category>
		<category><![CDATA[Gluttony]]></category>

		<guid isPermaLink="false">http://blog.ez2learn.com/?p=988</guid>
		<description><![CDATA[前一陣子在裝TurboGears2時遇到某個套件因為依賴的某個套件不支援Python2.6，無法使用easy_install成功安裝，必需先手動安裝那個套件再安裝TurboGears2，我當時覺得很討厭，就開始思考，當一個專案的依賴套件越來越多時，只要其中有一個套件不能用，整個系統可能就會因此無法使用，特別是在越底層的套件出問題，所造成的影響可能就會越大，我開始想說，如果我們能夠把這些套件之間的依賴關係圖用一個工具畫出來，那就可以瞭解它的依賴情況，如此一來在決定是否要使用或是找問題時會較容易，也可以研究依賴關係對於大型專案會造成的影響，於是就花了一點時間寫了一個工具，是基於pip上面的，它叫做Gluttony，如果有看過鋼之鍊金術士大概就知道那是什麼東西，七原罪之一的暴食，因為我看那工具在爬依賴套件時就像在瘋狂吃東西似的，於是就想到這名字，而且這個名字在pypi裡沒有人用，於是就選了這個名字 工具的用法和下載、安裝等等，都可以在Google code的頁面找到，在此就不贅述 Google code: Glottony 一些專案的依賴關係圖 當我完成這些工具之後，最開心的事就是去找一些專案來跑跑看，看到底會出現什麼結果 TurboGears1.1 像這樣的依賴關系其實算還好，簡單明瞭，而且也很有結構 TurboGears2 我們可以發現在TurboGears2的依賴關係又更複雜了一些，因為它是架構在Pylons上，所以大部份依賴都在Pylons上，而devtools是用於開發用的工具，事實上有一些細節上的依賴關係會在setup專案時才會被裝進來，因為TurboGears2講求的是彈性，像是Template language就可以支援好幾種，也可以自行將那部份換掉，所以圖上可能看不到，整體上來看，這樣的依賴關係算是還蠻健康的，層次分明，接著我們就來看一個病態一點的例子 Plone 點圖片下載全尺吋圖檔 你一定在想這沱黑黑的到底是什麼東西，別懷疑，這個是Plone的套件依賴關系圖，全圖檔案大小是19.3MB，原本圖檔還太大，Graphiz還render不出來，我是縮小了一點尺吋才畫出來的，看了我真的覺得這實在是太誇張了，Plone到底是怎樣開發出來的，我很慶幸當時把ez2learn.com從Plone移到其它CMS，我原本還打算學Plone怎麼玩，還好當時覺得他底層東西實在太多，不然可能到現在我還在研究 囧]]></description>
			<content:encoded><![CDATA[<p>前一陣子在裝TurboGears2時遇到某個套件因為依賴的某個套件不支援Python2.6，無法使用easy_install成功安裝，必需先手動安裝那個套件再安裝TurboGears2，我當時覺得很討厭，就開始思考，當一個專案的依賴套件越來越多時，只要其中有一個套件不能用，整個系統可能就會因此無法使用，特別是在越底層的套件出問題，所造成的影響可能就會越大，我開始想說，如果我們能夠把這些套件之間的依賴關係圖用一個工具畫出來，那就可以瞭解它的依賴情況，如此一來在決定是否要使用或是找問題時會較容易，也可以研究依賴關係對於大型專案會造成的影響，於是就花了一點時間寫了一個工具，是基於<a href="http://pip.openplans.org/">pip</a>上面的，它叫做Gluttony，如果有看過鋼之鍊金術士大概就知道那是什麼東西，七原罪之一的暴食，因為我看那工具在爬依賴套件時就像在瘋狂吃東西似的，於是就想到這名字，而且這個名字在pypi裡沒有人用，於是就選了這個名字</p>
<p>工具的用法和下載、安裝等等，都可以在Google code的頁面找到，在此就不贅述</p>
<p>Google code: <a href="http://code.google.com/p/python-gluttony/">Glottony</a></p>
<h2>一些專案的依賴關係圖</h2>
<p>當我完成這些工具之後，最開心的事就是去找一些專案來跑跑看，看到底會出現什麼結果</p>
<h3>TurboGears1.1</h3>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2010/04/tg1.1.png"><img class="alignnone size-large wp-image-989" title="TurboGears1.1依賴關系圖" src="http://blog.ez2learn.com/wp-content/uploads/2010/04/tg1.1-1024x301.png" alt="TurboGears1.1依賴關系圖" width="1024" height="301" /></a></p>
<p>像這樣的依賴關系其實算還好，簡單明瞭，而且也很有結構</p>
<h3>TurboGears2</h3>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2010/04/tg2.png"><img class="alignnone size-large wp-image-990" title="TurboGears2依賴關系圖" src="http://blog.ez2learn.com/wp-content/uploads/2010/04/tg2-1024x177.png" alt="TurboGears2依賴關系圖" width="1024" height="177" /></a></p>
<p>我們可以發現在TurboGears2的依賴關係又更複雜了一些，因為它是架構在Pylons上，所以大部份依賴都在Pylons上，而devtools是用於開發用的工具，事實上有一些細節上的依賴關係會在setup專案時才會被裝進來，因為TurboGears2講求的是彈性，像是Template language就可以支援好幾種，也可以自行將那部份換掉，所以圖上可能看不到，整體上來看，這樣的依賴關係算是還蠻健康的，層次分明，接著我們就來看一個病態一點的例子</p>
<h3>Plone</h3>
<p><a href="http://python-gluttony.googlecode.com/files/plone.png"><img class="alignnone size-full wp-image-991" title="Plone依賴關係圖" src="http://blog.ez2learn.com/wp-content/uploads/2010/04/plone_small.png" alt="Plone依賴關係圖" width="590" height="48" /></a></p>
<p>點圖片下載全尺吋圖檔</p>
<p>你一定在想這沱黑黑的到底是什麼東西，別懷疑，這個是Plone的套件依賴關系圖，全圖檔案大小是19.3MB，原本圖檔還太大，Graphiz還render不出來，我是縮小了一點尺吋才畫出來的，看了我真的覺得這實在是太誇張了，Plone到底是怎樣開發出來的，我很慶幸當時把ez2learn.com從Plone移到其它CMS，我原本還打算學Plone怎麼玩，還好當時覺得他底層東西實在太多，不然可能到現在我還在研究 囧</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.ez2learn.com/2010/04/04/python-packages-dependency-graph-diagram-tool-gluttony/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>讓Python程式出錯時自動寄信給你</title>
		<link>http://blog.ez2learn.com/2010/03/29/python-error-mail-repor/</link>
		<comments>http://blog.ez2learn.com/2010/03/29/python-error-mail-repor/#comments</comments>
		<pubDate>Mon, 29 Mar 2010 11:49:51 +0000</pubDate>
		<dc:creator>victor</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Email]]></category>
		<category><![CDATA[Error report]]></category>
		<category><![CDATA[Logging]]></category>

		<guid isPermaLink="false">http://blog.ez2learn.com/?p=979</guid>
		<description><![CDATA[我喜歡Python的原因之一就是它的標準函式庫把該有的東西幾乎都包進去了，像是ConfigParser用來讀取設定檔，而logging更是不可缺少的，用來記錄程式的訊息非常好用的函式庫，而且這些函式庫不是只是陽春的玩具，而是功能齊全的基礎，其中logging一個很棒的功能就是將錯誤訊息寄給你，因為通常錯誤發生雖然都可以寫到記錄檔中，但是你有多少時間會去翻記錄檔? 通常程式出錯了，你也不會有認何知覺，將錯誤訊息寄給你的好處就是，一有錯誤你就立刻可以知道，而且少見的錯誤也不會被漏掉，例如有人試圖攻擊你的伺服器，在這過程中如果伺服器發生錯誤會寄信給你，在他成功之前你可能就有機會發現，總之將錯誤訊息寄給自己是很有用的技巧 一個錯誤報告的例子 最棒的就是，要用此功能非常簡單，以下是一個簡單的範例: # -*- coding: utf8 -*- import logging import logging.handlers &#160; rootLogger = logging.getLogger&#40;''&#41; rootLogger.setLevel&#40;logging.ERROR&#41; handler = logging.handlers.SMTPHandler&#40; mailhost='smtp.example.com', fromaddr='marines@starcraft2.com', toaddrs='player@starcraft2.com', subject=&#34;Houston, We've Got a Problem&#34;, credentials=&#40;'username', 'password'&#41; &#41; rootLogger.addHandler&#40;handler&#41; &#160; log = logging.getLogger&#40;__name__&#41; log.fatal&#40;'HELP! We are under attack!'&#41; 如果你的SMTP設定沒問題的話，以應該可以收到下面這樣的一封信 就是這麼簡單，這樣一來當你的程式出問題時，你再也不會沒有發覺了]]></description>
			<content:encoded><![CDATA[<p>我喜歡Python的原因之一就是它的標準函式庫把該有的東西幾乎都包進去了，像是ConfigParser用來讀取設定檔，而logging更是不可缺少的，用來記錄程式的訊息非常好用的函式庫，而且這些函式庫不是只是陽春的玩具，而是功能齊全的基礎，其中logging一個很棒的功能就是將錯誤訊息寄給你，因為通常錯誤發生雖然都可以寫到記錄檔中，但是你有多少時間會去翻記錄檔? 通常程式出錯了，你也不會有認何知覺，將錯誤訊息寄給你的好處就是，一有錯誤你就立刻可以知道，而且少見的錯誤也不會被漏掉，例如有人試圖攻擊你的伺服器，在這過程中如果伺服器發生錯誤會寄信給你，在他成功之前你可能就有機會發現，總之將錯誤訊息寄給自己是很有用的技巧</p>
<p>一個錯誤報告的例子<br />
<a href="http://blog.ez2learn.com/wp-content/uploads/2010/03/error-report.png"><img class="alignnone size-full wp-image-980" title="error-report" src="http://blog.ez2learn.com/wp-content/uploads/2010/03/error-report.png" alt="" width="921" height="421" /></a></p>
<p>最棒的就是，要用此功能非常簡單，以下是一個簡單的範例:</p>
<p><code></p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;"># -*- coding: utf8 -*-</span>
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">logging</span>
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">logging</span>.<span style="color: black;">handlers</span>
&nbsp;
rootLogger = <span style="color: #dc143c;">logging</span>.<span style="color: black;">getLogger</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">''</span><span style="color: black;">&#41;</span>
rootLogger.<span style="color: black;">setLevel</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">logging</span>.<span style="color: black;">ERROR</span><span style="color: black;">&#41;</span>
handler = <span style="color: #dc143c;">logging</span>.<span style="color: black;">handlers</span>.<span style="color: black;">SMTPHandler</span><span style="color: black;">&#40;</span>
    mailhost=<span style="color: #483d8b;">'smtp.example.com'</span>,
    fromaddr=<span style="color: #483d8b;">'marines@starcraft2.com'</span>,
    toaddrs=<span style="color: #483d8b;">'player@starcraft2.com'</span>,
    subject=<span style="color: #483d8b;">&quot;Houston, We've Got a Problem&quot;</span>,
    credentials=<span style="color: black;">&#40;</span><span style="color: #483d8b;">'username'</span>, <span style="color: #483d8b;">'password'</span><span style="color: black;">&#41;</span>
<span style="color: black;">&#41;</span>
rootLogger.<span style="color: black;">addHandler</span><span style="color: black;">&#40;</span>handler<span style="color: black;">&#41;</span>
&nbsp;
log = <span style="color: #dc143c;">logging</span>.<span style="color: black;">getLogger</span><span style="color: black;">&#40;</span>__name__<span style="color: black;">&#41;</span>
log.<span style="color: black;">fatal</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'HELP! We are under attack!'</span><span style="color: black;">&#41;</span></pre></div></div>

<p></code></p>
<p>如果你的SMTP設定沒問題的話，以應該可以收到下面這樣的一封信</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2010/03/error-report2.png"><img class="alignnone size-full wp-image-982" title="error-report2" src="http://blog.ez2learn.com/wp-content/uploads/2010/03/error-report2.png" alt="" width="373" height="256" /></a></p>
<p>就是這麼簡單，這樣一來當你的程式出問題時，你再也不會沒有發覺了</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.ez2learn.com/2010/03/29/python-error-mail-repor/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
