jenkins-bot has submitted this change and it was merged.
Change subject: [Breaking]Make uncertainties in WbQuantity optional ......................................................................
[Breaking]Make uncertainties in WbQuantity optional
Per T115269/Idd62bdebaa066ba33c4005fa69501e0409adffbc uncertainties should not be added unless explicitly set, also uncertainties cannot be assumed to exist in the API responses.
This patch is [Breaking] in that it: 1. It no longer sets +/- 0 as default (which comes down to the breaking change in WikiBase) 2. It requires the WbQuantity be initialised with a site object (which is due to needing to support older WikiBase versions)
Bug: T150210 Change-Id: Ifbc3fc0f324c7483f0eba092529a05a8f617ec8c --- M pywikibot/__init__.py M pywikibot/page.py M tests/wikibase_edit_tests.py M tests/wikibase_tests.py 4 files changed, 121 insertions(+), 20 deletions(-)
Approvals: Ladsgroup: Looks good to me, approved jenkins-bot: Verified
diff --git a/pywikibot/__init__.py b/pywikibot/__init__.py index 37f03bb..413b036 100644 --- a/pywikibot/__init__.py +++ b/pywikibot/__init__.py @@ -70,6 +70,7 @@ deprecated as __deprecated, deprecate_arg as __deprecate_arg, normalize_username, + MediaWikiVersion, redirect_func, ModuleDeprecationWrapper as _ModuleDeprecationWrapper, PY2, @@ -594,9 +595,29 @@ _items = ('amount', 'upperBound', 'lowerBound', 'unit')
@staticmethod + def _require_errors(site): + """ + Check if the Wikibase site is so old it requires error bounds to be given. + + If no site item is supplied it raises a warning and returns True. + + @param site: The Wikibase site + @type site: pywikibot.site.DataSite + @rtype: bool + """ + if not site: + warning( + "WbQuantity now expects a 'site' parameter. This is needed to " + "ensure correct handling of error bounds.") + return True + return MediaWikiVersion(site.version()) < MediaWikiVersion('1.29.0-wmf.2') + + @staticmethod def _todecimal(value): """ Convert a string to a Decimal for use in WbQuantity. + + None value is returned as is.
@param value: decimal number to convert @type value: str @@ -604,6 +625,8 @@ """ if isinstance(value, Decimal): return value + elif value is None: + return None return Decimal(str(value))
@staticmethod @@ -611,13 +634,17 @@ """ Convert a Decimal to a string representation suitable for WikiBase.
+ None value is returned as is. + @param value: decimal number to convert @type value: Decimal @rtype: str """ + if value is None: + return None return format(value, "+g")
- def __init__(self, amount, unit=None, error=None): + def __init__(self, amount, unit=None, error=None, site=None): u""" Create a new WbQuantity object.
@@ -628,6 +655,8 @@ @param error: the uncertainty of the amount (e.g. ±1) @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. + @param site: The Wikibase site + @type site: pywikibot.site.DataSite """ if amount is None: raise ValueError('no amount given') @@ -637,16 +666,19 @@ self.amount = self._todecimal(amount) self.unit = unit
- if error is None: - upperError = lowerError = Decimal(0) - elif isinstance(error, tuple): - upperError = self._todecimal(error[0]) - lowerError = self._todecimal(error[1]) + if error is None and not self._require_errors(site): + self.upperBound = self.lowerBound = None else: - upperError = lowerError = self._todecimal(error) + if error is None: + self.upperBound = self.lowerBound = 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 + self.upperBound = self.amount + upperError + self.lowerBound = self.amount - lowerError
def toWikibase(self): """ @@ -663,7 +695,7 @@ return json
@classmethod - def fromWikibase(cls, wb): + def fromWikibase(cls, wb, site=None): """ Create a WbQuanity from the JSON data given by the Wikibase API.
@@ -672,10 +704,12 @@ @rtype: pywikibot.WbQuanity """ 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) + upperBound = cls._todecimal(wb.get('upperBound')) + lowerBound = cls._todecimal(wb.get('lowerBound')) + error = None + if (upperBound and lowerBound) or cls._require_errors(site): + error = (upperBound - amount, amount - lowerBound) + return cls(amount, wb['unit'], error, site)
class WbMonolingualText(_WbRepresentation): diff --git a/pywikibot/page.py b/pywikibot/page.py index 165e7c8..30a1bb0 100644 --- a/pywikibot/page.py +++ b/pywikibot/page.py @@ -4279,7 +4279,7 @@ FilePage(pywikibot.Site('commons', 'commons'), value), 'globe-coordinate': pywikibot.Coordinate.fromWikibase, 'time': lambda value, site: pywikibot.WbTime.fromWikibase(value), - 'quantity': lambda value, site: pywikibot.WbQuantity.fromWikibase(value), + 'quantity': pywikibot.WbQuantity.fromWikibase, 'monolingualtext': lambda value, site: pywikibot.WbMonolingualText.fromWikibase(value) } diff --git a/tests/wikibase_edit_tests.py b/tests/wikibase_edit_tests.py index 59bcd29..a1e0f32 100644 --- a/tests/wikibase_edit_tests.py +++ b/tests/wikibase_edit_tests.py @@ -18,6 +18,7 @@ import time
import pywikibot +from pywikibot.tools import MediaWikiVersion
from tests.aspects import unittest, WikibaseTestCase
@@ -238,6 +239,28 @@ claim = item.claims['P271'][0] self.assertEqual(claim.getTarget(), target)
+ def test_WbQuantity_edit_unbound(self): + """Attempt adding a quantity with unbound errors.""" + # Clean the slate in preparation for test. + testsite = self.get_repo() + item = self._clean_item(testsite, 'P69') + + # Make sure the wiki supports wikibase-conceptbaseuri + if MediaWikiVersion(testsite.version()) < MediaWikiVersion('1.29.0-wmf.2'): + raise unittest.SkipTest('Wiki version must be 1.29.0-wmf.2 or ' + 'newer to support unbound uncertainties.') + + # set new claim + claim = pywikibot.page.Claim(testsite, 'P69', datatype='quantity') + target = pywikibot.WbQuantity(amount=1234) + claim.setTarget(target) + item.addClaim(claim) + + # confirm new claim + item.get(force=True) + claim = item.claims['P69'][0] + self.assertEqual(claim.getTarget(), target) + def test_identifier_edit(self): """Attempt adding a math claim with valid input.""" testsite = self.get_repo() diff --git a/tests/wikibase_tests.py b/tests/wikibase_tests.py index f428475..c746c36 100644 --- a/tests/wikibase_tests.py +++ b/tests/wikibase_tests.py @@ -20,6 +20,7 @@ from pywikibot import pagegenerators from pywikibot.page import WikibasePage, ItemPage from pywikibot.site import Namespace, NamespacesDict +from pywikibot.tools import MediaWikiVersion
from tests import join_pages_path from tests.aspects import ( @@ -186,7 +187,7 @@
def test_WbQuantity_float_27(self): """Test WbQuantity for float value.""" - q = pywikibot.WbQuantity(amount=0.044405586) + q = pywikibot.WbQuantity(amount=0.044405586, error=0.0) q_dict = {'amount': '+0.044405586', 'lowerBound': '+0.044405586', 'upperBound': '+0.044405586', 'unit': '1', } self.assertEqual(q.toWikibase(), q_dict) @@ -200,21 +201,32 @@
def test_WbQuantity_decimal(self): """Test WbQuantity for decimal value.""" - q = pywikibot.WbQuantity(amount=Decimal('0.044405586')) + q = pywikibot.WbQuantity(amount=Decimal('0.044405586'), + error=Decimal('0.0')) q_dict = {'amount': '+0.044405586', 'lowerBound': '+0.044405586', 'upperBound': '+0.044405586', 'unit': '1', } self.assertEqual(q.toWikibase(), q_dict)
def test_WbQuantity_string(self): """Test WbQuantity for decimal notation.""" - q = pywikibot.WbQuantity(amount='0.044405586') + q = pywikibot.WbQuantity(amount='0.044405586', error='0') q_dict = {'amount': '+0.044405586', 'lowerBound': '+0.044405586', 'upperBound': '+0.044405586', 'unit': '1', } self.assertEqual(q.toWikibase(), q_dict)
+ def test_WbQuantity_unbound(self): + """Test WbQuantity for integer value without bounds.""" + repo = self.get_repo() + if MediaWikiVersion(repo.version()) < MediaWikiVersion('1.29.0-wmf.2'): + raise unittest.SkipTest('Wiki version must be 1.29.0-wmf.2 or ' + 'newer to support unbound uncertainties.') + q = pywikibot.WbQuantity(amount=1234.5, site=repo) + self.assertEqual(q.toWikibase(), + {'amount': '+1234.5', 'unit': '1', }) + def test_WbQuantity_formatting(self): """Test other WbQuantity methods.""" - q = pywikibot.WbQuantity(amount='0.044405586') + q = pywikibot.WbQuantity(amount='0.044405586', error='0') self.assertEqual("%s" % q, '{\n' ' "amount": "+%(val)s",\n' @@ -227,9 +239,28 @@ "upperBound=%(val)s, lowerBound=%(val)s, " "unit=1)" % {'val': '0.044405586'})
+ def test_WbQuantity_formatting_unbound(self): + """Test WbQuantity formatting without bounds.""" + repo = self.get_repo() + if MediaWikiVersion(repo.version()) < MediaWikiVersion('1.29.0-wmf.2'): + raise unittest.SkipTest('Wiki version must be 1.29.0-wmf.2 or ' + 'newer to support unbound uncertainties.') + q = pywikibot.WbQuantity(amount='0.044405586', site=repo) + self.assertEqual("%s" % q, + '{\n' + ' "amount": "+%(val)s",\n' + ' "lowerBound": null,\n' + ' "unit": "1",\n' + ' "upperBound": null\n' + '}' % {'val': '0.044405586'}) + self.assertEqual("%r" % q, + "WbQuantity(amount=%(val)s, " + "upperBound=None, lowerBound=None, " + "unit=1)" % {'val': '0.044405586'}) + def test_WbQuantity_equality(self): """Test WbQuantity equality.""" - q = pywikibot.WbQuantity(amount='0.044405586') + q = pywikibot.WbQuantity(amount='0.044405586', error='0') self.assertEqual(q, q)
def test_WbQuantity_fromWikibase(self): @@ -243,6 +274,19 @@ {'amount': '+0.0229', 'lowerBound': '+0.0000', 'upperBound': '+1.0000', 'unit': '1', })
+ def test_WbQuantity_fromWikibase_unbound(self): + """Test WbQuantity.fromWikibase() instantiating without bounds.""" + repo = self.get_repo() + if MediaWikiVersion(repo.version()) < MediaWikiVersion('1.29.0-wmf.2'): + raise unittest.SkipTest('Wiki version must be 1.29.0-wmf.2 or ' + 'newer to support unbound uncertainties.') + q = pywikibot.WbQuantity.fromWikibase({u'amount': u'+0.0229', + u'unit': u'1'}, + site=repo) + self.assertEqual(q.toWikibase(), + {'amount': '+0.0229', 'lowerBound': None, + 'upperBound': None, 'unit': '1', }) + def test_WbQuantity_errors(self): """Test WbQuantity error handling.""" self.assertRaises(ValueError, pywikibot.WbQuantity, amount=None,
pywikibot-commits@lists.wikimedia.org