API

This part of the documentation covers all the interfaces of regopy.

Interpreter

class regopy.Interpreter

Pythonic interface to the rego-cpp interpreter.

This wraps the Rego C API, and handles passing calls to the C API and converting the results to Python types.

Examples

>>> from regopy import Interpreter
>>> rego = Interpreter()
>>> print(rego.query("x=5;y=x + (2 - 4 * 0.25) * -3 + 7.4;2 * 5"))
{"expressions":[true, true, 10], "bindings":{"x":5, "y":9.4}}
>>> input = {
...     "a": 10,
...     "b": "20",
...     "c": 30.0,
...     "d": True
... }
>>> data0 = {
...     "one": {
...         "bar": "Foo",
...         "baz": 5,
...         "be": True,
...         "bop": 23.4
...     },
...     "two": {
...         "bar": "Bar",
...         "baz": 12.3,
...         "be": False,
...         "bop": 42
...     }
... }
>>> data1 = {
...     "three": {
...         "bar": "Baz",
...         "baz": 15,
...         "be": True,
...         "bop": 4.23
...     }
... }
>>> module = '''
...     package objects
...
...     rect := {`width`: 2, "height": 4}
...     cube := {"width": 3, `height`: 4, "depth": 5}
...     a := 42
...     b := false
...     c := null
...     d := {"a": a, "x": [b, c]}
...     index := 1
...     shapes := [rect, cube]
...     names := ["prod", `smoke1`, "dev"]
...     sites := [{"name": "prod"}, {"name": names[index]}, {"name": "dev"}]
...     e := {
...         a: "foo",
...         "three": c,
...         names[2]: b,
...         "four": d,
...     }
...     f := e["dev"]
... '''
>>> rego.set_input(input)
>>> rego.add_data(data0)
>>> rego.add_data(data1)
>>> rego.add_module("objects", module)
>>> print(rego.query("x=[data.one, input.b, data.objects.sites[1]]"))
{"expressions":[true], "bindings":{"x":[{"bar":"Foo", "baz":5, "be":true, "bop":23.4},"20",{"name":"smoke1"}]}}
add_data(data)

Adds data (i.e. a base document) to the interpreter.

The data should be provided as single, JSON-encodable Python object.

Parameters:

data (Any) – The data.

Raises:

RegoError – If an error occurs in the Rego interpreter.

See also

add_data_json()

add_data_json(json)

Adds data (i.e. a base document) to the interpreter.

The data should be provided as single JSON-encoded object.

Parameters:

json (str) – The data JSON.

Raises:

RegoError – If an error occurs in the Rego interpreter.

Example

>>> from regopy import Interpreter
>>> rego = Interpreter()
>>> data = '''
...   {
...     "one": {
...       "bar": "Foo",
...       "baz": 5,
...       "be": true,
...       "bop": 23.4
...     },
...     "two": {
...       "bar": "Bar",
...       "baz": 12.3,
...       "be": false,
...       "bop": 42
...     }
...   }
... '''
>>> rego.add_data_json(data)
>>> output = rego.query("data.one.bar")
>>> print(output)
{"expressions":["Foo"]}
add_module(name, source)

Adds a module (i.e. a virtual document) to the interpreter.

Parameters:
  • name (str) – The module name.

  • source (str) – The module source.

Raises:

RegoError – If an error occurs in the Rego interpreter.

Example

>>> from regopy import Interpreter
>>> module = '''
...   package scalars
...
...   greeting := "Hello"
...   max_height := 42
...   pi := 3.14159
...   allowed := true
...   location := null
... '''
>>> rego = Interpreter()
>>> rego.add_module("scalars", module)
>>> output = rego.query("data.scalars.greeting")
>>> print(output)
{"expressions":["Hello"]}
build(query, entrypoints=())

Builds a bundle, which contains a compiled version of the base and virtual documents.

Parameters:
  • query (str) – The query string. Either a query or at least one entrypoint must be provided.

  • entrypoints (Sequence[str]) – Zero or more entrypoints, in the form <document>/<rule>, for example objects/sites or parent/child/foo. There must either be at least one entrypoint or a query for the bundle to be produced.

Returns:

contains the compiled base and virtual documents, and plans to execute the

specified entrypoints (and the query, if provided). Can be written to disk and loaded back again by a different interpreter.

Return type:

Bundle

Raises:

RegoError – If an error occurs during compilation.

Example

>>> from regopy import Interpreter
>>> input0 = {
...     "x": 104,
...     "y": 119
... }
>>> data = {
...     "a": 7,
...     "b": 13
... }
>>> module = '''
...     package example
...
...     foo := data.a * input.x + data.b * input.y
...     bar := data.b * input.x + data.a * input.y
... '''
>>> rego_build = Interpreter()
>>> rego_build.add_data(data)
>>> rego_build.add_module("example.rego", module)
>>> bundle = rego_build.build("x=data.example.foo + data.example.bar",
...                     ["example/foo", "example/bar"])
>>> rego_run = Interpreter()
>>> rego_run.set_input(input0)
>>> output = rego_run.query_bundle(bundle)
>>> print(output)
{"expressions":[true], "bindings":{"x":4460}}
property debug_enabled: bool

Whether debug mode is enabled.

When debug mode is enabled, the Rego interpreter will output extensive debugging information about the compliation process, including intermediary ASTs and the generated bytecode. This is mostly useful for debugging the Rego compiler itself, and may not be as useful for debugging Rego policies.

is_builtin(name)

Returns whether the given name is a built-in function.

Parameters:

name (str) – The name of the function.

Returns:

Whether the given name is a built-in function.

Return type:

bool

load_bundle(path, format=BundleFormat.JSON)

Loads a bundle from the disk.

Parameters:
  • path (str) – A path to a location on disk. If being saved in default mode, this will be a directory. Otherwise, it will become a single binary file.

  • bundle (Bundle) – The bundle to save

  • binary (bool) – Whether to save the bundle as a directory (default) or as a single binary file.

Returns:

The serialized bundle

Return type:

bundle (Bundle)

Raises:

RegoError – If an error occurs during compilation.

property log_level: LogLevel

The log level of the interpreter

query(query)

Performs a query against the current set of base and virtual documents.

While the Rego interpreter can be used to perform simple queries, in most cases users will want to load one or more base documents (using add_data() or add_data_json()) and one or more Rego modules (using add_module() or add_module_file()). Then, multiple queries can be performed by providing an input (using set_input() or set_input_json()) and then calling this method.

Parameters:

query (str) – The query.

Returns:

The query result.

Return type:

Output

Raises:

RegoError – If an error occurs in the Rego interpreter.

Examples

>>> from regopy import Interpreter
>>> input0 = {"a": 10}
>>> input1 = {"a": 4}
>>> input2 = {"a": 7}
>>> multi = '''
...   package multi
...
...   default a := 0
...
...   a := val {
...       input.a > 0
...       input.a < 10
...       input.a % 2 == 1
...       val := input.a * 10
...   } {
...       input.a > 0
...       input.a < 10
...       input.a % 2 == 0
...       val := input.a * 10 + 1
...   }
...
...   a := input.a / 10 {
...       input.a >= 10
...   }
... '''
>>> rego = Interpreter()
>>> rego.add_module("multi", multi)
>>> rego.set_input(input0)
>>> output = rego.query("data.multi.a")
>>> print(output)
{"expressions":[1]}
>>> rego.set_input(input1)
>>> output = rego.query("data.multi.a")
>>> print(output)
{"expressions":[41]}
>>> rego.set_input(input2)
>>> output = rego.query("data.multi.a")
>>> print(output)
{"expressions":[70]}
query_bundle(bundle)

Performs a query using the compiled policy in the bundle.

Parameters:

bundle (Bundle) – The bundle to execute

Returns:

The result of the query

Return type:

output (Output)

Raises:

RegoError – If an error occurs during execution

This method requires that a query was provided to :func:`~regopy.Interpreter.build. Otherwise, it will throw an exception.

query_bundle_entrypoint(bundle, entrypoint)

Performs a query using the compiled policy in the bundle.

Parameters:
  • bundle (Bundle) – The bundle to execute

  • entrypoint (str) – The entrypoint to execute

Returns:

The result of the query

Return type:

output (Output)

Raises:

RegoError – If an error occurs during execution

This method requires that the specified entrypoint was provided to :func:`~regopy.Interpreter.build. Otherwise, it will throw an exception.

save_bundle(path, bundle, format=BundleFormat.JSON)

Save a bundle to disk.

Parameters:
  • path (str) – A path to a location on disk. If being saved in default mode, this will be a directory. Otherwise, it will become a single binary file.

  • bundle (Bundle) – The bundle to save

  • binary (bool) – Whether to save the bundle as a directory (default) or as a single binary file.

Raises:

RegoError – If an error occurs during compilation.

