In Python 3 I have a class representing a range [x,y] of values and computing the length of such range.
If the length is too large, I'm not sure how to catch the OverflowError exception inside the class itself. It is raised only in the outer code using a class instance...
class ValRange:
val_min = None
val_max = None
range_len = 0
def __init__(self, val_min, val_max):
self.val_min = val_min
self.val_max = val_max
def __len__(self):
if (self.val_min is not None and
self.val_max is not None):
length = 0
try:
length = int(self.val_max) - int(self.val_min) + 1
except OverflowError as e:
# Somehow no exception is caught here...
print('OverflowError...')
length = 10**5 # arbitrarily large number
except Exception as e:
print(e)
if length > 0:
self.range_len = length
return self.range_len
import traceback
import uuid
x = uuid.UUID('00000000-cb9d-4a99-994d-53a499f260b3')
y = uuid.UUID('ea205f99-0564-4aa0-84c3-1b99fcd679fd')
r = ValRange(x, y)
try:
print(len(r))
except:
# The exception is caught in this outer code and not in the class itself. Why?
print(traceback.format_exc())
# The following, which is equivalent to the operations in the
# code above, will work.
a = int(y) - int(x) + 1
print(a)
This is what happens upon execution:
Traceback (most recent call last):
File "/home/rira/bugs/overflow.py", line 35, in <module>
print(len(r))
OverflowError: cannot fit 'int' into an index-sized integer
311207443402617699746040548788952897867
That's because the OverflowError
doesn't occur within your magic __len__()
method - Python is perfectly capable of handling much bigger integers than that - but in CPython len()
itself is implemented as PyObject_Size()
which returns a Py_ssize_t
, which is limited to 2^31-1
(32-bit) or 2^63-1
(64-bit) and thus the overflow occurs when your __len__()
result is coerced to it.
You can do a pre-check before returning the result to make sure you capture the overflow before it even occurs, something like:
def __len__(self):
if (self.val_min is not None and self.val_max is not None):
length = int(self.val_max) - int(self.val_min) + 1
if length > sys.maxsize:
print('OverflowError...')
length = 10**5 # arbitrarily large number
self.range_len = length
return self.range_len