jenkins-bot has submitted this change and it was merged.

Change subject: WbQuantity: use Decimal rather than floats
......................................................................


WbQuantity: use Decimal rather than floats

As in Ib54be2898a035a99592cba1298f354bd1637a6b5,
this patch passes strings to the wikibase api rather than
floats. In contrast to the original patch, this change
switches all WbQuantity values to use Decimal rather than
floats, which should maintain accuracy within the error
arithmetic as well.

Tests:
  - split up in different functions
  - added 'scientific' case
  - added Decimal case
  - switched from number to strings in api (by Tobias Schönberg)

The tests were contributed by Tobias Schönberg in Ib54be28.

Change-Id: Ie1b9ffbc9c1cc4b5d843469603ebfa1eaa5fd429
Co-Author: Tobias Schönberg <[email protected]>
---
M pywikibot/__init__.py
M tests/wikibase_tests.py
2 files changed, 85 insertions(+), 28 deletions(-)

Approvals:
  John Vandenberg: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/pywikibot/__init__.py b/pywikibot/__init__.py
index 812329f..7c83b5f 100644
--- a/pywikibot/__init__.py
+++ b/pywikibot/__init__.py
@@ -16,6 +16,7 @@
 import re
 import sys
 import threading
+from decimal import Decimal
 
 if sys.version_info[0] > 2:
     from queue import Queue
@@ -470,36 +471,54 @@
 
     _items = ('amount', 'upperBound', 'lowerBound', 'unit')
 
+    @staticmethod
+    def _todecimal(value):
+        """Convert a string to a Decimal for use in WbQuantity."""
+        if isinstance(value, Decimal):
+            return value
+        return Decimal(str(value))
+
+    @staticmethod
+    def _fromdecimal(value):
+        """Convert a Decimal to a string representation suitable for 
WikiBase."""
+        return format(value, "+g")
+
     def __init__(self, amount, unit=None, error=None):
         u"""
         Create a new WbQuantity object.
 
         @param amount: number representing this quantity
-        @type amount: float
+        @type amount: string or Decimal. Other types are accepted, and 
converted
+                      via str to Decimal.
         @param unit: not used (only unit-less quantities are supported)
         @param error: the uncertainty of the amount (e.g. ±1)
-        @type error: float, or tuple of two floats, where the first value is
+        @type error: same as amount, or tuple of two values, where the first 
value is
                      the upper error and the second is the lower error value.
         """
         if amount is None:
             raise ValueError('no amount given')
         if unit is None:
             unit = '1'
-        self.amount = amount
+
+        self.amount = self._todecimal(amount)
         self.unit = unit
-        upperError = lowerError = 0
-        if isinstance(error, tuple):
-            upperError, lowerError = error
-        elif error is not None:
-            upperError = lowerError = error
+
+        if error is None:
+            upperError = lowerError = Decimal(0)
+        elif isinstance(error, tuple):
+            upperError = self._todecimal(error[0])
+            lowerError = self._todecimal(error[1])
+        else:
+            upperError = lowerError = self._todecimal(error)
+
         self.upperBound = self.amount + upperError
         self.lowerBound = self.amount - lowerError
 
     def toWikibase(self):
         """Convert the data to a JSON object for the Wikibase API."""
