Personal tools
You are here: Home KtJ's Blog Categories Bottle
Document Actions

Bottle

Up one level

Document Actions

サイトをFreeBSD10+python3.3+Bottleで作り直すメモ(1)-qjailとpkgng

by ktj posted at 2014-05-08 07:58 last modified 2015-07-11 11:06

サーバ入れ替えに伴ってブログエンジンを変えなきゃいけない、だったら自分で作ってみよう、という無謀な試み。コンテンツは今までどおりreStructuredTextで書きたいのでpythonベースの奴がいいよね&自分専用だからできるだけシンプルな方がいいよね&もうこれからはpython3だよね、ということでフレームワークはBottleを選択してみた。

OSはいつも通りFreeBSD。で、どのリリースを選択すべきか。ちょうど今はFreeBSDのライフサイクル的にちと微妙な時期で、

  • 8.4は来年7月までサポートだけどさすがに今8系を選択するのはない
  • 9.1は今年の年末まで、9.2は今年の9月まで
  • 10.0は来年の1月まで

ということで、とりあえず10.0を入れておいて、来年頭くらいに長期(2年)サポートの10.1が出たらそっちにアップグレード、というのがいいかなと。

てことで、まず10.0をインストール。デフォルトのパーティション/ブート管理がGPTという奴に変わって、スライスごとのパーティションの数の制限が緩和されたっぽい。サーバの仕様上パーティションをたくさん切る必要があるのでこれはありがたい。

さて、FreeBSD10系からjailの仕様が若干変わってezjailがうまく動かないらしいので、qjailを入れてみる。手順は以下の通り。まず、pkgngでqjailをインストール。

# pkg search qjail
(qjailの2系と3系(ここでは3.2)のパッケージが表示される)
# pkg install qjail-3.2

次に、/etc/rc.confにネットワークアダプタのエイリアスとqjail_enableを追加。こんな感じで。

ifconfig_ネットワークIF_alias0="inet IPアドレス netmask 255.255.255.255"
qjail_enable="YES"

で、ネットワークを再起動して、それからjailのベースを作成

# /etc/rc.d/netif restart
# source ~/.cshrc
# qjail install

さらに、qjailを作って実行

# qjail create -n ネットワークIF -4 IPアドレス jail名
# qjail start

qjailが起動したのちは、 qjail console jail名 (jexec jailID csh でもいいけど)でjailに入る。この時点でrootのパスワードがない状態なのでpasswdコマンドで作っておくのがいいと思う。ユーザランドのアップデートはホスト側から qjail update -b だけでOK。また、portsツリーのアップデートもホスト側から qjail update -p でOK。これはらくちんでよい。

ここで問題発生。jail側でpkgngが機能しない。「PACKAGESITEを設定ファイルから削除しろ」とか「pkgのデータ参照元のftpなんちゃらが見つからないよ」とかいうメッセージが出てpkgを構築できない。なんでかな?と思ったが、pkgngのデータ参照元のサイトがどっかのタイミングで変わったけど、qjailのデフォルトのユーザランドイメージ(qjail install でダウンロードされるイメージ)は古い参照元のURLを見に行っているようだ。もっと具体的に書くと、qjailのイメージだと環境変数PACKAGESITEが設定されていて、これをpkgngが参照している、ということ。で、PACKAGESITEをどこで設定しているか確認したところ、/root/.cshrcで設定されていた。なんで、.cshrcを編集して該当個所をコメントアウトしたところこの問題は解消された。

サイトをFreeBSD10+python3.3+Bottleで作り直すメモ(2)-python3とBottleのインストール

by ktj posted at 2014-05-10 10:02 last modified 2015-04-04 16:10

まずpython3.3のインストール。現状ではFreeBSD-10.0 Releaseの標準のpythonのバージョンは2.7なので、 /etc/make.confPYTHON_VERSION=3.3 を追加する。で、あとはportsを使ってlang/pythonをインストール。で、Bottleとかをインストールするわけだが、portsで入れてもよさそうだがあえてpip(pythonのパッケージ管理システム)で入れてみる。こっちの方がパッケージが新しいし。

まず、portsからdevel/pipをインストール。docutilsとかpytzとかも一緒に入ってくる。で、 pip install パッケージ名 でインストール。データベースとしてMariaDB(MySQLの元々の開発者によるMySQL5.5からのフォーク)を使うつもりなので、Bottleと、PyMySQLをpipでインストールした。

The URL to Trackback this entry is:
http://www.ktjdragon.com/ktj/ktjs-blog/30b530a430c83092freebsd10-python3-3-bottle4f5c308a76f4305930e130e2-2-python33068bottle306e30a430f330b930c830fc30eb/tbping

サイトをFreeBSD10+python3.3+Bottleで作り直すメモ(3)-とりあえず何か書いてみる

