Converted to use commands.

This commit is contained in:
Jeremy Fincher 2004-10-03 09:05:37 +00:00
parent 4c31e63ff8
commit c563596405
2 changed files with 48 additions and 76 deletions

View File

@ -45,68 +45,53 @@ import string
from itertools import imap from itertools import imap
import supybot.utils as utils import supybot.utils as utils
import supybot.privmsgs as privmsgs from supybot.commands import wrap
import supybot.callbacks as callbacks import supybot.callbacks as callbacks
import convertcore import convertcore
baseArg = ('int', 'base', lambda i: i <= 36)
class Math(callbacks.Privmsg): class Math(callbacks.Privmsg):
### def base(self, irc, msg, args, frm, to, number):
# So this is how the 'calc' command works:
# First, we make a nice little safe environment for evaluation; basically,
# the names in the 'math' and 'cmath' modules. Then, we remove the ability
# of a random user to get ints evaluated: this means we have to turn all
# int literals (even octal numbers and hexadecimal numbers) into floats.
# Then we delete all square brackets, underscores, and whitespace, so no
# one can do list comprehensions or call __...__ functions.
###
def base(self, irc, msg, args):
"""<fromBase> [<toBase>] <number> """<fromBase> [<toBase>] <number>
Converts from base <fromBase> to base <toBase>. If <toBase> is left Converts from base <fromBase> to base <toBase>. If <toBase> is left
out, it converts to decimal. out, it converts to decimal.
""" """
(frm, to, number) = privmsgs.getArgs(args, required=2, optional=1) if not number:
if number == '': number = str(to)
number = to to = 10
to = '10'
try:
frm = int(frm)
to = int(to)
if not ((2 <= frm <= 36) and (2 <= to <= 36)):
raise ValueError
except ValueError:
irc.error('Bases must be an integer between 2 and 36.')
return
try: try:
irc.reply(self._convertBaseToBase(number, to, frm)) irc.reply(self._convertBaseToBase(number, to, frm))
except ValueError: except ValueError:
irc.error('Invalid <number> for base %s: %s' % (frm, number)) irc.error('Invalid <number> for base %s: %s' % (frm, number))
base = wrap(base, [('int', 'base', lambda i: 2 <= i <= 36),
('int?', 10, 'base', lambda i: 2 <= i <= 36),
'?something'])
def _convertDecimalToBase(self, number, base): def _convertDecimalToBase(self, number, base):
""" """
Convert a decimal number to another base; returns a string. Convert a decimal number to another base; returns a string.
""" """
negative = False
if number < 0:
negative = True
number = -number
valStr = ''
if number == 0: if number == 0:
return '0' return '0'
elif number < 0:
negative = True
number = -number
else:
negative = False
digits = []
while number != 0: while number != 0:
digit = number % base digit = number % base
if digit >= 10: if digit >= 10:
digit = string.uppercase[digit - 10] digit = string.uppercase[digit - 10]
else: else:
digit = str(digit) digit = str(digit)
valStr = digit + valStr digits.append(digit)
number = number // base number = number // base
if negative: digits.reverse()
return '-%s' % valStr return '-'*negative + ''.join(digits)
else:
return valStr
def _convertBaseToBase(self, number, toBase, fromBase): def _convertBaseToBase(self, number, toBase, fromBase):
""" """
@ -167,7 +152,16 @@ class Math(callbacks.Privmsg):
else: else:
return '%s%s' % (realS, imagS) return '%s%s' % (realS, imagS)
def calc(self, irc, msg, args): ###
# So this is how the 'calc' command works:
# First, we make a nice little safe environment for evaluation; basically,
# the names in the 'math' and 'cmath' modules. Then, we remove the ability
# of a random user to get ints evaluated: this means we have to turn all
# int literals (even octal numbers and hexadecimal numbers) into floats.
# Then we delete all square brackets, underscores, and whitespace, so no
# one can do list comprehensions or call __...__ functions.
###
def calc(self, irc, msg, args, text):
"""<math expression> """<math expression>
Returns the value of the evaluated <math expression>. The syntax is Returns the value of the evaluated <math expression>. The syntax is
@ -176,7 +170,6 @@ class Math(callbacks.Privmsg):
crash to the bot with something like 10**10**10**10. One consequence crash to the bot with something like 10**10**10**10. One consequence
is that large values such as 10**24 might not be exact. is that large values such as 10**24 might not be exact.
""" """
text = privmsgs.getArgs(args)
if text != text.translate(string.ascii, '_[]'): if text != text.translate(string.ascii, '_[]'):
irc.error('There\'s really no reason why you should have ' irc.error('There\'s really no reason why you should have '
'underscores or brackets in your mathematical ' 'underscores or brackets in your mathematical '
@ -216,15 +209,15 @@ class Math(callbacks.Privmsg):
irc.error('%s is not a defined function.' % str(e).split()[1]) irc.error('%s is not a defined function.' % str(e).split()[1])
except Exception, e: except Exception, e:
irc.error(str(e)) irc.error(str(e))
calc = wrap(calc, ['text'])
def icalc(self, irc, msg, args): def icalc(self, irc, msg, args, text):
"""<math expression> """<math expression>
This is the same as the calc command except that it allows integer This is the same as the calc command except that it allows integer
math, and can thus cause the bot to suck up CPU. Hence it requires math, and can thus cause the bot to suck up CPU. Hence it requires
the 'trusted' capability to use. the 'trusted' capability to use.
""" """
text = privmsgs.getArgs(args)
if text != text.translate(string.ascii, '_[]'): if text != text.translate(string.ascii, '_[]'):
irc.error('There\'s really no reason why you should have ' irc.error('There\'s really no reason why you should have '
'underscores or brackets in your mathematical ' 'underscores or brackets in your mathematical '
@ -249,7 +242,7 @@ class Math(callbacks.Privmsg):
irc.error('%s is not a defined function.' % str(e).split()[1]) irc.error('%s is not a defined function.' % str(e).split()[1])
except Exception, e: except Exception, e:
irc.error(utils.exnToString(e)) irc.error(utils.exnToString(e))
icalc = privmsgs.checkCapability(icalc, 'trusted') icalc = wrap(icalc, [('checkCapability', 'trusted'), 'text'])
_rpnEnv = { _rpnEnv = {
'dup': lambda s: s.extend([s.pop()]*2), 'dup': lambda s: s.extend([s.pop()]*2),
@ -302,38 +295,22 @@ class Math(callbacks.Privmsg):
s = ', '.join(imap(self._complexToString, imap(complex, stack))) s = ', '.join(imap(self._complexToString, imap(complex, stack)))
irc.reply('Stack: [%s]' % s) irc.reply('Stack: [%s]' % s)
def convert(self, irc, msg, args): def convert(self, irc, msg, args, number, unit1, to, unit2):
"""[<number>] <unit> to <other unit> """[<number>] <unit> to <other unit>
Converts from <unit> to <other unit>. If number isn't given, it Converts from <unit> to <other unit>. If number isn't given, it
defaults to 1. For unit information, see 'units' command. defaults to 1. For unit information, see 'units' command.
""" """
# see if the first arg is a number of some sort
if args:
try: try:
num = float(args[0]) newNum = convertcore.convert(number, unit1, unit2)
args.pop(0)
except ValueError:
num = 1.0
else:
raise callbacks.ArgumentError
try:
the_rest = ' '.join(args)
(unit1, unit2) = the_rest.split(' to ')
except ValueError:
raise callbacks.ArgumentError
try:
newNum = convertcore.convert(num, unit1, unit2)
newNum = self._floatToString(newNum) newNum = self._floatToString(newNum)
irc.reply(str(newNum))
irc.reply('%s' % newNum)
except convertcore.UnitDataError, ude: except convertcore.UnitDataError, ude:
irc.error(str(ude)) irc.error(str(ude))
convert = wrap(convert, [('float?', 1.0), 'something',
('literal?', '', 'to'), 'something'])
def units(self, irc, msg, args): def units(self, irc, msg, args, type):
""" [<type>] """ [<type>]
With no arguments, returns a list of measurement types, which can be With no arguments, returns a list of measurement types, which can be
@ -341,11 +318,8 @@ class Math(callbacks.Privmsg):
the units of that type. the units of that type.
""" """
if len(args) == 0:
type = None
else:
type = ' '.join(args)
irc.reply(convertcore.units(type)) irc.reply(convertcore.units(type))
units = wrap(units, [('?something', None)])
Class = Math Class = Math

View File

@ -78,15 +78,15 @@ class MathTestCase(PluginTestCase, PluginDocumentation):
self.assertResponse('base 10 36 [base 36 csalnwea]', 'CSALNWEA') self.assertResponse('base 10 36 [base 36 csalnwea]', 'CSALNWEA')
self.assertResponse('base 5 4 [base 4 5 212231]', '212231') self.assertResponse('base 5 4 [base 4 5 212231]', '212231')
self.assertRegexp('base 37 1', 'between 2 and 36') self.assertError('base 37 1')
self.assertRegexp('base 1 1', 'between 2 and 36') self.assertError('base 1 1')
self.assertRegexp('base 12 1 1', 'between 2 and 36') self.assertError('base 12 1 1')
self.assertRegexp('base 1 12 1', 'between 2 and 36') self.assertError('base 1 12 1')
self.assertRegexp('base 1.0 12 1', 'between 2 and 36') self.assertError('base 1.0 12 1')
self.assertRegexp('base A 1', 'between 2 and 36') self.assertError('base A 1')
self.assertRegexp('base 4 4', 'Invalid <number>') self.assertError('base 4 4')
self.assertRegexp('base 10 12 A', 'Invalid <number>') self.assertError('base 10 12 A')
print print
print "If we have not fixed a bug with Math.base, the following ", print "If we have not fixed a bug with Math.base, the following ",
@ -163,10 +163,8 @@ class MathTestCase(PluginTestCase, PluginDocumentation):
self.assertError('convert 1 m to kpa') self.assertError('convert 1 m to kpa')
def testConvertSingularPlural(self): def testConvertSingularPlural(self):
self.assertResponse('convert [calc 2*pi] rads to degrees', self.assertResponse('convert [calc 2*pi] rads to degrees', '360')
'360') self.assertResponse('convert 1 carat to grams', '0.2')
self.assertResponse('convert 1 carat to grams',
'0.2')
self.assertResponse('convert 10 lbs to oz', '160') self.assertResponse('convert 10 lbs to oz', '160')
self.assertResponse('convert mA to amps', '0.001') self.assertResponse('convert mA to amps', '0.001')