diff --git a/pyproject.toml b/pyproject.toml index 5ace0a70e..202d1e37c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -66,6 +66,7 @@ select = [ "I001", # isort "N", # pep8-naming "B", # bugbear + "UP", # pyupgrade ] [tool.ruff.lint.isort] diff --git a/src/ccip/views.py b/src/ccip/views.py index 6fea89f7f..c11d4fd50 100644 --- a/src/ccip/views.py +++ b/src/ccip/views.py @@ -77,8 +77,7 @@ def __bool__(self): @property def speakers(self): - for s in self._speakers: - yield s + yield from self._speakers def _get_empty_event_info(event): diff --git a/src/core/context_processors.py b/src/core/context_processors.py index 5cff9e7d0..8c69cf1ff 100644 --- a/src/core/context_processors.py +++ b/src/core/context_processors.py @@ -9,7 +9,7 @@ def _build_google_form_url(uid): - return 'https://docs.google.com/forms/d/e/{uid}/viewform'.format(uid=uid) + return f'https://docs.google.com/forms/d/e/{uid}/viewform' def script_prefix(request): diff --git a/src/core/difftools.py b/src/core/difftools.py index a1e70a396..f3b2ca8d9 100644 --- a/src/core/difftools.py +++ b/src/core/difftools.py @@ -91,12 +91,12 @@ def dump_equal(a, b, loa, lob, hia, hib): def dump_delete(a, b, loa, lob, hia, hib): for line in a[loa:hia]: - yield mark_safe('{}'.format(conditional_escape(line))) + yield mark_safe(f'{conditional_escape(line)}') def dump_insert(a, b, loa, lob, hia, hib): for line in b[lob:hib]: - yield mark_safe('{}'.format(conditional_escape(line))) + yield mark_safe(f'{conditional_escape(line)}') BLOCK_HANDLERS = { diff --git a/src/core/models.py b/src/core/models.py index bbc833ab8..3e1f6267b 100644 --- a/src/core/models.py +++ b/src/core/models.py @@ -224,7 +224,7 @@ class Meta: def save(self, *args, **kwargs): if not self.key: self.key = self.generate_key() - return super(Token, self).save(*args, **kwargs) + return super().save(*args, **kwargs) @classmethod def generate_key(cls): diff --git a/src/core/tests/test_views.py b/src/core/tests/test_views.py index 4d808cb08..16f148970 100644 --- a/src/core/tests/test_views.py +++ b/src/core/tests/test_views.py @@ -89,7 +89,7 @@ def activate_default_language(): @pytest.fixture def content_page_full_path(language, content_page_path): - return '/{}{}'.format(language, content_page_path) + return f'/{language}{content_page_path}' def test_content_pages(client, parser, content_page_full_path): @@ -162,7 +162,7 @@ def get_link_safty_pair(tag): def get_error_message(): errors = [ - ' {0!r}'.format(*p) + f' {p[0]!r}' for p in link_noopener_pairs if p[1] is not True ] diff --git a/src/core/utils.py b/src/core/utils.py index c98936121..d9aef386d 100644 --- a/src/core/utils.py +++ b/src/core/utils.py @@ -81,7 +81,7 @@ def __init__(self, seq): self._seq = seq def __repr__(self): - return ''.format(seq=self._seq) + return f'' def __len__(self): return len(self._seq) diff --git a/src/events/admin.py b/src/events/admin.py index 3acb18aee..f5ba89006 100644 --- a/src/events/admin.py +++ b/src/events/admin.py @@ -35,13 +35,13 @@ class TimeRangeFilter(admin.SimpleListFilter): title = _('time value') parameter_name = 'time-range' day_queries = { - 'day{}'.format(i): Q(value__date=date) + f'day{i}': Q(value__date=date) for i, date in enumerate(settings.EVENTS_DAY_NAMES, 1) } def lookups(self, request, model_admin): return [ - ('day{}'.format(i), name) + (f'day{i}', name) for i, name in enumerate(settings.EVENTS_DAY_NAMES.values(), 1) ] @@ -84,13 +84,13 @@ def get_minute(self, instance): class EventTimeRangeFilter(admin.SimpleListFilter): filter_kwargs_dict = { - 'day{}'.format(i): day + f'day{i}': day for i, day in enumerate(settings.EVENTS_DAY_NAMES, 1) } def lookups(self, request, model_admin): return [ - ('day{}'.format(i), name) + (f'day{i}', name) for i, name in enumerate(settings.EVENTS_DAY_NAMES.values(), 1) ] diff --git a/src/events/models.py b/src/events/models.py index 110f1ae66..d5b0229d4 100644 --- a/src/events/models.py +++ b/src/events/models.py @@ -44,10 +44,7 @@ def select_storage(): def photo_upload_to(instance, filename): - return 'speaker/{speakername}/{filename}'.format( - speakername=slugify(instance.speaker_name, allow_unicode=True), - filename=filename, - ) + return f'speaker/{slugify(instance.speaker_name, allow_unicode=True)}/{filename}' class TimeManager(models.Manager): @@ -302,7 +299,7 @@ def __str__(self): def get_absolute_url(self): url = reverse('page', kwargs={'path': 'conference/keynotes'}) split = urllib.parse.urlsplit(url) - frag = 'keynote-speaker-{slug}'.format(slug=self.slug) + frag = f'keynote-speaker-{self.slug}' return urllib.parse.urlunsplit(split._replace(fragment=frag)) def get_static_data(self): diff --git a/src/events/renderers.py b/src/events/renderers.py index d5ad70e58..f72cce981 100644 --- a/src/events/renderers.py +++ b/src/events/renderers.py @@ -79,12 +79,12 @@ def render_sponsoredevent(e): def render_event(e): - func_name = 'render_{cls}'.format(cls=type(e).__name__.lower()) + func_name = f'render_{type(e).__name__.lower()}' try: func = globals()[func_name] except KeyError as err: raise ValueError( - 'No suitable renderer for {!r} of {!r}'.format(e, type(e)), + f'No suitable renderer for {e!r} of {type(e)!r}', ) from err return func(e) diff --git a/src/events/tests/renderers/test_render_block.py b/src/events/tests/renderers/test_render_block.py index dde9a322b..cfbacccdd 100644 --- a/src/events/tests/renderers/test_render_block.py +++ b/src/events/tests/renderers/test_render_block.py @@ -9,7 +9,7 @@ def simple_component_renderer(mocker): mocker.patch.multiple( 'events.renderers', render_event=str, - render_block_location=lambda v: '{} '.format(v), + render_block_location=lambda v: f'{v} ', ) @@ -108,9 +108,7 @@ def test_render_attached_period(utils, events, event_key, begin, end): rendered = renderers.render_attached_period(e.begin_time, e.end_time) assert utils.is_safe(rendered) assert rendered == ( - '
{} – {}
'.format( - begin, end, - ) + f'
{begin} – {end}
' ) diff --git a/src/events/tests/test_views.py b/src/events/tests/test_views.py index 42d4e3643..4eed11f08 100644 --- a/src/events/tests/test_views.py +++ b/src/events/tests/test_views.py @@ -17,7 +17,7 @@ def test_talk_list(client, accepted_talk_proposal, sponsored_block_event): ) @pytest.mark.parametrize('pk,status', [(42, 200), (9, 404)]) def test_talk_detail(client, accepted_talk_proposal, pk, status): - r = client.get('/en-us/events/talk/{pk}/'.format(pk=pk)) + r = client.get(f'/en-us/events/talk/{pk}/') assert r.status_code == status @@ -30,5 +30,5 @@ def test_talk_detail(client, accepted_talk_proposal, pk, status): ('carmona-eugene', 404), ]) def test_sponsored_event_detail(client, sponsored_block_event, slug, status): - r = client.get('/en-us/events/talk/sponsored/{slug}/'.format(slug=slug)) + r = client.get(f'/en-us/events/talk/sponsored/{slug}/') assert r.status_code == status diff --git a/src/events/views.py b/src/events/views.py index 255ef6aa8..dc4f5ed7a 100644 --- a/src/events/views.py +++ b/src/events/views.py @@ -190,7 +190,7 @@ def get_day_grouped_events(self): try: day_info = day_info_dict[begin.value.date()] except KeyError: - logger.warn('Invalid time sot dropped: {}'.format(begin)) + logger.warn(f'Invalid time sot dropped: {begin}') continue for event in begin_time_event_dict[begin]: location = event.location diff --git a/src/proposals/management/commands/export_proposals_create_time.py b/src/proposals/management/commands/export_proposals_create_time.py index 5680ffc7a..b9c3c521d 100644 --- a/src/proposals/management/commands/export_proposals_create_time.py +++ b/src/proposals/management/commands/export_proposals_create_time.py @@ -25,7 +25,7 @@ def export_proposals_create_time(self): # ^you can edit 'Asia/Taipei to other area.' time = timezone.localtime(p.created_at).strftime('%Y-%m-%d %H:%M:%S') joint_proposals.append({ - 'proposal_type(id)': ptype + '({})'.format(p.id), + 'proposal_type(id)': ptype + f'({p.id})', 'title': p.title, 'speaker_name': p.submitter.speaker_name, 'email': p.submitter.email, diff --git a/src/proposals/management/commands/recent_proposals.py b/src/proposals/management/commands/recent_proposals.py index 84709e9f7..c18b134b1 100644 --- a/src/proposals/management/commands/recent_proposals.py +++ b/src/proposals/management/commands/recent_proposals.py @@ -99,15 +99,11 @@ def handle(self, *args, **options): else: self.summary(recent_talks, recent_tutorials) self.msg.write( - '\n\nGot total {:d} new proposals.\n'.format( - recent_talks.count() + recent_tutorials.count() - )) + f'\n\nGot total {recent_talks.count() + recent_tutorials.count():d} new proposals.\n') + talk_count = TalkProposal.objects.filter(cancelled=False).count() + tutorial_count = TutorialProposal.objects.filter(cancelled=False).count() self.msg.write( - 'So far {:d} talk and {:d} tutorial proposals have been submitted.' - .format( - TalkProposal.objects.filter(cancelled=False).count(), - TutorialProposal.objects.filter(cancelled=False).count() - ) + f'So far {talk_count:d} talk and {tutorial_count:d} tutorial proposals have been submitted.' ) self.report(start_dt, end_dt, options['mailto'], options['slack']) self.msg.close() # close the StringIO @@ -126,8 +122,7 @@ def report(self, start_dt, end_dt, mailto=None, slack=False): """Report to either the stdout or mailing to some address""" self.stdout.write(self.msg.getvalue()) title = ( - 'Proposal submission summary from {:%m/%d} to {:%m/%d}' - .format(start_dt, end_dt) + f'Proposal submission summary from {start_dt:%m/%d} to {end_dt:%m/%d}' ) if mailto: subject = '[PyConTW2016][Program] %s' % title @@ -145,7 +140,7 @@ def report(self, start_dt, end_dt, mailto=None, slack=False): # Create the Slack client and send message slack = Slack(url=settings.SLACK_WEBHOOK_URL) status, msg = slack.notify( - text='*%s*\n```\n%s\n```' % (title, self.msg.getvalue()) + text=f'*{title}*\n```\n{self.msg.getvalue()}\n```' ) if status != 200: self.stderr.write(self.style.ERROR( @@ -175,15 +170,13 @@ def create_datetime_range_lookup(self, recent_days, day_shift_hour): ) from e if today_dt > today_utc_dt: raise CommandError( - "Today's datetime {:%Y-%m-%d %H:%M} ({!s}) is yet present" - .format(today_dt, taiwan_tz) + f"Today's datetime {today_dt:%Y-%m-%d %H:%M} ({taiwan_tz!s}) is yet present" ) earliest_dt = today_dt - timedelta(days=recent_days) self.msg.write( - 'Proposals submitted during the recent {:d} days\n' - 'From {:%Y-%m-%d %H:%M} to {:%Y-%m-%d %H:%M}\n' - '(Timezone: {!s})\n\n' - .format(recent_days, earliest_dt, today_dt, taiwan_tz) + f'Proposals submitted during the recent {recent_days:d} days\n' + f'From {earliest_dt:%Y-%m-%d %H:%M} to {today_dt:%Y-%m-%d %H:%M}\n' + f'(Timezone: {taiwan_tz!s})\n\n' ) recent_lookup = Q( created_at__gte=earliest_dt, diff --git a/src/proposals/models.py b/src/proposals/models.py index dfe5cc77e..c100df23f 100644 --- a/src/proposals/models.py +++ b/src/proposals/models.py @@ -35,7 +35,7 @@ def __init__(self, *, proposal=None, user=None): self._user = user or proposal.submitter def __repr__(self): - return ''.format(name=self.user.speaker_name) + return f'' def __eq__(self, other): return ( @@ -105,10 +105,7 @@ class Meta: verbose_name_plural = _('additional speakers') def __str__(self): - return '{name} ({status})'.format( - name=self.user.speaker_name, - status=self.get_status_display(), - ) + return f'{self.user.speaker_name} ({self.get_status_display()})' class ProposalQuerySet(models.QuerySet): @@ -231,8 +228,7 @@ def speakers(self): .select_related('user') ) - for speaker in additionals: - yield speaker + yield from additionals @property def speaker_count(self): diff --git a/src/proposals/tests/test_management.py b/src/proposals/tests/test_management.py index a7dd614af..e6a209b43 100644 --- a/src/proposals/tests/test_management.py +++ b/src/proposals/tests/test_management.py @@ -255,7 +255,7 @@ def test_default_hour_option(capsys): call_command('recent_proposals') out, err = capsys.readouterr() assert re.search( - r'to {:%Y-%m-%d %H}:00$'.format(now_dt), + rf'to {now_dt:%Y-%m-%d %H}:00$', out, re.MULTILINE ) diff --git a/src/proposals/tests/views/test_create.py b/src/proposals/tests/views/test_create.py index 0c1781e61..46d74f1e6 100644 --- a/src/proposals/tests/views/test_create.py +++ b/src/proposals/tests/views/test_create.py @@ -105,7 +105,7 @@ def test_talk_proposal_create_post(agreed_user, agreed_user_client): title='Beyond the Style Guides
', ) assert response.redirect_chain == [ - ('/en-us/proposals/talk/{pk}/edit/'.format(pk=proposal.pk), 302), + (f'/en-us/proposals/talk/{proposal.pk}/edit/', 302), ], response.context['form'].errors msgs = [(m.level, m.message) for m in response.context['messages']] @@ -135,7 +135,7 @@ def test_tutorial_proposal_create_post(agreed_user, agreed_user_client): title='Beyond the Style Guides
', ) assert response.redirect_chain == [ - ('/en-us/proposals/tutorial/{pk}/edit/'.format(pk=proposal.pk), 302), + (f'/en-us/proposals/tutorial/{proposal.pk}/edit/', 302), ], response.context['form'].errors msgs = [(m.level, m.message) for m in response.context['messages']] diff --git a/src/proposals/tests/views/test_speakers.py b/src/proposals/tests/views/test_speakers.py index b82d0b5f4..b697ccd20 100644 --- a/src/proposals/tests/views/test_speakers.py +++ b/src/proposals/tests/views/test_speakers.py @@ -160,7 +160,7 @@ def test_remove_speaker_post(user_client, proposal_type, additional_speaker): follow=True, ) assert response.redirect_chain == [ - ('/en-us/proposals/{}/42/manage-speakers/'.format(proposal_type), 302), + (f'/en-us/proposals/{proposal_type}/42/manage-speakers/', 302), ] diff --git a/src/proposals/utils.py b/src/proposals/utils.py index bb2e2d818..bfa31f4c2 100644 --- a/src/proposals/utils.py +++ b/src/proposals/utils.py @@ -8,4 +8,4 @@ def format_names(names, sep_default=SEP_DEFAULT, sep_last=SEP_LAST): assert names if len(names) == 1: return names[0] - return '{}{}{}'.format(sep_default.join(names[:-1]), sep_last, names[-1]) + return f'{sep_default.join(names[:-1])}{sep_last}{names[-1]}' diff --git a/src/proposals/views/mixins.py b/src/proposals/views/mixins.py index 6e2348345..1df149f69 100644 --- a/src/proposals/views/mixins.py +++ b/src/proposals/views/mixins.py @@ -32,7 +32,8 @@ def dispatch(self, request, *args, **kwargs): class CocAgreementMixin: def dispatch(self, request, *args, **kwargs): if not self.request.user.has_agreed_coc: - return redirect('%s?next=%s' % (reverse('coc_agreement'), request.path)) + url = reverse('coc_agreement') + return redirect(f'{url}?next={request.path}') return super().dispatch(request, *args, **kwargs) diff --git a/src/pycontw2016/logger.py b/src/pycontw2016/logger.py index 20e1bb982..0aa999d68 100644 --- a/src/pycontw2016/logger.py +++ b/src/pycontw2016/logger.py @@ -17,7 +17,7 @@ import logging -class NewStyleLogMessage(object): +class NewStyleLogMessage: def __init__(self, message, *args, **kwargs): self.message = message self.args = args @@ -36,7 +36,7 @@ def __str__(self): class StyleAdapter(logging.LoggerAdapter): def __init__(self, logger, extra=None): - super(StyleAdapter, self).__init__(logger, extra or {}) + super().__init__(logger, extra or {}) def log(self, level, msg, *args, **kwargs): if self.isEnabledFor(level): diff --git a/src/sponsors/models.py b/src/sponsors/models.py index 6d9525563..c339cdcca 100644 --- a/src/sponsors/models.py +++ b/src/sponsors/models.py @@ -14,10 +14,7 @@ def select_storage(): def logo_upload_to(instance, filename): - return 'sponsors/{name}/{filename}'.format( - name=slugify(instance.name, allow_unicode=True), - filename=filename, - ) + return f'sponsors/{slugify(instance.name, allow_unicode=True)}/{filename}' class Sponsor(ConferenceRelated): diff --git a/src/sponsors/templatetags/sponsors.py b/src/sponsors/templatetags/sponsors.py index d8dbafb6d..4a2de2c26 100644 --- a/src/sponsors/templatetags/sponsors.py +++ b/src/sponsors/templatetags/sponsors.py @@ -14,7 +14,7 @@ def sponsor_jsonize(sponsors): sponsor_lists = {level: [] for level, _ in Sponsor.LEVEL_CHOICES} sponsor_info_dict = collections.OrderedDict( - ('level-{}'.format(level), { + (f'level-{level}', { 'name': name, 'sponsors': sponsor_lists[level], }) diff --git a/src/users/models.py b/src/users/models.py index b9865c4c4..3097900c4 100644 --- a/src/users/models.py +++ b/src/users/models.py @@ -103,11 +103,7 @@ def get_with_verification_key(self, verification_key): def photo_upload_to(instance, filename): - return 'avatars/{pk}/{date}-{filename}'.format( - pk=instance.pk, - date=str(datetime.date.today()), - filename=filename, - ) + return f'avatars/{instance.pk}/{str(datetime.date.today())}-{filename}' class User(AbstractBaseUser, PermissionsMixin): @@ -261,13 +257,13 @@ def cospeaking_info_set(self): def twitter_profile_url(self): if not self.twitter_id: return "" - return 'https://twitter.com/{}'.format(self.twitter_id) + return f'https://twitter.com/{self.twitter_id}' @property def github_profile_url(self): if not self.github_id: return "" - return 'https://github.com/{}'.format(self.github_id) + return f'https://github.com/{self.github_id}' def get_verification_key(self): key = signing.dumps( diff --git a/src/users/tests/test_views_signup.py b/src/users/tests/test_views_signup.py index 213932f5b..42f8c5a02 100644 --- a/src/users/tests/test_views_signup.py +++ b/src/users/tests/test_views_signup.py @@ -70,7 +70,7 @@ def test_verify(bare_user, bare_user_client): message, and redirected to dashboard. """ key = bare_user.get_verification_key() - link = '/en-us/accounts/verify/{key}/'.format(key=key) + link = f'/en-us/accounts/verify/{key}/' response = bare_user_client.get(link, follow=True) assert response.redirect_chain[0] == ('/en-us/dashboard/', 302)