Просмотр исходного кода

fix #740: setup basic pylint, cleanup codebase a little, default yapf and cleansource scripts

Rafał Pitoń 8 лет назад
Родитель
Сommit
be5bb6a97f
143 измененных файлов с 240 добавлено и 426 удалено
  1. 2 2
      .gitignore
  2. 3 3
      .pylintrc
  3. 13 0
      .style.yapf
  4. 5 0
      cleansource
  5. 0 2
      misago/acl/api.py
  6. 1 1
      misago/acl/migrations/0002_acl_version_tracker.py
  7. 0 1
      misago/admin/admin.py
  8. 10 10
      misago/admin/urlpatterns.py
  9. 1 1
      misago/admin/urls.py
  10. 1 1
      misago/admin/views/generic/list.py
  11. 1 1
      misago/categories/apps.py
  12. 2 2
      misago/categories/migrations/0002_default_categories.py
  13. 4 4
      misago/categories/migrations/0003_categories_roles.py
  14. 3 3
      misago/categories/tests/test_categories_admin_views.py
  15. 3 9
      misago/categories/tests/test_category_model.py
  16. 8 8
      misago/categories/tests/test_prunecategories.py
  17. 2 2
      misago/categories/tests/test_synchronizecategories.py
  18. 0 3
      misago/categories/utils.py
  19. 1 1
      misago/categories/views/categoriesadmin.py
  20. 0 2
      misago/conf/context_processors.py
  21. 1 1
      misago/conf/models.py
  22. 0 2
      misago/core/exceptionhandler.py
  23. 1 1
      misago/core/migrations/0002_basic_settings.py
  24. 2 2
      misago/core/page.py
  25. 1 1
      misago/core/setup.py
  26. 1 1
      misago/core/shortcuts.py
  27. 0 2
      misago/core/templatetags/misago_pagetitle.py
  28. 1 2
      misago/core/tests/test_cachebuster.py
  29. 0 1
      misago/core/tests/test_decorators.py
  30. 1 1
      misago/core/tests/test_deprecations.py
  31. 0 1
      misago/core/tests/test_momentjs.py
  32. 0 2
      misago/core/tests/test_templatetags.py
  33. 1 1
      misago/core/tests/test_utils.py
  34. 0 8
      misago/core/utils.py
  35. 0 2
      misago/core/views.py
  36. 1 1
      misago/datamover/attachments.py
  37. 1 1
      misago/datamover/avatars.py
  38. 1 1
      misago/datamover/markup/quotes.py
  39. 0 1
      misago/datamover/threads.py
  40. 1 1
      misago/faker/management/commands/createfakebans.py
  41. 0 2
      misago/faker/management/commands/createfakecategories.py
  42. 3 5
      misago/faker/management/commands/createfakethreads.py
  43. 0 2
      misago/faker/management/commands/createfakeusers.py
  44. 1 1
      misago/legal/migrations/0001_initial.py
  45. 0 1
      misago/markup/api.py
  46. 1 1
      misago/markup/mentions.py
  47. 0 1
      misago/markup/parser.py
  48. 1 1
      misago/readtracker/apps.py
  49. 0 3
      misago/readtracker/models.py
  50. 2 1
      misago/readtracker/tests/test_readtracker.py
  51. 0 1
      misago/threads/api/postendpoints/edits.py
  52. 0 2
      misago/threads/api/postendpoints/split.py
  53. 1 1
      misago/threads/api/postingendpoint/attachments.py
  54. 0 2
      misago/threads/api/postingendpoint/reply.py
  55. 1 2
      misago/threads/api/postingendpoint/subscribe.py
  56. 1 1
      misago/threads/api/threadendpoints/merge.py
  57. 2 3
      misago/threads/api/threadendpoints/patch.py
  58. 1 1
      misago/threads/apps.py
  59. 1 1
      misago/threads/forms.py
  60. 0 1
      misago/threads/middleware.py
  61. 1 1
      misago/threads/migrations/0001_initial.py
  62. 1 2
      misago/threads/migrations/0002_threads_settings.py
  63. 1 2
      misago/threads/migrations/0003_attachment_types.py
  64. 3 3
      misago/threads/models/post.py
  65. 1 1
      misago/threads/models/thread.py
  66. 0 1
      misago/threads/moderation/threads.py
  67. 0 3
      misago/threads/paginator.py
  68. 0 1
      misago/threads/participants.py
  69. 1 3
      misago/threads/permissions/privatethreads.py
  70. 1 1
      misago/threads/permissions/threads.py
  71. 0 1
      misago/threads/serializers/attachment.py
  72. 0 2
      misago/threads/serializers/feed.py
  73. 0 1
      misago/threads/serializers/post.py
  74. 0 2
      misago/threads/templatetags/misago_poststags.py
  75. 7 7
      misago/threads/tests/test_attachments_api.py
  76. 3 3
      misago/threads/tests/test_clearattachments.py
  77. 1 4
      misago/threads/tests/test_events.py
  78. 1 2
      misago/threads/tests/test_floodprotection_middleware.py
  79. 12 12
      misago/threads/tests/test_gotoviews.py
  80. 17 2
      misago/threads/tests/test_post_model.py
  81. 3 3
      misago/threads/tests/test_privatethread_patch_api.py
  82. 0 1
      misago/threads/tests/test_privatethread_reply_api.py
  83. 1 3
      misago/threads/tests/test_privatethread_start_api.py
  84. 3 1
      misago/threads/tests/test_privatethreads_api.py
  85. 3 1
      misago/threads/tests/test_privatethreads_lists.py
  86. 1 13
      misago/threads/tests/test_subscriptions.py
  87. 2 2
      misago/threads/tests/test_synchronizethreads.py
  88. 1 6
      misago/threads/tests/test_thread_editreply_api.py
  89. 5 7
      misago/threads/tests/test_thread_merge_api.py
  90. 0 6
      misago/threads/tests/test_thread_postedits_api.py
  91. 6 9
      misago/threads/tests/test_thread_postmerge_api.py
  92. 1 1
      misago/threads/tests/test_thread_postpatch_api.py
  93. 0 1
      misago/threads/tests/test_thread_postread_api.py
  94. 0 5
      misago/threads/tests/test_thread_start_api.py
  95. 1 2
      misago/threads/tests/test_threadparticipant_model.py
  96. 1 1
      misago/threads/tests/test_threads_editor_api.py
  97. 8 7
      misago/threads/tests/test_threads_merge_api.py
  98. 1 1
      misago/threads/tests/test_threads_moderation.py
  99. 6 6
      misago/threads/tests/test_threadslists.py
  100. 5 6
      misago/threads/tests/test_threadview.py
  101. 1 1
      misago/threads/tests/test_validators.py
  102. 8 8
      misago/threads/urls/__init__.py
  103. 0 3
      misago/threads/views/admin/attachments.py
  104. 0 2
      misago/threads/views/admin/attachmenttypes.py
  105. 0 3
      misago/threads/views/attachment.py
  106. 5 6
      misago/threads/views/list.py
  107. 2 2
      misago/threads/views/thread.py
  108. 1 1
      misago/users/api/userendpoints/avatar.py
  109. 0 1
      misago/users/api/userendpoints/create.py
  110. 1 9
      misago/users/api/userendpoints/list.py
  111. 0 2
      misago/users/api/userendpoints/username.py
  112. 1 1
      misago/users/api/usernamechanges.py
  113. 2 4
      misago/users/api/users.py
  114. 1 4
      misago/users/apps.py
  115. 1 2
      misago/users/avatars/dynamic.py
  116. 0 3
      misago/users/avatars/uploaded.py
  117. 0 1
      misago/users/decorators.py
  118. 0 1
      misago/users/management/commands/buildactivepostersranking.py
  119. 0 3
      misago/users/middleware.py
  120. 1 1
      misago/users/migrations/0003_bans_version_tracker.py
  121. 1 1
      misago/users/migrations/0004_default_ranks.py
  122. 1 2
      misago/users/models/user.py
  123. 1 1
      misago/users/serializers/moderation.py
  124. 0 3
      misago/users/templatetags/misago_avatars.py
  125. 1 1
      misago/users/tests/test_auth_api.py
  126. 0 1
      misago/users/tests/test_auth_views.py
  127. 2 2
      misago/users/tests/test_avatars.py
  128. 1 1
      misago/users/tests/test_banadmin_views.py
  129. 0 2
      misago/users/tests/test_forgottenpassword_views.py
  130. 1 1
      misago/users/tests/test_invalidatebans.py
  131. 0 4
      misago/users/tests/test_user_avatar_api.py
  132. 0 1
      misago/users/tests/test_user_changepassword_api.py
  133. 2 2
      misago/users/tests/test_user_feeds_api.py
  134. 4 13
      misago/users/tests/test_user_signature_api.py
  135. 2 1
      misago/users/tests/test_user_username_api.py
  136. 2 44
      misago/users/tests/test_useradmin_views.py
  137. 0 2
      misago/users/tests/test_usernamechanges_api.py
  138. 0 15
      misago/users/tests/test_users_api.py
  139. 2 2
      misago/users/validators.py
  140. 0 1
      misago/users/viewmodels/rankusers.py
  141. 0 2
      misago/users/views/activation.py
  142. 9 6
      misago/users/views/admin/users.py
  143. 1 2
      misago/users/views/options.py

+ 2 - 2
.gitignore

@@ -39,8 +39,8 @@ coverage.xml
 report.txt
 flake8.txt
 
-# Translations
-*.mo
+# Pylint report
+pylint.txt
 
 # Mr Developer
 .mr.developer.cfg

+ 3 - 3
.pylintrc

@@ -1,4 +1,4 @@
 [Basic]
-disable=abstract-method,cyclic-import,duplicate-code,file-ignored,invalid-name,locally-disabled,missing-docstring,no-init,no-member,no-self-use,old-style-class,super-on-old-class,too-few-public-methods,too-many-ancestors,unused-argument
-max-line-length=120
-max-locals=20
+disable=all
+enable=function-redefined,import-self,redefined-outer-name,reimported,return-in-init,undefined-all-variable,undefined-variable,unreachable,unused-import,unused-variable
+reports=no

+ 13 - 0
.style.yapf

@@ -0,0 +1,13 @@
+[style]
+coalesce_brackets = true
+column_limit=100
+dedent_closing_brackets = true
+each_dict_entry_on_separate_line = true
+indent_dictionary_value = true
+join_multiple_lines = false
+spaces_before_comment = 4
+split_arguments_when_comma_terminated = true
+split_before_first_argument = true
+split_before_logical_operator = true
+split_before_named_assigns = true
+split_penalty_import_names = 2

+ 5 - 0
cleansource

@@ -0,0 +1,5 @@
+#!/bin/bash
+
+isort -rc misago
+yapf -ir misago
+pylint misago

+ 0 - 2
misago/acl/api.py

@@ -10,8 +10,6 @@ properties defined by ACL providers within their "add_acl_to_target"
 """
 import copy
 
-from django.contrib.auth import get_user_model
-
 from misago.core import threadstore
 from misago.core.cache import cache
 

+ 1 - 1
misago/acl/migrations/0002_acl_version_tracker.py

@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
 from __future__ import unicode_literals
 
-from django.db import migrations, models
+from django.db import migrations
 
 from misago.acl.constants import ACL_CACHEBUSTER
 from misago.core.migrationutils import cachebuster_register_cache

+ 0 - 1
misago/admin/admin.py

@@ -1,4 +1,3 @@
-from django.conf.urls import url
 from django.utils.deprecation import MiddlewareMixin
 from django.utils.translation import ugettext_lazy as _
 

+ 10 - 10
misago/admin/urlpatterns.py

@@ -11,13 +11,13 @@ class URLPatterns(object):
             'path': path,
             'parent': parent,
             'namespace': namespace,
-            })
+        })
 
-    def patterns(self, namespace, *urlpatterns):
+    def patterns(self, namespace, *new_patterns):
         self._patterns.append({
             'namespace': namespace,
-            'urlpatterns': urlpatterns,
-            })
+            'urlpatterns': new_patterns,
+        })
 
     def get_child_patterns(self, parent):
         prefix = '%s:' % parent if parent else ''
@@ -26,8 +26,8 @@ class URLPatterns(object):
         for namespace in self._namespaces:
             if namespace['parent'] == parent:
                 prefixed_namespace = prefix + namespace['namespace']
-                urlpatterns = self.get_child_patterns(prefixed_namespace)
-                included_patterns = include(urlpatterns, namespace=namespace['namespace'])
+                child_patterns = self.get_child_patterns(prefixed_namespace)
+                included_patterns = include(child_patterns, namespace=namespace['namespace'])
                 namespace_urlpatterns.append(url(namespace['path'], included_patterns))
 
         return namespace_urlpatterns
@@ -36,8 +36,8 @@ class URLPatterns(object):
         all_patterns = {}
         for urls in self._patterns:
             namespace = urls['namespace']
-            urlpatterns = urls['urlpatterns']
-            all_patterns.setdefault(namespace, []).extend(urlpatterns)
+            added_patterns = urls['urlpatterns']
+            all_patterns.setdefault(namespace, []).extend(added_patterns)
 
         self.namespace_patterns = all_patterns
 
@@ -45,8 +45,8 @@ class URLPatterns(object):
         root_urlpatterns = []
         for namespace in self._namespaces:
             if not namespace['parent']:
-                urlpatterns = self.get_child_patterns(namespace['namespace'])
-                included_patterns = include(urlpatterns, namespace=namespace['namespace'])
+                child_patterns = self.get_child_patterns(namespace['namespace'])
+                included_patterns = include(child_patterns, namespace=namespace['namespace'])
                 root_urlpatterns.append(url(namespace['path'], included_patterns))
 
         return root_urlpatterns

+ 1 - 1
misago/admin/urls.py

@@ -1,4 +1,4 @@
-from django.conf.urls import include, url
+from django.conf.urls import url
 
 from misago import admin
 

+ 1 - 1
misago/admin/views/generic/list.py

@@ -262,7 +262,7 @@ class ListView(AdminView):
         return self.clean_ordering(new_ordering)
 
     def clean_ordering(self, new_ordering):
-        for order_by, name in self.ordering:
+        for order_by, _ in self.ordering:
             if order_by == new_ordering:
                 return order_by
         else:

+ 1 - 1
misago/categories/apps.py

@@ -7,4 +7,4 @@ class MisagoCategoriesConfig(AppConfig):
     verbose_name = "Misago Categories"
 
     def ready(self):
-        from . import signals
+        from . import signals as _

+ 2 - 2
misago/categories/migrations/0002_default_categories.py

@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
 from __future__ import unicode_literals
 
-from django.db import migrations, models
+from django.db import migrations
 from django.utils.translation import ugettext as _
 
 from misago.core.utils import slugify
@@ -32,7 +32,7 @@ def create_default_categories_tree(apps, schema_editor):
 
     category_name = _("First category")
 
-    category = Category.objects.create(
+    Category.objects.create(
         parent=root,
         lft=4,
         rght=5,

+ 4 - 4
misago/categories/migrations/0003_categories_roles.py

@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
 from __future__ import unicode_literals
 
-from django.db import migrations, models
+from django.db import migrations
 from django.utils.translation import ugettext as _
 
 
@@ -11,7 +11,7 @@ def create_default_categories_roles(apps, schema_editor):
     """
     CategoryRole = apps.get_model('misago_categories', 'CategoryRole')
 
