jenkins-bot has submitted this change and it was merged.
Change subject: Add encode_url to pywikibot.data.api ......................................................................
Add encode_url to pywikibot.data.api
Use it instead of stock urlencode from urllib so that token parameters are moved to the end (and 'wpEditToken' to the very and) to comply with https://www.mediawiki.org/wiki/API:Edit#Parameters
Bug: T85321 Change-Id: Idb24ef35e290b26479fdff328833fab4eb63b5c4 --- M pywikibot/data/api.py M pywikibot/site.py M tests/api_tests.py 3 files changed, 69 insertions(+), 3 deletions(-)
Approvals: John Vandenberg: Looks good to me, approved jenkins-bot: Verified
diff --git a/pywikibot/data/api.py b/pywikibot/data/api.py index 86cf089..efbab77 100644 --- a/pywikibot/data/api.py +++ b/pywikibot/data/api.py @@ -1764,7 +1764,7 @@
URL encodes the parameters provided by _encoded_items() """ - return urlencode(self._encoded_items()) + return encode_url(self._encoded_items())
def __str__(self): """Return a string representation.""" @@ -3028,6 +3028,31 @@ http.cookie_jar.save()
+def encode_url(query): + """ + Encode parameters to pass with a url. + + Reorder parameters so that token parameters go last and call wraps + L{urlencode}. Return an HTTP URL query fragment which complies with + https://www.mediawiki.org/wiki/API:Edit#Parameters + (See the 'token' bullet.) + + @param query: keys and values to be uncoded for passing with a url + @type query: mapping object or a sequence of two-element tuples + @return: encoded parameters with token parameters at the end + @rtype: str + """ + if hasattr(query, 'items'): + query = query.items() + if PY2: + query = [(pair[0], pair[1].encode('utf-8')) for pair in query] + # parameters ending on 'token' should go last + # wpEditToken should go very last + query.sort(key=lambda x: x[0].lower().endswith('token') + + (x[0] == 'wpEditToken')) + return urlencode(query) + + def update_page(page, pagedict, props=[]): """Update attributes of Page object page, based on query data in pagedict.
diff --git a/pywikibot/site.py b/pywikibot/site.py index e4aebcd..0c486d7 100644 --- a/pywikibot/site.py +++ b/pywikibot/site.py @@ -1212,10 +1212,10 @@ """Return Family object for this Site.""" return self.family
- @deprecated("urllib.urlencode()") + @deprecated("pywikibot.data.api.encode_url") def urlEncode(self, query): """DEPRECATED.""" - return urlencode(query) + return api.encode_url(query)
@deprecated('pywikibot.data.api.Request or pywikibot.comms.http.request') @deprecated_args(compress=None, no_hostname=None, cookies_only=None, diff --git a/tests/api_tests.py b/tests/api_tests.py index 481504c..0b06b80 100644 --- a/tests/api_tests.py +++ b/tests/api_tests.py @@ -973,6 +973,47 @@ page.save(summary='Bad token test')
+class TestUrlEncoding(TestCase): + + """Test url_encode.""" + + family = 'wikipedia' + code = 'test' + + def test_url_encoding_from_list(self): + """Test moving 'token' parameters from a list to the end.""" + query = [('action', 'edit'), ('token', 'a'), ('supertoken', 'b'), + ('text', 'text')] + expect = 'action=edit&text=text&token=a&supertoken=b' + result = api.encode_url(query) + self.assertEqual(result, expect) + self.assertIsInstance(result, str) + + def test_url_encoding_from_dict(self): + """Test moving 'token' parameters from a dict to the end.""" + # do not add other keys because dictionary is not deterministic + query = {'supertoken': 'b', 'text': 'text'} + expect = 'text=text&supertoken=b' + result = api.encode_url(query) + self.assertEqual(result, expect) + self.assertIsInstance(result, str) + + def test_url_encoding_from_unicode(self): + """Test encoding unicode values.""" + query = {'token': 'токен'} + expect = 'token=%D1%82%D0%BE%D0%BA%D0%B5%D0%BD' + result = api.encode_url(query) + self.assertEqual(result, expect) + self.assertIsInstance(result, str) + + def test_moving_special_tokens(self): + """Test moving wpEditToken to the very end.""" + query = {'wpEditToken': 'c', 'token': 'b', 'text': 'a'} + expect = 'text=a&token=b&wpEditToken=c' + result = api.encode_url(query) + self.assertEqual(result, expect) + self.assertIsInstance(result, str) + if __name__ == '__main__': try: unittest.main()
pywikibot-commits@lists.wikimedia.org