Chore(mypy): Annotate None returns for typing improvements (#11213)

This commit is contained in:
Sebastian Steinbeißer
2026-02-02 17:44:12 +01:00
committed by GitHub
parent a9c0b06e28
commit 3b5ffbf9fa
113 changed files with 1598 additions and 1510 deletions

View File

@@ -10,7 +10,7 @@ class PaperlessMailConfig(AppConfig):
verbose_name = _("Paperless mail")
def ready(self):
def ready(self) -> None:
from documents.signals import document_consumer_declaration
if settings.TIKA_ENABLED:

View File

@@ -107,7 +107,7 @@ class DeleteMailAction(BaseMailAction):
A mail action that deletes mails after processing.
"""
def post_consume(self, M: MailBox, message_uid: str, parameter: str):
def post_consume(self, M: MailBox, message_uid: str, parameter: str) -> None:
M.delete(message_uid)
@@ -119,7 +119,7 @@ class MarkReadMailAction(BaseMailAction):
def get_criteria(self):
return {"seen": False}
def post_consume(self, M: MailBox, message_uid: str, parameter: str):
def post_consume(self, M: MailBox, message_uid: str, parameter: str) -> None:
M.flag(message_uid, [MailMessageFlags.SEEN], value=True)
@@ -128,7 +128,7 @@ class MoveMailAction(BaseMailAction):
A mail action that moves mails to a different folder after processing.
"""
def post_consume(self, M, message_uid, parameter):
def post_consume(self, M, message_uid, parameter) -> None:
M.move(message_uid, parameter)
@@ -140,7 +140,7 @@ class FlagMailAction(BaseMailAction):
def get_criteria(self):
return {"flagged": False}
def post_consume(self, M: MailBox, message_uid: str, parameter: str):
def post_consume(self, M: MailBox, message_uid: str, parameter: str) -> None:
M.flag(message_uid, [MailMessageFlags.FLAGGED], value=True)
@@ -149,7 +149,7 @@ class TagMailAction(BaseMailAction):
A mail action that tags mails after processing.
"""
def __init__(self, parameter: str, *, supports_gmail_labels: bool):
def __init__(self, parameter: str, *, supports_gmail_labels: bool) -> None:
# The custom tag should look like "apple:<color>"
if "apple:" in parameter.lower():
_, self.color = parameter.split(":")
@@ -177,7 +177,7 @@ class TagMailAction(BaseMailAction):
else: # pragma: no cover
raise ValueError("This should never happen.")
def post_consume(self, M: MailBox, message_uid: str, parameter: str):
def post_consume(self, M: MailBox, message_uid: str, parameter: str) -> None:
if self.supports_gmail_labels:
M.client.uid("STORE", message_uid, "+X-GM-LABELS", self.keyword)
@@ -205,7 +205,7 @@ class TagMailAction(BaseMailAction):
raise MailError("No keyword specified.")
def mailbox_login(mailbox: MailBox, account: MailAccount):
def mailbox_login(mailbox: MailBox, account: MailAccount) -> None:
logger = logging.getLogger("paperless_mail")
try:
@@ -241,7 +241,7 @@ def apply_mail_action(
message_uid: str,
message_subject: str,
message_date: datetime.datetime,
):
) -> None:
"""
This shared task applies the mail action of a particular mail rule to the
given mail. Creates a ProcessedMail object, so that the mail won't be
@@ -310,7 +310,7 @@ def error_callback(
message_uid: str,
message_subject: str,
message_date: datetime.datetime,
):
) -> None:
"""
A shared task that is called whenever something goes wrong during
consumption of a file. See queue_consumption_tasks.
@@ -333,7 +333,7 @@ def queue_consumption_tasks(
consume_tasks: list[Signature],
rule: MailRule,
message: MailMessage,
):
) -> None:
"""
Queue a list of consumption tasks (Signatures for the consume_file shared
task) with celery.
@@ -450,12 +450,12 @@ class MailAccountHandler(LoggingMixin):
self.renew_logging_group()
self._init_preprocessors()
def _init_preprocessors(self):
def _init_preprocessors(self) -> None:
self._message_preprocessors: list[MailMessagePreprocessor] = []
for preprocessor_type in self._message_preprocessor_types:
self._init_preprocessor(preprocessor_type)
def _init_preprocessor(self, preprocessor_type):
def _init_preprocessor(self, preprocessor_type) -> None:
if preprocessor_type.able_to_run():
try:
self._message_preprocessors.append(preprocessor_type())

View File

@@ -128,7 +128,7 @@ class MailDocumentParser(DocumentParser):
mime_type: str,
file_name=None,
mailrule_id: int | None = None,
):
) -> None:
"""
Parses the given .eml into formatted text, based on the decoded email.
@@ -471,7 +471,7 @@ class MailDocumentParser(DocumentParser):
html_pdf.write_bytes(response.content)
return html_pdf
def get_settings(self):
def get_settings(self) -> None:
"""
This parser does not implement additional settings yet
"""

View File

@@ -39,7 +39,7 @@ class MailMessageDecryptor(MailMessagePreprocessor, LoggingMixin):
NAME = "MailMessageDecryptor"
def __init__(self):
def __init__(self) -> None:
super().__init__()
self.renew_logging_group()
self._gpg = GPG(gnupghome=settings.EMAIL_GNUPG_HOME)

View File

@@ -21,7 +21,7 @@ from paperless_mail.tests.test_mail import BogusMailBox
class TestAPIMailAccounts(DirectoriesMixin, APITestCase):
ENDPOINT = "/api/mail_accounts/"
def setUp(self):
def setUp(self) -> None:
self.bogus_mailbox = BogusMailBox()
patcher = mock.patch("paperless_mail.mail.MailBox")
@@ -36,7 +36,7 @@ class TestAPIMailAccounts(DirectoriesMixin, APITestCase):
self.user.save()
self.client.force_authenticate(user=self.user)
def test_get_mail_accounts(self):
def test_get_mail_accounts(self) -> None:
"""
GIVEN:
- Configured mail accounts
@@ -73,7 +73,7 @@ class TestAPIMailAccounts(DirectoriesMixin, APITestCase):
self.assertEqual(returned_account1["imap_security"], account1.imap_security)
self.assertEqual(returned_account1["character_set"], account1.character_set)
def test_create_mail_account(self):
def test_create_mail_account(self) -> None:
"""
WHEN:
- API request is made to add a mail account
@@ -108,7 +108,7 @@ class TestAPIMailAccounts(DirectoriesMixin, APITestCase):
self.assertEqual(returned_account1.imap_security, account1["imap_security"])
self.assertEqual(returned_account1.character_set, account1["character_set"])
def test_delete_mail_account(self):
def test_delete_mail_account(self) -> None:
"""
GIVEN:
- Existing mail account
@@ -136,7 +136,7 @@ class TestAPIMailAccounts(DirectoriesMixin, APITestCase):
self.assertEqual(len(MailAccount.objects.all()), 0)
def test_update_mail_account(self):
def test_update_mail_account(self) -> None:
"""
GIVEN:
- Existing mail accounts
@@ -184,7 +184,7 @@ class TestAPIMailAccounts(DirectoriesMixin, APITestCase):
self.assertEqual(returned_account2.name, "Updated Name 2")
self.assertEqual(returned_account2.password, "123xyz")
def test_mail_account_test_fail(self):
def test_mail_account_test_fail(self) -> None:
"""
GIVEN:
- Errnoeous mail account details
@@ -210,7 +210,7 @@ class TestAPIMailAccounts(DirectoriesMixin, APITestCase):
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
def test_mail_account_test_success(self):
def test_mail_account_test_success(self) -> None:
"""
GIVEN:
- Working mail account details
@@ -236,7 +236,7 @@ class TestAPIMailAccounts(DirectoriesMixin, APITestCase):
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data["success"], True)
def test_mail_account_test_existing(self):
def test_mail_account_test_existing(self) -> None:
"""
GIVEN:
- Testing server details for an existing account with obfuscated password (***)
@@ -272,7 +272,7 @@ class TestAPIMailAccounts(DirectoriesMixin, APITestCase):
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data["success"], True)
def test_get_mail_accounts_owner_aware(self):
def test_get_mail_accounts_owner_aware(self) -> None:
"""
GIVEN:
- Configured accounts with different users
@@ -343,7 +343,7 @@ class TestAPIMailAccounts(DirectoriesMixin, APITestCase):
class TestAPIMailRules(DirectoriesMixin, APITestCase):
ENDPOINT = "/api/mail_rules/"
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.user = User.objects.create_user(username="temp_admin")
@@ -351,7 +351,7 @@ class TestAPIMailRules(DirectoriesMixin, APITestCase):
self.user.save()
self.client.force_authenticate(user=self.user)
def test_get_mail_rules(self):
def test_get_mail_rules(self) -> None:
"""
GIVEN:
- Configured mail accounts and rules
@@ -415,7 +415,7 @@ class TestAPIMailRules(DirectoriesMixin, APITestCase):
self.assertEqual(returned_rule1["order"], rule1.order)
self.assertEqual(returned_rule1["attachment_type"], rule1.attachment_type)
def test_create_mail_rule(self):
def test_create_mail_rule(self) -> None:
"""
GIVEN:
- Configured mail account exists
@@ -520,7 +520,7 @@ class TestAPIMailRules(DirectoriesMixin, APITestCase):
rule1["assign_owner_from_rule"],
)
def test_delete_mail_rule(self):
def test_delete_mail_rule(self) -> None:
"""
GIVEN:
- Existing mail rule
@@ -564,7 +564,7 @@ class TestAPIMailRules(DirectoriesMixin, APITestCase):
self.assertEqual(len(MailRule.objects.all()), 0)
def test_update_mail_rule(self):
def test_update_mail_rule(self) -> None:
"""
GIVEN:
- Existing mail rule
@@ -614,7 +614,7 @@ class TestAPIMailRules(DirectoriesMixin, APITestCase):
self.assertEqual(returned_rule1.name, "Updated Name 1")
self.assertEqual(returned_rule1.action, MailRule.MailAction.DELETE)
def test_get_mail_rules_owner_aware(self):
def test_get_mail_rules_owner_aware(self) -> None:
"""
GIVEN:
- Configured rules with different users
@@ -683,7 +683,7 @@ class TestAPIMailRules(DirectoriesMixin, APITestCase):
self.assertEqual(response.data["results"][1]["name"], rule2.name)
self.assertEqual(response.data["results"][2]["name"], rule4.name)
def test_mailrule_maxage_validation(self):
def test_mailrule_maxage_validation(self) -> None:
"""
GIVEN:
- An existing mail account
@@ -728,7 +728,7 @@ class TestAPIMailRules(DirectoriesMixin, APITestCase):
class TestAPIProcessedMails(DirectoriesMixin, APITestCase):
ENDPOINT = "/api/processed_mail/"
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.user = User.objects.create_user(username="temp_admin")
@@ -736,7 +736,7 @@ class TestAPIProcessedMails(DirectoriesMixin, APITestCase):
self.user.save()
self.client.force_authenticate(user=self.user)
def test_get_processed_mails_owner_aware(self):
def test_get_processed_mails_owner_aware(self) -> None:
"""
GIVEN:
- Configured processed mails with different users
@@ -821,7 +821,7 @@ class TestAPIProcessedMails(DirectoriesMixin, APITestCase):
returned_ids = {r["id"] for r in response.data["results"]}
self.assertSetEqual(returned_ids, {pm1.id, pm2.id, pm4.id})
def test_get_processed_mails_filter_by_rule(self):
def test_get_processed_mails_filter_by_rule(self) -> None:
"""
GIVEN:
- Processed mails belonging to two different rules
@@ -893,7 +893,7 @@ class TestAPIProcessedMails(DirectoriesMixin, APITestCase):
returned_ids = {r["id"] for r in response.data["results"]}
self.assertSetEqual(returned_ids, {pm1.id, pm2.id})
def test_bulk_delete_processed_mails(self):
def test_bulk_delete_processed_mails(self) -> None:
"""
GIVEN:
- Processed mails belonging to two different rules and different users

View File

@@ -51,30 +51,30 @@ class _AttachmentDef:
class BogusFolderManager:
current_folder = "INBOX"
def set(self, new_folder):
def set(self, new_folder) -> None:
if new_folder not in ["INBOX", "spam"]:
raise MailboxFolderSelectError(None, "uhm")
self.current_folder = new_folder
class BogusClient:
def __init__(self, messages):
def __init__(self, messages) -> None:
self.messages: list[MailMessage] = messages
self.capabilities: list[str] = []
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
def __exit__(self, exc_type, exc_val, exc_tb) -> None:
pass
def authenticate(self, mechanism, authobject):
def authenticate(self, mechanism, authobject) -> None:
# authobject must be a callable object
auth_bytes = authobject(None)
if auth_bytes != b"\x00admin\x00w57\xc3\xa4\xc3\xb6\xc3\xbcw4b6huwb6nhu":
raise MailboxLoginError("BAD", "OK")
def uid(self, command, *args):
def uid(self, command, *args) -> None:
if command == "STORE":
for message in self.messages:
if message.uid == args[0]:
@@ -94,7 +94,7 @@ class BogusMailBox(AbstractContextManager):
# A dummy access token
ACCESS_TOKEN = "ea7e075cd3acf2c54c48e600398d5d5a"
def __init__(self):
def __init__(self) -> None:
self.messages: list[MailMessage] = []
self.messages_spam: list[MailMessage] = []
self.folder = BogusFolderManager()
@@ -104,25 +104,25 @@ class BogusMailBox(AbstractContextManager):
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
def __exit__(self, exc_type, exc_val, exc_tb) -> None:
pass
def updateClient(self):
def updateClient(self) -> None:
self.client = BogusClient(self.messages)
def login(self, username, password):
def login(self, username, password) -> None:
# This will raise a UnicodeEncodeError if the password is not ASCII only
password.encode("ascii")
# Otherwise, check for correct values
if username != self.USERNAME or password != self.ASCII_PASSWORD:
raise MailboxLoginError("BAD", "OK")
def login_utf8(self, username, password):
def login_utf8(self, username, password) -> None:
# Expected to only be called with the UTF-8 password
if username != self.USERNAME or password != self.UTF_PASSWORD:
raise MailboxLoginError("BAD", "OK")
def xoauth2(self, username: str, access_token: str):
def xoauth2(self, username: str, access_token: str) -> None:
if username != self.USERNAME or access_token != self.ACCESS_TOKEN:
raise MailboxLoginError("BAD", "OK")
@@ -166,10 +166,10 @@ class BogusMailBox(AbstractContextManager):
return list(msg)
def delete(self, uid_list):
def delete(self, uid_list) -> None:
self.messages = list(filter(lambda m: m.uid not in uid_list, self.messages))
def flag(self, uid_list, flag_set, value):
def flag(self, uid_list, flag_set, value) -> None:
for message in self.messages:
if message.uid in uid_list:
for flag in flag_set:
@@ -182,7 +182,7 @@ class BogusMailBox(AbstractContextManager):
if hasattr(message, "flags"):
del message.flags
def move(self, uid_list, folder):
def move(self, uid_list, folder) -> None:
if folder == "spam":
self.messages_spam += list(
filter(lambda m: m.uid in uid_list, self.messages),
@@ -203,7 +203,7 @@ def fake_magic_from_buffer(buffer, *, mime=False):
class MessageBuilder:
def __init__(self):
def __init__(self) -> None:
self._used_uids = set()
def create_message(
@@ -274,7 +274,10 @@ class MessageBuilder:
return imap_msg
def reset_bogus_mailbox(bogus_mailbox: BogusMailBox, message_builder: MessageBuilder):
def reset_bogus_mailbox(
bogus_mailbox: BogusMailBox,
message_builder: MessageBuilder,
) -> None:
bogus_mailbox.messages = []
bogus_mailbox.messages_spam = []
bogus_mailbox.messages.append(
@@ -310,7 +313,7 @@ def reset_bogus_mailbox(bogus_mailbox: BogusMailBox, message_builder: MessageBui
class MailMocker(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
def setUp(self):
def setUp(self) -> None:
self.bogus_mailbox = BogusMailBox()
self.messageBuilder = MessageBuilder()
@@ -330,7 +333,7 @@ class MailMocker(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
def assert_queue_consumption_tasks_call_args(
self,
expected_call_args: list[list[dict[str, str]]],
):
) -> None:
"""
Verifies that queue_consumption_tasks has been called with the expected arguments.
@@ -377,7 +380,7 @@ class MailMocker(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
else:
self.fail("No match for expected arg")
def apply_mail_actions(self):
def apply_mail_actions(self) -> None:
"""
Applies pending actions to mails by inspecting calls to the queue_consumption_tasks method.
"""
@@ -387,7 +390,12 @@ class MailMocker(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
apply_mail_action([], rule.pk, message.uid, message.subject, message.date)
def assert_eventually_equals(getter_fn, expected_value, timeout=1.0, interval=0.05):
def assert_eventually_equals(
getter_fn,
expected_value,
timeout=1.0,
interval=0.05,
) -> None:
"""
Repeatedly calls `getter_fn()` until the result equals `expected_value`,
or times out after `timeout` seconds.
@@ -395,7 +403,7 @@ def assert_eventually_equals(getter_fn, expected_value, timeout=1.0, interval=0.
deadline = time.time() + timeout
while time.time() < deadline:
if getter_fn() == expected_value:
return
return None
time.sleep(interval)
actual = getter_fn()
raise AssertionError(f"Expected {expected_value}, but got {actual}")
@@ -407,14 +415,14 @@ class TestMail(
FileSystemAssertsMixin,
TestCase,
):
def setUp(self):
def setUp(self) -> None:
self.mailMocker = MailMocker()
self.mailMocker.setUp()
self.mail_account_handler = MailAccountHandler()
super().setUp()
def test_get_correspondent(self):
def test_get_correspondent(self) -> None:
message = namedtuple("MailMessage", [])
message.from_ = "someone@somewhere.com"
message.from_values = EmailAddress(
@@ -473,7 +481,7 @@ class TestMail(
c = handler._get_correspondent(message, rule)
self.assertEqual(c, someone_else)
def test_get_title(self):
def test_get_title(self) -> None:
message = namedtuple("MailMessage", [])
message.subject = "the message title"
att = namedtuple("Attachment", [])
@@ -497,7 +505,7 @@ class TestMail(
)
self.assertEqual(handler._get_title(message, att, rule), None)
def test_handle_message(self):
def test_handle_message(self) -> None:
message = self.mailMocker.messageBuilder.create_message(
subject="the message title",
from_="Myself",
@@ -526,7 +534,7 @@ class TestMail(
],
)
def test_handle_empty_message(self):
def test_handle_empty_message(self) -> None:
message = namedtuple("MailMessage", [])
message.attachments = []
@@ -537,7 +545,7 @@ class TestMail(
self.mailMocker._queue_consumption_tasks_mock.assert_not_called()
self.assertEqual(result, 0)
def test_handle_unknown_mime_type(self):
def test_handle_unknown_mime_type(self) -> None:
message = self.mailMocker.messageBuilder.create_message(
attachments=[
_AttachmentDef(filename="f1.pdf"),
@@ -566,7 +574,7 @@ class TestMail(
],
)
def test_handle_disposition(self):
def test_handle_disposition(self) -> None:
message = self.mailMocker.messageBuilder.create_message(
attachments=[
_AttachmentDef(
@@ -594,7 +602,7 @@ class TestMail(
],
)
def test_handle_inline_files(self):
def test_handle_inline_files(self) -> None:
message = self.mailMocker.messageBuilder.create_message(
attachments=[
_AttachmentDef(
@@ -624,7 +632,7 @@ class TestMail(
],
)
def test_filename_filter(self):
def test_filename_filter(self) -> None:
"""
GIVEN:
- Email with multiple similar named attachments
@@ -745,7 +753,7 @@ class TestMail(
)
@pytest.mark.flaky(reruns=4)
def test_filename_filter_inline_no_consumption(self):
def test_filename_filter_inline_no_consumption(self) -> None:
"""
GIVEN:
- Rule that processes all attachments but filters by filename
@@ -787,7 +795,7 @@ class TestMail(
self.assertEqual(len(self.mailMocker.bogus_mailbox.messages), 1)
def test_handle_mail_account_mark_read(self):
def test_handle_mail_account_mark_read(self) -> None:
account = MailAccount.objects.create(
name="test",
imap_server="",
@@ -817,7 +825,7 @@ class TestMail(
self.assertEqual(len(self.mailMocker.bogus_mailbox.messages), 3)
@pytest.mark.flaky(reruns=4)
def test_handle_mail_account_delete(self):
def test_handle_mail_account_delete(self) -> None:
account = MailAccount.objects.create(
name="test",
imap_server="",
@@ -839,7 +847,7 @@ class TestMail(
assert_eventually_equals(lambda: len(self.mailMocker.bogus_mailbox.messages), 1)
def test_handle_mail_account_delete_no_filters(self):
def test_handle_mail_account_delete_no_filters(self) -> None:
account = MailAccount.objects.create(
name="test",
imap_server="",
@@ -862,7 +870,7 @@ class TestMail(
self.assertEqual(len(self.mailMocker.bogus_mailbox.messages), 0)
@pytest.mark.flaky(reruns=4)
def test_handle_mail_account_flag(self):
def test_handle_mail_account_flag(self) -> None:
account = MailAccount.objects.create(
name="test",
imap_server="",
@@ -893,7 +901,7 @@ class TestMail(
self.assertEqual(len(self.mailMocker.bogus_mailbox.messages), 3)
@pytest.mark.flaky(reruns=4)
def test_handle_mail_account_move(self):
def test_handle_mail_account_move(self) -> None:
account = MailAccount.objects.create(
name="test",
imap_server="",
@@ -918,7 +926,7 @@ class TestMail(
self.assertEqual(len(self.mailMocker.bogus_mailbox.messages), 2)
self.assertEqual(len(self.mailMocker.bogus_mailbox.messages_spam), 1)
def test_handle_mail_account_move_no_filters(self):
def test_handle_mail_account_move_no_filters(self) -> None:
account = MailAccount.objects.create(
name="test",
imap_server="",
@@ -943,7 +951,7 @@ class TestMail(
self.assertEqual(len(self.mailMocker.bogus_mailbox.messages), 0)
self.assertEqual(len(self.mailMocker.bogus_mailbox.messages_spam), 3)
def test_handle_mail_account_tag(self):
def test_handle_mail_account_tag(self) -> None:
account = MailAccount.objects.create(
name="test",
imap_server="",
@@ -983,7 +991,7 @@ class TestMail(
0,
)
def test_handle_mail_account_tag_gmail(self):
def test_handle_mail_account_tag_gmail(self) -> None:
self.mailMocker.bogus_mailbox._host = "imap.gmail.com"
self.mailMocker.bogus_mailbox.client.capabilities = ["X-GM-EXT-1"]
@@ -1017,7 +1025,7 @@ class TestMail(
)
self.assertEqual(len(self.mailMocker.bogus_mailbox.messages), 3)
def test_tag_mail_action_applemail_wrong_input(self):
def test_tag_mail_action_applemail_wrong_input(self) -> None:
self.assertRaises(
MailError,
TagMailAction,
@@ -1025,7 +1033,7 @@ class TestMail(
supports_gmail_labels=False,
)
def test_handle_mail_account_tag_applemail(self):
def test_handle_mail_account_tag_applemail(self) -> None:
# all mails will be FLAGGED afterwards
account = MailAccount.objects.create(
@@ -1057,7 +1065,7 @@ class TestMail(
)
self.assertEqual(len(self.mailMocker.bogus_mailbox.messages), 3)
def test_error_login(self):
def test_error_login(self) -> None:
"""
GIVEN:
- Account configured with incorrect password
@@ -1080,7 +1088,7 @@ class TestMail(
self.mail_account_handler.handle_mail_account(account)
@pytest.mark.flaky(reruns=4)
def test_error_skip_account(self):
def test_error_skip_account(self) -> None:
_ = MailAccount.objects.create(
name="test",
imap_server="",
@@ -1109,7 +1117,7 @@ class TestMail(
self.assertEqual(len(self.mailMocker.bogus_mailbox.messages_spam), 1)
@pytest.mark.flaky(reruns=4)
def test_error_skip_rule(self):
def test_error_skip_rule(self) -> None:
account = MailAccount.objects.create(
name="test2",
imap_server="",
@@ -1140,7 +1148,7 @@ class TestMail(
self.assertEqual(len(self.mailMocker.bogus_mailbox.messages), 2)
self.assertEqual(len(self.mailMocker.bogus_mailbox.messages_spam), 1)
def test_error_folder_set(self):
def test_error_folder_set(self) -> None:
"""
GIVEN:
- Mail rule with non-existent folder
@@ -1173,7 +1181,7 @@ class TestMail(
self.mailMocker.bogus_mailbox.folder.list.assert_called_once()
self.mailMocker._queue_consumption_tasks_mock.assert_not_called()
def test_error_folder_set_error_listing(self):
def test_error_folder_set_error_listing(self) -> None:
"""
GIVEN:
- Mail rule with non-existent folder
@@ -1208,8 +1216,8 @@ class TestMail(
@pytest.mark.flaky(reruns=4)
@mock.patch("paperless_mail.mail.MailAccountHandler._get_correspondent")
def test_error_skip_mail(self, m):
def get_correspondent_fake(message, rule):
def test_error_skip_mail(self, m) -> None:
def get_correspondent_fake(message, rule) -> None:
if message.from_ == "amazon@amazon.de":
raise ValueError("Does not compute.")
else:
@@ -1243,7 +1251,7 @@ class TestMail(
"amazon@amazon.de",
)
def test_error_create_correspondent(self):
def test_error_create_correspondent(self) -> None:
account = MailAccount.objects.create(
name="test2",
imap_server="",
@@ -1292,7 +1300,7 @@ class TestMail(
)
@pytest.mark.flaky(reruns=4)
def test_filters(self):
def test_filters(self) -> None:
account = MailAccount.objects.create(
name="test3",
imap_server="",
@@ -1342,7 +1350,7 @@ class TestMail(
expected_mail_count,
)
def test_auth_plain_fallback(self):
def test_auth_plain_fallback(self) -> None:
"""
GIVEN:
- Mail account with password containing non-ASCII characters
@@ -1382,7 +1390,7 @@ class TestMail(
)
self.assertEqual(len(self.mailMocker.bogus_mailbox.messages), 3)
def test_auth_plain_fallback_fails_still(self):
def test_auth_plain_fallback_fails_still(self) -> None:
"""
GIVEN:
- Mail account with password containing non-ASCII characters
@@ -1413,7 +1421,7 @@ class TestMail(
account,
)
def test_auth_with_valid_token(self):
def test_auth_with_valid_token(self) -> None:
"""
GIVEN:
- Mail account configured with access token
@@ -1454,7 +1462,7 @@ class TestMail(
)
self.assertEqual(len(self.mailMocker.bogus_mailbox.messages), 3)
def test_disabled_rule(self):
def test_disabled_rule(self) -> None:
"""
GIVEN:
- Mail rule is disabled
@@ -1494,7 +1502,7 @@ class TestMail(
class TestPostConsumeAction(TestCase):
def setUp(self):
def setUp(self) -> None:
self.account = MailAccount.objects.create(
name="test",
imap_server="imap.test.com",
@@ -1586,7 +1594,7 @@ class TestManagementCommand(TestCase):
@mock.patch(
"paperless_mail.management.commands.mail_fetcher.tasks.process_mail_accounts",
)
def test_mail_fetcher(self, m):
def test_mail_fetcher(self, m) -> None:
call_command("mail_fetcher")
m.assert_called_once()
@@ -1594,7 +1602,7 @@ class TestManagementCommand(TestCase):
class TestTasks(TestCase):
@mock.patch("paperless_mail.tasks.MailAccountHandler.handle_mail_account")
def test_all_accounts(self, m):
def test_all_accounts(self, m) -> None:
m.side_effect = lambda account: 6
MailAccount.objects.create(
@@ -1628,7 +1636,7 @@ class TestTasks(TestCase):
self.assertIn("No new", result)
@mock.patch("paperless_mail.tasks.MailAccountHandler.handle_mail_account")
def test_accounts_no_enabled_rules(self, m):
def test_accounts_no_enabled_rules(self, m) -> None:
m.side_effect = lambda account: 6
MailAccount.objects.create(
@@ -1658,7 +1666,7 @@ class TestTasks(TestCase):
self.assertEqual(m.call_count, 0)
@mock.patch("paperless_mail.tasks.MailAccountHandler.handle_mail_account")
def test_process_with_account_ids(self, m):
def test_process_with_account_ids(self, m) -> None:
m.side_effect = lambda account: 6
account_a = MailAccount.objects.create(
@@ -1693,7 +1701,7 @@ class TestTasks(TestCase):
class TestMailAccountTestView(APITestCase):
def setUp(self):
def setUp(self) -> None:
self.mailMocker = MailMocker()
self.mailMocker.setUp()
self.user = User.objects.create_user(
@@ -1703,7 +1711,7 @@ class TestMailAccountTestView(APITestCase):
self.client.force_authenticate(user=self.user)
self.url = "/api/mail_accounts/test/"
def test_mail_account_test_view_success(self):
def test_mail_account_test_view_success(self) -> None:
data = {
"imap_server": "imap.example.com",
"imap_port": 993,
@@ -1717,7 +1725,7 @@ class TestMailAccountTestView(APITestCase):
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data, {"success": True})
def test_mail_account_test_view_mail_error(self):
def test_mail_account_test_view_mail_error(self) -> None:
data = {
"imap_server": "imap.example.com",
"imap_port": 993,
@@ -1818,7 +1826,7 @@ class TestMailAccountTestView(APITestCase):
class TestMailAccountProcess(APITestCase):
def setUp(self):
def setUp(self) -> None:
self.mailMocker = MailMocker()
self.mailMocker.setUp()
self.user = User.objects.create_superuser(
@@ -1838,14 +1846,14 @@ class TestMailAccountProcess(APITestCase):
self.url = f"/api/mail_accounts/{self.account.pk}/process/"
@mock.patch("paperless_mail.tasks.process_mail_accounts.delay")
def test_mail_account_process_view(self, m):
def test_mail_account_process_view(self, m) -> None:
response = self.client.post(self.url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
m.assert_called_once()
class TestMailRuleAPI(APITestCase):
def setUp(self):
def setUp(self) -> None:
self.user = User.objects.create_superuser(
username="testuser",
password="testpassword",
@@ -1862,7 +1870,7 @@ class TestMailRuleAPI(APITestCase):
)
self.url = "/api/mail_rules/"
def test_create_mail_rule(self):
def test_create_mail_rule(self) -> None:
"""
GIVEN:
- Valid data for creating a mail rule
@@ -1884,7 +1892,7 @@ class TestMailRuleAPI(APITestCase):
rule = MailRule.objects.first()
self.assertEqual(rule.name, "Test Rule")
def test_mail_rule_action_parameter_required_for_tag_or_move(self):
def test_mail_rule_action_parameter_required_for_tag_or_move(self) -> None:
"""
GIVEN:
- Valid data for creating a mail rule without action_parameter

View File

@@ -39,7 +39,7 @@ class TestMailOAuth(
settings.OUTLOOK_OAUTH_CLIENT_SECRET = "test_outlook_client_secret"
super().setUp()
def test_generate_paths(self):
def test_generate_paths(self) -> None:
"""
GIVEN:
- Mocked settings for OAuth callback and base URLs
@@ -148,7 +148,7 @@ class TestMailOAuth(
)
@mock.patch("httpx_oauth.oauth2.BaseOAuth2.get_access_token")
def test_oauth_callback_view_fails(self, mock_get_access_token):
def test_oauth_callback_view_fails(self, mock_get_access_token) -> None:
"""
GIVEN:
- Mocked settings for Gmail and Outlook OAuth client IDs and secrets
@@ -193,7 +193,7 @@ class TestMailOAuth(
self.assertIn("Error getting access token: test_error", cm.output[0])
def test_oauth_callback_view_insufficient_permissions(self):
def test_oauth_callback_view_insufficient_permissions(self) -> None:
"""
GIVEN:
- Mocked settings for Gmail and Outlook OAuth client IDs and secrets
@@ -223,7 +223,7 @@ class TestMailOAuth(
MailAccount.objects.filter(imap_server="outlook.office365.com").exists(),
)
def test_oauth_callback_view_no_code(self):
def test_oauth_callback_view_no_code(self) -> None:
"""
GIVEN:
- Mocked settings for Gmail and Outlook OAuth client IDs and secrets
@@ -244,7 +244,7 @@ class TestMailOAuth(
MailAccount.objects.filter(imap_server="outlook.office365.com").exists(),
)
def test_oauth_callback_view_invalid_state(self):
def test_oauth_callback_view_invalid_state(self) -> None:
"""
GIVEN:
- Mocked settings for Gmail and Outlook OAuth client IDs and secrets

View File

@@ -24,7 +24,7 @@ class TestEmailFileParsing:
self,
mail_parser: MailDocumentParser,
sample_dir: Path,
):
) -> None:
"""
GIVEN:
- Fresh parser
@@ -45,7 +45,7 @@ class TestEmailFileParsing:
self,
mail_parser: MailDocumentParser,
broken_email_file: Path,
):
) -> None:
"""
GIVEN:
- Fresh parser
@@ -63,7 +63,7 @@ class TestEmailFileParsing:
self,
mail_parser: MailDocumentParser,
simple_txt_email_file: Path,
):
) -> None:
"""
GIVEN:
- Fresh parser
@@ -97,7 +97,7 @@ class TestEmailMetadataExtraction:
self,
caplog: pytest.LogCaptureFixture,
mail_parser: MailDocumentParser,
):
) -> None:
"""
GIVEN:
- Fresh start
@@ -120,7 +120,7 @@ class TestEmailMetadataExtraction:
self,
mail_parser: MailDocumentParser,
simple_txt_email_file: Path,
):
) -> None:
"""
GIVEN:
- Fresh start
@@ -234,7 +234,7 @@ class TestEmailThumbnailGenerate:
mocker: MockerFixture,
mail_parser: MailDocumentParser,
simple_txt_email_file: Path,
):
) -> None:
"""
GIVEN:
- An E-Mail was parsed
@@ -271,7 +271,7 @@ class TestTikaHtmlParse:
self,
httpx_mock: HTTPXMock,
mail_parser: MailDocumentParser,
):
) -> None:
"""
GIVEN:
- Fresh start
@@ -287,7 +287,11 @@ class TestTikaHtmlParse:
parsed = mail_parser.tika_parse("None")
assert parsed == ""
def test_tika_parse(self, httpx_mock: HTTPXMock, mail_parser: MailDocumentParser):
def test_tika_parse(
self,
httpx_mock: HTTPXMock,
mail_parser: MailDocumentParser,
) -> None:
"""
GIVEN:
- Fresh start
@@ -314,7 +318,7 @@ class TestTikaHtmlParse:
self,
httpx_mock: HTTPXMock,
mail_parser: MailDocumentParser,
):
) -> None:
"""
GIVEN:
- Fresh start
@@ -334,7 +338,7 @@ class TestTikaHtmlParse:
self,
settings: SettingsWrapper,
mail_parser: MailDocumentParser,
):
) -> None:
"""
GIVEN:
- Fresh start
@@ -357,7 +361,7 @@ class TestParser:
mocker: MockerFixture,
mail_parser: MailDocumentParser,
simple_txt_email_file: Path,
):
) -> None:
"""
GIVEN:
- Fresh start
@@ -403,7 +407,7 @@ class TestParser:
httpx_mock: HTTPXMock,
mail_parser: MailDocumentParser,
html_email_file: Path,
):
) -> None:
"""
GIVEN:
- Fresh start
@@ -457,7 +461,7 @@ class TestParser:
httpx_mock: HTTPXMock,
mail_parser: MailDocumentParser,
simple_txt_email_file: Path,
):
) -> None:
"""
GIVEN:
- Fresh start
@@ -477,7 +481,7 @@ class TestParser:
mail_parser: MailDocumentParser,
simple_txt_email_file: Path,
simple_txt_email_pdf_file: Path,
):
) -> None:
"""
GIVEN:
- Simple text email with no HTML content
@@ -505,7 +509,7 @@ class TestParser:
mail_parser: MailDocumentParser,
html_email_file: Path,
html_email_pdf_file: Path,
):
) -> None:
"""
GIVEN:
- email with HTML content
@@ -545,7 +549,7 @@ class TestParser:
mail_parser: MailDocumentParser,
html_email_file: Path,
html_email_pdf_file: Path,
):
) -> None:
"""
GIVEN:
- email with HTML content
@@ -584,7 +588,7 @@ class TestParser:
mail_parser: MailDocumentParser,
html_email_file: Path,
html_email_pdf_file: Path,
):
) -> None:
"""
GIVEN:
- email with HTML content
@@ -621,7 +625,7 @@ class TestParser:
mail_parser: MailDocumentParser,
html_email_file: Path,
html_email_html_file: Path,
):
) -> None:
"""
GIVEN:
- Email message with HTML content
@@ -643,7 +647,7 @@ class TestParser:
httpx_mock: HTTPXMock,
mail_parser: MailDocumentParser,
html_email_file: Path,
):
) -> None:
"""
GIVEN:
- Email message with HTML content
@@ -675,7 +679,7 @@ class TestParser:
mail_parser: MailDocumentParser,
html_email_file: Path,
html_email_pdf_file: Path,
):
) -> None:
"""
GIVEN:
- Email message

View File

@@ -21,7 +21,7 @@ from paperless_mail.tests.test_mail import _AttachmentDef
class MessageEncryptor:
def __init__(self):
def __init__(self) -> None:
self.gpg_home = tempfile.mkdtemp()
self.gpg = gnupg.GPG(gnupghome=self.gpg_home)
self._testUser = "testuser@example.com"
@@ -112,53 +112,53 @@ class MessageEncryptor:
class TestMailMessageGpgDecryptor(TestMail):
@classmethod
def setUpClass(cls):
def setUpClass(cls) -> None:
"""Create GPG encryptor once for all tests in this class."""
super().setUpClass()
cls.messageEncryptor = MessageEncryptor()
@classmethod
def tearDownClass(cls):
def tearDownClass(cls) -> None:
"""Clean up GPG resources after all tests complete."""
if hasattr(cls, "messageEncryptor"):
cls.messageEncryptor.cleanup()
super().tearDownClass()
def setUp(self):
def setUp(self) -> None:
with override_settings(
EMAIL_GNUPG_HOME=self.messageEncryptor.gpg_home,
EMAIL_ENABLE_GPG_DECRYPTOR=True,
):
super().setUp()
def test_preprocessor_is_able_to_run(self):
def test_preprocessor_is_able_to_run(self) -> None:
with override_settings(
EMAIL_GNUPG_HOME=self.messageEncryptor.gpg_home,
EMAIL_ENABLE_GPG_DECRYPTOR=True,
):
self.assertTrue(MailMessageDecryptor.able_to_run())
def test_preprocessor_is_able_to_run2(self):
def test_preprocessor_is_able_to_run2(self) -> None:
with override_settings(
EMAIL_GNUPG_HOME=None,
EMAIL_ENABLE_GPG_DECRYPTOR=True,
):
self.assertTrue(MailMessageDecryptor.able_to_run())
def test_is_not_able_to_run_disabled(self):
def test_is_not_able_to_run_disabled(self) -> None:
with override_settings(
EMAIL_ENABLE_GPG_DECRYPTOR=False,
):
self.assertFalse(MailMessageDecryptor.able_to_run())
def test_is_not_able_to_run_bogus_path(self):
def test_is_not_able_to_run_bogus_path(self) -> None:
with override_settings(
EMAIL_ENABLE_GPG_DECRYPTOR=True,
EMAIL_GNUPG_HOME="_)@# notapath &%#$",
):
self.assertFalse(MailMessageDecryptor.able_to_run())
def test_fails_at_initialization(self):
def test_fails_at_initialization(self) -> None:
with (
mock.patch("gnupg.GPG.__init__") as mock_run,
override_settings(
@@ -174,7 +174,7 @@ class TestMailMessageGpgDecryptor(TestMail):
handler = MailAccountHandler()
self.assertEqual(len(handler._message_preprocessors), 0)
def test_decrypt_fails(self):
def test_decrypt_fails(self) -> None:
encrypted_message, _ = self.create_encrypted_unencrypted_message_pair()
# This test creates its own empty GPG home to test decryption failure
empty_gpg_home = tempfile.mkdtemp()
@@ -199,7 +199,7 @@ class TestMailMessageGpgDecryptor(TestMail):
pass
shutil.rmtree(empty_gpg_home, ignore_errors=True)
def test_decrypt_encrypted_mail(self):
def test_decrypt_encrypted_mail(self) -> None:
"""
Creates a mail with attachments. Then encrypts it with a new key.
Verifies that this encrypted message can be decrypted with attachments intact.
@@ -241,7 +241,7 @@ class TestMailMessageGpgDecryptor(TestMail):
encrypted_message = self.messageEncryptor.encrypt(message)
return encrypted_message, message
def test_handle_encrypted_message(self):
def test_handle_encrypted_message(self) -> None:
message = self.mailMocker.messageBuilder.create_message(
subject="the message title",
from_="Myself",