チュートリアル:独自のヒストリーバックエンドを書く

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_docsAPI だけで、履歴項目を順番に取得できます。

ヘルパー関数ができ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し、sqlitexonshが開始されたとき、またはユーザーが実行したときに、ガベージコレクションがトリガされます$ 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の履歴バックエンドをカスタマイズする方法を理解できますように!