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)