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
