jsonlogic-py 0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ Copyright 2023-2024 Lawrence Livermore National Laboratory
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,42 @@
1
+ Metadata-Version: 2.1
2
+ Name: jsonlogic-py
3
+ Version: 0.1
4
+ Summary: A Python package that emits JSON Logic
5
+ Author: Peter Pirkelbauer, Seth Bromberger
6
+ Project-URL: Homepage, https://github.com/MetallData/jsonlogic
7
+ Project-URL: Issues, https://github.com/MetallData/jsonlogic/issues
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Intended Audience :: Science/Research
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: OS Independent
13
+ Requires-Python: >=3.10
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE.txt
16
+ Provides-Extra: dev
17
+ Requires-Dist: pytest; extra == "dev"
18
+ Requires-Dist: coverage; extra == "dev"
19
+ Requires-Dist: json-logic-qubit; extra == "dev"
20
+ Requires-Dist: flake8; extra == "dev"
21
+ Requires-Dist: mypy; extra == "dev"
22
+
23
+ ## jsonlogic.py - JSON Logic expression generator
24
+
25
+ This package provides functionality to express JSON Logic using standard Python datastructures.
26
+
27
+ An example:
28
+
29
+ ```python
30
+ >>> from jsonlogic import Variable
31
+ >>> v1 = Variable('var1')
32
+ >>> v2 = Variable('var2')
33
+ >>> e = (v1 < v2)
34
+ >>> print(e)
35
+ {"<": [{"var": "var1"}, {"var": "var2"}]}
36
+ >>> print (v1 < 3)
37
+ {"<": [{"var": "var1"}, 3]}
38
+ >>> print ( (v1 < 3) & (v1 > v2))
39
+ {"and": [{"<": [{"var": "var1"}, 3]}, {">": [{"var": "var1"}, {"var": "var2"}]}]}
40
+ >>> print ( (v1 < 3) & ~(v1 > v2))) # ~ is "not"
41
+ {"and": [{"<": [{"var": "v1"}, 3]}, {"not": [{">": [{"var": "v1"}, {"var": "v2"}]}]}]}
42
+ ```
@@ -0,0 +1,20 @@
1
+ ## jsonlogic.py - JSON Logic expression generator
2
+
3
+ This package provides functionality to express JSON Logic using standard Python datastructures.
4
+
5
+ An example:
6
+
7
+ ```python
8
+ >>> from jsonlogic import Variable
9
+ >>> v1 = Variable('var1')
10
+ >>> v2 = Variable('var2')
11
+ >>> e = (v1 < v2)
12
+ >>> print(e)
13
+ {"<": [{"var": "var1"}, {"var": "var2"}]}
14
+ >>> print (v1 < 3)
15
+ {"<": [{"var": "var1"}, 3]}
16
+ >>> print ( (v1 < 3) & (v1 > v2))
17
+ {"and": [{"<": [{"var": "var1"}, 3]}, {">": [{"var": "var1"}, {"var": "var2"}]}]}
18
+ >>> print ( (v1 < 3) & ~(v1 > v2))) # ~ is "not"
19
+ {"and": [{"<": [{"var": "v1"}, 3]}, {"not": [{">": [{"var": "v1"}, {"var": "v2"}]}]}]}
20
+ ```
@@ -0,0 +1,30 @@
1
+ [build-system]
2
+ requires = ["setuptools >= 68.0.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "jsonlogic-py"
7
+ version = "0.1"
8
+ authors = [
9
+ { name="Peter Pirkelbauer"}, { name="Seth Bromberger"}
10
+ ]
11
+ description="A Python package that emits JSON Logic"
12
+ readme = "README.md"
13
+ dynamic = ["dependencies", "optional-dependencies"]
14
+ requires-python = ">=3.10"
15
+ classifiers = [
16
+ "Development Status :: 3 - Alpha",
17
+ "Programming Language :: Python :: 3",
18
+ "Intended Audience :: Science/Research",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Operating System :: OS Independent"
21
+ ]
22
+
23
+ [tool.setuptools.dynamic]
24
+ dependencies = {file = ["requirements.txt"]}
25
+ optional-dependencies = {dev = {file = ["requirements-dev.txt"] }}
26
+
27
+
28
+ [project.urls]
29
+ Homepage = "https://github.com/MetallData/jsonlogic"
30
+ Issues = "https://github.com/MetallData/jsonlogic/issues"
@@ -0,0 +1,5 @@
1
+ pytest
2
+ coverage
3
+ json-logic-qubit
4
+ flake8
5
+ mypy
File without changes
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,128 @@
1
+ """ JSONLogic emitters """
2
+
3
+ from __future__ import annotations
4
+ from typing import Any
5
+ import json
6
+ from .operations import Operation, jl_operations
7
+ from .errors import JsonLogicArgumentError
8
+ from .classes import Entity
9
+ from .jsontypes import PyJsonType, deduce_type
10
+
11
+
12
+ class Operand(Entity):
13
+ """An abstract base class representing an JSONLogic operand"""
14
+
15
+ def __new__(cls, *_, **__):
16
+ for dunder, op in jl_operations.items():
17
+ setattr(cls, dunder, lambda self, *x, o=op: Expression(o, self, *x))
18
+ return super().__new__(cls)
19
+
20
+ def prepare(self) -> Any:
21
+ """prepares the structure for json by converting it into something that can be dumped"""
22
+ raise NotImplementedError()
23
+
24
+ def to_json(self) -> str:
25
+ """represents the object as JSON"""
26
+ return json.dumps(self.prepare())
27
+
28
+ def __str__(self):
29
+ return self.to_json()
30
+
31
+
32
+ class Literal(Operand):
33
+ """A JSONLogic literal."""
34
+
35
+ def __init__(self, val: PyJsonType | Literal, docstr: str | None = None):
36
+ super().__init__()
37
+ self._rawval: PyJsonType = val._rawval if isinstance(val, Literal) else val
38
+
39
+ self.type = deduce_type(val)
40
+ if docstr is not None:
41
+ self.__doc__ = docstr
42
+
43
+ def __repr__(self):
44
+ return str(self.type)
45
+
46
+ def __str__(self):
47
+ return str(self.type)
48
+
49
+ def prepare(self) -> PyJsonType:
50
+ return self.type.prepare()
51
+
52
+
53
+ class Variable(Operand):
54
+ """A JSONLogic variable"""
55
+
56
+ def __init__(self, var: str, docstr: str | None = None):
57
+ super().__init__()
58
+ self.var = var
59
+ if docstr is not None:
60
+ self.__doc__ = docstr
61
+
62
+ def prepare(self):
63
+ return {"var": self.var}
64
+
65
+
66
+ class Expression(Operand):
67
+ """A JSONLogic expression"""
68
+
69
+ def __init__(
70
+ self,
71
+ op: Operation,
72
+ o1: Variable | Expression,
73
+ *on: Operand | PyJsonType,
74
+ ):
75
+ super().__init__()
76
+ # if op is None then this expression is just a native Selector held in o1.
77
+ self.op = op
78
+ self.o1 = o1
79
+ if op.arity is not None and len(on) != op.arity - 1:
80
+ raise JsonLogicArgumentError(
81
+ f"incorrect number of arguments for {op}: wanted {op.arity}, got {len(on) + 1}"
82
+ )
83
+ # add the remaining variables, casting them to Literals if they're not Variables, Expressions, or Literals.
84
+ self.on = tuple(Literal(o) if not isinstance(o, Operand) else o for o in on)
85
+
86
+ def prepare(self):
87
+ return {
88
+ str(self.op): [self.o1.prepare()]
89
+ + list(x.prepare() if isinstance(x, Operand) else x for x in self.on)
90
+ }
91
+
92
+
93
+ # class Type(ABC):
94
+ # """The abstract base class for all JSONLogic Types"""
95
+
96
+ # def __init__(self, value):
97
+ # _value = value
98
+
99
+
100
+ # class Number(Type, float):
101
+ # """Type representing a JSONLogic number"""
102
+
103
+
104
+ # class String(Type, str):
105
+ # """Type representing a JSONLogic string"""
106
+
107
+
108
+ # class Object(Type, dict):
109
+ # """Type representing a JSONLogic object (map)"""
110
+
111
+
112
+ # class Bool(Type):
113
+ # """Type representing a JSONLogic boolean"""
114
+
115
+ # def __init__(self, value):
116
+ # self.
117
+
118
+
119
+ # class Array(Type, list):
120
+ # """Type representing a JSONLogic array (list)"""
121
+
122
+
123
+ # class Null(Type, NoneType):
124
+ # """Type representing a JSONLogic null"""
125
+
126
+
127
+ # class _Any(Type, Any):
128
+ # """Type representing any JSONLogic type"""
@@ -0,0 +1,8 @@
1
+ # pylint: disable=too-few-public-methods
2
+ """ Abstract base class for JsonLogic """
3
+
4
+ from abc import ABC
5
+
6
+
7
+ class Entity(ABC):
8
+ """The abstract base class for all JSONLogic entities"""
@@ -0,0 +1,9 @@
1
+ """ Error classes for jsonlogic """
2
+
3
+
4
+ class JsonLogicError(Exception):
5
+ """top-level error for JsonLogic"""
6
+
7
+
8
+ class JsonLogicArgumentError(JsonLogicError):
9
+ """raised when there is a problem with an argument to an expression."""
@@ -0,0 +1,89 @@
1
+ # pylint: disable=too-few-public-methods
2
+
3
+ """ JSON typing classes and functions """
4
+
5
+ from __future__ import annotations
6
+ from abc import ABC
7
+ import warnings
8
+
9
+ from typing import Any
10
+
11
+ PyJsonType = int | float | bool | str | list | dict
12
+
13
+
14
+ class JsonType(ABC):
15
+ """The abstract base class for JSON types"""
16
+
17
+ typename = "UNDEFINED"
18
+
19
+ def __init__(self, val):
20
+ self.val = val
21
+
22
+ def __str__(self):
23
+ return f"[{self.typename}]{self.val}"
24
+
25
+ def prepare(self) -> PyJsonType:
26
+ """Prepares the type for JSON serialization"""
27
+ raise NotImplementedError("class has not defined prepare()")
28
+
29
+
30
+ class JsonNumber(JsonType):
31
+ """A JSON Number type"""
32
+
33
+ typename = "Number"
34
+
35
+ def prepare(self):
36
+ return int(self.val) if isinstance(self.val, int) else float(self.val)
37
+
38
+
39
+ class JsonBool(JsonType):
40
+ """A JSON Boolean type"""
41
+
42
+ typename = "Boolean"
43
+
44
+ def prepare(self) -> bool:
45
+ return bool(self.val)
46
+
47
+
48
+ class JsonStr(JsonType):
49
+ """A JSON String type"""
50
+
51
+ typename = "String"
52
+
53
+ def prepare(self) -> str:
54
+ return str(self.val)
55
+
56
+
57
+ class JsonArray(JsonType):
58
+ """A JSON Array type"""
59
+
60
+ typename = "Array"
61
+
62
+ def prepare(self) -> list:
63
+ return list(self.val)
64
+
65
+
66
+ class JsonObj(JsonType):
67
+ """A JSON Object type"""
68
+
69
+ typename = "Object"
70
+
71
+ def prepare(self) -> dict:
72
+ return dict(self.val)
73
+
74
+
75
+ def deduce_type(val: Any) -> JsonType:
76
+ """Given a value, try to deduce the json datatype. Default is string."""
77
+ if isinstance(val, bool):
78
+ return JsonBool(val)
79
+ if isinstance(val, (float, int)):
80
+ return JsonNumber(val)
81
+ if isinstance(val, list):
82
+ return JsonArray(val)
83
+ if isinstance(val, dict):
84
+ return JsonObj(val)
85
+ if not isinstance(val, str):
86
+ warnings.warn(
87
+ f"cannot deduce type of {val} ({type(val)}); assuming string", UserWarning
88
+ )
89
+ return JsonStr(str(val))
@@ -0,0 +1,90 @@
1
+ # Copyright 2020 Lawrence Livermore National Security, LLC and other CLIPPy Project Developers.
2
+ # See the top-level COPYRIGHT file for details.
3
+ #
4
+ # SPDX-License-Identifier: MIT
5
+
6
+ """ Holds the expression building code. """
7
+
8
+ from __future__ import annotations
9
+
10
+ from dataclasses import dataclass
11
+ from typing import Callable
12
+ from .classes import Entity
13
+
14
+
15
+ @dataclass
16
+ class Operation(Entity):
17
+ """A JSONLogic operation"""
18
+
19
+ op: str
20
+ arity: int | None # None means unlimited
21
+
22
+ def __repr__(self):
23
+ return self.op
24
+
25
+ def __str__(self):
26
+ return str(self.op)
27
+
28
+
29
+ _jl_lt = Operation("<", 2)
30
+ _jl_le = Operation("<=", 2)
31
+ _jl_eq = Operation("==", 2)
32
+ _jl_ne = Operation("!=", 2)
33
+ _jl_gt = Operation(">", 2)
34
+ _jl_ge = Operation(">=", 2)
35
+ _jl_add = Operation("+", 2)
36
+ _jl_sub = Operation("-", 2)
37
+ _jl_mul = Operation("*", 2)
38
+ _jl_matmul = Operation("@", 2)
39
+ _jl_truediv = Operation("/", 2)
40
+ _jl_floordiv = Operation("//", 2)
41
+ _jl_mod = Operation("%", 2)
42
+ _jl_divmod = Operation("divmod", 2)
43
+ _jl_pow = Operation("**", 2)
44
+ _jl_lshift = Operation("<<", 2)
45
+ _jl_rshift = Operation(">>", 2)
46
+ _jl_and = Operation("and", 2) # single &
47
+ _jl_not = Operation("not", 1)
48
+ _jl_xor = Operation("^", 2)
49
+ _jl_or = Operation("or", 2) # single |
50
+ # _jl_contains = Operation():
51
+ # raise NotImplementedError("syntax a in b is not supported. Use b.contains(a) instead.")
52
+ # # will not work when written as "x in set",
53
+ # # b/c the in-operator always converts the result to bool
54
+ # # https://stackoverflow.com/questions/38542543/functionality-of-python-in-vs-contains
55
+ # # https://bugs.python.org/issue16011
56
+ # # return Expression("in", o, self)
57
+
58
+ # # to be modeled after Pandas' str.contains
59
+ _jl_contains = Operation("in", 2)
60
+ _jl_regex = Operation("regex", 2)
61
+
62
+ # string and array concatenation
63
+ _jl_cat = Operation("cat", None)
64
+
65
+ jl_operations: dict[str, Operation | Callable[..., Operation]] = {
66
+ "__lt__": _jl_lt,
67
+ "__le__": _jl_le,
68
+ "__eq__": _jl_eq,
69
+ "__ne__": _jl_ne,
70
+ "__gt__": _jl_gt,
71
+ "__ge__": _jl_ge,
72
+ "__add__": _jl_add,
73
+ "__sub__": _jl_sub,
74
+ "__mul__": _jl_mul,
75
+ "__matmul__": _jl_matmul,
76
+ "__truediv__": _jl_truediv,
77
+ "__floordiv__": _jl_floordiv,
78
+ "__mod__": _jl_mod,
79
+ "__divmod__": _jl_divmod,
80
+ "__pow__": _jl_pow,
81
+ "__lshift__": _jl_lshift,
82
+ "__rshift__": _jl_rshift,
83
+ "__and__": _jl_and,
84
+ "__xor__": _jl_xor,
85
+ "__or__": _jl_or,
86
+ "__invert__": _jl_not,
87
+ "contains": _jl_contains,
88
+ "regex": _jl_regex,
89
+ "cat": _jl_cat,
90
+ }
@@ -0,0 +1,42 @@
1
+ Metadata-Version: 2.1
2
+ Name: jsonlogic-py
3
+ Version: 0.1
4
+ Summary: A Python package that emits JSON Logic
5
+ Author: Peter Pirkelbauer, Seth Bromberger
6
+ Project-URL: Homepage, https://github.com/MetallData/jsonlogic
7
+ Project-URL: Issues, https://github.com/MetallData/jsonlogic/issues
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Intended Audience :: Science/Research
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: OS Independent
13
+ Requires-Python: >=3.10
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE.txt
16
+ Provides-Extra: dev
17
+ Requires-Dist: pytest; extra == "dev"
18
+ Requires-Dist: coverage; extra == "dev"
19
+ Requires-Dist: json-logic-qubit; extra == "dev"
20
+ Requires-Dist: flake8; extra == "dev"
21
+ Requires-Dist: mypy; extra == "dev"
22
+
23
+ ## jsonlogic.py - JSON Logic expression generator
24
+
25
+ This package provides functionality to express JSON Logic using standard Python datastructures.
26
+
27
+ An example:
28
+
29
+ ```python
30
+ >>> from jsonlogic import Variable
31
+ >>> v1 = Variable('var1')
32
+ >>> v2 = Variable('var2')
33
+ >>> e = (v1 < v2)
34
+ >>> print(e)
35
+ {"<": [{"var": "var1"}, {"var": "var2"}]}
36
+ >>> print (v1 < 3)
37
+ {"<": [{"var": "var1"}, 3]}
38
+ >>> print ( (v1 < 3) & (v1 > v2))
39
+ {"and": [{"<": [{"var": "var1"}, 3]}, {">": [{"var": "var1"}, {"var": "var2"}]}]}
40
+ >>> print ( (v1 < 3) & ~(v1 > v2))) # ~ is "not"
41
+ {"and": [{"<": [{"var": "v1"}, 3]}, {"not": [{">": [{"var": "v1"}, {"var": "v2"}]}]}]}
42
+ ```
@@ -0,0 +1,18 @@
1
+ LICENSE.txt
2
+ README.md
3
+ pyproject.toml
4
+ requirements-dev.txt
5
+ requirements.txt
6
+ src/jsonlogic/__init__.py
7
+ src/jsonlogic/classes.py
8
+ src/jsonlogic/errors.py
9
+ src/jsonlogic/jsontypes.py
10
+ src/jsonlogic/operations.py
11
+ src/jsonlogic_py.egg-info/PKG-INFO
12
+ src/jsonlogic_py.egg-info/SOURCES.txt
13
+ src/jsonlogic_py.egg-info/dependency_links.txt
14
+ src/jsonlogic_py.egg-info/requires.txt
15
+ src/jsonlogic_py.egg-info/top_level.txt
16
+ test/test_classes.py
17
+ test/test_jsonlogic.py
18
+ test/test_types.py
@@ -0,0 +1,7 @@
1
+
2
+ [dev]
3
+ pytest
4
+ coverage
5
+ json-logic-qubit
6
+ flake8
7
+ mypy
@@ -0,0 +1 @@
1
+ jsonlogic
@@ -0,0 +1,66 @@
1
+ import pytest
2
+ import sys
3
+
4
+ sys.path.append("src")
5
+
6
+ from jsonlogic import Expression, Variable, Literal, Operand, Operation
7
+ from jsonlogic.classes import Entity
8
+ from dataclasses import dataclass
9
+
10
+
11
+ @pytest.fixture
12
+ def op1():
13
+ return Operation("op1", 1)
14
+
15
+
16
+ @pytest.fixture
17
+ def op2():
18
+ return Operation("op2", 2)
19
+
20
+
21
+ @pytest.fixture
22
+ def v1():
23
+ return Variable("var1", "docstring for var1")
24
+
25
+
26
+ @pytest.fixture
27
+ def v2():
28
+ return Variable("var2")
29
+
30
+
31
+ @pytest.fixture
32
+ def l1():
33
+ return Literal(5, "docstring for literal")
34
+
35
+
36
+ @pytest.fixture
37
+ def l2():
38
+ return Literal("stringliteral")
39
+
40
+
41
+ def test_expression(op1, op2, v1, v2, l1, l2):
42
+ e = Expression(op1, v1)
43
+ assert e.to_json() == str(e) == '{"op1": [{"var": "var1"}]}'
44
+
45
+ e = Expression(op2, v1, v2)
46
+ assert e.to_json() == str(e) == '{"op2": [{"var": "var1"}, {"var": "var2"}]}'
47
+
48
+ e = Expression(op2, v1, l1)
49
+ assert e.to_json() == str(e) == '{"op2": [{"var": "var1"}, 5]}'
50
+
51
+
52
+ def test_operations():
53
+ o = Operation("foo", 1)
54
+ assert repr(o) == "foo"
55
+ assert str(o) == "foo"
56
+
57
+
58
+ def test_literals(l1, l2):
59
+ assert repr(l1) == str(l1) == "[Number]5"
60
+ assert repr(l2) == str(l2) == "[String]stringliteral"
61
+
62
+
63
+ def test_operand():
64
+ o = Operand()
65
+ with pytest.raises(NotImplementedError):
66
+ o.prepare()
@@ -0,0 +1,126 @@
1
+ import pytest
2
+ import sys
3
+
4
+ sys.path.append("src")
5
+
6
+ from json_logic import jsonLogic
7
+
8
+ import jsonlogic as jl
9
+ from jsonlogic.errors import JsonLogicArgumentError
10
+ from dataclasses import dataclass
11
+
12
+
13
+ @dataclass
14
+ class TV(dict):
15
+
16
+ def __init__(self, *args, **kwargs):
17
+ super().__init__(*args, **kwargs)
18
+
19
+ def __getitem__(self, val):
20
+ return self.get(val)[1]
21
+
22
+ def getname(self, val):
23
+ return self.get(val)[0]
24
+
25
+
26
+ @pytest.fixture
27
+ def _i():
28
+ return TV({n: (f"var{n}", jl.Variable(f"var{n}")) for n in range(1, 5)})
29
+
30
+
31
+ @pytest.fixture
32
+ def _f():
33
+ fs = [1.1, 2.0, 2.1, 2.2, 3.3, 4.4]
34
+ return TV({n: (f"var{int(n*10)}", jl.Variable(f"var{int(n*10)}")) for n in fs})
35
+
36
+
37
+ @pytest.fixture
38
+ def _s():
39
+ ss = ["a", "b", "c", "d", "e"]
40
+ return TV({n: (f"var{n}", jl.Variable(f"var{n}")) for n in ss})
41
+
42
+
43
+ def assert_op(e: jl.Expression, d):
44
+ assert jsonLogic(e.prepare(), d)
45
+
46
+
47
+ def test_lt_gt_ne(_s, _i, _f):
48
+ test_int = [
49
+ _i[1] < _i[2],
50
+ _i[1] <= _i[1],
51
+ _i[1] <= _i[2],
52
+ _i[2] > _i[1],
53
+ _i[2] >= _i[1],
54
+ _i[2] >= _i[2],
55
+ _i[1] != _i[2],
56
+ ]
57
+ test_float = [
58
+ _f[1.1] < _f[2.0],
59
+ _f[2.0] <= _f[2.0],
60
+ _f[2.0] <= _f[2.1],
61
+ _f[4.4] >= _f[2.1],
62
+ _f[4.4] != _f[2.2],
63
+ ]
64
+ test_str = {
65
+ _s["a"] < _s["b"],
66
+ _s["a"] <= _s["a"],
67
+ _s["b"] > _s["a"],
68
+ _s["b"] >= _s["b"],
69
+ _s["a"] != _s["b"],
70
+ }
71
+
72
+ for t in test_int:
73
+ d = {_i.getname(k): k for k in _i}
74
+ assert len(d) > 0
75
+ assert_op(t, d)
76
+
77
+ for t in test_float:
78
+ d = {_f.getname(k): k for k in _f}
79
+ assert_op(t, d)
80
+
81
+ for t in test_str:
82
+ d = {_s.getname(k): k for k in _s}
83
+ assert_op(t, d)
84
+
85
+
86
+ def test_eq_add_mul_truediv_floordiv(_i, _f):
87
+ test_int = [
88
+ _i[1] + 1 == _i[2],
89
+ _i[2] + 2 == _i[4],
90
+ _i[2] + _i[2] == _i[4],
91
+ _i[2] * 2 == _i[4],
92
+ _i[2] * _i[2] == _i[4],
93
+ ]
94
+ test_float = [
95
+ _f[1.1] + 1.1 == _f[2.2],
96
+ _f[2.2] + 2.2 == _f[4.4],
97
+ _f[2.2] + _f[2.2] == _f[4.4],
98
+ _f[2.2] * 2 == _f[4.4],
99
+ _f[2.2] * _f[2.0] == _f[4.4],
100
+ ]
101
+
102
+ for t in test_int:
103
+ d = {_i.getname(k): k for k in _i}
104
+ assert_op(t, d)
105
+
106
+ for t in test_float:
107
+ d = {_f.getname(k): k for k in _f}
108
+ assert_op(t, d)
109
+
110
+
111
+ def test_bad_args():
112
+ with pytest.raises(JsonLogicArgumentError):
113
+ o = jl.Operation("foo", 1)
114
+ v = jl.Variable("var1")
115
+ jl.Expression(o, v, 1)
116
+
117
+
118
+ def test_operations():
119
+ o = jl.Operation("foo", 1)
120
+ assert repr(o) == "foo"
121
+ assert str(o) == "foo"
122
+
123
+
124
+ # def test_float_eq_add_mul_truediv_floordiv(var1, var2, var4, var5, data_float):
125
+ # for t in tests:
126
+ # assert_op(t, data_float)
@@ -0,0 +1,85 @@
1
+ import warnings
2
+ import sys
3
+ import pytest
4
+
5
+ sys.path.append('src')
6
+ from jsonlogic.jsontypes import JsonArray, JsonBool, JsonNumber, JsonStr, JsonObj, deduce_type, PyJsonType, JsonType
7
+
8
+
9
+ def test_bool():
10
+ b = JsonBool(True)
11
+ assert b.typename == 'Boolean'
12
+ assert b.val is True
13
+ assert isinstance(b.val, bool)
14
+ assert str(b) == "[Boolean]True"
15
+ assert b.prepare() is True
16
+
17
+
18
+ def test_int():
19
+ b = JsonNumber(10)
20
+ assert b.typename == 'Number'
21
+ assert b.val == 10
22
+ assert isinstance(b.val, int)
23
+ assert str(b) == "[Number]10"
24
+ assert b.prepare() == 10
25
+
26
+
27
+ def test_float():
28
+ b = JsonNumber(10.1)
29
+ assert b.typename == 'Number'
30
+ assert b.val == 10.1
31
+ assert isinstance(b.val, float)
32
+ assert str(b) == "[Number]10.1"
33
+ assert b.prepare() == 10.1
34
+
35
+
36
+ def test_dict():
37
+ d = {'a': 1, 'b': 2}
38
+ b = JsonObj(d)
39
+ assert b.typename == 'Object'
40
+ assert b.val == d
41
+ assert isinstance(b.val, dict)
42
+ assert str(b).startswith("[Object]")
43
+ assert b.prepare() == d
44
+
45
+
46
+ def test_str():
47
+ b = JsonStr("hello")
48
+ assert b.typename == 'String'
49
+ assert b.val == "hello"
50
+ assert isinstance(b.val, str)
51
+ assert str(b).startswith("[String]")
52
+ assert b.prepare() == "hello"
53
+
54
+
55
+ def test_list():
56
+ d = [1, 2, 3, 4, 5]
57
+ b = JsonArray(d)
58
+ assert b.typename == 'Array'
59
+ assert b.val == d
60
+ assert isinstance(b.val, list)
61
+ assert str(b).startswith("[Array]")
62
+ assert b.prepare() == d
63
+
64
+
65
+ def test_deduce():
66
+ assert isinstance(deduce_type(True), JsonBool)
67
+ assert isinstance(deduce_type(10), JsonNumber)
68
+ assert isinstance(deduce_type(10.1), JsonNumber)
69
+ assert isinstance(deduce_type({'a': 1}), JsonObj)
70
+ assert isinstance(deduce_type(""), JsonStr)
71
+ assert isinstance(deduce_type([]), JsonArray)
72
+
73
+ with pytest.warns(UserWarning, match=r"cannot deduce type.*"):
74
+ assert isinstance(deduce_type(complex(1, 2)), JsonStr)
75
+
76
+
77
+ def test_jsontype():
78
+ class TestType(JsonType):
79
+ pass
80
+
81
+ tt = TestType(10)
82
+ assert tt.typename == "UNDEFINED"
83
+ assert str(tt) == "[UNDEFINED]10"
84
+ with pytest.raises(NotImplementedError):
85
+ tt.prepare()