bindings: add bindings for metalog

Summary: Expose metalog APIs to Python.

Reviewed By: kulshrax

Differential Revision: D18273001

fbshipit-source-id: b8fbf0c22a925e3e5373498076a3392901c00aed
This commit is contained in:
Jun Wu 2019-11-13 14:41:18 -08:00 committed by Facebook Github Bot
parent ec7157bef5
commit f0e8dee2ac
4 changed files with 117 additions and 0 deletions

View File

@ -22,6 +22,7 @@ pydag = { path = "modules/pydag" }
pyedenapi = { path = "modules/pyedenapi" }
pylz4 = { path = "modules/pylz4" }
pymanifest = { path = "modules/pymanifest" }
pymetalog = { path = "modules/pymetalog" }
pymutationstore = { path = "modules/pymutationstore" }
pynodemap = { path = "modules/pynodemap" }
pypathmatcher = { path = "modules/pypathmatcher" }

View File

@ -0,0 +1,11 @@
[package]
name = "pymetalog"
version = "0.1.0"
edition = "2018"
[dependencies]
cpython = { version = "0.3", features = ["python27-sys"], default-features = false }
cpython-ext = { path = "../../../../lib/cpython-ext" }
cpython-failure = { path = "../../../../lib/cpython-failure" }
encoding = { path = "../../../../lib/encoding" }
metalog = { path = "../../../../lib/metalog" }

View File

@ -0,0 +1,104 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This software may be used and distributed according to the terms of the
* GNU General Public License version 2.
*/
#![allow(non_camel_case_types)]
use ::metalog::{CommitOptions, Id20, MetaLog};
use cpython::*;
use cpython_ext::Bytes;
use cpython_failure::ResultPyErrExt;
use std::cell::RefCell;
use std::time::SystemTime;
pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
let name = [package, "metalog"].join(".");
let m = PyModule::new(py, &name)?;
m.add_class::<metalog>(py)?;
Ok(m)
}
py_class!(class metalog |py| {
data log: RefCell<MetaLog>;
def __new__(_cls, path: String, root: Option<Bytes> = None) -> PyResult<Self> {
let root = root.and_then(|s| Id20::from_slice(s.as_ref()).ok());
let log = MetaLog::open(&path, root).map_pyerr::<exc::IOError>(py)?;
Self::create_instance(py, RefCell::new(log))
}
@staticmethod
def listroots(path: String) -> PyResult<Vec<Bytes>> {
let root_ids = MetaLog::list_roots(&path).map_pyerr::<exc::IOError>(py)?;
Ok(root_ids.into_iter().map(|id| Bytes::from(id.as_ref().to_vec())).collect())
}
/// Lookup an item by key. Return None if the key does not exist.
def get(&self, key: &str) -> PyResult<Option<Bytes>> {
let log = self.log(py).borrow();
let data = log.get(key).map_pyerr::<exc::IOError>(py)?;
Ok(data.map(Bytes::from))
}
/// Set an item. Return the Id of value.
def set(&self, key: &str, value: Bytes) -> PyResult<Bytes> {
let mut log = self.log(py).borrow_mut();
let id = log.set(key, value.as_ref()).map_pyerr::<exc::IOError>(py)?;
Ok(Bytes::from(id.as_ref().to_vec()))
}
/// Remove an item. Does not raise if the key does not exist.
def remove(&self, key: &str) -> PyResult<PyObject> {
let mut log = self.log(py).borrow_mut();
log.remove(key).map_pyerr::<exc::IOError>(py)?;
Ok(py.None())
}
/// Get all keys.
def keys(&self) -> PyResult<Vec<Bytes>> {
let keys = self.log(py).borrow()
.keys().iter().map(|s| Bytes::from(s.as_bytes().to_vec())).collect();
Ok(keys)
}
/// Write pending data to disk. Raise if race condition is detected.
def commit(&self, message: &str, time: Option<u64> = None, pending: bool = false) -> PyResult<Bytes> {
let mut opts = CommitOptions::default();
opts.detached = pending;
opts.timestamp = time.unwrap_or_else(|| {
SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.map(|d| d.as_secs()).unwrap_or(0)
});
opts.message = message;
let id = self.log(py).borrow_mut().commit(opts).map_pyerr::<exc::IOError>(py)?;
Ok(Bytes::from(id.as_ref().to_vec()))
}
/// Why the change was made.
def message(&self) -> PyResult<Bytes> {
Ok(Bytes::from(self.log(py).borrow().message().to_string()))
}
/// When the change was made.
def timestamp(&self) -> PyResult<u64> {
Ok(self.log(py).borrow().timestamp())
}
def __getitem__(&self, key: String) -> PyResult<Option<Bytes>> {
self.get(py, &key)
}
def __setitem__(&self, key: String, value: Bytes) -> PyResult<()> {
self.set(py, &key, value)?;
Ok(())
}
def __delitem__(&self, key: String) -> PyResult<()> {
self.remove(py, &key)?;
Ok(())
}
});

View File

@ -28,6 +28,7 @@ pub fn populate_module(py: Python<'_>, module: &PyModule) -> PyResult<PyObject>
m.add(py, "edenapi", pyedenapi::init_module(py, &name)?)?;
m.add(py, "lz4", pylz4::init_module(py, &name)?)?;
m.add(py, "manifest", pymanifest::init_module(py, &name)?)?;
m.add(py, "metalog", pymetalog::init_module(py, &name)?)?;
m.add(
py,
"mutationstore",