Skip to content

Fix/unit duplication check - #484

Merged
erikbosch merged 3 commits into
COVESA:masterfrom
jdacoello:fix/unit-duplication-check
Feb 10, 2026
Merged

Fix/unit duplication check#484
erikbosch merged 3 commits into
COVESA:masterfrom
jdacoello:fix/unit-duplication-check

Conversation

@jdacoello

Copy link
Copy Markdown
Contributor

Solves issue #483 . It does solve it at the vss-tools level, which means that the validation occurs for every exporter.

jdacoello and others added 2 commits February 5, 2026 10:37
…esponding tests

Signed-off-by: JD Alvarez <daniel.alvarez-coello@bmwgroup.com>
…dev0

Signed-off-by: JD Alvarez <8550265+jdacoello@users.noreply.github.com>
Comment thread src/vss_tools/units_quantities.py Outdated
MalformedDictException: If duplicate unit descriptions are found within a quantity
"""
# Group units by quantity and track unit descriptions
quantity_unit_descriptions: dict[str, dict[str, str]] = {}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

description was a bit misleading for me. I see it as a a long version of the key, kind of an alias. But not sure what the initial intention was behind having the embedded "unit" at all

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems to be a label of the unit.

Comment thread src/vss_tools/units_quantities.py Outdated
if not unit_description:
continue

if quantity not in quantity_unit_descriptions:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't quite get why it needs to be only unique within one quantity since you can either use the key or the unit to reference a unit from the model. Therefore it needs to be globally unique. Therefore this can be simplified:

def check_unique_units(units: dict[str, VSSUnit]) -> None:
    # <unit>: <first-key-using-it>
    unit_defs: dict[str, str] = {}
    for key, value in units.items():
        unit = value.unit
        if not unit:
            continue
        if unit in unit_defs:
            log.error(f"Duplicated unit: '{value.unit}'. Used by: {[unit_defs[unit], key]}")
            raise DuplicatedUnitException()
        unit_defs[unit] = key

Which will cause a log error:

ERROR    Duplicated unit: 'kilometers per hour'. Used by: ['km/h', 'km/x']

units.yaml

km/h:
  unit: kilometers per hour
  definition: Velocity
  quantity: velocity
  allowed-datatypes:
    - numeric

km/x:
  unit: kilometers per hour
  definition: Velocity
  quantity: velocity
  allowed-datatypes:
    - numeric

@sschleemilch sschleemilch Feb 6, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While we are at it, we probably should improve the raising and logging. We should only raise and put the logging statement into it I guess:

diff --git i/src/vss_tools/units_quantities.py w/src/vss_tools/units_quantities.py
index 3d2ba39..3cae468 100644
--- i/src/vss_tools/units_quantities.py
+++ w/src/vss_tools/units_quantities.py
@@ -18,6 +18,10 @@ class MalformedDictException(Exception):
     pass
 
 
+class DuplicatedUnitException(Exception):
+    pass
+
+
 def load_units_or_quantities(
     files: list[Path], class_type: type[VSSUnit | VSSQuantity]
 ) -> dict[str, VSSUnit | VSSQuantity]:
@@ -30,8 +34,7 @@ def load_units_or_quantities(
         log.info(f"Loaded '{class_type.__name__}', file={file.absolute()}, elements={len(content)}")
         for k, v in content.items():
             if v is None:
-                log.error(f"'{class_type.__name__}', '{k}' is 'None'")
-                raise MalformedDictException()
+                raise MalformedDictException(f"'{class_type.__name__}', '{k}' is 'None'")
             overwrite = False
             if k in data:
                 overwrite = True
@@ -51,8 +54,22 @@ def load_units_or_quantities(
 
 
 def load_units(unit_files: list[Path]) -> dict[str, VSSUnit]:
-    return load_units_or_quantities(unit_files, VSSUnit)  # type: ignore[return-value]
+    units: dict[str, VSSUnit] = load_units_or_quantities(unit_files, VSSUnit)  # type: ignore[return-value]
+    check_unique_units(units)
+    return units
 
 
 def load_quantities(quantities: list[Path]) -> dict[str, VSSQuantity]:
     return load_units_or_quantities(quantities, VSSQuantity)  # type: ignore[return-value]
+
+
+def check_unique_units(units: dict[str, VSSUnit]) -> None:
+    # def: <first-key-using-it>
+    unit_defs: dict[str, str] = {}
+    for key, value in units.items():
+        unit = value.unit
+        if not unit:
+            continue
+        if unit in unit_defs:
+            raise DuplicatedUnitException(f"Duplicated unit: '{value.unit}'. Used by: {[unit_defs[unit], key]}")
+        unit_defs[unit] = key

And then in main catch those to post a last critical log before exiting:

diff --git i/src/vss_tools/main.py w/src/vss_tools/main.py
index ec47e13..02c9dbd 100644
--- i/src/vss_tools/main.py
+++ w/src/vss_tools/main.py
@@ -24,7 +24,7 @@ from vss_tools.model import (
 )
 from vss_tools.strict import StrictExceptions, StrictOption, load_strict_exceptions
 from vss_tools.tree import ModelValidationException, VSSNode, add_struct_schemas, build_tree
-from vss_tools.units_quantities import load_quantities, load_units
+from vss_tools.units_quantities import load_quantities, load_units, DuplicatedUnitException, MalformedDictException
 from vss_tools.vspec import InvalidSpecDuplicatedEntryException, InvalidSpecException, load_vspec
 
 
@@ -201,7 +201,7 @@ def get_trees(
         log.info(f"User defined extra attributes: {extended_attributes}")
     try:
         load_quantities_and_units(quantities, units, vspec.parent)
-    except ModelValidationException as e:
+    except (ModelValidationException, DuplicatedUnitException, MalformedDictException) as e:
         log.critical(e)
         exit(1)

That probably will be a bit nicer for the end user without a stack trace:
image

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Considered the comments and suggestions and updated the PR with the latest commit.

Change validation scope from per-quantity to global as units can be referenced by either key or description. Add dedicated exception type and tests for duplicate detection.

Signed-off-by: JD Alvarez <8550265+jdacoello@users.noreply.github.com>

@erikbosch erikbosch left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@erikbosch

Copy link
Copy Markdown
Collaborator

MoM: Merge

@erikbosch
erikbosch merged commit 70c3719 into COVESA:master Feb 10, 2026
5 checks passed
erikbosch added a commit to boschglobal/vehicle_signal_specification that referenced this pull request Feb 11, 2026
Based on COVESA/vss-tools#484 we now require unique unit names.
This gives problems here. We could alternatively remove the old mpg unit.

Signed-off-by: Erik Jaegervall <erik.jaegervall@se.bosch.com>
erikbosch added a commit to boschglobal/vehicle_signal_specification that referenced this pull request Feb 11, 2026
Based on COVESA/vss-tools#484 we now require unique unit names.
This gives problems here. We could alternatively remove the old mpg unit.

Signed-off-by: Erik Jaegervall <erik.jaegervall@se.bosch.com>
erikbosch added a commit to COVESA/vehicle_signal_specification that referenced this pull request Feb 12, 2026
Based on COVESA/vss-tools#484 we now require unique unit names.
This gives problems here. We could alternatively remove the old mpg unit.

Signed-off-by: Erik Jaegervall <erik.jaegervall@se.bosch.com>
ali-momin12 pushed a commit to ali-momin12/vehicle_signal_specification that referenced this pull request Mar 2, 2026
Based on COVESA/vss-tools#484 we now require unique unit names.
This gives problems here. We could alternatively remove the old mpg unit.

Signed-off-by: Erik Jaegervall <erik.jaegervall@se.bosch.com>
Signed-off-by: Ali Momin <ali_momin@jp.honda>
ali-momin12 pushed a commit to ali-momin12/vehicle_signal_specification that referenced this pull request Mar 3, 2026
Based on COVESA/vss-tools#484 we now require unique unit names.
This gives problems here. We could alternatively remove the old mpg unit.

Signed-off-by: Erik Jaegervall <erik.jaegervall@se.bosch.com>
Signed-off-by: Ali Momin <ali_momin@jp.honda>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants