Skip to content

Commit 5c0ad37

Browse files
committed
Merge branch 'fix-tests2'
1 parent 3ea5615 commit 5c0ad37

File tree

3 files changed

+49
-42
lines changed

3 files changed

+49
-42
lines changed

envelope/address.py

+20-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import logging
33
from os import environ
44
import re
5+
import sys
56
from .utils import assure_list
67

78
environ['PY3VE_IGNORE_UPDATER'] = '1'
@@ -11,6 +12,23 @@
1112
logger = logging.getLogger(__name__)
1213

1314

15+
def _getaddresses(*args):
16+
# NOTE Python finally changed the old way of parsing wrong addresses.
17+
# We might start using strict=True (default) in the future.
18+
if sys.version_info <= (3, 11):
19+
return getaddresses(*args)
20+
return getaddresses(*args, strict=False)
21+
22+
23+
def _parseaddr(*args):
24+
# NOTE Python finally changed the old way of parsing wrong addresses.
25+
# We might start using strict=True (default) in the future.
26+
# README should reflect that.
27+
if sys.version_info <= (3, 11):
28+
return parseaddr(*args)
29+
return parseaddr(*args, strict=False)
30+
31+
1432
class Address(str):
1533
"""
1634
You can safely access the `self.name` property to access the real name and `self.address` to access the e-mail address.
@@ -37,7 +55,7 @@ class Address(str):
3755

3856
def __new__(cls, displayed_email=None, name=None, address=None):
3957
if displayed_email:
40-
v = parseaddr(cls.remedy(displayed_email))
58+
v = _parseaddr(cls.remedy(displayed_email))
4159
name, address = v[0] or name, v[1] or address
4260
if name:
4361
displayed_email = f"{name} <{address}>"
@@ -143,7 +161,7 @@ def parse(cls, email_or_list, single=False, allow_false=False):
143161
if allow_false and email_or_list is False:
144162
return False
145163

146-
addrs = getaddresses(cls.remedy(x) for x in assure_list(email_or_list))
164+
addrs = _getaddresses(cls.remedy(x) for x in assure_list(email_or_list))
147165
addresses = [Address(name=real_name, address=address)
148166
for real_name, address in addrs if not (real_name == address == "")]
149167
if single:

envelope/envelope.py

+25-35
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,23 @@
88
import sys
99
from tempfile import NamedTemporaryFile
1010
from unittest import mock
11-
import warnings
12-
from base64 import b64decode, b64encode
11+
from base64 import b64decode
1312
from configparser import ConfigParser
1413
from copy import deepcopy
1514
from email import message_from_bytes
1615
from email.header import decode_header
1716
from email.generator import Generator
1817
from email.message import EmailMessage, Message
1918
from email.parser import BytesParser
20-
from email.utils import make_msgid, formatdate, getaddresses
21-
from getpass import getpass
19+
from email.utils import make_msgid, formatdate
2220
from itertools import chain
23-
from os import environ, urandom
21+
from os import environ
2422
from pathlib import Path
2523
from quopri import decodestring
2624
from types import GeneratorType
27-
from typing import Literal, Union, List, Set, Optional, Any
25+
from typing import Literal, Union, Optional, Any
2826

29-
from .address import Address
27+
from .address import Address, _getaddresses
3028
from .attachment import Attachment
3129
from .constants import ISSUE_LINK, smime_import_error, gnupg, CRLF, AUTO, PLAIN, HTML, SIMULATION, SAFE_LOCALE
3230
from .message import _Message
@@ -35,7 +33,6 @@
3533
from .utils import AutoSubmittedHeader, Fetched, is_gpg_importable_key, assure_list, assure_fetched, get_mimetype
3634

3735

38-
3936
__doc__ = """Quick layer over python-gnupg, M2Crypto, smtplib, magic and email handling packages.
4037
Their common use cases merged into a single function. Want to sign a text and tired of forgetting how to do it right?
4138
You do not need to know everything about GPG or S/MIME, you do not have to bother with importing keys.
@@ -320,14 +317,14 @@ def __init__(self, message=None, from_=None, to=None, subject=None, headers=None
320317
# that explicitly states we have no from header
321318
self._from: Union[Address, False, None] = None # e-mail From header
322319
self._from_addr: Optional[Address] = None # SMTP envelope MAIL FROM address
323-
self._to: List[Address] = []
324-
self._cc: List[Address] = []
325-
self._bcc: List[Address] = []
326-
self._reply_to: List[Address] = []
320+
self._to: list[Address] = []
321+
self._cc: list[Address] = []
322+
self._bcc: list[Address] = []
323+
self._reply_to: list[Address] = []
327324
self._subject: Union[str, None] = None
328325
self._subject_encrypted: Union[str, bool] = True
329326
self._smtp = None
330-
self._attachments: List[Attachment] = []
327+
self._attachments: list[Attachment] = []
331328
self._mime = AUTO
332329
self._nl2br = AUTO
333330
self._headers = EmailMessage() # object for storing headers the most standard way possible
@@ -336,7 +333,7 @@ def __init__(self, message=None, from_=None, to=None, subject=None, headers=None
336333
# variables defined while processing
337334
self._status: bool = False # whether we successfully encrypted/signed/send
338335
self._processed: bool = False # prevent the user from mistakenly call .sign().send() instead of .signature().send()
339-
self._result: List[Union[str, EmailMessage, Message]] = [] # text output for str() conversion
336+
self._result: list[Union[str, EmailMessage, Message]] = [] # text output for str() conversion
340337
self._result_cache: Optional[str] = None
341338
self._result_cache_hash: Optional[int] = None
342339
self._smtp = SMTPHandler()
@@ -407,32 +404,32 @@ def _parse_addresses(registry, email_or_more):
407404
if addresses:
408405
registry += (a for a in Address.parse(addresses) if a not in registry)
409406

410-
def to(self, email_or_more=None) -> Union["Envelope", List[Address]]:
407+
def to(self, email_or_more=None) -> Union["Envelope", list[Address]]:
411408
""" Multiple addresses may be given in a string, delimited by comma (or semicolon).
412409
(The same is valid for `to`, `cc`, `bcc` and `reply-to`.)
413410
414-
:param email_or_more: str|Tuple[str]|List[str]|Generator[str]|Set[str]|Frozenset[str]
411+
:param email_or_more: str|Tuple[str]|list[str]|Generator[str]|Set[str]|Frozenset[str]
415412
Set e-mail address/es. If None, we are reading.
416-
return: Envelope if `email_or_more` set or List[Address] if not set
413+
return: Envelope if `email_or_more` set or list[Address] if not set
417414
"""
418415
if email_or_more is None:
419416
return self._to
420417
self._parse_addresses(self._to, email_or_more)
421418
return self
422419

423-
def cc(self, email_or_more=None) -> Union["Envelope", List[Address]]:
420+
def cc(self, email_or_more=None) -> Union["Envelope", list[Address]]:
424421
if email_or_more is None:
425422
return self._cc
426423
self._parse_addresses(self._cc, email_or_more)
427424
return self
428425

429-
def bcc(self, email_or_more=None) -> Union["Envelope", List[Address]]:
426+
def bcc(self, email_or_more=None) -> Union["Envelope", list[Address]]:
430427
if email_or_more is None:
431428
return self._bcc
432429
self._parse_addresses(self._bcc, email_or_more)
433430
return self
434431

435-
def reply_to(self, email_or_more=None) -> Union["Envelope", List[Address]]:
432+
def reply_to(self, email_or_more=None) -> Union["Envelope", list[Address]]:
436433
if email_or_more is None:
437434
return self._reply_to
438435
self._parse_addresses(self._reply_to, email_or_more)
@@ -586,7 +583,7 @@ def subject(self, subject=None, encrypted: Union[str, bool] = None) -> Union["En
586583
self._subject_encrypted = encrypted
587584
return self
588585

589-
def mime(self, subtype=AUTO, nl2br: Literal["auto"] | bool=AUTO):
586+
def mime(self, subtype=AUTO, nl2br: Literal["auto"] | bool = AUTO):
590587
"""
591588
Ignored if `Content-Type` header put to the message.
592589
@type subtype: str Set contents mime subtype: "auto" (default), "html" or "plain" for plain text.
@@ -1084,7 +1081,6 @@ def _gpg_import_or_fail(self, key):
10841081
else:
10851082
raise ValueError(f"Could not import key starting: {key[:80]}...")
10861083

1087-
10881084
def _get_gnupg_home(self, for_help=False):
10891085
s = self._gpg if type(self._gpg) is str else None
10901086
if for_help:
@@ -1236,7 +1232,7 @@ def _encrypt_gpg_now(self, message, encrypt, sign_fingerprint):
12361232
return False
12371233

12381234
def _gpg_list_keys(self, secret=False):
1239-
return ((key, address) for key in self._gnupg.list_keys(secret) for _, address in getaddresses(key["uids"]))
1235+
return ((key, address) for key in self._gnupg.list_keys(secret) for _, address in _getaddresses(key["uids"]))
12401236

12411237
def _gpg_verify(self, signature: bytes, data: bytes):
12421238
""" Allows verifying detached GPG signature.
@@ -1250,7 +1246,7 @@ def _gpg_verify(self, signature: bytes, data: bytes):
12501246
fp.seek(0)
12511247
return bool(self._gnupg.verify_data(fp.name, data))
12521248

1253-
def _get_decipherers(self) -> Set[str]:
1249+
def _get_decipherers(self) -> set[str]:
12541250
"""
12551251
:return: Set of e-mail addresses
12561252
"""
@@ -1299,7 +1295,6 @@ def smime_sign_only(self, email, sign):
12991295

13001296
return signed_email
13011297

1302-
13031298
def smime_sign_encrypt(self, email, sign, encrypt):
13041299
from cryptography.hazmat.primitives.serialization import load_pem_private_key, pkcs7
13051300
from cryptography.x509 import load_pem_x509_certificate
@@ -1347,7 +1342,7 @@ def smime_sign_encrypt(self, email, sign, encrypt):
13471342
raise ValueError("failed to load certificate from file")
13481343

13491344
recipient_certs.append(c)
1350-
1345+
13511346
try:
13521347
pubkey = load_pem_x509_certificate(pubkey)
13531348
except ValueError as e:
@@ -1360,7 +1355,6 @@ def smime_sign_encrypt(self, email, sign, encrypt):
13601355
for recip in recipient_certs:
13611356
envelope_builder = envelope_builder.add_recipient(recip)
13621357

1363-
13641358
options = [pkcs7.PKCS7Options.Binary]
13651359
encrypted_email = envelope_builder.encrypt(serialization.Encoding.SMIME, options)
13661360
return encrypted_email
@@ -1371,7 +1365,6 @@ def smime_encrypt_only(self, email, encrypt):
13711365
from cryptography.x509 import load_pem_x509_certificate
13721366
from cryptography.hazmat.primitives import serialization
13731367

1374-
13751368
if self._cert:
13761369
certificates = [self._cert]
13771370
else:
@@ -1396,16 +1389,15 @@ def smime_encrypt_only(self, email, encrypt):
13961389

13971390
return encrypted_email
13981391

1399-
1400-
def _encrypt_smime_now(self, email, sign, encrypt: Union[None, bool, bytes, List[bytes]]):
1392+
def _encrypt_smime_now(self, email, sign, encrypt: Union[None, bool, bytes, list[bytes]]):
14011393
"""
14021394
14031395
:type encrypt: Can be None, False, bytes or list[bytes]
14041396
"""
14051397

14061398
# passphrase has to be bytes
14071399
if (self._passphrase is not None):
1408-
self._passphrase = self._passphrase.encode('utf-8')
1400+
self._passphrase = self._passphrase.encode('utf-8')
14091401

14101402
if sign is not None and type(sign) != bool:
14111403
sign = assure_fetched(sign, bytes)
@@ -1421,8 +1413,6 @@ def _encrypt_smime_now(self, email, sign, encrypt: Union[None, bool, bytes, List
14211413

14221414
return output
14231415

1424-
1425-
14261416
def _compose_gpg_signed(self, email, text, micalg=None):
14271417
msg_payload = email
14281418
email = EmailMessage()
@@ -1600,7 +1590,7 @@ def _prepare_email(self, plain: bytes, html: bytes, encrypt_gpg: bool, sign_gpg:
16001590
email["Subject"] = self._subject
16011591
return email
16021592

1603-
def recipients(self, *, clear=False) -> Union[Set[Address], 'Envelope']:
1593+
def recipients(self, *, clear=False) -> Union[set[Address], 'Envelope']:
16041594
""" Return set of all recipients – To, Cc, Bcc
16051595
:param: clear If true, all To, Cc and Bcc recipients are removed and the object is returned.
16061596
@@ -1632,7 +1622,7 @@ def _report(self) -> Union[dict, None]:
16321622
raise NotImplemented("Current multipart/report has not been impemented."
16331623
f"Please post current message as a new issue at {ISSUE_LINK}")
16341624

1635-
def attachments(self, name=None, inline=None) -> Union[Attachment, List[Attachment], bool]:
1625+
def attachments(self, name=None, inline=None) -> Union[Attachment, list[Attachment], bool]:
16361626
""" Access the attachments.
16371627
XX make available from CLI too
16381628
--attachments(-inline)(-enclosed) [name]

test_.py

+4-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
from email.utils import getaddresses, parseaddr
21
import logging
32
import re
43
import sys
@@ -14,7 +13,7 @@
1413
from unittest import main, TestCase, mock
1514

1615
from envelope import Envelope
17-
from envelope.address import Address
16+
from envelope.address import Address, _parseaddr, _getaddresses
1817
from envelope.constants import AUTO, PLAIN, HTML
1918
from envelope.parser import Parser
2019
from envelope.smtp_handler import SMTPHandler
@@ -1245,11 +1244,11 @@ def test_disguised_addresses(self):
12451244
# https://github.com/python/cpython/issues/40889#issuecomment-1094001067
12461245
12471246
1248-
self.assertEqual(('', '[email protected]'), parseaddr(disguise_addr))
1247+
self.assertEqual(('', '[email protected]'), _parseaddr(disguise_addr))
12491248
self.assertEqual([('', '[email protected]'), ('', '[email protected]')],
1250-
getaddresses([disguise_addr]))
1249+
_getaddresses([disguise_addr]))
12511250
self.assertEqual([('', '[email protected]'), ('', '[email protected]')],
1252-
getaddresses([same]))
1251+
_getaddresses([same]))
12531252

12541253
# For the same input, Envelope receives better results.
12551254
self.assertEqual(Address(name='[email protected]', address='[email protected]'), Address(disguise_addr))

0 commit comments

Comments
 (0)