Skip to content

Commit

Permalink
return NotImplemented for binary special methods
Browse files Browse the repository at this point in the history
This patch fixes the behaviour or special binary methods (e.g. __add__) by
returning NotImplemented upon failure rather than raising DimensionError. See
https://docs.python.org/3.6/library/constants.html#NotImplemented.
  • Loading branch information
gertjanvanzwieten committed Mar 4, 2024
1 parent da3c1e7 commit 55d6978
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 18 deletions.
46 changes: 30 additions & 16 deletions nutils/SI.py
Original file line number Diff line number Diff line change
Expand Up @@ -414,40 +414,47 @@ def __dotarg(op, *args, **kwargs):

## DEFINE OPERATORS

def op(name, with_reverse=False, *, __table=__DISPATCH_TABLE):
dispatch = __table[name]
op = getattr(operator, name)
ret = lambda *args: dispatch(op, *args)
if with_reverse:
ret = ret, lambda self, other: dispatch(op, other, self)
return ret
def op(name, __table=__DISPATCH_TABLE):
dispatch, op = __table[name], getattr(operator, name)
return lambda *args: dispatch(op, *args)

__getitem__ = op('getitem')
__setitem__ = op('setitem')
__neg__ = op('neg')
__pos__ = op('pos')
__abs__ = op('abs')

def op(name, __table=__DISPATCH_TABLE):
dispatch, op = __table[name], getattr(operator, name)
return lambda *args: _try_or_noimp(dispatch, op, *args)

__lt__ = op('lt')
__le__ = op('le')
__eq__ = op('eq')
__ne__ = op('ne')
__gt__ = op('gt')
__ge__ = op('ge')
__add__, __radd__ = op('add', True)
__sub__, __rsub__ = op('sub', True)
__mul__, __rmul__ = op('mul', True)
__matmul__, __rmatmul__ = op('matmul', True)
__truediv, __rtruediv__ = op('truediv', True)
__mod__, __rmod__ = op('mod', True)
__pow__, __rpow__ = op('pow', True)

def op(name, __table=__DISPATCH_TABLE):
dispatch, op = __table[name], getattr(operator, name)
return lambda self, other: _try_or_noimp(dispatch, op, self, other), \
lambda self, other: _try_or_noimp(dispatch, op, other, self)

__add__, __radd__ = op('add')
__sub__, __rsub__ = op('sub')
__mul__, __rmul__ = op('mul')
__matmul__, __rmatmul__ = op('matmul')
__truediv, __rtruediv__ = op('truediv')
__mod__, __rmod__ = op('mod')
__pow__, __rpow__ = op('pow')

del op

def __truediv__(self, other):
if type(other) is str:
return self.__value / self.__class__(other).__value
return self.__truediv(other)

del op

## DISPATCH THIRD PARTY CALLS

def __array_ufunc__(self, ufunc, method, *inputs, **kwargs):
Expand Down Expand Up @@ -504,6 +511,13 @@ def _split_factors(s):
isnumer = False


def _try_or_noimp(func, *args):
try:
return func(*args)
except DimensionError:
return NotImplemented


## SI DIMENSIONS

Dimensionless = Dimension.from_powers({})
Expand Down
4 changes: 2 additions & 2 deletions tests/test_SI.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,13 +104,13 @@ def test_power(self):
def test_add(self):
self.assertEqual(SI.Mass('2kg') + SI.Mass('3kg'), SI.Mass('5kg'))
self.assertEqual(numpy.add(SI.Mass('2kg'), SI.Mass('3kg')), SI.Mass('5kg'))
with self.assertRaisesRegex(SI.DimensionError, r'incompatible arguments for add: \[M\], \[L\]'):
with self.assertRaisesRegex(TypeError, r"unsupported operand type\(s\) for \+: '\[M\]' and '\[L\]'"):
SI.Mass('2kg') + SI.Length('3m')

def test_sub(self):
self.assertEqual(SI.Mass('2kg') - SI.Mass('3kg'), SI.Mass('-1kg'))
self.assertEqual(numpy.subtract(SI.Mass('2kg'), SI.Mass('3kg')), SI.Mass('-1kg'))
with self.assertRaisesRegex(SI.DimensionError, r'incompatible arguments for sub: \[M\], \[L\]'):
with self.assertRaisesRegex(TypeError, r"unsupported operand type\(s\) for \-: '\[M\]' and '\[L\]'"):
SI.Mass('2kg') - SI.Length('3m')

def test_hypot(self):
Expand Down

0 comments on commit 55d6978

Please # to comment.