mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-07-28 18:24:38 -05:00
Removes support for Python 3.8 and lower from the code base
This commit is contained in:

committed by
Trenton H

parent
c8bfbb9315
commit
650c816a7b
@@ -2,9 +2,7 @@ import logging
|
||||
import tempfile
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Dict
|
||||
from typing import Final
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
|
||||
from django.conf import settings
|
||||
@@ -53,7 +51,7 @@ class BarcodeReader:
|
||||
self.file: Final[Path] = filepath
|
||||
self.mime: Final[str] = mime_type
|
||||
self.pdf_file: Path = self.file
|
||||
self.barcodes: List[Barcode] = []
|
||||
self.barcodes: list[Barcode] = []
|
||||
self.temp_dir: Optional[tempfile.TemporaryDirectory] = None
|
||||
|
||||
if settings.CONSUMER_BARCODE_TIFF_SUPPORT:
|
||||
@@ -111,7 +109,7 @@ class BarcodeReader:
|
||||
return asn
|
||||
|
||||
@staticmethod
|
||||
def read_barcodes_zxing(image: Image) -> List[str]:
|
||||
def read_barcodes_zxing(image: Image) -> list[str]:
|
||||
barcodes = []
|
||||
|
||||
import zxingcpp
|
||||
@@ -127,7 +125,7 @@ class BarcodeReader:
|
||||
return barcodes
|
||||
|
||||
@staticmethod
|
||||
def read_barcodes_pyzbar(image: Image) -> List[str]:
|
||||
def read_barcodes_pyzbar(image: Image) -> list[str]:
|
||||
barcodes = []
|
||||
|
||||
from pyzbar import pyzbar
|
||||
@@ -209,7 +207,7 @@ class BarcodeReader:
|
||||
f"Exception during barcode scanning: {e}",
|
||||
)
|
||||
|
||||
def get_separation_pages(self) -> Dict[int, bool]:
|
||||
def get_separation_pages(self) -> dict[int, bool]:
|
||||
"""
|
||||
Search the parsed barcodes for separators and returns a dict of page
|
||||
numbers, which separate the file into new files, together with the
|
||||
@@ -228,7 +226,7 @@ class BarcodeReader:
|
||||
**{bc.page: True for bc in self.barcodes if bc.is_asn and bc.page != 0},
|
||||
}
|
||||
|
||||
def separate_pages(self, pages_to_split_on: Dict[int, bool]) -> List[Path]:
|
||||
def separate_pages(self, pages_to_split_on: dict[int, bool]) -> list[Path]:
|
||||
"""
|
||||
Separate the provided pdf file on the pages_to_split_on.
|
||||
The pages which are defined by the keys in page_numbers
|
||||
@@ -241,9 +239,9 @@ class BarcodeReader:
|
||||
fname = self.file.with_suffix("").name
|
||||
with Pdf.open(self.pdf_file) as input_pdf:
|
||||
# Start with an empty document
|
||||
current_document: List[Page] = []
|
||||
current_document: list[Page] = []
|
||||
# A list of documents, ie a list of lists of pages
|
||||
documents: List[List[Page]] = [current_document]
|
||||
documents: list[list[Page]] = [current_document]
|
||||
|
||||
for idx, page in enumerate(input_pdf.pages):
|
||||
# Keep building the new PDF as long as it is not a
|
||||
|
@@ -3,11 +3,10 @@ import os
|
||||
import pickle
|
||||
import re
|
||||
import warnings
|
||||
from collections.abc import Iterator
|
||||
from datetime import datetime
|
||||
from hashlib import sha256
|
||||
from pathlib import Path
|
||||
from typing import Iterator
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
|
||||
from django.conf import settings
|
||||
@@ -357,7 +356,7 @@ class DocumentClassifier:
|
||||
|
||||
# Tokenize
|
||||
# This splits the content into tokens, roughly words
|
||||
words: List[str] = word_tokenize(
|
||||
words: list[str] = word_tokenize(
|
||||
content,
|
||||
language=settings.NLTK_LANGUAGE,
|
||||
)
|
||||
@@ -404,7 +403,7 @@ class DocumentClassifier:
|
||||
else:
|
||||
return None
|
||||
|
||||
def predict_tags(self, content: str) -> List[int]:
|
||||
def predict_tags(self, content: str) -> list[int]:
|
||||
from sklearn.utils.multiclass import type_of_target
|
||||
|
||||
if self.tags_classifier:
|
||||
|
@@ -8,7 +8,6 @@ from pathlib import Path
|
||||
from subprocess import CompletedProcess
|
||||
from subprocess import run
|
||||
from typing import Optional
|
||||
from typing import Type
|
||||
|
||||
import magic
|
||||
from asgiref.sync import async_to_sync
|
||||
@@ -372,7 +371,7 @@ class Consumer(LoggingMixin):
|
||||
self.log.debug(f"Detected mime type: {mime_type}")
|
||||
|
||||
# Based on the mime type, get the parser for that type
|
||||
parser_class: Optional[Type[DocumentParser]] = get_parser_class_for_mime_type(
|
||||
parser_class: Optional[type[DocumentParser]] = get_parser_class_for_mime_type(
|
||||
mime_type,
|
||||
)
|
||||
if not parser_class:
|
||||
|
@@ -2,7 +2,6 @@ import dataclasses
|
||||
import datetime
|
||||
import enum
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
|
||||
import magic
|
||||
@@ -20,7 +19,7 @@ class DocumentMetadataOverrides:
|
||||
title: Optional[str] = None
|
||||
correspondent_id: Optional[int] = None
|
||||
document_type_id: Optional[int] = None
|
||||
tag_ids: Optional[List[int]] = None
|
||||
tag_ids: Optional[list[int]] = None
|
||||
created: Optional[datetime.datetime] = None
|
||||
asn: Optional[int] = None
|
||||
owner_id: Optional[int] = None
|
||||
|
@@ -115,12 +115,10 @@ def collate(input_doc: ConsumableDocument) -> str:
|
||||
staging.unlink()
|
||||
|
||||
else:
|
||||
# In Python 3.9 move supports Path objects directly,
|
||||
# but for now we have to be compatible with 3.8
|
||||
shutil.move(str(pdf_file), str(staging))
|
||||
shutil.move(pdf_file, staging)
|
||||
# update access to modification time so we know if the file
|
||||
# is outdated when another file gets uploaded
|
||||
os.utime(str(staging), (dt.datetime.now().timestamp(),) * 2)
|
||||
os.utime(staging, (dt.datetime.now().timestamp(),) * 2)
|
||||
logger.info(
|
||||
"Got scan with odd numbered pages of double-sided scan, moved it to %s",
|
||||
staging,
|
||||
|
@@ -8,7 +8,6 @@ from threading import Event
|
||||
from time import monotonic
|
||||
from time import sleep
|
||||
from typing import Final
|
||||
from typing import Set
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.management.base import BaseCommand
|
||||
@@ -32,7 +31,7 @@ except ImportError: # pragma: nocover
|
||||
logger = logging.getLogger("paperless.management.consumer")
|
||||
|
||||
|
||||
def _tags_from_path(filepath) -> Set[Tag]:
|
||||
def _tags_from_path(filepath) -> set[Tag]:
|
||||
"""
|
||||
Walk up the directory tree from filepath to CONSUMPTION_DIR
|
||||
and get or create Tag IDs for every directory.
|
||||
|
@@ -5,8 +5,6 @@ import shutil
|
||||
import tempfile
|
||||
import time
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
from typing import Set
|
||||
|
||||
import tqdm
|
||||
from django.conf import settings
|
||||
@@ -138,8 +136,8 @@ class Command(BaseCommand):
|
||||
BaseCommand.__init__(self, *args, **kwargs)
|
||||
self.target: Path = None
|
||||
self.split_manifest = False
|
||||
self.files_in_export_dir: Set[Path] = set()
|
||||
self.exported_files: List[Path] = []
|
||||
self.files_in_export_dir: set[Path] = set()
|
||||
self.exported_files: list[Path] = []
|
||||
self.compare_checksums = False
|
||||
self.use_filename_format = False
|
||||
self.use_folder_prefix = False
|
||||
|
@@ -6,12 +6,11 @@ import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
from collections.abc import Iterator
|
||||
from functools import lru_cache
|
||||
from pathlib import Path
|
||||
from typing import Iterator
|
||||
from typing import Match
|
||||
from re import Match
|
||||
from typing import Optional
|
||||
from typing import Set
|
||||
|
||||
from django.conf import settings
|
||||
from django.utils import timezone
|
||||
@@ -90,7 +89,7 @@ def is_file_ext_supported(ext: str) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
def get_supported_file_extensions() -> Set[str]:
|
||||
def get_supported_file_extensions() -> set[str]:
|
||||
extensions = set()
|
||||
for response in document_consumer_declaration.send(None):
|
||||
parser_declaration = response[1]
|
||||
|
@@ -1,14 +1,10 @@
|
||||
import datetime
|
||||
import math
|
||||
import re
|
||||
import zoneinfo
|
||||
|
||||
from celery import states
|
||||
|
||||
try:
|
||||
import zoneinfo
|
||||
except ImportError:
|
||||
from backports import zoneinfo
|
||||
import magic
|
||||
from celery import states
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import Group
|
||||
from django.contrib.auth.models import User
|
||||
|
@@ -3,7 +3,6 @@ import logging
|
||||
import shutil
|
||||
import uuid
|
||||
from typing import Optional
|
||||
from typing import Type
|
||||
|
||||
import tqdm
|
||||
from asgiref.sync import async_to_sync
|
||||
@@ -216,7 +215,7 @@ def update_document_archive_file(document_id):
|
||||
|
||||
mime_type = document.mime_type
|
||||
|
||||
parser_class: Type[DocumentParser] = get_parser_class_for_mime_type(mime_type)
|
||||
parser_class: type[DocumentParser] = get_parser_class_for_mime_type(mime_type)
|
||||
|
||||
if not parser_class:
|
||||
logger.error(
|
||||
|
@@ -7,18 +7,13 @@ import tempfile
|
||||
import urllib.request
|
||||
import uuid
|
||||
import zipfile
|
||||
import zoneinfo
|
||||
from datetime import timedelta
|
||||
from pathlib import Path
|
||||
from unittest import mock
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import celery
|
||||
|
||||
try:
|
||||
import zoneinfo
|
||||
except ImportError:
|
||||
from backports import zoneinfo
|
||||
|
||||
import pytest
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from django.conf import settings
|
||||
|
@@ -5,16 +5,11 @@ import shutil
|
||||
import stat
|
||||
import tempfile
|
||||
import uuid
|
||||
import zoneinfo
|
||||
from unittest import mock
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from dateutil import tz
|
||||
|
||||
try:
|
||||
import zoneinfo
|
||||
except ImportError:
|
||||
from backports import zoneinfo
|
||||
|
||||
from django.conf import settings
|
||||
from django.test import TestCase
|
||||
from django.test import override_settings
|
||||
|
@@ -1,13 +1,9 @@
|
||||
import shutil
|
||||
import tempfile
|
||||
import zoneinfo
|
||||
from pathlib import Path
|
||||
from unittest import mock
|
||||
|
||||
try:
|
||||
import zoneinfo
|
||||
except ImportError:
|
||||
from backports import zoneinfo
|
||||
|
||||
from django.test import TestCase
|
||||
from django.test import override_settings
|
||||
from django.utils import timezone
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import shutil
|
||||
import tempfile
|
||||
from collections.abc import Iterable
|
||||
from random import randint
|
||||
from typing import Iterable
|
||||
|
||||
from django.contrib.admin.models import LogEntry
|
||||
from django.contrib.auth.models import User
|
||||
|
@@ -1,9 +1,9 @@
|
||||
import importlib
|
||||
import shutil
|
||||
import tempfile
|
||||
from collections.abc import Iterable
|
||||
from pathlib import Path
|
||||
from typing import Callable
|
||||
from typing import Iterable
|
||||
from typing import Union
|
||||
from unittest import mock
|
||||
|
||||
|
@@ -1,9 +1,9 @@
|
||||
import importlib
|
||||
import shutil
|
||||
import tempfile
|
||||
from collections.abc import Iterable
|
||||
from pathlib import Path
|
||||
from typing import Callable
|
||||
from typing import Iterable
|
||||
from typing import Union
|
||||
from unittest import mock
|
||||
|
||||
|
@@ -3,14 +3,12 @@ import tempfile
|
||||
import time
|
||||
import warnings
|
||||
from collections import namedtuple
|
||||
from collections.abc import Iterator
|
||||
from contextlib import contextmanager
|
||||
from os import PathLike
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
from typing import Callable
|
||||
from typing import Iterator
|
||||
from typing import List
|
||||
from typing import Tuple
|
||||
from typing import Union
|
||||
from unittest import mock
|
||||
|
||||
@@ -88,10 +86,10 @@ def paperless_environment():
|
||||
|
||||
def util_call_with_backoff(
|
||||
method_or_callable: Callable,
|
||||
args: Union[List, Tuple],
|
||||
args: Union[list, tuple],
|
||||
*,
|
||||
skip_on_50x_err=True,
|
||||
) -> Tuple[bool, Any]:
|
||||
) -> tuple[bool, Any]:
|
||||
"""
|
||||
For whatever reason, the images started during the test pipeline like to
|
||||
segfault sometimes, crash and otherwise fail randomly, when run with the
|
||||
@@ -219,7 +217,7 @@ class DocumentConsumeDelayMixin:
|
||||
|
||||
def get_last_consume_delay_call_args(
|
||||
self,
|
||||
) -> Tuple[ConsumableDocument, DocumentMetadataOverrides]:
|
||||
) -> tuple[ConsumableDocument, DocumentMetadataOverrides]:
|
||||
"""
|
||||
Returns the most recent arguments to the async task
|
||||
"""
|
||||
@@ -233,7 +231,7 @@ class DocumentConsumeDelayMixin:
|
||||
|
||||
def get_all_consume_delay_call_args(
|
||||
self,
|
||||
) -> Iterator[Tuple[ConsumableDocument, DocumentMetadataOverrides]]:
|
||||
) -> Iterator[tuple[ConsumableDocument, DocumentMetadataOverrides]]:
|
||||
"""
|
||||
Iterates over all calls to the async task and returns the arguments
|
||||
"""
|
||||
@@ -246,7 +244,7 @@ class DocumentConsumeDelayMixin:
|
||||
def get_specific_consume_delay_call_args(
|
||||
self,
|
||||
index: int,
|
||||
) -> Iterator[Tuple[ConsumableDocument, DocumentMetadataOverrides]]:
|
||||
) -> Iterator[tuple[ConsumableDocument, DocumentMetadataOverrides]]:
|
||||
"""
|
||||
Returns the arguments of a specific call to the async task
|
||||
"""
|
||||
|
@@ -1,14 +1,13 @@
|
||||
import shutil
|
||||
from os import utime
|
||||
from pathlib import Path
|
||||
from typing import Tuple
|
||||
from typing import Union
|
||||
|
||||
|
||||
def _coerce_to_path(
|
||||
source: Union[Path, str],
|
||||
dest: Union[Path, str],
|
||||
) -> Tuple[Path, Path]:
|
||||
) -> tuple[Path, Path]:
|
||||
return Path(source).resolve(), Path(dest).resolve()
|
||||
|
||||
|
||||
|
@@ -155,10 +155,8 @@ def settings_values_check(app_configs, **kwargs):
|
||||
"""
|
||||
Validates the user provided timezone is a valid timezone
|
||||
"""
|
||||
try:
|
||||
import zoneinfo
|
||||
except ImportError: # pragma: nocover
|
||||
from backports import zoneinfo
|
||||
import zoneinfo
|
||||
|
||||
msgs = []
|
||||
if settings.TIME_ZONE not in zoneinfo.available_timezones():
|
||||
msgs.append(
|
||||
|
@@ -7,12 +7,8 @@ import re
|
||||
import tempfile
|
||||
from os import PathLike
|
||||
from pathlib import Path
|
||||
from typing import Dict
|
||||
from typing import Final
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
from typing import Set
|
||||
from typing import Tuple
|
||||
from typing import Union
|
||||
from urllib.parse import urlparse
|
||||
|
||||
@@ -85,9 +81,9 @@ def __get_path(
|
||||
|
||||
def __get_list(
|
||||
key: str,
|
||||
default: Optional[List[str]] = None,
|
||||
default: Optional[list[str]] = None,
|
||||
sep: str = ",",
|
||||
) -> List[str]:
|
||||
) -> list[str]:
|
||||
"""
|
||||
Return a list of elements from the environment, as separated by the given
|
||||
string, or the default if the key does not exist
|
||||
@@ -100,7 +96,7 @@ def __get_list(
|
||||
return []
|
||||
|
||||
|
||||
def _parse_redis_url(env_redis: Optional[str]) -> Tuple[str]:
|
||||
def _parse_redis_url(env_redis: Optional[str]) -> tuple[str]:
|
||||
"""
|
||||
Gets the Redis information from the environment or a default and handles
|
||||
converting from incompatible django_channels and celery formats.
|
||||
@@ -138,7 +134,7 @@ def _parse_redis_url(env_redis: Optional[str]) -> Tuple[str]:
|
||||
return (env_redis, env_redis)
|
||||
|
||||
|
||||
def _parse_beat_schedule() -> Dict:
|
||||
def _parse_beat_schedule() -> dict:
|
||||
"""
|
||||
Configures the scheduled tasks, according to default or
|
||||
environment variables. Task expiration is configured so the task will
|
||||
@@ -492,7 +488,7 @@ EMAIL_CERTIFICATE_FILE = __get_path("PAPERLESS_EMAIL_CERTIFICATE_FILE")
|
||||
###############################################################################
|
||||
# Database #
|
||||
###############################################################################
|
||||
def _parse_db_settings() -> Dict:
|
||||
def _parse_db_settings() -> dict:
|
||||
databases = {
|
||||
"default": {
|
||||
"ENGINE": "django.db.backends.sqlite3",
|
||||
@@ -928,7 +924,7 @@ if TIKA_ENABLED:
|
||||
def _parse_ignore_dates(
|
||||
env_ignore: str,
|
||||
date_order: str = DATE_ORDER,
|
||||
) -> Set[datetime.datetime]:
|
||||
) -> set[datetime.datetime]:
|
||||
"""
|
||||
If the PAPERLESS_IGNORE_DATES environment variable is set, parse the
|
||||
user provided string(s) into dates
|
||||
@@ -957,7 +953,7 @@ def _parse_ignore_dates(
|
||||
|
||||
|
||||
# List dates that should be ignored when trying to parse date from document text
|
||||
IGNORE_DATES: Set[datetime.date] = set()
|
||||
IGNORE_DATES: set[datetime.date] = set()
|
||||
|
||||
if os.getenv("PAPERLESS_IGNORE_DATES") is not None:
|
||||
IGNORE_DATES = _parse_ignore_dates(os.getenv("PAPERLESS_IGNORE_DATES"))
|
||||
|
@@ -1,7 +1,6 @@
|
||||
from typing import Final
|
||||
from typing import Tuple
|
||||
|
||||
__version__: Final[Tuple[int, int, int]] = (1, 17, 4)
|
||||
__version__: Final[tuple[int, int, int]] = (1, 17, 4)
|
||||
# Version string like X.Y.Z
|
||||
__full_version_str__: Final[str] = ".".join(map(str, __version__))
|
||||
# Version string like X.Y
|
||||
|
@@ -8,8 +8,6 @@ import traceback
|
||||
from datetime import date
|
||||
from datetime import timedelta
|
||||
from fnmatch import fnmatch
|
||||
from typing import Dict
|
||||
from typing import List
|
||||
from typing import Union
|
||||
|
||||
import magic
|
||||
@@ -80,7 +78,7 @@ class BaseMailAction:
|
||||
read mails when the action is to mark mails as read).
|
||||
"""
|
||||
|
||||
def get_criteria(self) -> Union[Dict, LogicOperator]:
|
||||
def get_criteria(self) -> Union[dict, LogicOperator]:
|
||||
"""
|
||||
Returns filtering criteria/query for this mail action.
|
||||
"""
|
||||
@@ -232,7 +230,7 @@ def mailbox_login(mailbox: MailBox, account: MailAccount):
|
||||
|
||||
@shared_task
|
||||
def apply_mail_action(
|
||||
result: List[str],
|
||||
result: list[str],
|
||||
rule_id: int,
|
||||
message_uid: str,
|
||||
message_subject: str,
|
||||
@@ -319,7 +317,7 @@ def error_callback(
|
||||
|
||||
def queue_consumption_tasks(
|
||||
*,
|
||||
consume_tasks: List[Signature],
|
||||
consume_tasks: list[Signature],
|
||||
rule: MailRule,
|
||||
message: MailMessage,
|
||||
):
|
||||
|
@@ -1,7 +1,6 @@
|
||||
import re
|
||||
from html import escape
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
|
||||
import httpx
|
||||
from bleach import clean
|
||||
@@ -355,7 +354,7 @@ class MailDocumentParser(DocumentParser):
|
||||
def generate_pdf_from_html(
|
||||
self,
|
||||
orig_html: str,
|
||||
attachments: List[MailAttachment],
|
||||
attachments: list[MailAttachment],
|
||||
) -> Path:
|
||||
"""
|
||||
Generates a PDF file based on the HTML and attachments of the email
|
||||
|
@@ -3,8 +3,7 @@ import email.contentmanager
|
||||
import random
|
||||
import uuid
|
||||
from collections import namedtuple
|
||||
from typing import ContextManager
|
||||
from typing import List
|
||||
from contextlib import AbstractContextManager
|
||||
from typing import Optional
|
||||
from typing import Union
|
||||
from unittest import mock
|
||||
@@ -53,8 +52,8 @@ class BogusFolderManager:
|
||||
|
||||
class BogusClient:
|
||||
def __init__(self, messages):
|
||||
self.messages: List[MailMessage] = messages
|
||||
self.capabilities: List[str] = []
|
||||
self.messages: list[MailMessage] = messages
|
||||
self.capabilities: list[str] = []
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
@@ -78,7 +77,7 @@ class BogusClient:
|
||||
MailMessage.flags.fget.cache_clear()
|
||||
|
||||
|
||||
class BogusMailBox(ContextManager):
|
||||
class BogusMailBox(AbstractContextManager):
|
||||
# Common values so tests don't need to remember an accepted login
|
||||
USERNAME: str = "admin"
|
||||
ASCII_PASSWORD: str = "secret"
|
||||
@@ -88,8 +87,8 @@ class BogusMailBox(ContextManager):
|
||||
ACCESS_TOKEN = "ea7e075cd3acf2c54c48e600398d5d5a"
|
||||
|
||||
def __init__(self):
|
||||
self.messages: List[MailMessage] = []
|
||||
self.messages_spam: List[MailMessage] = []
|
||||
self.messages: list[MailMessage] = []
|
||||
self.messages_spam: list[MailMessage] = []
|
||||
self.folder = BogusFolderManager()
|
||||
self.client = BogusClient(self.messages)
|
||||
self._host = ""
|
||||
@@ -221,11 +220,11 @@ class TestMail(
|
||||
|
||||
def create_message(
|
||||
self,
|
||||
attachments: Union[int, List[_AttachmentDef]] = 1,
|
||||
attachments: Union[int, list[_AttachmentDef]] = 1,
|
||||
body: str = "",
|
||||
subject: str = "the subject",
|
||||
from_: str = "noone@mail.com",
|
||||
to: Optional[List[str]] = None,
|
||||
to: Optional[list[str]] = None,
|
||||
seen: bool = False,
|
||||
flagged: bool = False,
|
||||
processed: bool = False,
|
||||
@@ -1056,6 +1055,7 @@ class TestMail(
|
||||
],
|
||||
)
|
||||
|
||||
@pytest.mark.flaky(reruns=4)
|
||||
def test_filters(self):
|
||||
account = MailAccount.objects.create(
|
||||
name="test3",
|
||||
@@ -1203,7 +1203,7 @@ class TestMail(
|
||||
self.assertEqual(len(self.bogus_mailbox.fetch("UNSEEN", False)), 0)
|
||||
self.assertEqual(len(self.bogus_mailbox.messages), 3)
|
||||
|
||||
def assert_queue_consumption_tasks_call_args(self, expected_call_args: List):
|
||||
def assert_queue_consumption_tasks_call_args(self, expected_call_args: list):
|
||||
"""
|
||||
Verifies that queue_consumption_tasks has been called with the expected arguments.
|
||||
|
||||
|
@@ -2,8 +2,8 @@ import os
|
||||
import shutil
|
||||
import tempfile
|
||||
import uuid
|
||||
from contextlib import AbstractContextManager
|
||||
from pathlib import Path
|
||||
from typing import ContextManager
|
||||
from unittest import mock
|
||||
|
||||
from django.test import TestCase
|
||||
@@ -29,7 +29,7 @@ def fake_convert(input_file, output_file, **kwargs):
|
||||
f2.write(line.strip())
|
||||
|
||||
|
||||
class FakeImageFile(ContextManager):
|
||||
class FakeImageFile(AbstractContextManager):
|
||||
def __init__(self, fname):
|
||||
self.fname = fname
|
||||
|
||||
|
@@ -1,13 +1,9 @@
|
||||
import datetime
|
||||
import os
|
||||
import zoneinfo
|
||||
from pathlib import Path
|
||||
from unittest import mock
|
||||
|
||||
try:
|
||||
import zoneinfo
|
||||
except ImportError:
|
||||
from backports import zoneinfo
|
||||
|
||||
from django.test import TestCase
|
||||
from django.test import override_settings
|
||||
from httpx import Request
|
||||
|
Reference in New Issue
Block a user