Repository: microsoft/regorus (language: python)

Issue:
Python Bindings



Discussion / hints:
CC: @unexge I'm taking an initial stab at this. I've only recently learnt py03 and maturin; there might likely be better ways to do this. Feel free to improve this initial version.

Fix patch:
diff --git a/bindings/python/README.md b/bindings/python/README.md
new file mode 100644
index 00000000..2e91aa25
--- /dev/null
+++ b/bindings/python/README.md
@@ -0,0 +1,67 @@
+# regorus
+
+**Regorus** is
+
+  - *Rego*-*Rus(t)*  - A fast, light-weight [Rego](https://www.openpolicyagent.org/docs/latest/policy-language/)
+   interpreter written in Rust.
+  - *Rigorous* - A rigorous enforcer of well-defined Rego semantics.
+
+Regorus can be used in Python via `regorus` package. (It is not yet available in PyPI, but can be manually built.)
+
+See [Repository](https://github.com/microsoft/regorus).
+
+To build this binding, see [building](https://github.com/microsoft/regorus/bindings/python/building.md)
+
+## Usage
+```Python
+import regorus
+
+# Create engine
+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')
+
+# Add policy data
+data = {
+  "metadata": {
+    "devices": {
+      "/run/layers/p0-layer0": "1b80f120dbd88e4355d6241b519c3e25290215c469516b49dece9cf07175a766",
+      "/run/layers/p0-layer1": "e769d7487cc314d3ee748a4440805317c19262c7acd2fdbdb0d47d2e4613a15c",
+      "/run/layers/p0-layer2": "eb36921e1f82af46dfe248ef8f1b3afb6a5230a64181d960d10237a08cd73c79",
+      "/run/layers/p0-layer3": "41d64cdeb347bf236b4c13b7403b633ff11f1cf94dbc7cf881a44d6da88c5156",
+      "/run/layers/p0-layer4": "4dedae42847c704da891a28c25d32201a1ae440bce2aecccfa8e6f03b97a6a6c",
+      "/run/layers/p0-layer5": "fe84c9d5bfddd07a2624d00333cf13c1a9c941f3a261f13ead44fc6a93bc0e7a"
+    }
+  }
+}
+engine.add_data(data)
+
+# Set input
+input = {
+  "containerID": "container0",
+  "layerPaths": [
+    "/run/layers/p0-layer0",
+    "/run/layers/p0-layer1",
+    "/run/layers/p0-layer2",
+    "/run/layers/p0-layer3",
+    "/run/layers/p0-layer4",
+    "/run/layers/p0-layer5"
+  ],
+  "target": "/run/gcs/c/container0/rootfs"
+}
+engine.set_input(input)
+
+# Eval query
+results = engine.eval_query('data.framework.mount_overlay=x')
+
+# Print results
+print(results['result'][0])
+
+# Eval query as json
+results_json = engine.eval_query_as_json('data.framework.mount_overlay=x')
+print(results_json)
+```
+
diff --git a/bindings/python/building.md b/bindings/python/building.md
new file mode 100644
index 00000000..77e4182f
--- /dev/null
+++ b/bindings/python/building.md
@@ -0,0 +1,22 @@
+- Install maturin
+  ```
+  pipx install maturin
+  ```
+  See [Maturin User Guide](https://www.maturin.rs)
+  
+- Build bindings for Python
+  ```
+  cd bindings/python
+  maturin build --release --target-dir wheels
+  ```
+  
+- Install python wheel
+  ```
+  pip3 install ../../target/wheels/regorus*.whl --force-reinstall
+  ```
+  
+- Run test script
+  ```
+  python3 test.py
+  ```
+  
diff --git a/bindings/python/src/lib.rs b/bindings/python/src/lib.rs
index eb816400..ee1dc93d 100644
--- a/bindings/python/src/lib.rs
+++ b/bindings/python/src/lib.rs
@@ -182,8 +182,16 @@ 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<()> {
-        self.engine.add_policy(path, rego)?;
-        Ok(())
+        self.engine.add_policy(path, rego)
+    }
+
+    /// Add a policy from given file.
+    ///
+    /// The policy is parsed into AST.
+    ///
+    /// * `path`: Path to the policy file.
+    pub fn add_policy_from_file(&mut self, path: String) -> Result<()> {
+        self.engine.add_policy_from_file(path)
     }
 
     /// Add policy data.
@@ -203,6 +211,14 @@ impl Engine {
         self.engine.add_data(data)
     }
 
+    /// Add policy data from file.
+    ///
+    /// * `path`: Path to JSON policy data.
+    pub fn add_data_from_json_file(&mut self, path: String) -> Result<()> {
+        let data = Value::from_json_file(&path)?;
+        self.engine.add_data(data)
+    }
+
     /// Clear policy data.
     pub fn clear_data(&mut self) -> Result<()> {
         self.engine.clear_data();
@@ -228,6 +244,15 @@ impl Engine {
         Ok(())
     }
 
+    /// Set input.
+    ///
+    /// * `path`: Path to JSON input data.
+    pub fn set_input_from_json_file(&mut self, path: String) -> Result<()> {
+        let input = Value::from_json_file(&path)?;
+        self.engine.set_input(input);
+        Ok(())
+    }
+
     /// Evaluate query.
     ///
     /// * `query`: Rego expression to be evaluate.
diff --git a/bindings/wasm/README.md b/bindings/wasm/README.md
index b0dbd669..fb3e8703 100644
--- a/bindings/wasm/README.md
+++ b/bindings/wasm/README.md
@@ -6,9 +6,13 @@
    interpreter written in Rust.
   - *Rigorous* - A rigorous enforcer of well-defined Rego semantics.
 
+`regorusjs` is Regorus compiled into WASM.
+
 See [Repository](https://github.com/microsoft/regorus).
 
-`regorusjs` is Regorus compiled into WASM.
+To build this binding, see [building](https://github.com/microsoft/regorus/bindings/wasm/building.md)
+
+
 
 ## Usage
 
diff --git a/bindings/wasm/building.md b/bindings/wasm/building.md
new file mode 100644
index 00000000..12fefbaa
--- /dev/null
+++ b/bindings/wasm/building.md
@@ -0,0 +1,34 @@
+
+- Install `wasm-pack`
+  ```
+  cargo install wasm-pack
+  ```
+
+- Build `regorusjs` for nodejs.
+  ```
+  cd bindings/wasm
+  wasm-pack build --target nodejs --release
+  ```
+
+- Install [nodejs](https://nodejs.org/en/download)
+
+- Run the test script
+  ```
+  $ node test.js
+  \\{
+  \\  "result": [
+  \\    {
+  \\      "expressions": [
+  \\        {
+  \\          "value": "Hello, World!",
+  \\          "text": "data.test.message",
+  \\          "location": {
+  \\            "row": 1,
+  \\            "col": 1
+  \\          }
+  \\        }
+  \\      ]
+  \\    }
+  \\   ]
+  \\}
+  ```


Test patch:
diff --git a/bindings/python/test.py b/bindings/python/test.py
new file mode 100644
index 00000000..9cf0d592
--- /dev/null
+++ b/bindings/python/test.py
@@ -0,0 +1,52 @@
+# Copyright (c) Microsoft Corporation.
+# Licensed under the MIT License.
+
+import regorus
+
+# Create engine
+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')
+
+# Add policy data
+data = {
+  "metadata": {
+    "devices": {
+      "/run/layers/p0-layer0": "1b80f120dbd88e4355d6241b519c3e25290215c469516b49dece9cf07175a766",
+      "/run/layers/p0-layer1": "e769d7487cc314d3ee748a4440805317c19262c7acd2fdbdb0d47d2e4613a15c",
+      "/run/layers/p0-layer2": "eb36921e1f82af46dfe248ef8f1b3afb6a5230a64181d960d10237a08cd73c79",
+      "/run/layers/p0-layer3": "41d64cdeb347bf236b4c13b7403b633ff11f1cf94dbc7cf881a44d6da88c5156",
+      "/run/layers/p0-layer4": "4dedae42847c704da891a28c25d32201a1ae440bce2aecccfa8e6f03b97a6a6c",
+      "/run/layers/p0-layer5": "fe84c9d5bfddd07a2624d00333cf13c1a9c941f3a261f13ead44fc6a93bc0e7a"
+    }
+  }
+}
+engine.add_data(data)
+
+# Set input
+input = {
+  "containerID": "container0",
+  "layerPaths": [
+    "/run/layers/p0-layer0",
+    "/run/layers/p0-layer1",
+    "/run/layers/p0-layer2",
+    "/run/layers/p0-layer3",
+    "/run/layers/p0-layer4",
+    "/run/layers/p0-layer5"
+  ],
+  "target": "/run/gcs/c/container0/rootfs"
+}
+engine.set_input(input)
+
+# Eval query
+results = engine.eval_query('data.framework.mount_overlay=x')
+
+# Print results
+print(results['result'][0])
+
+# Eval query as json
+results_json = engine.eval_query_as_json('data.framework.mount_overlay=x')
+print(results_json)
diff --git a/bindings/wasm/test.js b/bindings/wasm/test.js
new file mode 100644
index 00000000..bd2d7ddc
--- /dev/null
+++ b/bindings/wasm/test.js
@@ -0,0 +1,47 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+var regorus = require('./pkg/regorusjs')
+
+// Create an engine.
+var engine = new regorus.Engine();
+
+// Add Rego policy.
+engine.add_policy(
+    // Associate this file name with policy
+    'hello.rego',
+    
+    // Rego policy
+`
+  package test
+  
+  # Join messages
+  message = concat(", ", [input.message, data.message])
+`)
+
+// Set policy data
+engine.add_data_json(`
+ {
+    "message" : "World!"
+ }
+`)
+
+// Set policy input
+engine.set_input_json(`
+ {
+	"message" : "Hello"
+ }
+`)
+
+// Eval query
+results = engine.eval_query('data.test.message')
+
+// Display
+console.log(results)
+
+// Convert results to object
+results = JSON.parse(results)
+
+// Process result
+console.log(results.result[0].expressions[0].value)
+