by ktj posted at 2014-05-10 13:56 last modified 2015-04-04 16:11

とりあえずお約束のHello, Worldでも。

hello.py:

# -*- coding: utf-8 -*-

from bottle import route, run, SimpleTemplate
from docutils.core import publish_parts

@route('/hello')
def hello():
  rest = '''heading
========'''
  settings = {
    'initial_header_level': 2,
    'doctitle_xform': 0,
  }
      
  DIR = '/some/where/'
  fp = open(DIR + 'header.tpl')
  header_tpl = SimpleTemplate(fp.read())
  fp.close()
  fp = open(DIR + 'footer.tpl')
  footer_tpl = SimpleTemplate(fp.read())
  fp.close

  tp = ("Hello," , "World!!!")
      
  return header_tpl.render(title='Hello World') + \
    publish_parts(rest, writer_name='html4css1', \
    settings_overrides=settings)['html_body'] + \
    footer_tpl.render(data=tp)

run(host='localhost', port=8080, debug=True, reloader=True)

/some/where/header.tpl:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
  "http://www.w3.org/TR/html4/strict.dtd">
<html lang="ja">
<head>
<title>{{title}}</title>
</head>
<body>
<h1>{{title}}</h1>
<hr>

/some/where/footer.tpl:

<p>
% for msg in data:
{{msg}}
% end
</p>

</body>
</html>

こういうソースを書いて、hello.pyを実行すると、localhost(うちはjail環境だから本当はlocalhost使えないけど)の8080番ポートで開発用のwebサーバが起動して、http://localhost:8080/helloにアクセスすると、下記のソースでページが表示される。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
  "http://www.w3.org/TR/html4/strict.dtd">
<html lang="ja">
<head>
<title>Hello World</title>
</head>
<body>
<h1>Hello World</h1>
<hr>
<div class="document">
<div class="section" id="id1">
<h2>heading</h2>
</div>
</div>
<p>
Hello,
World!!!
</p>

</body>
</html>

hello.pyの構成はこんな感じ。

  1. Bottleのroute, run ,SimpleTemplateをインポートする
  2. docutilsのpublish_partsもインポートする
  3. @route('/hello')デコレータに続いて関数hello()を記述。関数hello()内では、
    1. 変数restにreST文書を代入
    2. 変数settingsにreST→HTML変換用のパラメータをセットする。
    3. header.tplとfooter.tplからSimpleTemplateクラスのインスタンスheader_tplとfooter_tplを生成。
    4. 関数hello()の返り値として、「header.tpl内の{{title}}を引数title='Hello Worldで置き換えたHTMLコード」「変数restをHTML文書の断片に変換したコード」「『for文で引数tpのタプルの内容を出力する』という構成のfotter.tplから得られるHTMLコード」を結合したテキストを定義。
  4. bottleのrun関数でHTTPサーバを起動。引数debugはテンプレートなりhello.pyのコードにバグがあったときにブラウザにエラー内容を表示する為のフラグで、引数reloaderはhello.pyが書き換わった時に自動的にhello.pyをリスタートさせる為のフラグである。
The URL to Trackback this entry is:
http://www.ktjdragon.com/ktj/ktjs-blog/30b530a430c83092freebsd10-python3-3-bottle4f5c308a76f4305930e130e2-3-3068308a304230484f55304b66f830443066307f308b/tbping

サイトをFreeBSD10+python3.3+Bottleで作り直すメモ(4)-pymysql

by ktj posted at 2014-06-29 10:42 last modified 2015-08-02 07:01

手順としてはこんな感じ

準備

con = pymysql.connect(
        host='接続先のIPアドレス',
        port=ポート番号,
        db='データベース名',
        user='ユーザ名',
        passwd='パスワード',
        charset='utf8mb4')
cur = con.cursor()

connectメソッドとcursorメソッドを順番に実行。connectメソッドのcharsetはMySQL(or MariaDB)の設定に応じて適宜変更する。

SELECT

if cur.execute('SELECT ~')!=0:
  result = cur.fetchall()

まずexecuteしてfetchall。executeメソッドの返り値はヒットしたレコード数。resultは通常二次元のタプル。ヒットしたレコードが0の時は(一次元の)空タプル。一つのレコードだけ取り出したいときは下記のようにfetchoneメソッドを使う。

cur.execute('SELECT ~')
count = 1
result = []
while True:
  row = cur.fetchone()
  if row is None:
    break
  elif count % 2 == 1:
    result.append(row)
  count += 1

上記のソースの場合、奇数番目のレコードのみをresultリストに追加している。fetchmany(size=1)という複数(引数sizeで行数を指定)のレコードを取り出すメソッドもある。

INSERT、UPDATE、DELETE

cur.execute('INSERT INTO~')
con.commit()

