Skip to content

Commit 96d9562

Browse files
authored
fix: check_units result-callable receives units, not dimensions (#110)
The @check_units(result=callable) form passed each argument's *dimension* to the result callable, but the returned value is validated by unit via _check_unit/has_same_unit. has_same_unit calls get_unit() on the callable's output; a Dimension collapses to Unit("1") there, so the comparison always failed — even the documented example raised a spurious UnitMismatchError ("expected unit m^2 but got unit m^2"). Feed get_unit(a) instead so the callable produces a Unit, matching the unit-based validation (check_dims correctly uses dimensions; assign_units already uses units). Adds regression tests for the pass and mismatch paths. Co-authored-by: Chaoming Wang <wcm.brainsim@gmail.com>
1 parent a8f8c0f commit 96d9562

2 files changed

Lines changed: 25 additions & 1 deletion

File tree

saiunit/_base_decorators.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,10 @@ def new_f(*args, **kwds):
488488
result = f(*args, **kwds)
489489
if "result" in au:
490490
if isinstance(au["result"], Callable) and au["result"] != bool:
491-
expected_result = au["result"](*[get_dim(a) for a in args])
491+
# Pass the argument *units* (not dimensions): the result is
492+
# validated by unit with ``_check_unit`` below, so a Dimension
493+
# here would always mismatch the returned Quantity's unit.
494+
expected_result = au["result"](*[get_unit(a) for a in args])
492495
else:
493496
expected_result = au["result"]
494497

saiunit/_base_decorators_test.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,27 @@ def f():
266266
with pytest.raises(UnitMismatchError):
267267
f()
268268

269+
def test_result_callable(self):
270+
# Documented usage: the result unit is derived from the input unit.
271+
# The callable must receive the argument *unit* (not its dimension),
272+
# otherwise the unit comparison spuriously fails.
273+
@check_units(result=lambda un: un ** 2)
274+
def square(x):
275+
return x ** 2
276+
277+
q = Quantity(3.0, unit=_metre)
278+
result = square(q)
279+
assert result.unit == _metre ** 2
280+
assert jnp.allclose(result.mantissa, 9.0)
281+
282+
def test_result_callable_mismatch_raises(self):
283+
@check_units(result=lambda un: un ** 2)
284+
def bad(x):
285+
return x # wrong: returns metre, not metre**2
286+
287+
with pytest.raises(UnitMismatchError):
288+
bad(Quantity(3.0, unit=_metre))
289+
269290
def test_stores_metadata(self):
270291
@check_units(x=_metre, result=_metre)
271292
def f(x):

0 commit comments

Comments
 (0)