diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml deleted file mode 100644 index 9db7465..0000000 --- a/.github/workflows/lint.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: Lint -on: - push: - branches: - - main - pull_request: - branches: - - main -jobs: - Build: - runs-on: ubuntu-latest - steps: - - name: 'Set up Python' - uses: actions/setup-python@v3 - with: - python-version: '3.10' - - uses: actions/checkout@v2 - - run: pip install black flake8 - - run: make lint diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 97b5bb5..aa9d687 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -43,6 +43,7 @@ jobs: - name: Run Tests run: | echo "$(python --version) / Django $(django-admin --version)" - make test + coverage run --source=avatar `which django-admin` test tests + coverage report coverage xml - uses: codecov/codecov-action@v2 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..92c3839 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,29 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.3.0 + hooks: + - id: end-of-file-fixer + - id: trailing-whitespace + + - repo: https://github.com/pycqa/isort + rev: "5.10.1" + hooks: + - id: isort + args: ["--profile", "black"] + + - repo: https://github.com/psf/black + rev: 22.6.0 + hooks: + - id: black + args: [--target-version=py310] + + - repo: https://github.com/pycqa/flake8 + rev: '5.0.4' + hooks: + - id: flake8 + additional_dependencies: + - flake8-bugbear + - flake8-comprehensions + - flake8-tidy-imports + - flake8-print + args: [--max-line-length=120] diff --git a/LICENSE.txt b/LICENSE.txt index 56f2f5e..72714db 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -26,4 +26,3 @@ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - diff --git a/Makefile b/Makefile deleted file mode 100644 index 46ff801..0000000 --- a/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -export DJANGO_SETTINGS_MODULE=tests.settings -export PYTHONPATH=. - -.PHONY: test - -lint: - flake8 avatar --ignore=E124,E501,E127,E128,E722 - black --check . - -test: - coverage run --source=avatar `which django-admin` test tests - coverage report - -publish: clean - python setup.py sdist - twine upload dist/* - -clean: - rm -vrf ./build ./dist ./*.egg-info - find . -name '*.pyc' -delete - find . -name '*.tgz' -delete diff --git a/avatar/admin.py b/avatar/admin.py index 4d95177..a877086 100644 --- a/avatar/admin.py +++ b/avatar/admin.py @@ -1,6 +1,6 @@ from django.contrib import admin -from django.utils.translation import gettext_lazy as _ from django.template.loader import render_to_string +from django.utils.translation import gettext_lazy as _ from avatar.models import Avatar from avatar.signals import avatar_updated diff --git a/avatar/conf.py b/avatar/conf.py index 937f874..ed28b18 100644 --- a/avatar/conf.py +++ b/avatar/conf.py @@ -1,8 +1,6 @@ -from PIL import Image - -from django.conf import settings - from appconf import AppConf +from django.conf import settings +from PIL import Image class AvatarConf(AppConf): diff --git a/avatar/forms.py b/avatar/forms.py index a3c6beb..caf2a4c 100644 --- a/avatar/forms.py +++ b/avatar/forms.py @@ -2,9 +2,9 @@ import os from django import forms from django.forms import widgets +from django.template.defaultfilters import filesizeformat from django.utils.safestring import mark_safe from django.utils.translation import gettext_lazy as _ -from django.template.defaultfilters import filesizeformat from avatar.conf import settings from avatar.models import Avatar diff --git a/avatar/management/commands/rebuild_avatars.py b/avatar/management/commands/rebuild_avatars.py index a6033f1..2dda07c 100644 --- a/avatar/management/commands/rebuild_avatars.py +++ b/avatar/management/commands/rebuild_avatars.py @@ -14,6 +14,8 @@ class Command(BaseCommand): for avatar in Avatar.objects.all(): for size in settings.AVATAR_AUTO_GENERATE_SIZES: if options["verbosity"] != 0: - print("Rebuilding Avatar id=%s at size %s." % (avatar.id, size)) + self.stdout.write( + "Rebuilding Avatar id=%s at size %s." % (avatar.id, size) + ) avatar.create_thumbnail(size) diff --git a/avatar/migrations/0001_initial.py b/avatar/migrations/0001_initial.py index 7f2349d..3ef74bc 100644 --- a/avatar/migrations/0001_initial.py +++ b/avatar/migrations/0001_initial.py @@ -1,8 +1,9 @@ -from django.db import models, migrations -import django.utils.timezone -import avatar.models import django.core.files.storage +import django.utils.timezone from django.conf import settings +from django.db import migrations, models + +import avatar.models class Migration(migrations.Migration): diff --git a/avatar/migrations/0002_add_verbose_names_to_avatar_fields.py b/avatar/migrations/0002_add_verbose_names_to_avatar_fields.py index 4315b2d..52700e1 100644 --- a/avatar/migrations/0002_add_verbose_names_to_avatar_fields.py +++ b/avatar/migrations/0002_add_verbose_names_to_avatar_fields.py @@ -1,9 +1,10 @@ -import avatar.models -from django.conf import settings import django.core.files.storage -from django.db import migrations, models import django.db.models.deletion import django.utils.timezone +from django.conf import settings +from django.db import migrations, models + +import avatar.models class Migration(migrations.Migration): diff --git a/avatar/migrations/0003_auto_20170827_1345.py b/avatar/migrations/0003_auto_20170827_1345.py index 5e58509..8e39423 100644 --- a/avatar/migrations/0003_auto_20170827_1345.py +++ b/avatar/migrations/0003_auto_20170827_1345.py @@ -1,6 +1,7 @@ -import avatar.models from django.db import migrations +import avatar.models + class Migration(migrations.Migration): diff --git a/avatar/models.py b/avatar/models.py index ea7a711..c7f4a8a 100644 --- a/avatar/models.py +++ b/avatar/models.py @@ -1,22 +1,21 @@ import binascii -import os import hashlib +import os from io import BytesIO -from PIL import Image -from django.db import models from django.core.files import File from django.core.files.base import ContentFile from django.core.files.storage import get_storage_class -from django.utils.module_loading import import_string -from django.utils.translation import gettext_lazy as _ -from django.utils.encoding import force_str +from django.db import models from django.db.models import signals +from django.utils.encoding import force_str +from django.utils.module_loading import import_string from django.utils.timezone import now +from django.utils.translation import gettext_lazy as _ +from PIL import Image from avatar.conf import settings -from avatar.utils import get_username, force_bytes, invalidate_cache - +from avatar.utils import force_bytes, get_username, invalidate_cache avatar_storage = get_storage_class(settings.AVATAR_STORAGE)() @@ -139,7 +138,7 @@ class Avatar(models.Model): try: orientation = image._getexif()[0x0112] ops = EXIF_ORIENTATION_STEPS[orientation] - except: + except AttributeError: ops = [] for method in ops: image = image.transpose(getattr(Image, method)) diff --git a/avatar/providers.py b/avatar/providers.py index 7dfd1d9..68bb8e8 100644 --- a/avatar/providers.py +++ b/avatar/providers.py @@ -1,15 +1,10 @@ import hashlib -from urllib.parse import urljoin, urlencode +from urllib.parse import urlencode, urljoin from django.utils.module_loading import import_string from avatar.conf import settings -from avatar.utils import ( - force_bytes, - get_default_avatar_url, - get_primary_avatar, -) - +from avatar.utils import force_bytes, get_default_avatar_url, get_primary_avatar # If the FacebookAvatarProvider is used, a mechanism needs to be defined on # how to obtain the user's Facebook UID. This is done via diff --git a/avatar/signals.py b/avatar/signals.py index 9074a91..0491d4b 100644 --- a/avatar/signals.py +++ b/avatar/signals.py @@ -1,5 +1,4 @@ import django.dispatch - avatar_updated = django.dispatch.Signal() avatar_deleted = django.dispatch.Signal() diff --git a/avatar/templates/avatar/avatar_tag.html b/avatar/templates/avatar/avatar_tag.html index e21ca17..623b5df 100644 --- a/avatar/templates/avatar/avatar_tag.html +++ b/avatar/templates/avatar/avatar_tag.html @@ -1 +1 @@ - \ No newline at end of file + diff --git a/avatar/templates/avatar/base.html b/avatar/templates/avatar/base.html index 79ea0d4..a407d07 100644 --- a/avatar/templates/avatar/base.html +++ b/avatar/templates/avatar/base.html @@ -5,4 +5,4 @@ {% block content %}{% endblock %} - \ No newline at end of file + diff --git a/avatar/templates/notification/avatar_updated/notice.html b/avatar/templates/notification/avatar_updated/notice.html index 3c2abbf..0a8a548 100644 --- a/avatar/templates/notification/avatar_updated/notice.html +++ b/avatar/templates/notification/avatar_updated/notice.html @@ -1,2 +1,2 @@ {% load i18n %} -{% blocktrans with user as avatar_creator and avatar.get_absolute_url as avatar_url %}You have updated your avatar {{ avatar }}.{% endblocktrans %} \ No newline at end of file +{% blocktrans with user as avatar_creator and avatar.get_absolute_url as avatar_url %}You have updated your avatar {{ avatar }}.{% endblocktrans %} diff --git a/avatar/templatetags/avatar_tags.py b/avatar/templatetags/avatar_tags.py index d33fc7f..63f7480 100644 --- a/avatar/templatetags/avatar_tags.py +++ b/avatar/templatetags/avatar_tags.py @@ -1,19 +1,12 @@ from django import template -from django.urls import reverse - from django.template.loader import render_to_string -from django.utils.translation import gettext as _ +from django.urls import reverse from django.utils.module_loading import import_string +from django.utils.translation import gettext as _ from avatar.conf import settings from avatar.models import Avatar -from avatar.utils import ( - cache_result, - get_default_avatar_url, - get_user_model, - get_user, -) - +from avatar.utils import cache_result, get_default_avatar_url, get_user, get_user_model register = template.Library() diff --git a/avatar/utils.py b/avatar/utils.py index a7da903..e3c1daf 100644 --- a/avatar/utils.py +++ b/avatar/utils.py @@ -1,13 +1,12 @@ import hashlib +from django.contrib.auth import get_user_model from django.core.cache import cache from django.template.defaultfilters import slugify from django.utils.encoding import force_bytes -from django.contrib.auth import get_user_model from avatar.conf import settings - cached_funcs = set() diff --git a/avatar/views.py b/avatar/views.py index 7bac181..1b1d213 100644 --- a/avatar/views.py +++ b/avatar/views.py @@ -1,13 +1,13 @@ -from django.shortcuts import render, redirect -from django.utils.translation import gettext as _ from django.contrib import messages from django.contrib.auth.decorators import login_required +from django.shortcuts import redirect, render +from django.utils.translation import gettext as _ from avatar.conf import settings -from avatar.forms import PrimaryAvatarForm, DeleteAvatarForm, UploadAvatarForm +from avatar.forms import DeleteAvatarForm, PrimaryAvatarForm, UploadAvatarForm from avatar.models import Avatar -from avatar.signals import avatar_updated, avatar_deleted -from avatar.utils import get_primary_avatar, get_default_avatar_url, invalidate_cache +from avatar.signals import avatar_deleted, avatar_updated +from avatar.utils import get_default_avatar_url, get_primary_avatar, invalidate_cache def _get_next(request): @@ -60,7 +60,7 @@ def add( next_override=None, upload_form=UploadAvatarForm, *args, - **kwargs + **kwargs, ): if extra_context is None: extra_context = {} @@ -96,7 +96,7 @@ def change( upload_form=UploadAvatarForm, primary_form=PrimaryAvatarForm, *args, - **kwargs + **kwargs, ): if extra_context is None: extra_context = {} diff --git a/docs/conf.py b/docs/conf.py index 35af91b..b0d6823 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -11,7 +11,8 @@ # All configuration values have a default; values that are commented out # serve to show the default. -import sys, os +import os +import sys # 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 @@ -174,11 +175,11 @@ htmlhelp_basename = "django-avatardoc" latex_elements = { # The paper size ('letterpaper' or 'a4paper'). - #'papersize': 'letterpaper', + # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). - #'pointsize': '10pt', + # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. - #'preamble': '', + # 'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples diff --git a/setup.py b/setup.py index 0dcdc47..10777e0 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,8 @@ import codecs import re from os import path -from setuptools import setup, find_packages + +from setuptools import find_packages, setup def read(*parts): diff --git a/test_proj/manage.py b/test_proj/manage.py index 912417f..1844990 100755 --- a/test_proj/manage.py +++ b/test_proj/manage.py @@ -1,8 +1,11 @@ #!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" import os import sys -if __name__ == "__main__": + +def main(): + """Run administrative tasks.""" os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test_proj.settings") # Add the django-avatar directory to the Python path. That way the @@ -10,17 +13,14 @@ if __name__ == "__main__": sys.path.append("..") try: from django.core.management import execute_from_command_line - except ImportError: - # The above import may fail for some other reason. Ensure that the - # issue is really that Django is missing to avoid masking other - # exceptions on Python 2. - try: - import django - except ImportError: - raise ImportError( - "Couldn't import Django. Are you sure it's installed and " - "available on your PYTHONPATH environment variable? Did you " - "forget to activate a virtual environment?" - ) - raise + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc execute_from_command_line(sys.argv) + + +if __name__ == "__main__": + main() diff --git a/test_proj/test_proj/urls.py b/test_proj/test_proj/urls.py index c80c7e5..e874078 100644 --- a/test_proj/test_proj/urls.py +++ b/test_proj/test_proj/urls.py @@ -1,5 +1,5 @@ from django.conf import settings -from django.conf.urls import url, include +from django.conf.urls import include, url from django.contrib import admin from django.views.static import serve diff --git a/tests/tests.py b/tests/tests.py index d7bdf5b..d573cbb 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -1,23 +1,18 @@ +import math import os.path -import math from django.contrib.admin.sites import AdminSite from django.test import TestCase - -try: - from django.urls import reverse -except ImportError: - # For Django < 1.10 - from django.core.urlresolvers import reverse from django.test.utils import override_settings +from django.urls import reverse +from PIL import Image, ImageChops from avatar.admin import AvatarAdmin from avatar.conf import settings -from avatar.utils import get_primary_avatar, get_user_model from avatar.models import Avatar -from avatar.templatetags import avatar_tags from avatar.signals import avatar_deleted -from PIL import Image, ImageChops +from avatar.templatetags import avatar_tags +from avatar.utils import get_primary_avatar, get_user_model class AssertSignal: @@ -140,7 +135,7 @@ class AvatarTests(TestCase): self.assertEqual(a, None) def test_there_can_be_only_one_primary_avatar(self): - for i in range(1, 10): + for _ in range(1, 10): self.test_normal_image_upload() count = Avatar.objects.filter(user=self.user, primary=True).count() self.assertEqual(count, 1) @@ -212,7 +207,7 @@ class AvatarTests(TestCase): ) def test_too_many_avatars(self): - for i in range(0, settings.AVATAR_MAX_AVATARS_PER_USER): + for _ in range(0, settings.AVATAR_MAX_AVATARS_PER_USER): self.test_normal_image_upload() count_before = Avatar.objects.filter(user=self.user).count() response = upload_helper(self, "test.png") diff --git a/tests/urls.py b/tests/urls.py index b37a229..0bc6b7e 100644 --- a/tests/urls.py +++ b/tests/urls.py @@ -1,6 +1,5 @@ from django.urls import include, re_path - urlpatterns = [ re_path(r"^avatar/", include("avatar.urls")), ]