Fix bindings and add CI tests (#247)

Also update version numbers of binding Rust projects to match Regorus

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
This commit is contained in:
Anand Krishnamoorthi
2024-05-22 11:15:42 -04:00
committed by GitHub
parent 495e91c75a
commit 9894f00829
35 changed files with 448 additions and 107 deletions

View File

@@ -1,6 +1,6 @@
[package]
name = "regoruspy"
version = "0.1.0"
version = "0.1.5"
edition = "2021"
repository = "https://github.com/microsoft/regorus/bindings/python"
description = "Python bindings for Regorus - a fast, lightweight Rego interpreter written in Rust"

View File

@@ -33,12 +33,12 @@ impl Clone for Engine {
}
}
fn from<'source>(ob: &'source PyAny) -> Result<Value, PyErr> {
fn from<'source>(ob: &Bound<'_, PyAny>) -> Result<Value, PyErr> {
// dicts
Ok(if let Ok(dict) = ob.downcast::<PyDict>() {
let mut map = BTreeMap::new();
for (k, v) in dict {
map.insert(from(k)?, from(v)?);
map.insert(from(&k)?, from(&v)?);
}
map.into()
}
@@ -46,7 +46,7 @@ fn from<'source>(ob: &'source PyAny) -> Result<Value, PyErr> {
else if let Ok(pset) = ob.downcast::<PySet>() {
let mut set = BTreeSet::new();
for v in pset {
set.insert(from(v)?);
set.insert(from(&v)?);
}
set.into()
}
@@ -55,7 +55,7 @@ fn from<'source>(ob: &'source PyAny) -> Result<Value, PyErr> {
//
let mut set = BTreeSet::new();
for v in pfset {
set.insert(from(v)?);
set.insert(from(&v)?);
}
set.into()
}
@@ -63,30 +63,30 @@ fn from<'source>(ob: &'source PyAny) -> Result<Value, PyErr> {
else if let Ok(plist) = ob.downcast::<PyList>() {
let mut array = Vec::new();
for v in plist {
array.push(from(v)?);
array.push(from(&v)?);
}
array.into()
} else if let Ok(ptuple) = ob.downcast::<PyTuple>() {
let mut array = Vec::new();
for v in ptuple {
array.push(from(v)?);
array.push(from(&v)?);
}
array.into()
}
// String
else if let Ok(s) = String::extract(ob) {
else if let Ok(s) = ob.extract::<String>() {
s.into()
}
// Numeric
else if let Ok(v) = i64::extract(ob) {
else if let Ok(v) = ob.extract::<i64>() {
v.into()
} else if let Ok(v) = u64::extract(ob) {
} else if let Ok(v) = ob.extract::<u64>() {
v.into()
} else if let Ok(v) = f64::extract(ob) {
} else if let Ok(v) = ob.extract::<f64>() {
v.into()
}
// Boolean
else if let Ok(b) = bool::extract(ob) {
else if let Ok(b) = ob.extract::<bool>() {
b.into()
}
// None
@@ -97,7 +97,7 @@ fn from<'source>(ob: &'source PyAny) -> Result<Value, PyErr> {
else if let Ok(pseq) = ob.downcast::<PySequence>() {
let mut array = Vec::new();
for i in 0..pseq.len()? {
array.push(from(pseq.get_item(i)?)?);
array.push(from(&pseq.get_item(i)?)?);
}
array.into()
}
@@ -109,7 +109,7 @@ fn from<'source>(ob: &'source PyAny) -> Result<Value, PyErr> {
for i in 0..keys.len()? {
let key = keys.get_item(i)?;
let value = values.get_item(i)?;
map.insert(from(key)?, from(value)?);
map.insert(from(&key)?, from(&value)?);
}
map.into()
} else {
@@ -140,7 +140,7 @@ fn to(mut v: Value, py: Python<'_>) -> Result<PyObject> {
}
Value::Array(_) => {
let list = PyList::empty(py);
let list = PyList::empty_bound(py);
for v in std::mem::replace(v.as_array_mut()?, Vec::new()) {
list.append(to(v, py)?)?;
}
@@ -148,7 +148,7 @@ fn to(mut v: Value, py: Python<'_>) -> Result<PyObject> {
}
Value::Set(_) => {
let set = PySet::empty(py)?;
let set = PySet::empty_bound(py)?;
for v in std::mem::replace(v.as_set_mut()?, BTreeSet::new()) {
set.add(to(v, py)?)?;
}
@@ -156,7 +156,7 @@ fn to(mut v: Value, py: Python<'_>) -> Result<PyObject> {
}
Value::Object(_) => {
let dict = PyDict::new(py);
let dict = PyDict::new_bound(py);
for (k, v) in std::mem::replace(v.as_object_mut()?, BTreeMap::new()) {
dict.set_item(to(k, py)?, to(v, py)?)?;
}
@@ -181,7 +181,7 @@ impl Engine {
///
/// * `path`: A filename to be associated with the policy.
/// * `rego`: Rego policy.
pub fn add_policy(&mut self, path: String, rego: String) -> Result<()> {
pub fn add_policy(&mut self, path: String, rego: String) -> Result<String> {
self.engine.add_policy(path, rego)
}
@@ -190,15 +190,21 @@ impl Engine {
/// The policy is parsed into AST.
///
/// * `path`: Path to the policy file.
pub fn add_policy_from_file(&mut self, path: String) -> Result<()> {
pub fn add_policy_from_file(&mut self, path: String) -> Result<String> {
self.engine.add_policy_from_file(path)
}
/// Get the list of packages defined by loaded policies.
///
pub fn get_packages(&mut self) -> Result<Vec<String>> {
self.engine.get_packages()
}
/// Add policy data.
///
/// * `data`: Rego value. A Rego value is a number, bool, string, None
/// or a list/set/map whose items themselves are Rego values.
pub fn add_data(&mut self, data: &PyAny) -> Result<()> {
pub fn add_data(&mut self, data: &Bound<'_, PyAny>) -> Result<()> {
let data = from(data)?;
self.engine.add_data(data)
}
@@ -229,7 +235,7 @@ impl Engine {
///
/// * `input`: Rego value. A Rego value is a number, bool, string, None
/// or a list/set/map whose items themselves are Rego values.
pub fn set_input(&mut self, input: &PyAny) -> Result<()> {
pub fn set_input(&mut self, input: &Bound<'_, PyAny>) -> Result<()> {
let input = from(input)?;
self.engine.set_input(input);
Ok(())
@@ -259,17 +265,17 @@ impl Engine {
pub fn eval_query(&mut self, query: String, py: Python<'_>) -> Result<PyObject> {
let results = self.engine.eval_query(query, false)?;
let rlist = PyList::empty(py);
let rlist = PyList::empty_bound(py);
for result in results.result.into_iter() {
let rdict = PyDict::new(py);
let rdict = PyDict::new_bound(py);
let elist = PyList::empty(py);
let elist = PyList::empty_bound(py);
for expr in result.expressions.into_iter() {
let edict = PyDict::new(py);
let edict = PyDict::new_bound(py);
edict.set_item("value".to_object(py), to(expr.value, py)?)?;
edict.set_item("text".to_object(py), expr.text.as_ref().to_object(py))?;
let ldict = PyDict::new(py);
let ldict = PyDict::new_bound(py);
ldict.set_item("row".to_object(py), expr.location.row.to_object(py))?;
ldict.set_item("col".to_object(py), expr.location.col.to_object(py))?;
@@ -281,7 +287,7 @@ impl Engine {
rdict.set_item("bindings".to_object(py), to(result.bindings, py)?)?;
rlist.append(rdict)?;
}
let dict = PyDict::new(py);
let dict = PyDict::new_bound(py);
dict.set_item("result".to_object(py), rlist)?;
Ok(dict.into())
}
@@ -296,6 +302,6 @@ impl Engine {
}
#[pymodule]
pub fn regorus(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
pub fn regorus(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<crate::Engine>()
}

View File

@@ -7,9 +7,14 @@ import regorus
engine = regorus.Engine()
# Load policies
engine.add_policy_from_file('../../tests/aci/framework.rego')
engine.add_policy_from_file('../../tests/aci/api.rego')
engine.add_policy_from_file('../../tests/aci/policy.rego')
pkg = engine.add_policy_from_file('../../tests/aci/framework.rego')
print(' Loaded Policy %s' % pkg)
pkg = engine.add_policy_from_file('../../tests/aci/api.rego')
print(' Loaded Policy %s' % pkg)
pkg = engine.add_policy_from_file('../../tests/aci/policy.rego')
print(' Loaded Policy %s' % pkg)
# Add policy data
data = {