From ac6a6dbca308eed1edb16f87d288e9b10b21555b Mon Sep 17 00:00:00 2001 From: Baptiste Fontaine Date: Fri, 15 May 2020 14:10:28 +0200 Subject: [PATCH] encode URL path parts to avoid issues with funky titles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See e.g. user '?renommé201306261613' on frwiki. --- tests/test_base.py | 1 + tests/test_page.py | 2 +- tests/test_user.py | 10 +++++++++- xtools/base.py | 3 ++- xtools/page.py | 11 +++++++++-- 5 files changed, 22 insertions(+), 5 deletions(-) diff --git a/tests/test_base.py b/tests/test_base.py index a35c67d..ceaf444 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -27,6 +27,7 @@ def test_build_path(self): ("/x/2000-01-01/a", "/{a}/{start}/{b}", [("a", "", "x"), ("start", "", ""), ("b", "a", "x")]), ("/x/2050-12-31/a", "/{a}/{end}/{b}", [("a", "", "x"), ("end", "", ""), ("b", "a", "x")]), + ("/x/%3F", "/{a}/{b}", [("a", "", "x"), ("b", "?", "")]), ): self.assertEqual(expected, base.build_path(path_format, path_params), path_format) diff --git a/tests/test_page.py b/tests/test_page.py index a97689a..3d529ab 100644 --- a/tests/test_page.py +++ b/tests/test_page.py @@ -20,7 +20,7 @@ def test_simple_info(self): self.assertEqual(response, fn("en.wikipedia.org", "Albert_Einstein")) - m.get(prefix + "/en.wikipedia.org/i don't exist lol", + m.get(prefix + "/en.wikipedia.org/i%20don%27t%20exist%20lol", json={"error": "No matching results found for: i don't exist lol"}, status_code=404) self.assertRaises(exceptions.NotFound, diff --git a/tests/test_user.py b/tests/test_user.py index 8c946e7..c847966 100644 --- a/tests/test_user.py +++ b/tests/test_user.py @@ -52,4 +52,12 @@ def test_pages_created_non_existing_user(self): self.assertRaises(exceptions.NotFound, lambda: user.pages_created("project1", "foo")) self.assertRaises(exceptions.NotFound, - lambda: next(user.pages_created_iter("project1", "foo"))) \ No newline at end of file + lambda: next(user.pages_created_iter("project1", "foo"))) + + def test_pages_created_funky_username(self): + with requests_mock.Mocker() as m: + m.get("m://x/user/pages/project1/%3Fhello", json={"pages": {}}) + self.assertEqual({"pages": []}, + user.pages_created("project1", "?hello")) + self.assertEqual([], + list(user.pages_created_iter("project1", "?hello"))) \ No newline at end of file diff --git a/xtools/base.py b/xtools/base.py index cce5cdd..787fe13 100644 --- a/xtools/base.py +++ b/xtools/base.py @@ -4,6 +4,7 @@ from typing import Optional, Tuple, Any, Sequence from json.decoder import JSONDecodeError +from urllib.parse import quote as urlquote import time import requests @@ -122,7 +123,7 @@ def has_more(index): elif name == "end": param_value = END_TIME - params_dict[name] = param_value + params_dict[name] = urlquote(param_value) path = path_format.format(**params_dict).rstrip("/") return path \ No newline at end of file diff --git a/xtools/page.py b/xtools/page.py index 0c1aab2..759bd59 100644 --- a/xtools/page.py +++ b/xtools/page.py @@ -11,7 +11,11 @@ def _get_page_dict(what: str, project: str, article: str) -> dict: - return base.get("/page/%s/%s/%s" % (what, project, article)) + return base.get(base.build_path("/page/{what}/{project}/{article}", ( + ("what", what, ""), + ("project", project, ""), + ("article", article, ""), + ))) def article_info(project: str, article: str) -> dict: @@ -171,4 +175,7 @@ def assessments(project: str, articles: Sequence[str], if class_only: params["classonly"] = "1" - return base.get("/page/assessments/%s/%s" % (project, "|".join(articles)), params) \ No newline at end of file + return base.get(base.build_path("/page/assessments/{project}/{articles}", ( + ("project", project, ""), + ("articles", "|".join(articles), ""), + )), params) \ No newline at end of file