mirror of
https://github.com/semantica-agi/semantica.git
synced 2026-08-29 04:26:20 +00:00
fix(normalize): accept unit aliases during conversion (#939)
Convert_units() was validating categories on raw input like "kg" or "ft" instead of the normalized unit name, so aliases got checked against a category list that only has canonical names in it. Any alias-based conversion that should've worked just raised ValidationError instead. Fixed by normalizing both units before the category check runs. Also added foot/yard/mile/gallon to the alias map - they already had conversion factors but weren't mapped to their canonical names, so they'd still have failed even after the above fix. Turned out there was a second bug hiding behind the first one: the category check defaults both sides to None, and None == None is True, so two aliases from different categories that neither resolved to a real category would silently pass instead of raising. kg -> ft would just return a number instead of erroring. Normalizing first fixes this too, since aliases now resolve to their actual categories and the mismatch gets caught. Added a regression test locking that second one down - kg->ft and gal->lb now raise ValidationError instead of silently converting. Fixes #931.
This commit is contained in:
@@ -370,8 +370,12 @@ class UnitConverter:
|
||||
Raises:
|
||||
ValidationError: If units are incompatible or not in same category
|
||||
"""
|
||||
from_unit = from_unit.lower()
|
||||
to_unit = to_unit.lower()
|
||||
# Normalize aliases before validating categories and looking up factors.
|
||||
# The public API documents abbreviations such as ``kg`` and ``km``;
|
||||
# validating those raw aliases against the canonical category lists
|
||||
# incorrectly rejected otherwise supported conversions.
|
||||
from_unit = self.normalize_unit(from_unit)
|
||||
to_unit = self.normalize_unit(to_unit)
|
||||
|
||||
# Validate units
|
||||
if not self.validate_units(from_unit, to_unit):
|
||||
@@ -401,8 +405,8 @@ class UnitConverter:
|
||||
Returns:
|
||||
bool: True if units are compatible (same category), False otherwise
|
||||
"""
|
||||
from_unit = from_unit.lower()
|
||||
to_unit = to_unit.lower()
|
||||
from_unit = self.normalize_unit(from_unit)
|
||||
to_unit = self.normalize_unit(to_unit)
|
||||
|
||||
# Check if both units exist
|
||||
if (
|
||||
@@ -498,6 +502,18 @@ class UnitConverter:
|
||||
"ml": "milliliter",
|
||||
"milliliter": "milliliter",
|
||||
"milliliters": "milliliter",
|
||||
"ft": "foot",
|
||||
"foot": "foot",
|
||||
"feet": "foot",
|
||||
"yd": "yard",
|
||||
"yard": "yard",
|
||||
"yards": "yard",
|
||||
"mi": "mile",
|
||||
"mile": "mile",
|
||||
"miles": "mile",
|
||||
"gal": "gallon",
|
||||
"gallon": "gallon",
|
||||
"gallons": "gallon",
|
||||
}
|
||||
|
||||
return unit_map.get(unit_lower, unit_lower)
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import unittest
|
||||
|
||||
from semantica.utils.exceptions import ValidationError
|
||||
from semantica.normalize.number_normalizer import (
|
||||
NumberNormalizer,
|
||||
UnitConverter,
|
||||
@@ -32,6 +34,19 @@ class TestUnitConverter(unittest.TestCase):
|
||||
# 1 kg = 1000 g
|
||||
self.assertEqual(self.converter.convert_units(1, "kg", "g"), 1000.0)
|
||||
|
||||
def test_convert_accepts_aliases_for_category_validation(self):
|
||||
# Aliases are part of the documented API, not just parsing syntax.
|
||||
self.assertEqual(self.converter.convert_units(1, "feet", "m"), 0.3048)
|
||||
self.assertEqual(self.converter.convert_units(1, "gal", "liter"), 3.78541)
|
||||
|
||||
def test_convert_rejects_mismatched_categories_even_for_aliases(self):
|
||||
# Both units normalize to canonical names first, so the category
|
||||
# check sees real categories and rejects cross-category conversions.
|
||||
with self.assertRaises(ValidationError):
|
||||
self.converter.convert_units(1, "kg", "ft")
|
||||
with self.assertRaises(ValidationError):
|
||||
self.converter.convert_units(1, "gal", "lb")
|
||||
|
||||
def test_normalize_unit(self):
|
||||
self.assertEqual(self.converter.normalize_unit("km"), "kilometer")
|
||||
self.assertEqual(self.converter.normalize_unit("kgs"), "kilogram")
|
||||
|
||||
Reference in New Issue
Block a user