<?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>apt-blog.net   无证程序员的PT桑 &#187; Python</title>
	<atom:link href="http://apt-blog.net/category/tech/programming/python-programming-tech/feed" rel="self" type="application/rss+xml" />
	<link>http://apt-blog.net</link>
	<description>潜逃中。</description>
	<lastBuildDate>Fri, 18 May 2012 11:25:05 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.2</generator>
		<item>
		<title>&quot;Leave password blank if dont want to change&quot; in a django admin field</title>
		<link>http://apt-blog.net/leave-password-blank-if-dont-want-to-change-in-a-django-admin</link>
		<comments>http://apt-blog.net/leave-password-blank-if-dont-want-to-change-in-a-django-admin#comments</comments>
		<pubDate>Fri, 27 Apr 2012 11:29:22 +0000</pubDate>
		<dc:creator>BOYPT</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[web技术]]></category>
		<category><![CDATA[django]]></category>
		<guid isPermaLink="false">https://apt-blog.net/?p=1214</guid>
		<description><![CDATA[Django is a complicated but useful python web-framework, comparing to light weight frameworks like Bottle, web.py. I recently switch from Bottle, making use of it's powerful admin site to build a Email Account Management system. An AdminSite offer interfaces to manage databases. It includes traditional authentications, permissions, data display and POST saving mechanisms, etc. , [...]]]></description>
			<content:encoded><![CDATA[<p>Django is a complicated but useful python web-framework, comparing to light weight frameworks like <code>Bottle</code>, <code>web.py</code>. I recently switch from Bottle, making use of it's powerful <a href="https://docs.djangoproject.com/en/1.4/ref/contrib/admin/">admin site</a> to build a Email Account Management system.</p>
<p>An AdminSite offer interfaces to manage databases. It includes traditional authentications, permissions, data display and POST saving mechanisms, etc. , which are nasty trifles if you try implement them from scratch using <code>bottle</code> or other things.</p>
<p>Now I can build a decent system in about 100 lines code, after many many document reading and code digging, I want to share the story how I solve the problem encountered while implementing the "Leave password blank if don't want to change it" requirement. As commonly seen in other applications when user try to update their profile.</p>
<p>Firstly the background. My data Model:</p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">class</span> Users<span style="color: black;">&#40;</span>Model<span style="color: black;">&#41;</span>:
    account = models.<span style="color: black;">CharField</span><span style="color: black;">&#40;</span>max_length=<span style="color: #ff4500;">128</span>, unique = <span style="color: #008000;">True</span><span style="color: black;">&#41;</span>
    password = models.<span style="color: black;">CharField</span><span style="color: black;">&#40;</span>db_column = <span style="color: #483d8b;">'crypt'</span>,max_length=<span style="color: #ff4500;">384</span>, blank=<span style="color: #008000;">True</span><span style="color: black;">&#41;</span>
    name = models.<span style="color: black;">CharField</span><span style="color: black;">&#40;</span>max_length=<span style="color: #ff4500;">768</span><span style="color: black;">&#41;</span>
    <span style="color: #808080; font-style: italic;">#...</span></pre></td></tr></table></div>
<p>The password is stored hashed in the DB <code>crypt</code> column. If I just let the AdminSite pickup things like that, it just show the password value in the plain input text.</p>
<p>Firstly I tell django to show it as a password input widget.</p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">class</span> MailUsersForm<span style="color: black;">&#40;</span>forms.<span style="color: black;">ModelForm</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">class</span> Meta:
        model = Users
        widgets = <span style="color: black;">&#123;</span>
                <span style="color: #483d8b;">'password'</span>: forms.<span style="color: black;">PasswordInput</span><span style="color: black;">&#40;</span>render_value = <span style="color: #008000;">False</span><span style="color: black;">&#41;</span>,
        <span style="color: black;">&#125;</span>
<span style="color: #ff7700;font-weight:bold;">class</span> MailUsersAdmin<span style="color: black;">&#40;</span>admin.<span style="color: black;">ModelAdmin</span><span style="color: black;">&#41;</span>:
    form = MailUsersForm</pre></td></tr></table></div>
<p>Ah, overriding the <code>form</code> in the <code>ModelAdmin</code> does the trick.</p>
<p>Now in the edit page, the Password field shows an empty input widget.</p>
<p><a href="http://apt-blog.net/wp-content/uploads/2012/04/django-password-field.png" rel="lightbox[1214]" title="django-password-field.png"><img title="django-password-field.png" alt="django-password-field.png" src="http://apt-blog.net/wp-content/uploads/2012/04/django-password-field.png" class="aligncenter" /></a></p>
<p>Now I need to hash the password before commit to the DB. I found the <a href="https://docs.djangoproject.com/en/1.4/ref/contrib/admin/#django.contrib.admin.ModelAdmin.save_model">save_model method</a>.</p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">class</span> MailUsersAdmin<span style="color: black;">&#40;</span>admin.<span style="color: black;">ModelAdmin</span><span style="color: black;">&#41;</span>:
    <span style="color: #808080; font-style: italic;"># ...</span>
    <span style="color: #ff7700;font-weight:bold;">def</span> save_model<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, request, obj, form, change<span style="color: black;">&#41;</span>:
        new_psw = request.<span style="color: black;">POST</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">&quot;password&quot;</span><span style="color: black;">&#93;</span>
        <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #008000;">len</span><span style="color: black;">&#40;</span>new_psw<span style="color: black;">&#41;</span>:
            obj.<span style="color: black;">password</span> = obj._hash_password<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
            obj.<span style="color: black;">save</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>
<p>This works, but buggy. The password field is defaultly blank. It will clean the field in the DB without processing. The problem is, by the time in the <code>save_model</code> method is triggered, the <code>obj</code> is already updated by the values from the POST request. There's no way to get the old password value here. All I can do without changing the old password, is to skip the <code>obj.save()</code>. But what if I only want to update other things?</p>
<p>Through out the documents, I found some other places to approch the Model instance.</p>
<p>Firstly the <a href="https://docs.djangoproject.com/en/1.4/topics/forms/modelforms/#the-save-method">django.forms.ModelForm.save</a> method. The module instance is returned from this method (then passed to the <code>save_model</code>), so I inspect the object with <code>ipdb</code> to see if the data is updated here:</p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">class</span> MailUsersForm<span style="color: black;">&#40;</span>forms.<span style="color: black;">ModelForm</span><span style="color: black;">&#41;</span>:
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> save<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, <span style="color: #66cc66;">*</span>args, <span style="color: #66cc66;">**</span>kw<span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">import</span> ipdb<span style="color: #66cc66;">;</span> ipdb.<span style="color: black;">set_trace</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        obj = <span style="color: #008000;">super</span><span style="color: black;">&#40;</span>MailUsersForm, <span style="color: #008000;">self</span><span style="color: black;">&#41;</span>.<span style="color: black;">save</span><span style="color: black;">&#40;</span><span style="color: #66cc66;">*</span>args, <span style="color: #66cc66;">**</span>kw<span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> obj</pre></td></tr></table></div>
<p>Unfortunately Yes. The instanced obj from the derived <code>save</code> is already updated to the POST value.</p>
<p>And <a href="http://www.djangofoo.com/tag/pre_save">django signals, pre_save</a>, all the same way. (The signal thing is even emmited after the <code>forms.ModelForm.save</code>)</p>
<p>Googled around and nothing suitble, I decided to find the right way myself.</p>
<p>There must be somewhere in django codes that sets the attributes with values from the POST requsts. So I add a hook in the Model.</p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">class</span> Users<span style="color: black;">&#40;</span>Model<span style="color: black;">&#41;</span>:
    <span style="color: #808080; font-style: italic;">#....</span>
    <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__setattr__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, name, value<span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">if</span> name == <span style="color: #483d8b;">&quot;password&quot;</span>:
            <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #008000;">self</span>.<span style="color: #008000;">id</span> == <span style="color: #ff4500;">370</span>:
                <span style="color: #ff7700;font-weight:bold;">import</span> ipdb<span style="color: #66cc66;">;</span> ipdb.<span style="color: black;">set_trace</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
        <span style="color: #ff7700;font-weight:bold;">return</span> Model.<span style="color: #0000cd;">__setattr__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, name, value<span style="color: black;">&#41;</span></pre></td></tr></table></div>
<p>The <code>if self.id</code> thing is for filtering the specific page I submit the save in the browser. Now ipdb let me in debug mode.</p>
<div class="wp_syntax"><div class="code"><pre class="text" style="font-family:monospace;">ipdb&gt; w
&nbsp;
#................. Many Many unrelated things
&nbsp;
  /usr/lib/python2.7/site-packages/django/forms/forms.py(272)full_clean()
    270         self._clean_fields()
    271         self._clean_form()
--&gt; 272         self._post_clean()
    273         if self._errors:
    274             del self.cleaned_data
&nbsp;
  /usr/lib/python2.7/site-packages/django/forms/models.py(309)_post_clean()
    307         opts = self._meta
    308         # Update the model instance with self.cleaned_data.
&nbsp;
--&gt; 309         self.instance = construct_instance(self, self.instance, opts.fields, opts.exclude)
    310
    311         exclude = self._get_validation_exclusions()
&nbsp;
  /usr/lib/python2.7/site-packages/django/forms/models.py(51)construct_instance()
     49             file_field_list.append(f)
     50         else:
---&gt; 51             f.save_form_data(instance, cleaned_data[f.name])
     52
     53     for f in file_field_list:
&nbsp;
  /usr/lib/python2.7/site-packages/django/db/models/fields/__init__.py(454)save_form_data()
    452
    453     def save_form_data(self, instance, data):
--&gt; 454         setattr(instance, self.name, data)
#---------------^ this matters!
    455
    456     def formfield(self, form_class=forms.CharField, **kwargs):
&nbsp;
&gt; /home/boypt/Projects/maildbadmin/maildbadmin/models.py(55)__setattr__()
     53             if self.id == 370:
     54                 import ipdb; ipdb.set_trace()
---&gt; 55                 print &quot;set :&quot;, value
     56 #
     57 #</pre></div></div>
<p>From the call stack, the <code>setattr</code> call to the Model changes the data. I check <code>django/db/models/fields/__init__.py</code> source, find that this is a <code>Field</code> base class, so the obvious solution is to override this method.</p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">class</span> PasswordCharField<span style="color: black;">&#40;</span>models.<span style="color: black;">CharField</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">def</span> save_form_data<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, instance, data<span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">if</span> data <span style="color: #66cc66;">!</span>= u<span style="color: #483d8b;">''</span>:
            data = instance._hashed_pwd<span style="color: black;">&#40;</span>data<span style="color: black;">&#41;</span>
            <span style="color: #008000;">setattr</span><span style="color: black;">&#40;</span>instance, <span style="color: #008000;">self</span>.<span style="color: black;">name</span>, data<span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">class</span> Users<span style="color: black;">&#40;</span>Model<span style="color: black;">&#41;</span>:
    <span style="color: #808080; font-style: italic;">#...</span>
    password = PasswordCharField<span style="color: black;">&#40;</span>db_column = <span style="color: #483d8b;">'crypt'</span>, max_length=<span style="color: #ff4500;">384</span>, blank=<span style="color: #008000;">True</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;">#.....</span></pre></td></tr></table></div>
<p>OK, now everything's done here. The <code>save_model</code> overriding is no more needed, for the Model instance's password will not be set to empty at all when the user left the field blank when they submit.</p>
]]></content:encoded>
			<wfw:commentRss>http://apt-blog.net/leave-password-blank-if-dont-want-to-change-in-a-django-admin/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>uWSGI的几个使用技巧</title>
		<link>http://apt-blog.net/tips_on_using_uwsgi</link>
		<comments>http://apt-blog.net/tips_on_using_uwsgi#comments</comments>
		<pubDate>Thu, 24 Nov 2011 11:49:16 +0000</pubDate>
		<dc:creator>BOYPT</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[web技术]]></category>
		<category><![CDATA[uwsgi]]></category>
		<guid isPermaLink="false">https://apt-blog.net/?p=1137</guid>
		<description><![CDATA[uWSGI基本上是python做web服务的不二选择了，但似乎项目的开发者比较热衷于其新功能开发，对其使用文档还是相当缺乏的。 安装： Ubuntu 添加ppa，安装。 目前只有Lucid, Maverick, Natty几个版本还需要添加，以后版本都在官方原，直接apt-get install即可。 1 2 3 add-apt-repository ppa:uwsgi/release apt-get update apt-get install uwsgi-python Debian 目前Debian sid已经收录了uwsgi，一般服务器安装的testing/stable，只能自己修改apt的配置，从sid里面port过来。 注意：以下操作可能毁掉您的整个debian包系统。以下命令仅在Debian wheezy，即目前的testing运行测试安装过，如果你的是squeeze，自己修改01all那里 Pin: release a=squeeze 一行，但不保证能够正常运行。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 echo &#34;deb http://ftp.tw.debian.org/debian sid main non-free contrib&#34; &#62;&#62; /etc/apt/sources.list cat &#62; /etc/apt/preferences.d/01all &#60;&#60; [...]]]></description>
			<content:encoded><![CDATA[<p>uWSGI基本上是python做web服务的不二选择了，但似乎项目的开发者比较热衷于其新功能开发，对其使用文档还是相当缺乏的。</p>
<h2>安装：</h2>
<h3>Ubuntu</h3>
<p>添加ppa，安装。 目前只有Lucid, Maverick, Natty几个版本还需要添加，以后版本都在官方原，直接<code>apt-get install</code>即可。</p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
</pre></td><td class="code"><pre class="bash" style="font-family:monospace;">add-apt-repository ppa:uwsgi<span style="color: #000000; font-weight: bold;">/</span>release
<span style="color: #c20cb9; font-weight: bold;">apt-get</span> update
<span style="color: #c20cb9; font-weight: bold;">apt-get</span> <span style="color: #c20cb9; font-weight: bold;">install</span> uwsgi-python</pre></td></tr></table></div>
</p>
<h3>Debian</h3>
<p>目前Debian sid已经收录了uwsgi，一般服务器安装的testing/stable，只能自己修改apt的配置，从sid里面port过来。</p>
<p>注意：以下操作可能毁掉您的整个debian包系统。以下命令仅在Debian wheezy，即目前的testing运行测试安装过，如果你的是squeeze，自己修改01all那里 <code>Pin: release a=squeeze</code> 一行，但不保证能够正常运行。</p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
</pre></td><td class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #7a0874; font-weight: bold;">echo</span> <span style="color: #ff0000;">&quot;deb http://ftp.tw.debian.org/debian sid main non-free contrib&quot;</span> <span style="color: #000000; font-weight: bold;">&gt;&gt;</span> <span style="color: #000000; font-weight: bold;">/</span>etc<span style="color: #000000; font-weight: bold;">/</span>apt<span style="color: #000000; font-weight: bold;">/</span>sources.list
<span style="color: #c20cb9; font-weight: bold;">cat</span> <span style="color: #000000; font-weight: bold;">&gt;</span> <span style="color: #000000; font-weight: bold;">/</span>etc<span style="color: #000000; font-weight: bold;">/</span>apt<span style="color: #000000; font-weight: bold;">/</span>preferences.d<span style="color: #000000; font-weight: bold;">/</span>01all <span style="color: #cc0000; font-style: italic;">&lt;&lt; EOF
Package: *
Pin: release a=testing
Pin-Priority: 1000
EOF</span>
&nbsp;
<span style="color: #c20cb9; font-weight: bold;">cat</span> <span style="color: #000000; font-weight: bold;">&gt;</span> <span style="color: #000000; font-weight: bold;">/</span>etc<span style="color: #000000; font-weight: bold;">/</span>apt<span style="color: #000000; font-weight: bold;">/</span>preferences.d<span style="color: #000000; font-weight: bold;">/</span>02uwsgi <span style="color: #cc0000; font-style: italic;">&lt;&lt; EOF
Package: uwsgi
Pin: release a=sid
Pin-Priority: 1010
EOF</span>
&nbsp;
<span style="color: #c20cb9; font-weight: bold;">apt-get</span> update
<span style="color: #c20cb9; font-weight: bold;">apt-get</span> <span style="color: #c20cb9; font-weight: bold;">install</span> uwsgi uwsgi-core uwsgi-plugin-python</pre></td></tr></table></div>
<h3>Centos, RHEL, Fedora, ...</h3>
<p>You're on your own, linux user.</p>
<h2>配置</h2>
<p>常见配置文件：</p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
</pre></td><td class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #c20cb9; font-weight: bold;">cat</span> <span style="color: #000000; font-weight: bold;">&gt;</span> <span style="color: #000000; font-weight: bold;">/</span>etc<span style="color: #000000; font-weight: bold;">/</span>uwsgi<span style="color: #000000; font-weight: bold;">/</span>apps-enabled<span style="color: #000000; font-weight: bold;">/</span>test.ini <span style="color: #cc0000; font-style: italic;">&lt;&lt; EOF
[uwsgi]
chmod-socket = 666
limit-as = 256
processes = 6
max-request = 2000
memory-report = true
enable-threads = true
virtualenv = /somewhere/to/your/virtualenv
wsgi-file = /home/somewhere/app.py
EOF</span></pre></td></tr></table></div>
<p>用ini方式的配置是比xml的容易写多了。具体配置说明请看<a href="http://projects.unbit.it/uwsgi/wiki/Doc">Doc</a></p>
<p>默认的sock文件在<code>/var/run/uwsgi/test/socket</code></p>
<p>配合nginx使用的配置文件：</p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
</pre></td><td class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #c20cb9; font-weight: bold;">cat</span> <span style="color: #000000; font-weight: bold;">&gt;</span> <span style="color: #000000; font-weight: bold;">/</span>etc<span style="color: #000000; font-weight: bold;">/</span>nginx<span style="color: #000000; font-weight: bold;">/</span>sites-enabled<span style="color: #000000; font-weight: bold;">/</span><span style="color: #7a0874; font-weight: bold;">test</span> <span style="color: #cc0000; font-style: italic;">&lt;&lt; EOF
server {
        listen   80;
        server_name host.domain.com;
        location / {
                include uwsgi_params;
                uwsgi_pass unix:///var/run/uwsgi/test/socket;
        }
}
EOF</span></pre></td></tr></table></div>
<h3>切换Python版本</h3>
<p>系统中可能安装有多个版本的Python，又或者同个系统里面有的app跑python3, 有的跑python2.6 stackless, 有的跑python2.7...</p>
<p>uwsgi 是通过plugin的方式加载不同的python解析器的，debian的uwsgi-plugin-python包里面提供了python26_plugin.so, python27_plugin.so, 不过uwsgi如果在app配置里面没有<code>plugins</code>参数指定使用哪个时候，会尝试加载<code>/usr/lib/uwsgi/plugins/python_plugin.so</code>，这个文件在debian下是个链接文件，可以运行<code>update-alternatives --config uwsgi-plugin-python</code>命令来修改这个默认版本。</p>
<p>源里面没有提供stackless python的plugin，只能自己编译了，还好非常简单：</p>
<p><code>/path/to/your/stackless/python uwsgiconfig.py --plugin plugins/stackless</code></p>
<p>然后把生成的python_stackless.so放到<code>/usr/lib/uwsgi/plugins</code> 就能被uwsgi加载了。</p>
<p>其他plugin的编译基本都这样，看源代码里面的plugins目录。</p>
<h2>代码</h2>
<p>使用uwsgi运行代码的时候，可以import uwsgi模块，和uwsgi进行一定的交互。</p>
<p>uwsgi 的源代码包当中有个<code>uwsgidecorators.py</code>，提供了多数的uwsgi接口，<a href="http://projects.unbit.it/uwsgi/wiki/Decorators">描述文档在此</a>。以下是几个典型应用。</p>
<h3>自动重载</h3>
<p>好一些web框架都有检测修改了源文件就自动重新加载的功能，但是在uwsgi里面这个方法是不好用了。</p>
<h4>Django</h4>
<p>加入以下代码：</p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">import</span> uwsgi
<span style="color: #ff7700;font-weight:bold;">from</span> uwsgidecorators <span style="color: #ff7700;font-weight:bold;">import</span> timer
<span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">utils</span> <span style="color: #ff7700;font-weight:bold;">import</span> autoreload
&nbsp;
@timer<span style="color: black;">&#40;</span><span style="color: #ff4500;">3</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">def</span> change_code_gracefull_reload<span style="color: black;">&#40;</span>sig<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">if</span> autoreload.<span style="color: black;">code_changed</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
        uwsgi.<span style="color: #008000;">reload</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>
<h4>Bottle</h4>
<p>Bottle 的reload是它内置基础web服务器的功能，在uwsgi里面只能自己写代码监控。</p>
<p>想要简单很难做到完美：</p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">from</span> uwsgidecorators <span style="color: #ff7700;font-weight:bold;">import</span> filemon
&nbsp;
filemon<span style="color: black;">&#40;</span><span style="color: #483d8b;">'/tmp/foo'</span><span style="color: black;">&#41;</span><span style="color: black;">&#40;</span>uwsgi.<span style="color: #008000;">reload</span><span style="color: black;">&#41;</span>
filemon<span style="color: black;">&#40;</span><span style="color: #483d8b;">'/tmp/foo2'</span><span style="color: black;">&#41;</span><span style="color: black;">&#40;</span>uwsgi.<span style="color: #008000;">reload</span><span style="color: black;">&#41;</span>
filemon<span style="color: black;">&#40;</span><span style="color: #483d8b;">'/tmp/foo3'</span><span style="color: black;">&#41;</span><span style="color: black;">&#40;</span>uwsgi.<span style="color: #008000;">reload</span><span style="color: black;">&#41;</span>
filemon<span style="color: black;">&#40;</span><span style="color: #483d8b;">'/tmp/foo4'</span><span style="color: black;">&#41;</span><span style="color: black;">&#40;</span>uwsgi.<span style="color: #008000;">reload</span><span style="color: black;">&#41;</span>
filemon<span style="color: black;">&#40;</span><span style="color: #483d8b;">'/tmp/foo5'</span><span style="color: black;">&#41;</span><span style="color: black;">&#40;</span>uwsgi.<span style="color: #008000;">reload</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>
<p>于是这些文件出现修改的时候，就自动reload。其实你可以自己加入调用uwsgi.reload()的响应。</p>
<h4>pylons, web.py, cherrypy ..</h4>
<p>程序员自己研究去。</p>
<h3>threading</h3>
<p>原来使用内置线程做的异步程序会发现在uwsgi，里面的线程好像都没启动了、数据库没有连接了……原因是uwsgi加载了代码后，fork出多个子进程，子进程里面的线程如果没有被再度激活，是不工作的。</p>
<p>有两个方法，一是在这个app的配置里面加上：</p>
<p><code>lazy = true</code></p>
<p>这样代码会在子进程fork了之后才开始加载。</p>
<p>二是使用uwsgi模块的api，uwsgidecorators 里面有相应的装饰器，这样uwsgi会在fork之后自动调用你要重新启动的线程。</p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">from</span> uwsgidecorators <span style="color: #ff7700;font-weight:bold;">import</span> postfork
&nbsp;
@postfork
<span style="color: #ff7700;font-weight:bold;">def</span> reconnect_to_db<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    myfoodb.<span style="color: black;">connect</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
@postfork
<span style="color: #ff7700;font-weight:bold;">def</span> restart_threads<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">global</span> thread1, thread2
    thread1 = MyThread1<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    thread1.<span style="color: black;">start</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    thread2 = MyThread2<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    thread2.<span style="color: black;">start</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>
<h2>监控</h2>
<p>虽然uwsgi 默认带了SNMP功能，可以给外部监控其运行情况，但uwsgi的源码包里面带了个<code>uwsgistatus.py</code>文件，里面简单的提供了读取uwsgi模块的属性形成的报告。 可以把里面的代码copy到app项目里面，作为一个简单的监控数据。</p>
<p><a href="http://apt-blog.net/wp-content/uploads/2011/11/2011-11-24_19-04-14.png" rel="lightbox[1137]" title="2011-11-24_19-04-14.png"><img title="2011-11-24_19-04-14.png" alt="2011-11-24_19-04-14.png" src="http://apt-blog.net/wp-content/uploads/2011/11/2011-11-24_19-04-14.png" style="width: 30%;" class="aligncenter" /></a></p>
<h2>Reference</h2>
<p>以上部分代码取自uWSGI Wiki页面</p>
<ul>
<li><a href="http://projects.unbit.it/uwsgi/wiki/Doc">Doc</a></li>
<li><a href="http://projects.unbit.it/uwsgi/wiki/Example">Example</a> </li>
<li><a href="http://projects.unbit.it/uwsgi/wiki/TipsAndTricks">TipsAndTricks</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://apt-blog.net/tips_on_using_uwsgi/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Python多重继承的异构构造器</title>
		<link>http://apt-blog.net/constructors_with_different_arguments_in_python_multiinheri_class</link>
		<comments>http://apt-blog.net/constructors_with_different_arguments_in_python_multiinheri_class#comments</comments>
		<pubDate>Tue, 22 Nov 2011 17:23:51 +0000</pubDate>
		<dc:creator>BOYPT</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[class]]></category>
		<category><![CDATA[inheritace]]></category>
		<category><![CDATA[继承]]></category>
		<guid isPermaLink="false">http://apt-blog.net/?p=1131</guid>
		<description><![CDATA[What 在Python里面，如果你使用上Qt，SQLAlchemy，Twisted之类各种大型类库时候，有时候多重继承Multiple Inheritance是个简单的解决方法，但是多重继承的复杂性总容易造成误解和疑惑。 一般“常识”说，使用super访问父类的属性/方法，这种说法在多重继承里面是不成立的，多重继承的类并没有父类的概念(There is no superclass in a MI world)。类似的博客在过去几年被人写了无数遍了，因为过去版本里面python官方文档对super的解释非常有限而且有误导解释，直到2.6以后的文档，才详细说明了super在单继承和多继承的两种不同工作方式。当时苦逼的程序员甚至不得不去翻看Python源码才搞清楚是什么回事。以致几年来很多人对python的多重继承保持怀疑态度。 Python多重继承使用Method Resolution Order的动态算法来解决一个方法名的调用顺序，mro其实说来简单，就是一个深度优先的继承列表，很易理解，但随之来的是遇到互不相同的构造器__init__参数的问题。 Codepad运行结果：http://codepad.org/qQNiMzBl 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class A&#40;object&#41;: def __init__&#40;self, arg1&#41;: print &#34;init func in A, with arg1 '%s'&#34; % arg1 super&#40;A, self&#41;.__init__&#40;&#41; &#160; class B&#40;object&#41;: def __init__&#40;self, arg1, [...]]]></description>
			<content:encoded><![CDATA[<h2>What</h2>
<p>在Python里面，如果你使用上Qt，SQLAlchemy，Twisted之类各种大型类库时候，有时候多重继承Multiple Inheritance是个简单的解决方法，但是多重继承的复杂性总容易造成误解和疑惑。</p>
<p>一般“常识”说，使用super访问父类的属性/方法，这种说法在多重继承里面是不成立的，<a href="http://www.artima.com/weblogs/viewpost.jsp?thread=236275">多重继承的类并没有父类的概念(There is no superclass in a MI world)</a>。类似的博客在过去几年被人写了无数遍了，因为过去版本里面python官方文档对super的解释非常有限而且有误导解释，直到2.6以后的文档，才详细说明了<a href="http://docs.python.org/library/functions.html#super">super在单继承和多继承的两种不同工作方式</a>。当时苦逼的程序员甚至<a href="http://blog.csdn.net/johnsonguo/article/details/585193">不得不去翻看Python源码</a>才搞清楚是什么回事。以致几年来很多人对python的多重继承保持怀疑态度。</p>
<p>Python多重继承使用<a href="http://www.python.org/download/releases/2.3/mro/">Method Resolution Order</a>的动态算法来解决一个方法名的调用顺序，mro其实说来简单，就是一个深度优先的继承列表，很易理解，但随之来的是遇到互不相同的构造器<code>__init__</code>参数的问题。</p>
<p>Codepad运行结果：<a href="http://codepad.org/qQNiMzBl">http://codepad.org/qQNiMzBl</a></p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">class</span> A<span style="color: black;">&#40;</span><span style="color: #008000;">object</span><span style="color: black;">&#41;</span>:
    <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>, arg1<span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">&quot;init func in A, with arg1 '%s'&quot;</span> <span style="color: #66cc66;">%</span> arg1
        <span style="color: #008000;">super</span><span style="color: black;">&#40;</span>A, <span style="color: #008000;">self</span><span style="color: black;">&#41;</span>.<span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">class</span> B<span style="color: black;">&#40;</span><span style="color: #008000;">object</span><span style="color: black;">&#41;</span>:
    <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>, arg1, arg2<span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">&quot;init func in B, with arg1'%s', arg2 '%s'&quot;</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>arg1, arg2<span style="color: black;">&#41;</span>
        <span style="color: #008000;">super</span><span style="color: black;">&#40;</span>B, <span style="color: #008000;">self</span><span style="color: black;">&#41;</span>.<span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span>arg1<span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">class</span> C<span style="color: black;">&#40;</span>B, A<span style="color: black;">&#41;</span>:
    <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>, arg1, arg2<span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">&quot;init func in C, with arg1'%s', arg2 '%s'&quot;</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>arg1, arg2<span style="color: black;">&#41;</span>
        <span style="color: #008000;">super</span><span style="color: black;">&#40;</span>C, <span style="color: #008000;">self</span><span style="color: black;">&#41;</span>.<span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span>arg1, arg2<span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">print</span> C.__mro__
&nbsp;
c = C<span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;C's arg1&quot;</span>, <span style="color: #483d8b;">&quot;C's arg2&quot;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>
<p>执行结果：</p>
<div class="wp_syntax"><div class="code"><pre class="text" style="font-family:monospace;">init func in C, with arg1'C's arg1', arg2 'C's arg2'
init func in B, with arg1'C's arg1', arg2 'C's arg2'
init func in A, with arg1 'C's arg1'
(&lt;class '__main__.C'&gt;, &lt;class '__main__.B'&gt;, &lt;class '__main__.A'&gt;, &lt;type 'object'&gt;)</pre></div></div>
<p>可见几个类的构造器的执行顺序正是mro列表的顺序。重点是多重继承的各个类的构造器<code>__init__</code>之所以能够执行，是因为每个构造器里面都有一句super()，这个super完成mro列表中<strong>下一个类的构造器的调用</strong>。</p>
<p>这个事实听起来似乎很自然，但看代码，B的构造器还得必须知道A的构造器的参数？B需要知道自己将会被C同时继承A，并且调用A的构造？！！很荒谬，但不幸的这是mro的特点。代码是可以这么写，但不应该，为另一个不知道什么时候会被一起继承的类特地地写代码，跟面对对象设计的解耦原则相违背。</p>
<h2>How</h2>
<p>在mro方式的基础上，这个问题是不可能有效解决的，只能避免。概括起来大概有这么 两种方式。</p>
<h4>1.使用传统类的方式，显式调用<strong>被继承类</strong>的构造器，避开super的mro自动化工作。</h4>
<p>Codepad 看运行效果： <a href="http://codepad.org/XCVdKCPm">http://codepad.org/XCVdKCPm</a></p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">class</span> A<span style="color: black;">&#40;</span><span style="color: #008000;">object</span><span style="color: black;">&#41;</span>:
    <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>, arg1<span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">&quot;init func in A, with arg1 '%s'&quot;</span> <span style="color: #66cc66;">%</span> arg1
&nbsp;
<span style="color: #ff7700;font-weight:bold;">class</span> B<span style="color: black;">&#40;</span><span style="color: #008000;">object</span><span style="color: black;">&#41;</span>:
    <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>, arg1, arg2<span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">&quot;init func in B, with arg1'%s', arg2 '%s'&quot;</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>arg1, arg2<span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">class</span> C<span style="color: black;">&#40;</span>A, B<span style="color: black;">&#41;</span>:
    <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>, arg1, arg2<span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">&quot;init func in C, with arg1'%s', arg2 '%s'&quot;</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>arg1, arg2<span style="color: black;">&#41;</span>
        <span style="color: #808080; font-style: italic;">#super(C, self).__init__(arg1) #这两行</span>
        A.<span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, arg1<span style="color: black;">&#41;</span>       <span style="color: #808080; font-style: italic;">#等价</span>
        B.<span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, arg1, arg2<span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">print</span> C.__mro__
&nbsp;
c = C<span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;C's arg1&quot;</span>, <span style="color: #483d8b;">&quot;C's arg2&quot;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>
<p>注意 C继承A，B的顺序已经改变。</p>
<p>要排除一个容易产生的误解。Python文档里面的super有个很显著的Note：super() only works for new-style classes. super只适用于新类。但<strong>新类并不必须使用super</strong>。</p>
<p>直接调用<code>被继承类</code>的<code>__init__</code>作为unbound方法调用，需要指定一个实例，如self作为参数，依次调用各个<code>被继承类</code>。缺点是若果这几个<code>被继承类</code>也在构造方法里面使用这样调用了同一个上级<code>被继承类</code>，会出现“爷爷类”的构造方法被调用多次的情况。</p>
<p>如果一定使用super，要注意继承列表的顺序。super(TYPE, self).method调用的是mro列表中第一个，也即继承列表第一个类的方法。</p>
<p>PyQt里面的类内部一般（未细究）都使用<code>__init__</code>的方式来初始化代码，因而很多PyQt的例子代码都是使用<code>QtGui.QMainWindow.__init__(self)</code>这样的代码来初始化。当然在单继承时候和super的写法是等价的，但最好使用统一的原则:</p>
<p>一个简单好记的原则：</p>
<ul>
<li>如果"被继承类"都使用<code>__init__</code>，"继承类"就使用<code>__init__</code>来初始化；</li>
<li>如果"被继承类"都使用<code>super</code>，"继承类"就使用<code>super</code>来初始化；</li>
</ul>
<h4>2.使用Composition / Association Pattern的设计模式（即'Is-A'转换成'Has-A'）来实现相同功能，避免多重继承。</h4>
<p>这个方法听起来未免有点让人不快（破坏了原有设计思维），但实际上很可能这是更好的方式，更清晰的代码，尤其是要继承的类里面混合了使用<code>super</code>，<code>__init__</code>两种初始化方式的时候。</p>
<h2>Reference</h2>
<ul>
<li><a href="http://stackoverflow.com/questions/4029550/python-correct-way-to-initialize-when-superclasses-accept-different-arguments">Python: Correct way to initialize when superclasses accept different arguments?</a></li>
<li><a href="http://www.artima.com/weblogs/viewpost.jsp?thread=236275">Things to Know About Python Super</a> <a href="http://www.artima.com/weblogs/viewpost.jsp?thread=236278">2</a> <a href="http://www.artima.com/weblogs/viewpost.jsp?thread=237121">3</a></li>
<li><a href="http://blog.csdn.net/johnsonguo/article/details/585193">关于Python的super用法研究</a></li>
<li><a href="http://docs.python.org/library/functions.html#super">Python Built-in Functions#super()</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://apt-blog.net/constructors_with_different_arguments_in_python_multiinheri_class/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>以Python+Bottle框架作为jQuery.Uploadify控件的后端</title>
		<link>http://apt-blog.net/a_jquery_uploadify_backend_in_python_with_bottle</link>
		<comments>http://apt-blog.net/a_jquery_uploadify_backend_in_python_with_bottle#comments</comments>
		<pubDate>Mon, 05 Sep 2011 06:24:45 +0000</pubDate>
		<dc:creator>BOYPT</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[ajax]]></category>
		<category><![CDATA[jquery]]></category>
		<category><![CDATA[uploadify]]></category>
		<guid isPermaLink="false">http://apt-blog.net/a_jquery_uploadify_backend_in_python_with_bottle</guid>
		<description><![CDATA[jQuery.Uploadify是个功能强大的文件上传控件，基于Flash（3.0兼容HTML5），作为前端使用是个不错的选择。 目前的前端技术中，能够实现跨域POST的成熟方案一般只有借助flash，这也是Uploadify的特点吧。 Uploadify的官方提供的是一个php的后端，后端需要做的事情相当简单，就是把POST的临时数据存放到指定路径。 有不少中文资料是把uploadify作为.net程序的上传控件，原理其实一样，所以用Python实现也是很简单的。 其实那些实现的后端（包括官方实现的php）都有安全性问题，比如限制文件类型、指定上传路径都可以在远端控制，上传个木马之类会导致服务器被入侵。当然这是因为他们提供的例子是半成品，实际使用起码应该加上session id的验证之类，因为上传时候数据是由Flash发出的，和浏览器的Session无关。 下面的Python例子中，则就如还在beta阶段的uploadify 3的开发日志讨论的，暂时忽略掉folder参数。 完整的例子代码可在我的仓库下载。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 import bottle bottle.debug&#40;True&#41; [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.uploadify.com/about/">jQuery.Uploadify</a>是个功能强大的文件上传控件，基于Flash（3.0兼容HTML5），作为前端使用是个不错的选择。</p>
<p>目前的前端技术中，能够实现跨域POST的成熟方案一般只有借助flash，这也是Uploadify的特点吧。</p>
<p>Uploadify的官方提供的是一个php的后端，后端需要做的事情相当简单，就是把POST的临时数据存放到指定路径。 有不少中文资料是把uploadify作为.net程序的上传控件，原理其实一样，所以用Python实现也是很简单的。</p>
<p>其实那些实现的后端（包括官方实现的php）都有安全性问题，比如限制文件类型、指定上传路径都可以在远端控制，上传个木马之类会导致服务器被入侵。当然这是因为他们提供的例子是半成品，实际使用起码应该加上session id的验证之类，因为上传时候数据是由Flash发出的，和浏览器的Session无关。</p>
<p>下面的Python例子中，则就如<a href="http://www.uploadify.com/forums/discussion/7597/uploadify-3-with-new-features-corrections-and-documentation/">还在beta阶段的uploadify 3的开发日志讨论的</a>，暂时忽略掉folder参数。</p>
<p>完整的例子代码<a href="http://code.google.com/p/ptcoding/downloads/detail?name=uploadify_with_bottle.tar.gz">可在我的仓库下载</a>。</p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">import</span> bottle
bottle.<span style="color: black;">debug</span><span style="color: black;">&#40;</span><span style="color: #008000;">True</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">from</span> bottle <span style="color: #ff7700;font-weight:bold;">import</span> run, route, request, get, view, static_file, abort
<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;">os</span>
&nbsp;
UPLOAD_BASE = <span style="color: #483d8b;">&quot;/tmp/upload&quot;</span>
ALLOWED_TYPE = <span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;wmv&quot;</span>,<span style="color: #483d8b;">&quot;rmvb&quot;</span>,<span style="color: #483d8b;">&quot;mpeg&quot;</span>,<span style="color: #483d8b;">&quot;3gp&quot;</span>,<span style="color: #483d8b;">&quot;avi&quot;</span>,<span style="color: #483d8b;">&quot;mov&quot;</span>,<span style="color: #483d8b;">&quot;mp4&quot;</span>,<span style="color: #483d8b;">&quot;flv&quot;</span>,<span style="color: #483d8b;">&quot;mpg&quot;</span>,<span style="color: #483d8b;">&quot;vob&quot;</span>,<span style="color: #483d8b;">&quot;png&quot;</span>,<span style="color: #483d8b;">&quot;jpg&quot;</span><span style="color: black;">&#41;</span>
&nbsp;
@route<span style="color: black;">&#40;</span><span style="color: #483d8b;">'/upload'</span>, method=<span style="color: #483d8b;">'POST'</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">def</span> do_upload<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">try</span>:
        filename = request.<span style="color: black;">POST</span>.<span style="color: black;">get</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Filename'</span><span style="color: black;">&#41;</span>
        root, ext = <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">splitext</span><span style="color: black;">&#40;</span>filename<span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">assert</span> ext<span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span>:<span style="color: black;">&#93;</span>.<span style="color: black;">lower</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">in</span> ALLOWED_TYPE, <span style="color: #483d8b;">&quot;Invalid file type: &quot;</span> + ext
        <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;">isdir</span><span style="color: black;">&#40;</span>UPLOAD_BASE<span style="color: black;">&#41;</span>:
            <span style="color: #dc143c;">os</span>.<span style="color: black;">makedirs</span><span style="color: black;">&#40;</span>UPLOAD_BASE<span style="color: black;">&#41;</span>
        filedata = request.<span style="color: black;">POST</span>.<span style="color: black;">get</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Filedata'</span><span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">assert</span> filedata <span style="color: #ff7700;font-weight:bold;">is</span> <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #008000;">None</span>, <span style="color: #483d8b;">&quot;Invalid Upload data.&quot;</span>
        <span style="color: #ff7700;font-weight:bold;">with</span> <span style="color: #008000;">open</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">join</span><span style="color: black;">&#40;</span>UPLOAD_BASE, filename<span style="color: black;">&#41;</span>, <span style="color: #483d8b;">'wb'</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">as</span> f:
            <span style="color: #dc143c;">shutil</span>.<span style="color: black;">copyfileobj</span><span style="color: black;">&#40;</span>filedata.<span style="color: #008000;">file</span>, f, <span style="color: #ff4500;">8192</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">except</span> <span style="color: #008000;">AssertionError</span>, e:
        abort<span style="color: black;">&#40;</span><span style="color: #ff4500;">403</span>, <span style="color: #008000;">str</span><span style="color: black;">&#40;</span>e<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">else</span>:
        <span style="color: #ff7700;font-weight:bold;">return</span> filename
&nbsp;
@route<span style="color: black;">&#40;</span><span style="color: #483d8b;">'/crossdomain.xml'</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">def</span> crossdomain<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #483d8b;">&quot;&quot;&quot;&lt;cross-domain-policy&gt;
&lt;allow-access-from domain=&quot;*&quot;/&gt;
&lt;/cross-domain-policy&gt;
&quot;&quot;&quot;</span>
&nbsp;
@route<span style="color: black;">&#40;</span><span style="color: #483d8b;">'/static/:path#.+#'</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">def</span> server_static<span style="color: black;">&#40;</span>path<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">return</span> static_file<span style="color: black;">&#40;</span>path, root=<span style="color: #483d8b;">'static'</span><span style="color: black;">&#41;</span>
&nbsp;
@route<span style="color: black;">&#40;</span><span style="color: #483d8b;">'/'</span><span style="color: black;">&#41;</span>
@view<span style="color: black;">&#40;</span><span style="color: #483d8b;">'upload'</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">def</span> index<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">dict</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
run<span style="color: black;">&#40;</span>host=<span style="color: #483d8b;">'0.0.0.0'</span>, port=<span style="color: #ff4500;">8080</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>
]]></content:encoded>
			<wfw:commentRss>http://apt-blog.net/a_jquery_uploadify_backend_in_python_with_bottle/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Trac 运营环境最简易配置(uwsgi+nginx)</title>
		<link>http://apt-blog.net/trac-on-uwsgi-and-nginx-simple-steps</link>
		<comments>http://apt-blog.net/trac-on-uwsgi-and-nginx-simple-steps#comments</comments>
		<pubDate>Fri, 12 Aug 2011 17:34:29 +0000</pubDate>
		<dc:creator>BOYPT</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[nginx]]></category>
		<category><![CDATA[trac]]></category>
		<category><![CDATA[uwsgi]]></category>
		<guid isPermaLink="false">http://apt-blog.net/trac-on-uwsgi-and-nginx-simple-steps</guid>
		<description><![CDATA[@felixonmars 点名要一份trac的最简易配置步骤，不过其实trac这样一个geek东西，即使按本文配置好跑起来后也依然是一套半成品，还有很多东西需要去看文档去配置，就像awesome, vim等，在一段陡峭的学习曲线之前，都不怎么好用。 所以其实本文也不会降低多少门槛，大概比直接看官方的guide安装的时候少点压力，但不一定是好事。 此前有在公司技术部内推广使用trac做项目管理的想法，做过了解，不过因为各种原因还是用不下来。赖勇浩的博客有一系列文章介绍trac，也包括了在公司环境使用的经验。 Trac作为一套web应用，其实部署步骤和此前介绍的MoinMoin, Mercurial很类似的。 以下步骤介绍安装包含多国语言的基本trac环境。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 [...]]]></description>
			<content:encoded><![CDATA[<p>@felixonmars 点名要一份trac的最简易配置步骤，不过其实trac这样一个geek东西，即使按本文配置好跑起来后也依然是一套半成品，还有很多东西需要去看文档去配置，就像awesome, vim等，在一段陡峭的学习曲线之前，都不怎么好用。 所以其实本文也不会降低多少门槛，大概比直接看官方的guide安装的时候少点压力，但不一定是好事。</p>
<p>此前有在公司技术部内推广使用trac做项目管理的想法，做过了解，不过因为各种原因还是用不下来。赖勇浩的博客有<a href="http://blog.csdn.net/lanphaday/article/details/6609256">一系列文章</a>介绍trac，也包括了在公司环境使用的经验。</p>
<p>Trac作为一套web应用，其实部署步骤和此前介绍的<a href="http://apt-blog.net/moinmoin-on-nginx-via-fastcgi-and-uwgi">MoinMoin</a>, <a href="http://apt-blog.net/mercurial-repo-under-nginx-and-uwsgi">Mercurial</a>很类似的。</p>
<p>以下步骤介绍安装包含多国语言的基本trac环境。</p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
</pre></td><td class="code"><pre class="bash" style="font-family:monospace;">&nbsp;
<span style="color: #666666; font-style: italic;">#安装基本组建</span>
<span style="color: #c20cb9; font-weight: bold;">sudo</span> <span style="color: #660033;">-s</span>
add-apt-repository ppa:nginx<span style="color: #000000; font-weight: bold;">/</span>stable
add-apt-repository ppa:uwsgi<span style="color: #000000; font-weight: bold;">/</span>release
<span style="color: #c20cb9; font-weight: bold;">apt-get</span> update
<span style="color: #c20cb9; font-weight: bold;">apt-get</span> <span style="color: #c20cb9; font-weight: bold;">install</span> nginx uwsgi-python python-virtualenv subversion
&nbsp;
<span style="color: #666666; font-style: italic;">#本次安装所需的路径，可按需修改</span>
<span style="color: #007800;">TRAC_ENV_PYHOME</span>=<span style="color: #ff0000;">&quot;/srv/trac/python-env&quot;</span>
<span style="color: #007800;">TRAC_PROJECTS_DIR</span>=<span style="color: #ff0000;">&quot;/srv/trac/projects&quot;</span>
<span style="color: #007800;">PROJECT_PATH</span>=<span style="color: #ff0000;">&quot;<span style="color: #007800;">$TRAC_PROJECTS_DIR</span>/myproject1&quot;</span>
&nbsp;
<span style="color: #666666; font-style: italic;">#组件配置</span>
<span style="color: #c20cb9; font-weight: bold;">cat</span> <span style="color: #000000; font-weight: bold;">&gt;/</span>etc<span style="color: #000000; font-weight: bold;">/</span>uwsgi-python<span style="color: #000000; font-weight: bold;">/</span>apps-enabled<span style="color: #000000; font-weight: bold;">/</span>uwsgi.xml <span style="color: #cc0000; font-style: italic;">&lt;&lt; EOF
&lt;uwsgi&gt;
  &lt;limit-as&gt;256&lt;/limit-as&gt;
  &lt;processes&gt;6&lt;/processes&gt;
  &lt;memory-report/&gt;
  &lt;vhost/&gt;
  &lt;no-site/&gt;
&lt;/uwsgi&gt;
EOF</span>
&nbsp;
<span style="color: #666666; font-style: italic;">#安装Trac， dev版本。</span>
<span style="color: #666666; font-style: italic;">#虽然其实可以easy_install Trac，但是就不会编译多国语言了。</span>
&nbsp;
<span style="color: #7a0874; font-weight: bold;">cd</span> <span style="color: #000000; font-weight: bold;">/</span>tmp
virtualenv <span style="color: #007800;">$TRAC_ENV_PYHOME</span>
<span style="color: #7a0874; font-weight: bold;">source</span> <span style="color: #007800;">$TRAC_ENV_PYHOME</span><span style="color: #000000; font-weight: bold;">/</span>bin<span style="color: #000000; font-weight: bold;">/</span>activate
easy_install Genshi
easy_install Babel
&nbsp;
<span style="color: #c20cb9; font-weight: bold;">svn</span> <span style="color: #c20cb9; font-weight: bold;">co</span> http:<span style="color: #000000; font-weight: bold;">//</span>svn.edgewall.org<span style="color: #000000; font-weight: bold;">/</span>repos<span style="color: #000000; font-weight: bold;">/</span>trac<span style="color: #000000; font-weight: bold;">/</span>trunk trac
<span style="color: #7a0874; font-weight: bold;">cd</span> trac
python setup.py <span style="color: #c20cb9; font-weight: bold;">install</span>
&nbsp;
<span style="color: #666666; font-style: italic;">#建立项目</span>
<span style="color: #c20cb9; font-weight: bold;">mkdir</span> <span style="color: #007800;">$TRAC_PROJECTS_DIR</span>
trac-admin <span style="color: #007800;">$PROJECT_PATH</span> initenv
&nbsp;
<span style="color: #666666; font-style: italic;">#如果你的终端为中文（zh_CN.UTF-8，比如date命令是中文的年月日），现在应该是中文提示，否则以上安装可能问题。</span>
&nbsp;
<span style="color: #666666; font-style: italic;">#输入项目名称、数据库连接（默认）后，项目建立完成</span>
&nbsp;
trac-admin <span style="color: #007800;">$PROJECT_PATH</span> deploy <span style="color: #007800;">$PROJECT_PATH</span><span style="color: #000000; font-weight: bold;">/</span>deploy
&nbsp;
<span style="color: #7a0874; font-weight: bold;">cd</span> <span style="color: #007800;">$PROJECT_PATH</span><span style="color: #000000; font-weight: bold;">/</span>deploy<span style="color: #000000; font-weight: bold;">/</span>cgi-bin
<span style="color: #c20cb9; font-weight: bold;">cp</span> trac.wsgi trac_wsgi.py
&nbsp;
<span style="color: #c20cb9; font-weight: bold;">chown</span> www-data:www-data <span style="color: #660033;">-R</span> <span style="color: #007800;">$PROJECT_PATH</span>
&nbsp;
<span style="color: #c20cb9; font-weight: bold;">cat</span> <span style="color: #000000; font-weight: bold;">&gt;/</span>etc<span style="color: #000000; font-weight: bold;">/</span>nginx<span style="color: #000000; font-weight: bold;">/</span>sites-enabled<span style="color: #000000; font-weight: bold;">/</span>trac-project1 <span style="color: #cc0000; font-style: italic;">&lt;&lt; EOF
server {
    listen   80;
    server_name project1.trac.domain.com;
    location / {
                include uwsgi_params;
                uwsgi_pass unix:///var/run/uwsgi-python/uwsgi/socket;
                uwsgi_param UWSGI_PYHOME $TRAC_ENV_PYHOME;
                uwsgi_param UWSGI_CHDIR $PROJECT_PATH/deploy/cgi-bin;
                uwsgi_param UWSGI_SCRIPT trac_wsgi;
                uwsgi_param UWSGI_SCHEME \$scheme;
                uwsgi_param REMOTE_USER \$remote_user;
        }
}
EOF</span>
&nbsp;
service uwsgi-python restart
service nginx reload</pre></td></tr></table></div>
<p>现在打开浏览器，根据你的地址，应该能打开自己的Trac了。</p>
<p>一个Trac实例只管理一个项目，如果需要多个trac就需要重复上述“添加项目”后的步骤。</p>
<p>Tips:</p>
<p>上述<code>trac-admin /xxx deploy</code>命令后，生成的目录里面有个<code>htdocs</code>目录，包含了项目内的静态文件。如果在nginx配置里面加上这样一段：</p>
<div class="wp_syntax"><div class="code"><pre class="text" style="font-family:monospace;">        location /htdocs/ {
                root $PROJECT_PATH/deploy;
        }</pre></div></div>
<p>(注意里面的<code>$PROJECT_PATH</code>变量要对应你实际项目的路径)，</p>
<p>然后修改<code>$PROJECT_PATH/conf/trac.ini</code>内<code>htdocs_location =</code>的值为<code>/htdocs/</code></p>
<p>可以降低静态文件请求对服务器的压力。</p>
<p>最后，多看官方文档。</p>
]]></content:encoded>
			<wfw:commentRss>http://apt-blog.net/trac-on-uwsgi-and-nginx-simple-steps/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Vimrepress Having Big Changes (Updated Beta Version)</title>
		<link>http://apt-blog.net/vimrepress-having-big-changes</link>
		<comments>http://apt-blog.net/vimrepress-having-big-changes#comments</comments>
		<pubDate>Fri, 13 May 2011 07:11:49 +0000</pubDate>
		<dc:creator>BOYPT</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Vim]]></category>
		<category><![CDATA[vimpress]]></category>
		<guid isPermaLink="false">http://apt-blog.net/vimrepress-having-big-changes</guid>
		<description><![CDATA[Update Vimpress 已经升级到2.x版本，使用、配置都有改进，请关注在vim.org的插件页面： Vimpress had been updated to 2.x, usage and configurations are now different, read the officle page in vim.org: http://www.vim.org/scripts/script.php?script_id=3510 Hi guys, glad to know that many of you are having vimpress forked to improved it's power, while i'm also doing the same thing. I'm working on a version that had been refactored [...]]]></description>
			<content:encoded><![CDATA[<p><strong> Update </strong><br />
Vimpress 已经升级到2.x版本，使用、配置都有改进，请关注在vim.org的插件页面： </p>
<p>Vimpress had been updated to 2.x, usage and configurations are now different, read the officle page in vim.org:</p>
<p><a href="http://www.vim.org/scripts/script.php?script_id=3510">http://www.vim.org/scripts/script.php?script_id=3510</a></p>
<hr />
<p>Hi guys, glad to know that many of you are having vimpress forked to improved it's power, while i'm also doing the same thing.</p>
<p>I'm working on a version that had been refactored a lot, changes include:</p>
<ul>
<li>
<p>Commands simplified. No more BlogPageNew/BlogPageSave commands anymore, managing pages are available via the argument to the original BlogNew/BlogList commands, Meta fields shows you what kind of post you're editing.</p>
</li>
<li>
<p>Much better Markdown integrated. No more MarkdownXXX commands. You can decide what you edit (Markdown or HTML) in the meta field, when execute BlogSave or BlogPreview, program parse the meta field to got things done.</p>
</li>
<li>
<p>More easier editing wordpress in Markdown. Markdown rawtext will automaticly uploads as an attachment file. Updating a post before is complicated, now after you write a post in markdown in vimpress, and then want to do some update, just open the post in Vimpress, it downloads the attachment and show you your original markdown text, when you BlogSave again, the attachment also got updated for further editing.</p>
</li>
</ul>
<p><a href="http://apt-blog.net/wp-content/uploads/2011/05/new-vimpress.png" rel="lightbox[955]" title="new-vimpress.png"><img title="new-vimpress.png" src="http://apt-blog.net/wp-content/uploads/2011/05/new-vimpress.png" class="aligncenter" width="40%" /></a></p>
<p><S>This version of vimpress is not yet released, for i'm still debuging, i just finished all this features past few days. If any of you got interested, go to my svn:</S></p>
<p><S>svn checkout http://ptcoding.googlecode.com/svn/trunk/vimpress-dev</S></p>
<p><S>I separated python code from blog.vim to avoid my coding editor got confused about python syntax, i'll combine them together when final release, if you want to try now, got to change the last line about the python file in "blog.vim".</S></p>
<p>Now this version is now available as beta, you may try it for now:</p>
<p><a href="https://bitbucket.org/pentie/vimrepress/downloads/vimpress_2.1.0beta_r34_179c33a255c7.zip">https://bitbucket.org/pentie/vimrepress/downloads/vimpress_2.1.0beta_r34_179c33a255c7.zip</a></p>
<p>I would like to hear any advices from you.</p>
<p><S>By the way, the document attached is not yet updated, the commands list below are "legle" in the new version, you would find them easy to use:</S></p>
<p>The document attached is now updated, you may refer to it about the new usage, which are all simplified and powerfied, command examples:</p>
<ul>
<li>:BlogList             -  List 30 recent posts.</li>
<li>:BlogList page        -  List 30 recent pages.</li>
<li>
<p>:BlogList post 100    -  List 100 recent posts.</p>
</li>
<li>
<p>:BlogNew post         -  Write an new post.</p>
</li>
<li>
<p>:BlogNew page         -  Write an new page.</p>
</li>
<li>
<p>:BlogSave             -  Save (defautely published.)</p>
</li>
<li>
<p>:BlogSave draft       -  Save as draft.</p>
</li>
<li>
<p>:BlogPreview local    -  Preview page/post locally in your browser. </p>
</li>
<li>
<p>:BlogPreview publish  -  Same as `:BlogSave publish' with brower opened.</p>
</li>
<li>
<p>:BlogOpen 679</p>
</li>
<li>:BlogOpen http://your-first-blog.com/archives/679</li>
<li>:BlogOpen http://your-second-blog.com/?p=679</li>
<li>:BlogOpen http://your-third-blog.com/with-your-custom-permalink</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://apt-blog.net/vimrepress-having-big-changes/feed</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>nginx+uwsgi架设自用Mercurial hg仓库：hgwebdir</title>
		<link>http://apt-blog.net/mercurial-repo-under-nginx-and-uwsgi</link>
		<comments>http://apt-blog.net/mercurial-repo-under-nginx-and-uwsgi#comments</comments>
		<pubDate>Fri, 11 Mar 2011 07:43:59 +0000</pubDate>
		<dc:creator>BOYPT</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Unix/Linux]]></category>
		<category><![CDATA[hg]]></category>
		<category><![CDATA[mercurial]]></category>
		<category><![CDATA[nginx]]></category>
		<guid isPermaLink="false">http://apt-blog.net/mercurial-repo-under-nginx-and-uwsgi</guid>
		<description><![CDATA[Mercurial/hg是分布式版本管理工具之一，如google code等公共代码服务都直接支持。不过公共服务通常都有如公开开源、空间大小等等的限制，如果特殊情况，需要自己在VPS上假设一套，也不太难。 如果单独一个hg仓库，直接运行hg serve就已经是在线仓库，用nginx做的http_proxy反向代理就够了，各种功能齐全。但是要host多个hg仓库，需要用hgwebdir。 Ubuntu当中安装的mercurial已经包含了架设仓库所用的程序，包括cgi/fcgi/wcgi的，主接口在/usr/share/doc/mercurial-common/examples下的hgwebdir.(ws&#124;(f)c)gi。虽然其中的wsgi有效的只有三行。 Mercurial官方Wiki上主要介绍了用apache2来跑cgi方式的仓库，但是作为python的程序，wsgi才是最原生的嘛。用Nginx+uwsgi的方式，资源占用、响应效率等，都要比apache + cgi靠谱。 Nginx+uwsgi的方式很普遍，参见本博客之前的MoinMoin 与 Nginx, fastcgi 与 uwsgi 的配置一文。 但是因为uwsgi处于快速开发过程当中，ppa的打包也还不十分成熟，里面的路径跟以前有些不一样了。 add-apt-repository ppa:nginx/stable add-apt-repository ppa:uwsgi/release apt-get update apt-get install nginx uwsgi-python python-virtualenv mercurial &#160; mkdir -p /var/www/hgwebdir virtualenv /var/www/hgwebdir/python-home &#160; cat &#62;/var/www/hgwebdir/hgwebdir_wsgi.py &#60;&#60; EOF from mercurial import demandimport; demandimport.enable() from mercurial.hgweb.hgwebdir_mod import hgwebdir &#160; class hgwebdir_with_fixed_staticfile(hgwebdir): def __call__(self, env, respond): [...]]]></description>
			<content:encoded><![CDATA[<p>Mercurial/hg是分布式版本管理工具之一，如google code等公共代码服务都直接支持。不过公共服务通常都有如公开开源、空间大小等等的限制，如果特殊情况，需要自己在VPS上假设一套，也不太难。</p>
<p>如果单独一个hg仓库，直接运行hg serve就已经是在线仓库，用nginx做的http_proxy反向代理就够了，各种功能齐全。但是要host多个hg仓库，需要用hgwebdir。</p>
<p>Ubuntu当中安装的mercurial已经包含了架设仓库所用的程序，包括cgi/fcgi/wcgi的，主接口在<code>/usr/share/doc/mercurial-common/examples</code>下的<code>hgwebdir.(ws|(f)c)gi</code>。虽然其中的wsgi有效的只有三行。</p>
<p>Mercurial官方Wiki上主要<a href="http://mercurial.selenic.com/wiki/HgWebDirStepByStep">介绍了用apache2来跑cgi方式的仓库</a>，但是作为python的程序，wsgi才是最原生的嘛。用Nginx+uwsgi的方式，资源占用、响应效率等，都要比apache + cgi靠谱。</p>
<p>Nginx+uwsgi的方式很普遍，参见本博客之前的<a href="http://apt-blog.net/moinmoin-on-nginx-via-fastcgi-and-uwgi">MoinMoin 与 Nginx, fastcgi 与 uwsgi 的配置</a>一文。</p>
<p>但是因为uwsgi处于快速开发过程当中，ppa的打包也还不十分成熟，里面的路径跟以前有些不一样了。</p>
<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">add-apt-repository ppa:nginx<span style="color: #000000; font-weight: bold;">/</span>stable
add-apt-repository ppa:uwsgi<span style="color: #000000; font-weight: bold;">/</span>release
<span style="color: #c20cb9; font-weight: bold;">apt-get</span> update
<span style="color: #c20cb9; font-weight: bold;">apt-get</span> <span style="color: #c20cb9; font-weight: bold;">install</span> nginx uwsgi-python python-virtualenv mercurial
&nbsp;
<span style="color: #c20cb9; font-weight: bold;">mkdir</span> <span style="color: #660033;">-p</span> <span style="color: #000000; font-weight: bold;">/</span>var<span style="color: #000000; font-weight: bold;">/</span>www<span style="color: #000000; font-weight: bold;">/</span>hgwebdir
virtualenv <span style="color: #000000; font-weight: bold;">/</span>var<span style="color: #000000; font-weight: bold;">/</span>www<span style="color: #000000; font-weight: bold;">/</span>hgwebdir<span style="color: #000000; font-weight: bold;">/</span>python-home
&nbsp;
<span style="color: #c20cb9; font-weight: bold;">cat</span> <span style="color: #000000; font-weight: bold;">&gt;/</span>var<span style="color: #000000; font-weight: bold;">/</span>www<span style="color: #000000; font-weight: bold;">/</span>hgwebdir<span style="color: #000000; font-weight: bold;">/</span>hgwebdir_wsgi.py <span style="color: #cc0000; font-style: italic;">&lt;&lt; EOF
from mercurial import demandimport; demandimport.enable()
from mercurial.hgweb.hgwebdir_mod import hgwebdir
&nbsp;
class hgwebdir_with_fixed_staticfile(hgwebdir):
    def __call__(self, env, respond):
        response = super(self.__class__, self).__call__(env, respond)
        return response[0] if isinstance(response, tuple) else response
&nbsp;
application = hgwebdir_with_fixed_staticfile('/var/www/hgwebdir/hgweb.config')
EOF</span>
&nbsp;
<span style="color: #c20cb9; font-weight: bold;">cat</span> <span style="color: #000000; font-weight: bold;">&gt;/</span>var<span style="color: #000000; font-weight: bold;">/</span>www<span style="color: #000000; font-weight: bold;">/</span>hgwebdir<span style="color: #000000; font-weight: bold;">/</span>hgweb.config <span style="color: #cc0000; font-style: italic;">&lt;&lt; EOF
[collections]
/home/hg-repo = /home/hg-repo
&nbsp;
[web]
style = monoblue
allow_archive = gz, zip, bz2
baseurl = /
push_ssl = true
hidden = false
allow_read = *
allow_push = user1 user2
&nbsp;
[extensions]
#highlight =
EOF</span>
&nbsp;
<span style="color: #c20cb9; font-weight: bold;">cat</span> <span style="color: #000000; font-weight: bold;">&gt;/</span>etc<span style="color: #000000; font-weight: bold;">/</span>uwsgi-python<span style="color: #000000; font-weight: bold;">/</span>apps-enabled<span style="color: #000000; font-weight: bold;">/</span>uwsgi.xml <span style="color: #cc0000; font-style: italic;">&lt;&lt; EOF
&lt;uwsgi&gt;
  &lt;limit-as&gt;256&lt;/limit-as&gt;
  &lt;processes&gt;6&lt;/processes&gt;
  &lt;memory-report/&gt;
  &lt;vhost/&gt;
  &lt;no-site/&gt;
&lt;/uwsgi&gt;
EOF</span>
&nbsp;
<span style="color: #c20cb9; font-weight: bold;">cat</span> <span style="color: #000000; font-weight: bold;">&gt;/</span>etc<span style="color: #000000; font-weight: bold;">/</span>nginx<span style="color: #000000; font-weight: bold;">/</span>sites-enabled<span style="color: #000000; font-weight: bold;">/</span>hgwebdir <span style="color: #cc0000; font-style: italic;">&lt;&lt; EOF
server {
    listen   80;
    client_max_body_size 512m;
    server_name hg.mydomian.com;
    location / {
                include uwsgi_params;
                uwsgi_pass unix:///var/run/uwsgi-python/uwsgi/socket;
                uwsgi_param UWSGI_PYHOME /var/www/hgwebdir/python-home;
                uwsgi_param UWSGI_CHDIR /var/www/hgwebdir/;
                uwsgi_param UWSGI_SCRIPT hgwebdir_wsgi;
                uwsgi_param UWSGI_SCHEME \$scheme;
                uwsgi_param REMOTE_USER \$remote_user;
        }
}
EOF</span>
&nbsp;
<span style="color: #c20cb9; font-weight: bold;">mkdir</span> <span style="color: #000000; font-weight: bold;">/</span>home<span style="color: #000000; font-weight: bold;">/</span>hg-repo
<span style="color: #7a0874; font-weight: bold;">cd</span> <span style="color: #000000; font-weight: bold;">/</span>home<span style="color: #000000; font-weight: bold;">/</span>hg-repo
hg init example-repo1
hg init example-repo2
<span style="color: #c20cb9; font-weight: bold;">chown</span> <span style="color: #660033;">-R</span> www-data:www-data <span style="color: #000000; font-weight: bold;">/</span>home<span style="color: #000000; font-weight: bold;">/</span>hg-repo
&nbsp;
<span style="color: #000000; font-weight: bold;">/</span>etc<span style="color: #000000; font-weight: bold;">/</span>init.d<span style="color: #000000; font-weight: bold;">/</span>nginx restart
<span style="color: #000000; font-weight: bold;">/</span>etc<span style="color: #000000; font-weight: bold;">/</span>init.d<span style="color: #000000; font-weight: bold;">/</span>uwsgi-python restart</pre></div></div>
<p>所以，/home/hg-repo下的所有仓库(新建的两个空仓库example-repo1/2)都会被hgwebdir列表在目录上，直接使用其地址就可以pull/push了。</p>
<p>注意的是hgweb.config文件里面的配置，<code>push_ssl = true</code>只允许使用https协议的时候push，也就是需要另外配置一个支持https的nginx。这个属于nginx的基本配置，这里不重提了。</p>
<p>对于不公开的仓库，除了HTTPS，还需要用户名密码，只需要配置nginx的basic http auth，上面命令当中的<code>uwsgi_param REMOTE_USER $remote_user</code>一行配置会把http auth的USER通过协议发送到hgweb，完成其认证过程。</p>
<p>其实hgweb的认证仅仅是用户名的匹配，hgweb.config里面的<code>allow_push = user1 user2</code>是全局设定，各个仓库的<code>.hg/hgrc</code>里面的同样的[web]区下也可以定义allow_push/read，即仓库各自的读写权限。</p>
<p><code>hgweb.config</code>中注释掉的highlight一行，是启动语法高亮插件用的，打开需要先安装<code>python-pygments</code>包，不然会出错。</p>
<p>这个版本的uwsgi程序的log在<code>/var/log/uwsgi-python/uwsgi.log</code>，如果出任何问题，大概看看log的提示按提示去修复即可。</p>
<p><strong>Update: 2011-3-17</strong></p>
<p>原hgwebdir的程序跟wsgi2协议兼容不好，会出现仓库目录的首页里面的静态文件全部无法打开，log里面的信息是<code>invalid WSGI2.0 response size: 1 .</code>。 上述代码中已经修复（见1楼留言。）  <strong>Thank You, Leonid.</strong></p>
]]></content:encoded>
			<wfw:commentRss>http://apt-blog.net/mercurial-repo-under-nginx-and-uwsgi/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Manage multiple wordpresses with new Vimpress</title>
		<link>http://apt-blog.net/manage-multiple-wordpresses-with-new-vimpress</link>
		<comments>http://apt-blog.net/manage-multiple-wordpresses-with-new-vimpress#comments</comments>
		<pubDate>Thu, 10 Mar 2011 07:40:53 +0000</pubDate>
		<dc:creator>BOYPT</dc:creator>
				<category><![CDATA[Blogger Tech]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Shell]]></category>
		<category><![CDATA[Unix/Linux]]></category>
		<category><![CDATA[Vim]]></category>
		<category><![CDATA[Blog]]></category>
		<category><![CDATA[vimpress]]></category>
		<guid isPermaLink="false">http://apt-blog.net/manage-multiple-wordpresses-with-new-vimpress</guid>
		<description><![CDATA[Update Vimpress 已经升级到2.x版本，使用、配置都有改进，请关注在vim.org的插件页面： Vimpress had been updated to 2.x, usage and configurations are now different, read the officle page in vim.org: http://www.vim.org/scripts/script.php?script_id=3510 If you noticed Vimpress before and got more than one wordpress to write, must glad now to know this VimRePressed support multiple configs and swith freely. The new VimRePressed (Version: 1.2.0) Changes: Add: [...]]]></description>
			<content:encoded><![CDATA[<p><strong> Update </strong><br />
Vimpress 已经升级到2.x版本，使用、配置都有改进，请关注在vim.org的插件页面： </p>
<p>Vimpress had been updated to 2.x, usage and configurations are now different, read the officle page in vim.org:</p>
<p><a href="http://www.vim.org/scripts/script.php?script_id=3510">http://www.vim.org/scripts/script.php?script_id=3510</a></p>
<hr />
<p>If you noticed <a href="http://apt-blog.net/tag/vimpress">Vimpress</a> before and got more than one wordpress to write, must glad now to know this VimRePressed support multiple configs and swith freely.</p>
<p>The new VimRePressed (Version: 1.2.0) Changes:</p>
<div class="wp_syntax"><div class="code"><pre class="text" style="font-family:monospace;">      Add: MarkdownPreview command to preiview markdown in browser.
      Add: MarkdownNewPost command to convert a markdown
           written post into html and set to the new post view.
      Add: Move blog configs info to personal .vimrc
      Add: Multiple config is now supported, and with :BlogSwitch
           command added.
      Add: Show which blog your editing at :BlogList view.
      Fix: Running :BlogList in the List view got error.</pre></div></div>
<p>Now you got to wirte your blog config in your own ~/.vimrc file. (CAREFUL when your share your vim configure with others then. Use a <code>source my_vimpress.vimrc</code> instead. )</p>
<div class="wp_syntax"><div class="code"><pre class="text" style="font-family:monospace;">let VIMPRESS=[{'username':'user',
              \'password':'pass',
              \'blog_url':'http://your-first-blog.com/'
              \},
              \{'username':'user',
              \'password':'pass',
              \'blog_url':'http://your-second-blog.com/'
              \}]</pre></div></div>
<p>The syntax looks just like python or json, dictionarys in a list(or array), only that line brake need to write in the vim way.</p>
<p>BlogSwitch command rotate you from the first blog to the last, then the first. If you got confused, BlogList command can show you which one you're connecting to.</p>
<p>Then the Markdown support. I added two command to save my time saving .mkd file then convert them into html then check in a browser, MarkdownPreview command can do this for me. When flawless, the MarkdownNewPost command switch me to the new post view with all thing I write into html, which is ready to publish.</p>
<p>Download: <a href="http://code.google.com/p/ptcoding/downloads/detail?name=vimpress_1.2.5-r55.zip">from google code</a></p>
<p>SVN Version also available:</p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
</pre></td><td class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #c20cb9; font-weight: bold;">svn</span> checkout http:<span style="color: #000000; font-weight: bold;">//</span>ptcoding.googlecode.com<span style="color: #000000; font-weight: bold;">/</span>svn<span style="color: #000000; font-weight: bold;">/</span>trunk<span style="color: #000000; font-weight: bold;">/</span>vimpress</pre></td></tr></table></div>
]]></content:encoded>
			<wfw:commentRss>http://apt-blog.net/manage-multiple-wordpresses-with-new-vimpress/feed</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Vim中写WordPress博客 - VimRepress</title>
		<link>http://apt-blog.net/release-of-vimrepress_cn</link>
		<comments>http://apt-blog.net/release-of-vimrepress_cn#comments</comments>
		<pubDate>Sun, 27 Feb 2011 14:10:51 +0000</pubDate>
		<dc:creator>BOYPT</dc:creator>
				<category><![CDATA[Blogger Tech]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Vim]]></category>
		<category><![CDATA[plugin]]></category>
		<category><![CDATA[vimpress]]></category>
		<guid isPermaLink="false">http://apt-blog.net/release-of-vimrepress_cn</guid>
		<description><![CDATA[Update Vimpress 已经升级到2.x版本，使用、配置都有改进，请关注在vim.org的插件页面： http://www.vim.org/scripts/script.php?script_id=3510 WordPress的客户端其实不少，从live writter到scribefire，用户都不少。但是有时候我们只需要一篇博客包含简洁的HTML，这时候这些所见所得编辑器出来的结果都不一定都让人那么满意，经常需要登录到博客后台去编辑源码。 实际上最适合的还是方法还是先用markdown/reStructed/Tex/WikiText之类的语法写成，然后编译成HTML，再发布到博客。 Vim有个vimpress插件可以直接在Vim当中编辑博客，相当适合这个任务。不过由于官方页面上的vimpress已经久无人维护已经不能用了，之前对vimpress的代码做了 一些改写重构，而且得到外国网友的关注 ，而且冠名 VimRepress ，现在正式发布一个VimRepressed的版本。 比起原版的Vimpress，VimRepress有了更友好的错误提示，带了上传图片等的功能，改善了几个命令的可用性： BlogList &#60;count&#62; 列表文章，可以用参数定义列出多少篇文章。默认最近30篇。 BlogNew 新文章命令，会将当前的buffer内容作为文章的内容。 BlogOpen &#60;id&#62; 打开文章，需要手动输入post id. BlogSave &#60;draft&#124;publish&#62; 保存文章，可以保存为草稿、或者直接发布。 BlogPreview &#60;draft&#124;publish&#62; 预览文章，实际和BlogSave命令相同，之后会打开浏览器窗口预览文章。但如果你是保存成草稿的，还得在浏览器登录才能看到。 BlogUpload &#60;file&#62; 可上传图片等文件到博客。自动添加链接代码到光标后。 BlogCode &#60;code type&#62; 代码片段，添加一个&#60;pre&#62;元素的块，对程序博客特别有用，目前默认用wp-syntax插件的高亮模式。 下载: From Google Code SVN 版本: svn co http://ptcoding.googlecode.com/svn/trunk/vimpress/ 安装： 下载以上.zip文件，解压到~/.vim目录: cd ~/.vim unzip /where/you/downlaod/vimpress-1.x.x.zip 配置： 编辑 ~/.vim/plugin/blog.vim, 查找Settings，将会找到如下的一块代码： ##################### [...]]]></description>
			<content:encoded><![CDATA[<p><strong> Update </strong><br />
Vimpress 已经升级到2.x版本，使用、配置都有改进，请关注在vim.org的插件页面： </p>
<p><a href="http://www.vim.org/scripts/script.php?script_id=3510">http://www.vim.org/scripts/script.php?script_id=3510</a></p>
<hr />
<p>WordPress的客户端其实不少，从live writter到scribefire，用户都不少。但是有时候我们只需要一篇博客包含简洁的HTML，这时候这些所见所得编辑器出来的结果都不一定都让人那么满意，经常需要登录到博客后台去编辑源码。</p>
<p>实际上最适合的还是方法还是先用markdown/reStructed/Tex/WikiText之类的语法写成，然后编译成HTML，再发布到博客。</p>
<p>Vim有个vimpress插件可以直接在Vim当中编辑博客，相当适合这个任务。不过由于官方页面上的vimpress已经久无人维护已经不能用了，之前对vimpress的代码做了<a href="http://apt-blog.net/an-improved-vimpress"> 一些改写重构，而且得到外国网友的关注 </a>，而且冠名<a href="http://fzysqr.com/2010/12/12/vimrepressed/"> VimRepress </a>，现在正式发布一个VimRepressed的版本。</p>
<p>比起原版的Vimpress，VimRepress有了更友好的错误提示，带了上传图片等的功能，改善了几个命令的可用性：</p>
<ul>
<li><code>BlogList &lt;count&gt;</code> 列表文章，可以用参数定义列出多少篇文章。默认最近30篇。</li>
<li><code>BlogNew</code> 新文章命令，会将当前的buffer内容作为文章的内容。</li>
<li><code>BlogOpen &lt;id&gt;</code> 打开文章，需要手动输入post id.</li>
<li><code>BlogSave &lt;draft|publish&gt;</code> 保存文章，可以保存为草稿、或者直接发布。</li>
<li><code>BlogPreview &lt;draft|publish&gt;</code> 预览文章，实际和<code>BlogSave</code>命令相同，之后会打开浏览器窗口预览文章。但如果你是保存成草稿的，还得在浏览器登录才能看到。</li>
<li><code>BlogUpload &lt;file&gt;</code> 可上传图片等文件到博客。自动添加链接代码到光标后。</li>
<li><code>BlogCode &lt;code type&gt;</code> 代码片段，添加一个&lt;pre&gt;元素的块，对程序博客特别有用，目前默认用<code>wp-syntax</code>插件的高亮模式。</li>
</ul>
<p><strong>下载</strong>: <a href="http://code.google.com/p/ptcoding/downloads/detail?name=vimpress-1.1.2-r45.zip">From Google Code</a></p>
<p><strong>SVN 版本</strong>: <code>svn co http://ptcoding.googlecode.com/svn/trunk/vimpress/</code></p>
<p><strong>安装：</strong></p>
<p>下载以上.zip文件，解压到<code>~/.vim</code>目录: </p>
<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">  <span style="color: #7a0874; font-weight: bold;">cd</span> ~<span style="color: #000000; font-weight: bold;">/</span>.vim
  <span style="color: #c20cb9; font-weight: bold;">unzip</span> <span style="color: #000000; font-weight: bold;">/</span>where<span style="color: #000000; font-weight: bold;">/</span>you<span style="color: #000000; font-weight: bold;">/</span>downlaod<span style="color: #000000; font-weight: bold;">/</span>vimpress-<span style="color: #000000;">1</span>.x.x.zip</pre></div></div>
<p><strong>配置：</strong></p>
<p>编辑 <code>~/.vim/plugin/blog.vim</code>, 查找<code>Settings</code>，将会找到如下的一块代码：</p>
<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">#####################</span>
<span style="color: #808080; font-style: italic;">#      Settings     #</span>
<span style="color: #808080; font-style: italic;">#####################</span>
&nbsp;
blog_username = <span style="color: #483d8b;">'user'</span>
blog_password = <span style="color: #483d8b;">'pass'</span>
blog_url = <span style="color: #483d8b;">'http://yourblog.com'</span></pre></div></div>
<p>填入你的用户名、密码、博客地址，保存即可。</p>
<p>然后重新打开一个Vim，就可以敲<code>:BlogList</code>命令试试了。</p>
]]></content:encoded>
			<wfw:commentRss>http://apt-blog.net/release-of-vimrepress_cn/feed</wfw:commentRss>
		<slash:comments>13</slash:comments>
		</item>
		<item>
		<title>Release of VimRepress 1.1.2</title>
		<link>http://apt-blog.net/release-of-vimrepress-1-1-2</link>
		<comments>http://apt-blog.net/release-of-vimrepress-1-1-2#comments</comments>
		<pubDate>Wed, 23 Feb 2011 10:20:23 +0000</pubDate>
		<dc:creator>BOYPT</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Vim]]></category>
		<category><![CDATA[plugin]]></category>
		<category><![CDATA[vimpress]]></category>
		<guid isPermaLink="false">http://apt-blog.net/release-of-vimrepress-1-1-2</guid>
		<description><![CDATA[Update Vimpress 已经升级到2.x版本，使用、配置都有改进，请关注在vim.org的插件页面： Vimpress had been updated to 2.x, usage and configurations are now different, read the officle page in vim.org: http://www.vim.org/scripts/script.php?script_id=3510 VimRepress is a broken fixed version of vimpress, a vim plugin to manage your wordpress. The name VimRepress is given by Justin Sattery described it a "A mod of a mod of a [...]]]></description>
			<content:encoded><![CDATA[<p><strong> Update </strong><br />
Vimpress 已经升级到2.x版本，使用、配置都有改进，请关注在vim.org的插件页面： </p>
<p>Vimpress had been updated to 2.x, usage and configurations are now different, read the officle page in vim.org:</p>
<p><a href="http://www.vim.org/scripts/script.php?script_id=3510">http://www.vim.org/scripts/script.php?script_id=3510</a></p>
<hr />
<p>VimRepress is a broken fixed version of vimpress, a vim plugin to manage your wordpress. The name VimRepress is given by <a href="http://fzysqr.com/2010/12/12/vimrepressed/">Justin Sattery</a> described it a "A mod of a mod of a mod of Vimpress" which i like alot :) , who commented in my <a href="http://apt-blog.net/an-improved-vimpress">previous post</a> together with some bug fixed code. Many thanks to Justin.</p>
<p>Features compared to the old vimpress:</p>
<ul>
<li><code>BlogNew</code> command keep current buffer as post content.</li>
<li><code>BlogUpload</code> command to upload images or other files.</li>
<li><code>BlogList</code> command can be specify how many recent posts to list.</li>
<li><code>BlogSave</code> command can save as draft, or just publish.</li>
<li><code>BlogPreview</code> command works same as BlogSave, but open a browser for a preview page.</li>
<li><code>BlogCode</code> command to append a code block of &lt;pre&gt; element for code highlight.</li>
</ul>
<p><strong>Download</strong>: <a href="http://code.google.com/p/ptcoding/downloads/detail?name=vimpress-1.1.2-r45.zip">From Google Code</a></p>
<p><strong>SVN Version</strong>: <code>http://ptcoding.googlecode.com/svn/trunk/vimpress/</code></p>
<p>Full Help Manual:</p>
<div class="wp_syntax"><div class="code"><pre class="text" style="font-family:monospace;">*vimpress.txt* Manage wordpress blog from Vim
&nbsp;
Vimpress
&nbsp;
Author: Adrien Friggeri &lt;adrien@friggeri.net&gt;
        Preston M. [BOYPT] &lt;pentie@gmail.com&gt;
&nbsp;
==============================================================================
&nbsp;
== Install ==
&nbsp;
Download vimpress-1.x.x.zip, extract it in your .vim directory:
&nbsp;
cd ~/.vim
  unzip /where/you/downlaod/vimpress-1.x.x.zip
&nbsp;
== Configure ==
&nbsp;
Edit ~/.vim/plugin/blog.vim, search &quot;Settings&quot;, you will find such section:
&nbsp;
#####################
#      Settings     #
#####################
&nbsp;
blog_username = 'user'
blog_password = 'pass'
blog_url = 'http://yourblog.com'
&nbsp;
Fill in this about your blog, save, thats all.
&nbsp;
== Using Vimpress ==
&nbsp;
Vimpress Commands:
&nbsp;
:BlogList [&lt;count&gt;]
   Lists articles in the blog, defaultly recent 30, use an arg. to specify.
&nbsp;
:BlogNew
   Open for a new post. Contents in current buffer will append in the post edit area.
&nbsp;
:BlogOpen &lt;id&gt;
   Open a post &lt;id&gt; for editing.
&nbsp;
:BlogSave &lt;draft|publish&gt;
   Saves the article, defaultly as draft, an arg. to specify.
&nbsp;
:BlogPreview &lt;draft|publish&gt;
   Same as :BlogSave, will open a browser to preview current post. You have to login
   in your browser to preview the draft post.
&nbsp;
:BlogUpload &lt;file&gt;
   Upload media file to blog. Appends img element after cursor.
&nbsp;
:BlogCode [&lt;code type&gt;]
   Append a section of a &lt;pre&gt; element, which could be hilighted by wp-syntax plugin.
   When give a code type, will fillin the element for syntax.</pre></div></div>
]]></content:encoded>
			<wfw:commentRss>http://apt-blog.net/release-of-vimrepress-1-1-2/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

