Math: make `@icalc` fail early when result is too large

This avoids inconsistent errors between CPython 3.10.7 and older
versions; and the result would not be readable anyway.

Closes GH-1517.
This commit is contained in:
Valentin Lorentz 2022-09-18 19:47:01 +02:00 committed by Val Lorentz
parent acdae12bbd
commit 169824a9d2
2 changed files with 21 additions and 4 deletions

View File

@ -166,8 +166,8 @@ class Math(callbacks.Plugin):
"""
try:
self.log.info('evaluating %q from %s', text, msg.prefix)
x = safe_eval(text, allow_ints=True)
irc.reply(str(x))
result = safe_eval(text, allow_ints=True)
float(result) # fail early if it is too large to be displayed
except OverflowError:
maxFloat = math.ldexp(0.9999999999999999, 1024)
irc.error(_('The answer exceeded %s or so.') % maxFloat)
@ -177,6 +177,17 @@ class Math(callbacks.Plugin):
irc.error(_('%s is not a defined function.') % str(e).split()[1])
except Exception as e:
irc.error(utils.exnToString(e))
else:
try:
result_str = str(result)
except ValueError as e:
# Probably too large to be converted to string; go through
# floats instead.
# https://docs.python.org/3/library/stdtypes.html#int-max-str-digits
# (Depending on configuration, this may be dead code because it
# is caught by the float() check above.
result_str = str(float(result))
irc.reply(result_str)
icalc = wrap(icalc, [('checkCapability', 'trusted'), 'text'])
_rpnEnv = {

View File

@ -112,7 +112,10 @@ class MathTestCase(PluginTestCase):
self.assertNotError('calc (1600 * 1200) - 2*(1024*1280)')
self.assertNotError('calc 3-2*4')
self.assertNotError('calc (1600 * 1200)-2*(1024*1280)')
self.assertError('calc factorial(20000)')
self.assertResponse('calc factorial(20000)',
'Error: factorial argument too large')
self.assertResponse('calc factorial(20000) / factorial(19999)',
'Error: factorial argument too large')
def testCalcNoNameError(self):
self.assertRegexp('calc foobar(x)', 'foobar is not a defined function')
@ -147,7 +150,10 @@ class MathTestCase(PluginTestCase):
self.assertResponse('icalc 1^1', '0')
self.assertResponse('icalc 10**24', '1' + '0'*24)
self.assertRegexp('icalc 49/6', '8.16')
self.assertNotError('icalc factorial(20000)')
self.assertRegexp('icalc factorial(20000)',
'Error: The answer exceeded')
self.assertResponse('icalc factorial(20000) / factorial(19999)',
'20000.0')
def testRpn(self):
self.assertResponse('rpn 5 2 +', '7')