[Python-ideas] Should decimal.InvalidOperation subclass ValueError?

Steven D'Aprano steve at pearwood.info
Sun May 22 04:45:58 EDT 2016


On the tutor mailing list, somebody asked a question about 
decimal.InvalidOperation. They were converting strings to Decimal using 
something like this:

    try:
        d = Decimal(the_string)
    except ValueError:
        handle_error()


and were perplexed by the error that they got:

decimal.InvalidOperation: [<class 'decimal.ConversionSyntax'>]



This got me thinking. Normally, such a conversion error should raise 
ValueError, as ints, floats and Fractions do. Presumably decimal does 
something different as it must match the General Decimal Arithmetic 
Specification. But, is there any reason why InvalidOperation couldn't be 
a subclass of ValueError? Exception class hierarchies are not part of 
the GDAS, so this should be allowed.

Looking at the docs, there are nine examples given of things which can 
raise InvalidOperation (if not trapped, in which case they return a 
NAN):

Infinity - Infinity
0 * Infinity
Infinity / Infinity
x % 0
Infinity % x
sqrt(-x) and x > 0
0 ** 0
x ** (non-integer)
x ** Infinity

plus invalid string conversion. To my mind, ValueError would be an 
acceptable error for all of these things. E.g. the root of a negative 
value raises ValueError in Python 2:

>>> (-2.0)**0.5
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: negative number cannot be raised to a fractional power

(In Python 3, it returns a complex number.)

So I propose that InvalidOperation be changed to inherit from 
ValueError, to match the expected behaviour from other numeric types.

The only tricky bit is that division by zero doesn't raise ValueError, 
but DivideByZeroError instead. But that's no worse than the current 
situation:

# current
- people expect Decimal(1)/0 to raise DivideByZeroError, but it 
  raises InvalidOperation;

# proposal
- people expect Decimal(1)/0 to raise DivideByZero, but it 
  raises InvalidOperation (subclass of ValueError).


Oh, a further data point: if you pass an invalid list or tuple to 
Decimal, you get a ValueError:

py> decimal.Decimal([])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: argument must be a sequence of length 3


Thoughts?



-- 
Steve


More information about the Python-ideas mailing list