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

Proposed by Colin Watson
Status: Merged
Approved by: Colin Watson
Approved revision: 5d46ca0970916c2b161761aee29f46419dd3efea
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~cjwatson/launchpad:pyupgrade-py3-answers
Merge into: launchpad:master
Diff against target: 475 lines (+48/-53)
20 files modified
.pre-commit-config.yaml (+5/-0)
lib/lp/answers/browser/faqcollection.py (+4/-4)
lib/lp/answers/browser/question.py (+5/-5)
lib/lp/answers/browser/questiontarget.py (+3/-3)
lib/lp/answers/browser/tests/test_breadcrumbs.py (+2/-3)
lib/lp/answers/browser/tests/test_question.py (+1/-1)
lib/lp/answers/browser/tests/test_questiontarget.py (+2/-3)
lib/lp/answers/interfaces/question.py (+1/-1)
lib/lp/answers/model/faq.py (+3/-4)
lib/lp/answers/model/question.py (+5/-8)
lib/lp/answers/model/questionjob.py (+1/-1)
lib/lp/answers/notification.py (+1/-1)
lib/lp/answers/publisher.py (+1/-2)
lib/lp/answers/testing.py (+1/-2)
lib/lp/answers/tests/test_faq.py (+1/-1)
lib/lp/answers/tests/test_faqtarget.py (+3/-3)
lib/lp/answers/tests/test_question_webservice.py (+2/-2)
lib/lp/answers/tests/test_question_workflow.py (+2/-2)
lib/lp/answers/tests/test_questiontarget.py (+3/-3)
lib/lp/answers/vocabulary.py (+2/-4)
Reviewer Review Type Date Requested Status
Jürgen Gmach Approve
Review via email: mp+412173@code.launchpad.net

Commit message

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

Description of the change

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.

To post a comment you must log in.
Revision history for this message
Jürgen Gmach (jugmac00) wrote :

