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

Commit cbd82d0

Browse files
authored
Convert all namedtuples to attrs. (#11665)
To improve type hints throughout the code.
1 parent 07a3b5d commit cbd82d0

File tree

22 files changed

+231
-206
lines changed

22 files changed

+231
-206
lines changed

changelog.d/11665.misc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Convert `namedtuples` to `attrs`.

synapse/api/filtering.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -351,8 +351,7 @@ def _check(self, event: FilterEvent) -> bool:
351351
True if the event matches the filter.
352352
"""
353353
# We usually get the full "events" as dictionaries coming through,
354-
# except for presence which actually gets passed around as its own
355-
# namedtuple type.
354+
# except for presence which actually gets passed around as its own type.
356355
if isinstance(event, UserPresenceState):
357356
user_id = event.user_id
358357
field_matchers = {

synapse/config/repository.py

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@
1414

1515
import logging
1616
import os
17-
from collections import namedtuple
1817
from typing import Dict, List, Tuple
1918
from urllib.request import getproxies_environment # type: ignore
2019

20+
import attr
21+
2122
from synapse.config.server import DEFAULT_IP_RANGE_BLACKLIST, generate_ip_set
2223
from synapse.python_dependencies import DependencyException, check_requirements
2324
from synapse.types import JsonDict
@@ -44,18 +45,20 @@
4445
HTTP_PROXY_SET_WARNING = """\
4546
The Synapse config url_preview_ip_range_blacklist will be ignored as an HTTP(s) proxy is configured."""
4647

47-
ThumbnailRequirement = namedtuple(
48-
"ThumbnailRequirement", ["width", "height", "method", "media_type"]
49-
)
5048

51-
MediaStorageProviderConfig = namedtuple(
52-
"MediaStorageProviderConfig",
53-
(
54-
"store_local", # Whether to store newly uploaded local files
55-
"store_remote", # Whether to store newly downloaded remote files
56-
"store_synchronous", # Whether to wait for successful storage for local uploads
57-
),
58-
)
49+
@attr.s(frozen=True, slots=True, auto_attribs=True)
50+
class ThumbnailRequirement:
51+
width: int
52+
height: int
53+
method: str
54+
media_type: str
55+
56+
57+
@attr.s(frozen=True, slots=True, auto_attribs=True)
58+
class MediaStorageProviderConfig:
59+
store_local: bool # Whether to store newly uploaded local files
60+
store_remote: bool # Whether to store newly downloaded remote files
61+
store_synchronous: bool # Whether to wait for successful storage for local uploads
5962

6063

6164
def parse_thumbnail_requirements(
@@ -66,11 +69,10 @@ def parse_thumbnail_requirements(
6669
method, and thumbnail media type to precalculate
6770
6871
Args:
69-
thumbnail_sizes(list): List of dicts with "width", "height", and
70-
"method" keys
72+
thumbnail_sizes: List of dicts with "width", "height", and "method" keys
73+
7174
Returns:
72-
Dictionary mapping from media type string to list of
73-
ThumbnailRequirement tuples.
75+
Dictionary mapping from media type string to list of ThumbnailRequirement.
7476
"""
7577
requirements: Dict[str, List[ThumbnailRequirement]] = {}
7678
for size in thumbnail_sizes:

synapse/federation/federation_base.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
# See the License for the specific language governing permissions and
1414
# limitations under the License.
1515
import logging
16-
from collections import namedtuple
1716
from typing import TYPE_CHECKING
1817

1918
from synapse.api.constants import MAX_DEPTH, EventContentFields, EventTypes, Membership
@@ -104,10 +103,6 @@ async def _check_sigs_and_hash(
104103
return pdu
105104

106105

107-
class PduToCheckSig(namedtuple("PduToCheckSig", ["pdu", "sender_domain", "deferreds"])):
108-
pass
109-
110-
111106
async def _check_sigs_on_pdu(
112107
keyring: Keyring, room_version: RoomVersion, pdu: EventBase
113108
) -> None:

synapse/federation/send_queue.py

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
"""
3131

3232
import logging
33-
from collections import namedtuple
3433
from typing import (
3534
TYPE_CHECKING,
3635
Dict,
@@ -43,6 +42,7 @@
4342
Type,
4443
)
4544

45+
import attr
4646
from sortedcontainers import SortedDict
4747

4848
from synapse.api.presence import UserPresenceState
@@ -382,13 +382,11 @@ def add_to_buffer(self, buff: "ParsedFederationStreamData") -> None:
382382
raise NotImplementedError()
383383

384384

385-
class PresenceDestinationsRow(
386-
BaseFederationRow,
387-
namedtuple(
388-
"PresenceDestinationsRow",
389-
("state", "destinations"), # UserPresenceState # list[str]
390-
),
391-
):
385+
@attr.s(slots=True, frozen=True, auto_attribs=True)
386+
class PresenceDestinationsRow(BaseFederationRow):
387+
state: UserPresenceState
388+
destinations: List[str]
389+
392390
TypeId = "pd"
393391

394392
@staticmethod
@@ -404,17 +402,15 @@ def add_to_buffer(self, buff: "ParsedFederationStreamData") -> None:
404402
buff.presence_destinations.append((self.state, self.destinations))
405403

406404

407-
class KeyedEduRow(
408-
BaseFederationRow,
409-
namedtuple(
410-
"KeyedEduRow",
411-
("key", "edu"), # tuple(str) - the edu key passed to send_edu # Edu
412-
),
413-
):
405+
@attr.s(slots=True, frozen=True, auto_attribs=True)
406+
class KeyedEduRow(BaseFederationRow):
414407
"""Streams EDUs that have an associated key that is ued to clobber. For example,
415408
typing EDUs clobber based on room_id.
416409
"""
417410

411+
key: Tuple[str, ...] # the edu key passed to send_edu
412+
edu: Edu
413+
418414
TypeId = "k"
419415

420416
@staticmethod
@@ -428,9 +424,12 @@ def add_to_buffer(self, buff: "ParsedFederationStreamData") -> None:
428424
buff.keyed_edus.setdefault(self.edu.destination, {})[self.key] = self.edu
429425

430426

431-
class EduRow(BaseFederationRow, namedtuple("EduRow", ("edu",))): # Edu
427+
@attr.s(slots=True, frozen=True, auto_attribs=True)
428+
class EduRow(BaseFederationRow):
432429
"""Streams EDUs that don't have keys. See KeyedEduRow"""
433430

431+
edu: Edu
432+
434433
TypeId = "e"
435434

436435
@staticmethod
@@ -453,14 +452,14 @@ def add_to_buffer(self, buff: "ParsedFederationStreamData") -> None:
453452
TypeToRow = {Row.TypeId: Row for Row in _rowtypes}
454453

455454

456-
ParsedFederationStreamData = namedtuple(
457-
"ParsedFederationStreamData",
458-
(
459-
"presence_destinations", # list of tuples of UserPresenceState and destinations
460-
"keyed_edus", # dict of destination -> { key -> Edu }
461-
"edus", # dict of destination -> [Edu]
462-
),
463-
)
455+
@attr.s(slots=True, frozen=True, auto_attribs=True)
456+
class ParsedFederationStreamData:
457+
# list of tuples of UserPresenceState and destinations
458+
presence_destinations: List[Tuple[UserPresenceState, List[str]]]
459+
# dict of destination -> { key -> Edu }
460+
keyed_edus: Dict[str, Dict[Tuple[str, ...], Edu]]
461+
# dict of destination -> [Edu]
462+
edus: Dict[str, List[Edu]]
464463

465464

466465
def process_rows_for_federation(

synapse/handlers/appservice.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -462,9 +462,9 @@ async def query_room_alias_exists(
462462
463463
Args:
464464
room_alias: The room alias to query.
465+
465466
Returns:
466-
namedtuple: with keys "room_id" and "servers" or None if no
467-
association can be found.
467+
RoomAliasMapping or None if no association can be found.
468468
"""
469469
room_alias_str = room_alias.to_string()
470470
services = self.store.get_app_services()

synapse/handlers/directory.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -278,13 +278,15 @@ async def get_association(self, room_alias: RoomAlias) -> JsonDict:
278278

279279
users = await self.store.get_users_in_room(room_id)
280280
extra_servers = {get_domain_from_id(u) for u in users}
281-
servers = set(extra_servers) | set(servers)
281+
servers_set = set(extra_servers) | set(servers)
282282

283283
# If this server is in the list of servers, return it first.
284-
if self.server_name in servers:
285-
servers = [self.server_name] + [s for s in servers if s != self.server_name]
284+
if self.server_name in servers_set:
285+
servers = [self.server_name] + [
286+
s for s in servers_set if s != self.server_name
287+
]
286288
else:
287-
servers = list(servers)
289+
servers = list(servers_set)
288290

289291
return {"room_id": room_id, "servers": servers}
290292

synapse/handlers/room_list.py

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@
1313
# limitations under the License.
1414

1515
import logging
16-
from collections import namedtuple
1716
from typing import TYPE_CHECKING, Any, Optional, Tuple
1817

18+
import attr
1919
import msgpack
2020
from unpaddedbase64 import decode_base64, encode_base64
2121

@@ -474,16 +474,12 @@ async def _get_remote_list_cached(
474474
)
475475

476476

477-
class RoomListNextBatch(
478-
namedtuple(
479-
"RoomListNextBatch",
480-
(
481-
"last_joined_members", # The count to get rooms after/before
482-
"last_room_id", # The room_id to get rooms after/before
483-
"direction_is_forward", # Bool if this is a next_batch, false if prev_batch
484-
),
485-
)
486-
):
477+
@attr.s(slots=True, frozen=True, auto_attribs=True)
478+
class RoomListNextBatch:
479+
last_joined_members: int # The count to get rooms after/before
480+
last_room_id: str # The room_id to get rooms after/before
481+
direction_is_forward: bool # True if this is a next_batch, false if prev_batch
482+
487483
KEY_DICT = {
488484
"last_joined_members": "m",
489485
"last_room_id": "r",
@@ -502,12 +498,12 @@ def from_token(cls, token: str) -> "RoomListNextBatch":
502498
def to_token(self) -> str:
503499
return encode_base64(
504500
msgpack.dumps(
505-
{self.KEY_DICT[key]: val for key, val in self._asdict().items()}
501+
{self.KEY_DICT[key]: val for key, val in attr.asdict(self).items()}
506502
)
507503
)
508504

509505
def copy_and_replace(self, **kwds: Any) -> "RoomListNextBatch":
510-
return self._replace(**kwds)
506+
return attr.evolve(self, **kwds)
511507

512508

513509
def _matches_room_entry(room_entry: JsonDict, search_filter: dict) -> bool:

synapse/handlers/typing.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@
1313
# limitations under the License.
1414
import logging
1515
import random
16-
from collections import namedtuple
1716
from typing import TYPE_CHECKING, Dict, Iterable, List, Optional, Set, Tuple
1817

18+
import attr
19+
1920
from synapse.api.errors import AuthError, ShadowBanError, SynapseError
2021
from synapse.appservice import ApplicationService
2122
from synapse.metrics.background_process_metrics import (
@@ -37,7 +38,10 @@
3738

3839
# A tiny object useful for storing a user's membership in a room, as a mapping
3940
# key
40-
RoomMember = namedtuple("RoomMember", ("room_id", "user_id"))
41+
@attr.s(slots=True, frozen=True, auto_attribs=True)
42+
class RoomMember:
43+
room_id: str
44+
user_id: str
4145

4246

4347
# How often we expect remote servers to resend us presence.
@@ -119,7 +123,7 @@ def _handle_timeout_for_member(self, now: int, member: RoomMember) -> None:
119123
self.wheel_timer.insert(now=now, obj=member, then=now + 60 * 1000)
120124

121125
def is_typing(self, member: RoomMember) -> bool:
122-
return member.user_id in self._room_typing.get(member.room_id, [])
126+
return member.user_id in self._room_typing.get(member.room_id, set())
123127

124128
async def _push_remote(self, member: RoomMember, typing: bool) -> None:
125129
if not self.federation:
@@ -166,9 +170,9 @@ def process_replication_rows(
166170
for row in rows:
167171
self._room_serials[row.room_id] = token
168172

169-
prev_typing = set(self._room_typing.get(row.room_id, []))
173+
prev_typing = self._room_typing.get(row.room_id, set())
170174
now_typing = set(row.user_ids)
171-
self._room_typing[row.room_id] = row.user_ids
175+
self._room_typing[row.room_id] = now_typing
172176

173177
if self.federation:
174178
run_as_background_process(

synapse/http/server.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
# limitations under the License.
1515

1616
import abc
17-
import collections
1817
import html
1918
import logging
2019
import types
@@ -37,6 +36,7 @@
3736
Union,
3837
)
3938

39+
import attr
4040
import jinja2
4141
from canonicaljson import encode_canonical_json
4242
from typing_extensions import Protocol
@@ -354,9 +354,11 @@ def _send_error_response(
354354
return_json_error(f, request)
355355

356356

357-
_PathEntry = collections.namedtuple(
358-
"_PathEntry", ["pattern", "callback", "servlet_classname"]
359-
)
357+
@attr.s(slots=True, frozen=True, auto_attribs=True)
358+
class _PathEntry:
359+
pattern: Pattern
360+
callback: ServletCallback
361+
servlet_classname: str
360362

361363

362364
class JsonResource(DirectServeJsonResource):

0 commit comments

Comments
 (0)