チュートリアル:独自のヒストリーバックエンドを書く¶
xonshに関する素晴らしいことの1つは、カスタマイズが簡単であることです。このチュートリアルでは、CouchDBに基づいて独自の履歴バックエンドを作成しましょう。
最小履歴テンプレートで始める¶
ここから始まる最小限の履歴バックエンドがあります:
import collections
from xonsh.history.base import History
class CouchDBHistory(History):
def append(self, cmd):
pass
def items(self):
yield {'inp': 'couchdb in action', 'ts': 1464652800, 'ind': 0}
def all_items(self):
return self.items()
def info(self):
data = collections.OrderedDict()
data['backend'] = 'couchdb'
data['sessionid'] = str(self.sessionid)
return data
先に進んでファイルを作成し、~/.xonsh/history_couchdb.py
上にコンテンツを入れてください。
これでxonshに履歴バックエンドとして使用するよう指示する必要があります。これを行うには、ファイルとこのCouchDBHistory
クラスを見つけるためにxonshが必要です。次のコードを~/.xonshrc
ファイルに入れてこれを実現できます。
import os.path
import sys
xonsh_ext_dir = os.path.expanduser('~/.xonsh')
if os.path.isdir(xonsh_ext_dir):
sys.path.append(xonsh_ext_dir)
from history_couchdb import CouchDBHistory
$XONSH_HISTORY_BACKEND = CouchDBHistory
新しいxonshセッションを開始した後、次のコマンドを試してください:
$ history info
backend: couchdb
sessionid: 4198d678-1f0a-4ce3-aeb3-6d5517d7fc61
$ history -n
0: couchdb in action
ウーホー!私たちは作業履歴のバックエンドを書きました!
CouchDBのセットアップ¶
これを行うには、CouchDBが稼働している必要があります。行く のCouchDBのWebサイトと、それをインストールするには、いくつかの時間を費やします。私たちはあなたを待つでしょう。ゆっくりしてください。
インストール後、正しく設定されていることを確認してくださいcurl
:
$ curl -i 'http://127.0.0.1:5984/'
HTTP/1.1 200 OK
Cache-Control: must-revalidate
Content-Length: 91
Content-Type: application/json
Date: Wed, 01 Feb 2017 13:54:14 GMT
Server: CouchDB/2.0.0 (Erlang OTP/19)
X-Couch-Request-ID: 025a195bcb
X-CouchDB-Body-Time: 0
{
"couchdb": "Welcome",
"version": "2.0.0",
"vendor": {
"name": "The Apache Software Foundation"
}
}
さて、CouchDBは動作しています。ブラウザでhttp://127.0.0.1:5984/_utils/を開いて、新しいデータベースを作成しますxonsh-history
。
履歴バックエンドの初期化¶
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.gc = None
self.sessionid = self._build_session_id()
self.inps = []
self.rtns = []
self.outs = []
self.tss = []
def _build_session_id(self):
ts = int(time.time() * 1000)
return '{}-{}'.format(ts, str(uuid.uuid4())[:18])
この__init__()
メソッドでは、
xonshがさまざまな場所で使ういくつかのパブリック属性を初期化しましょう
。Unixのタイムスタンプといくつかのランダムなcharを使ってself.sessionid
ユニークにし、エントリを時間順に並べておくことに注意してください。次のセクションでもう少し詳しく説明します。
履歴をCouchDBに保存する¶
まず、CouchDBにドキュメントを書くためのヘルパー関数が必要です。
def _save_to_db(self, cmd):
data = cmd.copy()
data['inp'] = cmd['inp'].rstrip()
if 'out' in data:
data.pop('out')
data['_id'] = self._build_doc_id()
try:
self._request_db_data('/xonsh-history', data=data)
except Exception as e:
msg = 'failed to save history: {}: {}'.format(e.__class__.__name__, e)
print(msg, file=sys.stderr)
def _build_doc_id(self):
ts = int(time.time() * 1000)
return '{}-{}-{}'.format(self.sessionid, ts, str(uuid.uuid4())[:18])
def _request_db_data(self, path, data=None):
url = 'http://127.0.0.1:5984' + path
headers = {'Content-Type': 'application/json'}
if data is not None:
resp = requests.post(url, json.dumps(data), headers=headers)
else:
headers = {'Content-Type': 'text/plain'}
resp = requests.get(url, headers=headers)
return resp
_save_to_db()
ユーザが入力したコマンドに関する情報を含む入力をdictとし、CouchDBに保存します。
CouchDBに無作為な文書ID(data['_id']
コード内)を提供させるのではなく、私たち
自身のために構築します。UnixタイムスタンプとUUID文字列をもう一度使用します。これを
self.sessionid
プレフィックスとして、単一のxonshセッション内で順番に履歴エントリを作成します。CouchDBのデザインドキュメントとビューの追加
機能は必要ありません
。裸の_all_docs
API だけで、履歴項目を順番に取得できます。
ヘルパー関数ができappend()
たので、実際の作業を行うメソッドを更新しましょう。履歴をDBに保存します。
def append(self, cmd):
self.inps.append(cmd['inp'])
self.rtns.append(cmd['rtn'])
self.outs.append(None)
self.tss.append(cmd.get('ts', (None, None)))
self._save_to_db(cmd)
このメソッドは、ユーザーから新しいコマンドを実行するたびにxonshによって呼び出されます。
履歴項目を取得する¶
def items(self):
yield from self._get_db_items(self.sessionid)
def all_items(self):
yield from self._get_db_items()
これらの2つのメソッドは、現在のxonshセッションとすべての履歴セッションの履歴項目を取得します。
そして、DBからドキュメントを入手するヘルパーメソッドは次のとおりです:
def _get_db_items(self, sessionid=None):
path = '/xonsh-history/_all_docs?include_docs=true'
if sessionid is not None:
path += '&start_key="{0}"&end_key="{0}-z"'.format(sessionid)
try:
r = self._request_db_data(path)
except Exception as e:
msg = 'error when query db: {}: {}'.format(e.__class__.__name__, e)
print(msg, file=sys.stderr)
return
data = json.loads(r.text)
for item in data['rows']:
cmd = item['doc'].copy()
cmd['ts'] = cmd['ts'][0]
yield cmd
try-除く悪い何かが起こるときのCouchDB等、適切に実行されていないように私たちは、安全だようにここにあります
新しい履歴のバックエンドを試してみましょう¶
それでおしまい。私たちは新しい歴史バックエンドを終えました。import
一部はスキップされますが、私はあなたががそれを把握することができると思います。私たちのコードでは、余分なPythonライブラリが使われています:requests
。あなたはpip
他のライブラリマネージャーと簡単にインストールできます。フルコードは
https://gist.github.com/mitnk/2d08dc60aab33d8b8b758c544b37d570でご覧いただけます。
新しいxonshセッションを開始しましょう:
$ history info
backend: couchdb
sessionid: 1486035364166-3bb78606-dd59-4679
$ ls
Applications Desktop Documents Downloads
$ echo hi
hi
2番目のxonshセッションを開始する:
$ history info
backend: couchdb
sessionid: 1486035430658-6f81cd5d-b6d4-4f6a
$ echo new
new
$ history show all -nt
0:(2017-02-02 19:36) history info
1:(2017-02-02 19:36) ls
2:(2017-02-02 19:37) echo hi
3:(2017-02-02 19:37) history info
4:(2017-02-02 19:37) echo new
$ history -nt
0:(2017-02-02 19:37) history info
1:(2017-02-02 19:37) echo new
2:(2017-02-02 19:37) history show all -nt
私たちは歴史を欠いているわけではないので、私たちは行くのがいいようです!
履歴ガベージコレクション¶
以下のための歴史のバックエンドを内蔵json
し、sqlite
xonshが開始されたとき、またはユーザーが実行したときに、ガベージコレクションがトリガされます。$ XONSH_HISTORY_SIZEで定義された範囲外の履歴項目
は削除されます。history gc
class History:
def run_gc(self, size=None, blocking=True):
"""Run the garbage collector.
Parameters
----------
size: None or tuple of a int and a string
Determines the size and units of what would be allowed to remain.
blocking: bool
If set blocking, then wait until gc action finished.
"""
pass
履歴公開メソッドrun_gc()
は、この目的のためです。私たち
CouchDBHistory
はこのメソッドを定義していないので、親から継承しますHistory
が、何もしません。私たちはGC実装を練習問題として残します。
その他の履歴オプション¶
履歴バックエンドの動作を変更する環境変数がいくつかあります。$ HISTCONTROL、 $ XONSH_HISTORY_SIZE、 $ XONSH_STORE_STDOUTなどのようなものです。
これらのENVをCouchDBバックエンドに実装する必要があります。幸いにも、それは難しいことではありません。これらの機能の実装はあなたに任せておきますが、sqliteバックエンドの処理方法を確認できます 。
ラップアップ¶
これはベアボーンの実装ですが、あなたのニーズに合わせてxonshの履歴バックエンドをカスタマイズする方法を理解できますように!