diff --git a/.travis.yml b/.travis.yml index 8342cf946..248cc09f3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,22 +1,20 @@ language: python sudo: false +dist: xenial +os: linux matrix: include: - - python: 2.7 - os: linux - dist: xenial - env: TOXENV=py27 - - python: pypy - os: linux - dist: xenial - env: TOXENV=pypy - - python: 2.7 - os: linux - dist: xenial + - python: 3.5 + env: TOXENV=py35 + - python: 3.6 + env: TOXENV=py36 + - python: 3.7 + env: TOXENV=py37 + - python: 3.8-dev + env: TOXENV=py38 + - python: 3.5 env: TOXENV=flake8 - allow_failures: - - python: pypy install: - pip install -r requirements.d/development.txt diff --git a/_ui_tests/conftest.py b/_ui_tests/conftest.py index 8523020b6..e71be607c 100644 --- a/_ui_tests/conftest.py +++ b/_ui_tests/conftest.py @@ -29,4 +29,4 @@ def pytest_runtest_makereport(item, call): if call.excinfo is not None: if driver_register.get_driver() is not None and hasattr(item, 'obj'): driver_register.get_driver().get_screenshot_as_file( - unicode(item.obj).split(u" ")[2] + u'.png') + str(item.obj).split(" ")[2] + '.png') diff --git a/_ui_tests/test_subitems.py b/_ui_tests/test_subitems.py index 3ed907f3e..6233ed64d 100644 --- a/_ui_tests/test_subitems.py +++ b/_ui_tests/test_subitems.py @@ -7,27 +7,27 @@ import utils -class TestSubitems(object): +class TestSubitems: """Functional test: create subitem""" def setup_class(self): """opens browser and creates some random item names for these tests""" self.driver = utils.create_browser() self.base_url = config.BASE_URL - self.base_item_name = u"page_" + utils.generate_random_word(5) - self.subitem_name = u"subitem_" + utils.generate_random_word(5) + self.base_item_name = "page_" + utils.generate_random_word(5) + self.subitem_name = "subitem_" + utils.generate_random_word(5) def create_wiki_item(self, item_name): """Creates a new wiki item with name 'item_name'""" driver = self.driver - driver.get(self.base_url + u"/" + item_name) - driver.find_element_by_link_text(u"Default").click() - driver.find_element_by_link_text(u"Wiki (MoinMoin)").click() + driver.get(self.base_url + "/" + item_name) + driver.find_element_by_link_text("Default").click() + driver.find_element_by_link_text("Wiki (MoinMoin)").click() driver.find_element_by_link_text( - u"create the item from scratch").click() + "create the item from scratch").click() driver.find_element_by_id("f_content_form_data_text").send_keys( - u"This is a test item\n") + "This is a test item\n") driver.find_element_by_id("f_submit").click() def test_createsubitem(self): @@ -36,28 +36,28 @@ def test_createsubitem(self): self.create_wiki_item(self.base_item_name) - driver.get(self.base_url + u"/" + self.base_item_name) - driver.find_element_by_link_text(u"Modify").click() - driver.find_element_by_id(u"f_content_form_data_text").send_keys( - u"\n[[/" + self.subitem_name + "]]\n") - driver.find_element_by_id(u"f_submit").click() - driver.find_element_by_link_text(u"/" + self.subitem_name).click() - driver.find_element_by_link_text(u"Default").click() - driver.find_element_by_link_text(u"Wiki (MoinMoin)").click() - driver.find_element_by_link_text(u"create the item from scratch").click() - driver.find_element_by_id(u"f_content_form_data_text").send_keys( - u"This is a test subitem") - driver.find_element_by_id(u"f_submit").click() - assert u"This is a test subitem" in driver.find_element_by_id( - u"moin-content-data").text - assert driver.title.split(u" - ")[0] == self.base_item_name + \ - u"/" + self.subitem_name + driver.get(self.base_url + "/" + self.base_item_name) + driver.find_element_by_link_text("Modify").click() + driver.find_element_by_id("f_content_form_data_text").send_keys( + "\n[[/" + self.subitem_name + "]]\n") + driver.find_element_by_id("f_submit").click() + driver.find_element_by_link_text("/" + self.subitem_name).click() + driver.find_element_by_link_text("Default").click() + driver.find_element_by_link_text("Wiki (MoinMoin)").click() + driver.find_element_by_link_text("create the item from scratch").click() + driver.find_element_by_id("f_content_form_data_text").send_keys( + "This is a test subitem") + driver.find_element_by_id("f_submit").click() + assert "This is a test subitem" in driver.find_element_by_id( + "moin-content-data").text + assert driver.title.split(" - ")[0] == self.base_item_name + \ + "/" + self.subitem_name def teardown_class(self): """shuts down browser""" self.driver.quit() -if __name__ == u'__main__': +if __name__ == '__main__': # This lets us run the test directly, without using py.test # This is useful for example for being able to call help, eg # 'help(driver)', or 'help(driver.find_element_by_id("f_submit"))' diff --git a/_ui_tests/utils.py b/_ui_tests/utils.py index 7241bc3f4..849adb9fa 100644 --- a/_ui_tests/utils.py +++ b/_ui_tests/utils.py @@ -5,7 +5,7 @@ """Functions to facilitate functional testing""" import random -import urllib +import urllib.request, urllib.parse, urllib.error import pytest @@ -15,7 +15,7 @@ import config try: - f = urllib.urlopen(config.BASE_URL) + f = urllib.request.urlopen(config.BASE_URL) except IOError: pytestmark = pytest.mark.skip('The UI tests need a wiki server running on %s' % config.BASE_URL) @@ -41,7 +41,7 @@ def generate_random_word(length): """ generates a random string containing numbers, of length 'length' """ - word = unicode(random.randint(10 ** (length - 1), 10 ** length)) + word = str(random.randint(10 ** (length - 1), 10 ** length)) return word diff --git a/contrib/loadtesting/locust/locustfile.py b/contrib/loadtesting/locust/locustfile.py index 556d718a7..2038c783f 100644 --- a/contrib/loadtesting/locust/locustfile.py +++ b/contrib/loadtesting/locust/locustfile.py @@ -15,6 +15,11 @@ and create wiki items as part of the test. It is best to start with an empty wiki (./m new-wiki). +Each locust registers a new id, creates and updates a home page in the user namespace, +creates and updates a item in the default namespace, and logs-out. Because +each locust is working on unique items, it does not test edit locking. Use locustfile2.py +for stress testing edit locking. + To load test Moin2: * read about Locust at https://docs.locust.io/en/stable/index.html * install Locust per the docs @@ -39,15 +44,14 @@ import sys import argparse -import urllib -import urllib2 +import urllib.request, urllib.error, urllib.parse from locust import HttpLocust, Locust, TaskSet, TaskSequence, task, seq_task user_number = 0 # test content to put load on indexer -extra_content = u""" +extra_content = """ = %s Home Page = == Writing a locustfile == @@ -98,7 +102,7 @@ def get_home(self): # Home and users/Home have been created by setup, see below response = self.client.get("/Home") if response.status_code != 200: - print '%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code) + print('%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code)) @seq_task(1) def click_login(self): @@ -107,16 +111,16 @@ def click_login(self): self.user_name = 'JohnDoe' + str(user_number) self.user_email = self.user_name + '@john.doe' self.user_home_page = '/users/' + self.user_name - print '==== starting user = %s ====' % self.user_name + print('==== starting user = %s ====' % self.user_name) response = self.client.get("/+login") if response.status_code != 200: - print '%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code) + print('%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code)) @seq_task(2) def click_register(self): response = self.client.get("/+register") if response.status_code != 200: - print '%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code) + print('%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code)) @seq_task(3) def click_post_register(self): @@ -127,13 +131,13 @@ def click_post_register(self): "register_email": self.user_email, }) if response.status_code != 200: - print '%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code) + print('%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code)) @seq_task(4) def click_login_again(self): response = self.client.get("/+login") if response.status_code != 200: - print '%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code) + print('%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code)) @seq_task(5) def click_post_login(self): @@ -144,7 +148,7 @@ def click_post_login(self): "login_nexturl": "/Home", }) if response.status_code != 200: - print '%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code) + print('%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code)) @seq_task(6) def create_home_page(self): @@ -153,12 +157,12 @@ def create_home_page(self): if response.status_code == 404: response.success() else: - print '%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code) + print('%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code)) # click MoinMoin markup link home_page_get = '/+modify/users/' + self.user_name + '?contenttype=text%2Fx.moin.wiki%3Bcharset%3Dutf-8&itemtype=default' response = self.client.get(home_page_get) if response.status_code != 200: - print '%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code) + print('%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code)) # complete form and post new_content = '= %s =\n\nMy Home Page created by Locust' % self.user_name home_page_post = '/+modify/users/' + self.user_name + '?contenttype=text%2Fx.moin.wiki%3Bcharset%3Dutf-8&itemtype=default&template=' @@ -174,7 +178,7 @@ def create_home_page(self): "extra_meta_text": '{"namespace": "users","rev_number": 1}', }) if response.status_code != 200: - print '%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code) + print('%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code)) @seq_task(7) @task(4) @@ -182,18 +186,18 @@ def modify_home_page(self): # get users home page response = self.client.get(self.user_home_page) if response.status_code != 200: - print '%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code) + print('%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code)) # click modify link home_page = '/+modify' + self.user_home_page + '?contenttype=text%2Fx.moin.wiki%3Bcharset%3Dutf-8&itemtype=default&template=' response = self.client.get(home_page) if response.status_code != 200: - print '%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code) + print('%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code)) # update the page response = self.client.post(home_page, {"content_form_data_text": extra_content % self.user_name, "comment": "my homepage comment", "submit": "OK", - u'meta_form_contenttype': u'text/x.moin.wiki;charset=utf-8', + 'meta_form_contenttype': 'text/x.moin.wiki;charset=utf-8', "meta_form_itemtype": "default", "meta_form_acl": "None", "meta_form_tags": "apple, pear", @@ -201,7 +205,7 @@ def modify_home_page(self): "extra_meta_text": '{"namespace": "users","rev_number":1}', }) if response.status_code != 200: - print '%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code) + print('%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code)) @seq_task(8) def create_new_page(self): @@ -211,12 +215,12 @@ def create_new_page(self): if response.status_code == 404: response.success() else: - print '%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code) + print('%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code)) # click MoinMoin markup link page_get = '/+modify/' + new_item_name + '?contenttype=text%2Fx.moin.wiki%3Bcharset%3Dutf-8&itemtype=default' response = self.client.get(page_get) if response.status_code != 200: - print '%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code) + print('%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code)) # complete form and post new_content = '= %s =\n\nNew Item created by Locust' % new_item_name new_page_post = '/+modify/' + new_item_name + '?contenttype=text%2Fx.moin.wiki%3Bcharset%3Dutf-8&itemtype=default&template=' @@ -232,7 +236,7 @@ def create_new_page(self): "extra_meta_text": '{"namespace": "","rev_number": 1}', }) if response.status_code != 200: - print '%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code) + print('%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code)) @seq_task(9) @task(10) @@ -241,12 +245,12 @@ def modify_new_page(self): new_item_name = 'Locust-' + self.user_name response = self.client.get('/' + new_item_name) if response.status_code != 200: - print '%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code) + print('%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code)) # click MoinMoin markup link page_get = '/+modify/' + new_item_name + '?contenttype=text%2Fx.moin.wiki%3Bcharset%3Dutf-8&itemtype=default' response = self.client.get(page_get) if response.status_code != 200: - print '%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code) + print('%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code)) # complete form and post new_content = '= %s =\n\nNew Item created by Locust' % new_item_name new_page_post = '/+modify/' + new_item_name + '?contenttype=text%2Fx.moin.wiki%3Bcharset%3Dutf-8&itemtype=default&template=' @@ -262,13 +266,13 @@ def modify_new_page(self): "extra_meta_text": '{"namespace": "","rev_number": 1}', }) if response.status_code != 200: - print '%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code) + print('%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code)) @seq_task(10) def logout(self): - response = self.client.get(u"/+logout?logout_submit=1") + response = self.client.get("/+logout?logout_submit=1") if response.status_code != 200: - print '%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code) + print('%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code)) class WebsiteUser(HttpLocust): @@ -287,29 +291,31 @@ def setup(self): if host.endswith('/'): host = host[:-1] - print '==== creating Home and users/Home ====' - url = host + u"/+modify/users/Home?contenttype=text%2Fx.moin.wiki%3Bcharset%3Dutf-8&itemtype=default&template=" - data = urllib.urlencode({"content_form_data_text": "= users/Home =\n * created by Locust", + print('==== creating Home and users/Home ====') + url = host + "/+modify/users/Home?contenttype=text%2Fx.moin.wiki%3Bcharset%3Dutf-8&itemtype=default&template=" + data = urllib.parse.urlencode({"content_form_data_text": "= users/Home =\n * created by Locust", "comment": "", "submit": "OK", - u'meta_form_contenttype': u'text/x.moin.wiki;charset=utf-8', + 'meta_form_contenttype': 'text/x.moin.wiki;charset=utf-8', "meta_form_itemtype": "default", "meta_form_acl": "None", "meta_form_tags": "None", "meta_form_name": "Home", "extra_meta_text": '{"namespace": "users","rev_number": 1}', }) - content = urllib2.urlopen(url=url, data=data).read() + data = data.encode('utf-8') + content = urllib.request.urlopen(url=url, data=data).read() - url = host + u"/+modify/Home?contenttype=text%2Fx.moin.wiki%3Bcharset%3Dutf-8&itemtype=default&template=" - data = urllib.urlencode({"content_form_data_text": "= Home =\n * created by Locust", + url = host + "/+modify/Home?contenttype=text%2Fx.moin.wiki%3Bcharset%3Dutf-8&itemtype=default&template=" + data = urllib.parse.urlencode({"content_form_data_text": "= Home =\n * created by Locust", "comment": "", "submit": "OK", - u'meta_form_contenttype': u'text/x.moin.wiki;charset=utf-8', + 'meta_form_contenttype': 'text/x.moin.wiki;charset=utf-8', "meta_form_itemtype": "default", "meta_form_acl": "None", "meta_form_tags": "None", "meta_form_name": "Home", "extra_meta_text": '{"namespace": "","rev_number": 1}', }) - content = urllib2.urlopen(url=url, data=data).read() + data = data.encode('utf-8') + content = urllib.request.urlopen(url=url, data=data).read() diff --git a/contrib/loadtesting/locust/locustfile2.py b/contrib/loadtesting/locust/locustfile2.py index 8df3d6233..8a9618e71 100644 --- a/contrib/loadtesting/locust/locustfile2.py +++ b/contrib/loadtesting/locust/locustfile2.py @@ -15,6 +15,13 @@ and each user will update the Home item multiple times. It is best to start with an empty wiki (./m new-wiki). +Each locust will attempt to update the Home item 10 times. If the Home item is +locked by another user, a fail message is created and saved for the next update +attempt. If the Home item is available for update, all fail messages and a success +message is added to the Home item. If the update for a locust fails on the 10th +attempt, then a new item is created and all accumulated fail +messages are written to the file. + To load test Moin2: * read about Locust at https://docs.locust.io/en/stable/index.html * install Locust per the docs @@ -42,8 +49,7 @@ import sys import argparse -import urllib -import urllib2 +import urllib.request, urllib.parse, urllib.error import datetime from locust import HttpLocust, Locust, TaskSet, TaskSequence, task, seq_task @@ -81,7 +87,7 @@ def get_home(self): # Home and users/Home have been created by setup, see below response = self.client.get("/Home") if response.status_code != 200: - print '%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code) + print('%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code)) @seq_task(1) def click_login(self): @@ -90,16 +96,16 @@ def click_login(self): self.user_name = 'JohnDoe' + str(user_number) self.user_email = self.user_name + '@john.doe' self.user_home_page = '/users/' + self.user_name - print '==== starting user = %s ====' % self.user_name + print('==== starting user = %s ====' % self.user_name) response = self.client.get("/+login") if response.status_code != 200: - print '%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code) + print('%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code)) @seq_task(2) def click_register(self): response = self.client.get("/+register") if response.status_code != 200: - print '%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code) + print('%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code)) @seq_task(3) def click_post_register(self): @@ -110,13 +116,13 @@ def click_post_register(self): "register_email": self.user_email, }) if response.status_code != 200: - print '%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code) + print('%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code)) @seq_task(4) def click_login_again(self): response = self.client.get("/+login") if response.status_code != 200: - print '%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code) + print('%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code)) @seq_task(5) def click_post_login(self): @@ -127,7 +133,7 @@ def click_post_login(self): "login_nexturl": "/Home", }) if response.status_code != 200: - print '%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code) + print('%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code)) @seq_task(6) @task(10) @@ -138,18 +144,19 @@ def update_home_page(self): item_name = 'Home' response = self.client.get('/' + item_name) if response.status_code != 200: - print '%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code) + print('%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code)) # click modify link page_get = '/+modify/' + item_name + '?contenttype=text%2Fx.moin.wiki%3Bcharset%3Dutf-8&itemtype=default' response = self.client.get(page_get) if response.status_code != 200: - print '%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code) - if 'is locked by' in response.content: + print('%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code)) + if b'is locked by' in response.content: # someone else has item locked for editing self.message += '\n\n%s Item %s is locked, Locust user %s cannot do change number %s' % (dt, item_name, self.user_name, self.count) return - textarea_data = get_textarea(response.content) + self.message + content = response.content.decode('utf-8') + textarea_data = get_textarea(content) + self.message self.message = '' # complete form and post new_content = '%s\n\n%s Item %s updated by Locust user %s change number = %s' % (textarea_data, dt, item_name, self.user_name, self.count) @@ -168,8 +175,8 @@ def update_home_page(self): "extra_meta_text": '{"namespace": ""}', }) if response.status_code != 200: - print '%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code) - assert 'Deletions are marked like this.' in response.content + print('%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code)) + assert b'Deletions are marked like this.' in response.content # do save response = self.client.post(new_page_post, @@ -184,7 +191,7 @@ def update_home_page(self): "extra_meta_text": '{"namespace": ""}', }) if response.status_code != 200: - print '%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code) + print('%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code)) @seq_task(7) def save_messages(self): @@ -192,7 +199,7 @@ def save_messages(self): if self.message: response = self.client.get('/+modify/' + self.user_name + '?contenttype=text%2Fx.moin.wiki%3Bcharset%3Dutf-8&itemtype=default') if response.status_code != 200: - print '%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code) + print('%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code)) # complete form and post new_content = '= %s =\n\n== Unsaved locked out messages ==%s' % (self.user_name, self.message) home_page_post = '/+modify/' + self.user_name + '?contenttype=text%2Fx.moin.wiki%3Bcharset%3Dutf-8&itemtype=default&template=' @@ -208,13 +215,13 @@ def save_messages(self): "extra_meta_text": '{"namespace": "","rev_number": 1}', }) if response.status_code != 200: - print '%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code) + print('%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code)) @seq_task(8) def logout(self): - response = self.client.get(u"/+logout?logout_submit=1") + response = self.client.get("/+logout?logout_submit=1") if response.status_code != 200: - print '%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code) + print('%s: response.status_code = %s' % (sys._getframe().f_lineno, response.status_code)) class WebsiteUser(HttpLocust): @@ -233,16 +240,17 @@ def setup(self): if host.endswith('/'): host = host[:-1] - print '==== creating Home ====' - url = host + u"/+modify/Home?contenttype=text%2Fx.moin.wiki%3Bcharset%3Dutf-8&itemtype=default&template=" - data = urllib.urlencode({"content_form_data_text": "= Home =\n * created by Locust", + print('==== creating Home ====') + url = host + "/+modify/Home?contenttype=text%2Fx.moin.wiki%3Bcharset%3Dutf-8&itemtype=default&template=" + data = urllib.parse.urlencode({"content_form_data_text": "= Home =\n * created by Locust", "comment": "", "submit": "OK", - u'meta_form_contenttype': u'text/x.moin.wiki;charset=utf-8', + 'meta_form_contenttype': 'text/x.moin.wiki;charset=utf-8', "meta_form_itemtype": "default", "meta_form_acl": "None", "meta_form_tags": "None", "meta_form_name": "Home", "extra_meta_text": '{"namespace": "","rev_number": 1}', }) - content = urllib2.urlopen(url=url, data=data).read() + data = data.encode('utf-8') + content = urllib.request.urlopen(url=url, data=data).read() diff --git a/contrib/pythonanywhere/_README.txt b/contrib/pythonanywhere/_README.txt index 81b4da98c..29ed6acb6 100644 --- a/contrib/pythonanywhere/_README.txt +++ b/contrib/pythonanywhere/_README.txt @@ -58,7 +58,7 @@ On the pop up labeled "Your web app's domain name", click the Next button in the On the popup labeled "Select a Python Web framework" click "Manual configuration". -On the popup labeled "Select a Python version" select "Python 2.7". +On the popup labeled "Select a Python version" select "Python 3.5". On the "Manual Configuration" popup, click Next near lower right. That should bring you here: @@ -86,7 +86,7 @@ The code and Virtual env sections should look similar to: WSGI configuration file: /var/www/moinmoin2_pythonanywhere_com_wsgi.py - Python version: 2.7 + Python version: 3.5 Virtualenv: diff --git a/contrib/pythonanywhere/wsgi.py b/contrib/pythonanywhere/wsgi.py index 69e6da240..1882336e9 100644 --- a/contrib/pythonanywhere/wsgi.py +++ b/contrib/pythonanywhere/wsgi.py @@ -4,7 +4,7 @@ this_dir = os.path.dirname(os.path.abspath(__file__)) -site.addsitedir(this_dir + '-venv-python2.7/lib/python2.7/site-packages') +site.addsitedir(this_dir + '-venv-python3.5/lib/python3.5/site-packages') # make sure this directory is in sys.path (.lower() avoids duplicate entries in windows) if not (this_dir in sys.path or this_dir.lower() in sys.path): diff --git a/contrib/wikiconfig/wikiconfig_editme.py b/contrib/wikiconfig/wikiconfig_editme.py index 05e819609..e1eef1f05 100644 --- a/contrib/wikiconfig/wikiconfig_editme.py +++ b/contrib/wikiconfig/wikiconfig_editme.py @@ -28,14 +28,14 @@ class LocalConfig(Config): data_dir = os.path.join(instance_dir, 'data') # sitename is displayed in heading of all wiki pages - sitename = u'My MoinMoin' + sitename = 'My MoinMoin' # default root, use this to change the name of the default page # default_root = u'Home' # FrontPage, Main, etc # options for new user registration registration_only_by_superuser = True # if email configured, superuser can do > Admin > Register New User - registration_hint = u'To request an account, see bottom of Home page.' + registration_hint = 'To request an account, see bottom of Home page.' # to create a new user without configuring email, use the terminal/command/bash window # . activate # windows: activate # moin account-create -n JaneDoe -e j@jane.doe -p secretpasssword @@ -62,18 +62,18 @@ class LocalConfig(Config): namespace_mapping, backend_mapping, acl_mapping = create_simple_mapping( uri='stores:fs:{0}/%(backend)s/%(kind)s'.format(data_dir), - default_acl=dict(before=u'JaneDoe,JoeDoe:read,write,create,destroy,admin', - default=u'Known:read,write,create,destroy,admin All:read', - after=u'', + default_acl=dict(before='JaneDoe,JoeDoe:read,write,create,destroy,admin', + default='Known:read,write,create,destroy,admin All:read', + after='', hierarchic=False, ), - users_acl=dict(before=u'JaneDoe,JoeDoe:read,write,create,destroy,admin', - default=u'Known:read,write,create,destroy,admin All:read', - after=u'', + users_acl=dict(before='JaneDoe,JoeDoe:read,write,create,destroy,admin', + default='Known:read,write,create,destroy,admin All:read', + after='', hierarchic=False, ), # userprofiles contain only metadata, no content will be created - userprofiles_acl=dict(before=u'All:', - default=u'', - after=u'', + userprofiles_acl=dict(before='All:', + default='', + after='', hierarchic=False, ), ) diff --git a/contrib/wsgi/moin2.wsgi b/contrib/wsgi/moin2.wsgi index ee2ee2713..2127e0e94 100644 --- a/contrib/wsgi/moin2.wsgi +++ b/contrib/wsgi/moin2.wsgi @@ -26,7 +26,7 @@ moin_dir = '/path/to/moin/dir/where/wikiconfig.py/is/located' if sys.platform == 'win32': site.addsitedir(moin_dir + '-venv-python/Lib/site-packages') else: - site.addsitedir(moin_dir + '-venv-python/lib/python2.7/site-packages') + site.addsitedir(moin_dir + '-venv-python/lib/python3.5/site-packages') # make sure this directory is in sys.path (.lower() avoids duplicate entries on Windows) if not (moin_dir in sys.path or moin_dir.lower() in sys.path): diff --git a/contrib/wsgi/profiler.py b/contrib/wsgi/profiler.py index 620205643..38b1f078f 100644 --- a/contrib/wsgi/profiler.py +++ b/contrib/wsgi/profiler.py @@ -24,7 +24,7 @@ logging = log.getLogger(__name__) -class ProfilerMiddleware(object): +class ProfilerMiddleware: """ Abstract base class for profiling middlewares. Concrete implementations of this class should provide implementations @@ -44,7 +44,7 @@ def profile(self, environ, start_response): logging.debug("Profiling call for '%s %s'", method, url) try: res = self.run_profile(self.app, (environ, start_response)) - except Exception, e: + except Exception as e: logging.exception("Exception while profiling '%s %s'", method, url) raise return res @@ -77,18 +77,6 @@ def shutdown(self): self._profile.dump_stats(self._filename) -class HotshotMiddleware(ProfilerMiddleware): - """ A profiler based on the more recent hotshot module from the stdlib. """ - def __init__(self, app, *args, **kwargs): - super(HotshotMiddleware, self).__init__(app) - import hotshot - self._profile = hotshot.Profile(*args, **kwargs) - self.run_profile = self._profile.runcall - - def shutdown(self): - self._profile.close() - - class PycallgraphMiddleware(ProfilerMiddleware): """ A call graphing middleware utilizing the pycallgraph 3rd party module (available at http://pycallgraph.slowchop.com/). """ diff --git a/contrib/wsgi/proxy.py b/contrib/wsgi/proxy.py index aea54eaeb..f4856dc18 100644 --- a/contrib/wsgi/proxy.py +++ b/contrib/wsgi/proxy.py @@ -9,7 +9,7 @@ """ -class ProxyTrust(object): +class ProxyTrust: """ Middleware that rewrites the remote address according to trusted proxies in the forward chain. diff --git a/docs/admin/install.rst b/docs/admin/install.rst index 2a44c701b..5195bfca8 100644 --- a/docs/admin/install.rst +++ b/docs/admin/install.rst @@ -17,8 +17,8 @@ Before you can run moin, you need to install it: Using your standard user account, run the following command from the project root directory. Replace in the command -below with the path to a python 2.7 executable. This is usually -just "python", but may be "python2.7", "/opt/pypy/bin/pypy" +below with the path to a python 3.5+ executable. This is usually +just "python", but may be "python3", "python3.5", "/opt/pypy/bin/pypy" or even :: quickinstall.py diff --git a/docs/admin/requirements.rst b/docs/admin/requirements.rst index 496ab3cb7..d4797476b 100644 --- a/docs/admin/requirements.rst +++ b/docs/admin/requirements.rst @@ -2,11 +2,11 @@ Requirements ============ -MoinMoin requires Python 2.7.x. A CPython distribution is +MoinMoin requires Python 3.5+. A CPython distribution is recommended because it will likely be the fastest and most stable. Most developers use a CPython distribution for testing. -Typical linux distributions will either have Python 2.7 installed by -default or will have a package manager that will install Python 2.7 +Typical linux distributions will either have Python 3.5+ installed by +default or will have a package manager that will install Python 3.5+ as a secondary Python. Windows users may download CPython distributions from http://www.python.org/ or http://www.activestate.com/. diff --git a/docs/conf.py b/docs/conf.py index 357cf85cb..ef1b5e983 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -44,8 +44,8 @@ master_doc = 'index' # General information about the project. -project = u'MoinMoin' -copyright = u'2011-2018, The MoinMoin developers' +project = 'MoinMoin' +copyright = '2011-2018, The MoinMoin developers' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -198,8 +198,8 @@ # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ - ('index', 'MoinMoin.tex', u'MoinMoin Documentation', - u'The MoinMoin developers', 'manual'), + ('index', 'MoinMoin.tex', 'MoinMoin Documentation', + 'The MoinMoin developers', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of @@ -231,8 +231,8 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ('man/moin', 'moin', u'MoinMoin Commandline Interface', [u'The MoinMoin developers'], 1), - ('index', 'moinmoin', u'MoinMoin Documentation', [u'The MoinMoin developers'], 1), + ('man/moin', 'moin', 'MoinMoin Commandline Interface', ['The MoinMoin developers'], 1), + ('index', 'moinmoin', 'MoinMoin Documentation', ['The MoinMoin developers'], 1), ] @@ -242,7 +242,7 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - ('index', 'MoinMoin', u'MoinMoin Documentation', u'The MoinMoin developers', + ('index', 'MoinMoin', 'MoinMoin Documentation', 'The MoinMoin developers', 'MoinMoin', 'One line description of project.', 'Miscellaneous'), ] diff --git a/docs/examples/deployment/test.wsgi b/docs/examples/deployment/test.wsgi index acd4ce859..4c06721af 100644 --- a/docs/examples/deployment/test.wsgi +++ b/docs/examples/deployment/test.wsgi @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """ A simple WSGI test application. @@ -31,7 +31,7 @@ Apache's VirtualHost definition: # use the daemons we defined above to process requests! WSGIProcessGroup test-wsgi -@copyright: 2008 by MoinMoin:ThomasWaldmann +@copyright: 2008,2019 by MoinMoin:ThomasWaldmann @license: Python License, see LICENSE.Python for details. """ import os.path @@ -83,7 +83,7 @@ def application(environ, start_response): 'python_path': repr(sys.path), 'wsgi_env': '\n'.join([row_template % item for item in environ.items()]), } - return [content] + return [content.encode()] if __name__ == '__main__': @@ -91,12 +91,11 @@ if __name__ == '__main__': try: # create a simple WSGI server and run the application from wsgiref import simple_server - print "Running test application - point your browser at http://localhost:8080/ ..." + print("Running test application - point your browser at http://localhost:8080/ ...") httpd = simple_server.WSGIServer(('', 8080), simple_server.WSGIRequestHandler) httpd.set_app(application) httpd.serve_forever() except ImportError: # wsgiref not installed, just output html to stdout for content in application({}, lambda status, headers: None): - print content - + print(content.decode()) diff --git a/moin.spec b/moin.spec index 5b2c00d9d..aa831ec7f 100644 --- a/moin.spec +++ b/moin.spec @@ -33,7 +33,7 @@ BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) BuildArchitectures: noarch BuildRequires: python-devel -Requires: python >= 2.7 +Requires: python >= 3.5 %description @@ -68,4 +68,6 @@ rm -rf $RPM_BUILD_ROOT - Raised requirement to Python 2.6 (for MoinMoin 2.0.0alpha). * Sat May 4 2013 Thomas Waldmann - Raised requirement to Python 2.7 (for MoinMoin 2.0.0alpha). +* Fri Jul 19 2019 Thomas Waldmann +- Raised requirement to Python 3.5 (for MoinMoin 2.0.0alpha). diff --git a/quickinstall.py b/quickinstall.py index 4dc49fe67..96024fb97 100755 --- a/quickinstall.py +++ b/quickinstall.py @@ -7,9 +7,9 @@ Usage for installation: - quickinstall.py (where is any Python 2.7 executable) + quickinstall.py (where is any Python 3.5+ executable) -Requires: Python 2.7, virtualenv, pip +Requires: Python 3.5+, virtualenv, pip After initial installation, a menu of frequently used commands is provided for moin2 developers and desktop wiki users. @@ -61,9 +61,8 @@ or the virtual environment must be deactivated before rerunning quickinstall.py """) - -if sys.hexversion < 0x2070000 or sys.hexversion > 0x2999999: - sys.exit("Error: MoinMoin requires Python 2.7.x., current version is %s\n" % (platform.python_version(), )) +if sys.hexversion < 0x3050000: + sys.exit("Error: MoinMoin requires Python 3.5+, current version is %s\n" % (platform.python_version(), )) WIN_INFO = 'm.bat, activate.bat, and deactivate.bat are created by quickinstall.py' @@ -176,10 +175,10 @@ def search_for_phrase(filename): if filename in print_counts: counts[phrase] += 1 else: - print idx + 1, line.rstrip() + print(idx + 1, line.rstrip()) break for key in counts: - print 'The phrase "%s" was found %s times.' % (key, counts[key]) + print('The phrase "%s" was found %s times.' % (key, counts[key])) def wiki_exists(): @@ -190,18 +189,18 @@ def wiki_exists(): def make_wiki(command, mode='w', msg='\nSuccess: a new wiki has been created.'): """Process command to create a new wiki.""" if wiki_exists() and mode == 'w': - print 'Error: a wiki exists, delete it and try again.' + print('Error: a wiki exists, delete it and try again.') else: - print 'Output messages redirected to {0}.'.format(NEWWIKI) + print('Output messages redirected to {0}.'.format(NEWWIKI)) with open(NEWWIKI, mode) as messages: result = subprocess.call(command, shell=True, stderr=messages, stdout=messages) if result == 0: - print msg + print(msg) return True else: - print 'Important messages from %s are shown below:' % NEWWIKI + print('Important messages from %s are shown below:' % NEWWIKI) search_for_phrase(NEWWIKI) - print '\nError: attempt to create wiki failed. Do "%s log new-wiki" to see complete log.' % M + print('\nError: attempt to create wiki failed. Do "%s log new-wiki" to see complete log.' % M) return False @@ -219,7 +218,7 @@ def put_items(dir='contrib/sample/'): if file.endswith('.data'): datas.append(file) if not len(datas) == len(metas): - print 'Error: the number of .data and .meta files should be equal' + print('Error: the number of .data and .meta files should be equal') return False commands = [] command = 'moin item-put --meta {0} --data {1}' @@ -228,19 +227,19 @@ def put_items(dir='contrib/sample/'): if data in datas: commands.append(command.format(dir + meta, dir + data)) else: - print u'Error: file "{0} is missing'.format(data) + print('Error: file "{0} is missing'.format(data)) return False commands = ACTIVATE + SEP.join(commands) with open(NEWWIKI, 'a') as messages: result = subprocess.call(commands, shell=True, stderr=messages, stdout=messages) if result == 0: - print '{0} items were added to wiki'.format(len(metas)) + print('{0} items were added to wiki'.format(len(metas))) return True else: - print 'Important messages from %s are shown below:' % NEWWIKI + print('Important messages from %s are shown below:' % NEWWIKI) search_for_phrase(NEWWIKI) - print '\nError: attempt to add items to wiki failed. Do "%s log new-wiki" to see complete log.' % M + print('\nError: attempt to add items to wiki failed. Do "%s log new-wiki" to see complete log.' % M) return False @@ -251,25 +250,25 @@ def delete_files(pattern): for filename in fnmatch.filter(filenames, pattern): os.remove(os.path.join(root, filename)) matches += 1 - print 'Deleted %s files matching "%s".' % (matches, pattern) + print('Deleted %s files matching "%s".' % (matches, pattern)) def get_bootstrap_data_location(): """Return the virtualenv site-packages/xstatic/pkg/bootstrap/data location.""" - command = ACTIVATE + 'python -c "from xstatic.pkg.bootstrap import BASE_DIR; print BASE_DIR"' - return subprocess.check_output(command, shell=True) + command = ACTIVATE + 'python -c "from xstatic.pkg.bootstrap import BASE_DIR; print(BASE_DIR)"' + return subprocess.check_output(command, shell=True).decode() def get_pygments_data_location(): """Return the virtualenv site-packages/xstatic/pkg/pygments/data location.""" - command = ACTIVATE + 'python -c "from xstatic.pkg.pygments import BASE_DIR; print BASE_DIR"' - return subprocess.check_output(command, shell=True) + command = ACTIVATE + 'python -c "from xstatic.pkg.pygments import BASE_DIR; print(BASE_DIR)"' + return subprocess.check_output(command, shell=True).decode() def get_sitepackages_location(): """Return the location of the virtualenv site-packages directory.""" - command = ACTIVATE + 'python -c "from distutils.sysconfig import get_python_lib; print get_python_lib()"' - return subprocess.check_output(command, shell=True).strip() + command = ACTIVATE + 'python -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())"' + return subprocess.check_output(command, shell=True).decode().strip() def create_m(): @@ -280,10 +279,10 @@ def create_m(): else: with open('m', 'w') as f: f.write('#!/bin/sh\n# {0}\n\n{1} quickinstall.py $* --help\n'.format(NIX_INFO, sys.executable)) - os.fchmod(f.fileno(), 0775) + os.fchmod(f.fileno(), 0o775) -class Commands(object): +class Commands: """Each cmd_ method processes a choice on the menu.""" def __init__(self): pass @@ -293,28 +292,28 @@ def cmd_quickinstall(self, *args): if os.path.isdir('.tox'): # keep tox test virtualenvs in sync with moin-env-python command = '{0} quickinstall.py --FirstCall {1}{2}tox --recreate --notest'.format(sys.executable, ' '.join(args), SEP) - print 'Running quickinstall.py and tox recreate virtualenvs... output messages redirected to {0}'.format(QUICKINSTALL) + print('Running quickinstall.py and tox recreate virtualenvs... output messages redirected to {0}'.format(QUICKINSTALL)) else: command = '{0} quickinstall.py --FirstCall'.format(sys.executable, ' '.join(args), ) - print 'Running quickinstall.py... output messages redirected to {0}'.format(QUICKINSTALL) + print('Running quickinstall.py... output messages redirected to {0}'.format(QUICKINSTALL)) with open(QUICKINSTALL, 'w') as messages: # we run ourself as a subprocess so we can capture output in a log file result = subprocess.call(command, shell=True, stderr=messages, stdout=messages) - print '\nSearching {0}, important messages are shown below... Do "{1} log quickinstall" to see complete log.\n'.format(QUICKINSTALL, M) + print('\nSearching {0}, important messages are shown below... Do "{1} log quickinstall" to see complete log.\n'.format(QUICKINSTALL, M)) search_for_phrase(QUICKINSTALL) def cmd_docs(self, *args): """create local Sphinx html documentation""" command = '{0}sphinx-apidoc -f -o docs/devel/api src/moin {1}cd docs{1} make html'.format(ACTIVATE, SEP) - print 'Creating HTML docs... output messages written to {0}.'.format(DOCS) + print('Creating HTML docs... output messages written to {0}.'.format(DOCS)) with open(DOCS, 'w') as messages: result = subprocess.call(command, shell=True, stderr=messages, stdout=messages) - print '\nSearching {0}, important messages are shown below...\n'.format(DOCS) + print('\nSearching {0}, important messages are shown below...\n'.format(DOCS)) search_for_phrase(DOCS) if result == 0: - print 'HTML docs successfully created in {0}.'.format(os.path.normpath('docs/_build/html')) + print('HTML docs successfully created in {0}.'.format(os.path.normpath('docs/_build/html'))) else: - print 'Error: creation of HTML docs failed with return code "{0}". Do "{1} log docs" to see complete log.'.format(result, M) + print('Error: creation of HTML docs failed with return code "{0}". Do "{1} log docs" to see complete log.'.format(result, M)) def cmd_extras(self, *args): """install optional packages: Pillow, sqlalchemy, ldap, requirements.d""" @@ -326,16 +325,16 @@ def cmd_extras(self, *args): reqs = ['requirements.d/development.txt', 'requirements.d/docs.txt', ] reqs_installer = 'pip install -r ' command = ACTIVATE + SEP.join(list(installer + x for x in packages) + list(reqs_installer + x for x in reqs)) - print 'Installing {0}.'.format(', '.join(packages + reqs)) - print 'Output messages written to {0}.'.format(EXTRAS) + print('Installing {0}.'.format(', '.join(packages + reqs))) + print('Output messages written to {0}.'.format(EXTRAS)) with open(EXTRAS, 'w') as messages: subprocess.call(command, shell=True, stderr=messages, stdout=messages) - print '\nImportant messages from {0} are shown below. Do "{1} log extras" to see complete log.'.format(EXTRAS, M) + print('\nImportant messages from {0} are shown below. Do "{1} log extras" to see complete log.'.format(EXTRAS, M)) search_for_phrase(EXTRAS) def cmd_interwiki(self, *args): """refresh contrib/interwiki/intermap.txt""" - print 'Refreshing {0}...'.format(os.path.normpath('contrib/interwiki/intermap.txt')) + print('Refreshing {0}...'.format(os.path.normpath('contrib/interwiki/intermap.txt'))) command = '{0} scripts/wget.py http://master19.moinmo.in/InterWikiMap?action=raw contrib/interwiki/intermap.txt'.format(sys.executable) subprocess.call(command, shell=True) @@ -344,13 +343,13 @@ def cmd_log(self, *args): def log_help(logs): """Print list of available logs to view.""" - print "usage: {0} log where is:\n\n".format(M) + print("usage: {0} log where is:\n\n".format(M)) choices = '{0: <16}- {1}' for log in sorted(logs): if os.path.isfile(CMD_LOGS[log]): - print choices.format(log, CMD_LOGS[log]) + print(choices.format(log, CMD_LOGS[log])) else: - print choices.format(log, '* file does not exist') + print(choices.format(log, '* file does not exist')) logs = set(CMD_LOGS.keys()) if args and args[0] in logs and os.path.isfile(CMD_LOGS[args[0]]): @@ -366,14 +365,14 @@ def log_help(logs): def cmd_new_wiki(self, *args): """create empty wiki""" command = '{0}moin index-create -s -i'.format(ACTIVATE) - print 'Creating a new empty wiki...' + print('Creating a new empty wiki...') make_wiki(command) # share code with loading sample data and restoring backups def cmd_sample(self, *args): """create wiki and load sample data""" # load items with non-ASCII names from a serialized backup command = '{0}moin index-create -s -i{1} moin load --file contrib/sample/unicode.moin'.format(ACTIVATE, SEP) - print 'Creating a new wiki populated with sample data...' + print('Creating a new wiki populated with sample data...') success = make_wiki(command, msg='\nSuccess: a new wiki has been created... working...') # build the index if success: @@ -391,10 +390,10 @@ def cmd_restore(self, *args): filename = args[0] if os.path.isfile(filename): command = command % filename - print u'Creating a new wiki and loading it with data from {0}...'.format(filename) + print('Creating a new wiki and loading it with data from {0}...'.format(filename)) make_wiki(command) else: - print u'Error: cannot create wiki because {0} does not exist.'.format(filename) + print('Error: cannot create wiki because {0} does not exist.'.format(filename)) def cmd_import19(self, *args): """import a moin 1.9 wiki directory named dir""" @@ -402,24 +401,24 @@ def cmd_import19(self, *args): dirname = args[0] if os.path.isdir(dirname): command = '{0}moin import19 -s -i --data_dir {1}'.format(ACTIVATE, dirname) - print u'Creating a new wiki populated with data from {0}...'.format(dirname) + print('Creating a new wiki populated with data from {0}...'.format(dirname)) make_wiki(command) else: - print u'Error: cannot create wiki because {0} does not exist.'.format(dirname) + print('Error: cannot create wiki because {0} does not exist.'.format(dirname)) else: - print 'Error: a path to the Moin 1.9 wiki/data data directory is required.' + print('Error: a path to the Moin 1.9 wiki/data data directory is required.') def cmd_index(self, *args): """delete and rebuild index""" if wiki_exists(): command = '{0}moin index-create -i{1} moin index-build'.format(ACTIVATE, SEP) - print 'Rebuilding indexes...(ignore log messages from rst parser)...' + print('Rebuilding indexes...(ignore log messages from rst parser)...') try: subprocess.call(command, shell=True) except KeyboardInterrupt: pass # eliminates traceback on windows else: - print 'Error: a wiki must be created before rebuilding the indexes.' + print('Error: a wiki must be created before rebuilding the indexes.') def cmd_run(self, *args): """run built-in wiki server""" @@ -432,7 +431,7 @@ def cmd_run(self, *args): except KeyboardInterrupt: pass # eliminates traceback on windows else: - print 'Error: a wiki must be created before running the built-in server.' + print('Error: a wiki must be created before running the built-in server.') def cmd_backup(self, *args): """roll 3 prior backups and create new wiki/backup.moin or backup to user specified file""" @@ -440,9 +439,9 @@ def cmd_backup(self, *args): filename = BACKUP_FILENAME if args: filename = args[0] - print u'Creating a wiki backup to {0}...'.format(filename) + print('Creating a wiki backup to {0}...'.format(filename)) else: - print u'Creating a wiki backup to {0} after rolling 3 prior backups...'.format(filename) + print('Creating a wiki backup to {0} after rolling 3 prior backups...'.format(filename)) b3 = BACKUP_FILENAME.replace('.', '3.') b2 = BACKUP_FILENAME.replace('.', '2.') b1 = BACKUP_FILENAME.replace('.', '1.') @@ -456,30 +455,30 @@ def cmd_backup(self, *args): with open(BACKUPWIKI, 'w') as messages: result = subprocess.call(command, shell=True, stderr=messages, stdout=messages) if result == 0: - print u'Success: wiki was backed up to {0}'.format(filename) + print('Success: wiki was backed up to {0}'.format(filename)) else: - print 'Important messages from {0} are shown below. Do "{1} log backup" to see complete log.'.format(BACKUPWIKI, M) + print('Important messages from {0} are shown below. Do "{1} log backup" to see complete log.'.format(BACKUPWIKI, M)) search_for_phrase(BACKUPWIKI) - print '\nError: attempt to backup wiki failed.' + print('\nError: attempt to backup wiki failed.') else: - print 'Error: cannot backup wiki because it has not been created.' + print('Error: cannot backup wiki because it has not been created.') def cmd_dump_html(self, *args): """create a static html dump of this wiki""" if wiki_exists(): - print u'Creating static HTML image of wiki...' + print('Creating static HTML image of wiki...') command = '{0}moin dump-html {1}'.format(ACTIVATE, ' '.join(args)) with open(DUMPHTML, 'w') as messages: result = subprocess.call(command, shell=True, stderr=messages, stdout=messages) if result == 0: - print u'Success: wiki was dumped to html files' + print('Success: wiki was dumped to html files') else: - print '\nError: attempt to dump wiki to html files failed.' + print('\nError: attempt to dump wiki to html files failed.') # always show errors because individual items may fail - print 'Important messages from {0} are shown below. Do "{1} log dump-html" to see complete log.'.format(DUMPHTML, M) + print('Important messages from {0} are shown below. Do "{1} log dump-html" to see complete log.'.format(DUMPHTML, M)) search_for_phrase(DUMPHTML) else: - print 'Error: cannot dump wiki because it has not been created.' + print('Error: cannot dump wiki because it has not been created.') def cmd_css(self, *args): """run Stylus and lessc to update CSS files""" @@ -489,20 +488,20 @@ def cmd_css(self, *args): modernized_loc = 'src/moin/themes/modernized/static/css/stylus' basic_loc = 'src/moin/themes/basic/static/custom-less' - print 'Running Stylus to update Modernized theme CSS files...' + print('Running Stylus to update Modernized theme CSS files...') command = 'cd {0}{1}stylus --include {2} --include-css --compress < theme.styl > ../theme.css'.format(modernized_loc, SEP, pygments_loc) result = subprocess.call(command, shell=True) if result == 0: - print 'Success: Modernized CSS files updated.' + print('Success: Modernized CSS files updated.') else: - print 'Error: stylus failed to update css files, see error messages above.' + print('Error: stylus failed to update css files, see error messages above.') # stylus adds too many blank lines at end of modernized theme.css, fix it by running coding_std against css directory - command = 'python scripts/coding_std.py src/moin/themes/modernized/static/css' + command = '{0}python scripts/coding_std.py src/moin/themes/modernized/static/css'.format(ACTIVATE) result = subprocess.call(command, shell=True) if result != 0: - print 'Error: failure running coding_std.py against modernized css files' + print('Error: failure running coding_std.py against modernized css files') - print 'Running lessc to update Basic theme CSS files...' + print('Running lessc to update Basic theme CSS files...') if WINDOWS_OS: data_loc = '{0};{1}'.format(bootstrap_loc, pygments_loc) else: @@ -511,38 +510,38 @@ def cmd_css(self, *args): command = 'cd {0}{1}lessc {2} theme.less ../css/theme.css'.format(basic_loc, SEP, include) result = subprocess.call(command, shell=True) if result == 0: - print 'Success: Basic theme CSS files updated.' + print('Success: Basic theme CSS files updated.') else: - print 'Error: Basic theme CSS files update failed, see error messages above.' + print('Error: Basic theme CSS files update failed, see error messages above.') def cmd_tests(self, *args): """run tests, output goes to m-tox.txt""" - print 'Running tests... output written to {0}.'.format(TOX) + print('Running tests... output written to {0}.'.format(TOX)) command = '{0}tox -- {2} > {1} 2>&1'.format(ACTIVATE, TOX, ' '.join(args)) result = subprocess.call(command, shell=True) - print 'Important messages from {0} are shown below. Do "{1} log tests" to see complete log.'.format(TOX, M) + print('Important messages from {0} are shown below. Do "{1} log tests" to see complete log.'.format(TOX, M)) search_for_phrase(TOX) def cmd_coding_std(self, *args): """correct scripts that taint the HG repository and clutter subsequent code reviews""" - print 'Checking for trailing blanks, DOS line endings, Unix line endings, empty lines at eof...' + print('Checking for trailing blanks, DOS line endings, Unix line endings, empty lines at eof...') command = '%s scripts/coding_std.py' % sys.executable subprocess.call(command, shell=True) # not on menu, rarely used, similar code was in moin 1.9 def cmd_dist(self, *args): """create distribution archive in dist/""" - print 'Deleting wiki data, then creating distribution archive in /dist, output written to {0}.'.format(DIST) + print('Deleting wiki data, then creating distribution archive in /dist, output written to {0}.'.format(DIST)) self.cmd_del_wiki(*args) command = '{0} setup.py sdist'.format(sys.executable) with open(DIST, 'w') as messages: result = subprocess.call(command, shell=True, stderr=messages, stdout=messages) - print 'Summary message from {0} is shown below:'.format(DIST) + print('Summary message from {0} is shown below:'.format(DIST)) search_for_phrase(DIST) if result == 0: - print 'Success: a distribution archive was created in {0}.'.format(os.path.normpath('/dist')) + print('Success: a distribution archive was created in {0}.'.format(os.path.normpath('/dist'))) else: - print 'Error: create dist failed with return code = {0}. Do "{1} log dist" to see complete log.'.format(result, M) + print('Error: create dist failed with return code = {0}. Do "{1} log dist" to see complete log.'.format(result, M)) def cmd_del_all(self, *args): """same as running the 4 del-* commands below""" @@ -567,11 +566,11 @@ def cmd_del_wiki(self, *args): """create a just-in-case backup, then delete all wiki data""" command = '{0}moin save --all-backends --file {1}'.format(ACTIVATE, JUST_IN_CASE_BACKUP) if wiki_exists(): - print 'Creating a backup named {0}; then deleting all wiki data and indexes...'.format(JUST_IN_CASE_BACKUP) + print('Creating a backup named {0}; then deleting all wiki data and indexes...'.format(JUST_IN_CASE_BACKUP)) with open(DELWIKI, 'w') as messages: result = subprocess.call(command, shell=True, stderr=messages, stdout=messages) if result != 0: - print 'Error: backup failed with return code = {0}. Complete log is in {1}.'.format(result, DELWIKI) + print('Error: backup failed with return code = {0}. Complete log is in {1}.'.format(result, DELWIKI)) # destroy wiki even if backup fails if os.path.isdir('wiki/data') or os.path.isdir('wiki/index'): shutil.rmtree('wiki/data') @@ -580,12 +579,12 @@ def cmd_del_wiki(self, *args): shutil.rmtree('wiki/preview') if os.path.isdir('wiki/sql'): shutil.rmtree('wiki/sql') - print 'Wiki data successfully deleted.' + print('Wiki data successfully deleted.') else: - print 'Wiki data not deleted because it does not exist.' + print('Wiki data not deleted because it does not exist.') -class QuickInstall(object): +class QuickInstall: def __init__(self, source): self.dir_source = source @@ -612,7 +611,7 @@ def get_pip_version(self): command = ACTIVATE + 'pip --version' pip_txt = subprocess.check_output(command, shell=True) # expecting pip_txt similar to "pip 1.4.1 from /bitbucket/moin-2.0..." - pip_txt = pip_txt.split() + pip_txt = pip_txt.decode().split() if pip_txt[0] == 'pip': pip_version = [int(x) for x in pip_txt[1].split('.')] return pip_version @@ -678,7 +677,7 @@ def do_helpers(self): if (os.path.isfile('activate') or os.path.isfile('activate.bat')) and (len(args) == 2 and args[1] in ('-h', '--help')): # user keyed "./m", "./m -h", or "./m --help" - print help + print(help) else: if not (os.path.isfile('m') or os.path.isfile('m.bat')): @@ -688,7 +687,7 @@ def do_helpers(self): # run this same script (quickinstall.py) again in a subprocess to create the virtual env command() # a few success/failure messages will have printed on users terminal, suggest next step - print '\n> > > Type "%s" for menu < < <' % M + print('\n> > > Type "%s" for menu < < <' % M) elif args == ['quickinstall.py', 'quickinstall']: # user keyed "./m quickinstall" to update virtualenv @@ -722,5 +721,5 @@ def do_helpers(self): choice = getattr(commands, choice) choice(*args[2:]) else: - print help - print 'Error: unknown menu selection "%s"' % args[1] + print(help) + print('Error: unknown menu selection "%s"' % args[1]) diff --git a/scripts/coding_std.py b/scripts/coding_std.py index c5f47f3a3..b014f15b3 100644 --- a/scripts/coding_std.py +++ b/scripts/coding_std.py @@ -27,11 +27,11 @@ # file types to be processed SELECTED_SUFFIXES = set("py bat cmd html css js styl less rst".split()) -# stuff considered DOS/WIN +# stuff considered DOS/WIN that must have \r\n line endings WIN_SUFFIXES = set("bat cmd".split()) -class NoDupsLogger(object): +class NoDupsLogger: """ A simple report logger that suppresses duplicate headings and messages. """ @@ -41,11 +41,11 @@ def __init__(self): def log(self, heading, message): if heading and heading not in self.headings: - print u"\n%s" % heading + print("\n%s" % heading) self.headings.add(heading) if message and message not in self.messages: - print u" ", message + print(" ", message) self.messages.add(message) @@ -111,7 +111,7 @@ def check_template_indentation(lines, filename, logger): block_end = block_endings.get(block_start) if not block_end: # should never get here, mismatched indent_after and block_endings - logger.log(filename, u"Unexpected block type '%s' discovered at line %d!" % (block_start, idx + 1)) + logger.log(filename, "Unexpected block type '%s' discovered at line %d!" % (block_start, idx + 1)) continue if any(x in stripped for x in block_end): # found line similar to: {% block ... %}...{% endblock %} @@ -119,10 +119,10 @@ def check_template_indentation(lines, filename, logger): if any(x in lines[idx + incre] for x in block_end): # found 2 consecutive lines similar to: {% block....\n{% endblock %} continue - logger.log(filename, u"Non-standard indent after line %d -- not fixed!" % (idx + 1)) + logger.log(filename, "Non-standard indent after line %d -- not fixed!" % (idx + 1)) except IndexError: # should never get here, there is an unclosed block near end of template - logger.log(filename, u"End of file reached with open block element at line %d!" % (idx + 1)) + logger.log(filename, "End of file reached with open block element at line %d!" % (idx + 1)) elif stripped.startswith(indent_before): # we have found the end of a block @@ -131,7 +131,7 @@ def check_template_indentation(lines, filename, logger): decre -= 1 if idx + decre < 0: # should never get here; file begins with something like {% endblock %} or - logger.log(filename, u"Beginning of file reached searching for block content at line %d!" % (idx + 1)) + logger.log(filename, "Beginning of file reached searching for block content at line %d!" % (idx + 1)) continue prior_indentation, prior_line = calc_indentation(lines[idx + decre]) if prior_indentation <= indentation: @@ -148,7 +148,7 @@ def check_template_indentation(lines, filename, logger): if prior_line.startswith(block_end): # found lines similar to: {% block...\n{% endblock %} continue - logger.log(filename, u"Non-standard dedent before line %d -- not fixed!" % (idx + 1)) + logger.log(filename, "Non-standard dedent before line %d -- not fixed!" % (idx + 1)) def check_template_spacing(lines, filename, logger): @@ -163,7 +163,7 @@ def check_template_spacing(lines, filename, logger): m_start = [m.start() for m in re.finditer('{%|{{|{#', line)] for index in m_start: if not line.startswith((' ', '- '), index + 2) and not line.strip() in ('{{', '{%', '{#', '{{-', '{%-', '{#-'): - logger.log(filename, u'Missing space within "%s" on line %d - not fixed!' % (line[index:index + 4], idx + 1)) + logger.log(filename, 'Missing space within "%s" on line %d - not fixed!' % (line[index:index + 4], idx + 1)) m_end = [m.start() for m in re.finditer('%}|}}|#}', line)] for index in m_end: if not (line.startswith(' ', index - 1) or line.startswith(' -', index - 2)) and not line.strip() in ('}}', '%}', '#}', '-}}', '-%}', '-#}'): @@ -181,7 +181,8 @@ def check_files(filename, suffix): line_end = "\n" logger = NoDupsLogger() - with open(filename, "rb") as f: + # newline="" does not change incoming line endings + with open(filename, "r", encoding="utf-8", newline="") as f: lines = f.readlines() if filename.endswith('.html'): @@ -192,28 +193,28 @@ def check_files(filename, suffix): while lines: if not lines[-1].strip(): del lines[-1] - logger.log(filename, u"Empty lines at eof removed.") + logger.log(filename, "Empty lines at eof removed.") else: break - with open(filename, "wb") as f: + with open(filename, "w", encoding="utf-8", newline="") as f: for idx, line in enumerate(lines): line_length = len(line) line = line.replace('\t', ' ') if len(line) != line_length: - logger.log(filename, u"Tab characters replaced with 4 spaces.") + logger.log(filename, "Tab characters replaced with 4 spaces.") pep8_line = line.rstrip() + line_end f.write(pep8_line) # if line was changed, issue warning once for each type of change if suffix in WIN_SUFFIXES and not line.endswith("\r\n"): - logger.log(filename, u"Line endings changed to DOS style.") + logger.log(filename, "Line endings changed to DOS style.") elif suffix not in WIN_SUFFIXES and line.endswith("\r\n"): - logger.log(filename, u"Line endings changed to Unix style.") + logger.log(filename, "Line endings changed to Unix style.") elif pep8_line != line: if len(pep8_line) < len(line): - logger.log(filename, u"Trailing blanks removed.") + logger.log(filename, "Trailing blanks removed.") else: - logger.log(filename, u"End of line character added at end of file.") + logger.log(filename, "End of line character added at end of file.") def file_picker(starting_dir): @@ -241,5 +242,5 @@ def file_picker(starting_dir): else: starting_dir = os.path.abspath(os.path.dirname(__file__)) starting_dir = os.path.join(starting_dir.split(os.sep + 'scripts')[0], 'src') - NoDupsLogger().log(u"Starting directory is %s" % starting_dir, None) + NoDupsLogger().log("Starting directory is %s" % starting_dir, None) file_picker(starting_dir) diff --git a/scripts/dos2unix.py b/scripts/dos2unix.py index 1a35a73db..5fa99e3e5 100644 --- a/scripts/dos2unix.py +++ b/scripts/dos2unix.py @@ -40,7 +40,7 @@ def convert_file(filename): elif os.path.isfile(target): convert_file(target) else: - print "Error: %s does not exist." % target + print("Error: %s does not exist." % target) else: - print "Error: incorrect parameters passed." - print "usage: python dos2unix.py " + print("Error: incorrect parameters passed.") + print("usage: python dos2unix.py ") diff --git a/scripts/print_hotshot_profile.py b/scripts/print_hotshot_profile.py deleted file mode 100755 index ea824b49c..000000000 --- a/scripts/print_hotshot_profile.py +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env python -# Copyright: 2005 by MoinMoin:ThomasWaldmann -# License: GNU GPL v2 (or any later version), see LICENSE.txt for details. - -""" - MoinMoin - Print statistics gathered by hotshot profiler - - Usage: - print_stats.py statsfile - - Typical usage: - 1. Edit moin.py and activate the hotshot profiler, set profile file name - 2. Run moin.py - 3. Do some request, with a browser, script or ab - 4. Stop moin.py - 5. Run this tool: print_stats.py moin.prof - - Currently CGI and twisted also have a hotshot profiler integration. -""" - - -def run(): - import sys - from hotshot import stats - - if len(sys.argv) != 2: - print __doc__ - sys.exit() - - # Load and print stats - s = stats.load(sys.argv[1]) - s.strip_dirs() - s.sort_stats('cumulative', 'time', 'calls') - s.print_stats(40) - s.print_callers(40) - -if __name__ == "__main__": - run() diff --git a/scripts/raw_wsgi_bench.py b/scripts/raw_wsgi_bench.py index b94974b4e..22f9f55f1 100644 --- a/scripts/raw_wsgi_bench.py +++ b/scripts/raw_wsgi_bench.py @@ -17,18 +17,18 @@ sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'tests')) RUNS = 100 -RUNS_MARKER = RUNS / 10 -DOTS_MARKER = RUNS_MARKER / 5 +RUNS_MARKER = RUNS // 10 +DOTS_MARKER = RUNS_MARKER // 5 PAGES = ('FrontPage', 'HelpOnMoinWikiSyntax', 'RecentChanges') client = Client(application) for page in PAGES: - print '=== Run with page "%s" ===' % page - print 'Running %i WSGI-requests:' % RUNS + print('=== Run with page "%s" ===' % page) + print('Running %i WSGI-requests:' % RUNS) timing = time.time() - for run in xrange(RUNS): + for run in range(RUNS): appiter, status, headers = client.get('/%s' % page) # result = ''.join(appiter) if ((run + 1) % RUNS_MARKER == 0): @@ -37,7 +37,7 @@ sys.stdout.write('.') timing = time.time() - timing - print - print 'Finished %i WSGI-requests in %.2f seconds' % (RUNS, timing) - print 'Time per request: %.4f seconds' % (timing / RUNS) - print 'Requests per second: %.2f' % (RUNS / timing) + print() + print('Finished %i WSGI-requests in %.2f seconds' % (RUNS, timing)) + print('Time per request: %.4f seconds' % (timing / RUNS)) + print('Requests per second: %.2f' % (RUNS / timing)) diff --git a/scripts/wget.py b/scripts/wget.py index bd3f3db5e..80c661a95 100644 --- a/scripts/wget.py +++ b/scripts/wget.py @@ -10,12 +10,12 @@ """ import sys -import urllib +import urllib.request, urllib.parse, urllib.error if __name__ == '__main__': if len(sys.argv) == 3: - urllib.urlretrieve(sys.argv[1], sys.argv[2]) + urllib.request.urlretrieve(sys.argv[1], sys.argv[2]) else: - print "Error: incorrect parameters passed." - print "Usage: python wget.py " + print("Error: incorrect parameters passed.") + print("Usage: python wget.py ") diff --git a/setup.py b/setup.py index 522b40448..b0dc3d7f8 100644 --- a/setup.py +++ b/setup.py @@ -9,8 +9,8 @@ from setuptools import setup, find_packages -if sys.hexversion < 0x2070000 or sys.hexversion > 0x2999999: - sys.exit("Error: MoinMoin requires Python 2.7.x., current version is %s\n" % (platform.python_version(), )) +if sys.hexversion < 0x3050000: + sys.exit("Error: MoinMoin requires Python 3.5+., current version is %s\n" % (platform.python_version(), )) basedir = os.path.abspath(os.path.dirname(__file__)) @@ -41,7 +41,10 @@ "Natural Language :: English", "Operating System :: OS Independent", "Programming Language :: Python", - "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", "Topic :: Internet :: WWW/HTTP :: WSGI", "Topic :: Internet :: WWW/HTTP :: WSGI :: Application", "Topic :: Internet :: WWW/HTTP :: Dynamic Content", @@ -79,16 +82,14 @@ 'Flask-Babel>=0.11.1', # i18n support 'Flask-Caching>=1.2.0', # caching support 'Flask-Script>=2.0.5', # scripting support - # TODO: Flask-Theme 0.2.0 does not have python 3 support - # fix Flask-Theme or add new package to pypi based upon: https://bitbucket.org/RogerHaase/flask-themes/get/6f0fbeb3156b.tar.gz#egg=Flask-Themes-0.3.0 - 'Flask-Theme>=0.2.0', # theme support + 'Flask-Theme>=0.3.3', # theme support 'emeraldtree>=0.10.0', # xml processing 'flatland>=0.8', # form handling 'Jinja2>=2.7', # template engine 'pygments>=1.4', # src code / text file highlighting 'Werkzeug>=0.11.2', # wsgi toolkit 'whoosh>=2.7.0', # needed for indexed search - 'pdfminer', # pdf -> text/plain conversion + 'pdfminer3', # pdf -> text/plain conversion 'passlib>=1.6.0', # strong password hashing (1.6 needed for consteq) 'XStatic>=0.0.2', # support for static file pypi packages 'XStatic-Bootstrap==3.1.1.2', diff --git a/src/moin/__init__.py b/src/moin/__init__.py index 3329c02f0..86ece6593 100644 --- a/src/moin/__init__.py +++ b/src/moin/__init__.py @@ -7,8 +7,6 @@ """ -from __future__ import absolute_import, division - import sys import platform @@ -17,5 +15,5 @@ project = "MoinMoin" -if sys.hexversion < 0x2070000 or sys.hexversion > 0x2999999: - sys.exit("Error: %s requires Python 2.7.x., current version is %s\n" % (project, platform.python_version())) +if sys.hexversion < 0x3050000: + sys.exit("Error: %s requires Python 3.5+, current version is %s\n" % (project, platform.python_version())) diff --git a/src/moin/_template.py b/src/moin/_template.py index a505aa65e..8127a137e 100644 --- a/src/moin/_template.py +++ b/src/moin/_template.py @@ -1,4 +1,4 @@ -# Copyright: 2013 MoinMoin:YourNameHere +# Copyright: 2019 MoinMoin:YourNameHere # License: GNU GPL v2 (or any later version), see LICENSE.txt for details. """ @@ -13,6 +13,3 @@ Of course you'll have to edit/fix the Copyright line and also the docstring needs to be replaced with something making sense, but keep the structure. """ - - -from __future__ import absolute_import, division diff --git a/src/moin/_tests/__init__.py b/src/moin/_tests/__init__.py index cca4a443a..73fd22033 100644 --- a/src/moin/_tests/__init__.py +++ b/src/moin/_tests/__init__.py @@ -23,7 +23,7 @@ # need more privs... -def become_valid(username=u"ValidUser"): +def become_valid(username="ValidUser"): """ modify flaskg.user to make the user valid. Note that a valid user will only be in ACL special group "Known", if we have a user profile for this user as the ACL system will check if @@ -36,7 +36,7 @@ def become_valid(username=u"ValidUser"): flaskg.user.valid = 1 -def become_trusted(username=u"TrustedUser"): +def become_trusted(username="TrustedUser"): """ modify flaskg.user to make the user valid and trusted, so it is in acl group Trusted """ become_valid(username) flaskg.user.trusted = True @@ -46,7 +46,7 @@ def become_trusted(username=u"TrustedUser"): def update_item(name, meta, data): """ creates or updates an item """ - if isinstance(data, unicode): + if isinstance(data, str): data = data.encode(CHARSET) item = flaskg.storage[name] @@ -54,7 +54,7 @@ def update_item(name, meta, data): if NAME not in meta: meta[NAME] = [name, ] if CONTENTTYPE not in meta: - meta[CONTENTTYPE] = u'application/octet-stream' + meta[CONTENTTYPE] = 'application/octet-stream' rev = item.store_revision(meta, BytesIO(data), return_rev=True) return rev @@ -62,7 +62,7 @@ def update_item(name, meta, data): def create_random_string_list(length=14, count=10): """ creates a list of random strings """ chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' - return [u"{0}".format(random_string(length, chars)) for counter in range(count)] + return ["{0}".format(random_string(length, chars)) for counter in range(count)] def nuke_item(name): diff --git a/src/moin/_tests/_test_template.py b/src/moin/_tests/_test_template.py index 386d4bfcf..988ca4f52 100644 --- a/src/moin/_tests/_test_template.py +++ b/src/moin/_tests/_test_template.py @@ -14,7 +14,7 @@ from moin import module_tested -class TestSimpleStuff(object): +class TestSimpleStuff: """ The simplest MoinMoin test class Class name must start with 'Test' to be included in @@ -33,7 +33,7 @@ def testSimplest(self): assert result == expected -class TestComplexStuff(object): +class TestComplexStuff: """ Describe these tests here... Some tests may have a list of tests related to this test case. You diff --git a/src/moin/_tests/ldap_testbase.py b/src/moin/_tests/ldap_testbase.py index b74195c3d..42f7a486e 100644 --- a/src/moin/_tests/ldap_testbase.py +++ b/src/moin/_tests/ldap_testbase.py @@ -84,7 +84,7 @@ def check_environ(): return None -class Slapd(object): +class Slapd: """ Manage a slapd process for testing purposes """ def __init__( self, @@ -139,7 +139,7 @@ def stop(self): os.waitpid(pid, 0) -class LdapEnvironment(object): +class LdapEnvironment: """ Manage a (temporary) environment for running a slapd in it """ # default DB_CONFIG bdb configuration file contents @@ -202,7 +202,7 @@ def create_env(self, slapd_config, db_config=DB_CONFIG): 'rootdn': self.rootdn, 'rootpw': rootpw, } - if isinstance(slapd_config, unicode): + if isinstance(slapd_config, str): slapd_config = slapd_config.encode(self.coding) self.slapd_conf = os.path.join(self.ldap_dir, "slapd.conf") f = open(self.slapd_conf, 'w') @@ -240,7 +240,7 @@ def destroy_env(self): try: import pytest - class LDAPTstBase(object): + class LDAPTstBase: """ Test base class for pytest based tests which need a LDAP server to talk to. Inherit your test class from this base class to test LDAP stuff. diff --git a/src/moin/_tests/ldap_testdata.py b/src/moin/_tests/ldap_testdata.py index 53b585d13..0bed93d49 100644 --- a/src/moin/_tests/ldap_testdata.py +++ b/src/moin/_tests/ldap_testdata.py @@ -10,7 +10,7 @@ ROOTDN = "cn=root,{0}".format(BASEDN) ROOTPW = "secret" -SLAPD_CONFIG = u"""\ +SLAPD_CONFIG = """\ # See slapd.conf(5) for details on configuration options. include %(schema_dir)s/core.schema @@ -56,7 +56,7 @@ sizelimit -1 """ -LDIF_CONTENT = u"""\ +LDIF_CONTENT = """\ ######################################################################## # regression testing ######################################################################## diff --git a/src/moin/_tests/test_error.py b/src/moin/_tests/test_error.py index 9daad8f70..27d68c481 100644 --- a/src/moin/_tests/test_error.py +++ b/src/moin/_tests/test_error.py @@ -11,33 +11,27 @@ from moin import error -class TestEncoding(object): +class TestEncoding: """ MoinMoin errors do work with unicode transparently """ def testCreateWithUnicode(self): """ error: create with unicode """ - err = error.Error(u'טעות') - assert unicode(err) == u'טעות' + err = error.Error('טעות') assert str(err) == 'טעות' def testCreateWithEncodedString(self): """ error: create with encoded string """ - err = error.Error('טעות') - assert unicode(err) == u'טעות' + err = error.Error('טעות'.encode()) assert str(err) == 'טעות' def testCreateWithObject(self): """ error: create with any object """ - class Foo(object): - def __unicode__(self): - return u'טעות' - + class Foo: def __str__(self): return 'טעות' err = error.Error(Foo()) - assert unicode(err) == u'טעות' assert str(err) == 'טעות' def testAccessLikeDict(self): @@ -47,7 +41,7 @@ def testAccessLikeDict(self): assert '%(message)s' % dict(message=err) == test -class TestCompositeError(object): +class TestCompositeError: def setup_method(self, method): self.CompositeError_obj = error.CompositeError(error.InternalError) diff --git a/src/moin/_tests/test_forms.py b/src/moin/_tests/test_forms.py index abde17866..7593462ac 100644 --- a/src/moin/_tests/test_forms.py +++ b/src/moin/_tests/test_forms.py @@ -22,7 +22,7 @@ def test_datetimeunix(): dt = datetime.datetime(2012, 12, 21, 23, 45, 59) timestamp = timegm(dt.timetuple()) - dt_u = u'2012-12-21 23:45:59' + dt_u = '2012-12-21 23:45:59' d = DateTimeUNIX(timestamp) assert d.value == timestamp @@ -31,7 +31,7 @@ def test_datetimeunix(): incorrect_timestamp = 99999999999999999 d = DateTimeUNIX(incorrect_timestamp) assert d.value is None - assert d.u == unicode(incorrect_timestamp) + assert d.u == str(incorrect_timestamp) d = DateTimeUNIX(dt) assert d.value == timestamp @@ -42,14 +42,14 @@ def test_datetimeunix(): assert d.value == timestamp assert d.u == dt_u - incorrect_timestring = u'2012-10-30' + incorrect_timestring = '2012-10-30' d = DateTimeUNIX(incorrect_timestring) assert d.value is None assert d.u == incorrect_timestring d = DateTimeUNIX(None) assert d.value is None - assert d.u == u'' + assert d.u == '' def test_validjson(): @@ -58,23 +58,23 @@ def test_validjson(): Does not apply to usersettings form. """ - app.cfg.namespace_mapping = [(u'', 'default_backend'), (u'ns1/', 'default_backend'), (u'users/', 'other_backend')] - item = Item.create(u'users/existingname') - meta = {NAMESPACE: u'users', CONTENTTYPE: u'text/plain;charset=utf-8'} + app.cfg.namespace_mapping = [('', 'default_backend'), ('ns1/', 'default_backend'), ('users/', 'other_backend')] + item = Item.create('users/existingname') + meta = {NAMESPACE: 'users', CONTENTTYPE: 'text/plain;charset=utf-8'} become_trusted() item._save(meta, data='This is a valid Item.') valid_itemid = 'a1924e3d0a34497eab18563299d32178' # ('names', 'namespace', 'field', 'value', 'result') - tests = [([u'somename', u'@revid'], '', '', 'somename', False), # item names cannot begin with @ + tests = [(['somename', '@revid'], '', '', 'somename', False), # item names cannot begin with @ # TODO for above? - create item @x, get error message, change name in meta to xx, get an item with names @40x and alias of xx - ([u'bar', u'ns1'], '', '', 'bar', False), # item names cannot match namespace names - ([u'foo', u'foo', u'bar'], '', '', 'foo', False), # names in the name list must be unique. - ([u'ns1ns2ns3', u'ns1/subitem'], '', '', 'valid', False), # Item names must not match with existing namespaces; items cannot be in 2 namespaces - ([u'foobar', u'validname'], '', ITEMID, valid_itemid + '8080', False), # attempts to change itemid in meta result in "Item(s) named foobar, validname already exist." - ([u'barfoo', u'validname'], '', ITEMID, valid_itemid.replace('a', 'y'), False), # similar to above + (['bar', 'ns1'], '', '', 'bar', False), # item names cannot match namespace names + (['foo', 'foo', 'bar'], '', '', 'foo', False), # names in the name list must be unique. + (['ns1ns2ns3', 'ns1/subitem'], '', '', 'valid', False), # Item names must not match with existing namespaces; items cannot be in 2 namespaces + (['foobar', 'validname'], '', ITEMID, valid_itemid + '8080', False), # attempts to change itemid in meta result in "Item(s) named foobar, validname already exist." + (['barfoo', 'validname'], '', ITEMID, valid_itemid.replace('a', 'y'), False), # similar to above ([], '', 'itemid', valid_itemid, True), # deleting all names from the metadata of an existing item will make it nameless, succeeds - ([u'existingname'], 'users', '', 'existingname', False), # item already exists + (['existingname'], 'users', '', 'existingname', False), # item already exists ] for name, namespace, field, value, result in tests: meta = {NAME: name, NAMESPACE: namespace} diff --git a/src/moin/_tests/test_test_environ.py b/src/moin/_tests/test_test_environ.py index b63b0ac88..835704dbc 100644 --- a/src/moin/_tests/test_test_environ.py +++ b/src/moin/_tests/test_test_environ.py @@ -17,7 +17,7 @@ import pytest -class TestStorageEnvironWithoutConfig(object): +class TestStorageEnvironWithoutConfig: def setup_method(self, method): self.class_level_value = 123 @@ -29,10 +29,10 @@ def test_fresh_backends(self): storage = flaskg.storage assert storage assert hasattr(storage, '__getitem__') - itemname = u"this item shouldn't exist yet" + itemname = "this item shouldn't exist yet" assert not storage.has_item(itemname) item = storage[itemname] - new_rev = item.store_revision({NAME: [itemname, ], CONTENTTYPE: u'text/plain;charset=utf-8'}, BytesIO(b'')) + new_rev = item.store_revision({NAME: [itemname, ], CONTENTTYPE: 'text/plain;charset=utf-8'}, BytesIO(b'')) assert storage.has_item(itemname) @@ -44,7 +44,7 @@ def test_fresh_backends(self): ) -class TestStorageEnvironWithConfig(object): +class TestStorageEnvironWithConfig: @pytest.fixture def cfg(self): diff --git a/src/moin/_tests/test_user.py b/src/moin/_tests/test_user.py index 28f9b67f5..a45241876 100644 --- a/src/moin/_tests/test_user.py +++ b/src/moin/_tests/test_user.py @@ -18,11 +18,11 @@ import pytest -class TestSimple(object): +class TestSimple: def test_create_retrieve(self): - name = u"foo" - password = u"barbaz4711" - email = u"foo@example.org" + name = "foo" + password = "barbaz4711" + email = "foo@example.org" # nonexisting user u = user.User(name=name, password=password) assert u.name == [name, ] @@ -39,7 +39,7 @@ def test_create_retrieve(self): assert u.exists() -class TestUser(object): +class TestUser: @pytest.yield_fixture(autouse=True) def saved_user(self): @@ -53,7 +53,7 @@ def saved_user(self): def testAsciiPassword(self): """ user: login with ascii password """ # Create test user - name = u'__Non Existent User Name__' + name = '__Non Existent User Name__' password = name self.createUser(name, password) @@ -64,7 +64,7 @@ def testAsciiPassword(self): def testUnicodePassword(self): """ user: login with non-ascii password """ # Create test user - name = u'__שם משתמש לא קיים__' # Hebrew + name = '__שם משתמש לא קיים__' # Hebrew password = name self.createUser(name, password) @@ -75,7 +75,7 @@ def testUnicodePassword(self): def testInvalidatePassword(self): """ user: test invalidation of password """ # Create test user - name = u'__Non Existent User Name__' + name = '__Non Existent User Name__' password = name self.createUser(name, password) @@ -101,7 +101,7 @@ def testPasswordHash(self): with the correct password and can not log in with a wrong password. """ # Create test user - name = u'Test User' + name = 'Test User' # sha512_crypt passlib hash for '12345': pw_hash = '$6$rounds=1001$y9ObPHKb8cvRCs5G$39IW1i5w6LqXPRi4xqAu3OKv1UOpVKNkwk7zPnidsKZWqi1CrQBpl2wuq36J/s6yTxjCnmaGzv/2.dAmM8fDY/' self.createUser(name, pw_hash, True) @@ -117,22 +117,22 @@ def testPasswordHash(self): # Subscriptions --------------------------------------------------- def test_subscriptions(self): - pagename = u"Foo:foo 123" - tagname = u"xxx" + pagename = "Foo:foo 123" + tagname = "xxx" regexp = r"\d+" item = Item.create(pagename) - item._save({NAMESPACE: u"", TAGS: [tagname]}) + item._save({NAMESPACE: "", TAGS: [tagname]}) item = Item.create(pagename) meta = item.meta - name = u'bar' + name = 'bar' password = name email = "bar@example.org" user.create_user(name, password, email) the_user = user.User(name=name, password=password) assert not the_user.is_subscribed_to(item) - the_user.subscribe(NAME, u"SomeOtherPageName", u"") - result = the_user.unsubscribe(NAME, u"OneMorePageName", u"") + the_user.subscribe(NAME, "SomeOtherPageName", "") + result = the_user.unsubscribe(NAME, "OneMorePageName", "") assert result is False subscriptions = [(ITEMID, meta[ITEMID], None), @@ -150,7 +150,7 @@ def test_subscriptions(self): # Bookmarks ------------------------------------------------------- def test_bookmark(self): - name = u'Test_User_bookmark' + name = 'Test_User_bookmark' password = name self.createUser(name, password) theUser = user.User(name=name, password=password) @@ -174,8 +174,8 @@ def test_quicklinks(self): """ Test for the quicklinks """ - pagename = u'Test_page_quicklink' - name = u'Test_User_quicklink' + pagename = 'Test_page_quicklink' + name = 'Test_User_quicklink' password = name self.createUser(name, password) theUser = user.User(name=name, password=password) @@ -188,13 +188,13 @@ def test_quicklinks(self): assert not result # add quicklink - theUser.quicklink(u'Test_page_added') + theUser.quicklink('Test_page_added') result_on_addition = theUser.quicklinks - expected = [u'MoinTest/Test_page_added'] + expected = ['MoinTest/Test_page_added'] assert result_on_addition == expected # remove quicklink - theUser.quickunlink(u'Test_page_added') + theUser.quickunlink('Test_page_added') result_on_removal = theUser.quicklinks expected = [] assert result_on_removal == expected @@ -202,8 +202,8 @@ def test_quicklinks(self): # Trail ----------------------------------------------------------- def test_trail(self): - pagename = u'Test_page_trail' - name = u'Test_User_trail' + pagename = 'Test_page_trail' + name = 'Test_User_trail' password = name self.createUser(name, password) theUser = user.User(name=name, password=password) @@ -214,16 +214,16 @@ def test_trail(self): assert result == expected # item name added to trail - theUser.add_trail(u'item_added') + theUser.add_trail('item_added') theUser = user.User(name=name, password=password) result = theUser.get_trail() - expected = [u'MoinTest/item_added'] + expected = ['MoinTest/item_added'] assert result == expected # Sessions ------------------------------------------------------- def test_sessions(self): - name = u'Test_User_sessions' + name = 'Test_User_sessions' password = name self.createUser(name, password) theUser = user.User(name=name, password=password) @@ -245,18 +245,18 @@ def test_sessions(self): # Other ---------------------------------------------------------- def test_recovery_token(self): - name = u'Test_User_other' + name = 'Test_User_other' password = name self.createUser(name, password) theUser = user.User(name=name, password=password) # use recovery token to generate new password test_token = theUser.generate_recovery_token() - result_success = theUser.apply_recovery_token(test_token, u'test_newpass') + result_success = theUser.apply_recovery_token(test_token, 'test_newpass') assert result_success # wrong token - result_failure = theUser.apply_recovery_token('test_wrong_token', u'test_newpass') + result_failure = theUser.apply_recovery_token('test_wrong_token', 'test_newpass') assert not result_failure # Helpers --------------------------------------------------------- @@ -266,23 +266,23 @@ def createUser(self, name, password, pwencoded=False, email=None, validate=False assert ret is None, "create_user returned: {0}".format(ret) -class TestGroupName(object): +class TestGroupName: def testGroupNames(self): """ user: isValidName: reject group names """ - test = u'AdminGroup' + test = 'AdminGroup' assert not user.isValidName(test) -class TestIsValidName(object): +class TestIsValidName: def testNonAlnumCharacters(self): """ user: isValidName: reject unicode non alpha numeric characters : and , used in acl rules, we might add more characters to the syntax. """ - invalid = u'! # $ % ^ & * ( ) = + , : ; " | ~ / \\ \u0000 \u202a'.split() - base = u'User{0}Name' + invalid = '! # $ % ^ & * ( ) = + , : ; " | ~ / \\ \u0000 \u202a'.split() + base = 'User{0}Name' for c in invalid: name = base.format(c) assert not user.isValidName(name) @@ -290,9 +290,9 @@ def testNonAlnumCharacters(self): def testWhitespace(self): """ user: isValidName: reject leading, trailing or multiple whitespace """ cases = ( - u' User Name', - u'User Name ', - u'User Name', + ' User Name', + 'User Name ', + 'User Name', ) for test in cases: assert not user.isValidName(test) @@ -300,10 +300,10 @@ def testWhitespace(self): def testValid(self): """ user: isValidName: accept names in any language, with spaces """ cases = ( - u'Jürgen Hermann', # German - u'ניר סופר', # Hebrew - u'CamelCase', # Good old camel case - u'가각간갇갈 갉갊감 갬갯걀갼' # Hangul (gibberish) + 'Jürgen Hermann', # German + 'ניר סופר', # Hebrew + 'CamelCase', # Good old camel case + '가각간갇갈 갉갊감 갬갯걀갼' # Hangul (gibberish) ) for test in cases: assert user.isValidName(test) diff --git a/src/moin/_tests/test_wikiutil.py b/src/moin/_tests/test_wikiutil.py index a6129ba77..027fab24c 100644 --- a/src/moin/_tests/test_wikiutil.py +++ b/src/moin/_tests/test_wikiutil.py @@ -17,37 +17,38 @@ from werkzeug import MultiDict -class TestCleanInput(object): +class TestCleanInput: def testCleanInput(self): tests = [ - (u"", u""), # empty - (u"aaa\r\n\tbbb", u"aaa bbb"), # ws chars -> blanks - (u"aaa\x00\x01bbb", u"aaabbb"), # strip weird chars - (u"a" * 500, u""), # too long + ("", ""), # empty + ("aaa\r\n\tbbb", "aaa bbb"), # ws chars -> blanks + ("aaa\x00\x01bbb", "aaabbb"), # strip weird chars + ("a" * 500, ""), # too long ] for instr, outstr in tests: assert wikiutil.clean_input(instr) == outstr -class TestAnchorNames(object): +class TestAnchorNames: @pytest.mark.parametrize('text,expected', [ # text, expected output - # note: recent werkzeug encodes a "+" to %2B, giving .2B in the end - (u'\xf6\xf6ll\xdf\xdf', 'A.2BAPYA9g-ll.2BAN8A3w-'), - (u'level 2', 'level_2'), - (u'level_2', 'level_2'), - (u'', 'A'), - (u'123', 'A123'), + # note: recent werkzeug encodes a "+" to "%2B", giving ".2B" in the end, + # also "-" to "%2D", giving ".2D". + ('\xf6\xf6ll\xdf\xdf', 'A.2BAPYA9g.2Dll.2BAN8A3w.2D'), + ('level 2', 'level_2'), + ('level_2', 'level_2'), + ('', 'A'), + ('123', 'A123'), # make sure that a valid anchor is not modified: - (u'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789:_.-', - u'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789:_.-') + ('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789:_.', + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789:_.') ]) def test_anchor_name_encoding(self, text, expected): encoded = wikiutil.anchor_name_from_text(text) assert expected == encoded -class TestRelativeTools(object): +class TestRelativeTools: tests = [ # test expected output # CHILD_PREFIX @@ -71,26 +72,26 @@ def test_rel_pagename(self, current_page, relative_page, absolute_page): assert relative_page == wikiutil.RelItemName(current_page, absolute_page) -class TestNormalizePagename(object): +class TestNormalizePagename: def testPageInvalidChars(self): """ request: normalize pagename: remove invalid unicode chars Assume the default setting """ - test = u'\u0000\u202a\u202b\u202c\u202d\u202e' - expected = u'' + test = '\u0000\u202a\u202b\u202c\u202d\u202e' + expected = '' result = wikiutil.normalize_pagename(test, app.cfg) assert result == expected def testNormalizeSlashes(self): """ request: normalize pagename: normalize slashes """ cases = ( - (u'/////', u''), - (u'/a', u'a'), - (u'a/', u'a'), - (u'a/////b/////c', u'a/b/c'), - (u'a b/////c d/////e f', u'a b/c d/e f'), + ('/////', ''), + ('/a', 'a'), + ('a/', 'a'), + ('a/////b/////c', 'a/b/c'), + ('a b/////c d/////e f', 'a b/c d/e f'), ) for test, expected in cases: result = wikiutil.normalize_pagename(test, app.cfg) @@ -99,13 +100,13 @@ def testNormalizeSlashes(self): def testNormalizeWhitespace(self): """ request: normalize pagename: normalize whitespace """ cases = ( - (u' ', u''), - (u' a', u'a'), - (u'a ', u'a'), - (u'a b c', u'a b c'), - (u'a b / c d / e f', u'a b/c d/e f'), + (' ', ''), + (' a', 'a'), + ('a ', 'a'), + ('a b c', 'a b c'), + ('a b / c d / e f', 'a b/c d/e f'), # All 30 unicode spaces - (CHARS_SPACES, u''), + (CHARS_SPACES, ''), ) for test, expected in cases: result = wikiutil.normalize_pagename(test, app.cfg) @@ -118,18 +119,18 @@ def testUnderscoreTestCase(self): normalized, order is important! """ cases = ( - (u' ', u''), - (u' a', u'a'), - (u'a ', u'a'), - (u'a b c', u'a b c'), - (u'a b / c d / e f', u'a b/c d/e f'), + (' ', ''), + (' a', 'a'), + ('a ', 'a'), + ('a b c', 'a b c'), + ('a b / c d / e f', 'a b/c d/e f'), ) for test, expected in cases: result = wikiutil.normalize_pagename(test, app.cfg) assert result == expected -class TestGroupItems(object): +class TestGroupItems: def testNormalizeGroupName(self): """ request: normalize itemname: restrict groups to alpha numeric Unicode @@ -138,9 +139,9 @@ def testNormalizeGroupName(self): """ cases = ( # current acl chars - (u'Name,:Group', u'NameGroup'), + ('Name,:Group', 'NameGroup'), # remove than normalize spaces - (u'Name ! @ # $ % ^ & * ( ) + Group', u'Name Group'), + ('Name ! @ # $ % ^ & * ( ) + Group', 'Name Group'), ) for test, expected in cases: # validate we are testing valid group names @@ -151,12 +152,12 @@ def testNormalizeGroupName(self): def testParentItemName(): # with no parent - result = wikiutil.ParentItemName(u'itemname') - expected = u'' + result = wikiutil.ParentItemName('itemname') + expected = '' assert result == expected, 'Expected "%(expected)s" but got "%(result)s"' % locals() # with a parent - result = wikiutil.ParentItemName(u'some/parent/itemname') - expected = u'some/parent' + result = wikiutil.ParentItemName('some/parent/itemname') + expected = 'some/parent' assert result == expected diff --git a/src/moin/_tests/wikiconfig.py b/src/moin/_tests/wikiconfig.py index d1309643c..499817247 100644 --- a/src/moin/_tests/wikiconfig.py +++ b/src/moin/_tests/wikiconfig.py @@ -27,7 +27,7 @@ class Config(DefaultConfig): index_storage = 'FileStorage', (join(_here, 'wiki', 'index'), ), {} default_acl = None item_root = 'FrontPage' - interwikiname = u'MoinTest' + interwikiname = 'MoinTest' interwiki_map = dict(Self='http://localhost:8080/', MoinMoin='http://moinmo.in/') interwiki_map[interwikiname] = 'http://localhost:8080/' diff --git a/src/moin/app.py b/src/moin/app.py index 4ad37f98d..115b49f38 100644 --- a/src/moin/app.py +++ b/src/moin/app.py @@ -10,8 +10,6 @@ Use create_app(config) to create the WSGI application (using Flask). """ -from __future__ import absolute_import, division - import os import sys @@ -100,7 +98,7 @@ def create_app_ext(flask_config_file=None, flask_config_dict=None, if warn_default: logging.warning("using builtin default configuration") from moin.config.default import DefaultConfig as Config - for key, value in kwargs.iteritems(): + for key, value in kwargs.items(): setattr(Config, key, value) if Config.secrets is None: # reuse the secret configured for flask (which is required for sessions) diff --git a/src/moin/apps/admin/_tests/test_admin.py b/src/moin/apps/admin/_tests/test_admin.py index 84ce53216..de825aaf3 100644 --- a/src/moin/apps/admin/_tests/test_admin.py +++ b/src/moin/apps/admin/_tests/test_admin.py @@ -61,4 +61,4 @@ def test_admin(app, url_for_args, status, data): assert rv.status == status assert rv.headers['Content-Type'] == 'text/html; charset=utf-8' for item in data: - assert item in rv.data + assert item.encode() in rv.data diff --git a/src/moin/apps/admin/views.py b/src/moin/apps/admin/views.py index f368c257a..ce0f3fd47 100644 --- a/src/moin/apps/admin/views.py +++ b/src/moin/apps/admin/views.py @@ -47,13 +47,13 @@ @admin.route('/superuser') @require_permission(SUPERUSER) def index(): - return render_template('admin/index.html', title_name=_(u"Admin")) + return render_template('admin/index.html', title_name=_("Admin")) @admin.route('/user') def index_user(): return render_template('user/index_user.html', - title_name=_(u"User"), + title_name=_("User"), flaskg=flaskg, NAMESPACE_USERPROFILES=NAMESPACE_USERPROFILES, ) @@ -89,7 +89,7 @@ def register_new_user(): if not _using_moin_auth(): return Response('No MoinAuth in auth list', 403) - title_name = _(u'Register New User') + title_name = _('Register New User') FormClass = RegisterNewUserForm if request.method in ['GET', 'HEAD']: @@ -161,7 +161,7 @@ def userbrowser(): email=rev.meta[EMAIL] if EMAIL in rev.meta else rev.meta[EMAIL_UNVALIDATED], disabled=rev.meta[DISABLED], groups=user_groups)) - return render_template('admin/userbrowser.html', user_accounts=user_accounts, title_name=_(u"Users")) + return render_template('admin/userbrowser.html', user_accounts=user_accounts, title_name=_("Users")) @admin.route('/userprofile/', methods=['GET', 'POST', ]) @@ -172,7 +172,7 @@ def userprofile(user_name): """ u = user.User(auth_username=user_name) if request.method == 'GET': - return _(u"User profile of %(username)s: %(email)s %(disabled)s", username=user_name, + return _("User profile of %(username)s: %(email)s %(disabled)s", username=user_name, email=u.email, disabled=u.disabled) if request.method == 'POST': @@ -186,16 +186,16 @@ def userprofile(user_name): val = bool(int(val)) elif isinstance(oldval, int): val = int(val) - elif isinstance(oldval, unicode): - val = unicode(val) + elif isinstance(oldval, str): + val = str(val) else: ok = False if ok: u.profile[key] = val u.save() - flash(u'{0}.{1}: {2} -> {3}'.format(user_name, key, unicode(oldval), unicode(val), ), "info") + flash('{0}.{1}: {2} -> {3}'.format(user_name, key, str(oldval), str(val), ), "info") else: - flash(u'modifying {0}.{1} failed'.format(user_name, key, ), "error") + flash('modifying {0}.{1} failed'.format(user_name, key, ), "error") return redirect(url_for('.userbrowser')) @@ -265,7 +265,7 @@ def iter_vnames(cfg): found.sort() found_default.sort() return render_template('admin/wikiconfig.html', - title_name=_(u"Show Wiki Configuration"), + title_name=_("Show Wiki Configuration"), len=len, found=found, found_default=found_default, @@ -282,17 +282,17 @@ def format_default(default): default_txt = default.text else: if len(repr(default)) > max_len_default and isinstance(default, list) and len(default) > 1: - txt = [u'['] + txt = ['['] for entry in default: - txt.append(u' {0},'.format(repr(entry))) - txt.append(u' ]') - return u''.join(txt) + txt.append(' {0},'.format(repr(entry))) + txt.append(' ]') + return ''.join(txt) elif len(repr(default)) > max_len_default and isinstance(default, dict) and len(default) > 1: - txt = [u'{'] + txt = ['{'] for key, val in default.items(): - txt.append(u' {0}: {1},'.format(repr(key), repr(val))) - txt.append(u' }') - return u''.join(txt) + txt.append(' {0}: {1},'.format(repr(key), repr(val))) + txt.append(' }') + return ''.join(txt) else: default_txt = repr(default) return default_txt @@ -310,7 +310,7 @@ def format_default(default): groups.append((heading, desc, opts)) groups.sort() return render_template('admin/wikiconfighelp.html', - title_name=_(u"Wiki Configuration Help"), + title_name=_("Wiki Configuration Help"), groups=groups, len=len, max_len_default=max_len_default, @@ -331,7 +331,7 @@ def highlighterhelp(): rows = sorted([[desc, ' '.join(names), ' '.join(patterns), ' '.join(mimetypes), ] for desc, names, patterns, mimetypes in lexers]) return render_template('user/highlighterhelp.html', - title_name=_(u"Highlighters"), + title_name=_("Highlighters"), headings=headings, rows=rows) @@ -345,7 +345,7 @@ def interwikihelp(): ] rows = sorted(app.cfg.interwiki_map.items()) return render_template('user/interwikihelp.html', - title_name=_(u"Interwiki Names"), + title_name=_("Interwiki Names"), headings=headings, rows=rows) @@ -361,7 +361,7 @@ def itemsize(): for rev in flaskg.storage.documents(wikiname=app.cfg.interwikiname)] rows = sorted(rows, reverse=True) return render_template('user/itemsize.html', - title_name=_(u"Item Sizes"), + title_name=_("Item Sizes"), headings=headings, rows=rows) @@ -375,8 +375,8 @@ def trash(namespace): """ trash = _trashed(namespace) return render_template('admin/trash.html', - headline=_(u'Trashed Items'), - title_name=_(u'Trashed Items'), + headline=_('Trashed Items'), + title_name=_('Trashed Items'), results=trash) @@ -400,7 +400,7 @@ def user_acl_report(uid): theuser = user.User(uid=uid) itemwise_acl = [] for item in all_items: - fqname = CompositeName(item.meta.get(NAMESPACE), u'itemid', item.meta.get(ITEMID)) + fqname = CompositeName(item.meta.get(NAMESPACE), 'itemid', item.meta.get(ITEMID)) itemwise_acl.append({'name': item.meta.get(NAME), 'itemid': item.meta.get(ITEMID), 'fqname': fqname, @@ -410,7 +410,7 @@ def user_acl_report(uid): 'admin': theuser.may.admin(fqname), 'destroy': theuser.may.destroy(fqname)}) return render_template('admin/user_acl_report.html', - title_name=_(u'User ACL Report'), + title_name=_('User ACL Report'), user_names=theuser.name, itemwise_acl=itemwise_acl) @@ -434,7 +434,7 @@ def groupbrowser(): member_groups=all_groups[group].member_groups, grouptype=group_type)) return render_template('admin/groupbrowser.html', - title_name=_(u'Groups'), + title_name=_('Groups'), groups=groups) @@ -498,13 +498,13 @@ def group_acl_report(group_name): for modifier, entries, rights in acl_iterator: if group_name in entries: item_id = item.meta.get(ITEMID) - fqname = CompositeName(item.meta.get(NAMESPACE), u'itemid', item_id) + fqname = CompositeName(item.meta.get(NAMESPACE), 'itemid', item_id) group_items.append(dict(name=item.meta.get(NAME), itemid=item_id, fqname=fqname, rights=rights)) return render_template('admin/group_acl_report.html', - title_name=_(u'Group ACL Report'), + title_name=_('Group ACL Report'), group_items=group_items, group_name=group_name) diff --git a/src/moin/apps/feed/_tests/test_feed.py b/src/moin/apps/feed/_tests/test_feed.py index 315db8cd2..ff05928dd 100644 --- a/src/moin/apps/feed/_tests/test_feed.py +++ b/src/moin/apps/feed/_tests/test_feed.py @@ -11,31 +11,31 @@ from moin._tests import update_item -class TestFeeds(object): +class TestFeeds: def test_global_atom(self, app): with app.test_client() as c: rv = c.get(url_for('feed.atom')) assert rv.status == '200 OK' assert rv.headers['Content-Type'] == 'application/atom+xml' - assert rv.data.startswith('' in rv.data - assert '' in rv.data + assert rv.data.startswith(b'' in rv.data + assert b'' in rv.data def test_global_atom_with_an_item(self, app): - basename = u'Foo' - update_item(basename, {COMMENT: u"foo data for feed item"}, '') + basename = 'Foo' + update_item(basename, {COMMENT: "foo data for feed item"}, '') with app.test_client() as c: rv = c.get(url_for('feed.atom')) assert rv.status == '200 OK' assert rv.headers['Content-Type'] == 'application/atom+xml' - assert rv.data.startswith(' 80: content = render_template('atom.html', get='comment_cont_merge', comment=rev_comment[79:], content=Markup(content)) - rev_comment = u"{0}...".format(rev_comment[:79]) - feed_title = u"{0} - {1}".format(author.get(NAME, ''), rev_comment) + rev_comment = "{0}...".format(rev_comment[:79]) + feed_title = "{0} - {1}".format(author.get(NAME, ''), rev_comment) else: - feed_title = u"{0}".format(author.get(NAME, '')) + feed_title = "{0}".format(author.get(NAME, '')) if not item_name: - feed_title = u"{0} - {1}".format(name, feed_title) + feed_title = "{0} - {1}".format(name, feed_title) feed.add(title=feed_title, title_type='text', summary=content, summary_type=content_type, author=author, diff --git a/src/moin/apps/frontend/_tests/test_frontend.py b/src/moin/apps/frontend/_tests/test_frontend.py index b2146de48..8866ec372 100644 --- a/src/moin/apps/frontend/_tests/test_frontend.py +++ b/src/moin/apps/frontend/_tests/test_frontend.py @@ -18,7 +18,7 @@ import pytest -class TestFrontend(object): +class TestFrontend: @pytest.fixture(autouse=True) def custom_setup(self, app): @@ -32,13 +32,14 @@ def _test_view(self, viewname, status='200 OK', data=('', ''), cont with self.app.test_client() as c: for method in ['HEAD', 'GET']: - print '%s %s' % (method, url_for(viewname, **viewopts)) + print('%s %s' % (method, url_for(viewname, **viewopts))) rv = c.open(url_for(viewname, **viewopts), method=method, data=params) + rv_data = rv.data.decode() assert rv.status == status assert rv.headers['Content-Type'] in content_types if method == 'GET': for item in data: - assert item in rv.data + assert item in rv_data return rv def _test_view_post(self, viewname, status='302 FOUND', content_types=('text/html; charset=utf-8', ), data=('', ''), form=None, viewopts=None): @@ -46,13 +47,14 @@ def _test_view_post(self, viewname, status='302 FOUND', content_types=('text/htm viewopts = {} if form is None: form = {} - print 'POST %s' % url_for(viewname, **viewopts) + print('POST %s' % url_for(viewname, **viewopts)) with self.app.test_client() as c: rv = c.post(url_for(viewname, **viewopts), data=form) + rv_data = rv.data.decode() assert rv.status == status assert rv.headers['Content-Type'] in content_types for item in data: - assert item in rv.data + assert item in rv_data return rv def test_ajaxdelete_item_name_route(self): @@ -213,7 +215,7 @@ def test_global_tags(self): self._test_view('frontend.global_tags') -class TestUsersettings(object): +class TestUsersettings: reinit_storage = True # avoid username / email collisions @pytest.yield_fixture(autouse=True) @@ -224,26 +226,26 @@ def custom_setup(self, app): flaskg.user = saved_user def test_user_password_change(self): - self.createUser(u'moin', u'Xiwejr622') - flaskg.user = user.User(name=u'moin', password=u'Xiwejr622') - form = self.fillPasswordChangeForm(u'Xiwejr622', u'Woodoo645', u'Woodoo645') + self.createUser('moin', 'Xiwejr622') + flaskg.user = user.User(name='moin', password='Xiwejr622') + form = self.fillPasswordChangeForm('Xiwejr622', 'Woodoo645', 'Woodoo645') valid = form.validate() assert valid # form data is valid def test_user_unicode_password_change(self): - name = u'moin' - password = u'__שם משתמש לא קיים__' # Hebrew + name = 'moin' + password = '__שם משתמש לא קיים__' # Hebrew self.createUser(name, password) flaskg.user = user.User(name=name, password=password) - form = self.fillPasswordChangeForm(password, u'Woodoo645', u'Woodoo645') + form = self.fillPasswordChangeForm(password, 'Woodoo645', 'Woodoo645') valid = form.validate() assert valid # form data is valid def test_user_password_change_to_unicode_pw(self): - name = u'moin' - password = u'Xiwejr622' - new_password = u'__שם משתמש לא קיים__' # Hebrew + name = 'moin' + password = 'Xiwejr622' + new_password = '__שם משתמש לא קיים__' # Hebrew self.createUser(name, password) flaskg.user = user.User(name=name, password=password) @@ -252,17 +254,17 @@ def test_user_password_change_to_unicode_pw(self): assert valid # form data is valid def test_fail_user_password_change_pw_mismatch(self): - self.createUser(u'moin', u'Xiwejr622') - flaskg.user = user.User(name=u'moin', password=u'Xiwejr622') - form = self.fillPasswordChangeForm(u'Xiwejr622', u'Piped33', u'Woodoo645') + self.createUser('moin', 'Xiwejr622') + flaskg.user = user.User(name='moin', password='Xiwejr622') + form = self.fillPasswordChangeForm('Xiwejr622', 'Piped33', 'Woodoo645') valid = form.validate() # form data is invalid because password1 != password2 assert not valid def test_fail_password_change(self): - self.createUser(u'moin', u'Xiwejr622') - flaskg.user = user.User(name=u'moin', password=u'Xiwejr622') - form = self.fillPasswordChangeForm(u'Xinetd33', u'Woodoo645', u'Woodoo645') + self.createUser('moin', 'Xiwejr622') + flaskg.user = user.User(name='moin', password='Xiwejr622') + form = self.fillPasswordChangeForm('Xinetd33', 'Woodoo645', 'Woodoo645') valid = form.validate() # form data is invalid because password_current != user.password assert not valid @@ -273,12 +275,12 @@ def fillPasswordChangeForm(self, current_password, password1, password2): """ helper to fill UserSettingsPasswordForm form """ FormClass = views.UserSettingsPasswordForm - request_form = ImmutableMultiDict([ - ('usersettings_password_password_current', current_password), - ('usersettings_password_password1', password1), - ('usersettings_password_password2', password2), - ('usersettings_password_submit', u'Save') - ]) + request_form = ( + ('password_current', current_password), + ('password1', password1), + ('password2', password2), + ('submit', 'Save') + ) form = FormClass.from_flat(request_form) return form diff --git a/src/moin/apps/frontend/views.py b/src/moin/apps/frontend/views.py index 5648f96a6..7d2da70a4 100644 --- a/src/moin/apps/frontend/views.py +++ b/src/moin/apps/frontend/views.py @@ -22,7 +22,9 @@ import mimetypes import json import threading -import urllib +import urllib.request +import urllib.parse +import urllib.error from io import BytesIO from datetime import datetime from collections import namedtuple @@ -149,8 +151,8 @@ def global_views(): Provides a link to all the global views. """ return render_template('all.html', - title_name=_(u"Global Views"), - fqname=CompositeName(u'all', NAME_EXACT, u'') + title_name=_("Global Views"), + fqname=CompositeName('all', NAME_EXACT, '') ) @@ -220,7 +222,7 @@ def lookup(): if terms: LookupEntry = namedtuple('LookupEntry', 'name revid wikiname') name = lookup_form[NAME].value - name_exact = lookup_form[NAME_EXACT].value or u'' + name_exact = lookup_form[NAME_EXACT].value or '' terms.append(Term(WIKINAME, app.cfg.interwikiname)) q = And(terms) with flaskg.storage.indexer.ix[idx_name].searcher() as searcher: @@ -331,7 +333,7 @@ def add_facets(facets, time_sorting): @frontend.route('/+search/', methods=['GET', 'POST']) -@frontend.route('/+search', defaults=dict(item_name=u''), methods=['GET', 'POST']) +@frontend.route('/+search', defaults=dict(item_name=''), methods=['GET', 'POST']) def search(item_name): search_form = SearchForm.from_flat(request.values) ajax = True if request.args.get('boolajax') else False @@ -362,7 +364,7 @@ def search(item_name): _filter = [] _filter = add_file_filters(_filter, filetypes) if item_name: # Only search this item and subitems - prefix_name = item_name + u'/' + prefix_name = item_name + '/' terms = [Term(NAME_EXACT, item_name), Prefix(NAME_EXACT, prefix_name), ] show_transclusions = True @@ -417,22 +419,22 @@ def search(item_name): if ajax: html = render_template('ajaxsearch.html', results=results, - word_suggestions=u', '.join(word_suggestions), - name_suggestions=u', '.join(name_suggestions), - content_suggestions=u', '.join(content_suggestions), - omitted_words=u', '.join(omitted_words), + word_suggestions=', '.join(word_suggestions), + name_suggestions=', '.join(name_suggestions), + content_suggestions=', '.join(content_suggestions), + omitted_words=', '.join(omitted_words), history=history, is_ticket=is_ticket, ) else: html = render_template('search.html', results=results, - name_suggestions=u', '.join(name_suggestions), - content_suggestions=u', '.join(content_suggestions), + name_suggestions=', '.join(name_suggestions), + content_suggestions=', '.join(content_suggestions), query=query, medium_search_form=search_form, item_name=item_name, - omitted_words=u', '.join(omitted_words), + omitted_words=', '.join(omitted_words), history=history, ) flaskg.clock.stop('search render') @@ -690,7 +692,7 @@ def convert_item(item_name): backend = flaskg.storage storage_item = backend.get_item(**item.fqname.query) newrev = storage_item.store_revision(meta, out, overwrite=False, - action=unicode(ACTION_SAVE), + action=str(ACTION_SAVE), contenttype_current=Type(form['new_type'].value), contenttype_guessed=Type(form['new_type'].value), return_rev=True, @@ -749,7 +751,7 @@ def validate(self, element, state): validate_name(state['meta'], state['meta'].get(ITEMID)) return True except NameNotValidError as e: - self.invalid_name_msg = _(e) + self.invalid_name_msg = _(str(e)) return self.note_error(element, state, 'invalid_name_msg') @@ -913,8 +915,7 @@ def ajaxdestroy(item_name, req='destroy'): response = {"itemnames": [], "status": []} for itemname in itemnames: response["itemnames"].append(itemname) - # itemname is url quoted string coming across as unicode, must encode, unquote, decode - itemname = urllib.unquote(itemname.encode('ascii')).decode('utf-8') + itemname = urllib.parse.unquote(itemname) # itemname is url quoted str try: item = Item.create(itemname) if isinstance(item, NonExistent): @@ -942,7 +943,7 @@ def ajaxmodify(item_name): if not newitem: abort(404, item_name) if item_name: - newitem = item_name + u'/' + newitem + newitem = item_name + '/' + newitem return redirect(url_for_item(newitem)) @@ -1037,9 +1038,9 @@ def jfu_server(item_name): contenttype = data_file.content_type # guess by browser, based on file name data = data_file.stream if item_name: - subitem_prefix = item_name + u'/' + subitem_prefix = item_name + '/' else: - subitem_prefix = u'' + subitem_prefix = '' item_name = subitem_prefix + subitem_name jfu_server_lock.acquire() try: @@ -1060,9 +1061,9 @@ def jfu_server(item_name): def contenttype_selects_gen(): for g in content_registry.group_names: - description = u', '.join([e.display_name for e in content_registry.groups[g]]) + description = ', '.join([e.display_name for e in content_registry.groups[g]]) yield g, None, description - yield u'Unknown Items', None, u'Items of contenttype unknown to MoinMoin' + yield 'Unknown Items', None, 'Items of contenttype unknown to MoinMoin' ContenttypeGroup = MultiSelect.of(Enum.out_of(contenttype_selects_gen())).using(optional=True) @@ -1111,8 +1112,8 @@ def name_initial(files, uppercase=False, lowercase=False): initials = name_initial(files, uppercase=True) fqname = item.fqname if fqname.value == NAMESPACE_ALL: - fqname = CompositeName(NAMESPACE_ALL, NAME_EXACT, u'') - item_names = item_name.split(u'/') + fqname = CompositeName(NAMESPACE_ALL, NAME_EXACT, '') + item_names = item_name.split('/') ns_len = len(item.meta['namespace']) + 1 if item.meta['namespace'] else 0 # detect orphan subitems and make a list of their missing parents @@ -1174,8 +1175,8 @@ def mychanges(): my_changes_page = utils.getPageContent(my_changes, offset, results_per_page) return render_template('mychanges.html', - title_name=_(u'My Changes'), - headline=_(u'My Changes'), + title_name=_('My Changes'), + headline=_('My Changes'), my_changes_page=my_changes_page, ) @@ -1223,7 +1224,7 @@ def forwardrefs(item_name): return render_template('link_list_item_panel.html', item_name=item_name, fqname=split_fqname(item_name), - headline=_(u"Items that are referred by '%(item_name)s'", item_name=shorten_item_id(item_name)), + headline=_("Items that are referred by '%(item_name)s'", item_name=shorten_item_id(item_name)), fq_names=split_fqname_list(refs), ) @@ -1265,7 +1266,7 @@ def backrefs(item_name): item=item, item_name=item_name, fqname=split_fqname(item_name), - headline=_(u"Items which refer to '%(item_name)s'", item_name=shorten_item_id(item_name)), + headline=_("Items which refer to '%(item_name)s'", item_name=shorten_item_id(item_name)), fq_names=refs_here, ) @@ -1281,7 +1282,7 @@ def _backrefs(item_name): q = And([Term(WIKINAME, app.cfg.interwikiname), Or([Term(ITEMTRANSCLUSIONS, item_name), Term(ITEMLINKS, item_name)])]) revs = flaskg.storage.search(q) - return set([fqname for rev in revs for fqname in rev.fqnames]) + return {fqname for rev in revs for fqname in rev.fqnames} @frontend.route('/+history/') @@ -1302,7 +1303,7 @@ def history(item_name): else: results_per_page = app.cfg.results_per_page terms = [Term(WIKINAME, app.cfg.interwikiname), ] - terms.extend(Term(term, value) for term, value in fqname.query.iteritems()) + terms.extend(Term(term, value) for term, value in fqname.query.items()) if bookmark_time: terms.append(DateRange(MTIME, start=datetime.utcfromtimestamp(bookmark_time), end=None)) query = And(terms) @@ -1340,7 +1341,7 @@ def global_history(namespace): all_revs = bool(request.values.get('all')) idx_name = ALL_REVS if all_revs else LATEST_REVS terms = [Term(WIKINAME, app.cfg.interwikiname)] - fqname = CompositeName(NAMESPACE_ALL, NAME_EXACT, u'') + fqname = CompositeName(NAMESPACE_ALL, NAME_EXACT, '') if namespace != NAMESPACE_ALL: terms.append(Term(NAMESPACE, namespace)) fqname = split_fqname(namespace) @@ -1366,7 +1367,7 @@ def global_history(namespace): history.append(dh) del history[0] # kill the dummy - title_name = _(u'Global History') + title_name = _('Global History') if namespace == NAMESPACE_ALL: title = _("Global History of All Namespaces") elif namespace: @@ -1416,9 +1417,9 @@ def wanted_items(): existing, linked, transcluded = _compute_item_sets() referred = linked | transcluded wanteds = referred - existing - title_name = _(u'Wanted Items') + title_name = _('Wanted Items') return render_template('link_list_no_item_panel.html', - headline=_(u'Wanted Items'), + headline=_('Wanted Items'), title_name=title_name, fq_names=wanteds) @@ -1435,7 +1436,7 @@ def orphaned_items(): title_name = _('Orphaned Items') return render_template('link_list_no_item_panel.html', title_name=title_name, - headline=_(u'Orphaned Items'), + headline=_('Orphaned Items'), fq_names=orphans) @@ -1477,7 +1478,7 @@ def subscribe_item(item_name): # Try to unsubscribe if not u.unsubscribe(ITEMID, item.meta[ITEMID]): msg = _( - "Can't remove the subscription! You are subscribed to this page, but not by itemid.") + u' ' + _( + "Can't remove the subscription! You are subscribed to this page, but not by itemid.") + ' ' + _( "Please edit the subscription in your settings."), "error" else: # Try to subscribe @@ -1537,7 +1538,7 @@ def register(): if not _using_moin_auth(): return Response('No MoinAuth in auth list', 403) - title_name = _(u'Register') + title_name = _('Register') template = 'register.html' FormClass = RegistrationForm @@ -1635,7 +1636,7 @@ class PasswordLostForm(Form): @frontend.route('/+lostpass', methods=['GET', 'POST']) def lostpass(): # TODO use ?next=next_location check if target is in the wiki and not outside domain - title_name = _(u'Lost Password') + title_name = _('Lost Password') if not _using_moin_auth(): return Response('No MoinAuth in auth list', 403) @@ -1703,7 +1704,7 @@ class PasswordRecoveryForm(Form): @frontend.route('/+recoverpass', methods=['GET', 'POST']) def recoverpass(): # TODO use ?next=next_location check if target is in the wiki and not outside domain - title_name = _(u'Recover Password') + title_name = _('Recover Password') if not _using_moin_auth(): return Response('No MoinAuth in auth list', 403) @@ -1772,7 +1773,7 @@ def login(): form = LoginForm.from_flat(request.form) nexturl = form['nexturl'] return redirect(nexturl) - title_name = _(u'Login') + title_name = _('Login') if request.method in ['GET', 'HEAD']: form = LoginForm.from_defaults() @@ -1844,7 +1845,7 @@ def validate(self, element, state): class UserSettingsPasswordForm(Form): - name = 'usersettings_password' + form_name = 'usersettings_password' validators = [ValidChangePass()] password_current = RequiredPassword.using(label=L_('Current Password')).with_properties( @@ -1857,7 +1858,7 @@ class UserSettingsPasswordForm(Form): class UserSettingsNotificationForm(Form): - name = 'usersettings_notification' + form_name = 'usersettings_notification' email = YourEmail submit_label = L_('Save') @@ -1866,13 +1867,13 @@ class UserSettingsQuicklinksForm(Form): """ No validation is performed as lots of things are valid, existing items, non-existing items, external links, mailto, external wiki links... """ - name = 'usersettings_quicklinks' + form_name = 'usersettings_quicklinks' quicklinks = Quicklinks submit_label = L_('Save') class UserSettingsOptionsForm(Form): - name = 'usersettings_options' + form_name = 'usersettings_options' iso_8601 = Checkbox.using(label=L_('Always use ISO 8601 date-time format')) mailto_author = Checkbox.using(label=L_('Publish my email (not my wiki homepage) in author info')) edit_on_doubleclick = Checkbox.using(label=L_('Open editor on double click')) @@ -1920,7 +1921,7 @@ def validate(self, element, state): class UserSettingsSubscriptionsForm(Form): - name = 'usersettings_subscriptions' + form_name = 'usersettings_subscriptions' subscriptions = Subscriptions submit_label = L_('Save') @@ -1934,22 +1935,22 @@ def usersettings(): # these forms can't be global because we need app object, which is only available within a request: class UserSettingsPersonalForm(Form): - name = 'usersettings_personal' # "name" is duplicate + form_name = 'usersettings_personal' name = Names.using(label=L_('Usernames')).with_properties(placeholder=L_("The login usernames you want to use")) display_name = OptionalText.using(label=L_('Display-Name')).with_properties( placeholder=L_("Your display name (informational)")) # _timezones_keys = sorted(Locale('en').time_zones.keys()) - _timezones_keys = [unicode(tz) for tz in pytz.common_timezones] + _timezones_keys = [str(tz) for tz in pytz.common_timezones] timezone = Select.using(label=L_('Timezone')).out_of((e, e) for e in _timezones_keys) _supported_locales = [Locale('en')] + app.babel_instance.list_translations() locale = Select.using(label=L_('Locale')).out_of( - ((unicode(l), l.display_name) for l in _supported_locales), sort_by=1) + ((str(l), l.display_name) for l in _supported_locales), sort_by=1) submit_label = L_('Save') class UserSettingsUIForm(Form): - name = 'usersettings_ui' + form_name = 'usersettings_ui' theme_name = Select.using(label=L_('Theme name')).out_of( - ((unicode(t.identifier), t.name) for t in get_themes_list()), sort_by=1) + ((str(t.identifier), t.name) for t in get_themes_list()), sort_by=1) css_url = URL.using(label=L_('User CSS URL'), optional=True).with_properties( placeholder=L_("Give the URL of your custom CSS (optional)")) edit_rows = Natural.using(label=L_('Number rows in edit textarea')).with_properties( @@ -2071,7 +2072,7 @@ class UserSettingsUIForm(Form): forms[part] = form # initialize all remaining forms - for p, FormClass in form_classes.iteritems(): + for p, FormClass in form_classes.items(): if p not in forms: forms[p] = FormClass.from_object(flaskg.user) @@ -2092,7 +2093,7 @@ def bookmark(): else: try: tm = int(timestamp) - except StandardError: + except ValueError: tm = int(time.time()) else: tm = int(time.time()) @@ -2165,7 +2166,7 @@ def diff(item_name): else: results_per_page = app.cfg.results_per_page terms = [Term(WIKINAME, app.cfg.interwikiname), ] - terms.extend(Term(term, value) for term, value in fqname.query.iteritems()) + terms.extend(Term(term, value) for term, value in fqname.query.items()) query = And(terms) revs = flaskg.storage.search(query, idx_name=ALL_REVS, sortedby=[MTIME], reverse=True, limit=None) close_file(item.rev.data) @@ -2250,7 +2251,7 @@ def _diff(item, revid1, revid2, fqname): # create dict containing older and newer revids to be used in formatting links terms = [Term(WIKINAME, app.cfg.interwikiname), ] - terms.extend(Term(term, value) for term, value in item.fqname.query.iteritems()) + terms.extend(Term(term, value) for term, value in item.fqname.query.items()) query = And(terms) rev_ids = flaskg.storage.search(query, idx_name=ALL_REVS, sortedby=[MTIME], reverse=False, limit=None) rev_ids = [x.meta for x in rev_ids] @@ -2385,9 +2386,9 @@ def wikiMatches(fq_name, fq_names, start_re=None, end_re=None): :returns: start, end, matches dict """ if start_re is None: - start_re = re.compile(u'([{0}][{1}]+)'.format(CHARS_UPPER, CHARS_LOWER)) + start_re = re.compile('([{0}][{1}]+)'.format(CHARS_UPPER, CHARS_LOWER)) if end_re is None: - end_re = re.compile(u'([{0}][{1}]+)$'.format(CHARS_UPPER, CHARS_LOWER)) + end_re = re.compile('([{0}][{1}]+)$'.format(CHARS_UPPER, CHARS_LOWER)) # If we don't get results with wiki words matching, fall back to # simple first word and last word, using spaces. @@ -2446,7 +2447,7 @@ def closeMatches(fq_name, fq_names): lower[key] = [fqname] # Get all close matches item_name = fq_name.value - all_matches = difflib.get_close_matches(item_name.lower(), lower.keys(), + all_matches = difflib.get_close_matches(item_name.lower(), list(lower.keys()), n=len(lower), cutoff=0.6) # Replace lower names with original names @@ -2483,7 +2484,7 @@ def sitemap(item_name): ) -class NestedItemListBuilder(object): +class NestedItemListBuilder: def __init__(self): self.children = set() self.numnodes = 0 @@ -2535,9 +2536,9 @@ def global_tags(namespace): If namespace == '' tags from the default namespace are shown. If namespace == '' tags from that namespace are shown. """ - title_name = _(u'Global Tags') + title_name = _('Global Tags') query = {WIKINAME: app.cfg.interwikiname} - fqname = CompositeName(NAMESPACE_ALL, NAME_EXACT, u'') + fqname = CompositeName(NAMESPACE_ALL, NAME_EXACT, '') if namespace != NAMESPACE_ALL: query[NAMESPACE] = namespace fqname = split_fqname(namespace) @@ -2638,7 +2639,7 @@ def tickets(): status = request.form['status'] else: query = None - status = u'open' + status = 'open' current_timestamp = datetime.now().strftime("%Y_%m_%d-%H_%M_%S") idx_name = ALL_REVS @@ -2648,12 +2649,12 @@ def tickets(): if query: term2.append(qp.parse(query)) - if status == u'open': + if status == 'open': term1.append(Term(CLOSED, False)) - elif status == u'closed': + elif status == 'closed': term1.append(Term(CLOSED, True)) - selected_tags = set(request.args.getlist(u'selected_tags')) + selected_tags = set(request.args.getlist('selected_tags')) term1.extend(Term(TAGS, tag) for tag in selected_tags) assigned_username = request.args.get(ASSIGNED_TO) or query user = [Term(NAME, assigned_username)] @@ -2745,7 +2746,7 @@ def ticket_search(): ) -@frontend.route('/+comment', defaults=dict(item_name=u''), methods=['POST']) +@frontend.route('/+comment', defaults=dict(item_name=''), methods=['POST']) def comment(item_name): """ Initiated by tickets.js when user clicks Save button adding a reply to a prior comment. @@ -2757,9 +2758,9 @@ def comment(item_name): data = request.form.get('data') if data: current_timestamp = datetime.now().strftime("%Y_%m_%d-%H_%M_%S") - item_name = unicode(itemid) + u'/' + u'comment_' + unicode(current_timestamp) + item_name = str(itemid) + '/' + 'comment_' + str(current_timestamp) item = Item.create(item_name) - item.modify({}, data=data, element=u'comment', contenttype_guessed=u'text/x.moin.wiki;charset=utf-8', + item.modify({}, data=data, element='comment', contenttype_guessed='text/x.moin.wiki;charset=utf-8', refers_to=itemid, reply_to=reply_to, author=flaskg.user.name[0]) item = Item.create(item.name, rev_id=CURRENT) html = render_template('ticket/comment.html', diff --git a/src/moin/apps/misc/_tests/test_misc.py b/src/moin/apps/misc/_tests/test_misc.py index 94ae5634f..fa38b0fdf 100644 --- a/src/moin/apps/misc/_tests/test_misc.py +++ b/src/moin/apps/misc/_tests/test_misc.py @@ -8,15 +8,15 @@ from flask import url_for -class TestMisc(object): +class TestMisc: def test_global_sitemap(self, app): with app.test_client() as c: rv = c.get(url_for('misc.sitemap')) assert rv.status == '200 OK' assert rv.headers['Content-Type'] == 'text/xml; charset=utf-8' - assert rv.data.startswith('' in rv.data - assert '' in rv.data + assert rv.data.startswith(b'' in rv.data + assert b'' in rv.data def test_urls_names(self, app): with app.test_client() as c: diff --git a/src/moin/apps/serve/_tests/test_serve.py b/src/moin/apps/serve/_tests/test_serve.py index 329bbc60d..9b47b7f1c 100644 --- a/src/moin/apps/serve/_tests/test_serve.py +++ b/src/moin/apps/serve/_tests/test_serve.py @@ -8,7 +8,7 @@ from flask import url_for -class TestServe(object): +class TestServe: def test_index(self, app): with app.test_client() as c: rv = c.get(url_for('serve.index')) @@ -20,4 +20,4 @@ def test_files(self, app): rv = c.get(url_for('serve.files', name="DoesntExist")) assert rv.status == '404 NOT FOUND' assert rv.headers['Content-Type'] == 'text/html' - assert ' "user" - name = name.split(u'@')[0] + name = name.split('@')[0] if self.strip_windomain: # split off window domain, e.g. "DOMAIN\user" -> "user" - name = name.split(u'\\')[-1] + name = name.split('\\')[-1] if self.titlecase: # this "normalizes" the login name, e.g. meier, Meier, MEIER -> Meier @@ -328,7 +328,7 @@ def transform_username(self, name): if self.remove_blanks: # remove blanks e.g. "Joe Doe" -> "JoeDoe" - name = u''.join(name.split()) + name = ''.join(name.split()) return name diff --git a/src/moin/auth/_tests/test_auth.py b/src/moin/auth/_tests/test_auth.py index 525557ede..6a1f962a3 100644 --- a/src/moin/auth/_tests/test_auth.py +++ b/src/moin/auth/_tests/test_auth.py @@ -15,25 +15,25 @@ import pytest -class TestConfiguredGivenAuth(object): +class TestConfiguredGivenAuth: """ Test: configured GivenAuth """ @pytest.fixture def cfg(self): class Config(wikiconfig.Config): - auth = [GivenAuth(user_name=u'JoeDoe', autocreate=True)] + auth = [GivenAuth(user_name='JoeDoe', autocreate=True)] return Config def test(self): - assert flaskg.user.name == [u'JoeDoe', ] + assert flaskg.user.name == ['JoeDoe', ] -class TestGivenAuth(object): +class TestGivenAuth: """ Test: GivenAuth """ def test_decode_username(self): givenauth_obj = GivenAuth() result1 = givenauth_obj.decode_username('test_name') - assert result1 == u'test_name' + assert result1 == 'test_name' result2 = givenauth_obj.decode_username(123.45) assert result2 == 123.45 @@ -43,28 +43,28 @@ def test_transform_username(self): givenauth_obj.strip_windomain = True givenauth_obj.titlecase = True givenauth_obj.remove_blanks = True - result = givenauth_obj.transform_username(u'testDomain\\test name@moinmoin.org') + result = givenauth_obj.transform_username('testDomain\\test name@moinmoin.org') assert result == 'TestName' def test_request(self): givenauth_obj = GivenAuth() flaskg.user.auth_method = 'given' - givenauth_obj.user_name = u'testDomain\\test_user@moinmoin.org' + givenauth_obj.user_name = 'testDomain\\test_user@moinmoin.org' givenauth_obj.strip_maildomain = True givenauth_obj.strip_windomain = True givenauth_obj.titlecase = True givenauth_obj.remove_blanks = True - create_user(u'Test_User', u'test_pass', u'test@moinmoin.org') + create_user('Test_User', 'test_pass', 'test@moinmoin.org') test_user, bool_value = givenauth_obj.request(flaskg.user) assert test_user.valid - assert test_user.name == [u'Test_User', ] + assert test_user.name == ['Test_User', ] def test_handle_login(): # no messages in the beginning assert not flaskg._login_messages test_user1 = handle_login(flaskg.user, login_username='test_user', login_password='test_password', stage='moin') - test_login_message = [u'Invalid username or password.'] + test_login_message = ['Invalid username or password.'] assert flaskg._login_messages == test_login_message assert test_user1.name0 == ANON assert not test_user1.valid @@ -73,12 +73,12 @@ def test_handle_login(): # try with a valid user givenauth_obj = GivenAuth() flaskg.user.auth_method = 'given' - givenauth_obj.user_name = u'Test_User' - create_user(u'Test_User', u'test_pass', u'test@moinmoin.org') + givenauth_obj.user_name = 'Test_User' + create_user('Test_User', 'test_pass', 'test@moinmoin.org') test_user, bool_value = givenauth_obj.request(flaskg.user) test_user2 = handle_login(test_user, login_username='Test_User', login_password='test_pass', stage='moin') assert not flaskg._login_messages - assert test_user2.name == [u'Test_User', ] + assert test_user2.name == ['Test_User', ] assert test_user2.valid diff --git a/src/moin/auth/_tests/test_http.py b/src/moin/auth/_tests/test_http.py index 24b9ef804..a58735969 100644 --- a/src/moin/auth/_tests/test_http.py +++ b/src/moin/auth/_tests/test_http.py @@ -15,12 +15,12 @@ import pytest -class TestHTTPAuthMoin(object): +class TestHTTPAuthMoin: """ Test: HTTPAuthMoin """ @pytest.yield_fixture(autouse=True) def custom_setup(self): - class Auth(object): + class Auth: def __init__(self): self.username = 'ValidUser' self.password = 'test_pass' @@ -35,11 +35,11 @@ def __init__(self): def test_request(self): # create a new user - create_user(u'ValidUser', u'test_pass', u'test_email@moinmoin') + create_user('ValidUser', 'test_pass', 'test_email@moinmoin') httpauthmoin_obj = HTTPAuthMoin() test_user, bool_val = httpauthmoin_obj.request(flaskg.user) assert test_user.valid - assert test_user.name == [u'ValidUser', ] + assert test_user.name == ['ValidUser', ] assert bool_val # when auth_method is not 'http' diff --git a/src/moin/auth/_tests/test_ldap_login.py b/src/moin/auth/_tests/test_ldap_login.py index 7ae9efcaa..04266c63d 100644 --- a/src/moin/auth/_tests/test_ldap_login.py +++ b/src/moin/auth/_tests/test_ldap_login.py @@ -134,7 +134,7 @@ def testBugDefaultPasswd(self): assert u2 is None -class TestTwoLdapServers(object): +class TestTwoLdapServers: basedn = BASEDN rootdn = ROOTDN rootpw = ROOTPW @@ -174,7 +174,7 @@ def testLDAP(self): assert 'userb' in uids -class TestLdapFailover(object): +class TestLdapFailover: basedn = BASEDN rootdn = ROOTDN rootpw = ROOTPW diff --git a/src/moin/auth/_tests/test_log.py b/src/moin/auth/_tests/test_log.py index cd3137acb..eaee76e5f 100644 --- a/src/moin/auth/_tests/test_log.py +++ b/src/moin/auth/_tests/test_log.py @@ -15,7 +15,7 @@ logging = log.getLogger(__name__) -class TestAuthLog(object): +class TestAuthLog: """ Test: TestAuthLog """ def test_login(self): authlog_obj = AuthLog() diff --git a/src/moin/auth/http.py b/src/moin/auth/http.py index 78d87582c..a7920cad3 100644 --- a/src/moin/auth/http.py +++ b/src/moin/auth/http.py @@ -50,8 +50,7 @@ def request(self, user_obj, **kw): if auth and auth.username and auth.password is not None: logging.debug("http basic auth, received username: {0!r} password: {1!r}".format( auth.username, auth.password)) - u = user.User(name=auth.username.decode(self.coding), - password=auth.password.decode(self.coding), + u = user.User(name=auth.username, password=auth.password, auth_method=self.name, auth_attribs=[], trusted=self.trusted) logging.debug("user: {0!r}".format(u)) diff --git a/src/moin/auth/smb_mount.py b/src/moin/auth/smb_mount.py index 6353fc41a..2e5a3ceb1 100644 --- a/src/moin/auth/smb_mount.py +++ b/src/moin/auth/smb_mount.py @@ -61,10 +61,10 @@ def do_smb(self, username, password, login): mountpoint = self.mountpoint_fn(username) if login: - cmd = (u"sudo mount -t cifs -o user=%(user)s,domain=%(domain)s,uid=%(uid)d,dir_mode=%(dir_mode)s,file_mode=" - u"%(file_mode)s,iocharset=%(iocharset)s //%(server)s/%(share)s %(mountpoint)s >>%(log)s 2>&1") + cmd = ("sudo mount -t cifs -o user=%(user)s,domain=%(domain)s,uid=%(uid)d,dir_mode=%(dir_mode)s,file_mode=" + "%(file_mode)s,iocharset=%(iocharset)s //%(server)s/%(share)s %(mountpoint)s >>%(log)s 2>&1") else: - cmd = u"sudo umount %(mountpoint)s >>%(log)s 2>&1" + cmd = "sudo umount %(mountpoint)s >>%(log)s 2>&1" cmd = cmd % { 'user': username, diff --git a/src/moin/config/_tests/test_defaultconfig.py b/src/moin/config/_tests/test_defaultconfig.py index 93d0bdcaa..3c5c5bde6 100644 --- a/src/moin/config/_tests/test_defaultconfig.py +++ b/src/moin/config/_tests/test_defaultconfig.py @@ -11,21 +11,21 @@ from flask import current_app as app -class TestPasswordChecker(object): - username = u"SomeUser" +class TestPasswordChecker: + username = "SomeUser" tests_builtin = [ - (u'', False), # empty - (u'1966', False), # too short - (u'asdfghjk', False), # keyboard sequence - (u'QwertZuiop', False), # german keyboard sequence, with uppercase - (u'mnbvcx', False), # reverse keyboard sequence - (u'12345678', False), # keyboard sequence, too easy - (u'aaaaaaaa', False), # not enough different chars - (u'BBBaaaddd', False), # not enough different chars + ('', False), # empty + ('1966', False), # too short + ('asdfghjk', False), # keyboard sequence + ('QwertZuiop', False), # german keyboard sequence, with uppercase + ('mnbvcx', False), # reverse keyboard sequence + ('12345678', False), # keyboard sequence, too easy + ('aaaaaaaa', False), # not enough different chars + ('BBBaaaddd', False), # not enough different chars (username, False), # username == password (username[1:-1], False), # password in username - (u"XXX{0}XXX".format(username), False), # username in password - (u'Moin-2007', True), # this should be OK + ("XXX{0}XXX".format(username), False), # username in password + ('Moin-2007', True), # this should be OK ] def testBuiltinPasswordChecker(self): @@ -35,7 +35,7 @@ def testBuiltinPasswordChecker(self): else: for pw, result in self.tests_builtin: pw_error = pw_checker(self.username, pw) - print "{0!r}: {1}".format(pw, pw_error) + print("{0!r}: {1}".format(pw, pw_error)) assert result == (pw_error is None) diff --git a/src/moin/config/default.py b/src/moin/config/default.py index 546cc1a1e..6ef138bfc 100644 --- a/src/moin/config/default.py +++ b/src/moin/config/default.py @@ -29,12 +29,12 @@ logging = log.getLogger(__name__) -class CacheClass(object): +class CacheClass: """ just a container for stuff we cache """ pass -class ConfigFunctionality(object): +class ConfigFunctionality: """ Configuration base class with config class behaviour. This class contains the functionality for the DefaultConfig @@ -70,8 +70,8 @@ def __init__(self): self.cache.item_group_regex = re.compile(self.item_group_regex, re.UNICODE) # the ..._regexact versions only match if nothing is left (exact match) - self.cache.item_dict_regexact = re.compile(u'^{0}$'.format(self.item_dict_regex), re.UNICODE) - self.cache.item_group_regexact = re.compile(u'^{0}$'.format(self.item_group_regex), re.UNICODE) + self.cache.item_dict_regexact = re.compile('^{0}$'.format(self.item_dict_regex), re.UNICODE) + self.cache.item_group_regexact = re.compile('^{0}$'.format(self.item_group_regex), re.UNICODE) # compiled functions ACL self.cache.acl_functions = AccessControlList([self.acl_functions], valid=self.acl_rights_functions) @@ -200,7 +200,7 @@ def _decode(self): config files. """ charset = 'utf-8' - message = u""" + message = """ "%(name)s" configuration variable is a string, but should be unicode. Use %(name)s = u"value" syntax for unicode variables. @@ -221,19 +221,19 @@ def _decode(self): attr = getattr(self, name, None) if attr is not None: # Try to decode strings - if isinstance(attr, str): + if isinstance(attr, bytes): try: - setattr(self, name, unicode(attr, charset)) + setattr(self, name, str(attr, charset)) except UnicodeError: raise error.ConfigurationError(message % {'name': name}) # Look into lists and try to decode strings inside them elif isinstance(attr, list): - for i in xrange(len(attr)): + for i in range(len(attr)): item = attr[i] - if isinstance(item, str): + if isinstance(item, bytes): try: - attr[i] = unicode(item, charset) + attr[i] = str(item, charset) except UnicodeError: raise error.ConfigurationError(message % {'name': name}) @@ -280,8 +280,8 @@ def _default_password_checker(cfg, username, password, username_lower in password_lower or password_lower in username_lower: return _("Password is too easy to guess (password contains name or name contains password).") - keyboards = (ur"`1234567890-=qwertyuiop[]\asdfghjkl;'zxcvbnm,./", # US kbd - ur"^1234567890ß´qwertzuiopü+asdfghjklöä#yxcvbnm,.-", # german kbd + keyboards = (r"`1234567890-=qwertyuiop[]\asdfghjkl;'zxcvbnm,./", # US kbd + r"^1234567890ß´qwertzuiopü+asdfghjklöä#yxcvbnm,.-", # german kbd ) # TODO add more keyboards! for kbd in keyboards: rev_kbd = kbd[::-1] @@ -291,7 +291,7 @@ def _default_password_checker(cfg, username, password, return None -class DefaultExpression(object): +class DefaultExpression: def __init__(self, exprstr): self.text = exprstr self.value = eval(exprstr) @@ -344,7 +344,7 @@ def __init__(self, exprstr): )), # ========================================================================== 'style': ('Style / Theme / UI', 'These settings control how the wiki user interface will look like.', ( - ('sitename', u'Untitled Wiki', + ('sitename', 'Untitled Wiki', "Short description of your wiki site, displayed below the logo on each page, and used in RSS documents as the channel title [Unicode]"), ('interwikiname', None, "unique, stable and required InterWiki name (prefix, moniker) of the site [Unicode]"), ('html_pagetitle', None, "Allows you to set a specific HTML page title (if None, it defaults to the value of 'sitename') [Unicode]"), @@ -360,7 +360,7 @@ def __init__(self, exprstr): # ('wikilink', 'frontend.tickets', dict(), L_('Tickets'), L_('List of Tickets')), ], 'Data to create the navi_bar from. Users can add more items in their quick links in user preferences. You need to configure a list of tuples (css_class, endpoint, args, label, title). Use L_() for translating. [list of tuples]'), - ('theme_default', u'topside', "Default theme."), + ('theme_default', 'topside', "Default theme."), ('serve_files', {}, """ @@ -368,7 +368,7 @@ def __init__(self, exprstr): from the filesystem as url .../+serve//... """), - ('supplementation_item_names', [u'Discussion', ], + ('supplementation_item_names', ['Discussion', ], "List of names of the supplementation (sub)items [Unicode]"), ('interwiki_preferred', [], "In dialogues, show those wikis at the top of the list [list of Unicode]."), @@ -447,16 +447,16 @@ def __init__(self, exprstr): )), # ========================================================================== 'items': ('Special Item Names', None, ( - ('default_root', u'Home', "Default root, use this value in case no match is found in root_mapping. [Unicode]"), + ('default_root', 'Home', "Default root, use this value in case no match is found in root_mapping. [Unicode]"), ('root_mapping', {}, "mapping of namespaces to item_roots."), # the following regexes should match the complete name when used in free text # the group 'all' shall match all, while the group 'key' shall match the key only # e.g. FooGroup -> group 'all' == FooGroup, group 'key' == Foo # moin's code will add ^ / $ at beginning / end when needed - ('item_dict_regex', ur'(?P(?P\S+)Dict)', + ('item_dict_regex', r'(?P(?P\S+)Dict)', 'Item names exactly matching this regex are regarded as items containing variable dictionary definitions [Unicode]'), - ('item_group_regex', ur'(?P(?P\S+)Group)', + ('item_group_regex', r'(?P(?P\S+)Group)', 'Item names exactly matching this regex are regarded as items containing group definitions [Unicode]'), )), # ========================================================================== @@ -472,9 +472,9 @@ def __init__(self, exprstr): SCROLL_PAGE_AFTER_EDIT: True, SHOW_COMMENTS: False, WANT_TRIVIAL: False, - ENC_PASSWORD: u'', # empty value == invalid hash - RECOVERPASS_KEY: u'', # empty value == invalid key - SESSION_KEY: u'', # empty value == invalid key + ENC_PASSWORD: '', # empty value == invalid hash + RECOVERPASS_KEY: '', # empty value == invalid key + SESSION_KEY: '', # empty value == invalid key DISABLED: False, BOOKMARKS: {}, QUICKLINKS: [], @@ -500,8 +500,8 @@ def __init__(self, exprstr): ('config_check_enabled', False, "if True, check configuration for unknown settings."), - ('timezone_default', u'UTC', "Default time zone."), - ('locale_default', u'en_US', "Default locale for user interface and content."), + ('timezone_default', 'UTC', "Default time zone."), + ('locale_default', 'en_US', "Default locale for user interface and content."), # ('log_remote_addr', True, "if True, log the remote IP address (and maybe hostname)."), ('log_reverse_dns_lookups', True, @@ -545,7 +545,7 @@ def __init__(self, exprstr): # options = { 'acl': ('Access Control Lists', 'ACLs control who may do what.', ( - ('functions', u'', 'Access Control List for functions.'), + ('functions', '', 'Access Control List for functions.'), ('rights_contents', ACL_RIGHTS_CONTENTS, 'Valid tokens for right sides of content ACL entries.'), ('rights_functions', ACL_RIGHTS_FUNCTIONS, 'Valid tokens for right sides of function ACL entries.'), )), @@ -565,7 +565,7 @@ def __init__(self, exprstr): ('email_verification', False, "if True, require a new user to verify his or her email address before the first login."), - ('homewiki', u'Self', + ('homewiki', 'Self', "interwiki name of the wiki where the user home pages are located [Unicode] - useful if you have ''many'' users. You could even link to nonwiki \"user pages\" if the wiki username is in the target URL."), ('use_gravatar', False, "if True, gravatar.com will be used to find User's avatar") )), @@ -578,7 +578,7 @@ def __init__(self, exprstr): ('sendmail', None, "sendmail command to use for sending mail (None = don't use sendmail)"), )), - 'registration':('Registration', 'These settings control registration options', ( + 'registration': ('Registration', 'These settings control registration options', ( ('only_by_superuser', False, 'True is recommended value for public wikis on the internet.'), ('hint', _('To request an account, see bottom of Home page.'), 'message on login page when only_by_superuser is True'), )), diff --git a/src/moin/conftest.py b/src/moin/conftest.py index 1dc2e126d..d0080f7d0 100644 --- a/src/moin/conftest.py +++ b/src/moin/conftest.py @@ -15,9 +15,6 @@ use a Config class to define the required configuration within the test class. """ - -from __future__ import absolute_import, division - import pytest import py diff --git a/src/moin/constants/chartypes.py b/src/moin/constants/chartypes.py index a3064da55..a1cf421b8 100644 --- a/src/moin/constants/chartypes.py +++ b/src/moin/constants/chartypes.py @@ -1,8 +1,8 @@ -CHARS_UPPER = u"\u0041\u0042\u0043\u0044\u0045\u0046\u0047\u0048\u0049\u004a\u004b\u004c\u004d\u004e\u004f\u0050\u0051\u0052\u0053\u0054\u0055\u0056\u0057\u0058\u0059\u005a\u00c0\u00c1\u00c2\u00c3\u00c4\u00c5\u00c6\u00c7\u00c8\u00c9\u00ca\u00cb\u00cc\u00cd\u00ce\u00cf\u00d0\u00d1\u00d2\u00d3\u00d4\u00d5\u00d6\u00d8\u00d9\u00da\u00db\u00dc\u00dd\u00de\u0100\u0102\u0104\u0106\u0108\u010a\u010c\u010e\u0110\u0112\u0114\u0116\u0118\u011a\u011c\u011e\u0120\u0122\u0124\u0126\u0128\u012a\u012c\u012e\u0130\u0132\u0134\u0136\u0139\u013b\u013d\u013f\u0141\u0143\u0145\u0147\u014a\u014c\u014e\u0150\u0152\u0154\u0156\u0158\u015a\u015c\u015e\u0160\u0162\u0164\u0166\u0168\u016a\u016c\u016e\u0170\u0172\u0174\u0176\u0178\u0179\u017b\u017d\u0181\u0182\u0184\u0186\u0187\u0189\u018a\u018b\u018e\u018f\u0190\u0191\u0193\u0194\u0196\u0197\u0198\u019c\u019d\u019f\u01a0\u01a2\u01a4\u01a6\u01a7\u01a9\u01ac\u01ae\u01af\u01b1\u01b2\u01b3\u01b5\u01b7\u01b8\u01bc\u01c4\u01c7\u01ca\u01cd\u01cf\u01d1\u01d3\u01d5\u01d7\u01d9\u01db\u01de\u01e0\u01e2\u01e4\u01e6\u01e8\u01ea\u01ec\u01ee\u01f1\u01f4\u01f6\u01f7\u01f8\u01fa\u01fc\u01fe\u0200\u0202\u0204\u0206\u0208\u020a\u020c\u020e\u0210\u0212\u0214\u0216\u0218\u021a\u021c\u021e\u0220\u0222\u0224\u0226\u0228\u022a\u022c\u022e\u0230\u0232\u0386\u0388\u0389\u038a\u038c\u038e\u038f\u0391\u0392\u0393\u0394\u0395\u0396\u0397\u0398\u0399\u039a\u039b\u039c\u039d\u039e\u039f\u03a0\u03a1\u03a3\u03a4\u03a5\u03a6\u03a7\u03a8\u03a9\u03aa\u03ab\u03d2\u03d3\u03d4\u03d8\u03da\u03dc\u03de\u03e0\u03e2\u03e4\u03e6\u03e8\u03ea\u03ec\u03ee\u03f4\u0400\u0401\u0402\u0403\u0404\u0405\u0406\u0407\u0408\u0409\u040a\u040b\u040c\u040d\u040e\u040f\u0410\u0411\u0412\u0413\u0414\u0415\u0416\u0417\u0418\u0419\u041a\u041b\u041c\u041d\u041e\u041f\u0420\u0421\u0422\u0423\u0424\u0425\u0426\u0427\u0428\u0429\u042a\u042b\u042c\u042d\u042e\u042f\u0460\u0462\u0464\u0466\u0468\u046a\u046c\u046e\u0470\u0472\u0474\u0476\u0478\u047a\u047c\u047e\u0480\u048a\u048c\u048e\u0490\u0492\u0494\u0496\u0498\u049a\u049c\u049e\u04a0\u04a2\u04a4\u04a6\u04a8\u04aa\u04ac\u04ae\u04b0\u04b2\u04b4\u04b6\u04b8\u04ba\u04bc\u04be\u04c0\u04c1\u04c3\u04c5\u04c7\u04c9\u04cb\u04cd\u04d0\u04d2\u04d4\u04d6\u04d8\u04da\u04dc\u04de\u04e0\u04e2\u04e4\u04e6\u04e8\u04ea\u04ec\u04ee\u04f0\u04f2\u04f4\u04f8\u0500\u0502\u0504\u0506\u0508\u050a\u050c\u050e\u0531\u0532\u0533\u0534\u0535\u0536\u0537\u0538\u0539\u053a\u053b\u053c\u053d\u053e\u053f\u0540\u0541\u0542\u0543\u0544\u0545\u0546\u0547\u0548\u0549\u054a\u054b\u054c\u054d\u054e\u054f\u0550\u0551\u0552\u0553\u0554\u0555\u0556\u10a0\u10a1\u10a2\u10a3\u10a4\u10a5\u10a6\u10a7\u10a8\u10a9\u10aa\u10ab\u10ac\u10ad\u10ae\u10af\u10b0\u10b1\u10b2\u10b3\u10b4\u10b5\u10b6\u10b7\u10b8\u10b9\u10ba\u10bb\u10bc\u10bd\u10be\u10bf\u10c0\u10c1\u10c2\u10c3\u10c4\u10c5\u1e00\u1e02\u1e04\u1e06\u1e08\u1e0a\u1e0c\u1e0e\u1e10\u1e12\u1e14\u1e16\u1e18\u1e1a\u1e1c\u1e1e\u1e20\u1e22\u1e24\u1e26\u1e28\u1e2a\u1e2c\u1e2e\u1e30\u1e32\u1e34\u1e36\u1e38\u1e3a\u1e3c\u1e3e\u1e40\u1e42\u1e44\u1e46\u1e48\u1e4a\u1e4c\u1e4e\u1e50\u1e52\u1e54\u1e56\u1e58\u1e5a\u1e5c\u1e5e\u1e60\u1e62\u1e64\u1e66\u1e68\u1e6a\u1e6c\u1e6e\u1e70\u1e72\u1e74\u1e76\u1e78\u1e7a\u1e7c\u1e7e\u1e80\u1e82\u1e84\u1e86\u1e88\u1e8a\u1e8c\u1e8e\u1e90\u1e92\u1e94\u1ea0\u1ea2\u1ea4\u1ea6\u1ea8\u1eaa\u1eac\u1eae\u1eb0\u1eb2\u1eb4\u1eb6\u1eb8\u1eba\u1ebc\u1ebe\u1ec0\u1ec2\u1ec4\u1ec6\u1ec8\u1eca\u1ecc\u1ece\u1ed0\u1ed2\u1ed4\u1ed6\u1ed8\u1eda\u1edc\u1ede\u1ee0\u1ee2\u1ee4\u1ee6\u1ee8\u1eea\u1eec\u1eee\u1ef0\u1ef2\u1ef4\u1ef6\u1ef8\u1f08\u1f09\u1f0a\u1f0b\u1f0c\u1f0d\u1f0e\u1f0f\u1f18\u1f19\u1f1a\u1f1b\u1f1c\u1f1d\u1f28\u1f29\u1f2a\u1f2b\u1f2c\u1f2d\u1f2e\u1f2f\u1f38\u1f39\u1f3a\u1f3b\u1f3c\u1f3d\u1f3e\u1f3f\u1f48\u1f49\u1f4a\u1f4b\u1f4c\u1f4d\u1f59\u1f5b\u1f5d\u1f5f\u1f68\u1f69\u1f6a\u1f6b\u1f6c\u1f6d\u1f6e\u1f6f\u1fb8\u1fb9\u1fba\u1fbb\u1fc8\u1fc9\u1fca\u1fcb\u1fd8\u1fd9\u1fda\u1fdb\u1fe8\u1fe9\u1fea\u1feb\u1fec\u1ff8\u1ff9\u1ffa\u1ffb\u2102\u2107\u210b\u210c\u210d\u2110\u2111\u2112\u2115\u2119\u211a\u211b\u211c\u211d\u2124\u2126\u2128\u212a\u212b\u212c\u212d\u2130\u2131\u2133\u213e\u213f\u2145\uff21\uff22\uff23\uff24\uff25\uff26\uff27\uff28\uff29\uff2a\uff2b\uff2c\uff2d\uff2e\uff2f\uff30\uff31\uff32\uff33\uff34\uff35\uff36\uff37\uff38\uff39\uff3a" +CHARS_UPPER = "\u0041\u0042\u0043\u0044\u0045\u0046\u0047\u0048\u0049\u004a\u004b\u004c\u004d\u004e\u004f\u0050\u0051\u0052\u0053\u0054\u0055\u0056\u0057\u0058\u0059\u005a\u00c0\u00c1\u00c2\u00c3\u00c4\u00c5\u00c6\u00c7\u00c8\u00c9\u00ca\u00cb\u00cc\u00cd\u00ce\u00cf\u00d0\u00d1\u00d2\u00d3\u00d4\u00d5\u00d6\u00d8\u00d9\u00da\u00db\u00dc\u00dd\u00de\u0100\u0102\u0104\u0106\u0108\u010a\u010c\u010e\u0110\u0112\u0114\u0116\u0118\u011a\u011c\u011e\u0120\u0122\u0124\u0126\u0128\u012a\u012c\u012e\u0130\u0132\u0134\u0136\u0139\u013b\u013d\u013f\u0141\u0143\u0145\u0147\u014a\u014c\u014e\u0150\u0152\u0154\u0156\u0158\u015a\u015c\u015e\u0160\u0162\u0164\u0166\u0168\u016a\u016c\u016e\u0170\u0172\u0174\u0176\u0178\u0179\u017b\u017d\u0181\u0182\u0184\u0186\u0187\u0189\u018a\u018b\u018e\u018f\u0190\u0191\u0193\u0194\u0196\u0197\u0198\u019c\u019d\u019f\u01a0\u01a2\u01a4\u01a6\u01a7\u01a9\u01ac\u01ae\u01af\u01b1\u01b2\u01b3\u01b5\u01b7\u01b8\u01bc\u01c4\u01c7\u01ca\u01cd\u01cf\u01d1\u01d3\u01d5\u01d7\u01d9\u01db\u01de\u01e0\u01e2\u01e4\u01e6\u01e8\u01ea\u01ec\u01ee\u01f1\u01f4\u01f6\u01f7\u01f8\u01fa\u01fc\u01fe\u0200\u0202\u0204\u0206\u0208\u020a\u020c\u020e\u0210\u0212\u0214\u0216\u0218\u021a\u021c\u021e\u0220\u0222\u0224\u0226\u0228\u022a\u022c\u022e\u0230\u0232\u0386\u0388\u0389\u038a\u038c\u038e\u038f\u0391\u0392\u0393\u0394\u0395\u0396\u0397\u0398\u0399\u039a\u039b\u039c\u039d\u039e\u039f\u03a0\u03a1\u03a3\u03a4\u03a5\u03a6\u03a7\u03a8\u03a9\u03aa\u03ab\u03d2\u03d3\u03d4\u03d8\u03da\u03dc\u03de\u03e0\u03e2\u03e4\u03e6\u03e8\u03ea\u03ec\u03ee\u03f4\u0400\u0401\u0402\u0403\u0404\u0405\u0406\u0407\u0408\u0409\u040a\u040b\u040c\u040d\u040e\u040f\u0410\u0411\u0412\u0413\u0414\u0415\u0416\u0417\u0418\u0419\u041a\u041b\u041c\u041d\u041e\u041f\u0420\u0421\u0422\u0423\u0424\u0425\u0426\u0427\u0428\u0429\u042a\u042b\u042c\u042d\u042e\u042f\u0460\u0462\u0464\u0466\u0468\u046a\u046c\u046e\u0470\u0472\u0474\u0476\u0478\u047a\u047c\u047e\u0480\u048a\u048c\u048e\u0490\u0492\u0494\u0496\u0498\u049a\u049c\u049e\u04a0\u04a2\u04a4\u04a6\u04a8\u04aa\u04ac\u04ae\u04b0\u04b2\u04b4\u04b6\u04b8\u04ba\u04bc\u04be\u04c0\u04c1\u04c3\u04c5\u04c7\u04c9\u04cb\u04cd\u04d0\u04d2\u04d4\u04d6\u04d8\u04da\u04dc\u04de\u04e0\u04e2\u04e4\u04e6\u04e8\u04ea\u04ec\u04ee\u04f0\u04f2\u04f4\u04f8\u0500\u0502\u0504\u0506\u0508\u050a\u050c\u050e\u0531\u0532\u0533\u0534\u0535\u0536\u0537\u0538\u0539\u053a\u053b\u053c\u053d\u053e\u053f\u0540\u0541\u0542\u0543\u0544\u0545\u0546\u0547\u0548\u0549\u054a\u054b\u054c\u054d\u054e\u054f\u0550\u0551\u0552\u0553\u0554\u0555\u0556\u10a0\u10a1\u10a2\u10a3\u10a4\u10a5\u10a6\u10a7\u10a8\u10a9\u10aa\u10ab\u10ac\u10ad\u10ae\u10af\u10b0\u10b1\u10b2\u10b3\u10b4\u10b5\u10b6\u10b7\u10b8\u10b9\u10ba\u10bb\u10bc\u10bd\u10be\u10bf\u10c0\u10c1\u10c2\u10c3\u10c4\u10c5\u1e00\u1e02\u1e04\u1e06\u1e08\u1e0a\u1e0c\u1e0e\u1e10\u1e12\u1e14\u1e16\u1e18\u1e1a\u1e1c\u1e1e\u1e20\u1e22\u1e24\u1e26\u1e28\u1e2a\u1e2c\u1e2e\u1e30\u1e32\u1e34\u1e36\u1e38\u1e3a\u1e3c\u1e3e\u1e40\u1e42\u1e44\u1e46\u1e48\u1e4a\u1e4c\u1e4e\u1e50\u1e52\u1e54\u1e56\u1e58\u1e5a\u1e5c\u1e5e\u1e60\u1e62\u1e64\u1e66\u1e68\u1e6a\u1e6c\u1e6e\u1e70\u1e72\u1e74\u1e76\u1e78\u1e7a\u1e7c\u1e7e\u1e80\u1e82\u1e84\u1e86\u1e88\u1e8a\u1e8c\u1e8e\u1e90\u1e92\u1e94\u1ea0\u1ea2\u1ea4\u1ea6\u1ea8\u1eaa\u1eac\u1eae\u1eb0\u1eb2\u1eb4\u1eb6\u1eb8\u1eba\u1ebc\u1ebe\u1ec0\u1ec2\u1ec4\u1ec6\u1ec8\u1eca\u1ecc\u1ece\u1ed0\u1ed2\u1ed4\u1ed6\u1ed8\u1eda\u1edc\u1ede\u1ee0\u1ee2\u1ee4\u1ee6\u1ee8\u1eea\u1eec\u1eee\u1ef0\u1ef2\u1ef4\u1ef6\u1ef8\u1f08\u1f09\u1f0a\u1f0b\u1f0c\u1f0d\u1f0e\u1f0f\u1f18\u1f19\u1f1a\u1f1b\u1f1c\u1f1d\u1f28\u1f29\u1f2a\u1f2b\u1f2c\u1f2d\u1f2e\u1f2f\u1f38\u1f39\u1f3a\u1f3b\u1f3c\u1f3d\u1f3e\u1f3f\u1f48\u1f49\u1f4a\u1f4b\u1f4c\u1f4d\u1f59\u1f5b\u1f5d\u1f5f\u1f68\u1f69\u1f6a\u1f6b\u1f6c\u1f6d\u1f6e\u1f6f\u1fb8\u1fb9\u1fba\u1fbb\u1fc8\u1fc9\u1fca\u1fcb\u1fd8\u1fd9\u1fda\u1fdb\u1fe8\u1fe9\u1fea\u1feb\u1fec\u1ff8\u1ff9\u1ffa\u1ffb\u2102\u2107\u210b\u210c\u210d\u2110\u2111\u2112\u2115\u2119\u211a\u211b\u211c\u211d\u2124\u2126\u2128\u212a\u212b\u212c\u212d\u2130\u2131\u2133\u213e\u213f\u2145\uff21\uff22\uff23\uff24\uff25\uff26\uff27\uff28\uff29\uff2a\uff2b\uff2c\uff2d\uff2e\uff2f\uff30\uff31\uff32\uff33\uff34\uff35\uff36\uff37\uff38\uff39\uff3a" -CHARS_LOWER = u"\u0061\u0062\u0063\u0064\u0065\u0066\u0067\u0068\u0069\u006a\u006b\u006c\u006d\u006e\u006f\u0070\u0071\u0072\u0073\u0074\u0075\u0076\u0077\u0078\u0079\u007a\u00aa\u00b5\u00ba\u00df\u00e0\u00e1\u00e2\u00e3\u00e4\u00e5\u00e6\u00e7\u00e8\u00e9\u00ea\u00eb\u00ec\u00ed\u00ee\u00ef\u00f0\u00f1\u00f2\u00f3\u00f4\u00f5\u00f6\u00f8\u00f9\u00fa\u00fb\u00fc\u00fd\u00fe\u00ff\u0101\u0103\u0105\u0107\u0109\u010b\u010d\u010f\u0111\u0113\u0115\u0117\u0119\u011b\u011d\u011f\u0121\u0123\u0125\u0127\u0129\u012b\u012d\u012f\u0131\u0133\u0135\u0137\u0138\u013a\u013c\u013e\u0140\u0142\u0144\u0146\u0148\u0149\u014b\u014d\u014f\u0151\u0153\u0155\u0157\u0159\u015b\u015d\u015f\u0161\u0163\u0165\u0167\u0169\u016b\u016d\u016f\u0171\u0173\u0175\u0177\u017a\u017c\u017e\u017f\u0180\u0183\u0185\u0188\u018c\u018d\u0192\u0195\u0199\u019a\u019b\u019e\u01a1\u01a3\u01a5\u01a8\u01aa\u01ab\u01ad\u01b0\u01b4\u01b6\u01b9\u01ba\u01bd\u01be\u01bf\u01c6\u01c9\u01cc\u01ce\u01d0\u01d2\u01d4\u01d6\u01d8\u01da\u01dc\u01dd\u01df\u01e1\u01e3\u01e5\u01e7\u01e9\u01eb\u01ed\u01ef\u01f0\u01f3\u01f5\u01f9\u01fb\u01fd\u01ff\u0201\u0203\u0205\u0207\u0209\u020b\u020d\u020f\u0211\u0213\u0215\u0217\u0219\u021b\u021d\u021f\u0223\u0225\u0227\u0229\u022b\u022d\u022f\u0231\u0233\u0250\u0251\u0252\u0253\u0254\u0255\u0256\u0257\u0258\u0259\u025a\u025b\u025c\u025d\u025e\u025f\u0260\u0261\u0262\u0263\u0264\u0265\u0266\u0267\u0268\u0269\u026a\u026b\u026c\u026d\u026e\u026f\u0270\u0271\u0272\u0273\u0274\u0275\u0276\u0277\u0278\u0279\u027a\u027b\u027c\u027d\u027e\u027f\u0280\u0281\u0282\u0283\u0284\u0285\u0286\u0287\u0288\u0289\u028a\u028b\u028c\u028d\u028e\u028f\u0290\u0291\u0292\u0293\u0294\u0295\u0296\u0297\u0298\u0299\u029a\u029b\u029c\u029d\u029e\u029f\u02a0\u02a1\u02a2\u02a3\u02a4\u02a5\u02a6\u02a7\u02a8\u02a9\u02aa\u02ab\u02ac\u02ad\u0390\u03ac\u03ad\u03ae\u03af\u03b0\u03b1\u03b2\u03b3\u03b4\u03b5\u03b6\u03b7\u03b8\u03b9\u03ba\u03bb\u03bc\u03bd\u03be\u03bf\u03c0\u03c1\u03c2\u03c3\u03c4\u03c5\u03c6\u03c7\u03c8\u03c9\u03ca\u03cb\u03cc\u03cd\u03ce\u03d0\u03d1\u03d5\u03d6\u03d7\u03d9\u03db\u03dd\u03df\u03e1\u03e3\u03e5\u03e7\u03e9\u03eb\u03ed\u03ef\u03f0\u03f1\u03f2\u03f3\u03f5\u0430\u0431\u0432\u0433\u0434\u0435\u0436\u0437\u0438\u0439\u043a\u043b\u043c\u043d\u043e\u043f\u0440\u0441\u0442\u0443\u0444\u0445\u0446\u0447\u0448\u0449\u044a\u044b\u044c\u044d\u044e\u044f\u0450\u0451\u0452\u0453\u0454\u0455\u0456\u0457\u0458\u0459\u045a\u045b\u045c\u045d\u045e\u045f\u0461\u0463\u0465\u0467\u0469\u046b\u046d\u046f\u0471\u0473\u0475\u0477\u0479\u047b\u047d\u047f\u0481\u048b\u048d\u048f\u0491\u0493\u0495\u0497\u0499\u049b\u049d\u049f\u04a1\u04a3\u04a5\u04a7\u04a9\u04ab\u04ad\u04af\u04b1\u04b3\u04b5\u04b7\u04b9\u04bb\u04bd\u04bf\u04c2\u04c4\u04c6\u04c8\u04ca\u04cc\u04ce\u04d1\u04d3\u04d5\u04d7\u04d9\u04db\u04dd\u04df\u04e1\u04e3\u04e5\u04e7\u04e9\u04eb\u04ed\u04ef\u04f1\u04f3\u04f5\u04f9\u0501\u0503\u0505\u0507\u0509\u050b\u050d\u050f\u0561\u0562\u0563\u0564\u0565\u0566\u0567\u0568\u0569\u056a\u056b\u056c\u056d\u056e\u056f\u0570\u0571\u0572\u0573\u0574\u0575\u0576\u0577\u0578\u0579\u057a\u057b\u057c\u057d\u057e\u057f\u0580\u0581\u0582\u0583\u0584\u0585\u0586\u0587\u1e01\u1e03\u1e05\u1e07\u1e09\u1e0b\u1e0d\u1e0f\u1e11\u1e13\u1e15\u1e17\u1e19\u1e1b\u1e1d\u1e1f\u1e21\u1e23\u1e25\u1e27\u1e29\u1e2b\u1e2d\u1e2f\u1e31\u1e33\u1e35\u1e37\u1e39\u1e3b\u1e3d\u1e3f\u1e41\u1e43\u1e45\u1e47\u1e49\u1e4b\u1e4d\u1e4f\u1e51\u1e53\u1e55\u1e57\u1e59\u1e5b\u1e5d\u1e5f\u1e61\u1e63\u1e65\u1e67\u1e69\u1e6b\u1e6d\u1e6f\u1e71\u1e73\u1e75\u1e77\u1e79\u1e7b\u1e7d\u1e7f\u1e81\u1e83\u1e85\u1e87\u1e89\u1e8b\u1e8d\u1e8f\u1e91\u1e93\u1e95\u1e96\u1e97\u1e98\u1e99\u1e9a\u1e9b\u1ea1\u1ea3\u1ea5\u1ea7\u1ea9\u1eab\u1ead\u1eaf\u1eb1\u1eb3\u1eb5\u1eb7\u1eb9\u1ebb\u1ebd\u1ebf\u1ec1\u1ec3\u1ec5\u1ec7\u1ec9\u1ecb\u1ecd\u1ecf\u1ed1\u1ed3\u1ed5\u1ed7\u1ed9\u1edb\u1edd\u1edf\u1ee1\u1ee3\u1ee5\u1ee7\u1ee9\u1eeb\u1eed\u1eef\u1ef1\u1ef3\u1ef5\u1ef7\u1ef9\u1f00\u1f01\u1f02\u1f03\u1f04\u1f05\u1f06\u1f07\u1f10\u1f11\u1f12\u1f13\u1f14\u1f15\u1f20\u1f21\u1f22\u1f23\u1f24\u1f25\u1f26\u1f27\u1f30\u1f31\u1f32\u1f33\u1f34\u1f35\u1f36\u1f37\u1f40\u1f41\u1f42\u1f43\u1f44\u1f45\u1f50\u1f51\u1f52\u1f53\u1f54\u1f55\u1f56\u1f57\u1f60\u1f61\u1f62\u1f63\u1f64\u1f65\u1f66\u1f67\u1f70\u1f71\u1f72\u1f73\u1f74\u1f75\u1f76\u1f77\u1f78\u1f79\u1f7a\u1f7b\u1f7c\u1f7d\u1f80\u1f81\u1f82\u1f83\u1f84\u1f85\u1f86\u1f87\u1f90\u1f91\u1f92\u1f93\u1f94\u1f95\u1f96\u1f97\u1fa0\u1fa1\u1fa2\u1fa3\u1fa4\u1fa5\u1fa6\u1fa7\u1fb0\u1fb1\u1fb2\u1fb3\u1fb4\u1fb6\u1fb7\u1fbe\u1fc2\u1fc3\u1fc4\u1fc6\u1fc7\u1fd0\u1fd1\u1fd2\u1fd3\u1fd6\u1fd7\u1fe0\u1fe1\u1fe2\u1fe3\u1fe4\u1fe5\u1fe6\u1fe7\u1ff2\u1ff3\u1ff4\u1ff6\u1ff7\u2071\u207f\u210a\u210e\u210f\u2113\u212f\u2134\u2139\u213d\u2146\u2147\u2148\u2149\ufb00\ufb01\ufb02\ufb03\ufb04\ufb05\ufb06\ufb13\ufb14\ufb15\ufb16\ufb17\uff41\uff42\uff43\uff44\uff45\uff46\uff47\uff48\uff49\uff4a\uff4b\uff4c\uff4d\uff4e\uff4f\uff50\uff51\uff52\uff53\uff54\uff55\uff56\uff57\uff58\uff59\uff5a\u0030\u0031\u0032\u0033\u0034\u0035\u0036\u0037\u0038\u0039\u00b2\u00b3\u00b9\u0660\u0661\u0662\u0663\u0664\u0665\u0666\u0667\u0668\u0669\u06f0\u06f1\u06f2\u06f3\u06f4\u06f5\u06f6\u06f7\u06f8\u06f9\u0966\u0967\u0968\u0969\u096a\u096b\u096c\u096d\u096e\u096f\u09e6\u09e7\u09e8\u09e9\u09ea\u09eb\u09ec\u09ed\u09ee\u09ef\u0a66\u0a67\u0a68\u0a69\u0a6a\u0a6b\u0a6c\u0a6d\u0a6e\u0a6f\u0ae6\u0ae7\u0ae8\u0ae9\u0aea\u0aeb\u0aec\u0aed\u0aee\u0aef\u0b66\u0b67\u0b68\u0b69\u0b6a\u0b6b\u0b6c\u0b6d\u0b6e\u0b6f\u0be7\u0be8\u0be9\u0bea\u0beb\u0bec\u0bed\u0bee\u0bef\u0c66\u0c67\u0c68\u0c69\u0c6a\u0c6b\u0c6c\u0c6d\u0c6e\u0c6f\u0ce6\u0ce7\u0ce8\u0ce9\u0cea\u0ceb\u0cec\u0ced\u0cee\u0cef\u0d66\u0d67\u0d68\u0d69\u0d6a\u0d6b\u0d6c\u0d6d\u0d6e\u0d6f\u0e50\u0e51\u0e52\u0e53\u0e54\u0e55\u0e56\u0e57\u0e58\u0e59\u0ed0\u0ed1\u0ed2\u0ed3\u0ed4\u0ed5\u0ed6\u0ed7\u0ed8\u0ed9\u0f20\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28\u0f29\u1040\u1041\u1042\u1043\u1044\u1045\u1046\u1047\u1048\u1049\u1369\u136a\u136b\u136c\u136d\u136e\u136f\u1370\u1371\u17e0\u17e1\u17e2\u17e3\u17e4\u17e5\u17e6\u17e7\u17e8\u17e9\u1810\u1811\u1812\u1813\u1814\u1815\u1816\u1817\u1818\u1819\u2070\u2074\u2075\u2076\u2077\u2078\u2079\u2080\u2081\u2082\u2083\u2084\u2085\u2086\u2087\u2088\u2089\u2460\u2461\u2462\u2463\u2464\u2465\u2466\u2467\u2468\u2474\u2475\u2476\u2477\u2478\u2479\u247a\u247b\u247c\u2488\u2489\u248a\u248b\u248c\u248d\u248e\u248f\u2490\u24ea\u24f5\u24f6\u24f7\u24f8\u24f9\u24fa\u24fb\u24fc\u24fd\u2776\u2777\u2778\u2779\u277a\u277b\u277c\u277d\u277e\u2780\u2781\u2782\u2783\u2784\u2785\u2786\u2787\u2788\u278a\u278b\u278c\u278d\u278e\u278f\u2790\u2791\u2792\uff10\uff11\uff12\uff13\uff14\uff15\uff16\uff17\uff18\uff19" +CHARS_LOWER = "\u0061\u0062\u0063\u0064\u0065\u0066\u0067\u0068\u0069\u006a\u006b\u006c\u006d\u006e\u006f\u0070\u0071\u0072\u0073\u0074\u0075\u0076\u0077\u0078\u0079\u007a\u00aa\u00b5\u00ba\u00df\u00e0\u00e1\u00e2\u00e3\u00e4\u00e5\u00e6\u00e7\u00e8\u00e9\u00ea\u00eb\u00ec\u00ed\u00ee\u00ef\u00f0\u00f1\u00f2\u00f3\u00f4\u00f5\u00f6\u00f8\u00f9\u00fa\u00fb\u00fc\u00fd\u00fe\u00ff\u0101\u0103\u0105\u0107\u0109\u010b\u010d\u010f\u0111\u0113\u0115\u0117\u0119\u011b\u011d\u011f\u0121\u0123\u0125\u0127\u0129\u012b\u012d\u012f\u0131\u0133\u0135\u0137\u0138\u013a\u013c\u013e\u0140\u0142\u0144\u0146\u0148\u0149\u014b\u014d\u014f\u0151\u0153\u0155\u0157\u0159\u015b\u015d\u015f\u0161\u0163\u0165\u0167\u0169\u016b\u016d\u016f\u0171\u0173\u0175\u0177\u017a\u017c\u017e\u017f\u0180\u0183\u0185\u0188\u018c\u018d\u0192\u0195\u0199\u019a\u019b\u019e\u01a1\u01a3\u01a5\u01a8\u01aa\u01ab\u01ad\u01b0\u01b4\u01b6\u01b9\u01ba\u01bd\u01be\u01bf\u01c6\u01c9\u01cc\u01ce\u01d0\u01d2\u01d4\u01d6\u01d8\u01da\u01dc\u01dd\u01df\u01e1\u01e3\u01e5\u01e7\u01e9\u01eb\u01ed\u01ef\u01f0\u01f3\u01f5\u01f9\u01fb\u01fd\u01ff\u0201\u0203\u0205\u0207\u0209\u020b\u020d\u020f\u0211\u0213\u0215\u0217\u0219\u021b\u021d\u021f\u0223\u0225\u0227\u0229\u022b\u022d\u022f\u0231\u0233\u0250\u0251\u0252\u0253\u0254\u0255\u0256\u0257\u0258\u0259\u025a\u025b\u025c\u025d\u025e\u025f\u0260\u0261\u0262\u0263\u0264\u0265\u0266\u0267\u0268\u0269\u026a\u026b\u026c\u026d\u026e\u026f\u0270\u0271\u0272\u0273\u0274\u0275\u0276\u0277\u0278\u0279\u027a\u027b\u027c\u027d\u027e\u027f\u0280\u0281\u0282\u0283\u0284\u0285\u0286\u0287\u0288\u0289\u028a\u028b\u028c\u028d\u028e\u028f\u0290\u0291\u0292\u0293\u0294\u0295\u0296\u0297\u0298\u0299\u029a\u029b\u029c\u029d\u029e\u029f\u02a0\u02a1\u02a2\u02a3\u02a4\u02a5\u02a6\u02a7\u02a8\u02a9\u02aa\u02ab\u02ac\u02ad\u0390\u03ac\u03ad\u03ae\u03af\u03b0\u03b1\u03b2\u03b3\u03b4\u03b5\u03b6\u03b7\u03b8\u03b9\u03ba\u03bb\u03bc\u03bd\u03be\u03bf\u03c0\u03c1\u03c2\u03c3\u03c4\u03c5\u03c6\u03c7\u03c8\u03c9\u03ca\u03cb\u03cc\u03cd\u03ce\u03d0\u03d1\u03d5\u03d6\u03d7\u03d9\u03db\u03dd\u03df\u03e1\u03e3\u03e5\u03e7\u03e9\u03eb\u03ed\u03ef\u03f0\u03f1\u03f2\u03f3\u03f5\u0430\u0431\u0432\u0433\u0434\u0435\u0436\u0437\u0438\u0439\u043a\u043b\u043c\u043d\u043e\u043f\u0440\u0441\u0442\u0443\u0444\u0445\u0446\u0447\u0448\u0449\u044a\u044b\u044c\u044d\u044e\u044f\u0450\u0451\u0452\u0453\u0454\u0455\u0456\u0457\u0458\u0459\u045a\u045b\u045c\u045d\u045e\u045f\u0461\u0463\u0465\u0467\u0469\u046b\u046d\u046f\u0471\u0473\u0475\u0477\u0479\u047b\u047d\u047f\u0481\u048b\u048d\u048f\u0491\u0493\u0495\u0497\u0499\u049b\u049d\u049f\u04a1\u04a3\u04a5\u04a7\u04a9\u04ab\u04ad\u04af\u04b1\u04b3\u04b5\u04b7\u04b9\u04bb\u04bd\u04bf\u04c2\u04c4\u04c6\u04c8\u04ca\u04cc\u04ce\u04d1\u04d3\u04d5\u04d7\u04d9\u04db\u04dd\u04df\u04e1\u04e3\u04e5\u04e7\u04e9\u04eb\u04ed\u04ef\u04f1\u04f3\u04f5\u04f9\u0501\u0503\u0505\u0507\u0509\u050b\u050d\u050f\u0561\u0562\u0563\u0564\u0565\u0566\u0567\u0568\u0569\u056a\u056b\u056c\u056d\u056e\u056f\u0570\u0571\u0572\u0573\u0574\u0575\u0576\u0577\u0578\u0579\u057a\u057b\u057c\u057d\u057e\u057f\u0580\u0581\u0582\u0583\u0584\u0585\u0586\u0587\u1e01\u1e03\u1e05\u1e07\u1e09\u1e0b\u1e0d\u1e0f\u1e11\u1e13\u1e15\u1e17\u1e19\u1e1b\u1e1d\u1e1f\u1e21\u1e23\u1e25\u1e27\u1e29\u1e2b\u1e2d\u1e2f\u1e31\u1e33\u1e35\u1e37\u1e39\u1e3b\u1e3d\u1e3f\u1e41\u1e43\u1e45\u1e47\u1e49\u1e4b\u1e4d\u1e4f\u1e51\u1e53\u1e55\u1e57\u1e59\u1e5b\u1e5d\u1e5f\u1e61\u1e63\u1e65\u1e67\u1e69\u1e6b\u1e6d\u1e6f\u1e71\u1e73\u1e75\u1e77\u1e79\u1e7b\u1e7d\u1e7f\u1e81\u1e83\u1e85\u1e87\u1e89\u1e8b\u1e8d\u1e8f\u1e91\u1e93\u1e95\u1e96\u1e97\u1e98\u1e99\u1e9a\u1e9b\u1ea1\u1ea3\u1ea5\u1ea7\u1ea9\u1eab\u1ead\u1eaf\u1eb1\u1eb3\u1eb5\u1eb7\u1eb9\u1ebb\u1ebd\u1ebf\u1ec1\u1ec3\u1ec5\u1ec7\u1ec9\u1ecb\u1ecd\u1ecf\u1ed1\u1ed3\u1ed5\u1ed7\u1ed9\u1edb\u1edd\u1edf\u1ee1\u1ee3\u1ee5\u1ee7\u1ee9\u1eeb\u1eed\u1eef\u1ef1\u1ef3\u1ef5\u1ef7\u1ef9\u1f00\u1f01\u1f02\u1f03\u1f04\u1f05\u1f06\u1f07\u1f10\u1f11\u1f12\u1f13\u1f14\u1f15\u1f20\u1f21\u1f22\u1f23\u1f24\u1f25\u1f26\u1f27\u1f30\u1f31\u1f32\u1f33\u1f34\u1f35\u1f36\u1f37\u1f40\u1f41\u1f42\u1f43\u1f44\u1f45\u1f50\u1f51\u1f52\u1f53\u1f54\u1f55\u1f56\u1f57\u1f60\u1f61\u1f62\u1f63\u1f64\u1f65\u1f66\u1f67\u1f70\u1f71\u1f72\u1f73\u1f74\u1f75\u1f76\u1f77\u1f78\u1f79\u1f7a\u1f7b\u1f7c\u1f7d\u1f80\u1f81\u1f82\u1f83\u1f84\u1f85\u1f86\u1f87\u1f90\u1f91\u1f92\u1f93\u1f94\u1f95\u1f96\u1f97\u1fa0\u1fa1\u1fa2\u1fa3\u1fa4\u1fa5\u1fa6\u1fa7\u1fb0\u1fb1\u1fb2\u1fb3\u1fb4\u1fb6\u1fb7\u1fbe\u1fc2\u1fc3\u1fc4\u1fc6\u1fc7\u1fd0\u1fd1\u1fd2\u1fd3\u1fd6\u1fd7\u1fe0\u1fe1\u1fe2\u1fe3\u1fe4\u1fe5\u1fe6\u1fe7\u1ff2\u1ff3\u1ff4\u1ff6\u1ff7\u2071\u207f\u210a\u210e\u210f\u2113\u212f\u2134\u2139\u213d\u2146\u2147\u2148\u2149\ufb00\ufb01\ufb02\ufb03\ufb04\ufb05\ufb06\ufb13\ufb14\ufb15\ufb16\ufb17\uff41\uff42\uff43\uff44\uff45\uff46\uff47\uff48\uff49\uff4a\uff4b\uff4c\uff4d\uff4e\uff4f\uff50\uff51\uff52\uff53\uff54\uff55\uff56\uff57\uff58\uff59\uff5a\u0030\u0031\u0032\u0033\u0034\u0035\u0036\u0037\u0038\u0039\u00b2\u00b3\u00b9\u0660\u0661\u0662\u0663\u0664\u0665\u0666\u0667\u0668\u0669\u06f0\u06f1\u06f2\u06f3\u06f4\u06f5\u06f6\u06f7\u06f8\u06f9\u0966\u0967\u0968\u0969\u096a\u096b\u096c\u096d\u096e\u096f\u09e6\u09e7\u09e8\u09e9\u09ea\u09eb\u09ec\u09ed\u09ee\u09ef\u0a66\u0a67\u0a68\u0a69\u0a6a\u0a6b\u0a6c\u0a6d\u0a6e\u0a6f\u0ae6\u0ae7\u0ae8\u0ae9\u0aea\u0aeb\u0aec\u0aed\u0aee\u0aef\u0b66\u0b67\u0b68\u0b69\u0b6a\u0b6b\u0b6c\u0b6d\u0b6e\u0b6f\u0be7\u0be8\u0be9\u0bea\u0beb\u0bec\u0bed\u0bee\u0bef\u0c66\u0c67\u0c68\u0c69\u0c6a\u0c6b\u0c6c\u0c6d\u0c6e\u0c6f\u0ce6\u0ce7\u0ce8\u0ce9\u0cea\u0ceb\u0cec\u0ced\u0cee\u0cef\u0d66\u0d67\u0d68\u0d69\u0d6a\u0d6b\u0d6c\u0d6d\u0d6e\u0d6f\u0e50\u0e51\u0e52\u0e53\u0e54\u0e55\u0e56\u0e57\u0e58\u0e59\u0ed0\u0ed1\u0ed2\u0ed3\u0ed4\u0ed5\u0ed6\u0ed7\u0ed8\u0ed9\u0f20\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28\u0f29\u1040\u1041\u1042\u1043\u1044\u1045\u1046\u1047\u1048\u1049\u1369\u136a\u136b\u136c\u136d\u136e\u136f\u1370\u1371\u17e0\u17e1\u17e2\u17e3\u17e4\u17e5\u17e6\u17e7\u17e8\u17e9\u1810\u1811\u1812\u1813\u1814\u1815\u1816\u1817\u1818\u1819\u2070\u2074\u2075\u2076\u2077\u2078\u2079\u2080\u2081\u2082\u2083\u2084\u2085\u2086\u2087\u2088\u2089\u2460\u2461\u2462\u2463\u2464\u2465\u2466\u2467\u2468\u2474\u2475\u2476\u2477\u2478\u2479\u247a\u247b\u247c\u2488\u2489\u248a\u248b\u248c\u248d\u248e\u248f\u2490\u24ea\u24f5\u24f6\u24f7\u24f8\u24f9\u24fa\u24fb\u24fc\u24fd\u2776\u2777\u2778\u2779\u277a\u277b\u277c\u277d\u277e\u2780\u2781\u2782\u2783\u2784\u2785\u2786\u2787\u2788\u278a\u278b\u278c\u278d\u278e\u278f\u2790\u2791\u2792\uff10\uff11\uff12\uff13\uff14\uff15\uff16\uff17\uff18\uff19" -CHARS_DIGITS = u"\u0030\u0031\u0032\u0033\u0034\u0035\u0036\u0037\u0038\u0039\u00b2\u00b3\u00b9\u0660\u0661\u0662\u0663\u0664\u0665\u0666\u0667\u0668\u0669\u06f0\u06f1\u06f2\u06f3\u06f4\u06f5\u06f6\u06f7\u06f8\u06f9\u0966\u0967\u0968\u0969\u096a\u096b\u096c\u096d\u096e\u096f\u09e6\u09e7\u09e8\u09e9\u09ea\u09eb\u09ec\u09ed\u09ee\u09ef\u0a66\u0a67\u0a68\u0a69\u0a6a\u0a6b\u0a6c\u0a6d\u0a6e\u0a6f\u0ae6\u0ae7\u0ae8\u0ae9\u0aea\u0aeb\u0aec\u0aed\u0aee\u0aef\u0b66\u0b67\u0b68\u0b69\u0b6a\u0b6b\u0b6c\u0b6d\u0b6e\u0b6f\u0be7\u0be8\u0be9\u0bea\u0beb\u0bec\u0bed\u0bee\u0bef\u0c66\u0c67\u0c68\u0c69\u0c6a\u0c6b\u0c6c\u0c6d\u0c6e\u0c6f\u0ce6\u0ce7\u0ce8\u0ce9\u0cea\u0ceb\u0cec\u0ced\u0cee\u0cef\u0d66\u0d67\u0d68\u0d69\u0d6a\u0d6b\u0d6c\u0d6d\u0d6e\u0d6f\u0e50\u0e51\u0e52\u0e53\u0e54\u0e55\u0e56\u0e57\u0e58\u0e59\u0ed0\u0ed1\u0ed2\u0ed3\u0ed4\u0ed5\u0ed6\u0ed7\u0ed8\u0ed9\u0f20\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28\u0f29\u1040\u1041\u1042\u1043\u1044\u1045\u1046\u1047\u1048\u1049\u1369\u136a\u136b\u136c\u136d\u136e\u136f\u1370\u1371\u17e0\u17e1\u17e2\u17e3\u17e4\u17e5\u17e6\u17e7\u17e8\u17e9\u1810\u1811\u1812\u1813\u1814\u1815\u1816\u1817\u1818\u1819\u2070\u2074\u2075\u2076\u2077\u2078\u2079\u2080\u2081\u2082\u2083\u2084\u2085\u2086\u2087\u2088\u2089\u2460\u2461\u2462\u2463\u2464\u2465\u2466\u2467\u2468\u2474\u2475\u2476\u2477\u2478\u2479\u247a\u247b\u247c\u2488\u2489\u248a\u248b\u248c\u248d\u248e\u248f\u2490\u24ea\u24f5\u24f6\u24f7\u24f8\u24f9\u24fa\u24fb\u24fc\u24fd\u2776\u2777\u2778\u2779\u277a\u277b\u277c\u277d\u277e\u2780\u2781\u2782\u2783\u2784\u2785\u2786\u2787\u2788\u278a\u278b\u278c\u278d\u278e\u278f\u2790\u2791\u2792\uff10\uff11\uff12\uff13\uff14\uff15\uff16\uff17\uff18\uff19" +CHARS_DIGITS = "\u0030\u0031\u0032\u0033\u0034\u0035\u0036\u0037\u0038\u0039\u00b2\u00b3\u00b9\u0660\u0661\u0662\u0663\u0664\u0665\u0666\u0667\u0668\u0669\u06f0\u06f1\u06f2\u06f3\u06f4\u06f5\u06f6\u06f7\u06f8\u06f9\u0966\u0967\u0968\u0969\u096a\u096b\u096c\u096d\u096e\u096f\u09e6\u09e7\u09e8\u09e9\u09ea\u09eb\u09ec\u09ed\u09ee\u09ef\u0a66\u0a67\u0a68\u0a69\u0a6a\u0a6b\u0a6c\u0a6d\u0a6e\u0a6f\u0ae6\u0ae7\u0ae8\u0ae9\u0aea\u0aeb\u0aec\u0aed\u0aee\u0aef\u0b66\u0b67\u0b68\u0b69\u0b6a\u0b6b\u0b6c\u0b6d\u0b6e\u0b6f\u0be7\u0be8\u0be9\u0bea\u0beb\u0bec\u0bed\u0bee\u0bef\u0c66\u0c67\u0c68\u0c69\u0c6a\u0c6b\u0c6c\u0c6d\u0c6e\u0c6f\u0ce6\u0ce7\u0ce8\u0ce9\u0cea\u0ceb\u0cec\u0ced\u0cee\u0cef\u0d66\u0d67\u0d68\u0d69\u0d6a\u0d6b\u0d6c\u0d6d\u0d6e\u0d6f\u0e50\u0e51\u0e52\u0e53\u0e54\u0e55\u0e56\u0e57\u0e58\u0e59\u0ed0\u0ed1\u0ed2\u0ed3\u0ed4\u0ed5\u0ed6\u0ed7\u0ed8\u0ed9\u0f20\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28\u0f29\u1040\u1041\u1042\u1043\u1044\u1045\u1046\u1047\u1048\u1049\u1369\u136a\u136b\u136c\u136d\u136e\u136f\u1370\u1371\u17e0\u17e1\u17e2\u17e3\u17e4\u17e5\u17e6\u17e7\u17e8\u17e9\u1810\u1811\u1812\u1813\u1814\u1815\u1816\u1817\u1818\u1819\u2070\u2074\u2075\u2076\u2077\u2078\u2079\u2080\u2081\u2082\u2083\u2084\u2085\u2086\u2087\u2088\u2089\u2460\u2461\u2462\u2463\u2464\u2465\u2466\u2467\u2468\u2474\u2475\u2476\u2477\u2478\u2479\u247a\u247b\u247c\u2488\u2489\u248a\u248b\u248c\u248d\u248e\u248f\u2490\u24ea\u24f5\u24f6\u24f7\u24f8\u24f9\u24fa\u24fb\u24fc\u24fd\u2776\u2777\u2778\u2779\u277a\u277b\u277c\u277d\u277e\u2780\u2781\u2782\u2783\u2784\u2785\u2786\u2787\u2788\u278a\u278b\u278c\u278d\u278e\u278f\u2790\u2791\u2792\uff10\uff11\uff12\uff13\uff14\uff15\uff16\uff17\uff18\uff19" -CHARS_SPACES = u"\u0009\u000a\u000b\u000c\u000d\u001c\u001d\u001e\u001f\u0020\u0085\u00a0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000" +CHARS_SPACES = "\u0009\u000a\u000b\u000c\u000d\u001c\u001d\u001e\u001f\u0020\u0085\u00a0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000" diff --git a/src/moin/constants/contenttypes.py b/src/moin/constants/contenttypes.py index 85738871b..9a3df4ef1 100644 --- a/src/moin/constants/contenttypes.py +++ b/src/moin/constants/contenttypes.py @@ -21,62 +21,62 @@ 'cplusplus', 'java', 'pascal', 'diff', 'gettext', 'xslt', 'creole', ] -CONTENTTYPE_USER = u'application/x.moin.userprofile' -CONTENTTYPE_DEFAULT = u'application/octet-stream' -CONTENTTYPE_NONEXISTENT = u'application/x-nonexistent' +CONTENTTYPE_USER = 'application/x.moin.userprofile' +CONTENTTYPE_DEFAULT = 'application/octet-stream' +CONTENTTYPE_NONEXISTENT = 'application/x-nonexistent' CONTENTTYPE_MARKUP = [ - u'text/x.moin.wiki;charset=utf-8', - u'text/x-mediawiki;charset=utf-8', - u'text/x.moin.creole;charset=utf-8', - u'text/x-markdown;charset=utf-8', - u'text/x-rst;charset=utf-8', - u'text/html;charset=utf-8', - u'application/docbook+xml;charset=utf-8', + 'text/x.moin.wiki;charset=utf-8', + 'text/x-mediawiki;charset=utf-8', + 'text/x.moin.creole;charset=utf-8', + 'text/x-markdown;charset=utf-8', + 'text/x-rst;charset=utf-8', + 'text/html;charset=utf-8', + 'application/docbook+xml;charset=utf-8', ] CONTENTTYPE_NO_EXPANSION = [ # no need to expand transclusions, etc. when converting to/from these types - u'text/x.moin.wiki;charset=utf-8', - u'text/x.moin.creole;charset=utf-8', + 'text/x.moin.wiki;charset=utf-8', + 'text/x.moin.creole;charset=utf-8', ] CONTENTTYPE_VARIABLES = [ # content types that support variables: @SIG@, @EMAIL@, @TIME@, @DATE@, etc - u'text/x.moin.wiki;charset=utf-8', - u'text/x.moin.wiki;format=1.9;charset=utf-8', + 'text/x.moin.wiki;charset=utf-8', + 'text/x.moin.wiki;format=1.9;charset=utf-8', ] CONTENTTYPE_MOIN_19 = [ - u'text/x.moin.wiki;format=1.9;charset=utf-8', + 'text/x.moin.wiki;format=1.9;charset=utf-8', ] CONTENTTYPE_TEXT = [ - u'text/plain;charset=utf-8', - u'text/x-diff;charset=utf-8', - u'text/x-python;charset=utf-8', - u'text/csv;charset=utf-8', - u'text/x-irclog;charset=utf-8', + 'text/plain;charset=utf-8', + 'text/x-diff;charset=utf-8', + 'text/x-python;charset=utf-8', + 'text/csv;charset=utf-8', + 'text/x-irclog;charset=utf-8', ] CONTENTTYPE_IMAGE = [ - u'image/svg+xml', - u'image/png', - u'image/jpeg', - u'image/gif', + 'image/svg+xml', + 'image/png', + 'image/jpeg', + 'image/gif', ] CONTENTTYPE_AUDIO = [ - u'audio/wave', - u'audio/ogg', - u'audio/mpeg', - u'audio/webm', + 'audio/wave', + 'audio/ogg', + 'audio/mpeg', + 'audio/webm', ] CONTENTTYPE_VIDEO = [ - u'video/ogg', - u'video/webm', - u'video/mp4', + 'video/ogg', + 'video/webm', + 'video/mp4', ] # TODO: is there a source that maps all (or common) file suffixes to media contenttypes as used by /maint/dump_html.py @@ -84,52 +84,52 @@ CONTENTTYPE_MEDIA_SUFFIX = tuple('.svg .png .jpg .jpeg .gif .wave .wav .ogg .oga .ogv.mpeg .mpg .mp3 .webm .mp4'.split()) CONTENTTYPE_DRAWING = [ - u'application/x-twikidraw', - u'application/x-anywikidraw', - u'application/x-svgdraw', + 'application/x-twikidraw', + 'application/x-anywikidraw', + 'application/x-svgdraw', ] CONTENTTYPE_OTHER = [ - u'application/octet-stream', - u'application/x-tar', - u'application/x-gtar', - u'application/zip', - u'application/pdf', + 'application/octet-stream', + 'application/x-tar', + 'application/x-gtar', + 'application/zip', + 'application/pdf', ] CONTENTTYPES_MAP = { - u'text/x.moin.wiki;charset=utf-8': 'Moinmoin', - u'text/x.moin.wiki;format=1.9;charset=utf-8': 'Moinmoin 1.9', - u'text/x-mediawiki;charset=utf-8': 'MediaWiki', - u'text/x.moin.creole;charset=utf-8': 'Creole', - u'text/x-markdown;charset=utf-8': 'Markdown', - u'text/x-rst;charset=utf-8': 'ReST', - u'text/html;charset=utf-8': 'HTML', - u'application/docbook+xml;charset=utf-8': 'DocBook', - u'text/plain;charset=utf-8': 'Plain Text', - u'text/x-diff;charset=utf-8': 'Diff/Patch', - u'text/x-python;charset=utf-8': 'Python Code', - u'text/csv;charset=utf-8': 'CSV', - u'text/x-irclog;charset=utf-8': 'IRC Log', - u'image/svg+xml': 'SVG Image', - u'image/png': 'PNG Image', - u'image/jpeg': 'JPEG Image', - u'image/gif': 'GIF Image', - u'audio/wave': 'WAV Audio', - u'audio/ogg': 'OGG Audio', - u'audio/mpeg': 'MP3 Audio', - u'audio/webm': 'WebM Audio', - u'video/ogg': 'OGG Video', - u'video/webm': 'WebM Video', - u'video/mp4': 'MP4 Video', - u'application/x-twikidraw': 'TDRAW', - u'application/x-anywikidraw': 'ADRAW', - u'application/x-svgdraw': 'SVGDRAW', - u'application/octet-stream': 'Binary File', - u'application/x-tar': 'TAR', - u'application/x-gtar': 'TGZ', - u'application/zip': 'ZIP', - u'application/pdf': 'PDF', + 'text/x.moin.wiki;charset=utf-8': 'Moinmoin', + 'text/x.moin.wiki;format=1.9;charset=utf-8': 'Moinmoin 1.9', + 'text/x-mediawiki;charset=utf-8': 'MediaWiki', + 'text/x.moin.creole;charset=utf-8': 'Creole', + 'text/x-markdown;charset=utf-8': 'Markdown', + 'text/x-rst;charset=utf-8': 'ReST', + 'text/html;charset=utf-8': 'HTML', + 'application/docbook+xml;charset=utf-8': 'DocBook', + 'text/plain;charset=utf-8': 'Plain Text', + 'text/x-diff;charset=utf-8': 'Diff/Patch', + 'text/x-python;charset=utf-8': 'Python Code', + 'text/csv;charset=utf-8': 'CSV', + 'text/x-irclog;charset=utf-8': 'IRC Log', + 'image/svg+xml': 'SVG Image', + 'image/png': 'PNG Image', + 'image/jpeg': 'JPEG Image', + 'image/gif': 'GIF Image', + 'audio/wave': 'WAV Audio', + 'audio/ogg': 'OGG Audio', + 'audio/mpeg': 'MP3 Audio', + 'audio/webm': 'WebM Audio', + 'video/ogg': 'OGG Video', + 'video/webm': 'WebM Video', + 'video/mp4': 'MP4 Video', + 'application/x-twikidraw': 'TDRAW', + 'application/x-anywikidraw': 'ADRAW', + 'application/x-svgdraw': 'SVGDRAW', + 'application/octet-stream': 'Binary File', + 'application/x-tar': 'TAR', + 'application/x-gtar': 'TGZ', + 'application/zip': 'ZIP', + 'application/pdf': 'PDF', } GROUP_MARKUP_TEXT = 'Markup Text Items' @@ -158,37 +158,37 @@ def ext_link(href, link_text=None): CONTENTTYPES_HELP_DOCS = { # content type: tuple - must defer forming url until wiki root is known - u'text/x.moin.wiki;charset=utf-8': (('user/moinwiki.html', _("Click for help on Moin Wiki markup."))), - u'text/x-mediawiki;charset=utf-8': (('user/mediawiki.html', _("Click for help on Media Wiki markup."))), - u'text/x.moin.creole;charset=utf-8': (('user/creolewiki.html', _("Click for help on Creole Wiki markup."))), - u'text/x-markdown;charset=utf-8': (('user/markdown.html', _("Click for help on Markdown Wiki markup."))), - u'text/x-rst;charset=utf-8': (('user/rest.html', _("Click for help on reST Wiki markup."))), - u'application/docbook+xml;charset=utf-8': (('user/docbook.html', _("Click for help on Docbook Wiki markup."))), + 'text/x.moin.wiki;charset=utf-8': (('user/moinwiki.html', _("Click for help on Moin Wiki markup."))), + 'text/x-mediawiki;charset=utf-8': (('user/mediawiki.html', _("Click for help on Media Wiki markup."))), + 'text/x.moin.creole;charset=utf-8': (('user/creolewiki.html', _("Click for help on Creole Wiki markup."))), + 'text/x-markdown;charset=utf-8': (('user/markdown.html', _("Click for help on Markdown Wiki markup."))), + 'text/x-rst;charset=utf-8': (('user/rest.html', _("Click for help on reST Wiki markup."))), + 'application/docbook+xml;charset=utf-8': (('user/docbook.html', _("Click for help on Docbook Wiki markup."))), # content type: help string - u'text/html;charset=utf-8': ext_link('http://ckeditor.com/'), - u'text/plain;charset=utf-8': help_on_plain_text, - u'text/x-diff;charset=utf-8': help_on_plain_text, - u'text/x-python;charset=utf-8': help_on_plain_text, - u'text/csv;charset=utf-8': help_on_csv, - u'text/x-irclog;charset=utf-8': help_on_plain_text, - u'image/svg+xml': help_on_binary, - u'image/png': help_on_binary, - u'image/jpeg': help_on_binary, - u'image/gif': help_on_binary, - u'audio/wave': help_on_binary, - u'audio/ogg': help_on_binary, - u'audio/mpeg': help_on_binary, - u'audio/webm': help_on_binary, - u'video/ogg': help_on_binary, - u'video/webm': help_on_binary, - u'video/mp4': help_on_binary, - u'application/x-twikidraw': ext_link('http://twiki.org/cgi-bin/view/Plugins/TWikiDrawPlugin'), - u'application/x-anywikidraw': ext_link('http://anywikidraw.sourceforge.net/'), - u'application/x-svgdraw': ext_link('http://code.google.com/p/svg-edit/'), - u'application/octet-stream': help_on_binary, - u'application/x-tar': help_on_binary, - u'application/x-gtar': help_on_binary, - u'application/zip': help_on_binary, - u'application/pdf': help_on_binary, + 'text/html;charset=utf-8': ext_link('http://ckeditor.com/'), + 'text/plain;charset=utf-8': help_on_plain_text, + 'text/x-diff;charset=utf-8': help_on_plain_text, + 'text/x-python;charset=utf-8': help_on_plain_text, + 'text/csv;charset=utf-8': help_on_csv, + 'text/x-irclog;charset=utf-8': help_on_plain_text, + 'image/svg+xml': help_on_binary, + 'image/png': help_on_binary, + 'image/jpeg': help_on_binary, + 'image/gif': help_on_binary, + 'audio/wave': help_on_binary, + 'audio/ogg': help_on_binary, + 'audio/mpeg': help_on_binary, + 'audio/webm': help_on_binary, + 'video/ogg': help_on_binary, + 'video/webm': help_on_binary, + 'video/mp4': help_on_binary, + 'application/x-twikidraw': ext_link('http://twiki.org/cgi-bin/view/Plugins/TWikiDrawPlugin'), + 'application/x-anywikidraw': ext_link('http://anywikidraw.sourceforge.net/'), + 'application/x-svgdraw': ext_link('http://code.google.com/p/svg-edit/'), + 'application/octet-stream': help_on_binary, + 'application/x-tar': help_on_binary, + 'application/x-gtar': help_on_binary, + 'application/zip': help_on_binary, + 'application/pdf': help_on_binary, } CONTENTTYPES_HELP_DOCS = defaultdict(lambda: _('No help for unknown content type.'), CONTENTTYPES_HELP_DOCS) diff --git a/src/moin/constants/forms.py b/src/moin/constants/forms.py index f0fba1814..0a38a9ac2 100644 --- a/src/moin/constants/forms.py +++ b/src/moin/constants/forms.py @@ -7,28 +7,28 @@ # Widget types -WIDGET_TEXT = u'text' # single-line text -WIDGET_MULTILINE_TEXT = u'multiline_text' -WIDGET_URL = u'url' -WIDGET_EMAIL = u'email' -WIDGET_PASSWORD = u'password' -WIDGET_CHECKBOX = u'checkbox' -WIDGET_INLINE_CHECKBOX = u'inline_checkbox' -WIDGET_ANY_INTEGER = u'any_integer' -WIDGET_SMALL_NATURAL = u'small_natural' -WIDGET_DATETIME = u'datetime' +WIDGET_TEXT = 'text' # single-line text +WIDGET_MULTILINE_TEXT = 'multiline_text' +WIDGET_URL = 'url' +WIDGET_EMAIL = 'email' +WIDGET_PASSWORD = 'password' +WIDGET_CHECKBOX = 'checkbox' +WIDGET_INLINE_CHECKBOX = 'inline_checkbox' +WIDGET_ANY_INTEGER = 'any_integer' +WIDGET_SMALL_NATURAL = 'small_natural' +WIDGET_DATETIME = 'datetime' -WIDGET_FILE = u'file' -WIDGET_SEARCH = u'search' -WIDGET_SUBMIT = u'submit' -WIDGET_HIDDEN = u'hidden' +WIDGET_FILE = 'file' +WIDGET_SEARCH = 'search' +WIDGET_SUBMIT = 'submit' +WIDGET_HIDDEN = 'hidden' -WIDGET_SELECT = u'select' -WIDGET_SELECT_SUBMIT = u'select_submit' -WIDGET_MULTI_SELECT = u'multi_select' +WIDGET_SELECT = 'select' +WIDGET_SELECT_SUBMIT = 'select_submit' +WIDGET_MULTI_SELECT = 'multi_select' -WIDGET_READONLY_STRING_LIST = u'readonly_string_list' -WIDGET_READONLY_ITEM_LINK_LIST = u'readonly_item_link_list' +WIDGET_READONLY_STRING_LIST = 'readonly_string_list' +WIDGET_READONLY_ITEM_LINK_LIST = 'readonly_item_link_list' # CSS Classes -CLASS_BUTTON = u'button' +CLASS_BUTTON = 'button' diff --git a/src/moin/constants/itemtypes.py b/src/moin/constants/itemtypes.py index 6ae79cde2..4b51d0d67 100644 --- a/src/moin/constants/itemtypes.py +++ b/src/moin/constants/itemtypes.py @@ -5,9 +5,9 @@ MoinMoin - itemtype related constants """ -ITEMTYPE_NONEXISTENT = u'nonexistent' -ITEMTYPE_USERPROFILE = u'userprofile' -ITEMTYPE_DEFAULT = u'default' # == wiki-like -ITEMTYPE_TICKET = u'ticket' -ITEMTYPE_BLOG = u'blog' -ITEMTYPE_BLOGENTRY = u'blogentry' +ITEMTYPE_NONEXISTENT = 'nonexistent' +ITEMTYPE_USERPROFILE = 'userprofile' +ITEMTYPE_DEFAULT = 'default' # == wiki-like +ITEMTYPE_TICKET = 'ticket' +ITEMTYPE_BLOG = 'blog' +ITEMTYPE_BLOGENTRY = 'blogentry' diff --git a/src/moin/constants/keys.py b/src/moin/constants/keys.py index 19d21de59..c256aba72 100644 --- a/src/moin/constants/keys.py +++ b/src/moin/constants/keys.py @@ -6,60 +6,60 @@ """ # metadata keys -NAME = u"name" -NAME_OLD = u"name_old" -NAMESPACE = u"namespace" +NAME = "name" +NAME_OLD = "name_old" +NAMESPACE = "namespace" # if an item is reverted, we store the revision number we used for reverting there: -REVERTED_TO = u"reverted_to" +REVERTED_TO = "reverted_to" # some metadata key constants: -ACL = u"acl" +ACL = "acl" # keys for storing group and dict information # group of user names, e.g. for ACLs: -USERGROUP = u"usergroup" +USERGROUP = "usergroup" # needs more precise name / use case: -SOMEDICT = u"somedict" +SOMEDICT = "somedict" # TODO review plural constants -CONTENTTYPE = u"contenttype" -ITEMTYPE = u"itemtype" -SIZE = u"size" -LANGUAGE = u"language" -EXTERNALLINKS = u"externallinks" -ITEMLINKS = u"itemlinks" -ITEMTRANSCLUSIONS = u"itemtransclusions" -TAGS = u"tags" -TEMPLATE = u'template' # a TAGS value identifying an item as a template -CONTENTNGRAM = u"contentngram" - -ACTION = u"action" -ADDRESS = u"address" -HOSTNAME = u"hostname" -USERID = u"userid" -MTIME = u"mtime" -EXTRA = u"extra" -COMMENT = u"comment" -SUMMARY = u"summary" -TRASH = u"trash" +CONTENTTYPE = "contenttype" +ITEMTYPE = "itemtype" +SIZE = "size" +LANGUAGE = "language" +EXTERNALLINKS = "externallinks" +ITEMLINKS = "itemlinks" +ITEMTRANSCLUSIONS = "itemtransclusions" +TAGS = "tags" +TEMPLATE = 'template' # a TAGS value identifying an item as a template +CONTENTNGRAM = "contentngram" + +ACTION = "action" +ADDRESS = "address" +HOSTNAME = "hostname" +USERID = "userid" +MTIME = "mtime" +EXTRA = "extra" +COMMENT = "comment" +SUMMARY = "summary" +TRASH = "trash" # we need a specific hash algorithm to store hashes of revision data into meta # data. meta[HASH_ALGORITHM] = hash(rev_data, HASH_ALGORITHM) # some backends may use this also for other purposes. -HASH_ALGORITHM = u"sha1" +HASH_ALGORITHM = "sha1" HASH_LEN = 40 # length of hex str representation of hash value # some field names for whoosh index schema / documents in index: -NAME_EXACT = u"name_exact" -ITEMID = u"itemid" -REVID = u"revid" -REV_NUMBER = u"rev_number" -PARENTID = u"parentid" -DATAID = u"dataid" -WIKINAME = u"wikiname" -CONTENT = u"content" -REFERS_TO = u"refers_to" +NAME_EXACT = "name_exact" +ITEMID = "itemid" +REVID = "revid" +REV_NUMBER = "rev_number" +PARENTID = "parentid" +DATAID = "dataid" +WIKINAME = "wikiname" +CONTENT = "content" +REFERS_TO = "refers_to" IMMUTABLE_KEYS = [ ACTION, ADDRESS, @@ -79,40 +79,40 @@ ] # magic REVID for current revision: -CURRENT = u"current" +CURRENT = "current" # stuff from user profiles / for whoosh index -EMAIL = u"email" -DISPLAY_NAME = u"display_name" -THEME_NAME = u"theme_name" -LOCALE = u"locale" -TIMEZONE = u"timezone" -ENC_PASSWORD = u"enc_password" -SUBSCRIPTIONS = u"subscriptions" -SUBSCRIPTION_IDS = u"subscription_ids" -SUBSCRIPTION_PATTERNS = u"subscription_patterns" -BOOKMARKS = u"bookmarks" -QUICKLINKS = u"quicklinks" -SESSION_KEY = u"session_key" -SESSION_TOKEN = u"session_token" -RECOVERPASS_KEY = u"recoverpass_key" # TODO: this is used for email confirmation as well, maybe it needs better name -EDIT_ON_DOUBLECLICK = u"edit_on_doubleclick" -SCROLL_PAGE_AFTER_EDIT = u"scroll_page_after_edit" -SHOW_COMMENTS = u"show_comments" -ISO_8601 = u"iso_8601" -MAILTO_AUTHOR = u"mailto_author" -CSS_URL = u"css_url" -EDIT_ROWS = u"edit_rows" -RESULTS_PER_PAGE = u"results_per_page" -WANT_TRIVIAL = u"want_trivial" -EMAIL_SUBSCRIBED_EVENTS = u"email_subscribed_events" -DISABLED = u"disabled" -EMAIL_UNVALIDATED = u"email_unvalidated" -NAMERE = u"namere" -NAMEPREFIX = u"nameprefix" +EMAIL = "email" +DISPLAY_NAME = "display_name" +THEME_NAME = "theme_name" +LOCALE = "locale" +TIMEZONE = "timezone" +ENC_PASSWORD = "enc_password" +SUBSCRIPTIONS = "subscriptions" +SUBSCRIPTION_IDS = "subscription_ids" +SUBSCRIPTION_PATTERNS = "subscription_patterns" +BOOKMARKS = "bookmarks" +QUICKLINKS = "quicklinks" +SESSION_KEY = "session_key" +SESSION_TOKEN = "session_token" +RECOVERPASS_KEY = "recoverpass_key" # TODO: this is used for email confirmation as well, maybe it needs better name +EDIT_ON_DOUBLECLICK = "edit_on_doubleclick" +SCROLL_PAGE_AFTER_EDIT = "scroll_page_after_edit" +SHOW_COMMENTS = "show_comments" +ISO_8601 = "iso_8601" +MAILTO_AUTHOR = "mailto_author" +CSS_URL = "css_url" +EDIT_ROWS = "edit_rows" +RESULTS_PER_PAGE = "results_per_page" +WANT_TRIVIAL = "want_trivial" +EMAIL_SUBSCRIBED_EVENTS = "email_subscribed_events" +DISABLED = "disabled" +EMAIL_UNVALIDATED = "email_unvalidated" +NAMERE = "namere" +NAMEPREFIX = "nameprefix" # in which backend is some revision stored? -BACKENDNAME = u"backendname" +BACKENDNAME = "backendname" USEROBJ_ATTRS = [ # User objects proxy these attributes of the UserProfile objects: @@ -123,40 +123,40 @@ ] # keys for blog homepages -LOGO = u"logo" -SUPERTAGS = u"supertags" +LOGO = "logo" +SUPERTAGS = "supertags" # keys for blog entries -PTIME = u"ptime" +PTIME = "ptime" # keys for tickets -EFFORT = u"effort" -DIFFICULTY = u"difficulty" -SEVERITY = u"severity" -PRIORITY = u"priority" -ASSIGNED_TO = u"assigned_to" -SUPERSEDED_BY = u"superseded_by" -DEPENDS_ON = u"depends_on" -CLOSED = u"closed" -ELEMENT = u"element" -REPLY_TO = u"reply_to" +EFFORT = "effort" +DIFFICULTY = "difficulty" +SEVERITY = "severity" +PRIORITY = "priority" +ASSIGNED_TO = "assigned_to" +SUPERSEDED_BY = "superseded_by" +DEPENDS_ON = "depends_on" +CLOSED = "closed" +ELEMENT = "element" +REPLY_TO = "reply_to" # index names LATEST_REVS = 'latest_revs' ALL_REVS = 'all_revs' # values for ACTION key -ACTION_SAVE = u"SAVE" -ACTION_REVERT = u"REVERT" -ACTION_TRASH = u"TRASH" -ACTION_COPY = u"COPY" -ACTION_RENAME = u"RENAME" +ACTION_SAVE = "SAVE" +ACTION_REVERT = "REVERT" +ACTION_TRASH = "TRASH" +ACTION_COPY = "COPY" +ACTION_RENAME = "RENAME" # defaul LOCALE key value -DEFAULT_LOCALE = u"en" +DEFAULT_LOCALE = "en" # key for composite name -FQNAME = u'fqname' -FQNAMES = u'fqnames' +FQNAME = 'fqname' +FQNAMES = 'fqnames' # Values that FIELD can take in the composite name: [NAMESPACE/][@FIELD/]NAME FIELDS = [ NAME_EXACT, ITEMID, REVID, TAGS, USERID, ITEMLINKS, ITEMTRANSCLUSIONS diff --git a/src/moin/constants/misc.py b/src/moin/constants/misc.py index 0397853ce..947ed9402 100644 --- a/src/moin/constants/misc.py +++ b/src/moin/constants/misc.py @@ -7,12 +7,12 @@ import re -ANON = u'anonymous' +ANON = 'anonymous' # Invalid characters - invisible characters that should not be in page # names. Prevent user confusion and wiki abuse, e.g u'\u202aFrontPage'. ITEM_INVALID_CHARS_REGEX = re.compile( - ur""" + r""" \u0000 | # NULL # Bidi control characters @@ -27,11 +27,11 @@ CLEAN_INPUT_TRANSLATION_MAP = { # these chars will be replaced by blanks - ord(u'\t'): u' ', - ord(u'\r'): u' ', - ord(u'\n'): u' ', + ord('\t'): ' ', + ord('\r'): ' ', + ord('\n'): ' ', } -for c in u'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0b\x0c\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f': +for c in '\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0b\x0c\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f': # these chars will be removed CLEAN_INPUT_TRANSLATION_MAP[ord(c)] = None del c diff --git a/src/moin/constants/namespaces.py b/src/moin/constants/namespaces.py index 3d0d5f075..79e8dc675 100644 --- a/src/moin/constants/namespaces.py +++ b/src/moin/constants/namespaces.py @@ -5,8 +5,8 @@ MoinMoin - namespaces related constants """ -NAMESPACE_DEFAULT = u'' -NAMESPACE_USERPROFILES = u'userprofiles' -NAMESPACE_USERS = u'users' -NAMESPACE_ALL = u'all' # An identifier namespace which acts like a union of all the namespaces. +NAMESPACE_DEFAULT = '' +NAMESPACE_USERPROFILES = 'userprofiles' +NAMESPACE_USERS = 'users' +NAMESPACE_ALL = 'all' # An identifier namespace which acts like a union of all the namespaces. NAMESPACES_IDENTIFIER = [NAMESPACE_ALL, ] # List containing all the identifier namespaces. diff --git a/src/moin/constants/tools/chartypes_create.py b/src/moin/constants/tools/chartypes_create.py index db39b9cd7..ea2172bca 100644 --- a/src/moin/constants/tools/chartypes_create.py +++ b/src/moin/constants/tools/chartypes_create.py @@ -13,7 +13,7 @@ def main(): digits = [] space = [] for code in range(1, 65535): - c = unichr(code) + c = chr(code) str = "\\u{0:04x}".format(code) if c.isupper(): uppercase.append(str) @@ -24,12 +24,12 @@ def main(): elif c.isspace(): space.append(str) - chars_upper = u''.join(uppercase) - chars_lower = u''.join(lowercase + digits) - chars_digits = u''.join(digits) - chars_spaces = u''.join(space) + chars_upper = ''.join(uppercase) + chars_lower = ''.join(lowercase + digits) + chars_digits = ''.join(digits) + chars_spaces = ''.join(space) - print """ + print(""" CHARS_UPPER = u"%(chars_upper)s" CHARS_LOWER = u"%(chars_lower)s" @@ -39,7 +39,7 @@ def main(): CHARS_SPACES = u"%(chars_spaces)s" -""" % locals() +""" % locals()) if __name__ == '__main__': diff --git a/src/moin/converters/_args.py b/src/moin/converters/_args.py index 509d4c95c..a9d07cfab 100644 --- a/src/moin/converters/_args.py +++ b/src/moin/converters/_args.py @@ -6,7 +6,7 @@ """ -class Arguments(object): +class Arguments: """ Represent an argument list that may contain positional or keyword args. """ @@ -46,14 +46,14 @@ def items(self): """ for value in self.positional: yield None, value - for item in self.keyword.iteritems(): + for item in self.keyword.items(): yield item def keys(self): """ Return an iterator over all keys from the keyword arguments. """ - for key in self.keyword.iterkeys(): + for key in self.keyword.keys(): yield key def values(self): @@ -62,5 +62,5 @@ def values(self): """ for value in self.positional: yield value - for value in self.keyword.itervalues(): + for value in self.keyword.values(): yield value diff --git a/src/moin/converters/_args_wiki.py b/src/moin/converters/_args_wiki.py index 1dd89ca2d..04565dbea 100644 --- a/src/moin/converters/_args_wiki.py +++ b/src/moin/converters/_args_wiki.py @@ -5,9 +5,6 @@ MoinMoin - Arguments support for wiki formats """ - -from __future__ import absolute_import, division - import re from ._args import Arguments @@ -88,20 +85,23 @@ def unparse(args): :param args: Argument instance :returns: argument unicode object """ + def quote(s): + return '"%s"' % s.encode('unicode-escape').decode('ascii') + ret = [] for value in args.positional: if not _unparse_re.match(value): - value = u'"' + value.encode('unicode-escape') + u'"' + value = quote(value) ret.append(value) - keywords = args.keyword.items() + keywords = list(args.keyword.items()) keywords.sort(key=lambda a: a[0]) for key, value in keywords: if not _unparse_re.match(key): raise ValueError("Invalid keyword string") if not _unparse_re.match(value): - value = u'"' + value.encode('unicode-escape') + u'"' - ret.append(key + u'=' + value) + value = quote(value) + ret.append(key + '=' + value) - return u' '.join(ret) + return ' '.join(ret) diff --git a/src/moin/converters/_table.py b/src/moin/converters/_table.py index e50f4ffd6..83b4b22bd 100644 --- a/src/moin/converters/_table.py +++ b/src/moin/converters/_table.py @@ -5,16 +5,13 @@ MoinMoin - table data to DOM conversion support """ - -from __future__ import absolute_import, division - from moin.utils.tree import moin_page from emeraldtree import ElementTree as ET WORDBREAK_LEN = 30 -class TableMixin(object): +class TableMixin: """ Mixin to support building a DOM table. """ @@ -51,7 +48,7 @@ def build_dom_table(self, rows, head=None, cls=None): for row in rows: table_row = moin_page.table_row() for cell in row: - if isinstance(cell, ET.Node) and len(cell) and isinstance(cell[0], unicode) and \ + if isinstance(cell, ET.Node) and len(cell) and isinstance(cell[0], str) and \ len(cell[0].split()) == 1 and len(cell[0]) > WORDBREAK_LEN: # avoid destroying table layout by applying special styling to cells with long file name hyperlinks table_cell = moin_page.table_cell(children=[cell, ], attrib={moin_page.class_: 'moin-wordbreak'}) diff --git a/src/moin/converters/_tests/test__args_wiki.py b/src/moin/converters/_tests/test__args_wiki.py index 3159a2470..f032bb2eb 100644 --- a/src/moin/converters/_tests/test__args_wiki.py +++ b/src/moin/converters/_tests/test__args_wiki.py @@ -11,17 +11,17 @@ @pytest.mark.parametrize('wiki,positional,keyword', [ - (ur'both positional both=foo keyword=bar', - [u'both', u'positional'], - {u'both': u'foo', u'keyword': u'bar'}), + (r'both positional both=foo keyword=bar', + ['both', 'positional'], + {'both': 'foo', 'keyword': 'bar'}), - (ur'a-b a_b a-c=foo a_c=bar', - [u'a-b', u'a_b'], - {u'a-c': u'foo', u'a_c': u'bar'}), + (r'a-b a_b a-c=foo a_c=bar', + ['a-b', 'a_b'], + {'a-c': 'foo', 'a_c': 'bar'}), - (ur'''"a b\tc\nd" k="a b\tc\nd"''', - [u'a b\tc\nd'], - {u'k': u'a b\tc\nd'}), + (r'''"a b\tc\nd" k="a b\tc\nd"''', + ['a b\tc\nd'], + {'k': 'a b\tc\nd'}), ]) def test(wiki, positional, keyword): a = parse(wiki) @@ -33,6 +33,6 @@ def test(wiki, positional, keyword): def test_parse(): - a = parse(ur''''a b\tc\nd',k="a b\tc\nd"''') - assert a.positional == [u'a b\tc\nd'] - assert a.keyword == {u'k': u'a b\tc\nd'} + a = parse(r''''a b\tc\nd',k="a b\tc\nd"''') + assert a.positional == ['a b\tc\nd'] + assert a.keyword == {'k': 'a b\tc\nd'} diff --git a/src/moin/converters/_tests/test__wiki_macro.py b/src/moin/converters/_tests/test__wiki_macro.py index 1b3bf2580..83682a560 100644 --- a/src/moin/converters/_tests/test__wiki_macro.py +++ b/src/moin/converters/_tests/test__wiki_macro.py @@ -14,7 +14,7 @@ from moin.converters._args import Arguments -class TestConverter(object): +class TestConverter: namespaces = { moin_page.namespace: '', xinclude.namespace: 'xi', @@ -30,7 +30,7 @@ def setup_class(self): ('Macro', None, 'text', '', ''), - ('Macro', u'arg1', 'text', + ('Macro', 'arg1', 'text', 'arg1', 'arg1'), ] @@ -43,11 +43,11 @@ def test_macro(self, name, args, text, output_block, output_inline): data = [ ('Macro', None, 'text', ''), - ('Macro', u'arg1,arg2', 'text', + ('Macro', 'arg1,arg2', 'text', 'arg1,arg2'), ('Macro', 'key=value', 'text', 'key=value'), - ('Macro', u'arg1,arg2,key=value', 'text', + ('Macro', 'arg1,arg2,key=value', 'text', 'arg1,arg2,key=value'), ] @@ -59,29 +59,29 @@ def test_macro_arguments(self, name, args, text, output): ('BR', None, 'text', None, ''), - ('FootNote', u'note', 'text', + ('FootNote', 'note', 'text', '

note

', 'note'), ('TableOfContents', None, 'text', '', 'text'), - ('Include', u'page', 'text', + ('Include', 'page', 'text', '
', ''), - ('Include', u'^page', 'text', + ('Include', '^page', 'text', '
', ''), # each Include macro performs its own parsing as needed - ('Include', u'^page, sort=ascending', 'text', + ('Include', '^page, sort=ascending', 'text', '
', ''), - ('Include', u'^page, sort=descending', 'text', + ('Include', '^page, sort=descending', 'text', '
', ''), - ('Include', u'^page, items=5', 'text', + ('Include', '^page, items=5', 'text', '
', ''), - ('Include', u'^page, skipitems=5', 'text', + ('Include', '^page, skipitems=5', 'text', '
', ''), ] @@ -95,7 +95,7 @@ def test_pseudomacro(self, name, args, text, output_block, output_inline): ('test', None, ('text',), 'text'), # this form works, but is no longer used - ('test', Arguments([u'arg1']), ('text',), + ('test', Arguments(['arg1']), ('text',), 'arg1text'), ] @@ -105,12 +105,12 @@ def test_parser(self, name, args, text, output): def serialize_strip(self, elem, **options): result = serialize(elem, namespaces=self.namespaces, **options) - return self.output_re.sub(u'', result) + return self.output_re.sub('', result) def _do(self, name, args, text, context_block, output): result = self.conv.macro(name, args, text, context_block) if output is not None or result is not None: - if not isinstance(result, basestring): + if not isinstance(result, str): result = self.serialize_strip(result) assert result == output diff --git a/src/moin/converters/_tests/test_creole_in.py b/src/moin/converters/_tests/test_creole_in.py index d512a180b..2ec0e69d9 100644 --- a/src/moin/converters/_tests/test_creole_in.py +++ b/src/moin/converters/_tests/test_creole_in.py @@ -15,7 +15,7 @@ from ..creole_in import Converter -class TestConverter(object): +class TestConverter: namespaces = { moin_page: '', xlink: 'xlink', @@ -29,42 +29,42 @@ def setup_class(self): self.conv = Converter() data = [ - (u'Text', + ('Text', '

Text

'), - (u'Text\nTest', + ('Text\nTest', '

Text\nTest

'), - (u'Text\n\nTest', + ('Text\n\nTest', '

Text

Test

'), - (u'Line\\\\Break', + ('Line\\\\Break', '

LineBreak

'), - (u'Line\\\\\nBreak', + ('Line\\\\\nBreak', '

Line\nBreak

'), - (u'http://moinmo.in/', + ('http://moinmo.in/', '

http://moinmo.in/

'), - (u'[[http://moinmo.in/]]', + ('[[http://moinmo.in/]]', '

http://moinmo.in/

'), - (u'[[MoinMoin:InterWiki]]', + ('[[MoinMoin:InterWiki]]', '

InterWiki

'), - (u'[[mailto:fred@flinstones.org|drop me a note]]', + ('[[mailto:fred@flinstones.org|drop me a note]]', '

drop me a note

'), - (u'[[xmpp:room@conference.example.com?join|the chatroom]]', + ('[[xmpp:room@conference.example.com?join|the chatroom]]', '

the chatroom

'), # garbage input defaults to wiki.local name - (u'[[invalid:fred@flinstones.org|drop me a note]]', + ('[[invalid:fred@flinstones.org|drop me a note]]', '

drop me a note

'), - (u'[[javascript:alert("xss")]]', + ('[[javascript:alert("xss")]]', '

javascript:alert("xss")

'), - (u'[[http://moinmo.in/|MoinMoin]]', + ('[[http://moinmo.in/|MoinMoin]]', '

MoinMoin

'), - (u'[[MoinMoin]]', + ('[[MoinMoin]]', '

MoinMoin

'), - (u'{{http://moinmo.in/}}', + ('{{http://moinmo.in/}}', '

'), - (u'{{http://moinmo.in/|MoinMoin}}', + ('{{http://moinmo.in/|MoinMoin}}', '

'), - (u'{{my.png}}', + ('{{my.png}}', '

'), - (u'----', + ('----', ''), ] @@ -73,7 +73,7 @@ def test_base(self, input, output): self.do(input, output) data = [ - (u'Text', + ('Text', '

Text

', {'arguments': Arguments(keyword={'style': 'background-color: red'})}), ] @@ -83,17 +83,17 @@ def test_args(self, args): self.do(*args) data = [ - (u'//Emphasis//', + ('//Emphasis//', '

Emphasis

'), - (u'**Strong**', + ('**Strong**', '

Strong

'), - (u'//**Both**//', + ('//**Both**//', '

Both

'), - (u'**//Both//**', + ('**//Both//**', '

Both

'), - (u'Text //Emphasis\n//Text', + ('Text //Emphasis\n//Text', '

Text Emphasis\nText

'), - (u'Text //Emphasis\n\nText', + ('Text //Emphasis\n\nText', '

Text Emphasis

Text

'), ] @@ -102,13 +102,13 @@ def test_emphasis(self, input, output): self.do(input, output) data = [ - (u'~http://moinmo.in/', + ('~http://moinmo.in/', '

http://moinmo.in/

'), - (u'~[[escape]]', + ('~[[escape]]', '

[[escape]]

'), - (u'~<>', + ('~<>', '

<<escape>>

'), - (u'~{~{{escape}}}', + ('~{~{{escape}}}', '

{{{escape}}}

'), ] @@ -117,35 +117,35 @@ def test_escape(self, input, output): self.do(input, output) data = [ - (u'= Heading 1', + ('= Heading 1', 'Heading 1'), - (u'== Heading 2', + ('== Heading 2', 'Heading 2'), - (u'=== Heading 3', + ('=== Heading 3', 'Heading 3'), - (u'==== Heading 4', + ('==== Heading 4', 'Heading 4'), - (u'===== Heading 5', + ('===== Heading 5', 'Heading 5'), - (u'====== Heading 6', + ('====== Heading 6', 'Heading 6'), - (u'= Heading 1 =', + ('= Heading 1 =', 'Heading 1'), - (u'== Heading 2 ==', + ('== Heading 2 ==', 'Heading 2'), - (u'=== Heading 3 ===', + ('=== Heading 3 ===', 'Heading 3'), - (u'==== Heading 4 ====', + ('==== Heading 4 ====', 'Heading 4'), - (u'===== Heading 5 =====', + ('===== Heading 5 =====', 'Heading 5'), - (u'====== Heading 6 ======', + ('====== Heading 6 ======', 'Heading 6'), - (u'=== Heading 3', + ('=== Heading 3', 'Heading 3'), - (u'=== Heading 3 =', + ('=== Heading 3 =', 'Heading 3'), - (u'=== Heading 3 ==', + ('=== Heading 3 ==', 'Heading 3'), ] @@ -154,23 +154,23 @@ def test_heading(self, input, output): self.do(input, output) data = [ - (u'* Item', + ('* Item', 'Item'), - (u' *Item', + (' *Item', 'Item'), - (u'*Item', + ('*Item', 'Item'), - (u'* Item\nItem', + ('* Item\nItem', 'Item\nItem'), - (u'* Item 1\n*Item 2', + ('* Item 1\n*Item 2', 'Item 1Item 2'), - (u'* Item 1\n** Item 1.2\n* Item 2', + ('* Item 1\n** Item 1.2\n* Item 2', 'Item 1Item 1.2Item 2'), - (u'* List 1\n\n* List 2', + ('* List 1\n\n* List 2', 'List 1List 2'), - (u'# Item', + ('# Item', 'Item'), - (u'* List 1\n# List 2', + ('* List 1\n# List 2', 'List 1List 2'), ] @@ -179,27 +179,27 @@ def test_list(self, input, output): self.do(input, output) data = [ - (u'<
>', + ('<
>', ''), - (u'Text<
>Text', + ('Text<
>Text', '

TextText

'), - (u'<>', + ('<>', ''), - (u'<><>', + ('<><>', '

'), - (u'<>', + ('<>', 'arg'), - (u' <> ', + (' <> ', ''), - (u'Text <>', + ('Text <>', '

Text

'), - (u'Text <>', + ('Text <>', '

Text arg

'), - (u'Text\n<>', + ('Text\n<>', '

Text

'), - (u'Text\nText <>', + ('Text\nText <>', '

Text\nText

'), - (u'Text\n\n<>', + ('Text\n\n<>', '

Text

'), ] @@ -208,23 +208,23 @@ def test_macro(self, input, output): self.do(input, output) data = [ - (u'|Cell', + ('|Cell', 'Cell
'), - (u'| Cell ', + ('| Cell ', 'Cell
'), - (u'|Cell|', + ('|Cell|', 'Cell
'), - (u'|=Heading|', + ('|=Heading|', 'Heading
'), - (u'|Cell 1|Cell 2', + ('|Cell 1|Cell 2', 'Cell 1Cell 2
'), - (u'|Cell 1|Cell 2|', + ('|Cell 1|Cell 2|', 'Cell 1Cell 2
'), - (u'|Row 1\n|Row 2\n', + ('|Row 1\n|Row 2\n', 'Row 1Row 2
'), - (u'|Row 1|\n|Row 2|\n', + ('|Row 1|\n|Row 2|\n', 'Row 1Row 2
'), - (u'|Cell 1.1|Cell 1.2|\n|Cell 2.1|Cell 2.2|\n', + ('|Cell 1.1|Cell 1.2|\n|Cell 2.1|Cell 2.2|\n', 'Cell 1.1Cell 1.2Cell 2.1Cell 2.2
'), ] @@ -233,29 +233,29 @@ def test_table(self, input, output): self.do(input, output) data = [ - (u'{{{nowiki}}}', + ('{{{nowiki}}}', '

nowiki

'), - (u'{{{{nowiki}}}}', + ('{{{{nowiki}}}}', '

{nowiki}

'), - (u'text: {{{nowiki}}}, text', + ('text: {{{nowiki}}}, text', '

text: nowiki, text

'), - (u'{{{\nnowiki\n}}}', + ('{{{\nnowiki\n}}}', 'nowiki'), - (u'{{{\nnowiki\nno\nwiki\n}}}', + ('{{{\nnowiki\nno\nwiki\n}}}', 'nowiki\nno\nwiki'), - (u'{{{nowiki}}} {{{nowiki}}}', + ('{{{nowiki}}} {{{nowiki}}}', '

nowiki nowiki

'), # XXX: Is correct? - (u'{{{\n#!\nwiki\n}}}', + ('{{{\n#!\nwiki\n}}}', '

wiki

'), - (u'{{{\n#!(style="background-color: red")\nwiki\n}}}', + ('{{{\n#!(style="background-color: red")\nwiki\n}}}', '

wiki

'), - (u'{{{\n#!creole\nwiki\n}}}', + ('{{{\n#!creole\nwiki\n}}}', '

wiki

'), - (u'{{{\n#!creole(style="background-color: red")\nwiki\n}}}', + ('{{{\n#!creole(style="background-color: red")\nwiki\n}}}', '

wiki

'), - (u'{{{\n#!text/plain\ntext\n}}}', - u'text'), + ('{{{\n#!text/plain\ntext\n}}}', + 'text'), ] @pytest.mark.parametrize('input,output', data) @@ -263,17 +263,17 @@ def test_nowiki(self, input, output): self.do(input, output) data = [ - (u'Text\n* Item\n\nText', + ('Text\n* Item\n\nText', '

Text

Item

Text

'), - (u'Text\n* Item\n= Heading', + ('Text\n* Item\n= Heading', '

Text

ItemHeading
'), - (u'Text\n* Item\n{{{\nnowiki\n}}}', + ('Text\n* Item\n{{{\nnowiki\n}}}', '

Text

Itemnowiki
'), - (u'Text\n* Item\n|Item', + ('Text\n* Item\n|Item', '

Text

ItemItem
'), - (u'Text\n|Item\nText', + ('Text\n|Item\nText', '

Text

Item

Text

'), - (u'| text [[http://localhost | link]] |', + ('| text [[http://localhost | link]] |', 'text link
'), ] @@ -283,7 +283,7 @@ def test_composite(self, input, output): def serialize_strip(self, elem, **options): result = serialize(elem, namespaces=self.namespaces, **options) - return self.output_re.sub(u'', result) + return self.output_re.sub('', result) def do(self, input, output, args={}): out = self.conv(input, 'text/x.moin.creole;charset=utf-8', **args) diff --git a/src/moin/converters/_tests/test_csv_in.py b/src/moin/converters/_tests/test_csv_in.py index 6deae666c..b37792705 100644 --- a/src/moin/converters/_tests/test_csv_in.py +++ b/src/moin/converters/_tests/test_csv_in.py @@ -14,7 +14,7 @@ from ..text_csv_in import Converter -class TestConverter(object): +class TestConverter: namespaces = { moin_page: '', xlink: 'xlink', @@ -29,10 +29,10 @@ def setup_class(self): data = [ # first row not recognized as a header by Python csv module - (u'Head A,Head B\na,bb\nccc,dddd', + ('Head A,Head B\na,bb\nccc,dddd', 'Head AHead Babbcccdddd
'), # first row recognized as header - (u'Head A;Head B\n1;2\n3;4', + ('Head A;Head B\n1;2\n3;4', 'Head AHead B1234
'), ] @@ -42,7 +42,7 @@ def test_csv(self, input, output): def serialize_strip(self, elem, **options): result = serialize(elem, namespaces=self.namespaces, **options) - return self.output_re.sub(u'', result) + return self.output_re.sub('', result) def do(self, input, output, args={}): out = self.conv(input, 'text/csv;charset=utf-8', **args) diff --git a/src/moin/converters/_tests/test_docbook_in.py b/src/moin/converters/_tests/test_docbook_in.py index dc7455011..4df64545e 100644 --- a/src/moin/converters/_tests/test_docbook_in.py +++ b/src/moin/converters/_tests/test_docbook_in.py @@ -21,13 +21,13 @@ logging = log.getLogger(__name__) -class Base(object): - input_namespaces = ns_all = u'xmlns="{0}" xmlns:xlink="{1}"'.format(docbook.namespace, xlink.namespace) +class Base: + input_namespaces = ns_all = 'xmlns="{0}" xmlns:xlink="{1}"'.format(docbook.namespace, xlink.namespace) output_namespaces = { - moin_page.namespace: u'', - xlink.namespace: u'xlink', - xml.namespace: u'xml', - html.namespace: u'html', + moin_page.namespace: '', + xlink.namespace: 'xlink', + xml.namespace: 'xml', + html.namespace: 'html', } namespaces_xpath = {'xlink': xlink.namespace, @@ -48,18 +48,18 @@ def handle_output(self, input, **args): to_conv = input out = self.conv(to_conv, 'application/docbook+xml;charset=utf-8') output = serialize(out, namespaces=self.output_namespaces) - return self.output_re.sub(u'', output) + return self.output_re.sub('', output) def do(self, input, xpath_query): string_to_parse = self.handle_output(input) - logging.debug(u"After the DOCBOOK_IN conversion : {0}".format(string_to_parse)) + logging.debug("After the DOCBOOK_IN conversion : {0}".format(string_to_parse)) tree = etree.parse(StringIO(string_to_parse)) - print 'string_to_parse = %s' % string_to_parse # provide a clue for failing tests + print('string_to_parse = %s' % string_to_parse) # provide a clue for failing tests assert (tree.xpath(xpath_query, namespaces=self.namespaces_xpath)) def do_nonamespace(self, input, xpath_query): string_to_parse = self.handle_output(input, nonamespace=True) - logging.debug(u"After the DOCBOOK_IN conversion : {0}".format(string_to_parse)) + logging.debug("After the DOCBOOK_IN conversion : {0}".format(string_to_parse)) tree = etree.parse(StringIO(string_to_parse)) assert (tree.xpath(xpath_query, namespaces=self.namespaces_xpath)) @@ -82,9 +82,9 @@ def setup_class(self): #
Heading 1

First Paragraph

'/page/body/div[./h[@outline-level="1"][text()="Heading 1"]][./p[text()="First Paragraph"]]'), # Test for conversion with unicode char - (u'
안녕 유빈
', + ('
안녕 유빈
', #

안녕 유빈

- u'/page/body/div[p="안녕 유빈"]'), + '/page/body/div[p="안녕 유빈"]'), # Ignored tags ('
TitleAuthortext
', #

text

@@ -429,13 +429,13 @@ def test_admonition(self, input, xpath): data = [ ('
MoinMoin
', #

\xa9 MoinMoin

- u'/page/body/div/p/span[@html:class="db-trademark"][text()="\xa9 MoinMoin"]'), + '/page/body/div/p/span[@html:class="db-trademark"][text()="\xa9 MoinMoin"]'), ('
Nutshell Handbook
', #

Nutshell Handbook\xae

- u'/page/body/div/p/span[@html:class="db-trademark"][text()="Nutshell Handbook\xae"]'), + '/page/body/div/p/span[@html:class="db-trademark"][text()="Nutshell Handbook\xae"]'), ('
Foo Bar
', #

Foo Bar\u2122

- u'/page/body/div/p/span[@html:class="db-trademark"][text()="Foo Bar\u2122"]'), + '/page/body/div/p/span[@html:class="db-trademark"][text()="Foo Bar\u2122"]'), ('
MoinMoin
', #

MoinMoinSM

'/page/body/div/p/span[@html:class="db-trademark"][text()="MoinMoin"]/span[@baseline-shift="super"][text()="SM"]'), diff --git a/src/moin/converters/_tests/test_docbook_out.py b/src/moin/converters/_tests/test_docbook_out.py index f119cdf4f..4af12b719 100644 --- a/src/moin/converters/_tests/test_docbook_out.py +++ b/src/moin/converters/_tests/test_docbook_out.py @@ -22,7 +22,7 @@ logging = log.getLogger(__name__) -class Base(object): +class Base: input_namespaces = ns_all = 'xmlns="{0}" xmlns:page="{1}" xmlns:html="{2}" xmlns:xlink="{3}" xmlns:xml="{4}"'.format(moin_page.namespace, moin_page.namespace, html.namespace, xlink.namespace, xml.namespace) output_namespaces = { docbook.namespace: '', @@ -44,7 +44,7 @@ def handle_input(self, input): def handle_output(self, elem, **options): output = serialize(elem, namespaces=self.output_namespaces, **options) - return self.output_re.sub(u'', output) + return self.output_re.sub('', output) def do(self, input, xpath, args={}): out = self.conv(self.handle_input(input), **args) diff --git a/src/moin/converters/_tests/test_html_in.py b/src/moin/converters/_tests/test_html_in.py index 2713cea25..1c7c6ca66 100644 --- a/src/moin/converters/_tests/test_html_in.py +++ b/src/moin/converters/_tests/test_html_in.py @@ -22,7 +22,7 @@ logging = log.getLogger(__name__) -class Base(object): +class Base: namespaces = { moin_page.namespace: '', xlink.namespace: 'xlink', @@ -41,13 +41,13 @@ class Base(object): def handle_input(self, input, args): out = self.conv(input, **args) output = serialize(out, namespaces=self.namespaces) - return self.output_re.sub(u'', output) + return self.output_re.sub('', output) def do(self, input, path): string_to_parse = self.handle_input(input, args={}) logging.debug("After the HTML_IN conversion : {0}".format(string_to_parse)) tree = etree.parse(StringIO(string_to_parse)) - print 'string_to_parse = %s' % string_to_parse + print('string_to_parse = %s' % string_to_parse) assert (tree.xpath(path, namespaces=self.namespaces_xpath)) diff --git a/src/moin/converters/_tests/test_html_in_out.py b/src/moin/converters/_tests/test_html_in_out.py index 50b4988af..c6fff94c1 100644 --- a/src/moin/converters/_tests/test_html_in_out.py +++ b/src/moin/converters/_tests/test_html_in_out.py @@ -24,7 +24,7 @@ logging = log.getLogger(__name__) -class Base(object): +class Base: namespaces = { html.namespace: '', @@ -37,15 +37,15 @@ class Base(object): def handle_input(self, input, args): out = self.conv_html_dom(input, **args) output = serialize(out, namespaces=self.namespaces) - logging.debug("After the HTML_IN conversion : {0}".format(self.output_re.sub(u'', output))) + logging.debug("After the HTML_IN conversion : {0}".format(self.output_re.sub('', output))) out = self.conv_dom_html(out, **args) output = serialize(out, namespaces=self.namespaces) - return self.output_re.sub(u'', output) + return self.output_re.sub('', output) def do(self, input, path): string_to_parse = self.handle_input(input, args={}) logging.debug("After the roundtrip : {0}".format(string_to_parse)) - print 'string_to_parse = %s' % string_to_parse + print('string_to_parse = %s' % string_to_parse) tree = etree.parse(StringIO(string_to_parse)) assert (tree.xpath(path)) diff --git a/src/moin/converters/_tests/test_html_out.py b/src/moin/converters/_tests/test_html_out.py index 8e4785c19..ca0822385 100644 --- a/src/moin/converters/_tests/test_html_out.py +++ b/src/moin/converters/_tests/test_html_out.py @@ -23,7 +23,7 @@ logging = log.getLogger(__name__) -class Base(object): +class Base: input_namespaces = ns_all = 'xmlns="{0}" xmlns:page="{1}" xmlns:html="{2}" xmlns:xlink="{3}" xmlns:xml="{4}"'.format(moin_page.namespace, moin_page.namespace, html.namespace, xlink.namespace, xml.namespace) output_namespaces = { html.namespace: '', @@ -40,7 +40,7 @@ def handle_input(self, input): def handle_output(self, elem, **options): output = serialize(elem, namespaces=self.output_namespaces, **options) - return self.output_re.sub(u'', output) + return self.output_re.sub('', output) def do(self, input, xpath, args={}): out = self.conv(self.handle_input(input), **args) diff --git a/src/moin/converters/_tests/test_include.py b/src/moin/converters/_tests/test_include.py index 42825cb8b..868ef5c9a 100644 --- a/src/moin/converters/_tests/test_include.py +++ b/src/moin/converters/_tests/test_include.py @@ -12,7 +12,7 @@ from moin._tests import wikiconfig, update_item -class TestInclude(object): +class TestInclude: class Config(wikiconfig.Config): """ we just have this so the test framework creates a new app with empty backends for us. @@ -64,139 +64,139 @@ def test_XPointer(self): def test_IncludeHandlesCircularRecursion(self): # detect circular recursion and create error message - update_item(u'page1', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u'{{page2}}') - update_item(u'page2', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u'{{page3}}') - update_item(u'page3', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u'{{page4}}') - update_item(u'page4', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u'{{page2}}') - page1 = Item.create(u'page1') + update_item('page1', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, '{{page2}}') + update_item('page2', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, '{{page3}}') + update_item('page3', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, '{{page4}}') + update_item('page4', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, '{{page2}}') + page1 = Item.create('page1') rendered = page1.content._render_data() # an error message will follow strong tag assert '' in rendered def test_Include_Read_Permission_Denied(self): # attempt to include an item that user cannot read - update_item(u'page1', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8', ACL: u'All:write,create,admin,destroy'}, u'no one can read') - update_item(u'page2', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u'some text{{page1}}more text') - page2 = Item.create(u'page2') + update_item('page1', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8', ACL: 'All:write,create,admin,destroy'}, 'no one can read') + update_item('page2', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, 'some text{{page1}}more text') + page2 = Item.create('page2') rendered = page2.content._render_data() # an error message will follow p tag, similar to: Access Denied, transcluded content suppressed. assert '

' in rendered def test_ExternalInclude(self): # external include - update_item(u'page1', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u'{{http://moinmo.in}}') - rendered = Item.create(u'page1').content._render_data() + update_item('page1', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, '{{http://moinmo.in}}') + rendered = Item.create('page1').content._render_data() assert 'http://moinmo.in' in rendered # external include embedded within text (object is an inline tag) - update_item(u'page1', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u'before {{http://moinmo.in}} after') - rendered = Item.create(u'page1').content._render_data() + update_item('page1', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, 'before {{http://moinmo.in}} after') + rendered = Item.create('page1').content._render_data() assert '

before http://moinmo.in after

' in rendered # external include embedded within text italic and bold markup (object is an inline tag) - update_item(u'page1', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u"before ''italic '''bold {{http://moinmo.in}} bold''' italic'' normal") - rendered = Item.create(u'page1').content._render_data() + update_item('page1', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, "before ''italic '''bold {{http://moinmo.in}} bold''' italic'' normal") + rendered = Item.create('page1').content._render_data() assert '

before italic bold http://moinmo.in bold italic normal

' in rendered def test_InlineInclude(self): - update_item(u'page1', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u'before {{page2}} after') + update_item('page1', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, 'before {{page2}} after') # transclude single paragraph as inline - update_item(u'page2', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u'Single line') - rendered = Item.create(u'page1').content._render_data() + update_item('page2', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, 'Single line') + rendered = Item.create('page1').content._render_data() assert '

before Single line after

' in rendered # transclude multiple paragraphs as block - update_item(u'page2', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u'Two\n\nParagraphs') - rendered = Item.create(u'page1').content._render_data() + update_item('page2', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, 'Two\n\nParagraphs') + rendered = Item.create('page1').content._render_data() assert '

before

Two

Paragraphs

after

' in rendered # transclude single paragraph with internal markup as inline - update_item(u'page2', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u"this text contains ''italic'' string") - rendered = Item.create(u'page1').content._render_data() + update_item('page2', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, "this text contains ''italic'' string") + rendered = Item.create('page1').content._render_data() assert 'before this text contains italic' in rendered # transclude single paragraph as only content within a paragraph - update_item(u'page1', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u'Content of page2 is\n\n{{page2}}') - update_item(u'page2', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u"Single Line") - rendered = Item.create(u'page1').content._render_data() + update_item('page1', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, 'Content of page2 is\n\n{{page2}}') + update_item('page2', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, "Single Line") + rendered = Item.create('page1').content._render_data() assert '

Content of page2 is

Single Line

' in rendered # transclude single row table within a paragraph, block element forces paragraph to be split into 2 parts - update_item(u'page1', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u'before {{page2}} after') - update_item(u'page2', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u"|| table || cell ||") - rendered = Item.create(u'page1').content._render_data() + update_item('page1', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, 'before {{page2}} after') + update_item('page2', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, "|| table || cell ||") + rendered = Item.create('page1').content._render_data() assert '

before

after

' in rendered assert rendered.count('before

after

' in rendered assert rendered.count('before ' in rendered assert ' after

' in rendered # transclude empty item - update_item(u'page1', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u'text {{page2}} text') - update_item(u'page2', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u"") - rendered = Item.create(u'page1').content._render_data() + update_item('page1', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, 'text {{page2}} text') + update_item('page2', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, "") + rendered = Item.create('page1').content._render_data() assert '

text text

' in rendered def test_InlineIncludeCreole(self): # transclude single paragraph as inline using creole parser - update_item(u'creole', {CONTENTTYPE: u'text/x.moin.creole;charset=utf-8'}, u'creole item') - update_item(u'page1', {CONTENTTYPE: u'text/x.moin.creole;charset=utf-8'}, u'before {{creole}} after') - rendered = Item.create(u'page1').content._render_data() + update_item('creole', {CONTENTTYPE: 'text/x.moin.creole;charset=utf-8'}, 'creole item') + update_item('page1', {CONTENTTYPE: 'text/x.moin.creole;charset=utf-8'}, 'before {{creole}} after') + rendered = Item.create('page1').content._render_data() assert '

before creole item after

' in rendered def test_InlineIncludeWithinMarkup(self): # transclude single line item within italic and bold markup - update_item(u'page1', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u"Normal ''italic '''bold {{page2}} bold''' italic'' normal") - update_item(u'page2', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u"Single Line") - rendered = Item.create(u'page1').content._render_data() + update_item('page1', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, "Normal ''italic '''bold {{page2}} bold''' italic'' normal") + update_item('page2', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, "Single Line") + rendered = Item.create('page1').content._render_data() assert '

Normal italic bold Single Line bold italic normal

' in rendered # transclude double line item within italic and bold markup - update_item(u'page1', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u"Normal ''italic '''bold {{page2}} bold''' italic'' normal") - update_item(u'page2', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u"Double\n\nLine") - rendered = Item.create(u'page1').content._render_data() + update_item('page1', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, "Normal ''italic '''bold {{page2}} bold''' italic'' normal") + update_item('page2', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, "Double\n\nLine") + rendered = Item.create('page1').content._render_data() assert '

Normal italic bold

Double

Line

bold italic normal

' in rendered # transclude single line item within comment - update_item(u'page1', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u"comment /* before {{page2}} after */") - update_item(u'page2', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u"Single Line") - rendered = Item.create(u'page1').content._render_data() + update_item('page1', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, "comment /* before {{page2}} after */") + update_item('page2', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, "Single Line") + rendered = Item.create('page1').content._render_data() assert '

comment before Single Line after

' in rendered # transclude double line item within comment - update_item(u'page1', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u"comment /* before {{page2}} after */") - update_item(u'page2', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u"Double\n\nLine") - rendered = Item.create(u'page1').content._render_data() + update_item('page1', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, "comment /* before {{page2}} after */") + update_item('page2', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, "Double\n\nLine") + rendered = Item.create('page1').content._render_data() assert '

comment before

Double

Line

after

' in rendered def test_InlineIncludeImage(self): # the 3rd parameter, u'', should be a binary string defining a png image, but it is not needed for this simple test - update_item(u'logo.png', {CONTENTTYPE: u'image/png'}, u'') + update_item('logo.png', {CONTENTTYPE: 'image/png'}, '') # simple transclusion - update_item(u'page1', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u'{{logo.png}}') - rendered = Item.create(u'page1').content._render_data() + update_item('page1', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, '{{logo.png}}') + rendered = Item.create('page1').content._render_data() assert '

logo.png

' in rendered # simple transclusion with alt text and width - update_item(u'page1', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u'{{logo.png|my alt text|width="100"}}') - rendered = Item.create(u'page1').content._render_data() + update_item('page1', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, '{{logo.png|my alt text|width="100"}}') + rendered = Item.create('page1').content._render_data() assert '

my alt text

' in rendered # within paragraph - update_item(u'page1', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u'text {{logo.png}} text') - rendered = Item.create(u'page1').content._render_data() + update_item('page1', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, 'text {{logo.png}} text') + rendered = Item.create('page1').content._render_data() assert '

text logo.png text

' in rendered # within markup - update_item(u'page1', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u"Normal ''italic '''bold {{logo.png}} bold''' italic'' normal") - rendered = Item.create(u'page1').content._render_data() + update_item('page1', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, "Normal ''italic '''bold {{logo.png}} bold''' italic'' normal") + rendered = Item.create('page1').content._render_data() assert '

Normal italic bold logo.png bold italic normal

' in rendered # multiple transclusions - update_item(u'page1', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u'{{logo.png}}{{logo.png}}') - rendered = Item.create(u'page1').content._render_data() + update_item('page1', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, '{{logo.png}}{{logo.png}}') + rendered = Item.create('page1').content._render_data() assert '

logo.pnglogo.pngtext logo.png text

' in rendered # link alternate with image embedded in markup - update_item(u'page1', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u"text [[page2|plain '''bold {{logo.png}} bold''' plain]] text") - rendered = Item.create(u'page1').content._render_data() + update_item('page1', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, "text [[page2|plain '''bold {{logo.png}} bold''' plain]] text") + rendered = Item.create('page1').content._render_data() assert '

text plain bold logo.png bold plain text

' in rendered # nonexistent image used in link alternate # XXX html validation errora: A inside A - the image alternate turns into an A-tag to create the non-existant image. Error is easily seen. # IE9, Firefox, Chrome, Safari, and Opera display this OK; the only usable hyperlink is to create the missing image. - update_item(u'page1', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u"text [[page2|{{logoxxx.png}}]] text") - rendered = Item.create(u'page1').content._render_data() + update_item('page1', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, "text [[page2|{{logoxxx.png}}]] text") + rendered = Item.create('page1').content._render_data() assert '

text ' in rendered assert ' text

' in rendered # image used as alternate to nonexistent page - update_item(u'page1', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u"text [[page2xxx|{{logo.png}}]] text") - rendered = Item.create(u'page1').content._render_data() + update_item('page1', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, "text [[page2xxx|{{logo.png}}]] text") + rendered = Item.create('page1').content._render_data() assert '

text logo.png text

' in rendered # transclude block elem as link alternate to nonexistent page # XXX html validation errors, block element inside A. # IE9, Firefox, Chrome, Safari, and Opera display this OK; the hyperlink is the entire div enclosing the block elem - update_item(u'page1', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u'text [[MyPage|{{page2}}]] text') - update_item(u'page2', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u"Double\n\nLine") - rendered = Item.create(u'page1').content._render_data() + update_item('page1', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, 'text [[MyPage|{{page2}}]] text') + update_item('page2', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, "Double\n\nLine") + rendered = Item.create('page1').content._render_data() assert '

text

text

' in rendered # transclude empty item as link alternate to nonexistent page # hyperlink will be empty span and invisible - update_item(u'page1', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u'text [[MyPage|{{page2}}]] text') - update_item(u'page2', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u"") - rendered = Item.create(u'page1').content._render_data() + update_item('page1', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, 'text [[MyPage|{{page2}}]] text') + update_item('page2', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, "") + rendered = Item.create('page1').content._render_data() assert '

text text

' in rendered # transclude external page as link alternate to nonexistent page - update_item(u'page1', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u'text [[MyPage|{{http://moinmo.in}}]] text') - rendered = Item.create(u'page1').content._render_data() + update_item('page1', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, 'text [[MyPage|{{http://moinmo.in}}]] text') + rendered = Item.create('page1').content._render_data() assert '

text http://moinmo.in text

' in rendered diff --git a/src/moin/converters/_tests/test_link.py b/src/moin/converters/_tests/test_link.py index ef0c2a326..cc00fc5d7 100644 --- a/src/moin/converters/_tests/test_link.py +++ b/src/moin/converters/_tests/test_link.py @@ -66,14 +66,14 @@ def test_wikiexternal(conv, input_, output): elem = ET.Element(None) conv.handle_external_links(elem, Iri(input_)) href = elem.get(xlink.href) - assert href == output + assert str(href) == output @pytest.mark.parametrize( 'tree_xml,links_expected,transclusions_expected,external_expected', ( ( - u""" + """ @@ -85,24 +85,24 @@ def test_wikiexternal(conv, input_, output): moin_linked moin2_linked """, - (u"moin_linked", u"moin2_linked"), - (u"moin_transcluded", u"moin2_transcluded"), + ("moin_linked", "moin2_linked"), + ("moin_transcluded", "moin2_transcluded"), [], ), ( - u""" + """ ../../moin_linked /moin2_linked """, - (u"moin_linked", u"Home/Subpage/moin2_linked"), - (u"Home/Subpage/moin2_transcluded", u"moin_transcluded"), + ("moin_linked", "Home/Subpage/moin2_linked"), + ("Home/Subpage/moin2_transcluded", "moin_transcluded"), [], ), ( - u""" + """ test test @@ -110,7 +110,7 @@ def test_wikiexternal(conv, input_, output): """, [], [], - (u"http://example.org/", u"mailto:foo.bar@example.org"), + ("http://example.org/", "mailto:foo.bar@example.org"), ), diff --git a/src/moin/converters/_tests/test_markdown_in.py b/src/moin/converters/_tests/test_markdown_in.py index b7fefcd0f..e1ca63108 100644 --- a/src/moin/converters/_tests/test_markdown_in.py +++ b/src/moin/converters/_tests/test_markdown_in.py @@ -15,7 +15,7 @@ from ..markdown_in import Converter -class TestConverter(object): +class TestConverter: namespaces = { moin_page: '', xlink: 'xlink', @@ -30,19 +30,19 @@ def setup_class(self): self.conv = Converter() data = [ - (u'Text', + ('Text', '

Text

'), - (u'Text\nTest', + ('Text\nTest', '

Text\nTest

'), - (u'Text\n\nTest', + ('Text\n\nTest', '

Text

Test

'), - (u'', + ('', '

http://moinmo.in/

'), - (u'[yo](javascript:alert("xss"))', + ('[yo](javascript:alert("xss"))', '

javascript:alert()

'), - (u'[MoinMoin](http://moinmo.in/)', + ('[MoinMoin](http://moinmo.in/)', '

MoinMoin

'), - (u'----', + ('----', ''), ] @@ -51,15 +51,15 @@ def test_base(self, input, output): self.do(input, output) data = [ - (u'*Emphasis*', + ('*Emphasis*', '

Emphasis

'), - (u'_Emphasis_', + ('_Emphasis_', '

Emphasis

'), - (u'**Strong**', + ('**Strong**', '

Strong

'), - (u'_**Both**_', + ('_**Both**_', '

Both

'), - (u'**_Both_**', + ('**_Both_**', '

Both

'), ] @@ -68,11 +68,11 @@ def test_emphasis(self, input, output): self.do(input, output) data = [ - (u'http://moinmo.in/', + ('http://moinmo.in/', '

http://moinmo.in/

'), - (u'\\[escape](yo)', + ('\\[escape](yo)', '

[escape](yo)

'), - (u'\\*yo\\*', + ('\\*yo\\*', '

*yo*

'), ] @@ -81,35 +81,35 @@ def test_escape(self, input, output): self.do(input, output) data = [ - (u'# Heading 1', + ('# Heading 1', 'Heading 1'), - (u'## Heading 2', + ('## Heading 2', 'Heading 2'), - (u'### Heading 3', + ('### Heading 3', 'Heading 3'), - (u'#### Heading 4', + ('#### Heading 4', 'Heading 4'), - (u'##### Heading 5', + ('##### Heading 5', 'Heading 5'), - (u'###### Heading 6', + ('###### Heading 6', 'Heading 6'), - (u'# Heading 1 #', + ('# Heading 1 #', 'Heading 1'), - (u'## Heading 2 ##', + ('## Heading 2 ##', 'Heading 2'), - (u'### Heading 3 ###', + ('### Heading 3 ###', 'Heading 3'), - (u'#### Heading 4 ####', + ('#### Heading 4 ####', 'Heading 4'), - (u'##### Heading 5 #####', + ('##### Heading 5 #####', 'Heading 5'), - (u'###### Heading 6 ######', + ('###### Heading 6 ######', 'Heading 6'), - (u'Heading 1\n=========\nHeading 2\n---------\n', + ('Heading 1\n=========\nHeading 2\n---------\n', 'Heading 1Heading 2'), - (u'Heading 2\n---------\n', + ('Heading 2\n---------\n', 'Heading 2'), - (u'Heading\n=======\n\nxxxx', + ('Heading\n=======\n\nxxxx', 'Heading

xxxx

'), ] @@ -118,17 +118,17 @@ def test_heading(self, input, output): self.do(input, output) data = [ - (u'* Item', + ('* Item', 'Item'), - (u'* Item\nItem', + ('* Item\nItem', 'Item\nItem'), - (u'* Item 1\n* Item 2', + ('* Item 1\n* Item 2', 'Item 1Item 2'), - (u'* Item 1\n * Item 1.2\n* Item 2', + ('* Item 1\n * Item 1.2\n* Item 2', 'Item 1Item 1.2Item 2'), - (u'* List 1\n\nyo\n\n\n* List 2', + ('* List 1\n\nyo\n\n\n* List 2', 'List 1

yo

List 2'), - (u'8. Item', + ('8. Item', 'Item'), ] @@ -137,15 +137,15 @@ def test_list(self, input, output): self.do(input, output) data = [ - (u'![Alt text](png "Optional title")', + ('![Alt text](png "Optional title")', '

'), - (u'![](png "Optional title")', + ('![](png "Optional title")', '

'), - (u'![remote image](http://static.moinmo.in/logos/moinmoin.png)', + ('![remote image](http://static.moinmo.in/logos/moinmoin.png)', '

'), - (u'![Alt text](http://test.moinmo.in/png)', + ('![Alt text](http://test.moinmo.in/png)', '

'), - (u'![transclude local wiki item](someitem)', + ('![transclude local wiki item](someitem)', '

'), ] @@ -155,15 +155,15 @@ def test_image(self, input, output): def serialize_strip(self, elem, **options): result = serialize(elem, namespaces=self.namespaces, **options) - return self.output_re.sub(u'', result) + return self.output_re.sub('', result) def do(self, input, output, args={}): out = self.conv(input, 'text/x-markdown;charset=utf-8', **args) got_output = self.serialize_strip(out) desired_output = "%s" % output - print '------------------------------------' - print "WANTED:" - print desired_output - print "GOT:" - print got_output + print('------------------------------------') + print("WANTED:") + print(desired_output) + print("GOT:") + print(got_output) assert got_output == desired_output diff --git a/src/moin/converters/_tests/test_markdown_in_out.py b/src/moin/converters/_tests/test_markdown_in_out.py index 055814eca..c8cadaa88 100644 --- a/src/moin/converters/_tests/test_markdown_in_out.py +++ b/src/moin/converters/_tests/test_markdown_in_out.py @@ -18,7 +18,7 @@ from moin.converters.markdown_out import Converter as conv_out -class TestConverter(object): +class TestConverter: input_namespaces = 'xmlns="{0}" xmlns:page="{1}" xmlns:xlink="{2}" xmlns:xinclude="{3}" xmlns:html="{4}"'.format( moin_page.namespace, moin_page.namespace, xlink.namespace, xinclude.namespace, html.namespace) @@ -39,35 +39,35 @@ def setup_class(self): self.conv_out = conv_out() data = [ - (u'Text', - u'Text\n'), - (u'Text\n\nText\n', - u'Text\n\nText\n'), - (u'xxx\n\n------\n\n------\n\n------\n', - u'xxx\n\n----\n\n----\n\n----\n'), - (u'----\n\n------\n\n--------\n', - u'----\n\n----\n\n----\n'), - (u'**strong**\n', - u'**strong**\n'), - (u'*emphasis*\n', - u'*emphasis*\n'), - (u' blockcode\n', - u' blockcode\n'), - (u'`monospace`\n', - u'`monospace`\n'), - (u'stroke\n', - u'stroke\n'), + ('Text', + 'Text\n'), + ('Text\n\nText\n', + 'Text\n\nText\n'), + ('xxx\n\n------\n\n------\n\n------\n', + 'xxx\n\n----\n\n----\n\n----\n'), + ('----\n\n------\n\n--------\n', + '----\n\n----\n\n----\n'), + ('**strong**\n', + '**strong**\n'), + ('*emphasis*\n', + '*emphasis*\n'), + (' blockcode\n', + ' blockcode\n'), + ('`monospace`\n', + '`monospace`\n'), + ('stroke\n', + 'stroke\n'), # is changed to - (u'underline\n', - u'underline\n'), - (u'larger\n', - u'larger\n'), - (u'smaller\n', - u'smaller\n'), - (u'superscript\n', - u'superscript\n'), - (u'subscript\n', - u'subscript\n'), + ('underline\n', + 'underline\n'), + ('larger\n', + 'larger\n'), + ('smaller\n', + 'smaller\n'), + ('superscript\n', + 'superscript\n'), + ('subscript\n', + 'subscript\n'), ] @pytest.mark.parametrize('input,output', data) @@ -75,14 +75,14 @@ def test_base(self, input, output): self.do(input, output) data = [ - (u'level 1\n=======\n', - u'# level 1 #\n'), - (u'# level 1 #\n', - u'# level 1 #\n'), - (u'## level 2 ##\n', - u'## level 2 ##\n'), - (u'## level 2\n', - u'## level 2 ##\n'), + ('level 1\n=======\n', + '# level 1 #\n'), + ('# level 1 #\n', + '# level 1 #\n'), + ('## level 2 ##\n', + '## level 2 ##\n'), + ('## level 2\n', + '## level 2 ##\n'), ] @pytest.mark.parametrize('input,output', data) @@ -90,36 +90,36 @@ def test_headings(self, input, output): self.do(input, output) data = [ - (u'[TOC]\n', - u'[TOC]\n'), - (u'|Tables|Are|Very|Cool|\n|------|:----:|-----:|:-----|\n|col 2 is|centered|$12|Gloves|\n', - u'|Tables|Are|Very|Cool|\n|------|:----:|-----:|:-----|\n|col 2 is|centered|$12|Gloves|\n'), + ('[TOC]\n', + '[TOC]\n'), + ('|Tables|Are|Very|Cool|\n|------|:----:|-----:|:-----|\n|col 2 is|centered|$12|Gloves|\n', + '|Tables|Are|Very|Cool|\n|------|:----:|-----:|:-----|\n|col 2 is|centered|$12|Gloves|\n'), # TODO: wrong output - (u'``` javascript\nvar s = "JavaScript syntax highlighting";\nalert(s);\n```\n', - u' var s = "JavaScript syntax highlighting";\n alert(s);\n'), + ('``` javascript\nvar s = "JavaScript syntax highlighting";\nalert(s);\n```\n', + ' var s = "JavaScript syntax highlighting";\n alert(s);\n'), # TODO: wrong output - (u'~~~ {python}\ndef hello():\n print "Hello World!"\n~~~\n', - u' def hello():\n print "Hello World!"\n'), - (u'~~~\nddd\neee\nfff\n~~~\n', - u' ddd\n eee\n fff\n'), + ('~~~ {python}\ndef hello():\n print "Hello World!"\n~~~\n', + ' def hello():\n print "Hello World!"\n'), + ('~~~\nddd\neee\nfff\n~~~\n', + ' ddd\n eee\n fff\n'), # TODO: maybe wrong output - (u'Text with double__underscore__words.\n\n__Strong__ still works.\n\n__this__works__too__.\n', - u'Text with double__underscore__words.\n\n**Strong** still works.\n\n**this__works__too**.\n'), + ('Text with double__underscore__words.\n\n__Strong__ still works.\n\n__this__works__too__.\n', + 'Text with double__underscore__words.\n\n**Strong** still works.\n\n**this__works__too**.\n'), # TODO: missing class - (u'### orange heading #### {: .orange }\n', - u'### orange heading ###\n'), + ('### orange heading #### {: .orange }\n', + '### orange heading ###\n'), # TODO: missing class - (u'A class of LawnGreen is added to this paragraph.\n{: class="LawnGreen "}\n', - u'A class of LawnGreen is added to this paragraph.\n'), - (u'{: #para3 }\n', - u'{: #para3 }\n'), - (u'so [click to see 3rd paragraph](#para3).\n', - u'so [click to see 3rd paragraph](#para3).\n'), - (u'Apple\n: Pomaceous fruit of plants of the genus Malus in\n the family Rosaceae.\n: An american computer company.\n', - u'Apple\n: Pomaceous fruit of plants of the genus Malus in\n the family Rosaceae.\n: An american computer company.\n'), + ('A class of LawnGreen is added to this paragraph.\n{: class="LawnGreen "}\n', + 'A class of LawnGreen is added to this paragraph.\n'), + ('{: #para3 }\n', + '{: #para3 }\n'), + ('so [click to see 3rd paragraph](#para3).\n', + 'so [click to see 3rd paragraph](#para3).\n'), + ('Apple\n: Pomaceous fruit of plants of the genus Malus in\n the family Rosaceae.\n: An american computer company.\n', + 'Apple\n: Pomaceous fruit of plants of the genus Malus in\n the family Rosaceae.\n: An american computer company.\n'), # footnotes test is incomplete, footnotes are positioned but not defined - (u'Footnotes[^1] have a label[^label] and a definition[^!DEF].\n', - u'Footnotes[^1] have a label[^label] and a definition[^!DEF].\n'), + ('Footnotes[^1] have a label[^label] and a definition[^!DEF].\n', + 'Footnotes[^1] have a label[^label] and a definition[^!DEF].\n'), # TODO: spectacular failure, causes 19 UnicodeEncodeErrors, claims \xA0 characters # (u'Footnotes[^a\n\n[^a]: This is a footnote\n', # u'Footnotes[^a\n\n[^a]: This is a footnote\n'), @@ -130,12 +130,12 @@ def test_extensions(self, input, output): self.do(input, output) data = [ - (u'[MoinMoin](http://moinmo.in)\n', - u'[MoinMoin](http://moinmo.in)\n'), - (u'[PNG](png)\n', - u'[PNG](png)\n'), - (u'[MoinMoin][moin]\n[moin]: http://moinmo.in\n', - u'[MoinMoin](http://moinmo.in)\n'), + ('[MoinMoin](http://moinmo.in)\n', + '[MoinMoin](http://moinmo.in)\n'), + ('[PNG](png)\n', + '[PNG](png)\n'), + ('[MoinMoin][moin]\n[moin]: http://moinmo.in\n', + '[MoinMoin](http://moinmo.in)\n'), ] @pytest.mark.parametrize('input,output', data) @@ -143,18 +143,18 @@ def test_link(self, input, output): self.do(input, output) data = [ - (u'* A\n* B\n 1. C\n 1. D\n 1. E\n 1. F\n', - u'* A\n* B\n 1. C\n 1. D\n 1. E\n 1. F\n'), - (u' * A\n 1. C\n - E\n', - u' * A\n 1. C\n * E\n'), - (u' * A\n 1. C\n 1. D\n', - u' * A\n 1. C\n 1. D\n'), - (u'1. E\n1. F\n', - u'1. E\n1. F\n'), - (u' 1. E\n 1. F\n', - u' 1. E\n 1. F\n'), - (u'Apple\n: B\n: C\n: D\n', - u'Apple\n: B\n: C\n: D\n'), + ('* A\n* B\n 1. C\n 1. D\n 1. E\n 1. F\n', + '* A\n* B\n 1. C\n 1. D\n 1. E\n 1. F\n'), + (' * A\n 1. C\n - E\n', + ' * A\n 1. C\n * E\n'), + (' * A\n 1. C\n 1. D\n', + ' * A\n 1. C\n 1. D\n'), + ('1. E\n1. F\n', + '1. E\n1. F\n'), + (' 1. E\n 1. F\n', + ' 1. E\n 1. F\n'), + ('Apple\n: B\n: C\n: D\n', + 'Apple\n: B\n: C\n: D\n'), ] @pytest.mark.parametrize('input,output', data) @@ -162,14 +162,14 @@ def test_list(self, input, output): self.do(input, output) data = [ - (u'|A|B|C|\n|-|-|-|\n|1|2|3|\n', - u'|A|B|C|\n|------|------|------|\n|1|2|3|\n'), - (u'|A|B|C|\n|:-|:-:|-:|\n|1|2|3|\n', - u'|A|B|C|\n|:-----|:----:|-----:|\n|1|2|3|\n'), - (u'A|B|C\n-|-|-\n1|2|3\n', - u'|A|B|C|\n|------|------|------|\n|1|2|3|\n'), - (u'`A`|*B*|_C_\n:-|:-:|-:\n1|2|3\n', - u'|`A`|*B*|*C*|\n|:-----|:----:|-----:|\n|1|2|3|\n'), + ('|A|B|C|\n|-|-|-|\n|1|2|3|\n', + '|A|B|C|\n|------|------|------|\n|1|2|3|\n'), + ('|A|B|C|\n|:-|:-:|-:|\n|1|2|3|\n', + '|A|B|C|\n|:-----|:----:|-----:|\n|1|2|3|\n'), + ('A|B|C\n-|-|-\n1|2|3\n', + '|A|B|C|\n|------|------|------|\n|1|2|3|\n'), + ('`A`|*B*|_C_\n:-|:-:|-:\n1|2|3\n', + '|`A`|*B*|*C*|\n|:-----|:----:|-----:|\n|1|2|3|\n'), ] @pytest.mark.parametrize('input,output', data) @@ -177,16 +177,16 @@ def test_table(self, input, output): self.do(input, output) data = [ - (u'\n![Alt text](png "Optional title")', - u'\n![Alt text](png "Optional title")\n'), - (u'![Alt text](png)', - u'![Alt text](png)\n'), - (u'![Alt text][logo]\n[logo]: png "Optional title attribute"', - u'![Alt text](png "Optional title attribute")\n'), - (u'![remote image](http://static.moinmo.in/logos/moinmoin.png)', - u'![remote image](http://static.moinmo.in/logos/moinmoin.png)\n'), - (u'![Alt text](http://test.moinmo.in/png)', - u'![Alt text](http://test.moinmo.in/png)\n'), + ('\n![Alt text](png "Optional title")', + '\n![Alt text](png "Optional title")\n'), + ('![Alt text](png)', + '![Alt text](png)\n'), + ('![Alt text][logo]\n[logo]: png "Optional title attribute"', + '![Alt text](png "Optional title attribute")\n'), + ('![remote image](http://static.moinmo.in/logos/moinmoin.png)', + '![remote image](http://static.moinmo.in/logos/moinmoin.png)\n'), + ('![Alt text](http://test.moinmo.in/png)', + '![Alt text](http://test.moinmo.in/png)\n'), ] @pytest.mark.parametrize('input,output', data) @@ -202,7 +202,7 @@ def handle_output(self, elem, **options): def serialize_strip(self, elem, **options): result = serialize(elem, namespaces=self.namespaces, **options) - return self.output_re.sub(u'', result) + return self.output_re.sub('', result) def do(self, input, output, args={}, skip=None): if skip: diff --git a/src/moin/converters/_tests/test_mediawiki_in.py b/src/moin/converters/_tests/test_mediawiki_in.py index c7f33c180..62164c4c2 100644 --- a/src/moin/converters/_tests/test_mediawiki_in.py +++ b/src/moin/converters/_tests/test_mediawiki_in.py @@ -14,7 +14,7 @@ from moin.converters.mediawiki_in import Converter -class TestConverter(object): +class TestConverter: namespaces = { moin_page.namespace: '', xlink.namespace: 'xlink', @@ -26,36 +26,36 @@ def setup_class(self): self.conv = Converter() data = [ - (u"''italic''", u'

italic

'), - (u"Text\nTest", u'

Text\nTest

'), - (u'Text\n\nTest', u'

Text

Test

'), - (u"'''bold'''", u'

bold

'), - (u"'''''bold and italic'''''", - u'

bold and italic

'), - (u"no ''markup''\n\nno ''markup''\n\nno ''markup''", + ("''italic''", '

italic

'), + ("Text\nTest", '

Text\nTest

'), + ('Text\n\nTest', '

Text

Test

'), + ("'''bold'''", '

bold

'), + ("'''''bold and italic'''''", + '

bold and italic

'), + ("no ''markup''\n\nno ''markup''\n\nno ''markup''", "

no ''markup''

no ''markup''

no ''markup''

"), - (u"
no ''markup'' block
", - u"

no ''markup'' block

"), - (u'underlined', u'

underlined

'), - (u'inserted', u'

inserted

'), - (u'Strikethrough', u'

Strikethrough

'), - (u'Strikethrough', u'

Strikethrough

'), - (u"test super or sub", - u'

test super or sub

'), - (u"text
quote quote quote quote quote quote
text", - u"

text

quote quote quote quote quote quote
text

"), - (u"aaa
bbb", u"

aaabbb

"), - (u"aaa sdf test\n\n asd", + ("
no ''markup'' block
", + "

no ''markup'' block

"), + ('underlined', '

underlined

'), + ('inserted', '

inserted

'), + ('Strikethrough', '

Strikethrough

'), + ('Strikethrough', '

Strikethrough

'), + ("test super or sub", + '

test super or sub

'), + ("text
quote quote quote quote quote quote
text", + "

text

quote quote quote quote quote quote
text

"), + ("aaa
bbb", "

aaabbb

"), + ("aaa sdf test\n\n asd", '

aaa sdf test

asd

'), - (u"""=level 1= + ("""=level 1= == level 2 == ===level 3=== ====level 4==== =====level 5===== ======level 6====== """, - u'level 1level 2level 3level 4level 5level 6'), - (u"[javascript:alert('xss')]", "

[javascript:alert('xss')]

"), + 'level 1level 2level 3level 4level 5level 6'), + ("[javascript:alert('xss')]", "

[javascript:alert('xss')]

"), ] @pytest.mark.parametrize('input,output', data) @@ -63,14 +63,14 @@ def test_base(self, input, output): self.do(input, output) data = [ - (u"""* one + ("""* one * two * three ** three point one ** three point two """, - u'

one

two

three

three point one

three point two

'), - (u"""# one + '

one

two

three

three point one

three point two

'), + ("""# one # two
spanning more lines
doesn't break numbering # three ## three point one @@ -78,10 +78,10 @@ def test_base(self, input, output): # 4 no point """, - u'''

one

twospanning more linesdoesn't break numbering

three

three point one

three point two

4

no point

'''), + '''

one

twospanning more linesdoesn't break numbering

three

three point one

three point two

4

no point

'''), (""";item 1:definition 1 ;item 2:definition 2 -""", u'''\ +""", '''\ \ \ \ @@ -98,13 +98,13 @@ def test_base(self, input, output): # TODO add a test for a definition list with term and definition on separate lines like: # ; term # : definition - (u";aaa : bbb", - u"aaa

bbb

"), - (u""": Single indent + (";aaa : bbb", + "aaa

bbb

"), + (""": Single indent :: Double indent ::::: Multiple indent """, - u'

Single indent

Double indent

Multiple indent

'), + '

Single indent

Double indent

Multiple indent

'), ("""# one # two #* two point one @@ -112,7 +112,7 @@ def test_base(self, input, output): # three #; three item one:three def one """, - u'

one

two

two point one

two point two

three

three item one

three def one

'), + '

one

two

two point one

two point two

three

three item one

three def one

'), ] @pytest.mark.parametrize('input,output', data) @@ -120,7 +120,7 @@ def test_list(self, input, output): self.do(input, output) data = [ - (u"""{| + ("""{| |Orange |Apple |- @@ -131,8 +131,8 @@ def test_list(self, input, output): |Ice cream |} """, - u"OrangeAppleBreadPieButterIce cream
"), - (u"""{|style="border-width: 1px;" + "OrangeAppleBreadPieButterIce cream
"), + ("""{|style="border-width: 1px;" |style="border-style: solid; border-width: 1px" colspan="2"| Orange Apple |- @@ -142,12 +142,12 @@ def test_list(self, input, output): |test |} """, - u'Orange\nAppleBreadPietest
'), + 'Orange\nAppleBreadPietest
'), ("""{| |class="test"|text||style="border:1px"|test |} """, - u'texttest
'), + 'texttest
'), ] @pytest.mark.parametrize('input,output', data) @@ -155,32 +155,32 @@ def test_table(self, input, output): self.do(input, output) data = [ - (u"[[SomeLink]]", u'

SomeLink

'), - (u"[http://external.link]", u'

'), - (u"[http://external.link alt text]", - u'

alt text

'), - (u"[[SomeLink|Some text]]", - u'

Some text

'), - (u"[[SomeLink|arg1=value|arg2=otherval|Some text]]", - u'

Some text

'), - (u"[[File:Test.jpg|test]]", - u'

test

'), - (u"[[File:MyImage.png]]", - u'

MyImage.png

'), - (u"[[File:MyImage.png|arg=http://google.com|caption]]", - u'

caption

'), - (u"[[File:Test.png|do=get|arg1=test|arg2=something else]]", - u'

Test.png

'), + ("[[SomeLink]]", '

SomeLink

'), + ("[http://external.link]", '

'), + ("[http://external.link alt text]", + '

alt text

'), + ("[[SomeLink|Some text]]", + '

Some text

'), + ("[[SomeLink|arg1=value|arg2=otherval|Some text]]", + '

Some text

'), + ("[[File:Test.jpg|test]]", + '

test

'), + ("[[File:MyImage.png]]", + '

MyImage.png

'), + ("[[File:MyImage.png|arg=http://google.com|caption]]", + '

caption

'), + ("[[File:Test.png|do=get|arg1=test|arg2=something else]]", + '

Test.png

'), # The do=xxx part is just to test if do in args is being updated correctly, it's invalid otherwise - (u"[[File:Test2.png|do=xxx|caption|arg1=test]]", - u'

caption

'), - (u"[[File:myimg.png|'Graph showing width |= k for 5 < k < 10']]", - u'

Graph showing width |= k for 5 < k < 10

'), - (u"[[File:myimg.png|arg1='longish caption value with |= to test'|arg2=other|test stuff]]", - u'

test stuff

'), + ("[[File:Test2.png|do=xxx|caption|arg1=test]]", + '

caption

'), + ("[[File:myimg.png|'Graph showing width |= k for 5 < k < 10']]", + '

Graph showing width |= k for 5 < k < 10

'), + ("[[File:myimg.png|arg1='longish caption value with |= to test'|arg2=other|test stuff]]", + '

test stuff

'), # Unicode test - (u"[[File:Test.jpg|\xe8]]", - u'

\xe8

') + ("[[File:Test.jpg|\xe8]]", + '

\xe8

') ] @pytest.mark.parametrize('input,output', data) @@ -189,12 +189,12 @@ def test_links(self, input, output): def serialize_strip(self, elem, **options): result = serialize(elem, namespaces=self.namespaces, **options) - return self.output_re.sub(u'', result) + return self.output_re.sub('', result) def do(self, input, output, args={}, skip=None): out = self.conv(input, 'text/x-mediawiki;charset=utf-8', **args) result = self.serialize_strip(out) - print result + print(result) assert result == output diff --git a/src/moin/converters/_tests/test_moinwiki19_in.py b/src/moin/converters/_tests/test_moinwiki19_in.py index 6ce3851ae..6f230b201 100644 --- a/src/moin/converters/_tests/test_moinwiki19_in.py +++ b/src/moin/converters/_tests/test_moinwiki19_in.py @@ -14,7 +14,7 @@ from moin.utils.tree import moin_page, xlink, html, xinclude -class TestConverter(object): +class TestConverter: namespaces = { moin_page: '', xlink: 'xlink', @@ -28,20 +28,20 @@ def setup_class(self): self.conv = ConverterFormat19() data = [ - (u'MoinMoin', + ('MoinMoin', '

MoinMoin

'), - (u'!MoinMoin', + ('!MoinMoin', '

MoinMoin

'), - (u'Self:FrontPage', + ('Self:FrontPage', '

FrontPage

'), - (u'http://moinmo.in/', + ('http://moinmo.in/', '

http://moinmo.in/

'), # email tests - (u'mailto:foo@bar.baz', + ('mailto:foo@bar.baz', '

mailto:foo@bar.baz

'), - (u'foo@bar.baz', + ('foo@bar.baz', '

foo@bar.baz

'), - (u'foo@bar', # 1.9 requires domain + ('foo@bar', # 1.9 requires domain '

foo@bar

'), ] @@ -51,7 +51,7 @@ def test_freelink(self, input, output): def serialize_strip(self, elem, **options): result = serialize(elem, namespaces=self.namespaces, **options) - return self.output_re.sub(u'', result) + return self.output_re.sub('', result) def do(self, input, output, args={}, skip=None): if skip: diff --git a/src/moin/converters/_tests/test_moinwiki19_in_20_out.py b/src/moin/converters/_tests/test_moinwiki19_in_20_out.py index 00798768b..579417a00 100644 --- a/src/moin/converters/_tests/test_moinwiki19_in_20_out.py +++ b/src/moin/converters/_tests/test_moinwiki19_in_20_out.py @@ -24,7 +24,7 @@ from moin.converters.moinwiki_out import Converter as conv_out -class TestConverter(object): +class TestConverter: input_namespaces = 'xmlns="{0}" xmlns:page="{1}" xmlns:xlink="{2}" xmlns:xinclude="{3}" xmlns:html="{4}"'.format( moin_page.namespace, moin_page.namespace, xlink.namespace, xinclude.namespace, html.namespace) @@ -45,9 +45,9 @@ def setup_class(self): data = [ # Note: old style attachments are are supported in moinwiki_in so conversion to moin 2 markup is not necessary # TODO: in a perfect world, moinwiki19_in should convert attachments - (u'[[attachment:filename.txt]]', '[[/filename.txt]]\n'), + ('[[attachment:filename.txt]]', '[[/filename.txt]]\n'), # moin 1.9 to 2.0 conversion - (u'TestPage', '[[TestPage]]\n'), + ('TestPage', '[[TestPage]]\n'), # (u'../SisterPage', '[[../SisterPage]]\n'), ] @@ -64,7 +64,7 @@ def handle_output(self, elem, **options): def serialize_strip(self, elem, **options): result = serialize(elem, namespaces=self.namespaces, **options) - return self.output_re.sub(u'', result) + return self.output_re.sub('', result) def do(self, input, output, args={}, skip=None): if skip: diff --git a/src/moin/converters/_tests/test_moinwiki_in.py b/src/moin/converters/_tests/test_moinwiki_in.py index 3067ced4a..aa58560c3 100644 --- a/src/moin/converters/_tests/test_moinwiki_in.py +++ b/src/moin/converters/_tests/test_moinwiki_in.py @@ -16,7 +16,7 @@ from moin.converters._args import Arguments -class TestConverter(object): +class TestConverter: namespaces = { moin_page: '', xlink: 'xlink', @@ -30,45 +30,45 @@ def setup_class(self): self.conv = Converter() data = [ - (u'Text', + ('Text', '

Text

'), - (u'Text\nTest', + ('Text\nTest', '

Text\nTest

'), - (u'Text\n\nTest', + ('Text\n\nTest', '

Text

Test

'), - (u'[[http://moinmo.in/]]', + ('[[http://moinmo.in/]]', '

http://moinmo.in/

'), - (u'[[javascript:alert("xss")]]', + ('[[javascript:alert("xss")]]', '

javascript:alert("xss")

'), - (u'[[http://moinmo.in/|MoinMoin]]', + ('[[http://moinmo.in/|MoinMoin]]', '

MoinMoin

'), - (u'[[MoinMoin]]', + ('[[MoinMoin]]', '

MoinMoin

'), - (u'[[MoinMoin#Heading]]', + ('[[MoinMoin#Heading]]', '

MoinMoin

'), - (u'[[#Heading]]', + ('[[#Heading]]', '

'), - (u'[[/inner2.png|{{/inner2.png||width=500}}]]', + ('[[/inner2.png|{{/inner2.png||width=500}}]]', '

'), - (u'{{somelocalimage|my alt text|width=10, height=10}}', + ('{{somelocalimage|my alt text|width=10, height=10}}', '

'), # html5 requires img tags to have an alt attribute, html_out.py will add any required attributes that are missing - (u'{{somelocalimage||width=10, height=10}}', + ('{{somelocalimage||width=10, height=10}}', '

'), - (u'{{somelocalimage||width=10, &h=10}}', + ('{{somelocalimage||width=10, &h=10}}', '

'), - (u'before {{somelocalimage}} middle {{somelocalimage}} after', + ('before {{somelocalimage}} middle {{somelocalimage}} after', '

before middle after

'), - (u'before {{http://moinmo.in}} middle {{http://moinmo.in}} after', + ('before {{http://moinmo.in}} middle {{http://moinmo.in}} after', '

before middle after

'), # in html5, object tags must not have alt attributes, html_out.py will adjust this so alt text is placed before the
- (u'{{http://moinmo.in/|test|width=10, height=10}}', + ('{{http://moinmo.in/|test|width=10, height=10}}', '

'), - (u'{{http://moinmo.in/}}', + ('{{http://moinmo.in/}}', '

'), - (u'{{http://moinmo.in/|MoinMoin}}', + ('{{http://moinmo.in/|MoinMoin}}', '

'), - (u'----', + ('----', ''), ] @@ -77,7 +77,7 @@ def test_base(self, args): self.do(*args) data = [ - (u'Text', + ('Text', '

Text

', {'arguments': Arguments(keyword={'style': 'background-color: red'})}), ] @@ -114,19 +114,19 @@ def test_emphasis(self, input, output): self.do(input, output) data = [ - (u'=Not_a_Heading=', # this is for better moin 1.x compatibility + ('=Not_a_Heading=', # this is for better moin 1.x compatibility '

=Not_a_Heading=

'), - (u'= Heading 1 =', + ('= Heading 1 =', 'Heading 1'), - (u'== Heading 2 ==', + ('== Heading 2 ==', 'Heading 2'), - (u'=== Heading 3 ===', + ('=== Heading 3 ===', 'Heading 3'), - (u'==== Heading 4 ====', + ('==== Heading 4 ====', 'Heading 4'), - (u'===== Heading 5 =====', + ('===== Heading 5 =====', 'Heading 5'), - (u'====== Heading 6 ======', + ('====== Heading 6 ======', 'Heading 6'), ] @@ -151,11 +151,11 @@ def test_heading(self, input, output): '

normal big underline big normal

'), ("/* normal __underline__ normal */", '

normal underline normal

'), - (u'"', + ('"', '

"

'), - (u'"', + ('"', '

"

'), - (u'"', + ('"', '

"

'), ] @@ -164,27 +164,27 @@ def test_inline(self, input, output): self.do(input, output) data = [ - (u' *Item', + (' *Item', 'Item'), - (u' * Item', + (' * Item', 'Item'), - (u' 1. Item', + (' 1. Item', 'Item'), - (u' Key:: Item', + (' Key:: Item', 'KeyItem'), - (u' Item', + (' Item', 'Item'), - (u' *Item\nText', + (' *Item\nText', 'Item

Text

'), - (u' *Item\n Item', + (' *Item\n Item', 'Item\nItem'), - (u' *Item 1\n *Item 2', + (' *Item 1\n *Item 2', 'Item 1Item 2'), - (u' *Item 1\n *Item 1.2\n *Item 2', + (' *Item 1\n *Item 1.2\n *Item 2', 'Item 1Item 1.2Item 2'), - (u' *List 1\n\n *List 2', + (' *List 1\n\n *List 2', 'List 1List 2'), - (u' * List 1\n 1. List 2', + (' * List 1\n 1. List 2', 'List 1List 2'), ] @@ -193,28 +193,28 @@ def test_list(self, input, output): self.do(input, output) data = [ - (u'<
>', + ('<
>', ''), - (u'Text<
>Text', + ('Text<
>Text', '

TextText

'), - (u'<>', + ('<>', ''), - (u'<><>', + ('<><>', '

'), - (u'<>', + ('<>', 'arg'), # these macro tests copied from test_creole_in, next test is different because leading space creates unordered list in moin2 - (u' <> ', + (' <> ', ''), - (u'Text <>', + ('Text <>', '

Text

'), - (u'Text <>', + ('Text <>', '

Text arg

'), - (u'Text\n<>', + ('Text\n<>', '

Text

'), - (u'Text\nText <>', + ('Text\nText <>', '

Text\nText

'), - (u'Text\n\n<>', + ('Text\n\n<>', '

Text

'), ] @@ -223,17 +223,17 @@ def test_macro(self, input, output): self.do(input, output) data = [ - (u'||Cell||', + ('||Cell||', 'Cell
'), - (u'||Cell 1||Cell 2||', + ('||Cell 1||Cell 2||', 'Cell 1Cell 2
'), - (u'||Row 1||\n||Row 2||\n', + ('||Row 1||\n||Row 2||\n', 'Row 1Row 2
'), - (u'||Cell 1.1||Cell 1.2||\n||Cell 2.1||Cell 2.2||\n', + ('||Cell 1.1||Cell 1.2||\n||Cell 2.1||Cell 2.2||\n', 'Cell 1.1Cell 1.2Cell 2.1Cell 2.2
'), - (u'||Header||\n===\n||Body||\n=====\n||Footer||', + ('||Header||\n===\n||Body||\n=====\n||Footer||', 'HeaderBodyFooter
'), - (u'||<>||', + ('||<>||', '
'), ] @@ -243,57 +243,57 @@ def test_table(self, input, output): data = [ # a class of moin-wiki-table is added to all tables, html_out may create thead and tfoot tags - (u'||||Span||\n\n', + ('||||Span||\n\n', 'Span
'), - (u'||<-2>Span||\n\n', + ('||<-2>Span||\n\n', 'Span
'), - (u'||<|2>Span||\n\n', + ('||<|2>Span||\n\n', 'Span
'), - (u'||Cell||\n', + ('||Cell||\n', 'Cell
'), - (u'||Cell||\n', + ('||Cell||\n', 'Cell
'), - (u'||Cell||\n', - 'Cell
'), - (u'||Cell||\n', + ('||Cell||\n', + 'Cell
'), + ('||Cell||\n', 'Cell
'), - (u'||Cell||\n', + ('||Cell||\n', 'Cell
'), - (u'||Cell||\n', + ('||Cell||\n', 'Cell
'), - (u'||Cell||\n', + ('||Cell||\n', 'Cell
'), - (u'||Cell||\n', + ('||Cell||\n', 'Cell
'), - (u'||Cell||\n', + ('||Cell||\n', 'Cell
'), - (u'||Cell||\n', + ('||Cell||\n', 'Cell
'), - (u'||Cell||\n', + ('||Cell||\n', 'Cell
'), - (u'||Cell||\n', + ('||Cell||\n', 'Cell
'), - (u'||Cell||\n', + ('||Cell||\n', 'Cell
My Table
'), - (u"||'''Cell'''||\n", + ("||'''Cell'''||\n", 'Cell
'), - (u'||<^>Cell||\n', + ('||<^>Cell||\n', 'Cell
'), - (u'||Cell||\n', + ('||Cell||\n', 'Cell
'), - (u'||<(>Cell||\n', + ('||<(>Cell||\n', 'Cell
'), - (u'||<:>Cell||\n', + ('||<:>Cell||\n', 'Cell
'), - (u'||<)>Cell||\n', + ('||<)>Cell||\n', 'Cell
'), - (u'||<99%>Cell||\n', + ('||<99%>Cell||\n', 'Cell
'), - (u'||Cell||\n', + ('||Cell||\n', # u'\xa0' below is equal to   '[ Error: "X" is invalid within <X>' + - u'\xa0' + ']Cell
'), + '\xa0' + ']Cell'), ] @pytest.mark.parametrize('input,output', data) @@ -301,31 +301,31 @@ def test_table_attributes(self, input, output): self.do(input, output) data = [ - (u'{{{nowiki}}}', + ('{{{nowiki}}}', '

nowiki

'), - (u'`nowiki`', + ('`nowiki`', '

nowiki

'), - (u'{{{{nowiki}}}}', + ('{{{{nowiki}}}}', '

{nowiki}

'), - (u'text: {{{nowiki}}}, text', + ('text: {{{nowiki}}}, text', '

text: nowiki, text

'), - (u'{{{\nnowiki\n}}}', + ('{{{\nnowiki\n}}}', '3nowiki'), - (u'{{{\nnowiki\nno\nwiki\n}}}', + ('{{{\nnowiki\nno\nwiki\n}}}', '3nowiki\nno\nwiki'), - (u'{{{nowiki}}} {{{nowiki}}}', + ('{{{nowiki}}} {{{nowiki}}}', '

nowiki nowiki

'), - (u'{{{}}}', + ('{{{}}}', '

'), - (u'``', + ('``', '

'), # XXX: Is correct? - (u'{{{#!\ntest\n}}}', + ('{{{#!\ntest\n}}}', '3#!test'), - (u'{{{#!(style="background-color: red")\nwiki\n}}}', + ('{{{#!(style="background-color: red")\nwiki\n}}}', '3#!(style="background-color: red")wiki'), - (u'{{{#!text/plain\ntext\n}}}', - u'3#!text/plaintext'), + ('{{{#!text/plain\ntext\n}}}', + '3#!text/plaintext'), ] @pytest.mark.parametrize('input,output', data) @@ -333,38 +333,38 @@ def test_nowiki(self, input, output): self.do(input, output) data = [ - (u'{{{#!wiki\nwiki\n}}}', + ('{{{#!wiki\nwiki\n}}}', "3#!wikiwiki"), - (u'{{{{{#!wiki\nwiki\n}}}}}', + ('{{{{{#!wiki\nwiki\n}}}}}', "5#!wikiwiki"), - (u'{{{#!wiki(style="background-color: red")\nwiki\n}}}', + ('{{{#!wiki(style="background-color: red")\nwiki\n}}}', '3#!wiki(style="background-color: red")wiki'), - (u'{{{#!highlight python\nimport os\n}}}', + ('{{{#!highlight python\nimport os\n}}}', "3#!highlight pythonimport os"), - (u'{{{#!python\nimport os\n}}}', + ('{{{#!python\nimport os\n}}}', "3#!pythonimport os"), - (u'{{{#!csv\na;b;c\nd;e;22\n}}}', + ('{{{#!csv\na;b;c\nd;e;22\n}}}', "3#!csva;b;c\nd;e;22"), - (u'{{{#!csv ,\na,b,c\nd,e,22\n}}}', + ('{{{#!csv ,\na,b,c\nd,e,22\n}}}', "3#!csv ,a,b,c\nd,e,22"), # TODO: Backward compatibility - (u'{{{#!wiki red/solid\nwiki\n}}}', + ('{{{#!wiki red/solid\nwiki\n}}}', "3#!wiki red/solidwiki"), - (u'{{{#!wiki red/solid\nwiki\n\nwiki\n}}}', + ('{{{#!wiki red/solid\nwiki\n\nwiki\n}}}', "3#!wiki red/solidwiki\n\nwiki"), - (u'{{{#!rst\nHeading\n-------\n}}}', + ('{{{#!rst\nHeading\n-------\n}}}', "3#!rstHeading\n-------"), ( - u"{{{#!docbook\n
\n
\nA docbook document\n
\n
\n}}}", + "{{{#!docbook\n
\n
\nA docbook document\n
\n
\n}}}", "3#!docbook<article xmlns='http://docbook.org/ns/docbook' xmlns:xlink='http://www.w3.org/1999/xlink'>\n<section>\n<title>A docbook document</title>\n</section>\n</article>"), - (u'{{{#!creole\n|=A|1\n|=B|2\n}}}', + ('{{{#!creole\n|=A|1\n|=B|2\n}}}', "3#!creole|=A|1\n|=B|2"), - (u'{{{#!text/x.moin.creole\ntext\n}}}', - u"3#!text/x.moin.creoletext"), - (u'{{{#!markdown\n~~~\naaa\nbbb\nccc\n~~~\n}}}', - u"3#!markdown~~~\naaa\nbbb\nccc\n~~~"), - (u'{{{#!mediawiki\n=== Level 3 ===\n}}}', - u"3#!mediawiki=== Level 3 ==="), + ('{{{#!text/x.moin.creole\ntext\n}}}', + "3#!text/x.moin.creoletext"), + ('{{{#!markdown\n~~~\naaa\nbbb\nccc\n~~~\n}}}', + "3#!markdown~~~\naaa\nbbb\nccc\n~~~"), + ('{{{#!mediawiki\n=== Level 3 ===\n}}}', + "3#!mediawiki=== Level 3 ==="), ] @pytest.mark.parametrize('input,output', data) @@ -372,9 +372,9 @@ def test_nowiki_parsers(self, input, output): self.do(input, output) data = [ - (u'Text\n * Item\n\nText', + ('Text\n * Item\n\nText', '

Text

Item

Text

'), - (u'Text\n||Item||\nText', + ('Text\n||Item||\nText', '

Text

Item

Text

'), ] @@ -383,15 +383,15 @@ def test_composite(self, input, output): self.do(input, output) data = [ - (u'[[MoinMoin:RecentChanges]]', + ('[[MoinMoin:RecentChanges]]', '

RecentChanges

'), - (u'[[MoinMoin:RecentChanges|changes]]', + ('[[MoinMoin:RecentChanges|changes]]', '

changes

'), - (u'[[MoinMoin:Foo/Bar.Baz]]', + ('[[MoinMoin:Foo/Bar.Baz]]', '

Foo/Bar.Baz

'), - (u'[[MoinMoin:Blank In Page Name|blank in page name]]', + ('[[MoinMoin:Blank In Page Name|blank in page name]]', '

blank in page name

'), - (u'[[InvalidWikiName:RecentChanges]]', + ('[[InvalidWikiName:RecentChanges]]', '

InvalidWikiName:RecentChanges

'), ] @@ -400,13 +400,13 @@ def test_interwiki(self, input, output): self.do(input, output) data = [ - (u'[[mailto:root]]', + ('[[mailto:root]]', '

mailto:root

'), - (u'[[mailto:foo@bar.baz]]', + ('[[mailto:foo@bar.baz]]', '

mailto:foo@bar.baz

'), - (u'[[mailto:foo@bar.baz|write me]]', + ('[[mailto:foo@bar.baz|write me]]', '

write me

'), - (u'[[mailto:foo.bar_baz@bar.baz]]', # . and _ are special characters commonly allowed by email systems + ('[[mailto:foo.bar_baz@bar.baz]]', # . and _ are special characters commonly allowed by email systems '

mailto:foo.bar_baz@bar.baz

'), ] @@ -416,7 +416,7 @@ def test_email(self, input, output): def serialize_strip(self, elem, **options): result = serialize(elem, namespaces=self.namespaces, **options) - return self.output_re.sub(u'', result) + return self.output_re.sub('', result) def do(self, input, output, args={}, skip=None): if skip: diff --git a/src/moin/converters/_tests/test_moinwiki_in_out.py b/src/moin/converters/_tests/test_moinwiki_in_out.py index 4fc2a13c4..7aca6a34e 100644 --- a/src/moin/converters/_tests/test_moinwiki_in_out.py +++ b/src/moin/converters/_tests/test_moinwiki_in_out.py @@ -21,7 +21,7 @@ from moin.converters.moinwiki_out import Converter as conv_out -class TestConverter(object): +class TestConverter: input_namespaces = 'xmlns="{0}" xmlns:page="{1}" xmlns:xlink="{2}" xmlns:xinclude="{3}" xmlns:html="{4}"'.format( moin_page.namespace, moin_page.namespace, xlink.namespace, xinclude.namespace, html.namespace) @@ -40,35 +40,35 @@ def setup_class(self): self.conv_out = conv_out() data = [ - (u"Text", - u"Text\n"), - (u"Text\n\nText\n", - u"Text\n\nText\n"), - (u"----\n-----\n------\n", - u"----\n-----\n------\n"), - (u"'''strong or bold'''\n", - u"'''strong or bold'''\n"), - (u"''emphasis or italic''\n", - u"''emphasis or italic''\n"), + ("Text", + "Text\n"), + ("Text\n\nText\n", + "Text\n\nText\n"), + ("----\n-----\n------\n", + "----\n-----\n------\n"), + ("'''strong or bold'''\n", + "'''strong or bold'''\n"), + ("''emphasis or italic''\n", + "''emphasis or italic''\n"), # extraneous x required below to prevent IndexError, side effect of serializer - (u"{{{{{x\nblockcode\n}}}}}\n", - u"{{{{{x\nblockcode\n}}}}}\n"), - (u"`monospace`\n", - u"`monospace`\n"), - (u"--(stroke)--\n", - u"--(stroke)--\n"), - (u"__underline__\n", - u"__underline__\n"), - (u"~+larger+~\n", - u"~+larger+~\n"), - (u"~-smaller-~\n", - u"~-smaller-~\n"), - (u"^super^script\n", - u"^super^script\n"), - (u",,sub,,script\n", - u",,sub,,script\n"), - (u"#ANY any", - u"#ANY any\n"), + ("{{{{{x\nblockcode\n}}}}}\n", + "{{{{{x\nblockcode\n}}}}}\n"), + ("`monospace`\n", + "`monospace`\n"), + ("--(stroke)--\n", + "--(stroke)--\n"), + ("__underline__\n", + "__underline__\n"), + ("~+larger+~\n", + "~+larger+~\n"), + ("~-smaller-~\n", + "~-smaller-~\n"), + ("^super^script\n", + "^super^script\n"), + (",,sub,,script\n", + ",,sub,,script\n"), + ("#ANY any", + "#ANY any\n"), ] @pytest.mark.parametrize('input,output', data) @@ -76,16 +76,16 @@ def test_base(self, input, output): self.do(input, output) data = [ - (u"/* simple inline */", - u"/* simple inline */"), - (u"text /* text ''with '''markup''''' */ text", - u"text /* text ''with '''markup''''' */ text"), - (u"## block 1\n\n## block 2", - u"## block 1\n\n## block 2"), + ("/* simple inline */", + "/* simple inline */"), + ("text /* text ''with '''markup''''' */ text", + "text /* text ''with '''markup''''' */ text"), + ("## block 1\n\n## block 2", + "## block 1\n\n## block 2"), # \n is omitted from output because serialize method (see below) joins adjacent text children - (u"## block line 1\n## block line 2\n\n", - u"## block line 1## block line 2\n\n"), + ("## block line 1\n## block line 2\n\n", + "## block line 1## block line 2\n\n"), ] @pytest.mark.parametrize('input,output', data) @@ -93,37 +93,37 @@ def test_comments(self, input, output): self.do(input, output) data = [ - (u'{{{\ndef hello():\n print "Hello World!"\n}}}', - u'{{{\ndef hello():\n print "Hello World!"\n}}}'), - (u'{{{{\ndef hello():\n print "Hello World!"\n}}}}', - u'{{{{\ndef hello():\n print "Hello World!"\n}}}}'), - (u'{{{#!highlight python\ndef hello():\n print "Hello World!"\n}}}', - u'{{{#!highlight python\ndef hello():\n print "Hello World!"\n}}}'), - (u"{{{#!python\nimport sys\n}}}", - u"{{{#!python\nimport sys\n}}}"), - (u"{{{#!creole\n... **bold** ...\n}}}", - u"{{{#!creole\n... **bold** ...\n}}}"), - (u"{{{#!creole\n|=X|1\n|=Y|123\n|=Z|12345\n}}}", - u"{{{#!creole\n|=X|1\n|=Y|123\n|=Z|12345\n}}}"), - (u"{{{#!csv ,\nFruit,Color,Quantity\napple,red,5\nbanana,yellow,23\ngrape,purple,126\n}}}", - u"{{{#!csv ,\nFruit,Color,Quantity\napple,red,5\nbanana,yellow,23\ngrape,purple,126\n}}}"), + ('{{{\ndef hello():\n print "Hello World!"\n}}}', + '{{{\ndef hello():\n print "Hello World!"\n}}}'), + ('{{{{\ndef hello():\n print "Hello World!"\n}}}}', + '{{{{\ndef hello():\n print "Hello World!"\n}}}}'), + ('{{{#!highlight python\ndef hello():\n print "Hello World!"\n}}}', + '{{{#!highlight python\ndef hello():\n print "Hello World!"\n}}}'), + ("{{{#!python\nimport sys\n}}}", + "{{{#!python\nimport sys\n}}}"), + ("{{{#!creole\n... **bold** ...\n}}}", + "{{{#!creole\n... **bold** ...\n}}}"), + ("{{{#!creole\n|=X|1\n|=Y|123\n|=Z|12345\n}}}", + "{{{#!creole\n|=X|1\n|=Y|123\n|=Z|12345\n}}}"), + ("{{{#!csv ,\nFruit,Color,Quantity\napple,red,5\nbanana,yellow,23\ngrape,purple,126\n}}}", + "{{{#!csv ,\nFruit,Color,Quantity\napple,red,5\nbanana,yellow,23\ngrape,purple,126\n}}}"), # old style arguments - (u"{{{#!wiki caution\n '''Don't overuse admonitions'''\n}}}", - u"{{{#!wiki caution\n '''Don't overuse admonitions'''\n}}}"), - (u"{{{#!wiki comment/dotted\nThis is a wiki parser.\n\nIts visibility gets toggled the same way.\n}}}", - u"{{{#!wiki comment/dotted\nThis is a wiki parser.\n\nIts visibility gets toggled the same way.\n}}}"), - (u"""{{{#!wiki red/solid\nThis is wiki markup in a '''div''' with __css__ `class="red solid"`.\n}}}""", - u"""{{{#!wiki red/solid\nThis is wiki markup in a '''div''' with __css__ `class="red solid"`.\n}}}"""), + ("{{{#!wiki caution\n '''Don't overuse admonitions'''\n}}}", + "{{{#!wiki caution\n '''Don't overuse admonitions'''\n}}}"), + ("{{{#!wiki comment/dotted\nThis is a wiki parser.\n\nIts visibility gets toggled the same way.\n}}}", + "{{{#!wiki comment/dotted\nThis is a wiki parser.\n\nIts visibility gets toggled the same way.\n}}}"), + ("""{{{#!wiki red/solid\nThis is wiki markup in a '''div''' with __css__ `class="red solid"`.\n}}}""", + """{{{#!wiki red/solid\nThis is wiki markup in a '''div''' with __css__ `class="red solid"`.\n}}}"""), # new style arguments - (u'{{{#!wiki (style="color: green")\nThis is wiki markup in a """div""" with `style="color: green"`.\n}}}', - u'{{{#!wiki (style="color: green")\nThis is wiki markup in a """div""" with `style="color: green"`.\n}}}'), - (u'{{{#!wiki (style="color: green")\ngreen\n}}}', - u'{{{#!wiki (style="color: green")\ngreen\n}}}'), - (u'{{{#!wiki (style="color: green" class="dotted")\ngreen\n}}}', - u'{{{#!wiki (style="color: green" class="dotted")\ngreen\n}}}'), + ('{{{#!wiki (style="color: green")\nThis is wiki markup in a """div""" with `style="color: green"`.\n}}}', + '{{{#!wiki (style="color: green")\nThis is wiki markup in a """div""" with `style="color: green"`.\n}}}'), + ('{{{#!wiki (style="color: green")\ngreen\n}}}', + '{{{#!wiki (style="color: green")\ngreen\n}}}'), + ('{{{#!wiki (style="color: green" class="dotted")\ngreen\n}}}', + '{{{#!wiki (style="color: green" class="dotted")\ngreen\n}}}'), # multi-level - (u"{{{#!wiki green\ngreen\n{{{{#!wiki orange\norange\n}}}}\ngreen\n}}}", - u"{{{#!wiki green\ngreen\n{{{{#!wiki orange\norange\n}}}}\ngreen\n}}}"), + ("{{{#!wiki green\ngreen\n{{{{#!wiki orange\norange\n}}}}\ngreen\n}}}", + "{{{#!wiki green\ngreen\n{{{{#!wiki orange\norange\n}}}}\ngreen\n}}}"), ] @pytest.mark.parametrize('input,output', data) @@ -131,16 +131,16 @@ def test_nowiki(self, input, output): self.do(input, output) data = [ - (u"<>", - u"<>\n"), - (u"<>", - u"<>\n"), - (u"<>", - u"<>\n"), - (u"<>", - u"<>\n"), - (u"||<>||", - u"||<>||\n"), + ("<>", + "<>\n"), + ("<>", + "<>\n"), + ("<>", + "<>\n"), + ("<>", + "<>\n"), + ("||<>||", + "||<>||\n"), ] @pytest.mark.parametrize('input,output', data) @@ -152,7 +152,7 @@ def test_macros(self, input, output): # This is possibly due to a defect in the serialize_strip method data = [ # moinwiki_in converter changes indented text to no-bullet lists - (u""" + (""" indented text text indented to the 2nd level first level @@ -160,7 +160,7 @@ def test_macros(self, input, output): second level again, will be combined with line above . second level as no bullet list continuation of no bullet list""", - u""" + """ . indented text . text indented to the 2nd level . first level @@ -169,7 +169,7 @@ def test_macros(self, input, output): . second level as no bullet list continuation of no bullet list"""), # output should equal input, but note todo above - (u""" + (""" . indented text . text indented to the 2nd level . first level @@ -177,7 +177,7 @@ def test_macros(self, input, output): second level again, will be combined with line above . second level as no bullet list continuation of no bullet list""", - u""" + """ . indented text . text indented to the 2nd level . first level @@ -192,54 +192,54 @@ def test_indented_text(self, input, output): self.do(input, output) data = [ - (u"[[Home]]", - u"[[Home]]"), - (u"[[Home/subitem]]", - u"[[Home/subitem]]"), - (u"[[/Subitem]]", - u"[[/Subitem]]"), - (u"[[../Home]]", - u"[[../Home]]"), - (u"[[#Heading]]", - u"[[#Heading]]"), - (u"[[SomePage#subsection|subsection of Some Page]]", - u"[[SomePage#subsection|subsection of Some Page]]\n"), - (u'[[SomePage|{{attachment:samplegraphic.png}}|target=_blank]]', - u'[[SomePage|{{/samplegraphic.png}}|target="_blank"]]\n'), - (u"[[../SisterPage|link text]]", - u"[[../SisterPage|link text]]\n"), - (u"[[http://static.moinmo.in/logos/moinmoin.png|{{attachment:samplegraphic.png}}|target=_blank]]", - u'[[http://static.moinmo.in/logos/moinmoin.png|{{/samplegraphic.png}}|target="_blank"]]\n'), - (u'[[https://moinmo.in/|MoinMoin Wiki|class="green dotted", accesskey=1]]', - u'[[https://moinmo.in/|MoinMoin Wiki|accesskey="1",class="green dotted"]]\n'), - (u'[[https://moinmo.in/| |title="go there!"]]', - u'[[https://moinmo.in/||title="go there!"]]'), + ("[[Home]]", + "[[Home]]"), + ("[[Home/subitem]]", + "[[Home/subitem]]"), + ("[[/Subitem]]", + "[[/Subitem]]"), + ("[[../Home]]", + "[[../Home]]"), + ("[[#Heading]]", + "[[#Heading]]"), + ("[[SomePage#subsection|subsection of Some Page]]", + "[[SomePage#subsection|subsection of Some Page]]\n"), + ('[[SomePage|{{attachment:samplegraphic.png}}|target=_blank]]', + '[[SomePage|{{/samplegraphic.png}}|target="_blank"]]\n'), + ("[[../SisterPage|link text]]", + "[[../SisterPage|link text]]\n"), + ("[[http://static.moinmo.in/logos/moinmoin.png|{{attachment:samplegraphic.png}}|target=_blank]]", + '[[http://static.moinmo.in/logos/moinmoin.png|{{/samplegraphic.png}}|target="_blank"]]\n'), + ('[[https://moinmo.in/|MoinMoin Wiki|class="green dotted", accesskey=1]]', + '[[https://moinmo.in/|MoinMoin Wiki|accesskey="1",class="green dotted"]]\n'), + ('[[https://moinmo.in/| |title="go there!"]]', + '[[https://moinmo.in/||title="go there!"]]'), # interwiki # TODO: should this obsolete (1.9.x) form be made to work? # (u'[[MoinMoin:MoinMoinWiki|MoinMoin Wiki|&action=diff,&rev1=1,&rev2=2]]', '[[MoinMoin:MoinMoinWiki?action=diff,&rev1=1,&rev2=2|MoinMoin Wiki]]\n'), - (u"[[MeatBall:InterWiki]]", - u"[[MeatBall:InterWiki]]"), - (u"[[MeatBall:InterWiki|InterWiki page on MeatBall]]", - u"[[MeatBall:InterWiki|InterWiki page on MeatBall]]"), + ("[[MeatBall:InterWiki]]", + "[[MeatBall:InterWiki]]"), + ("[[MeatBall:InterWiki|InterWiki page on MeatBall]]", + "[[MeatBall:InterWiki|InterWiki page on MeatBall]]"), # TODO: attachments should be converted within import19.py and support removed from moin2 # Note: old style attachments are converted to new style sub-item syntax; "&do-get" is appended to link and ignored - (u"[[attachment:HelpOnImages/pineapple.jpg|a pineapple|&do=get]]", - u"[[/HelpOnImages/pineapple.jpg?do=get|a pineapple]]\n"), - (u"[[attachment:filename.txt]]", - u"[[/filename.txt]]\n"), + ("[[attachment:HelpOnImages/pineapple.jpg|a pineapple|&do=get]]", + "[[/HelpOnImages/pineapple.jpg?do=get|a pineapple]]\n"), + ("[[attachment:filename.txt]]", + "[[/filename.txt]]\n"), # test parameters - (u'[[SomePage|Some Page|target=_blank]]', - u'[[SomePage|Some Page|target="_blank"]]\n'), - (u'[[SomePage|Some Page|download=MyItem,title=Download]]', - u'[[SomePage|Some Page|download="MyItem",title="Download"]]\n'), - (u'[[SomePage|Some Page|download="MyItem",title="Download"]]', - u'[[SomePage|Some Page|download="MyItem",title="Download"]]\n'), - (u'[[SomePage|Some Page|class=orange,accesskey=1]]', - u'[[SomePage|Some Page|accesskey="1",class="orange"]]\n'), - (u'[[/inner2.png|{{/inner2.png||width=500}}]]', - u'[[/inner2.png|{{/inner2.png||width="500"}}]]\n'), - (u'[[file://///server/share/filename%20with%20spaces.txt|link to filename.txt]]', - u'[[file://///server/share/filename%20with%20spaces.txt|link to filename.txt]]'), + ('[[SomePage|Some Page|target=_blank]]', + '[[SomePage|Some Page|target="_blank"]]\n'), + ('[[SomePage|Some Page|download=MyItem,title=Download]]', + '[[SomePage|Some Page|download="MyItem",title="Download"]]\n'), + ('[[SomePage|Some Page|download="MyItem",title="Download"]]', + '[[SomePage|Some Page|download="MyItem",title="Download"]]\n'), + ('[[SomePage|Some Page|class=orange,accesskey=1]]', + '[[SomePage|Some Page|accesskey="1",class="orange"]]\n'), + ('[[/inner2.png|{{/inner2.png||width=500}}]]', + '[[/inner2.png|{{/inner2.png||width="500"}}]]\n'), + ('[[file://///server/share/filename%20with%20spaces.txt|link to filename.txt]]', + '[[file://///server/share/filename%20with%20spaces.txt|link to filename.txt]]'), ] @pytest.mark.parametrize('input,output', data) @@ -247,24 +247,24 @@ def test_link(self, input, output): self.do(input, output) data = [ - (u" * A\n * B\n 1. C\n 1. D\n I. E\n I. F\n", - u" * A\n * B\n 1. C\n 1. D\n I. E\n I. F\n"), - (u" * A\n 1. C\n I. E\n", - u" * A\n 1. C\n I. E\n"), - (u" * A\n 1. C\n 1. D\n", - u" * A\n 1. C\n 1. D\n"), - (u" . A\n . C\n . D\n", - u" . A\n . C\n . D\n"), - (u" i. E\n i. F\n", - u" i. E\n i. F\n"), - (u" i.#11 K\n i. L\n", - u" i.#11 K\n i. L\n"), - (u" 1.#11 eleven\n 1. twelve\n", - u" 1.#11 eleven\n 1. twelve\n"), - (u" A:: B\n :: C\n :: D\n", - u" A::\n :: B\n :: C\n :: D\n"), - (u" A::\n :: B\n :: C\n :: D\n", - u" A::\n :: B\n :: C\n :: D\n"), + (" * A\n * B\n 1. C\n 1. D\n I. E\n I. F\n", + " * A\n * B\n 1. C\n 1. D\n I. E\n I. F\n"), + (" * A\n 1. C\n I. E\n", + " * A\n 1. C\n I. E\n"), + (" * A\n 1. C\n 1. D\n", + " * A\n 1. C\n 1. D\n"), + (" . A\n . C\n . D\n", + " . A\n . C\n . D\n"), + (" i. E\n i. F\n", + " i. E\n i. F\n"), + (" i.#11 K\n i. L\n", + " i.#11 K\n i. L\n"), + (" 1.#11 eleven\n 1. twelve\n", + " 1.#11 eleven\n 1. twelve\n"), + (" A:: B\n :: C\n :: D\n", + " A::\n :: B\n :: C\n :: D\n"), + (" A::\n :: B\n :: C\n :: D\n", + " A::\n :: B\n :: C\n :: D\n"), ] @pytest.mark.parametrize('input,output', data) @@ -272,32 +272,32 @@ def test_list(self, input, output): self.do(input, output) data = [ - (u'||A||B||<|2>D||\n||||C||\n', - u'||A||B||D||\n||C||\n'), - (u"||'''A'''||'''B'''||'''C'''||\n||1 ||2 ||3 ||\n", - u"||'''A'''||'''B'''||'''C'''||\n||1 ||2 ||3 ||\n"), - (u'||<|2> cell spanning 2 rows ||cell in the 2nd column ||\n||cell in the 2nd column of the 2nd row ||\n||<-2>test||\n||||test||', - u'|| cell spanning 2 rows ||cell in the 2nd column ||\n||cell in the 2nd column of the 2nd row ||\n||test||\n||test||\n'), - (u'|| narrow ||<99%> wide ||', - u'|| narrow || wide ||\n'), - (u'|| narrow ||<:> wide ||', - u'|| narrow || wide ||\n'), - (u'||table 1||\n\n||table 2||', - u'||table 1||\n\n||table 2||'), - (u'||<#FF8080> red ||<#80FF80> green ||<#8080FF> blue ||', - u'|| red || green || blue ||\n'), - (u'|| normal || bold || red || boldred ||', - u'|| normal || bold || red || boldred ||\n'), - (u'|| red || green || blue ||', - u'|| red || green || blue ||\n'), - (u'||Fruit||Quantity||\n=====\n||Apple||2||\n||Orange||1||\n||Banana||4||\n=====\n||Total||7||', - u'||Fruit||Quantity||\n=====\n||Apple||2||\n||Orange||1||\n||Banana||4||\n=====\n||Total||7||'), - (u'|| top|| center|| bottom||\n|| right||\n|| left||', - u'|| top|| center|| bottom||\n|| right||\n|| left||'), - (u'||<^|3> top ||<:99%> center || bottom ||\n||<)> right ||\n||<(> left ||', - u'|| top || center || bottom ||\n|| right ||\n|| left ||'), - (u'||A || like <|2> ||\n|| like <#00FF00> ||\n|| like <-2>||', - u'||A || like <|2> ||\n|| like <#00FF00> ||\n|| like <-2>||'), + ('||A||B||<|2>D||\n||||C||\n', + '||A||B||D||\n||C||\n'), + ("||'''A'''||'''B'''||'''C'''||\n||1 ||2 ||3 ||\n", + "||'''A'''||'''B'''||'''C'''||\n||1 ||2 ||3 ||\n"), + ('||<|2> cell spanning 2 rows ||cell in the 2nd column ||\n||cell in the 2nd column of the 2nd row ||\n||<-2>test||\n||||test||', + '|| cell spanning 2 rows ||cell in the 2nd column ||\n||cell in the 2nd column of the 2nd row ||\n||test||\n||test||\n'), + ('|| narrow ||<99%> wide ||', + '|| narrow || wide ||\n'), + ('|| narrow ||<:> wide ||', + '|| narrow || wide ||\n'), + ('||table 1||\n\n||table 2||', + '||table 1||\n\n||table 2||'), + ('||<#FF8080> red ||<#80FF80> green ||<#8080FF> blue ||', + '|| red || green || blue ||\n'), + ('|| normal || bold || red || boldred ||', + '|| normal || bold || red || boldred ||\n'), + ('|| red || green || blue ||', + '|| red || green || blue ||\n'), + ('||Fruit||Quantity||\n=====\n||Apple||2||\n||Orange||1||\n||Banana||4||\n=====\n||Total||7||', + '||Fruit||Quantity||\n=====\n||Apple||2||\n||Orange||1||\n||Banana||4||\n=====\n||Total||7||'), + ('|| top|| center|| bottom||\n|| right||\n|| left||', + '|| top|| center|| bottom||\n|| right||\n|| left||'), + ('||<^|3> top ||<:99%> center || bottom ||\n||<)> right ||\n||<(> left ||', + '|| top || center || bottom ||\n|| right ||\n|| left ||'), + ('||A || like <|2> ||\n|| like <#00FF00> ||\n|| like <-2>||', + '||A || like <|2> ||\n|| like <#00FF00> ||\n|| like <-2>||'), ] @pytest.mark.parametrize('input,output', data) @@ -305,44 +305,44 @@ def test_table(self, input, output): self.do(input, output) data = [ - (u'{{png}}', - u'{{png}}\n'), - (u'{{png|png}}', - u'{{png|png}}\n'), # alt text same as default test - (u'{{png|my png}}', - u'{{png|my png}}\n'), - (u'{{{{video.mp4}}}}', - u'{{{{video.mp4}}}}\n'), - (u'{{{{audio.mp3}}}}', - u'{{{{audio.mp3}}}}\n'), + ('{{png}}', + '{{png}}\n'), + ('{{png|png}}', + '{{png|png}}\n'), # alt text same as default test + ('{{png|my png}}', + '{{png|my png}}\n'), + ('{{{{video.mp4}}}}', + '{{{{video.mp4}}}}\n'), + ('{{{{audio.mp3}}}}', + '{{{{audio.mp3}}}}\n'), # output attributes will always be quoted, even if input is not quoted - (u'{{png|my png|width=100}}', - u'{{png|my png|width="100"}}\n'), - (u'{{png|my png|&w=100"}}', - u'{{png|my png|&w=100}}\n'), - (u'{{png||width="100"}}', - u'{{png||width="100"}}\n'), - (u'{{drawing:anywikitest.adraw}}', - u'{{drawing:anywikitest.adraw}}\n'), - (u'{{http://static.moinmo.in/logos/moinmoin.png}}\n', - u'{{http://static.moinmo.in/logos/moinmoin.png}}\n'), - (u'{{http://static.moinmo.in/logos/moinmoin.png|alt text}}\n', - u'{{http://static.moinmo.in/logos/moinmoin.png|alt text}}\n'), + ('{{png|my png|width=100}}', + '{{png|my png|width="100"}}\n'), + ('{{png|my png|&w=100"}}', + '{{png|my png|&w=100}}\n'), + ('{{png||width="100"}}', + '{{png||width="100"}}\n'), + ('{{drawing:anywikitest.adraw}}', + '{{drawing:anywikitest.adraw}}\n'), + ('{{http://static.moinmo.in/logos/moinmoin.png}}\n', + '{{http://static.moinmo.in/logos/moinmoin.png}}\n'), + ('{{http://static.moinmo.in/logos/moinmoin.png|alt text}}\n', + '{{http://static.moinmo.in/logos/moinmoin.png|alt text}}\n'), # output sequence of height, width, class may not be the same as input, # so here we test only one attribute at a time to avoid random test failures - (u'{{http://static.moinmo.in/logos/moinmoin.png|alt text|height="150"}}\n', - u'{{http://static.moinmo.in/logos/moinmoin.png|alt text|height="150"}}\n'), - (u'{{http://static.moinmo.in/logos/moinmoin.png|alt text|width="100"}}', - u'{{http://static.moinmo.in/logos/moinmoin.png|alt text|width="100"}}\n'), - (u'{{http://static.moinmo.in/logos/moinmoin.png|alt text|class="right"}}', - u'{{http://static.moinmo.in/logos/moinmoin.png|alt text|class="right"}}\n'), + ('{{http://static.moinmo.in/logos/moinmoin.png|alt text|height="150"}}\n', + '{{http://static.moinmo.in/logos/moinmoin.png|alt text|height="150"}}\n'), + ('{{http://static.moinmo.in/logos/moinmoin.png|alt text|width="100"}}', + '{{http://static.moinmo.in/logos/moinmoin.png|alt text|width="100"}}\n'), + ('{{http://static.moinmo.in/logos/moinmoin.png|alt text|class="right"}}', + '{{http://static.moinmo.in/logos/moinmoin.png|alt text|class="right"}}\n'), # Note: old style attachments are converted to new style sub-item syntax - (u'{{attachment:image.png}}', - u'{{/image.png}}\n'), - (u'{{attachment:image.png|alt text}}', - u'{{/image.png|alt text}}\n'), - (u'{{attachment:image.png|alt text|height="150"}}', - u'{{/image.png|alt text|height="150"}}\n'), + ('{{attachment:image.png}}', + '{{/image.png}}\n'), + ('{{attachment:image.png|alt text}}', + '{{/image.png|alt text}}\n'), + ('{{attachment:image.png|alt text|height="150"}}', + '{{/image.png|alt text|height="150"}}\n'), ] @pytest.mark.parametrize('input,output', data) @@ -350,16 +350,16 @@ def test_object(self, input, output): self.do(input, output) data = [ - (u'Images are aligned to bottom {{png}} of text by default.', - u'Images are aligned to bottom {{png}} of text by default.'), - (u'This image is the big logo floated to the right: {{svg|my svg|class="right"}}', - u'This image is the big logo floated to the right: {{svg|my svg|class="right"}}'), - (u'Image aligned to top of text. {{jpeg||&w=75 class="top"}}', - u'Image aligned to top of text. {{jpeg||&w=75 class="top"}}'), - (u'Image aligned to middle of text. {{http://static.moinmo.in/logos/moinmoin.png||class=middle}}', - u'Image aligned to middle of text. {{http://static.moinmo.in/logos/moinmoin.png||class="middle"}}'), - (u'Transclude an HTTPS web page: <
>{{https://moinmo.in||width="800"}}', - u'Transclude an HTTPS web page: <
>{{https://moinmo.in||width="800"}}'), + ('Images are aligned to bottom {{png}} of text by default.', + 'Images are aligned to bottom {{png}} of text by default.'), + ('This image is the big logo floated to the right: {{svg|my svg|class="right"}}', + 'This image is the big logo floated to the right: {{svg|my svg|class="right"}}'), + ('Image aligned to top of text. {{jpeg||&w=75 class="top"}}', + 'Image aligned to top of text. {{jpeg||&w=75 class="top"}}'), + ('Image aligned to middle of text. {{http://static.moinmo.in/logos/moinmoin.png||class=middle}}', + 'Image aligned to middle of text. {{http://static.moinmo.in/logos/moinmoin.png||class="middle"}}'), + ('Transclude an HTTPS web page: <
>{{https://moinmo.in||width="800"}}', + 'Transclude an HTTPS web page: <
>{{https://moinmo.in||width="800"}}'), ] @pytest.mark.parametrize('input,output', data) @@ -367,8 +367,8 @@ def test_transclusions(self, input, output): self.do(input, output) data = [ - (ur"smileys: X-( :D >:> <:( :\ :o :-( :( :) B) :)) ;) |) |-) :-? /!\ (./) {X} {i} (!) {1} {2} {3} {*} {o} {OK}", - ur"smileys: X-( :D >:> <:( :\ :o :-( :( :) B) :)) ;) |) |-) :-? /!\ (./) {X} {i} (!) {1} {2} {3} {*} {o} {OK}"), + (r"smileys: X-( :D >:> <:( :\ :o :-( :( :) B) :)) ;) |) |-) :-? /!\ (./) {X} {i} (!) {1} {2} {3} {*} {o} {OK}", + r"smileys: X-( :D >:> <:( :\ :o :-( :( :) B) :)) ;) |) |-) :-? /!\ (./) {X} {i} (!) {1} {2} {3} {*} {o} {OK}"), ] @pytest.mark.parametrize('input,output', data) @@ -384,7 +384,7 @@ def handle_output(self, elem, **options): def serialize_strip(self, elem, **options): result = serialize(elem, namespaces=self.namespaces, **options) - return self.output_re.sub(u'', result) + return self.output_re.sub('', result) def do(self, input, output, args={}, skip=None): if skip: diff --git a/src/moin/converters/_tests/test_moinwiki_out.py b/src/moin/converters/_tests/test_moinwiki_out.py index a47b62790..9c7301736 100644 --- a/src/moin/converters/_tests/test_moinwiki_out.py +++ b/src/moin/converters/_tests/test_moinwiki_out.py @@ -16,7 +16,7 @@ from moin.converters.moinwiki_out import Converter -class Base(object): +class Base: input_namespaces = ns_all = 'xmlns="{0}" xmlns:page="{1}" xmlns:xlink="{2}" xmlns:html="{3}"'.format( moin_page.namespace, moin_page.namespace, xlink.namespace, html.namespace) output_namespaces = { @@ -44,20 +44,20 @@ def setup_class(self): self.conv = Converter() data = [ - (u'Текст', u'Текст\n'), - (u'Text', u'Text\n'), - (u"TextText", 'Text\n\nText\n'), - (u"", '----\n'), - (u"strong", "'''strong'''"), - (u"emphasis", "''emphasis''"), - (u"blockcode", "{{{\nblockcode\n}}}\n"), - (u"monospace", '`monospace`'), - (u'stroke', '--(stroke)--'), - (u'underline', '__underline__'), - (u'larger', '~+larger+~'), - (u'smaller', '~-smaller-~'), - (u'superscript', '^super^script'), - (u'subscript', ',,sub,,script'), + ('Текст', 'Текст\n'), + ('Text', 'Text\n'), + ("TextText", 'Text\n\nText\n'), + ("", '----\n'), + ("strong", "'''strong'''"), + ("emphasis", "''emphasis''"), + ("blockcode", "{{{\nblockcode\n}}}\n"), + ("monospace", '`monospace`'), + ('stroke', '--(stroke)--'), + ('underline', '__underline__'), + ('larger', '~+larger+~'), + ('smaller', '~-smaller-~'), + ('superscript', '^super^script'), + ('subscript', ',,sub,,script'), ] @pytest.mark.parametrize('input,output', data) @@ -65,26 +65,26 @@ def test_base(self, input, output): self.do(input, output) data = [ - (u'subsection of Some Page', + ('subsection of Some Page', '[[SomePage#subsection|subsection of Some Page]]'), - (u'{{attachment:samplegraphic.png}}', + ('{{attachment:samplegraphic.png}}', '[[SomePage|{{attachment:samplegraphic.png}}|target="_blank"]]'), - (u'{{attachment:samplegraphic.png}}', + ('{{attachment:samplegraphic.png}}', '[[SomePage?target=_blank|{{attachment:samplegraphic.png}}]]'), - (u'{{/samplegraphic.png}}', + ('{{/samplegraphic.png}}', '[[SomePage|{{/samplegraphic.png}}|target="_blank"]]'), - (u'link text', '[[../SisterPage|link text]]'), + ('link text', '[[../SisterPage|link text]]'), ( - u'{{attachment:samplegraphic.png}}', + '{{attachment:samplegraphic.png}}', '[[http://static.moinmo.in/logos/moinmoin.png|{{attachment:samplegraphic.png}}|class="aaa",target="_blank"]]'), - (u'MoinMoin Wiki', + ('MoinMoin Wiki', '[[http://moinmo.in/|MoinMoin Wiki|accesskey="1",class="green dotted"]]'), - (u'MoinMoin Wiki', + ('MoinMoin Wiki', '[[MoinMoin:MoinMoinWiki?action=diff&rev1=1&rev2=2|MoinMoin Wiki]]'), - (u'a pineapple', + ('a pineapple', '[[attachment:HelpOnImages/pineapple.jpg?do=get|a pineapple]]'), ( - u'attachment:filename.txt', '[[attachment:filename.txt]]') + 'attachment:filename.txt', '[[attachment:filename.txt]]') ] @pytest.mark.parametrize('input,output', data) @@ -92,17 +92,17 @@ def test_link(self, input, output): self.do(input, output) data = [ - (u"", '{{drawing:anywikitest.adraw}}'), - (u"", + ("", '{{drawing:anywikitest.adraw}}'), + ("", '{{http://static.moinmo.in/logos/moinmoin.png}}'), ( - u'alt text', + 'alt text', '{{http://static.moinmo.in/logos/moinmoin.png|alt text}}'), - (u'', '{{attachment:image.png}}'), - (u'alt text', + ('', '{{attachment:image.png}}'), + ('alt text', '{{attachment:image.png|alt text}}'), ( - u'alt text', + 'alt text', '{{attachment:image.png|alt text|class="left" height="150" width="100"}}'), ] @@ -112,19 +112,19 @@ def test_object(self, input, output): data = [ ( - u"A", + "A", " * A\n"), ( - u"A", + "A", " 1. A\n"), ( - u"A", + "A", " I. A\n"), ( - u"ABCDEF", + "ABCDEF", " * A\n * B\n 1. C\n 1. D\n I. E\n I. F\n"), ( - u"ABCD", + "ABCD", " A::\n :: B\n :: C\n :: D\n"), ] @@ -134,13 +134,13 @@ def test_list(self, input, output): data = [ ( - u"ABDC", + "ABDC", '||A||B||D||\n||C||\n'), ( - u"ABC123", - u"||'''A'''||'''B'''||'''C'''||\n||1||2||3||\n"), + "ABC123", + "||'''A'''||'''B'''||'''C'''||\n||1||2||3||\n"), ( - u"cell spanning 2 rowscell in the 2nd columncell in the 2nd column of the 2nd rowtesttest", + "cell spanning 2 rowscell in the 2nd columncell in the 2nd column of the 2nd rowtesttest", '||cell spanning 2 rows||cell in the 2nd column||\n||cell in the 2nd column of the 2nd row||\n||test||\n||test||\n'), ] @@ -149,14 +149,14 @@ def test_table(self, input, output): self.do(input, output) data = [ - (u"test", + ("test", "<>"), - (u"", "<>\n"), + ("", "<>\n"), ( - u"anchorname", + "anchorname", "<>\n"), ( - u",,12", + ",,12", "<>\n"), ] @@ -166,13 +166,13 @@ def test_macros(self, input, output): data = [ ( - u"This is a wiki parser.Its visibility gets toggled the same way.", + "This is a wiki parser.Its visibility gets toggled the same way.", "{{{#!wiki comment/dotted\nThis is a wiki parser.\n\nIts visibility gets toggled the same way.\n}}}\n"), ( - u"This is wiki markup in a div with css class=\"red solid\".", + "This is wiki markup in a div with css class=\"red solid\".", "{{{#!wiki red/solid\nThis is wiki markup in a \'\'\'div\'\'\' with __css__ `class=\"red solid\"`.\n}}}\n"), ( - u"st: erpar: arg para: arga... **bold** ...", + "st: erpar: arg para: arga... **bold** ...", "{{{#!creole(style=\"st: er\" class=\"par: arg para: arga\")\n... **bold** ...\n}}}\n"), ] @@ -181,19 +181,19 @@ def test_parser(self, input, output): self.do(input, output) data = [ - (u"ABCD", + ("ABCD", "A\n\nB\n\nC\n\nD\n"), ( - u"ABCD", + "ABCD", "||A<
>B<
>C<
>D||\n"), ( - u"ZABCD", + "ZABCD", "||Z||A<
>B<
>C<
>D||\n"), ( - u"ZABCD", + "ZABCD", "||Z||\n||A<
>B<
>C<
>D||\n"), ( - u"AAAAAA", + "AAAAAA", " * A<
>A\n * A<
>A<
>A\n * A\n"), ] @@ -202,12 +202,12 @@ def test_br(self, input, output): self.do(input, output) data = [ - (u"A", "A\n----\n"), - (u"A", + ("A", "A\n----\n"), + ("A", "A\n----\n"), - (u"A", + ("A", "A\n------\n"), - (u"A", + ("A", "A\n---------\n"), ] diff --git a/src/moin/converters/_tests/test_rst_in.py b/src/moin/converters/_tests/test_rst_in.py index 403f8443c..856f2c32e 100644 --- a/src/moin/converters/_tests/test_rst_in.py +++ b/src/moin/converters/_tests/test_rst_in.py @@ -14,7 +14,7 @@ from moin.converters.rst_in import Converter -class TestConverter(object): +class TestConverter: namespaces = { moin_page.namespace: '', xlink.namespace: 'xlink', @@ -28,25 +28,25 @@ def setup_class(self): self.conv = Converter() data = [ - (u'Text', + ('Text', '

Text

'), - (u'Text\nTest', + ('Text\nTest', '

Text\nTest

'), - (u'Text\n\nTest', + ('Text\n\nTest', '

Text

Test

'), - (u'H\\ :sub:`2`\\ O\n\nE = mc\\ :sup:`2`', + ('H\\ :sub:`2`\\ O\n\nE = mc\\ :sup:`2`', '

H2O

E = mc2

'), - (u'| Lend us a couple of bob till Thursday.', + ('| Lend us a couple of bob till Thursday.', 'Lend us a couple of bob till Thursday.'), - (u'**Text**', '

Text

'), - (u'*Text*', '

Text

'), - (u'``Text``', '

Text

'), - (u"`Text `_", - u'

Text

'), - (u'Text\n\n~~~~~\n\nTest', + ('**Text**', '

Text

'), + ('*Text*', '

Text

'), + ('``Text``', '

Text

'), + ("`Text `_", + '

Text

'), + ('Text\n\n~~~~~\n\nTest', '

Text

Test

'), - (u'.. comment', '
comment
'), - (u'..\n comment', '
comment
'), + ('.. comment', '
comment
'), + ('..\n comment', '
comment
'), ] @pytest.mark.parametrize('input,output', data) @@ -54,17 +54,17 @@ def test_base(self, input, output): self.do(input, output) data = [ - (u'1. a\n b\n c\n\n2. b\n\n d', '''

a + ('1. a\n b\n c\n\n2. b\n\n d', '''

a b c

b

d

'''), - (u'1. a\n2. b\n\nA. c\n\na. A\n\n 1. B\n\n 2. C\n\n', + ('1. a\n2. b\n\nA. c\n\na. A\n\n 1. B\n\n 2. C\n\n', '

a

b

c

A

B

C

'), - (u'* A\n\n - B\n\n + C\n\n - D\n\n* E', + ('* A\n\n - B\n\n + C\n\n - D\n\n* E', '

A

B

C

D

E

'), - (u'what\n def\n\nhow\n to', + ('what\n def\n\nhow\n to', 'what

def

how

to

'), # starting an ordered list with a value other than 1 generates an error - (u' 3. A\n #. B', + (' 3. A\n #. B', '

A

' '

B

' '

Enumerated list start value not ordinal-1: "3" (ordinal 3)

' @@ -76,9 +76,9 @@ def test_list(self, input, output): self.do(input, output) data = [ - (u'term 1\n definition 1', + ('term 1\n definition 1', 'term 1

definition 1

'), - (u'term 2 : classifier 1 : classifier 2\n definition 2', + ('term 2 : classifier 1 : classifier 2\n definition 2', 'term 2:classifier 1:classifier 2classifier 1classifier 2

definition 2

'), ] @@ -87,15 +87,15 @@ def test_definition_list(self, input, output): self.do(input, output) data = [ - (u'.. image:: images/biohazard.png', + ('.. image:: images/biohazard.png', ''), - (u""".. image:: images/biohazard.png + (""".. image:: images/biohazard.png :height: 100 :width: 200 :scale: 50 :alt: alternate text""", ''), - (u'abc |test| cba\n\n.. |test| image:: test.png', + ('abc |test| cba\n\n.. |test| image:: test.png', '

abc cba

'), ] @@ -106,20 +106,20 @@ def test_image(self, input, output): data = [ # from http://docutils.sourceforge.net/docs/user/rst/quickstart.html#sections; note first header is level 2 because same underlining was used for Chapter 2 Title ( - u'Chapter 1 Title\n===============\n\nSection 1.1 Title\n-----------------\n\nSubsection 1.1.1 Title\n~~~~~~~~~~~~~~~~~~~~~~\n\nSection 1.2 Title\n-----------------\n\nChapter 2 Title\n===============\n', + 'Chapter 1 Title\n===============\n\nSection 1.1 Title\n-----------------\n\nSubsection 1.1.1 Title\n~~~~~~~~~~~~~~~~~~~~~~\n\nSection 1.2 Title\n-----------------\n\nChapter 2 Title\n===============\n', 'Chapter 1 TitleSection 1.1 TitleSubsection 1.1.1 TitleSection 1.2 TitleChapter 2 Title'), # from http://docutils.sourceforge.net/docs/user/rst/quickstart.html#document-title-subtitle; note Subtitle and Section Title are level 2 ( - u'================\n Document Title\n================\n\n----------\n Subtitle\n----------\n\nSection Title\n=============', + '================\n Document Title\n================\n\n----------\n Subtitle\n----------\n\nSection Title\n=============', 'Document TitleSubtitleSection Title'), # similar to test above; note that H3 is level 2, H4 is level 3, ... - (u'==\nH1\n==\n\nH2\n==\n\nH3\n--\n\nH4\n**\n\nH5\n::\n\nH6\n++\n\n', + ('==\nH1\n==\n\nH2\n==\n\nH3\n--\n\nH4\n**\n\nH5\n::\n\nH6\n++\n\n', 'H1H2H3H4H5H6'), # adding a H2a heading using the H2 style underlining results in "normal" heading levels: H1 is a title, h2 and all other headings are sections - (u'==\nH1\n==\n\nH2\n==\n\nH3\n--\n\nH4\n**\n\nH5\n::\n\nH6\n++\n\nH2a\n===\n\n', + ('==\nH1\n==\n\nH2\n==\n\nH3\n--\n\nH4\n**\n\nH5\n::\n\nH6\n++\n\nH2a\n===\n\n', 'H1H2H3H4H5H6H2a'), # when a document starts with a paragraph, then the first heading is rendered as a section level 2 heading - (u'Paragraph\n\n==\nH1\n==\n\nH2\n==\n\nH3\n--\n\nH4\n**\n\nH5\n::\n\n', + ('Paragraph\n\n==\nH1\n==\n\nH2\n==\n\nH3\n--\n\nH4\n**\n\nH5\n::\n\n', '

Paragraph

H1H2H3H4H5
'), ] @@ -143,9 +143,9 @@ def test_headers(self, input, output): self.do(input, output) data = [ - (u'Abra [1]_\n\n.. [1] arba', + ('Abra [1]_\n\n.. [1] arba', '

Abra arba

'), - (u'Abra [#]_\n\n.. [#] arba', + ('Abra [#]_\n\n.. [#] arba', '

Abra arba

'), ] @@ -154,7 +154,7 @@ def test_footnote(self, input, output): self.do(input, output) data = [ - (u':Date: 2001-08-16\n:Version: 1\n:Authors: Joe Doe', + (':Date: 2001-08-16\n:Version: 1\n:Authors: Joe Doe', '2001-08-16Version:1Author:Joe Doe
'), ] @@ -163,18 +163,18 @@ def test_field_list(self, input, output): self.do(input, output) data = [ - (u'Abra test_ arba\n\n.. _test: http://python.org', + ('Abra test_ arba\n\n.. _test: http://python.org', '

Abra test arba

'), - (u'Abra test__ arba\n\n.. __: http://python.org', + ('Abra test__ arba\n\n.. __: http://python.org', '

Abra test arba

'), - (u'Abra\n\n.. _example:\n\nAbra example_ arba\n', + ('Abra\n\n.. _example:\n\nAbra example_ arba\n', '

Abra

Abra example arba

'), - (u'Abra example_ arba\n\n.. _example:\n\ntext', + ('Abra example_ arba\n\n.. _example:\n\ntext', '

Abra example arba

text

'), - (u'http://www.python.org/', + ('http://www.python.org/', '

http://www.python.org/

'), - (u'http:Home', '

http:Home

'), - (u'`Home `_', '

Home

'), + ('http:Home', '

http:Home

'), + ('`Home `_', '

Home

'), ] @pytest.mark.parametrize('input,output', data) @@ -182,16 +182,16 @@ def test_link(self, input, output): self.do(input, output) data = [ - (u'.. macro:: <>', ''), - (u'.. macro:: <>', ''), - (u'.. macro:: Macro(arg)', + ('.. macro:: <>', ''), + ('.. macro:: <>', ''), + ('.. macro:: Macro(arg)', 'arg'), - (u'test |a| test\n\n.. |a| macro:: <>', + ('test |a| test\n\n.. |a| macro:: <>', '

test test

'), - (u'.. contents::\n :depth: 1\n', ''), - (u'.. parser:: python test=test\n import test\n test.s = 11', + ('.. contents::\n :depth: 1\n', ''), + ('.. parser:: python test=test\n import test\n test.s = 11', 'testimport test\ntest.s = 11'), - (u'.. include:: RecentChanges', + ('.. include:: RecentChanges', ''), ] @@ -200,10 +200,10 @@ def test_directive(self, input, output): self.do(input, output) data = [ - (u"+-+-+-+\n|A|B|D|\n+-+-+ +\n|C | |\n+---+-+\n\n", + ("+-+-+-+\n|A|B|D|\n+-+-+ +\n|C | |\n+---+-+\n\n", '

A

B

D

C

'), ( - u"+-----+-----+-----+\n|**A**|**B**|**C**|\n+-----+-----+-----+\n|1 |2 |3 |\n+-----+-----+-----+\n\n", + "+-----+-----+-----+\n|**A**|**B**|**C**|\n+-----+-----+-----+\n|1 |2 |3 |\n+-----+-----+-----+\n\n", '

A

B

C

1

2

3

'), ("""+--------------------+-------------------------------------+ |cell spanning 2 rows|cell in the 2nd column | @@ -239,7 +239,7 @@ def test_table(self, input, output): self.do(input, output) data = [ - (u':Author: Test\n:Version: $Revision: 1.17 $\n:Copyright: c\n:Test: t', + (':Author: Test\n:Version: $Revision: 1.17 $\n:Copyright: c\n:Test: t', 'Author:TestVersion:1.17Copyright:cTest:

t

'), ] @@ -249,7 +249,7 @@ def test_docutils_feature(self, input, output): def serialize_strip(self, elem, **options): result = serialize(elem, namespaces=self.namespaces, **options) - return self.output_re.sub(u'', result) + return self.output_re.sub('', result) def do(self, input, output, args={}, skip=None): out = self.conv(input, 'text/x-rst;charset=utf-8', **args) diff --git a/src/moin/converters/_tests/test_rst_in_out.py b/src/moin/converters/_tests/test_rst_in_out.py index ccf5e90ef..cb8ac24ef 100644 --- a/src/moin/converters/_tests/test_rst_in_out.py +++ b/src/moin/converters/_tests/test_rst_in_out.py @@ -35,7 +35,7 @@ from moin.converters.rst_out import Converter as conv_out -class TestConverter(object): +class TestConverter: input_namespaces = 'xmlns="{0}" xmlns:page="{1}" xmlns:xlink="{2}" xmlns:xinclude="{3}" xmlns:html="{4}"'.format( moin_page.namespace, moin_page.namespace, xlink.namespace, xinclude.namespace, html.namespace) @@ -55,13 +55,13 @@ def setup_class(self): data = [ # output is not identical to input, but HTML out display is the same - (u'=====\nTitle\n=====\n\nSubTitle\n========\n\nSection\n-------\n', + ('=====\nTitle\n=====\n\nSubTitle\n========\n\nSection\n-------\n', '\n=====\nTitle\n=====\n\nSubTitle\n========\n\nSection\n=======\n'), - (u'para\n\n=======\nSection\n=======\n\nSubsection\n==========\n\nSubsubection\n------------\n', + ('para\n\n=======\nSection\n=======\n\nSubsection\n==========\n\nSubsubection\n------------\n', 'para\n\nSection\n=======\n\nSubsection\n----------\n\nSubsubection\n************\n'), # output identical to input - (u'\n==\nH1\n==\n\nH2\n==\n\nH3\n--\n\nH4\n**\n\nH5\n::\n\nH6\n++\n\nH2a\n===\n', - u'\n==\nH1\n==\n\nH2\n==\n\nH3\n--\n\nH4\n**\n\nH5\n::\n\nH6\n++\n\nH2a\n===\n'), + ('\n==\nH1\n==\n\nH2\n==\n\nH3\n--\n\nH4\n**\n\nH5\n::\n\nH6\n++\n\nH2a\n===\n', + '\n==\nH1\n==\n\nH2\n==\n\nH3\n--\n\nH4\n**\n\nH5\n::\n\nH6\n++\n\nH2a\n===\n'), ] @pytest.mark.parametrize('input,output', data) @@ -69,19 +69,19 @@ def test_headers(self, input, output): self.do(input, output) data = [ - (u'Text', 'Text\n'), - (u"Text\n\nText\n", 'Text\n\nText\n'), - (u"**strong**\n", "**strong**\n"), - (u"*emphasis*\n", "*emphasis*\n"), + ('Text', 'Text\n'), + ("Text\n\nText\n", 'Text\n\nText\n'), + ("**strong**\n", "**strong**\n"), + ("*emphasis*\n", "*emphasis*\n"), # extraneous x required below to prevent IndexError, side effect of serializer - (u"{{{{{x\nblockcode\n}}}}}\n", "{{{{{x\nblockcode\n}}}}}\n"), - (u"--(stroke)--\n", '--(stroke)--\n'), - (u"__underline__\n", '__underline__\n'), - (u"~+larger+~\n", '~+larger+~\n'), - (u"~-smaller-~\n", '~-smaller-~\n'), - (u"^super^script\n", '^super^script\n'), - (u",,sub,,script\n", ',,sub,,script\n'), - (u"#ANY any", "#ANY any\n"), + ("{{{{{x\nblockcode\n}}}}}\n", "{{{{{x\nblockcode\n}}}}}\n"), + ("--(stroke)--\n", '--(stroke)--\n'), + ("__underline__\n", '__underline__\n'), + ("~+larger+~\n", '~+larger+~\n'), + ("~-smaller-~\n", '~-smaller-~\n'), + ("^super^script\n", '^super^script\n'), + (",,sub,,script\n", ',,sub,,script\n'), + ("#ANY any", "#ANY any\n"), # line blocks ('\n| Lend us a couple of bob till Thursday.\n', '\n| Lend us a couple of bob till Thursday.\n'), ] @@ -91,11 +91,11 @@ def test_base(self, input, output): self.do(input, output) data = [ - (u".. This is a comment", u"\n..\n This is a comment\n"), - (u"..\n This is a comment", u"\n..\n This is a comment\n"), - (u"..\n [and] this!", u"\n..\n [and] this!\n"), - (u"..\n this:: too!", u"\n..\n this:: too!\n"), - (u"..\n |even| this:: !", u"\n..\n |even| this:: !\n"), + (".. This is a comment", "\n..\n This is a comment\n"), + ("..\n This is a comment", "\n..\n This is a comment\n"), + ("..\n [and] this!", "\n..\n [and] this!\n"), + ("..\n this:: too!", "\n..\n this:: too!\n"), + ("..\n |even| this:: !", "\n..\n |even| this:: !\n"), ] @pytest.mark.parametrize('input,output', data) @@ -103,13 +103,13 @@ def test_comments(self, input, output): self.do(input, output) data = [ - (u".. macro:: <>", '\n\n.. contents::\n\n'), - (u".. contents::", '\n\n.. contents::\n\n'), - (u".. macro:: <>", '\n.. include:: MyPage\n'), - (u".. include:: MyPage", '\n.. include:: MyPage\n'), - (u".. macro:: <>", '\n.. macro:: <>\n'), - (u".. macro:: <>", '\n.. macro:: <>\n'), - (u".. macro:: <>", '\n.. macro:: <>\n'), + (".. macro:: <>", '\n\n.. contents::\n\n'), + (".. contents::", '\n\n.. contents::\n\n'), + (".. macro:: <>", '\n.. include:: MyPage\n'), + (".. include:: MyPage", '\n.. include:: MyPage\n'), + (".. macro:: <>", '\n.. macro:: <>\n'), + (".. macro:: <>", '\n.. macro:: <>\n'), + (".. macro:: <>", '\n.. macro:: <>\n'), ] @pytest.mark.parametrize('input,output', data) @@ -119,29 +119,29 @@ def test_macros(self, input, output): data = [ # examples taken from http://docutils.sourceforge.net/docs/user/rst/quickref.html#explicit-markup # output is not identical to input, but HTML out display is the same - (u'External hyperlinks, like Python_.\n\n.. _Python: http://www.python.org/', - u'External hyperlinks, like `Python`_.\n\n\n.. _Python: http://www.python.org/\n\n'), - (u'External hyperlinks, like `Python `_.', - u'External hyperlinks, like `Python`_.\n\n\n.. _Python: http://www.python.org/\n\n'), - (u'Internal crossreferences, like example_.\n\n.. _example:\n\nThis is an example crossreference target.', - u'Internal crossreferences, like `example`_.\n\n.. _example:\n\nThis is an example crossreference target.\n'), - (u'Python_ is `my favourite programming language`__.\n\n.. _Python: http://www.python.org/\n\n__ Python_', - u'`Python`_ is `my favourite programming language`_.\n\n\n.. _Python: http://www.python.org/\n\n.. _my favourite programming language: http://www.python.org/\n\n'), - (u'Titles are targets, too \n======================= \nImplict references, like `Titles are targets, too`_.', - u'\n=======================\nTitles are targets, too\n=======================\n\nImplict references, like `Titles are targets, too`_.\n'), + ('External hyperlinks, like Python_.\n\n.. _Python: http://www.python.org/', + 'External hyperlinks, like `Python`_.\n\n\n.. _Python: http://www.python.org/\n\n'), + ('External hyperlinks, like `Python `_.', + 'External hyperlinks, like `Python`_.\n\n\n.. _Python: http://www.python.org/\n\n'), + ('Internal crossreferences, like example_.\n\n.. _example:\n\nThis is an example crossreference target.', + 'Internal crossreferences, like `example`_.\n\n.. _example:\n\nThis is an example crossreference target.\n'), + ('Python_ is `my favourite programming language`__.\n\n.. _Python: http://www.python.org/\n\n__ Python_', + '`Python`_ is `my favourite programming language`_.\n\n\n.. _Python: http://www.python.org/\n\n.. _my favourite programming language: http://www.python.org/\n\n'), + ('Titles are targets, too \n======================= \nImplict references, like `Titles are targets, too`_.', + '\n=======================\nTitles are targets, too\n=======================\n\nImplict references, like `Titles are targets, too`_.\n'), # output is same as input - (u'External hyperlinks, like `Python`_.\n\n\n.. _Python: http://www.python.org/\n\n', - u'External hyperlinks, like `Python`_.\n\n\n.. _Python: http://www.python.org/\n\n'), - (u'External hyperlinks, like `Python`_.\n\n\n.. _Python: http://www.python.org/\n\n', - u'External hyperlinks, like `Python`_.\n\n\n.. _Python: http://www.python.org/\n\n'), - (u'Internal crossreferences, like `example`_.\n\n.. _example:\n\nThis is an example crossreference target.\n', - u'Internal crossreferences, like `example`_.\n\n.. _example:\n\nThis is an example crossreference target.\n'), + ('External hyperlinks, like `Python`_.\n\n\n.. _Python: http://www.python.org/\n\n', + 'External hyperlinks, like `Python`_.\n\n\n.. _Python: http://www.python.org/\n\n'), + ('External hyperlinks, like `Python`_.\n\n\n.. _Python: http://www.python.org/\n\n', + 'External hyperlinks, like `Python`_.\n\n\n.. _Python: http://www.python.org/\n\n'), + ('Internal crossreferences, like `example`_.\n\n.. _example:\n\nThis is an example crossreference target.\n', + 'Internal crossreferences, like `example`_.\n\n.. _example:\n\nThis is an example crossreference target.\n'), ( - u'`Python`_ is `my favourite programming language`_.\n\n\n.. _Python: http://www.python.org/\n\n.. _my favourite programming language: http://www.python.org/\n\n', - u'`Python`_ is `my favourite programming language`_.\n\n\n.. _Python: http://www.python.org/\n\n.. _my favourite programming language: http://www.python.org/\n\n'), + '`Python`_ is `my favourite programming language`_.\n\n\n.. _Python: http://www.python.org/\n\n.. _my favourite programming language: http://www.python.org/\n\n', + '`Python`_ is `my favourite programming language`_.\n\n\n.. _Python: http://www.python.org/\n\n.. _my favourite programming language: http://www.python.org/\n\n'), ( - u'\n=======================\nTitles are targets, too\n=======================\n\nImplict references, like `Titles are targets, too`_.\n', - u'\n=======================\nTitles are targets, too\n=======================\n\nImplict references, like `Titles are targets, too`_.\n'), + '\n=======================\nTitles are targets, too\n=======================\n\nImplict references, like `Titles are targets, too`_.\n', + '\n=======================\nTitles are targets, too\n=======================\n\nImplict references, like `Titles are targets, too`_.\n'), ] @pytest.mark.parametrize('input,output', data) @@ -149,11 +149,11 @@ def test_link(self, input, output): self.do(input, output) data = [ - (u"- a\n- b\n\n - aa\n - ab\n", + ("- a\n- b\n\n - aa\n - ab\n", "\n* a\n* b\n\n * aa\n * ab\n"), - (u"1. a\n#. b\n\n (A) aa\n (#) ab\n\n", + ("1. a\n#. b\n\n (A) aa\n (#) ab\n\n", "\n1. a\n#. b\n\n A. aa\n #. ab\n"), - (u"1. a\n#. b\n\n (A) aa\n (#) ab\n", + ("1. a\n#. b\n\n (A) aa\n (#) ab\n", "\n1. a\n#. b\n\n A. aa\n #. ab\n"), ] @@ -163,8 +163,8 @@ def test_list(self, input, output): data = [ # simple tables are converted to grid tables - (u'== == ==\na b c \n== == ==\n1 2 3\n== == ==', u'\n+-+-+-+\n|a|b|c|\n+=+=+=+\n|1|2|3|\n+-+-+-+\n\n'), - (u'\n+-+-+-+\n|a|b|c|\n+=+=+=+\n|1|2|3|\n+-+-+-+\n\n', u'\n+-+-+-+\n|a|b|c|\n+=+=+=+\n|1|2|3|\n+-+-+-+\n\n'), + ('== == ==\na b c \n== == ==\n1 2 3\n== == ==', '\n+-+-+-+\n|a|b|c|\n+=+=+=+\n|1|2|3|\n+-+-+-+\n\n'), + ('\n+-+-+-+\n|a|b|c|\n+=+=+=+\n|1|2|3|\n+-+-+-+\n\n', '\n+-+-+-+\n|a|b|c|\n+=+=+=+\n|1|2|3|\n+-+-+-+\n\n'), ] @pytest.mark.parametrize('input,output', data) @@ -173,10 +173,10 @@ def test_table(self, input, output): data = [ ( - u'.. image:: png\n :scale: 50\n :alt: alternate text png\n :align: center\n :height: 100\n :width: 200\n', + '.. image:: png\n :scale: 50\n :alt: alternate text png\n :align: center\n :height: 100\n :width: 200\n', '\n.. image:: png\n :alt: alternate text png\n :align: center\n :height: 50\n :width: 100\n'), ( - u'.. figure:: png\n :alt: alternate text png\n :height: 100\n :width: 200\n :scale: 50\n\n Moin Logo\n\n This logo replaced the "MoinMoin Man"\n logo long ago.\n', + '.. figure:: png\n :alt: alternate text png\n :height: 100\n :width: 200\n :scale: 50\n\n Moin Logo\n\n This logo replaced the "MoinMoin Man"\n logo long ago.\n', '\n.. figure:: png\n :alt: alternate text png\n :height: 50\n :width: 100\n\n Moin Logo\n\n This logo replaced the "MoinMoin Man"\n logo long ago.\n'), ] @@ -193,7 +193,7 @@ def handle_output(self, elem, **options): def serialize_strip(self, elem, **options): result = serialize(elem, namespaces=self.namespaces, **options) - return self.output_re.sub(u'', result) + return self.output_re.sub('', result) def do(self, input, output, args={}, skip=None): if skip: diff --git a/src/moin/converters/_tests/test_rst_out.py b/src/moin/converters/_tests/test_rst_out.py index 49c80fb55..bbe5e674a 100644 --- a/src/moin/converters/_tests/test_rst_out.py +++ b/src/moin/converters/_tests/test_rst_out.py @@ -15,7 +15,7 @@ from moin.converters.rst_out import Converter -class Base(object): +class Base: input_namespaces = ns_all = 'xmlns="{0}" xmlns:page="{1}" xmlns:xlink="{2}"'.format(moin_page.namespace, moin_page.namespace, xlink.namespace) output_namespaces = { moin_page.namespace: 'page' @@ -41,24 +41,24 @@ def setup_class(self): self.conv = Converter() data = [ - (u'Text', 'Text\n'), - (u"TextText", 'Text\n\nText\n'), - (u"", '\n\n----\n\n'), - (u"strong", "**strong**"), - (u"emphasis", "*emphasis*"), - (u"blockcode", "\n::\n\n blockcode\n\n"), - (u"monospace", '``monospace``'), + ('Text', 'Text\n'), + ("TextText", 'Text\n\nText\n'), + ("", '\n\n----\n\n'), + ("strong", "**strong**"), + ("emphasis", "*emphasis*"), + ("blockcode", "\n::\n\n blockcode\n\n"), + ("monospace", '``monospace``'), ( - u"""h1h2h3h4h5h6""", - u"""\n==\nh1\n==\n\nh2\n==\n\nh3\n--\n\nh4\n**\n\nh5\n::\n\nh6\n++\n"""), + """h1h2h3h4h5h6""", + """\n==\nh1\n==\n\nh2\n==\n\nh3\n--\n\nh4\n**\n\nh5\n::\n\nh6\n++\n"""), ( - u'H2OE = mc2', - u'H\\ :sub:`2`\\ O\n\nE = mc\\ :sup:`2`\\ \n'), - (u'H2O', 'H2O\n'), - (u'comment', + 'H2OE = mc2', + 'H\\ :sub:`2`\\ O\n\nE = mc\\ :sup:`2`\\ \n'), + ('H2O', 'H2O\n'), + ('comment', '\n..\n comment\n'), ( - u'Lend us a couple of bob till Thursday.', + 'Lend us a couple of bob till Thursday.', '\n| Lend us a couple of bob till Thursday.\n'), ] @@ -68,19 +68,19 @@ def test_base(self, input, output): data = [ ( - u"A", + "A", "\n* A\n"), ( - u"A", + "A", "\n1. A\n"), ( - u"A", + "A", "\nI. A\n"), ( - u"ABCDEF", + "ABCDEF", "\n* A\n* B\n\n 1. C\n #. D\n\n I. E\n #. F\n"), ( - u"ABCD", + "ABCD", "A\n B\n C\n D\n"), ] @@ -90,13 +90,13 @@ def test_list(self, input, output): data = [ ( - u"ABDC", + "ABDC", "\n+-+-+-+\n|A|B|D|\n+-+-+ +\n|C | |\n+---+-+\n\n"), ( - u"ABC123", - u"\n+-----+-----+-----+\n|**A**|**B**|**C**|\n+-----+-----+-----+\n|1 |2 |3 |\n+-----+-----+-----+\n\n"), + "ABC123", + "\n+-----+-----+-----+\n|**A**|**B**|**C**|\n+-----+-----+-----+\n|1 |2 |3 |\n+-----+-----+-----+\n\n"), ( - u'AAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBcell spanning 2 rowscell in the 2nd columncell in the 2nd column of the 2nd rowtesttest', """\n+--------------------+-------------------------------------+ + 'AAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBcell spanning 2 rowscell in the 2nd columncell in the 2nd column of the 2nd rowtesttest', """\n+--------------------+-------------------------------------+ |AAAAAAAAAAAAAAAAAA |BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB | +====================+=====================================+ |cell spanning 2 rows|cell in the 2nd column | @@ -110,8 +110,8 @@ def test_list(self, input, output): """), ( - u"AC1", - u"\n+------+-----+\n|**A** |**C**|\n+------+-----+\n|1 | |\n+------+-----+\n\n"), + "AC1", + "\n+------+-----+\n|**A** |**C**|\n+------+-----+\n|1 | |\n+------+-----+\n\n"), ] @pytest.mark.parametrize('input,output', data) @@ -119,19 +119,19 @@ def test_table(self, input, output): self.do(input, output) data = [ - (u"ABCD", + ("ABCD", "A\n\nB\n\nC\n\nD\n"), ( - u"ABCD", + "ABCD", "\n+-+\n|A|\n| |\n|B|\n| |\n|C|\n| |\n|D|\n+-+\n\n"), ( - u"ZABCD", + "ZABCD", "\n+-+-+\n|Z|A|\n| | |\n| |B|\n| | |\n| |C|\n| | |\n| |D|\n+-+-+\n\n"), ( - u"ZABCD", + "ZABCD", "\n+-+\n|Z|\n+-+\n|A|\n| |\n|B|\n| |\n|C|\n| |\n|D|\n+-+\n\n"), ( - u"AAAAAAAA", + "AAAAAAAA", "A\n* A\n\n A\n* A\n\n A\n\n A\n* A\nA") ] @@ -141,7 +141,7 @@ def test_p(self, input, output): data = [ ( - u'Author:TestVersion:1.17Copyright:cTest:

t

', """\n+--------------+----+ + 'Author:TestVersion:1.17Copyright:cTest:

t

', """\n+--------------+----+ |**Author:** |Test| +--------------+----+ |**Version:** |1.17| @@ -160,11 +160,11 @@ def test_docutils_features(self, input, output): data = [ ( - u'Abra arba', - u'Abra [#]_ \n\n\n.. [#] arba\n\n'), + 'Abra arba', + 'Abra [#]_ \n\n\n.. [#] arba\n\n'), ( - u'Abra arbaAbra arbaAbra arbaarba', - u'Abra [#]_ \n\nAbra [#]_ \n\nAbra [#]_ [#]_ \n\n\n.. [#] arba\n\n.. [#] arba\n\n.. [#] arba\n\n.. [#] arba\n\n'), + 'Abra arbaAbra arbaAbra arbaarba', + 'Abra [#]_ \n\nAbra [#]_ \n\nAbra [#]_ [#]_ \n\n\n.. [#] arba\n\n.. [#] arba\n\n.. [#] arba\n\n.. [#] arba\n\n'), ] @pytest.mark.parametrize('input,output', data) @@ -173,14 +173,14 @@ def test_note(self, input, output): data = [ ( - u'Abra test arba', - u'Abra `test`_ arba\n\n\n.. _test: http://python.org\n\n'), + 'Abra test arba', + 'Abra `test`_ arba\n\n\n.. _test: http://python.org\n\n'), ( - u'Abra test arba test', - u'Abra `test`_ arba `test`_\n\n\n.. _test: http://python.org\n\n.. _test: http://python.ru\n\n'), + 'Abra test arba test', + 'Abra `test`_ arba `test`_\n\n\n.. _test: http://python.org\n\n.. _test: http://python.ru\n\n'), ( - u'Abra test arba test rbaa test', - u'Abra `test`_ arba `test`_ rbaa `test`_\n\n\n.. _test: http://python.org\n\n.. _test: http://python.ru\n\n.. _test: http://python.su\n\n'), + 'Abra test arba test rbaa test', + 'Abra `test`_ arba `test`_ rbaa `test`_\n\n\n.. _test: http://python.org\n\n.. _test: http://python.ru\n\n.. _test: http://python.su\n\n'), ] @pytest.mark.parametrize('input,output', data) @@ -189,7 +189,7 @@ def test_link(self, input, output): data = [ ( - u"AAuthor:TestVersion:1.17Copyright:cTest:t", """\n* A + "AAuthor:TestVersion:1.17Copyright:cTest:t", """\n* A +--------------+----+ |**Author:** |Test| @@ -202,10 +202,10 @@ def test_link(self, input, output): +--------------+----+ """), ( - u"A test ", - u"\n* A\n::\n\n test \n\n"), + "A test ", + "\n* A\n::\n\n test \n\n"), ( - u'AABB', + 'AABB', '\n+-----+-----+\n|**A**|**B**|\n| | |\n|**A**|**B**|\n+-----+-----+\n\n'), ] @@ -214,18 +214,18 @@ def test_mixed(self, input, output): self.do(input, output) data = [ - (u"{{drawing:anywikitest.adraw", + ("{{drawing:anywikitest.adraw", '\n\n.. image:: drawing:anywikitest.adraw\n\n'), - (u"", + ("", '\n\n.. image:: http://static.moinmo.in/logos/moinmoin.png\n\n'), ( - u'alt text', - u'|alt text|\n\n.. |alt text| image:: http://static.moinmo.in/logos/moinmoin.png\n\n'), - (u'', '\n\n.. image:: attachment:image.png\n\n'), - (u'alt text', + 'alt text', + '|alt text|\n\n.. |alt text| image:: http://static.moinmo.in/logos/moinmoin.png\n\n'), + ('', '\n\n.. image:: attachment:image.png\n\n'), + ('alt text', '|alt text|\n\n.. |alt text| image:: attachment:image.png\n\n'), ( - u'', + '', '|alt text|\n\n.. |alt text| image:: attachment:image.png\n :width: 100\n :height: 150\n :align: left\n\n'), ] @@ -234,14 +234,14 @@ def test_object(self, input, output): self.do(input, output) data = [ - (u"", + ("", "\n\n.. contents::\n :depth: 2\n\n"), ( - u"anchorname", + "anchorname", "\n.. macro:: <>\n"), ( - u",,12", - u'\n.. macro:: <>\n'), + ",,12", + '\n.. macro:: <>\n'), ] @pytest.mark.parametrize('input,output', data) @@ -250,8 +250,8 @@ def test_macros(self, input, output): data = [ ( - u"st: erpar: arg para: arga... **bold** ...", - u"""\n\n.. parser:creole style="st: er" class="par: arg para: arga"\n ... **bold** ..."""), + "st: erpar: arg para: arga... **bold** ...", + """\n\n.. parser:creole style="st: er" class="par: arg para: arga"\n ... **bold** ..."""), ] @pytest.mark.parametrize('input,output', data) diff --git a/src/moin/converters/_tests/test_smiley.py b/src/moin/converters/_tests/test_smiley.py index 86dcaa7ac..f0b6c9bee 100644 --- a/src/moin/converters/_tests/test_smiley.py +++ b/src/moin/converters/_tests/test_smiley.py @@ -23,7 +23,7 @@ def serialize_strip(elem, **options): result = serialize(elem, namespaces=output_namespaces, **options) - return output_re.sub(u'', result) + return output_re.sub('', result) @pytest.mark.parametrize('input,query', [ @@ -56,12 +56,12 @@ def serialize_strip(elem, **options): ]) def test_smiley_convert(input, query): conv = Converter() - print 'input:', input + print('input:', input) out_elem = conv(ET.XML(input)) after_conversion = serialize_strip(out_elem) - print 'output:', after_conversion - print 'query:', query + print('output:', after_conversion) + print('query:', query) tree = etree.fromstring(after_conversion) result = tree.xpath(query) - print 'query result:', result + print('query result:', result) assert result diff --git a/src/moin/converters/_util.py b/src/moin/converters/_util.py index dec5d9152..5808a5fdb 100644 --- a/src/moin/converters/_util.py +++ b/src/moin/converters/_util.py @@ -5,9 +5,6 @@ MoinMoin - converter utilities """ - -from __future__ import absolute_import, division - try: from flask import g as flaskg except ImportError: @@ -32,22 +29,22 @@ def decode_data(data, contenttype=None): supported types for data: - rev object + - bytes - str - - unicode - file-like objects and str need to be either utf-8 (or ascii, which is a subset of utf-8) + file-like objects and bytes need to be either utf-8 (or ascii, which is a subset of utf-8) encoded or contenttype (including a charset parameter) needs to be given. """ - if not isinstance(data, (str, unicode)): + if not isinstance(data, (bytes, str)): data = data.data.read() - if isinstance(data, str): + if isinstance(data, bytes): coding = 'utf-8' if contenttype is not None: ct = Type(contenttype) coding = ct.parameters.get('charset', coding) data = data.decode(coding) - if not isinstance(data, unicode): - raise TypeError("data must be rev or str (requires contenttype with charset) or unicode, " + if not isinstance(data, str): + raise TypeError("data must be rev or bytes (requires contenttype with charset) or str, " "but we got {0!r}".format(data)) return data @@ -56,12 +53,12 @@ def normalize_split_text(text): """ normalize line endings, split text into a list of lines """ - text = text.replace(u'\r\n', u'\n') - lines = text.split(u'\n') + text = text.replace('\r\n', '\n') + lines = text.split('\n') return lines -class _Iter(object): +class _Iter: """ Iterator with push back support @@ -81,7 +78,7 @@ def __init__(self, parent, startno=0): def __iter__(self): return self - def next(self): + def __next__(self): if self.__finished: raise StopIteration @@ -90,7 +87,7 @@ def next(self): return self.__prepend.pop(0) try: - return self.__parent.next() + return next(self.__parent) except StopIteration: self.__finished = True raise @@ -100,8 +97,8 @@ def push(self, item): self.lineno -= 1 -class _Stack(object): - class Item(object): +class _Stack: + class Item: def __init__(self, elem): self.elem = elem if elem.tag.uri == moin_page: @@ -164,4 +161,4 @@ def top_check(self, *names, **kwargs): Check if the top of the stack name and attrib matches the parameters. """ attrib = kwargs.get('attrib', {}) - return self._list[-1].name in names and set(attrib.items()).issubset(self._list[-1].elem.attrib.items()) + return self._list[-1].name in names and set(attrib.items()).issubset(set(self._list[-1].elem.attrib.items())) diff --git a/src/moin/converters/_wiki_macro.py b/src/moin/converters/_wiki_macro.py index 987d6f0eb..040795ec4 100644 --- a/src/moin/converters/_wiki_macro.py +++ b/src/moin/converters/_wiki_macro.py @@ -21,7 +21,7 @@ logging = log.getLogger(__name__) -class ConverterMacro(object): +class ConverterMacro: def _BR_repl(self, args, text, context_block): if context_block: return @@ -88,34 +88,34 @@ def error_message(msg): xpointer_moin = [] def add_moin_xpointer(function, args): - args = unicode(args).replace('^', '^^').replace('(', '^(').replace(')', '^)') - xpointer_moin.append(function + u'(' + args + u')') + args = str(args).replace('^', '^^').replace('(', '^(').replace(')', '^)') + xpointer_moin.append(function + '(' + args + ')') moin_args = [] - if pagename.startswith(u'^'): - add_moin_xpointer(u'pages', pagename) + if pagename.startswith('^'): + add_moin_xpointer('pages', pagename) if sort: - add_moin_xpointer(u'sort', sort) + add_moin_xpointer('sort', sort) if items: - add_moin_xpointer(u'items', items) + add_moin_xpointer('items', items) if skipitems: - add_moin_xpointer(u'skipitems', skipitems) + add_moin_xpointer('skipitems', skipitems) else: - link = iri.Iri(scheme=u'wiki.local', path=pagename) + link = iri.Iri(scheme='wiki.local', path=pagename) attrib[xinclude.href] = link if heading is not None: - add_moin_xpointer(u'heading', heading) + add_moin_xpointer('heading', heading) if level: - add_moin_xpointer(u'level', str(level)) + add_moin_xpointer('level', str(level)) if titlesonly: - add_moin_xpointer(u'titlesonly', u'') + add_moin_xpointer('titlesonly', '') if editlink: - add_moin_xpointer(u'editlink', u'') + add_moin_xpointer('editlink', '') if xpointer_moin: - xpointer.append(u'page:include({0})'.format(u' '.join(xpointer_moin))) + xpointer.append('page:include({0})'.format(' '.join(xpointer_moin))) if xpointer: # TODO: Namespace? diff --git a/src/moin/converters/archive_in.py b/src/moin/converters/archive_in.py index 382fe3e45..e4b7d9adb 100644 --- a/src/moin/converters/archive_in.py +++ b/src/moin/converters/archive_in.py @@ -40,18 +40,17 @@ def _factory(cls, input, output, **kw): return cls() def process_name(self, member_name): - name = unicode(member_name, 'utf-8') attrib = { xlink.href: Iri(scheme='wiki', authority='', path='/' + self.item_name, - query=u'do=get&member={0}'.format(name)), + query='do=get&member={0}'.format(member_name)), } - return moin_page.a(attrib=attrib, children=[name, ]) + return moin_page.a(attrib=attrib, children=[member_name, ]) def process_datetime(self, dt): - return unicode(dt.isoformat(' ')) + return str(dt.isoformat(' ')) def process_size(self, size): - return unicode(size) + return str(size) def __call__(self, rev, contenttype=None, arguments=None): self.item_name = rev.item.name diff --git a/src/moin/converters/audio_video_in.py b/src/moin/converters/audio_video_in.py index 3ede2e0f6..01b77ebbf 100644 --- a/src/moin/converters/audio_video_in.py +++ b/src/moin/converters/audio_video_in.py @@ -18,7 +18,7 @@ from moin.utils.mime import Type, type_moin_document -class Converter(object): +class Converter: """ Convert audio/video to the corresponding in the DOM Tree """ @@ -32,11 +32,11 @@ def __init__(self, input_type): def __call__(self, rev, contenttype=None, arguments=None): item_name = rev.item.name attrib = { - moin_page.type_: unicode(self.input_type), + moin_page.type_: str(self.input_type), xlink.href: Iri(scheme='wiki', authority='', path='/' + item_name, query='do=get&rev={0}'.format(rev.revid)), } - obj = moin_page.object_(attrib=attrib, children=[u'Your Browser does not support HTML5 audio/video element.', ]) + obj = moin_page.object_(attrib=attrib, children=['Your Browser does not support HTML5 audio/video element.', ]) body = moin_page.body(children=(obj, )) return moin_page.page(children=(body, )) diff --git a/src/moin/converters/creole_in.py b/src/moin/converters/creole_in.py index 812102bae..3d0ae7bc1 100644 --- a/src/moin/converters/creole_in.py +++ b/src/moin/converters/creole_in.py @@ -22,9 +22,6 @@ for the rest of the paragraph. """ - -from __future__ import absolute_import, division - import re from moin.constants.misc import URI_SCHEMES @@ -177,7 +174,7 @@ def block_nowiki_repl(self, iter_content, stack, nowiki): stack.clear() try: - firstline = iter_content.next() + firstline = next(iter_content) except StopIteration: stack.push(moin_page.blockcode()) return @@ -217,7 +214,7 @@ def block_nowiki_repl(self, iter_content, stack, nowiki): block_separator = r'(?P ^ \s* ---- \s* $ )' - def block_separator_repl(self, _iter_content, stack, separator, hr_class=u'moin-hr3'): + def block_separator_repl(self, _iter_content, stack, separator, hr_class='moin-hr3'): stack.clear() stack.top_append(moin_page.separator(attrib={moin_page.class_: hr_class})) @@ -633,13 +630,13 @@ def _apply(self, match, prefix, *args): """ Call the _repl method for the last matched group with the given prefix. """ - data = dict(((k, v) for k, v in match.groupdict().iteritems() if v is not None)) + data = dict(((k, v) for k, v in match.groupdict().items() if v is not None)) getattr(self, '{0}_{1}_repl'.format(prefix, match.lastgroup))(*args, **data) def parse_block(self, iter_content, arguments): attrib = {} if arguments: - for key, value in arguments.keyword.iteritems(): + for key, value in arguments.keyword.items(): if key in ('style', ): attrib[moin_page(key)] = value diff --git a/src/moin/converters/docbook_in.py b/src/moin/converters/docbook_in.py index 443a09f15..22ec9075c 100644 --- a/src/moin/converters/docbook_in.py +++ b/src/moin/converters/docbook_in.py @@ -13,9 +13,6 @@ - ulink """ - -from __future__ import absolute_import, division - import re from emeraldtree import ElementTree as ET @@ -66,7 +63,7 @@ def _start_list(self, tag, attrib_in): return elem -class Converter(object): +class Converter: """ Converter application/docbook+xml -> x.moin.document """ @@ -77,104 +74,105 @@ class Converter(object): # DocBook elements which are completely ignored by our converter # We even do not process children of these elements - ignored_tags = set([ # Info elements - 'abstract', 'artpagenums', 'annotation', - 'artpagenums', 'author', 'authorgroup', - 'authorinitials', 'bibliocoverage', 'biblioid', - 'bibliomisc', 'bibliomset', 'bibliorelation', - 'biblioset', 'bibliosource', 'collab', 'confdates', - 'confgroup', 'confnum', 'confsponsor', 'conftitle', - 'contractnum', 'contractsponsor', 'copyright', - 'contrib', 'cover', 'edition', 'editor', - 'extendedlink', 'issuenum', 'itermset', 'keyword', - 'keywordset', 'legalnotice', 'org', 'orgname', - 'orgdiv', 'otheraddr', 'othercredit', 'pagenums', - 'personblurb', 'printhistory', 'productname', - 'productnumber', 'pubdate', 'publisher', - 'publishername', 'releaseinfo', 'revdescription', - 'revhistory', 'revision', 'revnumber', 'revremark', - 'seriesvolnums', 'subjectset', 'volumenum', - # Other bibliography elements - 'bibliodiv', 'biblioentry', 'bibliography', - 'bibliolist', 'bibliomixed', 'biblioref', - 'bibliorelation', 'citation', 'citerefentry', - 'citetitle', - # Callout elements - 'callout', 'calloutlist', 'area', 'areaset', - 'areaspec', 'co', 'imageobjectco', - # Class information - 'classname', 'classsynopsis', 'classsynopsisinfo', - 'constructorsynopsis', 'destructorsynopsis', - 'fieldsynopsis', 'funcdef', 'funcparams', - 'funcprototype', 'funcsynopsis', - 'funcsynopsisinfo', 'function', 'group', - 'initializer', 'interfacename', - 'methodname', 'methodparam', 'methodsynopsis', - 'ooclass', 'ooexception', 'oointerface', 'varargs', - 'void', - # GUI elements - 'guibutton', 'guiicon', 'guilabel', - 'guimenu', 'guimenuitem', 'guisubmenu', - # EBNF Elements - 'constraint', 'constraintdef', 'lhs', 'rhs', - 'nonterminal', - # msg elements - 'msg', 'msgaud', 'msgentry', 'msgexplan', - 'msginfo', 'msglevel', 'msgmain', 'msgorig', - 'msgrel', 'msgset', 'msgsub', 'msgtext', - # REF entry - 'refclass', 'refdescriptor', 'refentry', - 'refentrytitle', 'reference', 'refmeta', - 'refmiscinfo', 'refname', 'refnamediv', - 'refpurpose', 'refsect1', 'refsect2', 'refsect3', - 'refsection', 'refsynopsisdiv' - # TOC - 'toc', 'tocdiv', 'tocentry', - # Index elements - 'index', 'indexdiv', 'indexentry', 'indexterm', - 'primary', 'primaryie', 'secondary', - 'secondaryie', 'see', 'seealso', - 'tertiary', 'tertiaryie', - # Other elements - 'info', 'bridgehead', 'arc', 'titleabbrev', - 'spanspec', 'xref', - ]) + ignored_tags = { + # Info elements + 'abstract', 'artpagenums', 'annotation', + 'artpagenums', 'author', 'authorgroup', + 'authorinitials', 'bibliocoverage', 'biblioid', + 'bibliomisc', 'bibliomset', 'bibliorelation', + 'biblioset', 'bibliosource', 'collab', 'confdates', + 'confgroup', 'confnum', 'confsponsor', 'conftitle', + 'contractnum', 'contractsponsor', 'copyright', + 'contrib', 'cover', 'edition', 'editor', + 'extendedlink', 'issuenum', 'itermset', 'keyword', + 'keywordset', 'legalnotice', 'org', 'orgname', + 'orgdiv', 'otheraddr', 'othercredit', 'pagenums', + 'personblurb', 'printhistory', 'productname', + 'productnumber', 'pubdate', 'publisher', + 'publishername', 'releaseinfo', 'revdescription', + 'revhistory', 'revision', 'revnumber', 'revremark', + 'seriesvolnums', 'subjectset', 'volumenum', + # Other bibliography elements + 'bibliodiv', 'biblioentry', 'bibliography', + 'bibliolist', 'bibliomixed', 'biblioref', + 'bibliorelation', 'citation', 'citerefentry', + 'citetitle', + # Callout elements + 'callout', 'calloutlist', 'area', 'areaset', + 'areaspec', 'co', 'imageobjectco', + # Class information + 'classname', 'classsynopsis', 'classsynopsisinfo', + 'constructorsynopsis', 'destructorsynopsis', + 'fieldsynopsis', 'funcdef', 'funcparams', + 'funcprototype', 'funcsynopsis', + 'funcsynopsisinfo', 'function', 'group', + 'initializer', 'interfacename', + 'methodname', 'methodparam', 'methodsynopsis', + 'ooclass', 'ooexception', 'oointerface', 'varargs', + 'void', + # GUI elements + 'guibutton', 'guiicon', 'guilabel', + 'guimenu', 'guimenuitem', 'guisubmenu', + # EBNF Elements + 'constraint', 'constraintdef', 'lhs', 'rhs', + 'nonterminal', + # msg elements + 'msg', 'msgaud', 'msgentry', 'msgexplan', + 'msginfo', 'msglevel', 'msgmain', 'msgorig', + 'msgrel', 'msgset', 'msgsub', 'msgtext', + # REF entry + 'refclass', 'refdescriptor', 'refentry', + 'refentrytitle', 'reference', 'refmeta', + 'refmiscinfo', 'refname', 'refnamediv', + 'refpurpose', 'refsect1', 'refsect2', 'refsect3', + 'refsection', 'refsynopsisdiv', + # TOC + 'toc', 'tocdiv', 'tocentry', + # Index elements + 'index', 'indexdiv', 'indexentry', 'indexterm', + 'primary', 'primaryie', 'secondary', + 'secondaryie', 'see', 'seealso', + 'tertiary', 'tertiaryie', + # Other elements + 'info', 'bridgehead', 'arc', 'titleabbrev', + 'spanspec', 'xref', + } # DocBook inline elements which does not have equivalence in the DOM # tree, but we keep the information using - inline_tags = set(['abbrev', 'address', 'accel', 'acronym', 'alt', - 'affiliation', 'city', 'command', 'constant', - 'country', 'database', 'date', 'errorcode', - 'errorname', 'errortext', 'errortype', - 'exceptionname', 'fax', 'filename', 'firstname', - 'firstterm', 'foreignphrase', 'hardware', 'holder', - 'honorific', 'jobtitle', 'keycap', 'keycode', - 'keycombo', 'keysym', 'lineannotation', - 'manvolnum', 'mousebutton', 'option', 'optional', - 'package', 'person', 'personname', 'phone', 'pob', - 'postcode', 'prompt', 'remark', 'replaceable', - 'returnvalue', 'shortaffil', 'shortcut', 'state', - 'street', 'surname', 'symbol', 'systemitem', - 'termdef', 'type', 'uri', 'userinput', - 'wordasword', 'varname', 'anchor', - ]) + inline_tags = {'abbrev', 'address', 'accel', 'acronym', 'alt', + 'affiliation', 'city', 'command', 'constant', + 'country', 'database', 'date', 'errorcode', + 'errorname', 'errortext', 'errortype', + 'exceptionname', 'fax', 'filename', 'firstname', + 'firstterm', 'foreignphrase', 'hardware', 'holder', + 'honorific', 'jobtitle', 'keycap', 'keycode', + 'keycombo', 'keysym', 'lineannotation', + 'manvolnum', 'mousebutton', 'option', 'optional', + 'package', 'person', 'personname', 'phone', 'pob', + 'postcode', 'prompt', 'remark', 'replaceable', + 'returnvalue', 'shortaffil', 'shortcut', 'state', + 'street', 'surname', 'symbol', 'systemitem', + 'termdef', 'type', 'uri', 'userinput', + 'wordasword', 'varname', 'anchor', + } # DocBook block element which does not have equivalence in the DOM # tree, but we keep the information using
- block_tags = set(['acknowledgements', 'appendix', 'article', 'book', - 'caption', 'chapter', 'cmdsynopsis', 'colophon', - 'dedication', 'epigraph', 'example', 'figure', - 'equation', 'part', 'partintro', - 'screenshoot', 'set', 'setindex', 'sidebar', - 'simplesect', 'subtitle', 'synopsis', - 'synopfragment', 'task', 'taskprerequisites', - 'taskrelated', 'tasksummary', 'title', - ]) + block_tags = {'acknowledgements', 'appendix', 'article', 'book', + 'caption', 'chapter', 'cmdsynopsis', 'colophon', + 'dedication', 'epigraph', 'example', 'figure', + 'equation', 'part', 'partintro', + 'screenshoot', 'set', 'setindex', 'sidebar', + 'simplesect', 'subtitle', 'synopsis', + 'synopfragment', 'task', 'taskprerequisites', + 'taskrelated', 'tasksummary', 'title', + } # DocBook has admonition as individual element, but the DOM Tree # has only one element for it, so we will convert all the DocBook # admonitions in this list, into the admonition element of the DOM Tree. - admonition_tags = set(['attention', 'caution', 'danger', 'error', 'hint', 'important', 'note', 'tip', 'warning']) + admonition_tags = {'attention', 'caution', 'danger', 'error', 'hint', 'important', 'note', 'tip', 'warning'} # DocBook can handle three kinds of media: audio, image, video. # TODO: a media format attribute is optional, e.g.: @@ -222,16 +220,16 @@ class Converter(object): 'tr': moin_page('table-row'), 'variablelist': moin_page('list'), 'varlistentry': moin_page('list-item'), - } + } # Other block elements which can be root element. - root_tags = set(['blockquote', 'formalpara', 'informalequation', - 'informalexample', 'informalfigure', - 'informalfigure', 'orderedlist', 'sect1', 'sect2', - 'sect3', 'sect4', 'sect5', 'section', - 'segmentedlist', 'simplelist', 'procedure', - 'qandaset', - ]) + root_tags = {'blockquote', 'formalpara', 'informalequation', + 'informalexample', 'informalfigure', + 'informalfigure', 'orderedlist', 'sect1', 'sect2', + 'sect3', 'sect4', 'sect5', 'section', + 'segmentedlist', 'simplelist', 'procedure', + 'qandaset', + } # Regular expression to find section tag. sect_re = re.compile('sect[1-5]') @@ -243,7 +241,7 @@ def _factory(cls, input, output, **kw): def __call__(self, data, contenttype=None, arguments=None): text = decode_data(data, contenttype) content = normalize_split_text(text) - docbook_str = u'\n'.join(content) + docbook_str = '\n'.join(content) logging.debug(docbook_str) # Initalize our attributes @@ -328,7 +326,7 @@ def get_standard_attributes(self, element): We save the result in our standard attribute. """ result = {} - for key, value in element.attrib.iteritems(): + for key, value in element.attrib.items(): if key.uri == xml and key.name in ['id', 'base', 'lang'] or key.name == 'data-lineno': result[key] = value if result: @@ -480,7 +478,7 @@ def visit_data_element(self, element, depth, object_data, text_object, caption): else: attrib[moin_page('type')] = mimetype align = object_to_show.get('align') - if align and align in set(['left', 'center', 'right', 'top', 'middle', 'bottom']): + if align and align in {'left', 'center', 'right', 'top', 'middle', 'bottom'}: attrib[html.class_] = align # return object tag, html_out.py will convert to img, audio, or video based on type attr @@ -527,7 +525,7 @@ def visit_docbook_blockquote(self, element, depth): -->
Text
""" # TODO: Translate - source = u"Unknow" + source = "Unknow" children = [] for child in element: if isinstance(child, ET.Element): @@ -550,7 +548,7 @@ def visit_docbook_emphasis(self, element, depth): However, it is still semantic, so we call it emphasis and strong. """ - for key, value in element.attrib.iteritems(): + for key, value in element.attrib.items(): if key.name == 'role' and value == 'bold': return self.new_copy(moin_page.strong, element, depth, attrib={}) @@ -685,7 +683,7 @@ def visit_docbook_link(self, element, depth): the anchors. """ attrib = {} - for key, value in element.attrib.iteritems(): + for key, value in element.attrib.items(): if key.uri == xlink and allowed_uri_scheme(value): attrib[key] = value linkend = element.get('linkend') @@ -986,9 +984,9 @@ def visit_docbook_trademark(self, element, depth): Docbook supports 4 types of trademark: copyright, registered, trade (mark), and service (mark). --> """ - trademark_entities = {'copyright': u'\xa9 ', # '© ', - 'registered': u'\xae', # u'®', - 'trade': u'\u2122', # no entity name defined for superscript TM + trademark_entities = {'copyright': '\xa9 ', # '© ', + 'registered': '\xae', # u'®', + 'trade': '\u2122', # no entity name defined for superscript TM } trademark_class = element.get('class') children = self.do_children(element, depth) @@ -1015,9 +1013,9 @@ def visit_docbook_entry(self, element, depth): colspan = element.get('morecols') try: if rowspan: - attrib[moin_page.number_rows_spanned] = unicode(1 + int(rowspan)) + attrib[moin_page.number_rows_spanned] = str(1 + int(rowspan)) if colspan: - attrib[moin_page.number_columns_spanned] = unicode(1 + int(colspan)) + attrib[moin_page.number_columns_spanned] = str(1 + int(colspan)) except ValueError: pass return self.new_copy(moin_page.table_cell, @@ -1048,7 +1046,7 @@ def visit_docbook_ulink(self, element, depth): # Since it is an element of DocBook v.4, # The namespace does not always work, so we will try to retrive the attribute whatever if not href: - for key, value in element.attrib.iteritems(): + for key, value in element.attrib.items(): if key.name == 'url' and allowed_uri_scheme(value): href = value key = xlink.href @@ -1177,7 +1175,7 @@ def visit_simple_list(self, moin_page_tag, attrib, element, depth): Here we handle the conversion of such of list. """ - list_item_tags = set(['listitem', 'step', 'stepalternatives', 'member']) + list_item_tags = {'listitem', 'step', 'stepalternatives', 'member'} items = [] for child in element: if isinstance(child, ET.Element): diff --git a/src/moin/converters/docbook_out.py b/src/moin/converters/docbook_out.py index fbb2879fc..9f971e0e4 100644 --- a/src/moin/converters/docbook_out.py +++ b/src/moin/converters/docbook_out.py @@ -8,22 +8,19 @@ Converts an internal document tree into a DocBook v5 document. """ - -from __future__ import absolute_import, division - from emeraldtree import ElementTree as ET from moin.utils.tree import html, moin_page, xlink, docbook, xml from moin.constants.contenttypes import CONTENTTYPE_NONEXISTENT from moin.utils.mime import Type, type_moin_document -from . import default_registry +from . import default_registry, ElementException from moin import log logging = log.getLogger(__name__) -class Converter(object): +class Converter: """ Converter application/x.moin.document -> application/docbook+xml """ @@ -31,10 +28,10 @@ class Converter(object): moin_page: 'moinpage' } - unsupported_tags = set(['separator', ]) + unsupported_tags = {'separator'} # Only these admonitions are supported by DocBook 5 - admonition_tags = set(['caution', 'important', 'note', 'tip', 'warning']) + admonition_tags = {'caution', 'important', 'note', 'tip', 'warning'} # DOM Tree element which can easily be converted into a DocBook # element, without attributes. @@ -118,7 +115,7 @@ def get_standard_attributes(self, element): We save the result in standard_attribute. """ result = {} - for key, value in element.attrib.iteritems(): + for key, value in element.attrib.items(): if key.uri == xml: result[key] = value if result: @@ -137,7 +134,7 @@ def visit(self, element): """ uri = element.tag.uri name = self.namespaces_visit.get(uri, None) - print '==== uri = %s, name = %s' % (uri, name) # @@@@@@@@@@@@@ + print('==== uri = %s, name = %s' % (uri, name)) # @@@@@@@@@@@@@ if name is not None: method_name = 'visit_' + name method = getattr(self, method_name, None) @@ -185,7 +182,7 @@ def visit_moinpage_a(self, element): into an tag. """ attrib = {} - for key, value in element.attrib.iteritems(): + for key, value in element.attrib.items(): if key.uri == xlink: attrib[key] = value return self.new_copy(docbook.link, element, attrib=attrib) @@ -258,7 +255,11 @@ def visit_moinpage_h(self, element): A section is closed when we have a new heading with an equal or higher level. """ - depth = element.get(moin_page('outline-level')) + depth = element.get(moin_page('outline-level'), 1) + try: + depth = int(depth) + except ValueError: + raise ElementException('page:outline-level needs to be an integer') # We will have a new section # under another section if depth > self.current_section: @@ -418,7 +419,7 @@ def visit_moinpage_table_cell(self, element): attrib = {} rowspan = element.get(moin_page('number-rows-spanned')) colspan = element.get(moin_page('number-columns-spanned')) - print "rowspan : {0}".format(rowspan) + print("rowspan : {0}".format(rowspan)) if rowspan: attrib[docbook.rowspan] = rowspan if colspan: @@ -490,7 +491,7 @@ def visit_moinpage_p(self, element): """ title_attr = element.get(html('title')) if title_attr: - print title_attr + print(title_attr) children = [] title_elem = self.new(docbook('title'), attrib={}, children=[title_attr]) @@ -509,7 +510,7 @@ def visit_moinpage_span(self, element): TODO: Add support for font-size attribute """ # Check for the attributes of span - for key, value in element.attrib.iteritems(): + for key, value in element.attrib.items(): if key.name == 'baseline-shift': if value == 'super': return self.new_copy(docbook.superscript, diff --git a/src/moin/converters/everything.py b/src/moin/converters/everything.py index a398c9f63..cc34e0971 100644 --- a/src/moin/converters/everything.py +++ b/src/moin/converters/everything.py @@ -17,7 +17,7 @@ from . import default_registry -class Converter(object): +class Converter: """ Convert a unsupported item to DOM Tree. """ @@ -31,7 +31,7 @@ def __call__(self, rev, contenttype=None, arguments=None): xlink.href: Iri(scheme='wiki', authority='', path='/' + item_name, query='do=get&rev={0}'.format(rev.revid)), } - a = moin_page.a(attrib=attrib, children=[u"Download {0}.".format(item_name)]) + a = moin_page.a(attrib=attrib, children=["Download {0}.".format(item_name)]) body = moin_page.body(children=(a, )) return moin_page.page(children=(body, )) diff --git a/src/moin/converters/highlight.py b/src/moin/converters/highlight.py index d90c8cb90..da93e22ed 100644 --- a/src/moin/converters/highlight.py +++ b/src/moin/converters/highlight.py @@ -5,9 +5,6 @@ MoinMoin - Text highlighting converter """ - -from __future__ import absolute_import, division - import re from flask import request @@ -18,7 +15,7 @@ from . import default_registry -class Converter(object): +class Converter: @classmethod def _factory(cls, input, output, highlight='', regex='', **kw): if highlight == 'highlight': @@ -29,7 +26,7 @@ def recurse(self, elem): new_childs = [] for child in elem: - if isinstance(child, (unicode, str)): + if isinstance(child, (bytes, str)): pos = 0 # Restrict it to our own namespace for now diff --git a/src/moin/converters/html_in.py b/src/moin/converters/html_in.py index eb1f3e5f9..871fd175e 100644 --- a/src/moin/converters/html_in.py +++ b/src/moin/converters/html_in.py @@ -9,9 +9,6 @@ TODO : Add support for style """ - -from __future__ import absolute_import, division - import re from flask import flash @@ -31,7 +28,7 @@ logging = log.getLogger(__name__) -class NoDupsFlash(object): +class NoDupsFlash: """ Issue flash messages for unsupported HTML tags; but do not create duplicate messages. """ @@ -44,7 +41,7 @@ def log(self, message, category): flash(message, category) -class Converter(object): +class Converter: """ Converter html -> .x.moin.document """ @@ -54,10 +51,10 @@ class Converter(object): } # HTML tags which can be converted directly to the moin_page namespace - symmetric_tags = set(['div', 'p', 'strong', 'code', 'quote', 'blockquote', 'span']) + symmetric_tags = {'div', 'p', 'strong', 'code', 'quote', 'blockquote', 'span'} # HTML tags to define a list, except dl which is a little bit different - list_tags = set(['ul', 'dir', 'ol']) + list_tags = {'ul', 'dir', 'ol'} # HTML tags which can be convert without attributes in a different DOM tag simple_tags = { # Emphasis @@ -74,20 +71,20 @@ class Converter(object): # HTML Tag which does not have equivalence in the DOM Tree # But we keep the information using - inline_tags = set(['abbr', 'acronym', 'address', 'dfn', 'kbd']) + inline_tags = {'abbr', 'acronym', 'address', 'dfn', 'kbd'} # HTML tags which are completely ignored by our converter. # We even do not process children of these elements. - ignored_tags = set(['applet', 'area', 'button', 'caption', 'center', 'fieldset', - 'form', 'frame', 'frameset', 'head', 'iframe', 'input', 'isindex', - 'label', 'legend', 'link', 'map', 'menu', 'noframes', 'noscript', - 'optgroup', 'option', 'param', 'script', 'select', 'style', - 'textarea', 'title', 'var', - ]) + ignored_tags = {'applet', 'area', 'button', 'caption', 'center', 'fieldset', + 'form', 'frame', 'frameset', 'head', 'iframe', 'input', 'isindex', + 'label', 'legend', 'link', 'map', 'menu', 'noframes', 'noscript', + 'optgroup', 'option', 'param', 'script', 'select', 'style', + 'textarea', 'title', 'var', + } # standard_attributes are html attributes which are used # directly in the DOM tree, without any conversion - standard_attributes = set(['title', 'class', 'style', 'alt']) + standard_attributes = {'title', 'class', 'style', 'alt'} # Regular expression to detect an html heading tag heading_re = re.compile('h[1-6]') @@ -121,7 +118,7 @@ def __call__(self, data, contenttype=None, arguments=None): # We create an element tree from the HTML content # The content is a list of string, line per line # We can concatenate all in one string - html_str = u'\n'.join(content) + html_str = '\n'.join(content) try: html_tree = HTML(html_str) except AssertionError as reason: @@ -202,7 +199,7 @@ def new_copy_symmetric(self, element, attrib): def convert_attributes(self, element): result = {} - for key, value in element.attrib.iteritems(): + for key, value in element.attrib.items(): if key.uri == html and \ key.name in self.standard_attributes: result[key] = value @@ -369,12 +366,12 @@ def visit_xhtml_strike(self, element): """ return self.new_copy(moin_page.s, element, {}) - def visit_xhtml_hr(self, element, min_class=u'moin-hr1', max_class=u'moin-hr6', default_class=u'moin-hr3'): + def visit_xhtml_hr(self, element, min_class='moin-hr1', max_class='moin-hr6', default_class='moin-hr3'): """
--> """ hr_class = element.attrib.get(html('class')) - if not (min_class <= hr_class <= max_class): + if hr_class is None or not (min_class <= hr_class <= max_class): element.attrib[html('class')] = default_class return self.new_copy(moin_page.separator, element, {}) @@ -421,7 +418,7 @@ def visit_xhtml_object(self, element): attrib[key] = element.get(html.data) # Convert the href attribute into unicode - attrib[key] = unicode(attrib[key]) + attrib[key] = str(attrib[key]) return moin_page.object(attrib) def visit_xhtml_inline(self, element): diff --git a/src/moin/converters/html_out.py b/src/moin/converters/html_out.py index 2b77055d0..fa975747c 100644 --- a/src/moin/converters/html_out.py +++ b/src/moin/converters/html_out.py @@ -9,9 +9,6 @@ Converts an internal document tree into a HTML tree. """ - -from __future__ import absolute_import, division - import re from flask import request @@ -32,7 +29,7 @@ # strings not allowed in style attributes -SUSPECT = set(('/*', '/>', '\\', '`', 'script', '&#', 'http', 'expression', 'behavior', )) +SUSPECT = {'/*', '/>', '\\', '`', 'script', '&#', 'http', 'expression', 'behavior', } def style_attr_filter(style): @@ -64,7 +61,7 @@ def mark_item_as_transclusion(elem, href): On the client side, a Javascript function will wrap the element (or a parent element) in a span or div and 2 overlay siblings will be created. """ - href = unicode(href) + href = str(href) # href will be "/wikiroot/SomeObject" or "/SomePage" for internal wiki items # or "http://Some.Org/SomeThing" for external link if elem.tag.name not in ('object', 'img'): @@ -82,7 +79,7 @@ def mark_item_as_transclusion(elem, href): return elem -class Attribute(object): +class Attribute: """ Adds the attribute with the HTML namespace to the output. """ __slots__ = 'key' @@ -93,7 +90,7 @@ def __call__(self, value, out): out[self.key] = value -class Attributes(object): +class Attributes: namespaces_valid_output = frozenset([ html, ]) @@ -128,7 +125,7 @@ def convert(self): new = {} new_default = {} - for key, value in self.element.attrib.iteritems(): + for key, value in self.element.attrib.items(): if key == html.style: value = style_attr_filter(value) if key.uri == moin_page: @@ -160,7 +157,7 @@ def convert(self): return new_default -class Converter(object): +class Converter: """ Converter application/x.moin.document -> application/x.moin.document """ @@ -170,7 +167,7 @@ class Converter(object): } # Inline tags which can be directly converted into an HTML element - direct_inline_tags = set(['abbr', 'address', 'dfn', 'kbd']) + direct_inline_tags = {'abbr', 'address', 'dfn', 'kbd'} def __call__(self, element): return self.visit(element) @@ -221,7 +218,7 @@ def visit_moinpage_a(self, elem, _tag_html_a=html.a, _tag_html_href=html.href, _ href = elem.get(_tag_xlink_href) if href: attrib[_tag_html_href] = href - if len(elem) == 1 and isinstance(elem[0], unicode) and elem[0] == u'': + if len(elem) == 1 and isinstance(elem[0], str) and elem[0] == '': # input similar to [[#Heading]] will create an invisible link like 1 + caption and html('class') in attrib and u'moin-wiki-table' in attrib[html('class')]: + if len(elem) > 1 + caption and html('class') in attrib and 'moin-wiki-table' in attrib[html('class')]: # moinwiki tables require special handling because # moinwiki_in converts "||header||\n===\n||body||\n===\n||footer||" into multiple table-body's if idx == 0 + caption: @@ -625,7 +622,7 @@ def visit_moinpage_u(self, elem): return self.new_copy(html.u, elem) -class SpecialId(object): +class SpecialId: def __init__(self): self._ids = {} @@ -644,10 +641,10 @@ def gen_text(self, text): nr = self._ids[id] = self._ids.get(id, 0) + 1 if nr == 1: return id - return id + u'-{0}'.format(nr) + return id + '-{0}'.format(nr) -class SpecialPage(object): +class SpecialPage: def __init__(self): self._footnotes = [] self._headings = [] @@ -792,7 +789,7 @@ def visit_moinpage_h(self, elem, _tag_html_id=html.id): elem.set(_tag_html_id, id) try: # do not create duplicate anchors to this heading when converting from one markup to another - if not elem[-1].attrib[html.class_] == u'moin-permalink': + if not elem[-1].attrib[html.class_] == 'moin-permalink': self._special_stack[-1].add_heading(elem, elem.level, id) except (AttributeError, KeyError): self._special_stack[-1].add_heading(elem, elem.level, id) diff --git a/src/moin/converters/image_in.py b/src/moin/converters/image_in.py index d9babd02a..68b62ae9f 100644 --- a/src/moin/converters/image_in.py +++ b/src/moin/converters/image_in.py @@ -19,7 +19,7 @@ from . import default_registry -class Converter(object): +class Converter: """ Convert an image to the corresponding in the DOM Tree """ @@ -44,7 +44,7 @@ def __call__(self, rev, contenttype=None, arguments=None): query = url_encode(query_keys, charset=CHARSET, encode_keys=True) attrib.update({ - moin_page.type_: unicode(self.input_type), + moin_page.type_: str(self.input_type), xlink.href: Iri(scheme='wiki', authority='', path='/' + item_name, query=query), }) diff --git a/src/moin/converters/include.py b/src/moin/converters/include.py index ebc96d665..03ac062c8 100644 --- a/src/moin/converters/include.py +++ b/src/moin/converters/include.py @@ -88,9 +88,6 @@ In the example above, only class="comment" will be applied to detail.csv. """ - -from __future__ import absolute_import, division - from emeraldtree import ElementTree as ET import re import types @@ -153,7 +150,7 @@ class XPointer(list): """ tokenizer_re = re.compile(tokenizer_rules, re.X) - class Entry(object): + class Entry: __slots__ = 'name', 'data' def __init__(self, name, data): @@ -198,7 +195,7 @@ def __init__(self, input): self.append(self.Entry(''.join(name), None)) -class Converter(object): +class Converter: @classmethod def _factory(cls, input, output, includes=None, **kw): @@ -301,8 +298,8 @@ def recurse(self, elem, page_href): link.path = path - if flaskg.user.may.read(unicode(path)): - page = Item.create(unicode(path)) + if flaskg.user.may.read(str(path)): + page = Item.create(str(path)) pages = ((page, link), ) else: # ACLs prevent user from viewing a transclusion - show message @@ -335,8 +332,8 @@ def recurse(self, elem, page_href): if p_href in self.stack: # we have a transclusion loop, create an error message showing list of pages forming loop loop = self.stack[self.stack.index(p_href):] - loop = [u'{0}'.format(ref.path[1:]) for ref in loop if ref is not None] + [page.name] - msg = u'Error: Transclusion loop via: ' + u', '.join(loop) + loop = ['{0}'.format(ref.path[1:]) for ref in loop if ref is not None] + [page.name] + msg = 'Error: Transclusion loop via: ' + ', '.join(loop) attrib = {html.class_: 'moin-error'} strong = ET.Element(moin_page.strong, attrib, (msg, )) included_elements.append(strong) @@ -430,7 +427,7 @@ def recurse(self, elem, page_href): classes += ret.attrib.get(html.class_, '').split() ret.attrib[html.class_] = ' '.join(classes) elem[i] = ret - elif isinstance(ret, types.ListType): + elif isinstance(ret, list): # a container has been returned. # Note: there are multiple places where a container may be constructed ret_container, trans_ptr = ret @@ -459,7 +456,7 @@ def recurse(self, elem, page_href): else: # elem is a block element for grandchild in child: - if isinstance(grandchild, ET.Node) and grandchild.tag.name == u'include': + if isinstance(grandchild, ET.Node) and grandchild.tag.name == 'include': # the include may have classes that must be added to transcluded element classes = grandchild.attrib.get(html.class_, '').split() classes += ret_container[trans_ptr].attrib.get(html.class_, '').split() diff --git a/src/moin/converters/link.py b/src/moin/converters/link.py index 8095fa515..edb4a2347 100644 --- a/src/moin/converters/link.py +++ b/src/moin/converters/link.py @@ -8,9 +8,6 @@ special wiki links. """ - -from __future__ import absolute_import, division - from flask import g as flaskg from moin.utils.interwiki import is_known_wiki, url_for_item @@ -22,7 +19,7 @@ from . import default_registry -class ConverterBase(object): +class ConverterBase: _tag_xlink_href = xlink.href _tag_xinclude_href = xinclude.href @@ -76,9 +73,9 @@ def traverse_tree(self, elem, page=None, __tag_page_href=moin_page.page_href, elif xinclude_href.scheme == 'wiki': self.handle_wiki_transclusions(elem, xinclude_href) - elif xlink_href == u'': + elif xlink_href == '': # reST link to page fragment - elem.set(self._tag_xlink_href, u'#' + elem.text.replace(' ', '_')) + elem.set(self._tag_xlink_href, '#' + elem.text.replace(' ', '_')) for child in elem.iter_elements(): self.traverse_tree(child, page) @@ -138,7 +135,7 @@ def _get_do_rev(self, query): if k == 'rev': rev = v continue # we remove rev=n from qs - result.append(u'{0}={1}'.format(k, v)) + result.append('{0}={1}'.format(k, v)) if result: query = separator.join(result) else: @@ -157,12 +154,12 @@ def _get_do_rev(self, query): def handle_wiki_links(self, elem, input): wiki_name = 'Self' if input.authority and input.authority.host: - wn = unicode(input.authority.host) + wn = str(input.authority.host) if is_known_wiki(wn): # interwiki link elem.set(moin_page.class_, 'moin-interwiki') wiki_name = wn - item_name = unicode(input.path[1:]) + item_name = str(input.path[1:]) endpoint, rev, query = self._get_do_rev(input.query) url = url_for_item(item_name, wiki_name=wiki_name, rev=rev, endpoint=endpoint) link = Iri(url, query=query, fragment=input.fragment) @@ -174,12 +171,12 @@ def handle_wikilocal_links(self, elem, input, page): path = input.path if page: path = self.absolute_path(path, page.path) - item_name = unicode(path) + item_name = str(path) if not flaskg.storage.has_item(item_name): # XXX these index accesses slow down the link converter quite a bit elem.set(moin_page.class_, 'moin-nonexistent') else: - item_name = unicode(page.path[1:]) + item_name = str(page.path[1:]) endpoint, rev, query = self._get_do_rev(input.query) url = url_for_item(item_name, rev=rev, endpoint=endpoint) if not page: @@ -234,7 +231,7 @@ def handle_wikilocal_links(self, elem, input, page): return path = self.absolute_path(path, page.path) - self.links.add(unicode(path)) + self.links.add(str(path)) def handle_wikilocal_transclusions(self, elem, input, page): """ @@ -248,7 +245,7 @@ def handle_wikilocal_transclusions(self, elem, input, page): return path = self.absolute_path(path, page.path) - self.transclusions.add(unicode(path)) + self.transclusions.add(str(path)) def handle_external_links(self, elem, input): """ @@ -256,7 +253,7 @@ def handle_external_links(self, elem, input): :param elem: the element of the link :param input: the iri of the link """ - self.external_links.add(unicode(input)) + self.external_links.add(str(input)) def get_links(self): """ diff --git a/src/moin/converters/macro.py b/src/moin/converters/macro.py index 2ead7b958..6f8a83bbb 100644 --- a/src/moin/converters/macro.py +++ b/src/moin/converters/macro.py @@ -7,8 +7,6 @@ Expands all macro elements in an internal Moin document. """ -from __future__ import absolute_import, division - from flask import current_app as app from emeraldtree import ElementTree as ET @@ -26,7 +24,7 @@ logging = log.getLogger(__name__) -class Converter(object): +class Converter: @classmethod def _factory(cls, input, output, macros=None, **kw): if macros == 'expandall': @@ -67,7 +65,7 @@ def handle_macro(self, elem, page): # some standard text. logging.exception("Macro {0} raised an exception:".format(name)) elem_error.append(_('<<%(macro_name)s: execution failed [%(error_msg)s] (see also the log)>>', - macro_name=name, error_msg=unicode(e), )) + macro_name=name, error_msg=str(e), )) if len(elem_body): elem.append(elem_body) diff --git a/src/moin/converters/markdown_in.py b/src/moin/converters/markdown_in.py index 2ca455fcf..7f8b88e0e 100644 --- a/src/moin/converters/markdown_in.py +++ b/src/moin/converters/markdown_in.py @@ -8,11 +8,8 @@ http://daringfireball.net/projects/markdown/ """ - -from __future__ import absolute_import, division - import re -import htmlentitydefs +from html.entities import name2codepoint from collections import deque from moin.utils.tree import moin_page, xml, html, xlink, xinclude @@ -72,15 +69,15 @@ def fixup(m): # character reference try: if text[:3] == "&#x": - return unichr(int(text[3:-1], 16)) + return chr(int(text[3:-1], 16)) else: - return unichr(int(text[2:-1])) + return chr(int(text[2:-1])) except ValueError: pass else: # named entity try: - text = unichr(htmlentitydefs.name2codepoint[text[1:-1]]) + text = chr(name2codepoint[text[1:-1]]) except KeyError: pass return text # leave as is @@ -88,14 +85,14 @@ def fixup(m): return re.sub(r"&#?\w+;", fixup, text) -class Converter(object): +class Converter: # {{{ html conversion # HTML tags which can be converted directly to the moin_page namespace - symmetric_tags = set(['div', 'p', 'strong', 'code', 'quote', 'blockquote']) + symmetric_tags = {'div', 'p', 'strong', 'code', 'quote', 'blockquote'} # HTML tags to define a list, except dl which is a little bit different - list_tags = set(['ul', 'ol']) + list_tags = {'ul', 'ol'} # HTML tags which can be convert without attributes in a different DOM tag simple_tags = { # Emphasis @@ -121,20 +118,20 @@ class Converter(object): # HTML Tag which does not have equivalence in the DOM Tree # But we keep the information using - inline_tags = set(['abbr', 'acronym', 'address', 'dfn', 'kbd']) + inline_tags = {'abbr', 'acronym', 'address', 'dfn', 'kbd'} # HTML tags which are completely ignored by our converter. # We even do not process children of these elements. - ignored_tags = set(['applet', 'area', 'button', 'caption', 'center', 'fieldset', - 'form', 'frame', 'frameset', 'head', 'iframe', 'input', 'isindex', - 'label', 'legend', 'link', 'map', 'menu', 'noframes', 'noscript', - 'optgroup', 'option', 'param', 'script', 'select', 'style', - 'textarea', 'title', 'var', - ]) + ignored_tags = {'applet', 'area', 'button', 'caption', 'center', 'fieldset', + 'form', 'frame', 'frameset', 'head', 'iframe', 'input', 'isindex', + 'label', 'legend', 'link', 'map', 'menu', 'noframes', 'noscript', + 'optgroup', 'option', 'param', 'script', 'select', 'style', + 'textarea', 'title', 'var', + } # standard_attributes are html attributes which are used # directly in the DOM tree, without any conversion - standard_attributes = set(['title', 'class', 'style']) + standard_attributes = {'title', 'class', 'style'} # Regular expression to detect an html heading tag heading_re = re.compile('h[1-6]') @@ -169,7 +166,7 @@ def new_copy_symmetric(self, element, attrib): def convert_attributes(self, element): result = {} - for key, value in element.attrib.iteritems(): + for key, value in element.attrib.items(): if key in self.standard_attributes: result[html(key)] = value if key == 'id': @@ -244,7 +241,7 @@ def visit_strike(self, element): attrib[key] = 'line-through' return self.new_copy(moin_page.span, element, attrib) - def visit_hr(self, element, default_class=u'moin-hr3'): + def visit_hr(self, element, default_class='moin-hr3'): return self.new_copy(moin_page.separator, element, {moin_page.class_: default_class}) def visit_img(self, element): @@ -280,7 +277,7 @@ def visit_object(self, element): attrib[key] = element.get(html.data) # Convert the href attribute into unicode - attrib[key] = unicode(attrib[key]) + attrib[key] = str(attrib[key]) return moin_page.object(attrib) def visit_inline(self, element): @@ -352,7 +349,7 @@ def visit_a(self, element): def convert_align_to_class(self, attrib): attr = {} alignment = attrib.get('align') - if alignment in (u'right', u'center', u'left'): + if alignment in ('right', 'center', 'left'): attr[moin_page.class_] = alignment return attr @@ -413,7 +410,7 @@ def do_children(self, element, add_lineno=False): """ new = [] # copy anything but '\n' - if hasattr(element, "text") and element.text is not None and element.text != u'\n': + if hasattr(element, "text") and element.text is not None and element.text != '\n': new.append(postproc_text(self.markdown, element.text)) for child in element: @@ -427,7 +424,7 @@ def do_children(self, element, add_lineno=False): r = (r, ) new.extend(r) # copy anything but '\n' - if hasattr(child, "tail") and child.tail is not None and child.tail != u'\n': + if hasattr(child, "tail") and child.tail is not None and child.tail != '\n': new.append(postproc_text(self.markdown, child.tail)) return new @@ -453,16 +450,16 @@ def count_lines(self, text): line_numbers = deque() lineno = 1 in_blockquote = False - blocks = text.split(u'\n\n') + blocks = text.split('\n\n') for block in blocks: if not block: # bump count because empty blocks will be discarded lineno += 2 continue - line_count = block.count(u'\n') + line_count = block.count('\n') # detect and fix the problem of interspersed blank lines within blockquotes - if block.startswith(u' ') or block.startswith(u'\n '): + if block.startswith(' ') or block.startswith('\n '): if in_blockquote: lineno += line_count + 2 continue @@ -470,7 +467,7 @@ def count_lines(self, text): else: in_blockquote = False - if block.startswith(u'\n'): + if block.startswith('\n'): lineno += 1 line_numbers.append(lineno) lineno += line_count + 2 - 1 # -1 is already in count @@ -492,7 +489,7 @@ def embedded_markup(self, text): """ try: # we enclose plain text and block tags with DIV-tags - p_text = html_in_converter(u'
%s
' % text) + p_text = html_in_converter('
%s
' % text) # discard page and body tags return p_text[0][0] except AssertionError: @@ -506,8 +503,8 @@ def convert_embedded_markup(self, node): :param node: a tree node """ for idx, child in enumerate(node): - if isinstance(child, unicode): - if u'<' in child: + if isinstance(child, str): + if '<' in child: node[idx] = self.embedded_markup(child) # child is immutable string, so must do node[idx] else: # do not convert markup within a
 tag
@@ -525,10 +522,10 @@ def convert_invalid_p_nodes(self, node):
         """
         for child in node:
             # TODO: bug in codehilite? <, > are returned as string (not unicode) given ~~~{html}\n\n~~~
-            if not isinstance(child, (unicode, str)):
+            if not isinstance(child, (bytes, str)):
                 if child.tag == moin_page.p and len(child):
                     for grandchild in child:
-                        if not isinstance(grandchild, unicode) and grandchild.tag in BLOCK_ELEMENTS:
+                        if not isinstance(grandchild, str) and grandchild.tag in BLOCK_ELEMENTS:
                             child.tag = moin_page.div
                 self.convert_invalid_p_nodes(child)
 
diff --git a/src/moin/converters/markdown_out.py b/src/moin/converters/markdown_out.py
index 0b48fab90..7da2e35c6 100644
--- a/src/moin/converters/markdown_out.py
+++ b/src/moin/converters/markdown_out.py
@@ -9,10 +9,9 @@
 Converts an internal document tree into markdown markup.
 """
 
-
-from __future__ import absolute_import, division
-
-import urllib
+import urllib.request
+import urllib.parse
+import urllib.error
 
 from . import ElementException
 
@@ -29,58 +28,58 @@
 from moin.utils.mime import Type, type_moin_document
 
 
-class Markdown(object):
+class Markdown:
     """
     Markdown syntax elements
     It's dummy
     """
-    h = u'#'
-    a_open = u'<'
+    h = '#'
+    a_open = '<'
     a_desc_open = '('
     a_desc_close = ')'
-    a_close = u'>'
+    a_close = '>'
     comment_open = ''
-    verbatim_open = u'    '  # * 3
-    verbatim_close = u'    '  # * 3
-    monospace = u'`'
-    strong = u"**"
-    emphasis = u"*"
-    underline_open = u''
-    underline_close = u''
-    samp_open = u'`'
-    samp_close = u'`'
-    stroke_open = u''
-    stroke_close = u''
-    table_marker = u'|'
-    p = u'\n'
-    linebreak = u'  '
-    larger_open = u''
-    larger_close = u''
-    smaller_open = u''
-    smaller_close = u''
-    object_open = u'{{'
-    object_close = u'}}'
-    definition_list_marker = u':  '
-    separator = u'----'
+    verbatim_open = '    '  # * 3
+    verbatim_close = '    '  # * 3
+    monospace = '`'
+    strong = "**"
+    emphasis = "*"
+    underline_open = ''
+    underline_close = ''
+    samp_open = '`'
+    samp_close = '`'
+    stroke_open = ''
+    stroke_close = ''
+    table_marker = '|'
+    p = '\n'
+    linebreak = '  '
+    larger_open = ''
+    larger_close = ''
+    smaller_open = ''
+    smaller_close = ''
+    object_open = '{{'
+    object_close = '}}'
+    definition_list_marker = ':  '
+    separator = '----'
     # TODO: definition list
     list_type = {
-        (u'definition', None): u'',
-        (u'ordered', None): u'1.',
-        (u'ordered', u'lower-alpha'): u'1.',
-        (u'ordered', u'upper-alpha'): u'1.',
-        (u'ordered', u'lower-roman'): u'1.',
-        (u'ordered', u'upper-roman'): u'1.',
-        (u'unordered', None): u'*',
-        (u'unordered', u'no-bullet'): u'*',
-        (None, None): u'::',
+        ('definition', None): '',
+        ('ordered', None): '1.',
+        ('ordered', 'lower-alpha'): '1.',
+        ('ordered', 'upper-alpha'): '1.',
+        ('ordered', 'lower-roman'): '1.',
+        ('ordered', 'upper-roman'): '1.',
+        ('unordered', None): '*',
+        ('unordered', 'no-bullet'): '*',
+        (None, None): '::',
     }
 
     def __init__(self):
         pass
 
 
-class Converter(object):
+class Converter:
     """
     Converter application/x.moin.document -> text/x.moin.wiki
     """
@@ -94,8 +93,8 @@ def _factory(cls, input, output, **kw):
         return cls()
 
     def __init__(self):
-        self.list_item_labels = [u'', ]
-        self.list_item_label = u''
+        self.list_item_labels = ['', ]
+        self.list_item_label = ''
         self.list_level = 0
         self.footnotes = []  # tuple of (name, text)
         self.footnote_number = 0  # incremented if a footnote name was not passed
@@ -111,25 +110,25 @@ def __call__(self, root):
             # add footnote definitions to end of content
             notes = []
             for name, txt in self.footnotes:
-                notes.append(u'[^{0}]: {1}'.format(name, txt))
-            notes = u'\n'.join(notes)
-            content += u'\n\n' + notes + u'\n'
+                notes.append('[^{0}]: {1}'.format(name, txt))
+            notes = '\n'.join(notes)
+            content += '\n\n' + notes + '\n'
         return content
 
-    def open_children(self, elem, join_char=u''):
+    def open_children(self, elem, join_char=''):
         childrens_output = []
         for child in elem:
             if isinstance(child, ET.Element):
                 # open function can change self.output
                 childrens_output.append(self.open(child))
             else:
-                ret = u''
+                ret = ''
                 if self.status[-1] == "text":
                     if self.last_closed == "p":
-                        ret = u'\n'
+                        ret = '\n'
                 if child == '\n' and getattr(elem, 'level', 0):
-                    child = child + ' ' * (len(u''.join(self.list_item_labels[:-1])) + len(self.list_item_labels[:-1]))
-                childrens_output.append(u'{0}{1}'.format(ret, child))
+                    child = child + ' ' * (len(''.join(self.list_item_labels[:-1])) + len(self.list_item_labels[:-1]))
+                childrens_output.append('{0}{1}'.format(ret, child))
                 self.last_closed = 'text'
         out = join_char.join(childrens_output)
         return out
@@ -157,12 +156,12 @@ def open_moinpage(self, elem):
     def open_moinpage_a(self, elem):
         href = elem.get(xlink.href, None)
         if isinstance(href, Iri):
-            href = unicode(href)
-        href = href.split(u'wiki.local:')[-1]
+            href = str(href)
+        href = href.split('wiki.local:')[-1]
         ret = href
         text = self.open_children(elem)
         if text:
-            return u'[{0}]({1})'.format(text, href)
+            return '[{0}]({1})'.format(text, href)
         if ret.startswith('wiki://'):
             # interwiki fixup
             ret = ret[7:]
@@ -170,7 +169,7 @@ def open_moinpage_a(self, elem):
         return Markdown.a_open + ret + Markdown.a_close
 
     def open_moinpage_blockcode(self, elem):
-        text = u''.join(elem.itertext())
+        text = ''.join(elem.itertext())
 
         if elem.attrib.get(html.class_, None) == 'codehilite':
             return text
@@ -190,12 +189,12 @@ def open_moinpage_blockquote(self, elem):
         ret = ret.strip()
         indented = []
         for line in ret.split('\n'):
-            indented.append(u' > ' + line)
+            indented.append(' > ' + line)
         return '\n' + '\n'.join(indented) + '\n'
 
     def open_moinpage_code(self, elem):
         ret = Markdown.monospace
-        ret += u''.join(elem.itertext())
+        ret += ''.join(elem.itertext())
         ret += Markdown.monospace
         return ret
 
@@ -207,17 +206,17 @@ def open_moinpage_div(self, elem):
             # we do not want expanded toc
             return '\n\n[TOC]\n\n'
 
-        if elem.attrib.get(html.class_, None) == 'codehilite' and isinstance(elem[0][1], unicode):
+        if elem.attrib.get(html.class_, None) == 'codehilite' and isinstance(elem[0][1], str):
             # in most cases, codehilite returns plain text blocks; return an indented block
             text = elem[0][1].split('\n')
-            return '\n' + '\n'.join([u'    ' + x for x in text]) + '\n'
+            return '\n' + '\n'.join(['    ' + x for x in text]) + '\n'
 
         childrens_output = self.open_children(elem)
         return '\n\n' + childrens_output + '\n\n'
 
     def open_moinpage_emphasis(self, elem):
         childrens_output = self.open_children(elem)
-        return u"{0}{1}{2}".format(Markdown.emphasis, childrens_output, Markdown.emphasis)
+        return "{0}{1}{2}".format(Markdown.emphasis, childrens_output, Markdown.emphasis)
 
     def open_moinpage_h(self, elem):
         level = elem.get(moin_page.outline_level, 1)
@@ -229,79 +228,79 @@ def open_moinpage_h(self, elem):
             level = 1
         elif level > 6:
             level = 6
-        ret = Markdown.h * level + u' '
-        ret += u''.join(elem.itertext())
-        ret += u' {0}\n'.format(Markdown.h * level)
-        return u'\n' + ret
+        ret = Markdown.h * level + ' '
+        ret += ''.join(elem.itertext())
+        ret += ' {0}\n'.format(Markdown.h * level)
+        return '\n' + ret
 
     def open_moinpage_line_break(self, elem):
         return Markdown.linebreak
 
     def open_moinpage_list(self, elem):
         label_type = elem.get(moin_page.item_label_generate, None), elem.get(moin_page.list_style_type, None)
-        self.list_item_labels.append(Markdown.list_type.get(label_type, u'*'))
+        self.list_item_labels.append(Markdown.list_type.get(label_type, '*'))
         self.list_level += 1
-        ret = u''
+        ret = ''
         if self.status[-1] != 'text' or self.last_closed:
-            ret = u'\n'
+            ret = '\n'
         self.status.append('list')
         self.last_closed = None
         childrens_output = self.open_children(elem)
         list_start = elem.attrib.get(moin_page.list_start)
         if list_start:
-            child_out1, child_out2 = childrens_output.split(u'.', 1)
-            childrens_output = u'{0}.#{1}{2}'.format(child_out1, list_start, child_out2)
+            child_out1, child_out2 = childrens_output.split('.', 1)
+            childrens_output = '{0}.#{1}{2}'.format(child_out1, list_start, child_out2)
         self.list_item_labels.pop()
         self.list_level -= 1
         self.status.pop()
         if self.status[-1] == 'list':
-            ret_end = u''
+            ret_end = ''
         else:
-            ret_end = u'\n'
-        return u"{0}{1}{2}".format(ret, childrens_output, ret_end)
+            ret_end = '\n'
+        return "{0}{1}{2}".format(ret, childrens_output, ret_end)
 
     def open_moinpage_list_item(self, elem):
-        self.list_item_label = self.list_item_labels[-1] + u' '
+        self.list_item_label = self.list_item_labels[-1] + ' '
         return self.open_children(elem)
 
     def open_moinpage_list_item_label(self, elem):
         """Used for definition list terms"""
-        ret = u''
-        if self.list_item_labels[-1] == u'' or self.list_item_labels[-1] == Markdown.definition_list_marker:
+        ret = ''
+        if self.list_item_labels[-1] == '' or self.list_item_labels[-1] == Markdown.definition_list_marker:
             self.list_item_labels[-1] = Markdown.definition_list_marker
-            self.list_item_label = self.list_item_labels[-1] + u' '
-            ret = u'   ' * (len(u''.join(self.list_item_labels[:-1])) + len(self.list_item_labels[:-1]))
+            self.list_item_label = self.list_item_labels[-1] + ' '
+            ret = '   ' * (len(''.join(self.list_item_labels[:-1])) + len(self.list_item_labels[:-1]))
             if self.last_closed:
-                ret = u'\n{0}'.format(ret)
+                ret = '\n{0}'.format(ret)
         childrens_output = self.open_children(elem)
         return "\n{0}{1}".format(ret, childrens_output)
 
     def open_moinpage_list_item_body(self, elem):
-        ret = u''
+        ret = ''
         if self.last_closed:
-            ret = u'\n'
-        ret += u'    ' * len(self.list_item_labels[:-2]) + self.list_item_label
+            ret = '\n'
+        ret += '    ' * len(self.list_item_labels[:-2]) + self.list_item_label
         child_out = self.open_children(elem)
         if self.list_item_label[0] == Markdown.definition_list_marker[0]:
-            child_out = u'\n    '.join(child_out.split('\n'))
+            child_out = '\n    '.join(child_out.split('\n'))
         return ret + child_out
 
     def open_moinpage_note(self, elem):
         # used for moinwiki to markdown conversion; not used for broken markdown to markdown conversion
-        class_ = elem.get(moin_page.note_class, u"")
+        class_ = elem.get(moin_page.note_class, "")
         if class_:
             if class_ == "footnote":
                 self.footnote_number += 1
                 self.footnotes.append((self.footnote_number, self.open_children(elem)))
-                return u'[^{0}]'.format(self.footnote_number)
+                return '[^{0}]'.format(self.footnote_number)
         # moinwiki footnote placement is ignored; markdown cannot place footnotes in middle of document like moinwiki
-        return u''
+        return ''
 
     def open_moinpage_nowiki(self, elem):
         """No support for moin features like highlight or nowiki within nowiki."""
-        if isinstance(elem[0], ET.Element) and elem[0].tag.name == 'blockcode' and isinstance(elem[0][0], unicode):
+        if isinstance(elem[0], ET.Element) and elem[0].tag.name == 'blockcode' and isinstance(elem[0][0], str):
             text = elem[0][0].split('\n')
-            return '\n' + '\n'.join([u'    ' + x for x in text]) + '\n'
+            return '\n' + '\n'.join(['    ' + x for x in text]) + '\n'
         return self.open_children(elem)
 
     def open_moinpage_object(self, elem):
@@ -310,32 +309,32 @@ def open_moinpage_object(self, elem):
 
         Transcluded objects are expanded in output because Markdown does not support transclusions.
         """
-        href = elem.get(xlink.href, elem.get(xinclude.href, u''))
+        href = elem.get(xlink.href, elem.get(xinclude.href, ''))
         if isinstance(href, Iri):
-            href = unicode(href)
-            href = urllib.unquote(href)
+            href = str(href)
+            href = urllib.parse.unquote(href)
             if href.startswith('/+get/+'):
                 href = href.split('/')[-1]
-        href = href.split(u'wiki.local:')[-1]
-        if len(elem) and isinstance(elem[0], unicode):
+        href = href.split('wiki.local:')[-1]
+        if len(elem) and isinstance(elem[0], str):
             # alt text for objects is enclosed within ...
alt = elem[0] else: - alt = elem.attrib.get(html.alt, u'') - title = elem.attrib.get(html.title_, u'') + alt = elem.attrib.get(html.alt, '') + title = elem.attrib.get(html.title_, '') if title: - title = u'"{0}"'.format(title) - ret = u'![{0}]({1} {2})'.format(alt, href, title) + title = '"{0}"'.format(title) + ret = '![{0}]({1} {2})'.format(alt, href, title) ret = ret.replace(' )', ')') return ret def open_moinpage_p(self, elem): if moin_page.class_ in elem.attrib and 'moin-error' in elem.attrib[moin_page.class_]: # ignore error messages inserted into DOM - return u'' + return '' self.status.append("p") - ret = u"" + ret = "" if self.status[-2] == 'text': if self.last_closed == 'text': ret = Markdown.p * 2 + self.open_children(elem) + Markdown.p @@ -362,7 +361,7 @@ def open_moinpage_p(self, elem): def open_moinpage_page(self, elem): self.last_closed = None - ret = u'' + ret = '' if len(self.status) > 1: self.status.append('text') childrens_output = self.open_children(elem) @@ -375,39 +374,39 @@ def open_moinpage_page(self, elem): return childrens_output def open_moinpage_body(self, elem): - class_ = elem.get(moin_page.class_, u'').replace(u' ', u'/') + class_ = elem.get(moin_page.class_, '').replace(' ', '/') if class_: - ret = u' {0}\n'.format(class_) + ret = ' {0}\n'.format(class_) elif len(self.status) > 2: - ret = u'\n' + ret = '\n' else: - ret = u'' + ret = '' childrens_output = self.open_children(elem) - return u"{0}{1}".format(ret, childrens_output) + return "{0}{1}".format(ret, childrens_output) def open_moinpage_samp(self, elem): # text {{{more text}}} end ret = Markdown.samp_open - ret += u''.join(elem.itertext()) + ret += ''.join(elem.itertext()) ret += Markdown.samp_close return ret def open_moinpage_separator(self, elem): - return u'\n----\n' + return '\n----\n' def open_moinpage_span(self, elem): - font_size = elem.get(moin_page.font_size, u'') - baseline_shift = elem.get(moin_page.baseline_shift, u'') + font_size = elem.get(moin_page.font_size, '') + baseline_shift = elem.get(moin_page.baseline_shift, '') if font_size: - return u"{0}{1}{2}".format( - Markdown.larger_open if font_size == u"120%" else Markdown.smaller_open, + return "{0}{1}{2}".format( + Markdown.larger_open if font_size == "120%" else Markdown.smaller_open, self.open_children(elem), - Markdown.larger_close if font_size == u"120%" else Markdown.smaller_close) - if baseline_shift == u'super': - return u'{0}'.format(u''.join(elem.itertext())) - if baseline_shift == u'sub': - return u'{0}'.format(u''.join(elem.itertext())) - return u''.join(self.open_children(elem)) + Markdown.larger_close if font_size == "120%" else Markdown.smaller_close) + if baseline_shift == 'super': + return '{0}'.format(''.join(elem.itertext())) + if baseline_shift == 'sub': + return '{0}'.format(''.join(elem.itertext())) + return ''.join(self.open_children(elem)) def open_moinpage_del(self, elem): # stroke or strike-through return Markdown.stroke_open + self.open_children(elem) + Markdown.stroke_close @@ -423,7 +422,7 @@ def open_moinpage_u(self, elem): # underline via html_in def open_moinpage_strong(self, elem): ret = Markdown.strong - return u"{0}{1}{2}".format(Markdown.strong, self.open_children(elem), Markdown.strong) + return "{0}{1}{2}".format(Markdown.strong, self.open_children(elem), Markdown.strong) def open_moinpage_table(self, elem): self.status.append('table') @@ -438,7 +437,7 @@ def open_moinpage_table(self, elem): marker = Markdown.table_marker + Markdown.table_marker.join(['----' for x in cells]) + Markdown.table_marker rows.insert(1, marker) ret = '\n'.join(rows) - return u'\n' + ret + u'\n' + return '\n' + ret + '\n' def open_moinpage_table_header(self, elem): # used for reST to moinwiki conversion, maybe others that generate table head @@ -454,7 +453,7 @@ def open_moinpage_table_header(self, elem): separator.append('------') separator = Markdown.table_marker.join(separator) ret = self.open_children(elem) - ret = ret + u'{0}{1}{0}\n'.format(Markdown.table_marker, separator) + ret = ret + '{0}{1}{0}\n'.format(Markdown.table_marker, separator) return ret def open_moinpage_table_body(self, elem): @@ -463,10 +462,10 @@ def open_moinpage_table_body(self, elem): def open_moinpage_table_row(self, elem): ret = self.open_children(elem, join_char=Markdown.table_marker) - return u'{0}{1}{0}\n'.format(Markdown.table_marker, ret) + return '{0}{1}{0}\n'.format(Markdown.table_marker, ret) def open_moinpage_table_of_content(self, elem): - return u"\n[TOC]\n" + return "\n[TOC]\n" def open_xinclude(self, elem): """Processing of transclusions is similar to objects.""" diff --git a/src/moin/converters/mediawiki_in.py b/src/moin/converters/mediawiki_in.py index 5a77cad49..0f18e867e 100644 --- a/src/moin/converters/mediawiki_in.py +++ b/src/moin/converters/mediawiki_in.py @@ -9,11 +9,8 @@ MoinMoin - Media Wiki input converter """ - -from __future__ import absolute_import, division - import re -from htmlentitydefs import name2codepoint +from html.entities import name2codepoint from werkzeug import url_encode @@ -32,7 +29,7 @@ logging = log.getLogger(__name__) -class _TableArguments(object): +class _TableArguments: rules = r''' (?: (?P @@ -63,8 +60,7 @@ class _TableArguments(object): def arg_repl(self, args, arg, key=None, value_u=None, value_q1=None, value_q2=None): key = self.map_keys.get(key, key) - value = (value_u or value_q1 or value_q2).decode('unicode-escape') - + value = (value_u or value_q1 or value_q2).encode('ascii', errors='backslashreplace').decode('unicode-escape') if key: args.keyword[key] = value else: @@ -80,7 +76,7 @@ def __call__(self, input): args = Arguments() for match in self._re.finditer(input): - data = dict(((str(k), v) for k, v in match.groupdict().iteritems() if v is not None)) + data = dict(((str(k), v) for k, v in match.groupdict().items() if v is not None)) getattr(self, '{0}_repl'.format(match.lastgroup))(args, **data) return args @@ -168,7 +164,7 @@ def block_table_repl(self, iter_content, stack, table, table_args=''): stack.push(elem) if table_args: table_args = _TableArguments()(table_args) - for key, value in table_args.keyword.iteritems(): + for key, value in table_args.keyword.items(): attrib = elem.attrib if key in ('class', 'style', 'number-columns-spanned', 'number-rows-spanned'): attrib[moin_page(key)] = value @@ -198,7 +194,7 @@ def block_table_repl(self, iter_content, stack, table, table_args=''): element = moin_page.table_cell() if len(cell) > 1: cell_args = _TableArguments()(cell[0]) - for key, value in cell_args.keyword.iteritems(): + for key, value in cell_args.keyword.items(): attrib = element.attrib if key in ('class', 'style', 'number-columns-spanned', 'number-rows-spanned'): attrib[moin_page(key)] = value @@ -254,7 +250,10 @@ def indent_iter(self, iter_content, line, level, is_list): yield line while True: - line = iter_content.next() + try: + line = next(iter_content) + except StopIteration: + return match = self.indent_re.match(line) @@ -438,9 +437,9 @@ def inline_entity_repl(self, stack, entity): c = int(entity[3:-1], 16) else: c = int(entity[2:-1], 10) - c = unichr(c) + c = chr(c) else: - c = unichr(name2codepoint.get(entity[1:-1], 0xfffe)) + c = chr(name2codepoint.get(entity[1:-1], 0xfffe)) stack.top_append(c) inline_blockquote = r""" @@ -637,7 +636,7 @@ def parse_args(self, input): return ret def inline_link_repl(self, stack, link, link_url=None, link_item=None, - link_args=u'', external_link_url=None, alt_text=u''): + link_args='', external_link_url=None, alt_text=''): """Handle all kinds of links.""" link_text = '' link_args_list = [] @@ -821,9 +820,9 @@ def inline_nowiki_repl(self, stack, nowiki, nowiki_text=None, # Table row tablerow_re = re.compile(tablerow, re.X | re.U) - class Mediawiki_preprocessor(object): + class Mediawiki_preprocessor: - class Preprocessor_tag(object): + class Preprocessor_tag: def __init__(self, name='', text='', tag='', status=True): self.tag_name = name self.tag = tag @@ -966,7 +965,7 @@ def _apply(self, match, prefix, *args): """ Call the _repl method for the last matched group with the given prefix. """ - data = dict(((str(k), v) for k, v in match.groupdict().iteritems() if v is not None)) + data = dict(((str(k), v) for k, v in match.groupdict().items() if v is not None)) func = '{0}_{1}_repl'.format(prefix, match.lastgroup) # logging.debug("calling %s(%r, %r)" % (func, args, data)) getattr(self, func)(*args, **data) @@ -974,7 +973,7 @@ def _apply(self, match, prefix, *args): def parse_block(self, iter_content, arguments): attrib = {} if arguments: - for key, value in arguments.keyword.iteritems(): + for key, value in arguments.keyword.items(): if key in ('style', ): attrib[moin_page(key)] = value elif key == '_old': @@ -987,7 +986,7 @@ def parse_block(self, iter_content, arguments): for line in iter_content: match = self.indent_re.match(line) if match: - data = dict(((str(k), v) for k, v in match.groupdict().iteritems() if v is not None)) + data = dict(((str(k), v) for k, v in match.groupdict().items() if v is not None)) self.indent_repl(iter_content, stack, line, **data) else: self.indent_repl(iter_content, stack, line, '', line) diff --git a/src/moin/converters/moinwiki19_in.py b/src/moin/converters/moinwiki19_in.py index c41594c96..7fb264cba 100644 --- a/src/moin/converters/moinwiki19_in.py +++ b/src/moin/converters/moinwiki19_in.py @@ -8,9 +8,6 @@ MoinMoin - Moin Wiki 1.9 input converter """ - -from __future__ import absolute_import, division - import re from moin import wikiutil @@ -94,11 +91,10 @@ def inline_freelink_repl(self, stack, freelink, freelink_bang=None, attrib = {} if freelink_page: - page = freelink_page.encode('utf-8') - if '#' in page: - path, fragment = page.rsplit('#', 1) + if '#' in freelink_page: + path, fragment = freelink_page.rsplit('#', 1) else: - path, fragment = page, None + path, fragment = freelink_page, None link = Iri(scheme='wiki.local', path=path, fragment=fragment) text = freelink_page diff --git a/src/moin/converters/moinwiki_in.py b/src/moin/converters/moinwiki_in.py index 2687e3c65..adf57a520 100644 --- a/src/moin/converters/moinwiki_in.py +++ b/src/moin/converters/moinwiki_in.py @@ -8,9 +8,6 @@ MoinMoin - Moin Wiki input converter """ - -from __future__ import absolute_import, division - import re from flask import request @@ -35,7 +32,7 @@ logging = log.getLogger(__name__) -class _TableArguments(object): +class _TableArguments: rules = r''' (?: - (?P \d+) @@ -86,7 +83,7 @@ class _TableArguments(object): def arg_repl(self, args, arg, key=None, value_u=None, value_q1=None, value_q2=None): key = self.map_keys.get(key, key) - value = (value_u or value_q1 or value_q2).decode('unicode-escape') + value = (value_u or value_q1 or value_q2).encode('ascii', errors='backslashreplace').decode('unicode-escape') if key: args.keyword[key] = value else: @@ -129,7 +126,7 @@ def __call__(self, input): args = Arguments() for match in self._re.finditer(input): - data = dict(((str(k), v) for k, v in match.groupdict().iteritems() if v is not None)) + data = dict(((str(k), v) for k, v in match.groupdict().items() if v is not None)) getattr(self, '{0}_repl'.format(match.lastgroup))(args, **data) return args @@ -214,7 +211,7 @@ def block_line_repl(self, _iter_content, stack, line): $ """ - def block_macro_repl(self, _iter_content, stack, macro, macro_name, macro_args=u''): + def block_macro_repl(self, _iter_content, stack, macro, macro_name, macro_args=''): """Handles macros using the placeholder syntax. Arguments are passed as a single positional parameter, each macro must parse as required. @@ -274,7 +271,7 @@ def block_nowiki_repl(self, iter_content, stack, nowiki, nowiki_marker, optional_args=None): stack.clear() lines = _Iter(self.block_nowiki_lines(iter_content, len(nowiki_marker)), startno=iter_content.lineno) - content = u'\n'.join(lines) + content = '\n'.join(lines) # the arguments for wiki, csv, and highlight are diverse, one parser does not fit all # we push everything after {{{ to DOM; nowiki.py can insert error messages or moinwiki_out can recreate exact input all_nowiki_args = moin_page.nowiki_args(children=(nowiki_interpret, )) @@ -284,7 +281,7 @@ def block_nowiki_repl(self, iter_content, stack, nowiki, nowiki_marker, block_separator = r'(?P ^ \s* -{4,} \s* $ )' - def block_separator_repl(self, _iter_content, stack, separator, hr_class=u'moin-hr{0}'): + def block_separator_repl(self, _iter_content, stack, separator, hr_class='moin-hr{0}'): stack.clear() hr_height = min((len(separator) - 3), 6) hr_height = max(hr_height, 1) @@ -317,7 +314,7 @@ def block_separator_repl(self, _iter_content, stack, separator, hr_class=u'moin- def block_table_repl(self, iter_content, stack, table): stack.clear() - element = moin_page.table(attrib={moin_page('class'): u'moin-wiki-table'}) + element = moin_page.table(attrib={moin_page('class'): 'moin-wiki-table'}) stack.push(element) stack.push(moin_page.table_body()) @@ -393,7 +390,10 @@ def indent_iter(self, iter_content, line, level): yield line while True: - line = iter_content.next() + try: + line = next(iter_content) + except StopIteration: + return match = self.indent_re.match(line) @@ -432,7 +432,7 @@ def indent_repl(self, iter_content, stack, line, list_type = 'ordered', 'upper-roman' elif list_roman: list_type = 'ordered', 'lower-roman' - elif list_bullet == u'*': + elif list_bullet == '*': list_type = 'unordered', None element_use = None @@ -583,10 +583,10 @@ def inline_entity_repl(self, stack, entity): c = int(entity[3:-1], 16) else: c = int(entity[2:-1], 10) - c = unichr(c) + c = chr(c) else: - from htmlentitydefs import name2codepoint - c = unichr(name2codepoint.get(entity[1:-1], 0xfffe)) + from html.entities import name2codepoint + c = chr(name2codepoint.get(entity[1:-1], 0xfffe)) stack.top_append(c) inline_size = r""" @@ -776,7 +776,7 @@ def inline_link_repl(self, stack, link, link_url=None, link_item=None, ) """ - def inline_macro_repl(self, stack, macro, macro_name, macro_args=u''): + def inline_macro_repl(self, stack, macro, macro_name, macro_args=''): """Handles macros using the placeholder syntax.""" elem = self.macro(macro_name, macro_args, macro) stack.top_append(elem) @@ -842,7 +842,7 @@ def inline_object_repl(self, stack, object, object_url=None, object_item=None, query_keys = {} attrib = {} whitelist = ['width', 'height', 'class'] - for attr, value in args.iteritems(): + for attr, value in args.items(): if attr.startswith('&'): query_keys[attr[1:]] = value elif attr in whitelist: @@ -891,7 +891,7 @@ def inline_object_repl(self, stack, object, object_url=None, object_item=None, def tablerow_cell_repl(self, stack, table, row, cell, cell_marker, cell_text, cell_args=None): def add_attr_to_style(attrib, attr): - attr = attr.strip().decode('unicode-escape') + attr = attr.strip().encode('ascii', errors='backslashreplace').decode('unicode-escape') if not attr.endswith(';'): attr += ';' if attrib.get(moin_page('style'), ""): @@ -926,7 +926,7 @@ def add_attr_to_style(attrib, attr): elif key == 'caption': table.insert(0, moin_page.caption(children=[value, ])) elif key == 'tableclass': - table.attrib[moin_page('class')] = value + u' moin-wiki-table' + table.attrib[moin_page('class')] = value + ' moin-wiki-table' elif key == 'rowclass': row.attrib[moin_page('class')] = value elif key == 'class': @@ -1029,7 +1029,7 @@ def _apply(self, match, prefix, *args): """ Call the _repl method for the last matched group with the given prefix. """ - data = dict(((str(k), v) for k, v in match.groupdict().iteritems() if v is not None)) + data = dict(((str(k), v) for k, v in match.groupdict().items() if v is not None)) func = '{0}_{1}_repl'.format(prefix, match.lastgroup) # logging.debug("calling %s(%r, %r)" % (func, args, data)) getattr(self, func)(*args, **data) @@ -1037,7 +1037,7 @@ def _apply(self, match, prefix, *args): def parse_block(self, iter_content, arguments): attrib = {} if arguments: - for key, value in arguments.keyword.iteritems(): + for key, value in arguments.keyword.items(): if key in ('style', 'class', ): attrib[moin_page(key)] = value @@ -1045,7 +1045,7 @@ def parse_block(self, iter_content, arguments): stack = _Stack(body, iter_content=iter_content) for line in iter_content: - data = dict(((str(k), v) for k, v in self.indent_re.match(line).groupdict().iteritems() if v is not None)) + data = dict(((str(k), v) for k, v in self.indent_re.match(line).groupdict().items() if v is not None)) self.indent_repl(iter_content, stack, line, **data) return body diff --git a/src/moin/converters/moinwiki_out.py b/src/moin/converters/moinwiki_out.py index f446e29bb..97f7bf9b5 100644 --- a/src/moin/converters/moinwiki_out.py +++ b/src/moin/converters/moinwiki_out.py @@ -8,10 +8,9 @@ Converts an internal document tree into moinwiki markup. """ - -from __future__ import absolute_import, division - -import urllib +import urllib.request +import urllib.parse +import urllib.error from re import findall, sub from emeraldtree import ElementTree as ET @@ -25,54 +24,54 @@ from . import default_registry -class Moinwiki(object): +class Moinwiki: """ Moinwiki syntax elements It's dummy """ - h = u'=' - a_open = u'[[' - a_separator = u'|' - a_close = u']]' - verbatim_open = u'{' # * 3 - verbatim_close = u'}' # * 3 - monospace = u'`' - strong = u"'''" - emphasis = u"''" - underline = u'__' - samp_open = u'{{{' # 3 brackets is only option for inline - samp_close = u'}}}' - stroke_open = u'--(' - stroke_close = u')--' - table_marker = u'||' - p = u'\n' - linebreak = u'<
>' - larger_open = u'~+' - larger_close = u'+~' - smaller_open = u'~-' - smaller_close = u'-~' - object_open = u'{{' - object_close = u'}}' - definition_list_marker = u'::' - separator = u'----' + h = '=' + a_open = '[[' + a_separator = '|' + a_close = ']]' + verbatim_open = '{' # * 3 + verbatim_close = '}' # * 3 + monospace = '`' + strong = "'''" + emphasis = "''" + underline = '__' + samp_open = '{{{' # 3 brackets is only option for inline + samp_close = '}}}' + stroke_open = '--(' + stroke_close = ')--' + table_marker = '||' + p = '\n' + linebreak = '<
>' + larger_open = '~+' + larger_close = '+~' + smaller_open = '~-' + smaller_close = '-~' + object_open = '{{' + object_close = '}}' + definition_list_marker = '::' + separator = '----' # TODO: definition list list_type = { - (u'definition', None): u'', - (u'ordered', None): u'1.', - (u'ordered', u'lower-alpha'): u'a.', - (u'ordered', u'upper-alpha'): u'A.', - (u'ordered', u'lower-roman'): u'i.', - (u'ordered', u'upper-roman'): u'I.', - (u'unordered', None): u'*', - (u'unordered', u'no-bullet'): u'.', - (None, None): u'::', + ('definition', None): '', + ('ordered', None): '1.', + ('ordered', 'lower-alpha'): 'a.', + ('ordered', 'upper-alpha'): 'A.', + ('ordered', 'lower-roman'): 'i.', + ('ordered', 'upper-roman'): 'I.', + ('unordered', None): '*', + ('unordered', 'no-bullet'): '.', + (None, None): '::', } def __init__(self): pass -class Converter(object): +class Converter: """ Converter application/x.moin.document -> text/x.moin.wiki """ @@ -120,15 +119,15 @@ def factory(cls, input, output, **kw): def __init__(self): # TODO: create class containing all table attributes - self.table_tableclass = u'' - self.table_tablestyle = u'' - self.table_rowsclass = u'' - self.table_rowsstyle = u'' - self.table_rowstyle = u'' - self.table_rowclass = u'' - - self.list_item_labels = [u'', ] - self.list_item_label = u'' + self.table_tableclass = '' + self.table_tablestyle = '' + self.table_rowsclass = '' + self.table_rowsstyle = '' + self.table_rowstyle = '' + self.table_rowclass = '' + + self.list_item_labels = ['', ] + self.list_item_label = '' self.list_level = 0 # 'text' - default status -

= '/n' and

= '/n' @@ -152,15 +151,15 @@ def open_children(self, elem): # open function can change self.output childrens_output.append(self.open(child)) else: - ret = u'' + ret = '' if self.status[-1] == "text": if self.last_closed == "p": - ret = u'\n' + ret = '\n' if child == '\n' and getattr(elem, 'level', 0): - child = child + ' ' * (len(u''.join(self.list_item_labels[:-1])) + len(self.list_item_labels[:-1])) - childrens_output.append(u'{0}{1}'.format(ret, child)) + child = child + ' ' * (len(''.join(self.list_item_labels[:-1])) + len(self.list_item_labels[:-1])) + childrens_output.append('{0}{1}'.format(ret, child)) self.last_closed = 'text' - out = u''.join(childrens_output) + out = ''.join(childrens_output) return out def open(self, elem): @@ -192,31 +191,31 @@ def open_moinpage_a(self, elem): params['class'] = elem.get(html.class_, None) params['accesskey'] = elem.get(html.accesskey, None) # we sort so output order is predictable for tests - params = u','.join([u'{0}="{1}"'.format(p, params[p]) for p in sorted(params) if params[p]]) + params = ','.join(['{0}="{1}"'.format(p, params[p]) for p in sorted(params) if params[p]]) # XXX: We don't have Iri support for now if isinstance(href, Iri): - href = unicode(href) + href = str(href) # TODO: this can be done using one regex, can it? - href = href.split(u'#') + href = href.split('#') if len(href) > 1: href, fragment = href else: href, fragment = href[0], '' - href = href.split(u'?') - args = u'' + href = href.split('?') + args = '' if len(href) > 1: # With normal - args = u''.join([u'&' + s for s in findall(r'(?:^|;|,|&|)(\w+=\w+)(?:,|&|$|)', href[1])]) - href = href[0].split(u'wiki.local:')[-1] + args = ''.join(['&' + s for s in findall(r'(?:^|;|,|&|)(\w+=\w+)(?:,|&|$|)', href[1])]) + href = href[0].split('wiki.local:')[-1] if args: args = '?' + args[1:] if fragment: args += '#' + fragment text = self.open_children(elem) if text == href: - text = u'' - ret = u'{0}{1}|{2}|{3}'.format(href, args, text, params) + text = '' + ret = '{0}{1}|{2}|{3}'.format(href, args, text, params) ret = ret.rstrip('|') if ret.startswith('wiki://'): # interwiki fixup @@ -225,12 +224,12 @@ def open_moinpage_a(self, elem): return Moinwiki.a_open + ret + Moinwiki.a_close def open_moinpage_blockcode(self, elem): - text = u''.join(elem.itertext()) + text = ''.join(elem.itertext()) max_subpage_lvl = 3 for s in findall(r'}+', text): if max_subpage_lvl <= len(s): max_subpage_lvl = len(s) + 1 - ret = u'{0}\n{1}\n{2}\n'.format( + ret = '{0}\n{1}\n{2}\n'.format( Moinwiki.verbatim_open * max_subpage_lvl, text, Moinwiki.verbatim_close * max_subpage_lvl) return '\n' + ret + '\n' @@ -247,12 +246,12 @@ def open_moinpage_blockquote(self, elem): ret = ret.split('<
>') indented = [] for line in ret: - indented.append(u' . ' + line) + indented.append(' . ' + line) return '\n\n' + '\n'.join(indented) + '\n\n' def open_moinpage_code(self, elem): ret = Moinwiki.monospace - ret += u''.join(elem.itertext()) + ret += ''.join(elem.itertext()) ret += Moinwiki.monospace return ret @@ -262,7 +261,7 @@ def open_moinpage_div(self, elem): def open_moinpage_emphasis(self, elem): childrens_output = self.open_children(elem) - return u"{0}{1}{2}".format(Moinwiki.emphasis, childrens_output, Moinwiki.emphasis) + return "{0}{1}{2}".format(Moinwiki.emphasis, childrens_output, Moinwiki.emphasis) def open_moinpage_h(self, elem): level = elem.get(moin_page.outline_level, 1) @@ -274,10 +273,10 @@ def open_moinpage_h(self, elem): level = 1 elif level > 6: level = 6 - ret = Moinwiki.h * level + u' ' - ret += u''.join(elem.itertext()) - ret += u' {0}\n'.format(Moinwiki.h * level) - return u'\n' + ret + ret = Moinwiki.h * level + ' ' + ret += ''.join(elem.itertext()) + ret += ' {0}\n'.format(Moinwiki.h * level) + return '\n' + ret def open_moinpage_line_break(self, elem): return Moinwiki.linebreak @@ -285,57 +284,57 @@ def open_moinpage_line_break(self, elem): def open_moinpage_list(self, elem): label_type = elem.get(moin_page.item_label_generate, None), elem.get(moin_page.list_style_type, None) self.list_item_labels.append( - Moinwiki.list_type.get(label_type, u'')) + Moinwiki.list_type.get(label_type, '')) self.list_level += 1 - ret = u'' + ret = '' if self.status[-1] != 'text' or self.last_closed: - ret = u'\n' + ret = '\n' self.status.append('list') self.last_closed = None childrens_output = self.open_children(elem) list_start = elem.attrib.get(moin_page.list_start) if list_start: - child_out1, child_out2 = childrens_output.split(u'.', 1) - childrens_output = u'{0}.#{1}{2}'.format(child_out1, list_start, child_out2) + child_out1, child_out2 = childrens_output.split('.', 1) + childrens_output = '{0}.#{1}{2}'.format(child_out1, list_start, child_out2) self.list_item_labels.pop() self.list_level -= 1 self.status.pop() if self.status[-1] == 'list': - ret_end = u'' + ret_end = '' else: - ret_end = u'\n' - return u"{0}{1}{2}".format(ret, childrens_output, ret_end) + ret_end = '\n' + return "{0}{1}{2}".format(ret, childrens_output, ret_end) def open_moinpage_list_item(self, elem): - self.list_item_label = self.list_item_labels[-1] + u' ' + self.list_item_label = self.list_item_labels[-1] + ' ' return self.open_children(elem) def open_moinpage_list_item_label(self, elem): - ret = u'' - if self.list_item_labels[-1] == u'' or self.list_item_labels[-1] == Moinwiki.definition_list_marker: + ret = '' + if self.list_item_labels[-1] == '' or self.list_item_labels[-1] == Moinwiki.definition_list_marker: self.list_item_labels[-1] = Moinwiki.definition_list_marker - self.list_item_label = self.list_item_labels[-1] + u' ' - ret = u' ' * (len(u''.join(self.list_item_labels[:-1])) + - len(self.list_item_labels[:-1])) # self.list_level + self.list_item_label = self.list_item_labels[-1] + ' ' + ret = ' ' * (len(''.join(self.list_item_labels[:-1])) + + len(self.list_item_labels[:-1])) # self.list_level if self.last_closed: - ret = u'\n{0}'.format(ret) + ret = '\n{0}'.format(ret) childrens_output = self.open_children(elem) return "{0}{1}{2}".format(ret, childrens_output, Moinwiki.definition_list_marker) def open_moinpage_list_item_body(self, elem): - ret = u'' + ret = '' if self.last_closed: - ret = u'\n' - ret += u' ' * (len(u''.join(self.list_item_labels[:-1])) + - len(self.list_item_labels[:-1])) + self.list_item_label + ret = '\n' + ret += ' ' * (len(''.join(self.list_item_labels[:-1])) + + len(self.list_item_labels[:-1])) + self.list_item_label return ret + self.open_children(elem) def open_moinpage_note(self, elem): - class_ = elem.get(moin_page.note_class, u"") + class_ = elem.get(moin_page.note_class, "") if class_: if class_ == "footnote": - return u'<>'.format(self.open_children(elem)) - return u'\n<>\n' + return '<>'.format(self.open_children(elem)) + return '\n<>\n' def open_moinpage_nowiki(self, elem): """{{{#!wiki ... or {{{#!highlight ... etc.""" @@ -346,8 +345,8 @@ def open_moinpage_nowiki(self, elem): # this happens only with pytest, why wasn't open_moinpage_blockcode called? nowiki_args = '' nowiki_marker_len = int(nowiki_marker_len) - return u'\n' + Moinwiki.verbatim_open * nowiki_marker_len + u'{0}\n{1}\n'.format(nowiki_args, content) + \ - Moinwiki.verbatim_close * nowiki_marker_len + u'\n' + return '\n' + Moinwiki.verbatim_open * nowiki_marker_len + '{0}\n{1}\n'.format(nowiki_args, content) + \ + Moinwiki.verbatim_close * nowiki_marker_len + '\n' def include_object(self, xpointer, href): """ @@ -376,7 +375,7 @@ def include_object(self, xpointer, href): if not href and 'pages' in arguments: # xpointer needs unescaping, see comments above href = arguments['pages'].replace('^(', '(').replace('^)', ')').replace('^^', '^') - return u'<>'.format(href, parms) + return '<>'.format(href, parms) def open_moinpage_object(self, elem): """ @@ -384,27 +383,27 @@ def open_moinpage_object(self, elem): Other macros are processes by open_moinpage_part. """ - href = elem.get(xlink.href, elem.get(xinclude.href, u'')) + href = elem.get(xlink.href, elem.get(xinclude.href, '')) if isinstance(href, Iri): - href = unicode(href) - href = urllib.unquote(href) + href = str(href) + href = urllib.parse.unquote(href) try: return self.include_object(elem.attrib[xinclude.xpointer], href) except KeyError: pass - href = href.split(u'?') - args = u'' + href = href.split('?') + args = '' if len(href) > 1: - args = u' '.join([s for s in findall(r'(?:^|;|,|&|)(\w+=\w+)(?:,|&|$)', href[1]) if s[:3] != u'do=']) - href = href[0].split(u'wiki.local:')[-1] + args = ' '.join([s for s in findall(r'(?:^|;|,|&|)(\w+=\w+)(?:,|&|$)', href[1]) if s[:3] != 'do=']) + href = href[0].split('wiki.local:')[-1] - if len(elem) and isinstance(elem[0], unicode): + if len(elem) and isinstance(elem[0], str): # alt text for objects is enclosed within ...
alt = elem[0] else: - alt = elem.attrib.get(html.alt, u'') + alt = elem.attrib.get(html.alt, '') whitelist = {html.width: 'width', html.height: 'height', html.class_: 'class'} options = [] @@ -413,23 +412,23 @@ def open_moinpage_object(self, elem): options.append('{0}="{1}"'.format(whitelist[attr], value)) if args: - args = u'&' + args + args = '&' + args if options: if args: - args += u' ' - args += u' '.join(options) + args += ' ' + args += ' '.join(options) - ret = u'{0}{1}|{2}|{3}{4}'.format(Moinwiki.object_open, href, alt, args, Moinwiki.object_close) + ret = '{0}{1}|{2}|{3}{4}'.format(Moinwiki.object_open, href, alt, args, Moinwiki.object_close) ret = sub(r"\|+}}", "}}", ret) return ret def open_moinpage_p(self, elem): if moin_page.class_ in elem.attrib and 'moin-error' in elem.attrib[moin_page.class_]: # ignore error messages inserted into DOM - return u'' + return '' self.status.append("p") - ret = u"" + ret = "" if self.status[-2] == 'text': if self.last_closed == 'text': ret = Moinwiki.p * 2 + self.open_children(elem) + Moinwiki.p @@ -456,9 +455,9 @@ def open_moinpage_p(self, elem): def open_moinpage_page(self, elem): self.last_closed = None - ret = u'' + ret = '' if len(self.status) > 1: - ret = u"#!wiki" + ret = "#!wiki" max_subpage_lvl = 3 self.status.append('text') childrens_output = self.open_children(elem) @@ -466,7 +465,7 @@ def open_moinpage_page(self, elem): for s in findall(r'}+', childrens_output): if max_subpage_lvl <= len(s): max_subpage_lvl = len(s) + 1 - return u'{0}{1}{2}{3}\n'.format( + return '{0}{1}{2}{3}\n'.format( Moinwiki.verbatim_open * max_subpage_lvl, ret, childrens_output, Moinwiki.verbatim_close * max_subpage_lvl) @@ -477,56 +476,56 @@ def open_moinpage_page(self, elem): return childrens_output def open_moinpage_body(self, elem): - class_ = elem.get(moin_page.class_, u'').replace(u' ', u'/') + class_ = elem.get(moin_page.class_, '').replace(' ', '/') if class_: - ret = u' {0}\n'.format(class_) + ret = ' {0}\n'.format(class_) elif len(self.status) > 2: - ret = u'\n' + ret = '\n' else: - ret = u'' + ret = '' childrens_output = self.open_children(elem) - return u"{0}{1}".format(ret, childrens_output) + return "{0}{1}".format(ret, childrens_output) def open_moinpage_part(self, elem): - type = elem.get(moin_page.content_type, u"").split(u';') + type = elem.get(moin_page.content_type, "").split(';') if len(type) == 2: if type[0] == "x-moin/macro": - name = type[1].split(u'=')[1] + name = type[1].split('=')[1] eol = '\n\n' if elem.tag.name == 'part' else '' if len(elem) and elem[0].tag.name == "arguments": - return u"{0}<<{1}({2})>>{0}".format( + return "{0}<<{1}({2})>>{0}".format( eol, name, elem[0][0]) else: - return u"{0}<<{1}()>>{0}".format(eol, name) + return "{0}<<{1}()>>{0}".format(eol, name) elif type[0] == "x-moin/format": elem_it = iter(elem) - ret = u"{{{{{{#!{0}".format(type[1].split(u'=')[1]) - if len(elem) and elem_it.next().tag.name == "arguments": + ret = "{{{{{{#!{0}".format(type[1].split('=')[1]) + if len(elem) and next(elem_it).tag.name == "arguments": args = [] - for arg in iter(elem).next(): + for arg in next(iter(elem)): if arg.tag.name == "argument": - args.append(u"{0}=\"{1}\"".format(arg.get(moin_page.name, u""), u' '.join(arg.itertext()))) - ret = u'{0}({1})'.format(ret, u' '.join(args)) - elem = elem_it.next() - ret = u"{0}\n{1}\n}}}}}}\n".format(ret, u' '.join(elem.itertext())) + args.append("{0}=\"{1}\"".format(arg.get(moin_page.name, ""), ' '.join(arg.itertext()))) + ret = '{0}({1})'.format(ret, ' '.join(args)) + elem = next(elem_it) + ret = "{0}\n{1}\n}}}}}}\n".format(ret, ' '.join(elem.itertext())) return ret - return unescape(elem.get(moin_page.alt, u'')) + u"\n" + return unescape(elem.get(moin_page.alt, '')) + "\n" def open_moinpage_inline_part(self, elem): ret = self.open_moinpage_part(elem) - if ret[-1] == u'\n': + if ret[-1] == '\n': ret = ret[:-1] return ret def open_moinpage_samp(self, elem): # text {{{more text}}} end ret = Moinwiki.samp_open - ret += u''.join(elem.itertext()) + ret += ''.join(elem.itertext()) ret += Moinwiki.samp_close return ret - def open_moinpage_separator(self, elem, hr_class_prefix=u'moin-hr'): - hr_ending = u'\n' + def open_moinpage_separator(self, elem, hr_class_prefix='moin-hr'): + hr_ending = '\n' hr_class = elem.attrib.get(moin_page('class')) if hr_class: try: @@ -535,25 +534,25 @@ def open_moinpage_separator(self, elem, hr_class_prefix=u'moin-hr'): raise ElementException('page:separator has invalid class {0}'.format(hr_class)) else: if 0 <= height <= 5: - hr_ending = (u'-' * height) + hr_ending + hr_ending = ('-' * height) + hr_ending return Moinwiki.separator + hr_ending def open_moinpage_span(self, elem): - font_size = elem.get(moin_page.font_size, u'') - baseline_shift = elem.get(moin_page.baseline_shift, u'') - class_ = elem.get(moin_page.class_, u'') + font_size = elem.get(moin_page.font_size, '') + baseline_shift = elem.get(moin_page.baseline_shift, '') + class_ = elem.get(moin_page.class_, '') if class_ == 'comment': - return u'/* {0} */'.format(self.open_children(elem)) + return '/* {0} */'.format(self.open_children(elem)) if font_size: - return u"{0}{1}{2}".format( - Moinwiki.larger_open if font_size == u"120%" else Moinwiki.smaller_open, + return "{0}{1}{2}".format( + Moinwiki.larger_open if font_size == "120%" else Moinwiki.smaller_open, self.open_children(elem), - Moinwiki.larger_close if font_size == u"120%" else Moinwiki.smaller_close) - if baseline_shift == u'super': - return u'^{0}^'.format(u''.join(elem.itertext())) - if baseline_shift == u'sub': - return u',,{0},,'.format(u''.join(elem.itertext())) - return u''.join(self.open_children(elem)) + Moinwiki.larger_close if font_size == "120%" else Moinwiki.smaller_close) + if baseline_shift == 'super': + return '^{0}^'.format(''.join(elem.itertext())) + if baseline_shift == 'sub': + return ',,{0},,'.format(''.join(elem.itertext())) + return ''.join(self.open_children(elem)) def open_moinpage_del(self, elem): # stroke or strike-through return Moinwiki.stroke_open + self.open_children(elem) + Moinwiki.stroke_close @@ -569,60 +568,60 @@ def open_moinpage_u(self, elem): # underline via html_in def open_moinpage_strong(self, elem): ret = Moinwiki.strong - return u"{0}{1}{2}".format(Moinwiki.strong, self.open_children(elem), Moinwiki.strong) + return "{0}{1}{2}".format(Moinwiki.strong, self.open_children(elem), Moinwiki.strong) def open_moinpage_table(self, elem): - self.table_tableclass = elem.attrib.get(moin_page.class_, u'') + self.table_tableclass = elem.attrib.get(moin_page.class_, '') # moin-wiki-table class was added by moinwiki_in so html_out can convert multiple body's into head, foot - self.table_tableclass = self.table_tableclass.replace(u'moin-wiki-table', u'').strip() - self.table_tablestyle = elem.attrib.get(moin_page.style, u'') + self.table_tableclass = self.table_tableclass.replace('moin-wiki-table', '').strip() + self.table_tablestyle = elem.attrib.get(moin_page.style, '') if elem[0].tag == moin_page.caption: self.table_caption = elem[0][0] else: - self.table_caption = u'' - self.table_rowsstyle = u'' - self.table_rowsclass = u'' - self.table_multi_body = u'' + self.table_caption = '' + self.table_rowsstyle = '' + self.table_rowsclass = '' + self.table_multi_body = '' self.status.append('table') self.last_closed = None ret = self.open_children(elem) self.status.pop() - return u'\n' + ret + u'\n' + return '\n' + ret + '\n' def open_moinpage_caption(self, elem): # return empty string, text has already been processed in open_moinpage_table above - return u'' + return '' def open_moinpage_table_header(self, elem): # used for reST to moinwiki conversion, maybe others that generate table head ret = self.open_children(elem) - return ret + u'=====\n' + return ret + '=====\n' def open_moinpage_table_footer(self, elem): # no known use, need some markup that generates table foot ret = self.open_children(elem) - return u'=====\n' + ret + return '=====\n' + ret def open_moinpage_table_body(self, elem): self.table_rowsclass = '' ret = self.table_multi_body + self.open_children(elem) # multible body elements separate header/body/footer within DOM created by moinwiki_in - self.table_multi_body = u'=====\n' + self.table_multi_body = '=====\n' return ret def open_moinpage_table_row(self, elem): - self.table_rowclass = elem.attrib.get(moin_page.class_, u'') - self.table_rowclass = u' '.join([s for s in [self.table_rowsclass, self.table_rowclass] if s]) - self.table_rowstyle = elem.attrib.get(moin_page.style, u'') - self.table_rowstyle = u' '.join([s for s in [self.table_rowsstyle, self.table_rowstyle] if s]) + self.table_rowclass = elem.attrib.get(moin_page.class_, '') + self.table_rowclass = ' '.join([s for s in [self.table_rowsclass, self.table_rowclass] if s]) + self.table_rowstyle = elem.attrib.get(moin_page.style, '') + self.table_rowstyle = ' '.join([s for s in [self.table_rowsstyle, self.table_rowstyle] if s]) ret = self.open_children(elem) - self.table_rowstyle = u'' - self.table_rowclass = u'' - return ret + Moinwiki.table_marker + u'\n' + self.table_rowstyle = '' + self.table_rowclass = '' + return ret + Moinwiki.table_marker + '\n' def open_moinpage_table_cell(self, elem): - table_cellclass = elem.attrib.get(moin_page.class_, u'') - table_cellstyle = elem.attrib.get(moin_page.style, u'') + table_cellclass = elem.attrib.get(moin_page.class_, '') + table_cellstyle = elem.attrib.get(moin_page.style, '') number_columns_spanned = int(elem.get(moin_page.number_columns_spanned, 1)) number_rows_spanned = elem.get(moin_page.number_rows_spanned, None) ret = Moinwiki.table_marker @@ -630,38 +629,38 @@ def open_moinpage_table_cell(self, elem): # TODO: maybe this can be written shorter if self.table_tableclass: - attrib.append(u'tableclass="{0}"'.format(self.table_tableclass)) - self.table_tableclass = u'' + attrib.append('tableclass="{0}"'.format(self.table_tableclass)) + self.table_tableclass = '' if self.table_tablestyle: - attrib.append(u'tablestyle="{0}"'.format(self.table_tablestyle)) - self.table_tablestyle = u'' + attrib.append('tablestyle="{0}"'.format(self.table_tablestyle)) + self.table_tablestyle = '' if self.table_caption: - attrib.append(u'caption="{0}"'.format(self.table_caption)) - self.table_caption = u'' + attrib.append('caption="{0}"'.format(self.table_caption)) + self.table_caption = '' if self.table_rowclass: - attrib.append(u'rowclass="{0}"'.format(self.table_rowclass)) - self.table_rowclass = u'' + attrib.append('rowclass="{0}"'.format(self.table_rowclass)) + self.table_rowclass = '' if self.table_rowstyle: - attrib.append(u'rowstyle="{0}"'.format(self.table_rowstyle)) - self.table_rowstyle = u'' + attrib.append('rowstyle="{0}"'.format(self.table_rowstyle)) + self.table_rowstyle = '' if table_cellclass: - attrib.append(u'class="{0}"'.format(table_cellclass)) + attrib.append('class="{0}"'.format(table_cellclass)) if table_cellstyle: - attrib.append(u'style="{0}"'.format(table_cellstyle)) + attrib.append('style="{0}"'.format(table_cellstyle)) if number_rows_spanned: - attrib.append(u'rowspan="{0}"'.format(number_rows_spanned)) + attrib.append('rowspan="{0}"'.format(number_rows_spanned)) if number_columns_spanned > 1: - attrib.append(u'colspan="{0}"'.format(number_columns_spanned)) + attrib.append('colspan="{0}"'.format(number_columns_spanned)) - attrib = u' '.join(attrib) + attrib = ' '.join(attrib) if attrib: - ret += u'<{0}>'.format(attrib) + ret += '<{0}>'.format(attrib) childrens_output = self.open_children(elem) return ret + childrens_output def open_moinpage_table_of_content(self, elem): - return u"<>\n".format(elem.get(moin_page.outline_level, u"")) + return "<>\n".format(elem.get(moin_page.outline_level, "")) def open_xinclude(self, elem): """Processing of transclusions is similar to objects.""" diff --git a/src/moin/converters/nonexistent_in.py b/src/moin/converters/nonexistent_in.py index c20957acf..e769d68ee 100644 --- a/src/moin/converters/nonexistent_in.py +++ b/src/moin/converters/nonexistent_in.py @@ -19,7 +19,7 @@ from . import default_registry -class Converter(object): +class Converter: """ Convert a non-existing item to DOM Tree. """ diff --git a/src/moin/converters/nowiki.py b/src/moin/converters/nowiki.py index b8b75bed7..75caf9f88 100644 --- a/src/moin/converters/nowiki.py +++ b/src/moin/converters/nowiki.py @@ -7,8 +7,6 @@ Expands nowiki elements in an internal Moin document. """ -from __future__ import absolute_import, division - import re from emeraldtree import ElementTree as ET @@ -30,7 +28,7 @@ logging = log.getLogger(__name__) -class Converter(object): +class Converter: @classmethod def _factory(cls, input, output, nowiki=None, **kw): if nowiki == 'expandall': @@ -65,12 +63,12 @@ def handle_nowiki(self, elem, page): nowiki_name = optional_args = None lexer = None - if nowiki_name in set(('diff', 'cplusplus', 'python', 'java', 'pascal', 'irc')): + if nowiki_name in {'diff', 'cplusplus', 'python', 'java', 'pascal', 'irc'}: # make old style markup similar to {{{#!python like new style {{{#!highlight python optional_args = nowiki_name if not optional_args else nowiki_name + ' ' + optional_args nowiki_name = 'highlight' - if nowiki_name == u'highlight': + if nowiki_name == 'highlight': # TODO: support moin 1.9 options like numbers=on start=222 step=10 optional_args = optional_args.split()[0] # ignore all parameters except lexer name try: @@ -98,7 +96,7 @@ def handle_nowiki(self, elem, page): delim = optional_args.split()[0] # ignore all parameters except a delimiter in first position if len(delim) > 1: delim = None - sep = delim or u';' + sep = delim or ';' content = content.split('\n') head = content[0].split(sep) rows = [x.split(sep) for x in content[1:]] @@ -115,7 +113,7 @@ def handle_nowiki(self, elem, page): # reparse arguments from original: {{{#!wiki solid/orange (style="color: red;") wiki_args = parse_arguments(all_nowiki_args[0][2:]) if len(wiki_args.positional) > 1: - wiki_args.keyword['class'] = u' '.join(wiki_args.positional[1:]) + wiki_args.keyword['class'] = ' '.join(wiki_args.positional[1:]) del wiki_args.positional[:] body = moinwiki.parse_block(lines, wiki_args) page = moin_page.page(children=(body, )) @@ -135,21 +133,21 @@ def handle_nowiki(self, elem, page): if nowiki_name in ('rst', 'text/x-rst'): from .rst_in import Converter as rst_converter rst = rst_converter() - page = rst(content, contenttype=u'text/x-rst;charset=utf-8') + page = rst(content, contenttype='text/x-rst;charset=utf-8') elem.append(page) return if nowiki_name in ('docbook', 'application/docbook+xml'): from .docbook_in import Converter as docbook_converter docbook = docbook_converter() - page = docbook(content, contenttype=u'application/docbook+xml;charset=utf-8') + page = docbook(content, contenttype='application/docbook+xml;charset=utf-8') elem.append(page) return if nowiki_name in ('markdown', 'text/x-markdown'): from .markdown_in import Converter as markdown_converter markdown = markdown_converter() - page = markdown(content, contenttype=u'text/x-markdown;charset=utf-8') + page = markdown(content, contenttype='text/x-markdown;charset=utf-8') elem.append(page) return diff --git a/src/moin/converters/opendocument_in.py b/src/moin/converters/opendocument_in.py index c2df4f76b..339d70094 100644 --- a/src/moin/converters/opendocument_in.py +++ b/src/moin/converters/opendocument_in.py @@ -7,9 +7,6 @@ ODF documents can be created with OpenOffice.org, Libre Office and other software. """ - -from __future__ import absolute_import, division - import zipfile from moin.utils.mime import Type, type_text_plain @@ -21,7 +18,7 @@ logging = log.getLogger(__name__) -class OpenDocumentIndexingConverter(object): +class OpenDocumentIndexingConverter: @classmethod def _factory(cls, input, output, **kw): return cls() diff --git a/src/moin/converters/pdf_in.py b/src/moin/converters/pdf_in.py index 0a9e8f62a..9dd9dc52c 100644 --- a/src/moin/converters/pdf_in.py +++ b/src/moin/converters/pdf_in.py @@ -5,17 +5,14 @@ MoinMoin - PDF input converter """ - -from __future__ import absolute_import, division - -from pdfminer.pdfpage import PDFPage -from pdfminer.pdfparser import PDFParser -from pdfminer.pdfdocument import PDFDocument -from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter -from pdfminer.pdfdevice import PDFDevice -from pdfminer.converter import TextConverter -from pdfminer.cmapdb import CMapDB -from pdfminer.layout import LAParams +from pdfminer3.pdfpage import PDFPage +from pdfminer3.pdfparser import PDFParser +from pdfminer3.pdfdocument import PDFDocument +from pdfminer3.pdfinterp import PDFResourceManager, PDFPageInterpreter +from pdfminer3.pdfdevice import PDFDevice +from pdfminer3.converter import TextConverter +from pdfminer3.cmapdb import CMapDB +from pdfminer3.layout import LAParams from . import default_registry from moin.utils.mime import Type, type_text_plain @@ -52,10 +49,10 @@ def write_text(self, text): self.__text.append(text) def read_result(self): - return u''.join(self.__text) + return ''.join(self.__text) -class PDFIndexingConverter(object): +class PDFIndexingConverter: @classmethod def _factory(cls, input, output, **kw): return cls() diff --git a/src/moin/converters/pygments_in.py b/src/moin/converters/pygments_in.py index 72c55629d..39c3144fb 100644 --- a/src/moin/converters/pygments_in.py +++ b/src/moin/converters/pygments_in.py @@ -6,9 +6,6 @@ MoinMoin - Pygments driven syntax highlighting input converter """ - -from __future__ import absolute_import, division - try: import pygments import pygments.formatter @@ -50,7 +47,7 @@ def format(self, tokensource, element): if lastval: self._append(lasttype, lastval, element) - class Converter(object): + class Converter: @classmethod def _factory(cls, type_input, type_output, **kw): pygments_name = None @@ -113,7 +110,7 @@ def __init__(self, lexer=None, contenttype=None): def __call__(self, data, contenttype=None, arguments=None): text = decode_data(data, contenttype) content = normalize_split_text(text) - content = u'\n'.join(content) + content = '\n'.join(content) blockcode = moin_page.blockcode(attrib={moin_page.class_: 'highlight'}) pygments.highlight(content, self.lexer, TreeFormatter(), blockcode) body = moin_page.body(children=(blockcode, )) @@ -126,7 +123,7 @@ def __call__(self, data, contenttype=None, arguments=None): else: # we have no Pygments, minimal Converter replacement, so highlight view does not crash - class Converter(object): + class Converter: def __init__(self, lexer=None, contenttype=None): pass diff --git a/src/moin/converters/rst_in.py b/src/moin/converters/rst_in.py index 40d380715..97008f17c 100644 --- a/src/moin/converters/rst_in.py +++ b/src/moin/converters/rst_in.py @@ -14,9 +14,6 @@ Works with docutils version 0.5 (2008-06-25) or higher. """ - -from __future__ import absolute_import, division - import re import docutils @@ -44,7 +41,7 @@ logging = log.getLogger(__name__) -class NodeVisitor(object): +class NodeVisitor: """ Part of docutils which converts docutils DOM tree to Moin DOM tree """ @@ -67,7 +64,8 @@ def dispatch_visit(self, node): """ node_name = node.__class__.__name__ method = getattr(self, 'visit_' + node_name, self.unknown_visit) - self.current_lineno = node.line + if isinstance(node.line, int): + self.current_lineno = node.line return method(node) def dispatch_departure(self, node): @@ -189,7 +187,7 @@ def depart_attribution(self, node): def visit_bullet_list(self, node): self.open_moin_page_node(moin_page.list( - attrib={moin_page.item_label_generate: u'unordered'})) + attrib={moin_page.item_label_generate: 'unordered'})) def depart_bullet_list(self, node): self.close_moin_page_node() @@ -225,7 +223,7 @@ def visit_author(self, node): self.open_moin_page_node(moin_page.table_cell()) self.open_moin_page_node(moin_page.strong()) # TODO: i18n for docutils: - self.open_moin_page_node(u"Author:") + self.open_moin_page_node("Author:") self.close_moin_page_node() self.close_moin_page_node() self.close_moin_page_node() @@ -240,7 +238,7 @@ def visit_version(self, node): self.open_moin_page_node(moin_page.table_cell()) self.open_moin_page_node(moin_page.strong()) # TODO: i18n for docutils: - self.open_moin_page_node(u"Version:") + self.open_moin_page_node("Version:") self.close_moin_page_node() self.close_moin_page_node() self.close_moin_page_node() @@ -261,7 +259,7 @@ def visit_copyright(self, node): self.open_moin_page_node(moin_page.table_cell()) self.open_moin_page_node(moin_page.strong()) # TODO: i18n for docutils: - self.open_moin_page_node(u"Copyright:") + self.open_moin_page_node("Copyright:") self.close_moin_page_node() self.close_moin_page_node() self.close_moin_page_node() @@ -304,13 +302,13 @@ def depart_entry(self, node): def visit_enumerated_list(self, node): enum_style = { 'arabic': None, - 'loweralpha': u'lower-alpha', - 'upperalpha': u'upper-alpha', - 'lowerroman': u'lower-roman', - 'upperroman': u'upper-roman', + 'loweralpha': 'lower-alpha', + 'upperalpha': 'upper-alpha', + 'lowerroman': 'lower-roman', + 'upperroman': 'upper-roman', } new_node = moin_page.list( - attrib={moin_page.item_label_generate: u'ordered'}) + attrib={moin_page.item_label_generate: 'ordered'}) type = enum_style.get(node['enumtype'], None) if type: new_node.set(moin_page.list_style_type, type) @@ -343,7 +341,7 @@ def depart_field_list(self, node): def visit_field_name(self, node): self.open_moin_page_node(moin_page.table_cell()) self.open_moin_page_node(moin_page.strong()) - self.open_moin_page_node(u'{0}:'.format(node.astext())) + self.open_moin_page_node('{0}:'.format(node.astext())) node.children = [] self.close_moin_page_node() @@ -371,7 +369,7 @@ def depart_footnote(self, node): def visit_footnote_reference(self, node): self.open_moin_page_node(moin_page.note( - attrib={moin_page.note_class: u'footnote'})) + attrib={moin_page.note_class: 'footnote'})) new_footnote = moin_page.note_body() self.open_moin_page_node(new_footnote) self.footnotes[node.children[-1]] = new_footnote @@ -477,7 +475,7 @@ def visit_literal(self, node): self.close_moin_page_node() def visit_literal_block(self, node): - parser = node.get('parser', u'') + parser = node.get('parser', '') if parser: named_args = re.findall(r"(\w+)=(\w+)", parser) simple_args = re.findall(r"(?:\s)\w+(?:\s|$)", parser) @@ -542,21 +540,21 @@ def depart_problematic(self, node): pass def visit_reference(self, node): - refuri = node.get('refuri', u'') - if refuri.startswith(u'<<') and refuri.endswith(u'>>'): # moin macro - macro_name = refuri[2:-2].split(u'(')[0] - if macro_name == u"TableOfContents": - arguments = refuri[2:-2].split(u'(')[1][:-1].split(u',') + refuri = node.get('refuri', '') + if refuri.startswith('<<') and refuri.endswith('>>'): # moin macro + macro_name = refuri[2:-2].split('(')[0] + if macro_name == "TableOfContents": + arguments = refuri[2:-2].split('(')[1][:-1].split(',') node = moin_page.table_of_content() self.open_moin_page_node(node) if arguments and arguments[0]: node.set(moin_page.outline_level, arguments[0]) return - if macro_name == u"Include": + if macro_name == "Include": # include macros are expanded by include.py similar to transclusions # rst include handles only wiki pages and does not support additional arguments like moinwiki - arguments = refuri[2:-2].split(u'(')[1][:-1].split(u',') - link = Iri(scheme=u'wiki.local', path=arguments) + arguments = refuri[2:-2].split('(')[1][:-1].split(',') + link = Iri(scheme='wiki.local', path=arguments) node = xinclude.include(attrib={ xinclude.href: link, moin_page.alt: refuri, @@ -565,9 +563,9 @@ def visit_reference(self, node): self.open_moin_page_node(node) return try: - arguments = refuri[2:-2].split(u'(')[1][:-1] + arguments = refuri[2:-2].split('(')[1][:-1] except IndexError: - arguments = u'' # <> + arguments = '' # <> self.open_moin_page_node( moin_page.inline_part( @@ -582,7 +580,7 @@ def visit_reference(self, node): if not allowed_uri_scheme(refuri): self.visit_error(node) return - if refuri == u'': + if refuri == '': # build a link to a heading or an explicitly defined anchor refuri = Iri(scheme='wiki.local', fragment=node.attributes['name'].replace(' ', '_')) self.open_moin_page_node(moin_page.a(attrib={xlink.href: refuri})) @@ -635,7 +633,7 @@ def depart_strong(self, node): def visit_subscript(self, node): self.open_moin_page_node( moin_page.span( - attrib={moin_page.baseline_shift: u'sub'})) + attrib={moin_page.baseline_shift: 'sub'})) def depart_subscript(self, node): self.close_moin_page_node() @@ -653,7 +651,7 @@ def depart_subtitle(self, node): def visit_superscript(self, node): self.open_moin_page_node( moin_page.span( - attrib={moin_page.baseline_shift: u'super'})) + attrib={moin_page.baseline_shift: 'super'})) def depart_superscript(self, node): self.close_moin_page_node() @@ -698,7 +696,7 @@ def depart_term(self, node): # classifiers arrive as siblings of the term; search the parent and convert them to children for child in node.parent: if isinstance(child, docutils.nodes.classifier): - classifier = u":" + child[0] + classifier = ":" + child[0] self.open_moin_page_node(moin_page.span(children=[classifier])) self.close_moin_page_node() self.close_moin_page_node() @@ -742,7 +740,7 @@ def visit_title_reference(self, node): def depart_title_reference(self, node): pass - def visit_transition(self, node, default_class=u'moin-hr3'): + def visit_transition(self, node, default_class='moin-hr3'): # TODO: add to rst_out attrib = {html.class_: default_class} self.open_moin_page_node(moin_page.separator(attrib=attrib)) @@ -798,7 +796,7 @@ def translate(self): self.output = visitor.tree() -class MoinDirectives(object): +class MoinDirectives: """ Class to handle all custom directive handling. This code is called as part of the parsing stage. @@ -849,9 +847,9 @@ def include(self, name, arguments, options, content, lineno, return [] if content: - macro = u'<>'.format(content[0]) + macro = '<>'.format(content[0]) else: - macro = u'<>' + macro = '<>' ref = reference(macro, refuri=macro) return [ref] @@ -871,10 +869,10 @@ def macro(self, name, arguments, options, content, lineno, content_offset, block # content contains macro to be called if len(content): # Allow either with or without brackets - if content[0].startswith(u'<<'): + if content[0].startswith('<<'): macro = content[0] else: - macro = u'<<{0}>>'.format(content[0]) + macro = '<<{0}>>'.format(content[0]) ref = reference(macro, refuri=macro) ref['name'] = macro return [ref] @@ -890,9 +888,9 @@ def table_of_content(self, name, arguments, options, content, lineno, content_of for i in content: m = re.search(r':(\w+): (\w+)', i) if m and len(m.groups()) == 2: - if m.groups()[0] == u'depth': + if m.groups()[0] == 'depth': text = m.groups()[1] - macro = u'<>'.format(text) + macro = '<>'.format(text) ref = reference(macro, refuri=macro) ref['name'] = macro return [ref] @@ -905,7 +903,7 @@ def table_of_content(self, name, arguments, options, content, lineno, content_of def parser(self, name, arguments, options, content, lineo, content_offset, block_text, state, state_machine): block = literal_block() block['parser'] = content[0] - block.children = [nodes.Text(u"\n".join(content[1:]))] + block.children = [nodes.Text("\n".join(content[1:]))] return [block] parser.has_content = parser.content = True @@ -914,7 +912,7 @@ def parser(self, name, arguments, options, content, lineo, content_offset, block parser.optional_arguments = 0 -class Converter(object): +class Converter: @classmethod def factory(cls, input, output, **kw): return cls() @@ -924,7 +922,7 @@ def __call__(self, data, contenttype=None, arguments=None): input = normalize_split_text(text) parser = MoinDirectives() while True: - input = u'\n'.join(input) + input = '\n'.join(input) try: docutils_tree = core.publish_doctree(source=input) except utils.SystemMessage as inst: diff --git a/src/moin/converters/rst_out.py b/src/moin/converters/rst_out.py index 959eefc0a..3c69cc74e 100644 --- a/src/moin/converters/rst_out.py +++ b/src/moin/converters/rst_out.py @@ -10,9 +10,6 @@ This converter based on ReStructuredText 2006-09-22. """ - -from __future__ import absolute_import, division - import re from emeraldtree import ElementTree as ET @@ -26,7 +23,7 @@ from . import default_registry -class Cell(object): +class Cell: def __init__(self, text): self.text = text @@ -45,7 +42,7 @@ def width(self): return max -class Table(object): +class Table: """ An object of this class collects the structure of a table and represent it in ReStructuredText syntax. @@ -165,84 +162,84 @@ def __repr__(self): for row in range(self.height()): rows.append(self.row_height(row)) ret = [] - line = [u'+'] + line = ['+'] row = self.table[0] for col in range(len(cols)): - line.append(u'-' * cols[col]) + line.append('-' * cols[col]) if self.table[0][col][0] > 1: - line.append(u'-') + line.append('-') else: - line.append(u'+') + line.append('+') ret.append(''.join(line)) for row in range(len(rows)): for i in range(rows[row]): line = [] - line.append(u'|') + line.append('|') for col in range(len(cols)): if self.table[row][col][2].height() <= i: line.append(''.ljust(cols[col])[:cols[col]]) else: line.append( self.table[row][col][2]().split( - u'\n')[i].ljust(cols[col])[:cols[col]]) + '\n')[i].ljust(cols[col])[:cols[col]]) if self.table[row][col][0] > 1: line.append(' ') else: - line.append(u'|') + line.append('|') ret.append(''.join(line)) - line = [u'+'] + line = ['+'] for col in range(len(cols)): if self.table[row][col][1] > 1: - line.append(u' ' * cols[col]) + line.append(' ' * cols[col]) elif row == self.header_count - 1: - line.append(u'=' * cols[col]) + line.append('=' * cols[col]) else: - line.append(u'-' * cols[col]) + line.append('-' * cols[col]) if self.table[row][col][0] > 1: if row + 1 < len(rows)\ and self.table[row + 1][col][0] > 1\ or row + 1 >= len(rows): - line.append(u'-') + line.append('-') else: - line.append(u'+') + line.append('+') else: - line.append(u'+') + line.append('+') ret.append(''.join(line)) - return u'\n'.join(ret) + return '\n'.join(ret) -class ReST(object): +class ReST: """ reST syntax elements """ # moin2 reST standard headings, uses = above and below h1, = below h2, - below h3... + below h6 # these heading styles are used in all .rst files under /docs/ # does not agree with: http://documentation-style-guide-sphinx.readthedocs.io/en/latest/style-guide.html#headings - h_top = u" = " - h_bottom = u" ==-*:+" - - a_separator = u'|' - verbatim = u'::' - monospace = u'``' - strong = u"**" - emphasis = u"*" - p = u'\n' - linebreak = u'\n\n' - separator = u'----' + h_top = " = " + h_bottom = " ==-*:+" + + a_separator = '|' + verbatim = '::' + monospace = '``' + strong = "**" + emphasis = "*" + p = '\n' + linebreak = '\n\n' + separator = '----' list_type = { - (u'definition', None): u'', - (u'ordered', None): u'1.', - (u'ordered', u'lower-alpha'): u'a.', - (u'ordered', u'upper-alpha'): u'A.', - (u'ordered', u'lower-roman'): u'i.', - (u'ordered', u'upper-roman'): u'I.', - (u'unordered', None): u'*', - (None, None): u' ', + ('definition', None): '', + ('ordered', None): '1.', + ('ordered', 'lower-alpha'): 'a.', + ('ordered', 'upper-alpha'): 'A.', + ('ordered', 'lower-roman'): 'i.', + ('ordered', 'upper-roman'): 'I.', + ('unordered', None): '*', + (None, None): ' ', } -class Converter(object): +class Converter: """ Converter application/x.moin.document -> text/x.moin.rst """ @@ -284,15 +281,15 @@ def factory(cls, input, output, **kw): def __init__(self): # TODO: create class containing all table attributes - self.table_tableclass = u'' - self.table_tablestyle = u'' - self.table_rowsclass = u'' - self.table_rowsstyle = u'' - self.table_rowstyle = u'' - self.table_rowclass = u'' + self.table_tableclass = '' + self.table_tablestyle = '' + self.table_rowsclass = '' + self.table_rowsstyle = '' + self.table_rowstyle = '' + self.table_rowclass = '' self.list_item_labels = [] - self.list_item_label = u'' + self.list_item_label = '' self.list_level = -1 # 'text' - default status -

= '/n' and

= '/n' @@ -314,9 +311,9 @@ def __call__(self, root): self.delete_newlines = False self.line_block_indent = -4 ret = self.open(root) - notes = u"\n\n".join(u".. [#] {0}".format(note.replace(u"\n", u"\n ")) for note in self.footnotes) + notes = "\n\n".join(".. [#] {0}".format(note.replace("\n", "\n ")) for note in self.footnotes) if notes: - return ret + self.define_references() + u"\n\n{0}\n\n".format(notes) + return ret + self.define_references() + "\n\n{0}\n\n".format(notes) return ret + self.define_references() @@ -342,26 +339,26 @@ def open_children(self, elem): else: if self.status[-1] == "table": if self.last_closed == "p": - childrens_output.append(u'\n\n') + childrens_output.append('\n\n') elif self.status[-1] == "list": child =\ - re.sub(r"\n(.)", lambda m: u"\n{0}{1}".format( - u' ' * (len(u''.join(self.list_item_labels)) + len(self.list_item_labels)), m.group(1)), child) + re.sub(r"\n(.)", lambda m: "\n{0}{1}".format( + ' ' * (len(''.join(self.list_item_labels)) + len(self.list_item_labels)), m.group(1)), child) if self.last_closed == "p": childrens_output.append( - u'\n' + u' ' * (len(''.join(self.list_item_labels)) + len(self.list_item_labels))) + '\n' + ' ' * (len(''.join(self.list_item_labels)) + len(self.list_item_labels))) elif self.status[-1] == "text": if self.last_closed == "p": childrens_output.append(self.define_references()) - childrens_output.append(u'\n') + childrens_output.append('\n') elif self.status[-2] == "list": child =\ - re.sub(r"\n(.)", lambda m: u"\n{0}{1}".format(u' ' * - (len(u''.join(self.list_item_labels)) + len(self.list_item_labels)), m.group(1)), child) + re.sub(r"\n(.)", lambda m: "\n{0}{1}".format(' ' * + (len(''.join(self.list_item_labels)) + len(self.list_item_labels)), m.group(1)), child) childrens_output.append(child) self.last_closed = 'text' self.delete_newlines = delete_newlines - return u''.join(childrens_output) + return ''.join(childrens_output) def open(self, elem): uri = elem.tag.uri @@ -393,33 +390,33 @@ def open_xinclude(self, elem): def open_moinpage_a(self, elem): href = elem.get(xlink.href, None) - text = u''.join(elem.itertext()).replace(u'\n', u' ') + text = ''.join(elem.itertext()).replace('\n', ' ') # TODO: check that links have different alt texts if text in [t for (t, h) in self.all_used_references]: if (text, href) in self.all_used_references: - return u"`{0}`_".format(text) + return "`{0}`_".format(text) if not self.anonymous_reference: self.anonymous_reference = href - self.used_references.insert(0, (u"_", href)) - return u"`{0}`__".format(text) + self.used_references.insert(0, ("_", href)) + return "`{0}`__".format(text) else: while text in [t for (t, h) in self.all_used_references]: - text += u"~" + text += "~" self.used_references.append((text, href)) - return u"`{0}`_".format(text) + return "`{0}`_".format(text) def open_moinpage_blockcode(self, elem): - text = u''.join(elem.itertext()) + text = ''.join(elem.itertext()) max_subpage_lvl = 3 - text = text.replace(u'\n', u'\n ' + - u' ' * (len(u''.join(self.list_item_labels)) + len(self.list_item_labels))) + text = text.replace('\n', '\n ' + + ' ' * (len(''.join(self.list_item_labels)) + len(self.list_item_labels))) if self.list_level >= 0: self.delete_newlines = True - return u"\n::\n\n {0}{1}\n\n".format( - u' ' * (len(u''.join(self.list_item_labels)) + len(self.list_item_labels)), text) + return "\n::\n\n {0}{1}\n\n".format( + ' ' * (len(''.join(self.list_item_labels)) + len(self.list_item_labels)), text) def open_moinpage_code(self, elem): - ret = u"{0}{1}{2}".format(ReST.monospace, u''.join(elem.itertext()), ReST.monospace) + ret = "{0}{1}{2}".format(ReST.monospace, ''.join(elem.itertext()), ReST.monospace) return ret def open_moinpage_div(self, elem): @@ -435,7 +432,7 @@ def open_moinpage_div(self, elem): if comment.startswith('\n'): comment = comment[1:] comment = comment.replace('\n', '\n ') - return u'\n..\n {0}\n'.format(comment) + return '\n..\n {0}\n'.format(comment) # in case div has another use return self.open_children(elem) @@ -453,7 +450,7 @@ def open_moinpage_figure(self, elem): if r.startswith((' ', '..')) or not r: lines.append(r) else: - lines.append(u' ' + r) + lines.append(' ' + r) return '\n'.join(lines) def open_moinpage_figcaption(self, elem): @@ -462,24 +459,24 @@ def open_moinpage_figcaption(self, elem): def open_moinpage_emphasis(self, elem): childrens_output = self.open_children(elem) - return u"{0}{1}{2}".format(ReST.emphasis, childrens_output, ReST.emphasis) + return "{0}{1}{2}".format(ReST.emphasis, childrens_output, ReST.emphasis) def open_moinpage_h(self, elem): level = elem.get(moin_page.outline_level, 1) - text = u''.join(elem.itertext()) + text = ''.join(elem.itertext()) try: level = int(level) except ValueError: - raise ElementException(u'page:outline-level needs to be an integer') + raise ElementException('page:outline-level needs to be an integer') if level < 1: level = 1 elif level > 6: level = 6 self.headings.append(text) if ReST.h_top[level] == ' ': - ret = u"\n{0}\n{1}\n".format(text, ReST.h_bottom[level] * len(text)) + ret = "\n{0}\n{1}\n".format(text, ReST.h_bottom[level] * len(text)) else: - ret = u"\n{0}\n{1}\n{2}\n".format(ReST.h_top[level] * len(text), text, ReST.h_bottom[level] * len(text)) + ret = "\n{0}\n{1}\n{2}\n".format(ReST.h_top[level] * len(text), text, ReST.h_bottom[level] * len(text)) return ret def open_xinclude_include(self, elem): @@ -497,45 +494,45 @@ def open_xinclude_include(self, elem): try: href = href.path except Exception: - href = href.split(u'wiki.local:')[-1] - ret = [u'\n.. image:: {0}'.format(href), ] + href = href.split('wiki.local:')[-1] + ret = ['\n.. image:: {0}'.format(href), ] for key, val in sorted(whitelist.items()): if key in elem.attrib: - ret.append(u' :{0}: {1}'.format(val, elem.attrib[key])) + ret.append(' :{0}: {1}'.format(val, elem.attrib[key])) if len(ret) == 1: # if there are no attributes, then (for now) we assume it is an include ret[0] = ret[0].replace('image', 'include') - return u'\n'.join(ret) + u'\n' + return '\n'.join(ret) + '\n' def open_moinpage_line_blk(self, elem): out = self.open_children(elem) - if out.startswith(u'\n'): + if out.startswith('\n'): out = out[1:] - return u'| {0}{1}\n'.format(u' ' * self.line_block_indent, out) + return '| {0}{1}\n'.format(' ' * self.line_block_indent, out) def open_moinpage_line_block(self, elem): ret = [] if self.line_block_indent < 0: - ret.append(u'\n') + ret.append('\n') self.line_block_indent += 4 for child in elem: ret.append(self.open(child)) self.line_block_indent -= 4 - return u''.join(ret) + return ''.join(ret) def open_moinpage_line_break(self, elem): if self.status[-1] == "list": - return (ReST.linebreak + u' ' * (len(u''.join(self.list_item_labels)) + len(self.list_item_labels))) + return (ReST.linebreak + ' ' * (len(''.join(self.list_item_labels)) + len(self.list_item_labels))) if self.last_closed == 'p': - return u'\n\n' + return '\n\n' return ReST.linebreak def open_moinpage_list(self, elem): label_type = elem.get(moin_page.item_label_generate, None), elem.get(moin_page.list_style_type, None) self.list_item_labels.append( - ReST.list_type.get(label_type, u' ')) + ReST.list_type.get(label_type, ' ')) self.list_level += 1 - ret = u'' + ret = '' self.status.append('list') self.last_closed = None ret += self.open_children(elem) @@ -545,67 +542,67 @@ def open_moinpage_list(self, elem): return ret def open_moinpage_list_item(self, elem): - self.list_item_label = self.list_item_labels[-1] + u' ' + self.list_item_label = self.list_item_labels[-1] + ' ' return self.open_children(elem) def open_moinpage_list_item_label(self, elem): - ret = u'' - if self.list_item_labels[-1] == u'' or self.list_item_labels[-1] == u' ': - self.list_item_labels[-1] = u' ' - self.list_item_label = self.list_item_labels[-1] + u' ' - ret = (u' ' * (len(u''.join(self.list_item_labels[:-1])) + len(self.list_item_labels[:-1]))) + ret = '' + if self.list_item_labels[-1] == '' or self.list_item_labels[-1] == ' ': + self.list_item_labels[-1] = ' ' + self.list_item_label = self.list_item_labels[-1] + ' ' + ret = (' ' * (len(''.join(self.list_item_labels[:-1])) + len(self.list_item_labels[:-1]))) if self.last_closed and self.last_closed != 'list': - ret = u'\n{0}'.format(ret) + ret = '\n{0}'.format(ret) return ret + self.open_children(elem) return self.open_children(elem) def open_moinpage_list_item_body(self, elem): - ret = u'' - if not self.last_closed == u'list_item': - ret = u'\n' - ret += (u' ' * (len(u''.join(self.list_item_labels[:-1])) + - len(self.list_item_labels[:-1])) + self.list_item_label) - if self.list_item_labels[-1] in [u'1.', u'i.', u'I.', u'a.', u'A.']: - self.list_item_labels[-1] = u'#.' + ret = '' + if not self.last_closed == 'list_item': + ret = '\n' + ret += (' ' * (len(''.join(self.list_item_labels[:-1])) + + len(self.list_item_labels[:-1])) + self.list_item_label) + if self.list_item_labels[-1] in ['1.', 'i.', 'I.', 'a.', 'A.']: + self.list_item_labels[-1] = '#.' ret = self.define_references() + ret + self.open_children(elem) if self.last_closed == "text": - return ret + u'\n' + return ret + '\n' return ret def open_moinpage_note(self, elem): - class_ = elem.get(moin_page.note_class, u"") + class_ = elem.get(moin_page.note_class, "") if class_: self.status.append('list') - if class_ == u"footnote": + if class_ == "footnote": self.footnotes.append(self.open_children(elem)) self.status.pop() - return u' [#]_ ' + return ' [#]_ ' def open_moinpage_object(self, elem): # TODO: object parameters support - href = elem.get(xlink.href, elem.get(xinclude.href, u'')) + href = elem.get(xlink.href, elem.get(xinclude.href, '')) if isinstance(href, Iri): - href = unicode(href) - href = href.split(u'?') - args = u'' + href = str(href) + href = href.split('?') + args = '' if len(href) > 1: - args = [s for s in re.findall(r'(?:^|;|,|&|)(\w+=\w+)(?:,|&|$)', href[1]) if s[:3] != u'do='] + args = [s for s in re.findall(r'(?:^|;|,|&|)(\w+=\w+)(?:,|&|$)', href[1]) if s[:3] != 'do='] href = href[0] - alt = elem.get(moin_page.alt, u'') + alt = elem.get(moin_page.alt, '') if not alt: - ret = u'' + ret = '' else: - ret = u'|{0}|'.format(alt) - args_text = u'' + ret = '|{0}|'.format(alt) + args_text = '' if args: - args_text = u"\n {0}".format(u'\n '.join(u':{0}: {1}'.format( - arg.split(u'=')[0], arg.split(u'=')[1]) for arg in args)) - self.objects.append(u".. {0} image:: {1}{2}".format(ret, href, args_text)) + args_text = "\n {0}".format('\n '.join(':{0}: {1}'.format( + arg.split('=')[0], arg.split('=')[1]) for arg in args)) + self.objects.append(".. {0} image:: {1}{2}".format(ret, href, args_text)) return ret def open_moinpage_p(self, elem): - ret = u'' + ret = '' if self.status[-1] == 'text': self.status.append('p') set = self.define_references() @@ -637,16 +634,16 @@ def open_moinpage_p(self, elem): and self.last_closed != 'list_item_header' \ and self.last_closed != 'list_item_footer' \ and self.last_closed != 'p': - ret = (ReST.linebreak + u' ' * (len(u''.join(self.list_item_labels)) + - len(self.list_item_labels)) + self.open_children(elem)) + ret = (ReST.linebreak + ' ' * (len(''.join(self.list_item_labels)) + + len(self.list_item_labels)) + self.open_children(elem)) elif self.last_closed and self.last_closed == 'p': # return ReST.p +\ - ret = (u"\n" + u' ' * (len(u''.join(self.list_item_labels)) + - len(self.list_item_labels)) + self.open_children(elem)) + ret = ("\n" + ' ' * (len(''.join(self.list_item_labels)) + + len(self.list_item_labels)) + self.open_children(elem)) else: ret = self.open_children(elem) if not self.delete_newlines: - ret += u"\n" + ret += "\n" else: self.status.append('p') ret = self.open_children(elem) @@ -660,55 +657,55 @@ def open_moinpage_page(self, elem): def open_moinpage_body(self, elem): return self.open_children(elem) - def open_moinpage_part(self, elem, sep=u'\n'): - type = elem.get(moin_page.content_type, u"").split(u';') + def open_moinpage_part(self, elem, sep='\n'): + type = elem.get(moin_page.content_type, "").split(';') if len(type) == 2: - if type[0] == u"x-moin/macro": - if len(elem) and iter(elem).next().tag.name == "arguments": - alt = u"<<{0}({1})>>".format(type[1].split(u'=')[1], elem[0][0]) + if type[0] == "x-moin/macro": + if len(elem) and next(iter(elem)).tag.name == "arguments": + alt = "<<{0}({1})>>".format(type[1].split('=')[1], elem[0][0]) else: - alt = u"<<{0}()>>".format(type[1].split(u'=')[1]) - return sep + u".. macro:: {0}".format(alt) + sep - elif type[0] == u"x-moin/format": + alt = "<<{0}()>>".format(type[1].split('=')[1]) + return sep + ".. macro:: {0}".format(alt) + sep + elif type[0] == "x-moin/format": elem_it = iter(elem) - ret = u"\n\n.. parser:{0}".format(type[1].split(u'=')[1]) - if len(elem) and elem_it.next().tag.name == "arguments": + ret = "\n\n.. parser:{0}".format(type[1].split('=')[1]) + if len(elem) and next(elem_it).tag.name == "arguments": args = [] - for arg in iter(elem).next(): + for arg in next(iter(elem)): if arg.tag.name == "argument": - args.append(u"{0}=\"{1}\"".format(arg.get(moin_page.name, u""), u' '.join(arg.itertext()))) - ret = u'{0} {1}'.format(ret, u' '.join(args)) - elem = elem_it.next() - ret = u"{0}\n {1}".format(ret, u' '.join(elem.itertext())) + args.append("{0}=\"{1}\"".format(arg.get(moin_page.name, ""), ' '.join(arg.itertext()))) + ret = '{0} {1}'.format(ret, ' '.join(args)) + elem = next(elem_it) + ret = "{0}\n {1}".format(ret, ' '.join(elem.itertext())) return ret - return elem.get(moin_page.alt, u'') + u"\n" + return elem.get(moin_page.alt, '') + "\n" - def open_moinpage_inline_part(self, elem, sep=u''): + def open_moinpage_inline_part(self, elem, sep=''): return self.open_moinpage_part(elem) def open_moinpage_separator(self, elem): - return u'\n\n' + ReST.separator + u'\n\n' + return '\n\n' + ReST.separator + '\n\n' def open_moinpage_span(self, elem): - baseline_shift = elem.get(moin_page.baseline_shift, u'') + baseline_shift = elem.get(moin_page.baseline_shift, '') if baseline_shift == 'super': - return u"\\ :sup:`{0}`\\ ".format(u''.join(elem.itertext())) + return "\\ :sup:`{0}`\\ ".format(''.join(elem.itertext())) if baseline_shift == 'sub': - return u"\\ :sub:`{0}`\\ ".format(u''.join(elem.itertext())) - id = elem.get(moin_page.id, u'') + return "\\ :sub:`{0}`\\ ".format(''.join(elem.itertext())) + id = elem.get(moin_page.id, '') if id: self.headings.append(id) - return u'\n.. _{0}:\n'.format(id) + return '\n.. _{0}:\n'.format(id) return self.open_children(elem) def open_moinpage_strong(self, elem): return ReST.strong + self.open_children(elem) + ReST.strong def open_moinpage_table(self, elem): - self.table_tableclass = elem.attrib.get('class', u'') - self.table_tablestyle = elem.attrib.get('style', u'') - self.table_rowsstyle = u'' - self.table_rowsclass = u'' + self.table_tableclass = elem.attrib.get('class', '') + self.table_tablestyle = elem.attrib.get('style', '') + self.table_rowsstyle = '' + self.table_rowsclass = '' self.status.append('table') self.last_closed = None self.table = [] @@ -718,10 +715,10 @@ def open_moinpage_table(self, elem): table = repr(self.tablec) if self.status[-1] == "list": table =\ - re.sub(r"\n(.)", lambda m: u"\n{0}{1}".format( - u' ' * (len(u''.join(self.list_item_labels)) + len(self.list_item_labels)), m.group(1)), u"\n" + table) + re.sub(r"\n(.)", lambda m: "\n{0}{1}".format( + ' ' * (len(''.join(self.list_item_labels)) + len(self.list_item_labels)), m.group(1)), "\n" + table) return table + ReST.p - return u'\n' + table + ReST.linebreak + return '\n' + table + ReST.linebreak def open_moinpage_table_header(self, elem): # is this correct rowclass? @@ -733,10 +730,10 @@ def open_moinpage_table_body(self, elem): return self.open_children(elem) def open_moinpage_table_row(self, elem): - self.table_rowclass = elem.attrib.get('class', u'') - self.table_rowclass = u' '.join([s for s in [self.table_rowsclass, self.table_rowclass] if s]) - self.table_rowstyle = elem.attrib.get('style', u'') - self.table_rowstyle = u' '.join([s for s in [self.table_rowsstyle, self.table_rowstyle] if s]) + self.table_rowclass = elem.attrib.get('class', '') + self.table_rowclass = ' '.join([s for s in [self.table_rowsclass, self.table_rowclass] if s]) + self.table_rowstyle = elem.attrib.get('style', '') + self.table_rowstyle = ' '.join([s for s in [self.table_rowsstyle, self.table_rowstyle] if s]) self.table.append([]) self.tablec.add_row() ret = self.open_children(elem) @@ -746,41 +743,41 @@ def open_moinpage_table_row(self, elem): return ret def open_moinpage_table_cell(self, elem): - table_cellclass = elem.attrib.get('class', u'') - table_cellstyle = elem.attrib.get('style', u'') + table_cellclass = elem.attrib.get('class', '') + table_cellstyle = elem.attrib.get('style', '') number_cols_spanned = int(elem.get(moin_page.number_cols_spanned, 1)) number_rows_spanned = int(elem.get(moin_page.number_rows_spanned, 1)) self.table[-1].append((number_cols_spanned, number_rows_spanned, [self.open_children(elem)])) cell = self.table[-1][-1] - self.tablec.add_cell(cell[0], cell[1], Cell(u''.join(cell[2]))) - return u'' + self.tablec.add_cell(cell[0], cell[1], Cell(''.join(cell[2]))) + return '' def open_moinpage_table_of_content(self, elem): - depth = elem.get(moin_page.outline_level, u"") - ret = u"\n\n.. contents::" + depth = elem.get(moin_page.outline_level, "") + ret = "\n\n.. contents::" if depth: - ret += u"\n :depth: {0}".format(depth) - return ret + u"\n\n" + ret += "\n :depth: {0}".format(depth) + return ret + "\n\n" def define_references(self): """ Adds definitions of found links and objects to the converter output. """ - ret = u'' + ret = '' self.all_used_references.extend(self.used_references) - definitions = [u" " * (len(u''.join(self.list_item_labels)) + len(self.list_item_labels)) + - u".. _{0}: {1}".format(t, h) for t, h in self.used_references if t not in self.headings] - definitions.extend(u" " * (len(u''.join(self.list_item_labels)) + len(self.list_item_labels)) + + definitions = [" " * (len(''.join(self.list_item_labels)) + len(self.list_item_labels)) + + ".. _{0}: {1}".format(t, h) for t, h in self.used_references if t not in self.headings] + definitions.extend(" " * (len(''.join(self.list_item_labels)) + len(self.list_item_labels)) + link for link in self.objects) # convert ".. _example: wiki.local:#example" to ".. _example:" definitions = [x.split(' wiki.local')[0] for x in definitions] - definition_block = u"\n\n".join(definitions) + definition_block = "\n\n".join(definitions) if definitions: if self.last_closed == 'list_item_label': - ret += u"\n{0}\n\n".format(definition_block) + ret += "\n{0}\n\n".format(definition_block) else: - ret += u"\n\n{0}\n\n".format(definition_block) + ret += "\n\n{0}\n\n".format(definition_block) self.used_references = [] self.objects = [] diff --git a/src/moin/converters/smiley.py b/src/moin/converters/smiley.py index 5ba08c778..abc4bf6f5 100644 --- a/src/moin/converters/smiley.py +++ b/src/moin/converters/smiley.py @@ -9,9 +9,6 @@ element for the DOM Tree. """ - -from __future__ import absolute_import, division - import re from emeraldtree import ElementTree as ET @@ -22,7 +19,7 @@ from . import default_registry -class Converter(object): +class Converter: """ Replace each smiley by the corresponding element in the DOM Tree """ @@ -61,16 +58,16 @@ class Converter(object): '{o}': 'star_off', } - smiley_rule = ur""" + smiley_rule = r""" (^|(?<=\s)) # we require either beginning of line or some space before a smiley (%(smiley)s) # one of the smileys ($|(?=\s)) # we require either ending of line or some space after a smiley -""" % {'smiley': u'|'.join([re.escape(s) for s in smileys])} +""" % {'smiley': '|'.join([re.escape(s) for s in smileys])} smiley_re = re.compile(smiley_rule, re.UNICODE | re.VERBOSE) # We do not process any smiley conversion within these elements. - tags_to_ignore = set(['code', 'blockcode', 'nowiki', ]) + tags_to_ignore = {'code', 'blockcode', 'nowiki', } @classmethod def _factory(cls, input, output, icon=None, **kw): diff --git a/src/moin/converters/text_csv_in.py b/src/moin/converters/text_csv_in.py index 35fa47485..b003802fa 100644 --- a/src/moin/converters/text_csv_in.py +++ b/src/moin/converters/text_csv_in.py @@ -5,9 +5,6 @@ MoinMoin - CSV text data to DOM converter """ - -from __future__ import absolute_import, division - import csv from ._table import TableMixin @@ -31,23 +28,13 @@ def _factory(cls, type_input, type_output, **kw): def __call__(self, data, contenttype=None, arguments=None): text = decode_data(data, contenttype) content = normalize_split_text(text) - # as of py 2.7.x (and in the year 2013), the csv module seems to still - # have troubles with unicode, thus we encode to utf-8 ... - content = [line.encode('utf-8') for line in content] dialect = csv.Sniffer().sniff(content[0]) reader = csv.reader(content, dialect) - # ... and decode back to unicode - rows = [] - for encoded_row in reader: - row = [] - for encoded_cell in encoded_row: - row.append(encoded_cell.decode('utf-8')) - if row: - rows.append(row) + rows = list(reader) head = None cls = None try: - # fragile function throws errors when csv file is incorrectly formatted + # fragile function, throws errors when csv file is incorrectly formatted if csv.Sniffer().has_header('\n'.join(content)): head = rows[0] rows = rows[1:] diff --git a/src/moin/converters/text_in.py b/src/moin/converters/text_in.py index af4da9888..e131d0ca2 100644 --- a/src/moin/converters/text_in.py +++ b/src/moin/converters/text_in.py @@ -12,9 +12,6 @@ for the input mimetype. """ - -from __future__ import absolute_import, division - from moin.utils.mime import Type, type_moin_document from moin.utils.tree import moin_page @@ -22,7 +19,7 @@ from ._util import decode_data, normalize_split_text -class Converter(object): +class Converter: """ Parse the raw text and create a document object that can be converted into output using Emitter. diff --git a/src/moin/converters/text_out.py b/src/moin/converters/text_out.py index 301db9575..8dc60543b 100644 --- a/src/moin/converters/text_out.py +++ b/src/moin/converters/text_out.py @@ -11,14 +11,11 @@ way, so we get indexable plain text for our search index. """ - -from __future__ import absolute_import, division - from . import default_registry from moin.utils.mime import Type, type_moin_document, type_text_plain -class Converter(object): +class Converter: """ Converter application/x.moin.document -> text/plain """ @@ -27,7 +24,7 @@ def factory(cls, input, output, **kw): return cls() def __call__(self, root): - return u'\n'.join(root.itertext()) + return '\n'.join(root.itertext()) default_registry.register(Converter.factory, type_moin_document, type_text_plain) diff --git a/src/moin/converters/xml_in.py b/src/moin/converters/xml_in.py index 5cd08c483..0ebb7eecb 100644 --- a/src/moin/converters/xml_in.py +++ b/src/moin/converters/xml_in.py @@ -5,8 +5,6 @@ MoinMoin - Generic XML input converter """ -from __future__ import absolute_import, division - import re from . import default_registry @@ -18,16 +16,16 @@ logging = log.getLogger(__name__) -RX_STRIPXML = re.compile(u"<[^>]*?>", re.U | re.DOTALL | re.MULTILINE) +RX_STRIPXML = re.compile("<[^>]*?>", re.U | re.DOTALL | re.MULTILINE) def strip_xml(text): - text = RX_STRIPXML.sub(u" ", text) - text = u' '.join(text.split()) + text = RX_STRIPXML.sub(" ", text) + text = ' '.join(text.split()) return text -class XMLIndexingConverter(object): +class XMLIndexingConverter: """ We try to generically extract contents from XML documents by just throwing away all XML tags. This is for indexing, so this might be good enough. diff --git a/src/moin/datastructures/backends/__init__.py b/src/moin/datastructures/backends/__init__.py index 47c74308f..5838ab0a7 100644 --- a/src/moin/datastructures/backends/__init__.py +++ b/src/moin/datastructures/backends/__init__.py @@ -3,13 +3,9 @@ """ MoinMoin - base classes for datastructs. - -TODO: Per https://docs.python.org/2/library/userdict.html -Starting with Python version 2.6, it is recommended to use collections.MutableMapping instead of DictMixin. """ - -from UserDict import DictMixin +from collections import UserDict from flask import current_app as app from flask import g as flaskg @@ -27,7 +23,7 @@ class DictDoesNotExistError(Exception): """ -class BaseGroup(object): +class BaseGroup: """ Group is something which stores members. Groups are immutable. A member is some arbitrary entity name (Unicode object). @@ -50,7 +46,7 @@ def __iter__(self, yielded_members=None, processed_groups=None): raise NotImplementedError() -class BaseGroupsBackend(object): +class BaseGroupsBackend: """ Backend provides access to the group definitions for the other MoinMoin code. @@ -258,7 +254,7 @@ def __repr__(self): self.__class__, self.name, self.members, self.member_groups) -class BaseDict(object, DictMixin): +class BaseDict(UserDict): def __init__(self, name, backend): """ @@ -270,27 +266,7 @@ def __init__(self, name, backend): """ self.name = name self._backend = backend - self._dict = self._load_dict() - - def __iter__(self): - return self._dict.__iter__() - - def keys(self): - return list(self) - - def __len__(self): - return self._dict.__len__() - - def __getitem__(self, key): - return self._dict[key] - - def get(self, key, default=None): - """ - Return the value if key is in the dictionary, else default. If - default is not given, it defaults to None, so that this method - never raises a KeyError. - """ - return self._dict.get(key, default) + self.data = self._load_dict() def _load_dict(self): """ @@ -299,10 +275,10 @@ def _load_dict(self): return self._backend._retrieve_items(self.name) def __repr__(self): - return "<{0!r} name={1!r} items={2!r}>".format(self.__class__, self.name, self._dict.items()) + return "<{0!r} name={1!r} items={2!r}>".format(self.__class__, self.name, list(self.data.items())) -class BaseDictsBackend(object): +class BaseDictsBackend: def __init__(self): self.item_dict_regex = app.cfg.cache.item_dict_regexact diff --git a/src/moin/datastructures/backends/_tests/__init__.py b/src/moin/datastructures/backends/_tests/__init__.py index ab0733f63..4b32033a7 100644 --- a/src/moin/datastructures/backends/_tests/__init__.py +++ b/src/moin/datastructures/backends/_tests/__init__.py @@ -18,26 +18,26 @@ from moin.datastructures import GroupDoesNotExistError -class GroupsBackendTest(object): - - test_groups = {u'EditorGroup': [u'AdminGroup', u'John', u'JoeDoe', u'Editor1', u'John'], - u'AdminGroup': [u'Admin1', u'Admin2', u'John'], - u'OtherGroup': [u'SomethingOther'], - u'RecursiveGroup': [u'Something', u'OtherRecursiveGroup'], - u'OtherRecursiveGroup': [u'RecursiveGroup', u'Anything', u'NotExistingGroup'], - u'ThirdRecursiveGroup': [u'ThirdRecursiveGroup', u'Banana'], - u'EmptyGroup': [], - u'CheckNotExistingGroup': [u'NotExistingGroup']} - - expanded_groups = {u'EditorGroup': [u'Admin1', u'Admin2', u'John', - u'JoeDoe', u'Editor1'], - u'AdminGroup': [u'Admin1', u'Admin2', u'John'], - u'OtherGroup': [u'SomethingOther'], - u'RecursiveGroup': [u'Anything', u'Something', u'NotExistingGroup'], - u'OtherRecursiveGroup': [u'Anything', u'Something', u'NotExistingGroup'], - u'ThirdRecursiveGroup': [u'Banana'], - u'EmptyGroup': [], - u'CheckNotExistingGroup': [u'NotExistingGroup']} +class GroupsBackendTest: + + test_groups = {'EditorGroup': ['AdminGroup', 'John', 'JoeDoe', 'Editor1', 'John'], + 'AdminGroup': ['Admin1', 'Admin2', 'John'], + 'OtherGroup': ['SomethingOther'], + 'RecursiveGroup': ['Something', 'OtherRecursiveGroup'], + 'OtherRecursiveGroup': ['RecursiveGroup', 'Anything', 'NotExistingGroup'], + 'ThirdRecursiveGroup': ['ThirdRecursiveGroup', 'Banana'], + 'EmptyGroup': [], + 'CheckNotExistingGroup': ['NotExistingGroup']} + + expanded_groups = {'EditorGroup': ['Admin1', 'Admin2', 'John', + 'JoeDoe', 'Editor1'], + 'AdminGroup': ['Admin1', 'Admin2', 'John'], + 'OtherGroup': ['SomethingOther'], + 'RecursiveGroup': ['Anything', 'Something', 'NotExistingGroup'], + 'OtherRecursiveGroup': ['Anything', 'Something', 'NotExistingGroup'], + 'ThirdRecursiveGroup': ['Banana'], + 'EmptyGroup': [], + 'CheckNotExistingGroup': ['NotExistingGroup']} def test_contains(self): """ @@ -45,23 +45,23 @@ def test_contains(self): """ groups = flaskg.groups - for group, members in self.expanded_groups.iteritems(): + for group, members in self.expanded_groups.items(): assert group in groups for member in members: assert member in groups[group] - raises(GroupDoesNotExistError, lambda: groups[u'NotExistingGroup']) + raises(GroupDoesNotExistError, lambda: groups['NotExistingGroup']) def test_contains_group(self): groups = flaskg.groups - assert u'AdminGroup' in groups[u'EditorGroup'] - assert u'EditorGroup' not in groups[u'AdminGroup'] + assert 'AdminGroup' in groups['EditorGroup'] + assert 'EditorGroup' not in groups['AdminGroup'] def test_iter(self): groups = flaskg.groups - for group, members in self.expanded_groups.iteritems(): + for group, members in self.expanded_groups.items(): returned_members = list(groups[group]) assert len(returned_members) == len(members) for member in members: @@ -70,19 +70,19 @@ def test_iter(self): def test_get(self): groups = flaskg.groups - assert groups.get(u'AdminGroup') - assert u'NotExistingGroup' not in groups - assert groups.get(u'NotExistingGroup') is None - assert groups.get(u'NotExistingGroup', []) == [] + assert groups.get('AdminGroup') + assert 'NotExistingGroup' not in groups + assert groups.get('NotExistingGroup') is None + assert groups.get('NotExistingGroup', []) == [] def test_groups_with_member(self): groups = flaskg.groups - john_groups = list(groups.groups_with_member(u'John')) + john_groups = list(groups.groups_with_member('John')) assert 2 == len(john_groups) - assert u'EditorGroup' in john_groups - assert u'AdminGroup' in john_groups - assert u'ThirdGroup' not in john_groups + assert 'EditorGroup' in john_groups + assert 'AdminGroup' in john_groups + assert 'ThirdGroup' not in john_groups def test_backend_acl_allow(self): """ @@ -94,7 +94,7 @@ def test_backend_acl_allow(self): for user in self.expanded_groups['AdminGroup']: for permission in ["read", "write", "admin"]: - assert acl.may(u"Admin1", permission), '{0} must have {1} permission because he is member of the AdminGroup'.format(user, permission) + assert acl.may("Admin1", permission), '{0} must have {1} permission because he is member of the AdminGroup'.format(user, permission) def test_backend_acl_deny(self): """ @@ -104,42 +104,42 @@ def test_backend_acl_deny(self): acl_rights = ["AdminGroup:read,write"] acl = AccessControlList(acl_rights, valid=app.cfg.acl_rights_contents) - assert u"SomeUser" not in flaskg.groups['AdminGroup'] + assert "SomeUser" not in flaskg.groups['AdminGroup'] for permission in ["read", "write"]: - assert not acl.may(u"SomeUser", permission), 'SomeUser must not have {0} permission because he is not listed in the AdminGroup'.format(permission) + assert not acl.may("SomeUser", permission), 'SomeUser must not have {0} permission because he is not listed in the AdminGroup'.format(permission) - assert u'Admin1' in flaskg.groups['AdminGroup'] - assert not acl.may(u"Admin1", "admin") + assert 'Admin1' in flaskg.groups['AdminGroup'] + assert not acl.may("Admin1", "admin") def test_backend_acl_with_all(self): acl_rights = ["EditorGroup:read,write,admin All:read"] acl = AccessControlList(acl_rights, valid=app.cfg.acl_rights_contents) - for member in self.expanded_groups[u'EditorGroup']: + for member in self.expanded_groups['EditorGroup']: for permission in ["read", "write", "admin"]: assert acl.may(member, permission) - assert acl.may(u"Someone", "read") + assert acl.may("Someone", "read") for permission in ["write", "admin"]: - assert not acl.may(u"Someone", permission) + assert not acl.may("Someone", permission) def test_backend_acl_not_existing_group(self): - assert u'NotExistingGroup' not in flaskg.groups + assert 'NotExistingGroup' not in flaskg.groups acl_rights = ["NotExistingGroup:read,write,admin All:read"] acl = AccessControlList(acl_rights, valid=app.cfg.acl_rights_contents) - assert not acl.may(u"Someone", "write") + assert not acl.may("Someone", "write") -class DictsBackendTest(object): +class DictsBackendTest: - dicts = {u'SomeTestDict': {u'First': u'first item', - u'text with spaces': u'second item', - u'Empty string': u'', - u'Last': u'last item'}, - u'SomeOtherTestDict': {u'One': '1', - u'Two': '2'}} + dicts = {'SomeTestDict': {'First': 'first item', + 'text with spaces': 'second item', + 'Empty string': '', + 'Last': 'last item'}, + 'SomeOtherTestDict': {'One': '1', + 'Two': '2'}} def test_getitem(self): expected_dicts = self.dicts @@ -157,7 +157,7 @@ def test_contains(self): for key in self.dicts: assert key in dicts - assert u'SomeNotExistingDict' not in dicts + assert 'SomeNotExistingDict' not in dicts def test_update(self): dicts = flaskg.dicts @@ -165,7 +165,7 @@ def test_update(self): d = {} d.update(dicts['SomeTestDict']) - assert u'First' in d + assert 'First' in d def test_get(self): dicts = flaskg.dicts @@ -173,13 +173,13 @@ def test_get(self): for dict_name in self.dicts: assert dicts.get(dict_name) - assert u'SomeNotExistingDict' not in dicts - assert dicts.get(u'SomeNotExistingDict') is None - assert dicts.get(u'SomeNotExistingDict', {}) == {} + assert 'SomeNotExistingDict' not in dicts + assert dicts.get('SomeNotExistingDict') is None + assert dicts.get('SomeNotExistingDict', {}) == {} for dict_name, expected_dict in self.dicts.items(): test_dict = dicts[dict_name] for key, value in expected_dict.items(): - assert u'SomeNotExistingKey' not in test_dict - assert test_dict.get(u'SomeNotExistingKey') is None - assert test_dict.get(u'SomeNotExistingKey', {}) == {} + assert 'SomeNotExistingKey' not in test_dict + assert test_dict.get('SomeNotExistingKey') is None + assert test_dict.get('SomeNotExistingKey', {}) == {} diff --git a/src/moin/datastructures/backends/_tests/test_composite_dicts.py b/src/moin/datastructures/backends/_tests/test_composite_dicts.py index a7e985401..62093029d 100644 --- a/src/moin/datastructures/backends/_tests/test_composite_dicts.py +++ b/src/moin/datastructures/backends/_tests/test_composite_dicts.py @@ -20,13 +20,13 @@ class TestCompositeDict(DictsBackendTest): def cfg(self): class Config(wikiconfig.Config): - one_dict = {u'SomeTestDict': {u'First': u'first item', - u'text with spaces': u'second item', - u'Empty string': u'', - u'Last': u'last item'}} + one_dict = {'SomeTestDict': {'First': 'first item', + 'text with spaces': 'second item', + 'Empty string': '', + 'Last': 'last item'}} - other_dict = {u'SomeOtherTestDict': {u'One': '1', - u'Two': '2'}} + other_dict = {'SomeOtherTestDict': {'One': '1', + 'Two': '2'}} def dicts(self): return CompositeDicts(ConfigDicts(self.one_dict), diff --git a/src/moin/datastructures/backends/_tests/test_composite_groups.py b/src/moin/datastructures/backends/_tests/test_composite_groups.py index a8c4d0d73..253484d8e 100644 --- a/src/moin/datastructures/backends/_tests/test_composite_groups.py +++ b/src/moin/datastructures/backends/_tests/test_composite_groups.py @@ -31,33 +31,33 @@ def groups(self): return Config -class TestCompositeGroup(object): +class TestCompositeGroup: @pytest.fixture def cfg(self): class Config(wikiconfig.Config): - admin_group = frozenset([u'Admin', u'JohnDoe']) - editor_group = frozenset([u'MainEditor', u'JohnDoe']) - fruit_group = frozenset([u'Apple', u'Banana', u'Cherry']) + admin_group = frozenset(['Admin', 'JohnDoe']) + editor_group = frozenset(['MainEditor', 'JohnDoe']) + fruit_group = frozenset(['Apple', 'Banana', 'Cherry']) - first_backend_groups = {u'AdminGroup': admin_group, - u'EditorGroup': editor_group, - u'FruitGroup': fruit_group} + first_backend_groups = {'AdminGroup': admin_group, + 'EditorGroup': editor_group, + 'FruitGroup': fruit_group} - user_group = frozenset([u'JohnDoe', u'Bob', u'Joe']) - city_group = frozenset([u'Bolzano', u'Riga', u'London']) + user_group = frozenset(['JohnDoe', 'Bob', 'Joe']) + city_group = frozenset(['Bolzano', 'Riga', 'London']) # Suppose, someone hacked second backend and added himself to AdminGroup - second_admin_group = frozenset([u'TheHacker']) + second_admin_group = frozenset(['TheHacker']) - second_backend_groups = {u'UserGroup': user_group, - u'CityGroup': city_group, + second_backend_groups = {'UserGroup': user_group, + 'CityGroup': city_group, # Here group name clash occurs. # AdminGroup is defined in both # first_backend and second_backend. - u'AdminGroup': second_admin_group} + 'AdminGroup': second_admin_group} def groups(self): return CompositeGroups(ConfigGroups(self.first_backend_groups), @@ -66,7 +66,7 @@ def groups(self): return Config def test_getitem(self): - raises(GroupDoesNotExistError, lambda: flaskg.groups[u'NotExistingGroup']) + raises(GroupDoesNotExistError, lambda: flaskg.groups['NotExistingGroup']) def test_clashed_getitem(self): """ @@ -74,12 +74,12 @@ def test_clashed_getitem(self): backends. __getitem__ should return the first match (backends are considered in the order they are given in the backends list). """ - admin_group = flaskg.groups[u'AdminGroup'] + admin_group = flaskg.groups['AdminGroup'] # TheHacker added himself to the second backend, but that must not be # taken into consideration, because AdminGroup is defined in first # backend and we only use the first match. - assert u'TheHacker' not in admin_group + assert 'TheHacker' not in admin_group def test_iter(self): all_group_names = list(flaskg.groups) @@ -89,8 +89,8 @@ def test_iter(self): assert len(set(all_group_names)) == len(all_group_names) def test_contains(self): - assert u'UserGroup' in flaskg.groups - assert u'not existing group' not in flaskg.groups + assert 'UserGroup' in flaskg.groups + assert 'not existing group' not in flaskg.groups coverage_modules = ['moin.datastructures.backends.composite_groups'] diff --git a/src/moin/datastructures/backends/_tests/test_lazy_config_groups.py b/src/moin/datastructures/backends/_tests/test_lazy_config_groups.py index 8833cba30..313517655 100644 --- a/src/moin/datastructures/backends/_tests/test_lazy_config_groups.py +++ b/src/moin/datastructures/backends/_tests/test_lazy_config_groups.py @@ -16,10 +16,10 @@ class TestLazyConfigGroups(GroupsBackendTest): - test_groups = {u'EditorGroup': [u'John', u'JoeDoe', u'Editor1'], - u'AdminGroup': [u'Admin1', u'Admin2', u'John'], - u'OtherGroup': [u'SomethingOther'], - u'EmptyGroup': []} + test_groups = {'EditorGroup': ['John', 'JoeDoe', 'Editor1'], + 'AdminGroup': ['Admin1', 'Admin2', 'John'], + 'OtherGroup': ['SomethingOther'], + 'EmptyGroup': []} expanded_groups = test_groups @@ -47,15 +47,15 @@ def cfg(self): class Config(wikiconfig.Config): def groups(self): - config_groups = {u'EditorGroup': [u'AdminGroup', u'John', u'JoeDoe', u'Editor1', u'John'], - u'RecursiveGroup': [u'Something', u'OtherRecursiveGroup'], - u'OtherRecursiveGroup': [u'RecursiveGroup', u'Anything', u'NotExistingGroup'], - u'ThirdRecursiveGroup': [u'ThirdRecursiveGroup', u'Banana'], - u'CheckNotExistingGroup': [u'NotExistingGroup']} - - lazy_groups = {u'AdminGroup': [u'Admin1', u'Admin2', u'John'], - u'OtherGroup': [u'SomethingOther'], - u'EmptyGroup': []} + config_groups = {'EditorGroup': ['AdminGroup', 'John', 'JoeDoe', 'Editor1', 'John'], + 'RecursiveGroup': ['Something', 'OtherRecursiveGroup'], + 'OtherRecursiveGroup': ['RecursiveGroup', 'Anything', 'NotExistingGroup'], + 'ThirdRecursiveGroup': ['ThirdRecursiveGroup', 'Banana'], + 'CheckNotExistingGroup': ['NotExistingGroup']} + + lazy_groups = {'AdminGroup': ['Admin1', 'Admin2', 'John'], + 'OtherGroup': ['SomethingOther'], + 'EmptyGroup': []} return CompositeGroups(ConfigGroups(config_groups), ConfigLazyGroups(lazy_groups)) diff --git a/src/moin/datastructures/backends/_tests/test_wiki_dicts.py b/src/moin/datastructures/backends/_tests/test_wiki_dicts.py index 0d19a820e..6b7430a23 100644 --- a/src/moin/datastructures/backends/_tests/test_wiki_dicts.py +++ b/src/moin/datastructures/backends/_tests/test_wiki_dicts.py @@ -29,20 +29,20 @@ class TestWikiDictsBackend(DictsBackendTest): def custom_setup(self): become_trusted() - somedict = {u"First": u"first item", - u"text with spaces": u"second item", - u'Empty string': u'', - u"Last": u"last item"} - update_item(u'SomeTestDict', {SOMEDICT: somedict}, DATA) + somedict = {"First": "first item", + "text with spaces": "second item", + 'Empty string': '', + "Last": "last item"} + update_item('SomeTestDict', {SOMEDICT: somedict}, DATA) - somedict = {u"One": u"1", - u"Two": u"2"} - update_item(u'SomeOtherTestDict', {SOMEDICT: somedict}, DATA) + somedict = {"One": "1", + "Two": "2"} + update_item('SomeOtherTestDict', {SOMEDICT: somedict}, DATA) def test__retrieve_items(self): wikidict_obj = wiki_dicts.WikiDicts() - result = wiki_dicts.WikiDicts._retrieve_items(wikidict_obj, u'SomeOtherTestDict') - expected = {u'Two': u'2', u'One': u'1'} + result = wiki_dicts.WikiDicts._retrieve_items(wikidict_obj, 'SomeOtherTestDict') + expected = {'Two': '2', 'One': '1'} assert result == expected diff --git a/src/moin/datastructures/backends/_tests/test_wiki_groups.py b/src/moin/datastructures/backends/_tests/test_wiki_groups.py index ddb1fec6c..b434c0659 100644 --- a/src/moin/datastructures/backends/_tests/test_wiki_groups.py +++ b/src/moin/datastructures/backends/_tests/test_wiki_groups.py @@ -33,7 +33,7 @@ class TestWikiGroupBackend(GroupsBackendTest): @pytest.fixture(autouse=True) def custom_setup(self): become_trusted() - for group, members in self.test_groups.iteritems(): + for group, members in self.test_groups.items(): update_item(group, {USERGROUP: members}, DATA) def test_rename_group_item(self): @@ -41,13 +41,13 @@ def test_rename_group_item(self): Tests renaming of a group item. """ become_trusted() - update_item(u'SomeGroup', {USERGROUP: ["ExampleUser"]}, DATA) - assert u'ExampleUser' in flaskg.groups[u'SomeGroup'] - pytest.raises(GroupDoesNotExistError, lambda: flaskg.groups[u'AnotherGroup']) + update_item('SomeGroup', {USERGROUP: ["ExampleUser"]}, DATA) + assert 'ExampleUser' in flaskg.groups['SomeGroup'] + pytest.raises(GroupDoesNotExistError, lambda: flaskg.groups['AnotherGroup']) - update_item(u'SomeGroup', {NAME: [u'AnotherGroup', ], USERGROUP: ["ExampleUser"]}, DATA) - assert u'ExampleUser' in flaskg.groups[u'AnotherGroup'] - pytest.raises(GroupDoesNotExistError, lambda: flaskg.groups[u'SomeGroup']) + update_item('SomeGroup', {NAME: ['AnotherGroup', ], USERGROUP: ["ExampleUser"]}, DATA) + assert 'ExampleUser' in flaskg.groups['AnotherGroup'] + pytest.raises(GroupDoesNotExistError, lambda: flaskg.groups['SomeGroup']) def test_appending_group_item(self): """ @@ -57,8 +57,8 @@ def test_appending_group_item(self): # long list of users members = create_random_string_list(length=15, count=1234) test_user = create_random_string_list(length=15, count=1)[0] - update_item(u'UserGroup', {USERGROUP: members}, DATA) - update_item(u'UserGroup', {USERGROUP: members + [test_user]}, '') + update_item('UserGroup', {USERGROUP: members}, DATA) + update_item('UserGroup', {USERGROUP: members + [test_user]}, '') result = test_user in flaskg.groups['UserGroup'] assert result @@ -72,17 +72,17 @@ def test_member_removed_from_group_item(self): # long list of users members = create_random_string_list() - update_item(u'UserGroup', {USERGROUP: members}, DATA) + update_item('UserGroup', {USERGROUP: members}, DATA) # updates the text with the text_user test_user = create_random_string_list(length=15, count=1)[0] - update_item(u'UserGroup', {USERGROUP: [test_user]}, DATA) - result = test_user in flaskg.groups[u'UserGroup'] + update_item('UserGroup', {USERGROUP: [test_user]}, DATA) + result = test_user in flaskg.groups['UserGroup'] assert result # updates the text without test_user - update_item(u'UserGroup', {}, DATA) - result = test_user in flaskg.groups[u'UserGroup'] + update_item('UserGroup', {}, DATA) + result = test_user in flaskg.groups['UserGroup'] assert not result def test_wiki_backend_item_acl_usergroupmember_item(self): @@ -92,17 +92,17 @@ def test_wiki_backend_item_acl_usergroupmember_item(self): then add user member to an item group and check acl rights """ become_trusted() - update_item(u'NewGroup', {USERGROUP: ["ExampleUser"]}, DATA) + update_item('NewGroup', {USERGROUP: ["ExampleUser"]}, DATA) acl_rights = ["NewGroup:read,write"] acl = AccessControlList(acl_rights, valid=app.cfg.acl_rights_contents) - has_rights_before = acl.may(u"AnotherUser", "read") + has_rights_before = acl.may("AnotherUser", "read") # update item - add AnotherUser to a item group NewGroup - update_item(u'NewGroup', {USERGROUP: ["AnotherUser"]}, '') + update_item('NewGroup', {USERGROUP: ["AnotherUser"]}, '') - has_rights_after = acl.may(u"AnotherUser", "read") + has_rights_after = acl.may("AnotherUser", "read") assert not has_rights_before, 'AnotherUser has no read rights because in the beginning he is not a member of a group item NewGroup' assert has_rights_after, 'AnotherUser must have read rights because after appenditem he is member of NewGroup' diff --git a/src/moin/datastructures/backends/config_dicts.py b/src/moin/datastructures/backends/config_dicts.py index fd69aa91f..4c1814196 100644 --- a/src/moin/datastructures/backends/config_dicts.py +++ b/src/moin/datastructures/backends/config_dicts.py @@ -26,7 +26,7 @@ def __contains__(self, dict_name): return self.is_dict_name(dict_name) and dict_name in self._dicts def __iter__(self): - return self._dicts.iterkeys() + return iter(self._dicts.keys()) def __getitem__(self, dict_name): return ConfigDict(name=dict_name, backend=self) diff --git a/src/moin/datastructures/backends/config_groups.py b/src/moin/datastructures/backends/config_groups.py index caa79b256..6f0a423d7 100644 --- a/src/moin/datastructures/backends/config_groups.py +++ b/src/moin/datastructures/backends/config_groups.py @@ -31,7 +31,7 @@ def __contains__(self, group_name): return group_name in self._groups def __iter__(self): - return self._groups.iterkeys() + return iter(self._groups.keys()) def __getitem__(self, group_name): return ConfigGroup(name=group_name, backend=self) diff --git a/src/moin/datastructures/backends/config_lazy_groups.py b/src/moin/datastructures/backends/config_lazy_groups.py index e4fa819c5..861617eff 100644 --- a/src/moin/datastructures/backends/config_lazy_groups.py +++ b/src/moin/datastructures/backends/config_lazy_groups.py @@ -30,7 +30,7 @@ def __contains__(self, group_name): return group_name in self._groups def __iter__(self): - return self._groups.iterkeys() + return iter(self._groups.keys()) def __getitem__(self, group_name): return ConfigLazyGroup(group_name, self) diff --git a/src/moin/error.py b/src/moin/error.py index 6597f1bc9..5118ee4dd 100644 --- a/src/moin/error.py +++ b/src/moin/error.py @@ -5,9 +5,6 @@ MoinMoin errors / exception classes """ - -from __future__ import absolute_import, division - import sys from moin.constants.contenttypes import CHARSET @@ -33,24 +30,17 @@ class Error(Exception): def __init__(self, message): """ Initialize an error, decode if needed - :param message: unicode, str or object that support __unicode__ - and __str__. __str__ should use CHARSET. + :param message: str, bytes or object that supports __str__. """ + if isinstance(message, bytes): + message = message.decode() + if not isinstance(message, str): + message = str(message) self.message = message - def __unicode__(self): - """ Return unicode error message """ - if isinstance(self.message, str): - return unicode(self.message, CHARSET) - else: - return unicode(self.message) - def __str__(self): - """ Return encoded message """ - if isinstance(self.message, unicode): - return self.message.encode(CHARSET) - else: - return str(self.message) + """ Return str error message """ + return self.message def __getitem__(self, item): """ Make it possible to access attributes like a dict """ diff --git a/src/moin/forms.py b/src/moin/forms.py index b188f6d2a..17852f8d4 100644 --- a/src/moin/forms.py +++ b/src/moin/forms.py @@ -142,7 +142,7 @@ def validate(self, element, state): try: validate_name(state['meta'], state[ITEMID]) except NameNotValidError as e: - self.invalid_name_msg = _(e) + self.invalid_name_msg = _(str(e)) return self.note_error(element, state, 'invalid_name_msg') return True @@ -256,7 +256,7 @@ def u(self): name_ = _("This item doesn't exist.") except AttributeError: name_ = _("This item name is corrupt, delete and recreate.") - value = u"{0} ({1})".format(value, name_) + value = "{0} ({1})".format(value, name_) else: # name::ExampleItem | tags::demo | nameprefix::jp | namere::.* | name:MyNamespace:ExampleItem value = child.u @@ -278,7 +278,7 @@ def u(self): Quicklinks = MyJoinedString.of(String).with_properties(widget=WIDGET_MULTILINE_TEXT, rows=ROWS, cols=COLS).using( label=L_('Quick Links'), optional=True, separator='\n', separator_regex=re.compile(r'[\r\n]+')) -Search = Text.using(default=u'', optional=True).with_properties(widget=WIDGET_SEARCH, placeholder=L_("Search Query")) +Search = Text.using(default='', optional=True).with_properties(widget=WIDGET_SEARCH, placeholder=L_("Search Query")) _Integer = Integer.validated_by(Converted()) @@ -314,7 +314,7 @@ def adapt(self, value): # check if a value is a correct timestamp dt = datetime.datetime.utcfromtimestamp(value) return value - except ValueError: + except (ValueError, OSError): # OSError errno 75 "Value too large for defined data type" raise AdaptationError() dt = super(DateTimeUNIX, self).adapt(value) if isinstance(dt, datetime.datetime): @@ -353,7 +353,7 @@ def validate(self, element, state): return True -class Reference(Select.with_properties(empty_label=L_(u'(None)')).validated_by(ValidReference())): +class Reference(Select.with_properties(empty_label=L_('(None)')).validated_by(ValidReference())): """ A metadata property that points to another item selected out of the Results of a search query. @@ -370,7 +370,7 @@ def _get_choice_specs(cls): label_getter = cls.properties['label_getter'] choices = [(rev.meta[ITEMID], label_getter(rev)) for rev in revs] if cls.optional: - choices.append((u'', cls.properties['empty_label'])) + choices.append(('', cls.properties['empty_label'])) return choices def __init__(self, value=Unspecified, **kw): diff --git a/src/moin/i18n/__init__.py b/src/moin/i18n/__init__.py index b485a27f4..8f5e19e9a 100644 --- a/src/moin/i18n/__init__.py +++ b/src/moin/i18n/__init__.py @@ -100,5 +100,5 @@ def force_locale(locale): yield finally: babel.locale_selector_func = orig_locale_selector_func - for key, value in orig_attrs.iteritems(): + for key, value in orig_attrs.items(): setattr(ctx, key, value) diff --git a/src/moin/i18n/_tests/test_i18n.py b/src/moin/i18n/_tests/test_i18n.py index 3dfc5709d..232e5a43e 100644 --- a/src/moin/i18n/_tests/test_i18n.py +++ b/src/moin/i18n/_tests/test_i18n.py @@ -29,7 +29,7 @@ def test_text(): # test for lazy_gettext result = L_('test_lazy_text') - assert result == u'test_lazy_text' + assert result == 'test_lazy_text' # test for ngettext result1 = N_('text1', 'text2', 1) diff --git a/src/moin/items/__init__.py b/src/moin/items/__init__.py index b9367febd..f2ea85d07 100644 --- a/src/moin/items/__init__.py +++ b/src/moin/items/__init__.py @@ -148,7 +148,7 @@ def __init__(self, item, itemtype=None, contenttype=None): self.meta[fqname.field] = fqname.value -class DummyItem(object): +class DummyItem: """ if we have no stored Item, we use this dummy """ def __init__(self, fqname): self.fqname = fqname @@ -227,9 +227,9 @@ def acl_validate(acl_string): In later processes, None means no item ACLs, so the configured default ACLs will be used. Empty is same as "". If there are no configured 'after' ACLs, then Empty and "" are equivalent to "All:". """ - all_rights = set(('read', 'write', 'create', 'destroy', 'admin')) - acls = unicode(acl_string) - if acls in (u'None', u'Empty', u''): # u'' is not possible if field is required on form + all_rights = {'read', 'write', 'create', 'destroy', 'admin'} + acls = str(acl_string) + if acls in ('None', 'Empty', ''): # u'' is not possible if field is required on form return True acls = acls.split() for acl in acls: @@ -296,7 +296,7 @@ def from_request(cls, request): Flatland Form is straightforward, there should be rarely any need to override this class method. """ - form = cls.from_flat(request.form.items() + request.files.items()) + form = cls.from_flat(list(request.form.items()) + list(request.files.items())) return form @@ -310,9 +310,9 @@ def _build_contenttype_query(groups): queries = [] for g in groups: for e in content_registry.groups[g]: - ct_unicode = unicode(e.content_type) + ct_unicode = str(e.content_type) queries.append(Term(CONTENTTYPE, ct_unicode)) - queries.append(Prefix(CONTENTTYPE, ct_unicode + u';')) + queries.append(Prefix(CONTENTTYPE, ct_unicode + ';')) return Or(queries) @@ -352,12 +352,12 @@ class FieldNotUniqueError(ValueError): """ -class Item(object): +class Item: """ Highlevel (not storage) Item, wraps around a storage Revision""" # placeholder values for registry entry properties itemtype = '' - display_name = u'' - description = u'' + display_name = '' + description = '' shown = True order = 0 @@ -366,7 +366,7 @@ def _factory(cls, *args, **kw): return cls(*args, **kw) @classmethod - def create(cls, name=u'', itemtype=None, contenttype=None, rev_id=CURRENT, item=None): + def create(cls, name='', itemtype=None, contenttype=None, rev_id=CURRENT, item=None): """ Create a highlevel Item by looking up :name or directly wrapping :item and extract the Revision designated by :rev_id revision. @@ -423,7 +423,7 @@ def name(self): try: return self.names[0] except IndexError: - return u'' + return '' @property def names(self): @@ -515,7 +515,7 @@ def _rename(self, name, comment, action, delete=False): item._save(item.meta, item.content.data, name=child_newname, action=action, comment=comment, delete=delete) - def rename(self, name, comment=u''): + def rename(self, name, comment=''): """ rename this item to item (replace current name by another name in the NAME list) """ @@ -526,7 +526,7 @@ def rename(self, name, comment=u''): _verify_parents(self, name, self.fqname.namespace, old_name=self.fqname.value) return self._rename(name, comment, action=ACTION_RENAME) - def delete(self, comment=u''): + def delete(self, comment=''): """ delete this item (remove current name from NAME list) """ @@ -542,10 +542,10 @@ def delete(self, comment=u''): flash(msg, 'info') return ret - def revert(self, comment=u''): + def revert(self, comment=''): return self._save(self.meta, self.content.data, action=ACTION_REVERT, comment=comment) - def destroy(self, comment=u'', destroy_item=False, subitem_names=[]): + def destroy(self, comment='', destroy_item=False, subitem_names=[]): # called from destroy UI/POST action = DESTROY_ALL if destroy_item else DESTROY_REV item_modified.send(app, fqname=self.fqname, action=action, meta=self.meta, @@ -565,7 +565,7 @@ def destroy(self, comment=u'', destroy_item=False, subitem_names=[]): self.rev.item.destroy_revision(self.rev.revid) flash(L_('Rev Number %(rev_number)s of the item "%(name)s" was destroyed.', rev_number=self.meta['rev_number'], name=self.name), 'info') - def modify(self, meta, data, comment=u'', contenttype_guessed=None, **update_meta): + def modify(self, meta, data, comment='', contenttype_guessed=None, **update_meta): meta = dict(meta) # we may get a read-only dict-like, copy it # get rid of None values update_meta = {key: value for key, value in update_meta.items() if value is not None} @@ -599,7 +599,7 @@ def _load(self, item): meta['acl'] = "None" self['meta_form'].set(meta, policy='duck') - for k in self['meta_form'].field_schema_mapping.keys() + IMMUTABLE_KEYS: + for k in list(self['meta_form'].field_schema_mapping.keys()) + IMMUTABLE_KEYS: meta.pop(k, None) self['extra_meta_text'].set(item.meta_dict_to_text(meta)) self['content_form']._load(item.content) @@ -683,7 +683,7 @@ def _save(self, meta, data=None, name=None, action=ACTION_SAVE, contenttype_gues meta[NAMESPACE] = self.fqname.namespace if comment is not None: - meta[COMMENT] = unicode(comment) + meta[COMMENT] = str(comment) if currentrev: current_names = currentrev.meta.get(NAME, []) @@ -709,17 +709,21 @@ def _save(self, meta, data=None, name=None, action=ACTION_SAVE, contenttype_gues # a valid usecase of this is to just edit metadata. data = currentrev.data else: - data = '' + data = b'' - data = self.handle_variables(data, meta) + # TODO XXX broken: data can be all sorts of stuff there and handle_variables can't deal with it yet: + # - non-text binary as bytes + # - text as bytes? as str? as BytesIO? + # + # data = self.handle_variables(data, meta) - if isinstance(data, unicode): + if isinstance(data, str): data = data.encode(CHARSET) # XXX wrong! if contenttype gives a coding, we MUST use THAT. if isinstance(data, bytes): data = BytesIO(data) newrev = storage_item.store_revision(meta, data, overwrite=overwrite, - action=unicode(action), + action=str(action), contenttype_current=contenttype_current, contenttype_guessed=contenttype_guessed, return_rev=True, @@ -737,6 +741,7 @@ def handle_variables(self, data, meta): @rtype: string @return: new text of wikipage, variables replaced """ + assert isinstance(data, str) logging.debug("handle_variable data: %r" % data) if self.contenttype not in CONTENTTYPE_VARIABLES: return data @@ -774,7 +779,7 @@ def handle_variables(self, data, meta): for name in variables: try: - data = data.replace(u'@{0}@'.format(name), variables[name]) + data = data.replace('@{0}@'.format(name), variables[name]) except UnicodeError: logging.warning("handle_variables: UnicodeError! name: %r value: %r" % (name, variables[name])) return data @@ -785,7 +790,7 @@ def subitem_prefixes(self): Return the possible prefixes for subitems. """ names = self.names[0:1] if self.fqname.field == NAME_EXACT else self.names - return [name + u'/' if name else u'' for name in names] + return [name + '/' if name else '' for name in names] def get_prefix_match(self, name, prefixes): """ @@ -827,7 +832,7 @@ def make_flat_index(self, subitems, isglobalindex=False): :param isglobalindex: True if the query is for global indexes. """ - prefixes = [u''] if isglobalindex else self.subitem_prefixes + prefixes = [''] if isglobalindex else self.subitem_prefixes # IndexEntry instances of "file" subitems files = [] # IndexEntry instances of "directory" subitems @@ -875,7 +880,7 @@ def build_index_query(self, startswith=None, selected_groups=None, isglobalindex Output: Returns a whoosh.query.Prefix object for the input parameters """ - prefix = u'' if isglobalindex else self.subitem_prefixes[0] + prefix = '' if isglobalindex else self.subitem_prefixes[0] if startswith: query = Prefix(NAME_EXACT, prefix + startswith) | Prefix(NAME_EXACT, prefix + startswith.swapcase()) else: @@ -992,7 +997,7 @@ def do_show(self, revid): def doc_link(self, filename, link_text): """create a link to serve local doc files as help for wiki editors""" filename = url_for('serve.files', name='docs', filename=filename) - return u'%s' % (filename, link_text) + return '%s' % (filename, link_text) def meta_changed(self, meta): """ @@ -1006,7 +1011,7 @@ def meta_changed(self, meta): if request.values.get('meta_form_summary') != meta.get('summary', None): return True new_tags = request.values.get('meta_form_tags').replace(" ", "").split(',') - if new_tags == [u""]: + if new_tags == [""]: new_tags = [] if new_tags != meta.get('tags', None): return True @@ -1092,7 +1097,7 @@ def do_modify(self): if contenttype_guessed: m = re.search('charset=(.+?)($|;)', contenttype_guessed) if m: - data = unicode(data, m.group(1)) + data = str(data, m.group(1)) state = dict(fqname=self.fqname, itemid=meta.get(ITEMID), meta=meta) if form.validate(state): if request.values.get('preview'): @@ -1100,19 +1105,21 @@ def do_modify(self): edit_utils.put_draft(data) old_item = Item.create(self.fqname.fullname, rev_id=CURRENT, contenttype=self.contenttype) old_text = old_item.content.data - old_text = Text(self.contenttype, item=item).data_storage_to_internal(old_text) + old_text = Text(old_item.contenttype, item=old_item).data_storage_to_internal(old_text) preview_diffs = [(d[0], Markup(d[1]), d[2], Markup(d[3])) for d in html_diff(old_text, data)] preview_rendered = item.content._render_data(preview=data) else: # user clicked OK/Save button, check for conflicts, - draft, draft_data = edit_utils.get_draft() - if draft: + if 'charset' in self.contenttype: + draft, draft_data = edit_utils.get_draft() u_name, i_id, i_name, rev_number, save_time, rev_id = draft if not rev_id == 'new-item': original_item = Item.create(self.name, rev_id=rev_id, contenttype=self.contenttype) - original_text = original_item.content.data + original_rev = get_storage_revision(self.fqname, itemtype=self.itemtype, contenttype=original_item.contenttype, rev_id=rev_id) + charset = original_item.contenttype.split('charset=')[1] + original_text = original_rev.data.read().decode(charset) else: - original_text = u'' + original_text = '' if original_text == data and not self.meta_changed(item.meta): flash(_("Nothing changed, nothing saved."), "info") edit_utils.delete_draft() @@ -1124,7 +1131,8 @@ def do_modify(self): if rev_number < self.meta.get('rev_number', 0): # we have conflict - someone else has saved item, create and save 3-way diff, give user error message to fix it saved_item = Item.create(self.name, rev_id=CURRENT, contenttype=self.contenttype) - saved_text = saved_item.content.data + charset = saved_item.contenttype.split('charset=')[1] + saved_text = saved_item.content.data.decode(charset) data3 = diff3.text_merge(original_text, saved_text, data) data = data3 comment = _("CONFLICT ") + comment or '' @@ -1280,19 +1288,19 @@ def _select_itemtype(self): itemtypes=item_registry.shown_entries, ) - def rename(self, name, comment=u''): + def rename(self, name, comment=''): # pointless for non-existing items pass - def delete(self, comment=u''): + def delete(self, comment=''): # pointless for non-existing items pass - def revert(self, comment=u''): + def revert(self, comment=''): # pointless for non-existing items pass - def destroy(self, comment=u'', destroy_item=False): + def destroy(self, comment='', destroy_item=False): # pointless for non-existing items pass diff --git a/src/moin/items/_tests/test_Blog.py b/src/moin/items/_tests/test_Blog.py index 3c7406d68..12630f0b1 100644 --- a/src/moin/items/_tests/test_Blog.py +++ b/src/moin/items/_tests/test_Blog.py @@ -21,7 +21,7 @@ import pytest -class TestView(object): +class TestView: @pytest.fixture(autouse=True) def set_self_app(self, app): self.app = app @@ -29,26 +29,27 @@ def set_self_app(self, app): def _test_view(self, item_name, req_args={}, data_tokens=[], exclude_data_tokens=[], regex=None): with self.app.test_client() as c: rv = c.get(url_for('frontend.show_item', item_name=item_name, **req_args)) + rv_data = rv.data.decode() for data in data_tokens: - assert data in rv.data + assert data in rv_data for data in exclude_data_tokens: - assert data not in rv.data + assert data not in rv_data if regex: - assert regex.search(rv.data) + assert regex.search(rv_data) class TestBlog(TestView): - NO_ENTRIES_MSG = u"There are no entries" + NO_ENTRIES_MSG = "There are no entries" - name = u'NewBlogItem' - contenttype = u'text/x.moin.wiki;charset=utf-8' - data = u"This is the header item of this blog" + name = 'NewBlogItem' + contenttype = 'text/x.moin.wiki;charset=utf-8' + data = "This is the header item of this blog" meta = {CONTENTTYPE: contenttype, ITEMTYPE: ITEMTYPE_BLOG} - comment = u'saved it' - entries = [{'name': name + u'/NewBlogEntryItem1', 'data': u"First blog entry"}, - {'name': name + u'/NewBlogEntryItem2', 'data': u"Second blog entry"}, - {'name': name + u'/NewBlogEntryItem3', 'data': u"Third blog entry"}, - {'name': name + u'/NewBlogEntryItem4', 'data': u"Fourth blog entry"}, ] + comment = 'saved it' + entries = [{'name': name + '/NewBlogEntryItem1', 'data': "First blog entry"}, + {'name': name + '/NewBlogEntryItem2', 'data': "Second blog entry"}, + {'name': name + '/NewBlogEntryItem3', 'data': "Third blog entry"}, + {'name': name + '/NewBlogEntryItem4', 'data': "Fourth blog entry"}, ] entry_meta = {CONTENTTYPE: contenttype, ITEMTYPE: ITEMTYPE_BLOG_ENTRY} def _publish_entry(self, entry, ptime, acl=None): @@ -117,9 +118,9 @@ def test_filter_by_tag(self): item._save(self.meta, self.data, comment=self.comment) # publish some entries with tags entries_meta = [ - {PTIME: 1000, TAGS: [u'foo', u'bar', u'moin']}, - {PTIME: 3000, TAGS: [u'foo', u'bar', u'baz']}, - {PTIME: 2000, TAGS: [u'baz', u'moin']}, + {PTIME: 1000, TAGS: ['foo', 'bar', 'moin']}, + {PTIME: 3000, TAGS: ['foo', 'bar', 'baz']}, + {PTIME: 2000, TAGS: ['baz', 'moin']}, ] for entry, entry_meta in zip(self.entries, entries_meta): entry_meta.update(self.entry_meta) @@ -128,14 +129,14 @@ def test_filter_by_tag(self): # filter by non-existent tag 'non-existent' data_tokens = [self.data, self.NO_ENTRIES_MSG, ] exclude_data_tokens = [self.entries[0]['data'], self.entries[1]['data'], self.entries[2]['data'], ] - self._test_view(self.name, req_args={u'tag': u'non-existent'}, data_tokens=data_tokens, exclude_data_tokens=exclude_data_tokens) + self._test_view(self.name, req_args={'tag': 'non-existent'}, data_tokens=data_tokens, exclude_data_tokens=exclude_data_tokens) # filter by tag 'moin' exclude_data_tokens = [self.NO_ENTRIES_MSG, self.entries[1]['data'], ] ordered_data = [self.data, self.entries[2]['data'], self.entries[0]['data'], ] regex = re.compile(r'{0}.*{1}.*{2}'.format(*ordered_data), re.DOTALL) - self._test_view(self.name, req_args={u'tag': u'moin'}, exclude_data_tokens=exclude_data_tokens, regex=regex) + self._test_view(self.name, req_args={'tag': 'moin'}, exclude_data_tokens=exclude_data_tokens, regex=regex) def test_filter_by_acls(self): item = Item.create(self.name, itemtype=ITEMTYPE_BLOG) @@ -146,10 +147,10 @@ def test_filter_by_acls(self): item._save(self.entry_meta, entry['data'], comment=self.comment) # publish the first three entries with specific ACLs # we are an "anonymous" user - self._publish_entry(self.entries[0], ptime=1000, acl=u"%s:read" % ANON) - self._publish_entry(self.entries[1], ptime=3000, acl=u"%s:read" % ANON) + self._publish_entry(self.entries[0], ptime=1000, acl="%s:read" % ANON) + self._publish_entry(self.entries[1], ptime=3000, acl="%s:read" % ANON) # specify no rights on the 3rd entry - self._publish_entry(self.entries[2], ptime=2000, acl=u"%s:" % ANON) + self._publish_entry(self.entries[2], ptime=2000, acl="%s:" % ANON) # the blog is not empty and the 3rd entry is not displayed exclude_data_tokens = [self.NO_ENTRIES_MSG, self.entries[2]['data'], ] ordered_data = [self.data, @@ -160,13 +161,13 @@ def test_filter_by_acls(self): class TestBlogEntry(TestView): - blog_name = u'NewBlogItem' - contenttype = u'text/x.moin.wiki;charset=utf-8' - blog_data = u"This is the header item of this blog" + blog_name = 'NewBlogItem' + contenttype = 'text/x.moin.wiki;charset=utf-8' + blog_data = "This is the header item of this blog" blog_meta = {CONTENTTYPE: contenttype, ITEMTYPE: ITEMTYPE_BLOG} - comment = u'saved it' - entry_name = blog_name + u'/NewBlogEntryItem' - entry_data = u"Blog entry data" + comment = 'saved it' + entry_name = blog_name + '/NewBlogEntryItem' + entry_data = "Blog entry data" entry_meta = {CONTENTTYPE: contenttype, ITEMTYPE: ITEMTYPE_BLOG_ENTRY} def test_create(self): diff --git a/src/moin/items/_tests/test_Content.py b/src/moin/items/_tests/test_Content.py index b5e74d1aa..ba486f166 100644 --- a/src/moin/items/_tests/test_Content.py +++ b/src/moin/items/_tests/test_Content.py @@ -22,40 +22,41 @@ from moin.constants.keys import CONTENTTYPE, TAGS, TEMPLATE from moin.constants.itemtypes import ITEMTYPE_DEFAULT from moin.utils.interwiki import split_fqname +from functools import reduce -class TestContent(object): +class TestContent: """ Test for arbitrary content """ def testClassFinder(self): for contenttype, ExpectedClass in [ - (u'application/x-foobar', Binary), - (u'text/plain', Text), - (u'text/plain;charset=utf-8', Text), - (u'image/tiff', Image), - (u'image/png', TransformableBitmapImage), + ('application/x-foobar', Binary), + ('text/plain', Text), + ('text/plain;charset=utf-8', Text), + ('image/tiff', Image), + ('image/png', TransformableBitmapImage), ]: content = Content.create(contenttype) assert isinstance(content, ExpectedClass) def test_get_templates(self): - item_name1 = u'Template_Item1' + item_name1 = 'Template_Item1' item1 = Item.create(item_name1) - contenttype1 = u'text/plain;charset=utf-8' + contenttype1 = 'text/plain;charset=utf-8' meta = {CONTENTTYPE: contenttype1, TAGS: [TEMPLATE]} item1._save(meta) item1 = Item.create(item_name1) - item_name2 = u'Template_Item2' + item_name2 = 'Template_Item2' item2 = Item.create(item_name2) - contenttype1 = u'text/plain;charset=utf-8' + contenttype1 = 'text/plain;charset=utf-8' meta = {CONTENTTYPE: contenttype1, TAGS: [TEMPLATE]} item2._save(meta) item2 = Item.create(item_name2) - item_name3 = u'Template_Item3' + item_name3 = 'Template_Item3' item3 = Item.create(item_name3) - contenttype2 = u'image/png' + contenttype2 = 'image/png' meta = {CONTENTTYPE: contenttype2, TAGS: [TEMPLATE]} item3._save(meta) item3 = Item.create(item_name3) @@ -67,7 +68,7 @@ def test_get_templates(self): assert result2 == [item_name3] -class TestTarItems(object): +class TestTarItems: """ tests for the container items """ @@ -76,15 +77,15 @@ def testCreateContainerRevision(self): """ creates a container and tests the content saved to the container """ - item_name = u'ContainerItem1' - item = Item.create(item_name, itemtype=ITEMTYPE_DEFAULT, contenttype=u'application/x-tar') - filecontent = 'abcdefghij' + item_name = 'ContainerItem1' + item = Item.create(item_name, itemtype=ITEMTYPE_DEFAULT, contenttype='application/x-tar') + filecontent = b'abcdefghij' content_length = len(filecontent) - members = set(['example1.txt', 'example2.txt']) + members = {'example1.txt', 'example2.txt'} item.content.put_member('example1.txt', filecontent, content_length, expected_members=members) item.content.put_member('example2.txt', filecontent, content_length, expected_members=members) - item = Item.create(item_name, itemtype=ITEMTYPE_DEFAULT, contenttype=u'application/x-tar') + item = Item.create(item_name, itemtype=ITEMTYPE_DEFAULT, contenttype='application/x-tar') tf_names = set(item.content.list_members()) assert tf_names == members assert item.content.get_member('example1.txt').read() == filecontent @@ -93,39 +94,39 @@ def testRevisionUpdate(self): """ creates two revisions of a container item """ - item_name = u'ContainerItem2' - item = Item.create(item_name, itemtype=ITEMTYPE_DEFAULT, contenttype=u'application/x-tar') - filecontent = 'abcdefghij' + item_name = 'ContainerItem2' + item = Item.create(item_name, itemtype=ITEMTYPE_DEFAULT, contenttype='application/x-tar') + filecontent = b'abcdefghij' content_length = len(filecontent) - members = set(['example1.txt']) + members = {'example1.txt'} item.content.put_member('example1.txt', filecontent, content_length, expected_members=members) - filecontent = 'AAAABBBB' + filecontent = b'AAAABBBB' content_length = len(filecontent) item.content.put_member('example1.txt', filecontent, content_length, expected_members=members) - item = Item.create(item_name, contenttype=u'application/x-tar') + item = Item.create(item_name, contenttype='application/x-tar') assert item.content.get_member('example1.txt').read() == filecontent -class TestZipMixin(object): +class TestZipMixin: """ Test for zip-like items """ def test_put_member(self): - item_name = u'Zip_file' + item_name = 'Zip_file' item = Item.create(item_name, itemtype=ITEMTYPE_DEFAULT, contenttype='application/zip') filecontent = 'test_contents' content_length = len(filecontent) - members = set(['example1.txt', 'example2.txt']) + members = {'example1.txt', 'example2.txt'} with pytest.raises(NotImplementedError): item.content.put_member('example1.txt', filecontent, content_length, expected_members=members) -class TestTransformableBitmapImage(object): +class TestTransformableBitmapImage: def test__transform(self): - item_name = u'image_Item' + item_name = 'image_Item' item = Item.create(item_name) - contenttype = u'image/jpeg' + contenttype = 'image/jpeg' meta = {CONTENTTYPE: contenttype} item._save(meta) item = Item.create(item_name) @@ -135,12 +136,12 @@ def test__transform(self): result = TransformableBitmapImage._transform(item.content, 'text/plain') except ImportError: result = TransformableBitmapImage._transform(item.content, contenttype) - assert result == (u'image/jpeg', '') + assert result == ('image/jpeg', b'') def test__render_data_diff(self): - item_name = u'image_Item' + item_name = 'image_Item' item = Item.create(item_name) - contenttype = u'image/jpeg' + contenttype = 'image/jpeg' meta = {CONTENTTYPE: contenttype} item._save(meta) item1 = Item.create(item_name) @@ -158,126 +159,126 @@ def test__render_data_diff(self): pass def test__render_data_diff_text(self): - item_name = u'image_Item' + item_name = 'image_Item' item = Item.create(item_name) - contenttype = u'image/jpeg' + contenttype = 'image/jpeg' meta = {CONTENTTYPE: contenttype} item._save(meta) item1 = Item.create(item_name) data = 'test_data' - comment = u'next revision' + comment = 'next revision' item1._save(meta, data, comment=comment) item2 = Item.create(item_name) try: from PIL import Image as PILImage result = TransformableBitmapImage._render_data_diff_text(item1.content, item1.rev, item2.rev) - expected = u'The items have different data.' + expected = 'The items have different data.' assert result == expected except ImportError: pass -class TestText(object): +class TestText: def test_data_conversion(self): - item_name = u'Text_Item' - item = Item.create(item_name, ITEMTYPE_DEFAULT, u'text/plain;charset=utf-8') - test_text = u'This \n is \n a \n Test' + item_name = 'Text_Item' + item = Item.create(item_name, ITEMTYPE_DEFAULT, 'text/plain;charset=utf-8') + test_text = 'This \n is \n a \n Test' # test for data_internal_to_form result = Text.data_internal_to_form(item.content, test_text) - expected = u'This \r\n is \r\n a \r\n Test' + expected = 'This \r\n is \r\n a \r\n Test' assert result == expected # test for data_form_to_internal - test_form = u'This \r\n is \r\n a \r\n Test' + test_form = 'This \r\n is \r\n a \r\n Test' result = Text.data_form_to_internal(item.content, test_text) expected = test_text assert result == expected # test for data_internal_to_storage result = Text.data_internal_to_storage(item.content, test_text) - expected = 'This \r\n is \r\n a \r\n Test' + expected = b'This \r\n is \r\n a \r\n Test' assert result == expected # test for data_storage_to_internal - data_storage = 'This \r\n is \r\n a \r\n Test' + data_storage = b'This \r\n is \r\n a \r\n Test' result = Text.data_storage_to_internal(item.content, data_storage) expected = test_text assert result == expected def test__render_data_diff(self): - item_name = u'Html_Item' + item_name = 'Html_Item' fqname = split_fqname(item_name) - empty_html = u'' - html = u'\ud55c' - meta = {CONTENTTYPE: u'text/html;charset=utf-8'} + empty_html = '' + html = '\ud55c' + meta = {CONTENTTYPE: 'text/html;charset=utf-8'} item = Item.create(item_name) item._save(meta, empty_html) item = Item.create(item_name) # Unicode test, html escaping rev1 = update_item(item_name, meta, html) - rev2 = update_item(item_name, {}, u' ') + rev2 = update_item(item_name, {}, ' ') result = Text._render_data_diff(item.content, rev1, rev2, fqname=fqname) assert escape(html) in result # Unicode test, whitespace - rev1 = update_item(item_name, {}, u'\n\n') - rev2 = update_item(item_name, {}, u'\n \n') + rev1 = update_item(item_name, {}, '\n\n') + rev2 = update_item(item_name, {}, '\n \n') result = Text._render_data_diff(item.content, rev1, rev2, fqname=fqname) assert '     ' in result # If fairly similar diffs are correctly spanned or not, also check indent - rev1 = update_item(item_name, {}, u'One Two Three Four\nSix\n\ud55c') - rev2 = update_item(item_name, {}, u'Two Three Seven Four\nSix\n\ud55c') + rev1 = update_item(item_name, {}, 'One Two Three Four\nSix\n\ud55c') + rev2 = update_item(item_name, {}, 'Two Three Seven Four\nSix\n\ud55c') result = Text._render_data_diff(item.content, rev1, rev2, fqname=fqname) assert 'One Two Three Four' in result assert 'Two Three Seven Four' in result # Check for diff_html.diff return types - assert reduce(lambda x, y: x and y, [isinstance(i[1], unicode) and isinstance(i[3], unicode) for i in diff_html.diff(u'One Two Three Four\nSix\n', u'Two Three Seven Four\nSix Seven\n')], True) + assert reduce(lambda x, y: x and y, [isinstance(i[1], str) and isinstance(i[3], str) for i in diff_html.diff('One Two Three Four\nSix\n', 'Two Three Seven Four\nSix Seven\n')], True) def test__render_data_diff_text(self): - item_name = u'Text_Item' + item_name = 'Text_Item' item = Item.create(item_name) - contenttype = u'text/plain;charset=utf-8' + contenttype = 'text/plain;charset=utf-8' meta = {CONTENTTYPE: contenttype} data1 = "old_data" item._save(meta, data1) item1 = Item.create(item_name) data2 = 'new_data' - comment = u'next revision' + comment = 'next revision' item1._save(meta, data2, comment=comment) item2 = Item.create(item_name) result = Text._render_data_diff_text(item1.content, item1.rev, item2.rev) - expected = u'- old_data\n+ new_data' + expected = '- old_data\n+ new_data' assert result == expected - assert item2.content.data == '' + assert item2.content.data == b'' def test__render_data_highlight(self): - item_name = u'Text_Item' + item_name = 'Text_Item' item = Item.create(item_name) - contenttype = u'text/plain;charset=utf-8' + contenttype = 'text/plain;charset=utf-8' meta = {CONTENTTYPE: contenttype} item._save(meta) item1 = Item.create(item_name) data = 'test_data\nnext line' - comment = u'next revision' + comment = 'next revision' item1._save(meta, data, comment=comment) item2 = Item.create(item_name) result = Text._render_data_highlight(item2.content) - assert u'
test_data\n' in result
-        assert item2.content.data == ''
+        assert '
test_data\n' in result
+        assert item2.content.data == b''
 
     def test__get_data_diff_text(self):
-        item_name = u'Text_Item'
+        item_name = 'Text_Item'
         item = Item.create(item_name)
-        contenttypes = dict(texttypes=[u'text/plain;charset=utf-8',
-                                       u'text/x-markdown;charset=utf-8', ],
-                            othertypes=[u'image/png', u'audio/wave',
-                                        u'video/ogg',
-                                        u'application/x-svgdraw',
-                                        u'application/octet-stream', ])
+        contenttypes = dict(texttypes=['text/plain;charset=utf-8',
+                                       'text/x-markdown;charset=utf-8', ],
+                            othertypes=['image/png', 'audio/wave',
+                                        'video/ogg',
+                                        'application/x-svgdraw',
+                                        'application/octet-stream', ])
         for key in contenttypes:
             for contenttype in contenttypes[key]:
                 meta = {CONTENTTYPE: contenttype}
                 item._save(meta)
                 item_ = Item.create(item_name)
-                oldfile = BytesIO("x")
-                newfile = BytesIO("xx")
+                oldfile = BytesIO(b"x")
+                newfile = BytesIO(b"xx")
                 difflines = item_.content._get_data_diff_text(oldfile, newfile)
                 if key == 'texttypes':
                     assert difflines == ['- x', '+ xx']
@@ -285,16 +286,16 @@ def test__get_data_diff_text(self):
                     assert difflines == []
 
     def test__get_data_diff_html(self):
-        item_name = u"Test_Item"
+        item_name = "Test_Item"
         item = Item.create(item_name)
-        contenttype = u'text/plain;charset=utf-8'
+        contenttype = 'text/plain;charset=utf-8'
         meta = {CONTENTTYPE: contenttype}
         item._save(meta)
         item_ = Item.create(item_name)
-        oldfile = BytesIO("")
-        newfile = BytesIO("x")
+        oldfile = BytesIO(b"")
+        newfile = BytesIO(b"x")
         difflines = item_.content._get_data_diff_html(oldfile, newfile)
-        assert difflines == [(1, Markup(u''), 1, Markup(u'x'))]
+        assert difflines == [(1, Markup(''), 1, Markup('x'))]
 
 
 coverage_modules = ['moin.items.content']
diff --git a/src/moin/items/_tests/test_Item.py b/src/moin/items/_tests/test_Item.py
index 44d5483e2..8b1224cf2 100644
--- a/src/moin/items/_tests/test_Item.py
+++ b/src/moin/items/_tests/test_Item.py
@@ -37,25 +37,25 @@ def build_mixed_index(basename, spec):
             for relname, hassubitem in spec]
 
 
-class TestItem(object):
+class TestItem:
 
     def _testNonExistent(self):
-        item = Item.create(u'DoesNotExist')
+        item = Item.create('DoesNotExist')
         assert isinstance(item, NonExistent)
         meta, data = item.meta, item.content.data
         assert meta == {
             ITEMTYPE: ITEMTYPE_NONEXISTENT,
             CONTENTTYPE: CONTENTTYPE_NONEXISTENT,
-            NAME: u'DoesNotExist',
+            NAME: 'DoesNotExist',
         }
         assert data == ''
 
     def testCRUD(self):
-        name = u'NewItem'
-        contenttype = u'text/plain;charset=utf-8'
-        data = 'foobar'
+        name = 'NewItem'
+        contenttype = 'text/plain;charset=utf-8'
+        data = b'foobar'
         meta = {'foo': 'bar', CONTENTTYPE: contenttype}
-        comment = u'saved it'
+        comment = 'saved it'
         become_trusted()
         item = Item.create(name)
         # save rev 0
@@ -68,7 +68,7 @@ def testCRUD(self):
         assert saved_data == data
 
         data = rev1_data = data * 10000
-        comment += u' again'
+        comment += ' again'
         # save rev 1
         item._save(meta, data, comment=comment)
         # check save result
@@ -87,50 +87,50 @@ def testCRUD(self):
         saved_meta, saved_data = dict(item.meta), item.content.data
         assert saved_meta[CONTENTTYPE] == contenttype
         assert saved_meta[COMMENT] == comment
-        assert saved_data == data
+        assert saved_data == b''
 
     def testIndex(self):
         # create a toplevel and some sub-items
-        basename = u'Foo'
+        basename = 'Foo'
         for name in ['', '/ab', '/cd/ef', '/gh', '/ij', '/ij/kl', ]:
             item = Item.create(basename + name)
-            item._save({CONTENTTYPE: u'text/plain;charset=utf-8'}, "foo")
+            item._save({CONTENTTYPE: 'text/plain;charset=utf-8'}, "foo")
         item = Item.create(basename + '/mn')
-        item._save({CONTENTTYPE: u'image/jpeg'}, "JPG")
+        item._save({CONTENTTYPE: 'image/jpeg'}, "JPG")
 
         baseitem = Item.create(basename)
 
         # test Item.make_flat_index
         # TODO: test Item.get_subitem_revs
         dirs, files = baseitem.get_index()
-        assert dirs == build_index(basename, [u'cd', u'ij'])
-        assert files == build_index(basename, [u'ab', u'gh', u'ij', u'mn'])
+        assert dirs == build_index(basename, ['cd', 'ij'])
+        assert files == build_index(basename, ['ab', 'gh', 'ij', 'mn'])
 
         # test Item.get_mixed_index
         mixed_index = baseitem.get_mixed_index()
         assert mixed_index == build_mixed_index(basename, [
-            (u'ab', False),
-            (u'cd', True),
-            (u'gh', False),
-            (u'ij', True),
-            (u'mn', False),
+            ('ab', False),
+            ('cd', True),
+            ('gh', False),
+            ('ij', True),
+            ('mn', False),
         ])
 
         # check filtered index when startswith param is passed
-        dirs, files = baseitem.get_index(startswith=u'a')
+        dirs, files = baseitem.get_index(startswith='a')
         assert dirs == []
-        assert files == build_index(basename, [u'ab'])
+        assert files == build_index(basename, ['ab'])
 
         # check filtered index when contenttype_groups is passed
         ctgroups = ["Other Text Items"]
         dirs, files = baseitem.get_index(selected_groups=ctgroups)
-        assert dirs == build_index(basename, [u'cd', u'ij'])
-        assert files == build_index(basename, [u'ab', u'gh', u'ij'])
+        assert dirs == build_index(basename, ['cd', 'ij'])
+        assert files == build_index(basename, ['ab', 'gh', 'ij'])
 
     def test_meta_filter(self):
-        name = u'Test_item'
-        contenttype = u'text/plain;charset=utf-8'
-        meta = {'test_key': 'test_val', CONTENTTYPE: contenttype, NAME: [u'test_name'], ADDRESS: u'1.2.3.4'}
+        name = 'Test_item'
+        contenttype = 'text/plain;charset=utf-8'
+        meta = {'test_key': 'test_val', CONTENTTYPE: contenttype, NAME: ['test_name'], ADDRESS: '1.2.3.4'}
         item = Item.create(name)
         result = Item.meta_filter(item, meta)
         # keys like NAME, ITEMID, REVID, DATAID are filtered
@@ -138,17 +138,17 @@ def test_meta_filter(self):
         assert result == expected
 
     def test_meta_dict_to_text(self):
-        name = u'Test_item'
-        contenttype = u'text/plain;charset=utf-8'
-        meta = {'test_key': 'test_val', CONTENTTYPE: contenttype, NAME: [u'test_name']}
+        name = 'Test_item'
+        contenttype = 'text/plain;charset=utf-8'
+        meta = {'test_key': 'test_val', CONTENTTYPE: contenttype, NAME: ['test_name']}
         item = Item.create(name)
         result = Item.meta_dict_to_text(item, meta)
-        expected = '{\n  "contenttype": "text/plain;charset=utf-8", \n  "test_key": "test_val"\n}'
+        expected = '{\n  "contenttype": "text/plain;charset=utf-8",\n  "test_key": "test_val"\n}'
         assert result == expected
 
     def test_meta_text_to_dict(self):
-        name = u'Test_item'
-        contenttype = u'text/plain;charset=utf-8'
+        name = 'Test_item'
+        contenttype = 'text/plain;charset=utf-8'
         text = '{\n  "contenttype": "text/plain;charset=utf-8", \n  "test_key": "test_val", \n "name": ["test_name"] \n}'
         item = Item.create(name)
         result = Item.meta_text_to_dict(item, text)
@@ -156,204 +156,204 @@ def test_meta_text_to_dict(self):
         assert result == expected
 
     def test_item_can_have_several_names(self):
-        content = u"This is page content"
+        content = b"This is page content"
 
-        update_item(u'Page',
-                    {NAME: [u'Page',
-                            u'Another name',
+        update_item('Page',
+                    {NAME: ['Page',
+                            'Another name',
                             ],
-                     CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, content)
+                     CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, content)
 
-        item1 = Item.create(u'Page')
-        assert item1.name == u'Page'
+        item1 = Item.create('Page')
+        assert item1.name == 'Page'
         assert item1.meta[CONTENTTYPE] == 'text/x.moin.wiki;charset=utf-8'
         assert item1.content.data == content
 
-        item2 = Item.create(u'Another name')
-        assert item2.name == u'Another name'
+        item2 = Item.create('Another name')
+        assert item2.name == 'Another name'
         assert item2.meta[CONTENTTYPE] == 'text/x.moin.wiki;charset=utf-8'
         assert item2.content.data == content
 
         assert item1.rev.revid == item2.rev.revid
 
     def test_rename(self):
-        name = u'Test_Item'
-        contenttype = u'text/plain;charset=utf-8'
+        name = 'Test_Item'
+        contenttype = 'text/plain;charset=utf-8'
         data = 'test_data'
         meta = {'test_key': 'test_value', CONTENTTYPE: contenttype}
-        comment = u'saved it'
+        comment = 'saved it'
         item = Item.create(name)
         item._save(meta, data, comment=comment)
         # item and its contents before renaming
         item = Item.create(name)
-        assert item.name == u'Test_Item'
-        assert item.meta[COMMENT] == u'saved it'
-        new_name = u'Test_new_Item'
-        item.rename(new_name, comment=u'renamed')
+        assert item.name == 'Test_Item'
+        assert item.meta[COMMENT] == 'saved it'
+        new_name = 'Test_new_Item'
+        item.rename(new_name, comment='renamed')
         # item at original name and its contents after renaming
         item = Item.create(name)
-        assert item.name == u'Test_Item'
+        assert item.name == 'Test_Item'
         # this should be a fresh, new item, NOT the stuff we renamed:
         assert item.meta[CONTENTTYPE] == CONTENTTYPE_NONEXISTENT
         # item at new name and its contents after renaming
         item = Item.create(new_name)
-        assert item.name == u'Test_new_Item'
-        assert item.meta[NAME_OLD] == [u'Test_Item']
-        assert item.meta[COMMENT] == u'renamed'
-        assert item.content.data == u'test_data'
+        assert item.name == 'Test_new_Item'
+        assert item.meta[NAME_OLD] == ['Test_Item']
+        assert item.meta[COMMENT] == 'renamed'
+        assert item.content.data == b'test_data'
 
     def test_rename_acts_only_in_active_name_in_case_there_are_several_names(self):
-        content = u"This is page content"
+        content = "This is page content"
 
-        update_item(u'Page',
-                    {NAME: [u'First',
-                            u'Second',
-                            u'Third',
+        update_item('Page',
+                    {NAME: ['First',
+                            'Second',
+                            'Third',
                             ],
-                     CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, content)
+                     CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, content)
 
-        item = Item.create(u'Second')
-        item.rename(u'New name', comment=u'renamed')
+        item = Item.create('Second')
+        item.rename('New name', comment='renamed')
 
-        item1 = Item.create(u'First')
-        assert item1.name == u'First'
+        item1 = Item.create('First')
+        assert item1.name == 'First'
         assert item1.meta[CONTENTTYPE] == 'text/x.moin.wiki;charset=utf-8'
-        assert item1.content.data == content
+        assert item1.content.data == content.encode()
 
-        item2 = Item.create(u'New name')
-        assert item2.name == u'New name'
+        item2 = Item.create('New name')
+        assert item2.name == 'New name'
         assert item2.meta[CONTENTTYPE] == 'text/x.moin.wiki;charset=utf-8'
-        assert item2.content.data == content
+        assert item2.content.data == content.encode()
 
-        item3 = Item.create(u'Third')
-        assert item3.name == u'Third'
+        item3 = Item.create('Third')
+        assert item3.name == 'Third'
         assert item3.meta[CONTENTTYPE] == 'text/x.moin.wiki;charset=utf-8'
-        assert item3.content.data == content
+        assert item3.content.data == content.encode()
 
         assert item1.rev.revid == item2.rev.revid == item3.rev.revid
 
-        item4 = Item.create(u'Second')
+        item4 = Item.create('Second')
         assert item4.meta[CONTENTTYPE] == CONTENTTYPE_NONEXISTENT
 
     def test_rename_recursion(self):
-        update_item(u'Page', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u'Page 1')
-        update_item(u'Page/Child', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u'this is child')
-        update_item(u'Page/Child/Another', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u'another child')
+        update_item('Page', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, 'Page 1')
+        update_item('Page/Child', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, 'this is child')
+        update_item('Page/Child/Another', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, 'another child')
 
-        item = Item.create(u'Page')
-        item.rename(u'Renamed_Page', comment=u'renamed')
+        item = Item.create('Page')
+        item.rename('Renamed_Page', comment='renamed')
 
         # items at original name and its contents after renaming
-        item = Item.create(u'Page')
-        assert item.name == u'Page'
+        item = Item.create('Page')
+        assert item.name == 'Page'
         assert item.meta[CONTENTTYPE] == CONTENTTYPE_NONEXISTENT
-        item = Item.create(u'Page/Child')
-        assert item.name == u'Page/Child'
+        item = Item.create('Page/Child')
+        assert item.name == 'Page/Child'
         assert item.meta[CONTENTTYPE] == CONTENTTYPE_NONEXISTENT
-        item = Item.create(u'Page/Child/Another')
-        assert item.name == u'Page/Child/Another'
+        item = Item.create('Page/Child/Another')
+        assert item.name == 'Page/Child/Another'
         assert item.meta[CONTENTTYPE] == CONTENTTYPE_NONEXISTENT
 
         # item at new name and its contents after renaming
-        item = Item.create(u'Renamed_Page')
-        assert item.name == u'Renamed_Page'
-        assert item.meta[NAME_OLD] == [u'Page']
-        assert item.meta[COMMENT] == u'renamed'
-        assert item.content.data == u'Page 1'
-
-        item = Item.create(u'Renamed_Page/Child')
-        assert item.name == u'Renamed_Page/Child'
-        assert item.meta[NAME_OLD] == [u'Page/Child']
-        assert item.meta[COMMENT] == u'renamed'
-        assert item.content.data == u'this is child'
-
-        item = Item.create(u'Renamed_Page/Child/Another')
-        assert item.name == u'Renamed_Page/Child/Another'
-        assert item.meta[NAME_OLD] == [u'Page/Child/Another']
-        assert item.meta[COMMENT] == u'renamed'
-        assert item.content.data == u'another child'
+        item = Item.create('Renamed_Page')
+        assert item.name == 'Renamed_Page'
+        assert item.meta[NAME_OLD] == ['Page']
+        assert item.meta[COMMENT] == 'renamed'
+        assert item.content.data == b'Page 1'
+
+        item = Item.create('Renamed_Page/Child')
+        assert item.name == 'Renamed_Page/Child'
+        assert item.meta[NAME_OLD] == ['Page/Child']
+        assert item.meta[COMMENT] == 'renamed'
+        assert item.content.data == b'this is child'
+
+        item = Item.create('Renamed_Page/Child/Another')
+        assert item.name == 'Renamed_Page/Child/Another'
+        assert item.meta[NAME_OLD] == ['Page/Child/Another']
+        assert item.meta[COMMENT] == 'renamed'
+        assert item.content.data == b'another child'
 
     def test_rename_recursion_with_multiple_names_and_children(self):
-        update_item(u'Foo', {
-            CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8',
-            NAME: [u'Other', u'Page', u'Foo'],
-        }, u'Parent')
-        update_item(u'Page/Child', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u'Child of Page')
-        update_item(u'Other/Child2', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u'Child of Other')
-        update_item(u'Another', {
-            CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8',
-            NAME: [u'Another', u'Page/Second'],
-        }, u'Both')
-        update_item(u'Page/Second/Child', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u'Child of Second')
-        update_item(u'Another/Child', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u'Child of Another')
-
-        item = Item.create(u'Page')
-
-        item.rename(u'Renamed', comment=u'renamed')
-
-        assert Item.create(u'Page/Child').meta[CONTENTTYPE] == CONTENTTYPE_NONEXISTENT
-        assert Item.create(u'Renamed/Child').content.data == u'Child of Page'
-        assert Item.create(u'Page/Second').meta[CONTENTTYPE] == CONTENTTYPE_NONEXISTENT
-        assert Item.create(u'Renamed/Second').content.data == u'Both'
-        assert Item.create(u'Another').content.data == u'Both'
-        assert Item.create(u'Page/Second/Child').meta[CONTENTTYPE] == CONTENTTYPE_NONEXISTENT
-        assert Item.create(u'Renamed/Second/Child').content.data == u'Child of Second'
-        assert Item.create(u'Other/Child2').content.data == u'Child of Other'
-        assert Item.create(u'Another/Child').content.data == u'Child of Another'
+        update_item('Foo', {
+            CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8',
+            NAME: ['Other', 'Page', 'Foo'],
+        }, 'Parent')
+        update_item('Page/Child', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, 'Child of Page')
+        update_item('Other/Child2', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, 'Child of Other')
+        update_item('Another', {
+            CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8',
+            NAME: ['Another', 'Page/Second'],
+        }, 'Both')
+        update_item('Page/Second/Child', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, 'Child of Second')
+        update_item('Another/Child', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, 'Child of Another')
+
+        item = Item.create('Page')
+
+        item.rename('Renamed', comment='renamed')
+
+        assert Item.create('Page/Child').meta[CONTENTTYPE] == CONTENTTYPE_NONEXISTENT
+        assert Item.create('Renamed/Child').content.data == b'Child of Page'
+        assert Item.create('Page/Second').meta[CONTENTTYPE] == CONTENTTYPE_NONEXISTENT
+        assert Item.create('Renamed/Second').content.data == b'Both'
+        assert Item.create('Another').content.data == b'Both'
+        assert Item.create('Page/Second/Child').meta[CONTENTTYPE] == CONTENTTYPE_NONEXISTENT
+        assert Item.create('Renamed/Second/Child').content.data == b'Child of Second'
+        assert Item.create('Other/Child2').content.data == b'Child of Other'
+        assert Item.create('Another/Child').content.data == b'Child of Another'
 
     def test_delete(self):
-        name = u'Test_Item2'
-        contenttype = u'text/plain;charset=utf-8'
+        name = 'Test_Item2'
+        contenttype = 'text/plain;charset=utf-8'
         data = 'test_data'
         meta = {'test_key': 'test_value', CONTENTTYPE: contenttype}
-        comment = u'saved it'
+        comment = 'saved it'
         item = Item.create(name)
         item._save(meta, data, comment=comment)
         # item and its contents before deleting
         item = Item.create(name)
-        assert item.name == u'Test_Item2'
-        assert item.meta[COMMENT] == u'saved it'
-        item.delete(u'deleted')
+        assert item.name == 'Test_Item2'
+        assert item.meta[COMMENT] == 'saved it'
+        item.delete('deleted')
         # item and its contents after deletion
         item = Item.create(name)
-        assert item.name == u'Test_Item2'
+        assert item.name == 'Test_Item2'
         # this should be a fresh, new item, NOT the stuff we deleted:
         assert item.meta[CONTENTTYPE] == CONTENTTYPE_NONEXISTENT
 
     def test_revert(self):
-        name = u'Test_Item'
-        contenttype = u'text/plain;charset=utf-8'
+        name = 'Test_Item'
+        contenttype = 'text/plain;charset=utf-8'
         data = 'test_data'
         meta = {'test_key': 'test_value', CONTENTTYPE: contenttype}
-        comment = u'saved it'
+        comment = 'saved it'
         item = Item.create(name)
         item._save(meta, data, comment=comment)
         item = Item.create(name)
-        item.revert(u'revert')
+        item.revert('revert')
         item = Item.create(name)
         assert item.meta[ACTION] == ACTION_REVERT
 
     def test_modify(self):
-        name = u'Test_Item'
-        contenttype = u'text/plain;charset=utf-8'
+        name = 'Test_Item'
+        contenttype = 'text/plain;charset=utf-8'
         data = 'test_data'
         meta = {'test_key': 'test_value', CONTENTTYPE: contenttype}
-        comment = u'saved it'
+        comment = 'saved it'
         item = Item.create(name)
         item._save(meta, data, comment=comment)
         item = Item.create(name)
-        assert item.name == u'Test_Item'
+        assert item.name == 'Test_Item'
         assert item.meta['test_key'] == 'test_value'
         # modify
         another_data = 'another_test_data'
         another_meta = {'another_test_key': 'another_test_value'}
         item.modify(another_meta, another_data)
         item = Item.create(name)
-        assert item.name == u'Test_Item'
+        assert item.name == 'Test_Item'
         with pytest.raises(KeyError):
             item.meta['test_key']
         assert item.meta['another_test_key'] == another_meta['another_test_key']
-        assert item.content.data == another_data
+        assert item.content.data == another_data.encode()
         # add/update meta
         another_meta = {
             'test_key': 'test_value',
@@ -368,15 +368,15 @@ def test_modify(self):
         }
         item.modify(another_meta, another_data, **update_meta)
         item = Item.create(name)
-        assert item.name == u'Test_Item'
+        assert item.name == 'Test_Item'
         assert item.meta['test_key'] == another_meta['test_key']
         assert item.meta['another_test_key'] == update_meta['another_test_key']
         assert item.meta['new_test_key'] == update_meta['new_test_key']
         assert 'none_test_key' not in item.meta
 
     def test_trash(self):
-        fqname = u'trash_item_test'
-        contenttype = u'text/plain;charset=utf-8'
+        fqname = 'trash_item_test'
+        contenttype = 'text/plain;charset=utf-8'
         data = 'test_data'
         meta = {CONTENTTYPE: contenttype}
         item = Item.create(fqname)
@@ -389,20 +389,20 @@ def test_trash(self):
         meta[NAME] = []
         # save new rev with no names.
         item._save(meta, data)
-        new_fqname = u'@itemid/' + item.meta[ITEMID]
+        new_fqname = '@itemid/' + item.meta[ITEMID]
         item = Item.create(new_fqname)
         assert item.meta[TRASH]
 
-        new_meta = {NAME: [u'foobar', 'buz'], CONTENTTYPE: contenttype}
+        new_meta = {NAME: ['foobar', 'buz'], CONTENTTYPE: contenttype}
         item._save(new_meta, data)
-        item = Item.create(u'foobar')
+        item = Item.create('foobar')
 
-        item.delete(u'Deleting foobar.')
-        item = Item.create(u'buz')
+        item.delete('Deleting foobar.')
+        item = Item.create('buz')
         assert not item.meta.get(TRASH)
 
         # Also delete the only name left.
-        item.delete(u'Moving item to trash.')
+        item.delete('Moving item to trash.')
         item = Item.create(new_fqname)
         assert item.meta[TRASH]
 
diff --git a/src/moin/items/blog.py b/src/moin/items/blog.py
index 9e1c63f73..dd2bf9011 100644
--- a/src/moin/items/blog.py
+++ b/src/moin/items/blog.py
@@ -25,8 +25,8 @@
 from moin.utils.interwiki import split_fqname
 
 
-ITEMTYPE_BLOG = u'blog'
-ITEMTYPE_BLOG_ENTRY = u'blogentry'
+ITEMTYPE_BLOG = 'blog'
+ITEMTYPE_BLOG_ENTRY = 'blogentry'
 
 
 class BlogMetaForm(BaseMetaForm):
@@ -60,7 +60,7 @@ def do_show(self, revid):
         """
         # for now it is just one tag=value, later it could be tag=value1&tag=value2&...
         tag = request.values.get('tag')
-        prefix = self.name + u'/'
+        prefix = self.name + '/'
         current_timestamp = int(time.time())
         terms = [Term(WIKINAME, app.cfg.interwikiname),
                  # Only blog entry itemtypes
diff --git a/src/moin/items/content.py b/src/moin/items/content.py
index 9ac89e35c..e09a4b7b6 100644
--- a/src/moin/items/content.py
+++ b/src/moin/items/content.py
@@ -138,7 +138,7 @@ def conv_serialize(doc, namespaces, method='polyglot'):
     return out
 
 
-class Content(object):
+class Content:
     """
     Base for content classes defining some helpers, agnostic about content
     data.
@@ -214,8 +214,8 @@ def internal_representation(self, attributes=None, preview=None):
             # is a moin_page.page element? if yes, this is the wrong place to do that
             # as not every doc will have that element (e.g. for images, we just get
             # moin_page.object, for a tar item, we get a moin_page.table):
-            doc.set(moin_page.page_href, unicode(links))
-            if self.contenttype.startswith((u'text/x.moin.wiki', u'text/x-mediawiki', u'text/x.moin.creole', )):
+            doc.set(moin_page.page_href, str(links))
+            if self.contenttype.startswith(('text/x.moin.wiki', 'text/x-mediawiki', 'text/x.moin.creole', )):
                 doc = smiley_conv(doc)
             if cid:
                 app.cache.set(cid, doc)
@@ -428,7 +428,7 @@ class Application(Binary):
     """ Base class for application/* """
 
 
-class TarMixin(object):
+class TarMixin:
     """
     TarMixin offers additional functionality for tar-like items to list and
     access member files and to create new revisions by multiple posts.
@@ -458,30 +458,30 @@ def put_member(self, name, content, content_length, expected_members):
         to a new item revision.
 
         :param name: name of the data in the container file
-        :param content: the data to store into the tar file (str or file-like)
-        :param content_length: byte-length of content (for str, None can be given)
+        :param content: the data to store into the tar file (bytes or file-like)
+        :param content_length: byte-length of content (for bytes, None can be given)
         :param expected_members: set of expected member file names
         """
         if name not in expected_members:
             raise StorageError("tried to add unexpected member {0!r} to container item {1!r}".format(name, self.name))
-        if isinstance(name, unicode):
-            name = name.encode('utf-8')
+        assert isinstance(name, str)
         temp_fname = os.path.join(tempfile.gettempdir(), 'TarContainer_' +
                                   cache_key(usage='TarContainer', name=self.name))
-        tf = tarfile.TarFile(temp_fname, mode='a')
-        ti = tarfile.TarInfo(name)
-        if isinstance(content, bytes):
-            if content_length is None:
-                content_length = len(content)
-            content = BytesIO(content)  # we need a file obj
-        elif not hasattr(content, 'read'):
-            logging.error("unsupported content object: {0!r}".format(content))
-            raise StorageError("unsupported content object: {0!r}".format(content))
-        assert content_length >= 0  # we don't want -1 interpreted as 4G-1
-        ti.size = content_length
-        tf.addfile(ti, content)
-        tf_members = set(tf.getnames())
-        tf.close()
+        with tarfile.open(temp_fname, mode='a') as tf:
+            ti = tarfile.TarInfo(name)
+            if isinstance(content, bytes):
+                if content_length is None:
+                    content_length = len(content)
+                content = BytesIO(content)  # we need a file obj
+            elif not hasattr(content, 'read'):
+                logging.error("unsupported content object: {0!r}".format(content))
+                raise StorageError("unsupported content object: {0!r}".format(content))
+            else:
+                raise NotImplemented
+            assert content_length >= 0  # we don't want -1 interpreted as 4G-1
+            ti.size = content_length
+            tf.addfile(ti, content)
+            tf_members = set(tf.getnames())
         if tf_members - expected_members:
             msg = "found unexpected members in container item {0!r}".format(self.name)
             logging.error(msg)
@@ -490,9 +490,8 @@ def put_member(self, name, content, content_length, expected_members):
         if tf_members == expected_members:
             # everything we expected has been added to the tar file, save the container as revision
             meta = {CONTENTTYPE: self.contenttype}
-            data = open(temp_fname, 'rb')
-            self.item._save(meta, data, name=self.name, action=ACTION_SAVE, comment='')
-            data.close()
+            with open(temp_fname, 'rb') as data:
+                self.item._save(meta, data, name=self.name, action=ACTION_SAVE, comment='')
             os.remove(temp_fname)
 
 
@@ -514,7 +513,7 @@ class ApplicationXGTar(ApplicationXTar):
     display_name = 'TGZ'
 
 
-class ZipMixin(object):
+class ZipMixin:
     """
     ZipMixin offers additional functionality for zip-like items to list and
     access member files.
@@ -736,14 +735,14 @@ def _render_data_diff_atom(self, oldrev, newrev):
         url = url_for('frontend.diffraw', _external=True, item_name=self.name, rev1=oldrev.revid, rev2=newrev.revid)
         return render_template('atom.html',
                                oldrev=oldrev, newrev=newrev, get='binary',
-                               content=Markup(u''.format(escape(url))))
+                               content=Markup(''.format(escape(url))))
 
     def _render_data_diff(self, oldrev, newrev, rev_links={}):
         if PIL is None:
             # no PIL, we can't do anything, we just call the base class method
             return super(TransformableBitmapImage, self)._render_data_diff(oldrev, newrev)
         url = url_for('frontend.diffraw', item_name=self.name, rev1=oldrev.revid, rev2=newrev.revid)
-        return Markup(u''.format(escape(url)))
+        return Markup(''.format(escape(url)))
 
     def _render_data_diff_raw(self, oldrev, newrev):
         hash_name = HASH_ALGORITHM
@@ -838,25 +837,25 @@ def _dump(self, item):
                 data = item.data_form_to_internal(data)
                 data = item.data_internal_to_storage(data)
                 # we know it is text and utf-8 - XXX is there a way to get the charset of the form?
-                contenttype_guessed = u'text/plain;charset=utf-8'
+                contenttype_guessed = 'text/plain;charset=utf-8'
             return data, contenttype_guessed
 
     # text/plain mandates crlf - but in memory, we want lf only
     def data_internal_to_form(self, text):
         """ convert data from memory format to form format """
-        return text.replace(u'\n', u'\r\n')
+        return text.replace('\n', '\r\n')
 
     def data_form_to_internal(self, data):
         """ convert data from form format to memory format """
-        return data.replace(u'\r\n', u'\n')
+        return data.replace('\r\n', '\n')
 
     def data_internal_to_storage(self, text):
         """ convert data from memory format to storage format """
-        return text.replace(u'\n', u'\r\n').encode(CHARSET)
+        return text.replace('\n', '\r\n').encode(CHARSET)
 
     def data_storage_to_internal(self, data):
         """ convert data from storage format to memory format """
-        return data.decode(CHARSET).replace(u'\r\n', u'\n')
+        return data.decode(CHARSET).replace('\r\n', '\n')
 
     def _render_data_diff_html(self, oldrev, newrev, template, rev_links={}, fqname=None):
         """ Render HTML formatted meta and content diff of 2 revisions
@@ -1114,9 +1113,9 @@ def _render_data(self):
         if image_map:
             mapid, image_map = self._transform_map(image_map, title)
             title = _('Clickable drawing: %(filename)s', filename=self.name)
-            return Markup(image_map + u'{1}'.format(png_url, title, mapid))
+            return Markup(image_map + '{1}'.format(png_url, title, mapid))
         else:
-            return Markup(u'{1}'.format(png_url, title))
+            return Markup('{1}'.format(png_url, title))
 
 
 class DrawAWDTWDBase(DrawPNGMap):
@@ -1155,7 +1154,7 @@ class TWikiDraw(DrawAWDTWDBase):
     """
     contenttype = 'application/x-twikidraw'
     display_name = 'TDRAW'
-    _expected_members = set(['drawing.draw', 'drawing.map', 'drawing.png'])
+    _expected_members = {'drawing.draw', 'drawing.map', 'drawing.png'}
 
     class ModifyForm(Draw.ModifyForm):
         template = "modify_twikidraw.html"
@@ -1178,7 +1177,7 @@ class AnyWikiDraw(DrawAWDTWDBase):
     """
     contenttype = 'application/x-anywikidraw'
     display_name = 'ADRAW'
-    _expected_members = set(['drawing.svg', 'drawing.map', 'drawing.png'])
+    _expected_members = {'drawing.svg', 'drawing.map', 'drawing.png'}
 
     class ModifyForm(Draw.ModifyForm):
         template = "modify_anywikidraw.html"
@@ -1194,10 +1193,10 @@ def _load(self, item):
     def _transform_map(self, image_map, title):
         # drawing_url = url_for('frontend.get_item', item_name=self.name, member='drawing.svg', rev=self.rev.revid)
         mapid = 'ImageMapOf' + self.name  # TODO: make it unique
-        image_map = image_map.replace(u'id="drawing.svg"', '')
-        image_map = image_map.replace(u'name="drawing.svg"', u'name="{0}"'.format(mapid))
+        image_map = image_map.replace('id="drawing.svg"', '')
+        image_map = image_map.replace('name="drawing.svg"', 'name="{0}"'.format(mapid))
         # unxml, because 4.01 concrete will not validate />
-        image_map = image_map.replace(u'/>', u'>')
+        image_map = image_map.replace('/>', '>')
         return mapid, image_map
 
 
@@ -1219,13 +1218,13 @@ def handle_post(self):
         svg_content = svg_upload.decode('base_64')
         content_length = None
         self.put_member("drawing.svg", svg_content, content_length,
-                        expected_members=set(['drawing.svg', 'drawing.png']))
+                        expected_members={'drawing.svg', 'drawing.png'})
         self.put_member("drawing.png", png_content, content_length,
-                        expected_members=set(['drawing.svg', 'drawing.png']))
+                        expected_members={'drawing.svg', 'drawing.png'})
 
     def _render_data(self):
         # TODO: this could be a converter -> dom, then transcluding this kind
         # of items and also rendering them with the code in base class could work
         drawing_url = url_for('frontend.get_item', item_name=self.name, member='drawing.svg', rev=self.rev.revid)
         png_url = url_for('frontend.get_item', item_name=self.name, member='drawing.png', rev=self.rev.revid)
-        return Markup(u'{1}'.format(png_url, drawing_url))
+        return Markup('{1}'.format(png_url, drawing_url))
diff --git a/src/moin/items/ticket.py b/src/moin/items/ticket.py
index bcfbd58a2..de89ef01a 100644
--- a/src/moin/items/ticket.py
+++ b/src/moin/items/ticket.py
@@ -35,9 +35,6 @@
 ticket through a refers_to field preserved in meta data.
 """
 
-
-from __future__ import absolute_import, division
-
 import time
 import datetime
 
@@ -178,7 +175,7 @@ def _dump(self, item):
 
         # create an "Update" comment if metadata changes
         meta_changes = []
-        for key, value in self['meta'].value.iteritems():
+        for key, value in self['meta'].value.items():
             if not meta.get(key) == value:
                 if key == TAGS:
                     original = ', '.join(meta.get(key))
@@ -190,7 +187,7 @@ def _dump(self, item):
                     original = meta.get(key)
                     new = value
                 msg = L_('{key} changed from {original} to {new}'.format(key=key, original=original, new=new))
-                meta_changes.append(u' * ' + msg)
+                meta_changes.append(' * ' + msg)
         if meta_changes:
             meta_changes = 'Meta updates:\n' + '\n'.join(meta_changes)
             create_comment(meta, meta_changes)
@@ -215,8 +212,8 @@ def message_markup(message):
     """
     timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
     heading = L_('{author} wrote on {timestamp}:').format(author=flaskg.user.name[0], timestamp=timestamp)
-    message = u'{heading}\n\n{message}'.format(heading=heading, message=message)
-    return u"""{{{{{{#!wiki moin-ticket
+    message = '{heading}\n\n{message}'.format(heading=heading, message=message)
+    return """{{{{{{#!wiki moin-ticket
 %(message)s
 }}}}}}""" % dict(message=message)
 
@@ -234,7 +231,7 @@ def check_itemid(self):
                 try:
                     new_name = self.meta[ITEMID] + '/' + file_name
                     item = Item.create(new_name)
-                    item.modify({}, rev.meta[CONTENT], refers_to=self.meta[ITEMID], element=u'file')
+                    item.modify({}, rev.meta[CONTENT], refers_to=self.meta[ITEMID], element='file')
                     item = Item.create(old_name)
                     item._save(item.meta, name=old_name, action=ACTION_TRASH)  # delete
                 except AccessDenied:
@@ -253,7 +250,7 @@ def file_upload(self, data_file):
         refers_to = self.fqname.value
     try:
         item = Item.create(item_name)
-        item.modify({}, data, contenttype_guessed=contenttype, refers_to=refers_to, element=u'file')
+        item.modify({}, data, contenttype_guessed=contenttype, refers_to=refers_to, element='file')
     except AccessDenied:
         abort(403)
 
@@ -266,7 +263,7 @@ def get_files(self):
     else:
         refers_to = self.fqname.value
         prefix = self.fqname.value + '/'
-    query = And([Term(WIKINAME, app.cfg.interwikiname), Term(REFERS_TO, refers_to), Term(ELEMENT, u'file')])
+    query = And([Term(WIKINAME, app.cfg.interwikiname), Term(REFERS_TO, refers_to), Term(ELEMENT, 'file')])
     revs = flaskg.storage.search(query, limit=None)
     files = []
     for rev in revs:
@@ -283,7 +280,7 @@ def get_comments(self):
     Return a list of roots (comments to original ticket) and a dict of comments (comments to comments).
     """
     refers_to = self.meta[ITEMID]
-    query = And([Term(WIKINAME, app.cfg.interwikiname), Term(REFERS_TO, refers_to), Term(ELEMENT, u'comment')])
+    query = And([Term(WIKINAME, app.cfg.interwikiname), Term(REFERS_TO, refers_to), Term(ELEMENT, 'comment')])
     revs = flaskg.storage.search(query, sortedby=[MTIME], limit=None)
     comments = dict()  # {rev: [],...} comments to a comment
     lookup = dict()  # {itemid: rev,...}
@@ -335,10 +332,10 @@ def create_comment(meta, message):
     Create a new item comment against original description, refers_to links to original.
     """
     current_timestamp = datetime.datetime.now().strftime("%Y_%m_%d-%H_%M_%S")
-    item_name = meta[ITEMID] + u'/' + u'comment_' + unicode(current_timestamp)
+    item_name = meta[ITEMID] + '/' + 'comment_' + str(current_timestamp)
     item = Item.create(item_name)
-    item.modify({}, data=message, element=u'comment', contenttype_guessed=u'text/x.moin.wiki;charset=utf-8',
-                refers_to=meta[ITEMID], reply_to=u'', author=flaskg.user.name[0], timestamp=time.ctime())
+    item.modify({}, data=message, element='comment', contenttype_guessed='text/x.moin.wiki;charset=utf-8',
+                refers_to=meta[ITEMID], reply_to='', author=flaskg.user.name[0], timestamp=time.ctime())
 
 
 @register
diff --git a/src/moin/log.py b/src/moin/log.py
index babbc452c..d73bb70c4 100644
--- a/src/moin/log.py
+++ b/src/moin/log.py
@@ -51,10 +51,7 @@
     will do the logging.
 """
 
-
-from __future__ import absolute_import, division
-
-from io import BytesIO
+from io import StringIO
 import os
 import logging
 import logging.config
@@ -67,7 +64,7 @@
 # See http://docs.python.org/library/logging.html#configuring-logging
 # We just use stderr output by default, if you want anything else,
 # you will have to configure logging.
-logging_config = b"""\
+logging_config = """\
 [DEFAULT]
 # Default loglevel, to adjust verbosity: DEBUG, INFO, WARNING, ERROR, CRITICAL
 loglevel=INFO
@@ -144,7 +141,7 @@ def load_config(conf_fname=None):
             err_msg = str(err)
     if not configured:
         # load builtin fallback logging config
-        with BytesIO(logging_config) as f:
+        with StringIO(logging_config) as f:
             logging.config.fileConfig(f)
         configured = True
         logger = getLogger(__name__)
@@ -168,9 +165,8 @@ def getLogger(name):
     if not configured:
         load_config()
     logger = logging.getLogger(name)
-    for levelnumber, levelname in logging._levelNames.items():
-        if isinstance(levelnumber, int):  # that list has also the reverse mapping...
-            setattr(logger, levelname, levelnumber)
+    for levelnumber, levelname in logging._levelToName.items():
+        setattr(logger, levelname, levelnumber)
     return logger
 
 
@@ -178,14 +174,14 @@ class EmailHandler(logging.Handler):
     """ A custom handler class which sends email for each logging event using
     wiki mail configuration
     """
-    def __init__(self, toaddrs=[], subject=u''):
+    def __init__(self, toaddrs=[], subject=''):
         """ Initialize the handler
 
         :param toaddrs: address or a list of email addresses whom to send email
         :param subject: unicode email's subject
         """
         logging.Handler.__init__(self)
-        if isinstance(toaddrs, basestring):
+        if isinstance(toaddrs, str):
             toaddrs = [toaddrs]
         self.toaddrs = toaddrs
         self.subject = subject
@@ -215,7 +211,7 @@ def emit(self, record):
         try:
             toaddrs = self.toaddrs if self.toaddrs else app.cfg.admin_emails
             log_level = logging.getLevelName(self.level)
-            subject = self.subject if self.subject else u'[{0}][{1}] Log message'.format(
+            subject = self.subject if self.subject else '[{0}][{1}] Log message'.format(
                 app.cfg.sitename, log_level)
             msg = self.format(record)
             from moin.mail.sendmail import sendmail
diff --git a/src/moin/macros/GetVal.py b/src/moin/macros/GetVal.py
index 99f19c157..2026c18fb 100644
--- a/src/moin/macros/GetVal.py
+++ b/src/moin/macros/GetVal.py
@@ -22,7 +22,7 @@ def macro(self, content, arguments, page_url, alternative):
             key = args[1].strip()
         except (IndexError, AssertionError):
             raise ValueError(_("GetVal: invalid parameters, try <>"))
-        if not flaskg.user.may.read(unicode(item_name)):
+        if not flaskg.user.may.read(str(item_name)):
             raise ValueError(_("GetVal: permission to read denied: ") + item_name)
         try:
             d = flaskg.dicts[item_name]
diff --git a/src/moin/macros/MailTo.py b/src/moin/macros/MailTo.py
index 43adf342f..cfe7659e0 100644
--- a/src/moin/macros/MailTo.py
+++ b/src/moin/macros/MailTo.py
@@ -34,12 +34,12 @@ def macro(self, content, arguments, page_url, alternative):
         try:
             text = arguments[1]
         except IndexError:
-            text = u''
+            text = ''
 
         if flaskg.user.valid:
             # decode address and generate mailto: link
             email = decodeSpamSafeEmail(email)
-            result = moin_page.a(attrib={xlink.href: u'mailto:{0}'.format(email)}, children=[text or email])
+            result = moin_page.a(attrib={xlink.href: 'mailto:{0}'.format(email)}, children=[text or email])
         else:
             # unknown user, maybe even a spambot, so just return text as given in macro args
             if text:
diff --git a/src/moin/macros/PagenameList.py b/src/moin/macros/PagenameList.py
index 3aaf11e4b..5fa1ae05d 100644
--- a/src/moin/macros/PagenameList.py
+++ b/src/moin/macros/PagenameList.py
@@ -22,7 +22,7 @@ def macro(self, content, arguments, page_url, alternative):
             arguments = arguments[0].split(',')
         else:
             # default is to list all items
-            arguments = (u'^.*', u'True')
+            arguments = ('^.*', 'True')
 
         needle = arguments[0]
         try:
@@ -36,7 +36,7 @@ def macro(self, content, arguments, page_url, alternative):
             except re.error as err:
                 raise ValueError("Error in regex {0!r}: {1}".format(needle, err))
         else:
-            needle_re = re.compile(u'^' + re.escape(needle), re_flags)
+            needle_re = re.compile('^' + re.escape(needle), re_flags)
 
         item_names = []
         for item in self.get_item_names():
diff --git a/src/moin/macros/RandomItem.py b/src/moin/macros/RandomItem.py
index 2b76b8eba..949052946 100644
--- a/src/moin/macros/RandomItem.py
+++ b/src/moin/macros/RandomItem.py
@@ -54,7 +54,7 @@ def macro(self, content, arguments, page_url, alternative):
 
         result = moin_page.span()
         for name in random_item_names:
-            link = unicode(Iri(scheme=u'wiki', authority=u'', path=u'/' + name))
+            link = str(Iri(scheme='wiki', authority='', path='/' + name))
             result.append(moin_page.a(attrib={xlink.href: link}, children=[name]))
             result.append(", ")
 
diff --git a/src/moin/macros/_base.py b/src/moin/macros/_base.py
index d5434c17e..04e877669 100644
--- a/src/moin/macros/_base.py
+++ b/src/moin/macros/_base.py
@@ -14,7 +14,7 @@
 from moin.storage.middleware.protecting import AccessDenied
 
 
-class MacroBase(object):
+class MacroBase:
     """
     Macro base class.
     """
@@ -99,7 +99,7 @@ def create_pagelink_list(self, pagenames, ordered=False, display="FullPath"):
         page_list = moin_page.list(attrib={moin_page.item_label_generate: ordered and 'ordered' or 'unordered'})
         for pagename in pagenames:
             # This link can never reach pagelinks
-            url = unicode(iri.Iri(scheme=u'wiki', authority=u'', path=u'/' + pagename))
+            url = str(iri.Iri(scheme='wiki', authority='', path='/' + pagename))
 
             if display == "FullPath":
                 linkname = pagename
@@ -170,7 +170,7 @@ def create_number_pagelink_list(self, num_pagenames, ordered=False):
         for num, pagename in num_pagenames:
             num_code = moin_page.code(children=["{0:6d} ".format(num)])
             # This link can never reach pagelinks
-            url = unicode(iri.Iri(scheme=u'wiki', authority=u'', path=u'/' + pagename))
+            url = str(iri.Iri(scheme='wiki', authority='', path='/' + pagename))
             pagelink = moin_page.a(attrib={xlink.href: url}, children=[pagename])
             item_body = moin_page.list_item_body(children=[num_code, pagelink])
             item = moin_page.list_item(children=[item_body])
diff --git a/src/moin/macros/_tests/test_Anchor.py b/src/moin/macros/_tests/test_Anchor.py
index 754e11044..4ff22cb59 100644
--- a/src/moin/macros/_tests/test_Anchor.py
+++ b/src/moin/macros/_tests/test_Anchor.py
@@ -17,6 +17,6 @@ def test_Macro():
 
     arguments = [('test_argument1', 'test_argument2'), 'test_argument3']
     result = macro_obj.macro('content', arguments, 'page_url', 'alternative')
-    test_anchor = result.attrib.values()
+    test_anchor = list(result.attrib.values())
     # test_anchor[0] since it returns a list
     assert test_anchor[0] == arguments[0]
diff --git a/src/moin/macros/_tests/test_Date.py b/src/moin/macros/_tests/test_Date.py
index e758a60cd..f30d83f51 100644
--- a/src/moin/macros/_tests/test_Date.py
+++ b/src/moin/macros/_tests/test_Date.py
@@ -14,7 +14,7 @@
 from moin.macros.Date import MacroDateTimeBase, Macro
 
 
-class TestMacroDateTimeBase(object):
+class TestMacroDateTimeBase:
     def test_parse_time(self):
         MacroDateTimeBase_obj = MacroDateTimeBase()
         test_time_args = '2011-08-07T11:11:11+0533'
@@ -22,14 +22,14 @@ def test_parse_time(self):
         expected = 1312695491.0
         assert result == expected
         result = format_datetime(datetime.utcfromtimestamp(result))
-        expected = u'Aug 7, 2011, 5:38:11 AM'  # comma after year was added in recent CLDR
+        expected = 'Aug 7, 2011, 5:38:11 AM'  # comma after year was added in recent CLDR
         assert result == expected
         with pytest.raises(ValueError):
             # things after next 10,000 years can't be predicted
             MacroDateTimeBase_obj.parse_time('12011-08-07T11:11:11')
 
 
-class TestMacro(object):
+class TestMacro:
     def test_macro(self):
         macro_obj = Macro()
         # when arguments is None
@@ -40,5 +40,5 @@ def test_macro(self):
 
         arguments = ['2011-08-07T11:11:11+0533', 'argument2']
         result = macro_obj.macro('content', arguments, 'page_url', 'alternative')
-        expected = u'Aug 7, 2011'
+        expected = 'Aug 7, 2011'
         assert result == expected
diff --git a/src/moin/macros/_tests/test_DateTime.py b/src/moin/macros/_tests/test_DateTime.py
index 2c08ca031..4c1817dcb 100644
--- a/src/moin/macros/_tests/test_DateTime.py
+++ b/src/moin/macros/_tests/test_DateTime.py
@@ -26,7 +26,7 @@ def test_Macro():
 
     arguments = ['2011-08-07T11:11:11', 'argument2']
     result = macro_obj.macro('content', arguments, 'page_url', 'alternative')
-    expected = u'Aug 7, 2011, 11:11:11 AM'  # comma after year was added in recent CLDR
+    expected = 'Aug 7, 2011, 11:11:11 AM'  # comma after year was added in recent CLDR
     assert result == expected
 
     arguments = ['incorrect_argument']
diff --git a/src/moin/macros/_tests/test_GetText.py b/src/moin/macros/_tests/test_GetText.py
index a0166f2a8..960c3babb 100644
--- a/src/moin/macros/_tests/test_GetText.py
+++ b/src/moin/macros/_tests/test_GetText.py
@@ -14,5 +14,5 @@ def test_Macro():
     macro_obj = Macro()
     arguments = Arguments(['test_argument1 test_argument2'])
     result = macro_obj.macro('content', arguments, 'page_url', 'alternative')
-    expected = u'test_argument1 test_argument2'
+    expected = 'test_argument1 test_argument2'
     assert result == expected
diff --git a/src/moin/macros/_tests/test_GetVal.py b/src/moin/macros/_tests/test_GetVal.py
index 74ef75c90..ec9b87bc8 100644
--- a/src/moin/macros/_tests/test_GetVal.py
+++ b/src/moin/macros/_tests/test_GetVal.py
@@ -13,15 +13,15 @@
 from moin._tests import become_trusted, update_item
 
 
-class TestMacro(object):
+class TestMacro:
     @pytest.fixture
     def test_dict(self):
         become_trusted()
-        somedict = {u"One": u"1",
-                    u"Two": u"2"}
-        update_item(u'TestDict', {SOMEDICT: somedict}, "This is a dict item.")
+        somedict = {"One": "1",
+                    "Two": "2"}
+        update_item('TestDict', {SOMEDICT: somedict}, "This is a dict item.")
 
-        return u"TestDict"
+        return "TestDict"
 
     def test_Macro(self, test_dict):
         macro_obj = Macro()
@@ -33,11 +33,11 @@ def test_Macro(self, test_dict):
             with pytest.raises(ValueError):
                 macro_obj.macro('content', arguments, 'page_url', 'alternative')
 
-        arguments = [u'TestDict, One']
+        arguments = ['TestDict, One']
         result = macro_obj.macro('content', arguments, 'page_url', 'alternative')
-        assert result == u'1'
+        assert result == '1'
 
         # change the value of second element
-        arguments = [u'TestDict, Two']
+        arguments = ['TestDict, Two']
         result = macro_obj.macro('content', arguments, 'page_url', 'alternative')
-        assert result == u'2'
+        assert result == '2'
diff --git a/src/moin/macros/_tests/test_Verbatim.py b/src/moin/macros/_tests/test_Verbatim.py
index 044f1e3f0..5c5c9d3f5 100644
--- a/src/moin/macros/_tests/test_Verbatim.py
+++ b/src/moin/macros/_tests/test_Verbatim.py
@@ -12,4 +12,4 @@ def test_Macro():
     arguments = ['test text']
     macro_obj = Macro()
     result = macro_obj.macro('content', arguments, 'page_url', 'alternative')
-    assert result == u'test text'
+    assert result == 'test text'
diff --git a/src/moin/macros/_tests/test__base.py b/src/moin/macros/_tests/test__base.py
index cf26f774c..498e2e906 100644
--- a/src/moin/macros/_tests/test__base.py
+++ b/src/moin/macros/_tests/test__base.py
@@ -9,7 +9,7 @@
 from moin.macros._base import MacroBase, MacroBlockBase, MacroInlineBase, MacroInlineOnlyBase, MacroPageLinkListBase
 
 
-class TestMacroBase(object):
+class TestMacroBase:
     """ Test for Macro base and related classes """
 
     def test_MacroBase(self):
diff --git a/src/moin/mail/_tests/test_sendmail.py b/src/moin/mail/_tests/test_sendmail.py
index 53d0dd487..0bd0414dd 100644
--- a/src/moin/mail/_tests/test_sendmail.py
+++ b/src/moin/mail/_tests/test_sendmail.py
@@ -7,14 +7,10 @@
 """
 
 
-from email.charset import Charset, QP
-from email.header import Header
-
 from moin.mail import sendmail
-from moin.constants.contenttypes import CHARSET
 
 
-class TestdecodeSpamSafeEmail(object):
+class TestdecodeSpamSafeEmail:
     """mail.sendmail: testing mail"""
 
     _tests = (
@@ -43,7 +39,7 @@ def testDecodeSpamSafeMail(self):
             assert sendmail.decodeSpamSafeEmail(coded) == expected
 
 
-class TestencodeSpamSafeEmail(object):
+class TestencodeSpamSafeEmail:
     """mail.sendmail: testing spam safe mail"""
 
     _tests = (
@@ -70,65 +66,4 @@ def testEncodeSpamSafeMailAndObfuscate(self):
             assert sendmail.encodeSpamSafeEmail(coded, 'SYCTE') == expected
 
 
-class TestEncodeAddress(object):
-    """ Address encoding tests
-
-    See http://www.faqs.org/rfcs/rfc2822.html section 3.4.
-    Address Specification.
-
-    mailbox     =   name-addr / addr-spec
-    name-addr   =   [display-name] angle-addr
-    angle-addr  =   [CFWS] "<" addr-spec ">" [CFWS] / obs-angle-addr
-    """
-    charset = Charset(CHARSET)
-    charset.header_encoding = QP
-    charset.body_encoding = QP
-
-    def testSimpleAddress(self):
-        """ mail.sendmail: encode simple address: local@domain """
-        address = u'local@domain'
-        expected = address.encode(CHARSET)
-        assert sendmail.encodeAddress(address, self.charset) == expected
-
-    def testComposite(self):
-        """ mail.sendmail: encode address: 'Phrase ' """
-        address = u'Phrase '
-        expected = str(address)
-        assert sendmail.encodeAddress(address, self.charset) == expected
-
-    def testCompositeUnicode(self):
-        """ mail.sendmail: encode Uncode address: 'ויקי ' """
-        address = u'ויקי '
-        phrase = str(Header(u'ויקי'.encode('utf-8'), self.charset))
-        expected = phrase + ' ' + ''
-        assert sendmail.encodeAddress(address, self.charset) == expected
-
-    def testEmptyPhrase(self):
-        """ mail.sendmail: encode address with empty phrase: '' """
-        address = u''
-        expected = 'local@domain'
-        assert sendmail.encodeAddress(address, self.charset) == expected
-
-    def testEmptyAddress(self):
-        """ mail.sendmail: encode address with empty address: 'Phrase <>'
-
-        Let the smtp server handle this. We may raise error in such
-        case, but we don't do error checking for mail addresses.
-        """
-        address = u'Phrase <>'
-        expected = str(address)
-        assert sendmail.encodeAddress(address, self.charset) == expected
-
-    def testInvalidAddress(self):
-        """ mail.sendmail: encode invalid address 'Phrase '. According to the RFC, the name
-    part should be encoded, the address should not.
-
-    :param address: email address, possibly using '"name" 
' format - :type address: unicode - :param charset: specifying both the charset and the encoding, e.g - quoted printable or base64. - :type charset: email.Charset.Charset instance - :rtype: string - :returns: encoded address - """ - assert isinstance(address, unicode) - composite = re.compile(r'(?P.*?)(?P\s*)<(?P.*)>', re.UNICODE) - match = composite.match(address) - if match: - phrase = match.group('phrase') - try: - str(phrase) # is it pure ascii? - except UnicodeEncodeError: - phrase = phrase.encode(CHARSET) - phrase = Header(phrase, charset) - blanks = match.group('blanks') - addr = match.group('addr') - if phrase: - return "{0!s}{1!s}<{2!s}>".format(phrase, blanks, addr) - else: - return str(addr) - else: - # a pure email address, should encode to ascii without problem - return str(address) - - def sendmail(subject, text, to=None, cc=None, bcc=None, mail_from=None, html=None): """ Create and send a text/plain message Return a tuple of success or error indicator and message. :param subject: subject of email - :type subject: unicode + :type subject: str :param text: email body text - :type text: unicode + :type text: str :param to: recipients - :type to: list + :type to: list of str :param cc: recipients (CC) - :type cc: list + :type cc: list of str :param bcc: recipients (BCC) - :type bcc: list + :type bcc: list of str :param mail_from: override default mail_from - :type mail_from: unicode + :type mail_from: str :param html: html email body text - :type html: unicode + :type html: str :rtype: tuple :returns: (is_ok, Description of error or OK message) """ - import smtplib - import socket - from email.message import Message - from email.mime.multipart import MIMEMultipart - from email.mime.text import MIMEText - from email.charset import Charset, QP - from email.utils import formatdate, make_msgid - cfg = app.cfg if not cfg.mail_enabled: return (0, _("Contact administrator: cannot send password recovery e-mail " @@ -103,56 +62,28 @@ def sendmail(subject, text, to=None, cc=None, bcc=None, mail_from=None, html=Non if not to and not cc and not bcc: return 1, _("No recipients, nothing to do") - subject = subject.encode(CHARSET) - - # Create a text/plain body using CRLF (see RFC2822) - text = text.replace(u'\n', u'\r\n') - text = text.encode(CHARSET) - - # Create a message using CHARSET and quoted printable - # encoding, which should be supported better by mail clients. - # TODO: check if its really works better for major mail clients - text_msg = Message() - charset = Charset(CHARSET) - charset.header_encoding = QP - charset.body_encoding = QP - text_msg.set_charset(charset) - - # work around a bug in python 2.4.3 and above: - text_msg.set_payload('=') - if text_msg.as_string().endswith('='): - text = charset.body_encode(text) - - text_msg.set_payload(text) + msg = EmailMessage() + msg.set_content(text) if html: - msg = MIMEMultipart('alternative') - msg.attach(text_msg) - html = html.encode(CHARSET) - html_msg = MIMEText(html, 'html') - html_msg.set_charset(charset) - msg.attach(html_msg) - else: - msg = text_msg + msg.add_alternative(html, subtype='html') - address = encodeAddress(mail_from, charset) - msg['From'] = address + msg['From'] = mail_from if to: - msg['To'] = ','.join(to) + msg['To'] = to if cc: - msg['CC'] = ','.join(cc) + msg['CC'] = cc + msg['Subject'] = subject msg['Date'] = formatdate() msg['Message-ID'] = make_msgid() - msg['Subject'] = Header(subject, charset) - # See RFC 3834 section 5: - msg['Auto-Submitted'] = 'auto-generated' + msg['Auto-Submitted'] = 'auto-generated' # RFC 3834 section 5 if cfg.mail_sendmail: if bcc: # Set the BCC. This will be stripped later by sendmail. - msg['BCC'] = ','.join(bcc) + msg['BCC'] = bcc # Set Return-Path so that it isn't set (generally incorrectly) for us. - msg['Return-Path'] = address + msg['Return-Path'] = mail_from # Send the message if not cfg.mail_sendmail: @@ -161,19 +92,19 @@ def sendmail(subject, text, to=None, cc=None, bcc=None, mail_from=None, html=Non host, port = (cfg.mail_smarthost + ':25').split(':')[:2] server = smtplib.SMTP(host, int(port)) try: + server.ehlo() + try: # try to do TLS + if server.has_extn('starttls'): + server.starttls() + server.ehlo() + logging.debug("tls connection to smtp server established") + except Exception: + logging.debug("could not establish a tls connection to smtp server, continuing without tls") # server.set_debuglevel(1) if cfg.mail_username is not None and cfg.mail_password is not None: - try: # try to do TLS - server.ehlo() - if server.has_extn('starttls'): - server.starttls() - server.ehlo() - logging.debug("tls connection to smtp server established") - except Exception: - logging.debug("could not establish a tls connection to smtp server, continuing without tls") logging.debug("trying to log in to smtp server using account '{0}'".format(cfg.mail_username)) server.login(cfg.mail_username, cfg.mail_password) - server.sendmail(mail_from, (to or []) + (cc or []) + (bcc or []), msg.as_string()) + server.send_message(msg) finally: try: server.quit() @@ -190,18 +121,7 @@ def sendmail(subject, text, to=None, cc=None, bcc=None, mail_from=None, html=Non reason=str(e) )) else: - try: - logging.debug("trying to send mail (sendmail)") - sendmailp = os.popen(cfg.mail_sendmail, "w") - # msg contains everything we need, so this is a simple write - sendmailp.write(msg.as_string()) - sendmail_status = sendmailp.close() - if sendmail_status: - logging.error("sendmail failed with status: {0!s}".format(sendmail_status)) - return 0, str(sendmail_status) - except Exception: - logging.exception("sendmail failed with an exception.") - return 0, _("Mail not sent") + raise NotImplemented # TODO cli sendmail support logging.debug("Mail sent successfully") return 1, _("Mail sent successfully") diff --git a/src/moin/scripts/account/create.py b/src/moin/scripts/account/create.py index a1274d024..a32699c35 100644 --- a/src/moin/scripts/account/create.py +++ b/src/moin/scripts/account/create.py @@ -16,13 +16,13 @@ class Create_User(Command): description = 'This command allows you to create a user account' option_list = ( - Option('--name', '-n', required=True, dest='name', type=unicode, + Option('--name', '-n', required=True, dest='name', type=str, help="Set the wiki user name to NAME."), - Option('--display_name', '-d', required=False, dest="display_name", type=unicode, + Option('--display_name', '-d', required=False, dest="display_name", type=str, help="Set the wiki user's display name to DISPLAY_NAME (e.g. in case the NAME is cryptic)."), - Option('--email', '-e', required=True, dest='email', type=unicode, + Option('--email', '-e', required=True, dest='email', type=str, help="Set the user's email address to EMAIL."), - Option('--password', '-p', required=True, dest="password", type=unicode, + Option('--password', '-p', required=True, dest="password", type=str, help="Set the user's password to PASSWORD."), ) @@ -33,7 +33,7 @@ def run(self, name, display_name, email, password): email=email) if msg: - print msg + print(msg) else: u = user.User(auth_username=name) - print " %-20s %-25s %-35s - created." % (u.itemid, u.name, u.email), + print(" %-20s %-25s %-35s - created." % (u.itemid, u.name, u.email), end=' ') diff --git a/src/moin/scripts/account/disable.py b/src/moin/scripts/account/disable.py index 3f0e71dd4..488abadaa 100644 --- a/src/moin/scripts/account/disable.py +++ b/src/moin/scripts/account/disable.py @@ -16,16 +16,16 @@ class Disable_User(Command): description = 'This command allows you to disable user accounts.' option_list = ( - Option('--name', '-n', required=False, dest='name', type=unicode, + Option('--name', '-n', required=False, dest='name', type=str, help='Disable the user with user name NAME.'), - Option('--uid', '-u', required=False, dest='uid', type=unicode, + Option('--uid', '-u', required=False, dest='uid', type=str, help='Disable the user with user id UID.'), ) def run(self, name, uid): flags_given = name or uid if not flags_given: - print 'incorrect number of arguments' + print('incorrect number of arguments') import sys sys.exit() @@ -36,10 +36,10 @@ def run(self, name, uid): u = user.User(auth_username=name) if not u.exists(): - print 'This user "{0!r}" does not exists!'.format(u.name) + print('This user "{0!r}" does not exists!'.format(u.name)) return - print " {0:<20} {1!r:<25} {2:<35}".format(u.itemid, u.name, u.email), + print(" {0:<20} {1!r:<25} {2:<35}".format(u.itemid, u.name, u.email), end=' ') if not u.disabled: # only disable once u.disabled = 1 u.name = u"{0}-{1}".format(u.name, u.id) @@ -47,6 +47,6 @@ def run(self, name, uid): u.email = u"{0}-{1}".format(u.email, u.id) u.subscriptions = [] u.save() - print "- disabled." + print("- disabled.") else: - print "- is already disabled." + print("- is already disabled.") diff --git a/src/moin/scripts/account/resetpw.py b/src/moin/scripts/account/resetpw.py index 688e9cbce..005a14abb 100644 --- a/src/moin/scripts/account/resetpw.py +++ b/src/moin/scripts/account/resetpw.py @@ -56,11 +56,11 @@ def set_password(uid, password, notify=False, skip_invalid=False, subject=None, class Set_Password(Command): description = 'This command allows you to set a user password.' option_list = ( - Option('--name', '-n', required=False, dest='name', type=unicode, + Option('--name', '-n', required=False, dest='name', type=str, help='Set password for the user with user name NAME.'), - Option('--uid', '-u', required=False, dest='uid', type=unicode, + Option('--uid', '-u', required=False, dest='uid', type=str, help='Set password for the user with user id UID.'), - Option('--password', '-p', required=False, dest='password', type=unicode, + Option('--password', '-p', required=False, dest='password', type=str, help='New password for this account.'), Option('--all-users', '-a', required=False, dest='all_users', action='store_true', default=False, help='Reset password for ALL users.'), @@ -68,11 +68,11 @@ class Set_Password(Command): help='Notify user(s), send them an E-Mail with a password reset link.'), Option('--verbose', '-v', required=False, dest='verbose', action='store_true', default=False, help='Verbose operation'), - Option('--subject', required=False, dest='subject', type=unicode, + Option('--subject', required=False, dest='subject', type=str, help='Subject text for the password reset notification E-Mail.'), - Option('--text', required=False, dest='text', type=unicode, + Option('--text', required=False, dest='text', type=str, help='Template text for the password reset notification E-Mail. Default: use the builtin standard template'), - Option('--text-from-file', required=False, dest='text_file', type=unicode, + Option('--text-from-file', required=False, dest='text_file', type=str, help='Read full template for the password reset notification E-Mail from the given file, overrides --text. Default: None'), Option('--skip-invalid', required=False, dest='skip_invalid', action='store_true', help='If a user\'s password hash is already invalid (pw is already reset), skip this user.'), @@ -81,11 +81,11 @@ class Set_Password(Command): def run(self, name, uid, password, all_users, notify, verbose, subject, text, text_file, skip_invalid): flags_given = name or uid or all_users if not flags_given: - print 'incorrect number of arguments' + print('incorrect number of arguments') sys.exit(1) if notify and not app.cfg.mail_enabled: - print "This wiki is not enabled for mail processing. The --notify option requires this. Aborting..." + print("This wiki is not enabled for mail processing. The --notify option requires this. Aborting...") sys.exit(1) if text_file: @@ -120,4 +120,4 @@ def run(self, name, uid, password, all_users, notify, verbose, subject, text, te else: status = "SUCCESS" if verbose: - print "uid %s, name %s, email %s (%05d / %05d) %s" % (uid, name, email, nr, total, status) + print("uid %s, name %s, email %s (%05d / %05d) %s" % (uid, name, email, nr, total, status)) diff --git a/src/moin/scripts/maint/dump_html.py b/src/moin/scripts/maint/dump_html.py index bc1dbc17d..5d8769b4f 100644 --- a/src/moin/scripts/maint/dump_html.py +++ b/src/moin/scripts/maint/dump_html.py @@ -67,7 +67,7 @@ class Dump(Command): description = 'Create a static HTML image of this wiki.' option_list = [ - Option('--directory', '-d', dest='directory', type=unicode, required=False, default='HTML', + Option('--directory', '-d', dest='directory', type=str, required=False, default='HTML', help='Directory name containing the output files, default HTML'), Option('--theme', '-t', dest='theme', required=False, default='topside_cms', help='Name of theme used in creating output pages, default topside_cms'), @@ -100,7 +100,7 @@ def run(self, directory='HTML', theme='topside_cms', exclude_ns='userprofiles', acls['before'] = 'All:read' # create an empty output directory after deleting any existing directory - print u'Creating output directory {0}, starting to copy supporting files'.format(html_root) + print('Creating output directory {0}, starting to copy supporting files'.format(html_root)) if os.path.exists(html_root): shutil.rmtree(html_root, ignore_errors=False) else: @@ -132,7 +132,7 @@ def run(self, directory='HTML', theme='topside_cms', exclude_ns='userprofiles', # convert: svg # to: svg invalid_src = re.compile(r' src="/\+get/\+[0-9a-f]{32}/') - valid_src = u' src="+get/' + valid_src = ' src="+get/' # get ready to render and copy individual items names = [] @@ -145,7 +145,7 @@ def run(self, directory='HTML', theme='topside_cms', exclude_ns='userprofiles', else: q = Every() - print 'Starting to dump items' + print('Starting to dump items') for current_rev in app.storage.search(q, limit=None, sortedby="name"): if current_rev.namespace in exclude_ns: # we usually do not copy userprofiles, no one can login to a static wiki @@ -162,27 +162,27 @@ def run(self, directory='HTML', theme='topside_cms', exclude_ns='userprofiles', filename = norm(join(html_root, file_name)) names.append(file_name) except Forbidden: - print u'Failed to dump {0}: Forbidden'.format(current_rev.name) + print('Failed to dump {0}: Forbidden'.format(current_rev.name)) continue except KeyError: - print u'Failed to dump {0}: KeyError'.format(current_rev.name) + print('Failed to dump {0}: KeyError'.format(current_rev.name)) continue - if not isinstance(rendered, unicode): - print u'Rendering failed for {0} with response {1}'.format(file_name, rendered) + if not isinstance(rendered, str): + print('Rendering failed for {0} with response {1}'.format(file_name, rendered)) continue # make hrefs relative to current folder rendered = rendered.replace('href="/', 'href="') rendered = rendered.replace('src="/static/', 'src="static/') rendered = rendered.replace('src="/+serve/', 'src="+serve/') rendered = rendered.replace('href="+index/"', 'href="+index"') # trailing slash changes relative position - rendered = rendered.replace('', u''.format(app.cfg.default_root)) # TODO: fix basic theme + rendered = rendered.replace('', ''.format(app.cfg.default_root)) # TODO: fix basic theme # remove item ID from: src="/+get/+7cb364b8ca5d4b7e960a4927c99a2912/svg" rendered = re.sub(invalid_src, valid_src, rendered) rendered = self.subitems(rendered) # copy raw data for all items to output /+get directory; images are required, text items are of marginal/no benefit - item = app.storage[current_rev.name] + item = app.storage[current_rev.fqname.fullname] rev = item[CURRENT] with open(get_dir + '/' + file_name, 'wb') as f: shutil.copyfileobj(rev.data, f) @@ -194,11 +194,11 @@ def run(self, directory='HTML', theme='topside_cms', exclude_ns='userprofiles', with open(filename, 'wb') as f: rev.data.seek(0) shutil.copyfileobj(rev.data, f) - print u'Saved file named {0} as raw data'.format(filename).encode('utf-8') + print('Saved file named {0} as raw data'.format(filename)) else: with open(filename, 'wb') as f: f.write(rendered.encode('utf8')) - print u'Saved file named {0}'.format(filename).encode('utf-8') + print('Saved file named {0}'.format(filename)) if current_rev.name == app.cfg.default_root: # make duplicates of home page that are easy to find in directory list and open with a click @@ -220,25 +220,25 @@ def run(self, directory='HTML', theme='topside_cms', exclude_ns='userprofiles', end = '