diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 34e72a9b9..6dd355980 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -13,7 +13,7 @@ jobs: uses: actions/setup-python@v2 with: python-version: 3.9 - - name: Install flate8 + - name: Install flake8 run: pip install --upgrade flake8 - name: Run flake8 uses: liskin/gh-problem-matcher-wrap@v1 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000..5b7150d85 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,42 @@ +ci: + autofix_commit_msg: | + ci: auto fixes from pre-commit hooks + + for more information, see https://pre-commit.ci + autofix_prs: false + autoupdate_commit_msg: 'ci: pre-commit autoupdate' + autoupdate_schedule: monthly + +repos: +# - repo: https://github.com/asottile/pyupgrade +# rev: v2.37.3 +# hooks: +# - id: pyupgrade +# args: ["--py36-plus"] +# +# - repo: https://github.com/adamchainz/django-upgrade +# rev: '1.7.0' +# hooks: +# - id: django-upgrade +# args: [--target-version, "2.2"] + + - repo: https://github.com/PyCQA/flake8 + rev: 5.0.2 + hooks: + - id: flake8 + + - repo: https://github.com/asottile/yesqa + rev: v1.3.0 + hooks: + - id: yesqa + + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.3.0 + hooks: + - id: check-merge-conflict + - id: mixed-line-ending + + - repo: https://github.com/pycqa/isort + rev: 5.10.1 + hooks: + - id: isort diff --git a/aldryn_config.py b/aldryn_config.py index 963b8df09..f0cb6bc2f 100644 --- a/aldryn_config.py +++ b/aldryn_config.py @@ -5,6 +5,7 @@ class Form(forms.BaseForm): def to_settings(self, data, settings): from functools import partial + from aldryn_addons.utils import boolean_ish, djsenv from aldryn_django import storage diff --git a/docs/conf.py b/docs/conf.py index 6081a8a37..6cfd2285e 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -14,19 +14,21 @@ import os import sys + sys.path.append(os.path.abspath('../')) -from filer import __version__ +from filer import __version__ # NOQA + # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -#sys.path.append(os.path.abspath('.')) +# sys.path.append(os.path.abspath('.')) # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' +# needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. @@ -48,7 +50,7 @@ source_suffix = '.rst' # The encoding of source files. -#source_encoding = 'utf-8-sig' +# source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' @@ -68,37 +70,37 @@ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. -#language = None +# language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: -#today = '' +# today = '' # Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' +# today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build', '_images', 'README.rst'] # The reST default role (used for this markup: `text`) to use for all documents. -#default_role = None +# default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True +# add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +# add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. -#show_authors = False +# show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] +# modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- @@ -111,27 +113,27 @@ # further. For a list of options available for each theme, see the # documentation. html_theme_options = { - "navigation_with_keys": True, + "navigation_with_keys": True, } # Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] +# html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -#html_title = None +# html_title = None # A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None +# html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. -#html_logo = None +# html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -#html_favicon = None +# html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, @@ -140,44 +142,44 @@ # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' +# html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. -#html_use_smartypants = True +# html_use_smartypants = True # Custom sidebar templates, maps document names to template names. -#html_sidebars = {} +# html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. -#html_additional_pages = {} +# html_additional_pages = {} # If false, no module index is generated. -#html_domain_indices = True +# html_domain_indices = True # If false, no index is generated. -#html_use_index = True +# html_use_index = True # If true, the index is split into individual pages for each letter. -#html_split_index = False +# html_split_index = False # If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True +# html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True +# html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True +# html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. -#html_use_opensearch = '' +# html_use_opensearch = '' # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = '' +# html_file_suffix = '' # Output file base name for HTML help builder. htmlhelp_basename = 'django-filerdoc' @@ -186,40 +188,39 @@ # -- Options for LaTeX output -------------------------------------------------- # The paper size ('letter' or 'a4'). -#latex_paper_size = 'letter' +# latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). -#latex_font_size = '10pt' +# latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ - ('index', 'django-filer.tex', 'django-filer Documentation', - 'Stefan Foulis', 'manual'), + ('index', 'django-filer.tex', 'django-filer Documentation', 'Stefan Foulis', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. -#latex_logo = None +# latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. -#latex_use_parts = False +# latex_use_parts = False # If true, show page references after internal links. -#latex_show_pagerefs = False +# latex_show_pagerefs = False # If true, show URL addresses after external links. -#latex_show_urls = False +# latex_show_urls = False # Additional stuff for the LaTeX preamble. -#latex_preamble = '' +# latex_preamble = '' # Documents to append as an appendix to all manuals. -#latex_appendices = [] +# latex_appendices = [] # If false, no module index is generated. -#latex_domain_indices = True +# latex_domain_indices = True # -- Options for manual page output -------------------------------------------- @@ -227,8 +228,7 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ('index', 'django-filer', u'django-filer Documentation', - [u'Stefan Foulis'], 1) + ('index', 'django-filer', 'django-filer Documentation', ['Stefan Foulis'], 1) ] @@ -237,4 +237,4 @@ images_config = { 'override_image_directive': True, -} \ No newline at end of file +} diff --git a/filer/admin/folderadmin.py b/filer/admin/folderadmin.py index e118ea548..0a78e90cd 100644 --- a/filer/admin/folderadmin.py +++ b/filer/admin/folderadmin.py @@ -23,10 +23,7 @@ from django.utils.translation import ngettext_lazy from .. import settings -from ..models import ( - File, Folder, FolderPermission, FolderRoot, ImagesWithMissingData, - UnsortedImages, tools, -) +from ..models import File, Folder, FolderPermission, FolderRoot, ImagesWithMissingData, UnsortedImages, tools from ..settings import FILER_IMAGE_MODEL, FILER_PAGINATE_BY from ..thumbnail_processors import normalize_subject_location from ..utils.compatibility import get_delete_permission @@ -37,9 +34,8 @@ from .patched.admin_utils import get_deleted_objects from .permissions import PrimitivePermissionAwareModelAdmin from .tools import ( - AdminContext, admin_url_params_encoded, check_files_edit_permissions, - check_files_read_permissions, check_folder_edit_permissions, - check_folder_read_permissions, popup_status, userperms_for_request, + AdminContext, admin_url_params_encoded, check_files_edit_permissions, check_files_read_permissions, + check_folder_edit_permissions, check_folder_read_permissions, popup_status, userperms_for_request, ) diff --git a/filer/thumbnail_processors.py b/filer/thumbnail_processors.py index 87f1bde7b..b802b35e9 100644 --- a/filer/thumbnail_processors.py +++ b/filer/thumbnail_processors.py @@ -2,9 +2,7 @@ from easy_thumbnails import processors -from .settings import ( - FILER_SUBJECT_LOCATION_IMAGE_DEBUG, FILER_WHITESPACE_COLOR, -) +from .settings import FILER_SUBJECT_LOCATION_IMAGE_DEBUG, FILER_WHITESPACE_COLOR try: diff --git a/filer/urls.py b/filer/urls.py index 3f75b9350..91286086e 100644 --- a/filer/urls.py +++ b/filer/urls.py @@ -6,7 +6,7 @@ urlpatterns = [ re_path( - filer_settings.FILER_CANONICAL_URL + r'(?P[0-9]+)/(?P[0-9]+)/$', # flake8: noqa + filer_settings.FILER_CANONICAL_URL + r'(?P[0-9]+)/(?P[0-9]+)/$', views.canonical, name='canonical' ), diff --git a/filer/utils/compatibility.py b/filer/utils/compatibility.py index 7f83156b0..9a7cfbcc2 100644 --- a/filer/utils/compatibility.py +++ b/filer/utils/compatibility.py @@ -16,14 +16,14 @@ def truncate_words(s, num, end_text='...'): def get_delete_permission(opts): - from django.contrib.auth import get_permission_codename # noqa + from django.contrib.auth import get_permission_codename return '%s.%s' % (opts.app_label, get_permission_codename('delete', opts)) try: - from PIL import ExifTags as PILExifTags # noqa - from PIL import Image as PILImage # noqa - from PIL import ImageDraw as PILImageDraw # noqa + from PIL import ExifTags as PILExifTags + from PIL import Image as PILImage + from PIL import ImageDraw as PILImageDraw except ImportError: try: import ExifTags as PILExifTags # noqa diff --git a/filer/utils/files.py b/filer/utils/files.py index 44dda490c..8bbffbf3d 100644 --- a/filer/utils/files.py +++ b/filer/utils/files.py @@ -1,9 +1,7 @@ import mimetypes import os -from django.http.multipartparser import ( - ChunkIter, SkipFile, StopFutureHandlers, StopUpload, exhaust, -) +from django.http.multipartparser import ChunkIter, SkipFile, StopFutureHandlers, StopUpload, exhaust from django.template.defaultfilters import slugify as slugify_django from django.utils.encoding import force_str from django.utils.text import get_valid_filename as get_valid_filename_django diff --git a/setup.cfg b/setup.cfg index febce97a1..6f03acdee 100644 --- a/setup.cfg +++ b/setup.cfg @@ -18,7 +18,7 @@ exclude = ignore = E251,E128,E501,W503 [isort] -line_length = 79 +line_length = 119 skip = manage.py, *migrations*, .tox, .eggs, data, .env, .venv include_trailing_comma = true multi_line_output = 5 diff --git a/tests/test_admin.py b/tests/test_admin.py index 2dfa54897..f9ce54a26 100644 --- a/tests/test_admin.py +++ b/tests/test_admin.py @@ -10,11 +10,6 @@ from django.test import TestCase from django.urls import reverse -from tests.helpers import ( - SettingsOverride, create_folder_structure, create_image, create_superuser, -) -from tests.utils.extended_app.models import ExtImage, Video - from filer import settings as filer_settings from filer.admin.folderadmin import FolderAdmin from filer.models.filemodels import File @@ -24,6 +19,8 @@ from filer.templatetags.filer_admin_tags import file_icon_url from filer.thumbnail_processors import normalize_subject_location from filer.utils.loader import load_model +from tests.helpers import SettingsOverride, create_folder_structure, create_image, create_superuser +from tests.utils.extended_app.models import ExtImage, Video Image = load_model(FILER_IMAGE_MODEL) @@ -543,7 +540,7 @@ def test_move_files_and_folders_action(self): url = reverse('admin:filer-directory_listing', kwargs={ 'folder_id': self.src_folder.id, }) - response = self.client.post(url, { # noqa + response = self.client.post(url, { 'action': 'move_files_and_folders', 'post': 'yes', 'destination': self.dst_folder.id, @@ -695,7 +692,7 @@ def _do_test_rename(self, url, new_name, file_obj=None, folder_obj=None): # files inside this folder, non-recursive files = File.objects.filter(folder=folder_obj) else: - raise(ValueError('file_obj or folder_obj is required')) + raise ValueError('file_obj or folder_obj is required') response = self.client.post(url, { 'action': 'rename_files', diff --git a/tests/test_dump.py b/tests/test_dump.py index 5045cdfab..9121ef184 100644 --- a/tests/test_dump.py +++ b/tests/test_dump.py @@ -8,15 +8,12 @@ from django.core.management import call_command from django.test import TestCase -from tests.helpers import ( - SettingsOverride, create_folder_structure, create_image, create_superuser, -) - from filer import settings as filer_settings from filer.models import Folder from filer.models.filemodels import File from filer.settings import FILER_IMAGE_MODEL from filer.utils.loader import load_model +from tests.helpers import SettingsOverride, create_folder_structure, create_image, create_superuser Image = load_model(FILER_IMAGE_MODEL) diff --git a/tests/test_filer_check.py b/tests/test_filer_check.py index a6afd1917..01b951ca0 100644 --- a/tests/test_filer_check.py +++ b/tests/test_filer_check.py @@ -7,10 +7,9 @@ from django.test import TestCase from django.utils.module_loading import import_string -from tests.helpers import create_image - from filer import settings as filer_settings from filer.models.filemodels import File +from tests.helpers import create_image class FilerCheckTestCase(TestCase): diff --git a/tests/test_models.py b/tests/test_models.py index 2134a7537..7b316671f 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -6,11 +6,6 @@ from django.forms.models import modelform_factory from django.test import TestCase -from tests.helpers import ( - create_clipboard_item, create_folder_structure, create_image, - create_superuser, -) - from filer import settings as filer_settings from filer.models.clipboardmodels import Clipboard from filer.models.filemodels import File @@ -18,6 +13,7 @@ from filer.models.mixins import IconsMixin from filer.settings import FILER_IMAGE_MODEL from filer.utils.loader import load_model +from tests.helpers import create_clipboard_item, create_folder_structure, create_image, create_superuser Image = load_model(FILER_IMAGE_MODEL) diff --git a/tests/test_permissions.py b/tests/test_permissions.py index 1cd541aeb..23e7d93bc 100644 --- a/tests/test_permissions.py +++ b/tests/test_permissions.py @@ -5,13 +5,12 @@ from django.core.files import File as DjangoFile from django.test.testcases import TestCase -from tests.helpers import create_image, create_superuser - from filer import settings as filer_settings from filer.models.clipboardmodels import Clipboard from filer.models.foldermodels import Folder, FolderPermission from filer.settings import FILER_IMAGE_MODEL from filer.utils.loader import load_model +from tests.helpers import create_image, create_superuser Image = load_model(FILER_IMAGE_MODEL) diff --git a/tests/test_server_backends.py b/tests/test_server_backends.py index 54c0d9923..72e60f4e8 100644 --- a/tests/test_server_backends.py +++ b/tests/test_server_backends.py @@ -6,13 +6,12 @@ from django.test import TestCase from django.utils.http import http_date -from tests.helpers import create_image - from filer import settings as filer_settings from filer.models import File from filer.server.backends.default import DefaultServer from filer.server.backends.nginx import NginxXAccelRedirectServer from filer.server.backends.xsendfile import ApacheXSendfileServer +from tests.helpers import create_image class Mock(): diff --git a/tests/test_tools.py b/tests/test_tools.py index 4dc765074..2b548597a 100644 --- a/tests/test_tools.py +++ b/tests/test_tools.py @@ -4,13 +4,12 @@ from django.core.files import File as DjangoFile from django.test.testcases import TestCase -from tests.helpers import create_image, create_superuser - from filer.models import tools from filer.models.clipboardmodels import Clipboard from filer.models.foldermodels import Folder from filer.settings import FILER_IMAGE_MODEL from filer.utils.loader import load_model +from tests.helpers import create_image, create_superuser Image = load_model(FILER_IMAGE_MODEL) diff --git a/tests/test_utils.py b/tests/test_utils.py index e04192db0..a6ba470bd 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -5,10 +5,9 @@ from django.core.files import File as DjangoFile from django.test.testcases import TestCase -from tests.helpers import create_image - from filer.utils.loader import load_object from filer.utils.zip import unzip +from tests.helpers import create_image # Some target classes for the classloading tests diff --git a/tests/utils/custom_image/apps.py b/tests/utils/custom_image/apps.py new file mode 100644 index 000000000..d88c61473 --- /dev/null +++ b/tests/utils/custom_image/apps.py @@ -0,0 +1,8 @@ +from django.apps import AppConfig +from django.utils.translation import gettext_lazy as _ + + +class CustomImageConfig(AppConfig): + default_auto_field = 'django.db.models.AutoField' + name = 'tests.utils.custom_image' + verbose_name = _("Custom Image") diff --git a/tests/utils/extended_app/apps.py b/tests/utils/extended_app/apps.py new file mode 100644 index 000000000..e759d2b27 --- /dev/null +++ b/tests/utils/extended_app/apps.py @@ -0,0 +1,8 @@ +from django.apps import AppConfig +from django.utils.translation import gettext_lazy as _ + + +class ExtendedAppConfig(AppConfig): + default_auto_field = 'django.db.models.AutoField' + name = 'tests.utils.extended_app' + verbose_name = _("Extended App") diff --git a/tests/utils/test_app/apps.py b/tests/utils/test_app/apps.py new file mode 100644 index 000000000..134caacbd --- /dev/null +++ b/tests/utils/test_app/apps.py @@ -0,0 +1,8 @@ +from django.apps import AppConfig +from django.utils.translation import gettext_lazy as _ + + +class TestAppConfig(AppConfig): + default_auto_field = 'django.db.models.AutoField' + name = 'tests.utils.test_app' + verbose_name = _("Test app")