-        json = {'amount': self.amount,
-                'upperBound': self.upperBound,
-                'lowerBound': self.lowerBound,
+        json = {'amount': self._fromdecimal(self.amount),
+                'upperBound': self._fromdecimal(self.upperBound),
+                'lowerBound': self._fromdecimal(self.lowerBound),
                 'unit': self.unit
                 }
         return json
@@ -511,9 +530,9 @@
 
         @param wb: Wikibase JSON
         """
-        amount = eval(wb['amount'])
-        upperBound = eval(wb['upperBound'])
-        lowerBound = eval(wb['lowerBound'])
+        amount = cls._todecimal(wb['amount'])
+        upperBound = cls._todecimal(wb['upperBound'])
+        lowerBound = cls._todecimal(wb['lowerBound'])
         error = (upperBound - amount, amount - lowerBound)
         return cls(amount, wb['unit'], error)
 
diff --git a/tests/wikibase_tests.py b/tests/wikibase_tests.py
index d477591..a370f6a 100644
--- a/tests/wikibase_tests.py
+++ b/tests/wikibase_tests.py
@@ -12,6 +12,7 @@
 
 import copy
 import json
+from decimal import Decimal
 
 import pywikibot
 
@@ -146,42 +147,79 @@
         self.assertRaises(ValueError, pywikibot.WbTime, site=repo, 
precision=15)
         self.assertRaises(ValueError, pywikibot.WbTime, site=repo, 
precision='invalid_precision')
 
-    def test_WbQuantity(self):
+    def test_WbQuantity_integer(self):
         q = pywikibot.WbQuantity(amount=1234, error=1)
         self.assertEqual(q.toWikibase(),
-                         {'amount': 1234, 'lowerBound': 1233,
-                          'upperBound': 1235, 'unit': '1', })
+                         {'amount': '+1234', 'lowerBound': '+1233',
+                          'upperBound': '+1235', 'unit': '1', })
         q = pywikibot.WbQuantity(amount=5, error=(2, 3))
         self.assertEqual(q.toWikibase(),
-                         {'amount': 5, 'lowerBound': 2, 'upperBound': 7,
-                          'unit': '1', })
+                         {'amount': '+5', 'lowerBound': '+2',
+                          'upperBound': '+7', 'unit': '1', })
+        q = pywikibot.WbQuantity(amount=0, error=(0, 0))
+        self.assertEqual(q.toWikibase(),
+                         {'amount': '+0', 'lowerBound': '+0',
+                          'upperBound': '+0', 'unit': '1', })
+        q = pywikibot.WbQuantity(amount=-5, error=(2, 3))
+        self.assertEqual(q.toWikibase(),
+                         {'amount': '-5', 'lowerBound': '-8',
+                          'upperBound': '-3', 'unit': '1', })
+
+    def test_WbQuantity_float_27(self):
         q = pywikibot.WbQuantity(amount=0.044405586)
-        q_dict = {'amount': 0.044405586, 'lowerBound': 0.044405586,
-                  'upperBound': 0.044405586, 'unit': '1', }
+        q_dict = {'amount': '+0.044405586', 'lowerBound': '+0.044405586',
+                  'upperBound': '+0.044405586', 'unit': '1', }
         self.assertEqual(q.toWikibase(), q_dict)
+
+    def test_WbQuantity_scientific(self):
+        q = pywikibot.WbQuantity(amount='1.3e-13', error='1e-14')
+        q_dict = {'amount': '+1.3e-13', 'lowerBound': '+1.2e-13',
+                  'upperBound': '+1.4e-13', 'unit': '1', }
+        self.assertEqual(q.toWikibase(), q_dict)
+
+    def test_WbQuantity_decimal(self):
+        q = pywikibot.WbQuantity(amount=Decimal('0.044405586'))
+        q_dict = {'amount': '+0.044405586', 'lowerBound': '+0.044405586',
+                  'upperBound': '+0.044405586', 'unit': '1', }
+        self.assertEqual(q.toWikibase(), q_dict)
+
+    def test_WbQuantity_string(self):
+        q = pywikibot.WbQuantity(amount='0.044405586')
+        q_dict = {'amount': '+0.044405586', 'lowerBound': '+0.044405586',
+                  'upperBound': '+0.044405586', 'unit': '1', }
+        self.assertEqual(q.toWikibase(), q_dict)
+
+    def test_WbQuantity_formatting(self):
         # test other WbQuantity methods
+        q = pywikibot.WbQuantity(amount='0.044405586')
         self.assertEqual("%s" % q,
                          '{\n'
-                         '    "amount": %(val)r,\n'
-                         '    "lowerBound": %(val)r,\n'
+                         '    "amount": "+%(val)s",\n'
+                         '    "lowerBound": "+%(val)s",\n'
                          '    "unit": "1",\n'
-                         '    "upperBound": %(val)r\n'
-                         '}' % {'val': 0.044405586})
+                         '    "upperBound": "+%(val)s"\n'
+                         '}' % {'val': '0.044405586'})
         self.assertEqual("%r" % q,
                          "WbQuantity(amount=%(val)s, "
                          "upperBound=%(val)s, lowerBound=%(val)s, "
-                         "unit=1)" % {'val': 0.044405586})
+                         "unit=1)" % {'val': '0.044405586'})
+
+    def test_WbQuantity_equality(self):
+        q = pywikibot.WbQuantity(amount='0.044405586')
         self.assertEqual(q, q)
 
+    def test_WbQuantity_fromWikibase(self):
         # test WbQuantity.fromWikibase() instantiating
         q = pywikibot.WbQuantity.fromWikibase({u'amount': u'+0.0229',
                                                u'lowerBound': u'0',
                                                u'upperBound': u'1',
                                                u'unit': u'1'})
+        # note that the bounds are inputted as INT but are returned as FLOAT
         self.assertEqual(q.toWikibase(),
-                         {'amount': 0.0229, 'lowerBound': 0, 'upperBound': 1,
-                          'unit': '1', })
+                         {'amount': '+0.0229', 'lowerBound': '+0.0000',
+                          'upperBound': '+1.0000', 'unit': '1', })
 
+    def test_WbQuantity_errors(self):
         # test WbQuantity error handling
         self.assertRaises(ValueError, pywikibot.WbQuantity, amount=None,
                           error=1)

-- 
To view, visit https://gerrit.wikimedia.org/r/250497
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: Ie1b9ffbc9c1cc4b5d843469603ebfa1eaa5fd429
Gerrit-PatchSet: 10
Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Owner: Merlijn van Deen <[email protected]>
Gerrit-Reviewer: John Vandenberg <[email protected]>
Gerrit-Reviewer: Ladsgroup <[email protected]>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to