ruff: Fix UP006 Use list instead of List for type annotation.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
Anders Kaseorg
2024-07-11 17:30:17 -07:00
committed by Tim Abbott
parent c2214b3904
commit e08a24e47f
457 changed files with 3588 additions and 3857 deletions

View File

@@ -1,7 +1,7 @@
import logging
import time
from abc import ABC, abstractmethod
from typing import Dict, List, Optional, Set, Tuple, Type, cast
from typing import Optional, cast
import orjson
import redis
@@ -20,7 +20,7 @@ from zerver.models import UserProfile
# https://www.domaintools.com/resources/blog/rate-limiting-with-redis
client = get_redis_client()
rules: Dict[str, List[Tuple[int, int]]] = settings.RATE_LIMITING_RULES
rules: dict[str, list[tuple[int, int]]] = settings.RATE_LIMITING_RULES
logger = logging.getLogger(__name__)
@@ -30,13 +30,13 @@ class RateLimiterLockingError(Exception):
class RateLimitedObject(ABC):
def __init__(self, backend: Optional["Type[RateLimiterBackend]"] = None) -> None:
def __init__(self, backend: Optional["type[RateLimiterBackend]"] = None) -> None:
if backend is not None:
self.backend: Type[RateLimiterBackend] = backend
self.backend: type[RateLimiterBackend] = backend
else:
self.backend = RedisRateLimiterBackend
def rate_limit(self) -> Tuple[bool, float]:
def rate_limit(self) -> tuple[bool, float]:
# Returns (ratelimited, secs_to_freedom)
return self.backend.rate_limit_entity(
self.key(), self.get_rules(), self.max_api_calls(), self.max_api_window()
@@ -84,14 +84,14 @@ class RateLimitedObject(ABC):
"""Returns the API time window for the highest limit"""
return self.get_rules()[-1][0]
def api_calls_left(self) -> Tuple[int, float]:
def api_calls_left(self) -> tuple[int, float]:
"""Returns how many API calls in this range this client has, as well as when
the rate-limit will be reset to 0"""
max_window = self.max_api_window()
max_calls = self.max_api_calls()
return self.backend.get_api_calls_left(self.key(), max_window, max_calls)
def get_rules(self) -> List[Tuple[int, int]]:
def get_rules(self) -> list[tuple[int, int]]:
"""
This is a simple wrapper meant to protect against having to deal with
an empty list of rules, as it would require fiddling with that special case
@@ -106,7 +106,7 @@ class RateLimitedObject(ABC):
pass
@abstractmethod
def rules(self) -> List[Tuple[int, int]]:
def rules(self) -> list[tuple[int, int]]:
pass
@@ -116,7 +116,7 @@ class RateLimitedUser(RateLimitedObject):
self.rate_limits = user.rate_limits
self.domain = domain
if settings.RUNNING_INSIDE_TORNADO and domain in settings.RATE_LIMITING_DOMAINS_FOR_TORNADO:
backend: Optional[Type[RateLimiterBackend]] = TornadoInMemoryRateLimiterBackend
backend: Optional[type[RateLimiterBackend]] = TornadoInMemoryRateLimiterBackend
else:
backend = None
super().__init__(backend=backend)
@@ -126,10 +126,10 @@ class RateLimitedUser(RateLimitedObject):
return f"{type(self).__name__}:{self.user_id}:{self.domain}"
@override
def rules(self) -> List[Tuple[int, int]]:
def rules(self) -> list[tuple[int, int]]:
# user.rate_limits are general limits, applicable to the domain 'api_by_user'
if self.rate_limits != "" and self.domain == "api_by_user":
result: List[Tuple[int, int]] = []
result: list[tuple[int, int]] = []
for limit in self.rate_limits.split(","):
(seconds, requests) = limit.split(":", 2)
result.append((int(seconds), int(requests)))
@@ -142,7 +142,7 @@ class RateLimitedIPAddr(RateLimitedObject):
self.ip_addr = ip_addr
self.domain = domain
if settings.RUNNING_INSIDE_TORNADO and domain in settings.RATE_LIMITING_DOMAINS_FOR_TORNADO:
backend: Optional[Type[RateLimiterBackend]] = TornadoInMemoryRateLimiterBackend
backend: Optional[type[RateLimiterBackend]] = TornadoInMemoryRateLimiterBackend
else:
backend = None
super().__init__(backend=backend)
@@ -153,7 +153,7 @@ class RateLimitedIPAddr(RateLimitedObject):
return f"{type(self).__name__}:<{self.ip_addr}>:{self.domain}"
@override
def rules(self) -> List[Tuple[int, int]]:
def rules(self) -> list[tuple[int, int]]:
return rules[self.domain]
@@ -177,14 +177,14 @@ class RateLimiterBackend(ABC):
@abstractmethod
def get_api_calls_left(
cls, entity_key: str, range_seconds: int, max_calls: int
) -> Tuple[int, float]:
) -> tuple[int, float]:
pass
@classmethod
@abstractmethod
def rate_limit_entity(
cls, entity_key: str, rules: List[Tuple[int, int]], max_api_calls: int, max_api_window: int
) -> Tuple[bool, float]:
cls, entity_key: str, rules: list[tuple[int, int]], max_api_calls: int, max_api_window: int
) -> tuple[bool, float]:
# Returns (ratelimited, secs_to_freedom)
pass
@@ -192,15 +192,15 @@ class RateLimiterBackend(ABC):
class TornadoInMemoryRateLimiterBackend(RateLimiterBackend):
# reset_times[rule][key] is the time at which the event
# request from the rate-limited key will be accepted.
reset_times: Dict[Tuple[int, int], Dict[str, float]] = {}
reset_times: dict[tuple[int, int], dict[str, float]] = {}
# last_gc_time is the last time when the garbage was
# collected from reset_times for rule (time_window, max_count).
last_gc_time: Dict[Tuple[int, int], float] = {}
last_gc_time: dict[tuple[int, int], float] = {}
# timestamps_blocked_until[key] contains the timestamp
# up to which the key has been blocked manually.
timestamps_blocked_until: Dict[str, float] = {}
timestamps_blocked_until: dict[str, float] = {}
@classmethod
def _garbage_collect_for_rule(cls, now: float, time_window: int, max_count: int) -> None:
@@ -222,7 +222,7 @@ class TornadoInMemoryRateLimiterBackend(RateLimiterBackend):
del cls.reset_times[(time_window, max_count)]
@classmethod
def need_to_limit(cls, entity_key: str, time_window: int, max_count: int) -> Tuple[bool, float]:
def need_to_limit(cls, entity_key: str, time_window: int, max_count: int) -> tuple[bool, float]:
"""
Returns a tuple of `(rate_limited, time_till_free)`.
For simplicity, we have loosened the semantics here from
@@ -258,7 +258,7 @@ class TornadoInMemoryRateLimiterBackend(RateLimiterBackend):
@override
def get_api_calls_left(
cls, entity_key: str, range_seconds: int, max_calls: int
) -> Tuple[int, float]:
) -> tuple[int, float]:
now = time.time()
if (range_seconds, max_calls) in cls.reset_times and entity_key in cls.reset_times[
(range_seconds, max_calls)
@@ -291,8 +291,8 @@ class TornadoInMemoryRateLimiterBackend(RateLimiterBackend):
@classmethod
@override
def rate_limit_entity(
cls, entity_key: str, rules: List[Tuple[int, int]], max_api_calls: int, max_api_window: int
) -> Tuple[bool, float]:
cls, entity_key: str, rules: list[tuple[int, int]], max_api_calls: int, max_api_window: int
) -> tuple[bool, float]:
now = time.time()
if entity_key in cls.timestamps_blocked_until:
# Check whether the key is manually blocked.
@@ -314,7 +314,7 @@ class TornadoInMemoryRateLimiterBackend(RateLimiterBackend):
class RedisRateLimiterBackend(RateLimiterBackend):
@classmethod
def get_keys(cls, entity_key: str) -> List[str]:
def get_keys(cls, entity_key: str) -> list[str]:
return [
f"{redis_utils.REDIS_KEY_PREFIX}ratelimit:{entity_key}:{keytype}"
for keytype in ["list", "zset", "block"]
@@ -346,7 +346,7 @@ class RedisRateLimiterBackend(RateLimiterBackend):
@override
def get_api_calls_left(
cls, entity_key: str, range_seconds: int, max_calls: int
) -> Tuple[int, float]:
) -> tuple[int, float]:
list_key, set_key, _ = cls.get_keys(entity_key)
# Count the number of values in our sorted set
# that are between now and the cutoff
@@ -374,7 +374,7 @@ class RedisRateLimiterBackend(RateLimiterBackend):
return calls_left, time_reset - now
@classmethod
def is_ratelimited(cls, entity_key: str, rules: List[Tuple[int, int]]) -> Tuple[bool, float]:
def is_ratelimited(cls, entity_key: str, rules: list[tuple[int, int]]) -> tuple[bool, float]:
"""Returns a tuple of (rate_limited, time_till_free)"""
assert rules
list_key, set_key, blocking_key = cls.get_keys(entity_key)
@@ -390,7 +390,7 @@ class RedisRateLimiterBackend(RateLimiterBackend):
pipe.get(blocking_key)
pipe.ttl(blocking_key)
rule_timestamps: List[Optional[bytes]] = pipe.execute()
rule_timestamps: list[Optional[bytes]] = pipe.execute()
# Check if there is a manual block on this API key
blocking_ttl_b = rule_timestamps.pop()
@@ -477,8 +477,8 @@ class RedisRateLimiterBackend(RateLimiterBackend):
@classmethod
@override
def rate_limit_entity(
cls, entity_key: str, rules: List[Tuple[int, int]], max_api_calls: int, max_api_window: int
) -> Tuple[bool, float]:
cls, entity_key: str, rules: list[tuple[int, int]], max_api_calls: int, max_api_window: int
) -> tuple[bool, float]:
ratelimited, time = cls.is_ratelimited(entity_key, rules)
if not ratelimited:
@@ -515,7 +515,7 @@ class RateLimitedSpectatorAttachmentAccessByFile(RateLimitedObject):
return f"{type(self).__name__}:{self.path_id}"
@override
def rules(self) -> List[Tuple[int, int]]:
def rules(self) -> list[tuple[int, int]]:
return settings.RATE_LIMITING_RULES["spectator_attachment_access_by_file"]
@@ -531,7 +531,7 @@ def is_local_addr(addr: str) -> bool:
@cache_with_key(lambda: "tor_ip_addresses:", timeout=60 * 60)
@circuit(failure_threshold=2, recovery_timeout=60 * 10)
def get_tor_ips() -> Set[str]:
def get_tor_ips() -> set[str]:
if not settings.RATE_LIMIT_TOR_TOGETHER:
return set()