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

Proposed by Colin Watson
Status: Merged
Approved by: Colin Watson
Approved revision: 26f156d81c2e13a700775ac2059086e3ead12572
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~cjwatson/launchpad:pyupgrade-py3-app
Merge into: launchpad:master
Diff against target: 1864 lines (+226/-247)
44 files modified
.git-blame-ignore-revs (+4/-0)
.pre-commit-config.yaml (+1/-1)
lib/lp/app/browser/folder.py (+2/-3)
lib/lp/app/browser/launchpad.py (+1/-1)
lib/lp/app/browser/launchpadform.py (+1/-2)
lib/lp/app/browser/lazrjs.py (+10/-11)
lib/lp/app/browser/multistep.py (+3/-3)
lib/lp/app/browser/root.py (+7/-8)
lib/lp/app/browser/tales.py (+33/-35)
lib/lp/app/browser/tests/test_base_layout.py (+2/-3)
lib/lp/app/browser/tests/test_formatters.py (+8/-8)
lib/lp/app/browser/tests/test_launchpad.py (+12/-14)
lib/lp/app/browser/tests/test_launchpadform.py (+14/-14)
lib/lp/app/browser/tests/test_launchpadroot.py (+1/-1)
lib/lp/app/browser/tests/test_mixed_visibility.py (+1/-1)
lib/lp/app/browser/tests/test_page_macro.py (+3/-3)
lib/lp/app/browser/tests/test_stringformatter.py (+16/-16)
lib/lp/app/browser/tests/test_vocabulary.py (+1/-1)
lib/lp/app/browser/tests/test_webservice.py (+2/-2)
lib/lp/app/browser/vocabulary.py (+7/-13)
lib/lp/app/model/launchpad.py (+1/-1)
lib/lp/app/security.py (+1/-1)
lib/lp/app/services.py (+1/-1)
lib/lp/app/tests/test_security.py (+1/-1)
lib/lp/app/tests/test_tales.py (+3/-3)
lib/lp/app/utilities/celebrities.py (+1/-1)
lib/lp/app/validators/attachment.py (+2/-2)
lib/lp/app/vocabularies.py (+1/-1)
lib/lp/app/webservice/tests/test_marshallers.py (+4/-4)
lib/lp/app/widgets/date.py (+3/-3)
lib/lp/app/widgets/exception.py (+1/-1)
lib/lp/app/widgets/itemswidgets.py (+24/-26)
lib/lp/app/widgets/launchpadtarget.py (+3/-3)
lib/lp/app/widgets/owner.py (+1/-1)
lib/lp/app/widgets/popup.py (+4/-6)
lib/lp/app/widgets/product.py (+9/-11)
lib/lp/app/widgets/project.py (+2/-2)
lib/lp/app/widgets/suggestion.py (+10/-10)
lib/lp/app/widgets/tests/test_datetime.py (+2/-2)
lib/lp/app/widgets/tests/test_itemswidgets.py (+2/-2)
lib/lp/app/widgets/tests/test_launchpadtarget.py (+4/-5)
lib/lp/app/widgets/tests/test_popup.py (+2/-2)
lib/lp/app/widgets/tests/test_suggestion.py (+4/-4)
lib/lp/app/widgets/textwidgets.py (+11/-14)
Reviewer Review Type Date Requested Status
Colin Watson (community) Approve
Review via email: mp+412212@code.launchpad.net

Commit message

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

To post a comment you must log in.
Revision history for this message
Colin Watson (cjwatson) wrote :

