Skip to content
Merged
Binary file added .coverage
Binary file not shown.
30 changes: 30 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: Test

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

permissions:
contents: read

jobs:
build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- name: Set up Python 3.10
uses: actions/setup-python@v3
with:
python-version: "3.10"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install .
pip install .[dev]
- name: Test with pytest
run: |
python -m pytest tests/ --cov --cov-report term-missing
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,7 @@ __pycache__

# build metadata / files
*build
python_thingset.egg-info
*egg-info

# test metadata
.coverage
11 changes: 8 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ dependencies = [

[project.optional-dependencies]
dev = [
"mkdocs==1.6.1",
"mkdocs-material==9.5.49",
"mkdocstrings-python==1.13.0"
"pytest==8.3.5",
"pytest-cov==5.0.0",
"ruff==0.6.7"
]

[project.urls]
Expand All @@ -33,3 +33,8 @@ documentation = "https://gitlab.com/Brill-Power/python-thingset/"

[project.scripts]
thingset = "python_thingset.cli:run_cli"

[tool.coverage.run]
omit = [
"*/tests/*",
]
2 changes: 1 addition & 1 deletion python_thingset/backends/serial.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ def _send(self, data: bytes, _: Union[int, None]) -> None:
self._serial.send(data)

def _recv(self) -> bytes:
self._serial.get_message()
return self._serial.get_message()

@property
def port(self) -> str:
Expand Down
4 changes: 2 additions & 2 deletions python_thingset/encoders/binary.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def __init__(self):
18 41 # CBOR uint: 0x41 (object ID)
"""

def encode_fetch(self, parent_id: int, value_ids: List[int]) -> bytes:
def encode_fetch(self, parent_id: int, value_ids: List[Union[int, None]]) -> bytes:
req = bytearray()
req.append(ThingSetRequest.FETCH)
req += cbor2.dumps(parent_id, canonical=True)
Expand All @@ -43,7 +43,7 @@ def encode_fetch(self, parent_id: int, value_ids: List[int]) -> bytes:
def encode_get(self, value_id: int) -> bytes:
return bytes([ThingSetRequest.GET] + list(cbor2.dumps(value_id)))

def encode_exec(self, value_id: int, args: Union[Any, None]) -> bytes:
def encode_exec(self, value_id: int, args: List[Union[Any, None]]) -> bytes:
p_args = list()

for a in args:
Expand Down
25 changes: 7 additions & 18 deletions python_thingset/encoders/text.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class ThingSetTextEncoder(object):
def __init__(self):
pass

def encode_fetch(self, parent_id: int, ids: List[str]) -> bytes:
def encode_fetch(self, parent_id: str, ids: List[str]) -> bytes:
children = "null"

if len(ids) > 0:
Expand All @@ -26,25 +26,19 @@ def encode_fetch(self, parent_id: int, ids: List[str]) -> bytes:
def encode_get(self, value_id: str) -> bytes:
return f"thingset ?{value_id}\n".encode()

def encode_exec(self, value_id: str, args: Union[Any, None]) -> bytes:
def encode_exec(self, value_id: str, args: List[Union[Any, None]]) -> bytes:
"""properly format strings for transmission, add args to stringified list"""
processed_args = "["

""" leave numeric values as is, surround strings with escape chars """
for a in args:
try:
int(a)
if isinstance(a, int):
processed_args += f"{a},"
continue
except ValueError:
pass

try:
float(a)
if isinstance(a, float):
processed_args += f"{a},"
continue
except ValueError:
pass

processed_args += f'\\"{a}\\",'

Expand All @@ -58,16 +52,11 @@ def encode_update(self, parent_id: None, value_id: str, value: Any) -> bytes:

val = None

try:
if isinstance(value, int):
val = int(value)
except ValueError:
pass

if val is None:
try:
val = float(value)
except ValueError:
pass
if isinstance(value, float):
val = float(value)

if val is None:
val = f'\\"{value}\\"'
Expand Down
39 changes: 39 additions & 0 deletions tests/encoders/binary/test_enc_bin_exec.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from python_thingset.encoders import ThingSetBinaryEncoder


encoder = ThingSetBinaryEncoder()


def test_exec_no_args():
encoded = encoder.encode_exec(0xF09, [])
assert encoded == b"\x02\x19\x0f\t\x80"


def test_exec_one_int():
encoded = encoder.encode_exec(0xF09, [2])
assert encoded == b"\x02\x19\x0f\t\x81\x02"


def test_exec_one_float():
encoded = encoder.encode_exec(0xF09, [3.14])
assert encoded == b"\x02\x19\x0f\t\x81\xfa@H\xf5\xc3"


def test_exec_one_str():
encoded = encoder.encode_exec(0xF09, ["hello"])
assert encoded == b"\x02\x19\x0f\t\x81ehello"


def test_exec_one_bool_true():
encoded = encoder.encode_exec(0xF09, ["true"])
assert encoded == b"\x02\x19\x0f\t\x81\xf5"


def test_exec_one_bool_false():
encoded = encoder.encode_exec(0xF09, ["false"])
assert encoded == b"\x02\x19\x0f\t\x81\xf4"


def test_exec_multiple_args():
encoded = encoder.encode_exec(0xF09, ["false", 5, "age", 7.89, "true"])
assert encoded == b"\x02\x19\x0f\t\x85\xf4\x05cage\xfa@\xfcz\xe1\xf5"
19 changes: 19 additions & 0 deletions tests/encoders/binary/test_enc_bin_fetch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from python_thingset.encoders import ThingSetBinaryEncoder


encoder = ThingSetBinaryEncoder()


def test_fetch_no_children():
encoded = encoder.encode_fetch(0xF01, [])
assert encoded == b"\x05\x19\x0f\x01\xf6"


def test_fetch_one_child():
encoded = encoder.encode_fetch(0xF, [0xF01])
assert encoded == b"\x05\x0f\x81\x19\x0f\x01"


def test_fetch_two_children():
encoded = encoder.encode_fetch(0xF, [0xF01, 0xF02])
assert encoded == b"\x05\x0f\x82\x19\x0f\x01\x19\x0f\x02"
9 changes: 9 additions & 0 deletions tests/encoders/binary/test_enc_bin_get.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from python_thingset.encoders import ThingSetBinaryEncoder


encoder = ThingSetBinaryEncoder()


def test_get():
encoded = encoder.encode_get(0xF01)
assert encoded == b"\x01\x19\x0f\x01"
9 changes: 9 additions & 0 deletions tests/encoders/binary/test_enc_bin_get_paths.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from python_thingset.encoders import ThingSetBinaryEncoder


encoder = ThingSetBinaryEncoder()


def test_enc_get_paths():
encoded = encoder.encode_get_path(0xF)
assert encoded == b"\x05\x17\x81\x0f"
29 changes: 29 additions & 0 deletions tests/encoders/binary/test_enc_bin_update.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from python_thingset.encoders import ThingSetBinaryEncoder


encoder = ThingSetBinaryEncoder()


def test_update_int():
encoded = encoder.encode_update(0x0, 0x4F, 1)
assert encoded == b"\x07\x00\xa1\x18O\x01"


def test_update_float():
encoded = encoder.encode_update(0x0, 0x4F, 3.14)
assert encoded == b"\x07\x00\xa1\x18O\xfa@H\xf5\xc3"


def test_update_str():
encoded = encoder.encode_update(0x0, 0x4F, "hello")
assert encoded == b"\x07\x00\xa1\x18Oehello"


def test_update_bool_true():
encoded = encoder.encode_update(0x0, 0x4F, "true")
assert encoded == b"\x07\x00\xa1\x18O\xf5"


def test_update_bool_false():
encoded = encoder.encode_update(0x0, 0x4F, "false")
assert encoded == b"\x07\x00\xa1\x18O\xf4"
29 changes: 29 additions & 0 deletions tests/encoders/text/test_enc_txt_exec.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from python_thingset.encoders import ThingSetTextEncoder


encoder = ThingSetTextEncoder()


def test_exec_no_args():
encoded = encoder.encode_exec("Func", [])
assert encoded == "thingset !Func []\n".encode()


def test_exec_int_arg():
encoded = encoder.encode_exec("Func", [7])
assert encoded == "thingset !Func [7,]\n".encode()


def test_exec_float_arg():
encoded = encoder.encode_exec("Func", [3.14])
assert encoded == "thingset !Func [3.14,]\n".encode()


def test_exec_str_arg():
encoded = encoder.encode_exec("Func", ["a_text-arg"])
assert encoded == """thingset !Func [\\"a_text-arg\\",]\n""".encode()


def test_exec_one_of_each_arg():
encoded = encoder.encode_exec("Func", [1, 2.34, "moretext"])
assert encoded == """thingset !Func [1,2.34,\\"moretext\\",]\n""".encode()
24 changes: 24 additions & 0 deletions tests/encoders/text/test_enc_txt_fetch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from python_thingset.encoders import ThingSetTextEncoder


encoder = ThingSetTextEncoder()


def test_fetch_root():
encoded = encoder.encode_fetch("", [])
assert encoded == "thingset ? null\n".encode()


def test_fetch_root_one_child():
encoded = encoder.encode_fetch("", ["One"])
assert encoded == """thingset ? [\\"One\\",]\n""".encode()


def test_fetch_root_two_children():
encoded = encoder.encode_fetch("", ["One", "Two"])
assert encoded == """thingset ? [\\"One\\",\\"Two\\",]\n""".encode()


def test_fetch_not_root_one_child():
encoded = encoder.encode_fetch("NotRoot", ["One"])
assert encoded == """thingset ?NotRoot [\\"One\\",]\n""".encode()
19 changes: 19 additions & 0 deletions tests/encoders/text/test_enc_txt_get.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from python_thingset.encoders import ThingSetTextEncoder


encoder = ThingSetTextEncoder()


def test_get_root():
encoded = encoder.encode_get("")
assert encoded == "thingset ?\n".encode()


def test_get_depth_one():
encoded = encoder.encode_get("One")
assert encoded == "thingset ?One\n".encode()


def test_get_depth_two():
encoded = encoder.encode_get("One/Two")
assert encoded == "thingset ?One/Two\n".encode()
29 changes: 29 additions & 0 deletions tests/encoders/text/test_enc_txt_update.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from python_thingset.encoders import ThingSetTextEncoder


encoder = ThingSetTextEncoder()


def test_update_value_at_root_int():
encoded = encoder.encode_update(None, "Value", [1])
assert encoded == """thingset = {\\"Value\\":1}\n""".encode()


def test_update_value_at_root_float():
encoded = encoder.encode_update(None, "Value", [3.14])
assert encoded == """thingset = {\\"Value\\":3.14}\n""".encode()


def test_update_value_at_root_str():
encoded = encoder.encode_update(None, "Value", ["sometext"])
assert encoded == """thingset = {\\"Value\\":\\"sometext\\"}\n""".encode()


def test_update_value_at_depth_one():
encoded = encoder.encode_update(None, "One/Value", [1])
assert encoded == """thingset =One {\\"Value\\":1}\n""".encode()


def test_update_value_at_depth_two():
encoded = encoder.encode_update(None, "One/Two/Value", [1])
assert encoded == """thingset =One/Two {\\"Value\\":1}\n""".encode()