From bc74eb25e4efcb1fd0fd6d5dbd3b87c0d6de99c7 Mon Sep 17 00:00:00 2001 From: bruno-f-cruz <7049351+bruno-f-cruz@users.noreply.github.com> Date: Thu, 10 Jul 2025 12:05:07 -0700 Subject: [PATCH 1/3] Add tests for flow-style syntax --- tests/data/device.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/data/device.yml b/tests/data/device.yml index 636a6cf..523babb 100644 --- a/tests/data/device.yml +++ b/tests/data/device.yml @@ -48,6 +48,20 @@ bitMasks: DI1: 0x2 DI2: 0x4 DI3: 0x8 + State: + description: Specifies the state of the digital input lines. + bits: + False: + value: 0x0 + description: The state is False + True: + value: 0x1 + description: The state is True + StateFlowStyle: + description: Specifies the state of the digital input lines. + bits: + False: {value: 0x0, description: The state is False} + True: {value: 0x1, description: The state is True} groupMasks: InputMode: description: Specifies when the device reports the state of digital input lines. From 279cecafe3db0772e1e6a7eb162deec08fbf4b9e Mon Sep 17 00:00:00 2001 From: bruno-f-cruz <7049351+bruno-f-cruz@users.noreply.github.com> Date: Thu, 10 Jul 2025 12:05:28 -0700 Subject: [PATCH 2/3] Favor pyyaml as the yml file parser --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index c8fcaac..e778e21 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,7 @@ dynamic = ["version"] license = "MIT" dependencies = [ - "pydantic-yaml", + "pyyaml", "pandas" ] From ec9c2a92decff41b03297f56053bd24eaf685af4 Mon Sep 17 00:00:00 2001 From: bruno-f-cruz <7049351+bruno-f-cruz@users.noreply.github.com> Date: Thu, 10 Jul 2025 12:05:58 -0700 Subject: [PATCH 3/3] Refactor model parsing to use pyyaml --- harp/schema.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/harp/schema.py b/harp/schema.py index d60b797..a5bb1f1 100644 --- a/harp/schema.py +++ b/harp/schema.py @@ -2,18 +2,30 @@ from os import PathLike from typing import TextIO, Union -from pydantic_yaml import parse_yaml_raw_as +import yaml from harp.model import Model, Registers +def _convert_keys_to_strings(obj): + """Recursively converts all dictionary keys to strings. + This is necessary since pydantic deserialization from python objects + seems to expect keys to always be strings.""" + if isinstance(obj, dict): + return {str(k): _convert_keys_to_strings(v) for k, v in obj.items()} + elif isinstance(obj, list): + return [_convert_keys_to_strings(i) for i in obj] + return obj + + def _read_common_registers() -> Registers: if __package__ is None: raise ValueError("__package__ is None: unable to read common registers") file = resources.files(__package__) / "common.yml" with file.open("r") as fileIO: - return parse_yaml_raw_as(Registers, fileIO.read()) + regs_raw = _convert_keys_to_strings(yaml.safe_load(fileIO.read())) + return Registers.model_validate(regs_raw) def read_schema(file: Union[str, PathLike, TextIO], include_common_registers: bool = True) -> Model: @@ -37,7 +49,8 @@ def read_schema(file: Union[str, PathLike, TextIO], include_common_registers: bo with open(file) as fileIO: return read_schema(fileIO) else: - schema = parse_yaml_raw_as(Model, file.read()) + schema_raw = _convert_keys_to_strings(yaml.safe_load(file.read())) + schema = Model.model_validate(schema_raw) if "WhoAmI" not in schema.registers and include_common_registers: common = _read_common_registers() schema.registers = dict(common.registers, **schema.registers)