diff --git a/analytics/views.py b/analytics/views.py index d9147ae3b2..619a9f3f8c 100644 --- a/analytics/views.py +++ b/analytics/views.py @@ -243,7 +243,7 @@ def get_chart_data(request: HttpRequest, user_profile: UserProfile, chart_name: labels_sort_function = None include_empty_subgroups = True else: - raise JsonableError(_("Unknown chart name: %s") % (chart_name,)) + raise JsonableError(_("Unknown chart name: {}").format(chart_name)) # Most likely someone using our API endpoint. The /stats page does not # pass a start or end in its requests. @@ -252,8 +252,9 @@ def get_chart_data(request: HttpRequest, user_profile: UserProfile, chart_name: if end is not None: end = convert_to_UTC(end) if start is not None and end is not None and start > end: - raise JsonableError(_("Start time is later than end time. Start: %(start)s, End: %(end)s") % - {'start': start, 'end': end}) + raise JsonableError(_("Start time is later than end time. Start: {start}, End: {end}").format( + start=start, end=end, + )) if realm is None: # Note that this value is invalid for Remote tables; be diff --git a/corporate/lib/stripe.py b/corporate/lib/stripe.py index 3113e4a4ae..c94051a8c3 100644 --- a/corporate/lib/stripe.py +++ b/corporate/lib/stripe.py @@ -135,7 +135,9 @@ def renewal_amount(plan: CustomerPlan, event_time: datetime) -> int: # nocovera class BillingError(Exception): # error messages - CONTACT_SUPPORT = _("Something went wrong. Please contact %s.") % (settings.ZULIP_ADMINISTRATOR,) + CONTACT_SUPPORT = _("Something went wrong. Please contact {email}.").format( + email=settings.ZULIP_ADMINISTRATOR, + ) TRY_RELOADING = _("Something went wrong. Please reload the page.") # description is used only for tests diff --git a/corporate/views.py b/corporate/views.py index fe1628ac81..22d7f086d6 100644 --- a/corporate/views.py +++ b/corporate/views.py @@ -1,6 +1,6 @@ import logging from decimal import Decimal -from typing import Any, Dict, Optional, Union, cast +from typing import Any, Dict, Optional, Union import stripe from django.conf import settings @@ -85,14 +85,17 @@ def payment_method_string(stripe_customer: stripe.Customer) -> str: if stripe_source is None: # nocoverage return _("No payment method on file") if stripe_source.object == "card": - return _("%(brand)s ending in %(last4)s") % { - 'brand': cast(stripe.Card, stripe_source).brand, - 'last4': cast(stripe.Card, stripe_source).last4} + assert isinstance(stripe_source, stripe.Card) + return _("{brand} ending in {last4}").format( + brand=stripe_source.brand, last4=stripe_source.last4, + ) # There might be one-off stuff we do for a particular customer that # would land them here. E.g. by default we don't support ACH for # automatic payments, but in theory we could add it for a customer via # the Stripe dashboard. - return _("Unknown payment method. Please contact %s.") % (settings.ZULIP_ADMINISTRATOR,) # nocoverage + return _("Unknown payment method. Please contact {email}.").format( + email=settings.ZULIP_ADMINISTRATOR, + ) # nocoverage @has_request_variables def upgrade(request: HttpRequest, user: UserProfile, diff --git a/tools/lib/capitalization.py b/tools/lib/capitalization.py index 3386d47cfc..2e1fbc0452 100644 --- a/tools/lib/capitalization.py +++ b/tools/lib/capitalization.py @@ -171,7 +171,7 @@ SPLIT_BOUNDARY_REGEX = re.compile(fr'[{SPLIT_BOUNDARY}]') # Regexes which check capitalization in sentences. DISALLOWED_REGEXES = [re.compile(regex) for regex in [ - r'^[a-z]', # Checks if the sentence starts with a lower case character. + r'^[a-z](?!\})', # Checks if the sentence starts with a lower case character. r'^[A-Z][a-z]+[\sa-z0-9]+[A-Z]', # Checks if an upper case character exists # after a lower case character when the first character is in upper case. ]] diff --git a/tools/semgrep.yml b/tools/semgrep.yml index 5f4d2eafd8..d53f6eadbf 100644 --- a/tools/semgrep.yml +++ b/tools/semgrep.yml @@ -81,6 +81,14 @@ rules: severity: ERROR message: "Do not write a SQL injection vulnerability please" + - id: translated-format + languages: [python] + pattern-either: + - pattern: django.utils.translation.ugettext(... .format(...)) + - pattern: django.utils.translation.ugettext(f"...") + severity: ERROR + message: "Format strings after translation, not before" + - id: mutable-default-type languages: [python] pattern-either: @@ -122,5 +130,6 @@ rules: pattern-either: - pattern: '"..." % ...' - pattern: '("...") % ...' + - pattern: django.utils.translation.ugettext(...) % ... severity: ERROR message: "Prefer f-strings or .format for string formatting" diff --git a/zerver/lib/actions.py b/zerver/lib/actions.py index e322d95461..b28656b329 100644 --- a/zerver/lib/actions.py +++ b/zerver/lib/actions.py @@ -1948,7 +1948,7 @@ def validate_recipient_user_profiles(user_profiles: Sequence[UserProfile], for user_profile in user_profiles: if (not user_profile.is_active and not user_profile.is_mirror_dummy and not allow_deactivated) or user_profile.realm.deactivated: - raise ValidationError(_("'%s' is no longer using Zulip.") % (user_profile.email,)) + raise ValidationError(_("'{email}' is no longer using Zulip.").format(email=user_profile.email)) recipient_profiles_map[user_profile.id] = user_profile if not is_cross_realm_bot_email(user_profile.email): realms.add(user_profile.realm_id) @@ -2132,14 +2132,16 @@ def check_schedule_message(sender: UserProfile, client: Client, def check_default_stream_group_name(group_name: str) -> None: if group_name.strip() == "": - raise JsonableError(_("Invalid default stream group name '%s'") % (group_name,)) + raise JsonableError(_("Invalid default stream group name '{}'").format(group_name)) if len(group_name) > DefaultStreamGroup.MAX_NAME_LENGTH: - raise JsonableError(_("Default stream group name too long (limit: %s characters)") - % (DefaultStreamGroup.MAX_NAME_LENGTH,)) + raise JsonableError(_("Default stream group name too long (limit: {} characters)").format( + DefaultStreamGroup.MAX_NAME_LENGTH, + )) for i in group_name: if ord(i) == 0: - raise JsonableError(_("Default stream group name '%s' contains NULL (0x00) characters.") - % (group_name,)) + raise JsonableError(_("Default stream group name '{}' contains NULL (0x00) characters.").format( + group_name, + )) def send_rate_limited_pm_notification_to_bot_owner(sender: UserProfile, realm: Realm, @@ -2194,19 +2196,19 @@ def send_pm_if_empty_stream(stream: Optional[Stream], } if stream is None: if stream_id is not None: - content = _("Your bot `%(bot_identity)s` tried to send a message to stream ID " - "%(stream_id)s, but there is no stream with that ID.") % arg_dict + content = _("Your bot `{bot_identity}` tried to send a message to stream ID " + "{stream_id}, but there is no stream with that ID.").format(**arg_dict) else: assert(stream_name is not None) - content = _("Your bot `%(bot_identity)s` tried to send a message to stream " - "#**%(stream_name)s**, but that stream does not exist. " - "Click [here](#streams/new) to create it.") % arg_dict + content = _("Your bot `{bot_identity}` tried to send a message to stream " + "#**{stream_name}**, but that stream does not exist. " + "Click [here](#streams/new) to create it.").format(**arg_dict) else: if num_subscribers_for_stream_id(stream.id) > 0: return - content = _("Your bot `%(bot_identity)s` tried to send a message to " - "stream #**%(stream_name)s**. The stream exists but " - "does not have any subscribers.") % arg_dict + content = _("Your bot `{bot_identity}` tried to send a message to " + "stream #**{stream_name}**. The stream exists but " + "does not have any subscribers.").format(**arg_dict) send_rate_limited_pm_notification_to_bot_owner(sender, realm, content) @@ -2349,7 +2351,9 @@ def check_message(sender: UserProfile, client: Client, addressee: Addressee, error_msg = check_widget_content(widget_content) if error_msg: - raise JsonableError(_('Widgets: %s') % (error_msg,)) + raise JsonableError(_('Widgets: {error_msg}').format( + error_msg=error_msg, + )) return {'message': message, 'stream': stream, 'local_id': local_id, 'sender_queue_id': sender_queue_id, 'realm': realm, @@ -3583,12 +3587,13 @@ def do_rename_stream(stream: Stream, sender, stream, Realm.STREAM_EVENTS_NOTIFICATION_TOPIC, - _('@_**%(user_name)s|%(user_id)d** renamed stream **%(old_stream_name)s** to ' - '**%(new_stream_name)s**.') % { - 'user_name': user_profile.full_name, - 'user_id': user_profile.id, - 'old_stream_name': old_name, - 'new_stream_name': new_name}, + _('@_**{user_name}|{user_id}** renamed stream **{old_stream_name}** to ' + '**{new_stream_name}**.').format( + user_name=user_profile.full_name, + user_id=user_profile.id, + old_stream_name=old_name, + new_stream_name=new_name, + ), ) # Even though the token doesn't change, the web client needs to update the # email forwarding address to display the correctly-escaped new name. @@ -3734,7 +3739,7 @@ def lookup_default_stream_groups(default_stream_group_names: List[str], default_stream_group = DefaultStreamGroup.objects.get( name=group_name, realm=realm) except DefaultStreamGroup.DoesNotExist: - raise JsonableError(_('Invalid default stream group %s') % (group_name,)) + raise JsonableError(_('Invalid default stream group {}').format(group_name)) default_stream_groups.append(default_stream_group) return default_stream_groups @@ -3771,15 +3776,16 @@ def do_create_default_stream_group(realm: Realm, group_name: str, for stream in streams: if stream in default_streams: raise JsonableError(_( - "'%(stream_name)s' is a default stream and cannot be added to '%(group_name)s'") - % {'stream_name': stream.name, 'group_name': group_name}) + "'{stream_name}' is a default stream and cannot be added to '{group_name}'", + ).format(stream_name=stream.name, group_name=group_name)) check_default_stream_group_name(group_name) (group, created) = DefaultStreamGroup.objects.get_or_create( name=group_name, realm=realm, description=description) if not created: - raise JsonableError(_("Default stream group '%(group_name)s' already exists") - % {'group_name': group_name}) + raise JsonableError(_( + "Default stream group '{group_name}' already exists", + ).format(group_name=group_name)) group.streams.set(streams) notify_default_stream_groups(realm) @@ -3790,12 +3796,12 @@ def do_add_streams_to_default_stream_group(realm: Realm, group: DefaultStreamGro for stream in streams: if stream in default_streams: raise JsonableError(_( - "'%(stream_name)s' is a default stream and cannot be added to '%(group_name)s'") - % {'stream_name': stream.name, 'group_name': group.name}) + "'{stream_name}' is a default stream and cannot be added to '{group_name}'", + ).format(stream_name=stream.name, group_name=group.name)) if stream in group.streams.all(): raise JsonableError(_( - "Stream '%(stream_name)s' is already present in default stream group '%(group_name)s'") - % {'stream_name': stream.name, 'group_name': group.name}) + "Stream '{stream_name}' is already present in default stream group '{group_name}'", + ).format(stream_name=stream.name, group_name=group.name)) group.streams.add(stream) group.save() @@ -3806,8 +3812,8 @@ def do_remove_streams_from_default_stream_group(realm: Realm, group: DefaultStre for stream in streams: if stream not in group.streams.all(): raise JsonableError(_( - "Stream '%(stream_name)s' is not present in default stream group '%(group_name)s'") - % {'stream_name': stream.name, 'group_name': group.name}) + "Stream '{stream_name}' is not present in default stream group '{group_name}'", + ).format(stream_name=stream.name, group_name=group.name)) group.streams.remove(stream) group.save() @@ -3816,10 +3822,10 @@ def do_remove_streams_from_default_stream_group(realm: Realm, group: DefaultStre def do_change_default_stream_group_name(realm: Realm, group: DefaultStreamGroup, new_group_name: str) -> None: if group.name == new_group_name: - raise JsonableError(_("This default stream group is already named '%s'") % (new_group_name,)) + raise JsonableError(_("This default stream group is already named '{}'").format(new_group_name)) if DefaultStreamGroup.objects.filter(name=new_group_name, realm=realm).exists(): - raise JsonableError(_("Default stream group '%s' already exists") % (new_group_name,)) + raise JsonableError(_("Default stream group '{}' already exists").format(new_group_name)) group.name = new_group_name group.save() @@ -4172,9 +4178,9 @@ def do_update_message_flags(user_profile: UserProfile, valid_flags = [item for item in UserMessage.flags if item not in UserMessage.NON_API_FLAGS] if flag not in valid_flags: - raise JsonableError(_("Invalid flag: '%s'") % (flag,)) + raise JsonableError(_("Invalid flag: '{}'").format(flag)) if flag in UserMessage.NON_EDITABLE_FLAGS: - raise JsonableError(_("Flag not editable: '%s'") % (flag,)) + raise JsonableError(_("Flag not editable: '{}'").format(flag)) flagattr = getattr(UserMessage.flags, flag) msgs = UserMessage.objects.filter(user_profile=user_profile, @@ -4250,15 +4256,19 @@ def notify_topic_moved_streams(user_profile: UserProfile, if send_notification_to_new_thread: internal_send_stream_message( new_stream.realm, sender, new_stream, new_topic, - _("This topic was moved here from %(old_location)s by %(user)s") - % dict(old_location=old_topic_link, user=user_mention)) + _("This topic was moved here from {old_location} by {user}").format( + old_location=old_topic_link, user=user_mention, + ), + ) if send_notification_to_old_thread: # Send a notification to the old stream that the topic was moved. internal_send_stream_message( old_stream.realm, sender, old_stream, old_topic, - _("This topic was moved by %(user)s to %(new_location)s") - % dict(user=user_mention, new_location=new_topic_link)) + _("This topic was moved by {user} to {new_location}").format( + user=user_mention, new_location=new_topic_link, + ), + ) def get_user_info_for_message_updates(message_id: int) -> MessageUpdateUserInfoResult: @@ -4992,8 +5002,8 @@ def estimate_recent_invites(realms: Iterable[Realm], *, days: int) -> int: def check_invite_limit(realm: Realm, num_invitees: int) -> None: '''Discourage using invitation emails as a vector for carrying spam.''' msg = _("You do not have enough remaining invites. " - "Please contact %s to have your limit raised. " - "No invitations were sent.") % (settings.ZULIP_ADMINISTRATOR,) + "Please contact {email} to have your limit raised. " + "No invitations were sent.").format(email=settings.ZULIP_ADMINISTRATOR) if not settings.OPEN_REALM_CREATION: return @@ -5617,7 +5627,7 @@ def check_add_user_group(realm: Realm, name: str, initial_members: List[UserProf user_group = create_user_group(name, initial_members, realm, description=description) do_send_create_user_group_event(user_group, initial_members) except django.db.utils.IntegrityError: - raise JsonableError(_("User group '%s' already exists.") % (name,)) + raise JsonableError(_("User group '{}' already exists.").format(name)) def do_send_user_group_update_event(user_group: UserGroup, data: Dict[str, Any]) -> None: event = dict(type="user_group", op='update', group_id=user_group.id, data=data) @@ -5628,7 +5638,7 @@ def do_update_user_group_name(user_group: UserGroup, name: str) -> None: user_group.name = name user_group.save(update_fields=['name']) except django.db.utils.IntegrityError: - raise JsonableError(_("User group '%s' already exists.") % (name,)) + raise JsonableError(_("User group '{}' already exists.").format(name)) do_send_user_group_update_event(user_group, dict(name=name)) def do_update_user_group_description(user_group: UserGroup, description: str) -> None: diff --git a/zerver/lib/addressee.py b/zerver/lib/addressee.py index ec1c6fd7a9..b0b57c1a4d 100644 --- a/zerver/lib/addressee.py +++ b/zerver/lib/addressee.py @@ -18,7 +18,7 @@ def get_user_profiles(emails: Iterable[str], realm: Realm) -> List[UserProfile]: try: user_profile = get_user_including_cross_realm(email, realm) except UserProfile.DoesNotExist: - raise JsonableError(_("Invalid email '%s'") % (email,)) + raise JsonableError(_("Invalid email '{}'").format(email)) user_profiles.append(user_profile) return user_profiles diff --git a/zerver/lib/email_validation.py b/zerver/lib/email_validation.py index dc37305518..ca6f314e61 100644 --- a/zerver/lib/email_validation.py +++ b/zerver/lib/email_validation.py @@ -174,7 +174,7 @@ def get_existing_user_errors( if existing_user_profile.is_active: if verbose: - msg = _('%s already has an account') % (email,) + msg = _('{email} already has an account').format(email=email) else: msg = _("Already has an account.") else: diff --git a/zerver/lib/emoji.py b/zerver/lib/emoji.py index df339df2f2..30a87049dd 100644 --- a/zerver/lib/emoji.py +++ b/zerver/lib/emoji.py @@ -52,7 +52,7 @@ def emoji_name_to_emoji_code(realm: Realm, emoji_name: str) -> Tuple[str, str]: return emoji_name, Reaction.ZULIP_EXTRA_EMOJI if emoji_name in name_to_codepoint: return name_to_codepoint[emoji_name], Reaction.UNICODE_EMOJI - raise JsonableError(_("Emoji '%s' does not exist") % (emoji_name,)) + raise JsonableError(_("Emoji '{}' does not exist").format(emoji_name)) def check_emoji_request(realm: Realm, emoji_name: str, emoji_code: str, emoji_type: str) -> None: diff --git a/zerver/lib/narrow.py b/zerver/lib/narrow.py index e77f189130..948b8578b7 100644 --- a/zerver/lib/narrow.py +++ b/zerver/lib/narrow.py @@ -21,7 +21,7 @@ def check_supported_events_narrow_filter(narrow: Iterable[Sequence[str]]) -> Non for element in narrow: operator = element[0] if operator not in ["stream", "topic", "sender", "is"]: - raise JsonableError(_("Operator %s not supported.") % (operator,)) + raise JsonableError(_("Operator {} not supported.").format(operator)) def is_web_public_compatible(narrow: Iterable[Dict[str, str]]) -> bool: for element in narrow: diff --git a/zerver/lib/push_notifications.py b/zerver/lib/push_notifications.py index bc255d6b82..672932d4ab 100644 --- a/zerver/lib/push_notifications.py +++ b/zerver/lib/push_notifications.py @@ -238,15 +238,17 @@ def parse_gcm_options(options: Dict[str, Any], data: Dict[str, Any]) -> str: else: # `'event': 'remove'`, presumably priority = 'normal' if priority not in ('normal', 'high'): - raise JsonableError(_("Invalid GCM option to bouncer: priority %r") - % (priority,)) + raise JsonableError(_( + "Invalid GCM option to bouncer: priority {!r}", + ).format(priority)) if options: # We're strict about the API; there is no use case for a newer Zulip # server talking to an older bouncer, so we only need to provide # one-way compatibility. - raise JsonableError(_("Invalid GCM options to bouncer: %s") - % (ujson.dumps(options),)) + raise JsonableError(_( + "Invalid GCM options to bouncer: {}", + ).format(ujson.dumps(options))) return priority # when this grows a second option, can make it a tuple @@ -600,9 +602,9 @@ def get_apns_alert_subtitle(message: Message) -> str: On an iOS notification, this is the second bolded line. """ if message.trigger == "mentioned": - return _("%(full_name)s mentioned you:") % dict(full_name=message.sender.full_name) + return _("{full_name} mentioned you:").format(full_name=message.sender.full_name) elif message.trigger == "wildcard_mentioned": - return _("%(full_name)s mentioned everyone:") % dict(full_name=message.sender.full_name) + return _("{full_name} mentioned everyone:").format(full_name=message.sender.full_name) elif message.recipient.type == Recipient.PERSONAL: return "" # For group PMs, or regular messages to a stream, just use a colon to indicate this is the sender. diff --git a/zerver/lib/remote_server.py b/zerver/lib/remote_server.py index cace1c9199..2f0338327e 100644 --- a/zerver/lib/remote_server.py +++ b/zerver/lib/remote_server.py @@ -77,7 +77,7 @@ def send_to_push_bouncer(method: str, if 'code' in result_dict and result_dict['code'] == 'INVALID_ZULIP_SERVER': # Invalid Zulip server credentials should email this server's admins raise PushNotificationBouncerException( - _("Push notifications bouncer error: %s") % (msg,)) + _("Push notifications bouncer error: {}").format(msg)) else: # But most other errors coming from the push bouncer # server are client errors (e.g. never-registered token) diff --git a/zerver/lib/request.py b/zerver/lib/request.py index 5ab4f73e7e..dcbe00d6cb 100644 --- a/zerver/lib/request.py +++ b/zerver/lib/request.py @@ -348,7 +348,7 @@ def has_request_variables(view_func: ViewFuncT) -> ViewFuncT: try: val = ujson.loads(val) except Exception: - raise JsonableError(_('Argument "%s" is not valid JSON.') % (post_var_name,)) + raise JsonableError(_('Argument "{}" is not valid JSON.').format(post_var_name)) error = param.validator(post_var_name, val) if error: diff --git a/zerver/lib/streams.py b/zerver/lib/streams.py index cf96a5d9c5..8b06436493 100644 --- a/zerver/lib/streams.py +++ b/zerver/lib/streams.py @@ -115,12 +115,12 @@ def create_streams_if_needed(realm: Realm, def check_stream_name(stream_name: str) -> None: if stream_name.strip() == "": - raise JsonableError(_("Invalid stream name '%s'") % (stream_name,)) + raise JsonableError(_("Invalid stream name '{}'").format(stream_name)) if len(stream_name) > Stream.MAX_NAME_LENGTH: - raise JsonableError(_("Stream name too long (limit: %s characters).") % (Stream.MAX_NAME_LENGTH,)) + raise JsonableError(_("Stream name too long (limit: {} characters).").format(Stream.MAX_NAME_LENGTH)) for i in stream_name: if ord(i) == 0: - raise JsonableError(_("Stream name '%s' contains NULL (0x00) characters.") % (stream_name,)) + raise JsonableError(_("Stream name '{}' contains NULL (0x00) characters.").format(stream_name)) def subscribed_to_stream(user_profile: UserProfile, stream_id: int) -> bool: return Subscription.objects.filter( @@ -177,7 +177,7 @@ def access_stream_for_send_message(sender: UserProfile, return # All other cases are an error. - raise JsonableError(_("Not authorized to send to stream '%s'") % (stream.name,)) + raise JsonableError(_("Not authorized to send to stream '{}'").format(stream.name)) def check_for_exactly_one_stream_arg(stream_id: Optional[int], stream: Optional[str]) -> None: if stream_id is None and stream is None: @@ -276,14 +276,14 @@ def check_stream_name_available(realm: Realm, name: str) -> None: check_stream_name(name) try: get_stream(name, realm) - raise JsonableError(_("Stream name '%s' is already taken.") % (name,)) + raise JsonableError(_("Stream name '{}' is already taken.").format(name)) except Stream.DoesNotExist: pass def access_stream_by_name(user_profile: UserProfile, stream_name: str, allow_realm_admin: bool=False) -> Tuple[Stream, Recipient, Optional[Subscription]]: - error = _("Invalid stream name '%s'") % (stream_name,) + error = _("Invalid stream name '{}'").format(stream_name) try: stream = get_realm_stream(stream_name, user_profile.realm_id) except Stream.DoesNotExist: @@ -345,7 +345,7 @@ def can_access_stream_history(user_profile: UserProfile, stream: Stream) -> bool if stream.is_history_public_to_subscribers(): # In this case, we check if the user is subscribed. - error = _("Invalid stream name '%s'") % (stream.name,) + error = _("Invalid stream name '{}'").format(stream.name) try: (recipient, sub) = access_stream_common(user_profile, stream, error) except JsonableError: @@ -440,8 +440,9 @@ def list_to_streams(streams_raw: Iterable[Mapping[str, Any]], if not user_profile.can_create_streams(): raise JsonableError(_('User cannot create streams.')) elif not autocreate: - raise JsonableError(_("Stream(s) (%s) do not exist") % ", ".join( - stream_dict["name"] for stream_dict in missing_stream_dicts)) + raise JsonableError(_("Stream(s) ({}) do not exist").format( + ", ".join(stream_dict["name"] for stream_dict in missing_stream_dicts), + )) # We already filtered out existing streams, so dup_streams # will normally be an empty list below, but we protect against somebody @@ -458,7 +459,7 @@ def access_default_stream_group_by_id(realm: Realm, group_id: int) -> DefaultStr try: return DefaultStreamGroup.objects.get(realm=realm, id=group_id) except DefaultStreamGroup.DoesNotExist: - raise JsonableError(_("Default stream group with id '%s' does not exist.") % (group_id,)) + raise JsonableError(_("Default stream group with id '{}' does not exist.").format(group_id)) def get_stream_by_narrow_operand_access_unchecked(operand: Union[str, int], realm: Realm) -> Stream: """This is required over access_stream_* in certain cases where diff --git a/zerver/lib/users.py b/zerver/lib/users.py index 1db7309f84..5c9ebda2f3 100644 --- a/zerver/lib/users.py +++ b/zerver/lib/users.py @@ -78,12 +78,13 @@ def check_valid_bot_config(bot_type: int, service_name: str, config_options = {c[1]: c[2] for c in integration.config_options} break if not config_options: - raise JsonableError(_("Invalid integration '%s'.") % (service_name,)) + raise JsonableError(_("Invalid integration '{}'.").format(service_name)) missing_keys = set(config_options.keys()) - set(config_data.keys()) if missing_keys: - raise JsonableError(_("Missing configuration parameters: %s") % ( - missing_keys,)) + raise JsonableError(_("Missing configuration parameters: {}").format( + missing_keys, + )) for key, validator in config_options.items(): value = config_data[key] @@ -196,12 +197,12 @@ def user_ids_to_users(user_ids: Sequence[int], realm: Realm) -> List[UserProfile found_user_ids = user_profiles_by_id.keys() missed_user_ids = [user_id for user_id in user_ids if user_id not in found_user_ids] if missed_user_ids: - raise JsonableError(_("Invalid user ID: %s") % (missed_user_ids[0],)) + raise JsonableError(_("Invalid user ID: {}").format(missed_user_ids[0])) user_profiles = list(user_profiles_by_id.values()) for user_profile in user_profiles: if user_profile.realm != realm: - raise JsonableError(_("Invalid user ID: %s") % (user_profile.id,)) + raise JsonableError(_("Invalid user ID: {}").format(user_profile.id)) return user_profiles def access_bot_by_id(user_profile: UserProfile, user_id: int) -> UserProfile: diff --git a/zerver/lib/validator.py b/zerver/lib/validator.py index 4eeda8c14e..985b40ac7b 100644 --- a/zerver/lib/validator.py +++ b/zerver/lib/validator.py @@ -56,7 +56,7 @@ def set_type_structure(type_structure: TypeStructure) -> Callable[[FuncT], Any]: @set_type_structure("str") def check_string(var_name: str, val: object) -> Optional[str]: if not isinstance(val, str): - return _('%s is not a string') % (var_name,) + return _('{var_name} is not a string').format(var_name=var_name) return None @set_type_structure("str") @@ -78,7 +78,7 @@ def check_string_in(possible_values: Union[Set[str], List[str]]) -> Validator: if not_str is not None: return not_str if val not in possible_values: - return _("Invalid %s") % (var_name,) + return _("Invalid {var_name}").format(var_name=var_name) return None return validator @@ -91,7 +91,7 @@ def check_capped_string(max_length: int) -> Validator: @set_type_structure("str") def validator(var_name: str, val: object) -> Optional[str]: if not isinstance(val, str): - return _('%s is not a string') % (var_name,) + return _('{var_name} is not a string').format(var_name=var_name) if len(val) > max_length: return _("{var_name} is too long (limit: {max_length} characters)").format( var_name=var_name, max_length=max_length) @@ -103,7 +103,7 @@ def check_string_fixed_length(length: int) -> Validator: @set_type_structure("str") def validator(var_name: str, val: object) -> Optional[str]: if not isinstance(val, str): - return _('%s is not a string') % (var_name,) + return _('{var_name} is not a string').format(var_name=var_name) if len(val) != length: return _("{var_name} has incorrect length {length}; should be {target_length}").format( var_name=var_name, target_length=length, length=len(val)) @@ -117,17 +117,17 @@ def check_long_string(var_name: str, val: object) -> Optional[str]: @set_type_structure("date") def check_date(var_name: str, val: object) -> Optional[str]: if not isinstance(val, str): - return _('%s is not a string') % (var_name,) + return _('{var_name} is not a string').format(var_name=var_name) try: datetime.strptime(val, '%Y-%m-%d') except ValueError: - return _('%s is not a date') % (var_name,) + return _('{var_name} is not a date').format(var_name=var_name) return None @set_type_structure("int") def check_int(var_name: str, val: object) -> Optional[str]: if not isinstance(val, int): - return _('%s is not an integer') % (var_name,) + return _('{var_name} is not an integer').format(var_name=var_name) return None def check_int_in(possible_values: List[int]) -> Validator: @@ -137,7 +137,7 @@ def check_int_in(possible_values: List[int]) -> Validator: if not_int is not None: return not_int if val not in possible_values: - return _("Invalid %s") % (var_name,) + return _("Invalid {var_name}").format(var_name=var_name) return None return validator @@ -145,23 +145,23 @@ def check_int_in(possible_values: List[int]) -> Validator: @set_type_structure("float") def check_float(var_name: str, val: object) -> Optional[str]: if not isinstance(val, float): - return _('%s is not a float') % (var_name,) + return _('{var_name} is not a float').format(var_name=var_name) return None @set_type_structure("bool") def check_bool(var_name: str, val: object) -> Optional[str]: if not isinstance(val, bool): - return _('%s is not a boolean') % (var_name,) + return _('{var_name} is not a boolean').format(var_name=var_name) return None @set_type_structure("str") def check_color(var_name: str, val: object) -> Optional[str]: if not isinstance(val, str): - return _('%s is not a string') % (var_name,) + return _('{var_name} is not a string').format(var_name=var_name) valid_color_pattern = re.compile(r'^#([a-fA-F0-9]{3,6})$') matched_results = valid_color_pattern.match(val) if not matched_results: - return _('%s is not a valid hex color code') % (var_name,) + return _('{var_name} is not a valid hex color code').format(var_name=var_name) return None def check_none_or(sub_validator: Validator) -> Validator: @@ -190,11 +190,12 @@ def check_list(sub_validator: Optional[Validator], length: Optional[int]=None) - @set_type_structure(type_structure) def f(var_name: str, val: object) -> Optional[str]: if not isinstance(val, list): - return _('%s is not a list') % (var_name,) + return _('{var_name} is not a list').format(var_name=var_name) if length is not None and length != len(val): - return (_('%(container)s should have exactly %(length)s items') % - {'container': var_name, 'length': length}) + return (_('{container} should have exactly {length} items').format( + container=var_name, length=length, + )) if sub_validator: for i, item in enumerate(val): @@ -215,12 +216,13 @@ def check_dict(required_keys: Iterable[Tuple[str, Validator]]=[], @set_type_structure(type_structure) def f(var_name: str, val: object) -> Optional[str]: if not isinstance(val, dict): - return _('%s is not a dict') % (var_name,) + return _('{var_name} is not a dict').format(var_name=var_name) for k, sub_validator in required_keys: if k not in val: - return (_('%(key_name)s key is missing from %(var_name)s') % - {'key_name': k, 'var_name': var_name}) + return (_('{key_name} key is missing from {var_name}').format( + key_name=k, var_name=var_name, + )) vname = f'{var_name}["{k}"]' error = sub_validator(vname, val[k]) if error: @@ -251,7 +253,7 @@ def check_dict(required_keys: Iterable[Tuple[str, Validator]]=[], optional_keys_set = {x[0] for x in optional_keys} delta_keys = set(val.keys()) - required_keys_set - optional_keys_set if len(delta_keys) != 0: - return _("Unexpected arguments: %s") % (", ".join(list(delta_keys)),) + return _("Unexpected arguments: {}").format(", ".join(list(delta_keys))) return None @@ -280,17 +282,16 @@ def check_variable_type(allowed_type_funcs: Iterable[Validator]) -> Validator: for func in allowed_type_funcs: if not func(var_name, val): return None - return _('%s is not an allowed_type') % (var_name,) + return _('{var_name} is not an allowed_type').format(var_name=var_name) return enumerated_type_check def equals(expected_val: object) -> Validator: @set_type_structure(f'equals("{str(expected_val)}")') def f(var_name: str, val: object) -> Optional[str]: if val != expected_val: - return (_('%(variable)s != %(expected_value)s (%(value)s is wrong)') % - {'variable': var_name, - 'expected_value': expected_val, - 'value': val}) + return (_('{variable} != {expected_value} ({value} is wrong)').format( + variable=var_name, expected_value=expected_val, value=val, + )) return None return f @@ -313,7 +314,7 @@ def check_url(var_name: str, val: object) -> Optional[str]: validate(val) return None except ValidationError: - return _('%s is not a URL') % (var_name,) + return _('{var_name} is not a URL').format(var_name=var_name) @set_type_structure('str') def check_external_account_url_pattern(var_name: str, val: object) -> Optional[str]: @@ -435,7 +436,7 @@ def check_string_or_int_list(var_name: str, val: object) -> Optional[str]: return None if not isinstance(val, list): - return _('%s is not a string or an integer list') % (var_name,) + return _('{var_name} is not a string or an integer list').format(var_name=var_name) return check_list(check_int)(var_name, val) @@ -444,4 +445,4 @@ def check_string_or_int(var_name: str, val: object) -> Optional[str]: if isinstance(val, str) or isinstance(val, int): return None - return _('%s is not a string or integer') % (var_name,) + return _('{var_name} is not a string or integer').format(var_name=var_name) diff --git a/zerver/lib/zcommand.py b/zerver/lib/zcommand.py index f6909b347d..16beb63894 100644 --- a/zerver/lib/zcommand.py +++ b/zerver/lib/zcommand.py @@ -54,4 +54,4 @@ def process_zcommands(content: str, user_profile: UserProfile) -> Dict[str, Any] switch_command='fluid-width', setting='fluid_layout_width', setting_value=False)) - raise JsonableError(_('No such command: %s') % (command,)) + raise JsonableError(_('No such command: {}').format(command)) diff --git a/zerver/tests/test_realm_emoji.py b/zerver/tests/test_realm_emoji.py index 4874fb1fa9..e73f18c266 100644 --- a/zerver/tests/test_realm_emoji.py +++ b/zerver/tests/test_realm_emoji.py @@ -211,7 +211,7 @@ class RealmEmojiTest(ZulipTestCase): with get_test_image_file('img.png') as fp: with self.settings(MAX_EMOJI_FILE_SIZE=0): result = self.client_post('/json/realm/emoji/my_emoji', {'file': fp}) - self.assert_json_error(result, 'Uploaded file is larger than the allowed limit of 0 MB') + self.assert_json_error(result, 'Uploaded file is larger than the allowed limit of 0 MiB') def test_upload_already_existed_emoji(self) -> None: self.login('iago') diff --git a/zerver/tests/test_upload.py b/zerver/tests/test_upload.py index fa0084658c..0afd388466 100644 --- a/zerver/tests/test_upload.py +++ b/zerver/tests/test_upload.py @@ -164,7 +164,7 @@ class FileUploadTest(UploadSerializeMixin, ZulipTestCase): # would be 1MB. with self.settings(MAX_FILE_UPLOAD_SIZE=0): result = self.client_post("/json/user_uploads", {'f1': fp}) - self.assert_json_error(result, 'Uploaded file is larger than the allowed limit of 0 MB') + self.assert_json_error(result, 'Uploaded file is larger than the allowed limit of 0 MiB') def test_multiple_upload_failure(self) -> None: """ @@ -1163,7 +1163,7 @@ class AvatarTest(UploadSerializeMixin, ZulipTestCase): with get_test_image_file(self.correct_files[0][0]) as fp: with self.settings(MAX_AVATAR_FILE_SIZE=0): result = self.client_post("/json/users/me/avatar", {'file': fp}) - self.assert_json_error(result, "Uploaded file is larger than the allowed limit of 0 MB") + self.assert_json_error(result, "Uploaded file is larger than the allowed limit of 0 MiB") def tearDown(self) -> None: destroy_uploads() @@ -1336,7 +1336,7 @@ class RealmIconTest(UploadSerializeMixin, ZulipTestCase): with get_test_image_file(self.correct_files[0][0]) as fp: with self.settings(MAX_ICON_FILE_SIZE=0): result = self.client_post("/json/realm/icon", {'file': fp}) - self.assert_json_error(result, "Uploaded file is larger than the allowed limit of 0 MB") + self.assert_json_error(result, "Uploaded file is larger than the allowed limit of 0 MiB") def tearDown(self) -> None: destroy_uploads() @@ -1479,7 +1479,7 @@ class RealmLogoTest(UploadSerializeMixin, ZulipTestCase): with self.settings(MAX_LOGO_FILE_SIZE=0): result = self.client_post("/json/realm/logo", {'file': fp, 'night': ujson.dumps(self.night)}) - self.assert_json_error(result, "Uploaded file is larger than the allowed limit of 0 MB") + self.assert_json_error(result, "Uploaded file is larger than the allowed limit of 0 MiB") def tearDown(self) -> None: destroy_uploads() diff --git a/zerver/tornado/event_queue.py b/zerver/tornado/event_queue.py index 14f8c40070..120303af7f 100644 --- a/zerver/tornado/event_queue.py +++ b/zerver/tornado/event_queue.py @@ -566,13 +566,17 @@ def fetch_events(query: Mapping[str, Any]) -> Dict[str, Any]: client.event_queue.newest_pruned_id is not None and last_event_id < client.event_queue.newest_pruned_id ): - raise JsonableError(_("An event newer than %s has already been pruned!") % (last_event_id,)) + raise JsonableError(_("An event newer than {event_id} has already been pruned!").format( + event_id=last_event_id, + )) client.event_queue.prune(last_event_id) if ( client.event_queue.newest_pruned_id is not None and last_event_id != client.event_queue.newest_pruned_id ): - raise JsonableError(_("Event %s was not in this queue") % (last_event_id,)) + raise JsonableError(_("Event {event_id} was not in this queue").format( + event_id=last_event_id, + )) was_connected = client.finish_current_handler() if not client.event_queue.empty() or dont_block: diff --git a/zerver/views/hotspots.py b/zerver/views/hotspots.py index 929ee2692e..7a8d5f2abe 100644 --- a/zerver/views/hotspots.py +++ b/zerver/views/hotspots.py @@ -15,6 +15,6 @@ from zerver.models import UserProfile def mark_hotspot_as_read(request: HttpRequest, user: UserProfile, hotspot: str=REQ(validator=check_string)) -> HttpResponse: if hotspot not in ALL_HOTSPOTS: - return json_error(_('Unknown hotspot: %s') % (hotspot,)) + return json_error(_('Unknown hotspot: {}').format(hotspot)) do_mark_hotspot_as_read(user, hotspot) return json_success() diff --git a/zerver/views/messages.py b/zerver/views/messages.py index e27f7ce0c3..9b82f9ad96 100644 --- a/zerver/views/messages.py +++ b/zerver/views/messages.py @@ -852,8 +852,9 @@ def get_messages_backend(request: HttpRequest, user_profile: UserProfile, apply_markdown: bool=REQ(validator=check_bool, default=True)) -> HttpResponse: anchor = parse_anchor_value(anchor_val, use_first_unread_anchor_val) if num_before + num_after > MAX_MESSAGES_PER_FETCH: - return json_error(_("Too many messages requested (maximum %s).") - % (MAX_MESSAGES_PER_FETCH,)) + return json_error(_("Too many messages requested (maximum {}).").format( + MAX_MESSAGES_PER_FETCH, + )) if user_profile.realm.email_address_visibility != Realm.EMAIL_ADDRESS_VISIBILITY_EVERYONE: # If email addresses are only available to administrators, @@ -1218,7 +1219,7 @@ def mark_topic_as_read(request: HttpRequest, ) if not topic_exists: - raise JsonableError(_('No such topic \'%s\'') % (topic_name,)) + raise JsonableError(_('No such topic \'{}\'').format(topic_name)) count = do_mark_stream_messages_as_read(user_profile, request.client, stream, topic_name) @@ -1413,7 +1414,7 @@ def send_message_backend(request: HttpRequest, user_profile: UserProfile, try: realm = get_realm(realm_str) except Realm.DoesNotExist: - return json_error(_("Unknown organization '%s'") % (realm_str,)) + return json_error(_("Unknown organization '{}'").format(realm_str)) if client.name in ["zephyr_mirror", "irc_mirror", "jabber_mirror", "JabberMirror"]: # Here's how security works for mirroring: diff --git a/zerver/views/presence.py b/zerver/views/presence.py index 7d7e23d6be..0bb30ece03 100644 --- a/zerver/views/presence.py +++ b/zerver/views/presence.py @@ -30,7 +30,7 @@ def get_presence_backend(request: HttpRequest, user_profile: UserProfile, presence_dict = get_presence_for_user(target.id) if len(presence_dict) == 0: - return json_error(_('No presence data for %s') % (email,)) + return json_error(_('No presence data for {email}').format(email=email)) # For initial version, we just include the status and timestamp keys result = dict(presence=presence_dict[target.email]) @@ -77,7 +77,7 @@ def update_active_status_backend(request: HttpRequest, user_profile: UserProfile ) -> HttpResponse: status_val = UserPresence.status_from_string(status) if status_val is None: - raise JsonableError(_("Invalid status: %s") % (status,)) + raise JsonableError(_("Invalid status: {}").format(status)) elif user_profile.presence_enabled: update_user_presence(user_profile, request.client, timezone_now(), status_val, new_user_input) diff --git a/zerver/views/realm.py b/zerver/views/realm.py index 4297549310..91cef0e71d 100644 --- a/zerver/views/realm.py +++ b/zerver/views/realm.py @@ -94,7 +94,7 @@ def update_realm( # Additional validation/error checking beyond types go here, so # the entire request can succeed or fail atomically. if default_language is not None and default_language not in get_available_language_codes(): - raise JsonableError(_("Invalid language '%s'") % (default_language,)) + raise JsonableError(_("Invalid language '{}'").format(default_language)) if description is not None and len(description) > 1000: return json_error(_("Organization description is too long.")) if name is not None and len(name) > Realm.MAX_REALM_NAME_LENGTH: diff --git a/zerver/views/realm_domains.py b/zerver/views/realm_domains.py index 60d682ccb7..9c347bc522 100644 --- a/zerver/views/realm_domains.py +++ b/zerver/views/realm_domains.py @@ -26,8 +26,8 @@ def create_realm_domain(request: HttpRequest, user_profile: UserProfile, except ValidationError as e: return json_error(_('Invalid domain: {}').format(e.messages[0])) if RealmDomain.objects.filter(realm=user_profile.realm, domain=domain).exists(): - return json_error(_("The domain %(domain)s is already" - " a part of your organization.") % {'domain': domain}) + return json_error(_("The domain {domain} is already" + " a part of your organization.").format(domain=domain)) realm_domain = do_add_realm_domain(user_profile.realm, domain, allow_subdomains) return json_success({'new_domain': [realm_domain.id, realm_domain.domain]}) @@ -39,7 +39,7 @@ def patch_realm_domain(request: HttpRequest, user_profile: UserProfile, domain: realm_domain = RealmDomain.objects.get(realm=user_profile.realm, domain=domain) do_change_realm_domain(realm_domain, allow_subdomains) except RealmDomain.DoesNotExist: - return json_error(_('No entry found for domain %(domain)s.') % {'domain': domain}) + return json_error(_('No entry found for domain {domain}.').format(domain=domain)) return json_success() @require_realm_admin @@ -50,5 +50,5 @@ def delete_realm_domain(request: HttpRequest, user_profile: UserProfile, realm_domain = RealmDomain.objects.get(realm=user_profile.realm, domain=domain) do_remove_realm_domain(realm_domain) except RealmDomain.DoesNotExist: - return json_error(_('No entry found for domain %(domain)s.') % {'domain': domain}) + return json_error(_('No entry found for domain {domain}.').format(domain=domain)) return json_success() diff --git a/zerver/views/realm_emoji.py b/zerver/views/realm_emoji.py index 0758caceaa..911a702788 100644 --- a/zerver/views/realm_emoji.py +++ b/zerver/views/realm_emoji.py @@ -32,8 +32,9 @@ def upload_emoji(request: HttpRequest, user_profile: UserProfile, return json_error(_("You must upload exactly one file.")) emoji_file = list(request.FILES.values())[0] if (settings.MAX_EMOJI_FILE_SIZE * 1024 * 1024) < emoji_file.size: - return json_error(_("Uploaded file is larger than the allowed limit of %s MB") % ( - settings.MAX_EMOJI_FILE_SIZE)) + return json_error(_("Uploaded file is larger than the allowed limit of {} MiB").format( + settings.MAX_EMOJI_FILE_SIZE, + )) realm_emoji = check_add_realm_emoji(user_profile.realm, emoji_name, @@ -49,7 +50,7 @@ def delete_emoji(request: HttpRequest, user_profile: UserProfile, if not RealmEmoji.objects.filter(realm=user_profile.realm, name=emoji_name, deactivated=False).exists(): - raise JsonableError(_("Emoji '%s' does not exist") % (emoji_name,)) + raise JsonableError(_("Emoji '{}' does not exist").format(emoji_name)) check_emoji_admin(user_profile, emoji_name) do_remove_realm_emoji(user_profile.realm, emoji_name) return json_success() diff --git a/zerver/views/realm_export.py b/zerver/views/realm_export.py index 29d7bdcc58..cf3aefb297 100644 --- a/zerver/views/realm_export.py +++ b/zerver/views/realm_export.py @@ -43,8 +43,9 @@ def export_realm(request: HttpRequest, user: UserProfile) -> HttpResponse: property='messages_sent:client:day')) if (total_messages > MAX_MESSAGE_HISTORY or user.realm.currently_used_upload_space_bytes() > MAX_UPLOAD_QUOTA): - return json_error(_('Please request a manual export from %s.') % ( - settings.ZULIP_ADMINISTRATOR,)) + return json_error(_('Please request a manual export from {email}.').format( + email=settings.ZULIP_ADMINISTRATOR, + )) row = RealmAuditLog.objects.create(realm=realm, event_type=event_type, diff --git a/zerver/views/realm_icon.py b/zerver/views/realm_icon.py index 70dff5711b..cc6d4f44fb 100644 --- a/zerver/views/realm_icon.py +++ b/zerver/views/realm_icon.py @@ -20,8 +20,9 @@ def upload_icon(request: HttpRequest, user_profile: UserProfile) -> HttpResponse icon_file = list(request.FILES.values())[0] if ((settings.MAX_ICON_FILE_SIZE * 1024 * 1024) < icon_file.size): - return json_error(_("Uploaded file is larger than the allowed limit of %s MB") % ( - settings.MAX_ICON_FILE_SIZE)) + return json_error(_("Uploaded file is larger than the allowed limit of {} MiB").format( + settings.MAX_ICON_FILE_SIZE, + )) upload_icon_image(icon_file, user_profile) do_change_icon_source(user_profile.realm, user_profile.realm.ICON_UPLOADED) icon_url = realm_icon_url(user_profile.realm) diff --git a/zerver/views/realm_logo.py b/zerver/views/realm_logo.py index ea09ff8cd0..0641f6b395 100644 --- a/zerver/views/realm_logo.py +++ b/zerver/views/realm_logo.py @@ -24,8 +24,9 @@ def upload_logo(request: HttpRequest, user_profile: UserProfile, return json_error(_("You must upload exactly one logo.")) logo_file = list(request.FILES.values())[0] if ((settings.MAX_LOGO_FILE_SIZE * 1024 * 1024) < logo_file.size): - return json_error(_("Uploaded file is larger than the allowed limit of %s MB") % ( - settings.MAX_LOGO_FILE_SIZE)) + return json_error(_("Uploaded file is larger than the allowed limit of {} MiB").format( + settings.MAX_LOGO_FILE_SIZE, + )) upload_logo_image(logo_file, user_profile, night) do_change_logo_source(user_profile.realm, user_profile.realm.LOGO_UPLOADED, night) return json_success() diff --git a/zerver/views/streams.py b/zerver/views/streams.py index 5fc30d6846..06281b62a8 100644 --- a/zerver/views/streams.py +++ b/zerver/views/streams.py @@ -356,12 +356,14 @@ def you_were_just_subscribed_message(acting_user: UserProfile, stream_names: Set[str]) -> str: subscriptions = sorted(list(stream_names)) if len(subscriptions) == 1: - return _("@**%(full_name)s** subscribed you to the stream #**%(stream_name)s**.") % \ - {"full_name": acting_user.full_name, - "stream_name": subscriptions[0]} + return _("@**{full_name}** subscribed you to the stream #**{stream_name}**.").format( + full_name=acting_user.full_name, + stream_name=subscriptions[0], + ) - message = _("@**%(full_name)s** subscribed you to the following streams:") % \ - {"full_name": acting_user.full_name} + message = _("@**{full_name}** subscribed you to the following streams:").format( + full_name=acting_user.full_name, + ) message += "\n\n" for stream_name in subscriptions: message += f"* #**{stream_name}**\n" @@ -416,7 +418,9 @@ def add_subscriptions_backend( authorized_streams, unauthorized_streams = \ filter_stream_authorization(user_profile, existing_streams) if len(unauthorized_streams) > 0 and authorization_errors_fatal: - return json_error(_("Unable to access stream (%s).") % unauthorized_streams[0].name) + return json_error(_("Unable to access stream ({stream_name}).").format( + stream_name=unauthorized_streams[0].name, + )) # Newly created streams are also authorized for the creator streams = authorized_streams + created_streams @@ -519,9 +523,10 @@ def add_subscriptions_backend( sender=sender, stream=stream, topic=Realm.STREAM_EVENTS_NOTIFICATION_TOPIC, - content=_('Stream created by @_**%(user_name)s|%(user_id)d**.') % { - 'user_name': user_profile.full_name, - 'user_id': user_profile.id}, + content=_('Stream created by @_**{user_name}|{user_id}**.').format( + user_name=user_profile.full_name, + user_id=user_profile.id, + ), ), ) @@ -675,11 +680,11 @@ def update_subscription_properties_backend( value = change["value"] if property not in property_converters: - return json_error(_("Unknown subscription property: %s") % (property,)) + return json_error(_("Unknown subscription property: {}").format(property)) (stream, recipient, sub) = access_stream_by_id(user_profile, stream_id) if sub is None: - return json_error(_("Not subscribed to stream id %d") % (stream_id,)) + return json_error(_("Not subscribed to stream id {}").format(stream_id)) property_conversion = property_converters[property](property, value) if property_conversion: diff --git a/zerver/views/upload.py b/zerver/views/upload.py index eaa724e29b..fb8df3cabe 100644 --- a/zerver/views/upload.py +++ b/zerver/views/upload.py @@ -107,8 +107,9 @@ def upload_file_backend(request: HttpRequest, user_profile: UserProfile) -> Http user_file = list(request.FILES.values())[0] file_size = user_file.size if settings.MAX_FILE_UPLOAD_SIZE * 1024 * 1024 < file_size: - return json_error(_("Uploaded file is larger than the allowed limit of %s MB") % ( - settings.MAX_FILE_UPLOAD_SIZE)) + return json_error(_("Uploaded file is larger than the allowed limit of {} MiB").format( + settings.MAX_FILE_UPLOAD_SIZE, + )) check_upload_within_quota(user_profile.realm, file_size) uri = upload_message_image_from_request(request, user_file, user_profile) diff --git a/zerver/views/user_groups.py b/zerver/views/user_groups.py index 82d408a472..5af07b854b 100644 --- a/zerver/views/user_groups.py +++ b/zerver/views/user_groups.py @@ -99,7 +99,9 @@ def add_members_to_group_backend(request: HttpRequest, user_profile: UserProfile for user_profile in user_profiles: if user_profile.id in existing_member_ids: - raise JsonableError(_("User %s is already a member of this group") % (user_profile.id,)) + raise JsonableError(_("User {user_id} is already a member of this group").format( + user_id=user_profile.id, + )) bulk_add_members_to_user_group(user_group, user_profiles) return json_success() @@ -114,7 +116,7 @@ def remove_members_from_group_backend(request: HttpRequest, user_profile: UserPr group_member_ids = get_user_group_members(user_group) for member in members: if (member not in group_member_ids): - raise JsonableError(_("There is no member '%s' in this user group") % (member,)) + raise JsonableError(_("There is no member '{}' in this user group").format(member)) remove_members_from_user_group(user_group, user_profiles) return json_success() diff --git a/zerver/views/user_settings.py b/zerver/views/user_settings.py index b19fe08ff3..9437b4ca17 100644 --- a/zerver/views/user_settings.py +++ b/zerver/views/user_settings.py @@ -95,7 +95,7 @@ def json_change_settings(request: HttpRequest, user_profile: UserProfile, except RateLimited as e: secs_to_freedom = int(float(str(e))) return json_error( - _("You're making too many attempts! Try again in %s seconds.") % (secs_to_freedom,), + _("You're making too many attempts! Try again in {} seconds.").format(secs_to_freedom), ) if not check_password_strength(new_password): @@ -221,7 +221,7 @@ def json_change_notify_settings( if (notification_sound is not None and notification_sound not in get_available_notification_sounds()): - raise JsonableError(_("Invalid notification sound '%s'") % (notification_sound,)) + raise JsonableError(_("Invalid notification sound '{}'").format(notification_sound)) req_vars = {k: v for k, v in list(locals().items()) if k in user_profile.notification_setting_types} @@ -241,8 +241,9 @@ def set_avatar_backend(request: HttpRequest, user_profile: UserProfile) -> HttpR user_file = list(request.FILES.values())[0] if ((settings.MAX_AVATAR_FILE_SIZE * 1024 * 1024) < user_file.size): - return json_error(_("Uploaded file is larger than the allowed limit of %s MB") % ( - settings.MAX_AVATAR_FILE_SIZE)) + return json_error(_("Uploaded file is larger than the allowed limit of {} MiB").format( + settings.MAX_AVATAR_FILE_SIZE, + )) upload_avatar_image(user_file, user_profile, user_profile) do_change_avatar_fields(user_profile, UserProfile.AVATAR_FROM_USER) user_avatar_url = avatar_url(user_profile) diff --git a/zerver/views/users.py b/zerver/views/users.py index 2b88c66661..9c4db54fc5 100644 --- a/zerver/views/users.py +++ b/zerver/views/users.py @@ -479,8 +479,9 @@ def create_user_backend(request: HttpRequest, user_profile: UserProfile, try: email_allowed_for_realm(email, user_profile.realm) except DomainNotAllowedForRealmError: - return json_error(_("Email '%(email)s' not allowed in this organization") % - {'email': email}) + return json_error(_("Email '{email}' not allowed in this organization").format( + email=email, + )) except DisposableEmailError: return json_error(_("Disposable email addresses are not allowed in this organization")) except EmailContainsPlusError: @@ -488,7 +489,7 @@ def create_user_backend(request: HttpRequest, user_profile: UserProfile, try: get_user_by_delivery_email(email, user_profile.realm) - return json_error(_("Email '%s' already in use") % (email,)) + return json_error(_("Email '{}' already in use").format(email)) except UserProfile.DoesNotExist: pass diff --git a/zerver/webhooks/freshdesk/view.py b/zerver/webhooks/freshdesk/view.py index 4156d64244..7c07f46c0f 100644 --- a/zerver/webhooks/freshdesk/view.py +++ b/zerver/webhooks/freshdesk/view.py @@ -146,7 +146,7 @@ def api_freshdesk_webhook(request: HttpRequest, user_profile: UserProfile, if ticket_data.get(key) is None: logging.warning("Freshdesk webhook error. Payload was:") logging.warning(request.body) - return json_error(_("Missing key %s in JSON") % (key,)) + return json_error(_("Missing key {} in JSON").format(key)) ticket = TicketDict(ticket_data) diff --git a/zerver/webhooks/wordpress/view.py b/zerver/webhooks/wordpress/view.py index bdf0898343..3901e4a65d 100644 --- a/zerver/webhooks/wordpress/view.py +++ b/zerver/webhooks/wordpress/view.py @@ -42,7 +42,7 @@ def api_wordpress_webhook(request: HttpRequest, user_profile: UserProfile, data = WP_LOGIN_TEMPLATE.format(name=user_login) else: - return json_error(_("Unknown WordPress webhook action: %s") % (hook,)) + return json_error(_("Unknown WordPress webhook action: {}").format(hook)) topic = 'WordPress Notification' diff --git a/zilencer/views.py b/zilencer/views.py index ad659c4f1c..26e6530688 100644 --- a/zilencer/views.py +++ b/zilencer/views.py @@ -74,7 +74,7 @@ def register_remote_server( url_validator = URLValidator() url_validator('http://' + hostname) except ValidationError: - raise JsonableError(_('%s is not a valid hostname') % (hostname,)) + raise JsonableError(_('{} is not a valid hostname').format(hostname)) try: validate_email(contact_email) @@ -179,7 +179,7 @@ def validate_incoming_table_data(server: RemoteZulipServer, model: Any, last_id = get_last_id_from_server(server, model) for row in rows: if is_count_stat and row['property'] not in COUNT_STATS: - raise JsonableError(_("Invalid property %s") % (row['property'],)) + raise JsonableError(_("Invalid property {}").format(row['property'])) if row['id'] <= last_id: raise JsonableError(_("Data is out of order.")) last_id = row['id']