LGTM

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
2index 4042766..34ba814 100644
3--- a/.pre-commit-config.yaml
4+++ b/.pre-commit-config.yaml
5@@ -34,6 +34,11 @@ repos:
6 - id: pyupgrade
7 args: [--keep-percent-format]
8 exclude: ^lib/contrib/
9+ - id: pyupgrade
10+ alias: pyupgrade-py3
11+ name: pyupgrade (--py3-plus)
12+ args: [--keep-percent-format, --py3-plus]
13+ files: ^lib/lp/answers/
14 - repo: https://github.com/PyCQA/isort
15 rev: 5.9.2
16 hooks:
17diff --git a/lib/lp/answers/browser/faqcollection.py b/lib/lp/answers/browser/faqcollection.py
18index 0c2734f..a32ebca 100644
19--- a/lib/lp/answers/browser/faqcollection.py
20+++ b/lib/lp/answers/browser/faqcollection.py
21@@ -85,8 +85,8 @@ class SearchFAQsView(LaunchpadFormView):
22 displayname=self.context.displayname,
23 search_text=self.search_text)
24 if self.search_text:
25- return _(u'FAQs matching \u201c${search_text}\u201d for '
26- u'$displayname', mapping=replacements)
27+ return _('FAQs matching \u201c${search_text}\u201d for '
28+ '$displayname', mapping=replacements)
29 else:
30 return _('FAQs for $displayname', mapping=replacements)
31
32@@ -99,8 +99,8 @@ class SearchFAQsView(LaunchpadFormView):
33 displayname=self.context.displayname,
34 search_text=self.search_text)
35 if self.search_text:
36- return _(u'There are no FAQs for $displayname matching '
37- u'\u201c${search_text}\u201d.', mapping=replacements)
38+ return _('There are no FAQs for $displayname matching '
39+ '\u201c${search_text}\u201d.', mapping=replacements)
40 else:
41 return _('There are no FAQs for $displayname.',
42 mapping=replacements)
43diff --git a/lib/lp/answers/browser/question.py b/lib/lp/answers/browser/question.py
44index 82560a8..a72083e 100644
45--- a/lib/lp/answers/browser/question.py
46+++ b/lib/lp/answers/browser/question.py
47@@ -1251,7 +1251,7 @@ class SearchAllQuestionsView(SearchQuestionsView):
48 Saves the user submitted search parameters in an instance
49 attribute and redirects to questions when the term is a question id.
50 """
51- super(SearchAllQuestionsView, self).search_action.success(data)
52+ super().search_action.success(data)
53
54 if not self.search_text:
55 return
56@@ -1292,7 +1292,7 @@ class QuestionCreateFAQView(LinkFAQMixin, LaunchpadFormView):
57
58 Adds a message field to the form.
59 """
60- super(QuestionCreateFAQView, self).setUpFields()
61+ super().setUpFields()
62 self.form_fields += form.Fields(
63 copy_field(IQuestionLinkFAQForm['message']))
64 self.form_fields['message'].field.title = _(
65@@ -1341,7 +1341,7 @@ class SearchableFAQRadioWidget(LaunchpadRadioWidget):
66
67 def renderValue(self, value):
68 """Render the widget with the value."""
69- content = super(SearchableFAQRadioWidget, self).renderValue(value)
70+ content = super().renderValue(value)
71 return "<br />".join([content, self.renderSearchWidget()])
72
73 def renderItemsWithValues(self, values):
74@@ -1413,7 +1413,7 @@ class SearchableFAQRadioWidget(LaunchpadRadioWidget):
75 type='radio')
76 if selected:
77 attributes['checked'] = 'checked'
78- input = renderElement(u'input', **attributes)
79+ input = renderElement('input', **attributes)
80 button = structured(
81 '<label style="font-weight: normal">%s&nbsp;%s:</label>',
82 structured(input), term.token)
83@@ -1472,7 +1472,7 @@ class QuestionLinkFAQView(LinkFAQMixin, LaunchpadFormView):
84
85 def setUpWidgets(self):
86 """Set the query on the search widget to the question title."""
87- super(QuestionLinkFAQView, self).setUpWidgets()
88+ super().setUpWidgets()
89 self.widgets['faq'].default_query = self.context.title
90
91 def validate(self, data):
92diff --git a/lib/lp/answers/browser/questiontarget.py b/lib/lp/answers/browser/questiontarget.py
93index d039539..57d8caa 100644
94--- a/lib/lp/answers/browser/questiontarget.py
95+++ b/lib/lp/answers/browser/questiontarget.py
96@@ -341,11 +341,11 @@ class SearchQuestionsView(UserSupportLanguagesMixin, LaunchpadFormView):
97 if len(language_counts) == 0:
98 return ''
99 url = canonical_url(self.context, rootsite='answers')
100- format = (u'%s in <a href="' + url + u'/+by-language'
101- u'?field.language=%s&field.status=Open">%s</a>')
102+ format = ('%s in <a href="' + url + '/+by-language'
103+ '?field.language=%s&field.status=Open">%s</a>')
104 links = [format % (language_counts[key], key.code, key.englishname)
105 for key in language_counts]
106- return u', '.join(links)
107+ return ', '.join(links)
108
109 @property
110 def empty_listing_message(self):
111diff --git a/lib/lp/answers/browser/tests/test_breadcrumbs.py b/lib/lp/answers/browser/tests/test_breadcrumbs.py
112index 070cb67..cca8dd6 100644
113--- a/lib/lp/answers/browser/tests/test_breadcrumbs.py
114+++ b/lib/lp/answers/browser/tests/test_breadcrumbs.py
115@@ -17,8 +17,7 @@ class TestQuestionTargetProjectAndPersonBreadcrumbOnAnswersFacet(
116 """
117
118 def setUp(self):
119- super(TestQuestionTargetProjectAndPersonBreadcrumbOnAnswersFacet,
120- self).setUp()
121+ super().setUp()
122 self.person = self.factory.makePerson()
123 self.person_questions_url = canonical_url(
124 self.person, rootsite='answers')
125@@ -54,7 +53,7 @@ class TestAnswersBreadcrumb(BaseBreadcrumbTestCase):
126 """Test Breadcrumbs for answer module objects."""
127
128 def setUp(self):
129- super(TestAnswersBreadcrumb, self).setUp()
130+ super().setUp()
131 self.product = self.factory.makeProduct(name="mellon")
132 login_person(self.product.owner)
133
134diff --git a/lib/lp/answers/browser/tests/test_question.py b/lib/lp/answers/browser/tests/test_question.py
135index 7d7897d..9178619 100644
136--- a/lib/lp/answers/browser/tests/test_question.py
137+++ b/lib/lp/answers/browser/tests/test_question.py
138@@ -26,7 +26,7 @@ class TestQuestionAddView(TestCaseWithFactory):
139 layer = DatabaseFunctionalLayer
140
141 def setUp(self):
142- super(TestQuestionAddView, self).setUp()
143+ super().setUp()
144 self.question_target = self.factory.makeProduct()
145 self.user = self.factory.makePerson()
146 login_person(self.user)
147diff --git a/lib/lp/answers/browser/tests/test_questiontarget.py b/lib/lp/answers/browser/tests/test_questiontarget.py
148index c0aefe0..307d038 100644
149--- a/lib/lp/answers/browser/tests/test_questiontarget.py
150+++ b/lib/lp/answers/browser/tests/test_questiontarget.py
151@@ -10,7 +10,6 @@ from lazr.restful.interfaces import (
152 IJSONRequestCache,
153 IWebServiceClientRequest,
154 )
155-import six
156 from six.moves.urllib.parse import quote
157 from zope.component import getUtility
158 from zope.security.proxy import removeSecurityProxy
159@@ -196,7 +195,7 @@ class TestSearchQuestionsViewUnknown(TestCaseWithFactory):
160 hoary, sourcepackagename, product.owner)
161
162 def setUp(self):
163- super(TestSearchQuestionsViewUnknown, self).setUp()
164+ super().setUp()
165 self.product = self.factory.makeProduct()
166 self.view = create_initialized_view(self.product, '+questions')
167
168@@ -246,7 +245,7 @@ class QuestionSetViewTestCase(TestCaseWithFactory):
169 target_widget = view.widgets['scope'].target_widget
170 self.assertIsNot(
171 None, content.find(True, id=target_widget.show_widget_id))
172- text = six.text_type(content)
173+ text = str(content)
174 picker_vocab = "DistributionOrProductOrProjectGroup"
175 self.assertIn(picker_vocab, text)
176 focus_script = "setFocusByName('field.search_text')"
177diff --git a/lib/lp/answers/interfaces/question.py b/lib/lp/answers/interfaces/question.py
178index 41efbf7..b7a0680 100644
179--- a/lib/lp/answers/interfaces/question.py
180+++ b/lib/lp/answers/interfaces/question.py
181@@ -69,7 +69,7 @@ class IQuestion(IHasOwner):
182 description = exported(Text(
183 title=_('Description'), required=True, description=_(
184 "Include as much detail as possible: what "
185- u"you\N{right single quotation mark}re trying to achieve, what steps "
186+ "you\N{right single quotation mark}re trying to achieve, what steps "
187 "you take, what happens, and what you think should happen instead.")),
188 as_of="devel")
189 status = exported(Choice(
190diff --git a/lib/lp/answers/model/faq.py b/lib/lp/answers/model/faq.py
191index ac541ed..7c9945f 100644
192--- a/lib/lp/answers/model/faq.py
193+++ b/lib/lp/answers/model/faq.py
194@@ -11,7 +11,6 @@ __all__ = [
195
196 from lazr.lifecycle.event import ObjectCreatedEvent
197 import pytz
198-import six
199 from storm.expr import (
200 And,
201 Desc,
202@@ -139,8 +138,8 @@ class FAQ(StormBase):
203 if date_created is None:
204 date_created = DEFAULT
205 faq = FAQ(
206- owner=owner, title=six.text_type(title),
207- content=six.text_type(content),
208+ owner=owner, title=str(title),
209+ content=str(content),
210 keywords=keywords,
211 date_created=date_created, product=product,
212 distribution=distribution)
213@@ -218,7 +217,7 @@ class FAQSearch:
214 :param projectgroup: The project group in which to search for FAQs.
215 """
216 if search_text is not None:
217- assert isinstance(search_text, six.string_types), (
218+ assert isinstance(search_text, str), (
219 'search_text should be a string, not %s' % type(search_text))
220 self.search_text = search_text
221
222diff --git a/lib/lp/answers/model/question.py b/lib/lp/answers/model/question.py
223index 61a534c..5d92043 100644
224--- a/lib/lp/answers/model/question.py
225+++ b/lib/lp/answers/model/question.py
226@@ -29,7 +29,6 @@ from lazr.lifecycle.event import (
227 )
228 from lazr.lifecycle.snapshot import Snapshot
229 import pytz
230-import six
231 from storm.expr import (
232 Alias,
233 LeftJoin,
234@@ -702,7 +701,7 @@ class Question(StormBase, BugLinkTargetMixin):
235 from lp.bugs.model.bug import Bug
236 bug_ids = [
237 int(id) for _, id in getUtility(IXRefSet).findFrom(
238- (u'question', six.text_type(self.id)), types=[u'bug'])]
239+ ('question', str(self.id)), types=['bug'])]
240 return list(sorted(
241 bulk.load(Bug, bug_ids), key=operator.attrgetter('id')))
242
243@@ -713,14 +712,12 @@ class Question(StormBase, BugLinkTargetMixin):
244 props = {}
245 # XXX: Should set creator.
246 getUtility(IXRefSet).create(
247- {(u'question', six.text_type(self.id)):
248- {(u'bug', six.text_type(bug.id)): props}})
249+ {('question', str(self.id)): {('bug', str(bug.id)): props}})
250
251 def deleteBugLink(self, bug):
252 """See BugLinkTargetMixin."""
253 getUtility(IXRefSet).delete(
254- {(u'question', six.text_type(self.id)):
255- [(u'bug', six.text_type(bug.id))]})
256+ {('question', str(self.id)): [('bug', str(bug.id))]})
257
258 def setCommentVisibility(self, user, comment_number, visible):
259 """See `IQuestion`."""
260@@ -749,9 +746,9 @@ class QuestionSet:
261 origin = [
262 Question,
263 LeftJoin(XRef, And(
264- XRef.from_type == u'question',
265+ XRef.from_type == 'question',
266 XRef.from_id_int == Question.id,
267- XRef.to_type == u'bug')),
268+ XRef.to_type == 'bug')),
269 LeftJoin(BugTask, And(
270 BugTask.bug == XRef.to_id_int,
271 BugTask._status != BugTaskStatus.INVALID)),
272diff --git a/lib/lp/answers/model/questionjob.py b/lib/lp/answers/model/questionjob.py
273index 7a11475..39b9f73 100644
274--- a/lib/lp/answers/model/questionjob.py
275+++ b/lib/lp/answers/model/questionjob.py
276@@ -76,7 +76,7 @@ class QuestionJob(StormBase):
277 :param metadata: The type-specific variables, as a JSON-compatible
278 dict.
279 """
280- super(QuestionJob, self).__init__()
281+ super().__init__()
282 self.job = Job()
283 self.job_type = job_type
284 self.question = question
285diff --git a/lib/lp/answers/notification.py b/lib/lp/answers/notification.py
286index 42f04cd..59970e2 100644
287--- a/lib/lp/answers/notification.py
288+++ b/lib/lp/answers/notification.py
289@@ -257,7 +257,7 @@ class QuestionModifiedDefaultNotification(QuestionNotification):
290
291 def getSubject(self):
292 """The reply subject line."""
293- line = super(QuestionModifiedDefaultNotification, self).getSubject()
294+ line = super().getSubject()
295 return 'Re: %s' % line
296
297 def getHeaders(self):
298diff --git a/lib/lp/answers/publisher.py b/lib/lp/answers/publisher.py
299index 266d225..4805f4c 100644
300--- a/lib/lp/answers/publisher.py
301+++ b/lib/lp/answers/publisher.py
302@@ -43,8 +43,7 @@ class AnswersBrowserRequest(LaunchpadBrowserRequest):
303 """Instances of AnswersBrowserRequest provide `AnswersLayer`."""
304
305 def __init__(self, body_instream, environ, response=None):
306- super(AnswersBrowserRequest, self).__init__(
307- body_instream, environ, response)
308+ super().__init__(body_instream, environ, response)
309 # Many of the responses from Answers vary based on language.
310 self.response.setHeader(
311 'Vary', 'Cookie, Authorization, Accept-Language')
312diff --git a/lib/lp/answers/testing.py b/lib/lp/answers/testing.py
313index 5ea8002..0bdc487 100644
314--- a/lib/lp/answers/testing.py
315+++ b/lib/lp/answers/testing.py
316@@ -7,7 +7,6 @@ __all__ = [
317 'QuestionFactory',
318 ]
319
320-import six
321 from zope.component import getUtility
322
323 from lp.answers.interfaces.questiontarget import IQuestionTarget
324@@ -25,7 +24,7 @@ class QuestionFactory:
325 It returns the pillar with the target_name and makes sure it
326 provides `IQuestionTarget`.
327 """
328- assert isinstance(target_name, six.string_types), (
329+ assert isinstance(target_name, str), (
330 "expected a project name: %r", target_name)
331 target = getUtility(IPillarNameSet).getByName(target_name)
332 assert target is not None, (
333diff --git a/lib/lp/answers/tests/test_faq.py b/lib/lp/answers/tests/test_faq.py
334index 84e34e8..07ba82b 100644
335--- a/lib/lp/answers/tests/test_faq.py
336+++ b/lib/lp/answers/tests/test_faq.py
337@@ -27,7 +27,7 @@ class TestFAQPermissions(TestCaseWithFactory):
338 layer = DatabaseFunctionalLayer
339
340 def setUp(self):
341- super(TestFAQPermissions, self).setUp()
342+ super().setUp()
343 target = self.factory.makeProduct()
344 self.owner = target.owner
345 with person_logged_in(self.owner):
346diff --git a/lib/lp/answers/tests/test_faqtarget.py b/lib/lp/answers/tests/test_faqtarget.py
347index d008ef9..82fafc1 100644
348--- a/lib/lp/answers/tests/test_faqtarget.py
349+++ b/lib/lp/answers/tests/test_faqtarget.py
350@@ -73,7 +73,7 @@ class TestDistributionPermissions(BaseIFAQTargetTests, TestCaseWithFactory):
351 """Test who can add FAQs to a distribution."""
352
353 def setUp(self):
354- super(TestDistributionPermissions, self).setUp()
355+ super().setUp()
356 self.target = self.factory.makeDistribution()
357 self.owner = self.target.owner
358
359@@ -82,7 +82,7 @@ class TestProductPermissions(BaseIFAQTargetTests, TestCaseWithFactory):
360 """Test who can add FAQs to a product."""
361
362 def setUp(self):
363- super(TestProductPermissions, self).setUp()
364+ super().setUp()
365 self.target = self.factory.makeProduct()
366 self.owner = self.target.owner
367
368@@ -91,7 +91,7 @@ class TestDSPPermissions(BaseIFAQTargetTests, TestCaseWithFactory):
369 """Test who can add FAQs for a distribution source package."""
370
371 def setUp(self):
372- super(TestDSPPermissions, self).setUp()
373+ super().setUp()
374 distribution = self.factory.makeDistribution()
375 self.owner = distribution.owner
376 self.target = self.factory.makeDistributionSourcePackage(
377diff --git a/lib/lp/answers/tests/test_question_webservice.py b/lib/lp/answers/tests/test_question_webservice.py
378index 53a05f3..30088ad 100644
379--- a/lib/lp/answers/tests/test_question_webservice.py
380+++ b/lib/lp/answers/tests/test_question_webservice.py
381@@ -86,7 +86,7 @@ class TestQuestionRepresentation(TestCaseWithFactory):
382 layer = DatabaseFunctionalLayer
383
384 def setUp(self):
385- super(TestQuestionRepresentation, self).setUp()
386+ super().setUp()
387 with celebrity_logged_in('admin'):
388 self.question = self.factory.makeQuestion(
389 title="This is a question")
390@@ -178,7 +178,7 @@ class TestSetCommentVisibility(TestCaseWithFactory):
391 layer = DatabaseFunctionalLayer
392
393 def setUp(self):
394- super(TestSetCommentVisibility, self).setUp()
395+ super().setUp()
396 self.commenter = self.factory.makePerson()
397 with person_logged_in(self.commenter):
398 self.question = self.factory.makeQuestion()
399diff --git a/lib/lp/answers/tests/test_question_workflow.py b/lib/lp/answers/tests/test_question_workflow.py
400index 1ec3999..cafe661 100644
401--- a/lib/lp/answers/tests/test_question_workflow.py
402+++ b/lib/lp/answers/tests/test_question_workflow.py
403@@ -65,7 +65,7 @@ class BaseAnswerTrackerWorkflowTestCase(TestCase):
404 layer = DatabaseFunctionalLayer
405
406 def setUp(self):
407- super(BaseAnswerTrackerWorkflowTestCase, self).setUp()
408+ super().setUp()
409
410 self.now = datetime.now(UTC)
411
412@@ -514,7 +514,7 @@ class LinkFAQTestCase(BaseAnswerTrackerWorkflowTestCase):
413
414 def setUp(self):
415 """Create an additional FAQ."""
416- super(LinkFAQTestCase, self).setUp()
417+ super().setUp()
418
419 # Only admin can create FAQ on ubuntu.
420 login_person(self.admin)
421diff --git a/lib/lp/answers/tests/test_questiontarget.py b/lib/lp/answers/tests/test_questiontarget.py
422index e825d34..dbf5582 100644
423--- a/lib/lp/answers/tests/test_questiontarget.py
424+++ b/lib/lp/answers/tests/test_questiontarget.py
425@@ -25,7 +25,7 @@ class QuestionTargetAnswerContactTestCase(TestCaseWithFactory):
426 layer = DatabaseFunctionalLayer
427
428 def setUp(self):
429- super(QuestionTargetAnswerContactTestCase, self).setUp()
430+ super().setUp()
431 self.project = self.factory.makeProduct()
432 self.user = self.factory.makePerson()
433
434@@ -79,7 +79,7 @@ class TestQuestionTarget_answer_contacts_with_languages(TestCaseWithFactory):
435 layer = DatabaseFunctionalLayer
436
437 def setUp(self):
438- super(TestQuestionTarget_answer_contacts_with_languages, self).setUp()
439+ super().setUp()
440 self.answer_contact = self.factory.makePerson()
441 login_person(self.answer_contact)
442 lang_set = getUtility(ILanguageSet)
443@@ -137,7 +137,7 @@ class TestQuestionTargetCreateQuestionFromBug(TestCaseWithFactory):
444 layer = DatabaseFunctionalLayer
445
446 def setUp(self):
447- super(TestQuestionTargetCreateQuestionFromBug, self).setUp()
448+ super().setUp()
449 self.bug = self.factory.makeBug(description="first comment")
450 self.target = self.bug.bugtasks[0].target
451 self.contributor = self.target.owner
452diff --git a/lib/lp/answers/vocabulary.py b/lib/lp/answers/vocabulary.py
453index ec3ab2d..20cea4c 100644
454--- a/lib/lp/answers/vocabulary.py
455+++ b/lib/lp/answers/vocabulary.py
456@@ -93,8 +93,7 @@ class UsesAnswersProductVocabulary(ProductVocabulary):
457 vocab_filter = []
458 vocab_filter.append(
459 And(Product.official_answers == True))
460- return super(UsesAnswersProductVocabulary, self).search(
461- query, vocab_filter)
462+ return super().search(query, vocab_filter)
463
464
465 class UsesAnswersDistributionVocabulary(DistributionVocabulary):
466@@ -107,8 +106,7 @@ class UsesAnswersDistributionVocabulary(DistributionVocabulary):
467 """
468
469 def __init__(self, context=None):
470- super(UsesAnswersDistributionVocabulary, self).__init__(
471- context=context)
472+ super().__init__(context=context)
473 self.distribution = IDistribution(self.context, None)
474
475 @property

Subscribers

People subscribed via source and target branches

to status/vote changes: