deskbar-applet r2216 - in trunk: . deskbar/handlers
- From: sebp svn gnome org
- To: svn-commits-list gnome org
- Subject: deskbar-applet r2216 - in trunk: . deskbar/handlers
- Date: Mon, 16 Jun 2008 12:27:17 +0000 (UTC)
Author: sebp
Date: Mon Jun 16 12:27:16 2008
New Revision: 2216
URL: http://svn.gnome.org/viewvc/deskbar-applet?rev=2216&view=rev
Log:
Added calculator module with permission from the original authors Callum McKenzie <callum at spooky-possum org>, Michael Hofmann <mh21 at piware de> and Johannes Buchner <buchner.johannes gmx at>
Added:
trunk/deskbar/handlers/calculator.py
trunk/deskbar/handlers/test_calculator.py
Modified:
trunk/ChangeLog
trunk/deskbar/handlers/Makefile.am
Modified: trunk/deskbar/handlers/Makefile.am
==============================================================================
--- trunk/deskbar/handlers/Makefile.am (original)
+++ trunk/deskbar/handlers/Makefile.am Mon Jun 16 12:27:16 2008
@@ -11,6 +11,7 @@
deskbar_handlers_PYTHON = \
beagle-static.py \
beagle-live.py \
+ calculator.py \
desklicious.py \
epiphany.py \
files.py \
Added: trunk/deskbar/handlers/calculator.py
==============================================================================
--- (empty file)
+++ trunk/deskbar/handlers/calculator.py Mon Jun 16 12:27:16 2008
@@ -0,0 +1,207 @@
+#
+# calculator.py : A calculator module for the deskbar applet.
+#
+# Copyright (C) 2008 by Johannes Buchner
+# Copyright (C) 2007 by Michael Hofmann
+# Copyright (C) 2006 by Callum McKenzie
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# Authors:
+# Callum McKenzie <callum spooky-possum org> - Original author
+# Michael Hofmann <mh21 piware de> - compatibility changes for deskbar 2.20
+# Johannes Buchner <buchner johannes gmx at> - Made externally usable
+#
+# This version of calculator can be used with converter
+# read how at http://twoday.tuwien.ac.at/jo/search?q=calculator+converter+deskbar
+#
+
+from __future__ import division
+from deskbar.handlers.actions.CopyToClipboardAction import CopyToClipboardAction
+from deskbar.defs import VERSION
+from gettext import gettext as _
+import deskbar.core.Utils
+import deskbar.interfaces.Match
+import deskbar.interfaces.Module
+import logging
+import math
+import re
+
+LOGGER = logging.getLogger(__name__)
+
+HANDLERS = ["CalculatorModule"]
+
+def bin (n):
+ """A local binary equivalent of the hex and oct builtins."""
+ if (n == 0):
+ return "0b0"
+ s = ""
+ if (n < 0):
+ while n != -1:
+ s = str (n & 1) + s
+ n >>= 1
+ return "0b" + "...111" + s
+ else:
+ while n != 0:
+ s = str (n & 1) + s
+ n >>= 1
+ return "0b" + s
+
+# These next three make sure {hex, oct, bin} can handle floating point,
+# by rounding. This makes sure things like hex(255/2) behave as a
+# programmer would expect while allowing 255/2 to equal 127.5 for normal
+# people. Abstracting out the body of these into a single function which
+# takes hex, oct or bin as an argument seems to run into problems with
+# those functions not being defined correctly in the resticted eval (?).
+
+def lenient_hex (c):
+ try:
+ return hex (c)
+ except TypeError:
+ return hex (int (c))
+
+def lenient_oct (c):
+ try:
+ return oct (c)
+ except TypeError:
+ return oct (int (c))
+
+def lenient_bin (c):
+ try:
+ return bin (c)
+ except TypeError:
+ return bin (int (c))
+
+class CalculatorAction (CopyToClipboardAction):
+
+ def __init__ (self, text, answer):
+ CopyToClipboardAction.__init__ (self, answer, answer)
+ self.text = text
+
+ def get_verb(self):
+ return _("Copy <b>%(origtext)s = %(name)s</b> to clipboard")
+
+ def get_name(self, text = None):
+ """Because the text variable for history entries contains the text
+ typed for the history search (and not the text of the orginal action),
+ we store the original text seperately."""
+ result = CopyToClipboardAction.get_name (self, text)
+ result["origtext"] = self.text
+ return result
+
+ def get_tooltip(self, text=None):
+ return self._name
+
+class CalculatorMatch (deskbar.interfaces.Match):
+
+ def __init__ (self, text, answer, **kwargs):
+ deskbar.interfaces.Match.__init__ (self, name = text,
+ icon = "gtk-add", category = "calculator", **kwargs)
+ self.answer = str (answer)
+ self.add_action (CalculatorAction (text, self.answer))
+
+ def get_hash (self):
+ return self.answer
+
+class CalculatorModule (deskbar.interfaces.Module):
+
+ INFOS = {"icon": deskbar.core.Utils.load_icon ("gtk-add"),
+ "name": _("Calculator"),
+ "description": _("Calculate simple equations"),
+ "version" : VERSION,
+ "categories" : { "calculator" : { "name" : _("Calculator") }}}
+
+ def __init__ (self):
+ deskbar.interfaces.Module.__init__ (self)
+ self.hexre = re.compile ("0[Xx][0-9a-fA-F_]*[0-9a-fA-F]")
+ self.binre = re.compile ("0[bB][01_]*[01]")
+
+ def _number_parser (self, match, base):
+ """A generic number parser, regardless of base. It also ignores the
+ '_' character so it can be used as a separator. Note how we skip
+ the first two characters since we assume it is something like '0x'
+ or '0b' and identifies the base."""
+ table = { '0' : 0, '1' : 1, '2' : 2, '3' : 3, '4' : 4,
+ '5' : 5, '6' : 6, '7' : 7, '8' : 8, '9' : 9,
+ 'a' : 10, 'b' : 11, 'c' : 12, 'd' : 13,
+ 'e' : 14, 'f' : 15 }
+ d = 0
+ for c in match.group()[2:]:
+ if c != "_":
+ d = d * base + table[c]
+ return str (d)
+
+ def _binsub (self, match):
+ """Because python doesn't handle binary literals, we parse it
+ ourselves and replace it with a decimal representation."""
+ return self._number_parser (match, 2)
+
+ def _hexsub (self, match):
+ """Parse the hex literal ourselves. We could let python do it, but
+ since we have a generic parser we use that instead."""
+ return self._number_parser (match, 16)
+
+ def run_query (self, query):
+ """We evaluate the equation by first replacing hex and binary literals
+ with their decimal representation. (We need to check hex, so we can
+ distinguish 0x10b1 as a hex number, not 0x1 followed by 0b1.) We
+ severely restrict the eval environment. Any errors are ignored."""
+ restricted_dictionary = { "__builtins__" : None, "abs" : abs,
+ "acos" : math.acos, "asin" : math.asin,
+ "atan" : math.atan, "atan2" : math.atan2,
+ "bin" : lenient_bin,"ceil" : math.ceil,
+ "cos" : math.cos, "cosh" : math.cosh,
+ "degrees" : math.degrees,
+ "exp" : math.exp, "floor" : math.floor,
+ "hex" : lenient_hex, "int" : int,
+ "log" : math.log, "pow" : math.pow,
+ "log10" : math.log10, "oct" : lenient_oct,
+ "pi" : math.pi, "radians" : math.radians,
+ "round": round, "sin" : math.sin,
+ "sinh" : math.sinh, "sqrt" : math.sqrt,
+ "tan" : math.tan, "tanh" : math.tanh}
+ try:
+ scrubbedquery = query.lower()
+ scrubbedquery = self.hexre.sub (self._hexsub, scrubbedquery)
+ scrubbedquery = self.binre.sub (self._binsub, scrubbedquery)
+ for (c1, c2) in (("[", "("), ("{", "("), ("]", ")"), ("}", ")")):
+ scrubbedquery = scrubbedquery.replace (c1, c2)
+
+ answer = eval (scrubbedquery, restricted_dictionary)
+
+ # Try and avoid echoing back simple numbers. Note that this
+ # doesn't work well for floating point, e.g. '3.' behaves badly.
+ if str (answer) == query:
+ return None
+
+ # We need this check because the eval can return function objects
+ # when we are halfway through typing the expression.
+ if isinstance (answer, (float, int, long, str)):
+ return answer
+ else:
+ return None
+ except Exception, e:
+ LOGGER.debug (e.message)
+ return None
+
+ def query (self, query):
+ answer = self.run_query(query)
+ if answer != None:
+ result = [CalculatorMatch (query, answer)]
+ self._emit_query_ready (query, result)
+ return answer
+ else:
+ return []
+
Added: trunk/deskbar/handlers/test_calculator.py
==============================================================================
--- (empty file)
+++ trunk/deskbar/handlers/test_calculator.py Mon Jun 16 12:27:16 2008
@@ -0,0 +1,245 @@
+#!/usr/bin/env python
+#
+# test_calculator.py : Tests for the deskbar calculator handler
+#
+# Copyright (C) 2006 by Callum McKenzie
+#
+# Time-stamp: <2007-10-05 18:37:08 callum>
+#
+
+import unittest
+import re
+from math import pi
+import calculator
+
+class CalculatorBinTest (unittest.TestCase):
+ # Testing the bin function
+ def testBinZero (self):
+ self.assertEqual (calculator.bin (0), "0b0")
+ def testBinPositive (self):
+ self.assertEqual (calculator.bin (42), "0b101010")
+ self.assertEqual (calculator.bin (511), "0b111111111")
+ def testBinNegative (self):
+ self.assertEqual (calculator.bin (-2),
+ "0b...1110")
+ self.assertEqual (calculator.bin (-1),
+ "0b...111")
+ def testBinBigPositive (self):
+ self.assertEqual (calculator.bin (4294967296L),
+ "0b100000000000000000000000000000000")
+ self.assertEqual (calculator.bin (11453246122L),
+ "0b1010101010101010101010101010101010")
+ def testBinBigNegative (self):
+ self.assertEqual (calculator.bin (-4294967296L),
+ "0b...11100000000000000000000000000000000")
+ self.assertEqual (calculator.bin (-11453246123L),
+ "0b...1110101010101010101010101010101010101")
+ def testBinType (self):
+ self.assertRaises (TypeError, calculator.bin, "42")
+ self.assertRaises (TypeError, calculator.bin, 1.0)
+
+class CalculatorModuleTest (unittest.TestCase):
+ def testHexRe (self):
+ h = calculator.CalculatorModule ()
+ self.failUnless (h.hexre.match ("0x12"))
+ self.failUnless (h.hexre.match ("0X12"))
+ self.failUnless (h.hexre.match ("0x0123456789abcdef"))
+ self.failUnless (h.hexre.match ("0x0123456789ABCDEF"))
+ self.failUnless (h.hexre.match ("0xfedcba9876543210"))
+ self.failUnless (h.hexre.match ("0xFEDCBA9876543210"))
+ self.failUnless (h.hexre.match ("0xfedc_ba98_7654_3210"))
+ self.failUnless (h.hexre.match ("0x_fedc_ba98_7654_3210"))
+ self.failIf (h.hexre.match ("0x_"))
+ self.failIf (h.hexre.match ("x1234"))
+ self.failIf (h.hexre.match ("cafebabe"))
+ self.failIf (h.hexre.match ("Purple"))
+
+ def testBinRe (self):
+ h = calculator.CalculatorModule ()
+ self.failUnless (h.binre.match ("0b10"))
+ self.failUnless (h.binre.match ("0B10"))
+ self.failUnless (h.binre.match ("0b01101100"))
+ self.failUnless (h.binre.match ("0b0110_1100"))
+ self.failUnless (h.binre.match ("0b_0110_1100"))
+ self.failUnless (h.binre.match ("0b11111111111111111111111111111111"))
+ self.failUnless (h.binre.match ("0b1111111111111111111111111111111111111111111111111111111111111111"))
+ self.failUnless (h.binre.match ("0b11111111111111111111111111111111111111111111111111111111111111111"))
+ self.failUnless (h.binre.match ("0b1111111111_111111111111111__111111111111111111111___1111111111"))
+ self.failIf (h.binre.match ("b1000"))
+ self.failIf (h.binre.match ("b1234"))
+ self.failIf (h.binre.match ("1010101"))
+ self.failIf (h.binre.match ("Purple"))
+
+ def testHexSub (self):
+ h = calculator.CalculatorModule ()
+ match = h.hexre.match ("0x2f")
+ self.assertEqual (h._hexsub (match), '47')
+ match = h.hexre.match ("0xee789a45")
+ self.assertEqual (h._hexsub (match), '4000881221')
+ match = h.hexre.match ("0xee78_9a45")
+ self.assertEqual (h._hexsub (match), '4000881221')
+ match = h.hexre.match ("0x2f34897ef98d8922")
+ self.assertEqual (h._hexsub (match), '3401494797017254178')
+ match = h.hexre.match ("0x2f34897ef98d89226c")
+ self.assertEqual (h._hexsub (match), '870782668036417069676')
+
+ def testBinSub (self):
+ h = calculator.CalculatorModule ()
+ match = h.binre.match ("0b0")
+ self.assertEqual (h._binsub (match), "0")
+ match = h.binre.match ("0b101010")
+ self.assertEqual (h._binsub (match), "42")
+ match = h.binre.match ("0b11111111111111111111111111111111")
+ self.assertEqual (h._binsub (match), "4294967295")
+ match = h.binre.match ("0b100000000000000000000000000000001")
+ self.assertEqual (h._binsub (match), "4294967297")
+ match = h.binre.match ("0b_")
+ self.assertEqual (match, None)
+ match = h.binre.match ("0b101_010")
+ self.assertEqual (h._binsub (match), "42")
+ match = h.binre.match ("0b1111_1111_1111_1111_1111_1111_1111_1111")
+ self.assertEqual (h._binsub (match), "4294967295")
+ match = h.binre.match ("0b1____000000000000_00___000000000000000001")
+ self.assertEqual (h._binsub (match), "4294967297")
+
+ def testQuery (self):
+ """Generic tests of the query function that don't fit anywhere else."""
+ h = calculator.CalculatorModule ()
+ self.assertEqual (h.query ("print 'Hello World'"), [])
+ # Don't echo back simple identities ...
+ self.assertEqual (h.query ("1"), [])
+ # ... but do base conversions.
+ self.assertEqual (h.query ("0x2"), 2)
+ # This makes sure that hex, oct and bin always do the right
+ # thing in the presence of fractions.
+ self.assertEqual (h.query ("hex(0x5/2)"), "0x2")
+ self.assertEqual (h.query ("oct(05/2)"), "02")
+ self.assertEqual (h.query ("bin(0b101/2)"), "0b10")
+ # Make sure that we accept all brackets.
+ self.assertEqual (h.query ("abs(-1)"), 1)
+ self.assertEqual (h.query ("abs[-1]"), 1)
+ self.assertEqual (h.query ("abs{-1}"), 1)
+ # Now test some "complex" equations to check that bracketing
+ # roks and try and provoke other issues.
+ self.assertEqual (h.query ("1 + 3*(2+3)"), 16)
+ self.assertEqual (h.query ("1 + 2*(2 - (3 - 2))"), 3)
+ self.assertAlmostEqual (h.query ("sqrt(sin(pi/2)**2 + cos(pi/2)**2)"), 1.0)
+ self.assertAlmostEqual (h.query ("sqrt(2)**sqrt(3)"), 1.8226346549)
+ self.assertAlmostEqual (h.query ("1/(2 + log(3)) + atan(3)"), 1.571770885)
+
+ def testQueryOperators (self):
+ h = calculator.CalculatorModule ()
+ self.assertEqual (h.query ("1 + 1"), 2)
+ self.assertEqual (h.query ("2 - 4"), -2)
+ self.assertEqual (h.query ("3 * 7"), 21)
+ self.assertEqual (h.query ("35 / 5"), 7)
+ self.assertEqual (h.query ("35 / 2"), 17.5)
+ self.assertEqual (h.query ("6 % 4"), 2)
+ self.assertEqual (h.query ("-6 % 4"), 2)
+ self.assertEqual (h.query ("3 // 2"), 1)
+ self.assertEqual (h.query ("-3 // 2"), -2)
+ self.assertEqual (h.query ("3 << 3"), 24)
+ self.assertEqual (h.query ("1 << 65"), 36893488147419103232)
+ self.assertEqual (h.query ("8 >> 3"), 1)
+ self.assertEqual (h.query ("8 >> 4"), 0)
+ self.assertEqual (h.query ("-1 >> 3"), -1)
+ self.assertEqual (h.query ("7 & 3"), 3)
+ self.assertEqual (h.query ("-1 & 6"), 6)
+ self.assertEqual (h.query ("8 & 3"), 0)
+ self.assertEqual (h.query ("36893488147419103233 & 1"), 1)
+ self.assertEqual (h.query ("8 | 4"), 12)
+ self.assertEqual (h.query ("-1 | 5"), -1)
+ self.assertEqual (h.query ("7 | 3"), 7)
+ self.assertEqual (h.query ("36893488147419103232 | 1"),
+ 36893488147419103233)
+ self.assertEqual (h.query ("1 ^ 3"), 2)
+ self.assertEqual (h.query ("7 ^ 7"), 0)
+ self.assertEqual (h.query ("-1 ^ 3"), -4)
+ self.assertEqual (h.query ("36893488147419103233 ^ 1"),
+ 36893488147419103232)
+ self.assertEqual (h.query ("~3"), -4)
+ self.assertEqual (h.query ("~36893488147419103233"),
+ -36893488147419103234)
+ self.assertEqual (h.query ("~-1"), 0)
+ self.assertEqual (h.query ("~-36893488147419103234"),
+ 36893488147419103233)
+ self.assertEqual (h.query ("~~5"), 5)
+ self.assertEqual (h.query ("~~-5"), -5)
+
+ def testQueryConversions (self):
+ h = calculator.CalculatorModule ()
+ # Start off with some simple stuff even though these have
+ # _theoretically_ been tested above.
+ self.assertEqual (h.query ("0x10"), 16)
+ self.assertEqual (h.query ("0xabcd"), 43981)
+ self.assertEqual (h.query ("0x2f34897ef98d89226c"),
+ 870782668036417069676)
+ self.assertEqual (h.query ("-0x10"), -16)
+
+ self.assertEqual (h.query ("0b101"), 5)
+ self.assertEqual (h.query ("-0b101"), -5)
+ self.assertEqual (h.query ("-0b100000000000000000000000000000001"),
+ -4294967297)
+
+ # Octal hasn't been exercised above.
+ self.assertEqual (h.query ("0123"), 83)
+ self.assertEqual (h.query ("01234567012345670123456701234567"),
+ 1616895878810725189668911479)
+ self.assertEqual (h.query ("-0123"), -83)
+
+ # Floating point identities.
+ self.assertEqual (h.query ("0.12345 + 0"), 0.12345)
+ self.assertEqual (h.query ("-0.12345 + 0"), -0.12345)
+ self.assertEqual (h.query ("-0.12e5 + 0"), -0.12e5)
+ self.assertEqual (h.query ("0.12e-5 + 0"), 0.12e-5)
+
+ # Now do the really stupid case.
+ self.assertEqual (h.query ("-10 + 0"), -10)
+ self.assertEqual (h.query ("12345678901234567890 + 0"),
+ 12345678901234567890)
+
+ # Test that this is counted as one hex literal rather than
+ # one hex literal followed by a binary literal.
+ self.assertEqual (h.query ("0x10b1"), 4273)
+
+ def testQueryFunctions (self):
+ h = calculator.CalculatorModule ()
+ # These aren't so much a test of cosine, as a test of
+ # general parsing prinicples.
+ self.assertAlmostEqual (h.query ("cos(0)"), 1.0, 3)
+ self.assertAlmostEqual (h.query ("cos(0.0)"), 1.0, 3)
+ self.assertAlmostEqual (h.query ("cos (0)"), 1.0, 3)
+ self.assertAlmostEqual (h.query ("CoS(0)"), 1.0, 3)
+ # We require brackets for functions.
+ self.assertEqual (h.query ("cos 0"), [])
+ self.assertEqual (h.query ("cosine(0)"), [])
+ # Now for the rest of the functions.
+ self.assertAlmostEqual (h.query ("asin(0.0)"), 0.0, 3)
+ self.assertAlmostEqual (h.query ("acos(1.0)"), 0.0, 3)
+ self.assertAlmostEqual (h.query ("atan2(1.0, 0.0)"), pi/2.0, 3)
+ self.assertAlmostEqual (h.query ("atan2(0.0, 1.0)"), 0.0, 3)
+ self.assertAlmostEqual (h.query ("atan2(-1.0, 1.0)"), -pi/4.0, 3)
+ self.assertAlmostEqual (h.query ("sinh(0.0)"), 0.0, 3)
+ self.assertAlmostEqual (h.query ("cosh(0.0)"), 1.0, 3)
+ self.assertAlmostEqual (h.query ("tanh(0.0)"), 0.0, 3)
+ self.assertAlmostEqual (h.query ("sin(0.0)"), 0.0, 3)
+ self.assertAlmostEqual (h.query ("cos(pi/2)"), 0.0, 3)
+ self.assertAlmostEqual (h.query ("tan(pi/4)"), 1.0, 3)
+ self.assertAlmostEqual (h.query ("abs(-1.0)"), 1.0, 3)
+ self.assertAlmostEqual (h.query ("sqrt(2.0)"), 1.41421, 3)
+ self.assertAlmostEqual (h.query ("pi"), 3.14159, 3)
+ self.assertAlmostEqual (h.query ("log10(345)"), 2.537819, 3)
+ self.assertAlmostEqual (h.query ("log(pi)"), 1.1447299, 3)
+ self.assertAlmostEqual (h.query ("exp(1.0)"), 2.718282, 3)
+ self.assertAlmostEqual (h.query ("radians(180)"), pi, 3)
+ self.assertAlmostEqual (h.query ("degrees(-pi/2)"), -90, 3)
+ self.assertAlmostEqual (h.query ("ceil(2.3)"), 3.0, 3)
+ self.assertAlmostEqual (h.query ("ceil(-2.3)"), -2.0, 3)
+ self.assertAlmostEqual (h.query ("floor(2.3)"), 2.0, 3)
+ self.assertAlmostEqual (h.query ("floor(-2.3)"), -3.0, 3)
+ self.assertAlmostEqual (h.query ("round(2.3)"), 2.0, 3)
+ self.assertAlmostEqual (h.query ("round(-2.3)"), -2.0, 3)
+ self.assertEqual (h.query ("int(2.3)"), 2)
+ self.assertEqual (h.query ("int(-2.3)"), -2)
+
+unittest.main ()
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]