Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.

Commit d0c713c

Browse files
squahtxDavid Robertson
and
David Robertson
authored
Return read-only collections from @cached methods (#13755)
It's important that collections returned from `@cached` methods are not modified, otherwise future retrievals from the cache will return the modified collection. This applies to the return values from `@cached` methods and the values inside the dictionaries returned by `@cachedList` methods. It's not necessary for the dictionaries returned by `@cachedList` methods themselves to be read-only. Signed-off-by: Sean Quah <[email protected]> Co-authored-by: David Robertson <[email protected]>
1 parent 14be78d commit d0c713c

27 files changed

+98
-77
lines changed

changelog.d/13755.misc

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Re-type hint some collections as read-only.

synapse/app/phone_stats_home.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import math
1616
import resource
1717
import sys
18-
from typing import TYPE_CHECKING, List, Sized, Tuple
18+
from typing import TYPE_CHECKING, List, Mapping, Sized, Tuple
1919

2020
from prometheus_client import Gauge
2121

@@ -194,7 +194,7 @@ def performance_stats_init() -> None:
194194
@wrap_as_background_process("generate_monthly_active_users")
195195
async def generate_monthly_active_users() -> None:
196196
current_mau_count = 0
197-
current_mau_count_by_service = {}
197+
current_mau_count_by_service: Mapping[str, int] = {}
198198
reserved_users: Sized = ()
199199
store = hs.get_datastores().main
200200
if hs.config.server.limit_usage_by_mau or hs.config.server.mau_stats_only:

synapse/config/room_directory.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
# See the License for the specific language governing permissions and
1414
# limitations under the License.
1515

16-
from typing import Any, List
16+
from typing import Any, Collection
1717

1818
from matrix_common.regex import glob_to_regex
1919

@@ -70,7 +70,7 @@ def is_alias_creation_allowed(self, user_id: str, room_id: str, alias: str) -> b
7070
return False
7171

7272
def is_publishing_room_allowed(
73-
self, user_id: str, room_id: str, aliases: List[str]
73+
self, user_id: str, room_id: str, aliases: Collection[str]
7474
) -> bool:
7575
"""Checks if the given user is allowed to publish the room
7676
@@ -122,7 +122,7 @@ def __init__(self, option_name: str, rule: JsonDict):
122122
except Exception as e:
123123
raise ConfigError("Failed to parse glob into regex") from e
124124

125-
def matches(self, user_id: str, room_id: str, aliases: List[str]) -> bool:
125+
def matches(self, user_id: str, room_id: str, aliases: Collection[str]) -> bool:
126126
"""Tests if this rule matches the given user_id, room_id and aliases.
127127
128128
Args:

synapse/events/builder.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414
import logging
15-
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union
15+
from typing import TYPE_CHECKING, Any, Collection, Dict, List, Optional, Tuple, Union
1616

1717
import attr
1818
from signedjson.types import SigningKey
@@ -103,7 +103,7 @@ def is_state(self) -> bool:
103103

104104
async def build(
105105
self,
106-
prev_event_ids: List[str],
106+
prev_event_ids: Collection[str],
107107
auth_event_ids: Optional[List[str]],
108108
depth: Optional[int] = None,
109109
) -> EventBase:
@@ -136,7 +136,7 @@ async def build(
136136

137137
format_version = self.room_version.event_format
138138
# The types of auth/prev events changes between event versions.
139-
prev_events: Union[List[str], List[Tuple[str, Dict[str, str]]]]
139+
prev_events: Union[Collection[str], List[Tuple[str, Dict[str, str]]]]
140140
auth_events: Union[List[str], List[Tuple[str, Dict[str, str]]]]
141141
if format_version == EventFormatVersions.ROOM_V1_V2:
142142
auth_events = await self._store.add_event_hashes(auth_event_ids)

synapse/federation/federation_server.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
Collection,
2424
Dict,
2525
List,
26+
Mapping,
2627
Optional,
2728
Tuple,
2829
Union,
@@ -1512,7 +1513,7 @@ async def on_query(self, query_type: str, args: dict) -> JsonDict:
15121513
def _get_event_ids_for_partial_state_join(
15131514
join_event: EventBase,
15141515
prev_state_ids: StateMap[str],
1515-
summary: Dict[str, MemberSummary],
1516+
summary: Mapping[str, MemberSummary],
15161517
) -> Collection[str]:
15171518
"""Calculate state to be returned in a partial_state send_join
15181519

synapse/handlers/directory.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
import logging
1616
import string
17-
from typing import TYPE_CHECKING, Iterable, List, Optional
17+
from typing import TYPE_CHECKING, Iterable, List, Optional, Sequence
1818

1919
from typing_extensions import Literal
2020

@@ -486,7 +486,7 @@ async def edit_published_room_list(
486486
)
487487
if canonical_alias:
488488
# Ensure we do not mutate room_aliases.
489-
room_aliases = room_aliases + [canonical_alias]
489+
room_aliases = list(room_aliases) + [canonical_alias]
490490

491491
if not self.config.roomdirectory.is_publishing_room_allowed(
492492
user_id, room_id, room_aliases
@@ -529,7 +529,7 @@ async def edit_published_appservice_room_list(
529529

530530
async def get_aliases_for_room(
531531
self, requester: Requester, room_id: str
532-
) -> List[str]:
532+
) -> Sequence[str]:
533533
"""
534534
Get a list of the aliases that currently point to this room on this server
535535
"""

synapse/handlers/receipts.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414
import logging
15-
from typing import TYPE_CHECKING, Iterable, List, Optional, Tuple
15+
from typing import TYPE_CHECKING, Iterable, List, Optional, Sequence, Tuple
1616

1717
from synapse.api.constants import EduTypes, ReceiptTypes
1818
from synapse.appservice import ApplicationService
@@ -189,7 +189,7 @@ def __init__(self, hs: "HomeServer"):
189189

190190
@staticmethod
191191
def filter_out_private_receipts(
192-
rooms: List[JsonDict], user_id: str
192+
rooms: Sequence[JsonDict], user_id: str
193193
) -> List[JsonDict]:
194194
"""
195195
Filters a list of serialized receipts (as returned by /sync and /initialSync)

synapse/handlers/room.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1928,6 +1928,6 @@ async def shutdown_room(
19281928
return {
19291929
"kicked_users": kicked_users,
19301930
"failed_to_kick_users": failed_to_kick_users,
1931-
"local_aliases": aliases_for_room,
1931+
"local_aliases": list(aliases_for_room),
19321932
"new_room_id": new_room_id,
19331933
}

synapse/handlers/sync.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1519,7 +1519,7 @@ async def generate_sync_result(
15191519
one_time_keys_count = await self.store.count_e2e_one_time_keys(
15201520
user_id, device_id
15211521
)
1522-
unused_fallback_key_types = (
1522+
unused_fallback_key_types = list(
15231523
await self.store.get_e2e_unused_fallback_key_types(user_id, device_id)
15241524
)
15251525

@@ -2301,7 +2301,7 @@ async def _generate_room_entry(
23012301
sync_result_builder: "SyncResultBuilder",
23022302
room_builder: "RoomSyncResultBuilder",
23032303
ephemeral: List[JsonDict],
2304-
tags: Optional[Dict[str, Dict[str, Any]]],
2304+
tags: Optional[Mapping[str, Mapping[str, Any]]],
23052305
account_data: Mapping[str, JsonDict],
23062306
always_include: bool = False,
23072307
) -> None:

synapse/push/bulk_push_rule_evaluator.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
List,
2323
Mapping,
2424
Optional,
25+
Sequence,
2526
Set,
2627
Tuple,
2728
Union,
@@ -149,7 +150,7 @@ async def _get_rules_for_event(
149150
# little, we can skip fetching a huge number of push rules in large rooms.
150151
# This helps make joins and leaves faster.
151152
if event.type == EventTypes.Member:
152-
local_users = []
153+
local_users: Sequence[str] = []
153154
# We never notify a user about their own actions. This is enforced in
154155
# `_action_for_event_by_user` in the loop over `rules_by_user`, but we
155156
# do the same check here to avoid unnecessary DB queries.
@@ -184,7 +185,6 @@ async def _get_rules_for_event(
184185
if event.type == EventTypes.Member and event.membership == Membership.INVITE:
185186
invited = event.state_key
186187
if invited and self.hs.is_mine_id(invited) and invited not in local_users:
187-
local_users = list(local_users)
188188
local_users.append(invited)
189189

190190
if not local_users:

synapse/state/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ async def compute_state_after_events(
226226
return await ret.get_state(self._state_storage_controller, state_filter)
227227

228228
async def get_current_user_ids_in_room(
229-
self, room_id: str, latest_event_ids: List[str]
229+
self, room_id: str, latest_event_ids: Collection[str]
230230
) -> Set[str]:
231231
"""
232232
Get the users IDs who are currently in a room.

synapse/storage/controllers/state.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import logging
1515
from typing import (
1616
TYPE_CHECKING,
17+
AbstractSet,
1718
Any,
1819
Awaitable,
1920
Callable,
@@ -23,7 +24,6 @@
2324
List,
2425
Mapping,
2526
Optional,
26-
Set,
2727
Tuple,
2828
)
2929

@@ -527,7 +527,7 @@ async def get_current_state_event(
527527
)
528528
return state_map.get(key)
529529

530-
async def get_current_hosts_in_room(self, room_id: str) -> Set[str]:
530+
async def get_current_hosts_in_room(self, room_id: str) -> AbstractSet[str]:
531531
"""Get current hosts in room based on current state.
532532
533533
Blocks until we have full state for the given room. This only happens for rooms
@@ -584,7 +584,7 @@ async def get_current_hosts_in_room_or_partial_state_approximation(
584584

585585
async def get_users_in_room_with_profiles(
586586
self, room_id: str
587-
) -> Dict[str, ProfileInfo]:
587+
) -> Mapping[str, ProfileInfo]:
588588
"""
589589
Get the current users in the room with their profiles.
590590
If the room is currently partial-stated, this will block until the room has

synapse/storage/databases/main/account_data.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ async def get_global_account_data_by_type_for_user(
240240
@cached(num_args=2, tree=True)
241241
async def get_account_data_for_room(
242242
self, user_id: str, room_id: str
243-
) -> Dict[str, JsonDict]:
243+
) -> Mapping[str, JsonDict]:
244244
"""Get all the client account_data for a user for a room.
245245
246246
Args:

synapse/storage/databases/main/appservice.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ async def get_app_service_users_in_room(
166166
room_id: str,
167167
app_service: "ApplicationService",
168168
cache_context: _CacheContext,
169-
) -> List[str]:
169+
) -> Sequence[str]:
170170
"""
171171
Get all users in a room that the appservice controls.
172172

synapse/storage/databases/main/devices.py

+11-6
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
Dict,
2222
Iterable,
2323
List,
24+
Mapping,
2425
Optional,
2526
Set,
2627
Tuple,
@@ -202,7 +203,9 @@ def _invalidate_caches_for_devices(
202203
def get_device_stream_token(self) -> int:
203204
return self._device_list_id_gen.get_current_token()
204205

205-
async def count_devices_by_users(self, user_ids: Optional[List[str]] = None) -> int:
206+
async def count_devices_by_users(
207+
self, user_ids: Optional[Collection[str]] = None
208+
) -> int:
206209
"""Retrieve number of all devices of given users.
207210
Only returns number of devices that are not marked as hidden.
208211
@@ -213,7 +216,7 @@ async def count_devices_by_users(self, user_ids: Optional[List[str]] = None) ->
213216
"""
214217

215218
def count_devices_by_users_txn(
216-
txn: LoggingTransaction, user_ids: List[str]
219+
txn: LoggingTransaction, user_ids: Collection[str]
217220
) -> int:
218221
sql = """
219222
SELECT count(*)
@@ -747,7 +750,7 @@ def _add_user_signature_change_txn(
747750
@cancellable
748751
async def get_user_devices_from_cache(
749752
self, user_ids: Set[str], user_and_device_ids: List[Tuple[str, str]]
750-
) -> Tuple[Set[str], Dict[str, Dict[str, JsonDict]]]:
753+
) -> Tuple[Set[str], Dict[str, Mapping[str, JsonDict]]]:
751754
"""Get the devices (and keys if any) for remote users from the cache.
752755
753756
Args:
@@ -775,16 +778,18 @@ async def get_user_devices_from_cache(
775778
user_ids_not_in_cache = unique_user_ids - user_ids_in_cache
776779

777780
# First fetch all the users which all devices are to be returned.
778-
results: Dict[str, Dict[str, JsonDict]] = {}
781+
results: Dict[str, Mapping[str, JsonDict]] = {}
779782
for user_id in user_ids:
780783
if user_id in user_ids_in_cache:
781784
results[user_id] = await self.get_cached_devices_for_user(user_id)
782785
# Then fetch all device-specific requests, but skip users we've already
783786
# fetched all devices for.
787+
device_specific_results: Dict[str, Dict[str, JsonDict]] = {}
784788
for user_id, device_id in user_and_device_ids:
785789
if user_id in user_ids_in_cache and user_id not in user_ids:
786790
device = await self._get_cached_user_device(user_id, device_id)
787-
results.setdefault(user_id, {})[device_id] = device
791+
device_specific_results.setdefault(user_id, {})[device_id] = device
792+
results.update(device_specific_results)
788793

789794
set_tag("in_cache", str(results))
790795
set_tag("not_in_cache", str(user_ids_not_in_cache))
@@ -802,7 +807,7 @@ async def _get_cached_user_device(self, user_id: str, device_id: str) -> JsonDic
802807
return db_to_json(content)
803808

804809
@cached()
805-
async def get_cached_devices_for_user(self, user_id: str) -> Dict[str, JsonDict]:
810+
async def get_cached_devices_for_user(self, user_id: str) -> Mapping[str, JsonDict]:
806811
devices = await self.db_pool.simple_select_list(
807812
table="device_lists_remote_cache",
808813
keyvalues={"user_id": user_id},

synapse/storage/databases/main/directory.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
from typing import Iterable, List, Optional, Tuple
15+
from typing import Iterable, List, Optional, Sequence, Tuple
1616

1717
import attr
1818

@@ -74,7 +74,7 @@ async def get_room_alias_creator(self, room_alias: str) -> str:
7474
)
7575

7676
@cached(max_entries=5000)
77-
async def get_aliases_for_room(self, room_id: str) -> List[str]:
77+
async def get_aliases_for_room(self, room_id: str) -> Sequence[str]:
7878
return await self.db_pool.simple_select_onecol(
7979
"room_aliases",
8080
{"room_id": room_id},

0 commit comments

Comments
 (0)