ruff: Enable B023 Function definition does not bind loop variable.

Python’s loop scoping is misdesigned, resulting in a very common
gotcha for functions that close over loop variables [1].  The general
problem is so bad that even the Go developers plan to break
compatibility in order to fix the same design mistake in their
language [2].

Enable the Ruff rule function-uses-loop-variable (B023) [3], which
conservatively prohibits functions from binding loop variables at all.

[1] https://docs.python-guide.org/writing/gotchas/#late-binding-closures
[2] https://go.dev/s/loopvar-design
[3] https://beta.ruff.rs/docs/rules/function-uses-loop-variable/

Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
Anders Kaseorg 2023-04-12 17:05:54 -07:00 committed by Tim Abbott
parent cf4791264c
commit 6988622fe8
11 changed files with 143 additions and 92 deletions

View File

@ -73,26 +73,24 @@ def get_realm_day_counts() -> Dict[str, Dict[str, Markup]]:
for row in rows: for row in rows:
counts[row["string_id"]][row["age"]] = row["cnt"] counts[row["string_id"]][row["age"]] = row["cnt"]
def format_count(cnt: int, style: Optional[str] = None) -> Markup:
if style is not None:
good_bad = style
elif cnt == min_cnt:
good_bad = "bad"
elif cnt == max_cnt:
good_bad = "good"
else:
good_bad = "neutral"
return Markup('<td class="number {good_bad}">{cnt}</td>').format(good_bad=good_bad, cnt=cnt)
result = {} result = {}
for string_id in counts: for string_id in counts:
raw_cnts = [counts[string_id].get(age, 0) for age in range(8)] raw_cnts = [counts[string_id].get(age, 0) for age in range(8)]
min_cnt = min(raw_cnts[1:]) min_cnt = min(raw_cnts[1:])
max_cnt = max(raw_cnts[1:]) max_cnt = max(raw_cnts[1:])
def format_count(cnt: int, style: Optional[str] = None) -> Markup:
if style is not None:
good_bad = style
elif cnt == min_cnt:
good_bad = "bad"
elif cnt == max_cnt:
good_bad = "good"
else:
good_bad = "neutral"
return Markup('<td class="number {good_bad}">{cnt}</td>').format(
good_bad=good_bad, cnt=cnt
)
cnts = format_count(raw_cnts[0], "neutral") + Markup().join(map(format_count, raw_cnts[1:])) cnts = format_count(raw_cnts[0], "neutral") + Markup().join(map(format_count, raw_cnts[1:]))
result[string_id] = dict(cnts=cnts) result[string_id] = dict(cnts=cnts)

View File

@ -134,7 +134,6 @@ ignore = [
"ANN401", # Dynamically typed expressions (typing.Any) are disallowed "ANN401", # Dynamically typed expressions (typing.Any) are disallowed
"B007", # Loop control variable not used within the loop body "B007", # Loop control variable not used within the loop body
"B008", # Do not perform function calls in argument defaults "B008", # Do not perform function calls in argument defaults
"B023", # Function definition does not bind loop variable
"B904", # Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling "B904", # Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling
"C408", # Unnecessary `dict` call (rewrite as a literal) "C408", # Unnecessary `dict` call (rewrite as a literal)
"COM812", # Trailing comma missing "COM812", # Trailing comma missing

View File

@ -107,6 +107,10 @@ def adjust_block_indentation(tokens: List[Token], fn: str) -> None:
def fix_indents_for_multi_line_tags(tokens: List[Token]) -> None: def fix_indents_for_multi_line_tags(tokens: List[Token]) -> None:
def fix(frag: str) -> str:
frag = frag.strip()
return continue_indent + frag if frag else ""
for token in tokens: for token in tokens:
if token.kind == "code": if token.kind == "code":
continue continue
@ -121,10 +125,6 @@ def fix_indents_for_multi_line_tags(tokens: List[Token]) -> None:
frags = token.new_s.split("\n") frags = token.new_s.split("\n")
def fix(frag: str) -> str:
frag = frag.strip()
return continue_indent + frag if frag else ""
token.new_s = frags[0] + "\n" + "\n".join(fix(frag) for frag in frags[1:]) token.new_s = frags[0] + "\n" + "\n".join(fix(frag) for frag in frags[1:])

View File

@ -489,6 +489,20 @@ def validate(fn: Optional[str] = None, text: Optional[str] = None) -> List[Token
def ensure_matching_indentation(fn: str, tokens: List[Token], lines: List[str]) -> None: def ensure_matching_indentation(fn: str, tokens: List[Token], lines: List[str]) -> None:
def has_bad_indentation() -> bool:
is_inline_tag = start_tag in HTML_INLINE_TAGS and start_token.kind == "html_start"
if end_line > start_line + 1:
if is_inline_tag:
end_row_text = lines[end_line - 1]
if end_row_text.lstrip().startswith(end_token.s) and end_col != start_col:
return True
else:
if end_col != start_col:
return True
return False
for token in tokens: for token in tokens:
if token.start_token is None: if token.start_token is None:
continue continue
@ -503,20 +517,6 @@ def ensure_matching_indentation(fn: str, tokens: List[Token], lines: List[str])
end_line = end_token.line end_line = end_token.line
end_col = end_token.col end_col = end_token.col
def has_bad_indentation() -> bool:
is_inline_tag = start_tag in HTML_INLINE_TAGS and start_token.kind == "html_start"
if end_line > start_line + 1:
if is_inline_tag:
end_row_text = lines[end_line - 1]
if end_row_text.lstrip().startswith(end_token.s) and end_col != start_col:
return True
else:
if end_col != start_col:
return True
return False
if has_bad_indentation(): if has_bad_indentation():
raise TemplateParserError( raise TemplateParserError(
f""" f"""

View File

@ -316,6 +316,9 @@ def bulk_get_subscriber_user_ids(
) -> Dict[int, List[int]]: ) -> Dict[int, List[int]]:
"""sub_dict maps stream_id => whether the user is subscribed to that stream.""" """sub_dict maps stream_id => whether the user is subscribed to that stream."""
target_stream_dicts = [] target_stream_dicts = []
is_subscribed: bool
check_user_subscribed = lambda user_profile: is_subscribed
for stream_dict in stream_dicts: for stream_dict in stream_dicts:
stream_id = stream_dict["id"] stream_id = stream_dict["id"]
is_subscribed = stream_id in subscribed_stream_ids is_subscribed = stream_id in subscribed_stream_ids
@ -324,7 +327,7 @@ def bulk_get_subscriber_user_ids(
validate_user_access_to_subscribers_helper( validate_user_access_to_subscribers_helper(
user_profile, user_profile,
stream_dict, stream_dict,
lambda user_profile: is_subscribed, check_user_subscribed,
) )
except JsonableError: except JsonableError:
continue continue

View File

@ -4,6 +4,7 @@ from typing import Any, Callable, Optional
from django.conf import settings from django.conf import settings
from django.core.management.base import BaseCommand, CommandError, CommandParser from django.core.management.base import BaseCommand, CommandError, CommandParser
from returns.curry import partial
from zerver.lib.rate_limiter import RateLimitedUser, client from zerver.lib.rate_limiter import RateLimitedUser, client
from zerver.models import get_user_profile_by_id from zerver.models import get_user_profile_by_id
@ -61,7 +62,7 @@ than max_api_calls! (trying to trim) %s %s",
lists = client.keys(wildcard_list) lists = client.keys(wildcard_list)
for list_name in lists: for list_name in lists:
self._check_within_range(list_name, lambda: client.llen(list_name), trim_func) self._check_within_range(list_name, partial(client.llen, list_name), trim_func)
zsets = client.keys(wildcard_zset) zsets = client.keys(wildcard_zset)
for zset in zsets: for zset in zsets:
@ -70,5 +71,7 @@ than max_api_calls! (trying to trim) %s %s",
# elements to trim. We'd have to go through every list item and take # elements to trim. We'd have to go through every list item and take
# the intersection. The best we can do is expire it # the intersection. The best we can do is expire it
self._check_within_range( self._check_within_range(
zset, lambda: client.zcount(zset, 0, now), lambda key, max_calls: None zset,
partial(client.zcount, zset, 0, now),
lambda key, max_calls: None,
) )

View File

@ -14,6 +14,7 @@ from unittest import mock
import orjson import orjson
from dateutil.parser import parse as dateparser from dateutil.parser import parse as dateparser
from django.utils.timezone import now as timezone_now from django.utils.timezone import now as timezone_now
from returns.curry import partial
from zerver.actions.alert_words import do_add_alert_words, do_remove_alert_words from zerver.actions.alert_words import do_add_alert_words, do_remove_alert_words
from zerver.actions.bots import ( from zerver.actions.bots import (
@ -457,21 +458,21 @@ class NormalActionsTest(BaseAction):
for i in range(3): for i in range(3):
content = "mentioning... @**" + user.full_name + "** hello " + str(i) content = "mentioning... @**" + user.full_name + "** hello " + str(i)
self.verify_action( self.verify_action(
lambda: self.send_stream_message(self.example_user("cordelia"), "Verona", content), partial(self.send_stream_message, self.example_user("cordelia"), "Verona", content),
) )
def test_topic_wildcard_mentioned_send_message_events(self) -> None: def test_topic_wildcard_mentioned_send_message_events(self) -> None:
for i in range(3): for i in range(3):
content = "mentioning... @**topic** hello " + str(i) content = "mentioning... @**topic** hello " + str(i)
self.verify_action( self.verify_action(
lambda: self.send_stream_message(self.example_user("cordelia"), "Verona", content), partial(self.send_stream_message, self.example_user("cordelia"), "Verona", content),
) )
def test_stream_wildcard_mentioned_send_message_events(self) -> None: def test_stream_wildcard_mentioned_send_message_events(self) -> None:
for i in range(3): for i in range(3):
content = "mentioning... @**all** hello " + str(i) content = "mentioning... @**all** hello " + str(i)
self.verify_action( self.verify_action(
lambda: self.send_stream_message(self.example_user("cordelia"), "Verona", content), partial(self.send_stream_message, self.example_user("cordelia"), "Verona", content),
) )
def test_pm_send_message_events(self) -> None: def test_pm_send_message_events(self) -> None:
@ -747,12 +748,12 @@ class NormalActionsTest(BaseAction):
) )
self.verify_action( self.verify_action(
lambda: do_update_message_flags(user_profile, "add", "read", [message]), partial(do_update_message_flags, user_profile, "add", "read", [message]),
state_change_expected=True, state_change_expected=True,
) )
events = self.verify_action( events = self.verify_action(
lambda: do_update_message_flags(user_profile, "remove", "read", [message]), partial(do_update_message_flags, user_profile, "remove", "read", [message]),
state_change_expected=True, state_change_expected=True,
) )
check_update_message_flags_remove("events[0]", events[0]) check_update_message_flags_remove("events[0]", events[0])
@ -761,12 +762,14 @@ class NormalActionsTest(BaseAction):
from_user=user_profile, to_user=self.example_user("cordelia"), content=content from_user=user_profile, to_user=self.example_user("cordelia"), content=content
) )
self.verify_action( self.verify_action(
lambda: do_update_message_flags(user_profile, "add", "read", [personal_message]), partial(do_update_message_flags, user_profile, "add", "read", [personal_message]),
state_change_expected=True, state_change_expected=True,
) )
events = self.verify_action( events = self.verify_action(
lambda: do_update_message_flags(user_profile, "remove", "read", [personal_message]), partial(
do_update_message_flags, user_profile, "remove", "read", [personal_message]
),
state_change_expected=True, state_change_expected=True,
) )
check_update_message_flags_remove("events[0]", events[0]) check_update_message_flags_remove("events[0]", events[0])
@ -778,12 +781,12 @@ class NormalActionsTest(BaseAction):
) )
self.verify_action( self.verify_action(
lambda: do_update_message_flags(user_profile, "add", "read", [huddle_message]), partial(do_update_message_flags, user_profile, "add", "read", [huddle_message]),
state_change_expected=True, state_change_expected=True,
) )
events = self.verify_action( events = self.verify_action(
lambda: do_update_message_flags(user_profile, "remove", "read", [huddle_message]), partial(do_update_message_flags, user_profile, "remove", "read", [huddle_message]),
state_change_expected=True, state_change_expected=True,
) )
check_update_message_flags_remove("events[0]", events[0]) check_update_message_flags_remove("events[0]", events[0])
@ -1712,8 +1715,11 @@ class NormalActionsTest(BaseAction):
): ):
with fake_backends(): with fake_backends():
events = self.verify_action( events = self.verify_action(
lambda: do_set_realm_authentication_methods( partial(
self.user_profile.realm, auth_method_dict, acting_user=None do_set_realm_authentication_methods,
self.user_profile.realm,
auth_method_dict,
acting_user=None,
) )
) )
@ -1727,8 +1733,14 @@ class NormalActionsTest(BaseAction):
) )
for pinned in (True, False): for pinned in (True, False):
events = self.verify_action( events = self.verify_action(
lambda: do_change_subscription_property( partial(
self.user_profile, sub, stream, "pin_to_top", pinned, acting_user=None do_change_subscription_property,
self.user_profile,
sub,
stream,
"pin_to_top",
pinned,
acting_user=None,
) )
) )
check_subscription_update( check_subscription_update(
@ -1795,8 +1807,14 @@ class NormalActionsTest(BaseAction):
# First test with notification_settings_null enabled # First test with notification_settings_null enabled
for value in (True, False): for value in (True, False):
events = self.verify_action( events = self.verify_action(
lambda: do_change_subscription_property( partial(
self.user_profile, sub, stream, setting_name, value, acting_user=None do_change_subscription_property,
self.user_profile,
sub,
stream,
setting_name,
value,
acting_user=None,
), ),
notification_settings_null=True, notification_settings_null=True,
) )
@ -1809,8 +1827,14 @@ class NormalActionsTest(BaseAction):
for value in (True, False): for value in (True, False):
events = self.verify_action( events = self.verify_action(
lambda: do_change_subscription_property( partial(
self.user_profile, sub, stream, setting_name, value, acting_user=None do_change_subscription_property,
self.user_profile,
sub,
stream,
setting_name,
value,
acting_user=None,
) )
) )
check_subscription_update( check_subscription_update(
@ -1825,7 +1849,8 @@ class NormalActionsTest(BaseAction):
for notifications_stream, notifications_stream_id in ((stream, stream.id), (None, -1)): for notifications_stream, notifications_stream_id in ((stream, stream.id), (None, -1)):
events = self.verify_action( events = self.verify_action(
lambda: do_set_realm_notifications_stream( partial(
do_set_realm_notifications_stream,
self.user_profile.realm, self.user_profile.realm,
notifications_stream, notifications_stream,
notifications_stream_id, notifications_stream_id,
@ -1842,7 +1867,8 @@ class NormalActionsTest(BaseAction):
(None, -1), (None, -1),
): ):
events = self.verify_action( events = self.verify_action(
lambda: do_set_realm_signup_notifications_stream( partial(
do_set_realm_signup_notifications_stream,
self.user_profile.realm, self.user_profile.realm,
signup_notifications_stream, signup_notifications_stream,
signup_notifications_stream_id, signup_notifications_stream_id,
@ -1871,7 +1897,7 @@ class NormalActionsTest(BaseAction):
num_events = 5 num_events = 5
events = self.verify_action( events = self.verify_action(
lambda: do_change_user_role(self.user_profile, role, acting_user=None), partial(do_change_user_role, self.user_profile, role, acting_user=None),
num_events=num_events, num_events=num_events,
) )
check_realm_user_update("events[0]", events[0], "role") check_realm_user_update("events[0]", events[0], "role")
@ -1919,7 +1945,7 @@ class NormalActionsTest(BaseAction):
else: else:
num_events = 5 num_events = 5
events = self.verify_action( events = self.verify_action(
lambda: do_change_user_role(self.user_profile, role, acting_user=None), partial(do_change_user_role, self.user_profile, role, acting_user=None),
num_events=num_events, num_events=num_events,
) )
check_realm_user_update("events[0]", events[0], "role") check_realm_user_update("events[0]", events[0], "role")
@ -1947,7 +1973,7 @@ class NormalActionsTest(BaseAction):
do_change_user_role(self.user_profile, UserProfile.ROLE_MEMBER, acting_user=None) do_change_user_role(self.user_profile, UserProfile.ROLE_MEMBER, acting_user=None)
for role in [UserProfile.ROLE_MODERATOR, UserProfile.ROLE_MEMBER]: for role in [UserProfile.ROLE_MODERATOR, UserProfile.ROLE_MEMBER]:
events = self.verify_action( events = self.verify_action(
lambda: do_change_user_role(self.user_profile, role, acting_user=None), partial(do_change_user_role, self.user_profile, role, acting_user=None),
num_events=4, num_events=4,
) )
check_realm_user_update("events[0]", events[0], "role") check_realm_user_update("events[0]", events[0], "role")
@ -1982,7 +2008,7 @@ class NormalActionsTest(BaseAction):
else: else:
num_events = 5 num_events = 5
events = self.verify_action( events = self.verify_action(
lambda: do_change_user_role(self.user_profile, role, acting_user=None), partial(do_change_user_role, self.user_profile, role, acting_user=None),
num_events=num_events, num_events=num_events,
) )
check_realm_user_update("events[0]", events[0], "role") check_realm_user_update("events[0]", events[0], "role")
@ -2028,7 +2054,8 @@ class NormalActionsTest(BaseAction):
for setting_value in [True, False]: for setting_value in [True, False]:
events = self.verify_action( events = self.verify_action(
lambda: do_change_user_setting( partial(
do_change_user_setting,
self.user_profile, self.user_profile,
notification_setting, notification_setting,
setting_value, setting_value,
@ -2042,7 +2069,8 @@ class NormalActionsTest(BaseAction):
# Also test with notification_settings_null=True # Also test with notification_settings_null=True
events = self.verify_action( events = self.verify_action(
lambda: do_change_user_setting( partial(
do_change_user_setting,
self.user_profile, self.user_profile,
notification_setting, notification_setting,
setting_value, setting_value,
@ -2070,8 +2098,12 @@ class NormalActionsTest(BaseAction):
for val in [True, False]: for val in [True, False]:
events = self.verify_action( events = self.verify_action(
lambda: do_change_user_setting( partial(
self.user_profile, presence_enabled_setting, val, acting_user=self.user_profile do_change_user_setting,
self.user_profile,
presence_enabled_setting,
val,
acting_user=self.user_profile,
), ),
num_events=3, num_events=3,
) )
@ -2545,7 +2577,7 @@ class NormalActionsTest(BaseAction):
stream = self.make_stream(old_name) stream = self.make_stream(old_name)
self.subscribe(self.user_profile, stream.name) self.subscribe(self.user_profile, stream.name)
action = lambda: do_rename_stream(stream, new_name, self.user_profile) action = partial(do_rename_stream, stream, new_name, self.user_profile)
events = self.verify_action(action, num_events=3, include_streams=include_streams) events = self.verify_action(action, num_events=3, include_streams=include_streams)
check_stream_update("events[0]", events[0]) check_stream_update("events[0]", events[0])
@ -2574,14 +2606,14 @@ class NormalActionsTest(BaseAction):
def test_deactivate_stream_neversubscribed(self) -> None: def test_deactivate_stream_neversubscribed(self) -> None:
for i, include_streams in enumerate([True, False]): for i, include_streams in enumerate([True, False]):
stream = self.make_stream(f"stream{i}") stream = self.make_stream(f"stream{i}")
action = lambda: do_deactivate_stream(stream, acting_user=None) action = partial(do_deactivate_stream, stream, acting_user=None)
events = self.verify_action(action, include_streams=include_streams) events = self.verify_action(action, include_streams=include_streams)
check_stream_delete("events[0]", events[0]) check_stream_delete("events[0]", events[0])
self.assertIsNone(events[0]["streams"][0]["stream_weekly_traffic"]) self.assertIsNone(events[0]["streams"][0]["stream_weekly_traffic"])
def test_subscribe_other_user_never_subscribed(self) -> None: def test_subscribe_other_user_never_subscribed(self) -> None:
for i, include_streams in enumerate([True, False]): for i, include_streams in enumerate([True, False]):
action = lambda: self.subscribe(self.example_user("othello"), f"test_stream{i}") action = partial(self.subscribe, self.example_user("othello"), f"test_stream{i}")
events = self.verify_action(action, num_events=2, include_streams=True) events = self.verify_action(action, num_events=2, include_streams=True)
check_subscription_peer_add("events[1]", events[1]) check_subscription_peer_add("events[1]", events[1])
@ -2938,8 +2970,12 @@ class RealmPropertyActionTest(BaseAction):
num_events = 1 num_events = 1
events = self.verify_action( events = self.verify_action(
lambda: do_set_realm_property( partial(
self.user_profile.realm, name, val, acting_user=self.user_profile do_set_realm_property,
self.user_profile.realm,
name,
val,
acting_user=self.user_profile,
), ),
state_change_expected=state_change_expected, state_change_expected=state_change_expected,
num_events=num_events, num_events=num_events,
@ -3009,7 +3045,8 @@ class RealmPropertyActionTest(BaseAction):
new_group_id = user_group.id new_group_id = user_group.id
events = self.verify_action( events = self.verify_action(
lambda: do_change_realm_permission_group_setting( partial(
do_change_realm_permission_group_setting,
self.user_profile.realm, self.user_profile.realm,
setting_name, setting_name,
user_group, user_group,
@ -3089,8 +3126,12 @@ class RealmPropertyActionTest(BaseAction):
now = timezone_now() now = timezone_now()
state_change_expected = True state_change_expected = True
events = self.verify_action( events = self.verify_action(
lambda: do_set_realm_user_default_setting( partial(
realm_user_default, name, val, acting_user=self.user_profile do_set_realm_user_default_setting,
realm_user_default,
name,
val,
acting_user=self.user_profile,
), ),
state_change_expected=state_change_expected, state_change_expected=state_change_expected,
) )
@ -3174,8 +3215,12 @@ class UserDisplayActionTest(BaseAction):
num_events = 3 num_events = 3
events = self.verify_action( events = self.verify_action(
lambda: do_change_user_setting( partial(
self.user_profile, setting_name, value, acting_user=self.user_profile do_change_user_setting,
self.user_profile,
setting_name,
value,
acting_user=self.user_profile,
), ),
num_events=num_events, num_events=num_events,
user_settings_object=user_settings_object, user_settings_object=user_settings_object,
@ -3201,8 +3246,12 @@ class UserDisplayActionTest(BaseAction):
for value in values: for value in values:
events = self.verify_action( events = self.verify_action(
lambda: do_change_user_setting( partial(
self.user_profile, "timezone", value, acting_user=self.user_profile do_change_user_setting,
self.user_profile,
"timezone",
value,
acting_user=self.user_profile,
), ),
num_events=num_events, num_events=num_events,
) )

View File

@ -14,6 +14,7 @@ from django.http import HttpRequest
from django.test import override_settings from django.test import override_settings
from django.urls import reverse from django.urls import reverse
from django.utils.timezone import now as timezone_now from django.utils.timezone import now as timezone_now
from returns.curry import partial
from confirmation import settings as confirmation_settings from confirmation import settings as confirmation_settings
from confirmation.models import ( from confirmation.models import (
@ -1116,7 +1117,7 @@ so we didn't send them an invitation. We did send invitations to everyone else!"
for email in existing: for email in existing:
self.assertRaises( self.assertRaises(
PreregistrationUser.DoesNotExist, PreregistrationUser.DoesNotExist,
lambda: PreregistrationUser.objects.get(email=email), partial(PreregistrationUser.objects.get, email=email),
) )
for email in new: for email in new:
self.assertTrue(PreregistrationUser.objects.get(email=email)) self.assertTrue(PreregistrationUser.objects.get(email=email))

View File

@ -1350,20 +1350,20 @@ class EmojiTest(UploadSerializeMixin, ZulipTestCase):
with self.assertRaises(BadImageError): with self.assertRaises(BadImageError):
resize_emoji(corrupted_img_data) resize_emoji(corrupted_img_data)
def test_resize(size: int = 50) -> None:
resized_img_data, is_animated, still_img_data = resize_emoji(
animated_large_img_data, size=50
)
im = Image.open(io.BytesIO(resized_img_data))
self.assertEqual((size, size), im.size)
self.assertTrue(is_animated)
assert still_img_data
still_image = Image.open(io.BytesIO(still_img_data))
self.assertEqual((50, 50), still_image.size)
for img_format in ("gif", "png"): for img_format in ("gif", "png"):
animated_large_img_data = read_test_image_file(f"animated_large_img.{img_format}") animated_large_img_data = read_test_image_file(f"animated_large_img.{img_format}")
def test_resize(size: int = 50) -> None:
resized_img_data, is_animated, still_img_data = resize_emoji(
animated_large_img_data, size=50
)
im = Image.open(io.BytesIO(resized_img_data))
self.assertEqual((size, size), im.size)
self.assertTrue(is_animated)
assert still_img_data
still_image = Image.open(io.BytesIO(still_img_data))
self.assertEqual((50, 50), still_image.size)
# Test an image larger than max is resized # Test an image larger than max is resized
with patch("zerver.lib.upload.base.MAX_EMOJI_GIF_SIZE", 128): with patch("zerver.lib.upload.base.MAX_EMOJI_GIF_SIZE", 128):
test_resize() test_resize()

View File

@ -9,6 +9,7 @@ from django.conf import settings
from django.db import transaction from django.db import transaction
from requests.adapters import ConnectionError, HTTPAdapter from requests.adapters import ConnectionError, HTTPAdapter
from requests.models import PreparedRequest, Response from requests.models import PreparedRequest, Response
from returns.curry import partial
from urllib3.util import Retry from urllib3.util import Retry
from zerver.lib.queue import queue_json_publish from zerver.lib.queue import queue_json_publish
@ -185,7 +186,7 @@ def send_event(
queue_json_publish( queue_json_publish(
notify_tornado_queue_name(port), notify_tornado_queue_name(port),
dict(event=event, users=port_users), dict(event=event, users=port_users),
lambda *args, **kwargs: send_notification_http(port, *args, **kwargs), partial(send_notification_http, port),
) )

View File

@ -70,10 +70,7 @@ class Command(BaseCommand):
lambda: queue_json_publish(queue_name, {}), lambda: queue_json_publish(queue_name, {}),
number=count, number=count,
) )
duration = timeit( duration = timeit(worker.start, number=1)
lambda: worker.start(),
number=1,
)
print(f" {i}/{reps}: {count}/{duration}s = {count / duration}/s") print(f" {i}/{reps}: {count}/{duration}s = {count / duration}/s")
total_time += duration total_time += duration
writer.writerow( writer.writerow(