def test_dict_specializers(polar, qvar, qeval, query): class Animal: def __init__(self, species=None, genus=None, family=None): self.genus = genus self.species = species polar.register_class(Animal) rules = """ what_is(_: {genus: "canis"}, res) if res = "canine"; what_is(_: {species: "canis lupus", genus: "canis"}, res) if res = "wolf"; what_is(_: {species: "canis familiaris", genus: "canis"}, res) if res = "dog"; """ polar.load_str(rules) wolf = 'new Animal(species: "canis lupus", genus: "canis", family: "canidae")' dog = 'new Animal(species: "canis familiaris", genus: "canis", family: "canidae")' canine = 'new Animal(genus: "canis", family: "canidae")' assert len(query(f"what_is({wolf}, res)")) == 2 assert len(query(f"what_is({dog}, res)")) == 2 assert len(query(f"what_is({canine}, res)")) == 1 assert qvar(f"what_is({wolf}, res)", "res") == ["wolf", "canine"] assert qvar(f"what_is({dog}, res)", "res") == ["dog", "canine"] assert qvar(f"what_is({canine}, res)", "res") == ["canine"]
def test_data_conversions(polar, qvar, query): polar.load_str('a(1);b("two");c(true);d([1,"two",true]);') assert qvar("a(x)", "x", one=True) == 1 assert qvar("b(x)", "x", one=True) == "two" assert qvar("c(x)", "x", one=True) assert qvar("d(x)", "x", one=True) == [1, "two", True] y = qvar("x = y", "x", one=True) assert str(y) == "Variable('y')" assert repr(y) == "Variable('y')"
def test_field_specializers(load_file, qvar): # Contains test queries. load_file(Path(__file__).parent / "policies/people.pol") # Test method ordering w/field specializers. assert qvar('froody(Manager{name: "Sam"}, x)', "x") == [1] assert qvar('froody(Manager{name: "Sam", id: 1}, x)', "x") == [2, 1] assert qvar( 'froody(Manager{name: "Sam", id: 1, manager: Person{name: "Sam"}}, x)', "x" ) == [3, 2, 1]
def test_recursive_rule(tell, qeval, qvar): tell('derive("apple", "orange")') tell('derive("orange", "avacado")') tell('derive("avacado", "juniper_berry")') results = qvar('derive("apple", x)', "x") assert results == ["orange"] tell("derives(a, b) if derive(a, b);") tell("derives(a, b) if derive(a, z) and derives(z, b);") assert qeval('derives("apple", "juniper_berry")') results = qvar('derives("apple", x)', "x") assert results == ["orange", "avacado", "juniper_berry"]
def test_method_with_kwargs(polar, qvar): class Test: def kwarg_method(self, x=1, y=2): self.x = x self.y = y return True polar.register_class(Test) rules = """ defaults(result) if test = new Test() and test.kwarg_method() and result = [test.x, test.y]; kwargs(result) if test = new Test() and test.kwarg_method(y: 4, x: 3) and result = [test.x, test.y]; args(result) if test = new Test() and test.kwarg_method(5, 6) and result = [test.x, test.y]; mixed(result) if test = new Test() and test.kwarg_method(7, y: 8) and result = [test.x, test.y]; """ polar.load_str(rules) qvar("defaults(result)", "result") == [1, 2] qvar("kwargs(result)", "result") == [3, 4] qvar("args(result)", "result") == [5, 6] qvar("mixed(result)", "result") == [7, 8]
def test_class_field_specializers(polar, qvar, qeval, query): class Animal: def __init__(self, species=None, genus=None, family=None): self.genus = genus self.species = species self.family = family polar.register_class(Animal) rules = """ what_is(_: Animal, res) if res = "animal"; what_is(_: Animal{genus: "canis"}, res) if res = "canine"; what_is(_: Animal{family: "canidae"}, res) if res = "canid"; what_is(_: Animal{species: "canis lupus", genus: "canis"}, res) if res = "wolf"; what_is(_: Animal{species: "canis familiaris", genus: "canis"}, res) if res = "dog"; what_is(_: Animal{species: s, genus: "canis"}, res) if res = s; """ polar.load_str(rules) wolf = 'new Animal(species: "canis lupus", genus: "canis", family: "canidae")' dog = 'new Animal(species: "canis familiaris", genus: "canis", family: "canidae")' canine = 'new Animal(genus: "canis", family: "canidae")' canid = 'new Animal(family: "canidae")' animal = "new Animal()" assert len(query(f"what_is({wolf}, res)")) == 5 assert len(query(f"what_is({dog}, res)")) == 5 assert len(query(f"what_is({canine}, res)")) == 4 assert len(query(f"what_is({canid}, res)")) == 2 assert len(query(f"what_is({animal}, res)")) == 1 assert qvar(f"what_is({wolf}, res)", "res") == [ "wolf", "canis lupus", "canine", "canid", "animal", ] assert qvar(f"what_is({dog}, res)", "res") == [ "dog", "canis familiaris", "canine", "canid", "animal", ] assert qvar(f"what_is({canine}, res)", "res") == [None, "canine", "canid", "animal"] assert qvar(f"what_is({canid}, res)", "res") == ["canid", "animal"] assert qvar(f"what_is({animal}, res)", "res") == ["animal"]
def test_iterators(polar, qeval, qvar): class Foo: pass polar.register_class(Foo) with pytest.raises(exceptions.InvalidIteratorError) as e: qeval("x in new Foo()") class Bar(list): def sum(self): return sum(x for x in self) polar.register_class(Bar) assert qvar("x in new Bar([1, 2, 3])", "x") == [1, 2, 3] assert qvar("x = new Bar([1, 2, 3]).sum()", "x", one=True) == 6
def test_dictionaries(tell, qeval, qvar): # basic dictionary lookup tell('dict({hello: "world", foo: "bar"})') assert qeval('dict(d) and d.hello = "world"') ### dictionary lookups with variable fields ### tell("attr(d, k, d.(k))") # k = "hello", {hello: "steve"}.(k) = "steve" assert qeval('attr({hello: "steve"}, "hello", "steve")') # k = "hello", {hello: "steve"}.(k) = value, value = "steve" assert qvar('attr({hello: "steve"}, "hello", value)', "value", one=True) == "steve" # k = key, {hello: "steve"}.(k) = "steve", key = "hello" assert qvar('attr({hello: "steve"}, key, "steve")', "key", one=True) == "hello" ### nested lookups ### assert qeval( 'attr({hello: {this: {is: "nested"}}}, "hello", {this: {is: "nested"}})' ) tell("deepget(d, d.hello.this.is)") assert qeval('deepget({hello: {this: {is: "nested"}}}, "nested")') tell("myget(d, d.get.inner)") assert qeval('myget({get: {inner: "nested"}}, "nested")') tell("x({a: {b:{c:123}}})") tell("x({a: {y:{c:456}}})") assert qvar("x(d) and d.a.(k).c = value", "value") == [123, 456] tell("lookup(dict, result) if result = dict.a.b.c;") assert qeval('lookup({a: {b: {c: "nested"}}}, "nested")') ### more basic lookup tests ### tell('user({name: "steve", job: "programmer", state: "NY"})') tell('user({name: "alex", job: "programmer", state: "CO"})') tell('user({name: "graham", job: "business", state: "NY"})') assert qeval('user(d) and d.name = "steve"') assert qvar('user({job: "programmer", name: name, state: state})', "name") == [ "steve", "alex", ]
def test_numbers_from_external_call(polar, qeval, qvar, query): class Numberer: def give_me_an_int(self): yield 1 def give_me_a_float(self): yield 1.234 polar.register_class(Numberer) result = qvar("new Numberer().give_me_an_int() = var", "var", one=True) assert result == 1 assert qeval("new Numberer().give_me_an_int() = 1") result = qvar("new Numberer().give_me_a_float() = var", "var", one=True) assert result == 1.234 assert qeval("new Numberer().give_me_a_float() = 1.234")
def test_bool_from_external_call(polar, qeval, qvar, query): class Booler: def whats_up(self): yield True polar.register_class(Booler) result = qvar("new Booler().whats_up() = var", "var", one=True) assert qeval("new Booler().whats_up()")
def test_constructor(polar, qvar): """Test that class constructor is called correctly with constructor syntax.""" class Foo: def __init__(self, a, b, bar, baz): self.a = a self.b = b self.bar = bar self.baz = baz polar.register_class(Foo) # test positional args instance = qvar( "instance = new Foo(1,2,3,4)", "instance", one=True, ) assert instance.a == 1 assert instance.b == 2 assert instance.bar == 3 assert instance.baz == 4 # test positional and kwargs instance = qvar( "instance = new Foo(1, 2, bar: 3, baz: 4)", "instance", one=True, ) assert instance.a == 1 assert instance.b == 2 assert instance.bar == 3 assert instance.baz == 4 # test kwargs instance = qvar( "instance = new Foo(bar: 3, a: 1, baz: 4, b: 2)", "instance", one=True, ) assert instance.a == 1 assert instance.b == 2 assert instance.bar == 3 assert instance.baz == 4
def test_constructor(polar, qvar): """Test that class constructor is called correctly with constructor syntax.""" class TestConstructor: def __init__(self, x): self.x = x polar.register_class(TestConstructor) assert ( qvar("instance = new TestConstructor{x: 1} and y = instance.x", "y", one=True) == 1 ) assert ( qvar("instance = new TestConstructor{x: 2} and y = instance.x", "y", one=True) == 2 ) assert ( qvar( "instance = new TestConstructor{x: new TestConstructor{x: 3}} and y = instance.x.x", "y", one=True, ) == 3 ) class TestConstructorTwo: def __init__(self, x, y): self.x = x self.y = y polar.register_class(TestConstructorTwo) assert ( qvar( "instance = new TestConstructorTwo{x: 1, y: 2} and x = instance.x and y = instance.y", "y", one=True, ) == 2 )
def test_load_function(polar, query, qvar): """Make sure the load function works.""" # Loading the same file twice doesn't mess stuff up. polar.load_file(Path(__file__).parent / "test_file.polar") with pytest.raises(exceptions.PolarRuntimeException) as e: polar.load_file(Path(__file__).parent / "test_file.polar") assert ( str(e.value) == f"Problem loading file: File {Path(__file__).parent}/test_file.polar has already been loaded." ) with pytest.raises(exceptions.PolarRuntimeException) as e: polar.load_file(Path(__file__).parent / "test_file_renamed.polar") assert ( str(e.value) == f"Problem loading file: A file with the same contents as {Path(__file__).parent}/test_file_renamed.polar named {Path(__file__).parent}/test_file.polar has already been loaded." ) assert query("f(x)") == [{"x": 1}, {"x": 2}, {"x": 3}] assert qvar("f(x)", "x") == [1, 2, 3] polar.clear() polar.load_file(Path(__file__).parent / "test_file.polar") polar.load_file(Path(__file__).parent / "test_file_gx.polar") assert query("f(x)") == [{"x": 1}, {"x": 2}, {"x": 3}] assert query("g(x)") == [{"x": 1}, {"x": 2}, {"x": 3}]
def test_specializers_mixed(polar, qvar, qeval, query): class Animal: def __init__(self, species=None, genus=None, family=None): self.genus = genus self.species = species self.family = family polar.register_class(Animal) # load rules rules = """ what_is(_: Animal, res) if res = "animal_class"; what_is(_: Animal{genus: "canis"}, res) if res = "canine_class"; what_is(_: {genus: "canis"}, res) if res = "canine_dict"; what_is(_: Animal{family: "canidae"}, res) if res = "canid_class"; what_is(_: {species: "canis lupus", genus: "canis"}, res) if res = "wolf_dict"; what_is(_: {species: "canis familiaris", genus: "canis"}, res) if res = "dog_dict"; what_is(_: Animal{species: "canis lupus", genus: "canis"}, res) if res = "wolf_class"; what_is(_: Animal{species: "canis familiaris", genus: "canis"}, res) if res = "dog_class"; """ polar.load_str(rules) wolf = 'new Animal(species: "canis lupus", genus: "canis", family: "canidae")' dog = 'new Animal(species: "canis familiaris", genus: "canis", family: "canidae")' canine = 'new Animal(genus: "canis", family: "canidae")' wolf_dict = '{species: "canis lupus", genus: "canis", family: "canidae"}' dog_dict = '{species: "canis familiaris", genus: "canis", family: "canidae"}' canine_dict = '{genus: "canis", family: "canidae"}' # test number of results assert len(query(f"what_is({wolf}, res)")) == 6 assert len(query(f"what_is({dog}, res)")) == 6 assert len(query(f"what_is({canine}, res)")) == 4 assert len(query(f"what_is({wolf_dict}, res)")) == 2 assert len(query(f"what_is({dog_dict}, res)")) == 2 assert len(query(f"what_is({canine_dict}, res)")) == 1 # test rule ordering for instances assert qvar(f"what_is({wolf}, res)", "res") == [ "wolf_class", "canine_class", "canid_class", "animal_class", "wolf_dict", "canine_dict", ] assert qvar(f"what_is({dog}, res)", "res") == [ "dog_class", "canine_class", "canid_class", "animal_class", "dog_dict", "canine_dict", ] assert qvar(f"what_is({canine}, res)", "res") == [ "canine_class", "canid_class", "animal_class", "canine_dict", ] # test rule ordering for dicts assert qvar(f"what_is({wolf_dict}, res)", "res") == ["wolf_dict", "canine_dict"] assert qvar(f"what_is({dog_dict}, res)", "res") == ["dog_dict", "canine_dict"] assert qvar(f"what_is({canine_dict}, res)", "res") == ["canine_dict"]
def test_external(polar, qvar): class Bar: def y(self): return "y" class Foo: def __init__(self, a="a"): self.a = a def b(self): yield "b" @classmethod def c(cls): assert issubclass(cls, Foo) return "c" def d(self, x): return x def bar(self): return Bar() def e(self): return [1, 2, 3] def f(self): yield [1, 2, 3] yield [4, 5, 6] yield 7 def g(self): return {"hello": "world"} def h(self): return True def capital_foo(): return Foo(a="A") polar.register_class(Foo, from_polar=capital_foo) assert qvar("new Foo{}.a = x", "x", one=True) == "A" assert qvar("new Foo{}.a() = x", "x", one=True) == "A" assert qvar("new Foo{}.b = x", "x", one=True) == "b" assert qvar("new Foo{}.b() = x", "x", one=True) == "b" assert qvar("Foo.c = x", "x", one=True) == "c" assert qvar("Foo.c() = x", "x", one=True) == "c" assert qvar("new Foo{} = f and f.a() = x", "x", one=True) == "A" assert qvar("new Foo{}.bar().y() = x", "x", one=True) == "y" assert qvar("new Foo{}.e = x", "x", one=True) == [1, 2, 3] assert qvar("new Foo{}.f = x", "x") == [[1, 2, 3], [4, 5, 6], 7] assert qvar("new Foo{}.g.hello = x", "x", one=True) == "world" assert qvar("new Foo{}.h = x", "x", one=True) is True
def test_helpers(polar, load_file, query, qeval, qvar): load_file(Path(__file__).parent / "test_file.polar") # f(1); assert query("f(x)") == [{"x": 1}, {"x": 2}, {"x": 3}] assert qvar("f(x)", "x") == [1, 2, 3]
def test_class_specializers(polar, qvar, qeval, query): class A: def a(self): return "A" def x(self): return "A" class B(A): def b(self): return "B" def x(self): return "B" class C(B): def c(self): return "C" def x(self): return "C" class X: def x(self): return "X" polar.register_class(A) polar.register_class(B) polar.register_class(C) polar.register_class(X) rules = """ test(_: A); test(_: B); try(_: B, res) if res = 2; try(_: C, res) if res = 3; try(_: A, res) if res = 1; """ polar.load_str(rules) assert qvar("new A().a() = x", "x", one=True) == "A" assert qvar("new A().x() = x", "x", one=True) == "A" assert qvar("new B().a() = x", "x", one=True) == "A" assert qvar("new B().b() = x", "x", one=True) == "B" assert qvar("new B().x() = x", "x", one=True) == "B" assert qvar("new C().a() = x", "x", one=True) == "A" assert qvar("new C().b() = x", "x", one=True) == "B" assert qvar("new C().c() = x", "x", one=True) == "C" assert qvar("new C().x() = x", "x", one=True) == "C" assert qvar("new X().x() = x", "x", one=True) == "X" assert len(query("test(new A())")) == 1 assert len(query("test(new B())")) == 2 assert qvar("try(new A(), x)", "x") == [1] assert qvar("try(new B(), x)", "x") == [2, 1] assert qvar("try(new C(), x)", "x") == [3, 2, 1] assert qvar("try(new X(), x)", "x") == []
def test_nil(polar, query, qvar): """Test that nil is pre-registered as None.""" polar.load_str("null(nil);") assert qvar("null(x)", "x") == [None] assert query(Predicate("null", [None])) == [{}] assert not query(Predicate("null", [[]]))
def test_query_multiple(tell, qvar): tell('a("foo")') tell('a("bar")') tell('a("baz")') results = qvar("a(x)", "x") assert results == ["foo", "bar", "baz"]
def test_other_constants(polar, qvar, query): d = {"a": 1} polar.register_constant("d", d) assert qvar("x = d.a", "x") == [1]
def test_group_field_access(load_file, qvar): load_file(Path(__file__).parent / "policies/groups.pol") assert qvar('get_bar(Baz{bar: "test"}, val)', "val", one=True) == "test"
def test_other_constants(polar, qvar): """Test that other objects may be registered as constants.""" d = {"a": 1} polar.register_constant(d, "d") assert qvar("x = d.a", "x") == [1]
def test_predicate(polar, qvar, query): """Test that predicates can be converted to and from python.""" polar.load_str("f(x) if x = pred(1, 2);") assert qvar("f(x)", "x") == [Predicate("pred", [1, 2])] assert query(Predicate(name="f", args=[Predicate("pred", [1, 2])])) == [{}]
def test_load_file(load_file, tell, qeval, qvar): load_file(Path(__file__).parent / "policies/test.polar") assert qeval('test("true")') tell('b("foo")') assert qvar("a(x)", "x", one=True) == "foo"
def test_external(polar, qvar, qeval): class Bar: def y(self): return "y" class Foo: def __init__(self, a="a"): self.a = a def b(self): yield "b" @classmethod def c(cls): assert issubclass(cls, Foo) return "c" def d(self, x): return x def bar(self): return Bar() def e(self): return [1, 2, 3] def f(self): yield [1, 2, 3] yield [4, 5, 6] yield 7 def g(self): return {"hello": "world"} def h(self): return True def capital_foo(): return Foo(a="A") polar.register_class(Foo, from_polar=capital_foo) assert qvar("new Foo().a = x", "x", one=True) == "A" with pytest.raises(RuntimeError, match="tried to call 'a' but it is not callable"): assert not qeval("new Foo().a() = x") assert not qvar("new Foo().b = x", "x", one=True) == "b" assert qvar("new Foo().b() = x", "x", one=True) == "b" assert not qvar("Foo.c = x", "x", one=True) == "c" assert qvar("Foo.c() = x", "x", one=True) == "c" assert qvar("new Foo() = f and f.a = x", "x", one=True) == "A" assert qvar("new Foo().bar().y() = x", "x", one=True) == "y" assert qvar("new Foo().e() = x", "x", one=True) == [1, 2, 3] assert qvar("new Foo().f() = x", "x") == [[1, 2, 3], [4, 5, 6], 7] assert qvar("new Foo().g().hello = x", "x", one=True) == "world" assert qvar("new Foo().h() = x", "x", one=True) is True
def test_class_specializers(polar, qvar, qeval, query): class A: def a(self): return "A" def x(self): return "A" class B(A): def b(self): return "B" def x(self): return "B" class C(B): def c(self): return "C" def x(self): return "C" class X: def x(self): return "X" polar.register_class(A) polar.register_class(B) polar.register_class(C) polar.register_class(X) rules = """ test(_: A{}); test(_: B{}); try(_: B{}, res) if res = 2; try(_: C{}, res) if res = 3; try(_: A{}, res) if res = 1; """ polar.load_str(rules) assert qvar("new A{}.a = x", "x", one=True) == "A" assert qvar("new A{}.x = x", "x", one=True) == "A" assert qvar("new B{}.a = x", "x", one=True) == "A" assert qvar("new B{}.b = x", "x", one=True) == "B" assert qvar("new B{}.x = x", "x", one=True) == "B" assert qvar("new C{}.a = x", "x", one=True) == "A" assert qvar("new C{}.b = x", "x", one=True) == "B" assert qvar("new C{}.c = x", "x", one=True) == "C" assert qvar("new C{}.x = x", "x", one=True) == "C" assert qvar("new X{}.x = x", "x", one=True) == "X" assert len(query("test(new A{})")) == 1 assert len(query("test(new B{})")) == 2 assert qvar("try(new A{}, x)", "x") == [1] assert qvar("try(new B{}, x)", "x") == [2, 1] assert qvar("try(new C{}, x)", "x") == [3, 2, 1] assert qvar("try(new X{}, x)", "x") == []