Self-approving (mechanical).

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
2index 2cb1da8..6115e5c 100644
3--- a/.git-blame-ignore-revs
4+++ b/.git-blame-ignore-revs
5@@ -1,2 +1,6 @@
6 # apply pyupgrade
7 67e3b53a4375288983a72a7beb9a5a67ba739527
8+# apply pyupgrade --py3-plus to lp.answers
9+5d46ca0970916c2b161761aee29f46419dd3efea
10+# apply pyupgrade --py3-plus to lp.app
11+7fbeaae55d7d282ccda47c76a27c879e04ee14e9
12diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
13index 34ba814..162ad2f 100644
14--- a/.pre-commit-config.yaml
15+++ b/.pre-commit-config.yaml
16@@ -38,7 +38,7 @@ repos:
17 alias: pyupgrade-py3
18 name: pyupgrade (--py3-plus)
19 args: [--keep-percent-format, --py3-plus]
20- files: ^lib/lp/answers/
21+ files: ^lib/lp/(answers|app)/
22 - repo: https://github.com/PyCQA/isort
23 rev: 5.9.2
24 hooks:
25diff --git a/lib/lp/app/browser/folder.py b/lib/lp/app/browser/folder.py
26index 8c8c9ac..d38a5be 100644
27--- a/lib/lp/app/browser/folder.py
28+++ b/lib/lp/app/browser/folder.py
29@@ -85,7 +85,7 @@ class ExportedFolder:
30 name = os.path.basename(filename)
31 try:
32 fileobj = File(filename, name)
33- except IOError as ioerror:
34+ except OSError as ioerror:
35 expected = (errno.ENOENT, errno.EISDIR, errno.ENOTDIR)
36 if ioerror.errno in expected:
37 # No such file or is a directory.
38@@ -147,5 +147,4 @@ class ExportedImageFolder(ExportedFolder):
39 if os.path.exists(root + image_ext):
40 filename = filename + image_ext
41 break
42- return super(
43- ExportedImageFolder, self).prepareDataForServing(filename)
44+ return super().prepareDataForServing(filename)
45diff --git a/lib/lp/app/browser/launchpad.py b/lib/lp/app/browser/launchpad.py
46index 6c5045f..110a34a 100644
47--- a/lib/lp/app/browser/launchpad.py
48+++ b/lib/lp/app/browser/launchpad.py
49@@ -1105,7 +1105,7 @@ class LaunchpadTourFolder(ExportedFolder):
50 """
51 if name == 'source':
52 raise NotFound(request, name)
53- return super(LaunchpadTourFolder, self).publishTraverse(request, name)
54+ return super().publishTraverse(request, name)
55
56 def browserDefault(self, request):
57 """Redirect to index.html if the directory itself is requested."""
58diff --git a/lib/lp/app/browser/launchpadform.py b/lib/lp/app/browser/launchpadform.py
59index 642f044..220b013 100644
60--- a/lib/lp/app/browser/launchpadform.py
61+++ b/lib/lp/app/browser/launchpadform.py
62@@ -17,7 +17,6 @@ __all__ = [
63 from lazr.lifecycle.event import ObjectModifiedEvent
64 from lazr.lifecycle.snapshot import Snapshot
65 import simplejson
66-import six
67 import transaction
68 from zope.event import notify
69 from zope.formlib import form
70@@ -212,7 +211,7 @@ class LaunchpadFormView(LaunchpadView):
71 self.form_fields, self.prefix, context, self.request,
72 data=self.initial_values, adapters=self.adapters,
73 ignore_request=False)
74- for field_name, help_link in six.iteritems(self.help_links):
75+ for field_name, help_link in self.help_links.items():
76 self.widgets[field_name].help_link = help_link
77
78 @property
79diff --git a/lib/lp/app/browser/lazrjs.py b/lib/lp/app/browser/lazrjs.py
80index 51c22da..1d4a85f 100644
81--- a/lib/lp/app/browser/lazrjs.py
82+++ b/lib/lp/app/browser/lazrjs.py
83@@ -22,7 +22,6 @@ from lazr.restful.utils import (
84 safe_hasattr,
85 )
86 import simplejson
87-import six
88 from zope.browserpage import ViewPageTemplateFile
89 from zope.component import getUtility
90 from zope.schema.interfaces import (
91@@ -119,7 +118,7 @@ class TextWidgetBase(WidgetBase):
92
93 def __init__(self, context, exported_field, title, content_box_id,
94 edit_view, edit_url, edit_title):
95- super(TextWidgetBase, self).__init__(
96+ super().__init__(
97 context, exported_field, content_box_id,
98 edit_view, edit_url, edit_title)
99 self.accept_empty = simplejson.dumps(self.optional_field)
100@@ -181,7 +180,7 @@ class TextLineEditorWidget(TextWidgetBase, DefinedTagMixin):
101 field value instead of the attribute's current value.
102 :param width: Initial widget width.
103 """
104- super(TextLineEditorWidget, self).__init__(
105+ super().__init__(
106 context, exported_field, title, content_box_id,
107 edit_view, edit_url, edit_title)
108 self.tag = tag
109@@ -242,7 +241,7 @@ class TextAreaEditorWidget(TextWidgetBase):
110 :param linkify_text: If True the HTML version of the text will have
111 things that look like links made into anchors.
112 """
113- super(TextAreaEditorWidget, self).__init__(
114+ super().__init__(
115 context, exported_field, title, content_box_id,
116 edit_view, edit_url, edit_title)
117 self.hide_empty = hide_empty
118@@ -294,7 +293,7 @@ class InlineEditPickerWidget(WidgetBase):
119 in and when JS is off. Defaults to the edit_view on the context.
120 :param edit_title: Used to set the title attribute of the anchor.
121 """
122- super(InlineEditPickerWidget, self).__init__(
123+ super().__init__(
124 context, exported_field, content_box_id,
125 edit_view, edit_url, edit_title)
126 self.default_html = default_html
127@@ -394,7 +393,7 @@ class InlinePersonEditPickerWidget(InlineEditPickerWidget):
128 :param edit_title: Used to set the title attribute of the anchor.
129 :param help_link: Used to set a link for help for the widget.
130 """
131- super(InlinePersonEditPickerWidget, self).__init__(
132+ super().__init__(
133 context, exported_field, default_html, content_box_id, header,
134 step_title, null_display_value,
135 edit_view, edit_url, edit_title, help_link)
136@@ -425,7 +424,7 @@ class InlinePersonEditPickerWidget(InlineEditPickerWidget):
137 return self._show_create_team
138
139 def getConfig(self):
140- config = super(InlinePersonEditPickerWidget, self).getConfig()
141+ config = super().getConfig()
142 config.update(dict(
143 show_remove_button=self.optional_field,
144 show_assign_me_button=self.show_assign_me_button,
145@@ -477,7 +476,7 @@ class InlineMultiCheckboxWidget(WidgetBase):
146 :param edit_title: Used to set the title attribute of the anchor.
147
148 """
149- super(InlineMultiCheckboxWidget, self).__init__(
150+ super().__init__(
151 context, exported_field, content_box_id,
152 edit_view, edit_url, edit_title)
153
154@@ -502,7 +501,7 @@ class InlineMultiCheckboxWidget(WidgetBase):
155 else:
156 vocabulary = exported_field.vocabularyName
157
158- if isinstance(vocabulary, six.string_types):
159+ if isinstance(vocabulary, str):
160 vocabulary = getVocabularyRegistry().get(context, vocabulary)
161
162 # Construct checkbox data dict for each item in the vocabulary.
163@@ -638,7 +637,7 @@ class BooleanChoiceWidget(WidgetBase, DefinedTagMixin):
164 Automatically generated if this is not provided.
165 :param header: The large text at the top of the choice popup.
166 """
167- super(BooleanChoiceWidget, self).__init__(
168+ super().__init__(
169 context, exported_field, content_box_id,
170 edit_view, edit_url, edit_title)
171 self.header = header
172@@ -698,7 +697,7 @@ class EnumChoiceWidget(WidgetBase):
173 :param edit_title: Used to set the title attribute of the anchor.
174 :param css_class_prefix: Added to the start of the enum titles.
175 """
176- super(EnumChoiceWidget, self).__init__(
177+ super().__init__(
178 context, exported_field, content_box_id,
179 edit_view, edit_url, edit_title)
180 self.header = header
181diff --git a/lib/lp/app/browser/multistep.py b/lib/lp/app/browser/multistep.py
182index 278f8e1..6215eca 100644
183--- a/lib/lp/app/browser/multistep.py
184+++ b/lib/lp/app/browser/multistep.py
185@@ -152,7 +152,7 @@ class StepView(LaunchpadFormView):
186
187 _field_names = []
188 step_name = ''
189- main_action_label = u'Continue'
190+ main_action_label = 'Continue'
191 next_step = None
192
193 # Step information. These get filled in by the controller view.
194@@ -201,7 +201,7 @@ class StepView(LaunchpadFormView):
195 `validateStep()` if they have any custom validation they need to
196 perform.
197 """
198- super(StepView, self).validate(data)
199+ super().validate(data)
200 if self.shouldProcess(data):
201 self.validateStep(data)
202
203@@ -243,7 +243,7 @@ class StepView(LaunchpadFormView):
204 operation.label = self.main_action_label
205 actions.append(operation)
206 self.actions = actions
207- return super(StepView, self).render()
208+ return super().render()
209
210 @property
211 def cancel_url(self):
212diff --git a/lib/lp/app/browser/root.py b/lib/lp/app/browser/root.py
213index f0e85f7..694c8d2 100644
214--- a/lib/lp/app/browser/root.py
215+++ b/lib/lp/app/browser/root.py
216@@ -14,7 +14,6 @@ import time
217 import feedparser
218 from lazr.batchnavigator.z3batching import batch
219 import requests
220-import six
221 from zope.component import getUtility
222 from zope.formlib.interfaces import ConversionError
223 from zope.interface import Interface
224@@ -79,7 +78,7 @@ class LaunchpadRootIndexView(HasAnnouncementsView, LaunchpadView):
225
226 def initialize(self):
227 """Set up featured projects list and the top featured project."""
228- super(LaunchpadRootIndexView, self).initialize()
229+ super().initialize()
230 # The maximum number of projects to be displayed as defined by the
231 # number of items plus one top featured project.
232 self.featured_projects = list(
233@@ -275,7 +274,7 @@ class LaunchpadSearchView(LaunchpadFormView):
234
235 Set the state of the search_params and matches.
236 """
237- super(LaunchpadSearchView, self).__init__(context, request)
238+ super().__init__(context, request)
239 self.has_page_service = True
240 self._bug = None
241 self._question = None
242@@ -346,7 +345,7 @@ class LaunchpadSearchView(LaunchpadFormView):
243 """Focus the first widget when there are no matches."""
244 if self.has_matches:
245 return None
246- return super(LaunchpadSearchView, self).focusedElementScript()
247+ return super().focusedElementScript()
248
249 @property
250 def bug(self):
251@@ -427,7 +426,7 @@ class LaunchpadSearchView(LaunchpadFormView):
252 if isinstance(error, ConversionError):
253 self.setFieldError(
254 'text', 'Can not convert your search term.')
255- elif isinstance(error, six.text_type):
256+ elif isinstance(error, str):
257 continue
258 elif (error.field_name == 'text'
259 and isinstance(error.errors, TooLong)):
260@@ -435,7 +434,7 @@ class LaunchpadSearchView(LaunchpadFormView):
261 'text', 'The search text cannot exceed 250 characters.')
262
263 @safe_action
264- @action(u'Search', name='search')
265+ @action('Search', name='search')
266 def search_action(self, action, data):
267 """The Action executed when the user uses the search button.
268
269@@ -579,7 +578,7 @@ class WindowedListBatch(batch._Batch):
270
271 def __iter__(self):
272 """Iterate over objects that are not None."""
273- for item in super(WindowedListBatch, self).__iter__():
274+ for item in super().__iter__():
275 if item is not None:
276 # Never yield None
277 yield item
278@@ -614,7 +613,7 @@ class SiteSearchBatchNavigator(BatchNavigator):
279 :param callback: Not used.
280 """
281 results = WindowedList(results, start, results.total)
282- super(SiteSearchBatchNavigator, self).__init__(results, request,
283+ super().__init__(results, request,
284 start=start, size=size, callback=callback,
285 transient_parameters=transient_parameters,
286 force_start=force_start, range_factory=range_factory)
287diff --git a/lib/lp/app/browser/tales.py b/lib/lp/app/browser/tales.py
288index 08d2a71..905ba0d 100644
289--- a/lib/lp/app/browser/tales.py
290+++ b/lib/lp/app/browser/tales.py
291@@ -21,7 +21,6 @@ from lazr.enum import enumerated_type_registry
292 from lazr.restful.utils import get_current_browser_request
293 from lazr.uri import URI
294 import pytz
295-import six
296 from six.moves.urllib.parse import quote
297 from zope.browserpage import ViewPageTemplateFile
298 from zope.component import (
299@@ -688,9 +687,9 @@ class ObjectFormatterAPI:
300 text = breadcrumb.detail
301 if len(text) > 64:
302 truncated = '%s...' % text[0:64]
303- if truncated.count(u'\u201c') > truncated.count(u'\u201cd'):
304+ if truncated.count('\u201c') > truncated.count('\u201cd'):
305 # Close the open smartquote if it was dropped.
306- truncated += u'\u201d'
307+ truncated += '\u201d'
308 return truncated
309 return text
310
311@@ -1252,7 +1251,7 @@ class PersonFormatterAPI(ObjectFormatterAPI):
312
313 The default URL for a person is to the mainsite.
314 """
315- return super(PersonFormatterAPI, self).url(view_name, rootsite)
316+ return super().url(view_name, rootsite)
317
318 def _makeLink(self, view_name, rootsite, text):
319 person = self._context
320@@ -1315,7 +1314,7 @@ class MixedVisibilityError(Exception):
321 class TeamFormatterAPI(PersonFormatterAPI):
322 """Adapter for `ITeam` objects to a formatted string."""
323
324- hidden = u'<hidden>'
325+ hidden = '<hidden>'
326
327 def url(self, view_name=None, rootsite='mainsite'):
328 """See `ObjectFormatterAPI`.
329@@ -1327,7 +1326,7 @@ class TeamFormatterAPI(PersonFormatterAPI):
330 # This person has no permission to view the team details.
331 self._report_visibility_leak()
332 return None
333- return super(TeamFormatterAPI, self).url(view_name, rootsite)
334+ return super().url(view_name, rootsite)
335
336 def api_url(self, context):
337 """See `ObjectFormatterAPI`."""
338@@ -1335,7 +1334,7 @@ class TeamFormatterAPI(PersonFormatterAPI):
339 # This person has no permission to view the team details.
340 self._report_visibility_leak()
341 return None
342- return super(TeamFormatterAPI, self).api_url(context)
343+ return super().api_url(context)
344
345 def link(self, view_name, rootsite='mainsite'):
346 """See `ObjectFormatterAPI`.
347@@ -1349,7 +1348,7 @@ class TeamFormatterAPI(PersonFormatterAPI):
348 self._report_visibility_leak()
349 return structured(
350 '<span class="sprite team">%s</span>', self.hidden).escapedtext
351- return super(TeamFormatterAPI, self).link(view_name, rootsite)
352+ return super().link(view_name, rootsite)
353
354 def icon(self, view_name):
355 team = self._context
356@@ -1357,7 +1356,7 @@ class TeamFormatterAPI(PersonFormatterAPI):
357 css_class = ObjectImageDisplayAPI(team).sprite_css()
358 return '<span class="' + css_class + '"></span>'
359 else:
360- return super(TeamFormatterAPI, self).icon(view_name)
361+ return super().icon(view_name)
362
363 def displayname(self, view_name, rootsite=None):
364 """See `PersonFormatterAPI`."""
365@@ -1366,7 +1365,7 @@ class TeamFormatterAPI(PersonFormatterAPI):
366 # This person has no permission to view the team details.
367 self._report_visibility_leak()
368 return self.hidden
369- return super(TeamFormatterAPI, self).displayname(view_name, rootsite)
370+ return super().displayname(view_name, rootsite)
371
372 def unique_displayname(self, view_name):
373 """See `PersonFormatterAPI`."""
374@@ -1375,7 +1374,7 @@ class TeamFormatterAPI(PersonFormatterAPI):
375 # This person has no permission to view the team details.
376 self._report_visibility_leak()
377 return self.hidden
378- return super(TeamFormatterAPI, self).unique_displayname(view_name)
379+ return super().unique_displayname(view_name)
380
381 def _report_visibility_leak(self):
382 request = get_current_browser_request()
383@@ -1426,7 +1425,7 @@ class CustomizableFormatter(ObjectFormatterAPI):
384 """
385 values = {
386 k: v if v is not None else ''
387- for k, v in six.iteritems(self._link_summary_values())}
388+ for k, v in self._link_summary_values().items()}
389 return structured(self._link_summary_template, **values).escapedtext
390
391 def _title_values(self):
392@@ -1448,7 +1447,7 @@ class CustomizableFormatter(ObjectFormatterAPI):
393 return None
394 values = {
395 k: v if v is not None else ''
396- for k, v in six.iteritems(self._title_values())}
397+ for k, v in self._title_values().items()}
398 return structured(title_template, **values).escapedtext
399
400 def sprite_css(self):
401@@ -1512,7 +1511,7 @@ class PillarFormatterAPI(CustomizableFormatter):
402
403 The default URL for a pillar is to the mainsite.
404 """
405- return super(PillarFormatterAPI, self).url(view_name, rootsite)
406+ return super().url(view_name, rootsite)
407
408 def _getLinkHTML(self, view_name, rootsite,
409 template, custom_icon_template):
410@@ -1550,11 +1549,11 @@ class PillarFormatterAPI(CustomizableFormatter):
411 In the case of Products or ProjectGroups we display the custom
412 icon, if one exists. The default URL for a pillar is to the mainsite.
413 """
414- super(PillarFormatterAPI, self).link(view_name)
415- template = u'<a href="%(url)s" class="%(css_class)s">%(summary)s</a>'
416+ super().link(view_name)
417+ template = '<a href="%(url)s" class="%(css_class)s">%(summary)s</a>'
418 custom_icon_template = (
419- u'<a href="%(url)s" class="bg-image" '
420- u'style="background-image: url(%(custom_icon)s)">%(summary)s</a>'
421+ '<a href="%(url)s" class="bg-image" '
422+ 'style="background-image: url(%(custom_icon)s)">%(summary)s</a>'
423 )
424 return self._getLinkHTML(
425 view_name, rootsite, template, custom_icon_template)
426@@ -1566,15 +1565,15 @@ class PillarFormatterAPI(CustomizableFormatter):
427 In the case of Products or ProjectGroups we display the custom
428 icon, if one exists. The default URL for a pillar is to the mainsite.
429 """
430- super(PillarFormatterAPI, self).link(view_name)
431+ super().link(view_name)
432 template = (
433- u'<a href="%(url)s" class="%(css_class)s">%(displayname)s</a>'
434- u'&nbsp;(<a href="%(url)s">%(name)s</a>)'
435+ '<a href="%(url)s" class="%(css_class)s">%(displayname)s</a>'
436+ '&nbsp;(<a href="%(url)s">%(name)s</a>)'
437 )
438 custom_icon_template = (
439- u'<a href="%(url)s" class="bg-image" '
440- u'style="background-image: url(%(custom_icon)s)">'
441- u'%(displayname)s</a>&nbsp;(<a href="%(url)s">%(name)s</a>)'
442+ '<a href="%(url)s" class="bg-image" '
443+ 'style="background-image: url(%(custom_icon)s)">'
444+ '%(displayname)s</a>&nbsp;(<a href="%(url)s">%(name)s</a>)'
445 )
446 return self._getLinkHTML(
447 view_name, rootsite, template, custom_icon_template)
448@@ -1739,7 +1738,7 @@ class GitRefFormatterAPI(CustomizableFormatter):
449 """
450 if self._context.repository_url is not None:
451 return None
452- return super(GitRefFormatterAPI, self).url(view_name, rootsite)
453+ return super().url(view_name, rootsite)
454
455 def _link_summary_values(self):
456 return {'display_name': self._context.display_name}
457@@ -2085,7 +2084,7 @@ class BugTrackerFormatterAPI(ObjectFormatterAPI):
458 """
459 url = self._context.baseurl
460 if url.startswith('mailto:') and getUtility(ILaunchBag).user is None:
461- return html_escape(u'mailto:<email address hidden>')
462+ return html_escape('mailto:<email address hidden>')
463 else:
464 return structured(
465 '<a class="link-external" href="%(url)s">%(url)s</a>',
466@@ -2118,7 +2117,7 @@ class BugTrackerFormatterAPI(ObjectFormatterAPI):
467 anonymous = getUtility(ILaunchBag).user is None
468 for alias in self._context.aliases:
469 if anonymous and alias.startswith('mailto:'):
470- yield u'mailto:<email address hidden>'
471+ yield 'mailto:<email address hidden>'
472 else:
473 yield alias
474
475@@ -2141,7 +2140,7 @@ class BugWatchFormatterAPI(ObjectFormatterAPI):
476 an email address, only the summary is returned (i.e. no link).
477 """
478 if summary is None or len(summary) == 0:
479- summary = structured(u'&mdash;')
480+ summary = structured('&mdash;')
481 url = self._context.url
482 if url.startswith('mailto:') and getUtility(ILaunchBag).user is None:
483 return html_escape(summary)
484@@ -2159,7 +2158,7 @@ class BugWatchFormatterAPI(ObjectFormatterAPI):
485 summary = self._context.bugtracker.name
486 remotebug = self._context.remotebug
487 if remotebug is not None and len(remotebug) > 0:
488- summary = u'%s #%s' % (summary, remotebug)
489+ summary = '%s #%s' % (summary, remotebug)
490 return self._make_external_link(summary)
491
492 def external_link_short(self):
493@@ -2584,7 +2583,7 @@ class LinkFormatterAPI(ObjectFormatterAPI):
494 if self._context.enabled:
495 return self._context.url
496 else:
497- return u''
498+ return ''
499
500
501 class RevisionAuthorFormatterAPI(ObjectFormatterAPI):
502@@ -2629,7 +2628,7 @@ class PermissionRequiredQuery:
503
504
505 class IMainTemplateFile(Interface):
506- path = TextLine(title=u'The absolute path to this main template.')
507+ path = TextLine(title='The absolute path to this main template.')
508
509
510 @adapter(LaunchpadLayer)
511@@ -2776,8 +2775,7 @@ class TranslationGroupFormatterAPI(ObjectFormatterAPI):
512
513 def url(self, view_name=None, rootsite='translations'):
514 """See `ObjectFormatterAPI`."""
515- return super(TranslationGroupFormatterAPI, self).url(
516- view_name, rootsite)
517+ return super().url(view_name, rootsite)
518
519 def link(self, view_name, rootsite='translations'):
520 """See `ObjectFormatterAPI`."""
521@@ -2800,7 +2798,7 @@ class LanguageFormatterAPI(ObjectFormatterAPI):
522
523 def url(self, view_name=None, rootsite='translations'):
524 """See `ObjectFormatterAPI`."""
525- return super(LanguageFormatterAPI, self).url(view_name, rootsite)
526+ return super().url(view_name, rootsite)
527
528 def link(self, view_name, rootsite='translations'):
529 """See `ObjectFormatterAPI`."""
530@@ -2825,7 +2823,7 @@ class POFileFormatterAPI(ObjectFormatterAPI):
531
532 def url(self, view_name=None, rootsite='translations'):
533 """See `ObjectFormatterAPI`."""
534- return super(POFileFormatterAPI, self).url(view_name, rootsite)
535+ return super().url(view_name, rootsite)
536
537 def link(self, view_name, rootsite='translations'):
538 """See `ObjectFormatterAPI`."""
539diff --git a/lib/lp/app/browser/tests/test_base_layout.py b/lib/lp/app/browser/tests/test_base_layout.py
540index 1cbdaa6..d3c0c0b 100644
541--- a/lib/lp/app/browser/tests/test_base_layout.py
542+++ b/lib/lp/app/browser/tests/test_base_layout.py
543@@ -34,7 +34,7 @@ class TestBaseLayout(TestCaseWithFactory):
544 layer = DatabaseFunctionalLayer
545
546 def setUp(self):
547- super(TestBaseLayout, self).setUp()
548+ super().setUp()
549 self.user = self.factory.makePerson(name='waffles')
550 self.context = None
551
552@@ -116,8 +116,7 @@ class TestBaseLayout(TestCaseWithFactory):
553 self.assertEqual(['watermark-apps-portlet'], watermark['class'])
554 if self.context.is_team:
555 self.assertEqual('/@@/team-logo', watermark.img['src'])
556- self.assertEqual(
557- u'\u201cWaffles\u201d team', watermark.h2.a.string)
558+ self.assertEqual('\u201cWaffles\u201d team', watermark.h2.a.string)
559 else:
560 self.assertEqual('/@@/person-logo', watermark.img['src'])
561 self.assertEqual('Waffles', watermark.h2.a.string)
562diff --git a/lib/lp/app/browser/tests/test_formatters.py b/lib/lp/app/browser/tests/test_formatters.py
563index 25f355c..c917b5e 100644
564--- a/lib/lp/app/browser/tests/test_formatters.py
565+++ b/lib/lp/app/browser/tests/test_formatters.py
566@@ -64,7 +64,7 @@ class ObjectFormatterAPITestCase(TestCaseWithFactory, FakeAdapterMixin):
567 view.request.traversed_objects = [project, bug.bugtasks[0], view]
568 formatter = ObjectFormatterAPI(view)
569 self.assertEqual(
570- u'%s \u201cbang\u201d : Bugs : Fnord' % bug.displayname,
571+ '%s \u201cbang\u201d : Bugs : Fnord' % bug.displayname,
572 formatter.pagetitle())
573
574 def test_pagetitle_last_breadcrumb_detail_too_long(self):
575@@ -76,8 +76,8 @@ class ObjectFormatterAPITestCase(TestCaseWithFactory, FakeAdapterMixin):
576 current_request=True, server_url='https://bugs.launchpad.test/')
577 view.request.traversed_objects = [project, bug.bugtasks[0], view]
578 formatter = ObjectFormatterAPI(view)
579- detail = u'%s \u201c%s\u201d' % (bug.displayname, title)
580- expected_title = u'%s...\u201d : Bugs : Fnord' % detail[0:64]
581+ detail = '%s \u201c%s\u201d' % (bug.displayname, title)
582+ expected_title = '%s...\u201d : Bugs : Fnord' % detail[0:64]
583 self.assertEqual(expected_title, formatter.pagetitle())
584
585 def test_global_css(self):
586@@ -97,10 +97,10 @@ class TestPillarFormatterAPI(TestCaseWithFactory):
587
588 layer = DatabaseFunctionalLayer
589
590- FORMATTER_CSS_CLASS = u'sprite product'
591+ FORMATTER_CSS_CLASS = 'sprite product'
592
593 def setUp(self):
594- super(TestPillarFormatterAPI, self).setUp()
595+ super().setUp()
596 self.product = self.factory.makeProduct()
597 self.formatter = PillarFormatterAPI(self.product)
598 self.product_url = canonical_url(
599@@ -111,7 +111,7 @@ class TestPillarFormatterAPI(TestCaseWithFactory):
600 # current context, formatted to include a custom icon if the
601 # context has one, and to display the context summary.
602 link = self.formatter.link(None)
603- template = u'<a href="%(url)s" class="%(css_class)s">%(summary)s</a>'
604+ template = '<a href="%(url)s" class="%(css_class)s">%(summary)s</a>'
605 mapping = {
606 'url': self.product_url,
607 'summary': self.product.displayname,
608@@ -126,8 +126,8 @@ class TestPillarFormatterAPI(TestCaseWithFactory):
609 # (displayname and name of the context).
610 link = self.formatter.link_with_displayname(None)
611 template = (
612- u'<a href="%(url)s" class="%(css_class)s">%(summary)s</a>'
613- u'&nbsp;(<a href="%(url)s">%(name)s</a>)'
614+ '<a href="%(url)s" class="%(css_class)s">%(summary)s</a>'
615+ '&nbsp;(<a href="%(url)s">%(name)s</a>)'
616 )
617 mapping = {
618 'url': self.product_url,
619diff --git a/lib/lp/app/browser/tests/test_launchpad.py b/lib/lp/app/browser/tests/test_launchpad.py
620index dc697fd..3b2ceaa 100644
621--- a/lib/lp/app/browser/tests/test_launchpad.py
622+++ b/lib/lp/app/browser/tests/test_launchpad.py
623@@ -167,8 +167,7 @@ class TestBranchTraversal(TestCaseWithFactory, TraversalMixin):
624 path, notification, BrowserNotificationLevel.ERROR)
625
626 def traverse(self, path, **kwargs):
627- return super(TestBranchTraversal, self).traverse(
628- path, '+branch', **kwargs)
629+ return super().traverse(path, '+branch', **kwargs)
630
631 def test_unique_name_traversal(self):
632 # Traversing to /+branch/<unique_name> redirects to the page for that
633@@ -216,14 +215,14 @@ class TestBranchTraversal(TestCaseWithFactory, TraversalMixin):
634 InformationType.USERDATA)
635 login(ANONYMOUS)
636 requiredMessage = (
637- u"The target %s does not have a linked branch." %
638+ "The target %s does not have a linked branch." %
639 naked_product.name)
640 self.assertDisplaysNotice(naked_product.name, requiredMessage)
641
642 def test_nonexistent_product(self):
643 # Traversing to /+branch/<no-such-product> displays an error message.
644 non_existent = 'non-existent'
645- required_message = u"No such product: '%s'." % non_existent
646+ required_message = "No such product: '%s'." % non_existent
647 self.assertDisplaysError(non_existent, html_escape(required_message))
648
649 def test_nonexistent_product_without_referer(self):
650@@ -249,7 +248,7 @@ class TestBranchTraversal(TestCaseWithFactory, TraversalMixin):
651 # user message on the same page.
652 product = self.factory.makeProduct()
653 requiredMessage = (
654- u"The target %s does not have a linked branch." % product.name)
655+ "The target %s does not have a linked branch." % product.name)
656 self.assertDisplaysNotice(product.name, requiredMessage)
657
658 def test_distro_package_alias(self):
659@@ -279,7 +278,7 @@ class TestBranchTraversal(TestCaseWithFactory, TraversalMixin):
660 login(ANONYMOUS)
661 path = ICanHasLinkedBranch(distro_package).bzr_path
662 requiredMessage = (
663- u"The target %s does not have a linked branch." % path)
664+ "The target %s does not have a linked branch." % path)
665 self.assertDisplaysNotice(path, requiredMessage)
666
667 def test_trailing_path_redirect(self):
668@@ -323,7 +322,7 @@ class TestBranchTraversal(TestCaseWithFactory, TraversalMixin):
669 login(ANONYMOUS)
670 path = ICanHasLinkedBranch(series).bzr_path
671 requiredMessage = (
672- u"The target %s does not have a linked branch." % path)
673+ "The target %s does not have a linked branch." % path)
674 self.assertDisplaysNotice(path, requiredMessage)
675
676 def test_too_short_branch_name(self):
677@@ -331,13 +330,13 @@ class TestBranchTraversal(TestCaseWithFactory, TraversalMixin):
678 # that's too short to be a real unique name.
679 owner = self.factory.makePerson()
680 requiredMessage = html_escape(
681- u"Cannot understand namespace name: '%s'" % owner.name)
682+ "Cannot understand namespace name: '%s'" % owner.name)
683 self.assertDisplaysError('~%s' % owner.name, requiredMessage)
684
685 def test_invalid_product_name(self):
686 # error notification if the thing following +branch has an invalid
687 # product name.
688- self.assertDisplaysError('_foo', u"Invalid name for product: _foo.")
689+ self.assertDisplaysError('_foo', "Invalid name for product: _foo.")
690
691 def test_invalid_product_name_without_referer(self):
692 # error notification if the thing following +branch has an invalid
693@@ -350,8 +349,7 @@ class TestCodeTraversal(TestCaseWithFactory, TraversalMixin):
694 layer = DatabaseFunctionalLayer
695
696 def traverse(self, path, **kwargs):
697- return super(TestCodeTraversal, self).traverse(
698- path, '+code', **kwargs)
699+ return super().traverse(path, '+code', **kwargs)
700
701 def test_project_bzr_branch(self):
702 branch = self.factory.makeAnyBranch()
703@@ -603,7 +601,7 @@ class TestPersonTraversal(TestCaseWithFactory, TraversalMixin):
704 layer = DatabaseFunctionalLayer
705
706 def setUp(self):
707- super(TestPersonTraversal, self).setUp()
708+ super().setUp()
709 self.any_user = self.factory.makePerson()
710 self.admin = getUtility(IPersonSet).getByName('name16')
711 self.registry_expert = self.factory.makePerson()
712@@ -638,7 +636,7 @@ class TestPersonTraversal(TestCaseWithFactory, TraversalMixin):
713
714 def test_placeholder_person_visibility(self):
715 # Verify a placeholder user is only traversable by an admin.
716- name = u'placeholder-person'
717+ name = 'placeholder-person'
718 person = getUtility(IPersonSet).createPlaceholderPerson(name, name)
719 login_person(self.admin)
720 segment = '~%s' % name
721@@ -765,7 +763,7 @@ class TestProductTraversal(TestCaseWithFactory, TraversalMixin):
722 layer = DatabaseFunctionalLayer
723
724 def setUp(self):
725- super(TestProductTraversal, self).setUp()
726+ super().setUp()
727 self.active_public_product = self.factory.makeProduct()
728 self.inactive_public_product = self.factory.makeProduct()
729 removeSecurityProxy(self.inactive_public_product).active = False
730diff --git a/lib/lp/app/browser/tests/test_launchpadform.py b/lib/lp/app/browser/tests/test_launchpadform.py
731index 95190a5..cb08991 100644
732--- a/lib/lp/app/browser/tests/test_launchpadform.py
733+++ b/lib/lp/app/browser/tests/test_launchpadform.py
734@@ -41,11 +41,11 @@ from lp.testing.layers import (
735 class TestInterface(Interface):
736 """Test interface for the view below."""
737
738- normal = Text(title=u'normal', description=u'plain text')
739+ normal = Text(title='normal', description='plain text')
740
741 structured = has_structured_doc(
742- Text(title=u'structured',
743- description=u'<strong>structured text</strong'))
744+ Text(title='structured',
745+ description='<strong>structured text</strong'))
746
747
748 class TestView(LaunchpadFormView):
749@@ -90,9 +90,9 @@ class TestQueryTalesForHasStructuredDoc(TestCase):
750 class TestHelpLinksInterface(Interface):
751 """Test interface for the view below."""
752
753- nickname = Text(title=u'nickname')
754+ nickname = Text(title='nickname')
755
756- displayname = Text(title=u'displayname')
757+ displayname = Text(title='displayname')
758
759
760 class TestHelpLinksView(LaunchpadFormView):
761@@ -100,13 +100,13 @@ class TestHelpLinksView(LaunchpadFormView):
762
763 schema = TestHelpLinksInterface
764
765- page_title = u"TestHelpLinksView"
766+ page_title = "TestHelpLinksView"
767 template = ViewPageTemplateFile(
768 config.root + '/lib/lp/app/templates/generic-edit.pt')
769
770 help_links = {
771- "nickname": u"http://widget.example.com/name",
772- "displayname": u"http://widget.example.com/displayname",
773+ "nickname": "http://widget.example.com/name",
774+ "displayname": "http://widget.example.com/displayname",
775 }
776
777
778@@ -122,10 +122,10 @@ class TestHelpLinks(TestCaseWithFactory):
779 view.initialize()
780 nickname_widget, displayname_widget = view.widgets
781 self.assertEqual(
782- u"http://widget.example.com/name",
783+ "http://widget.example.com/name",
784 nickname_widget.help_link)
785 self.assertEqual(
786- u"http://widget.example.com/displayname",
787+ "http://widget.example.com/displayname",
788 displayname_widget.help_link)
789
790 def test_help_links_render(self):
791@@ -140,20 +140,20 @@ class TestHelpLinks(TestCaseWithFactory):
792 [nickname_help_link] = root.cssselect(
793 "label[for$=nickname] ~ a[target=help]")
794 self.assertEqual(
795- u"http://widget.example.com/name",
796+ "http://widget.example.com/name",
797 nickname_help_link.get("href"))
798 [displayname_help_link] = root.cssselect(
799 "label[for$=displayname] ~ a[target=help]")
800 self.assertEqual(
801- u"http://widget.example.com/displayname",
802+ "http://widget.example.com/displayname",
803 displayname_help_link.get("href"))
804
805
806 class TestWidgetDivInterface(Interface):
807 """Test interface for the view below."""
808
809- single_line = TextLine(title=u'single_line')
810- multi_line = Text(title=u'multi_line')
811+ single_line = TextLine(title='single_line')
812+ multi_line = Text(title='multi_line')
813 checkbox = Choice(
814 vocabulary=SimpleVocabulary.fromItems(
815 (('yes', True), ('no', False))))
816diff --git a/lib/lp/app/browser/tests/test_launchpadroot.py b/lib/lp/app/browser/tests/test_launchpadroot.py
817index 92cc935..5c3b1d7 100644
818--- a/lib/lp/app/browser/tests/test_launchpadroot.py
819+++ b/lib/lp/app/browser/tests/test_launchpadroot.py
820@@ -127,7 +127,7 @@ class LaunchpadRootIndexViewTestCase(TestCaseWithFactory):
821 layer = LaunchpadFunctionalLayer
822
823 def setUp(self):
824- super(LaunchpadRootIndexViewTestCase, self).setUp()
825+ super().setUp()
826 # Use a FakeLogger fixture to prevent Memcached warnings to be
827 # printed to stdout while browsing pages.
828 self.useFixture(FakeLogger())
829diff --git a/lib/lp/app/browser/tests/test_mixed_visibility.py b/lib/lp/app/browser/tests/test_mixed_visibility.py
830index be6014a..ee7bcce 100644
831--- a/lib/lp/app/browser/tests/test_mixed_visibility.py
832+++ b/lib/lp/app/browser/tests/test_mixed_visibility.py
833@@ -21,7 +21,7 @@ class TestMixedVisibility(TestCaseWithFactory):
834 viewer = self.factory.makePerson()
835 with person_logged_in(viewer):
836 self.assertEqual(
837- u'<hidden>', TeamFormatterAPI(team).displayname(None))
838+ '<hidden>', TeamFormatterAPI(team).displayname(None))
839 self.assertEqual(1, len(self.oopses))
840 self.assertTrue(
841 'MixedVisibilityError' in self.oopses[0]['tb_text'])
842diff --git a/lib/lp/app/browser/tests/test_page_macro.py b/lib/lp/app/browser/tests/test_page_macro.py
843index 6184afd..fb8f1fd 100644
844--- a/lib/lp/app/browser/tests/test_page_macro.py
845+++ b/lib/lp/app/browser/tests/test_page_macro.py
846@@ -63,7 +63,7 @@ class PageMacroDispatcherTestCase(TestPageMacroDispatcherMixin, TestCase):
847 layer = FunctionalLayer
848
849 def setUp(self):
850- super(PageMacroDispatcherTestCase, self).setUp()
851+ super().setUp()
852 self._setUpView()
853
854 def test_base_template(self):
855@@ -152,7 +152,7 @@ class PageMacroDispatcherInteractionTestCase(TestPageMacroDispatcherMixin,
856 layer = DatabaseFunctionalLayer
857
858 def setUp(self):
859- super(PageMacroDispatcherInteractionTestCase, self).setUp()
860+ super().setUp()
861 self._setUpView()
862 login_person(self.factory.makePerson())
863
864@@ -165,7 +165,7 @@ class PageMacroDispatcherInteractionTestCase(TestPageMacroDispatcherMixin,
865 return FakeSecurityChecker(adaptee)
866
867 def __init__(self, adaptee=None):
868- super(FakeSecurityChecker, self).__init__(adaptee)
869+ super().__init__(adaptee)
870
871 def checkUnauthenticated(self):
872 return has_permission
873diff --git a/lib/lp/app/browser/tests/test_stringformatter.py b/lib/lp/app/browser/tests/test_stringformatter.py
874index 0266453..1663f49 100644
875--- a/lib/lp/app/browser/tests/test_stringformatter.py
876+++ b/lib/lp/app/browser/tests/test_stringformatter.py
877@@ -315,8 +315,8 @@ class TestParseDiff(TestCase):
878 def test_unicode(self):
879 # Diffs containing Unicode work too.
880 self.assertEqual(
881- [('text', 1, 0, 0, u'Unicode \u1010')],
882- list(parse_diff(u'Unicode \u1010')))
883+ [('text', 1, 0, 0, 'Unicode \u1010')],
884+ list(parse_diff('Unicode \u1010')))
885
886 def assertParses(self, expected, diff):
887 diff_lines = diff.splitlines()
888@@ -488,10 +488,10 @@ class TestDiffFormatter(TestCase):
889 def test_format_unicode(self):
890 # Sometimes the strings contain unicode, those should work too.
891 self.assertEqual(
892- u'<table class="diff unidiff"><tr id="diff-line-1">'
893- u'<td class="line-no unselectable">1</td><td class="text">'
894- u'Unicode \u1010</td></tr></table>',
895- FormattersAPI(u'Unicode \u1010').format_diff())
896+ '<table class="diff unidiff"><tr id="diff-line-1">'
897+ '<td class="line-no unselectable">1</td><td class="text">'
898+ 'Unicode \u1010</td></tr></table>',
899+ FormattersAPI('Unicode \u1010').format_diff())
900
901 def test_cssClasses(self):
902 # Different parts of the diff have different css classes.
903@@ -586,14 +586,14 @@ class TestSideBySideDiffFormatter(TestCase):
904 def test_format_unicode(self):
905 # Sometimes the strings contain unicode, those should work too.
906 self.assertEqual(
907- u'<table class="diff ssdiff"><tr id="diff-line-1">'
908- u'<td class="line-no unselectable" style="display: none">1</td>'
909- u'<td class="ss-line-no unselectable">0</td>'
910- u'<td class="text">Unicode \u1010</td>'
911- u'<td class="ss-line-no unselectable">0</td>'
912- u'<td class="text">Unicode \u1010</td>'
913- u'</tr></table>',
914- FormattersAPI(u'Unicode \u1010').format_ssdiff())
915+ '<table class="diff ssdiff"><tr id="diff-line-1">'
916+ '<td class="line-no unselectable" style="display: none">1</td>'
917+ '<td class="ss-line-no unselectable">0</td>'
918+ '<td class="text">Unicode \u1010</td>'
919+ '<td class="ss-line-no unselectable">0</td>'
920+ '<td class="text">Unicode \u1010</td>'
921+ '</tr></table>',
922+ FormattersAPI('Unicode \u1010').format_ssdiff())
923
924 def test_cssClasses(self):
925 # Different parts of the diff have different css classes.
926@@ -743,7 +743,7 @@ class TestMarkdownDisabled(TestCase):
927 layer = DatabaseFunctionalLayer # Fixtures need the database for now
928
929 def setUp(self):
930- super(TestMarkdownDisabled, self).setUp()
931+ super().setUp()
932 self.useFixture(FeatureFixture({'markdown.enabled': None}))
933
934 def test_plain_text(self):
935@@ -762,7 +762,7 @@ class TestMarkdown(TestCase):
936 layer = DatabaseFunctionalLayer # Fixtures need the database for now
937
938 def setUp(self):
939- super(TestMarkdown, self).setUp()
940+ super().setUp()
941 self.useFixture(FeatureFixture({'markdown.enabled': 'on'}))
942
943 def test_plain_text(self):
944diff --git a/lib/lp/app/browser/tests/test_vocabulary.py b/lib/lp/app/browser/tests/test_vocabulary.py
945index c7cf33d..2585a02 100644
946--- a/lib/lp/app/browser/tests/test_vocabulary.py
947+++ b/lib/lp/app/browser/tests/test_vocabulary.py
948@@ -487,7 +487,7 @@ class HugeVocabularyJSONViewTestCase(TestCaseWithFactory):
949 layer = DatabaseFunctionalLayer
950
951 def setUp(self):
952- super(HugeVocabularyJSONViewTestCase, self).setUp()
953+ super().setUp()
954 test_persons = []
955 for name in range(1, 7):
956 test_persons.append(
957diff --git a/lib/lp/app/browser/tests/test_webservice.py b/lib/lp/app/browser/tests/test_webservice.py
958index 0237760..7af01a7 100644
959--- a/lib/lp/app/browser/tests/test_webservice.py
960+++ b/lib/lp/app/browser/tests/test_webservice.py
961@@ -33,7 +33,7 @@ class TestXHTMLRepresentations(TestCaseWithFactory):
962
963 def test_text(self):
964 # Test the XHTML representation of a text field.
965- text = u'\N{SNOWMAN} snowman@example.com bug 1'
966+ text = '\N{SNOWMAN} snowman@example.com bug 1'
967 # We need something that has an IPersonChoice, a project will do.
968 product = self.factory.makeProduct()
969 field = IProduct['description']
970@@ -42,7 +42,7 @@ class TestXHTMLRepresentations(TestCaseWithFactory):
971 (product, field, request), IFieldHTMLRenderer)
972 # The representation is linkified html.
973 self.assertEqual(
974- u'<p>\N{SNOWMAN} snowman@example.com '
975+ '<p>\N{SNOWMAN} snowman@example.com '
976 '<a href="/bugs/1" class="bug-link">bug 1</a></p>',
977 renderer(text))
978
979diff --git a/lib/lp/app/browser/vocabulary.py b/lib/lp/app/browser/vocabulary.py
980index 4f3cde4..7cf7eb3 100644
981--- a/lib/lp/app/browser/vocabulary.py
982+++ b/lib/lp/app/browser/vocabulary.py
983@@ -114,7 +114,7 @@ class IPickerEntrySource(Interface):
984
985 @adapter(Interface)
986 @implementer(IPickerEntrySource)
987-class DefaultPickerEntrySourceAdapter(object):
988+class DefaultPickerEntrySourceAdapter:
989 """Adapts Interface to IPickerEntrySource."""
990
991 def __init__(self, context):
992@@ -151,9 +151,7 @@ class PersonPickerEntrySourceAdapter(DefaultPickerEntrySourceAdapter):
993
994 def getPickerEntries(self, term_values, context_object, **kwarg):
995 """See `IPickerEntrySource`"""
996- picker_entries = (
997- super(PersonPickerEntrySourceAdapter, self)
998- .getPickerEntries(term_values, context_object))
999+ picker_entries = super().getPickerEntries(term_values, context_object)
1000
1001 affiliated_context = IHasAffiliation(context_object, None)
1002 if affiliated_context is not None:
1003@@ -214,8 +212,7 @@ class BranchPickerEntrySourceAdapter(DefaultPickerEntrySourceAdapter):
1004 def getPickerEntries(self, term_values, context_object, **kwarg):
1005 """See `IPickerEntrySource`"""
1006 entries = (
1007- super(BranchPickerEntrySourceAdapter, self)
1008- .getPickerEntries(term_values, context_object, **kwarg))
1009+ super().getPickerEntries(term_values, context_object, **kwarg))
1010 for branch, picker_entry in izip(term_values, entries):
1011 picker_entry.description = branch.bzr_identity
1012 return entries
1013@@ -241,8 +238,7 @@ class TargetPickerEntrySourceAdapter(DefaultPickerEntrySourceAdapter):
1014 def getPickerEntries(self, term_values, context_object, **kwarg):
1015 """See `IPickerEntrySource`"""
1016 entries = (
1017- super(TargetPickerEntrySourceAdapter, self)
1018- .getPickerEntries(term_values, context_object, **kwarg))
1019+ super().getPickerEntries(term_values, context_object, **kwarg))
1020 for target, picker_entry in izip(term_values, entries):
1021 picker_entry.description = self.getDescription(target)
1022 picker_entry.details = []
1023@@ -284,8 +280,7 @@ class SourcePackageNamePickerEntrySourceAdapter(
1024 def getPickerEntries(self, term_values, context_object, **kwarg):
1025 """See `IPickerEntrySource`"""
1026 entries = (
1027- super(SourcePackageNamePickerEntrySourceAdapter, self)
1028- .getPickerEntries(term_values, context_object, **kwarg))
1029+ super().getPickerEntries(term_values, context_object, **kwarg))
1030 for sourcepackagename, picker_entry in izip(term_values, entries):
1031 descriptions = getSourcePackageDescriptions([sourcepackagename])
1032 picker_entry.description = descriptions.get(
1033@@ -313,7 +308,7 @@ class DistributionSourcePackagePickerEntrySourceAdapter(
1034 return description
1035
1036 def getPickerEntries(self, term_values, context_object, **kwarg):
1037- this = super(DistributionSourcePackagePickerEntrySourceAdapter, self)
1038+ this = super()
1039 entries = this.getPickerEntries(term_values, context_object, **kwarg)
1040 for picker_entry in entries:
1041 picker_entry.alt_title = None
1042@@ -384,8 +379,7 @@ class ArchivePickerEntrySourceAdapter(DefaultPickerEntrySourceAdapter):
1043 def getPickerEntries(self, term_values, context_object, **kwarg):
1044 """See `IPickerEntrySource`"""
1045 entries = (
1046- super(ArchivePickerEntrySourceAdapter, self)
1047- .getPickerEntries(term_values, context_object, **kwarg))
1048+ super().getPickerEntries(term_values, context_object, **kwarg))
1049 for archive, picker_entry in izip(term_values, entries):
1050 picker_entry.description = archive.reference
1051 return entries
1052diff --git a/lib/lp/app/model/launchpad.py b/lib/lp/app/model/launchpad.py
1053index 22156c3..b03359d 100644
1054--- a/lib/lp/app/model/launchpad.py
1055+++ b/lib/lp/app/model/launchpad.py
1056@@ -37,7 +37,7 @@ class ExceptionPrivacy(Privacy):
1057 private = True
1058 else:
1059 private = False
1060- super(ExceptionPrivacy, self).__init__(error, private)
1061+ super().__init__(error, private)
1062
1063
1064 class InformationTypeMixin:
1065diff --git a/lib/lp/app/security.py b/lib/lp/app/security.py
1066index e3fb8b1..2500a80 100644
1067--- a/lib/lp/app/security.py
1068+++ b/lib/lp/app/security.py
1069@@ -129,7 +129,7 @@ class non_boolean_izip(izip):
1070 class DelegatedAuthorization(AuthorizationBase):
1071
1072 def __init__(self, obj, forwarded_object=None, permission=None):
1073- super(DelegatedAuthorization, self).__init__(obj)
1074+ super().__init__(obj)
1075 self.forwarded_object = forwarded_object
1076 if permission is not None:
1077 self.permission = permission
1078diff --git a/lib/lp/app/services.py b/lib/lp/app/services.py
1079index 154d44f..f18ccad 100644
1080--- a/lib/lp/app/services.py
1081+++ b/lib/lp/app/services.py
1082@@ -26,7 +26,7 @@ class ServiceFactory(Navigation):
1083 """
1084
1085 def __init__(self):
1086- super(ServiceFactory, self).__init__(None)
1087+ super().__init__(None)
1088
1089 def traverse(self, name):
1090 return self.getService(name)
1091diff --git a/lib/lp/app/tests/test_security.py b/lib/lp/app/tests/test_security.py
1092index febb5b9..cd8ecce 100644
1093--- a/lib/lp/app/tests/test_security.py
1094+++ b/lib/lp/app/tests/test_security.py
1095@@ -59,7 +59,7 @@ def registerFakeSecurityAdapter(interface, permission, adapter=None):
1096 class FakeSecurityAdapter(AuthorizationBase):
1097
1098 def __init__(self, adaptee=None):
1099- super(FakeSecurityAdapter, self).__init__(adaptee)
1100+ super().__init__(adaptee)
1101 self.checkAuthenticated = FakeMethod()
1102 self.checkUnauthenticated = FakeMethod()
1103
1104diff --git a/lib/lp/app/tests/test_tales.py b/lib/lp/app/tests/test_tales.py
1105index ffd5f91..d238e2b 100644
1106--- a/lib/lp/app/tests/test_tales.py
1107+++ b/lib/lp/app/tests/test_tales.py
1108@@ -162,7 +162,7 @@ class TestTeamFormatterAPI(TestCaseWithFactory):
1109 layer = LaunchpadFunctionalLayer
1110
1111 def setUp(self):
1112- super(TestTeamFormatterAPI, self).setUp()
1113+ super().setUp()
1114 icon = self.factory.makeLibraryFileAlias(
1115 filename='smurf.png', content_type='image/png')
1116 self.team = self.factory.makeTeam(
1117@@ -227,7 +227,7 @@ class TestTeamFormatterAPI(TestCaseWithFactory):
1118
1119 def test_can_view_link(self):
1120 self._test_can_view_attribute(
1121- 'link', u'<span class="sprite team">&lt;hidden&gt;</span>')
1122+ 'link', '<span class="sprite team">&lt;hidden&gt;</span>')
1123
1124 def test_can_view_api_url(self):
1125 self._test_can_view_attribute('api_url')
1126@@ -396,7 +396,7 @@ class TestIRCNicknameFormatterAPI(TestCaseWithFactory):
1127 expected_html = test_tales(
1128 'nick/fmt:formatted_displayname', nick=ircID)
1129 self.assertEqual(
1130- u'<strong>fred</strong>\n'
1131+ '<strong>fred</strong>\n'
1132 '<span class="lesser"> on </span>\n'
1133 '<strong>&lt;b&gt;irc.canonical.com&lt;/b&gt;</strong>\n',
1134 expected_html)
1135diff --git a/lib/lp/app/utilities/celebrities.py b/lib/lp/app/utilities/celebrities.py
1136index 99a7f70..b26d830 100644
1137--- a/lib/lp/app/utilities/celebrities.py
1138+++ b/lib/lp/app/utilities/celebrities.py
1139@@ -104,7 +104,7 @@ class PersonCelebrityDescriptor(CelebrityDescriptor):
1140
1141 def __init__(self, name):
1142 PersonCelebrityDescriptor.names.add(name)
1143- super(PersonCelebrityDescriptor, self).__init__(IPersonSet, name)
1144+ super().__init__(IPersonSet, name)
1145
1146
1147 class LanguageCelebrityDescriptor(CelebrityDescriptor):
1148diff --git a/lib/lp/app/validators/attachment.py b/lib/lp/app/validators/attachment.py
1149index 404790f..566ab85 100644
1150--- a/lib/lp/app/validators/attachment.py
1151+++ b/lib/lp/app/validators/attachment.py
1152@@ -17,9 +17,9 @@ def attachment_size_constraint(value):
1153 size = len(value)
1154 max_size = config.launchpad.max_attachment_size
1155 if size == 0:
1156- raise LaunchpadValidationError(u'Cannot upload empty file.')
1157+ raise LaunchpadValidationError('Cannot upload empty file.')
1158 elif max_size > 0 and size > max_size:
1159 raise LaunchpadValidationError(
1160- u'Cannot upload files larger than %i bytes' % max_size)
1161+ 'Cannot upload files larger than %i bytes' % max_size)
1162 else:
1163 return True
1164diff --git a/lib/lp/app/vocabularies.py b/lib/lp/app/vocabularies.py
1165index a6e7a87..33eea4a 100644
1166--- a/lib/lp/app/vocabularies.py
1167+++ b/lib/lp/app/vocabularies.py
1168@@ -44,4 +44,4 @@ class InformationTypeVocabulary(SimpleVocabulary):
1169 term.name = type.name
1170 term.description = type.description
1171 terms.append(term)
1172- super(InformationTypeVocabulary, self).__init__(terms)
1173+ super().__init__(terms)
1174diff --git a/lib/lp/app/webservice/tests/test_marshallers.py b/lib/lp/app/webservice/tests/test_marshallers.py
1175index 1a6ce21..96b305f 100644
1176--- a/lib/lp/app/webservice/tests/test_marshallers.py
1177+++ b/lib/lp/app/webservice/tests/test_marshallers.py
1178@@ -48,15 +48,15 @@ class TestTextFieldMarshaller(TestCaseWithFactory):
1179 def test_unmarshall_obfuscated(self):
1180 # Data is obfuscated if the user is anonynous.
1181 marshaller = TextFieldMarshaller(None, WebServiceTestRequest())
1182- result = marshaller.unmarshall(None, u"foo@example.com")
1183- self.assertEqual(u"<email address hidden>", result)
1184+ result = marshaller.unmarshall(None, "foo@example.com")
1185+ self.assertEqual("<email address hidden>", result)
1186
1187 def test_unmarshall_not_obfuscated(self):
1188 # Data is not obfuscated if the user is authenticated.
1189 marshaller = TextFieldMarshaller(None, WebServiceTestRequest())
1190 with person_logged_in(self.factory.makePerson()):
1191- result = marshaller.unmarshall(None, u"foo@example.com")
1192- self.assertEqual(u"foo@example.com", result)
1193+ result = marshaller.unmarshall(None, "foo@example.com")
1194+ self.assertEqual("foo@example.com", result)
1195
1196
1197 class TestWebServiceObfuscation(TestCaseWithFactory):
1198diff --git a/lib/lp/app/widgets/date.py b/lib/lp/app/widgets/date.py
1199index 7d9a780..0f9c52d 100644
1200--- a/lib/lp/app/widgets/date.py
1201+++ b/lib/lp/app/widgets/date.py
1202@@ -128,7 +128,7 @@ class DateTimeWidget(TextWidget):
1203 __call__ = ViewPageTemplateFile('templates/datetime.pt')
1204
1205 def __init__(self, context, request):
1206- super(DateTimeWidget, self).__init__(context, request)
1207+ super().__init__(context, request)
1208 launchbag = getUtility(ILaunchBag)
1209 self.system_time_zone = launchbag.time_zone
1210
1211@@ -313,7 +313,7 @@ class DateTimeWidget(TextWidget):
1212
1213 def getInputValue(self):
1214 """Return the date, if it is in the allowed date range."""
1215- value = super(DateTimeWidget, self).getInputValue()
1216+ value = super().getInputValue()
1217 if value is None:
1218 return None
1219 # Establish if the value is within the date range.
1220@@ -613,6 +613,6 @@ class DatetimeDisplayWidget(DisplayWidget):
1221 else:
1222 value = self.context.default
1223 if value == self.context.missing_value:
1224- return u""
1225+ return ""
1226 value = value.astimezone(time_zone)
1227 return html_escape(value.strftime("%Y-%m-%d %H:%M:%S %Z"))
1228diff --git a/lib/lp/app/widgets/exception.py b/lib/lp/app/widgets/exception.py
1229index a7c68a7..e91f643 100644
1230--- a/lib/lp/app/widgets/exception.py
1231+++ b/lib/lp/app/widgets/exception.py
1232@@ -36,7 +36,7 @@ class WidgetInputError(_WidgetInputError):
1233
1234
1235 @implementer(IWidgetInputErrorView)
1236-class WidgetInputErrorView(object):
1237+class WidgetInputErrorView:
1238 """Rendering of IWidgetInputError"""
1239
1240 def __init__(self, context, request):
1241diff --git a/lib/lp/app/widgets/itemswidgets.py b/lib/lp/app/widgets/itemswidgets.py
1242index 571f65f..c3142ba 100644
1243--- a/lib/lp/app/widgets/itemswidgets.py
1244+++ b/lib/lp/app/widgets/itemswidgets.py
1245@@ -38,7 +38,7 @@ class LaunchpadDropdownWidget(DropdownWidget):
1246 class PlainMultiCheckBoxWidget(MultiCheckBoxWidget):
1247 """MultiCheckBoxWidget that copes with CustomWidgetFactory."""
1248
1249- _joinButtonToMessageTemplate = u'%s&nbsp;%s '
1250+ _joinButtonToMessageTemplate = '%s&nbsp;%s '
1251
1252 def __init__(self, field, vocabulary, request):
1253 # XXX flacoste 2006-07-23 Workaround Zope3 bug #545:
1254@@ -70,7 +70,7 @@ class PlainMultiCheckBoxWidget(MultiCheckBoxWidget):
1255 text = html_escape(text)
1256 id = '%s.%s' % (name, index)
1257 element = renderElement(
1258- u'input', value=value, name=name, id=id,
1259+ 'input', value=value, name=name, id=id,
1260 cssClass=cssClass, type='checkbox', **kw)
1261 return self._joinButtonToMessageTemplate % (element, text)
1262
1263@@ -81,7 +81,7 @@ class LabeledMultiCheckBoxWidget(PlainMultiCheckBoxWidget):
1264 """
1265
1266 _joinButtonToMessageTemplate = (
1267- u'<label for="%s" style="font-weight: normal">%s&nbsp;%s</label> ')
1268+ '<label for="%s" style="font-weight: normal">%s&nbsp;%s</label> ')
1269
1270 def _renderItem(self, index, text, value, name, cssClass, checked=False):
1271 """Render a checkbox and text in a label with a style attribute."""
1272@@ -93,7 +93,7 @@ class LabeledMultiCheckBoxWidget(PlainMultiCheckBoxWidget):
1273 value = html_escape(value)
1274 text = html_escape(text)
1275 id = '%s.%s' % (name, index)
1276- elem = renderElement(u'input',
1277+ elem = renderElement('input',
1278 value=value,
1279 name=name,
1280 id=id,
1281@@ -119,7 +119,7 @@ class LaunchpadRadioWidget(RadioWidget):
1282 value = html_escape(value)
1283 text = html_escape(text)
1284 id = '%s.%s' % (name, index)
1285- elem = renderElement(u'input',
1286+ elem = renderElement('input',
1287 value=value,
1288 name=name,
1289 id=id,
1290@@ -129,7 +129,7 @@ class LaunchpadRadioWidget(RadioWidget):
1291 if '<label' in text:
1292 return '%s&nbsp;%s' % (elem, text)
1293 else:
1294- return renderElement(u'label',
1295+ return renderElement('label',
1296 contents='%s&nbsp;%s' % (elem, text),
1297 **{'style': 'font-weight: normal'})
1298
1299@@ -145,27 +145,26 @@ class LaunchpadRadioWidgetWithDescription(LaunchpadRadioWidget):
1300 """
1301
1302 _labelWithDescriptionTemplate = (
1303- u'''<tr>
1304- <td rowspan="2">%s</td>
1305- <td><label for="%s">%s</label></td>
1306- </tr>
1307- <tr>
1308- <td class="formHelp">%s</td>
1309- </tr>
1310- ''')
1311+ '''<tr>
1312+ <td rowspan="2">%s</td>
1313+ <td><label for="%s">%s</label></td>
1314+ </tr>
1315+ <tr>
1316+ <td class="formHelp">%s</td>
1317+ </tr>
1318+ ''')
1319 _labelWithoutDescriptionTemplate = (
1320- u'''<tr>
1321- <td>%s</td>
1322- <td><label for="%s">%s</label></td>
1323- </tr>
1324- ''')
1325+ '''<tr>
1326+ <td>%s</td>
1327+ <td><label for="%s">%s</label></td>
1328+ </tr>
1329+ ''')
1330
1331 def __init__(self, field, vocabulary, request):
1332 """Initialize the widget."""
1333 assert IEnumeratedType.providedBy(vocabulary), (
1334 'The vocabulary must implement IEnumeratedType')
1335- super(LaunchpadRadioWidgetWithDescription, self).__init__(
1336- field, vocabulary, request)
1337+ super().__init__(field, vocabulary, request)
1338 self.extra_hint = None
1339 self.extra_hint_class = None
1340
1341@@ -187,7 +186,7 @@ class LaunchpadRadioWidgetWithDescription(LaunchpadRadioWidget):
1342 """Render an item of the list."""
1343 text = html_escape(text)
1344 id = '%s.%s' % (name, index)
1345- elem = renderElement(u'input',
1346+ elem = renderElement('input',
1347 value=value,
1348 name=name,
1349 id=id,
1350@@ -199,7 +198,7 @@ class LaunchpadRadioWidgetWithDescription(LaunchpadRadioWidget):
1351 """Render a selected item of the list."""
1352 text = html_escape(text)
1353 id = '%s.%s' % (name, index)
1354- elem = renderElement(u'input',
1355+ elem = renderElement('input',
1356 value=value,
1357 name=name,
1358 id=id,
1359@@ -245,8 +244,7 @@ class LaunchpadBooleanRadioWidget(LaunchpadRadioWidget):
1360 """Initialize the widget."""
1361 vocabulary = SimpleVocabulary.fromItems(
1362 ((self.TRUE, True), (self.FALSE, False)))
1363- super(LaunchpadBooleanRadioWidget, self).__init__(
1364- field, vocabulary, request)
1365+ super().__init__(field, vocabulary, request)
1366 # Suppress the missing value behaviour; this is a boolean field.
1367 self.required = True
1368 self._displayItemForMissingValue = False
1369@@ -261,7 +259,7 @@ class LaunchpadBooleanRadioWidget(LaunchpadRadioWidget):
1370 else:
1371 # value == self.FALSE.
1372 text = self.false_label
1373- return super(LaunchpadBooleanRadioWidget, self)._renderItem(
1374+ return super()._renderItem(
1375 index, text, value, name, cssClass, checked=checked)
1376
1377
1378diff --git a/lib/lp/app/widgets/launchpadtarget.py b/lib/lp/app/widgets/launchpadtarget.py
1379index 57d684e..3a9b805 100644
1380--- a/lib/lp/app/widgets/launchpadtarget.py
1381+++ b/lib/lp/app/widgets/launchpadtarget.py
1382@@ -66,14 +66,14 @@ class LaunchpadTargetWidget(BrowserWidget, InputWidget):
1383 return
1384 fields = [
1385 Choice(
1386- __name__='product', title=u'Project',
1387+ __name__='product', title='Project',
1388 required=True, vocabulary=self.getProductVocabulary()),
1389 Choice(
1390- __name__='distribution', title=u"Distribution",
1391+ __name__='distribution', title="Distribution",
1392 required=True, vocabulary=self.getDistributionVocabulary(),
1393 default=getUtility(ILaunchpadCelebrities).ubuntu),
1394 Choice(
1395- __name__='package', title=u"Package",
1396+ __name__='package', title="Package",
1397 required=False, vocabulary=self.getPackageVocabularyName()),
1398 ]
1399 self.distribution_widget = CustomWidgetFactory(
1400diff --git a/lib/lp/app/widgets/owner.py b/lib/lp/app/widgets/owner.py
1401index e9fccae..477f98c 100644
1402--- a/lib/lp/app/widgets/owner.py
1403+++ b/lib/lp/app/widgets/owner.py
1404@@ -15,7 +15,7 @@ from lp.services.webapp.interfaces import ILaunchBag
1405
1406
1407 @implementer(IInputWidget, IBrowserWidget)
1408-class RequestWidget(object):
1409+class RequestWidget:
1410 '''A widget that sets itself to a value calculated from request
1411
1412 This is a bit of a hack, but necessary. If we are using the Zope
1413diff --git a/lib/lp/app/widgets/popup.py b/lib/lp/app/widgets/popup.py
1414index d5e4ec5..34264ae 100644
1415--- a/lib/lp/app/widgets/popup.py
1416+++ b/lib/lp/app/widgets/popup.py
1417@@ -15,7 +15,6 @@ __all__ = [
1418
1419 from lazr.restful.utils import safe_hasattr
1420 import simplejson
1421-import six
1422 from zope.browserpage import ViewPageTemplateFile
1423 from zope.component import getUtility
1424 from zope.formlib.interfaces import ConversionError
1425@@ -77,14 +76,14 @@ class VocabularyPickerWidget(SingleDataHelper, ItemsWidgetBase):
1426 user currently has entered in the form.
1427 """
1428 # Pull form value using the parent class to avoid loop
1429- formValue = super(VocabularyPickerWidget, self)._getFormInput()
1430+ formValue = super()._getFormInput()
1431 if not formValue:
1432 return []
1433
1434 vocab = self.vocabulary
1435 # Special case - if the entered value is valid, it is an object
1436 # rather than a string (I think this is a bug somewhere)
1437- if not isinstance(formValue, six.string_types):
1438+ if not isinstance(formValue, str):
1439 return [vocab.getTerm(formValue)]
1440
1441 search_results = vocab.searchForTerms(formValue)
1442@@ -101,7 +100,7 @@ class VocabularyPickerWidget(SingleDataHelper, ItemsWidgetBase):
1443 val = self._getFormValue()
1444
1445 # We have a valid object - return the corresponding token
1446- if not isinstance(val, six.string_types):
1447+ if not isinstance(val, str):
1448 return self.vocabulary.getTerm(val).token
1449
1450 # Just return the existing invalid token
1451@@ -323,8 +322,7 @@ class SourcePackageNameWidgetBase(DistributionSourcePackagePickerWidget):
1452 distribution_id = ''
1453
1454 def __init__(self, field, vocabulary, request):
1455- super(SourcePackageNameWidgetBase, self).__init__(
1456- field, vocabulary, request)
1457+ super().__init__(field, vocabulary, request)
1458 self.cached_values = {}
1459 if bool(getFeatureFlag('disclosure.dsp_picker.enabled')):
1460 # The distribution may change later when we process form input,
1461diff --git a/lib/lp/app/widgets/product.py b/lib/lp/app/widgets/product.py
1462index 86e1d91..1b7ca66 100644
1463--- a/lib/lp/app/widgets/product.py
1464+++ b/lib/lp/app/widgets/product.py
1465@@ -57,7 +57,7 @@ from lp.services.webapp.vhosts import allvhosts
1466 class ProductBugTrackerWidget(LaunchpadRadioWidget):
1467 """Widget for selecting a product bug tracker."""
1468
1469- _joinButtonToMessageTemplate = u'%s&nbsp;%s'
1470+ _joinButtonToMessageTemplate = '%s&nbsp;%s'
1471 template = ViewPageTemplateFile('templates/product-bug-tracker.pt')
1472
1473 def __init__(self, field, vocabulary, request):
1474@@ -101,7 +101,7 @@ class ProductBugTrackerWidget(LaunchpadRadioWidget):
1475 if checked:
1476 kw['checked'] = 'checked'
1477 id = '%s.%s' % (name, index)
1478- elem = renderElement(u'input',
1479+ elem = renderElement('input',
1480 value=value,
1481 name=name,
1482 id=id,
1483@@ -140,13 +140,12 @@ class ProductBugTrackerWidget(LaunchpadRadioWidget):
1484 def _renderLabel(self, text, index):
1485 """Render a label for the option with the specified index."""
1486 option_id = '%s.%s' % (self.name, index)
1487- return u'<label for="%s" style="font-weight: normal">%s</label>' % (
1488+ return '<label for="%s" style="font-weight: normal">%s</label>' % (
1489 option_id, text)
1490
1491 def error(self):
1492 """Concatenate errors from this widget and sub-widgets."""
1493- errors = [super(ProductBugTrackerWidget, self).error(),
1494- self.upstream_email_address_widget.error()]
1495+ errors = [super().error(), self.upstream_email_address_widget.error()]
1496 return '; '.join(err for err in errors if len(err) > 0)
1497
1498 def renderItems(self, value):
1499@@ -309,7 +308,7 @@ class LicenseWidget(CheckBoxMatrixWidget):
1500 items_by_category = None
1501
1502 def __init__(self, field, vocabulary, request):
1503- super(LicenseWidget, self).__init__(field, vocabulary, request)
1504+ super().__init__(field, vocabulary, request)
1505 # We want to put the license_info widget inside the licences widget's
1506 # HTML, for better alignment and JavaScript dynamism. This is
1507 # accomplished by ghosting the form's license_info widget (see
1508@@ -344,7 +343,7 @@ class LicenseWidget(CheckBoxMatrixWidget):
1509 # This will return just the DBItem's text. We want to wrap that text
1510 # in the URL to the licence, which is stored in the DBItem's
1511 # description.
1512- value = super(LicenseWidget, self).textForValue(term)
1513+ value = super().textForValue(term)
1514 if term.value.url is None:
1515 return value
1516 else:
1517@@ -355,14 +354,13 @@ class LicenseWidget(CheckBoxMatrixWidget):
1518
1519 def renderItem(self, index, text, value, name, cssClass):
1520 """See `ItemsEditWidgetBase`."""
1521- rendered = super(LicenseWidget, self).renderItem(
1522- index, text, value, name, cssClass)
1523+ rendered = super().renderItem(index, text, value, name, cssClass)
1524 self._categorize(value, rendered)
1525 return rendered
1526
1527 def renderSelectedItem(self, index, text, value, name, cssClass):
1528 """See `ItemsEditWidgetBase`."""
1529- rendered = super(LicenseWidget, self).renderSelectedItem(
1530+ rendered = super().renderSelectedItem(
1531 index, text, value, name, cssClass)
1532 category = self._categorize(value, rendered)
1533 # Increment the category counter. This is used by the template to
1534@@ -390,7 +388,7 @@ class LicenseWidget(CheckBoxMatrixWidget):
1535 # individual checkbox items. We don't actually care about the return
1536 # value though since we'll be building up our checkbox tables
1537 # manually.
1538- super(LicenseWidget, self).__call__()
1539+ super().__call__()
1540 self.recommended = self._renderTable('recommended', 3)
1541 self.more = self._renderTable('more', 3)
1542 self.deprecated = self._renderTable('deprecated')
1543diff --git a/lib/lp/app/widgets/project.py b/lib/lp/app/widgets/project.py
1544index 4e5b796..151b15c 100644
1545--- a/lib/lp/app/widgets/project.py
1546+++ b/lib/lp/app/widgets/project.py
1547@@ -33,7 +33,7 @@ class ProjectScopeWidget(BrowserWidget, InputWidget):
1548 _error = None
1549
1550 def __init__(self, field, vocabulary, request):
1551- super(ProjectScopeWidget, self).__init__(field, request)
1552+ super().__init__(field, request)
1553
1554 # We copy the title, description and vocabulary from the main
1555 # field since it determines the valid target types.
1556@@ -143,4 +143,4 @@ class ProjectScopeWidget(BrowserWidget, InputWidget):
1557 if self._error:
1558 return self._error.doc()
1559 else:
1560- return u""
1561+ return ""
1562diff --git a/lib/lp/app/widgets/suggestion.py b/lib/lp/app/widgets/suggestion.py
1563index 6ce8e46..294489e 100644
1564--- a/lib/lp/app/widgets/suggestion.py
1565+++ b/lib/lp/app/widgets/suggestion.py
1566@@ -141,7 +141,7 @@ class SuggestionWidget(LaunchpadRadioWidget):
1567 def _renderLabel(self, text, index):
1568 """Render a label for the option with the specified index."""
1569 label = structured(
1570- u'<label for="%s" style="font-weight: normal">%s</label>',
1571+ '<label for="%s" style="font-weight: normal">%s</label>',
1572 self._optionId(index), text)
1573 return label
1574
1575@@ -183,7 +183,7 @@ class SuggestionWidget(LaunchpadRadioWidget):
1576 other_selection_onclick = (
1577 "this.form['%s'].focus()" % self.other_selection_widget.name)
1578
1579- elem = renderElement(u'input',
1580+ elem = renderElement('input',
1581 value="other",
1582 name=self.name,
1583 id='%s.%s' % (self.name, index),
1584@@ -264,13 +264,13 @@ class TargetBranchWidget(SuggestionWidget):
1585 # radio buttons that is not a hyperlink in order to select the radio
1586 # button. It was decided not to have the entire text as a link, but
1587 # instead to have a separate link to the branch details.
1588- text = u'%s (<a href="%s">branch details</a>)'
1589+ text = '%s (<a href="%s">branch details</a>)'
1590 # If the branch is the development focus, say so.
1591 if branch == self.context.context.target.default_merge_target:
1592- text += u"&ndash; <em>development focus</em>"
1593+ text += "&ndash; <em>development focus</em>"
1594 label = (
1595- u'<label for="%s" style="font-weight: normal">' + text +
1596- u'</label>')
1597+ '<label for="%s" style="font-weight: normal">' + text +
1598+ '</label>')
1599 return structured(
1600 label, self._optionId(index), branch.displayname,
1601 canonical_url(branch))
1602@@ -345,17 +345,17 @@ class TargetGitRepositoryWidget(SuggestionWidget):
1603 # radio buttons that is not a hyperlink in order to select the radio
1604 # button. It was decided not to have the entire text as a link, but
1605 # instead to have a separate link to the repository details.
1606- text = u'%s (<a href="%s">repository details</a>)'
1607+ text = '%s (<a href="%s">repository details</a>)'
1608 # If the repository is the default for the target, say so.
1609 if not IPerson.providedBy(repository.target):
1610 repository_set = getUtility(IGitRepositorySet)
1611 default_target = repository_set.getDefaultRepository(
1612 repository.target)
1613 if repository == default_target:
1614- text += u"&ndash; <em>default repository</em>"
1615+ text += "&ndash; <em>default repository</em>"
1616 label = (
1617- u'<label for="%s" style="font-weight: normal">' + text +
1618- u'</label>')
1619+ '<label for="%s" style="font-weight: normal">' + text +
1620+ '</label>')
1621 return structured(
1622 label, self._optionId(index), repository.display_name,
1623 canonical_url(repository))
1624diff --git a/lib/lp/app/widgets/tests/test_datetime.py b/lib/lp/app/widgets/tests/test_datetime.py
1625index fb1f8df..c748e93 100644
1626--- a/lib/lp/app/widgets/tests/test_datetime.py
1627+++ b/lib/lp/app/widgets/tests/test_datetime.py
1628@@ -17,8 +17,8 @@ class TestDateTimeWidget(TestCase):
1629 layer = DatabaseFunctionalLayer
1630
1631 def setUp(self):
1632- super(TestDateTimeWidget, self).setUp()
1633- field = Field(__name__='foo', title=u'Foo')
1634+ super().setUp()
1635+ field = Field(__name__='foo', title='Foo')
1636 request = LaunchpadTestRequest()
1637 self.widget = DateTimeWidget(field, request)
1638
1639diff --git a/lib/lp/app/widgets/tests/test_itemswidgets.py b/lib/lp/app/widgets/tests/test_itemswidgets.py
1640index 51b31e2..903611b 100644
1641--- a/lib/lp/app/widgets/tests/test_itemswidgets.py
1642+++ b/lib/lp/app/widgets/tests/test_itemswidgets.py
1643@@ -49,7 +49,7 @@ class ItemWidgetTestCase(TestCaseWithFactory):
1644 UNSAFE_TERM = SimpleTerm('object-2', 'token-2', '<unsafe> &nbsp; title')
1645
1646 def setUp(self):
1647- super(ItemWidgetTestCase, self).setUp()
1648+ super().setUp()
1649 self.request = LaunchpadTestRequest()
1650 self.vocabulary = SimpleVocabulary([self.SAFE_TERM, self.UNSAFE_TERM])
1651 field = Choice(__name__='test_field', vocabulary=self.vocabulary)
1652@@ -186,7 +186,7 @@ class TestLaunchpadRadioWidgetWithDescription(TestCaseWithFactory):
1653 UNSAFE_TERM = Item('item-<2>', description='<unsafe> &nbsp; title')
1654
1655 def setUp(self):
1656- super(TestLaunchpadRadioWidgetWithDescription, self).setUp()
1657+ super().setUp()
1658 self.request = LaunchpadTestRequest()
1659 field = Choice(__name__='test_field', vocabulary=self.TestEnum)
1660 self.field = field.bind(object())
1661diff --git a/lib/lp/app/widgets/tests/test_launchpadtarget.py b/lib/lp/app/widgets/tests/test_launchpadtarget.py
1662index 5deb272..e94e145 100644
1663--- a/lib/lp/app/widgets/tests/test_launchpadtarget.py
1664+++ b/lib/lp/app/widgets/tests/test_launchpadtarget.py
1665@@ -59,15 +59,14 @@ class LaunchpadTargetWidgetTestCase(TestCaseWithFactory):
1666 }
1667
1668 def setUp(self):
1669- super(LaunchpadTargetWidgetTestCase, self).setUp()
1670+ super().setUp()
1671 self.distribution = self.factory.makeDistribution(name='fnord')
1672 distroseries = self.factory.makeDistroSeries(
1673 distribution=self.distribution)
1674 self.package = self.factory.makeDSPCache(
1675 distroseries=distroseries, sourcepackagename='snarf')
1676 self.project = self.factory.makeProduct('pting')
1677- field = Reference(
1678- __name__='target', schema=Interface, title=u'target')
1679+ field = Reference(__name__='target', schema=Interface, title='target')
1680 field = field.bind(Thing())
1681 request = LaunchpadTestRequest()
1682 self.widget = LaunchpadTargetWidget(field, request)
1683@@ -129,7 +128,7 @@ class LaunchpadTargetWidgetTestCase(TestCaseWithFactory):
1684 def test_setUpSubWidgets_dsp_picker_feature_flag(self):
1685 # The DistributionSourcePackageVocabulary is used when the
1686 # disclosure.dsp_picker.enabled is true.
1687- with FeatureFixture({u"disclosure.dsp_picker.enabled": u"on"}):
1688+ with FeatureFixture({"disclosure.dsp_picker.enabled": "on"}):
1689 self.widget.setUpSubWidgets()
1690 self.assertIsInstance(
1691 self.widget.package_widget.context.vocabulary,
1692@@ -200,7 +199,7 @@ class LaunchpadTargetWidgetTestCase(TestCaseWithFactory):
1693 # The field value is the package when the package radio button
1694 # is selected and the package sub field has valid input.
1695 self.widget.request = LaunchpadTestRequest(form=self.form)
1696- with FeatureFixture({u"disclosure.dsp_picker.enabled": u"on"}):
1697+ with FeatureFixture({"disclosure.dsp_picker.enabled": "on"}):
1698 self.widget.setUpSubWidgets()
1699 self.assertEqual(self.package, self.widget.getInputValue())
1700
1701diff --git a/lib/lp/app/widgets/tests/test_popup.py b/lib/lp/app/widgets/tests/test_popup.py
1702index 5627eaa..3ea8bb0 100644
1703--- a/lib/lp/app/widgets/tests/test_popup.py
1704+++ b/lib/lp/app/widgets/tests/test_popup.py
1705@@ -27,7 +27,7 @@ class TestMetaClass(InterfaceClass):
1706 "test_filtered.item": Choice(vocabulary='DistributionOrProduct'),
1707 "test_target": Choice(vocabulary='DistributionSourcePackage'),
1708 }
1709- super(TestMetaClass, self).__init__(
1710+ super().__init__(
1711 name, bases=bases, attrs=attrs, __doc__=__doc__,
1712 __module__=__module__)
1713
1714@@ -42,7 +42,7 @@ class TestVocabularyPickerWidget(TestCaseWithFactory):
1715 layer = DatabaseFunctionalLayer
1716
1717 def setUp(self):
1718- super(TestVocabularyPickerWidget, self).setUp()
1719+ super().setUp()
1720 self.context = self.factory.makeTeam()
1721 self.vocabulary_registry = getVocabularyRegistry()
1722 self.vocabulary = self.vocabulary_registry.get(
1723diff --git a/lib/lp/app/widgets/tests/test_suggestion.py b/lib/lp/app/widgets/tests/test_suggestion.py
1724index cb1b031..6139a9d 100644
1725--- a/lib/lp/app/widgets/tests/test_suggestion.py
1726+++ b/lib/lp/app/widgets/tests/test_suggestion.py
1727@@ -84,7 +84,7 @@ class TestSuggestionWidget(TestCaseWithFactory):
1728 self.other_selection_widget.onKeyPress = on_key_press
1729
1730 def setUp(self):
1731- super(TestSuggestionWidget, self).setUp()
1732+ super().setUp()
1733 request = LaunchpadTestRequest()
1734 vocabulary = SimpleHugeVocabulary(
1735 [self.SAFE_TERM, self.UNSAFE_TERM])
1736@@ -234,13 +234,13 @@ class TestTargetGitRepositoryWidget(TestCaseWithFactory):
1737 owner = self.factory.makePerson()
1738 this_source, this_target = self.factory.makeGitRefs(
1739 owner=owner, target=owner,
1740- paths=[u"refs/heads/source", u"refs/heads/target"])
1741+ paths=["refs/heads/source", "refs/heads/target"])
1742 bmp = self.factory.makeBranchMergeProposalForGit(
1743 source_ref=this_source, target_ref=this_target,
1744 date_created=datetime.now(utc) - timedelta(days=1))
1745 other_source, other_target = self.factory.makeGitRefs(
1746 owner=owner, target=owner,
1747- paths=[u"refs/heads/source", u"refs/heads/target"])
1748+ paths=["refs/heads/source", "refs/heads/target"])
1749 self.factory.makeBranchMergeProposalForGit(
1750 source_ref=other_source, target_ref=other_target,
1751 date_created=datetime.now(utc) - timedelta(days=1))
1752@@ -272,7 +272,7 @@ class TestTargetGitRepositoryWidget(TestCaseWithFactory):
1753 owner = self.factory.makePerson()
1754 source, target = self.factory.makeGitRefs(
1755 owner=owner, target=owner,
1756- paths=[u"refs/heads/source", u"refs/heads/target"])
1757+ paths=["refs/heads/source", "refs/heads/target"])
1758 bmp = self.factory.makeBranchMergeProposalForGit(
1759 source_ref=source, target_ref=target,
1760 date_created=datetime.now(utc) - timedelta(days=1))
1761diff --git a/lib/lp/app/widgets/textwidgets.py b/lib/lp/app/widgets/textwidgets.py
1762index f4dd12c..7657af2 100644
1763--- a/lib/lp/app/widgets/textwidgets.py
1764+++ b/lib/lp/app/widgets/textwidgets.py
1765@@ -3,7 +3,6 @@
1766
1767 import re
1768
1769-import six
1770 from zope.browserpage import ViewPageTemplateFile
1771 from zope.formlib.textwidgets import (
1772 TextAreaWidget,
1773@@ -42,14 +41,14 @@ class TokensTextWidget(StrippedTextWidget):
1774 else is replaced with a single space.
1775 """
1776 normalised_text = re.sub(r'[^\w-]+', ' ', input)
1777- return super(TokensTextWidget, self)._toFieldValue(normalised_text)
1778+ return super()._toFieldValue(normalised_text)
1779
1780
1781 class NoneableTextWidget(StrippedTextWidget):
1782 """A widget that that is None if it's value is empty or whitespace."""
1783
1784 def _toFieldValue(self, input):
1785- value = super(NoneableTextWidget, self)._toFieldValue(input)
1786+ value = super()._toFieldValue(input)
1787 if value == '':
1788 return None
1789 else:
1790@@ -63,13 +62,13 @@ class URIWidget(StrippedTextWidget):
1791 cssClass = 'urlTextType'
1792
1793 def __init__(self, field, request):
1794- super(URIWidget, self).__init__(field, request)
1795+ super().__init__(field, request)
1796 self.field = field
1797
1798 def _toFieldValue(self, input):
1799 if isinstance(input, list):
1800 raise UnexpectedFormData('Only a single value is expected')
1801- return super(URIWidget, self)._toFieldValue(input)
1802+ return super()._toFieldValue(input)
1803
1804
1805 class URIComponentWidget(LowerCaseTextWidget):
1806@@ -106,17 +105,17 @@ class DelimitedListWidget(TextAreaWidget):
1807
1808 def __init__(self, field, value_type, request):
1809 # We don't use value_type.
1810- super(DelimitedListWidget, self).__init__(field, request)
1811+ super().__init__(field, request)
1812
1813 # The default splitting function, which splits on
1814 # white-space. Subclasses can override this if different
1815 # delimiting rules are needed.
1816- split = staticmethod(six.text_type.split)
1817+ split = staticmethod(str.split)
1818
1819 # The default joining function, which simply separates each list
1820 # item with a newline. Subclasses can override this if different
1821 # delimiters are needed.
1822- join = staticmethod(u'\n'.join)
1823+ join = staticmethod('\n'.join)
1824
1825 def _toFormValue(self, value):
1826 """Converts a list to a newline separated string.
1827@@ -134,7 +133,7 @@ class DelimitedListWidget(TextAreaWidget):
1828 By default, lists are displayed one item on a line:
1829
1830 >>> names = ['fred', 'bob', 'harry']
1831- >>> six.ensure_str(widget._toFormValue(names))
1832+ >>> widget._toFormValue(names)
1833 'fred\\r\\nbob\\r\\nharry'
1834 """
1835 if value == self.context.missing_value:
1836@@ -143,7 +142,7 @@ class DelimitedListWidget(TextAreaWidget):
1837 value = self._missing
1838 else:
1839 value = self.join(value)
1840- return super(DelimitedListWidget, self)._toFormValue(value)
1841+ return super()._toFormValue(value)
1842
1843 def _toFieldValue(self, value):
1844 """Convert the input string into a list.
1845@@ -166,8 +165,7 @@ class DelimitedListWidget(TextAreaWidget):
1846 'bob'
1847 'harry'
1848 """
1849- value = super(
1850- DelimitedListWidget, self)._toFieldValue(value)
1851+ value = super()._toFieldValue(value)
1852 if value == self.context.missing_value:
1853 return value
1854 else:
1855@@ -195,8 +193,7 @@ class NoneableDescriptionWidget(DescriptionWidget):
1856 """A widget that is None if it's value is empty or whitespace.."""
1857
1858 def _toFieldValue(self, input):
1859- value = super(
1860- NoneableDescriptionWidget, self)._toFieldValue(input.strip())
1861+ value = super()._toFieldValue(input.strip())
1862 if value == '':
1863 return None
1864 else:

Subscribers

People subscribed via source and target branches

to status/vote changes: