← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~cjwatson/launchpad:pyupgrade-py3-answers into launchpad:master

 

Colin Watson has proposed merging ~cjwatson/launchpad:pyupgrade-py3-answers into launchpad:master.

Commit message:
lp.answers: Apply "pyupgrade --py3-plus"

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/412173

This is mostly a proof of concept in applying changes like this in a way that doesn't require changing the whole codebase at once, but it seems workable enough.
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:pyupgrade-py3-answers into launchpad:master.
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 4042766..34ba814 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -34,6 +34,11 @@ repos:
     -   id: pyupgrade
         args: [--keep-percent-format]
         exclude: ^lib/contrib/
+    -   id: pyupgrade
+        alias: pyupgrade-py3
+        name: pyupgrade (--py3-plus)
+        args: [--keep-percent-format, --py3-plus]
+        files: ^lib/lp/answers/
 -   repo: https://github.com/PyCQA/isort
     rev: 5.9.2
     hooks:
diff --git a/lib/lp/answers/browser/faqcollection.py b/lib/lp/answers/browser/faqcollection.py
index 0c2734f..a32ebca 100644
--- a/lib/lp/answers/browser/faqcollection.py
+++ b/lib/lp/answers/browser/faqcollection.py
@@ -85,8 +85,8 @@ class SearchFAQsView(LaunchpadFormView):
             displayname=self.context.displayname,
             search_text=self.search_text)
         if self.search_text:
-            return _(u'FAQs matching \u201c${search_text}\u201d for '
-                     u'$displayname', mapping=replacements)
+            return _('FAQs matching \u201c${search_text}\u201d for '
+                     '$displayname', mapping=replacements)
         else:
             return _('FAQs for $displayname', mapping=replacements)
 
@@ -99,8 +99,8 @@ class SearchFAQsView(LaunchpadFormView):
             displayname=self.context.displayname,
             search_text=self.search_text)
         if self.search_text:
-            return _(u'There are no FAQs for $displayname matching '
-                     u'\u201c${search_text}\u201d.', mapping=replacements)
+            return _('There are no FAQs for $displayname matching '
+                     '\u201c${search_text}\u201d.', mapping=replacements)
         else:
             return _('There are no FAQs for $displayname.',
                      mapping=replacements)
diff --git a/lib/lp/answers/browser/question.py b/lib/lp/answers/browser/question.py
index 82560a8..a72083e 100644
--- a/lib/lp/answers/browser/question.py
+++ b/lib/lp/answers/browser/question.py
@@ -1251,7 +1251,7 @@ class SearchAllQuestionsView(SearchQuestionsView):
         Saves the user submitted search parameters in an instance
         attribute and redirects to questions when the term is a question id.
         """
-        super(SearchAllQuestionsView, self).search_action.success(data)
+        super().search_action.success(data)
 
         if not self.search_text:
             return
@@ -1292,7 +1292,7 @@ class QuestionCreateFAQView(LinkFAQMixin, LaunchpadFormView):
 
         Adds a message field to the form.
         """
-        super(QuestionCreateFAQView, self).setUpFields()
+        super().setUpFields()
         self.form_fields += form.Fields(
             copy_field(IQuestionLinkFAQForm['message']))
         self.form_fields['message'].field.title = _(
@@ -1341,7 +1341,7 @@ class SearchableFAQRadioWidget(LaunchpadRadioWidget):
 
     def renderValue(self, value):
         """Render the widget with the value."""
-        content = super(SearchableFAQRadioWidget, self).renderValue(value)
+        content = super().renderValue(value)
         return "<br />".join([content, self.renderSearchWidget()])
 
     def renderItemsWithValues(self, values):
@@ -1413,7 +1413,7 @@ class SearchableFAQRadioWidget(LaunchpadRadioWidget):
             type='radio')
         if selected:
             attributes['checked'] = 'checked'
-        input = renderElement(u'input', **attributes)
+        input = renderElement('input', **attributes)
         button = structured(
             '<label style="font-weight: normal">%s&nbsp;%s:</label>',
             structured(input), term.token)
@@ -1472,7 +1472,7 @@ class QuestionLinkFAQView(LinkFAQMixin, LaunchpadFormView):
 
     def setUpWidgets(self):
         """Set the query on the search widget to the question title."""
-        super(QuestionLinkFAQView, self).setUpWidgets()
+        super().setUpWidgets()
         self.widgets['faq'].default_query = self.context.title
 
     def validate(self, data):
diff --git a/lib/lp/answers/browser/questiontarget.py b/lib/lp/answers/browser/questiontarget.py
index d039539..57d8caa 100644
--- a/lib/lp/answers/browser/questiontarget.py
+++ b/lib/lp/answers/browser/questiontarget.py
@@ -341,11 +341,11 @@ class SearchQuestionsView(UserSupportLanguagesMixin, LaunchpadFormView):
         if len(language_counts) == 0:
             return ''
         url = canonical_url(self.context, rootsite='answers')
-        format = (u'%s in <a href="' + url + u'/+by-language'
-                  u'?field.language=%s&field.status=Open">%s</a>')
+        format = ('%s in <a href="' + url + '/+by-language'
+                  '?field.language=%s&field.status=Open">%s</a>')
         links = [format % (language_counts[key], key.code, key.englishname)
                  for key in language_counts]
-        return u', '.join(links)
+        return ', '.join(links)
 
     @property
     def empty_listing_message(self):
diff --git a/lib/lp/answers/browser/tests/test_breadcrumbs.py b/lib/lp/answers/browser/tests/test_breadcrumbs.py
index 070cb67..cca8dd6 100644
--- a/lib/lp/answers/browser/tests/test_breadcrumbs.py
+++ b/lib/lp/answers/browser/tests/test_breadcrumbs.py
@@ -17,8 +17,7 @@ class TestQuestionTargetProjectAndPersonBreadcrumbOnAnswersFacet(
     """
 
     def setUp(self):
-        super(TestQuestionTargetProjectAndPersonBreadcrumbOnAnswersFacet,
-              self).setUp()
+        super().setUp()
         self.person = self.factory.makePerson()
         self.person_questions_url = canonical_url(
             self.person, rootsite='answers')
@@ -54,7 +53,7 @@ class TestAnswersBreadcrumb(BaseBreadcrumbTestCase):
     """Test Breadcrumbs for answer module objects."""
 
     def setUp(self):
-        super(TestAnswersBreadcrumb, self).setUp()
+        super().setUp()
         self.product = self.factory.makeProduct(name="mellon")
         login_person(self.product.owner)
 
diff --git a/lib/lp/answers/browser/tests/test_question.py b/lib/lp/answers/browser/tests/test_question.py
index 7d7897d..9178619 100644
--- a/lib/lp/answers/browser/tests/test_question.py
+++ b/lib/lp/answers/browser/tests/test_question.py
@@ -26,7 +26,7 @@ class TestQuestionAddView(TestCaseWithFactory):
     layer = DatabaseFunctionalLayer
 
     def setUp(self):
-        super(TestQuestionAddView, self).setUp()
+        super().setUp()
         self.question_target = self.factory.makeProduct()
         self.user = self.factory.makePerson()
         login_person(self.user)
diff --git a/lib/lp/answers/browser/tests/test_questiontarget.py b/lib/lp/answers/browser/tests/test_questiontarget.py
index c0aefe0..307d038 100644
--- a/lib/lp/answers/browser/tests/test_questiontarget.py
+++ b/lib/lp/answers/browser/tests/test_questiontarget.py
@@ -10,7 +10,6 @@ from lazr.restful.interfaces import (
     IJSONRequestCache,
     IWebServiceClientRequest,
     )
-import six
 from six.moves.urllib.parse import quote
 from zope.component import getUtility
 from zope.security.proxy import removeSecurityProxy
@@ -196,7 +195,7 @@ class TestSearchQuestionsViewUnknown(TestCaseWithFactory):
             hoary, sourcepackagename, product.owner)
 
     def setUp(self):
-        super(TestSearchQuestionsViewUnknown, self).setUp()
+        super().setUp()
         self.product = self.factory.makeProduct()
         self.view = create_initialized_view(self.product, '+questions')
 
@@ -246,7 +245,7 @@ class QuestionSetViewTestCase(TestCaseWithFactory):
         target_widget = view.widgets['scope'].target_widget
         self.assertIsNot(
             None, content.find(True, id=target_widget.show_widget_id))
-        text = six.text_type(content)
+        text = str(content)
         picker_vocab = "DistributionOrProductOrProjectGroup"
         self.assertIn(picker_vocab, text)
         focus_script = "setFocusByName('field.search_text')"
diff --git a/lib/lp/answers/interfaces/question.py b/lib/lp/answers/interfaces/question.py
index 41efbf7..b7a0680 100644
--- a/lib/lp/answers/interfaces/question.py
+++ b/lib/lp/answers/interfaces/question.py
@@ -69,7 +69,7 @@ class IQuestion(IHasOwner):
     description = exported(Text(
         title=_('Description'), required=True, description=_(
         "Include as much detail as possible: what "
-        u"you\N{right single quotation mark}re trying to achieve, what steps "
+        "you\N{right single quotation mark}re trying to achieve, what steps "
         "you take, what happens, and what you think should happen instead.")),
         as_of="devel")
     status = exported(Choice(
diff --git a/lib/lp/answers/model/faq.py b/lib/lp/answers/model/faq.py
index ac541ed..7c9945f 100644
--- a/lib/lp/answers/model/faq.py
+++ b/lib/lp/answers/model/faq.py
@@ -11,7 +11,6 @@ __all__ = [
 
 from lazr.lifecycle.event import ObjectCreatedEvent
 import pytz
-import six
 from storm.expr import (
     And,
     Desc,
@@ -139,8 +138,8 @@ class FAQ(StormBase):
         if date_created is None:
             date_created = DEFAULT
         faq = FAQ(
-            owner=owner, title=six.text_type(title),
-            content=six.text_type(content),
+            owner=owner, title=str(title),
+            content=str(content),
             keywords=keywords,
             date_created=date_created, product=product,
             distribution=distribution)
@@ -218,7 +217,7 @@ class FAQSearch:
         :param projectgroup: The project group in which to search for FAQs.
         """
         if search_text is not None:
-            assert isinstance(search_text, six.string_types), (
+            assert isinstance(search_text, str), (
                 'search_text should be a string, not %s' % type(search_text))
             self.search_text = search_text
 
diff --git a/lib/lp/answers/model/question.py b/lib/lp/answers/model/question.py
index 61a534c..5d92043 100644
--- a/lib/lp/answers/model/question.py
+++ b/lib/lp/answers/model/question.py
@@ -29,7 +29,6 @@ from lazr.lifecycle.event import (
     )
 from lazr.lifecycle.snapshot import Snapshot
 import pytz
-import six
 from storm.expr import (
     Alias,
     LeftJoin,
@@ -702,7 +701,7 @@ class Question(StormBase, BugLinkTargetMixin):
         from lp.bugs.model.bug import Bug
         bug_ids = [
             int(id) for _, id in getUtility(IXRefSet).findFrom(
-                (u'question', six.text_type(self.id)), types=[u'bug'])]
+                ('question', str(self.id)), types=['bug'])]
         return list(sorted(
             bulk.load(Bug, bug_ids), key=operator.attrgetter('id')))
 
@@ -713,14 +712,12 @@ class Question(StormBase, BugLinkTargetMixin):
             props = {}
         # XXX: Should set creator.
         getUtility(IXRefSet).create(
-            {(u'question', six.text_type(self.id)):
-                {(u'bug', six.text_type(bug.id)): props}})
+            {('question', str(self.id)): {('bug', str(bug.id)): props}})
 
     def deleteBugLink(self, bug):
         """See BugLinkTargetMixin."""
         getUtility(IXRefSet).delete(
-            {(u'question', six.text_type(self.id)):
-                [(u'bug', six.text_type(bug.id))]})
+            {('question', str(self.id)): [('bug', str(bug.id))]})
 
     def setCommentVisibility(self, user, comment_number, visible):
         """See `IQuestion`."""
@@ -749,9 +746,9 @@ class QuestionSet:
         origin = [
             Question,
             LeftJoin(XRef, And(
-                XRef.from_type == u'question',
+                XRef.from_type == 'question',
                 XRef.from_id_int == Question.id,
-                XRef.to_type == u'bug')),
+                XRef.to_type == 'bug')),
             LeftJoin(BugTask, And(
                 BugTask.bug == XRef.to_id_int,
                 BugTask._status != BugTaskStatus.INVALID)),
diff --git a/lib/lp/answers/model/questionjob.py b/lib/lp/answers/model/questionjob.py
index 7a11475..39b9f73 100644
--- a/lib/lp/answers/model/questionjob.py
+++ b/lib/lp/answers/model/questionjob.py
@@ -76,7 +76,7 @@ class QuestionJob(StormBase):
         :param metadata: The type-specific variables, as a JSON-compatible
             dict.
         """
-        super(QuestionJob, self).__init__()
+        super().__init__()
         self.job = Job()
         self.job_type = job_type
         self.question = question
diff --git a/lib/lp/answers/notification.py b/lib/lp/answers/notification.py
index 42f04cd..59970e2 100644
--- a/lib/lp/answers/notification.py
+++ b/lib/lp/answers/notification.py
@@ -257,7 +257,7 @@ class QuestionModifiedDefaultNotification(QuestionNotification):
 
     def getSubject(self):
         """The reply subject line."""
-        line = super(QuestionModifiedDefaultNotification, self).getSubject()
+        line = super().getSubject()
         return 'Re: %s' % line
 
     def getHeaders(self):
diff --git a/lib/lp/answers/publisher.py b/lib/lp/answers/publisher.py
index 266d225..4805f4c 100644
--- a/lib/lp/answers/publisher.py
+++ b/lib/lp/answers/publisher.py
@@ -43,8 +43,7 @@ class AnswersBrowserRequest(LaunchpadBrowserRequest):
     """Instances of AnswersBrowserRequest provide `AnswersLayer`."""
 
     def __init__(self, body_instream, environ, response=None):
-        super(AnswersBrowserRequest, self).__init__(
-            body_instream, environ, response)
+        super().__init__(body_instream, environ, response)
         # Many of the responses from Answers vary based on language.
         self.response.setHeader(
             'Vary', 'Cookie, Authorization, Accept-Language')
diff --git a/lib/lp/answers/testing.py b/lib/lp/answers/testing.py
index 5ea8002..0bdc487 100644
--- a/lib/lp/answers/testing.py
+++ b/lib/lp/answers/testing.py
@@ -7,7 +7,6 @@ __all__ = [
     'QuestionFactory',
     ]
 
-import six
 from zope.component import getUtility
 
 from lp.answers.interfaces.questiontarget import IQuestionTarget
@@ -25,7 +24,7 @@ class QuestionFactory:
         It returns the pillar with the target_name and makes sure it
         provides `IQuestionTarget`.
         """
-        assert isinstance(target_name, six.string_types), (
+        assert isinstance(target_name, str), (
             "expected a project name: %r", target_name)
         target = getUtility(IPillarNameSet).getByName(target_name)
         assert target is not None, (
diff --git a/lib/lp/answers/tests/test_faq.py b/lib/lp/answers/tests/test_faq.py
index 84e34e8..07ba82b 100644
--- a/lib/lp/answers/tests/test_faq.py
+++ b/lib/lp/answers/tests/test_faq.py
@@ -27,7 +27,7 @@ class TestFAQPermissions(TestCaseWithFactory):
     layer = DatabaseFunctionalLayer
 
     def setUp(self):
-        super(TestFAQPermissions, self).setUp()
+        super().setUp()
         target = self.factory.makeProduct()
         self.owner = target.owner
         with person_logged_in(self.owner):
diff --git a/lib/lp/answers/tests/test_faqtarget.py b/lib/lp/answers/tests/test_faqtarget.py
index d008ef9..82fafc1 100644
--- a/lib/lp/answers/tests/test_faqtarget.py
+++ b/lib/lp/answers/tests/test_faqtarget.py
@@ -73,7 +73,7 @@ class TestDistributionPermissions(BaseIFAQTargetTests, TestCaseWithFactory):
     """Test who can add FAQs to a distribution."""
 
     def setUp(self):
-        super(TestDistributionPermissions, self).setUp()
+        super().setUp()
         self.target = self.factory.makeDistribution()
         self.owner = self.target.owner
 
@@ -82,7 +82,7 @@ class TestProductPermissions(BaseIFAQTargetTests, TestCaseWithFactory):
     """Test who can add FAQs to a product."""
 
     def setUp(self):
-        super(TestProductPermissions, self).setUp()
+        super().setUp()
         self.target = self.factory.makeProduct()
         self.owner = self.target.owner
 
@@ -91,7 +91,7 @@ class TestDSPPermissions(BaseIFAQTargetTests, TestCaseWithFactory):
     """Test who can add FAQs for a distribution source package."""
 
     def setUp(self):
-        super(TestDSPPermissions, self).setUp()
+        super().setUp()
         distribution = self.factory.makeDistribution()
         self.owner = distribution.owner
         self.target = self.factory.makeDistributionSourcePackage(
diff --git a/lib/lp/answers/tests/test_question_webservice.py b/lib/lp/answers/tests/test_question_webservice.py
index 53a05f3..30088ad 100644
--- a/lib/lp/answers/tests/test_question_webservice.py
+++ b/lib/lp/answers/tests/test_question_webservice.py
@@ -86,7 +86,7 @@ class TestQuestionRepresentation(TestCaseWithFactory):
     layer = DatabaseFunctionalLayer
 
     def setUp(self):
-        super(TestQuestionRepresentation, self).setUp()
+        super().setUp()
         with celebrity_logged_in('admin'):
             self.question = self.factory.makeQuestion(
                 title="This is a question")
@@ -178,7 +178,7 @@ class TestSetCommentVisibility(TestCaseWithFactory):
     layer = DatabaseFunctionalLayer
 
     def setUp(self):
-        super(TestSetCommentVisibility, self).setUp()
+        super().setUp()
         self.commenter = self.factory.makePerson()
         with person_logged_in(self.commenter):
             self.question = self.factory.makeQuestion()
diff --git a/lib/lp/answers/tests/test_question_workflow.py b/lib/lp/answers/tests/test_question_workflow.py
index 1ec3999..cafe661 100644
--- a/lib/lp/answers/tests/test_question_workflow.py
+++ b/lib/lp/answers/tests/test_question_workflow.py
@@ -65,7 +65,7 @@ class BaseAnswerTrackerWorkflowTestCase(TestCase):
     layer = DatabaseFunctionalLayer
 
     def setUp(self):
-        super(BaseAnswerTrackerWorkflowTestCase, self).setUp()
+        super().setUp()
 
         self.now = datetime.now(UTC)
 
@@ -514,7 +514,7 @@ class LinkFAQTestCase(BaseAnswerTrackerWorkflowTestCase):
 
     def setUp(self):
         """Create an additional FAQ."""
-        super(LinkFAQTestCase, self).setUp()
+        super().setUp()
 
         # Only admin can create FAQ on ubuntu.
         login_person(self.admin)
diff --git a/lib/lp/answers/tests/test_questiontarget.py b/lib/lp/answers/tests/test_questiontarget.py
index e825d34..dbf5582 100644
--- a/lib/lp/answers/tests/test_questiontarget.py
+++ b/lib/lp/answers/tests/test_questiontarget.py
@@ -25,7 +25,7 @@ class QuestionTargetAnswerContactTestCase(TestCaseWithFactory):
     layer = DatabaseFunctionalLayer
 
     def setUp(self):
-        super(QuestionTargetAnswerContactTestCase, self).setUp()
+        super().setUp()
         self.project = self.factory.makeProduct()
         self.user = self.factory.makePerson()
 
@@ -79,7 +79,7 @@ class TestQuestionTarget_answer_contacts_with_languages(TestCaseWithFactory):
     layer = DatabaseFunctionalLayer
 
     def setUp(self):
-        super(TestQuestionTarget_answer_contacts_with_languages, self).setUp()
+        super().setUp()
         self.answer_contact = self.factory.makePerson()
         login_person(self.answer_contact)
         lang_set = getUtility(ILanguageSet)
@@ -137,7 +137,7 @@ class TestQuestionTargetCreateQuestionFromBug(TestCaseWithFactory):
     layer = DatabaseFunctionalLayer
 
     def setUp(self):
-        super(TestQuestionTargetCreateQuestionFromBug, self).setUp()
+        super().setUp()
         self.bug = self.factory.makeBug(description="first comment")
         self.target = self.bug.bugtasks[0].target
         self.contributor = self.target.owner
diff --git a/lib/lp/answers/vocabulary.py b/lib/lp/answers/vocabulary.py
index ec3ab2d..20cea4c 100644
--- a/lib/lp/answers/vocabulary.py
+++ b/lib/lp/answers/vocabulary.py
@@ -93,8 +93,7 @@ class UsesAnswersProductVocabulary(ProductVocabulary):
             vocab_filter = []
         vocab_filter.append(
             And(Product.official_answers == True))
-        return super(UsesAnswersProductVocabulary, self).search(
-            query, vocab_filter)
+        return super().search(query, vocab_filter)
 
 
 class UsesAnswersDistributionVocabulary(DistributionVocabulary):
@@ -107,8 +106,7 @@ class UsesAnswersDistributionVocabulary(DistributionVocabulary):
     """
 
     def __init__(self, context=None):
-        super(UsesAnswersDistributionVocabulary, self).__init__(
-            context=context)
+        super().__init__(context=context)
         self.distribution = IDistribution(self.context, None)
 
     @property