There are two modes for bundle serialization. The default, which uses JSON, creates a directory at the specified path (if it does not already exist) and then writes at least two files: plan.json (which contains the compiled virtual documents and plans for execution) and data.json (which contains the base documents merged into a single JSON hierarchy). Module source files will also be copied into the directory.

The second mode is binary serialization. This uses the Rego Bundle Binary format (https://microsoft.github.io/rego-cpp/cpp/rbb.html) to create a single file which contains all the bundle information.

Example

>>> from regopy import Interpreter
>>> rego_build = Interpreter()
>>> bundle = rego_build.build("a=1")
>>> rego_build.save_bundle("bundle", bundle)
>>> rego_build.save_bundle("bundle_bin.rbb", bundle, BundleFormat.Binary)
>>>
>>> rego_run = Interpreter()
>>> bundle = rego_run.load_bundle("bundle")
>>> output = rego_run.query_bundle(bundle)
>>> print(output.binding("a"))
1
>>> bundle = rego_run.load_bundle("bundle_bin.rbb", BundleFormat.Binary)
>>> output = rego_run.query_bundle(bundle)
>>> print(output.binding("a"))
1
set_debug_path(path)

Sets the path to the directory where this will write debug AST files.

This is only useful when debug mode is enabled.

Parameters:

path (str) – The path to the directory where this will write debug AST files.

set_input(value)

Sets the input document of the interpreter.

This can be called several times during the lifetime of the interpreter. The value must be JSON encodable.

Parameters:

value (Input) – The input value.

Raises:

RegoError – If an error occurs in the Rego interpreter.

See also

set_input_json()

set_input_term(term)

Sets the input term of the interpreter.

This can be called several times during the lifetime of the interpreter.

Parameters:

term (str) – The input term.

Raises:

RegoError – If an error occurs in the Rego interpreter.

Example

>>> from regopy import Interpreter
>>> input = '''
...   {
...     "a": 10,
...     "b": "20",
...     "c": 30.0,
...     "d": true
...   }
... '''
>>> rego = Interpreter()
>>> rego.set_input_term(input)
>>> output = rego.query("input.a")
>>> print(output)
{"expressions":[10]}
property strict_built_in_errors: bool

Whether the interpreter will forward errors thrown by the built-ins.

By default, the Rego interpreter will catch errors thrown by the built-ins and return them as an Undefined result. When this is enabled, the interpreter will instead forward the error to the caller.

property well_formed_checks_enabled: bool

Whether the Rego interpreter will perform well-formedness checks.

When enabled, the Rego interpreter will perform well-formedness checks on the AST during each pass of the compiler.

class regopy.Bundle(impl)

Compiled base and virtual documents.

node()

The AST which represents the saved state of the bundle.

Return type:

Node

ok()

Whether the bundle was built successfully.

Return type:

bool

class regopy.Input(value)

Used to create an in-memory Input object for passing to a policy.

Example

>>> from regopy import Input, Interpreter
>>> rego = Interpreter()
>>> rego.set_input(Input({"a": 10, "b": "20", "c": 30.0, "d": True}))
>>> print(rego.query("input.a"))
{"expressions":[10]}
enum regopy.BundleFormat(value)

Formats for bundle serialization.

Valid values are as follows:

JSON = <BundleFormat.JSON: 0>
Binary = <BundleFormat.Binary: 1>
enum regopy.LogLevel(value)
Member Type:

int

Valid values are as follows:

NONE = <LogLevel.NONE: 0>
ERROR = <LogLevel.ERROR: 1>
OUTPUT = <LogLevel.OUTPUT: 2>
WARN = <LogLevel.WARN: 3>
INFO = <LogLevel.INFO: 4>
DEBUG = <LogLevel.DEBUG: 5>
TRACE = <LogLevel.TRACE: 6>

Output

class regopy.Output(impl)

Interface for the Rego output.

Outputs can either be examined as strings, or inspected using Rego Nodes. It is also possible to extract bindings for specific variables.

Examples

>>> from regopy import Interpreter
>>> rego = Interpreter()
>>> output = rego.query("x=5;y=x + (2 - 4 * 0.25) * -3 + 7.4;2 * 5")
>>> print(output)
{"expressions":[true, true, 10], "bindings":{"x":5, "y":9.4}}
>>> x = output.binding("x")
>>> print(x.json())
5
binding(name, index=0)

Attempts to return the binding for the given variable name.

Parameters:
  • name (str) – The name of the variable to return.

  • index (int, optional) – The index of the binding to return.

Returns:

The binding for the given variable name.

Return type:

Node

Raises:

ValueError – If the variable is not bound.

expressions(index=0)

Returns the output terms at the given index.

Parameters:

index (int, optional) – The index of the term to return.

Returns:

A node contains a list of node objects

Return type:

Node

node()

Returns the root node of the output.

Return type:

Node

ok()

Returns whether the output is ok.

The output of a successful query will always be a Node. However, if there was an error that arose with the Rego engine, then this Node will be of kind NodeKind.ErrorSeq and contain one or more errors. This method gives a quick way, without inspecting the result node, of finding whether it is ok.

Return type:

bool

Node

class regopy.Node(impl)

Interface for a Rego Node.

Rego Nodes are the basic building blocks of a Rego result. They exist in a tree structure. Each node has a kind, which is one of the variants of [NodeKind]. Each node also has zero or more children, which are also nodes.

Examples: >>> from regopy import Interpreter >>> rego = Interpreter() >>> output = rego.query(‘x={“a”: 10, “b”: “20”, “c”: [30.0, 60], “d”: true, “e”: null}’) >>> x = output.binding(“x”) >>> print(“x =”, x.json()) x = {“a”:10, “b”:”20”, “c”:[30,60], “d”:true, “e”:null}

>>> print("x['a'] =", x["a"].value)
x['a'] = 10
>>> print("x['b'] =", '"' + x["b"].value + '"')
x['b'] = "20"
>>> print("x['c'][0] =", x["c"][0].value)
x['c'][0] = 30.0
>>> print("x['c'][1] =", x["c"][1].value)
x['c'][1] = 60
>>> print("x['d'] =", x["d"].value)
x['d'] = True
>>> print("x['e'] =", x["e"].value)
x['e'] = None
at(index)

Returns the child node at the given index.

Return type:

Node

index(index)

Returns the node at an index of an array.

Returns:

The child node.

Return type:

Node

Raises:
  • IndexError – If the index is out of bounds.

  • TypeError – If the node is not an array.

json()

Returns the node as a JSON string.

Return type:

str

property kind: NodeKind

Returns the node type.

property kind_name: str

Returns a human-readable string representation of the node kind.

lookup(key)

Returns the child node for the given key.

If this is of kind Object or Set, then the key must be a string.

Returns:

The child node.

Return type:

Node

Raises:
  • KeyError – If the key is not found.

  • TypeError – If the node does not support lookup

property value: str | int | float | bool | None

Returns the node value.

If the node has a singular value (i.e is a Scalar or a Term containing a scalar) then this will return that value. Otherwise it will raise a RegoError.

Returns:

The node value.

Return type:

Union[str, int, float, bool, None]

Raises:

RegoError – If the node does not have a singular value.

Examples

>>> from regopy import Interpreter
>>> rego = Interpreter()
>>> output = rego.query('x=10; y="20"; z=true')
>>> x = output.binding("x")
>>> print("x =", x.value)
x = 10
>>> y = output.binding("y")
>>> print("y =", y.value)
y = "20"
>>> z = output.binding("z")
>>> print("z =", z.value)
z = True
enum regopy.NodeKind(value)

Enumeration of node types.

Member Type:

int

Valid values are as follows:

Binding = <NodeKind.Binding: 1000>
Var = <NodeKind.Var: 1001>
Term = <NodeKind.Term: 1002>
Scalar = <NodeKind.Scalar: 1003>
Array = <NodeKind.Array: 1004>
Set = <NodeKind.Set: 1005>
Object = <NodeKind.Object: 1006>
ObjectItem = <NodeKind.ObjectItem: 1007>
Int = <NodeKind.Int: 1008>
Float = <NodeKind.Float: 1009>
String = <NodeKind.String: 1010>
True_ = <NodeKind.True_: 1011>
False_ = <NodeKind.False_: 1012>
Boolean = <NodeKind.Boolean: 1111>
Null = <NodeKind.Null: 1013>
Undefined = <NodeKind.Undefined: 1014>
Terms = <NodeKind.Terms: 1015>
Bindings = <NodeKind.Bindings: 1016>
Results = <NodeKind.Results: 1017>
Result = <NodeKind.Result: 1018>
Error = <NodeKind.Error: 1800>
ErrorMessage = <NodeKind.ErrorMessage: 1801>
ErrorAst = <NodeKind.ErrorAst: 1802>
ErrorCode = <NodeKind.ErrorCode: 1803>
ErrorSeq = <NodeKind.ErrorSeq: 1804>
Internal = <NodeKind.Internal: 1999>