Merlijn van Deen has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/250497

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.

This patch includes the updated tests that were contributed
by Tobias Schönberg as part of Ib54be28.

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


  git pull ssh://gerrit.wikimedia.org:29418/pywikibot/core 
refs/changes/97/250497/1

diff --git a/pywikibot/__init__.py b/pywikibot/__init__.py
index 812329f..ca8d840 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
@@ -463,6 +464,13 @@
                                ts[u'before'], ts[u'after'],
                                ts[u'timezone'], ts[u'calendarmodel'])
 
+def _s2q(value):
+    """Convert a string to a Decimal for use in WbQuantity"""
+    return Decimal(str(value))
+
+def _q2s(value):
+    """Convert a Decimal to a string representation suitable for WikiBase"""
+    return format(value, "+g")
 
 class WbQuantity(_WbRepresentation):
 
@@ -475,31 +483,37 @@
         Create a new WbQuantity object.
 
         @param amount: number representing this quantity
-        @type amount: float
+        @type amount: string, int or Decimal. Floats are accepted, but 
converted to Decimal. The
+                      specific conversion depends on the Python version.
         @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 = _s2q(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 = _s2q(error[0])
+            lowerError = _s2q(error[1])
+        else:
+            upperError = lowerError = _s2q(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': _q2s(self.amount),
+                'upperBound': _q2s(self.upperBound),
+                'lowerBound': _q2s(self.lowerBound),
                 'unit': self.unit
                 }
         return json
@@ -511,9 +525,9 @@
 
         @param wb: Wikibase JSON
         """
-        amount = eval(wb['amount'])
-        upperBound = eval(wb['upperBound'])
-        lowerBound = eval(wb['lowerBound'])
+        amount = _s2q(wb['amount'])
+        upperBound = _s2q(wb['upperBound'])
+        lowerBound = _s2q(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..10c56ab 100644
--- a/tests/wikibase_tests.py
+++ b/tests/wikibase_tests.py
@@ -24,7 +24,6 @@
     unittest, TestCase,
     WikidataTestCase,
     DeprecationTestCase,
-    DefaultWikibaseClientTestCase,
 )
 
 from tests.basepage_tests import (
@@ -149,23 +148,35 @@
     def test_WbQuantity(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', })
+        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)
         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)
         # test other WbQuantity methods
         self.assertEqual("%s" % q,
                          '{\n'
-                         '    "amount": %(val)r,\n'
-                         '    "lowerBound": %(val)r,\n'
+                         '    "amount": "+%(val)r",\n'
+                         '    "lowerBound": "+%(val)r",\n'
                          '    "unit": "1",\n'
-                         '    "upperBound": %(val)r\n'
+                         '    "upperBound": "+%(val)r"\n'
                          '}' % {'val': 0.044405586})
         self.assertEqual("%r" % q,
                          "WbQuantity(amount=%(val)s, "
@@ -178,9 +189,10 @@
                                                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', })
 
         # test WbQuantity error handling
         self.assertRaises(ValueError, pywikibot.WbQuantity, amount=None,
@@ -627,40 +639,6 @@
         self._test_no_wikitext()
 
 
-class TestDryPageGetNotImplemented(DefaultWikibaseClientTestCase,
-                                   DeprecationTestCase):
-
-    """Test not implement get arguments of WikibasePage classes."""
-
-    dry = True
-
-    def test_base_get_args(self):
-        """Test WikibasePage.get() with sysop argument."""
-        item = WikibasePage(self.repo, 'Q1')
-        # avoid loading anything
-        item._content = {}
-        self.assertRaises(NotImplementedError,
-                          item.get, force=True, sysop=True)
-        self.assertRaises(NotImplementedError,
-                          item.get, force=False, sysop=True)
-        self.assertRaises(NotImplementedError,
-                          item.get, force=False, sysop=False)
-        self.assertRaises(NotImplementedError,
-                          item.get, sysop=True)
-
-    def test_item_get_args(self):
-        """Test ItemPage.get() with sysop argument."""
-        item = pywikibot.ItemPage(self.repo, 'Q1')
-        item._content = {}
-        self.assertRaises(NotImplementedError, item.get, sysop=True)
-
-    def test_property_get_args(self):
-        """Test PropertyPage.get() with sysop argument."""
-        pp = pywikibot.PropertyPage(self.repo, 'P1')
-        pp._content = {}
-        self.assertRaises(NotImplementedError, pp.get, sysop=True)
-
-
 class TestLinks(WikidataTestCase):
 
     """Test cases to test links stored in Wikidata.
@@ -889,7 +867,6 @@
 
     """Test cases to test namespaces of Wikibase entities."""
 
-    cached = False
     dry = True
 
     @classmethod
@@ -1045,47 +1022,56 @@
     """Test deprecated DataSite get_* methods."""
 
     cached = True
+    # These methods are implemented using __getattr__,
+    # so the filename will be detected as site.py,
+    # requiring _do_test_warning_filename = False
 
     def test_get_info(self):
         """Test get_info."""
+        self._do_test_warning_filename = False
         data = self.repo.get_info(60)
-        self.assertOneDeprecation()
+        self.assertDeprecation()
         self.assertIsInstance(data, dict)
         self.assertIn('title', data)
         self.assertEqual(data['title'], 'Q60')
 
     def test_get_labels(self):
         """Test get_labels."""
+        self._do_test_warning_filename = False
         data = self.repo.get_labels(60)
-        self.assertOneDeprecation()
+        self.assertDeprecation()
         self.assertIsInstance(data, dict)
         self.assertIn('en', data)
 
     def test_get_aliases(self):
         """Test get_aliases."""
+        self._do_test_warning_filename = False
         data = self.repo.get_aliases(60)
-        self.assertOneDeprecation()
+        self.assertDeprecation()
         self.assertIsInstance(data, dict)
         self.assertIn('en', data)
 
     def test_get_descriptions(self):
         """Test get_descriptions."""
+        self._do_test_warning_filename = False
         data = self.repo.get_descriptions(60)
-        self.assertOneDeprecation()
+        self.assertDeprecation()
         self.assertIsInstance(data, dict)
         self.assertIn('en', data)
 
     def test_get_sitelinks(self):
         """Test get_sitelinks."""
+        self._do_test_warning_filename = False
         data = self.repo.get_sitelinks(60)
-        self.assertOneDeprecation()
+        self.assertDeprecation()
         self.assertIsInstance(data, dict)
         self.assertIn('enwiki', data)
 
     def test_get_urls(self):
         """Test get_urls."""
+        self._do_test_warning_filename = False
         data = self.repo.get_urls(60)
-        self.assertOneDeprecation()
+        self.assertDeprecation()
         self.assertIsInstance(data, dict)
         self.assertIn('enwiki', data)
 

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

Gerrit-MessageType: newchange
Gerrit-Change-Id: Ie1b9ffbc9c1cc4b5d843469603ebfa1eaa5fd429
Gerrit-PatchSet: 1
Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Owner: Merlijn van Deen <[email protected]>

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

Reply via email to