Skip to content

Commit 332935d

Browse files
authored
Merge pull request #534 from JohnVillalovos/jlvillal/dataclasses
chore: convert `namedtuple` to `dataclass`
2 parents b3aef9b + d677574 commit 332935d

File tree

4 files changed

+56
-21
lines changed

4 files changed

+56
-21
lines changed

imapclient/imapclient.py

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# Released subject to the New BSD License
33
# Please see http://en.wikipedia.org/wiki/BSD_licenses
44

5+
import dataclasses
56
import functools
67
import imaplib
78
import itertools
@@ -10,11 +11,10 @@
1011
import socket
1112
import sys
1213
import warnings
13-
from collections import namedtuple
1414
from datetime import date, datetime
1515
from logging import getLogger, LoggerAdapter
1616
from operator import itemgetter
17-
from typing import Optional
17+
from typing import List, Optional
1818

1919
from . import exceptions, imap4, response_lexer, tls
2020
from .datetime_util import datetime_to_INTERNALDATE, format_criteria_date
@@ -117,7 +117,8 @@ def __new__(cls, personal, other, shared):
117117
shared = property(itemgetter(2))
118118

119119

120-
class SocketTimeout(namedtuple("SocketTimeout", "connect read")):
120+
@dataclasses.dataclass
121+
class SocketTimeout:
121122
"""Represents timeout configuration for an IMAP connection.
122123
123124
:ivar connect: maximum time to wait for a connection attempt to remote server
@@ -128,8 +129,12 @@ class SocketTimeout(namedtuple("SocketTimeout", "connect read")):
128129
read/write operations can take up to 60 seconds once the connection is done.
129130
"""
130131

132+
connect: float
133+
read: float
131134

132-
class MailboxQuotaRoots(namedtuple("MailboxQuotaRoots", "mailbox quota_roots")):
135+
136+
@dataclasses.dataclass
137+
class MailboxQuotaRoots:
133138
"""Quota roots associated with a mailbox.
134139
135140
Represents the response of a GETQUOTAROOT command.
@@ -138,8 +143,12 @@ class MailboxQuotaRoots(namedtuple("MailboxQuotaRoots", "mailbox quota_roots")):
138143
:ivar quota_roots: list of quota roots associated with the mailbox
139144
"""
140145

146+
mailbox: str
147+
quota_roots: List[str]
148+
141149

142-
class Quota(namedtuple("Quota", "quota_root resource usage limit")):
150+
@dataclasses.dataclass
151+
class Quota:
143152
"""Resource quota.
144153
145154
Represents the response of a GETQUOTA command.
@@ -150,6 +159,11 @@ class Quota(namedtuple("Quota", "quota_root resource usage limit")):
150159
:ivar limit: the maximum allowed usage of the resource
151160
"""
152161

162+
quota_root: str
163+
resource: str
164+
usage: bytes
165+
limit: bytes
166+
153167

154168
def require_capability(capability):
155169
"""Decorator raising CapabilityError when a capability is not available."""

imapclient/response_parser.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,12 @@ def _convert_ENVELOPE(
209209
pass
210210

211211
subject = envelope_response[1]
212+
in_reply_to = envelope_response[8]
213+
message_id = envelope_response[9]
214+
if TYPE_CHECKING:
215+
assert isinstance(subject, bytes)
216+
assert isinstance(in_reply_to, bytes)
217+
assert isinstance(message_id, bytes)
212218

213219
# addresses contains a tuple of addresses
214220
# from, sender, reply_to, to, cc, bcc headers
@@ -222,6 +228,8 @@ def _convert_ENVELOPE(
222228
if TYPE_CHECKING:
223229
assert isinstance(addr_tuple, tuple)
224230
if addr_tuple:
231+
if TYPE_CHECKING:
232+
addr_tuple = cast(Tuple[bytes, bytes, bytes, bytes], addr_tuple)
225233
addrs.append(Address(*addr_tuple))
226234
addresses.append(tuple(addrs))
227235
else:
@@ -236,8 +244,8 @@ def _convert_ENVELOPE(
236244
to=addresses[3],
237245
cc=addresses[4],
238246
bcc=addresses[5],
239-
in_reply_to=envelope_response[8],
240-
message_id=envelope_response[9],
247+
in_reply_to=in_reply_to,
248+
message_id=message_id,
241249
)
242250

243251

imapclient/response_types.py

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,17 @@
22
# Released subject to the New BSD License
33
# Please see http://en.wikipedia.org/wiki/BSD_licenses
44

5-
from collections import namedtuple
5+
import dataclasses
6+
import datetime
67
from email.utils import formataddr
78
from typing import Any, List, Optional, Tuple, TYPE_CHECKING, Union
89

910
from .typing_imapclient import _Atom
1011
from .util import to_unicode
1112

1213

13-
class Envelope(
14-
namedtuple(
15-
"Envelope",
16-
"date subject from_ sender reply_to to cc bcc in_reply_to message_id",
17-
)
18-
):
14+
@dataclasses.dataclass
15+
class Envelope:
1916
r"""Represents envelope structures of messages. Returned when parsing
2017
ENVELOPE responses.
2118
@@ -56,9 +53,20 @@ class Envelope(
5653
See :rfc:`3501#section-7.4.2` and :rfc:`2822` for further details.
5754
5855
"""
59-
60-
61-
class Address(namedtuple("Address", "name route mailbox host")):
56+
date: Optional[datetime.datetime]
57+
subject: bytes
58+
from_: Optional[Tuple["Address", ...]]
59+
sender: Optional[Tuple["Address", ...]]
60+
reply_to: Optional[Tuple["Address", ...]]
61+
to: Optional[Tuple["Address", ...]]
62+
cc: Optional[Tuple["Address", ...]]
63+
bcc: Optional[Tuple["Address", ...]]
64+
in_reply_to: bytes
65+
message_id: bytes
66+
67+
68+
@dataclasses.dataclass
69+
class Address:
6270
"""Represents electronic mail addresses. Used to store addresses in
6371
:py:class:`Envelope`.
6472
@@ -81,6 +89,11 @@ class Address(namedtuple("Address", "name route mailbox host")):
8189
"group syntax".
8290
"""
8391

92+
name: bytes
93+
route: bytes
94+
mailbox: bytes
95+
host: bytes
96+
8497
def __str__(self) -> str:
8598
if self.mailbox and self.host:
8699
address = to_unicode(self.mailbox) + "@" + to_unicode(self.host)

tests/test_response_parser.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -511,7 +511,7 @@ def test_ENVELOPE(self):
511511

512512
output = parse_fetch_response([envelope_str], normalise_times=False)
513513

514-
self.assertSequenceEqual(
514+
self.assertEqual(
515515
output[1][b"ENVELOPE"],
516516
Envelope(
517517
datetime(2013, 3, 24, 22, 6, 10, tzinfo=FixedOffset(120)),
@@ -547,7 +547,7 @@ def test_ENVELOPE_with_no_date(self):
547547

548548
output = parse_fetch_response([envelope_str], normalise_times=False)
549549

550-
self.assertSequenceEqual(
550+
self.assertEqual(
551551
output[1][b"ENVELOPE"],
552552
Envelope(
553553
None,
@@ -574,7 +574,7 @@ def test_ENVELOPE_with_invalid_date(self):
574574

575575
output = parse_fetch_response([envelope_str], normalise_times=False)
576576

577-
self.assertSequenceEqual(
577+
self.assertEqual(
578578
output[1][b"ENVELOPE"],
579579
Envelope(
580580
None,
@@ -606,7 +606,7 @@ def test_ENVELOPE_with_empty_addresses(self):
606606

607607
output = parse_fetch_response([envelope_str], normalise_times=False)
608608

609-
self.assertSequenceEqual(
609+
self.assertEqual(
610610
output[1][b"ENVELOPE"],
611611
Envelope(
612612
None,

0 commit comments

Comments
 (0)