-    see_only = CategoryRole.objects.create(
+    CategoryRole.objects.create(
         name=_('See only'),
         permissions={
             # categories perms
@@ -41,7 +41,7 @@ def create_default_categories_roles(apps, schema_editor):
         }
     )
 
-    reply_only = CategoryRole.objects.create(
+    CategoryRole.objects.create(
         name=_('Reply to threads'),
         permissions={
             # categories perms
@@ -87,7 +87,7 @@ def create_default_categories_roles(apps, schema_editor):
         }
     )
 
-    standard_with_polls = CategoryRole.objects.create(
+    CategoryRole.objects.create(
         name=_('Start and reply threads, make polls'),
         permissions={
             # categories perms

+ 3 - 3
misago/categories/tests/test_categories_admin_views.py

@@ -392,7 +392,7 @@ class CategoryAdminDeleteViewTests(CategoryAdminTestCate):
 
     def test_delete_category_move_contents(self):
         """category was deleted and its contents were moved"""
-        for i in range(10):
+        for _ in range(10):
             testutils.post_thread(self.category_b)
         self.assertEqual(Thread.objects.count(), 10)
 
@@ -426,7 +426,7 @@ class CategoryAdminDeleteViewTests(CategoryAdminTestCate):
 
     def test_delete_category_and_contents(self):
         """category and its contents were deleted"""
-        for i in range(10):
+        for _ in range(10):
             testutils.post_thread(self.category_b)
 
         response = self.client.get(
@@ -458,7 +458,7 @@ class CategoryAdminDeleteViewTests(CategoryAdminTestCate):
 
     def test_delete_leaf_category(self):
         """category was deleted and its contents were moved"""
-        for i in range(10):
+        for _ in range(10):
             testutils.post_thread(self.category_d)
         self.assertEqual(Thread.objects.count(), 10)
 

+ 3 - 9
misago/categories/tests/test_category_model.py

@@ -1,5 +1,3 @@
-from django.utils import timezone
-
 from misago.categories import THREADS_ROOT_NAME
 from misago.categories.models import Category
 from misago.core.testutils import MisagoTestCase
@@ -54,11 +52,7 @@ class CategoryModelTests(MisagoTestCase):
         self.category = Category.objects.all_categories()[:1][0]
 
     def create_thread(self):
-        datetime = timezone.now()
-
-        thread = testutils.post_thread(self.category)
-
-        return thread
+        return testutils.post_thread(self.category)
 
     def assertCategoryIsEmpty(self):
         self.assertIsNone(self.category.last_post_on)
@@ -114,7 +108,7 @@ class CategoryModelTests(MisagoTestCase):
 
     def test_delete_content(self):
         """delete_content empties category"""
-        for i in range(10):
+        for _ in range(10):
             self.create_thread()
 
         self.category.synchronize()
@@ -131,7 +125,7 @@ class CategoryModelTests(MisagoTestCase):
 
     def test_move_content(self):
         """move_content moves category threads and posts to other category"""
-        for i in range(10):
+        for _ in range(10):
             self.create_thread()
         self.category.synchronize()
 

+ 8 - 8
misago/categories/tests/test_prunecategories.py

@@ -22,12 +22,12 @@ class PruneCategoriesTests(TestCase):
         # post old threads with recent replies
         started_on = timezone.now() - timedelta(days=30)
         posted_on = timezone.now()
-        for t in range(10):
+        for _ in range(10):
             thread = testutils.post_thread(category, started_on=started_on)
             testutils.reply_thread(thread, posted_on=posted_on)
 
         # post recent threads that will be preserved
-        threads = [testutils.post_thread(category) for t in range(10)]
+        threads = [testutils.post_thread(category) for _ in range(10)]
 
         category.synchronize()
         self.assertEqual(category.threads, 20)
@@ -58,12 +58,12 @@ class PruneCategoriesTests(TestCase):
 
         # post old threads with recent replies
         started_on = timezone.now() - timedelta(days=30)
-        for t in range(10):
+        for _ in range(10):
             thread = testutils.post_thread(category, started_on=started_on)
             testutils.reply_thread(thread)
 
         # post recent threads that will be preserved
-        threads = [testutils.post_thread(category) for t in range(10)]
+        threads = [testutils.post_thread(category) for _ in range(10)]
 
         category.synchronize()
         self.assertEqual(category.threads, 20)
@@ -104,12 +104,12 @@ class PruneCategoriesTests(TestCase):
         # post old threads with recent replies
         started_on = timezone.now() - timedelta(days=30)
         posted_on = timezone.now()
-        for t in range(10):
+        for _ in range(10):
             thread = testutils.post_thread(category, started_on=started_on)
             testutils.reply_thread(thread, posted_on=posted_on)
 
         # post recent threads that will be preserved
-        threads = [testutils.post_thread(category) for t in range(10)]
+        threads = [testutils.post_thread(category) for _ in range(10)]
 
         category.synchronize()
         self.assertEqual(category.threads, 20)
@@ -153,12 +153,12 @@ class PruneCategoriesTests(TestCase):
 
         # post old threads with recent replies
         started_on = timezone.now() - timedelta(days=30)
-        for t in range(10):
+        for _ in range(10):
             thread = testutils.post_thread(category, started_on=started_on)
             testutils.reply_thread(thread)
 
         # post recent threads that will be preserved
-        threads = [testutils.post_thread(category) for t in range(10)]
+        threads = [testutils.post_thread(category) for _ in range(10)]
 
         category.synchronize()
         self.assertEqual(category.threads, 20)

+ 2 - 2
misago/categories/tests/test_synchronizecategories.py

@@ -13,9 +13,9 @@ class SynchronizeCategoriesTests(TestCase):
         """command synchronizes categories"""
         category = Category.objects.all_categories()[:1][0]
 
-        threads = [testutils.post_thread(category) for t in range(10)]
+        threads = [testutils.post_thread(category) for _ in range(10)]
         for thread in threads:
-            [testutils.reply_thread(thread) for r in range(5)]
+            [testutils.reply_thread(thread) for _ in range(5)]
 
         category.threads = 0
         category.posts = 0

+ 0 - 3
misago/categories/utils.py

@@ -1,5 +1,4 @@
 from misago.acl import add_acl
-from misago.core import threadstore
 from misago.readtracker import categoriestracker
 
 from .models import Category
@@ -74,8 +73,6 @@ def get_category_path(category):
     if category.special_role:
         return [category]
 
-    categories_dict = Category.objects.get_cached_categories_dict()
-
     category_path = []
     while category and category.level > 0:
         category_path.append(category)

+ 1 - 1
misago/categories/views/categoriesadmin.py

@@ -39,7 +39,7 @@ class CategoriesList(CategoryAdmin, generic.ListView):
 
         children_lists = {}
 
-        for i, item in enumerate(context['items']):
+        for item in context['items']:
             item.level_range = range(item.level - 1)
             item.first = False
             item.last = False

+ 0 - 2
misago/conf/context_processors.py

@@ -1,5 +1,3 @@
-import json
-
 from django.contrib.staticfiles.templatetags.staticfiles import static
 from django.urls import reverse
 from django.utils.translation import get_language

+ 1 - 1
misago/conf/models.py

@@ -1,7 +1,7 @@
 from django.contrib.postgres.fields import JSONField
 from django.db import models
 
-from . import hydrators, utils
+from . import utils
 
 
 class SettingsGroupsManager(models.Manager):

+ 0 - 2
misago/core/exceptionhandler.py

@@ -4,7 +4,6 @@ from django.core.exceptions import PermissionDenied
 from django.http import Http404, HttpResponsePermanentRedirect, JsonResponse
 from django.urls import reverse
 from django.utils import six
-from django.utils.translation import gettext as _
 
 from . import errorpages
 from .exceptions import AjaxError, Banned, ExplicitFirstPage, OutdatedSlug
@@ -46,7 +45,6 @@ def handle_outdated_slug_exception(request, exception):
     view_name = request.resolver_match.view_name
 
     model = exception.args[0]
-    model_name = model.__class__.__name__.lower()
     url_kwargs = request.resolver_match.kwargs
     url_kwargs['slug'] = model.slug
 

+ 1 - 1
misago/core/migrations/0002_basic_settings.py

@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
 from __future__ import unicode_literals
 
-from django.db import migrations, models
+from django.db import migrations
 
 from misago.conf.migrationutils import migrate_settings_group
 

+ 2 - 2
misago/core/page.py

@@ -42,7 +42,7 @@ class Page(object):
     def _insert_section(self, inserted_section, after=None, before=None):
         if after:
             new_sorted_list = []
-            for index, section in enumerate(self._sorted_list):
+            for section in self._sorted_list:
                 new_sorted_list.append(section)
                 if section['link'] == after:
                     new_sorted_list.append(inserted_section)
@@ -52,7 +52,7 @@ class Page(object):
                 return False
         elif before:
             new_sorted_list = []
-            for index, section in enumerate(self._sorted_list):
+            for section in self._sorted_list:
                 if section['link'] == before:
                     new_sorted_list.append(inserted_section)
                     new_sorted_list.append(section)

+ 1 - 1
misago/core/setup.py

@@ -36,7 +36,7 @@ def get_misago_project_template():
 
 def start_misago_project():
     parser = OptionParser(usage="usage: %prog project_name")
-    (options, args) = parser.parse_args()
+    _, args = parser.parse_args()
 
     if len(args) != 1:
         parser.error("project_name must be specified")

+ 1 - 1
misago/core/shortcuts.py

@@ -23,7 +23,7 @@ def paginate(object_list, page, per_page, orphans=0,
         return paginator(
             object_list, per_page, orphans=orphans,
             allow_empty_first_page=allow_empty_first_page).page(page)
-    except (EmptyPage, InvalidPage) as e:
+    except (EmptyPage, InvalidPage):
         raise Http404()
 
 

+ 0 - 2
misago/core/templatetags/misago_pagetitle.py

@@ -1,8 +1,6 @@
 from django import template
 from django.utils.translation import gettext as _
 
-from misago.conf import settings
-
 
 register = template.Library()
 

+ 1 - 2
misago/core/tests/test_cachebuster.py

@@ -1,5 +1,4 @@
-from misago.core import cachebuster, threadstore
-from misago.core.cache import cache
+from misago.core import cachebuster
 from misago.core.models import CacheVersion
 from misago.core.testutils import MisagoTestCase
 

+ 0 - 1
misago/core/tests/test_decorators.py

@@ -1,4 +1,3 @@
-from django.contrib.auth import get_user_model
 from django.test import TestCase, override_settings
 from django.urls import reverse
 

+ 1 - 1
misago/core/tests/test_deprecations.py

@@ -1,6 +1,6 @@
 import warnings
 
-from django.test import TestCase, override_settings
+from django.test import TestCase
 from django.utils import six
 
 from misago.core.deprecations import RemovedInMisagoWarning, warn

+ 0 - 1
misago/core/tests/test_momentjs.py

@@ -1,4 +1,3 @@
-from django.conf import settings
 from django.test import TestCase
 
 from misago.core.momentjs import clean_language_name, get_locale_url

+ 0 - 2
misago/core/tests/test_templatetags.py

@@ -2,9 +2,7 @@ from django import forms
 from django.template import Context, Template, TemplateSyntaxError
 from django.test import TestCase
 
-from misago.core.shortcuts import paginate
 from misago.core.templatetags import misago_batch
-from misago.core.utils import encode_json_html
 
 
 class CaptureTests(TestCase):

+ 1 - 1
misago/core/tests/test_utils.py

@@ -4,7 +4,7 @@ from __future__ import unicode_literals
 from django.test import TestCase
 from django.test.client import RequestFactory
 from django.urls import reverse
-from django.utils import six, timezone
+from django.utils import six
 
 from misago.core.utils import (
     clean_return_path, format_plaintext_for_html, is_referer_local, is_request_to_misago,

+ 0 - 8
misago/core/utils.py

@@ -6,19 +6,11 @@ from django.http import Http404
 from django.urls import resolve, reverse
 from django.utils import html, timezone
 from django.utils.encoding import force_text
-from django.utils.translation import ugettext_lazy as _
-from django.utils.translation import ungettext_lazy
 
 
 MISAGO_SLUGIFY = getattr(settings, 'MISAGO_SLUGIFY', 'misago.core.slugify.default')
 
 
-def slugify(string):
-    string = six.text_type(string)
-    string = unidecode(string)
-    return django_slugify(string.replace('_', ' ').strip())
-
-
 def resolve_slugify(path):
     path_bits = path.split('.')
     module, name = '.'.join(path_bits[:-1]), path_bits[-1]

+ 0 - 2
misago/core/views.py

@@ -4,8 +4,6 @@ from django.views import i18n
 from django.views.decorators.cache import cache_page
 from django.views.decorators.http import last_modified
 
-from . import momentjs
-
 
 def forum_index(request):
     return # blow up as this view is normally non-reachable!

+ 1 - 1
misago/datamover/attachments.py

@@ -5,7 +5,7 @@ import os
 from django.contrib.auth import get_user_model
 from django.core.files import File
 
-from misago.threads.models import Attachment, AttachmentType, Post, Thread
+from misago.threads.models import Attachment, AttachmentType, Post
 from misago.threads.serializers import AttachmentSerializer
 
 from . import OLD_FORUM, fetch_assoc, localise_datetime, movedids

+ 1 - 1
misago/datamover/avatars.py

@@ -6,7 +6,7 @@ from django.contrib.auth import get_user_model
 from django.core.exceptions import ValidationError
 
 from misago.conf import settings
-from misago.users.avatars import dynamic, gallery, gravatar, store, uploaded
+from misago.users.avatars import dynamic, gravatar, store, uploaded
 
 from . import OLD_FORUM, fetch_assoc, movedids
 

+ 1 - 1
misago/datamover/markup/quotes.py

@@ -11,7 +11,7 @@ def convert_quotes_to_bbcode(post):
     quote_author = None
     quote = []
 
-    for i, line in enumerate(post.splitlines() + ['']):
+    for line in post.splitlines() + ['']:
         if in_quote:
             if line.startswith('>'):
                 quote.append(line[1:].lstrip())

+ 0 - 1
misago/datamover/threads.py

@@ -4,7 +4,6 @@ from django.contrib.auth import get_user_model
 from django.utils import timezone
 
 from misago.categories.models import Category
-from misago.threads.checksums import update_post_checksum
 from misago.threads.models import Post, PostEdit, PostLike, Thread, ThreadParticipant
 
 from . import fetch_assoc, localise_datetime, markup, movedids

+ 1 - 1
misago/faker/management/commands/createfakebans.py

@@ -98,7 +98,7 @@ class Command(BaseCommand):
 
         created_count = 0
         show_progress(self, created_count, fake_bans_to_create)
-        for i in range(fake_bans_to_create):
+        for _ in range(fake_bans_to_create):
             ban = Ban(check_type=random.randint(Ban.USERNAME, Ban.IP))
             ban.banned_value = create_fake_test(fake, ban.check_type)
 

+ 0 - 2
misago/faker/management/commands/createfakecategories.py

@@ -1,11 +1,9 @@
 import random
-import sys
 import time
 
 from faker import Factory
 
 from django.core.management.base import BaseCommand
-from django.utils.six.moves import range
 
 from misago.acl import version as acl_version
 from misago.categories.models import Category, RoleCategoryACL

+ 3 - 5
misago/faker/management/commands/createfakethreads.py

@@ -47,8 +47,6 @@ class Command(BaseCommand):
 
         fake = Factory.create()
 
-        total_users = UserModel.objects.count()
-
         self.stdout.write('Creating fake threads...\n')
 
         message = '\nSuccessfully created %s fake threads in %s'
@@ -115,7 +113,7 @@ class Command(BaseCommand):
                 else:
                     thread_replies = random.randint(0, 10)
 
-                for x in range(thread_replies):
+                for _ in range(thread_replies):
                     datetime = timezone.now()
                     user = UserModel.objects.order_by('?')[:1][0]
 
@@ -168,7 +166,7 @@ class Command(BaseCommand):
 
         pinned_threads = random.randint(0, int(created_threads * 0.025)) or 1
         self.stdout.write('\nPinning %s threads...' % pinned_threads)
-        for i in range(0, pinned_threads):
+        for _ in range(0, pinned_threads):
             thread = Thread.objects.order_by('?')[:1][0]
             if random.randint(0, 100) > 75:
                 thread.weight = 2
@@ -193,7 +191,7 @@ class Command(BaseCommand):
         else:
             paragraphs_to_make = random.randint(1, 5)
 
-        for i in range(paragraphs_to_make):
+        for _ in range(paragraphs_to_make):
             if random.randint(0, 100) > 95:
                 cat_width = random.randint(1, 16) * random.choice([100, 90, 80])
                 cat_height = random.randint(1, 12) * random.choice([100, 90, 80])

+ 0 - 2
misago/faker/management/commands/createfakeusers.py

@@ -1,5 +1,4 @@
 import random
-import sys
 import time
 
 from faker import Factory
@@ -8,7 +7,6 @@ from django.contrib.auth import get_user_model
 from django.core.exceptions import ValidationError
 from django.core.management.base import BaseCommand
 from django.db import IntegrityError
-from django.utils.six.moves import range
 
 from misago.core.management.progressbar import show_progress
 from misago.users.avatars import dynamic, gallery

+ 1 - 1
misago/legal/migrations/0001_initial.py

@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
 from __future__ import unicode_literals
 
-from django.db import migrations, models
+from django.db import migrations
 
 from misago.conf.migrationutils import migrate_settings_group
 

+ 0 - 1
misago/markup/api.py

@@ -4,7 +4,6 @@ from rest_framework.response import Response
 
 from django.core.exceptions import ValidationError
 from django.utils import six
-from django.utils.translation import ugettext as _
 
 from misago.threads.validators import validate_post
 

+ 1 - 1
misago/markup/mentions.py

@@ -1,6 +1,6 @@
 import re
 
-from bs4 import BeautifulSoup, NavigableString
+from bs4 import BeautifulSoup
 
 from django.contrib.auth import get_user_model
 from django.utils import six

+ 0 - 1
misago/markup/parser.py

@@ -150,7 +150,6 @@ def linkify_paragraphs(result):
 
 def clean_links(request, result, force_shva=False):
     host = request.get_host()
-    site_address = '%s://%s' % (request.scheme, request.get_host())
 
     soup = BeautifulSoup(result['parsed_text'], 'html5lib')
     for link in soup.find_all('a'):

+ 1 - 1
misago/readtracker/apps.py

@@ -7,4 +7,4 @@ class MisagoReadTrackerConfig(AppConfig):
     verbose_name = "Misago Read Tracker"
 
     def ready(self):
-        from . import signals
+        from . import signals as _

+ 0 - 3
misago/readtracker/models.py

@@ -1,8 +1,5 @@
-from datetime import timedelta
-
 from django.conf import settings
 from django.db import models
-from django.utils import timezone
 
 
 class CategoryRead(models.Model):

+ 2 - 1
misago/readtracker/tests/test_readtracker.py

@@ -93,7 +93,8 @@ class CategoriesTrackerTests(ReadTrackerTests):
         categoriestracker.make_read_aware(self.user, self.categories)
         self.assertTrue(self.category.is_read)
 
-        thread = self.post_thread(self.user.joined_on + timedelta(days=1))
+        self.post_thread(self.user.joined_on + timedelta(days=1))
+
         categoriestracker.sync_record(self.user, self.category)
         categoriestracker.make_read_aware(self.user, self.categories)
         self.assertFalse(self.category.is_read)

+ 0 - 1
misago/threads/api/postendpoints/edits.py

@@ -2,7 +2,6 @@ from rest_framework.response import Response
 
 from django.core.exceptions import PermissionDenied
 from django.db.models import F
-from django.http import Http404
 from django.shortcuts import get_object_or_404
 from django.utils import timezone
 from django.utils.translation import ugettext as _

+ 0 - 2
misago/threads/api/postendpoints/split.py

@@ -1,4 +1,3 @@
-from rest_framework import serializers
 from rest_framework.response import Response
 
 from django.core.exceptions import PermissionDenied
@@ -6,7 +5,6 @@ from django.utils.translation import ugettext as _
 from django.utils.translation import ungettext
 
 from misago.conf import settings
-from misago.threads.events import record_event
 from misago.threads.models import Thread
 from misago.threads.moderation import threads as moderation
 from misago.threads.permissions import exclude_invisible_posts

+ 1 - 1
misago/threads/api/postingendpoint/attachments.py

@@ -7,7 +7,7 @@ from misago.acl import add_acl
 from misago.conf import settings
 from misago.threads.serializers import AttachmentSerializer
 
-from . import PostingEndpoint, PostingInterrupt, PostingMiddleware
+from . import PostingEndpoint, PostingMiddleware
 
 
 class AttachmentsMiddleware(PostingMiddleware):

+ 0 - 2
misago/threads/api/postingendpoint/reply.py

@@ -1,9 +1,7 @@
 from rest_framework import serializers
 
-from django.db.models import F
 from django.utils.translation import ugettext_lazy
 
-from misago.conf import settings
 from misago.markup import common_flavour
 from misago.threads.checksums import update_post_checksum
 from misago.threads.validators import validate_post, validate_title

+ 1 - 2
misago/threads/api/postingendpoint/subscribe.py

@@ -37,8 +37,7 @@ class SubscribeMiddleware(PostingMiddleware):
             return
 
         try:
-            subscription = self.user.subscription_set.get(thread=self.thread)
-            return
+            return self.user.subscription_set.get(thread=self.thread)
         except Subscription.DoesNotExist:
             pass
 

+ 1 - 1
misago/threads/api/threadendpoints/merge.py

@@ -1,6 +1,6 @@
 from rest_framework.response import Response
 
-from django.core.exceptions import PermissionDenied, ValidationError
+from django.core.exceptions import PermissionDenied
 from django.http import Http404
 from django.utils.translation import gettext as _
 from django.utils.translation import ungettext

+ 2 - 3
misago/threads/api/threadendpoints/patch.py

@@ -10,7 +10,6 @@ from misago.categories.permissions import allow_browse_category, allow_see_categ
 from misago.categories.serializers import CategorySerializer
 from misago.core.apipatch import ApiPatch
 from misago.core.shortcuts import get_int_or_404
-from misago.threads.models import ThreadParticipant
 from misago.threads.moderation import threads as moderation
 from misago.threads.participants import (
     add_participant, change_owner, make_participants_aware, remove_participant)
@@ -122,7 +121,7 @@ def patch_flatten_categories(request, thread, value):
             'category': thread.category_id,
             'top_category': thread.top_category.pk,
         }
-    except AttributeError as e:
+    except AttributeError:
         return {
             'category': thread.category_id,
             'top_category': None
@@ -299,7 +298,7 @@ def thread_patch_endpoint(request, thread):
     unapproved_changed = old_is_unapproved != thread.is_unapproved
     category_changed = old_category != thread.category
 
-    title_changed = old_is_hidden != thread.is_hidden
+    title_changed = old_title != thread.title
     if thread.category.last_thread_id != thread.pk:
         title_changed = False # don't trigger resync on simple title change
 

+ 1 - 1
misago/threads/apps.py

@@ -7,4 +7,4 @@ class MisagoThreadsConfig(AppConfig):
     verbose_name = "Misago Threads"
 
     def ready(self):
-        from . import signals
+        from . import signals as _

+ 1 - 1
misago/threads/forms.py

@@ -1,7 +1,7 @@
 from django import forms
 from django.utils.translation import ugettext as _
 
-from .models import Attachment, AttachmentType
+from .models import AttachmentType
 
 
 def get_searchable_filetypes():

+ 0 - 1
misago/threads/middleware.py

@@ -3,7 +3,6 @@ from django.utils.deprecation import MiddlewareMixin
 from misago.categories.models import Category
 
 from .models import Thread
-from .permissions import exclude_invisible_threads
 from .viewmodels import filter_read_threads_queryset
 
 

+ 1 - 1
misago/threads/migrations/0001_initial.py

@@ -9,7 +9,7 @@ from django.contrib.postgres.search import SearchVectorField
 from django.db import migrations, models
 
 import misago.threads.models.attachment
-from misago.core.pgutils import CreatePartialCompositeIndex, CreatePartialIndex
+from misago.core.pgutils import CreatePartialIndex
 
 
 class Migration(migrations.Migration):

+ 1 - 2
misago/threads/migrations/0002_threads_settings.py

@@ -1,8 +1,7 @@
 # -*- coding: utf-8 -*-
 from __future__ import unicode_literals
 
-from django.conf import settings
-from django.db import migrations, models
+from django.db import migrations
 
 from misago.conf.migrationutils import migrate_settings_group
 

+ 1 - 2
misago/threads/migrations/0003_attachment_types.py

@@ -2,8 +2,7 @@
 # Generated by Django 1.9.7 on 2016-10-04 21:41
 from __future__ import unicode_literals
 
-from django.conf import settings
-from django.db import migrations, models
+from django.db import migrations
 
 
 ATTACHMENTS = (

+ 3 - 3
misago/threads/models/post.py

@@ -5,15 +5,12 @@ import copy
 from django.contrib.postgres.fields import JSONField
 from django.contrib.postgres.search import SearchVector, SearchVectorField
 from django.db import models
-from django.dispatch import receiver
-from django.urls import reverse
 from django.utils import six, timezone
 from django.utils.encoding import python_2_unicode_compatible
 
 from misago.conf import settings
 from misago.core.utils import parse_iso8601_string
 from misago.markup import finalise_markup
-from misago.threads import threadtypes
 from misago.threads.checksums import is_post_valid, update_post_checksum
 
 
@@ -100,6 +97,9 @@ class Post(models.Model):
         super(Post, self).delete(*args, **kwargs)
 
     def merge(self, other_post):
+        if not self.poster_id or self.poster_id != other_post.poster_id:
+            raise ValueError("post can't be merged with other user's post")
+
         if self.thread_id != other_post.thread_id:
             raise ValueError("only posts belonging to same thread can be merged")
 

+ 1 - 1
misago/threads/models/thread.py

@@ -1,5 +1,5 @@
 from django.core.exceptions import ObjectDoesNotExist
-from django.db import models, transaction
+from django.db import models
 from django.utils.encoding import python_2_unicode_compatible
 from django.utils.translation import ugettext_lazy as _
 

+ 0 - 1
misago/threads/moderation/threads.py

@@ -1,6 +1,5 @@
 from django.db.transaction import atomic
 from django.utils import timezone
-from django.utils.translation import ugettext as _
 
 from misago.threads.events import record_event
 

+ 0 - 3
misago/threads/paginator.py

@@ -1,7 +1,4 @@
-from math import ceil, floor
-
 from django.core.paginator import Paginator
-from django.utils.functional import cached_property
 
 
 class PostsPaginator(Paginator):

+ 0 - 1
misago/threads/participants.py

@@ -1,7 +1,6 @@
 from django.contrib.auth import get_user_model
 from django.utils.translation import ugettext as _
 
-from misago.core import deprecations
 from misago.core.mail import build_mail, send_messages
 
 from .events import record_event

+ 1 - 3
misago/threads/permissions/privatethreads.py

@@ -1,11 +1,9 @@
 from django import forms
-from django.contrib.auth import get_user_model
 from django.core.exceptions import PermissionDenied
-from django.db.models import Q
 from django.http import Http404
 from django.utils.translation import ugettext_lazy as _
 
-from misago.acl import add_acl, algebra
+from misago.acl import algebra
 from misago.acl.decorators import return_boolean
 from misago.acl.models import Role
 from misago.categories import PRIVATE_THREADS_ROOT_NAME

+ 1 - 1
misago/threads/permissions/threads.py

@@ -9,7 +9,7 @@ from django.utils.translation import ungettext
 from misago.acl import add_acl, algebra
 from misago.acl.decorators import return_boolean
 from misago.acl.models import Role
-from misago.categories.models import Category, CategoryRole, RoleCategoryACL
+from misago.categories.models import Category, CategoryRole
 from misago.categories.permissions import get_categories_roles
 from misago.core.forms import YesNoSwitch
 from misago.threads.models import Post, Thread

+ 0 - 1
misago/threads/serializers/attachment.py

@@ -2,7 +2,6 @@ from rest_framework import serializers
 
 from django.urls import reverse
 
-from misago.core.utils import format_plaintext_for_html
 from misago.threads.models import Attachment
 
 

+ 0 - 2
misago/threads/serializers/feed.py

@@ -1,7 +1,5 @@
 from rest_framework import serializers
 
-from django.urls import reverse
-
 from misago.categories.serializers import CategorySerializer
 from misago.core.serializers import MutableFields
 from misago.threads.models import Post

+ 0 - 1
misago/threads/serializers/post.py

@@ -2,7 +2,6 @@ from rest_framework import serializers
 
 from django.urls import reverse
 
-from misago.categories.serializers import CategorySerializer
 from misago.core.serializers import MutableFields
 from misago.threads.models import Post
 from misago.users.serializers import UserSerializer as BaseUserSerializer

+ 0 - 2
misago/threads/templatetags/misago_poststags.py

@@ -4,8 +4,6 @@ from django import template
 from django.utils.translation import gettext as _
 from django.utils.translation import ngettext
 
-from misago.conf import settings
-
 
 register = template.Library()
 

+ 7 - 7
misago/threads/tests/test_attachments_api.py

@@ -104,7 +104,7 @@ class AttachmentsApiTestCase(AuthenticatedUserTestCase):
 
     def test_type_is_locked(self):
         """new uploads for this filetype are locked"""
-        attachment_type = AttachmentType.objects.create(
+        AttachmentType.objects.create(
             name="Test extension",
             extensions='png',
             mimetypes='application/pdf',
@@ -119,7 +119,7 @@ class AttachmentsApiTestCase(AuthenticatedUserTestCase):
 
     def test_type_is_disabled(self):
         """new uploads for this filetype are disabled"""
-        attachment_type = AttachmentType.objects.create(
+        AttachmentType.objects.create(
             name="Test extension",
             extensions='png',
             mimetypes='application/pdf',
@@ -168,7 +168,7 @@ class AttachmentsApiTestCase(AuthenticatedUserTestCase):
 
     def test_corrupted_image_upload(self):
         """corrupted image upload is handled"""
-        attachment_type = AttachmentType.objects.create(
+        AttachmentType.objects.create(
             name="Test extension",
             extensions='gif'
         )
@@ -181,7 +181,7 @@ class AttachmentsApiTestCase(AuthenticatedUserTestCase):
 
     def test_document_upload(self):
         """successful upload creates orphan attachment"""
-        attachment_type = AttachmentType.objects.create(
+        AttachmentType.objects.create(
             name="Test extension",
             extensions='pdf',
             mimetypes='application/pdf'
@@ -220,7 +220,7 @@ class AttachmentsApiTestCase(AuthenticatedUserTestCase):
 
     def test_small_image_upload(self):
         """successful small image upload creates orphan attachment without thumbnail"""
-        attachment_type = AttachmentType.objects.create(
+        AttachmentType.objects.create(
             name="Test extension",
             extensions='jpeg,jpg',
             mimetypes='image/jpeg'
@@ -257,7 +257,7 @@ class AttachmentsApiTestCase(AuthenticatedUserTestCase):
             'max_attachment_size': 10 * 1024
         })
 
-        attachment_type = AttachmentType.objects.create(
+        AttachmentType.objects.create(
             name="Test extension",
             extensions='png',
             mimetypes='image/png'
@@ -308,7 +308,7 @@ class AttachmentsApiTestCase(AuthenticatedUserTestCase):
 
     def test_animated_image_upload(self):
         """successful gif upload creates orphan attachment with thumbnail"""
-        attachment_type = AttachmentType.objects.create(
+        AttachmentType.objects.create(
             name="Test extension",
             extensions='gif',
             mimetypes='image/gif'

+ 3 - 3
misago/threads/tests/test_clearattachments.py

@@ -31,7 +31,7 @@ class ClearAttachmentsTests(TestCase):
         cutoff = timezone.now() - timedelta(minutes=settings.MISAGO_ATTACHMENT_ORPHANED_EXPIRE)
         cutoff -= timedelta(minutes=5)
 
-        for i in range(5):
+        for _ in range(5):
             Attachment.objects.create(
                 secret=Attachment.generate_new_secret(),
                 filetype=filetype,
@@ -47,7 +47,7 @@ class ClearAttachmentsTests(TestCase):
         category = Category.objects.get(slug='first-category')
         post = testutils.post_thread(category).first_post
 
-        for i in range(5):
+        for _ in range(5):
             Attachment.objects.create(
                 secret=Attachment.generate_new_secret(),
                 filetype=filetype,
@@ -61,7 +61,7 @@ class ClearAttachmentsTests(TestCase):
             )
 
         # create 5 fresh orphaned attachments
-        for i in range(5):
+        for _ in range(5):
             Attachment.objects.create(
                 secret=Attachment.generate_new_secret(),
                 filetype=filetype,

+ 1 - 4
misago/threads/tests/test_events.py

@@ -1,6 +1,4 @@
 #-*- coding: utf-8 -*-
-import random
-
 from django.contrib.auth import get_user_model
 from django.test import TestCase
 from django.utils import timezone
@@ -8,8 +6,7 @@ from django.utils import timezone
 from misago.acl import add_acl
 from misago.categories.models import Category
 from misago.threads.events import record_event
-from misago.threads.models import Post, Thread
-from misago.threads.testutils import reply_thread
+from misago.threads.models import Thread
 
 
 UserModel = get_user_model()

+ 1 - 2
misago/threads/tests/test_floodprotection_middleware.py

@@ -4,8 +4,7 @@ from django.utils import timezone
 
 from misago.acl.testutils import override_acl
 from misago.threads.api.postingendpoint import PostingInterrupt
-from misago.threads.api.postingendpoint.floodprotection import (
-    MIN_POSTING_PAUSE, FloodProtectionMiddleware)
+from misago.threads.api.postingendpoint.floodprotection import FloodProtectionMiddleware
 from misago.users.testutils import AuthenticatedUserTestCase
 
 

+ 12 - 12
misago/threads/tests/test_gotoviews.py

@@ -32,7 +32,7 @@ class GotoPostTests(GotoViewTestCase):
 
     def test_goto_last_post_on_page(self):
         """last post on page redirect url is valid"""
-        for i in range(settings.MISAGO_POSTS_PER_PAGE + settings.MISAGO_POSTS_TAIL - 1):
+        for _ in range(settings.MISAGO_POSTS_PER_PAGE + settings.MISAGO_POSTS_TAIL - 1):
             post = testutils.reply_thread(self.thread)
 
         response = self.client.get(post.get_absolute_url())
@@ -44,7 +44,7 @@ class GotoPostTests(GotoViewTestCase):
 
     def test_goto_first_post_on_next_page(self):
         """first post on next page redirect url is valid"""
-        for i in range(settings.MISAGO_POSTS_PER_PAGE + settings.MISAGO_POSTS_TAIL):
+        for _ in range(settings.MISAGO_POSTS_PER_PAGE + settings.MISAGO_POSTS_TAIL):
             post = testutils.reply_thread(self.thread)
 
         response = self.client.get(post.get_absolute_url())
@@ -57,7 +57,7 @@ class GotoPostTests(GotoViewTestCase):
     def test_goto_first_post_on_page_three_out_of_five(self):
         """first post on next page redirect url is valid"""
         posts = []
-        for i in range(settings.MISAGO_POSTS_PER_PAGE * 4 - 1):
+        for _ in range(settings.MISAGO_POSTS_PER_PAGE * 4 - 1):
             post = testutils.reply_thread(self.thread)
             posts.append(post)
 
@@ -73,7 +73,7 @@ class GotoPostTests(GotoViewTestCase):
     def test_goto_first_event_on_page_three_out_of_five(self):
         """event redirect url is valid"""
         posts = []
-        for i in range(settings.MISAGO_POSTS_PER_PAGE * 4 - 1):
+        for _ in range(settings.MISAGO_POSTS_PER_PAGE * 4 - 1):
             post = testutils.reply_thread(self.thread)
             posts.append(post)
 
@@ -105,7 +105,7 @@ class GotoLastTests(GotoViewTestCase):
 
     def test_goto_last_post_on_page(self):
         """last post on page redirect url is valid"""
-        for i in range(settings.MISAGO_POSTS_PER_PAGE + settings.MISAGO_POSTS_TAIL - 1):
+        for _ in range(settings.MISAGO_POSTS_PER_PAGE + settings.MISAGO_POSTS_TAIL - 1):
             post = testutils.reply_thread(self.thread)
 
         response = self.client.get(self.thread.get_last_post_url())
@@ -129,7 +129,7 @@ class GotoNewTests(GotoViewTestCase):
         read_thread(self.user, self.thread, self.thread.last_post)
 
         post = testutils.reply_thread(self.thread, posted_on=timezone.now())
-        for i in range(settings.MISAGO_POSTS_PER_PAGE + settings.MISAGO_POSTS_TAIL - 1):
+        for _ in range(settings.MISAGO_POSTS_PER_PAGE + settings.MISAGO_POSTS_TAIL - 1):
             testutils.reply_thread(self.thread, posted_on=timezone.now())
 
         response = self.client.get(self.thread.get_new_post_url())
@@ -138,14 +138,14 @@ class GotoNewTests(GotoViewTestCase):
 
     def test_goto_first_new_post_on_next_page(self):
         """first unread post redirect url in already read multipage thread is valid"""
-        for i in range(settings.MISAGO_POSTS_PER_PAGE + settings.MISAGO_POSTS_TAIL):
+        for _ in range(settings.MISAGO_POSTS_PER_PAGE + settings.MISAGO_POSTS_TAIL):
             testutils.reply_thread(self.thread, posted_on=timezone.now())
 
         make_thread_read_aware(self.user, self.thread)
         read_thread(self.user, self.thread, self.thread.last_post)
 
         post = testutils.reply_thread(self.thread, posted_on=timezone.now())
-        for i in range(settings.MISAGO_POSTS_PER_PAGE + settings.MISAGO_POSTS_TAIL - 1):
+        for _ in range(settings.MISAGO_POSTS_PER_PAGE + settings.MISAGO_POSTS_TAIL - 1):
             testutils.reply_thread(self.thread, posted_on=timezone.now())
 
         response = self.client.get(self.thread.get_new_post_url())
@@ -154,7 +154,7 @@ class GotoNewTests(GotoViewTestCase):
 
     def test_goto_first_new_post_in_read_thread(self):
         """goto new in read thread points to last post"""
-        for i in range(settings.MISAGO_POSTS_PER_PAGE + settings.MISAGO_POSTS_TAIL):
+        for _ in range(settings.MISAGO_POSTS_PER_PAGE + settings.MISAGO_POSTS_TAIL):
             post = testutils.reply_thread(self.thread, posted_on=timezone.now())
 
         make_thread_read_aware(self.user, self.thread)
@@ -166,7 +166,7 @@ class GotoNewTests(GotoViewTestCase):
 
     def test_guest_goto_first_new_post_in_thread(self):
         """guest goto new in read thread points to last post"""
-        for i in range(settings.MISAGO_POSTS_PER_PAGE + settings.MISAGO_POSTS_TAIL):
+        for _ in range(settings.MISAGO_POSTS_PER_PAGE + settings.MISAGO_POSTS_TAIL):
             post = testutils.reply_thread(self.thread, posted_on=timezone.now())
 
         self.logout_user()
@@ -201,14 +201,14 @@ class GotoUnapprovedTests(GotoViewTestCase):
 
     def test_vie_handles_unapproved_posts(self):
         """if thread has unapproved posts, redirect to first of them"""
-        for i in range(settings.MISAGO_POSTS_PER_PAGE + settings.MISAGO_POSTS_TAIL):
+        for _ in range(settings.MISAGO_POSTS_PER_PAGE + settings.MISAGO_POSTS_TAIL):
             testutils.reply_thread(self.thread, posted_on=timezone.now())
 
         make_thread_read_aware(self.user, self.thread)
         read_thread(self.user, self.thread, self.thread.last_post)
 
         post = testutils.reply_thread(self.thread, is_unapproved=True, posted_on=timezone.now())
-        for i in range(settings.MISAGO_POSTS_PER_PAGE + settings.MISAGO_POSTS_TAIL - 1):
+        for _ in range(settings.MISAGO_POSTS_PER_PAGE + settings.MISAGO_POSTS_TAIL - 1):
             testutils.reply_thread(self.thread, posted_on=timezone.now())
 
         self.grant_permission()

+ 17 - 2
misago/threads/tests/test_post_model.py

@@ -70,6 +70,21 @@ class PostModelTests(TestCase):
             last_poster_slug='tester'
         )
 
+        # can't merge with other users posts
+        with self.assertRaises(ValueError):
+            self.post.merge(Post.objects.create(
+                category=self.category,
+                thread=self.thread,
+                poster=other_user,
+                poster_name=other_user.username,
+                poster_ip='127.0.0.1',
+                original="Hello! I am test message!",
+                parsed="<p>Hello! I am test message!</p>",
+                checksum="nope",
+                posted_on=timezone.now() + timedelta(minutes=5),
+                updated_on=timezone.now() + timedelta(minutes=5),
+            ))
+
         # can't merge across threads
         with self.assertRaises(ValueError):
             self.post.merge(Post.objects.create(
@@ -82,7 +97,7 @@ class PostModelTests(TestCase):
                 parsed="<p>Hello! I am test message!</p>",
                 checksum="nope",
                 posted_on=timezone.now() + timedelta(minutes=5),
-                updated_on=timezone.now() + timedelta(minutes=5)
+                updated_on=timezone.now() + timedelta(minutes=5),
             ))
 
         # can't merge with events
@@ -98,7 +113,7 @@ class PostModelTests(TestCase):
                 checksum="nope",
                 posted_on=timezone.now() + timedelta(minutes=5),
                 updated_on=timezone.now() + timedelta(minutes=5),
-                is_event=True
+                is_event=True,
             ))
 
     def test_merge(self):

+ 3 - 3
misago/threads/tests/test_privatethread_patch_api.py

@@ -131,7 +131,7 @@ class PrivateThreadAddParticipantApiTests(PrivateThreadPatchApiTestCase):
         """adding user to thread add user to thread as participant, sets event and emails him"""
         ThreadParticipant.objects.set_owner(self.thread, self.user)
 
-        response = self.patch(self.api_link, [
+        self.patch(self.api_link, [
             {'op': 'add', 'path': 'participants', 'value': self.other_user.username}
         ])
 
@@ -158,7 +158,7 @@ class PrivateThreadAddParticipantApiTests(PrivateThreadPatchApiTestCase):
             'can_moderate_private_threads': 1
         })
 
-        response = self.patch(self.api_link, [
+        self.patch(self.api_link, [
             {'op': 'add', 'path': 'participants', 'value': self.user.username}
         ])
 
@@ -181,7 +181,7 @@ class PrivateThreadAddParticipantApiTests(PrivateThreadPatchApiTestCase):
             'can_moderate_private_threads': 1
         })
 
-        response = self.patch(self.api_link, [
+        self.patch(self.api_link, [
             {'op': 'add', 'path': 'participants', 'value': self.other_user.username}
         ])
 

+ 0 - 1
misago/threads/tests/test_privatethread_reply_api.py

@@ -1,6 +1,5 @@
 from django.contrib.auth import get_user_model
 
-from misago.acl.testutils import override_acl
 from misago.threads import testutils
 from misago.threads.models import ThreadParticipant
 

+ 1 - 3
misago/threads/tests/test_privatethread_start_api.py

@@ -1,8 +1,6 @@
 # -*- coding: utf-8 -*-
 from __future__ import unicode_literals
 
-import json
-
 from django.contrib.auth import get_user_model
 from django.core import mail
 from django.urls import reverse
@@ -10,7 +8,7 @@ from django.utils.encoding import smart_str
 
 from misago.acl.testutils import override_acl
 from misago.categories.models import Category
-from misago.threads.models import Thread, ThreadParticipant
+from misago.threads.models import ThreadParticipant
 from misago.users.testutils import AuthenticatedUserTestCase
 
 

+ 3 - 1
misago/threads/tests/test_privatethreads_api.py

@@ -40,9 +40,11 @@ class PrivateThreadsListApiTests(PrivateThreadsTestCase):
     def test_thread_visibility(self):
         """only participated threads are returned by private threads api"""
         visible = testutils.post_thread(category=self.category, poster=self.user)
-        hidden = testutils.post_thread(category=self.category, poster=self.user)
         reported = testutils.post_thread(category=self.category, poster=self.user)
 
+        # hidden thread
+        testutils.post_thread(category=self.category, poster=self.user)
+
         ThreadParticipant.objects.add_participants(visible, [self.user])
 
         reported.has_reported_posts = True

+ 3 - 1
misago/threads/tests/test_privatethreads_lists.py

@@ -38,9 +38,11 @@ class PrivateThreadsListTests(PrivateThreadsTestCase):
     def test_thread_visibility(self):
         """only participated threads are returned by private threads view"""
         visible = testutils.post_thread(category=self.category, poster=self.user)
-        hidden = testutils.post_thread(category=self.category, poster=self.user)
         reported = testutils.post_thread(category=self.category, poster=self.user)
 
+        # post hidden thread
+        testutils.post_thread(category=self.category, poster=self.user)
+
         ThreadParticipant.objects.add_participants(visible, [self.user])
 
         reported.has_reported_posts = True

+ 1 - 13
misago/threads/tests/test_subscriptions.py

@@ -37,7 +37,7 @@ class SubscriptionsTests(TestCase):
     def test_anon_threads_subscription(self):
         """make multiple threads list sub aware for anon"""
         threads = []
-        for i in range(10):
+        for _ in range(10):
             threads.append(
                 self.post_thread(timezone.now() - timedelta(days=10)))
 
@@ -51,18 +51,6 @@ class SubscriptionsTests(TestCase):
         make_subscription_aware(self.user, self.thread)
         self.assertIsNone(self.thread.subscription)
 
-    def test_threads_no_subscription(self):
-        """make mulitple threads sub aware for authenticated"""
-        threads = []
-        for i in range(10):
-            threads.append(
-                self.post_thread(timezone.now() - timedelta(days=10)))
-
-        make_subscription_aware(self.user, threads)
-
-        for thread in threads:
-            self.assertIsNone(thread.subscription)
-
     def test_subscribed_thread(self):
         """make thread sub aware for authenticated"""
         self.user.subscription_set.create(

+ 2 - 2
misago/threads/tests/test_synchronizethreads.py

@@ -23,9 +23,9 @@ class SynchronizeThreadsTests(TestCase):
         """command synchronizes threads"""
         category = Category.objects.all_categories()[:1][0]
 
-        threads = [testutils.post_thread(category) for t in range(10)]
+        threads = [testutils.post_thread(category) for _ in range(10)]
         for i, thread in enumerate(threads):
-            [testutils.reply_thread(thread) for r in range(i)]
+            [testutils.reply_thread(thread) for _ in range(i)]
             thread.replies = 0
             thread.save()
 

+ 1 - 6
misago/threads/tests/test_thread_editreply_api.py

@@ -10,7 +10,6 @@ from django.utils.encoding import smart_str
 from misago.acl.testutils import override_acl
 from misago.categories.models import Category
 from misago.threads import testutils
-from misago.threads.models import Thread
 from misago.users.testutils import AuthenticatedUserTestCase
 
 
@@ -197,8 +196,6 @@ class EditReplyTests(AuthenticatedUserTestCase):
         })
         self.assertEqual(response.status_code, 200)
 
-        thread = Thread.objects.get(pk=self.thread.pk)
-
         self.override_acl()
         response = self.client.get(self.thread.get_absolute_url())
         self.assertContains(response, self.post.parsed)
@@ -222,8 +219,6 @@ class EditReplyTests(AuthenticatedUserTestCase):
         })
         self.assertEqual(response.status_code, 200)
 
-        thread = Thread.objects.get(pk=self.thread.pk)
-
         self.override_acl()
         response = self.client.get(self.thread.get_absolute_url())
         self.assertContains(response, "<p>This is test edit!</p>")
@@ -262,7 +257,7 @@ class EditReplyTests(AuthenticatedUserTestCase):
             'pk': self.thread.first_post.pk
         })
 
-        response = self.put(self.api_link, data={
+        response = self.put(api_link, data={
             'post': "This is test edit!"
         })
         self.assertEqual(response.status_code, 200)

+ 5 - 7
misago/threads/tests/test_thread_merge_api.py

@@ -1,7 +1,4 @@
-import json
-
 from django.urls import reverse
-from django.utils.encoding import smart_str
 
 from misago.acl.testutils import override_acl
 from misago.categories.models import Category
@@ -284,8 +281,9 @@ class ThreadMergeApiTests(ThreadsApiTestCase):
         })
 
         other_thread = testutils.post_thread(self.category_b)
-        poll = testutils.post_poll(self.thread, self.user)
-        other_poll = testutils.post_poll(other_thread, self.user)
+
+        testutils.post_poll(self.thread, self.user)
+        testutils.post_poll(other_thread, self.user)
 
         response = self.client.post(self.api_link, {
             'thread_url': other_thread.get_absolute_url(),
@@ -311,8 +309,8 @@ class ThreadMergeApiTests(ThreadsApiTestCase):
         })
 
         other_thread = testutils.post_thread(self.category_b)
-        poll = testutils.post_poll(self.thread, self.user)
-        other_poll = testutils.post_poll(other_thread, self.user)
+        testutils.post_poll(self.thread, self.user)
+        testutils.post_poll(other_thread, self.user)
 
         response = self.client.post(self.api_link, {
             'thread_url': other_thread.get_absolute_url(),

+ 0 - 6
misago/threads/tests/test_thread_postedits_api.py

@@ -1,15 +1,9 @@
 # -*- coding: utf-8 -*-
 from __future__ import unicode_literals
 
-import json
-from datetime import timedelta
-
 from django.urls import reverse
 
-from misago.acl.testutils import override_acl
-from misago.categories.models import Category
 from misago.threads import testutils
-from misago.threads.models import Post
 
 from .test_threads_api import ThreadsApiTestCase
 

+ 6 - 9
misago/threads/tests/test_thread_postmerge_api.py

@@ -2,11 +2,8 @@
 from __future__ import unicode_literals
 
 import json
-from datetime import timedelta
 
 from django.urls import reverse
-from django.utils import timezone
-from django.utils.encoding import smart_str
 from django.utils.six.moves import range
 
 from misago.acl.testutils import override_acl
@@ -223,8 +220,8 @@ class ThreadPostMergeApiTestCase(AuthenticatedUserTestCase):
 
     def test_merge_posts(self):
         """api merges two posts"""
-        post_a = testutils.reply_thread(self.thread, poster="Bob", message="Battęry")
-        post_b = testutils.reply_thread(self.thread, poster="Bob", message="Hórse")
+        post_a = testutils.reply_thread(self.thread, poster=self.user, message="Battęry")
+        post_b = testutils.reply_thread(self.thread, poster=self.user, message="Hórse")
 
         thread_replies = self.thread.replies
 
@@ -250,8 +247,8 @@ class ThreadPostMergeApiTestCase(AuthenticatedUserTestCase):
 
         response = self.client.post(self.api_link, json.dumps({
             'posts': [
-                testutils.reply_thread(self.thread, poster="Bob", is_hidden=True).pk,
-                testutils.reply_thread(self.thread, poster="Bob", is_hidden=True).pk
+                testutils.reply_thread(self.thread, poster=self.user, is_hidden=True).pk,
+                testutils.reply_thread(self.thread, poster=self.user, is_hidden=True).pk
             ]
         }), content_type="application/json")
         self.assertEqual(response.status_code, 200)
@@ -264,8 +261,8 @@ class ThreadPostMergeApiTestCase(AuthenticatedUserTestCase):
 
         response = self.client.post(self.api_link, json.dumps({
             'posts': [
-                testutils.reply_thread(self.thread, poster="Bob", is_unapproved=True).pk,
-                testutils.reply_thread(self.thread, poster="Bob", is_unapproved=True).pk
+                testutils.reply_thread(self.thread, poster=self.user, is_unapproved=True).pk,
+                testutils.reply_thread(self.thread, poster=self.user, is_unapproved=True).pk
             ]
         }), content_type="application/json")
         self.assertEqual(response.status_code, 200)

+ 1 - 1
misago/threads/tests/test_thread_postpatch_api.py

@@ -10,7 +10,7 @@ from django.utils import timezone
 from misago.acl.testutils import override_acl
 from misago.categories.models import Category
 from misago.threads import testutils
-from misago.threads.models import Post, Thread
+from misago.threads.models import Post
 from misago.users.testutils import AuthenticatedUserTestCase
 
 

+ 0 - 1
misago/threads/tests/test_thread_postread_api.py

@@ -2,7 +2,6 @@ from django.urls import reverse
 from django.utils import timezone
 
 from misago.threads import testutils
-from misago.threads.models import Post, Thread
 
 from .test_threads_api import ThreadsApiTestCase
 

+ 0 - 5
misago/threads/tests/test_thread_start_api.py

@@ -4,10 +4,7 @@ from __future__ import unicode_literals
 from django.urls import reverse
 
 from misago.acl.testutils import override_acl
-from misago.categories import THREADS_ROOT_NAME
 from misago.categories.models import Category
-from misago.threads.models import Thread
-from misago.threads.threadtypes import trees_map
 from misago.users.testutils import AuthenticatedUserTestCase
 
 
@@ -15,8 +12,6 @@ class StartThreadTests(AuthenticatedUserTestCase):
     def setUp(self):
         super(StartThreadTests, self).setUp()
 
-        threads_tree_id = trees_map.get_tree_id_for_root(THREADS_ROOT_NAME)
-
         self.category = Category.objects.get(slug='first-category')
         self.api_link = reverse('misago:api:thread-list')
 

+ 1 - 2
misago/threads/tests/test_threadparticipant_model.py

@@ -91,5 +91,4 @@ class ThreadParticipantTests(TestCase):
         self.assertEqual(self.thread.participants.count(), 1)
 
         with self.assertRaises(ThreadParticipant.DoesNotExist):
-            participant = ThreadParticipant.objects.get(
-                thread=self.thread, user=user)
+            ThreadParticipant.objects.get(thread=self.thread, user=user)

+ 1 - 1
misago/threads/tests/test_threads_editor_api.py

@@ -625,7 +625,7 @@ class EditReplyEditorApiTests(EditorApiTestCase):
 
     def test_edit(self):
         """endpoint returns valid configuration for editor"""
-        for i in range(3):
+        for _ in range(3):
             self.override_acl({
                 'max_attachment_size': 1000,
             })

+ 8 - 7
misago/threads/tests/test_threads_merge_api.py

@@ -4,7 +4,6 @@ from django.urls import reverse
 from django.utils.six.moves import range
 
 from misago.acl import add_acl
-from misago.acl.testutils import override_acl
 from misago.categories.models import Category
 from misago.threads import testutils
 from misago.threads.api.threadendpoints.merge import MERGE_LIMIT
@@ -83,7 +82,7 @@ class ThreadsMergeApiTests(ThreadsApiTestCase):
 
     def test_merge_with_nonexisting_thread(self):
         """api validates if we are trying to merge with invalid thread"""
-        unaccesible_thread = testutils.post_thread(category=self.category_b)
+        testutils.post_thread(category=self.category_b)
 
         response = self.client.post(self.api_link, json.dumps({
             'threads': [self.thread.id, self.thread.id + 1000]
@@ -139,7 +138,7 @@ class ThreadsMergeApiTests(ThreadsApiTestCase):
     def test_merge_too_many_threads(self):
         """api rejects too many threads to merge"""
         threads = []
-        for i in range(MERGE_LIMIT + 1):
+        for _ in range(MERGE_LIMIT + 1):
             threads.append(testutils.post_thread(category=self.category).pk)
 
         self.override_acl({
@@ -699,8 +698,9 @@ class ThreadsMergeApiTests(ThreadsApiTestCase):
         })
 
         other_thread = testutils.post_thread(self.category)
-        poll = testutils.post_poll(self.thread, self.user)
-        other_poll = testutils.post_poll(other_thread, self.user)
+
+        testutils.post_poll(self.thread, self.user)
+        testutils.post_poll(other_thread, self.user)
 
         response = self.client.post(self.api_link, json.dumps({
             'threads': [self.thread.id, other_thread.id],
@@ -725,8 +725,9 @@ class ThreadsMergeApiTests(ThreadsApiTestCase):
         })
 
         other_thread = testutils.post_thread(self.category)
-        poll = testutils.post_poll(self.thread, self.user)
-        other_poll = testutils.post_poll(other_thread, self.user)
+
+        testutils.post_poll(self.thread, self.user)
+        testutils.post_poll(other_thread, self.user)
 
         response = self.client.post(self.api_link, json.dumps({
             'threads': [self.thread.id, other_thread.id],

+ 1 - 1
misago/threads/tests/test_threads_moderation.py

@@ -1,6 +1,6 @@
 from misago.categories.models import Category
 from misago.threads import moderation, testutils
-from misago.threads.models import Post, Thread
+from misago.threads.models import Thread
 from misago.users.testutils import AuthenticatedUserTestCase
 
 

+ 6 - 6
misago/threads/tests/test_threadslists.py

@@ -9,9 +9,7 @@ from django.utils.six.moves import range
 from misago.acl.testutils import override_acl
 from misago.categories.models import Category
 from misago.conf import settings
-from misago.core import threadstore
-from misago.core.cache import cache
-from misago.readtracker import categoriestracker, threadstracker
+from misago.readtracker import threadstracker
 from misago.threads import testutils
 from misago.users.models import AnonymousUser
 from misago.users.testutils import AuthenticatedUserTestCase
@@ -374,7 +372,7 @@ class AllThreadsListTests(ThreadsListTestCase):
     def test_noscript_pagination(self):
         """threads list is paginated for users with js disabled"""
         threads = []
-        for i in range(settings.MISAGO_THREADS_PER_PAGE * 3):
+        for _ in range(settings.MISAGO_THREADS_PER_PAGE * 3):
             threads.append(testutils.post_thread(
                 category=self.first_category
             ))
@@ -592,8 +590,8 @@ class ThreadsVisibilityTests(ThreadsListTestCase):
             name='Hidden Category',
             slug='hidden-category',
         ).insert_at(self.root, position='last-child', save=True)
-        test_category = Category.objects.get(slug='hidden-category')
 
+        test_category = Category.objects.get(slug='hidden-category')
         test_thread = testutils.post_thread(
             category=test_category
         )
@@ -601,6 +599,7 @@ class ThreadsVisibilityTests(ThreadsListTestCase):
         response = self.client.get('/')
         self.assertEqual(response.status_code, 200)
         self.assertContains(response, "empty-message")
+        self.assertNotContains(response, test_thread.get_absolute_url())
 
     def test_api_hides_hidden_thread(self):
         """api returns empty due to no permission to see thread"""
@@ -608,9 +607,10 @@ class ThreadsVisibilityTests(ThreadsListTestCase):
             name='Hidden Category',
             slug='hidden-category',
         ).insert_at(self.root, position='last-child', save=True)
+
         test_category = Category.objects.get(slug='hidden-category')
 
-        test_thread = testutils.post_thread(
+        testutils.post_thread(
             category=test_category,
         )
 

+ 5 - 6
misago/threads/tests/test_threadview.py

@@ -3,7 +3,6 @@ from misago.categories.models import Category
 from misago.conf import settings
 from misago.threads import testutils
 from misago.threads.events import record_event
-from misago.threads.models import Post, Thread
 from misago.threads.moderation import threads as threads_moderation
 from misago.threads.moderation import hide_post
 from misago.users.testutils import AuthenticatedUserTestCase
@@ -317,7 +316,7 @@ class ThreadEventVisibilityTests(ThreadViewTestCase):
         events_limit = settings.MISAGO_EVENTS_PER_PAGE
         events = []
 
-        for i in range(events_limit + 5):
+        for _ in range(events_limit + 5):
             event = record_event(MockRequest(self.user), self.thread, 'closed')
             events.append(event)
 
@@ -335,12 +334,12 @@ class ThreadEventVisibilityTests(ThreadViewTestCase):
         events_limit = settings.MISAGO_EVENTS_PER_PAGE
         events = []
 
-        for i in range(events_limit + 5):
+        for _ in range(events_limit + 5):
             event = record_event(MockRequest(self.user), self.thread, 'closed')
             events.append(event)
 
         posts = []
-        for i in range(posts_limit - 1):
+        for _ in range(posts_limit - 1):
             post = testutils.reply_thread(self.thread)
             posts.append(post)
 
@@ -353,9 +352,9 @@ class ThreadEventVisibilityTests(ThreadViewTestCase):
             self.assertContains(response, post.get_absolute_url())
 
         # add second page to thread with more events
-        for i in range(posts_limit):
+        for _ in range(posts_limit):
             post = testutils.reply_thread(self.thread)
-        for i in range(events_limit):
+        for _ in range(events_limit):
             event = record_event(MockRequest(self.user), self.thread, 'closed')
             events.append(event)
 

+ 1 - 1
misago/threads/tests/test_validators.py

@@ -10,7 +10,7 @@ class ValidatePostTests(TestCase):
         """valid post passes validation"""
         validate_post("Lorem ipsum dolor met sit amet elit.")
 
-    def test_too_short_post(self):
+    def test_empty_post(self):
         """empty post is rejected"""
         with self.assertRaises(ValidationError):
             validate_post("")

+ 8 - 8
misago/threads/urls/__init__.py

@@ -6,8 +6,8 @@ from misago.threads.views.attachment import attachment_server
 from misago.threads.views.goto import (
     ThreadGotoPostView, ThreadGotoLastView, ThreadGotoNewView, ThreadGotoUnapprovedView,
     PrivateThreadGotoPostView, PrivateThreadGotoLastView, PrivateThreadGotoNewView)
-from misago.threads.views.list import ForumThreads, CategoryThreads, PrivateThreads
-from misago.threads.views.thread import Thread, PrivateThread
+from misago.threads.views.list import ForumThreadsList, CategoryThreadsList, PrivateThreadsList
+from misago.threads.views.thread import ThreadView, PrivateThreadView
 
 
 LISTS_TYPES = (
@@ -38,7 +38,7 @@ def threads_list_patterns(prefix, view, patterns):
 
 
 if settings.MISAGO_THREADS_ON_INDEX:
-    urlpatterns = threads_list_patterns('threads', ForumThreads, (
+    urlpatterns = threads_list_patterns('threads', ForumThreadsList, (
         r'^$',
         r'^my/$',
         r'^new/$',
@@ -47,7 +47,7 @@ if settings.MISAGO_THREADS_ON_INDEX:
         r'^unapproved/$',
     ))
 else:
-    urlpatterns = threads_list_patterns('threads', ForumThreads, (
+    urlpatterns = threads_list_patterns('threads', ForumThreadsList, (
         r'^threads/$',
         r'^threads/my/$',
         r'^threads/new/$',
@@ -57,7 +57,7 @@ else:
     ))
 
 
-urlpatterns += threads_list_patterns('category', CategoryThreads, (
+urlpatterns += threads_list_patterns('category', CategoryThreadsList, (
     r'^c/(?P<slug>[-a-zA-Z0-9]+)/(?P<pk>\d+)/$',
     r'^c/(?P<slug>[-a-zA-Z0-9]+)/(?P<pk>\d+)/my/$',
     r'^c/(?P<slug>[-a-zA-Z0-9]+)/(?P<pk>\d+)/new/$',
@@ -67,7 +67,7 @@ urlpatterns += threads_list_patterns('category', CategoryThreads, (
 ))
 
 
-urlpatterns += threads_list_patterns('private-threads', PrivateThreads, (
+urlpatterns += threads_list_patterns('private-threads', PrivateThreadsList, (
     r'^private-threads/$',
     r'^private-threads/my/$',
     r'^private-threads/new/$',
@@ -84,8 +84,8 @@ def thread_view_patterns(prefix, view):
     return urls
 
 
-urlpatterns += thread_view_patterns('thread', Thread)
-urlpatterns += thread_view_patterns('private-thread', PrivateThread)
+urlpatterns += thread_view_patterns('thread', ThreadView)
+urlpatterns += thread_view_patterns('private-thread', PrivateThreadView)
 
 
 def goto_patterns(prefix, **views):

+ 0 - 3
misago/threads/views/admin/attachments.py

@@ -1,8 +1,5 @@
 from django.contrib import messages
 from django.db import transaction
-from django.db.models import Count
-from django.shortcuts import redirect
-from django.urls import reverse
 from django.utils.translation import ugettext_lazy as _
 
 from misago.admin.views import generic

+ 0 - 2
misago/threads/views/admin/attachmenttypes.py

@@ -1,7 +1,5 @@
 from django.contrib import messages
 from django.db.models import Count
-from django.shortcuts import redirect
-from django.urls import reverse
 from django.utils.translation import ugettext_lazy as _
 
 from misago.admin.views import generic

+ 0 - 3
misago/threads/views/attachment.py

@@ -1,9 +1,6 @@
 from __future__ import unicode_literals
 
-import os
-
 from django.core.exceptions import PermissionDenied
-from django.db.models import F
 from django.http import Http404
 from django.shortcuts import get_object_or_404, redirect
 

+ 5 - 6
misago/threads/views/list.py

@@ -1,4 +1,3 @@
-from django.conf import settings
 from django.http import Http404
 from django.shortcuts import render
 from django.urls import reverse
@@ -9,7 +8,7 @@ from misago.threads.viewmodels import (
     ForumThreads, PrivateThreads, PrivateThreadsCategory, ThreadsCategory, ThreadsRootCategory)
 
 
-class ListBase(View):
+class ThreadsList(View):
     category = None
     threads = None
 
@@ -56,7 +55,7 @@ class ListBase(View):
         return {}
 
 
-class ForumThreads(ListBase):
+class ForumThreadsList(ThreadsList):
     category = ThreadsRootCategory
     threads = ForumThreads
 
@@ -68,19 +67,19 @@ class ForumThreads(ListBase):
         }
 
 
-class CategoryThreads(ForumThreads):
+class CategoryThreadsList(ForumThreadsList):
     category = ThreadsCategory
 
     template_name = 'misago/threadslist/category.html'
 
     def get_category(self, request, **kwargs):
-        category = super(CategoryThreads, self).get_category(request, **kwargs)
+        category = super(CategoryThreadsList, self).get_category(request, **kwargs)
         if not category.level:
             raise Http404() # disallow root category access
         return category
 
 
-class PrivateThreads(ListBase):
+class PrivateThreadsList(ThreadsList):
     category = PrivateThreadsCategory
     threads = PrivateThreads
 

+ 2 - 2
misago/threads/views/thread.py

@@ -56,7 +56,7 @@ class ThreadBase(View):
         return context
 
 
-class Thread(ThreadBase):
+class ThreadView(ThreadBase):
     thread = ForumThread
     template_name = 'misago/thread/thread.html'
 
@@ -66,6 +66,6 @@ class Thread(ThreadBase):
         }
 
 
-class PrivateThread(ThreadBase):
+class PrivateThreadView(ThreadBase):
     thread = PrivateThread
     template_name = 'misago/thread/private_thread.html'

+ 1 - 1
misago/users/api/userendpoints/avatar.py

@@ -3,7 +3,7 @@ import json
 from rest_framework import status
 from rest_framework.response import Response
 
-from django.core.exceptions import PermissionDenied, ValidationError
+from django.core.exceptions import ValidationError
 from django.utils.translation import ugettext as _
 
 from misago.conf import settings

+ 0 - 1
misago/users/api/userendpoints/create.py

@@ -10,7 +10,6 @@ from misago.conf import settings
 from misago.core.mail import mail_user
 from misago.users import captcha
 from misago.users.forms.register import RegisterForm
-from misago.users.serializers import AuthenticatedUserSerializer
 from misago.users.tokens import make_activation_token
 
 

+ 1 - 9
misago/users/api/userendpoints/list.py

@@ -1,18 +1,10 @@
-from datetime import timedelta
-
 from rest_framework.response import Response
 
 from django.contrib.auth import get_user_model
-from django.db.models import Count
-from django.http import Http404
 from django.shortcuts import get_object_or_404
-from django.utils import timezone
 
-from misago.conf import settings
-from misago.core.cache import cache
-from misago.core.shortcuts import get_int_or_404, paginate
+from misago.core.shortcuts import get_int_or_404
 from misago.users.models import Rank
-from misago.users.online.utils import make_users_status_aware
 from misago.users.serializers import UserCardSerializer
 from misago.users.viewmodels import ActivePosters, RankUsers
 

+ 0 - 2
misago/users/api/userendpoints/username.py

@@ -59,7 +59,6 @@ def change_username(request):
         except IntegrityError:
             return Response({
                 'detail': _("Error changing username. Please try again."),
-                'options': options
             },
             status=status.HTTP_400_BAD_REQUEST)
     else:
@@ -87,7 +86,6 @@ def moderate_username_endpoint(request, profile):
             except IntegrityError:
                 return Response({
                     'detail': _("Error changing username. Please try again."),
-                    'options': options
                 }, status=status.HTTP_400_BAD_REQUEST)
         else:
             return Response({

+ 1 - 1
misago/users/api/usernamechanges.py

@@ -1,4 +1,4 @@
-from rest_framework import mixins, status, viewsets
+from rest_framework import viewsets
 from rest_framework.response import Response
 
 from django.contrib.auth import get_user_model

+ 2 - 4
misago/users/api/users.py

@@ -1,9 +1,8 @@
-from rest_framework import mixins, status, viewsets
-from rest_framework.decorators import detail_route, list_route
+from rest_framework import status, viewsets
+from rest_framework.decorators import detail_route
 from rest_framework.parsers import FormParser, JSONParser, MultiPartParser
 from rest_framework.response import Response
 
-from django.conf import settings
 from django.contrib.auth import get_user_model
 from django.core.exceptions import PermissionDenied
 from django.db import transaction
@@ -14,7 +13,6 @@ from django.utils.translation import ugettext as _
 
 from misago.acl import add_acl
 from misago.categories.models import Category
-from misago.core.cache import cache
 from misago.core.rest_permissions import IsAuthenticatedOrReadOnly
 from misago.core.shortcuts import get_int_or_404
 from misago.threads.moderation import hide_post, hide_thread

+ 1 - 4
misago/users/apps.py

@@ -10,16 +10,13 @@ class MisagoUsersConfig(AppConfig):
     verbose_name = "Misago Auth"
 
     def ready(self):
-        from . import signals
+        from . import signals as _
 
         self.register_default_usercp_pages()
         self.register_default_users_list_pages()
         self.register_default_user_profile_pages()
 
     def register_default_usercp_pages(self):
-        def show_signature_cp(request):
-            return request.user.acl_cache['can_have_signature']
-
         usercp.add_section(
             link='misago:usercp-change-forum-options',
             name=_('Forum options'),

+ 1 - 2
misago/users/avatars/dynamic.py

@@ -1,8 +1,7 @@
-import math
 import os
 from importlib import import_module
 
-from PIL import Image, ImageColor, ImageDraw, ImageFilter, ImageFont
+from PIL import Image, ImageColor, ImageDraw, ImageFont
 
 from misago.conf import settings
 

+ 0 - 3
misago/users/avatars/uploaded.py

@@ -1,6 +1,3 @@
-from hashlib import sha256
-from math import floor
-
 from path import Path
 from PIL import Image
 

+ 0 - 1
misago/users/decorators.py

@@ -1,5 +1,4 @@
 from django.core.exceptions import PermissionDenied
-from django.shortcuts import redirect
 from django.utils.translation import gettext as _
 
 from misago.core.exceptions import Banned

+ 0 - 1
misago/users/management/commands/buildactivepostersranking.py

@@ -1,6 +1,5 @@
 from django.core.management.base import BaseCommand
 
-from misago.core.management.progressbar import show_progress
 from misago.users.activepostersranking import build_active_posters_ranking
 
 

+ 0 - 3
misago/users/middleware.py

@@ -1,9 +1,6 @@
 from django.contrib.auth import logout
-from django.contrib.auth.models import AnonymousUser as DjAnonymousUser
 from django.utils.deprecation import MiddlewareMixin
 
-import pytz
-
 from .bans import get_request_ip_ban, get_user_ban
 from .models import AnonymousUser, Online
 from .online import tracker

+ 1 - 1
misago/users/migrations/0003_bans_version_tracker.py

@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
 from __future__ import unicode_literals
 
-from django.db import migrations, models
+from django.db import migrations
 
 from misago.core.migrationutils import cachebuster_register_cache
 from misago.users.constants import BANS_CACHEBUSTER

+ 1 - 1
misago/users/migrations/0004_default_ranks.py

@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
 from __future__ import unicode_literals
 
-from django.db import migrations, models
+from django.db import migrations
 from django.utils.translation import ugettext as _
 
 from misago.core.utils import slugify

+ 1 - 2
misago/users/models/user.py

@@ -7,7 +7,6 @@ from django.contrib.auth.password_validation import validate_password
 from django.contrib.postgres.fields import JSONField
 from django.core.mail import send_mail
 from django.db import IntegrityError, models, transaction
-from django.dispatch import receiver
 from django.urls import reverse
 from django.utils import timezone
 from django.utils.translation import ugettext_lazy as _
@@ -17,7 +16,7 @@ from misago.acl.models import Role
 from misago.conf import settings
 from misago.core.utils import slugify
 from misago.users import avatars
-from misago.users.signatures import is_user_signature_valid, make_signature_checksum
+from misago.users.signatures import is_user_signature_valid
 from misago.users.utils import hash_email
 
 from .rank import Rank

+ 1 - 1
misago/users/serializers/moderation.py

@@ -36,7 +36,7 @@ class ModerateSignatureSerializer(serializers.ModelSerializer):
     def validate_signature(self, value):
         length_limit = settings.signature_length_max
         if len(value) > length_limit:
-            raise forms.ValidationError(ungettext(
+            raise serializers.ValidationError(ungettext(
                 "Signature can't be longer than %(limit)s character.",
                 "Signature can't be longer than %(limit)s characters.",
                 length_limit) % {'limit': length_limit})

+ 0 - 3
misago/users/templatetags/misago_avatars.py

@@ -1,7 +1,4 @@
 from django import template
-from django.urls import reverse
-
-from misago.conf import settings
 
 
 register = template.Library()

+ 1 - 1
misago/users/tests/test_auth_api.py

@@ -83,7 +83,7 @@ class GatewayTests(TestCase):
         user.is_staff = True
         user.save()
 
-        ban = Ban.objects.create(
+        Ban.objects.create(
             check_type=Ban.USERNAME,
             banned_value='bob',
             user_message='You are tragically banned.',

+ 0 - 1
misago/users/tests/test_auth_views.py

@@ -1,6 +1,5 @@
 import json
 
-from django.contrib.auth import get_user_model
 from django.test import TestCase
 from django.urls import reverse
 from django.utils.encoding import smart_str

+ 2 - 2
misago/users/tests/test_avatars.py

@@ -3,7 +3,7 @@ from PIL import Image
 
 from django.contrib.auth import get_user_model
 from django.core.exceptions import ValidationError
-from django.test import TestCase, override_settings
+from django.test import TestCase
 from django.utils.crypto import get_random_string
 
 from misago.conf import settings
@@ -24,7 +24,7 @@ class AvatarsStoreTests(TestCase):
         store.store_new_avatar(user, test_image)
 
         # reload user
-        test_user = UserModel.objects.get(pk=user.pk)
+        UserModel.objects.get(pk=user.pk)
 
         # assert that avatars were stored in media
         avatars_dict = {}

+ 1 - 1
misago/users/tests/test_banadmin_views.py

@@ -1,4 +1,4 @@
-from datetime import date, datetime, timedelta
+from datetime import datetime, timedelta
 
 from django.urls import reverse
 from django.utils.six.moves import range

+ 0 - 2
misago/users/tests/test_forgottenpassword_views.py

@@ -62,8 +62,6 @@ class ForgottenPasswordViewsTests(UserTestCase):
         """invalid form token errors"""
         test_user = UserModel.objects.create_user('Bob', 'bob@test.com', 'Pass.123')
 
-        password_token = make_password_change_token(test_user)
-
         response = self.client.get(
             reverse('misago:forgotten-password-change-form', kwargs={
                 'pk': test_user.pk,

+ 1 - 1
misago/users/tests/test_invalidatebans.py

@@ -19,7 +19,7 @@ class InvalidateBansTests(TestCase):
     def test_expired_bans_handling(self):
         """expired bans are flagged as such"""
         # create 5 bans then update their valid date to past one
-        for i in range(5):
+        for _ in range(5):
             Ban.objects.create(banned_value="abcd")
         expired_date = (timezone.now() - timedelta(days=10))
         Ban.objects.all().update(expires_on=expired_date, is_checked=True)

+ 0 - 4
misago/users/tests/test_user_avatar_api.py

@@ -4,11 +4,8 @@ import os
 from path import Path
 
 from django.contrib.auth import get_user_model
-from django.urls import reverse
-from django.utils.encoding import smart_str
 
 from misago.acl.testutils import override_acl
-from misago.conf import settings
 from misago.users.avatars import gallery, store
 from misago.users.models import AvatarGallery
 from misago.users.testutils import AuthenticatedUserTestCase
@@ -184,7 +181,6 @@ class UserAvatarTests(AuthenticatedUserTestCase):
         self.assertContains(response, "Avatar was re-cropped.")
 
         # delete user avatars, test if it deletes src and tmp
-        user = self.get_current_user()
         store.delete_avatar(self.get_current_user())
 
         self.assertTrue(self.get_current_user().avatar_src.path)

+ 0 - 1
misago/users/tests/test_user_changepassword_api.py

@@ -1,4 +1,3 @@
-from django.contrib.auth import get_user_model
 from django.core import mail
 from django.urls import reverse
 

+ 2 - 2
misago/users/tests/test_user_feeds_api.py

@@ -102,7 +102,7 @@ class UserPostsApiTests(ThreadsApiTestCase):
 
     def test_user_hidden_post(self):
         """hidden posts don't show in feeds at all"""
-        post = testutils.reply_thread(self.thread, poster=self.user, is_hidden=True)
+        testutils.reply_thread(self.thread, poster=self.user, is_hidden=True)
 
         response = self.client.get(self.api_link)
         self.assertEqual(response.status_code, 200)
@@ -110,7 +110,7 @@ class UserPostsApiTests(ThreadsApiTestCase):
 
     def test_user_unapproved_post(self):
         """unapproved posts don't show in feeds at all"""
-        post = testutils.reply_thread(self.thread, poster=self.user, is_unapproved=True)
+        testutils.reply_thread(self.thread, poster=self.user, is_unapproved=True)
 
         response = self.client.get(self.api_link)
         self.assertEqual(response.status_code, 200)

+ 4 - 13
misago/users/tests/test_user_signature_api.py

@@ -1,10 +1,4 @@
-import json
-
-from django.contrib.auth import get_user_model
-from django.utils.encoding import smart_str
-
 from misago.acl.testutils import override_acl
-from misago.conf import settings
 from misago.users.testutils import AuthenticatedUserTestCase
 
 
@@ -50,8 +44,7 @@ class UserSignatureTests(AuthenticatedUserTestCase):
         response = self.client.get(self.link)
         self.assertEqual(response.status_code, 200)
 
-        response_json = json.loads(smart_str(response.content))
-        self.assertFalse(response_json['signature'])
+        self.assertFalse(response.json()['signature'])
 
     def test_post_empty_signature(self):
         """empty POST empties user signature"""
@@ -65,8 +58,7 @@ class UserSignatureTests(AuthenticatedUserTestCase):
         response = self.client.post(self.link, data={'signature': ''})
         self.assertEqual(response.status_code, 200)
 
-        response_json = json.loads(smart_str(response.content))
-        self.assertFalse(response_json['signature'])
+        self.assertFalse(response.json()['signature'])
 
     def test_post_too_long_signature(self):
         """too long new signature errors"""
@@ -96,10 +88,9 @@ class UserSignatureTests(AuthenticatedUserTestCase):
         })
         self.assertEqual(response.status_code, 200)
 
-        response_json = json.loads(smart_str(response.content))
-        self.assertEqual(response_json['signature']['html'],
+        self.assertEqual(response.json()['signature']['html'],
                          '<p>Hello, <strong>bros</strong>!</p>')
-        self.assertEqual(response_json['signature']['plain'],
+        self.assertEqual(response.json()['signature']['plain'],
                          'Hello, **bros**!')
 
         self.reload_user()

+ 2 - 1
misago/users/tests/test_user_username_api.py

@@ -83,7 +83,7 @@ class UserUsernameTests(AuthenticatedUserTestCase):
         response = self.client.get(self.link)
         changes_left = json.loads(smart_str(response.content))['changes_left']
 
-        username = self.user.username
+        old_username = self.user.username
         new_username = 'NewUsernamu'
 
         response = self.client.post(self.link, data={
@@ -96,6 +96,7 @@ class UserUsernameTests(AuthenticatedUserTestCase):
 
         self.reload_user()
         self.assertEqual(self.user.username, new_username)
+        self.assertTrue(self.user.username != old_username)
 
         self.assertEqual(self.user.namechanges.last().new_username,
                          new_username)

+ 2 - 44
misago/users/tests/test_useradmin_views.py

@@ -484,48 +484,6 @@ class UserAdminViewsTests(AdminTestCase):
         self.assertTrue(updated_user.is_active)
         self.assertFalse(updated_user.is_active_staff_message)
 
-    def test_edit_superuser_disable_admin(self):
-        """edit user view allows superuser to disable admin"""
-        self.user.is_superuser = True
-        self.user.save()
-
-        test_user = UserModel.objects.create_user('Bob', 'bob@test.com', 'pass123')
-
-        test_user.is_staff = True
-        test_user.save()
-
-        test_link = reverse('misago:admin:users:accounts:edit',
-                            kwargs={'pk': test_user.pk})
-
-        response = self.client.get(test_link)
-        self.assertContains(response, 'id="id_is_active_1"')
-        self.assertContains(response, 'id="id_is_active_staff_message"')
-
-        response = self.client.post(test_link, data={
-            'username': 'Bawww',
-            'rank': six.text_type(test_user.rank_id),
-            'roles': six.text_type(test_user.roles.all()[0].pk),
-            'email': 'reg@stered.com',
-            'new_password': 'pass123',
-            'is_staff': '1',
-            'is_superuser': '0',
-            'signature': 'Hello world!',
-            'is_signature_locked': '1',
-            'is_hiding_presence': '0',
-            'limits_private_thread_invites_to': '0',
-            'signature_lock_staff_message': 'Staff message',
-            'signature_lock_user_message': 'User message',
-            'subscribe_to_started_threads': '2',
-            'subscribe_to_replied_threads': '2',
-            'is_active': '0',
-            'is_active_staff_message': "Disabled in test!"
-        })
-        self.assertEqual(response.status_code, 302)
-
-        updated_user = UserModel.objects.get(pk=test_user.pk)
-        self.assertFalse(updated_user.is_active)
-        self.assertEqual(updated_user.is_active_staff_message, "Disabled in test!")
-
     def test_delete_threads_view(self):
         """delete user threads view deletes threads"""
         test_user = UserModel.objects.create_user('Bob', 'bob@test.com', 'pass123')
@@ -533,7 +491,7 @@ class UserAdminViewsTests(AdminTestCase):
                             kwargs={'pk': test_user.pk})
 
         category = Category.objects.all_categories()[:1][0]
-        [post_thread(category, poster=test_user) for i in range(10)]
+        [post_thread(category, poster=test_user) for _ in range(10)]
 
         response = self.client.post(test_link, **self.AJAX_HEADER)
         self.assertEqual(response.status_code, 200)
@@ -557,7 +515,7 @@ class UserAdminViewsTests(AdminTestCase):
 
         category = Category.objects.all_categories()[:1][0]
         thread = post_thread(category)
-        [reply_thread(thread, poster=test_user) for i in range(10)]
+        [reply_thread(thread, poster=test_user) for _ in range(10)]
 
         response = self.client.post(test_link, **self.AJAX_HEADER)
         self.assertEqual(response.status_code, 200)

+ 0 - 2
misago/users/tests/test_usernamechanges_api.py

@@ -1,5 +1,3 @@
-from django.contrib.auth import get_user_model
-
 from misago.acl.testutils import override_acl
 from misago.users.testutils import AuthenticatedUserTestCase
 

+ 0 - 15
misago/users/tests/test_users_api.py

@@ -7,7 +7,6 @@ from django.utils.encoding import smart_str
 
 from misago.acl.testutils import override_acl
 from misago.categories.models import Category
-from misago.conf import settings
 from misago.core import threadstore
 from misago.core.cache import cache
 from misago.threads.models import Post, Thread
@@ -530,20 +529,6 @@ class UserDeleteTests(AuthenticatedUserTestCase):
         response = self.client.post(self.link)
         self.assertEqual(response.status_code, 403)
         self.assertContains(response, "can't delete users", status_code=403)
-
-    def test_delete_too_many_posts(self):
-        """raises 403 error when user has too many posts"""
-        override_acl(self.user, {
-            'can_delete_users_newer_than': 0,
-            'can_delete_users_with_less_posts_than': 5,
-        })
-
-        self.other_user.posts = 6
-        self.other_user.save()
-
-        response = self.client.post(self.link)
-        self.assertEqual(response.status_code, 403)
-        self.assertContains(response, "can't delete users", status_code=403)
         self.assertContains(response, "made more than 5 posts", status_code=403)
 
     def test_delete_too_old_member(self):

+ 2 - 2
misago/users/validators.py

@@ -149,9 +149,9 @@ def validate_gmail_email(request, form, cleaned_data):
 """
 Registration validation
 """
-def load_registration_validators(validators_list):
+def load_registration_validators(validators):
     loaded_validators = []
-    for path in validators_list:
+    for path in validators:
         module = import_module('.'.join(path.split('.')[:-1]))
         loaded_validators.append(getattr(module, path.split('.')[-1]))
     return loaded_validators

+ 0 - 1
misago/users/viewmodels/rankusers.py

@@ -1,6 +1,5 @@
 from misago.conf import settings
 from misago.core.shortcuts import paginate, pagination_dict
-from django.http import Http404
 from misago.users.online.utils import make_users_status_aware
 from misago.users.serializers import UserCardSerializer
 

+ 0 - 2
misago/users/views/activation.py

@@ -3,9 +3,7 @@ from django.shortcuts import get_object_or_404, render
 from django.urls import reverse
 from django.utils.translation import ugettext as _
 
-from misago.conf import settings
 from misago.core.exceptions import Banned
-from misago.core.mail import mail_user
 from misago.users.bans import get_user_ban
 from misago.users.decorators import deny_authenticated, deny_banned_ips
 from misago.users.tokens import is_activation_token_valid

+ 9 - 6
misago/users/views/admin/users.py

@@ -15,7 +15,7 @@ from misago.threads.models import Thread
 from misago.users.avatars.dynamic import set_avatar as set_dynamic_avatar
 from misago.users.forms.admin import (
     BanUsersForm, EditUserForm, EditUserFormFactory, NewUserForm, SearchUsersForm)
-from misago.users.models import Ban, User
+from misago.users.models import Ban
 from misago.users.signatures import set_user_signature
 
 
@@ -212,11 +212,6 @@ class UsersList(UserAdmin, generic.ListView):
         messages.success(request, message)
 
     def action_delete_all(self, request, users):
-        return self.render(
-            request, template='misago/admin/users/delete.html', context={
-                'users': users,
-            })
-
         for user in users:
             if user.is_staff or user.is_superuser:
                 message = _("%(user)s is admin and can't be deleted.")
@@ -229,6 +224,14 @@ class UsersList(UserAdmin, generic.ListView):
         message = _("Selected users and their content has been deleted.")
         messages.success(request, message)
 
+        return self.render(
+            request,
+            template='misago/admin/users/delete.html',
+            context={
+                'users': users,
+            }
+        )
+
 
 class NewUser(UserAdmin, generic.ModelFormView):
     form = NewUserForm

+ 1 - 2
misago/users/views/options.py

@@ -1,7 +1,6 @@
 from django.contrib.auth import update_session_auth_hash
 from django.db import IntegrityError
 from django.shortcuts import render
-from django.urls import reverse
 from django.utils import six
 from django.utils.translation import ugettext as _
 
@@ -36,7 +35,7 @@ def confirm_change_view(f):
     def decorator(request, token):
         try:
             return f(request, token)
-        except ChangeError as e:
+        except ChangeError:
             return render(request, 'misago/options/credentials_error.html',
                 status=400)
     return decorator