executeし、次いでコネクションのcommitメソッドを使ってDBに反映させる。

DB切断

cur.close()
con.close()

(2015/3/22追記)

SQLインジェクションを避けるためプレースホルダを使う方がよさそう。つまり、

cur.execute('SELECT * FROM table1 WHERE col1 = %s AND col2 = %s',
  (var1, var2))

みたいに、executeメソッドに二つパラメータを指定する。一番目のパラメータにはSQL文を書く。この時フォームから入力されうるものは%sとしておき、二番目のパラメータで%sに置き換える文字列群をタプルとして記述する。%sはシングルクウォートでくくる必要は無い。で、変数var1やvar2にシングルクウォートが含まれていても自動的にエスケープしてMariaDBに渡してくれるようだ。

(2015/8/2追記)

%sに与える値(上記例でのvar1やvar2)は文字列である必要はなく、intやfloat、あるいはdateやdatetime型でも大丈夫。いちいち型変換しなくて済むので便利。

The URL to Trackback this entry is:
http://www.ktjdragon.com/ktj/ktjs-blog/30b530a430c83092freebsd10-python3-3-bottle4f5c308a76f4305930e130e2-4-pymysql/tbping

サイトをFreeBSD10+python3.3+Bottleで作り直すメモ(5)-フォームの扱い

by ktj posted at 2014-06-29 11:26 last modified 2015-07-11 10:58

GETメソッドの場合

getメソッドの場合はrequest.queryを使う。

from bottle import route, run, request

@route('/')
def root():
  id = request.query.id

これでGETメソッドでの引数idを取得できる。 request.query['id'] としてもよい。

POSTメソッドの場合

POSTメソッドの場合はデコレータにpostを使用する。取り出しはrequest.forms.get。チェックボックスみたいに複数の値が指定されうる場合はrequest.forms.getallを使う。ただし、bottleの場合、フォームの文字エンコードはISO-8859-1であるため、日本語などを取り出す場合はgetunicodeを代わりに使う。(getallで日本語を取り出したいときはどうすればいいのだろう)

from bottle import route, run, post, request

@post('/do_post')
def do_post():
  username = request.forms.getunicode('username')
  checks = request.forms.getall('checks')

サイトをFreeBSD10+python3.3+Bottleで作り直すメモ(6)-py-mecab

by ktj posted at 2014-07-09 23:12 last modified 2015-04-04 16:16

サイトを作るに当たって「やっぱ全文検索機能は欲しいよね」ということで調べてみた。どうやらMySQLのFULLTEXTインデックスを使うといいらしい。で、日本語でFULLTEXTインデックスを機能させるには、分かち書きのデータを用意しておく必要があるとのこと。コンテンツを作成、更新する際に分かち書きのデータを作成して保存するようにすればいいのかな。

pythonで分かち書きをする方法としてはMeCabのpythonモジュールを使うと良さそう、ということでインストールしてみたがちとハマったのでメモ。http://kaworu.jpn.org/kaworu/2010-10-17-1.phphttp://anond.hatelabo.jp/20121113070853http://tatsuyaoiw.hatenablog.com/entry/20120414/1334405065を参考にさせていただいた。

  1. まずjapanese/mecabとjapanese/mecab-ipadicを「WITH_CHARSET=utf-8」を指定してportsからインストール。

  2. py-mecabもportsに入っているが、portsからはインストールできなかった。なので、http://mecab.googlecode.com/files/mecab-python-0.996.tar.gzを取ってくる。

  3. 取ってきたtar.gzを展開してsetup.pyから下記の箇所を探す。

    def cmd2(str):
      return string.split (cmd1(str))
    
  4. 上記箇所を下記のように置き換える。

    def cmd2(str):
      return cmd1(str).split()
    
  5. python setup.py build python setup.py install を順次実行してビルド&インストール

分かち書きの処理は下記のとおり。

import MeCab

m = MeCab.Tagger("-Owakati")
sourcetxt = "今日はいい天気ですね"
processedtxt = m.parse(sourcetxt)
The URL to Trackback this entry is:
http://www.ktjdragon.com/ktj/ktjs-blog/30b530a430c83092freebsd10-python3-3-bottle4f5c308a76f4305930e130e2-6-py-mecab/tbping

サイトをFreeBSD10+python3.3+Bottleで作り直すメモ(7)-MariaDB:FULLTEXTインデックス

by ktj posted at 2014-07-14 06:54 last modified 2015-04-04 16:16

たたみラボさんのところの記事を元にMariaDBにFULLTEXTインデックスを設定してみた。

1. テーブルのデータベースエンジンをMyISAMにする

FULLTEXTインデックスはMyISAMデータベースエンジン特有の機能なので、テーブルがInnoDB等MyISAM以外のエンジンを使っている場合は変換する必要があります。クラッシュ耐性やトランザクションの実装等、InnoDBの方が推奨される場合が多いのですが。

2. my.cnfを設定

デフォルトでは4字未満の単語はヒットしない設定となっているようですが、それだと日本語環境で不便なので、 [mysqld] セクションに ft_min_word_len=1 を追加して1文字から検索できるようにします。設定したらmysqlを再起動します。

3. テーブルにインデックスをつける

CREATE FULLTEXT INDEX インデックス名 ON テーブル名(列名)

でインデックスを作成します。

The URL to Trackback this entry is:
http://www.ktjdragon.com/ktj/ktjs-blog/30b530a430c83092freebsd10-python3-3-bottle4f5c308a76f4305930e130e2-7-mariadb-fulltext30a430f330c330af30b9/tbping

サイトをFreeBSD10+python3.3+Bottleで作り直すメモ(8)-javascriptで夏時間

by ktj posted at 2014-07-27 12:54 last modified 2015-04-04 16:17

ブログ何かの作成/更新日時は日本時間で管理するつもりだが、「現地時間も併記できるとカッコいいよね」と思った次第。ささやかながら英語のコンテンツもあるしね。

表示できるようにしたいのは以下の3点。

  • 作成/更新日時の現地時間
  • 作成/更新日時の時差
  • その日時が夏時間中であるかどうか

あまりクライアントサイドのプログラミングって詳しく無いけど(むろんサーバサイドもせいぜいカジュアルレベルだけどそれ未満)、javascriptにうってつけの機能があるみたい。

DateオブジェクトのgetTimezoneOffset()メソッドがそれで、Dateオブジェクトが指す日時に対する標準時への時間差を分単位で示してくれるようだ(+09:00の日本時間なら-540)。夏時間かどうかを判別するルーチンがabout.comにあったのでそれをそのままありがたく使わせてもらおう

Date.prototype.stdTimezoneOffset = function() {
  var jan = new Date(this.getFullYear(), 0, 1);
  var jul = new Date(this.getFullYear(), 6, 1);
  return Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset());
}

Date.prototype.dst = function() {
  return this.getTimezoneOffset() < this.stdTimezoneOffset();
}

srdTimezoneOffsetメソッドは、Dateオブジェクトが示す日時と同年における「夏時間でないときの」時差を検出するルーチン。今のところ、夏時間のある所では一月一日と七月一日の時差が必ず異なるようになっているとのことなので、これの大きい方を取り出すことで夏時間でないときの時差を取り出せる。dstメソッドは、Dateオブジェクトが示す時差とその年における「夏時間で無いときの」時差を比較して、後者が大きいとき(Dateが示す日時が夏時間中であるとき)にTrueを返すルーチン。

Date.prototype.outputFmtdDate = function() {
  var weekdays = ['日', '月', '火', '水', '木', '金', '土'];
  var monthdata = ['1', '2', '3', '4', '5', '6',
                   '7', '8', '9', '10', '11', '12'];
  var year = String(this.getFullYear());
  var month = monthdata[this.getMonth()];
  var day = weekdays[this.getDay()];
  var date = String(this.getDate());
  var hour = String(this.getHours());
  var minute = String(this.getMinutes());
  var second = String(this.getSeconds());
  var offsetText, outputText;

  var offsetMinute = this.getTimezoneOffset();
  if (offsetMinute <= -600){
    offsetText = "+" + String(offsetMinute * (-1) / 60) + ":" +
                String(offSetMinute * (-1) % 60);
  }else if (offsetMinute < 0){
    offsetText = "+0" + String(offsetMinute * (-1) / 60) + ":" +
                String(offSetMinute * (-1) % 60);
  }else if (offsetMinute >= 600){
    offsetText = "-" + String(offsetMinute / 60) + ":" +
                String(offSetMinute % 60);
  }else{
    offsetText = "-0" + String(offsetMinute / 60) + ":" +
                String(offSetMinute % 60);
  }

  outputText = year + "/" + month + "/" + date +
               "(" + day + ") " + hour + ":" + minute +
               ":" + second + "[" + offsetText + "]";

  if (this.dst()){
    outputText += "(DST)";
  }

  return outputText;

}

で、このようなメソッドを作ってみた。このメソッドの返り値は"yyyy/m/d H:M:S[時差]"形式の日時テキストで夏時間の時は末尾に"(DST)"が入るという寸法。まだテストしてないけど。

The URL to Trackback this entry is:
http://www.ktjdragon.com/ktj/ktjs-blog/30b530a430c83092freebsd10-python3-3-bottle4f5c308a76f4305930e130e2-8-300cx5e74y6708z756a3081306ew66dc65e5300d306e65e54ed8309253d65f973059308b/tbping

Powered by Plone CMS, the Open Source Content Management System

This site conforms to the following standards: