diff --git a/.ruff.toml b/.ruff.toml index d9ca6b321..a29b471c5 100644 --- a/.ruff.toml +++ b/.ruff.toml @@ -32,6 +32,7 @@ extend-select = [ "RUF", # https://docs.astral.sh/ruff/rules/#ruff-specific-rules-ruf "FLY", # https://docs.astral.sh/ruff/rules/#flynt-fly "PTH", # https://docs.astral.sh/ruff/rules/#flake8-use-pathlib-pth + "FBT", # https://docs.astral.sh/ruff/rules/#flake8-boolean-trap-fbt ] ignore = ["DJ001", "SIM105", "RUF012"] diff --git a/src/documents/bulk_download.py b/src/documents/bulk_download.py index 25dfb5a14..5bdc3e74a 100644 --- a/src/documents/bulk_download.py +++ b/src/documents/bulk_download.py @@ -10,7 +10,7 @@ if TYPE_CHECKING: class BulkArchiveStrategy: - def __init__(self, zipf: ZipFile, follow_formatting: bool = False) -> None: + def __init__(self, zipf: ZipFile, *, follow_formatting: bool = False) -> None: self.zipf: ZipFile = zipf if follow_formatting: self.make_unique_filename: Callable[..., Path | str] = ( @@ -22,6 +22,7 @@ class BulkArchiveStrategy: def _filename_only( self, doc: Document, + *, archive: bool = False, folder: str = "", ) -> str: @@ -33,7 +34,10 @@ class BulkArchiveStrategy: """ counter = 0 while True: - filename: str = folder + doc.get_public_filename(archive, counter) + filename: str = folder + doc.get_public_filename( + archive=archive, + counter=counter, + ) if filename in self.zipf.namelist(): counter += 1 else: @@ -42,6 +46,7 @@ class BulkArchiveStrategy: def _formatted_filepath( self, doc: Document, + *, archive: bool = False, folder: str = "", ) -> Path: diff --git a/src/documents/bulk_edit.py b/src/documents/bulk_edit.py index 0aadcc295..f6adfc8a9 100644 --- a/src/documents/bulk_edit.py +++ b/src/documents/bulk_edit.py @@ -245,6 +245,7 @@ def reprocess(doc_ids: list[int]) -> Literal["OK"]: def set_permissions( doc_ids: list[int], set_permissions, + *, owner=None, merge=False, ) -> Literal["OK"]: @@ -309,6 +310,7 @@ def rotate(doc_ids: list[int], degrees: int) -> Literal["OK"]: def merge( doc_ids: list[int], + *, metadata_document_id: int | None = None, delete_originals: bool = False, user: User | None = None, @@ -387,6 +389,7 @@ def merge( def split( doc_ids: list[int], pages: list[list[int]], + *, delete_originals: bool = False, user: User | None = None, ) -> Literal["OK"]: diff --git a/src/documents/file_handling.py b/src/documents/file_handling.py index 4198ecabb..3d1a643df 100644 --- a/src/documents/file_handling.py +++ b/src/documents/file_handling.py @@ -43,7 +43,7 @@ def delete_empty_directories(directory, root): directory = os.path.normpath(os.path.dirname(directory)) -def generate_unique_filename(doc, archive_filename=False): +def generate_unique_filename(doc, *, archive_filename=False): """ Generates a unique filename for doc in settings.ORIGINALS_DIR. @@ -77,7 +77,7 @@ def generate_unique_filename(doc, archive_filename=False): while True: new_filename = generate_filename( doc, - counter, + counter=counter, archive_filename=archive_filename, ) if new_filename == old_filename: @@ -92,6 +92,7 @@ def generate_unique_filename(doc, archive_filename=False): def generate_filename( doc: Document, + *, counter=0, append_gpg=True, archive_filename=False, diff --git a/src/documents/filters.py b/src/documents/filters.py index 142f3f519..fab029312 100644 --- a/src/documents/filters.py +++ b/src/documents/filters.py @@ -97,7 +97,7 @@ class StoragePathFilterSet(FilterSet): class ObjectFilter(Filter): - def __init__(self, exclude=False, in_list=False, field_name=""): + def __init__(self, *, exclude=False, in_list=False, field_name=""): super().__init__() self.exclude = exclude self.in_list = in_list diff --git a/src/documents/index.py b/src/documents/index.py index 4c5afb505..4b11325ff 100644 --- a/src/documents/index.py +++ b/src/documents/index.py @@ -85,7 +85,7 @@ def get_schema() -> Schema: ) -def open_index(recreate=False) -> FileIndex: +def open_index(*, recreate=False) -> FileIndex: try: if exists_in(settings.INDEX_DIR) and not recreate: return open_dir(settings.INDEX_DIR, schema=get_schema()) @@ -101,7 +101,7 @@ def open_index(recreate=False) -> FileIndex: @contextmanager -def open_index_writer(optimize=False) -> AsyncWriter: +def open_index_writer(*, optimize=False) -> AsyncWriter: writer = AsyncWriter(open_index()) try: @@ -425,7 +425,7 @@ def autocomplete( def get_permissions_criterias(user: User | None = None) -> list: - user_criterias = [query.Term("has_owner", False)] + user_criterias = [query.Term("has_owner", text=False)] if user is not None: if user.is_superuser: # superusers see all docs user_criterias = [] diff --git a/src/documents/management/commands/convert_mariadb_uuid.py b/src/documents/management/commands/convert_mariadb_uuid.py index 4000e67cb..76ccf9e76 100644 --- a/src/documents/management/commands/convert_mariadb_uuid.py +++ b/src/documents/management/commands/convert_mariadb_uuid.py @@ -9,7 +9,7 @@ class Command(BaseCommand): # This code is taken almost entirely from https://github.com/wagtail/wagtail/pull/11912 with all credit to the original author. help = "Converts UUID columns from char type to the native UUID type used in MariaDB 10.7+ and Django 5.0+." - def convert_field(self, model, field_name, null=False): + def convert_field(self, model, field_name, *, null=False): if model._meta.get_field(field_name).model != model: # pragma: no cover # Field is inherited from a parent model return diff --git a/src/documents/management/commands/document_consumer.py b/src/documents/management/commands/document_consumer.py index 6b2706733..36dcc7706 100644 --- a/src/documents/management/commands/document_consumer.py +++ b/src/documents/management/commands/document_consumer.py @@ -248,15 +248,15 @@ class Command(BaseCommand): return if settings.CONSUMER_POLLING == 0 and INotify: - self.handle_inotify(directory, recursive, options["testing"]) + self.handle_inotify(directory, recursive, is_testing=options["testing"]) else: if INotify is None and settings.CONSUMER_POLLING == 0: # pragma: no cover logger.warning("Using polling as INotify import failed") - self.handle_polling(directory, recursive, options["testing"]) + self.handle_polling(directory, recursive, is_testing=options["testing"]) logger.debug("Consumer exiting.") - def handle_polling(self, directory, recursive, is_testing: bool): + def handle_polling(self, directory, recursive, *, is_testing: bool): logger.info(f"Polling directory for changes: {directory}") timeout = None @@ -283,7 +283,7 @@ class Command(BaseCommand): observer.stop() observer.join() - def handle_inotify(self, directory, recursive, is_testing: bool): + def handle_inotify(self, directory, recursive, *, is_testing: bool): logger.info(f"Using inotify to watch directory for changes: {directory}") timeout_ms = None diff --git a/src/documents/migrations/1012_fix_archive_files.py b/src/documents/migrations/1012_fix_archive_files.py index 1d12c439b..46951471e 100644 --- a/src/documents/migrations/1012_fix_archive_files.py +++ b/src/documents/migrations/1012_fix_archive_files.py @@ -84,7 +84,7 @@ def source_path(doc): return os.path.join(settings.ORIGINALS_DIR, fname) -def generate_unique_filename(doc, archive_filename=False): +def generate_unique_filename(doc, *, archive_filename=False): if archive_filename: old_filename = doc.archive_filename root = settings.ARCHIVE_DIR @@ -97,7 +97,7 @@ def generate_unique_filename(doc, archive_filename=False): while True: new_filename = generate_filename( doc, - counter, + counter=counter, archive_filename=archive_filename, ) if new_filename == old_filename: @@ -110,7 +110,7 @@ def generate_unique_filename(doc, archive_filename=False): return new_filename -def generate_filename(doc, counter=0, append_gpg=True, archive_filename=False): +def generate_filename(doc, *, counter=0, append_gpg=True, archive_filename=False): path = "" try: diff --git a/src/documents/models.py b/src/documents/models.py index e7d866e24..25e3c62fd 100644 --- a/src/documents/models.py +++ b/src/documents/models.py @@ -337,7 +337,7 @@ class Document(SoftDeleteModel, ModelWithOwner): def archive_file(self): return open(self.archive_path, "rb") - def get_public_filename(self, archive=False, counter=0, suffix=None) -> str: + def get_public_filename(self, *, archive=False, counter=0, suffix=None) -> str: """ Returns a sanitized filename for the document, not including any paths. """ diff --git a/src/documents/parsers.py b/src/documents/parsers.py index d840817e4..28d903fdd 100644 --- a/src/documents/parsers.py +++ b/src/documents/parsers.py @@ -133,6 +133,7 @@ def get_parser_class_for_mime_type(mime_type: str) -> type["DocumentParser"] | N def run_convert( input_file, output_file, + *, density=None, scale=None, alpha=None, diff --git a/src/documents/permissions.py b/src/documents/permissions.py index 464916ad4..4380c6994 100644 --- a/src/documents/permissions.py +++ b/src/documents/permissions.py @@ -58,7 +58,7 @@ def get_groups_with_only_permission(obj, codename): return Group.objects.filter(id__in=group_object_perm_group_ids).distinct() -def set_permissions_for_object(permissions: list[str], object, merge: bool = False): +def set_permissions_for_object(permissions: list[str], object, *, merge: bool = False): """ Set permissions for an object. The permissions are given as a list of strings in the format "action_modelname", e.g. "view_document". diff --git a/src/documents/sanity_checker.py b/src/documents/sanity_checker.py index 9d44ff345..28d2024e7 100644 --- a/src/documents/sanity_checker.py +++ b/src/documents/sanity_checker.py @@ -57,7 +57,7 @@ class SanityCheckFailedException(Exception): pass -def check_sanity(progress=False) -> SanityCheckMessages: +def check_sanity(*, progress=False) -> SanityCheckMessages: messages = SanityCheckMessages() present_files = { diff --git a/src/documents/signals/handlers.py b/src/documents/signals/handlers.py index 4885910fd..1c4d36694 100644 --- a/src/documents/signals/handlers.py +++ b/src/documents/signals/handlers.py @@ -85,6 +85,7 @@ def _suggestion_printer( def set_correspondent( sender, document: Document, + *, logging_group=None, classifier: DocumentClassifier | None = None, replace=False, @@ -140,6 +141,7 @@ def set_correspondent( def set_document_type( sender, document: Document, + *, logging_group=None, classifier: DocumentClassifier | None = None, replace=False, @@ -196,6 +198,7 @@ def set_document_type( def set_tags( sender, document: Document, + *, logging_group=None, classifier: DocumentClassifier | None = None, replace=False, @@ -251,6 +254,7 @@ def set_tags( def set_storage_path( sender, document: Document, + *, logging_group=None, classifier: DocumentClassifier | None = None, replace=False, diff --git a/src/documents/tasks.py b/src/documents/tasks.py index 8b0cbf249..d8539d1ab 100644 --- a/src/documents/tasks.py +++ b/src/documents/tasks.py @@ -63,7 +63,7 @@ def index_optimize(): writer.commit(optimize=True) -def index_reindex(progress_bar_disable=False): +def index_reindex(*, progress_bar_disable=False): documents = Document.objects.all() ix = index.open_index(recreate=True) diff --git a/src/documents/tests/test_api_filter_by_custom_fields.py b/src/documents/tests/test_api_filter_by_custom_fields.py index deb97bf29..70d43dfde 100644 --- a/src/documents/tests/test_api_filter_by_custom_fields.py +++ b/src/documents/tests/test_api_filter_by_custom_fields.py @@ -165,6 +165,7 @@ class TestCustomFieldsSearch(DirectoriesMixin, APITestCase): self, query: list, reference_predicate: Callable[[DocumentWrapper], bool], + *, match_nothing_ok=False, ): """ diff --git a/src/documents/tests/test_bulk_edit.py b/src/documents/tests/test_bulk_edit.py index 7fde5f8ee..4a7145d34 100644 --- a/src/documents/tests/test_bulk_edit.py +++ b/src/documents/tests/test_bulk_edit.py @@ -535,7 +535,12 @@ class TestPDFActions(DirectoriesMixin, TestCase): metadata_document_id = self.doc1.id user = User.objects.create(username="test_user") - result = bulk_edit.merge(doc_ids, None, False, user) + result = bulk_edit.merge( + doc_ids, + metadata_document_id=None, + delete_originals=False, + user=user, + ) expected_filename = ( f"{'_'.join([str(doc_id) for doc_id in doc_ids])[:100]}_merged.pdf" @@ -638,7 +643,7 @@ class TestPDFActions(DirectoriesMixin, TestCase): doc_ids = [self.doc2.id] pages = [[1, 2], [3]] user = User.objects.create(username="test_user") - result = bulk_edit.split(doc_ids, pages, False, user) + result = bulk_edit.split(doc_ids, pages, delete_originals=False, user=user) self.assertEqual(mock_consume_file.call_count, 2) consume_file_args, _ = mock_consume_file.call_args self.assertEqual(consume_file_args[1].title, "B (split 2)") diff --git a/src/documents/tests/test_consumer.py b/src/documents/tests/test_consumer.py index aa452e15b..a862d7fa0 100644 --- a/src/documents/tests/test_consumer.py +++ b/src/documents/tests/test_consumer.py @@ -233,7 +233,7 @@ class FaultyGenericExceptionParser(_BaseTestParser): raise Exception("Generic exception.") -def fake_magic_from_file(file, mime=False): +def fake_magic_from_file(file, *, mime=False): if mime: if file.name.startswith("invalid_pdf"): return "application/octet-stream" diff --git a/src/documents/tests/test_delayedquery.py b/src/documents/tests/test_delayedquery.py index 1895bd6c6..3ee4fb15d 100644 --- a/src/documents/tests/test_delayedquery.py +++ b/src/documents/tests/test_delayedquery.py @@ -10,7 +10,7 @@ class TestDelayedQuery(TestCase): super().setUp() # all tests run without permission criteria, so has_no_owner query will always # be appended. - self.has_no_owner = query.Or([query.Term("has_owner", False)]) + self.has_no_owner = query.Or([query.Term("has_owner", text=False)]) def _get_testset__id__in(self, param, field): return ( @@ -43,12 +43,12 @@ class TestDelayedQuery(TestCase): def test_get_permission_criteria(self): # tests contains tuples of user instances and the expected filter tests = ( - (None, [query.Term("has_owner", False)]), + (None, [query.Term("has_owner", text=False)]), (User(42, username="foo", is_superuser=True), []), ( User(42, username="foo", is_superuser=False), [ - query.Term("has_owner", False), + query.Term("has_owner", text=False), query.Term("owner_id", 42), query.Term("viewer_id", "42"), ], diff --git a/src/documents/tests/test_management_consumer.py b/src/documents/tests/test_management_consumer.py index 7e2707403..808216d3d 100644 --- a/src/documents/tests/test_management_consumer.py +++ b/src/documents/tests/test_management_consumer.py @@ -93,7 +93,7 @@ class ConsumerThreadMixin(DocumentConsumeDelayMixin): else: print("Consumed a perfectly valid file.") # noqa: T201 - def slow_write_file(self, target, incomplete=False): + def slow_write_file(self, target, *, incomplete=False): with open(self.sample_file, "rb") as f: pdf_bytes = f.read() diff --git a/src/documents/tests/test_management_exporter.py b/src/documents/tests/test_management_exporter.py index 0a79b6cd7..eec2fcd4b 100644 --- a/src/documents/tests/test_management_exporter.py +++ b/src/documents/tests/test_management_exporter.py @@ -188,7 +188,7 @@ class TestExportImport( return manifest - def test_exporter(self, use_filename_format=False): + def test_exporter(self, *, use_filename_format=False): shutil.rmtree(os.path.join(self.dirs.media_dir, "documents")) shutil.copytree( os.path.join(os.path.dirname(__file__), "samples", "documents"), diff --git a/src/documents/tests/test_matchables.py b/src/documents/tests/test_matchables.py index 9ca23e53d..180cf77ed 100644 --- a/src/documents/tests/test_matchables.py +++ b/src/documents/tests/test_matchables.py @@ -23,6 +23,7 @@ class _TestMatchingBase(TestCase): match_algorithm: str, should_match: Iterable[str], no_match: Iterable[str], + *, case_sensitive: bool = False, ): for klass in (Tag, Correspondent, DocumentType): diff --git a/src/documents/views.py b/src/documents/views.py index 24578179a..f23c1b953 100644 --- a/src/documents/views.py +++ b/src/documents/views.py @@ -1608,7 +1608,7 @@ class BulkDownloadView(GenericAPIView): strategy_class = ArchiveOnlyStrategy with zipfile.ZipFile(temp.name, "w", compression) as zipf: - strategy = strategy_class(zipf, follow_filename_format) + strategy = strategy_class(zipf, follow_formatting=follow_filename_format) for document in documents: strategy.add_document(document) @@ -1872,7 +1872,7 @@ class SharedLinkView(View): ) -def serve_file(doc: Document, use_archive: bool, disposition: str): +def serve_file(*, doc: Document, use_archive: bool, disposition: str): if use_archive: file_handle = doc.archive_file filename = doc.get_public_filename(archive=True) diff --git a/src/paperless/views.py b/src/paperless/views.py index bcabd182f..6d297c49b 100644 --- a/src/paperless/views.py +++ b/src/paperless/views.py @@ -148,7 +148,7 @@ class UserViewSet(ModelViewSet): ).first() if authenticator is not None: delete_and_cleanup(request, authenticator) - return Response(True) + return Response(data=True) else: return HttpResponseNotFound("TOTP not found") @@ -262,7 +262,7 @@ class TOTPView(GenericAPIView): ).first() if authenticator is not None: delete_and_cleanup(request, authenticator) - return Response(True) + return Response(data=True) else: return HttpResponseNotFound("TOTP not found") diff --git a/src/paperless_mail/mail.py b/src/paperless_mail/mail.py index e25c4f227..cf35ea6cb 100644 --- a/src/paperless_mail/mail.py +++ b/src/paperless_mail/mail.py @@ -121,7 +121,7 @@ class MarkReadMailAction(BaseMailAction): return {"seen": False} def post_consume(self, M: MailBox, message_uid: str, parameter: str): - M.flag(message_uid, [MailMessageFlags.SEEN], True) + M.flag(message_uid, [MailMessageFlags.SEEN], value=True) class MoveMailAction(BaseMailAction): @@ -142,7 +142,7 @@ class FlagMailAction(BaseMailAction): return {"flagged": False} def post_consume(self, M: MailBox, message_uid: str, parameter: str): - M.flag(message_uid, [MailMessageFlags.FLAGGED], True) + M.flag(message_uid, [MailMessageFlags.FLAGGED], value=True) class TagMailAction(BaseMailAction): @@ -150,7 +150,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): # The custom tag should look like "apple:" if "apple:" in parameter.lower(): _, self.color = parameter.split(":") @@ -188,19 +188,19 @@ class TagMailAction(BaseMailAction): M.flag( message_uid, set(itertools.chain(*APPLE_MAIL_TAG_COLORS.values())), - False, + value=False, ) # Set new $MailFlagBits - M.flag(message_uid, APPLE_MAIL_TAG_COLORS.get(self.color), True) + M.flag(message_uid, APPLE_MAIL_TAG_COLORS.get(self.color), value=True) # Set the general \Flagged # This defaults to the "red" flag in AppleMail and # "stars" in Thunderbird or GMail - M.flag(message_uid, [MailMessageFlags.FLAGGED], True) + M.flag(message_uid, [MailMessageFlags.FLAGGED], value=True) elif self.keyword: - M.flag(message_uid, [self.keyword], True) + M.flag(message_uid, [self.keyword], value=True) else: raise MailError("No keyword specified.") @@ -268,7 +268,7 @@ def apply_mail_action( mailbox_login(M, account) M.folder.set(rule.folder) - action = get_rule_action(rule, supports_gmail_labels) + action = get_rule_action(rule, supports_gmail_labels=supports_gmail_labels) try: action.post_consume(M, message_uid, rule.action_parameter) except errors.ImapToolsError: @@ -356,7 +356,7 @@ def queue_consumption_tasks( ).delay() -def get_rule_action(rule: MailRule, supports_gmail_labels: bool) -> BaseMailAction: +def get_rule_action(rule: MailRule, *, supports_gmail_labels: bool) -> BaseMailAction: """ Returns a BaseMailAction instance for the given rule. """ @@ -370,12 +370,15 @@ def get_rule_action(rule: MailRule, supports_gmail_labels: bool) -> BaseMailActi elif rule.action == MailRule.MailAction.MARK_READ: return MarkReadMailAction() elif rule.action == MailRule.MailAction.TAG: - return TagMailAction(rule.action_parameter, supports_gmail_labels) + return TagMailAction( + rule.action_parameter, + supports_gmail_labels=supports_gmail_labels, + ) else: raise NotImplementedError("Unknown action.") # pragma: no cover -def make_criterias(rule: MailRule, supports_gmail_labels: bool): +def make_criterias(rule: MailRule, *, supports_gmail_labels: bool): """ Returns criteria to be applied to MailBox.fetch for the given rule. """ @@ -393,7 +396,10 @@ def make_criterias(rule: MailRule, supports_gmail_labels: bool): if rule.filter_body: criterias["body"] = rule.filter_body - rule_query = get_rule_action(rule, supports_gmail_labels).get_criteria() + rule_query = get_rule_action( + rule, + supports_gmail_labels=supports_gmail_labels, + ).get_criteria() if isinstance(rule_query, dict): if len(rule_query) or len(criterias): return AND(**rule_query, **criterias) @@ -563,7 +569,7 @@ class MailAccountHandler(LoggingMixin): total_processed_files += self._handle_mail_rule( M, rule, - supports_gmail_labels, + supports_gmail_labels=supports_gmail_labels, ) except Exception as e: self.log.exception( @@ -588,6 +594,7 @@ class MailAccountHandler(LoggingMixin): self, M: MailBox, rule: MailRule, + *, supports_gmail_labels: bool, ): folders = [rule.folder] @@ -616,7 +623,7 @@ class MailAccountHandler(LoggingMixin): f"does not exist in account {rule.account}", ) from err - criterias = make_criterias(rule, supports_gmail_labels) + criterias = make_criterias(rule, supports_gmail_labels=supports_gmail_labels) self.log.debug( f"Rule {rule}: Searching folder with criteria {criterias}", diff --git a/src/paperless_mail/tests/test_mail.py b/src/paperless_mail/tests/test_mail.py index 2311c3009..a73f9cf34 100644 --- a/src/paperless_mail/tests/test_mail.py +++ b/src/paperless_mail/tests/test_mail.py @@ -124,7 +124,7 @@ class BogusMailBox(AbstractContextManager): if username != self.USERNAME or access_token != self.ACCESS_TOKEN: raise MailboxLoginError("BAD", "OK") - def fetch(self, criteria, mark_seen, charset="", bulk=True): + def fetch(self, criteria, mark_seen, charset="", *, bulk=True): msg = self.messages criteria = str(criteria).strip("()").split(" ") @@ -190,7 +190,7 @@ class BogusMailBox(AbstractContextManager): raise Exception -def fake_magic_from_buffer(buffer, mime=False): +def fake_magic_from_buffer(buffer, *, mime=False): if mime: if "PDF" in str(buffer): return "application/pdf" @@ -206,6 +206,7 @@ class MessageBuilder: def create_message( self, + *, attachments: int | list[_AttachmentDef] = 1, body: str = "", subject: str = "the subject", @@ -783,12 +784,18 @@ class TestMail( ) self.assertEqual(len(self.mailMocker.bogus_mailbox.messages), 3) - self.assertEqual(len(self.mailMocker.bogus_mailbox.fetch("UNSEEN", False)), 2) + self.assertEqual( + len(self.mailMocker.bogus_mailbox.fetch("UNSEEN", mark_seen=False)), + 2, + ) self.mail_account_handler.handle_mail_account(account) self.mailMocker.apply_mail_actions() - self.assertEqual(len(self.mailMocker.bogus_mailbox.fetch("UNSEEN", False)), 0) + self.assertEqual( + len(self.mailMocker.bogus_mailbox.fetch("UNSEEN", mark_seen=False)), + 0, + ) self.assertEqual(len(self.mailMocker.bogus_mailbox.messages), 3) def test_handle_mail_account_delete(self): @@ -853,7 +860,7 @@ class TestMail( self.assertEqual(len(self.mailMocker.bogus_mailbox.messages), 3) self.assertEqual( - len(self.mailMocker.bogus_mailbox.fetch("UNFLAGGED", False)), + len(self.mailMocker.bogus_mailbox.fetch("UNFLAGGED", mark_seen=False)), 2, ) @@ -861,7 +868,7 @@ class TestMail( self.mailMocker.apply_mail_actions() self.assertEqual( - len(self.mailMocker.bogus_mailbox.fetch("UNFLAGGED", False)), + len(self.mailMocker.bogus_mailbox.fetch("UNFLAGGED", mark_seen=False)), 1, ) self.assertEqual(len(self.mailMocker.bogus_mailbox.messages), 3) @@ -934,7 +941,12 @@ class TestMail( self.assertEqual(len(self.mailMocker.bogus_mailbox.messages), 3) self.assertEqual( - len(self.mailMocker.bogus_mailbox.fetch("UNKEYWORD processed", False)), + len( + self.mailMocker.bogus_mailbox.fetch( + "UNKEYWORD processed", + mark_seen=False, + ), + ), 2, ) @@ -943,7 +955,12 @@ class TestMail( self.assertEqual(len(self.mailMocker.bogus_mailbox.messages), 3) self.assertEqual( - len(self.mailMocker.bogus_mailbox.fetch("UNKEYWORD processed", False)), + len( + self.mailMocker.bogus_mailbox.fetch( + "UNKEYWORD processed", + mark_seen=False, + ), + ), 0, ) @@ -967,12 +984,18 @@ class TestMail( self.assertEqual(len(self.mailMocker.bogus_mailbox.messages), 3) criteria = NOT(gmail_label="processed") - self.assertEqual(len(self.mailMocker.bogus_mailbox.fetch(criteria, False)), 2) + self.assertEqual( + len(self.mailMocker.bogus_mailbox.fetch(criteria, mark_seen=False)), + 2, + ) self.mail_account_handler.handle_mail_account(account) self.mailMocker.apply_mail_actions() - self.assertEqual(len(self.mailMocker.bogus_mailbox.fetch(criteria, False)), 0) + self.assertEqual( + len(self.mailMocker.bogus_mailbox.fetch(criteria, mark_seen=False)), + 0, + ) self.assertEqual(len(self.mailMocker.bogus_mailbox.messages), 3) def test_tag_mail_action_applemail_wrong_input(self): @@ -980,7 +1003,7 @@ class TestMail( MailError, TagMailAction, "apple:black", - False, + supports_gmail_labels=False, ) def test_handle_mail_account_tag_applemail(self): @@ -1002,7 +1025,7 @@ class TestMail( self.assertEqual(len(self.mailMocker.bogus_mailbox.messages), 3) self.assertEqual( - len(self.mailMocker.bogus_mailbox.fetch("UNFLAGGED", False)), + len(self.mailMocker.bogus_mailbox.fetch("UNFLAGGED", mark_seen=False)), 2, ) @@ -1010,7 +1033,7 @@ class TestMail( self.mailMocker.apply_mail_actions() self.assertEqual( - len(self.mailMocker.bogus_mailbox.fetch("UNFLAGGED", False)), + len(self.mailMocker.bogus_mailbox.fetch("UNFLAGGED", mark_seen=False)), 0, ) self.assertEqual(len(self.mailMocker.bogus_mailbox.messages), 3) @@ -1324,13 +1347,19 @@ class TestMail( self.assertEqual(len(self.mailMocker.bogus_mailbox.messages), 3) self.mailMocker._queue_consumption_tasks_mock.assert_not_called() - self.assertEqual(len(self.mailMocker.bogus_mailbox.fetch("UNSEEN", False)), 2) + self.assertEqual( + len(self.mailMocker.bogus_mailbox.fetch("UNSEEN", mark_seen=False)), + 2, + ) self.mail_account_handler.handle_mail_account(account) self.mailMocker.apply_mail_actions() self.assertEqual(self.mailMocker._queue_consumption_tasks_mock.call_count, 2) - self.assertEqual(len(self.mailMocker.bogus_mailbox.fetch("UNSEEN", False)), 0) + self.assertEqual( + len(self.mailMocker.bogus_mailbox.fetch("UNSEEN", mark_seen=False)), + 0, + ) self.assertEqual(len(self.mailMocker.bogus_mailbox.messages), 3) def test_auth_plain_fallback_fails_still(self): @@ -1390,13 +1419,19 @@ class TestMail( self.assertEqual(len(self.mailMocker.bogus_mailbox.messages), 3) self.assertEqual(self.mailMocker._queue_consumption_tasks_mock.call_count, 0) - self.assertEqual(len(self.mailMocker.bogus_mailbox.fetch("UNSEEN", False)), 2) + self.assertEqual( + len(self.mailMocker.bogus_mailbox.fetch("UNSEEN", mark_seen=False)), + 2, + ) self.mail_account_handler.handle_mail_account(account) self.mailMocker.apply_mail_actions() self.assertEqual(self.mailMocker._queue_consumption_tasks_mock.call_count, 2) - self.assertEqual(len(self.mailMocker.bogus_mailbox.fetch("UNSEEN", False)), 0) + self.assertEqual( + len(self.mailMocker.bogus_mailbox.fetch("UNSEEN", mark_seen=False)), + 0, + ) self.assertEqual(len(self.mailMocker.bogus_mailbox.messages), 3) def test_disabled_rule(self): @@ -1425,12 +1460,15 @@ class TestMail( self.mailMocker.apply_mail_actions() self.assertEqual(len(self.mailMocker.bogus_mailbox.messages), 3) - self.assertEqual(len(self.mailMocker.bogus_mailbox.fetch("UNSEEN", False)), 2) + self.assertEqual( + len(self.mailMocker.bogus_mailbox.fetch("UNSEEN", mark_seen=False)), + 2, + ) self.mail_account_handler.handle_mail_account(account) self.mailMocker.apply_mail_actions() self.assertEqual( - len(self.mailMocker.bogus_mailbox.fetch("UNSEEN", False)), + len(self.mailMocker.bogus_mailbox.fetch("UNSEEN", mark_seen=False)), 2, ) # still 2 diff --git a/src/paperless_tesseract/parsers.py b/src/paperless_tesseract/parsers.py index e7968a61e..a8be899f5 100644 --- a/src/paperless_tesseract/parsers.py +++ b/src/paperless_tesseract/parsers.py @@ -214,6 +214,7 @@ class RasterisedDocumentParser(DocumentParser): mime_type, output_file, sidecar_file, + *, safe_fallback=False, ): if TYPE_CHECKING: