<?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>Monolith &#187; python</title>
	<atom:link href="http://blog.monolith.pe.kr/archives/category/python/feed" rel="self" type="application/rss+xml" />
	<link>http://blog.monolith.pe.kr</link>
	<description>Everything is connected to everything else, somehow.</description>
	<lastBuildDate>Wed, 28 Jul 2010 12:52:00 +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>CodeBomb</title>
		<link>http://blog.monolith.pe.kr/archives/1159</link>
		<comments>http://blog.monolith.pe.kr/archives/1159#comments</comments>
		<pubDate>Mon, 12 Apr 2010 03:13:55 +0000</pubDate>
		<dc:creator>Haandol</dc:creator>
				<category><![CDATA[python]]></category>
		<category><![CDATA[일기]]></category>
		<category><![CDATA[codebomb]]></category>
		<category><![CDATA[mashup]]></category>
		<category><![CDATA[매쉬업]]></category>
		<category><![CDATA[파이썬]]></category>

		<guid isPermaLink="false">http://blog.monolith.pe.kr/?p=1159</guid>
		<description><![CDATA[원래 이름은 devday였는데 다음의 devday와 같다는 이유로 굳이 코드(code)로 날밤(bomb)을 깐다는 의미를 억지로 부여해가며 codebomb으로 이름을 바꿨다. 1회 행사에는 연구생위주로 구성해서 3명이서 조촐하게 했는데, 생각보다 오래걸렸지만 나름 재미있었던 것 같다. 이번에는 6명이 2인 1조, 총 3개팀을 구성해서 코딩해보는 계획을 가지고 있다. 사실 대안언어 축제 라던지, TEDx 같은 행사를 하고 싶은데 학생 수준에 맞지 않아서 아쉽다. 이번에도 [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fblog.monolith.pe.kr%2Farchives%2F1159"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fblog.monolith.pe.kr%2Farchives%2F1159&amp;style=normal" height="61" width="50" /><br />
			</a>
		</div>
<p>원래 이름은 devday였는데 다음의 devday와 같다는 이유로 굳이 코드(code)로 날밤(bomb)을 깐다는 의미를 억지로 부여해가며 <a href="http://codebomb.monolith.pe.kr" target="_blank">codebomb</a>으로 이름을 바꿨다.</p>
<p><a href="http://blog.monolith.pe.kr/archives/1027" target="_blank">1회 행사</a>에는 연구생위주로 구성해서 3명이서 조촐하게 했는데, 생각보다 오래걸렸지만 나름 재미있었던 것 같다.</p>
<p>이번에는 6명이 2인 1조, 총 3개팀을 구성해서 코딩해보는 계획을 가지고 있다.</p>
<p>사실 대안언어 축제 라던지, TEDx 같은 행사를 하고 싶은데 학생 수준에 맞지 않아서 아쉽다.</p>
<p>이번에도 파이썬을 이용한 매쉬업을 하는데, 1회 보다는 좀 더 짜임새 있게 할 수 있지 않을까 생각해본다.</p>
<div class="prezi-player"><!-- .prezi-player { width: 550px; } .prezi-player-links { text-align: center; } --><object id="prezi_5nozcnbfsf_2" classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="550" height="400" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"><param name="name" value="prezi_5nozcnbfsf_2" /><param name="allowfullscreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="bgcolor" value="#ffffff" /><param name="flashvars" value="prezi_id=5nozcnbfsf_2&amp;lock_to_path=1&amp;color=ffffff&amp;autoplay=no" /><param name="src" value="http://prezi.com/bin/preziloader.swf" /><embed id="prezi_5nozcnbfsf_2" type="application/x-shockwave-flash" width="550" height="400" src="http://prezi.com/bin/preziloader.swf" flashvars="prezi_id=5nozcnbfsf_2&amp;lock_to_path=1&amp;color=ffffff&amp;autoplay=no" bgcolor="#ffffff" allowscriptaccess="always" allowfullscreen="true" name="prezi_5nozcnbfsf_2"></embed></object></p>
<div class="prezi-player-links">
<p><a title="description" href="http://prezi.com/5nozcnbfsf_2/">Codebomb</a> on <a href="http://prezi.com">Prezi</a></p>
</div>
</div>
]]></content:encoded>
			<wfw:commentRss>http://blog.monolith.pe.kr/archives/1159/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>[Python] ImportError _imagingft 해결하기</title>
		<link>http://blog.monolith.pe.kr/archives/926</link>
		<comments>http://blog.monolith.pe.kr/archives/926#comments</comments>
		<pubDate>Tue, 08 Dec 2009 10:03:58 +0000</pubDate>
		<dc:creator>Haandol</dc:creator>
				<category><![CDATA[python]]></category>
		<category><![CDATA[imagefont]]></category>
		<category><![CDATA[imagingft]]></category>
		<category><![CDATA[truetype]]></category>

		<guid isPermaLink="false">http://blog.monolith.pe.kr/?p=926</guid>
		<description><![CDATA[이 포스트를 보고 있다면 아마 _imagingft 를 키워드로 검색을 했으리라 생각된다. 그리고 그 원인은 아마도 글자를 이미지로 인쇄하기 위한 ImageFont.truetype 메소드일 가능성이 매우 크리라. 이 문제는 PIL1.1.5 또는 PIL1.1.6을, tarball로 설치한 python2.6 에서 임포트할 때 생긴다. 진작에 ImageFont.py 를 뜯어봤으면 편한데 &#8216;너무&#8217; 쉽게 가려다 뱅뱅돌았다. 아인슈타인이 했던 말이 생각나는 순간이다. 원인은 ImageFont의 truetype이 freetype 라이브러리를 [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fblog.monolith.pe.kr%2Farchives%2F926"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fblog.monolith.pe.kr%2Farchives%2F926&amp;style=normal" height="61" width="50" /><br />
			</a>
		</div>
<p>이 포스트를 보고 있다면 아마 _imagingft 를 키워드로 검색을 했으리라 생각된다.</p>
<p>그리고 그 원인은 아마도 글자를 이미지로 인쇄하기 위한 ImageFont.truetype 메소드일 가능성이 매우 크리라.</p>
<p><br class="spacer_" /></p>
<p>이 문제는 PIL1.1.5 또는 PIL1.1.6을, tarball로 설치한 python2.6 에서 임포트할 때 생긴다.</p>
<p>진작에 ImageFont.py 를 뜯어봤으면 편한데 &#8216;너무&#8217; 쉽게 가려다 뱅뱅돌았다. 아인슈타인이 했던 말이 생각나는 순간이다.</p>
<p><br class="spacer_" /></p>
<p>원인은 ImageFont의 truetype이 freetype 라이브러리를 이용하고 있다는 점이고 해결방법은 다음의 3단계로 해결된다.</p>
<p>1. libfreetype6-dev 패키지 를 설차한다. apt-get이나 aptitude 등으로 설치하면 되겠다.</p>
<p>2. python2.6을 새로 컴파일해 설치한다.</p>
<p>3. PIL-1.1.6 을 새로 컴파일해 설치한다.</p>
<p>끗.</p>
<p>관련링크 : <a href="http://mail.python.org/pipermail/image-sig/2007-November/004651.html">http://mail.python.org/pipermail/image-sig/2007-November/004651.html</a></p>
<p>P.S 관련링크는 구글에서 _imagingft 를 검색어로 하면 첫페이지에 나오는 결과인데 간과하고 넘어갔다.. 이유는 제목이 _imagingft for OSX 였기 때문&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.monolith.pe.kr/archives/926/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>[Python]ImageFont unicode 글쓰기</title>
		<link>http://blog.monolith.pe.kr/archives/924</link>
		<comments>http://blog.monolith.pe.kr/archives/924#comments</comments>
		<pubDate>Mon, 07 Dec 2009 09:48:03 +0000</pubDate>
		<dc:creator>Haandol</dc:creator>
				<category><![CDATA[python]]></category>
		<category><![CDATA[ImageFont Python PIL]]></category>

		<guid isPermaLink="false">http://blog.monolith.pe.kr/?p=924</guid>
		<description><![CDATA[ImageFont 모듈은 사실 글쓰기는 아니고 글씨를 이미지로 쓰는 작업을 하는데 사용한다. 이미지에 글씨를 넣고 글씨가 포함된 이미지를 다시 내보내기 한다던지 하는 경우에 사용할 수 있겠다. 먼저 코드를 보자 def drawImage(myText, imageFile, targetFile): fontFile = '/usr/lib/fonts/gulim.ttc' im = Image.open(imageFile) draw = ImageDraw.Draw(im) font = ImageFont.truetype(fontFile, 15, encoding='unic') draw.text((0,0), myText.encode('utf-8').decode('utf-8'), font=font, fill=1) im.save(targetFile) 순서는 Image를 열어서 그릴 [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fblog.monolith.pe.kr%2Farchives%2F924"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fblog.monolith.pe.kr%2Farchives%2F924&amp;style=normal" height="61" width="50" /><br />
			</a>
		</div>
<p>ImageFont 모듈은 사실 글쓰기는 아니고 글씨를 이미지로 쓰는 작업을 하는데 사용한다. 이미지에 글씨를 넣고 글씨가 포함된 이미지를 다시 내보내기 한다던지 하는 경우에 사용할 수 있겠다.</p>
<p>먼저 코드를 보자</p>
<pre name="code" class="python">
def drawImage(myText, imageFile, targetFile):
fontFile = '/usr/lib/fonts/gulim.ttc'

im = Image.open(imageFile)
draw = ImageDraw.Draw(im)
font = ImageFont.truetype(fontFile, 15, encoding='unic')

draw.text((0,0), myText.encode('utf-8').decode('utf-8'), font=font, fill=1)
im.save(targetFile)
</pre>
<p>순서는 Image를 열어서 그릴 수 있도록 ImageDraw.Draw 객체를 만든다.</p>
<p>Draw 객체의 text 함수는 ImageFont객체를 통해 지정된 폰트로(없으면 기본 시스템폰트이고 기본크기는 10정도 되는 것 같다. &#8212; 코드는 귀찮아서 안까봄) 아까 입력받은 그림에 글씨를 써준다.</p>
<p>마지막으로 그림을 해당이미지 객체를 저장해서 써진 글씨를 포함한 수정된 그림을 파일로 저장한다.</p>
<p>myText 가 u&#8217;유니코드&#8217; 같은 unicode 일 경우에는 그냥 draw.text 를 사용하면 되지만 utf-8 이나 cp949 형식이면 유니코드로 변경해서 출력하는 편이 더 낫다.</p>
<p>왠일인지 구글에서 검색했을 때 ImageFont.truetype 의 매개변수로 encoding=&#8217;utf-8&#8242; 이라고 되어 있는 코드를 많이 볼 수 있는데, PIL함수 명세에는 언급되지도 않은 &#8216;utf-8&#8242;은 어디서 왔는지 잘 모르겠다. 사실 &#8216;utf-8&#8242;이라고 인코딩을 잡아도 상관없이 돌아가는 걸로 봐서는 암시적으로 지원하는 것 같기도 하고 -_-?</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.monolith.pe.kr/archives/924/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>오라클 포트 변경하기(Change the listener port for oracle 10g XE)</title>
		<link>http://blog.monolith.pe.kr/archives/904</link>
		<comments>http://blog.monolith.pe.kr/archives/904#comments</comments>
		<pubDate>Mon, 16 Nov 2009 11:41:36 +0000</pubDate>
		<dc:creator>Haandol</dc:creator>
				<category><![CDATA[python]]></category>
		<category><![CDATA[oracle]]></category>
		<category><![CDATA[오라클]]></category>
		<category><![CDATA[파이썬]]></category>

		<guid isPermaLink="false">http://blog.monolith.pe.kr/?p=904</guid>
		<description><![CDATA[정규포트인 1521에서 다른 포트(9000)로 옮기면 $ORACLE_HOME/network/admin/listener.ora 에 있는 SID_LIST에 리스너를 추가해야한다. #listener.ora Network Configuration File: SID_LIST_LISTENER = (SID_LIST = (SID_DESC = (SID_NAME = PLSExtProc) (ORACLE_HOME = /usr/lib/oracle/xe/app/oracle/product/10.2.0/server) (PROGRAM = extproc) ) (SID_DESC = (GLOBAL_DBNAME = XE) (ORACLE_HOME = /usr/lib/oracle/xe/app/oracle/product/10.2.0/server) (SID_NAME = XE) ) ) LISTENER = (DESCRIPTION_LIST = (DESCRIPTION = (ADDRESS = (PROTOCOL = IPC)(KEY [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fblog.monolith.pe.kr%2Farchives%2F904"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fblog.monolith.pe.kr%2Farchives%2F904&amp;style=normal" height="61" width="50" /><br />
			</a>
		</div>
<p>정규포트인 1521에서 다른 포트(9000)로 옮기면 $ORACLE_HOME/network/admin/listener.ora 에 있는 SID_LIST에 리스너를 추가해야한다.</p>
<pre name="code" class="shell" width="545" lines="30">
#listener.ora Network Configuration File:

SID_LIST_LISTENER =
(SID_LIST =
(SID_DESC =
(SID_NAME = PLSExtProc)
(ORACLE_HOME = /usr/lib/oracle/xe/app/oracle/product/10.2.0/server)
(PROGRAM = extproc)
)
(SID_DESC =
(GLOBAL_DBNAME = XE)
(ORACLE_HOME = /usr/lib/oracle/xe/app/oracle/product/10.2.0/server)
(SID_NAME = XE)
)
)

LISTENER =
(DESCRIPTION_LIST =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC_FOR_XE))
(ADDRESS = (PROTOCOL = TCP)(HOST = 아이피를 입력하시면 됩니다.)(PORT = 포트를 입력하시면 됩니다.))
)
)

TRACE_LEVEL_LISTENER = admin

DEFAULT_SERVICE_LISTENER = (XE)
</pre>
]]></content:encoded>
			<wfw:commentRss>http://blog.monolith.pe.kr/archives/904/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Django로 url shortener 구현하기</title>
		<link>http://blog.monolith.pe.kr/archives/856</link>
		<comments>http://blog.monolith.pe.kr/archives/856#comments</comments>
		<pubDate>Mon, 21 Sep 2009 12:16:53 +0000</pubDate>
		<dc:creator>Haandol</dc:creator>
				<category><![CDATA[python]]></category>
		<category><![CDATA[웹]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[short]]></category>
		<category><![CDATA[trim]]></category>
		<category><![CDATA[url]]></category>
		<category><![CDATA[url shortener]]></category>

		<guid isPermaLink="false">http://blog.monolith.pe.kr/?p=856</guid>
		<description><![CDATA[동기 http://www.kumi-dongkwang.com 을 Django로 만들고 있는데, URL shortening(이하 URL줄이기)이 필요해서 직접 구현하기로 했다. 알고리즘 구글링한 결과 URL 줄이기의 기본 원리는 다음과 같다. 1. 긴 URL을 저장하고 incremental id를 생성한다. (ex. 0부터 증가하는 숫자) 2. 긴 URL의 아이디를 Base62로 인코딩하여 키 저장한다. 3. 2에서 저장한 인코딩 된 키가 우리가 보는 짧은 URL의 주소가 된다. (ex. http://tr.im/0aoi8 [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fblog.monolith.pe.kr%2Farchives%2F856"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fblog.monolith.pe.kr%2Farchives%2F856&amp;style=normal" height="61" width="50" /><br />
			</a>
		</div>
<h2>동기</h2>
<p><a href="http://www.kumi-dongkwang.com">http://www.kumi-dongkwang.com</a> 을 Django로 만들고 있는데, URL shortening(이하 URL줄이기)이 필요해서 직접 구현하기로 했다.</p>
<h2>알고리즘</h2>
<p>구글링한 결과 URL 줄이기의 기본 원리는 다음과 같다.</p>
<p>1. 긴 URL을 저장하고 incremental id를 생성한다. (ex. 0부터 증가하는 숫자)</p>
<p>2. 긴 URL의 아이디를 Base62로 인코딩하여 키 저장한다.</p>
<p>3. 2에서 저장한 인코딩 된 키가 우리가 보는 짧은 URL의 주소가 된다. (ex. http://tr.im/0aoi8 에서 0aoi8 이 키가된다.)</p>
<p>* Base62 : 0-9, a-z, A-Z 가 총 62자 이므로 62진수 표기를 하는게 Cool url 을 유지하는 최적의 방법이 된다.</p>
<h2>효과</h2>
<p>효과는 다음과 같다.<sup>[1]</sup></p>
<p>Digit Base 10 Base 64</p>
<p>2 100 4,096</p>
<p>3 1,000 262,144</p>
<p>4 10,000 16,777,216</p>
<p>5 100,000 1,073,741,824</p>
<p>6 1,000,000 68,719,476,736</p>
<p>보면 알겠지만 6자리로 표기할 때 기존 10진수 ID에 비해 68,719배 높은 표현상의 효율을 보인다.</p>
<h2>구현</h2>
<p>Django에 익숙하다고 생각하고 디테일한 부분은 빼고, 따라해서 구현하는데 꼭 필요한 부분만 짚고 넘어간다.</p>
<p>적당한 프로젝트 이름(urlShortener)을 설정하고 프로젝트를 생성한뒤 적당한 이름의 앱(url_manager)를 만든다.</p>
<h3>Base62</h3>
<p>Base62<sup>[2]</sup> 는 위에서 대충 설명했으니 넘어가고 표현방법을 코드로 보자</p>
<pre class="python" name="code">BASE62 = '01234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
nBASE62 = len(BASE62)
def encode_basen(id, n=nBASE62):
     u'''아이디를 입력받아 인코딩한다'''
     base = id
     rests = []
     while base!=0:
         quotient, rest = divmod(base, n)
         rests.append(BASE62[rest])
         base = quotient
     return ''.join(rests)</pre>
<p>알고리즘은 보통 숫자 m을 n진수로 바꾸는 것과 동일한 방식으로 했다.</p>
<p>즉, 몫이 0이 아닌 동안 n으로 나누기해서 나머지를 값으로 취한다.</p>
<p>(직관적이 아니라면 굳이 비트를 쉬프트하고 이런거 별로 안좋아해서 그냥 제일 이해하기 쉬운 코드로)</p>
<p>자, 이제 인코딩을 했으니 디코딩을 해야하지 않을까? 그러나 우리는 굳이 디코딩을 할 필요는 없다. 다음에서 설명한다.</p>
<h3>Model</h3>
<p>알고리즘에 따라 URL모델은 ShortURL과 LongURL 두개를 만든다.</p>
<p>이 때, LongURL은 자신의 id를 키 값으로 Base62 인코더에 의해 hashing 되어 ShortURL로 변환된다.</p>
<p>따라서 인코더는 필요하지만 디코더는 필요가 없다.</p>
<p>ShortURL에서 LongURL의 인스턴스와 인코딩된 키값을 가지고 있기 때문에 키를 통해 긴 URL에 바로 접근할 수 있기 때문이다.</p>
<h4>LongURL</h4>
<p>LongURL은 실제 연결될 긴 URL을 저장하므로 PositiveIntegeriField인 id 와 URLField인 url 로 구성된다.</p>
<p>단, id는 지정안하면 Django가 알아서 설정해주므로 url필드만 설정한다.</p>
<h4>ShortURL</h4>
<p>ShortURL은 LongURL의 인스턴스와 id를 base62로 인코딩한 키를 가지고 있어야 한다.</p>
<h4>Model Code</h4>
<p>위의 모델링에 따른 url_manager 앱의 models.py 는 다음과 같다.</p>
<pre class="python" name="code">#coding: utf-8
from django.db
import models

class LongURL(models.Model):
    url = models.URLField(unique=True)
    def __unicode__(self):
         return self.url 

class ShortURL(models.Model):
    id = models.CharField(max_length=128, primary_key=True)
    longUrl = models.OneToOneField(LongURL)

    def __unicode__(self):
         return self.id

    class Meta:
        ordering = ['-id']</pre>
<h3>View Code</h3>
<p>사실 중요한 부분은 Model부분으로 View부분은 사용자 마음대로 하면 된다.</p>
<p>대부분의 서비스에 사용하는 데이터 디자인은 url을 값으로 넘겨받아 변환 후 저장하는 것이므로</p>
<p>여기서도 대부분의 서비스에서 하는 대로 만들어본다.</p>
<pre class="python" name="code">#coding: utf-8
import re
from models import WrongURL, LongURL, ShortURL

PATTERN = r'http://[^/^\s]+'

def get_short_id(url):
    u'''긴 URL을 받아서 짧은 URL의 id를 반환'''
    if not url:
         return WrongURL(0)
     elif not re.match(PATTERN, url):
         return WrongURL(1)
    longUrl, isnew = LongURL.objects.get_or_create(url=url)
    if isnew:
        id = encode_basen(longUrl.id)
        try:
            return HttpResponse(ShortURL.objects.create(id=id, longUrl=longUrl).id)
        except:
            raise WrongURL(2)
    else:
        try:
            return HttpResponse(ShortURL.objects.get(longUrl=longUrl).id)
        except ShortURL.DoesNotExist:
           raise WrongURL(3)

def get_long_url(id):
    u'''아이디를 입력받아 인코딩한다'''
    try:
        return ShortURL.objects.get(id=id).longUrl.url
    except:
        return ''     return ''.join(rests)</pre>
<p>코드가 간단하므로 크게 설명할 부분은 없는 것 같다. </p>
<p>WrongURL은 Exception을 상속받은 사용자 예외처리 클래스로 스트래티지 패턴을 적용했다.</p>
<pre class="python" name="code">ERROR_MSG = {
    0: &quot;data error&quot;,
    1: &quot;it is not a url&quot;,
    2: &quot;url creation error&quot;,
    3: &quot;there is no such a url&quot;,
}

class WrongURL(Exception):
    def __init__(self, key):
        self.error_message = ERROR_MSG[key] 

        def __str__(self):
        return self.error_message

        def __unicode__(self):
        return self.error_message</pre>
<p>get_short_id 함수에서 get_or_create 는 쿼리검색결과 매칭된 결과가 존재하면 해당 결과를 가져오고</p>
<p>매칭결과가 없다면 해당 레코드를 생성한다.</p>
<p>그리고 (생성/검색된 레코드 인스턴스, 생성된 인스턴스인지 참/거짓) 튜플을 반환한다.</p>
<h2>마무리</h2>
<p>urls.py 에 다음과 같이 접근 경로를 잡아주면 끝.</p>
<pre class="python" name="code">
(r'^(?P\w+)/?$', 'urlShortener.url_manager.views.long_url'),
(r'^get/short/url/?$', 'urlShortener.url_manager.views.short_url'),
</pre>
<p>python manage.py runserver 0.0.0.0:8080 으로 테스트서버를 작동시키고,</p>
<p>브라우저 주소창에서 http://localhost:8080/get/short/url/?url=줄일URL 을 입력하면 결과를 확인해 볼 수 있다.</p>
<p>실제로 구현되어 돌아가고 있는 사이트인 <a href="http://url.kumi-dongkwang.com">http://url.kumi-dongkwang.com</a> 의 코드는 </p>
<p><a href="http://hg.monolith.pe.kr/urlShortener">http://hg.monolith.pe.kr/urlShortener</a> 에서 확인하고 코드를 다운받아 볼 수 있다.</p>
<ol class="footnotes"><li id="footnote_0_856" class="footnote">http://ronny.haryan.to/archives/2009/04/07/build-your-own-url-shortening-service/</li><li id="footnote_1_856" class="footnote">http://en.wikipedia.org/wiki/Base_62</li></ol>]]></content:encoded>
			<wfw:commentRss>http://blog.monolith.pe.kr/archives/856/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>[Python] 정규표현식으로 트위터 id, url 링크 꾸미기</title>
		<link>http://blog.monolith.pe.kr/archives/618</link>
		<comments>http://blog.monolith.pe.kr/archives/618#comments</comments>
		<pubDate>Tue, 04 Aug 2009 05:43:16 +0000</pubDate>
		<dc:creator>Haandol</dc:creator>
				<category><![CDATA[python]]></category>
		<category><![CDATA[decoration]]></category>
		<category><![CDATA[re]]></category>
		<category><![CDATA[regex]]></category>
		<category><![CDATA[twitter]]></category>

		<guid isPermaLink="false">http://blog.monolith.pe.kr/?p=618</guid>
		<description><![CDATA[##요약## 트위터에서 @사용자아이디 하거나 http:// url을 입력하면 자동으로 링크를 걸어준다. 파이선과 정규표현식으로 이 기능을 읽기 쉽게, 직관적으로 구현하는 코드조각을 보인다. ##나는 머리도 나쁘고 단순하니까## 나는 머리도 나쁘고, 단순하니까 직관적으로 구현하도록 해보자. 방법도 역시 단순하다. 정규표현식으로 ID와 URL패턴을 각각 만들고 각각을 변환한다. 끝. ##코드조각## 백문이 불여일견이다. 코드를 보자. [cc lang="python" tab_size="2" lines="60"] #coding: utf-8 import re [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fblog.monolith.pe.kr%2Farchives%2F618"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fblog.monolith.pe.kr%2Farchives%2F618&amp;style=normal" height="61" width="50" /><br />
			</a>
		</div>
<p>##요약##</p>
<p>트위터에서 @사용자아이디 하거나 http:// url을 입력하면 자동으로 링크를 걸어준다.</p>
<p>파이선과 정규표현식으로 이 기능을 읽기 쉽게, 직관적으로 구현하는 코드조각을 보인다.</p>
<p>##나는 머리도 나쁘고 단순하니까##</p>
<p>나는 머리도 나쁘고, 단순하니까 직관적으로 구현하도록 해보자.</p>
<p>방법도 역시 단순하다. 정규표현식으로 ID와 URL패턴을 각각 만들고 각각을 변환한다.</p>
<p>끝.</p>
<p>##코드조각##</p>
<p>백문이 불여일견이다. 코드를 보자.</p>
<p>[cc lang="python" tab_size="2" lines="60"]</p>
<p>#coding: utf-8</p>
<p>import re</p>
<p>#변환용 패턴</p>
<p>PATTERN = {</p>
<p>&#8216;id&#8217;: r&#8217;@(?P<id>\w+) &#8216;,</p>
<p>&#8216;url&#8217;: r&#8217;(?P<url>http://[^/^\s]+[.]\w{2,4}\S/?)&#8217;,</p>
<p>}</p>
<p>#변환결과</p>
<p>DECORATION = {</p>
<p>&#8216;id&#8217;: r&#8221;@<a href="/user/\g<name>&#8220;>\g<name></a> &#8220;,</p>
<p>&#8216;url&#8217;: r&#8221;<a href="\g<url>&#8220;>\g<url></a>&#8220;,</p>
<p>}</p>
<p>def decorate_content(content):</p>
<p>u&#8221;&#8217;본문을 입력받아 적절한 html문을 추가해준다.&#8221;&#8217;</p>
<p>decorated_content = &#8221;</p>
<p>#ID 변환</p>
<p>pattern = re.compile(PATTERN['id'])</p>
<p>decorated_content = pattern.sub(DECORATION['id'], content)</p>
<p>#URL 변환</p>
<p>pattern = re.compile(PATTERN['url'])</p>
<p>decorated_content = pattern.sub(DECORATION['url'], decorated_content)</p>
<p>return decorated_content</p>
<p>[/cc]</p>
<p>끝이다. 더 압축하면 코드는 짧아지지만 이해하기가 복잡해지므로 절충했다.</p>
<p>###간단한 설명###</p>
<p>####패턴####</p>
<p>ID패턴에서 ID는 숫자, 문자,_ 로 이루어져 있다고 가정한다. ([a-zA-Z0-9_] == r&#8217;\w&#8217; 이다.)</p>
<p>그리고 맨 마지막에는 공백 하나를 줘서 아이디의 구분지점을 공백으로 설정한다</p>
<p>URL패턴은 대부분의 URL을 커버하는데 더 좋은 패턴이 있으면 알아서 사용하면 되겠다.</p>
<p>####함수####</p>
<p>re모듈의 sub 함수는 말그대로 패턴을 찾아 새로운 스트링으로 대치해준다.</p>
<p>이때, 변환결과에서 \g는 정규표현식의 \1(또는 $1) 등과 같은 그룹을 의미한다. \g는 뒤에 그룹명이 와야한다.</p>
<p>###테스트 케이스###</p>
<p>[cc lang="python" tab_size="2" lines="60"]</p>
<p>#coding: utf-8</p>
<p>import re</p>
<p>class UtilityTest(unittest.TestCase):</p>
<p>def setUp(self):</p>
<p>pass</p>
<p>def test_decorate_content(self):</p>
<p>#빈문자열은 빈문자열</p>
<p>content = &#8221;</p>
<p>decorated_content = decorate_content(content)</p>
<p>expected = &#8221;</p>
<p>self.assertEquals(expected, decorated_content)</p>
<p>#패턴매칭이 안되면 원래문자열</p>
<p>content = &#8216;@ alksjdlf&#8217;</p>
<p>decorated_content = decorate_content(content)</p>
<p>expected = &#8216;@ alksjdlf&#8217;</p>
<p>self.assertEquals(expected, decorated_content)</p>
<p>#ID체크</p>
<p>content = &#8216;@userid is user-id&#8217;</p>
<p>decorated_content = decorate_content(content)</p>
<p>expected = &#8220;@<a href="/user/userid">userid</a> is user-id&#8221;</p>
<p>self.assertEquals(expected, decorated_content)</p>
<p>#다중 ID체크</p>
<p>content = &#8216;@userid and @userid2 is user-id&#8217;</p>
<p>decorated_content = decorate_content(content)</p>
<p>expected = &#8220;@<a href="/user/userid">userid</a> and @<a href="/user/userid2">userid2</a> is user-id&#8221;</p>
<p>self.assertEquals(expected, decorated_content)</p>
<p>#URL체크</p>
<p>content = &#8216;http://www.monolith.pe.kr is the url&#8217;</p>
<p>decorated_content = decorate_content(content)</p>
<p>expected = &#8220;<a href="http://www.monolith.pe.kr">http://www.monolith.pe.kr</a> is the url&#8221;</p>
<p>self.assertEquals(expected, decorated_content)</p>
<p>#긴 URL체크</p>
<p>content = &#8216;http://www.monolith.pe.kr/?url=this_is_url is the url&#8217;</p>
<p>decorated_content = decorate_content(content)</p>
<p>expected = &#8220;<a href="http://www.monolith.pe.kr/?url=this_is_url">http://www.monolith.pe.kr/?url=this_is_url</a> is the url&#8221;</p>
<p>self.assertEquals(expected, decorated_content)</p>
<p>#다중 URL체크</p>
<p>content = &#8216;http://www.monolith.pe.kr and http://tr.im both are the url&#8217;</p>
<p>decorated_content = decorate_content(content)</p>
<p>expected = &#8220;<a href="http://www.monolith.pe.kr">http://www.monolith.pe.kr</a> and <a href="http://tr.im">http://tr.im</a> both are the url&#8221;</p>
<p>self.assertEquals(expected, decorated_content)</p>
<p>def tearDown(self):</p>
<p>pass</p>
<p>if __name__ == &#8220;__main__&#8221;:</p>
<p>unittest.main()</p>
<p>[/cc]</p>
<p>테스트케이스는 저렇게 스태틱하게 상수를 밀어넣으면 안되긴 하지만</p>
<p>짧은 문장이면 손으로 일일이 치는게 원하는 결과를 정확히 예측하는데</p>
<p>다소 도움이 된다.</p>
<p>##클로징##</p>
<p>욕심같아선 decorator를 이용해서 더 간단히 하고 싶지만 일단 여기서 마감.</p>
<p>처음엔 나도 sub를 몰랐는데 있을법한 느낌에 찾아보니 역시나 있다.</p>
<p>아는 것이 힘이라는 것을 다시금 느껴본다. 거인의 어깨위에 서는 버릇을 들이자.</p>
<p>그리고 요담엔 Django를 기반으로 http://tr.im/ 을 구현해보자. (URL을 짧게 줄여주는 사이트이다.)</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.monolith.pe.kr/archives/618/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>[GAE] 리눅스에 구글 앱 엔진 설치하기</title>
		<link>http://blog.monolith.pe.kr/archives/494</link>
		<comments>http://blog.monolith.pe.kr/archives/494#comments</comments>
		<pubDate>Sat, 20 Jun 2009 09:59:09 +0000</pubDate>
		<dc:creator>Haandol</dc:creator>
				<category><![CDATA[python]]></category>
		<category><![CDATA[gae google appengine]]></category>

		<guid isPermaLink="false">http://blog.monolith.pe.kr/?p=494</guid>
		<description><![CDATA[리눅스에서 구글 앱 엔진 설치하기 문서이다. 1. 먼저 리눅스에 Python 2.5 또는 2.6을 설치하고 Django도 설치한다. python은 http://python.org에서 Django는 http://djangoproject.com 에서 다운받을 수 있다. 2. 코드 구글에 가서 리눅스용 구글 앱 엔진 SDK를 다운받는다. http://code.google.com/intl/ko-KR/appengine/downloads.html 3.  적당한 위치에 압축을 푼다. 본인은 /usr/local/google_appengine 에 풀었음 4. 압축을 푼 위치를 PATH에 등록한다. /etc/profile 에 다음과 같이 추가하면 모든 [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fblog.monolith.pe.kr%2Farchives%2F494"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fblog.monolith.pe.kr%2Farchives%2F494&amp;style=normal" height="61" width="50" /><br />
			</a>
		</div>
<p>리눅스에서 구글 앱 엔진 설치하기 문서이다.</p>
<p>1. 먼저 리눅스에 Python 2.5 또는 2.6을 설치하고 Django도 설치한다.</p>
<blockquote><p>python은 <a href="http://python.org">http://python.org</a>에서</p>
<p>Django는 <a href="http://djangoproject.com">http://djangoproject.com</a> 에서 다운받을 수 있다.</p></blockquote>
<p>2. 코드 구글에 가서 리눅스용 구글 앱 엔진 SDK를 다운받는다.</p>
<p style="padding-left: 30px;"><a href="http://code.google.com/intl/ko-KR/appengine/downloads.html">http://code.google.com/intl/ko-KR/appengine/downloads.html</a></p>
<p>3.  적당한 위치에 압축을 푼다.</p>
<blockquote><p>본인은 /usr/local/google_appengine 에 풀었음</p></blockquote>
<p>4. 압축을 푼 위치를 PATH에 등록한다.</p>
<blockquote><p>/etc/profile 에 다음과 같이 추가하면 모든 사용자에 적용됨</p>
<p>export PATH=$PATH:/usr/local/google_appengine</p></blockquote>
<p>5. 압축푼 위치를 PYTHONPATH에 등록한다.</p>
<p>역시 마찬가지로 /etc/profile에 다음을 추가</p>
<blockquote><p>export PYTHONPATH=$PYTHONPATH:/usr/local/google_appengine</p></blockquote>
<p>6. 설치완료</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.monolith.pe.kr/archives/494/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>cx_Oracle을 이용해서 python에서 오라클 사용</title>
		<link>http://blog.monolith.pe.kr/archives/223</link>
		<comments>http://blog.monolith.pe.kr/archives/223#comments</comments>
		<pubDate>Tue, 10 Mar 2009 07:50:58 +0000</pubDate>
		<dc:creator>Haandol</dc:creator>
				<category><![CDATA[python]]></category>
		<category><![CDATA[팁과 트릭]]></category>
		<category><![CDATA[oracle]]></category>
		<category><![CDATA[xe]]></category>
		<category><![CDATA[오라클]]></category>
		<category><![CDATA[파이썬]]></category>

		<guid isPermaLink="false">http://www.monolith.pe.kr/wp/?p=223</guid>
		<description><![CDATA[굳이 이걸 해보는 이유는 서버컴퓨터가 구린탓에 학생 50명의 커넥션도 핸들할 수 없을지도 모른다는 생각이 들어서, 스트레스 테스트를 해보기 위함임 일단 윈도우용은 잘 모르겠음(똑같은걸 깔아도 안되는 이유는?!) 그래서 우분투기준으로 설명하겠음 1. 구글링으로 cx_Oracle을 검색해서 파이썬버전에 맞는 드라이버를 다운 여기서 검색 : http://cx-oracle.sourceforge.net/ 일단 제공되는 패키지가 rpm이므로 rpm과 alien을 깔고 패키지 설치 $ sudo apt-get install rpm [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fblog.monolith.pe.kr%2Farchives%2F223"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fblog.monolith.pe.kr%2Farchives%2F223&amp;style=normal" height="61" width="50" /><br />
			</a>
		</div>
<p style="clear: both">굳이 이걸 해보는 이유는 서버컴퓨터가 구린탓에 학생 50명의 커넥션도 핸들할 수 없을지도 모른다는 생각이 들어서, 스트레스 테스트를 해보기 위함임</p>
<p style="clear: both">일단 윈도우용은 잘 모르겠음(똑같은걸 깔아도 안되는 이유는?!) 그래서 우분투기준으로 설명하겠음</p>
<p style="clear: both">1. 구글링으로 cx_Oracle을 검색해서 파이썬버전에 맞는 드라이버를 다운</p>
<ul>
<li>
<p style="clear: both">여기서 검색 : <a href="http://cx-oracle.sourceforge.net/">http://cx-oracle.sourceforge.net/</a></p>
</li>
<li>
<p style="clear: both">일단 제공되는 패키지가 rpm이므로 rpm과 alien을 깔고 패키지 설치</p>
</li>
</ul>
<blockquote><p style="clear: both">$ sudo apt-get install rpm alien $ sudo alien -i cx_Oracle-4.4.1-10g-py26-1.i386.rpm</p>
</blockquote>
<p>2. 끗. 생성되어있는 테이블 테스트 등으로 쿼리를 날려본다. </p>
<pre class="python" name="code">
#coding: utf-8

import cx_Oracle

connection = cx_Oracle.connect(&quot;user1&quot;, &quot;1&quot;, &quot;XE&quot;)
cursor = connection.cursor()
cursor.execute(&quot;SELECT table_name FROM tabs&quot;)
print cursor.fetchall()
cursor.close()
cx</pre>
<p><em>Oracle을 임포트할 때 libclns 어쩌구 모듈에러가 나온다면, LD</em>LIBRARY_PATH 가 설정되어 있지 않아서 모듈 임포트 경로를 알수 없어서이다.</p>
<p>LD<em>LIBRARY</em>PATH 가 설정되지 않으면 라이브러리 경로는 자동으로 /usr/lib 와 /lib 가 되므로 해당경로에 파일을 갖다 두거나 /etc/profile 에 LD<em>LIBRARY</em>PATH를 등록해주면 되겠다.</p>
<blockquote>
<p style="clear: both">$vim /etc/profile export LD_LIBRARY_PATH=/usr/lib/oracle/xe/app/oracle/product/10.2.0/server/lib</p>
</blockquote>
<p>
  <br style="clear: both" class="final-break" /></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.monolith.pe.kr/archives/223/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>FastCGI로 Django 배치하기</title>
		<link>http://blog.monolith.pe.kr/archives/205</link>
		<comments>http://blog.monolith.pe.kr/archives/205#comments</comments>
		<pubDate>Sun, 22 Feb 2009 15:39:20 +0000</pubDate>
		<dc:creator>Haandol</dc:creator>
				<category><![CDATA[python]]></category>
		<category><![CDATA[팁과 트릭]]></category>
		<category><![CDATA[deploy]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[fastcgi]]></category>

		<guid isPermaLink="false">http://www.monolith.pe.kr/wp/?p=205</guid>
		<description><![CDATA[별 내용은 없고 그냥 공식 홈페이지에 있는 내용을 짧은 영어로 번역한 것이다. 원래 mod_python만 썼는데 이번기회에 좀 더 빠르다고 하는 FastCGI모듈인 mod_fastcgi와 mod_fcgid 를 설치해봤다. 첨부된 글을 읽다보면 알겠지만 fastcgi를 쓴다고 해도 겉에서 봤을 땐 그저 ./manage.py runserver 0.0.0.0:8080 처럼 서버를 TCP로 돌리는거랑 크게 다르지 않다. (내부적으로는 꽤나 다르다) 첨부파일 : fastcgi-with-django]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fblog.monolith.pe.kr%2Farchives%2F205"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fblog.monolith.pe.kr%2Farchives%2F205&amp;style=normal" height="61" width="50" /><br />
			</a>
		</div>
<p>별 내용은 없고 그냥 공식 홈페이지에 있는 내용을 짧은 영어로 번역한 것이다.</p>
<p>원래 mod_python만 썼는데 이번기회에 좀 더 빠르다고 하는 FastCGI모듈인 mod_fastcgi와 mod_fcgid 를 설치해봤다.</p>
<p>첨부된 글을 읽다보면 알겠지만 fastcgi를 쓴다고 해도 겉에서 봤을 땐 그저</p>
<p style="padding-left: 30px;">./manage.py runserver 0.0.0.0:8080</p>
<p>처럼 서버를 TCP로 돌리는거랑 크게 다르지 않다. (내부적으로는 꽤나 다르다)</p>
<p>첨부파일 : <a href="http://www.monolith.pe.kr/wordpress/wp-content/uploads/2009/02/fastcgi-with-django.doc">fastcgi-with-django</a></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.monolith.pe.kr/archives/205/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>미투데이 파이썬 API</title>
		<link>http://blog.monolith.pe.kr/archives/161</link>
		<comments>http://blog.monolith.pe.kr/archives/161#comments</comments>
		<pubDate>Sat, 14 Feb 2009 13:21:58 +0000</pubDate>
		<dc:creator>Haandol</dc:creator>
				<category><![CDATA[python]]></category>
		<category><![CDATA[웹]]></category>
		<category><![CDATA[API]]></category>
		<category><![CDATA[매쉬업]]></category>
		<category><![CDATA[미투데이]]></category>
		<category><![CDATA[파이썬]]></category>

		<guid isPermaLink="false">http://www.monolith.pe.kr/wp/?p=161</guid>
		<description><![CDATA[링크 : http://code.google.com/p/me2day-python-api/ 사람이든 무엇이든 마케팅이 중요하다고 생각하지만 최근엔 그런 꾸밈새에 신물이 난다.]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fblog.monolith.pe.kr%2Farchives%2F161"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fblog.monolith.pe.kr%2Farchives%2F161&amp;style=normal" height="61" width="50" /><br />
			</a>
		</div>
<p>링크 : <a href="http://code.google.com/p/me2day-python-api/">http://code.google.com/p/me2day-python-api/</a></p>
<p>사람이든 무엇이든 마케팅이 중요하다고 생각하지만 최근엔 그런 꾸밈새에 신물이 난다.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.monolith.pe.kr/archives/161/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>
