@@ -3,17 +3,27 @@ from email import _ParamsType, _ParamType
3
3
from email .charset import Charset
4
4
from email .contentmanager import ContentManager
5
5
from email .errors import MessageDefect
6
+ from email .header import Header
6
7
from email .policy import Policy
7
- from typing import Any , TypeVar , overload
8
- from typing_extensions import Self , TypeAlias
8
+ from typing import Any , Protocol , TypeVar , overload
9
+ from typing_extensions import Literal , Self , TypeAlias
9
10
10
11
__all__ = ["Message" , "EmailMessage" ]
11
12
12
13
_T = TypeVar ("_T" )
13
-
14
- _PayloadType : TypeAlias = list [Message ] | str | bytes | bytearray
14
+ _PayloadType : TypeAlias = Message | str
15
+ _EncodedPayloadType : TypeAlias = Message | bytes
16
+ _MultipartPayloadType : TypeAlias = list [_PayloadType ]
15
17
_CharsetType : TypeAlias = Charset | str | None
18
+ # Type returned by Policy.header_fetch_parse, AnyOf[str | Header]
16
19
_HeaderType : TypeAlias = Any
20
+ _HeaderTypeParam : TypeAlias = str | Header
21
+
22
+ class _SupportsEncodeToPayload (Protocol ):
23
+ def encode (self , __encoding : str ) -> _PayloadType | _MultipartPayloadType | _SupportsDecodeToPayload : ...
24
+
25
+ class _SupportsDecodeToPayload (Protocol ):
26
+ def decode (self , __encoding : str , __errors : str ) -> _PayloadType | _MultipartPayloadType : ...
17
27
18
28
class Message :
19
29
policy : Policy # undocumented
@@ -23,16 +33,43 @@ class Message:
23
33
def is_multipart (self ) -> bool : ...
24
34
def set_unixfrom (self
10000
span>, unixfrom : str ) -> None : ...
25
35
def get_unixfrom (self ) -> str | None : ...
26
- def attach (self , payload : Message ) -> None : ...
27
- def get_payload (self , i : int | None = None , decode : bool = False ) -> Any : ... # returns _PayloadType | None
28
- def set_payload (self , payload : _PayloadType , charset : _CharsetType = None ) -> None : ...
36
+ def attach (self , payload : _PayloadType ) -> None : ...
37
+ # `i: int` without a multipart payload results in an error
38
+ # `| Any`: can be None for cleared or unset payload, but annoying to check
39
+ @overload # multipart
40
+ def get_payload (self , i : int , decode : Literal [True ]) -> None : ...
41
+ @overload # multipart
42
+ def get_payload (self , i : int , decode : Literal [False ] = False ) -> _PayloadType | Any : ...
43
+ @overload # either
44
+ def get_payload (self , i : None = None , decode : Literal [False ] = False ) -> _PayloadType | _MultipartPayloadType | Any : ...
45
+ @overload # not multipart
46
+ def get_payload (self , i : None = None , * , decode : Literal [True ]) -> _EncodedPayloadType | Any : ...
47
+ @overload # not multipart, IDEM but w/o kwarg
48
+ def get_payload (self , i : None , decode : Literal [True ]) -> _EncodedPayloadType | Any : ...
49
+ # If `charset=None` and payload supports both `encode` AND `decode`, then an invalid payload could be passed, but this is unlikely
50
+ # Not[_SupportsEncodeToPayload]
51
+ @overload
52
+ def set_payload (
53
+ self , payload : _SupportsDecodeToPayload | _PayloadType | _MultipartPayloadType , charset : None = None
54
+ ) -> None : ...
55
+ @overload
56
+ def set_payload (
57
+ self ,
58
+ payload : _SupportsEncodeToPayload | _SupportsDecodeToPayload | _PayloadType | _MultipartPayloadType ,
59
+ charset : Charset | str ,
60
+ ) -> None : ...
29
61
def set_charset (self , charset : _CharsetType ) -> None : ...
30
62
def get_charset (self ) -> _CharsetType : ...
31
63
def __len__ (self ) -> int : ...
32
64
def __contains__ (self , name : str ) -> bool : ...
33
65
def __iter__ (self ) -> Iterator [str ]: ...
66
+ # Same as `get` with `failobj=None`, but with the expectation that it won't return None in most scenarios
67
+ # This is important for protocols using __getitem__, like SupportsKeysAndGetItem
68
+ # Morally, the return type should be `AnyOf[_HeaderType, None]`,
69
+ # which we could spell as `_HeaderType | Any`,
70
+ # *but* `_HeaderType` itself is currently an alias to `Any`...
34
71
def __getitem__ (self , name : str ) -> _HeaderType : ...
35
- def __setitem__ (self , name : str , val : _HeaderType ) -> None : ...
72
+ def __setitem__ (self , name : str , val : _HeaderTypeParam ) -> None : ...
36
73
def __delitem__ (self , name : str ) -> None : ...
37
74
def keys (self ) -> list [str ]: ...
38
75
def values (self ) -> list [_HeaderType ]: ...
@@ -46,7 +83,7 @@ class Message:
46
83
@overload
47
84
def get_all (self , name : str , failobj : _T ) -> list [_HeaderType ] | _T : ...
48
85
def add_header (self , _name : str , _value : str , ** _params : _ParamsType ) -> None : ...
49
- def replace_header (self , _name : str , _value : _HeaderType ) -> None : ...
86
+ def replace_header (self , _name : str , _value : _HeaderTypeParam ) -> None : ...
50
87
def get_content_type (self ) -> str : ...
51
88
def get_content_maintype (self ) -> str : ...
52
89
def get_content_subtype (self ) -> str : ...
@@ -100,7 +137,7 @@ class Message:
100
137
) -> None : ...
101
138
def __init__ (self , policy : Policy = ...) -> None : ...
102
139
# The following two methods are undocumented, but a source code comment states that they are public API
103
- def set_raw (self , name : str , value : _HeaderType ) -> None : ...
140
+ def set_raw (self , name : str , value : _HeaderTypeParam ) -> None : ...
104
141
def raw_items (self ) -> Iterator [tuple [str , _HeaderType ]]: ...
105
142
106
143
class MIMEPart (Message ):
0 commit comments