Browse Source

Merge pull request #1122 from rafalp/py36-only

Make Misago Python3.6 only
Rafał Pitoń 6 years ago
parent
commit
4b973e4058
264 changed files with 444 additions and 725 deletions
  1. 0 3
      .travis.yml
  2. 1 1
      Dockerfile
  3. 2 2
      README.rst
  4. 0 3
      misago/acl/migrations/0001_initial.py
  5. 0 3
      misago/acl/migrations/0002_acl_version_tracker.py
  6. 0 3
      misago/acl/migrations/0003_default_roles.py
  7. 2 4
      misago/acl/models.py
  8. 2 3
      misago/acl/tests/test_providers.py
  9. 0 3
      misago/admin/tests/test_admin_views.py
  10. 1 1
      misago/admin/testutils.py
  11. 2 1
      misago/admin/views/generic/list.py
  12. 3 3
      misago/categories/forms.py
  13. 0 3
      misago/categories/migrations/0001_initial.py
  14. 0 3
      misago/categories/migrations/0002_default_categories.py
  15. 0 3
      misago/categories/migrations/0003_categories_roles.py
  16. 0 3
      misago/categories/migrations/0004_category_last_thread.py
  17. 0 3
      misago/categories/migrations/0005_auto_20170303_2027.py
  18. 0 3
      misago/categories/migrations/0006_moderation_queue_roles.py
  19. 0 3
      misago/categories/migrations/0007_best_answers_roles.py
  20. 2 5
      misago/categories/models.py
  21. 1 1
      misago/categories/tests/test_categories_admin_views.py
  22. 1 1
      misago/categories/tests/test_category_model.py
  23. 2 1
      misago/categories/tests/test_fixcategoriestree.py
  24. 1 1
      misago/categories/tests/test_prunecategories.py
  25. 2 1
      misago/categories/tests/test_synchronizecategories.py
  26. 1 1
      misago/categories/tests/test_utils.py
  27. 1 1
      misago/categories/tests/test_views.py
  28. 1 1
      misago/categories/views/categoriesadmin.py
  29. 3 4
      misago/conf/hydrators.py
  30. 0 3
      misago/conf/migrations/0001_initial.py
  31. 4 4
      misago/core/apipatch.py
  32. 1 2
      misago/core/exceptionhandler.py
  33. 0 3
      misago/core/migrations/0001_initial.py
  34. 0 3
      misago/core/migrations/0002_basic_settings.py
  35. 4 6
      misago/core/pgutils.py
  36. 1 3
      misago/core/shortcuts.py
  37. 1 2
      misago/core/slugify.py
  38. 1 1
      misago/core/templatetags/misago_absoluteurl.py
  39. 2 2
      misago/core/templatetags/misago_pagetitle.py
  40. 1 1
      misago/core/testproject/searchfilters.py
  41. 5 5
      misago/core/tests/test_apipatch.py
  42. 1 1
      misago/core/tests/test_cachebuster.py
  43. 1 2
      misago/core/tests/test_deprecations.py
  44. 1 1
      misago/core/tests/test_forms.py
  45. 0 4
      misago/core/tests/test_utils.py
  46. 2 2
      misago/core/testutils.py
  47. 1 2
      misago/faker/englishcorpus.py
  48. 0 2
      misago/faker/management/commands/createfakethreads.py
  49. 2 2
      misago/legal/forms.py
  50. 0 3
      misago/legal/migrations/0001_initial.py
  51. 0 3
      misago/legal/migrations/0002_agreement_useragreement.py
  52. 0 3
      misago/legal/migrations/0003_create_agreements_from_settings.py
  53. 1 1
      misago/legal/tests/test_api.py
  54. 2 2
      misago/legal/tests/test_context_processors.py
  55. 1 1
      misago/legal/tests/test_required_agreement.py
  56. 3 3
      misago/legal/views/admin.py
  57. 1 3
      misago/markup/bbcode/blocks.py
  58. 1 3
      misago/markup/checksums.py
  59. 0 2
      misago/markup/finalise.py
  60. 2 3
      misago/markup/mentions.py
  61. 7 6
      misago/markup/parser.py
  62. 1 3
      misago/markup/pipeline.py
  63. 1 4
      misago/markup/tests/test_api.py
  64. 0 3
      misago/markup/tests/test_finalise.py
  65. 0 3
      misago/markup/tests/test_parser.py
  66. 0 3
      misago/readtracker/migrations/0001_initial.py
  67. 0 3
      misago/readtracker/migrations/0002_postread.py
  68. 0 3
      misago/readtracker/migrations/0003_migrate_reads_to_posts.py
  69. 0 3
      misago/readtracker/migrations/0004_auto_20171015_2010.py
  70. 1 1
      misago/readtracker/tests/test_clearreadtracker.py
  71. 1 2
      misago/search/api.py
  72. 1 2
      misago/search/context_processors.py
  73. 3 4
      misago/search/tests/test_api.py
  74. 1 1
      misago/search/tests/test_views.py
  75. 0 1
      misago/search/views.py
  76. 0 1
      misago/threads/api/postendpoints/split.py
  77. 1 1
      misago/threads/api/postingendpoint/category.py
  78. 1 1
      misago/threads/api/postingendpoint/emailnotification.py
  79. 2 4
      misago/threads/api/postingendpoint/participants.py
  80. 1 1
      misago/threads/api/postingendpoint/savechanges.py
  81. 1 2
      misago/threads/api/threadendpoints/merge.py
  82. 2 3
      misago/threads/api/threadendpoints/patch.py
  83. 1 3
      misago/threads/checksums.py
  84. 1 1
      misago/threads/mergeconflict.py
  85. 0 3
      misago/threads/migrations/0001_initial.py
  86. 0 3
      misago/threads/migrations/0002_threads_settings.py
  87. 0 3
      misago/threads/migrations/0003_attachment_types.py
  88. 0 3
      misago/threads/migrations/0004_update_settings.py
  89. 0 3
      misago/threads/migrations/0005_index_search_document.py
  90. 0 3
      misago/threads/migrations/0006_redo_partial_indexes.py
  91. 0 3
      misago/threads/migrations/0007_auto_20171008_0131.py
  92. 0 3
      misago/threads/migrations/0008_auto_20180310_2234.py
  93. 1 3
      misago/threads/migrations/0009_auto_20180326_0010.py
  94. 0 3
      misago/threads/migrations/0010_auto_20180609_1523.py
  95. 1 3
      misago/threads/models/attachment.py
  96. 0 4
      misago/threads/models/attachmenttype.py
  97. 5 9
      misago/threads/models/post.py
  98. 1 3
      misago/threads/models/thread.py
  99. 0 4
      misago/threads/moderation/exceptions.py
  100. 1 2
      misago/threads/paginator.py
  101. 1 2
      misago/threads/serializers/moderation.py
  102. 1 1
      misago/threads/serializers/poll.py
  103. 1 1
      misago/threads/signals.py
  104. 0 2
      misago/threads/templatetags/misago_poststags.py
  105. 3 3
      misago/threads/tests/test_anonymize_data.py
  106. 1 1
      misago/threads/tests/test_attachmentadmin_views.py
  107. 7 8
      misago/threads/tests/test_attachments_api.py
  108. 1 1
      misago/threads/tests/test_attachments_middleware.py
  109. 1 1
      misago/threads/tests/test_attachmenttypeadmin_views.py
  110. 1 1
      misago/threads/tests/test_attachmentview.py
  111. 1 1
      misago/threads/tests/test_clearattachments.py
  112. 1 1
      misago/threads/tests/test_delete_user_likes.py
  113. 1 4
      misago/threads/tests/test_emailnotification_middleware.py
  114. 0 1
      misago/threads/tests/test_events.py
  115. 1 4
      misago/threads/tests/test_floodprotection.py
  116. 1 1
      misago/threads/tests/test_gotoviews.py
  117. 2 2
      misago/threads/tests/test_mergeconflict.py
  118. 1 4
      misago/threads/tests/test_post_mentions.py
  119. 1 1
      misago/threads/tests/test_posts_moderation.py
  120. 1 1
      misago/threads/tests/test_privatethread_patch_api.py
  121. 1 1
      misago/threads/tests/test_privatethread_reply_api.py
  122. 1 4
      misago/threads/tests/test_privatethread_start_api.py
  123. 1 1
      misago/threads/tests/test_privatethread_view.py
  124. 1 1
      misago/threads/tests/test_privatethreads.py
  125. 3 3
      misago/threads/tests/test_privatethreads_api.py
  126. 1 1
      misago/threads/tests/test_privatethreads_lists.py
  127. 2 2
      misago/threads/tests/test_search.py
  128. 3 6
      misago/threads/tests/test_subscription_middleware.py
  129. 1 1
      misago/threads/tests/test_sync_unread_private_threads.py
  130. 2 1
      misago/threads/tests/test_synchronizethreads.py
  131. 2 2
      misago/threads/tests/test_thread_bulkpatch_api.py
  132. 1 4
      misago/threads/tests/test_thread_editreply_api.py
  133. 3 3
      misago/threads/tests/test_thread_merge_api.py
  134. 6 6
      misago/threads/tests/test_thread_patch_api.py
  135. 1 1
      misago/threads/tests/test_thread_poll_api.py
  136. 1 1
      misago/threads/tests/test_thread_polldelete_api.py
  137. 1 1
      misago/threads/tests/test_thread_polledit_api.py
  138. 2 2
      misago/threads/tests/test_thread_pollvotes_api.py
  139. 1 1
      misago/threads/tests/test_thread_postbulkdelete_api.py
  140. 1 4
      misago/threads/tests/test_thread_postbulkpatch_api.py
  141. 2 2
      misago/threads/tests/test_thread_postdelete_api.py
  142. 2 5
      misago/threads/tests/test_thread_postedits_api.py
  143. 1 1
      misago/threads/tests/test_thread_postlikes_api.py
  144. 1 4
      misago/threads/tests/test_thread_postmerge_api.py
  145. 1 4
      misago/threads/tests/test_thread_postmove_api.py
  146. 2 5
      misago/threads/tests/test_thread_postpatch_api.py
  147. 1 1
      misago/threads/tests/test_thread_postread_api.py
  148. 1 4
      misago/threads/tests/test_thread_postsplit_api.py
  149. 1 4
      misago/threads/tests/test_thread_reply_api.py
  150. 1 4
      misago/threads/tests/test_thread_start_api.py
  151. 3 3
      misago/threads/tests/test_threads_api.py
  152. 1 1
      misago/threads/tests/test_threads_bulkdelete_api.py
  153. 4 4
      misago/threads/tests/test_threads_editor_api.py
  154. 3 3
      misago/threads/tests/test_threads_merge_api.py
  155. 2 2
      misago/threads/tests/test_threads_moderation.py
  156. 4 4
      misago/threads/tests/test_threadslists.py
  157. 8 9
      misago/threads/tests/test_threadview.py
  158. 2 3
      misago/threads/tests/test_treesmap.py
  159. 2 1
      misago/threads/tests/test_updatepostschecksums.py
  160. 1 1
      misago/threads/tests/test_utils.py
  161. 1 4
      misago/threads/tests/test_validate_post.py
  162. 3 3
      misago/threads/utils.py
  163. 1 2
      misago/threads/viewmodels/threads.py
  164. 1 1
      misago/threads/views/admin/attachments.py
  165. 2 2
      misago/threads/views/admin/attachmenttypes.py
  166. 0 2
      misago/threads/views/attachment.py
  167. 1 1
      misago/threads/views/list.py
  168. 2 2
      misago/users/api/userendpoints/editdetails.py
  169. 16 8
      misago/users/avatars/gallery.py
  170. 3 2
      misago/users/avatars/uploaded.py
  171. 2 3
      misago/users/credentialchange.py
  172. 8 10
      misago/users/datadownloads/dataarchive.py
  173. 4 4
      misago/users/forms/admin.py
  174. 2 2
      misago/users/forms/auth.py
  175. 3 3
      misago/users/forms/register.py
  176. 6 7
      misago/users/management/commands/createsuperuser.py
  177. 0 2
      misago/users/management/commands/deleteinactiveusers.py
  178. 0 2
      misago/users/management/commands/deletemarkedusers.py
  179. 0 2
      misago/users/management/commands/deleteprofilefield.py
  180. 0 2
      misago/users/management/commands/listusedprofilefields.py
  181. 0 3
      misago/users/migrations/0001_initial.py
  182. 0 3
      misago/users/migrations/0002_users_settings.py
  183. 0 3
      misago/users/migrations/0003_bans_version_tracker.py
  184. 0 3
      misago/users/migrations/0004_default_ranks.py
  185. 0 3
      misago/users/migrations/0005_dj_19_update.py
  186. 0 3
      misago/users/migrations/0006_update_settings.py
  187. 0 3
      misago/users/migrations/0007_auto_20170219_1639.py
  188. 0 3
      misago/users/migrations/0008_ban_registration_only.py
  189. 0 3
      misago/users/migrations/0009_redo_partial_indexes.py
  190. 0 3
      misago/users/migrations/0010_user_profile_fields.py
  191. 0 3
      misago/users/migrations/0011_auto_20180331_2208.py
  192. 0 3
      misago/users/migrations/0012_audittrail.py
  193. 0 3
      misago/users/migrations/0013_auto_20180609_1523.py
  194. 0 3
      misago/users/migrations/0014_datadownload.py
  195. 0 3
      misago/users/migrations/0015_user_agreements.py
  196. 2 2
      misago/users/models/ban.py
  197. 1 1
      misago/users/models/datadownload.py
  198. 2 4
      misago/users/models/rank.py
  199. 2 2
      misago/users/models/user.py
  200. 0 2
      misago/users/profilefields/__init__.py
  201. 3 4
      misago/users/profilefields/basefields.py
  202. 0 2
      misago/users/profilefields/default.py
  203. 1 1
      misago/users/social/pipeline.py
  204. 2 2
      misago/users/tests/test_activepostersranking.py
  205. 3 3
      misago/users/tests/test_audittrail.py
  206. 1 2
      misago/users/tests/test_auth_views.py
  207. 5 4
      misago/users/tests/test_avatars.py
  208. 0 1
      misago/users/tests/test_ban_model.py
  209. 5 6
      misago/users/tests/test_bio_profilefield.py
  210. 2 1
      misago/users/tests/test_createsuperuser.py
  211. 1 1
      misago/users/tests/test_datadownloads.py
  212. 13 15
      misago/users/tests/test_datadownloads_dataarchive.py
  213. 1 1
      misago/users/tests/test_deleteinactiveusers.py
  214. 2 1
      misago/users/tests/test_deletemarkedusers.py
  215. 2 1
      misago/users/tests/test_deleteprofilefield.py
  216. 1 1
      misago/users/tests/test_djangoadmin_user.py
  217. 1 1
      misago/users/tests/test_expireuserdatadownloads.py
  218. 7 8
      misago/users/tests/test_gender_profilefield.py
  219. 1 1
      misago/users/tests/test_invalidatebans.py
  220. 3 4
      misago/users/tests/test_joinip_profilefield.py
  221. 1 1
      misago/users/tests/test_lists_views.py
  222. 2 1
      misago/users/tests/test_listusedprofilefields.py
  223. 2 1
      misago/users/tests/test_loadavatargallery.py
  224. 1 1
      misago/users/tests/test_online_utils.py
  225. 2 2
      misago/users/tests/test_options_views.py
  226. 2 1
      misago/users/tests/test_populateonlinetracker.py
  227. 2 1
      misago/users/tests/test_prepareuserdatadownloads.py
  228. 1 1
      misago/users/tests/test_profile_views.py
  229. 3 4
      misago/users/tests/test_profilefields.py
  230. 1 1
      misago/users/tests/test_removeoldips.py
  231. 2 2
      misago/users/tests/test_search.py
  232. 10 11
      misago/users/tests/test_social_pipeline.py
  233. 7 8
      misago/users/tests/test_twitter_profilefield.py
  234. 6 7
      misago/users/tests/test_user_avatar_api.py
  235. 1 1
      misago/users/tests/test_user_changeemail_api.py
  236. 1 1
      misago/users/tests/test_user_changepassword_api.py
  237. 1 1
      misago/users/tests/test_user_create_api.py
  238. 1 1
      misago/users/tests/test_user_datadownloads_api.py
  239. 1 1
      misago/users/tests/test_user_editdetails_api.py
  240. 2 2
      misago/users/tests/test_user_feeds_api.py
  241. 1 1
      misago/users/tests/test_user_middleware.py
  242. 6 7
      misago/users/tests/test_user_model.py
  243. 1 1
      misago/users/tests/test_user_requestdatadownload_api.py
  244. 1 1
      misago/users/tests/test_user_signature_api.py
  245. 2 2
      misago/users/tests/test_user_username_api.py
  246. 30 31
      misago/users/tests/test_useradmin_views.py
  247. 1 1
      misago/users/tests/test_usernamechanges_api.py
  248. 11 11
      misago/users/tests/test_users_api.py
  249. 1 2
      misago/users/tests/test_utils.py
  250. 2 3
      misago/users/tests/test_validators.py
  251. 1 1
      misago/users/testutils.py
  252. 1 2
      misago/users/tokens.py
  253. 1 1
      misago/users/validators.py
  254. 1 1
      misago/users/views/admin/bans.py
  255. 1 1
      misago/users/views/admin/datadownloads.py
  256. 1 1
      misago/users/views/admin/ranks.py
  257. 2 2
      misago/users/views/admin/users.py
  258. 5 4
      misago/users/views/auth.py
  259. 1 2
      misago/users/views/lists.py
  260. 1 2
      misago/users/views/options.py
  261. 1 2
      misago/users/views/profile.py
  262. 0 1
      requirements.in
  263. 0 1
      requirements.txt
  264. 0 3
      setup.py

+ 0 - 3
.travis.yml

@@ -4,9 +4,6 @@ addons:
   postgresql: "9.4"
   postgresql: "9.4"
 language: python
 language: python
 python:
 python:
-  - "2.7"
-  - "3.4"
-  - "3.5"
   - "3.6"
   - "3.6"
 install:
 install:
   - pip install -U pip setuptools
   - pip install -U pip setuptools

+ 1 - 1
Dockerfile

@@ -1,7 +1,7 @@
 # This dockerfile is only meant for local development of Misago
 # This dockerfile is only meant for local development of Misago
 # If you are looking for a proper docker setup for running Misago in production,
 # If you are looking for a proper docker setup for running Misago in production,
 # please use misago-docker instead
 # please use misago-docker instead
-FROM python:3.5
+FROM python:3.6
 
 
 ENV PYTHONUNBUFFERED 1
 ENV PYTHONUNBUFFERED 1
 ENV IN_MISAGO_DOCKER 1
 ENV IN_MISAGO_DOCKER 1

+ 2 - 2
README.rst

@@ -10,9 +10,9 @@ Misago
    :target: https://coveralls.io/github/rafalp/Misago?branch=master
    :target: https://coveralls.io/github/rafalp/Misago?branch=master
    :alt: Test Coverage
    :alt: Test Coverage
 
 
-.. image:: https://img.shields.io/badge/python-2.7%2C%203.4%2C%203.5%2C%203.6-blue.svg
+.. image:: https://img.shields.io/badge/python-3.6-blue.svg
    :target: https://travis-ci.org/rafalp/Misago
    :target: https://travis-ci.org/rafalp/Misago
-   :alt: Works on Python 2.7, 3.5, 3,6
+   :alt: Works on Python 3.6
 
 
 .. image:: https://img.shields.io/badge/chat-on_discord-7289da.svg
 .. image:: https://img.shields.io/badge/chat-on_discord-7289da.svg
    :target: https://discord.gg/fwvrZgB
    :target: https://discord.gg/fwvrZgB

+ 0 - 3
misago/acl/migrations/0001_initial.py

@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.contrib.postgres.fields import JSONField
 from django.contrib.postgres.fields import JSONField
 from django.db import migrations, models
 from django.db import migrations, models
 
 

+ 0 - 3
misago/acl/migrations/0002_acl_version_tracker.py

@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations
 from django.db import migrations
 
 
 from misago.acl.constants import ACL_CACHEBUSTER
 from misago.acl.constants import ACL_CACHEBUSTER

+ 0 - 3
misago/acl/migrations/0003_default_roles.py

@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations
 from django.db import migrations
 
 
 
 

+ 2 - 4
misago/acl/models.py

@@ -1,6 +1,5 @@
 from django.contrib.postgres.fields import JSONField
 from django.contrib.postgres.fields import JSONField
 from django.db import models
 from django.db import models
-from django.utils.encoding import python_2_unicode_compatible
 from django.utils.translation import ugettext as _
 from django.utils.translation import ugettext as _
 
 
 from . import version as acl_version
 from . import version as acl_version
@@ -10,7 +9,6 @@ def permissions_default():
     return {}
     return {}
 
 
 
 
-@python_2_unicode_compatible
 class BaseRole(models.Model):
 class BaseRole(models.Model):
     name = models.CharField(max_length=255)
     name = models.CharField(max_length=255)
     special_role = models.CharField(max_length=255, null=True, blank=True)
     special_role = models.CharField(max_length=255, null=True, blank=True)
@@ -25,11 +23,11 @@ class BaseRole(models.Model):
     def save(self, *args, **kwargs):
     def save(self, *args, **kwargs):
         if self.pk:
         if self.pk:
             acl_version.invalidate()
             acl_version.invalidate()
-        return super(BaseRole, self).save(*args, **kwargs)
+        return super().save(*args, **kwargs)
 
 
     def delete(self, *args, **kwargs):
     def delete(self, *args, **kwargs):
         acl_version.invalidate()
         acl_version.invalidate()
-        return super(BaseRole, self).delete(*args, **kwargs)
+        return super().delete(*args, **kwargs)
 
 
 
 
 class Role(BaseRole):
 class Role(BaseRole):

+ 2 - 3
misago/acl/tests/test_providers.py

@@ -1,7 +1,6 @@
 from types import ModuleType
 from types import ModuleType
 
 
 from django.test import TestCase
 from django.test import TestCase
-from django.utils import six
 
 
 from misago.acl.providers import PermissionProviders
 from misago.acl.providers import PermissionProviders
 from misago.conf import settings
 from misago.conf import settings
@@ -58,7 +57,7 @@ class PermissionProvidersTests(TestCase):
         self.assertEqual(len(providers_list), len(providers_setting))
         self.assertEqual(len(providers_list), len(providers_setting))
 
 
         for extension, module in providers_list:
         for extension, module in providers_list:
-            self.assertTrue(isinstance(extension, six.string_types))
+            self.assertTrue(isinstance(extension, str))
             self.assertEqual(type(module), ModuleType)
             self.assertEqual(type(module), ModuleType)
 
 
     def test_dict(self):
     def test_dict(self):
@@ -77,7 +76,7 @@ class PermissionProvidersTests(TestCase):
         self.assertEqual(len(providers_dict), len(providers_setting))
         self.assertEqual(len(providers_dict), len(providers_setting))
 
 
         for extension, module in providers_dict.items():
         for extension, module in providers_dict.items():
-            self.assertTrue(isinstance(extension, six.string_types))
+            self.assertTrue(isinstance(extension, str))
             self.assertEqual(type(module), ModuleType)
             self.assertEqual(type(module), ModuleType)
 
 
     def test_annotators(self):
     def test_annotators(self):

+ 0 - 3
misago/admin/tests/test_admin_views.py

@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.contrib.auth import get_user_model
 from django.contrib.auth import get_user_model
 from django.test import TestCase
 from django.test import TestCase
 from django.urls import reverse
 from django.urls import reverse

+ 1 - 1
misago/admin/testutils.py

@@ -5,7 +5,7 @@ from misago.users.testutils import SuperUserTestCase
 
 
 class AdminTestCase(SuperUserTestCase):
 class AdminTestCase(SuperUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(AdminTestCase, self).setUp()
+        super().setUp()
         self.login_admin(self.user)
         self.login_admin(self.user)
 
 
     def login_admin(self, user):
     def login_admin(self, user):

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

@@ -1,9 +1,10 @@
+from urllib.parse import urlencode
+
 from django.contrib import messages
 from django.contrib import messages
 from django.core.paginator import EmptyPage, Paginator
 from django.core.paginator import EmptyPage, Paginator
 from django.db import transaction
 from django.db import transaction
 from django.shortcuts import redirect
 from django.shortcuts import redirect
 from django.urls import reverse
 from django.urls import reverse
-from django.utils.six.moves.urllib.parse import urlencode
 from django.utils.translation import ugettext_lazy as _
 from django.utils.translation import ugettext_lazy as _
 
 
 from misago.core.exceptions import ExplicitFirstPage
 from misago.core.exceptions import ExplicitFirstPage

+ 3 - 3
misago/categories/forms.py

@@ -25,7 +25,7 @@ class AdminCategoryFieldMixin(object):
 
 
         kwargs.setdefault('queryset', queryset)
         kwargs.setdefault('queryset', queryset)
 
 
-        super(AdminCategoryFieldMixin, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
 
 
     def _get_level_indicator(self, obj):
     def _get_level_indicator(self, obj):
         level = getattr(obj, obj._mptt_meta.level_attr) - self.base_level
         level = getattr(obj, obj._mptt_meta.level_attr) - self.base_level
@@ -135,7 +135,7 @@ class CategoryFormBase(forms.ModelForm):
         return data
         return data
 
 
     def clean(self):
     def clean(self):
-        data = super(CategoryFormBase, self).clean()
+        data = super().clean()
         self.instance.set_name(data.get('name'))
         self.instance.set_name(data.get('name'))
         return data
         return data
 
 
@@ -185,7 +185,7 @@ class DeleteCategoryFormBase(forms.ModelForm):
         fields = []
         fields = []
 
 
     def clean(self):
     def clean(self):
-        data = super(DeleteCategoryFormBase, self).clean()
+        data = super().clean()
 
 
         if data.get('move_threads_to'):
         if data.get('move_threads_to'):
             if data['move_threads_to'].pk == self.instance.pk:
             if data['move_threads_to'].pk == self.instance.pk:

+ 0 - 3
misago/categories/migrations/0001_initial.py

@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 import mptt.fields
 import mptt.fields
 
 
 import django.db.models.deletion
 import django.db.models.deletion

+ 0 - 3
misago/categories/migrations/0002_default_categories.py

@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations
 from django.db import migrations
 
 
 from misago.core.utils import slugify
 from misago.core.utils import slugify

+ 0 - 3
misago/categories/migrations/0003_categories_roles.py

@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations
 from django.db import migrations
 
 
 
 

+ 0 - 3
misago/categories/migrations/0004_category_last_thread.py

@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 import django.db.models.deletion
 import django.db.models.deletion
 from django.db import migrations, models
 from django.db import migrations, models
 
 

+ 0 - 3
misago/categories/migrations/0005_auto_20170303_2027.py

@@ -1,7 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.10.5 on 2017-03-03 20:27
 # Generated by Django 1.10.5 on 2017-03-03 20:27
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 from django.db import migrations, models
 
 
 
 

+ 0 - 3
misago/categories/migrations/0006_moderation_queue_roles.py

@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations
 from django.db import migrations
 
 
 
 

+ 0 - 3
misago/categories/migrations/0007_best_answers_roles.py

@@ -1,7 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.9 on 2018-03-18 20:40
 # Generated by Django 1.11.9 on 2018-03-18 20:40
-from __future__ import unicode_literals
-
 from django.db import migrations
 from django.db import migrations
 
 
 
 

+ 2 - 5
misago/categories/models.py

@@ -2,8 +2,6 @@ from mptt.managers import TreeManager
 from mptt.models import MPTTModel, TreeForeignKey
 from mptt.models import MPTTModel, TreeForeignKey
 
 
 from django.db import models
 from django.db import models
-from django.utils import six
-from django.utils.encoding import python_2_unicode_compatible
 
 
 from misago.acl import version as acl_version
 from misago.acl import version as acl_version
 from misago.acl.models import BaseRole
 from misago.acl.models import BaseRole
@@ -58,7 +56,6 @@ class CategoryManager(TreeManager):
         cache.delete(CACHE_NAME)
         cache.delete(CACHE_NAME)
 
 
 
 
-@python_2_unicode_compatible
 class Category(MPTTModel):
 class Category(MPTTModel):
     parent = TreeForeignKey(
     parent = TreeForeignKey(
         'self',
         'self',
@@ -110,7 +107,7 @@ class Category(MPTTModel):
     objects = CategoryManager()
     objects = CategoryManager()
 
 
     def __str__(self):
     def __str__(self):
-        return six.text_type(self.thread_type.get_category_name(self))
+        return str(self.thread_type.get_category_name(self))
 
 
     @property
     @property
     def thread_type(self):
     def thread_type(self):
@@ -119,7 +116,7 @@ class Category(MPTTModel):
     def delete(self, *args, **kwargs):
     def delete(self, *args, **kwargs):
         Category.objects.clear_cache()
         Category.objects.clear_cache()
         acl_version.invalidate()
         acl_version.invalidate()
-        return super(Category, self).delete(*args, **kwargs)
+        return super().delete(*args, **kwargs)
 
 
     def synchronize(self):
     def synchronize(self):
         threads_queryset = self.thread_set.filter(is_hidden=False, is_unapproved=False)
         threads_queryset = self.thread_set.filter(is_hidden=False, is_unapproved=False)

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

@@ -342,7 +342,7 @@ class CategoryAdminDeleteViewTests(CategoryAdminTestCase):
           + Category F
           + Category F
         """
         """
 
 
-        super(CategoryAdminDeleteViewTests, self).setUp()
+        super().setUp()
 
 
         self.root = Category.objects.root_category()
         self.root = Category.objects.root_category()
         self.first_category = Category.objects.get(slug='first-category')
         self.first_category = Category.objects.get(slug='first-category')

+ 1 - 1
misago/categories/tests/test_category_model.py

@@ -47,7 +47,7 @@ class CategoryManagerTests(MisagoTestCase):
 
 
 class CategoryModelTests(MisagoTestCase):
 class CategoryModelTests(MisagoTestCase):
     def setUp(self):
     def setUp(self):
-        super(CategoryModelTests, self).setUp()
+        super().setUp()
 
 
         self.category = Category.objects.all_categories()[:1][0]
         self.category = Category.objects.all_categories()[:1][0]
 
 

+ 2 - 1
misago/categories/tests/test_fixcategoriestree.py

@@ -1,6 +1,7 @@
+from io import StringIO
+
 from django.core.management import call_command
 from django.core.management import call_command
 from django.test import TestCase
 from django.test import TestCase
-from django.utils.six import StringIO
 
 
 from misago.categories.management.commands import fixcategoriestree
 from misago.categories.management.commands import fixcategoriestree
 from misago.categories.models import Category
 from misago.categories.models import Category

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

@@ -1,9 +1,9 @@
 from datetime import timedelta
 from datetime import timedelta
+from io import StringIO
 
 
 from django.core.management import call_command
 from django.core.management import call_command
 from django.test import TestCase
 from django.test import TestCase
 from django.utils import timezone
 from django.utils import timezone
-from django.utils.six import StringIO
 
 
 from misago.categories.management.commands import prunecategories
 from misago.categories.management.commands import prunecategories
 from misago.categories.models import Category
 from misago.categories.models import Category

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

@@ -1,6 +1,7 @@
+from io import StringIO
+
 from django.core.management import call_command
 from django.core.management import call_command
 from django.test import TestCase
 from django.test import TestCase
-from django.utils.six import StringIO
 
 
 from misago.categories.management.commands import synchronizecategories
 from misago.categories.management.commands import synchronizecategories
 from misago.categories.models import Category
 from misago.categories.models import Category

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

@@ -21,7 +21,7 @@ class CategoriesUtilsTests(AuthenticatedUserTestCase):
           + Subcategory F
           + Subcategory F
         """
         """
 
 
-        super(CategoriesUtilsTests, self).setUp()
+        super().setUp()
         threadstore.clear()
         threadstore.clear()
 
 
         self.root = Category.objects.root_category()
         self.root = Category.objects.root_category()

+ 1 - 1
misago/categories/tests/test_views.py

@@ -52,7 +52,7 @@ class CategoryViewsTests(AuthenticatedUserTestCase):
 
 
 class CategoryAPIViewsTests(AuthenticatedUserTestCase):
 class CategoryAPIViewsTests(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(CategoryAPIViewsTests, self).setUp()
+        super().setUp()
 
 
         self.category = Category.objects.get(slug='first-category')
         self.category = Category.objects.get(slug='first-category')
 
 

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

@@ -17,7 +17,7 @@ class CategoryAdmin(generic.AdminBaseMixin):
     message_404 = _("Requested category does not exist.")
     message_404 = _("Requested category does not exist.")
 
 
     def get_target(self, kwargs):
     def get_target(self, kwargs):
-        target = super(CategoryAdmin, self).get_target(kwargs)
+        target = super().get_target(kwargs)
 
 
         threads_tree_id = trees_map.get_tree_id_for_root(THREADS_ROOT_NAME)
         threads_tree_id = trees_map.get_tree_id_for_root(THREADS_ROOT_NAME)
 
 

+ 3 - 4
misago/conf/hydrators.py

@@ -1,8 +1,7 @@
-import six
-
+# fixme: rename this moduleto serialize
 
 
 def hydrate_string(dry_value):
 def hydrate_string(dry_value):
-    return six.text_type(dry_value) if dry_value else ''
+    return str(dry_value) if dry_value else ''
 
 
 
 
 def dehydrate_string(wet_value):
 def dehydrate_string(wet_value):
@@ -22,7 +21,7 @@ def hydrate_int(dry_value):
 
 
 
 
 def dehydrate_int(wet_value):
 def dehydrate_int(wet_value):
-    return six.text_type(wet_value)
+    return str(wet_value)
 
 
 
 
 def hydrate_list(dry_value):
 def hydrate_list(dry_value):

+ 0 - 3
misago/conf/migrations/0001_initial.py

@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 import django.db.models.deletion
 import django.db.models.deletion
 from django.contrib.postgres.fields import JSONField
 from django.contrib.postgres.fields import JSONField
 from django.db import migrations, models
 from django.db import migrations, models

+ 4 - 4
misago/core/apipatch.py

@@ -98,16 +98,16 @@ class ApiPatch(object):
 
 
     def validate_action(self, action):
     def validate_action(self, action):
         if not action.get('op'):
         if not action.get('op'):
-            raise InvalidAction(u"undefined op")
+            raise InvalidAction("undefined op")
 
 
         if action.get('op') not in ALLOWED_OPS:
         if action.get('op') not in ALLOWED_OPS:
-            raise InvalidAction(u'"%s" op is unsupported' % action.get('op'))
+            raise InvalidAction('"%s" op is unsupported' % action.get('op'))
 
 
         if not action.get('path'):
         if not action.get('path'):
-            raise InvalidAction(u'"%s" op has to specify path' % action.get('op'))
+            raise InvalidAction('"%s" op has to specify path' % action.get('op'))
 
 
         if 'value' not in action:
         if 'value' not in action:
-            raise InvalidAction(u'"%s" op has to specify value' % action.get('op'))
+            raise InvalidAction('"%s" op has to specify value' % action.get('op'))
 
 
     def dispatch_action(self, patch, request, target, action):
     def dispatch_action(self, patch, request, target, action):
         for handler in self._actions:
         for handler in self._actions:

+ 1 - 2
misago/core/exceptionhandler.py

@@ -3,7 +3,6 @@ from rest_framework.views import exception_handler as rest_exception_handler
 from django.core.exceptions import PermissionDenied
 from django.core.exceptions import PermissionDenied
 from django.http import Http404, HttpResponsePermanentRedirect, JsonResponse
 from django.http import Http404, HttpResponsePermanentRedirect, JsonResponse
 from django.urls import reverse
 from django.urls import reverse
-from django.utils import six
 from social_core.exceptions import SocialAuthBaseException
 from social_core.exceptions import SocialAuthBaseException
 from social_core.utils import social_logger
 from social_core.utils import social_logger
 
 
@@ -29,7 +28,7 @@ def is_misago_exception(exception):
 def handle_ajax_error(request, exception):
 def handle_ajax_error(request, exception):
     json = {
     json = {
         'is_error': 1,
         'is_error': 1,
-        'message': six.text_type(exception.message),
+        'message': str(exception.message),
     }
     }
     return JsonResponse(json, status=exception.code)
     return JsonResponse(json, status=exception.code)
 
 

+ 0 - 3
misago/core/migrations/0001_initial.py

@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 from django.db import migrations, models
 
 
 
 

+ 0 - 3
misago/core/migrations/0002_basic_settings.py

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

+ 4 - 6
misago/core/pgutils.py

@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
 from django.core.paginator import Paginator
 from django.core.paginator import Paginator
 from django.db.models import Index
 from django.db.models import Index
 
 
@@ -13,7 +11,7 @@ class PgPartialIndex(Index):
             raise ValueError('partial index requires WHERE clause')
             raise ValueError('partial index requires WHERE clause')
         self.where = where
         self.where = where
 
 
-        super(PgPartialIndex, self).__init__(fields, name)
+        super().__init__(fields, name)
 
 
     def set_name_with_model(self, model):
     def set_name_with_model(self, model):
         table_name = model._meta.db_table
         table_name = model._meta.db_table
@@ -52,15 +50,15 @@ class PgPartialIndex(Index):
                 'where': "'{}'".format(', '.join(where_items)),
                 'where': "'{}'".format(', '.join(where_items)),
             }
             }
         else:
         else:
-            return super(PgPartialIndex, self).__repr__()
+            return super().__repr__()
 
 
     def deconstruct(self):
     def deconstruct(self):
-        path, args, kwargs = super(PgPartialIndex, self).deconstruct()
+        path, args, kwargs = super().deconstruct()
         kwargs['where'] = self.where
         kwargs['where'] = self.where
         return path, args, kwargs
         return path, args, kwargs
 
 
     def get_sql_create_template_values(self, model, schema_editor, using):
     def get_sql_create_template_values(self, model, schema_editor, using):
-        parameters = super(PgPartialIndex, self).get_sql_create_template_values(
+        parameters = super().get_sql_create_template_values(
             model, schema_editor, '')
             model, schema_editor, '')
         parameters['extra'] = self.get_sql_extra(model, schema_editor)
         parameters['extra'] = self.get_sql_extra(model, schema_editor)
         return parameters
         return parameters

+ 1 - 3
misago/core/shortcuts.py

@@ -2,8 +2,6 @@ from rest_framework.response import Response
 
 
 from django.http import Http404
 from django.http import Http404
 
 
-import six
-
 
 
 def paginate(
 def paginate(
         object_list,
         object_list,
@@ -82,7 +80,7 @@ def validate_slug(model, slug):
 
 
 
 
 def get_int_or_404(value):
 def get_int_or_404(value):
-    if six.text_type(value).isdigit():
+    if str(value).isdigit():
         return int(value)
         return int(value)
     else:
     else:
         raise Http404()
         raise Http404()

+ 1 - 2
misago/core/slugify.py

@@ -1,10 +1,9 @@
 from unidecode import unidecode
 from unidecode import unidecode
 
 
 from django.template.defaultfilters import slugify as django_slugify
 from django.template.defaultfilters import slugify as django_slugify
-from django.utils import six
 
 
 
 
 def default(string):
 def default(string):
-    string = six.text_type(string)
+    string = str(string)
     string = unidecode(string)
     string = unidecode(string)
     return django_slugify(string.replace('_', ' ').strip())
     return django_slugify(string.replace('_', ' ').strip())

+ 1 - 1
misago/core/templatetags/misago_absoluteurl.py

@@ -20,4 +20,4 @@ def absoluteurl(url_or_name, *args, **kwargs):
         if not url_or_name.startswith('/'):
         if not url_or_name.startswith('/'):
             return url_or_name
             return url_or_name
     
     
-    return u'{}{}'.format(absolute_url_prefix, url_or_name)
+    return '{}{}'.format(absolute_url_prefix, url_or_name)

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

@@ -8,9 +8,9 @@ register = template.Library()
 @register.simple_tag
 @register.simple_tag
 def pagetitle(title, **kwargs):
 def pagetitle(title, **kwargs):
     if 'page' in kwargs and kwargs['page'] > 1:
     if 'page' in kwargs and kwargs['page'] > 1:
-        title += u" (%s)" % (_(u"page: %(page)s") % {'page': kwargs['page']})
+        title += " (%s)" % (_("page: %(page)s") % {'page': kwargs['page']})
 
 
     if 'parent' in kwargs:
     if 'parent' in kwargs:
-        title += u" | %s" % kwargs['parent']
+        title += " | %s" % kwargs['parent']
 
 
     return title
     return title

+ 1 - 1
misago/core/testproject/searchfilters.py

@@ -1,2 +1,2 @@
 def test_filter(search):
 def test_filter(search):
-    return search.replace(u'MMM', u'Marines, Marauders and Medics')
+    return search.replace('MMM', 'Marines, Marauders and Medics')

+ 5 - 5
misago/core/tests/test_apipatch.py

@@ -91,19 +91,19 @@ class ApiPatchTests(TestCase):
             try:
             try:
                 patch.validate_action(action)
                 patch.validate_action(action)
             except InvalidAction as e:
             except InvalidAction as e:
-                self.assertEqual(e.args[0], u"undefined op")
+                self.assertEqual(e.args[0], "undefined op")
 
 
         # unsupported op
         # unsupported op
         try:
         try:
             patch.validate_action({'op': 'nope'})
             patch.validate_action({'op': 'nope'})
         except InvalidAction as e:
         except InvalidAction as e:
-            self.assertEqual(e.args[0], u'"nope" op is unsupported')
+            self.assertEqual(e.args[0], '"nope" op is unsupported')
 
 
         # op lacking patch
         # op lacking patch
         try:
         try:
             patch.validate_action({'op': 'add'})
             patch.validate_action({'op': 'add'})
         except InvalidAction as e:
         except InvalidAction as e:
-            self.assertEqual(e.args[0], u'"add" op has to specify path')
+            self.assertEqual(e.args[0], '"add" op has to specify path')
 
 
         # op lacking value
         # op lacking value
         try:
         try:
@@ -112,7 +112,7 @@ class ApiPatchTests(TestCase):
                 'path': 'yolo',
                 'path': 'yolo',
             })
             })
         except InvalidAction as e:
         except InvalidAction as e:
-            self.assertEqual(e.args[0], u'"add" op has to specify value')
+            self.assertEqual(e.args[0], '"add" op has to specify value')
 
 
         # empty value is allowed
         # empty value is allowed
         try:
         try:
@@ -122,7 +122,7 @@ class ApiPatchTests(TestCase):
                 'value': '',
                 'value': '',
             })
             })
         except InvalidAction as e:
         except InvalidAction as e:
-            self.assertEqual(e.args[0], u'"add" op has to specify value')
+            self.assertEqual(e.args[0], '"add" op has to specify value')
 
 
     def test_dispatch_action(self):
     def test_dispatch_action(self):
         """dispatch_action calls specified actions"""
         """dispatch_action calls specified actions"""

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

@@ -20,7 +20,7 @@ class CacheBusterTests(MisagoTestCase):
 
 
 class CacheBusterCacheTests(MisagoTestCase):
 class CacheBusterCacheTests(MisagoTestCase):
     def setUp(self):
     def setUp(self):
-        super(CacheBusterCacheTests, self).setUp()
+        super().setUp()
 
 
         self.cache_name = 'eric_the_fish'
         self.cache_name = 'eric_the_fish'
         cachebuster.register(self.cache_name)
         cachebuster.register(self.cache_name)

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

@@ -1,7 +1,6 @@
 import warnings
 import warnings
 
 
 from django.test import TestCase
 from django.test import TestCase
-from django.utils import six
 
 
 from misago.core.deprecations import RemovedInMisagoWarning, warn
 from misago.core.deprecations import RemovedInMisagoWarning, warn
 
 
@@ -13,5 +12,5 @@ class DeprecationsTests(TestCase):
             warn("test warning")
             warn("test warning")
 
 
             self.assertEqual(len(warning), 1)
             self.assertEqual(len(warning), 1)
-            self.assertEqual(six.text_type(warning[0].message), "test warning")
+            self.assertEqual(str(warning[0].message), "test warning")
             self.assertTrue(issubclass(warning[0].category, RemovedInMisagoWarning))
             self.assertTrue(issubclass(warning[0].category, RemovedInMisagoWarning))

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

@@ -23,6 +23,6 @@ class YesNoSwitchTests(TestCase):
 
 
     def test_dontstripme_input_is_ignored(self):
     def test_dontstripme_input_is_ignored(self):
         """YesNoSwitch returns valid values for invalid input"""
         """YesNoSwitch returns valid values for invalid input"""
-        form = YesNoForm({'test_field': u'221'})
+        form = YesNoForm({'test_field': '221'})
         form.full_clean()
         form.full_clean()
         self.assertFalse(form.cleaned_data.get('test_field'))
         self.assertFalse(form.cleaned_data.get('test_field'))

+ 0 - 4
misago/core/tests/test_utils.py

@@ -1,11 +1,7 @@
-#-*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.core.exceptions import PermissionDenied
 from django.core.exceptions import PermissionDenied
 from django.test import TestCase
 from django.test import TestCase
 from django.test.client import RequestFactory
 from django.test.client import RequestFactory
 from django.urls import reverse
 from django.urls import reverse
-from django.utils import six
 
 
 from misago.core.utils import (
 from misago.core.utils import (
     clean_return_path, format_plaintext_for_html, is_referer_local, is_request_to_misago,
     clean_return_path, format_plaintext_for_html, is_referer_local, is_request_to_misago,

+ 2 - 2
misago/core/testutils.py

@@ -12,9 +12,9 @@ class MisagoTestCase(TestCase):
         threadstore.clear()
         threadstore.clear()
 
 
     def setUp(self):
     def setUp(self):
-        super(MisagoTestCase, self).setUp()
+        super().setUp()
         self.clear_state()
         self.clear_state()
 
 
     def tearDown(self):
     def tearDown(self):
         self.clear_state()
         self.clear_state()
-        super(MisagoTestCase, self).tearDown()
+        super().tearDown()

+ 1 - 2
misago/faker/englishcorpus.py

@@ -1,4 +1,3 @@
-import codecs
 import os
 import os
 import random
 import random
 
 
@@ -12,7 +11,7 @@ class EnglishCorpus(object):
         self._previous = None
         self._previous = None
 
 
         self.phrases = []
         self.phrases = []
-        with codecs.open(phrases_file, "r", "utf-8") as f:
+        with open(phrases_file, "r") as f:
             for phrase in [l.strip() for l in f.readlines()]:
             for phrase in [l.strip() for l in f.readlines()]:
                 if min_length and len(phrase) < min_length:
                 if min_length and len(phrase) < min_length:
                     continue
                     continue

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

@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
 import random
 import random
 import time
 import time
 
 

+ 2 - 2
misago/legal/forms.py

@@ -40,7 +40,7 @@ class AgreementForm(forms.ModelForm):
         fields = ['type', 'title', 'link', 'text', 'is_active']
         fields = ['type', 'title', 'link', 'text', 'is_active']
 
 
     def clean(self):
     def clean(self):
-        data = super(AgreementForm, self).clean()
+        data = super().clean()
 
 
         if not data.get('link') and not data.get('text'):
         if not data.get('link') and not data.get('text'):
             raise forms.ValidationError(_("Please fill in agreement link or text."))
             raise forms.ValidationError(_("Please fill in agreement link or text."))
@@ -48,7 +48,7 @@ class AgreementForm(forms.ModelForm):
         return data
         return data
 
 
     def save(self):
     def save(self):
-        agreement = super(AgreementForm, self).save()
+        agreement = super().save()
         if agreement.is_active:
         if agreement.is_active:
             set_agreement_as_active(agreement)
             set_agreement_as_active(agreement)
         Agreement.objects.invalidate_cache()
         Agreement.objects.invalidate_cache()

+ 0 - 3
misago/legal/migrations/0001_initial.py

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

+ 0 - 3
misago/legal/migrations/0002_agreement_useragreement.py

@@ -1,7 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.15 on 2018-08-15 20:58
 # Generated by Django 1.11.15 on 2018-08-15 20:58
-from __future__ import unicode_literals
-
 from django.conf import settings
 from django.conf import settings
 from django.db import migrations, models
 from django.db import migrations, models
 import django.db.models.deletion
 import django.db.models.deletion

+ 0 - 3
misago/legal/migrations/0003_create_agreements_from_settings.py

@@ -1,7 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.15 on 2018-08-16 14:22
 # Generated by Django 1.11.15 on 2018-08-16 14:22
-from __future__ import unicode_literals
-
 from django.db import migrations
 from django.db import migrations
 
 
 from misago.conf.migrationutils import migrate_settings_group
 from misago.conf.migrationutils import migrate_settings_group

+ 1 - 1
misago/legal/tests/test_api.py

@@ -8,7 +8,7 @@ from misago.users.testutils import AuthenticatedUserTestCase
 
 
 class SubmitAgreementTests(AuthenticatedUserTestCase):
 class SubmitAgreementTests(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(SubmitAgreementTests, self).setUp()
+        super().setUp()
 
 
         self.agreement = Agreement.objects.create(
         self.agreement = Agreement.objects.create(
             type=Agreement.TYPE_TOS,
             type=Agreement.TYPE_TOS,

+ 2 - 2
misago/legal/tests/test_context_processors.py

@@ -16,7 +16,7 @@ class MockRequest(object):
 
 
 class PrivacyPolicyTests(AuthenticatedUserTestCase):
 class PrivacyPolicyTests(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(PrivacyPolicyTests, self).setUp()
+        super().setUp()
 
 
         Agreement.objects.invalidate_cache()
         Agreement.objects.invalidate_cache()
 
 
@@ -102,7 +102,7 @@ class PrivacyPolicyTests(AuthenticatedUserTestCase):
 
 
 class TermsOfServiceTests(AuthenticatedUserTestCase):
 class TermsOfServiceTests(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(TermsOfServiceTests, self).setUp()
+        super().setUp()
         
         
         Agreement.objects.invalidate_cache()
         Agreement.objects.invalidate_cache()
 
 

+ 1 - 1
misago/legal/tests/test_required_agreement.py

@@ -6,7 +6,7 @@ from misago.users.testutils import AuthenticatedUserTestCase
 
 
 class RequiredAgreementTests(AuthenticatedUserTestCase):
 class RequiredAgreementTests(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(RequiredAgreementTests, self).setUp()
+        super().setUp()
 
 
         self.test_link = reverse('misago:index')
         self.test_link = reverse('misago:index')
 
 

+ 3 - 3
misago/legal/views/admin.py

@@ -40,7 +40,7 @@ class AgreementsList(AgreementAdmin, generic.ListView):
     }, )
     }, )
 
 
     def get_queryset(self):
     def get_queryset(self):
-        qs = super(AgreementsList, self).get_queryset()
+        qs = super().get_queryset()
         return qs.select_related()
         return qs.select_related()
 
 
     def action_delete(self, request, items):
     def action_delete(self, request, items):
@@ -53,7 +53,7 @@ class NewAgreement(AgreementAdmin, generic.ModelFormView):
     message_submit = _('New agreement "%(title)s" has been saved.')
     message_submit = _('New agreement "%(title)s" has been saved.')
     
     
     def handle_form(self, form, request, target):
     def handle_form(self, form, request, target):
-        super(NewAgreement, self).handle_form(form, request, target)
+        super().handle_form(form, request, target)
 
 
         form.instance.set_created_by(request.user)
         form.instance.set_created_by(request.user)
         form.instance.save()
         form.instance.save()
@@ -63,7 +63,7 @@ class EditAgreement(AgreementAdmin, generic.ModelFormView):
     message_submit = _('Agreement "%(title)s" has been edited.')
     message_submit = _('Agreement "%(title)s" has been edited.')
 
 
     def handle_form(self, form, request, target):
     def handle_form(self, form, request, target):
-        super(EditAgreement, self).handle_form(form, request, target)
+        super().handle_form(form, request, target)
 
 
         form.instance.last_modified_on = timezone.now()
         form.instance.last_modified_on = timezone.now()
         form.instance.set_last_modified_by(request.user)
         form.instance.set_last_modified_by(request.user)

+ 1 - 3
misago/markup/bbcode/blocks.py

@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
 import re
 import re
 
 
 import markdown
 import markdown
@@ -68,7 +66,7 @@ class QuotePreprocessor(Preprocessor):
 
 
 class QuoteBlockProcessor(BlockProcessor):
 class QuoteBlockProcessor(BlockProcessor):
     def __init__(self, *args, **kwargs):
     def __init__(self, *args, **kwargs):
-        super(QuoteBlockProcessor, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
         self._title = None
         self._title = None
         self._quote = 0
         self._quote = 0
         self._children = []
         self._children = []

+ 1 - 3
misago/markup/checksums.py

@@ -22,12 +22,10 @@ in char fields with max_length=64
 """
 """
 from hashlib import sha256
 from hashlib import sha256
 
 
-from django.utils import six
-
 
 
 def make_checksum(parsed, unique_values=None):
 def make_checksum(parsed, unique_values=None):
     unique_values = unique_values or []
     unique_values = unique_values or []
-    seeds = [parsed] + [six.text_type(v) for v in unique_values]
+    seeds = [parsed] + [str(v) for v in unique_values]
 
 
     return sha256('+'.join(seeds).encode("utf-8")).hexdigest()
     return sha256('+'.join(seeds).encode("utf-8")).hexdigest()
 
 

+ 0 - 2
misago/markup/finalise.py

@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
 import re
 import re
 
 
 from django.utils.translation import ugettext as _
 from django.utils.translation import ugettext as _

+ 2 - 3
misago/markup/mentions.py

@@ -3,7 +3,6 @@ import re
 from bs4 import BeautifulSoup
 from bs4 import BeautifulSoup
 
 
 from django.contrib.auth import get_user_model
 from django.contrib.auth import get_user_model
-from django.utils import six
 
 
 
 
 SUPPORTED_TAGS = ('h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'div', 'p')
 SUPPORTED_TAGS = ('h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'div', 'p')
@@ -26,7 +25,7 @@ def add_mentions(request, result):
     for element in elements:
     for element in elements:
         add_mentions_to_element(request, element, mentions_dict)
         add_mentions_to_element(request, element, mentions_dict)
 
 
-    result['parsed_text'] = six.text_type(soup.body)[6:-7].strip()
+    result['parsed_text'] = str(soup.body)[6:-7].strip()
     result['mentions'] = list(filter(bool, mentions_dict.values()))
     result['mentions'] = list(filter(bool, mentions_dict.values()))
 
 
 
 
@@ -59,7 +58,7 @@ def parse_string(request, element, mentions_dict):
 
 
         if mentions_dict[username]:
         if mentions_dict[username]:
             user = mentions_dict[username]
             user = mentions_dict[username]
-            return u'<a href="{}">@{}</a>'.format(user.get_absolute_url(), user.username)
+            return '<a href="{}">@{}</a>'.format(user.get_absolute_url(), user.username)
         else:
         else:
             # we've failed to resolve user for username
             # we've failed to resolve user for username
             return matchobj.group(0)
             return matchobj.group(0)

+ 7 - 6
misago/markup/parser.py

@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
 import warnings
 import warnings
 
 
 import bleach
 import bleach
@@ -10,7 +8,6 @@ from markdown.extensions.fenced_code import FencedCodeExtension
 
 
 from django.http import Http404
 from django.http import Http404
 from django.urls import resolve
 from django.urls import resolve
-from django.utils import six
 
 
 from misago.conf import settings
 from misago.conf import settings
 
 
@@ -188,7 +185,7 @@ def clean_links(request, result, force_shva=False):
             img['src'] = assert_link_prefix(img['src'])
             img['src'] = assert_link_prefix(img['src'])
 
 
     # [6:-7] trims <body></body> wrap
     # [6:-7] trims <body></body> wrap
-    result['parsed_text'] = six.text_type(soup.body)[6:-7]
+    result['parsed_text'] = str(soup.body)[6:-7]
 
 
 
 
 def is_internal_link(link, host):
 def is_internal_link(link, host):
@@ -250,6 +247,10 @@ def clean_attachment_link(link, force_shva=False):
 
 
 
 
 def minify_result(result):
 def minify_result(result):
+    result['parsed_text'] = html_minify(result['parsed_text'])
+    result['parsed_text'] = strip_html_head_body(result['parsed_text'])
+
+
+def strip_html_head_body(parsed_text):
     # [25:-14] trims <html><head></head><body> and </body></html>
     # [25:-14] trims <html><head></head><body> and </body></html>
-    result['parsed_text'] = html_minify(result['parsed_text'].encode('utf-8'))
-    result['parsed_text'] = result['parsed_text'][25:-14]
+    return parsed_text[25:-14]

+ 1 - 3
misago/markup/pipeline.py

@@ -2,8 +2,6 @@ from importlib import import_module
 
 
 from bs4 import BeautifulSoup
 from bs4 import BeautifulSoup
 
 
-from django.utils import six
-
 from misago.conf import settings
 from misago.conf import settings
 
 
 
 
@@ -26,7 +24,7 @@ class MarkupPipeline(object):
                 hook = getattr(module, 'clean_parsed')
                 hook = getattr(module, 'clean_parsed')
                 hook.process_result(result, soup)
                 hook.process_result(result, soup)
 
 
-        souped_text = six.text_type(soup.body).strip()[6:-7]
+        souped_text = str(soup.body).strip()[6:-7]
         result['parsed_text'] = souped_text.strip()
         result['parsed_text'] = souped_text.strip()
         return result
         return result
 
 

+ 1 - 4
misago/markup/tests/test_api.py

@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.urls import reverse
 from django.urls import reverse
 
 
 from misago.users.testutils import AuthenticatedUserTestCase
 from misago.users.testutils import AuthenticatedUserTestCase
@@ -8,7 +5,7 @@ from misago.users.testutils import AuthenticatedUserTestCase
 
 
 class ParseMarkupApiTests(AuthenticatedUserTestCase):
 class ParseMarkupApiTests(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(ParseMarkupApiTests, self).setUp()
+        super().setUp()
 
 
         self.api_link = reverse('misago:api:parse-markup')
         self.api_link = reverse('misago:api:parse-markup')
 
 

+ 0 - 3
misago/markup/tests/test_finalise.py

@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.test import TestCase
 from django.test import TestCase
 
 
 from misago.markup.finalise import finalise_markup
 from misago.markup.finalise import finalise_markup

+ 0 - 3
misago/markup/tests/test_parser.py

@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.contrib.auth import get_user_model
 from django.contrib.auth import get_user_model
 from django.test import TestCase
 from django.test import TestCase
 
 

+ 0 - 3
misago/readtracker/migrations/0001_initial.py

@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 import django.db.models.deletion
 import django.db.models.deletion
 from django.conf import settings
 from django.conf import settings
 from django.db import migrations, models
 from django.db import migrations, models

+ 0 - 3
misago/readtracker/migrations/0002_postread.py

@@ -1,7 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.5 on 2017-10-07 14:32
 # Generated by Django 1.11.5 on 2017-10-07 14:32
-from __future__ import unicode_literals
-
 from django.conf import settings
 from django.conf import settings
 from django.db import migrations, models
 from django.db import migrations, models
 import django.db.models.deletion
 import django.db.models.deletion

+ 0 - 3
misago/readtracker/migrations/0003_migrate_reads_to_posts.py

@@ -1,7 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.5 on 2017-10-07 14:49
 # Generated by Django 1.11.5 on 2017-10-07 14:49
-from __future__ import unicode_literals
-
 from datetime import timedelta
 from datetime import timedelta
 
 
 from django.db import migrations
 from django.db import migrations

+ 0 - 3
misago/readtracker/migrations/0004_auto_20171015_2010.py

@@ -1,7 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.5 on 2017-10-15 20:10
 # Generated by Django 1.11.5 on 2017-10-15 20:10
-from __future__ import unicode_literals
-
 from django.db import migrations
 from django.db import migrations
 
 
 
 

+ 1 - 1
misago/readtracker/tests/test_clearreadtracker.py

@@ -1,10 +1,10 @@
 from datetime import timedelta
 from datetime import timedelta
+from io import StringIO
 
 
 from django.contrib.auth import get_user_model
 from django.contrib.auth import get_user_model
 from django.core.management import call_command
 from django.core.management import call_command
 from django.test import TestCase
 from django.test import TestCase
 from django.utils import timezone
 from django.utils import timezone
-from django.utils.six import StringIO
 
 
 from misago.categories.models import Category
 from misago.categories.models import Category
 from misago.conf import settings
 from misago.conf import settings

+ 1 - 2
misago/search/api.py

@@ -5,7 +5,6 @@ from rest_framework.response import Response
 
 
 from django.core.exceptions import PermissionDenied
 from django.core.exceptions import PermissionDenied
 from django.urls import reverse
 from django.urls import reverse
-from django.utils import six
 from django.utils.translation import ugettext as _
 from django.utils.translation import ugettext as _
 
 
 from misago.core.shortcuts import get_int_or_404
 from misago.core.shortcuts import get_int_or_404
@@ -24,7 +23,7 @@ def search(request, search_provider=None):
     for provider in allowed_providers:
     for provider in allowed_providers:
         provider_data = {
         provider_data = {
             'id': provider.url,
             'id': provider.url,
-            'name': six.text_type(provider.name),
+            'name': str(provider.name),
             'icon': provider.icon,
             'icon': provider.icon,
             'url': reverse('misago:search', kwargs={'search_provider': provider.url}),
             'url': reverse('misago:search', kwargs={'search_provider': provider.url}),
             'api': reverse('misago:api:search', kwargs={'search_provider': provider.url}),
             'api': reverse('misago:api:search', kwargs={'search_provider': provider.url}),

+ 1 - 2
misago/search/context_processors.py

@@ -1,6 +1,5 @@
 from django.core.exceptions import PermissionDenied
 from django.core.exceptions import PermissionDenied
 from django.urls import reverse
 from django.urls import reverse
-from django.utils import six
 
 
 from .searchproviders import searchproviders
 from .searchproviders import searchproviders
 
 
@@ -25,7 +24,7 @@ def search_providers(request):
     for provider in allowed_providers:
     for provider in allowed_providers:
         request.frontend_context['SEARCH_PROVIDERS'].append({
         request.frontend_context['SEARCH_PROVIDERS'].append({
             'id': provider.url,
             'id': provider.url,
-            'name': six.text_type(provider.name),
+            'name': str(provider.name),
             'icon': provider.icon,
             'icon': provider.icon,
             'url': reverse('misago:search', kwargs={'search_provider': provider.url}),
             'url': reverse('misago:search', kwargs={'search_provider': provider.url}),
             'api': reverse('misago:api:search', kwargs={'search_provider': provider.url}),
             'api': reverse('misago:api:search', kwargs={'search_provider': provider.url}),

+ 3 - 4
misago/search/tests/test_api.py

@@ -1,5 +1,4 @@
 from django.urls import reverse
 from django.urls import reverse
-from django.utils import six
 
 
 from misago.acl.testutils import override_acl
 from misago.acl.testutils import override_acl
 from misago.search.searchproviders import searchproviders
 from misago.search.searchproviders import searchproviders
@@ -8,7 +7,7 @@ from misago.users.testutils import AuthenticatedUserTestCase
 
 
 class SearchApiTests(AuthenticatedUserTestCase):
 class SearchApiTests(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(SearchApiTests, self).setUp()
+        super().setUp()
 
 
         self.test_link = reverse('misago:api:search')
         self.test_link = reverse('misago:api:search')
 
 
@@ -34,7 +33,7 @@ class SearchApiTests(AuthenticatedUserTestCase):
             )
             )
             self.assertEqual(provider_api, provider['api'])
             self.assertEqual(provider_api, provider['api'])
 
 
-            self.assertEqual(six.text_type(providers[i].name), provider['name'])
+            self.assertEqual(str(providers[i].name), provider['name'])
             self.assertEqual(provider['results']['results'], [])
             self.assertEqual(provider['results']['results'], [])
             self.assertEqual(int(provider['time']), 0)
             self.assertEqual(int(provider['time']), 0)
 
 
@@ -51,6 +50,6 @@ class SearchApiTests(AuthenticatedUserTestCase):
             )
             )
             self.assertEqual(provider_api, provider['api'])
             self.assertEqual(provider_api, provider['api'])
 
 
-            self.assertEqual(six.text_type(providers[i].name), provider['name'])
+            self.assertEqual(str(providers[i].name), provider['name'])
             self.assertEqual(provider['results']['results'], [])
             self.assertEqual(provider['results']['results'], [])
             self.assertEqual(int(provider['time']), 0)
             self.assertEqual(int(provider['time']), 0)

+ 1 - 1
misago/search/tests/test_views.py

@@ -7,7 +7,7 @@ from misago.users.testutils import AuthenticatedUserTestCase
 
 
 class LandingTests(AuthenticatedUserTestCase):
 class LandingTests(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(LandingTests, self).setUp()
+        super().setUp()
 
 
         self.test_link = reverse('misago:search')
         self.test_link = reverse('misago:search')
 
 

+ 0 - 1
misago/search/views.py

@@ -2,7 +2,6 @@ from django.core.exceptions import PermissionDenied
 from django.http import Http404
 from django.http import Http404
 from django.shortcuts import redirect, render
 from django.shortcuts import redirect, render
 from django.urls import reverse
 from django.urls import reverse
-from django.utils import six
 from django.utils.translation import ugettext as _
 from django.utils.translation import ugettext as _
 
 
 from .searchproviders import searchproviders
 from .searchproviders import searchproviders

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

@@ -1,7 +1,6 @@
 from rest_framework.response import Response
 from rest_framework.response import Response
 
 
 from django.core.exceptions import PermissionDenied
 from django.core.exceptions import PermissionDenied
-from django.utils import six
 from django.utils.translation import ugettext as _
 from django.utils.translation import ugettext as _
 
 
 from misago.threads.models import Thread
 from misago.threads.models import Thread

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

@@ -51,7 +51,7 @@ class CategorySerializer(serializers.Serializer):
         self.user = user
         self.user = user
         self.category_cache = None
         self.category_cache = None
 
 
-        super(CategorySerializer, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
 
 
     def validate_category(self, value):
     def validate_category(self, value):
         try:
         try:

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

@@ -8,7 +8,7 @@ from . import PostingEndpoint, PostingMiddleware
 
 
 class EmailNotificationMiddleware(PostingMiddleware):
 class EmailNotificationMiddleware(PostingMiddleware):
     def __init__(self, **kwargs):
     def __init__(self, **kwargs):
-        super(EmailNotificationMiddleware, self).__init__(**kwargs)
+        super().__init__(**kwargs)
 
 
         self.previous_last_post_on = self.thread.last_post_on
         self.previous_last_post_on = self.thread.last_post_on
 
 

+ 2 - 4
misago/threads/api/postingendpoint/participants.py

@@ -2,9 +2,7 @@ from rest_framework import serializers
 
 
 from django.contrib.auth import get_user_model
 from django.contrib.auth import get_user_model
 from django.core.exceptions import PermissionDenied
 from django.core.exceptions import PermissionDenied
-from django.utils import six
-from django.utils.translation import ugettext as _
-from django.utils.translation import ungettext
+from django.utils.translation import ugettext as _, ungettext
 
 
 from misago.categories import PRIVATE_THREADS_ROOT_NAME
 from misago.categories import PRIVATE_THREADS_ROOT_NAME
 from misago.threads.participants import add_participants, set_owner
 from misago.threads.participants import add_participants, set_owner
@@ -75,7 +73,7 @@ class ParticipantsSerializer(serializers.Serializer):
             try:
             try:
                 allow_message_user(self.context['user'], user)
                 allow_message_user(self.context['user'], user)
             except PermissionDenied as e:
             except PermissionDenied as e:
-                raise serializers.ValidationError(six.text_type(e))
+                raise serializers.ValidationError(str(e))
             users.append(user)
             users.append(user)
 
 
         if len(usernames) != len(users):
         if len(usernames) != len(users):

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

@@ -7,7 +7,7 @@ from . import PostingMiddleware
 
 
 class SaveChangesMiddleware(PostingMiddleware):
 class SaveChangesMiddleware(PostingMiddleware):
     def __init__(self, **kwargs):
     def __init__(self, **kwargs):
-        super(SaveChangesMiddleware, self).__init__(**kwargs)
+        super().__init__(**kwargs)
         self.reset_state()
         self.reset_state()
 
 
     def reset_state(self):
     def reset_state(self):

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

@@ -2,7 +2,6 @@ from rest_framework.exceptions import ValidationError
 from rest_framework.response import Response
 from rest_framework.response import Response
 
 
 from django.core.exceptions import PermissionDenied
 from django.core.exceptions import PermissionDenied
-from django.utils.six import text_type
 from django.utils.translation import ugettext as _
 from django.utils.translation import ugettext as _
 
 
 from misago.acl import add_acl
 from misago.acl import add_acl
@@ -114,7 +113,7 @@ def threads_merge_endpoint(request):
             invalid_threads.append({
             invalid_threads.append({
                 'id': thread.pk,
                 'id': thread.pk,
                 'title': thread.title,
                 'title': thread.title,
-                'errors': [text_type(e)]
+                'errors': [str(e)]
             })
             })
 
 
     if invalid_threads:
     if invalid_threads:

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

@@ -5,7 +5,6 @@ from django.contrib.auth import get_user_model
 from django.core.exceptions import PermissionDenied, ValidationError
 from django.core.exceptions import PermissionDenied, ValidationError
 from django.http import Http404
 from django.http import Http404
 from django.shortcuts import get_object_or_404
 from django.shortcuts import get_object_or_404
-from django.utils import six
 from django.utils.translation import ugettext as _
 from django.utils.translation import ugettext as _
 
 
 from misago.acl import add_acl
 from misago.acl import add_acl
@@ -49,7 +48,7 @@ thread_patch_dispatcher.add('acl', patch_acl)
 
 
 def patch_title(request, thread, value):
 def patch_title(request, thread, value):
     try:
     try:
-        value_cleaned = six.text_type(value).strip()
+        value_cleaned = str(value).strip()
     except (TypeError, ValueError):
     except (TypeError, ValueError):
         raise PermissionDenied(_('Not a valid string.'))
         raise PermissionDenied(_('Not a valid string.'))
 
 
@@ -272,7 +271,7 @@ def patch_add_participant(request, thread, value):
     allow_add_participants(request.user, thread)
     allow_add_participants(request.user, thread)
 
 
     try:
     try:
-        username = six.text_type(value).strip().lower()
+        username = str(value).strip().lower()
         if not username:
         if not username:
             raise PermissionDenied(_("You have to enter new participant's username."))
             raise PermissionDenied(_("You have to enter new participant's username."))
         participant = UserModel.objects.get(slug=username)
         participant = UserModel.objects.get(slug=username)

+ 1 - 3
misago/threads/checksums.py

@@ -1,5 +1,3 @@
-from django.utils import six
-
 from misago.markup import checksums
 from misago.markup import checksums
 
 
 
 
@@ -9,7 +7,7 @@ def is_post_valid(post):
 
 
 
 
 def make_post_checksum(post):
 def make_post_checksum(post):
-    post_seeds = [six.text_type(v) for v in (post.id, str(post.posted_on.date()))]
+    post_seeds = [str(v) for v in (post.id, str(post.posted_on.date()))]
     return checksums.make_checksum(post.parsed, post_seeds)
     return checksums.make_checksum(post.parsed, post_seeds)
 
 
 
 

+ 1 - 1
misago/threads/mergeconflict.py

@@ -75,7 +75,7 @@ class PollMergeHandler(MergeConflictHandler):
     def get_available_resolutions(self):
     def get_available_resolutions(self):
         resolutions = [[0, _("Delete all polls")]]
         resolutions = [[0, _("Delete all polls")]]
         for poll in self.items:
         for poll in self.items:
-            resolutions.append([poll.id, u'{} ({})'.format(poll.question, poll.thread.title)])
+            resolutions.append([poll.id, '{} ({})'.format(poll.question, poll.thread.title)])
         return resolutions
         return resolutions
 
 
 
 

+ 0 - 3
misago/threads/migrations/0001_initial.py

@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 import django.db.models.deletion
 import django.db.models.deletion
 import django.utils.timezone
 import django.utils.timezone
 from django.conf import settings
 from django.conf import settings

+ 0 - 3
misago/threads/migrations/0002_threads_settings.py

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

+ 0 - 3
misago/threads/migrations/0003_attachment_types.py

@@ -1,7 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.9.7 on 2016-10-04 21:41
 # Generated by Django 1.9.7 on 2016-10-04 21:41
-from __future__ import unicode_literals
-
 from django.db import migrations
 from django.db import migrations
 
 
 
 

+ 0 - 3
misago/threads/migrations/0004_update_settings.py

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

+ 0 - 3
misago/threads/migrations/0005_index_search_document.py

@@ -1,7 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.1 on 2017-05-21 17:52
 # Generated by Django 1.11.1 on 2017-05-21 17:52
-from __future__ import unicode_literals
-
 import django.contrib.postgres.indexes
 import django.contrib.postgres.indexes
 from django.contrib.postgres.operations import BtreeGinExtension
 from django.contrib.postgres.operations import BtreeGinExtension
 from django.db import migrations
 from django.db import migrations

+ 0 - 3
misago/threads/migrations/0006_redo_partial_indexes.py

@@ -1,7 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.1 on 2017-05-21 17:52
 # Generated by Django 1.11.1 on 2017-05-21 17:52
-from __future__ import unicode_literals
-
 import django.contrib.postgres.indexes
 import django.contrib.postgres.indexes
 from django.contrib.postgres.operations import BtreeGinExtension
 from django.contrib.postgres.operations import BtreeGinExtension
 from django.db import migrations
 from django.db import migrations

+ 0 - 3
misago/threads/migrations/0007_auto_20171008_0131.py

@@ -1,7 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.5 on 2017-10-08 01:31
 # Generated by Django 1.11.5 on 2017-10-08 01:31
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 from django.db import migrations, models
 
 
 
 

+ 0 - 3
misago/threads/migrations/0008_auto_20180310_2234.py

@@ -1,7 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.9 on 2018-03-10 22:34
 # Generated by Django 1.11.9 on 2018-03-10 22:34
-from __future__ import unicode_literals
-
 from django.conf import settings
 from django.conf import settings
 from django.db import migrations, models
 from django.db import migrations, models
 import django.db.models.deletion
 import django.db.models.deletion

+ 1 - 3
misago/threads/migrations/0009_auto_20180326_0010.py

@@ -1,8 +1,6 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.9 on 2018-03-26 00:10
 # Generated by Django 1.11.9 on 2018-03-26 00:10
-from __future__ import unicode_literals
-
 from django.db import migrations
 from django.db import migrations
+
 import misago.core.pgutils
 import misago.core.pgutils
 
 
 
 

+ 0 - 3
misago/threads/migrations/0010_auto_20180609_1523.py

@@ -1,7 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.13 on 2018-06-09 15:23
 # Generated by Django 1.11.13 on 2018-06-09 15:23
-from __future__ import unicode_literals
-
 from django.db import migrations
 from django.db import migrations
 
 
 
 

+ 1 - 3
misago/threads/models/attachment.py

@@ -10,7 +10,6 @@ from django.db import models
 from django.urls import reverse
 from django.urls import reverse
 from django.utils import timezone
 from django.utils import timezone
 from django.utils.crypto import get_random_string
 from django.utils.crypto import get_random_string
-from django.utils.encoding import python_2_unicode_compatible
 
 
 from misago.conf import settings
 from misago.conf import settings
 from misago.core.utils import slugify
 from misago.core.utils import slugify
@@ -30,7 +29,6 @@ def upload_to(instance, filename):
     return os.path.join('attachments', spread_path[:2], spread_path[2:4], secret, filename_clean)
     return os.path.join('attachments', spread_path[:2], spread_path[2:4], secret, filename_clean)
 
 
 
 
-@python_2_unicode_compatible
 class Attachment(models.Model):
 class Attachment(models.Model):
     secret = models.CharField(max_length=64)
     secret = models.CharField(max_length=64)
     filetype = models.ForeignKey('AttachmentType', on_delete=models.CASCADE)
     filetype = models.ForeignKey('AttachmentType', on_delete=models.CASCADE)
@@ -59,7 +57,7 @@ class Attachment(models.Model):
 
 
     def delete(self, *args, **kwargs):
     def delete(self, *args, **kwargs):
         self.delete_files()
         self.delete_files()
-        return super(Attachment, self).delete(*args, **kwargs)
+        return super().delete(*args, **kwargs)
 
 
     def delete_files(self):
     def delete_files(self):
         if self.thumbnail:
         if self.thumbnail:

+ 0 - 4
misago/threads/models/attachmenttype.py

@@ -1,11 +1,7 @@
-from __future__ import unicode_literals
-
 from django.db import models
 from django.db import models
-from django.utils.encoding import python_2_unicode_compatible
 from django.utils.translation import ugettext_lazy as _
 from django.utils.translation import ugettext_lazy as _
 
 
 
 
-@python_2_unicode_compatible
 class AttachmentType(models.Model):
 class AttachmentType(models.Model):
     ENABLED = 0
     ENABLED = 0
     LOCKED = 1
     LOCKED = 1

+ 5 - 9
misago/threads/models/post.py

@@ -1,13 +1,10 @@
-from __future__ import unicode_literals
-
 import copy
 import copy
 
 
 from django.contrib.postgres.indexes import GinIndex
 from django.contrib.postgres.indexes import GinIndex
 from django.contrib.postgres.fields import JSONField
 from django.contrib.postgres.fields import JSONField
 from django.contrib.postgres.search import SearchVector, SearchVectorField
 from django.contrib.postgres.search import SearchVector, SearchVectorField
 from django.db import models
 from django.db import models
-from django.utils import six, timezone
-from django.utils.encoding import python_2_unicode_compatible
+from django.utils import timezone
 
 
 from misago.conf import settings
 from misago.conf import settings
 from misago.core.pgutils import PgPartialIndex
 from misago.core.pgutils import PgPartialIndex
@@ -17,7 +14,6 @@ from misago.threads.checksums import is_post_valid, update_post_checksum
 from misago.threads.filtersearch import filter_search
 from misago.threads.filtersearch import filter_search
 
 
 
 
-@python_2_unicode_compatible
 class Post(models.Model):
 class Post(models.Model):
     category = models.ForeignKey(
     category = models.ForeignKey(
         'misago_categories.Category',
         'misago_categories.Category',
@@ -118,7 +114,7 @@ class Post(models.Model):
         from misago.threads.signals import delete_post
         from misago.threads.signals import delete_post
         delete_post.send(sender=self)
         delete_post.send(sender=self)
 
 
-        super(Post, self).delete(*args, **kwargs)
+        super().delete(*args, **kwargs)
 
 
     def merge(self, other_post):
     def merge(self, other_post):
         if self.poster_id != other_post.poster_id:
         if self.poster_id != other_post.poster_id:
@@ -136,8 +132,8 @@ class Post(models.Model):
         if self.pk == other_post.pk:
         if self.pk == other_post.pk:
             raise ValueError("post can't be merged with itself")
             raise ValueError("post can't be merged with itself")
 
 
-        other_post.original = six.text_type('\n\n').join((other_post.original, self.original))
-        other_post.parsed = six.text_type('\n').join((other_post.parsed, self.parsed))
+        other_post.original = str('\n\n').join((other_post.original, self.original))
+        other_post.parsed = str('\n').join((other_post.parsed, self.parsed))
         update_post_checksum(other_post)
         update_post_checksum(other_post)
 
 
         if self.is_protected:
         if self.is_protected:
@@ -217,7 +213,7 @@ class Post(models.Model):
     def short(self):
     def short(self):
         if self.is_valid:
         if self.is_valid:
             if len(self.original) > 150:
             if len(self.original) > 150:
-                return six.text_type('%s...') % self.original[:150].strip()
+                return str('%s...') % self.original[:150].strip()
             else:
             else:
                 return self.original
                 return self.original
         else:
         else:

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

@@ -1,7 +1,6 @@
 from django.core.exceptions import ObjectDoesNotExist
 from django.core.exceptions import ObjectDoesNotExist
 from django.db import models
 from django.db import models
 from django.utils import timezone
 from django.utils import timezone
-from django.utils.encoding import python_2_unicode_compatible
 from django.utils.translation import ugettext_lazy as _
 from django.utils.translation import ugettext_lazy as _
 
 
 from misago.conf import settings
 from misago.conf import settings
@@ -9,7 +8,6 @@ from misago.core.pgutils import PgPartialIndex
 from misago.core.utils import slugify
 from misago.core.utils import slugify
 
 
 
 
-@python_2_unicode_compatible
 class Thread(models.Model):
 class Thread(models.Model):
     WEIGHT_DEFAULT = 0
     WEIGHT_DEFAULT = 0
     WEIGHT_PINNED = 1
     WEIGHT_PINNED = 1
@@ -150,7 +148,7 @@ class Thread(models.Model):
         from misago.threads.signals import delete_thread
         from misago.threads.signals import delete_thread
         delete_thread.send(sender=self)
         delete_thread.send(sender=self)
 
 
-        super(Thread, self).delete(*args, **kwargs)
+        super().delete(*args, **kwargs)
 
 
     def merge(self, other_thread):
     def merge(self, other_thread):
         if self.pk == other_thread.pk:
         if self.pk == other_thread.pk:

+ 0 - 4
misago/threads/moderation/exceptions.py

@@ -1,7 +1,3 @@
-from django.utils.encoding import python_2_unicode_compatible
-
-
-@python_2_unicode_compatible
 class ModerationError(Exception):
 class ModerationError(Exception):
     def __init__(self, message):
     def __init__(self, message):
         self.message = message
         self.message = message

+ 1 - 2
misago/threads/paginator.py

@@ -8,8 +8,7 @@ class PostsPaginator(Paginator):
         per_page = int(per_page) - 1
         per_page = int(per_page) - 1
         if orphans:
         if orphans:
             orphans += 1
             orphans += 1
-        super(PostsPaginator,
-              self).__init__(object_list, per_page, orphans, allow_empty_first_page)
+        super().__init__(object_list, per_page, orphans, allow_empty_first_page)
 
 
     def page(self, number):
     def page(self, number):
         """returns a Page object for the given 1-based page number."""
         """returns a Page object for the given 1-based page number."""

+ 1 - 2
misago/threads/serializers/moderation.py

@@ -2,7 +2,6 @@ from rest_framework import serializers
 
 
 from django.core.exceptions import PermissionDenied, ValidationError
 from django.core.exceptions import PermissionDenied, ValidationError
 from django.http import Http404
 from django.http import Http404
-from django.utils import six
 from django.utils.translation import ugettext as _, ugettext_lazy, ungettext
 from django.utils.translation import ugettext as _, ugettext_lazy, ungettext
 
 
 from misago.acl import add_acl
 from misago.acl import add_acl
@@ -398,7 +397,7 @@ class DeleteThreadsSerializer(serializers.Serializer):
                         'id': thread.id,
                         'id': thread.id,
                         'title': thread.title
                         'title': thread.title
                     },
                     },
-                    'error': six.text_type(e)
+                    'error': str(e)
                 })
                 })
             except Http404 as e:
             except Http404 as e:
                 pass # skip invisible threads
                 pass # skip invisible threads

+ 1 - 1
misago/threads/serializers/poll.py

@@ -160,7 +160,7 @@ class EditPollSerializer(serializers.ModelSerializer):
         if instance.choices:
         if instance.choices:
             self.update_choices(instance, validated_data['choices'])
             self.update_choices(instance, validated_data['choices'])
 
 
-        return super(EditPollSerializer, self).update(instance, validated_data)
+        return super().update(instance, validated_data)
 
 
     def update_choices(self, instance, cleaned_choices):
     def update_choices(self, instance, cleaned_choices):
         removed_hashes = []
         removed_hashes = []

+ 1 - 1
misago/threads/signals.py

@@ -175,7 +175,7 @@ def archive_user_polls(sender, archive=None, **kwargs):
             item_name,
             item_name,
             OrderedDict([
             OrderedDict([
                 (_("Question"), poll.question),
                 (_("Question"), poll.question),
-                (_("Choices"), u', '.join([c['label'] for c in poll.choices])),
+                (_("Choices"), ', '.join([c['label'] for c in poll.choices])),
             ]),
             ]),
             date=poll.posted_on,
             date=poll.posted_on,
         )
         )

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

@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
 from django import template
 from django import template
 from django.utils.translation import ugettext as _
 from django.utils.translation import ugettext as _
 from django.utils.translation import ungettext
 from django.utils.translation import ungettext

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

@@ -22,7 +22,7 @@ def get_mock_user():
 
 
 class AnonymizeEventsTests(AuthenticatedUserTestCase):
 class AnonymizeEventsTests(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(AnonymizeEventsTests, self).setUp()
+        super().setUp()
         self.factory = RequestFactory()
         self.factory = RequestFactory()
 
 
         category = Category.objects.get(slug='first-category')
         category = Category.objects.get(slug='first-category')
@@ -173,7 +173,7 @@ class AnonymizeEventsTests(AuthenticatedUserTestCase):
 
 
 class AnonymizeLikesTests(AuthenticatedUserTestCase):
 class AnonymizeLikesTests(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(AnonymizeLikesTests, self).setUp()
+        super().setUp()
         self.factory = RequestFactory()
         self.factory = RequestFactory()
 
 
     def get_request(self, user=None):
     def get_request(self, user=None):
@@ -212,7 +212,7 @@ class AnonymizeLikesTests(AuthenticatedUserTestCase):
 
 
 class AnonymizePostsTests(AuthenticatedUserTestCase):
 class AnonymizePostsTests(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(AnonymizePostsTests, self).setUp()
+        super().setUp()
         self.factory = RequestFactory()
         self.factory = RequestFactory()
 
 
     def get_request(self, user=None):
     def get_request(self, user=None):

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

@@ -8,7 +8,7 @@ from misago.threads.models import Attachment, AttachmentType
 
 
 class AttachmentAdminViewsTests(AdminTestCase):
 class AttachmentAdminViewsTests(AdminTestCase):
     def setUp(self):
     def setUp(self):
-        super(AttachmentAdminViewsTests, self).setUp()
+        super().setUp()
 
 
         self.category = Category.objects.get(slug='first-category')
         self.category = Category.objects.get(slug='first-category')
         self.post = testutils.post_thread(category=self.category).first_post
         self.post = testutils.post_thread(category=self.category).first_post

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

@@ -3,7 +3,6 @@ import os
 from PIL import Image
 from PIL import Image
 
 
 from django.urls import reverse
 from django.urls import reverse
-from django.utils import six
 
 
 from misago.acl.models import Role
 from misago.acl.models import Role
 from misago.acl.testutils import override_acl
 from misago.acl.testutils import override_acl
@@ -22,7 +21,7 @@ TEST_CORRUPTEDIMG_PATH = os.path.join(TESTFILES_DIR, 'corrupted.gif')
 
 
 class AttachmentsApiTestCase(AuthenticatedUserTestCase):
 class AttachmentsApiTestCase(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(AttachmentsApiTestCase, self).setUp()
+        super().setUp()
 
 
         AttachmentType.objects.all().delete()
         AttachmentType.objects.all().delete()
 
 
@@ -218,7 +217,7 @@ class AttachmentsApiTestCase(AuthenticatedUserTestCase):
         self.assertTrue(not attachment.image)
         self.assertTrue(not attachment.image)
         self.assertTrue(not attachment.thumbnail)
         self.assertTrue(not attachment.thumbnail)
 
 
-        self.assertTrue(six.text_type(attachment.file).endswith('document.pdf'))
+        self.assertTrue(str(attachment.file).endswith('document.pdf'))
 
 
         self.assertIsNone(response_json['post'])
         self.assertIsNone(response_json['post'])
         self.assertEqual(response_json['uploader_name'], self.user.username)
         self.assertEqual(response_json['uploader_name'], self.user.username)
@@ -261,7 +260,7 @@ class AttachmentsApiTestCase(AuthenticatedUserTestCase):
         self.assertIsNotNone(attachment.image)
         self.assertIsNotNone(attachment.image)
         self.assertTrue(not attachment.thumbnail)
         self.assertTrue(not attachment.thumbnail)
 
 
-        self.assertTrue(six.text_type(attachment.image).endswith('small.jpg'))
+        self.assertTrue(str(attachment.image).endswith('small.jpg'))
 
 
         self.assertIsNone(response_json['post'])
         self.assertIsNone(response_json['post'])
         self.assertEqual(response_json['uploader_name'], self.user.username)
         self.assertEqual(response_json['uploader_name'], self.user.username)
@@ -300,8 +299,8 @@ class AttachmentsApiTestCase(AuthenticatedUserTestCase):
         self.assertIsNotNone(attachment.image)
         self.assertIsNotNone(attachment.image)
         self.assertIsNotNone(attachment.thumbnail)
         self.assertIsNotNone(attachment.thumbnail)
 
 
-        self.assertTrue(six.text_type(attachment.image).endswith('large.png'))
-        self.assertTrue(six.text_type(attachment.thumbnail).endswith('large.png'))
+        self.assertTrue(str(attachment.image).endswith('large.png'))
+        self.assertTrue(str(attachment.thumbnail).endswith('large.png'))
 
 
         self.assertIsNone(response_json['post'])
         self.assertIsNone(response_json['post'])
         self.assertEqual(response_json['uploader_name'], self.user.username)
         self.assertEqual(response_json['uploader_name'], self.user.username)
@@ -355,8 +354,8 @@ class AttachmentsApiTestCase(AuthenticatedUserTestCase):
         self.assertIsNotNone(attachment.image)
         self.assertIsNotNone(attachment.image)
         self.assertIsNotNone(attachment.thumbnail)
         self.assertIsNotNone(attachment.thumbnail)
 
 
-        self.assertTrue(six.text_type(attachment.image).endswith('animated.gif'))
-        self.assertTrue(six.text_type(attachment.thumbnail).endswith('animated.gif'))
+        self.assertTrue(str(attachment.image).endswith('animated.gif'))
+        self.assertTrue(str(attachment.thumbnail).endswith('animated.gif'))
 
 
         self.assertIsNone(response_json['post'])
         self.assertIsNone(response_json['post'])
         self.assertEqual(response_json['uploader_name'], self.user.username)
         self.assertEqual(response_json['uploader_name'], self.user.username)

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

@@ -18,7 +18,7 @@ class RequestMock(object):
 
 
 class AttachmentsMiddlewareTests(AuthenticatedUserTestCase):
 class AttachmentsMiddlewareTests(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(AttachmentsMiddlewareTests, self).setUp()
+        super().setUp()
 
 
         self.category = Category.objects.get(slug='first-category')
         self.category = Category.objects.get(slug='first-category')
         self.thread = testutils.post_thread(category=self.category)
         self.thread = testutils.post_thread(category=self.category)

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

@@ -7,7 +7,7 @@ from misago.threads.models import AttachmentType
 
 
 class AttachmentTypeAdminViewsTests(AdminTestCase):
 class AttachmentTypeAdminViewsTests(AdminTestCase):
     def setUp(self):
     def setUp(self):
-        super(AttachmentTypeAdminViewsTests, self).setUp()
+        super().setUp()
         self.admin_link = reverse('misago:admin:system:attachment-types:index')
         self.admin_link = reverse('misago:admin:system:attachment-types:index')
 
 
     def test_link_registered(self):
     def test_link_registered(self):

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

@@ -18,7 +18,7 @@ TEST_SMALLJPG_PATH = os.path.join(TESTFILES_DIR, 'small.jpg')
 
 
 class AttachmentViewTestCase(AuthenticatedUserTestCase):
 class AttachmentViewTestCase(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(AttachmentViewTestCase, self).setUp()
+        super().setUp()
 
 
         AttachmentType.objects.all().delete()
         AttachmentType.objects.all().delete()
 
 

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

@@ -1,9 +1,9 @@
 from datetime import timedelta
 from datetime import timedelta
+from io import StringIO
 
 
 from django.core.management import call_command
 from django.core.management import call_command
 from django.test import TestCase
 from django.test import TestCase
 from django.utils import timezone
 from django.utils import timezone
-from django.utils.six import StringIO
 
 
 from misago.categories.models import Category
 from misago.categories.models import Category
 from misago.conf import settings
 from misago.conf import settings

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

@@ -19,7 +19,7 @@ def get_mock_user():
 
 
 class DeleteUserLikesTests(AuthenticatedUserTestCase):
 class DeleteUserLikesTests(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(DeleteUserLikesTests, self).setUp()
+        super().setUp()
         self.factory = RequestFactory()
         self.factory = RequestFactory()
 
 
     def get_request(self, user=None):
     def get_request(self, user=None):

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

@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from copy import deepcopy
 from copy import deepcopy
 from datetime import timedelta
 from datetime import timedelta
 
 
@@ -21,7 +18,7 @@ UserModel = get_user_model()
 
 
 class EmailNotificationTests(AuthenticatedUserTestCase):
 class EmailNotificationTests(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(EmailNotificationTests, self).setUp()
+        super().setUp()
 
 
         self.category = Category.objects.get(slug='first-category')
         self.category = Category.objects.get(slug='first-category')
         self.thread = testutils.post_thread(
         self.thread = testutils.post_thread(

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

@@ -1,4 +1,3 @@
-#-*- coding: utf-8 -*-
 from django.contrib.auth import get_user_model
 from django.contrib.auth import get_user_model
 from django.test import TestCase
 from django.test import TestCase
 from django.utils import timezone
 from django.utils import timezone

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

@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.urls import reverse
 from django.urls import reverse
 
 
 from misago.acl.testutils import override_acl
 from misago.acl.testutils import override_acl
@@ -11,7 +8,7 @@ from misago.users.testutils import AuthenticatedUserTestCase
 
 
 class PostMentionsTests(AuthenticatedUserTestCase):
 class PostMentionsTests(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(PostMentionsTests, self).setUp()
+        super().setUp()
 
 
         self.category = Category.objects.get(slug='first-category')
         self.category = Category.objects.get(slug='first-category')
         self.thread = testutils.post_thread(category=self.category)
         self.thread = testutils.post_thread(category=self.category)

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

@@ -14,7 +14,7 @@ GOTO_PAGE_URL = '%s%s/#post-%s'
 
 
 class GotoViewTestCase(AuthenticatedUserTestCase):
 class GotoViewTestCase(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(GotoViewTestCase, self).setUp()
+        super().setUp()
 
 
         self.category = Category.objects.get(slug='first-category')
         self.category = Category.objects.get(slug='first-category')
         self.thread = testutils.post_thread(category=self.category)
         self.thread = testutils.post_thread(category=self.category)

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

@@ -174,7 +174,7 @@ class MergeConflictTests(TestCase):
                 'polls': [['0', 'Delete all polls']] + [
                 'polls': [['0', 'Delete all polls']] + [
                     [
                     [
                         str(thread.poll.id),
                         str(thread.poll.id),
-                        u'{} ({})'.format(thread.poll.question, thread.title),
+                        '{} ({})'.format(thread.poll.question, thread.title),
                     ] for thread in polls
                     ] for thread in polls
                 ]
                 ]
             })
             })
@@ -238,7 +238,7 @@ class MergeConflictTests(TestCase):
                 'polls': [['0', 'Delete all polls']] + [
                 'polls': [['0', 'Delete all polls']] + [
                     [
                     [
                         str(thread.poll.id),
                         str(thread.poll.id),
-                        u'{} ({})'.format(thread.poll.question, thread.title),
+                        '{} ({})'.format(thread.poll.question, thread.title),
                     ] for thread in polls
                     ] for thread in polls
                 ]
                 ]
             })
             })

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

@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.contrib.auth import get_user_model
 from django.contrib.auth import get_user_model
 from django.test.client import BOUNDARY, MULTIPART_CONTENT, encode_multipart
 from django.test.client import BOUNDARY, MULTIPART_CONTENT, encode_multipart
 from django.urls import reverse
 from django.urls import reverse
@@ -17,7 +14,7 @@ UserModel = get_user_model()
 
 
 class PostMentionsTests(AuthenticatedUserTestCase):
 class PostMentionsTests(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(PostMentionsTests, self).setUp()
+        super().setUp()
 
 
         self.category = Category.objects.get(slug='first-category')
         self.category = Category.objects.get(slug='first-category')
         self.thread = testutils.post_thread(category=self.category)
         self.thread = testutils.post_thread(category=self.category)

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

@@ -6,7 +6,7 @@ from misago.users.testutils import AuthenticatedUserTestCase
 
 
 class PostsModerationTests(AuthenticatedUserTestCase):
 class PostsModerationTests(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(PostsModerationTests, self).setUp()
+        super().setUp()
 
 
         self.category = Category.objects.all_categories()[:1][0]
         self.category = Category.objects.all_categories()[:1][0]
         self.thread = testutils.post_thread(self.category)
         self.thread = testutils.post_thread(self.category)

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

@@ -15,7 +15,7 @@ UserModel = get_user_model()
 
 
 class PrivateThreadPatchApiTestCase(PrivateThreadsTestCase):
 class PrivateThreadPatchApiTestCase(PrivateThreadsTestCase):
     def setUp(self):
     def setUp(self):
-        super(PrivateThreadPatchApiTestCase, self).setUp()
+        super().setUp()
 
 
         self.thread = testutils.post_thread(self.category, poster=self.user)
         self.thread = testutils.post_thread(self.category, poster=self.user)
         self.api_link = self.thread.get_api_url()
         self.api_link = self.thread.get_api_url()

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

@@ -11,7 +11,7 @@ UserModel = get_user_model()
 
 
 class PrivateThreadReplyApiTestCase(PrivateThreadsTestCase):
 class PrivateThreadReplyApiTestCase(PrivateThreadsTestCase):
     def setUp(self):
     def setUp(self):
-        super(PrivateThreadReplyApiTestCase, self).setUp()
+        super().setUp()
 
 
         self.thread = testutils.post_thread(self.category, poster=self.user)
         self.thread = testutils.post_thread(self.category, poster=self.user)
         self.api_link = self.thread.get_posts_api_url()
         self.api_link = self.thread.get_posts_api_url()

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

@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.contrib.auth import get_user_model
 from django.contrib.auth import get_user_model
 from django.core import mail
 from django.core import mail
 from django.urls import reverse
 from django.urls import reverse
@@ -17,7 +14,7 @@ UserModel = get_user_model()
 
 
 class StartPrivateThreadTests(AuthenticatedUserTestCase):
 class StartPrivateThreadTests(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(StartPrivateThreadTests, self).setUp()
+        super().setUp()
 
 
         self.category = Category.objects.private_threads()
         self.category = Category.objects.private_threads()
         self.api_link = reverse('misago:api:private-thread-list')
         self.api_link = reverse('misago:api:private-thread-list')

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

@@ -7,7 +7,7 @@ from .test_privatethreads import PrivateThreadsTestCase
 
 
 class PrivateThreadViewTests(PrivateThreadsTestCase):
 class PrivateThreadViewTests(PrivateThreadsTestCase):
     def setUp(self):
     def setUp(self):
-        super(PrivateThreadViewTests, self).setUp()
+        super().setUp()
 
 
         self.thread = testutils.post_thread(self.category, poster=self.user)
         self.thread = testutils.post_thread(self.category, poster=self.user)
         self.test_link = self.thread.get_absolute_url()
         self.test_link = self.thread.get_absolute_url()

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

@@ -5,7 +5,7 @@ from misago.users.testutils import AuthenticatedUserTestCase
 
 
 class PrivateThreadsTestCase(AuthenticatedUserTestCase):
 class PrivateThreadsTestCase(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(PrivateThreadsTestCase, self).setUp()
+        super().setUp()
         self.category = Category.objects.private_threads()
         self.category = Category.objects.private_threads()
 
 
         override_acl(self.user, {
         override_acl(self.user, {

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

@@ -9,7 +9,7 @@ from .test_privatethreads import PrivateThreadsTestCase
 
 
 class PrivateThreadsListApiTests(PrivateThreadsTestCase):
 class PrivateThreadsListApiTests(PrivateThreadsTestCase):
     def setUp(self):
     def setUp(self):
-        super(PrivateThreadsListApiTests, self).setUp()
+        super().setUp()
 
 
         self.api_link = reverse('misago:api:private-thread-list')
         self.api_link = reverse('misago:api:private-thread-list')
 
 
@@ -69,7 +69,7 @@ class PrivateThreadsListApiTests(PrivateThreadsTestCase):
 
 
 class PrivateThreadRetrieveApiTests(PrivateThreadsTestCase):
 class PrivateThreadRetrieveApiTests(PrivateThreadsTestCase):
     def setUp(self):
     def setUp(self):
-        super(PrivateThreadRetrieveApiTests, self).setUp()
+        super().setUp()
 
 
         self.thread = testutils.post_thread(self.category, poster=self.user)
         self.thread = testutils.post_thread(self.category, poster=self.user)
         self.api_link = self.thread.get_api_url()
         self.api_link = self.thread.get_api_url()
@@ -167,7 +167,7 @@ class PrivateThreadRetrieveApiTests(PrivateThreadsTestCase):
 
 
 class PrivateThreadDeleteApiTests(PrivateThreadsTestCase):
 class PrivateThreadDeleteApiTests(PrivateThreadsTestCase):
     def setUp(self):
     def setUp(self):
-        super(PrivateThreadDeleteApiTests, self).setUp()
+        super().setUp()
 
 
         self.thread = testutils.post_thread(self.category, poster=self.user)
         self.thread = testutils.post_thread(self.category, poster=self.user)
         self.api_link = self.thread.get_api_url()
         self.api_link = self.thread.get_api_url()

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

@@ -9,7 +9,7 @@ from .test_privatethreads import PrivateThreadsTestCase
 
 
 class PrivateThreadsListTests(PrivateThreadsTestCase):
 class PrivateThreadsListTests(PrivateThreadsTestCase):
     def setUp(self):
     def setUp(self):
-        super(PrivateThreadsListTests, self).setUp()
+        super().setUp()
 
 
         self.test_link = reverse('misago:private-threads')
         self.test_link = reverse('misago:private-threads')
 
 

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

@@ -7,7 +7,7 @@ from misago.users.testutils import AuthenticatedUserTestCase
 
 
 class SearchApiTests(AuthenticatedUserTestCase):
 class SearchApiTests(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(SearchApiTests, self).setUp()
+        super().setUp()
 
 
         self.category = Category.objects.get(slug='first-category')
         self.category = Category.objects.get(slug='first-category')
 
 
@@ -209,7 +209,7 @@ class SearchApiTests(AuthenticatedUserTestCase):
 
 
 class SearchProviderApiTests(SearchApiTests):
 class SearchProviderApiTests(SearchApiTests):
     def setUp(self):
     def setUp(self):
-        super(SearchProviderApiTests, self).setUp()
+        super().setUp()
 
 
         self.api_link = reverse(
         self.api_link = reverse(
             'misago:api:search', kwargs={
             'misago:api:search', kwargs={

+ 3 - 6
misago/threads/tests/test_subscription_middleware.py

@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.contrib.auth import get_user_model
 from django.contrib.auth import get_user_model
 from django.urls import reverse
 from django.urls import reverse
 
 
@@ -15,7 +12,7 @@ UserModel = get_user_model()
 
 
 class SubscriptionMiddlewareTestCase(AuthenticatedUserTestCase):
 class SubscriptionMiddlewareTestCase(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(SubscriptionMiddlewareTestCase, self).setUp()
+        super().setUp()
         self.category = Category.objects.get(slug='first-category')
         self.category = Category.objects.get(slug='first-category')
         self.override_acl()
         self.override_acl()
 
 
@@ -34,7 +31,7 @@ class SubscriptionMiddlewareTestCase(AuthenticatedUserTestCase):
 
 
 class SubscribeStartedThreadTests(SubscriptionMiddlewareTestCase):
 class SubscribeStartedThreadTests(SubscriptionMiddlewareTestCase):
     def setUp(self):
     def setUp(self):
-        super(SubscribeStartedThreadTests, self).setUp()
+        super().setUp()
         self.api_link = reverse('misago:api:thread-list')
         self.api_link = reverse('misago:api:thread-list')
 
 
     def test_dont_subscribe(self):
     def test_dont_subscribe(self):
@@ -103,7 +100,7 @@ class SubscribeStartedThreadTests(SubscriptionMiddlewareTestCase):
 
 
 class SubscribeRepliedThreadTests(SubscriptionMiddlewareTestCase):
 class SubscribeRepliedThreadTests(SubscriptionMiddlewareTestCase):
     def setUp(self):
     def setUp(self):
-        super(SubscribeRepliedThreadTests, self).setUp()
+        super().setUp()
         self.thread = testutils.post_thread(self.category)
         self.thread = testutils.post_thread(self.category)
         self.api_link = reverse(
         self.api_link = reverse(
             'misago:api:thread-post-list', kwargs={
             'misago:api:thread-post-list', kwargs={

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

@@ -11,7 +11,7 @@ UserModel = get_user_model()
 
 
 class SyncUnreadPrivateThreadsTestCase(PrivateThreadsTestCase):
 class SyncUnreadPrivateThreadsTestCase(PrivateThreadsTestCase):
     def setUp(self):
     def setUp(self):
-        super(SyncUnreadPrivateThreadsTestCase, self).setUp()
+        super().setUp()
 
 
         self.other_user = UserModel.objects.create_user(
         self.other_user = UserModel.objects.create_user(
             'BobBoberson', 'bob@boberson.com', 'pass123'
             'BobBoberson', 'bob@boberson.com', 'pass123'

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

@@ -1,6 +1,7 @@
+from io import StringIO
+
 from django.core.management import call_command
 from django.core.management import call_command
 from django.test import TestCase
 from django.test import TestCase
-from django.utils.six import StringIO
 
 
 from misago.categories.models import Category
 from misago.categories.models import Category
 from misago.threads import testutils
 from misago.threads import testutils

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

@@ -12,7 +12,7 @@ from .test_threads_api import ThreadsApiTestCase
 
 
 class ThreadsBulkPatchApiTestCase(ThreadsApiTestCase):
 class ThreadsBulkPatchApiTestCase(ThreadsApiTestCase):
     def setUp(self):
     def setUp(self):
-        super(ThreadsBulkPatchApiTestCase, self).setUp()
+        super().setUp()
 
 
         self.threads = list(reversed([
         self.threads = list(reversed([
             testutils.post_thread(category=self.category),
             testutils.post_thread(category=self.category),
@@ -243,7 +243,7 @@ class BulkThreadChangeTitleApiTests(ThreadsBulkPatchApiTestCase):
 
 
 class BulkThreadMoveApiTests(ThreadsBulkPatchApiTestCase):
 class BulkThreadMoveApiTests(ThreadsBulkPatchApiTestCase):
     def setUp(self):
     def setUp(self):
-        super(BulkThreadMoveApiTests, self).setUp()
+        super().setUp()
 
 
         Category(
         Category(
             name='Category B',
             name='Category B',

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

@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from datetime import timedelta
 from datetime import timedelta
 
 
 from django.test.client import BOUNDARY, MULTIPART_CONTENT, encode_multipart
 from django.test.client import BOUNDARY, MULTIPART_CONTENT, encode_multipart
@@ -16,7 +13,7 @@ from misago.users.testutils import AuthenticatedUserTestCase
 
 
 class EditReplyTests(AuthenticatedUserTestCase):
 class EditReplyTests(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(EditReplyTests, self).setUp()
+        super().setUp()
 
 
         self.category = Category.objects.get(slug='first-category')
         self.category = Category.objects.get(slug='first-category')
         self.thread = testutils.post_thread(category=self.category)
         self.thread = testutils.post_thread(category=self.category)

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

@@ -11,7 +11,7 @@ from .test_threads_api import ThreadsApiTestCase
 
 
 class ThreadMergeApiTests(ThreadsApiTestCase):
 class ThreadMergeApiTests(ThreadsApiTestCase):
     def setUp(self):
     def setUp(self):
-        super(ThreadMergeApiTests, self).setUp()
+        super().setUp()
 
 
         Category(
         Category(
             name='Category B',
             name='Category B',
@@ -708,11 +708,11 @@ class ThreadMergeApiTests(ThreadsApiTestCase):
                     ['0', "Delete all polls"],
                     ['0', "Delete all polls"],
                     [
                     [
                         str(poll.pk),
                         str(poll.pk),
-                        u'{} ({})'.format(poll.question, poll.thread.title),
+                        '{} ({})'.format(poll.question, poll.thread.title),
                     ],
                     ],
                     [
                     [
                         str(other_poll.pk),
                         str(other_poll.pk),
-                        u'{} ({})'.format(other_poll.question, other_poll.thread.title),
+                        '{} ({})'.format(other_poll.question, other_poll.thread.title),
                     ],
                     ],
                 ]
                 ]
             }
             }

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

@@ -1,7 +1,7 @@
 import json
 import json
 from datetime import timedelta
 from datetime import timedelta
 
 
-from django.utils import six, timezone
+from django.utils import timezone
 
 
 from misago.acl.testutils import override_acl
 from misago.acl.testutils import override_acl
 from misago.categories.models import Category
 from misago.categories.models import Category
@@ -443,7 +443,7 @@ class ThreadPinLocallyApiTests(ThreadPatchApiTestCase):
 
 
 class ThreadMoveApiTests(ThreadPatchApiTestCase):
 class ThreadMoveApiTests(ThreadPatchApiTestCase):
     def setUp(self):
     def setUp(self):
-        super(ThreadMoveApiTests, self).setUp()
+        super().setUp()
 
 
         Category(
         Category(
             name='Category B',
             name='Category B',
@@ -1204,7 +1204,7 @@ class ThreadHideApiTests(ThreadPatchApiTestCase):
 
 
 class ThreadUnhideApiTests(ThreadPatchApiTestCase):
 class ThreadUnhideApiTests(ThreadPatchApiTestCase):
     def setUp(self):
     def setUp(self):
-        super(ThreadUnhideApiTests, self).setUp()
+        super().setUp()
 
 
         self.thread.is_hidden = True
         self.thread.is_hidden = True
         self.thread.save()
         self.thread.save()
@@ -1388,7 +1388,7 @@ class ThreadSubscribeApiTests(ThreadPatchApiTestCase):
     def test_subscribe_nonexistant_thread(self):
     def test_subscribe_nonexistant_thread(self):
         """api makes it impossible to subscribe nonexistant thread"""
         """api makes it impossible to subscribe nonexistant thread"""
         bad_api_link = self.api_link.replace(
         bad_api_link = self.api_link.replace(
-            six.text_type(self.thread.pk), six.text_type(self.thread.pk + 9)
+            str(self.thread.pk), str(self.thread.pk + 9)
         )
         )
 
 
         response = self.patch(
         response = self.patch(
@@ -1857,7 +1857,7 @@ class ThreadMarkBestAnswerApiTests(ThreadPatchApiTestCase):
 
 
 class ThreadChangeBestAnswerApiTests(ThreadPatchApiTestCase):
 class ThreadChangeBestAnswerApiTests(ThreadPatchApiTestCase):
     def setUp(self):
     def setUp(self):
-        super(ThreadChangeBestAnswerApiTests, self).setUp()
+        super().setUp()
 
 
         self.best_answer = testutils.reply_thread(self.thread)
         self.best_answer = testutils.reply_thread(self.thread)
         self.thread.set_best_answer(self.user, self.best_answer)
         self.thread.set_best_answer(self.user, self.best_answer)
@@ -2150,7 +2150,7 @@ class ThreadChangeBestAnswerApiTests(ThreadPatchApiTestCase):
 
 
 class ThreadUnmarkBestAnswerApiTests(ThreadPatchApiTestCase):
 class ThreadUnmarkBestAnswerApiTests(ThreadPatchApiTestCase):
     def setUp(self):
     def setUp(self):
-        super(ThreadUnmarkBestAnswerApiTests, self).setUp()
+        super().setUp()
 
 
         self.best_answer = testutils.reply_thread(self.thread)
         self.best_answer = testutils.reply_thread(self.thread)
         self.thread.set_best_answer(self.user, self.best_answer)
         self.thread.set_best_answer(self.user, self.best_answer)

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

@@ -10,7 +10,7 @@ from misago.users.testutils import AuthenticatedUserTestCase
 
 
 class ThreadPollApiTestCase(AuthenticatedUserTestCase):
 class ThreadPollApiTestCase(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(ThreadPollApiTestCase, self).setUp()
+        super().setUp()
 
 
         self.category = Category.objects.get(slug='first-category')
         self.category = Category.objects.get(slug='first-category')
         self.thread = testutils.post_thread(self.category, poster=self.user)
         self.thread = testutils.post_thread(self.category, poster=self.user)

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

@@ -10,7 +10,7 @@ from .test_thread_poll_api import ThreadPollApiTestCase
 
 
 class ThreadPollDeleteTests(ThreadPollApiTestCase):
 class ThreadPollDeleteTests(ThreadPollApiTestCase):
     def setUp(self):
     def setUp(self):
-        super(ThreadPollDeleteTests, self).setUp()
+        super().setUp()
 
 
         self.mock_poll()
         self.mock_poll()
 
 

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

@@ -10,7 +10,7 @@ from .test_thread_poll_api import ThreadPollApiTestCase
 
 
 class ThreadPollEditTests(ThreadPollApiTestCase):
 class ThreadPollEditTests(ThreadPollApiTestCase):
     def setUp(self):
     def setUp(self):
-        super(ThreadPollEditTests, self).setUp()
+        super().setUp()
 
 
         self.mock_poll()
         self.mock_poll()
 
 

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

@@ -14,7 +14,7 @@ UserModel = get_user_model()
 
 
 class ThreadGetVotesTests(ThreadPollApiTestCase):
 class ThreadGetVotesTests(ThreadPollApiTestCase):
     def setUp(self):
     def setUp(self):
-        super(ThreadGetVotesTests, self).setUp()
+        super().setUp()
 
 
         self.mock_poll()
         self.mock_poll()
 
 
@@ -156,7 +156,7 @@ class ThreadGetVotesTests(ThreadPollApiTestCase):
 
 
 class ThreadPostVotesTests(ThreadPollApiTestCase):
 class ThreadPostVotesTests(ThreadPollApiTestCase):
     def setUp(self):
     def setUp(self):
-        super(ThreadPostVotesTests, self).setUp()
+        super().setUp()
 
 
         self.mock_poll()
         self.mock_poll()
 
 

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

@@ -12,7 +12,7 @@ from .test_threads_api import ThreadsApiTestCase
 
 
 class PostBulkDeleteApiTests(ThreadsApiTestCase):
 class PostBulkDeleteApiTests(ThreadsApiTestCase):
     def setUp(self):
     def setUp(self):
-        super(PostBulkDeleteApiTests, self).setUp()
+        super().setUp()
 
 
         self.posts = [
         self.posts = [
             testutils.reply_thread(self.thread, poster=self.user),
             testutils.reply_thread(self.thread, poster=self.user),

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

@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 import json
 import json
 from datetime import timedelta
 from datetime import timedelta
 
 
@@ -16,7 +13,7 @@ from misago.users.testutils import AuthenticatedUserTestCase
 
 
 class ThreadPostBulkPatchApiTestCase(AuthenticatedUserTestCase):
 class ThreadPostBulkPatchApiTestCase(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(ThreadPostBulkPatchApiTestCase, self).setUp()
+        super().setUp()
 
 
         self.category = Category.objects.get(slug='first-category')
         self.category = Category.objects.get(slug='first-category')
         self.thread = testutils.post_thread(category=self.category)
         self.thread = testutils.post_thread(category=self.category)

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

@@ -11,7 +11,7 @@ from .test_threads_api import ThreadsApiTestCase
 
 
 class PostDeleteApiTests(ThreadsApiTestCase):
 class PostDeleteApiTests(ThreadsApiTestCase):
     def setUp(self):
     def setUp(self):
-        super(PostDeleteApiTests, self).setUp()
+        super().setUp()
 
 
         self.post = testutils.reply_thread(self.thread, poster=self.user)
         self.post = testutils.reply_thread(self.thread, poster=self.user)
 
 
@@ -176,7 +176,7 @@ class PostDeleteApiTests(ThreadsApiTestCase):
 
 
 class EventDeleteApiTests(ThreadsApiTestCase):
 class EventDeleteApiTests(ThreadsApiTestCase):
     def setUp(self):
     def setUp(self):
-        super(EventDeleteApiTests, self).setUp()
+        super().setUp()
 
 
         self.event = testutils.reply_thread(self.thread, poster=self.user, is_event=True)
         self.event = testutils.reply_thread(self.thread, poster=self.user, is_event=True)
 
 

+ 2 - 5
misago/threads/tests/test_thread_postedits_api.py

@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.urls import reverse
 from django.urls import reverse
 
 
 from misago.threads import testutils
 from misago.threads import testutils
@@ -10,7 +7,7 @@ from .test_threads_api import ThreadsApiTestCase
 
 
 class ThreadPostEditsApiTestCase(ThreadsApiTestCase):
 class ThreadPostEditsApiTestCase(ThreadsApiTestCase):
     def setUp(self):
     def setUp(self):
-        super(ThreadPostEditsApiTestCase, self).setUp()
+        super().setUp()
 
 
         self.post = testutils.reply_thread(self.thread, poster=self.user)
         self.post = testutils.reply_thread(self.thread, poster=self.user)
 
 
@@ -129,7 +126,7 @@ class ThreadPostGetEditTests(ThreadPostEditsApiTestCase):
 
 
 class ThreadPostPostEditTests(ThreadPostEditsApiTestCase):
 class ThreadPostPostEditTests(ThreadPostEditsApiTestCase):
     def setUp(self):
     def setUp(self):
-        super(ThreadPostPostEditTests, self).setUp()
+        super().setUp()
         self.edits = self.mock_edit_record()
         self.edits = self.mock_edit_record()
 
 
         self.override_acl({'can_edit_posts': 2})
         self.override_acl({'can_edit_posts': 2})

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

@@ -8,7 +8,7 @@ from .test_threads_api import ThreadsApiTestCase
 
 
 class ThreadPostLikesApiTestCase(ThreadsApiTestCase):
 class ThreadPostLikesApiTestCase(ThreadsApiTestCase):
     def setUp(self):
     def setUp(self):
-        super(ThreadPostLikesApiTestCase, self).setUp()
+        super().setUp()
 
 
         self.post = testutils.reply_thread(self.thread, poster=self.user)
         self.post = testutils.reply_thread(self.thread, poster=self.user)
 
 

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

@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 import json
 import json
 
 
 from django.urls import reverse
 from django.urls import reverse
@@ -16,7 +13,7 @@ from misago.users.testutils import AuthenticatedUserTestCase
 
 
 class ThreadPostMergeApiTestCase(AuthenticatedUserTestCase):
 class ThreadPostMergeApiTestCase(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(ThreadPostMergeApiTestCase, self).setUp()
+        super().setUp()
 
 
         self.category = Category.objects.get(slug='first-category')
         self.category = Category.objects.get(slug='first-category')
         self.thread = testutils.post_thread(category=self.category)
         self.thread = testutils.post_thread(category=self.category)

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

@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 import json
 import json
 
 
 from django.urls import reverse
 from django.urls import reverse
@@ -16,7 +13,7 @@ from misago.users.testutils import AuthenticatedUserTestCase
 
 
 class ThreadPostMoveApiTestCase(AuthenticatedUserTestCase):
 class ThreadPostMoveApiTestCase(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(ThreadPostMoveApiTestCase, self).setUp()
+        super().setUp()
 
 
         self.category = Category.objects.get(slug='first-category')
         self.category = Category.objects.get(slug='first-category')
         self.thread = testutils.post_thread(category=self.category)
         self.thread = testutils.post_thread(category=self.category)

+ 2 - 5
misago/threads/tests/test_thread_postpatch_api.py

@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 import json
 import json
 from datetime import timedelta
 from datetime import timedelta
 
 
@@ -16,7 +13,7 @@ from misago.users.testutils import AuthenticatedUserTestCase
 
 
 class ThreadPostPatchApiTestCase(AuthenticatedUserTestCase):
 class ThreadPostPatchApiTestCase(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(ThreadPostPatchApiTestCase, self).setUp()
+        super().setUp()
 
 
         self.category = Category.objects.get(slug='first-category')
         self.category = Category.objects.get(slug='first-category')
         self.thread = testutils.post_thread(category=self.category)
         self.thread = testutils.post_thread(category=self.category)
@@ -1137,7 +1134,7 @@ class PostLikeApiTests(ThreadPostPatchApiTestCase):
 
 
 class ThreadEventPatchApiTestCase(ThreadPostPatchApiTestCase):
 class ThreadEventPatchApiTestCase(ThreadPostPatchApiTestCase):
     def setUp(self):
     def setUp(self):
-        super(ThreadEventPatchApiTestCase, self).setUp()
+        super().setUp()
 
 
         self.event = testutils.reply_thread(self.thread, poster=self.user, is_event=True)
         self.event = testutils.reply_thread(self.thread, poster=self.user, is_event=True)
 
 

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

@@ -8,7 +8,7 @@ from .test_threads_api import ThreadsApiTestCase
 
 
 class PostReadApiTests(ThreadsApiTestCase):
 class PostReadApiTests(ThreadsApiTestCase):
     def setUp(self):
     def setUp(self):
-        super(PostReadApiTests, self).setUp()
+        super().setUp()
 
 
         self.post = testutils.reply_thread(
         self.post = testutils.reply_thread(
             self.thread,
             self.thread,

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

@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 import json
 import json
 
 
 from django.urls import reverse
 from django.urls import reverse
@@ -16,7 +13,7 @@ from misago.users.testutils import AuthenticatedUserTestCase
 
 
 class ThreadPostSplitApiTestCase(AuthenticatedUserTestCase):
 class ThreadPostSplitApiTestCase(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(ThreadPostSplitApiTestCase, self).setUp()
+        super().setUp()
 
 
         self.category = Category.objects.get(slug='first-category')
         self.category = Category.objects.get(slug='first-category')
         self.thread = testutils.post_thread(category=self.category)
         self.thread = testutils.post_thread(category=self.category)

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

@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.urls import reverse
 from django.urls import reverse
 
 
 from misago.acl.testutils import override_acl
 from misago.acl.testutils import override_acl
@@ -12,7 +9,7 @@ from misago.users.testutils import AuthenticatedUserTestCase
 
 
 class ReplyThreadTests(AuthenticatedUserTestCase):
 class ReplyThreadTests(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(ReplyThreadTests, self).setUp()
+        super().setUp()
 
 
         self.category = Category.objects.get(slug='first-category')
         self.category = Category.objects.get(slug='first-category')
         self.thread = testutils.post_thread(category=self.category)
         self.thread = testutils.post_thread(category=self.category)

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

@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.urls import reverse
 from django.urls import reverse
 
 
 from misago.acl.testutils import override_acl
 from misago.acl.testutils import override_acl
@@ -10,7 +7,7 @@ from misago.users.testutils import AuthenticatedUserTestCase
 
 
 class StartThreadTests(AuthenticatedUserTestCase):
 class StartThreadTests(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(StartThreadTests, self).setUp()
+        super().setUp()
 
 
         self.category = Category.objects.get(slug='first-category')
         self.category = Category.objects.get(slug='first-category')
         self.api_link = reverse('misago:api:thread-list')
         self.api_link = reverse('misago:api:thread-list')

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

@@ -14,7 +14,7 @@ from misago.users.testutils import AuthenticatedUserTestCase
 
 
 class ThreadsApiTestCase(AuthenticatedUserTestCase):
 class ThreadsApiTestCase(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(ThreadsApiTestCase, self).setUp()
+        super().setUp()
 
 
         threads_tree_id = trees_map.get_tree_id_for_root(THREADS_ROOT_NAME)
         threads_tree_id = trees_map.get_tree_id_for_root(THREADS_ROOT_NAME)
 
 
@@ -72,7 +72,7 @@ class ThreadsApiTestCase(AuthenticatedUserTestCase):
 
 
 class ThreadRetrieveApiTests(ThreadsApiTestCase):
 class ThreadRetrieveApiTests(ThreadsApiTestCase):
     def setUp(self):
     def setUp(self):
-        super(ThreadRetrieveApiTests, self).setUp()
+        super().setUp()
 
 
         self.tested_links = [
         self.tested_links = [
             self.api_link,
             self.api_link,
@@ -196,7 +196,7 @@ class ThreadRetrieveApiTests(ThreadsApiTestCase):
 
 
 class ThreadDeleteApiTests(ThreadsApiTestCase):
 class ThreadDeleteApiTests(ThreadsApiTestCase):
     def setUp(self):
     def setUp(self):
-        super(ThreadDeleteApiTests, self).setUp()
+        super().setUp()
 
 
         self.last_thread = testutils.post_thread(category=self.category)
         self.last_thread = testutils.post_thread(category=self.category)
         self.api_link = self.last_thread.get_api_url()
         self.api_link = self.last_thread.get_api_url()

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

@@ -15,7 +15,7 @@ from .test_threads_api import ThreadsApiTestCase
 
 
 class ThreadsBulkDeleteApiTests(ThreadsApiTestCase):
 class ThreadsBulkDeleteApiTests(ThreadsApiTestCase):
     def setUp(self):
     def setUp(self):
-        super(ThreadsBulkDeleteApiTests, self).setUp()
+        super().setUp()
 
 
         self.api_link = reverse('misago:api:thread-list')
         self.api_link = reverse('misago:api:thread-list')
 
 

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

@@ -17,7 +17,7 @@ TEST_DOCUMENT_PATH = os.path.join(TESTFILES_DIR, 'document.pdf')
 
 
 class EditorApiTestCase(AuthenticatedUserTestCase):
 class EditorApiTestCase(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(EditorApiTestCase, self).setUp()
+        super().setUp()
 
 
         self.category = Category.objects.get(slug='first-category')
         self.category = Category.objects.get(slug='first-category')
 
 
@@ -71,7 +71,7 @@ class EditorApiTestCase(AuthenticatedUserTestCase):
 
 
 class ThreadPostEditorApiTests(EditorApiTestCase):
 class ThreadPostEditorApiTests(EditorApiTestCase):
     def setUp(self):
     def setUp(self):
-        super(ThreadPostEditorApiTests, self).setUp()
+        super().setUp()
 
 
         self.api_link = reverse('misago:api:thread-editor')
         self.api_link = reverse('misago:api:thread-editor')
 
 
@@ -257,7 +257,7 @@ class ThreadPostEditorApiTests(EditorApiTestCase):
 
 
 class ThreadReplyEditorApiTests(EditorApiTestCase):
 class ThreadReplyEditorApiTests(EditorApiTestCase):
     def setUp(self):
     def setUp(self):
-        super(ThreadReplyEditorApiTests, self).setUp()
+        super().setUp()
 
 
         self.thread = testutils.post_thread(category=self.category)
         self.thread = testutils.post_thread(category=self.category)
         self.api_link = reverse(
         self.api_link = reverse(
@@ -400,7 +400,7 @@ class ThreadReplyEditorApiTests(EditorApiTestCase):
 
 
 class EditReplyEditorApiTests(EditorApiTestCase):
 class EditReplyEditorApiTests(EditorApiTestCase):
     def setUp(self):
     def setUp(self):
-        super(EditReplyEditorApiTests, self).setUp()
+        super().setUp()
 
 
         self.thread = testutils.post_thread(category=self.category)
         self.thread = testutils.post_thread(category=self.category)
         self.post = testutils.reply_thread(self.thread, poster=self.user)
         self.post = testutils.reply_thread(self.thread, poster=self.user)

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

@@ -16,7 +16,7 @@ from .test_threads_api import ThreadsApiTestCase
 
 
 class ThreadsMergeApiTests(ThreadsApiTestCase):
 class ThreadsMergeApiTests(ThreadsApiTestCase):
     def setUp(self):
     def setUp(self):
-        super(ThreadsMergeApiTests, self).setUp()
+        super().setUp()
         self.api_link = reverse('misago:api:thread-merge')
         self.api_link = reverse('misago:api:thread-merge')
 
 
         Category(
         Category(
@@ -1065,11 +1065,11 @@ class ThreadsMergeApiTests(ThreadsApiTestCase):
                     ['0', "Delete all polls"],
                     ['0', "Delete all polls"],
                     [
                     [
                         str(other_poll.pk),
                         str(other_poll.pk),
-                        u'{} ({})'.format(other_poll.question, other_poll.thread.title),
+                        '{} ({})'.format(other_poll.question, other_poll.thread.title),
                     ],
                     ],
                     [
                     [
                         str(poll.pk),
                         str(poll.pk),
-                        u'{} ({})'.format(poll.question, poll.thread.title),
+                        '{} ({})'.format(poll.question, poll.thread.title),
                     ],
                     ],
                 ],
                 ],
             }
             }

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

@@ -12,14 +12,14 @@ class MockRequest(object):
 
 
 class ThreadsModerationTests(AuthenticatedUserTestCase):
 class ThreadsModerationTests(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(ThreadsModerationTests, self).setUp()
+        super().setUp()
 
 
         self.request = MockRequest(self.user)
         self.request = MockRequest(self.user)
         self.category = Category.objects.all_categories()[:1][0]
         self.category = Category.objects.all_categories()[:1][0]
         self.thread = testutils.post_thread(self.category)
         self.thread = testutils.post_thread(self.category)
 
 
     def tearDown(self):
     def tearDown(self):
-        super(ThreadsModerationTests, self).tearDown()
+        super().tearDown()
 
 
     def reload_thread(self):
     def reload_thread(self):
         self.thread = Thread.objects.get(pk=self.thread.pk)
         self.thread = Thread.objects.get(pk=self.thread.pk)

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

@@ -31,7 +31,7 @@ class ThreadsListTestCase(AuthenticatedUserTestCase):
         Category E
         Category E
           + Subcategory F
           + Subcategory F
         """
         """
-        super(ThreadsListTestCase, self).setUp()
+        super().setUp()
 
 
         self.api_link = reverse('misago:api:thread-list')
         self.api_link = reverse('misago:api:thread-list')
 
 
@@ -161,10 +161,10 @@ class ThreadsListTestCase(AuthenticatedUserTestCase):
         return categories_acl
         return categories_acl
 
 
     def assertContainsThread(self, response, thread):
     def assertContainsThread(self, response, thread):
-        self.assertContains(response, u' href="{}"'.format(thread.get_absolute_url()))
+        self.assertContains(response, ' href="{}"'.format(thread.get_absolute_url()))
 
 
     def assertNotContainsThread(self, response, thread):
     def assertNotContainsThread(self, response, thread):
-        self.assertNotContains(response, u' href="{}"'.format(thread.get_absolute_url()))
+        self.assertNotContains(response, ' href="{}"'.format(thread.get_absolute_url()))
 
 
 
 
 class ApiTests(ThreadsListTestCase):
 class ApiTests(ThreadsListTestCase):
@@ -1530,7 +1530,7 @@ class UnapprovedListTests(ThreadsListTestCase):
 
 
 class OwnerOnlyThreadsVisibilityTests(AuthenticatedUserTestCase):
 class OwnerOnlyThreadsVisibilityTests(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(OwnerOnlyThreadsVisibilityTests, self).setUp()
+        super().setUp()
 
 
         self.category = Category.objects.get(slug='first-category')
         self.category = Category.objects.get(slug='first-category')
 
 

+ 8 - 9
misago/threads/tests/test_threadview.py

@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 from misago.acl.testutils import override_acl
 from misago.acl.testutils import override_acl
 from misago.categories.models import Category
 from misago.categories.models import Category
 from misago.conf import settings
 from misago.conf import settings
@@ -18,7 +17,7 @@ class MockRequest(object):
 
 
 class ThreadViewTestCase(AuthenticatedUserTestCase):
 class ThreadViewTestCase(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(ThreadViewTestCase, self).setUp()
+        super().setUp()
 
 
         self.category = Category.objects.get(slug='first-category')
         self.category = Category.objects.get(slug='first-category')
         self.thread = testutils.post_thread(category=self.category)
         self.thread = testutils.post_thread(category=self.category)
@@ -528,7 +527,7 @@ class ThreadAnonViewTests(ThreadViewTestCase):
 class ThreadUnicodeSupportTests(ThreadViewTestCase):
 class ThreadUnicodeSupportTests(ThreadViewTestCase):
     def test_category_name(self):
     def test_category_name(self):
         """unicode in category name causes no showstopper"""
         """unicode in category name causes no showstopper"""
-        self.category.name = u'Łódź'
+        self.category.name = 'Łódź'
         self.category.slug = 'Lodz'
         self.category.slug = 'Lodz'
 
 
         self.category.save()
         self.category.save()
@@ -540,7 +539,7 @@ class ThreadUnicodeSupportTests(ThreadViewTestCase):
 
 
     def test_thread_title(self):
     def test_thread_title(self):
         """unicode in thread title causes no showstopper"""
         """unicode in thread title causes no showstopper"""
-        self.thread.title = u'Łódź'
+        self.thread.title = 'Łódź'
         self.thread.slug = 'Lodz'
         self.thread.slug = 'Lodz'
 
 
         self.thread.save()
         self.thread.save()
@@ -552,8 +551,8 @@ class ThreadUnicodeSupportTests(ThreadViewTestCase):
 
 
     def test_post_content(self):
     def test_post_content(self):
         """unicode in thread title causes no showstopper"""
         """unicode in thread title causes no showstopper"""
-        self.thread.first_post.original = u'Łódź'
-        self.thread.first_post.parsed = u'<p>Łódź</p>'
+        self.thread.first_post.original = 'Łódź'
+        self.thread.first_post.parsed = '<p>Łódź</p>'
 
 
         update_post_checksum(self.thread.first_post)
         update_post_checksum(self.thread.first_post)
 
 
@@ -566,9 +565,9 @@ class ThreadUnicodeSupportTests(ThreadViewTestCase):
 
 
     def test_user_rank(self):
     def test_user_rank(self):
         """unicode in user rank causes no showstopper"""
         """unicode in user rank causes no showstopper"""
-        self.user.title = u'Łódź'
-        self.user.rank.name = u'Łódź'
-        self.user.rank.title = u'Łódź'
+        self.user.title = 'Łódź'
+        self.user.rank.name = 'Łódź'
+        self.user.rank.title = 'Łódź'
 
 
         self.user.rank.save()
         self.user.rank.save()
         self.user.save()
         self.user.save()

+ 2 - 3
misago/threads/tests/test_treesmap.py

@@ -1,5 +1,4 @@
 from django.test import TestCase
 from django.test import TestCase
-from django.utils import six
 
 
 from misago.categories.models import Category
 from misago.categories.models import Category
 from misago.threads.threadtypes.treesmap import TreesMap
 from misago.threads.threadtypes.treesmap import TreesMap
@@ -82,7 +81,7 @@ class TreesMapTests(TestCase):
         except KeyError as e:
         except KeyError as e:
             self.assertIn(
             self.assertIn(
                 "tree id has no type defined",
                 "tree id has no type defined",
-                six.text_type(e), "invalid exception message as given"
+                str(e), "invalid exception message as given"
             )
             )
 
 
     def test_get_tree_id_for_root(self):
     def test_get_tree_id_for_root(self):
@@ -101,5 +100,5 @@ class TreesMapTests(TestCase):
         except KeyError as e:
         except KeyError as e:
             self.assertIn(
             self.assertIn(
                 '"hurr_durr" root has no tree defined',
                 '"hurr_durr" root has no tree defined',
-                six.text_type(e), "invalid exception message as given"
+                str(e), "invalid exception message as given"
             )
             )

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

@@ -1,6 +1,7 @@
+from io import StringIO
+
 from django.core.management import call_command
 from django.core.management import call_command
 from django.test import TestCase
 from django.test import TestCase
-from django.utils.six import StringIO
 
 
 from misago.categories.models import Category
 from misago.categories.models import Category
 from misago.threads import testutils
 from misago.threads import testutils

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

@@ -20,7 +20,7 @@ class AddCategoriesToItemsTests(MisagoTestCase):
           + Subcategory F
           + Subcategory F
         """
         """
 
 
-        super(AddCategoriesToItemsTests, self).setUp()
+        super().setUp()
 
 
         self.root = Category.objects.root_category()
         self.root = Category.objects.root_category()
 
 

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

@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.urls import reverse
 from django.urls import reverse
 
 
 from misago.categories.models import Category
 from misago.categories.models import Category
@@ -9,7 +6,7 @@ from misago.users.testutils import AuthenticatedUserTestCase
 
 
 class ValidatePostTests(AuthenticatedUserTestCase):
 class ValidatePostTests(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(ValidatePostTests, self).setUp()
+        super().setUp()
 
 
         self.category = Category.objects.get(slug='first-category')
         self.category = Category.objects.get(slug='first-category')
         self.api_link = reverse('misago:api:thread-list')
         self.api_link = reverse('misago:api:thread-list')

+ 3 - 3
misago/threads/utils.py

@@ -1,6 +1,6 @@
+from urllib.parse import urlparse
+
 from django.urls import resolve
 from django.urls import resolve
-from django.utils import six
-from django.utils.six.moves.urllib.parse import urlparse
 
 
 from .models import PostLike
 from .models import PostLike
 
 
@@ -40,7 +40,7 @@ SUPPORTED_THREAD_ROUTES = {
 
 
 def get_thread_id_from_url(request, url):
 def get_thread_id_from_url(request, url):
     try:
     try:
-        clean_url = six.text_type(url).strip()
+        clean_url = str(url).strip()
         bits = urlparse(clean_url)
         bits = urlparse(clean_url)
     except:
     except:
         return None
         return None

+ 1 - 2
misago/threads/viewmodels/threads.py

@@ -164,8 +164,7 @@ class ForumThreads(ViewModel):
 
 
 class PrivateThreads(ViewModel):
 class PrivateThreads(ViewModel):
     def get_base_queryset(self, request, threads_categories, list_type):
     def get_base_queryset(self, request, threads_categories, list_type):
-        queryset = super(PrivateThreads, self).get_base_queryset(
-            request, threads_categories, list_type)
+        queryset = super().get_base_queryset(request, threads_categories, list_type)
 
 
         # limit queryset to threads we are participant of
         # limit queryset to threads we are participant of
         participated_threads = request.user.threadparticipant_set.values('thread_id')
         participated_threads = request.user.threadparticipant_set.values('thread_id')

+ 1 - 1
misago/threads/views/admin/attachments.py

@@ -14,7 +14,7 @@ class AttachmentAdmin(generic.AdminBaseMixin):
     message_404 = _("Requested attachment could not be found.")
     message_404 = _("Requested attachment could not be found.")
 
 
     def get_queryset(self):
     def get_queryset(self):
-        qs = super(AttachmentAdmin, self).get_queryset()
+        qs = super().get_queryset()
         return qs.select_related('filetype', 'uploader', 'post', 'post__thread', 'post__category')
         return qs.select_related('filetype', 'uploader', 'post', 'post__thread', 'post__category')
 
 
 
 

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

@@ -20,7 +20,7 @@ class AttachmentTypeAdmin(generic.AdminBaseMixin):
             target.roles.add(*roles)
             target.roles.add(*roles)
 
 
     def handle_form(self, form, request, target):
     def handle_form(self, form, request, target):
-        super(AttachmentTypeAdmin, self).handle_form(form, request, target)
+        super().handle_form(form, request, target)
         form.save()
         form.save()
 
 
 
 
@@ -28,7 +28,7 @@ class AttachmentTypesList(AttachmentTypeAdmin, generic.ListView):
     ordering = (('name', None), )
     ordering = (('name', None), )
 
 
     def get_queryset(self):
     def get_queryset(self):
-        queryset = super(AttachmentTypesList, self).get_queryset()
+        queryset = super().get_queryset()
         return queryset.annotate(num_files=Count('attachment'))
         return queryset.annotate(num_files=Count('attachment'))
 
 
 
 

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

@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
 from django.core.exceptions import PermissionDenied
 from django.core.exceptions import PermissionDenied
 from django.http import Http404
 from django.http import Http404
 from django.shortcuts import get_object_or_404, redirect
 from django.shortcuts import get_object_or_404, redirect

+ 1 - 1
misago/threads/views/list.py

@@ -73,7 +73,7 @@ class CategoryThreadsList(ForumThreadsList):
     template_name = 'misago/threadslist/category.html'
     template_name = 'misago/threadslist/category.html'
 
 
     def get_category(self, request, **kwargs):
     def get_category(self, request, **kwargs):
-        category = super(CategoryThreadsList, self).get_category(request, **kwargs)
+        category = super().get_category(request, **kwargs)
         if not category.level:
         if not category.level:
             raise Http404()  # disallow root category access
             raise Http404()  # disallow root category access
         return category
         return category

+ 2 - 2
misago/users/api/userendpoints/editdetails.py

@@ -55,10 +55,10 @@ class DetailsForm(forms.Form):
         self.request = kwargs.pop('request')
         self.request = kwargs.pop('request')
         self.user = kwargs.pop('user')
         self.user = kwargs.pop('user')
 
 
-        super(DetailsForm, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
 
 
         profilefields.add_fields_to_form(self.request, self.user, self)
         profilefields.add_fields_to_form(self.request, self.user, self)
 
 
     def clean(self):
     def clean(self):
-        data = super(DetailsForm, self).clean()
+        data = super().clean()
         return profilefields.clean_form(self.request, self.user, self, data)
         return profilefields.clean_form(self.request, self.user, self, data)

+ 16 - 8
misago/users/avatars/gallery.py

@@ -1,6 +1,6 @@
 import random
 import random
+from pathlib import Path
 
 
-from path import Path
 from PIL import Image
 from PIL import Image
 
 
 from django.core.files.base import ContentFile
 from django.core.files.base import ContentFile
@@ -48,23 +48,31 @@ def load_avatar_galleries():
     from misago.users.models import AvatarGallery
     from misago.users.models import AvatarGallery
 
 
     galleries = []
     galleries = []
-    for directory in Path(settings.MISAGO_AVATAR_GALLERY).dirs():
-        gallery_name = directory.name
+    for directory in Path(settings.MISAGO_AVATAR_GALLERY).iterdir():
+        if not directory.is_dir():
+            continue
 
 
-        images = directory.files('*.gif')
-        images += directory.files('*.jpg')
-        images += directory.files('*.jpeg')
-        images += directory.files('*.png')
+        name = directory.name
+        images = glob_gallery_images(directory)
 
 
         for image in images:
         for image in images:
             with open(image, 'rb') as image_file:
             with open(image, 'rb') as image_file:
                 galleries.append(
                 galleries.append(
                     AvatarGallery.objects.
                     AvatarGallery.objects.
-                    create(gallery=gallery_name, image=ContentFile(image_file.read(), 'image'))
+                    create(gallery=name, image=ContentFile(image_file.read(), 'image'))
                 )
                 )
     return galleries
     return galleries
 
 
 
 
+def glob_gallery_images(directory):
+    images = []
+    images.extend(directory.glob('*.gif'))
+    images.extend(directory.glob('*.jpg'))
+    images.extend(directory.glob('*.jpeg'))
+    images.extend(directory.glob('*.png'))
+    return images
+
+
 def set_avatar(user, avatar):
 def set_avatar(user, avatar):
     store.store_new_avatar(user, Image.open(avatar.image))
     store.store_new_avatar(user, Image.open(avatar.image))
 
 

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

@@ -1,4 +1,5 @@
-from path import Path
+from pathlib import Path
+
 from PIL import Image
 from PIL import Image
 
 
 from django.core.exceptions import ValidationError
 from django.core.exceptions import ValidationError
@@ -62,7 +63,7 @@ def validate_uploaded_file(uploaded_file):
         try:
         try:
             temporary_file_path = Path(uploaded_file.temporary_file_path())
             temporary_file_path = Path(uploaded_file.temporary_file_path())
             if temporary_file_path.exists():
             if temporary_file_path.exists():
-                temporary_file_path.remove()
+                temporary_file_path.unlink()
         except Exception:
         except Exception:
             pass
             pass
         raise e
         raise e

+ 2 - 3
misago/users/credentialchange.py

@@ -6,7 +6,6 @@ Stores new e-mail and password in cache
 from hashlib import sha256
 from hashlib import sha256
 
 
 from django.conf import settings
 from django.conf import settings
-from django.utils import six
 from django.utils.encoding import force_bytes
 from django.utils.encoding import force_bytes
 
 
 
 
@@ -45,7 +44,7 @@ def read_new_credential(request, credential_type, link_token):
 def _make_change_token(user, token_type):
 def _make_change_token(user, token_type):
     seeds = (
     seeds = (
         user.pk, user.email, user.password, user.last_login.replace(microsecond=0, tzinfo=None),
         user.pk, user.email, user.password, user.last_login.replace(microsecond=0, tzinfo=None),
-        settings.SECRET_KEY, six.text_type(token_type)
+        settings.SECRET_KEY, str(token_type)
     )
     )
 
 
-    return sha256(force_bytes('+'.join([six.text_type(s) for s in seeds]))).hexdigest()
+    return sha256(force_bytes('+'.join([str(s) for s in seeds]))).hexdigest()

+ 8 - 10
misago/users/datadownloads/dataarchive.py

@@ -1,11 +1,9 @@
-import io  # fixme: remove explicit io imports after going py3k-only
 import os
 import os
 import shutil
 import shutil
 
 
 from django.core.files import File
 from django.core.files import File
 from django.utils import timezone
 from django.utils import timezone
 from django.utils.crypto import get_random_string
 from django.utils.crypto import get_random_string
-from django.utils import six
 
 
 from misago.core.utils import slugify
 from misago.core.utils import slugify
 
 
@@ -78,15 +76,15 @@ class DataArchive(object):
         clean_filename = slugify(str(name))
         clean_filename = slugify(str(name))
         file_dir_path = self.make_final_path(date=date, directory=directory)
         file_dir_path = self.make_final_path(date=date, directory=directory)
         file_path = os.path.join(file_dir_path, '{}.txt'.format(clean_filename))
         file_path = os.path.join(file_dir_path, '{}.txt'.format(clean_filename))
-        with io.open(file_path, 'w', encoding='utf-8') as fp:
-            fp.write(six.text_type(value))
+        with open(file_path, 'w') as fp:
+            fp.write(str(value))
             return file_path
             return file_path
 
 
     def add_dict(self, name, value, date=None, directory=None):
     def add_dict(self, name, value, date=None, directory=None):
         text_lines = []
         text_lines = []
         for key, value in value.items():
         for key, value in value.items():
-            text_lines.append(u"{}: {}".format(key, value))
-        text = u'\n'.join(text_lines)
+            text_lines.append("{}: {}".format(key, value))
+        text = '\n'.join(text_lines)
         return self.add_text(name, text, date=date, directory=directory)
         return self.add_text(name, text, date=date, directory=directory)
 
 
     def add_model_file(self, model_file, prefix=None, date=None, directory=None):
     def add_model_file(self, model_file, prefix=None, date=None, directory=None):
@@ -97,7 +95,7 @@ class DataArchive(object):
 
 
         filename = os.path.basename(model_file.name)
         filename = os.path.basename(model_file.name)
         if prefix:
         if prefix:
-            prefixed_filename = u"{}-{}".format(prefix, filename)
+            prefixed_filename = "{}-{}".format(prefix, filename)
             clean_filename = trim_long_filename(prefixed_filename)
             clean_filename = trim_long_filename(prefixed_filename)
             target_path = os.path.join(target_dir_path, clean_filename)
             target_path = os.path.join(target_dir_path, clean_filename)
         else:
         else:
@@ -121,13 +119,13 @@ class DataArchive(object):
             final_path = data_dir_path
             final_path = data_dir_path
             path_items = [date.strftime('%Y'), date.strftime('%m'), date.strftime('%d')]
             path_items = [date.strftime('%Y'), date.strftime('%m'), date.strftime('%d')]
             for path_item in path_items:
             for path_item in path_items:
-                final_path = os.path.join(final_path, six.text_type(path_item))
+                final_path = os.path.join(final_path, str(path_item))
                 if not os.path.isdir(final_path):
                 if not os.path.isdir(final_path):
                     os.mkdir(final_path)
                     os.mkdir(final_path)
             return final_path
             return final_path
 
 
         if directory:
         if directory:
-            final_path = os.path.join(data_dir_path, six.text_type(directory))
+            final_path = os.path.join(data_dir_path, str(directory))
             if not os.path.isdir(final_path):
             if not os.path.isdir(final_path):
                 os.mkdir(final_path)
                 os.mkdir(final_path)
             return final_path
             return final_path
@@ -153,4 +151,4 @@ def trim_long_filename(filename):
 
 
     name, extension = os.path.splitext(filename)
     name, extension = os.path.splitext(filename)
     name_len = FILENAME_MAX_LEN - len(extension)
     name_len = FILENAME_MAX_LEN - len(extension)
-    return u'{}{}'.format(name[:name_len], extension)
+    return '{}{}'.format(name[:name_len], extension)

+ 4 - 4
misago/users/forms/admin.py

@@ -193,7 +193,7 @@ class EditUserForm(UserBaseForm):
     def __init__(self, *args, **kwargs):
     def __init__(self, *args, **kwargs):
         self.request = kwargs.pop('request')
         self.request = kwargs.pop('request')
 
 
-        super(EditUserForm, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
 
 
         profilefields.add_fields_to_admin_form(self.request, self.instance, self)
         profilefields.add_fields_to_admin_form(self.request, self.instance, self)
 
 
@@ -227,7 +227,7 @@ class EditUserForm(UserBaseForm):
         return data
         return data
 
 
     def clean(self):
     def clean(self):
-        data = super(EditUserForm, self).clean()
+        data = super().clean()
         return profilefields.clean_form(self.request, self.instance, self, data)
         return profilefields.clean_form(self.request, self.instance, self, data)
 
 
 
 
@@ -493,7 +493,7 @@ class BanUsersForm(forms.Form):
     def __init__(self, *args, **kwargs):
     def __init__(self, *args, **kwargs):
         users = kwargs.pop('users')
         users = kwargs.pop('users')
 
 
-        super(BanUsersForm, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
 
 
         self.fields['ban_type'].choices = [
         self.fields['ban_type'].choices = [
             ('usernames', _('Usernames')),
             ('usernames', _('Usernames')),
@@ -672,7 +672,7 @@ class RequestDataDownloadsForm(forms.Form):
         return user_identifiers
         return user_identifiers
 
 
     def clean(self):
     def clean(self):
-        data = super(RequestDataDownloadsForm, self).clean()
+        data = super().clean()
 
 
         if data.get('user_identifiers'):
         if data.get('user_identifiers'):
             username_match = Q(slug__in=data['user_identifiers'])
             username_match = Q(slug__in=data['user_identifiers'])

+ 2 - 2
misago/users/forms/auth.py

@@ -91,7 +91,7 @@ class AdminAuthenticationForm(AuthenticationForm):
             'not_staff': _("Your account does not have admin privileges."),
             'not_staff': _("Your account does not have admin privileges."),
         })
         })
 
 
-        super(AdminAuthenticationForm, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
 
 
     def confirm_login_allowed(self, user):
     def confirm_login_allowed(self, user):
         if not user.is_staff:
         if not user.is_staff:
@@ -102,7 +102,7 @@ class GetUserForm(MisagoAuthMixin, forms.Form):
     email = forms.CharField()
     email = forms.CharField()
 
 
     def clean(self):
     def clean(self):
-        data = super(GetUserForm, self).clean()
+        data = super().clean()
 
 
         email = data.get('email')
         email = data.get('email')
         if not email or len(email) > 250:
         if not email or len(email) > 250:

+ 3 - 3
misago/users/forms/register.py

@@ -21,7 +21,7 @@ class BaseRegisterForm(forms.Form):
     def __init__(self, *args, **kwargs):
     def __init__(self, *args, **kwargs):
         self.agreements = kwargs.pop('agreements')
         self.agreements = kwargs.pop('agreements')
         self.request = kwargs.pop('request')
         self.request = kwargs.pop('request')
-        super(BaseRegisterForm, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
 
 
     def clean_username(self):
     def clean_username(self):
         data = self.cleaned_data['username']
         data = self.cleaned_data['username']
@@ -62,7 +62,7 @@ class BaseRegisterForm(forms.Form):
 
 
 class SocialAuthRegisterForm(BaseRegisterForm):
 class SocialAuthRegisterForm(BaseRegisterForm):
     def clean(self):
     def clean(self):
-        cleaned_data = super(SocialAuthRegisterForm, self).clean()
+        cleaned_data = super().clean()
 
 
         self.clean_agreements(cleaned_data)
         self.clean_agreements(cleaned_data)
         self.raise_if_ip_banned()
         self.raise_if_ip_banned()
@@ -89,7 +89,7 @@ class RegisterForm(BaseRegisterForm):
             )
             )
 
 
     def clean(self):
     def clean(self):
-        cleaned_data = super(RegisterForm, self).clean()
+        cleaned_data = super().clean()
 
 
         self.clean_agreements(cleaned_data)
         self.clean_agreements(cleaned_data)
         self.raise_if_ip_banned()
         self.raise_if_ip_banned()

+ 6 - 7
misago/users/management/commands/createsuperuser.py

@@ -11,7 +11,6 @@ from django.core.exceptions import ValidationError
 from django.core.management.base import BaseCommand
 from django.core.management.base import BaseCommand
 from django.db import DEFAULT_DB_ALIAS, IntegrityError
 from django.db import DEFAULT_DB_ALIAS, IntegrityError
 from django.utils.encoding import force_str
 from django.utils.encoding import force_str
-from django.utils.six.moves import input
 
 
 from misago.users.validators import validate_email, validate_username
 from misago.users.validators import validate_email, validate_username
 
 
@@ -70,7 +69,7 @@ class Command(BaseCommand):
 
 
     def execute(self, *args, **options):
     def execute(self, *args, **options):
         self.stdin = options.get('stdin', sys.stdin)  # Used for testing
         self.stdin = options.get('stdin', sys.stdin)  # Used for testing
-        return super(Command, self).execute(*args, **options)
+        return super().execute(*args, **options)
 
 
     def handle(self, *args, **options):
     def handle(self, *args, **options):
         username = options.get('username')
         username = options.get('username')
@@ -85,7 +84,7 @@ class Command(BaseCommand):
                 username = username.strip()
                 username = username.strip()
                 validate_username(username)
                 validate_username(username)
             except ValidationError as e:
             except ValidationError as e:
-                self.stderr.write(u'\n'.join(e.messages))
+                self.stderr.write('\n'.join(e.messages))
                 username = None
                 username = None
 
 
         if email is not None:
         if email is not None:
@@ -93,7 +92,7 @@ class Command(BaseCommand):
                 email = email.strip()
                 email = email.strip()
                 validate_email(email)
                 validate_email(email)
             except ValidationError as e:
             except ValidationError as e:
-                self.stderr.write(u'\n'.join(e.messages))
+                self.stderr.write('\n'.join(e.messages))
                 email = None
                 email = None
 
 
         if password is not None:
         if password is not None:
@@ -120,7 +119,7 @@ class Command(BaseCommand):
                         validate_username(raw_value)
                         validate_username(raw_value)
                         username = raw_value
                         username = raw_value
                     except ValidationError as e:
                     except ValidationError as e:
-                        self.stderr.write(u'\n'.join(e.messages))
+                        self.stderr.write('\n'.join(e.messages))
 
 
                 while not email:
                 while not email:
                     try:
                     try:
@@ -128,7 +127,7 @@ class Command(BaseCommand):
                         validate_email(raw_value)
                         validate_email(raw_value)
                         email = raw_value
                         email = raw_value
                     except ValidationError as e:
                     except ValidationError as e:
-                        self.stderr.write(u'\n'.join(e.messages))
+                        self.stderr.write('\n'.join(e.messages))
 
 
                 while not password:
                 while not password:
                     raw_value = getpass("Enter password: ")
                     raw_value = getpass("Enter password: ")
@@ -146,7 +145,7 @@ class Command(BaseCommand):
                             raw_value, user=UserModel(username=username, email=email)
                             raw_value, user=UserModel(username=username, email=email)
                         )
                         )
                     except ValidationError as e:
                     except ValidationError as e:
-                        self.stderr.write(u'\n'.join(e.messages))
+                        self.stderr.write('\n'.join(e.messages))
                         response = input('Bypass password validation and create user anyway? [y/N]: ')
                         response = input('Bypass password validation and create user anyway? [y/N]: ')
                         if response.lower() != 'y':
                         if response.lower() != 'y':
                             continue
                             continue

+ 0 - 2
misago/users/management/commands/deleteinactiveusers.py

@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
 from datetime import timedelta
 from datetime import timedelta
 
 
 from django.contrib.auth import get_user_model
 from django.contrib.auth import get_user_model

+ 0 - 2
misago/users/management/commands/deletemarkedusers.py

@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
 from django.contrib.auth import get_user_model
 from django.contrib.auth import get_user_model
 from django.core.management.base import CommandError, BaseCommand
 from django.core.management.base import CommandError, BaseCommand
 
 

+ 0 - 2
misago/users/management/commands/deleteprofilefield.py

@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
 from django.contrib.auth import get_user_model
 from django.contrib.auth import get_user_model
 from django.core.management.base import CommandError, BaseCommand
 from django.core.management.base import CommandError, BaseCommand
 
 

+ 0 - 2
misago/users/management/commands/listusedprofilefields.py

@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
 from django.contrib.auth import get_user_model
 from django.contrib.auth import get_user_model
 from django.core.management.base import BaseCommand
 from django.core.management.base import BaseCommand
 
 

+ 0 - 3
misago/users/migrations/0001_initial.py

@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 import django.db.models.deletion
 import django.db.models.deletion
 import django.utils.timezone
 import django.utils.timezone
 from django.conf import settings
 from django.conf import settings

+ 0 - 3
misago/users/migrations/0002_users_settings.py

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

+ 0 - 3
misago/users/migrations/0003_bans_version_tracker.py

@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations
 from django.db import migrations
 
 
 from misago.core.migrationutils import cachebuster_register_cache
 from misago.core.migrationutils import cachebuster_register_cache

+ 0 - 3
misago/users/migrations/0004_default_ranks.py

@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations
 from django.db import migrations
 from django.utils.translation import ugettext
 from django.utils.translation import ugettext
 
 

+ 0 - 3
misago/users/migrations/0005_dj_19_update.py

@@ -1,7 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.9.7 on 2016-07-17 02:05
 # Generated by Django 1.9.7 on 2016-07-17 02:05
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 from django.db import migrations, models
 
 
 import misago.users.models.user
 import misago.users.models.user

+ 0 - 3
misago/users/migrations/0006_update_settings.py

@@ -1,7 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.10.5 on 2017-02-05 14:34
 # Generated by Django 1.10.5 on 2017-02-05 14:34
-from __future__ import unicode_literals
-
 from django.db import migrations
 from django.db import migrations
 
 
 from misago.conf.migrationutils import delete_settings_cache, migrate_settings_group
 from misago.conf.migrationutils import delete_settings_cache, migrate_settings_group

+ 0 - 3
misago/users/migrations/0007_auto_20170219_1639.py

@@ -1,7 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.10.5 on 2017-02-19 16:39
 # Generated by Django 1.10.5 on 2017-02-19 16:39
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 from django.db import migrations, models
 
 
 
 

+ 0 - 3
misago/users/migrations/0008_ban_registration_only.py

@@ -1,7 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.10.5 on 2017-03-04 22:06
 # Generated by Django 1.10.5 on 2017-03-04 22:06
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 from django.db import migrations, models
 
 
 
 

+ 0 - 3
misago/users/migrations/0009_redo_partial_indexes.py

@@ -1,7 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.1 on 2017-05-26 21:56
 # Generated by Django 1.11.1 on 2017-05-26 21:56
-from __future__ import unicode_literals
-
 from django.db import migrations
 from django.db import migrations
 import misago.core.pgutils
 import misago.core.pgutils
 
 

+ 0 - 3
misago/users/migrations/0010_user_profile_fields.py

@@ -1,7 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.1 on 2017-06-03 22:15
 # Generated by Django 1.11.1 on 2017-06-03 22:15
-from __future__ import unicode_literals
-
 import django.contrib.postgres.fields
 import django.contrib.postgres.fields
 from django.contrib.postgres.operations import HStoreExtension
 from django.contrib.postgres.operations import HStoreExtension
 from django.db import migrations
 from django.db import migrations

+ 0 - 3
misago/users/migrations/0011_auto_20180331_2208.py

@@ -1,7 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.11 on 2018-03-31 22:08
 # Generated by Django 1.11.11 on 2018-03-31 22:08
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 from django.db import migrations, models
 import misago.core.pgutils
 import misago.core.pgutils
 
 

+ 0 - 3
misago/users/migrations/0012_audittrail.py

@@ -1,7 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.13 on 2018-06-03 18:46
 # Generated by Django 1.11.13 on 2018-06-03 18:46
-from __future__ import unicode_literals
-
 from django.conf import settings
 from django.conf import settings
 from django.db import migrations, models
 from django.db import migrations, models
 import django.db.models.deletion
 import django.db.models.deletion

+ 0 - 3
misago/users/migrations/0013_auto_20180609_1523.py

@@ -1,7 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.13 on 2018-06-09 15:23
 # Generated by Django 1.11.13 on 2018-06-09 15:23
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 from django.db import migrations, models
 
 
 
 

+ 0 - 3
misago/users/migrations/0014_datadownload.py

@@ -1,7 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.13 on 2018-06-24 00:13
 # Generated by Django 1.11.13 on 2018-06-24 00:13
-from __future__ import unicode_literals
-
 from django.conf import settings
 from django.conf import settings
 from django.db import migrations, models
 from django.db import migrations, models
 import django.db.models.deletion
 import django.db.models.deletion

+ 0 - 3
misago/users/migrations/0015_user_agreements.py

@@ -1,7 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.15 on 2018-08-16 17:29
 # Generated by Django 1.11.15 on 2018-08-16 17:29
-from __future__ import unicode_literals
-
 import django.contrib.postgres.fields
 import django.contrib.postgres.fields
 from django.db import migrations, models
 from django.db import migrations, models
 
 

+ 2 - 2
misago/users/models/ban.py

@@ -90,7 +90,7 @@ class Ban(models.Model):
         self.banned_value = self.banned_value.lower()
         self.banned_value = self.banned_value.lower()
         self.is_checked = not self.is_expired
         self.is_checked = not self.is_expired
 
 
-        return super(Ban, self).save(*args, **kwargs)
+        return super().save(*args, **kwargs)
 
 
     def get_serialized_message(self):
     def get_serialized_message(self):
         from misago.users.serializers import BanMessageSerializer
         from misago.users.serializers import BanMessageSerializer
@@ -138,7 +138,7 @@ class BanCache(models.Model):
 
 
     def save(self, *args, **kwargs):
     def save(self, *args, **kwargs):
         try:
         try:
-            super(BanCache, self).save(*args, **kwargs)
+            super().save(*args, **kwargs)
         except IntegrityError:
         except IntegrityError:
             pass  # first come is first serve with ban cache
             pass  # first come is first serve with ban cache
 
 

+ 1 - 1
misago/users/models/datadownload.py

@@ -50,4 +50,4 @@ class DataDownload(models.Model):
     def delete(self, *args, **kwargs):
     def delete(self, *args, **kwargs):
         if self.file:
         if self.file:
             self.file.delete(save=False)
             self.file.delete(save=False)
-        super(DataDownload, self).delete(*args, **kwargs)
+        super().delete(*args, **kwargs)

+ 2 - 4
misago/users/models/rank.py

@@ -1,6 +1,5 @@
 from django.db import models, transaction
 from django.db import models, transaction
 from django.urls import reverse
 from django.urls import reverse
-from django.utils.encoding import python_2_unicode_compatible
 
 
 from misago.acl import version as acl_version
 from misago.acl import version as acl_version
 from misago.core.utils import slugify
 from misago.core.utils import slugify
@@ -17,7 +16,6 @@ class RankManager(models.Manager):
             rank.save(update_fields=['is_default'])
             rank.save(update_fields=['is_default'])
 
 
 
 
-@python_2_unicode_compatible
 class Rank(models.Model):
 class Rank(models.Model):
     name = models.CharField(max_length=255)
     name = models.CharField(max_length=255)
     slug = models.CharField(unique=True, max_length=255)
     slug = models.CharField(unique=True, max_length=255)
@@ -42,11 +40,11 @@ class Rank(models.Model):
             self.set_order()
             self.set_order()
         else:
         else:
             acl_version.invalidate()
             acl_version.invalidate()
-        return super(Rank, self).save(*args, **kwargs)
+        return super().save(*args, **kwargs)
 
 
     def delete(self, *args, **kwargs):
     def delete(self, *args, **kwargs):
         acl_version.invalidate()
         acl_version.invalidate()
-        return super(Rank, self).delete(*args, **kwargs)
+        return super().delete(*args, **kwargs)
 
 
     def get_absolute_url(self):
     def get_absolute_url(self):
         return reverse('misago:users-rank', kwargs={'slug': self.slug})
         return reverse('misago:users-rank', kwargs={'slug': self.slug})

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

@@ -305,7 +305,7 @@ class User(AbstractBaseUser, PermissionsMixin):
 
 
         avatars.delete_avatar(self)
         avatars.delete_avatar(self)
 
 
-        return super(User, self).delete(*args, **kwargs)
+        return super().delete(*args, **kwargs)
 
 
     def delete_content(self):
     def delete_content(self):
         from misago.users.signals import delete_user_content
         from misago.users.signals import delete_user_content
@@ -479,7 +479,7 @@ class Online(models.Model):
 
 
     def save(self, *args, **kwargs):
     def save(self, *args, **kwargs):
         try:
         try:
-            super(Online, self).save(*args, **kwargs)
+            super().save(*args, **kwargs)
         except IntegrityError:
         except IntegrityError:
             pass  # first come is first serve in online tracker
             pass  # first come is first serve in online tracker
 
 

+ 0 - 2
misago/users/profilefields/__init__.py

@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
 import copy
 import copy
 import logging
 import logging
 
 

+ 3 - 4
misago/users/profilefields/basefields.py

@@ -1,7 +1,6 @@
 from django import forms
 from django import forms
 from django.db.models import Q
 from django.db.models import Q
 from django.utils import html
 from django.utils import html
-from django.utils.six import text_type
 
 
 from misago.core.utils import format_plaintext_for_html
 from misago.core.utils import format_plaintext_for_html
 
 
@@ -67,7 +66,7 @@ class ProfileField(object):
 
 
         data.update({
         data.update({
             'fieldname': self.fieldname,
             'fieldname': self.fieldname,
-            'name': text_type(self.get_label(user)),
+            'name': str(self.get_label(user)),
         })
         })
 
 
         return data
         return data
@@ -124,7 +123,7 @@ class ChoiceProfileField(ProfileField):
         for key, name in self.get_choices():
         for key, name in self.get_choices():
             if key == value:
             if key == value:
                 return {
                 return {
-                    'text': text_type(name),
+                    'text': str(name),
                 }
                 }
         return None
         return None
 
 
@@ -135,7 +134,7 @@ class ChoiceProfileField(ProfileField):
         })
         })
 
 
         for key, choice in self.get_choices():
         for key, choice in self.get_choices():
-            if key and criteria.lower() in text_type(choice).lower():
+            if key and criteria.lower() in str(choice).lower():
                 q_obj = q_obj | Q(**{
                 q_obj = q_obj | Q(**{
                     'profile_fields__{}'.format(self.fieldname): key
                     'profile_fields__{}'.format(self.fieldname): key
                 })
                 })

+ 0 - 2
misago/users/profilefields/default.py

@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
 import re
 import re
 
 
 from django.forms import ValidationError
 from django.forms import ValidationError

+ 1 - 1
misago/users/social/pipeline.py

@@ -181,7 +181,7 @@ def create_user_with_form(strategy, details, backend, user=None, *args, **kwargs
 
 
     if request.method == 'POST':
     if request.method == 'POST':
         try:
         try:
-            request_data = json.loads(request.body.decode('utf-8'))
+            request_data = json.loads(request.body)
         except (TypeError, ValueError):
         except (TypeError, ValueError):
             request_data = request.POST.copy()
             request_data = request.POST.copy()
             
             

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

@@ -17,7 +17,7 @@ UserModel = get_user_model()
 
 
 class TestActivePostersRanking(AuthenticatedUserTestCase):
 class TestActivePostersRanking(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(TestActivePostersRanking, self).setUp()
+        super().setUp()
 
 
         cache.clear()
         cache.clear()
         threadstore.clear()
         threadstore.clear()
@@ -25,7 +25,7 @@ class TestActivePostersRanking(AuthenticatedUserTestCase):
         self.category = Category.objects.get(slug='first-category')
         self.category = Category.objects.get(slug='first-category')
 
 
     def tearDown(self):
     def tearDown(self):
-        super(TestActivePostersRanking, self).tearDown()
+        super().tearDown()
 
 
         cache.clear()
         cache.clear()
         threadstore.clear()
         threadstore.clear()

+ 3 - 3
misago/users/tests/test_audittrail.py

@@ -23,7 +23,7 @@ class MockRequest(object):
 
 
 class CreateAuditTrailTests(UserTestCase):
 class CreateAuditTrailTests(UserTestCase):
     def setUp(self):
     def setUp(self):
-        super(CreateAuditTrailTests, self).setUp()
+        super().setUp()
 
 
         self.obj = UserModel.objects.create_user('BobBoberson', 'bob@example.com')
         self.obj = UserModel.objects.create_user('BobBoberson', 'bob@example.com')
 
 
@@ -95,7 +95,7 @@ class CreateAuditTrailTests(UserTestCase):
 
 
 class CreateUserAuditTrailTests(UserTestCase):
 class CreateUserAuditTrailTests(UserTestCase):
     def setUp(self):
     def setUp(self):
-        super(CreateUserAuditTrailTests, self).setUp()
+        super().setUp()
 
 
         self.obj = UserModel.objects.create_user('BobBoberson', 'bob@example.com')
         self.obj = UserModel.objects.create_user('BobBoberson', 'bob@example.com')
 
 
@@ -161,7 +161,7 @@ class CreateUserAuditTrailTests(UserTestCase):
 
 
 class RemoveOldAuditTrailsTest(UserTestCase):
 class RemoveOldAuditTrailsTest(UserTestCase):
     def setUp(self):
     def setUp(self):
-        super(RemoveOldAuditTrailsTest, self).setUp()
+        super().setUp()
 
 
         self.obj = UserModel.objects.create_user('BobBoberson', 'bob@example.com')
         self.obj = UserModel.objects.create_user('BobBoberson', 'bob@example.com')
         
         

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

@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 from django.test import TestCase
 from django.test import TestCase
 from django.urls import reverse
 from django.urls import reverse
 
 
@@ -68,7 +67,7 @@ class AuthViewsTests(TestCase):
         response = self.client.post(
         response = self.client.post(
             reverse('misago:login'),
             reverse('misago:login'),
             data={
             data={
-                'redirect_to': u'łelcome!',
+                'redirect_to': 'łelcome!',
             },
             },
         )
         )
 
 

+ 5 - 4
misago/users/tests/test_avatars.py

@@ -1,4 +1,5 @@
-from path import Path
+from pathlib import Path
+
 from PIL import Image
 from PIL import Image
 
 
 from django.contrib.auth import get_user_model
 from django.contrib.auth import get_user_model
@@ -47,7 +48,7 @@ class AvatarsStoreTests(TestCase):
         for old_avatar in avatars_dict.values():
         for old_avatar in avatars_dict.values():
             avatar_path = Path(old_avatar.image.path)
             avatar_path = Path(old_avatar.image.path)
             self.assertFalse(avatar_path.exists())
             self.assertFalse(avatar_path.exists())
-            self.assertFalse(avatar_path.isfile())
+            self.assertFalse(avatar_path.is_file())
 
 
             with self.assertRaises(Avatar.DoesNotExist):
             with self.assertRaises(Avatar.DoesNotExist):
                 Avatar.objects.get(pk=old_avatar.pk)
                 Avatar.objects.get(pk=old_avatar.pk)
@@ -76,7 +77,7 @@ class AvatarsStoreTests(TestCase):
         for removed_avatar in new_avatars_dict.values():
         for removed_avatar in new_avatars_dict.values():
             avatar_path = Path(removed_avatar.image.path)
             avatar_path = Path(removed_avatar.image.path)
             self.assertFalse(avatar_path.exists())
             self.assertFalse(avatar_path.exists())
-            self.assertFalse(avatar_path.isfile())
+            self.assertFalse(avatar_path.is_file())
 
 
             with self.assertRaises(Avatar.DoesNotExist):
             with self.assertRaises(Avatar.DoesNotExist):
                 Avatar.objects.get(pk=removed_avatar.pk)
                 Avatar.objects.get(pk=removed_avatar.pk)
@@ -106,7 +107,7 @@ class AvatarSetterTests(TestCase):
         for avatar in user.avatar_set.all():
         for avatar in user.avatar_set.all():
             avatar_path = Path(avatar.image.path)
             avatar_path = Path(avatar.image.path)
             self.assertTrue(avatar_path.exists())
             self.assertTrue(avatar_path.exists())
-            self.assertTrue(avatar_path.isfile())
+            self.assertTrue(avatar_path.is_file())
 
 
             avatars_dict[avatar.size] = avatar
             avatars_dict[avatar.size] = avatar
 
 

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

@@ -1,4 +1,3 @@
-#-*- coding: utf-8 -*-
 from django.test import TestCase
 from django.test import TestCase
 
 
 from misago.users.models import Ban
 from misago.users.models import Ban

+ 5 - 6
misago/users/tests/test_bio_profilefield.py

@@ -1,6 +1,5 @@
 from django.contrib.auth import get_user_model
 from django.contrib.auth import get_user_model
 from django.urls import reverse
 from django.urls import reverse
-from django.utils import six
 
 
 from misago.admin.testutils import AdminTestCase
 from misago.admin.testutils import AdminTestCase
 
 
@@ -10,7 +9,7 @@ UserModel = get_user_model()
 
 
 class BioProfileFieldTests(AdminTestCase):
 class BioProfileFieldTests(AdminTestCase):
     def setUp(self):
     def setUp(self):
-        super(BioProfileFieldTests, self).setUp()
+        super().setUp()
 
 
         self.test_link = reverse(
         self.test_link = reverse(
             'misago:admin:users:accounts:edit',
             'misago:admin:users:accounts:edit',
@@ -36,8 +35,8 @@ class BioProfileFieldTests(AdminTestCase):
             self.test_link,
             self.test_link,
             data={
             data={
                 'username': 'Edited',
                 'username': 'Edited',
-                'rank': six.text_type(self.user.rank_id),
-                'roles': six.text_type(self.user.roles.all()[0].pk),
+                'rank': str(self.user.rank_id),
+                'roles': str(self.user.roles.all()[0].pk),
                 'email': 'reg@stered.com',
                 'email': 'reg@stered.com',
                 'new_password': '',
                 'new_password': '',
                 'signature': '',
                 'signature': '',
@@ -61,8 +60,8 @@ class BioProfileFieldTests(AdminTestCase):
             self.test_link,
             self.test_link,
             data={
             data={
                 'username': 'Edited',
                 'username': 'Edited',
-                'rank': six.text_type(self.user.rank_id),
-                'roles': six.text_type(self.user.roles.all()[0].pk),
+                'rank': str(self.user.rank_id),
+                'roles': str(self.user.roles.all()[0].pk),
                 'email': 'reg@stered.com',
                 'email': 'reg@stered.com',
                 'bio': 'Edited field!',
                 'bio': 'Edited field!',
                 'new_password': '',
                 'new_password': '',

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

@@ -1,7 +1,8 @@
+from io import StringIO
+
 from django.contrib.auth import get_user_model
 from django.contrib.auth import get_user_model
 from django.core.management import call_command
 from django.core.management import call_command
 from django.test import TestCase
 from django.test import TestCase
-from django.utils.six import StringIO
 
 
 
 
 UserModel = get_user_model()
 UserModel = get_user_model()

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

@@ -60,7 +60,7 @@ class ExpireUserDataDownloadTests(AuthenticatedUserTestCase):
 
 
 class PrepareUserDataDownload(AuthenticatedUserTestCase):
 class PrepareUserDataDownload(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(PrepareUserDataDownload, self).setUp()
+        super().setUp()
         self.download = request_user_data_download(self.user)
         self.download = request_user_data_download(self.user)
 
 
     def assert_download_is_valid(self):
     def assert_download_is_valid(self):

+ 13 - 15
misago/users/tests/test_datadownloads_dataarchive.py

@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-import io  # fixme: remove explicit io imports after going py3k-only
 import os
 import os
 from collections import OrderedDict
 from collections import OrderedDict
 
 
@@ -56,14 +54,14 @@ class DataArchiveTests(AuthenticatedUserTestCase):
     def test_add_text_str(self):
     def test_add_text_str(self):
         """add_dict method creates text file with string"""
         """add_dict method creates text file with string"""
         with DataArchive(self.user, DATA_DOWNLOADS_WORKING_DIR) as archive:
         with DataArchive(self.user, DATA_DOWNLOADS_WORKING_DIR) as archive:
-            data_to_write = u"Hello, łorld!"
+            data_to_write = "Hello, łorld!"
             file_path = archive.add_text('testfile', data_to_write)
             file_path = archive.add_text('testfile', data_to_write)
             self.assertTrue(os.path.isfile(file_path))
             self.assertTrue(os.path.isfile(file_path))
 
 
             valid_output_path = os.path.join(archive.data_dir_path, 'testfile.txt')
             valid_output_path = os.path.join(archive.data_dir_path, 'testfile.txt')
             self.assertEqual(file_path, valid_output_path)
             self.assertEqual(file_path, valid_output_path)
 
 
-            with io.open(file_path, 'r', encoding="utf-8") as fp:
+            with open(file_path, 'r') as fp:
                 saved_data = fp.read().strip()
                 saved_data = fp.read().strip()
                 self.assertEqual(saved_data, data_to_write)
                 self.assertEqual(saved_data, data_to_write)
 
 
@@ -77,44 +75,44 @@ class DataArchiveTests(AuthenticatedUserTestCase):
             valid_output_path = os.path.join(archive.data_dir_path, 'testfile.txt')
             valid_output_path = os.path.join(archive.data_dir_path, 'testfile.txt')
             self.assertEqual(file_path, valid_output_path)
             self.assertEqual(file_path, valid_output_path)
 
 
-            with io.open(file_path, 'r', encoding="utf-8") as fp:
+            with open(file_path, 'r') as fp:
                 saved_data = fp.read().strip()
                 saved_data = fp.read().strip()
                 self.assertEqual(saved_data, str(data_to_write))
                 self.assertEqual(saved_data, str(data_to_write))
 
 
     def test_add_dict(self):
     def test_add_dict(self):
         """add_dict method creates text file from dict"""
         """add_dict method creates text file from dict"""
         with DataArchive(self.user, DATA_DOWNLOADS_WORKING_DIR) as archive:
         with DataArchive(self.user, DATA_DOWNLOADS_WORKING_DIR) as archive:
-            data_to_write = {'first': u"łorld!", 'second': u"łup!"}
+            data_to_write = {'first': "łorld!", 'second': "łup!"}
             file_path = archive.add_dict('testfile', data_to_write)
             file_path = archive.add_dict('testfile', data_to_write)
             self.assertTrue(os.path.isfile(file_path))
             self.assertTrue(os.path.isfile(file_path))
 
 
             valid_output_path = os.path.join(archive.data_dir_path, 'testfile.txt')
             valid_output_path = os.path.join(archive.data_dir_path, 'testfile.txt')
             self.assertEqual(file_path, valid_output_path)
             self.assertEqual(file_path, valid_output_path)
 
 
-            with io.open(file_path, 'r', encoding="utf-8") as fp:
+            with open(file_path, 'r') as fp:
                 saved_data = fp.read().strip()
                 saved_data = fp.read().strip()
                 # order of dict items in py<3.6 is non-deterministic
                 # order of dict items in py<3.6 is non-deterministic
                 # making testing for exact match a mistake
                 # making testing for exact match a mistake
-                self.assertIn(u"first: łorld!", saved_data)
-                self.assertIn(u"second: łup!", saved_data)
+                self.assertIn("first: łorld!", saved_data)
+                self.assertIn("second: łup!", saved_data)
 
 
     def test_add_dict_ordered(self):
     def test_add_dict_ordered(self):
         """add_dict method creates text file form ordered dict"""
         """add_dict method creates text file form ordered dict"""
         with DataArchive(self.user, DATA_DOWNLOADS_WORKING_DIR) as archive:
         with DataArchive(self.user, DATA_DOWNLOADS_WORKING_DIR) as archive:
-            data_to_write = OrderedDict((('first', u"łorld!"), ('second', u"łup!")))
+            data_to_write = OrderedDict((('first', "łorld!"), ('second', "łup!")))
             file_path = archive.add_dict('testfile', data_to_write)
             file_path = archive.add_dict('testfile', data_to_write)
             self.assertTrue(os.path.isfile(file_path))
             self.assertTrue(os.path.isfile(file_path))
 
 
             valid_output_path = os.path.join(archive.data_dir_path, 'testfile.txt')
             valid_output_path = os.path.join(archive.data_dir_path, 'testfile.txt')
             self.assertEqual(file_path, valid_output_path)
             self.assertEqual(file_path, valid_output_path)
 
 
-            with io.open(file_path, 'r', encoding="utf-8") as fp:
+            with open(file_path, 'r') as fp:
                 saved_data = fp.read().strip()
                 saved_data = fp.read().strip()
-                self.assertEqual(saved_data, u"first: łorld!\nsecond: łup!")
+                self.assertEqual(saved_data, "first: łorld!\nsecond: łup!")
 
 
     def test_add_model_file(self):
     def test_add_model_file(self):
         """add_model_file method adds model file"""
         """add_model_file method adds model file"""
-        with io.open(TEST_AVATAR_PATH, 'rb') as avatar:
+        with open(TEST_AVATAR_PATH, 'rb') as avatar:
             self.user.avatar_tmp = File(avatar)
             self.user.avatar_tmp = File(avatar)
             self.user.save()
             self.user.save()
 
 
@@ -136,7 +134,7 @@ class DataArchiveTests(AuthenticatedUserTestCase):
 
 
     def test_add_model_file_prefixed(self):
     def test_add_model_file_prefixed(self):
         """add_model_file method adds model file with prefix"""
         """add_model_file method adds model file with prefix"""
-        with io.open(TEST_AVATAR_PATH, 'rb') as avatar:
+        with open(TEST_AVATAR_PATH, 'rb') as avatar:
             self.user.avatar_tmp = File(avatar)
             self.user.avatar_tmp = File(avatar)
             self.user.save()
             self.user.save()
 
 
@@ -206,7 +204,7 @@ class DataArchiveTests(AuthenticatedUserTestCase):
         """get_file returns django file"""
         """get_file returns django file"""
         django_file = None
         django_file = None
         
         
-        with io.open(TEST_AVATAR_PATH, 'rb') as avatar:
+        with open(TEST_AVATAR_PATH, 'rb') as avatar:
             self.user.avatar_tmp = File(avatar)
             self.user.avatar_tmp = File(avatar)
             self.user.save()
             self.user.save()
 
 

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

@@ -1,10 +1,10 @@
 from datetime import timedelta
 from datetime import timedelta
+from io import StringIO
 
 
 from django.contrib.auth import get_user_model
 from django.contrib.auth import get_user_model
 from django.core.management import call_command
 from django.core.management import call_command
 from django.test import TestCase, override_settings
 from django.test import TestCase, override_settings
 from django.utils import timezone
 from django.utils import timezone
-from django.utils.six import StringIO
 
 
 from misago.users.management.commands import deleteinactiveusers
 from misago.users.management.commands import deleteinactiveusers
 
 

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

@@ -1,7 +1,8 @@
+from io import StringIO
+
 from django.contrib.auth import get_user_model
 from django.contrib.auth import get_user_model
 from django.core.management import call_command
 from django.core.management import call_command
 from django.test import TestCase, override_settings
 from django.test import TestCase, override_settings
-from django.utils.six import StringIO
 
 
 from misago.users.management.commands import deletemarkedusers
 from misago.users.management.commands import deletemarkedusers
 
 

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

@@ -1,7 +1,8 @@
+from io import StringIO
+
 from django.contrib.auth import get_user_model
 from django.contrib.auth import get_user_model
 from django.core.management import call_command
 from django.core.management import call_command
 from django.test import TestCase
 from django.test import TestCase
-from django.utils.six import StringIO
 
 
 from misago.users.management.commands import deleteprofilefield
 from misago.users.management.commands import deleteprofilefield
 
 

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

@@ -11,7 +11,7 @@ from misago.users.djangoadmin import UserAdminModel
 @override_settings(ROOT_URLCONF='misago.core.testproject.urls')
 @override_settings(ROOT_URLCONF='misago.core.testproject.urls')
 class TestDjangoAdminUserForm(AdminTestCase):
 class TestDjangoAdminUserForm(AdminTestCase):
     def setUp(self):
     def setUp(self):
-        super(TestDjangoAdminUserForm, self).setUp()
+        super().setUp()
         self.test_user = get_user_model().objects.create_user(
         self.test_user = get_user_model().objects.create_user(
             username='Bob',
             username='Bob',
             email='bob@test.com',
             email='bob@test.com',

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

@@ -1,9 +1,9 @@
 import os
 import os
 from datetime import timedelta
 from datetime import timedelta
+from io import StringIO
 
 
 from django.core.files import File
 from django.core.files import File
 from django.core.management import call_command
 from django.core.management import call_command
-from django.utils.six import StringIO
 
 
 from misago.users.datadownloads import request_user_data_download
 from misago.users.datadownloads import request_user_data_download
 from misago.users.management.commands import expireuserdatadownloads
 from misago.users.management.commands import expireuserdatadownloads

+ 7 - 8
misago/users/tests/test_gender_profilefield.py

@@ -1,6 +1,5 @@
 from django.contrib.auth import get_user_model
 from django.contrib.auth import get_user_model
 from django.urls import reverse
 from django.urls import reverse
-from django.utils import six
 
 
 from misago.admin.testutils import AdminTestCase
 from misago.admin.testutils import AdminTestCase
 
 
@@ -10,7 +9,7 @@ UserModel = get_user_model()
 
 
 class GenderProfileFieldTests(AdminTestCase):
 class GenderProfileFieldTests(AdminTestCase):
     def setUp(self):
     def setUp(self):
-        super(GenderProfileFieldTests, self).setUp()
+        super().setUp()
 
 
         self.test_link = reverse(
         self.test_link = reverse(
             'misago:admin:users:accounts:edit',
             'misago:admin:users:accounts:edit',
@@ -36,8 +35,8 @@ class GenderProfileFieldTests(AdminTestCase):
             self.test_link,
             self.test_link,
             data={
             data={
                 'username': 'Edited',
                 'username': 'Edited',
-                'rank': six.text_type(self.user.rank_id),
-                'roles': six.text_type(self.user.roles.all()[0].pk),
+                'rank': str(self.user.rank_id),
+                'roles': str(self.user.roles.all()[0].pk),
                 'email': 'reg@stered.com',
                 'email': 'reg@stered.com',
                 'new_password': '',
                 'new_password': '',
                 'signature': '',
                 'signature': '',
@@ -61,8 +60,8 @@ class GenderProfileFieldTests(AdminTestCase):
             self.test_link,
             self.test_link,
             data={
             data={
                 'username': 'Edited',
                 'username': 'Edited',
-                'rank': six.text_type(self.user.rank_id),
-                'roles': six.text_type(self.user.roles.all()[0].pk),
+                'rank': str(self.user.rank_id),
+                'roles': str(self.user.roles.all()[0].pk),
                 'email': 'reg@stered.com',
                 'email': 'reg@stered.com',
                 'gender': 'attackcopter',
                 'gender': 'attackcopter',
                 'new_password': '',
                 'new_password': '',
@@ -85,8 +84,8 @@ class GenderProfileFieldTests(AdminTestCase):
             self.test_link,
             self.test_link,
             data={
             data={
                 'username': 'Edited',
                 'username': 'Edited',
-                'rank': six.text_type(self.user.rank_id),
-                'roles': six.text_type(self.user.roles.all()[0].pk),
+                'rank': str(self.user.rank_id),
+                'roles': str(self.user.roles.all()[0].pk),
                 'email': 'reg@stered.com',
                 'email': 'reg@stered.com',
                 'gender': 'female',
                 'gender': 'female',
                 'new_password': '',
                 'new_password': '',

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

@@ -1,10 +1,10 @@
 from datetime import timedelta
 from datetime import timedelta
+from io import StringIO
 
 
 from django.contrib.auth import get_user_model
 from django.contrib.auth import get_user_model
 from django.core.management import call_command
 from django.core.management import call_command
 from django.test import TestCase
 from django.test import TestCase
 from django.utils import timezone
 from django.utils import timezone
-from django.utils.six import StringIO
 
 
 from misago.users import bans
 from misago.users import bans
 from misago.users.management.commands import invalidatebans
 from misago.users.management.commands import invalidatebans

+ 3 - 4
misago/users/tests/test_joinip_profilefield.py

@@ -1,6 +1,5 @@
 from django.contrib.auth import get_user_model
 from django.contrib.auth import get_user_model
 from django.urls import reverse
 from django.urls import reverse
-from django.utils import six
 
 
 from misago.admin.testutils import AdminTestCase
 from misago.admin.testutils import AdminTestCase
 from misago.acl.testutils import override_acl
 from misago.acl.testutils import override_acl
@@ -11,7 +10,7 @@ UserModel = get_user_model()
 
 
 class JoinIpProfileFieldTests(AdminTestCase):
 class JoinIpProfileFieldTests(AdminTestCase):
     def setUp(self):
     def setUp(self):
-        super(JoinIpProfileFieldTests, self).setUp()
+        super().setUp()
 
 
         self.test_link = reverse(
         self.test_link = reverse(
             'misago:admin:users:accounts:edit',
             'misago:admin:users:accounts:edit',
@@ -33,8 +32,8 @@ class JoinIpProfileFieldTests(AdminTestCase):
             self.test_link,
             self.test_link,
             data={
             data={
                 'username': 'Edited',
                 'username': 'Edited',
-                'rank': six.text_type(self.user.rank_id),
-                'roles': six.text_type(self.user.roles.all()[0].pk),
+                'rank': str(self.user.rank_id),
+                'roles': str(self.user.roles.all()[0].pk),
                 'email': 'reg@stered.com',
                 'email': 'reg@stered.com',
                 'join_ip': '127.0.0.1',
                 'join_ip': '127.0.0.1',
                 'new_password': '',
                 'new_password': '',

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

@@ -14,7 +14,7 @@ UserModel = get_user_model()
 
 
 class UsersListTestCase(AuthenticatedUserTestCase):
 class UsersListTestCase(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(UsersListTestCase, self).setUp()
+        super().setUp()
         override_acl(self.user, {
         override_acl(self.user, {
             'can_browse_users_list': 1,
             'can_browse_users_list': 1,
         })
         })

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

@@ -1,7 +1,8 @@
+from io import StringIO
+
 from django.contrib.auth import get_user_model
 from django.contrib.auth import get_user_model
 from django.core.management import call_command
 from django.core.management import call_command
 from django.test import TestCase
 from django.test import TestCase
-from django.utils.six import StringIO
 
 
 from misago.users.management.commands import listusedprofilefields
 from misago.users.management.commands import listusedprofilefields
 
 

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

@@ -1,6 +1,7 @@
+from io import StringIO
+
 from django.core.management import call_command
 from django.core.management import call_command
 from django.test import TestCase
 from django.test import TestCase
-from django.utils.six import StringIO
 
 
 from misago.users.management.commands import loadavatargallery
 from misago.users.management.commands import loadavatargallery
 from misago.users.models import AvatarGallery
 from misago.users.models import AvatarGallery

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

@@ -10,7 +10,7 @@ UserModel = get_user_model()
 
 
 class GetUserStatusTests(AuthenticatedUserTestCase):
 class GetUserStatusTests(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(GetUserStatusTests, self).setUp()
+        super().setUp()
         self.other_user = UserModel.objects.create_user('Tyrael', 't123@test.com', 'pass123')
         self.other_user = UserModel.objects.create_user('Tyrael', 't123@test.com', 'pass123')
 
 
     def test_user_hiding_presence(self):
     def test_user_hiding_presence(self):

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

@@ -25,7 +25,7 @@ class OptionsViewsTests(AuthenticatedUserTestCase):
 
 
 class ConfirmChangeEmailTests(AuthenticatedUserTestCase):
 class ConfirmChangeEmailTests(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(ConfirmChangeEmailTests, self).setUp()
+        super().setUp()
         link = '/api/users/%s/change-email/' % self.user.pk
         link = '/api/users/%s/change-email/' % self.user.pk
 
 
         response = self.client.post(
         response = self.client.post(
@@ -64,7 +64,7 @@ class ConfirmChangeEmailTests(AuthenticatedUserTestCase):
 
 
 class ConfirmChangePasswordTests(AuthenticatedUserTestCase):
 class ConfirmChangePasswordTests(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(ConfirmChangePasswordTests, self).setUp()
+        super().setUp()
         link = '/api/users/%s/change-password/' % self.user.pk
         link = '/api/users/%s/change-password/' % self.user.pk
 
 
         response = self.client.post(
         response = self.client.post(

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

@@ -1,7 +1,8 @@
+from io import StringIO
+
 from django.contrib.auth import get_user_model
 from django.contrib.auth import get_user_model
 from django.core.management import call_command
 from django.core.management import call_command
 from django.test import TestCase
 from django.test import TestCase
-from django.utils.six import StringIO
 
 
 from misago.users.management.commands import populateonlinetracker
 from misago.users.management.commands import populateonlinetracker
 from misago.users.models import Online
 from misago.users.models import Online

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

@@ -1,6 +1,7 @@
+from io import StringIO
+
 from django.core import mail
 from django.core import mail
 from django.core.management import call_command
 from django.core.management import call_command
-from django.utils.six import StringIO
 
 
 from misago.conf import settings
 from misago.conf import settings
 from misago.users.datadownloads import request_user_data_download
 from misago.users.datadownloads import request_user_data_download

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

@@ -13,7 +13,7 @@ UserModel = get_user_model()
 
 
 class UserProfileViewsTests(AuthenticatedUserTestCase):
 class UserProfileViewsTests(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(UserProfileViewsTests, self).setUp()
+        super().setUp()
         self.link_kwargs = {'slug': self.user.slug, 'pk': self.user.pk}
         self.link_kwargs = {'slug': self.user.slug, 'pk': self.user.pk}
 
 
         self.category = Category.objects.get(slug='first-category')
         self.category = Category.objects.get(slug='first-category')

+ 3 - 4
misago/users/tests/test_profilefields.py

@@ -1,6 +1,5 @@
 from django.contrib.auth import get_user_model
 from django.contrib.auth import get_user_model
 from django.test import TestCase
 from django.test import TestCase
-from django.utils.six import text_type
 
 
 from misago.users.profilefields import ProfileFields
 from misago.users.profilefields import ProfileFields
 
 
@@ -46,7 +45,7 @@ class ProfileFieldsLoadTests(TestCase):
         try:
         try:
             profilefields.load()
             profilefields.load()
         except ValueError as e:
         except ValueError as e:
-            error = text_type(e)
+            error = str(e)
 
 
             self.assertIn('misago.users.tests.testfiles.profilefields.NofieldnameField', error)
             self.assertIn('misago.users.tests.testfiles.profilefields.NofieldnameField', error)
             self.assertIn('profile field has to specify fieldname attribute', error)
             self.assertIn('profile field has to specify fieldname attribute', error)
@@ -74,7 +73,7 @@ class ProfileFieldsLoadTests(TestCase):
         try:
         try:
             profilefields.load()
             profilefields.load()
         except ValueError as e:
         except ValueError as e:
-            error = text_type(e)
+            error = str(e)
 
 
             self.assertIn('misago.users.profilefields.default.TwitterHandleField', error)
             self.assertIn('misago.users.profilefields.default.TwitterHandleField', error)
             self.assertIn('profile field has been specified twice', error)
             self.assertIn('profile field has been specified twice', error)
@@ -102,7 +101,7 @@ class ProfileFieldsLoadTests(TestCase):
         try:
         try:
             profilefields.load()
             profilefields.load()
         except ValueError as e:
         except ValueError as e:
-            error = text_type(e)
+            error = str(e)
 
 
             self.assertIn('misago.users.tests.testfiles.profilefields.FieldnameField', error)
             self.assertIn('misago.users.tests.testfiles.profilefields.FieldnameField', error)
             self.assertIn('misago.users.tests.testfiles.profilefields.RepeatedFieldnameField', error)
             self.assertIn('misago.users.tests.testfiles.profilefields.RepeatedFieldnameField', error)

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

@@ -1,10 +1,10 @@
 from datetime import timedelta
 from datetime import timedelta
+from io import StringIO
 
 
 from django.contrib.auth import get_user_model
 from django.contrib.auth import get_user_model
 from django.core.management import call_command
 from django.core.management import call_command
 from django.test import TestCase, override_settings
 from django.test import TestCase, override_settings
 from django.utils import timezone
 from django.utils import timezone
-from django.utils.six import StringIO
 
 
 from misago.users.management.commands import removeoldips
 from misago.users.management.commands import removeoldips
 
 

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

@@ -10,7 +10,7 @@ UserModel = get_user_model()
 
 
 class SearchApiTests(AuthenticatedUserTestCase):
 class SearchApiTests(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(SearchApiTests, self).setUp()
+        super().setUp()
 
 
         self.api_link = reverse('misago:api:search')
         self.api_link = reverse('misago:api:search')
 
 
@@ -138,7 +138,7 @@ class SearchApiTests(AuthenticatedUserTestCase):
 
 
 class SearchProviderApiTests(SearchApiTests):
 class SearchProviderApiTests(SearchApiTests):
     def setUp(self):
     def setUp(self):
-        super(SearchProviderApiTests, self).setUp()
+        super().setUp()
 
 
         self.api_link = reverse(
         self.api_link = reverse(
             'misago:api:search', kwargs={
             'misago:api:search', kwargs={

+ 10 - 11
misago/users/tests/test_social_pipeline.py

@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 import json
 import json
 
 
 from django.contrib.auth import get_user_model
 from django.contrib.auth import get_user_model
@@ -230,12 +229,12 @@ class CreateUser(PipelineTestCase):
 
 
 class CreateUserWithFormTests(PipelineTestCase):
 class CreateUserWithFormTests(PipelineTestCase):
     def setUp(self):
     def setUp(self):
-        super(CreateUserWithFormTests, self).setUp()
+        super().setUp()
 
 
         Agreement.objects.invalidate_cache()
         Agreement.objects.invalidate_cache()
 
 
     def tearDown(self):
     def tearDown(self):
-        super(CreateUserWithFormTests, self).tearDown()
+        super().tearDown()
         
         
         Agreement.objects.invalidate_cache()
         Agreement.objects.invalidate_cache()
 
 
@@ -580,14 +579,14 @@ class GetUsernameTests(PipelineTestCase):
 
 
     def test_normalize_username(self):
     def test_normalize_username(self):
         """pipeline step normalizes username"""
         """pipeline step normalizes username"""
-        result = get_username(None, {'username': u'Błop Błoperson'}, None)
+        result = get_username(None, {'username': 'Błop Błoperson'}, None)
         self.assertEqual(result, {'clean_username': 'BlopBloperson'})
         self.assertEqual(result, {'clean_username': 'BlopBloperson'})
 
 
     def test_resolve_to_first_name(self):
     def test_resolve_to_first_name(self):
         """pipeline attempts to use first name because username is taken"""
         """pipeline attempts to use first name because username is taken"""
         details = {
         details = {
             'username': self.user.username,
             'username': self.user.username,
-            'first_name': u'Błob',
+            'first_name': 'Błob',
         }
         }
         result = get_username(None, details, None)
         result = get_username(None, details, None)
         self.assertEqual(result, {'clean_username': 'Blob'})
         self.assertEqual(result, {'clean_username': 'Blob'})
@@ -596,7 +595,7 @@ class GetUsernameTests(PipelineTestCase):
         """pipeline will not fallback to last name because username is taken"""
         """pipeline will not fallback to last name because username is taken"""
         details = {
         details = {
             'username': self.user.username,
             'username': self.user.username,
-            'last_name': u'Błob',
+            'last_name': 'Błob',
         }
         }
         result = get_username(None, details, None)
         result = get_username(None, details, None)
         self.assertIsNone(result)
         self.assertIsNone(result)
@@ -605,7 +604,7 @@ class GetUsernameTests(PipelineTestCase):
         """pipeline will construct username from first name and first char of surname"""
         """pipeline will construct username from first name and first char of surname"""
         details = {
         details = {
             'first_name': self.user.username,
             'first_name': self.user.username,
-            'last_name': u'Błob',
+            'last_name': 'Błob',
         }
         }
         result = get_username(None, details, None)
         result = get_username(None, details, None)
         self.assertEqual(result, {'clean_username': self.user.username + 'B'})
         self.assertEqual(result, {'clean_username': self.user.username + 'B'})
@@ -615,7 +614,7 @@ class GetUsernameTests(PipelineTestCase):
         Ban.objects.create(banned_value='*Admin*', check_type=Ban.USERNAME)
         Ban.objects.create(banned_value='*Admin*', check_type=Ban.USERNAME)
         details = {
         details = {
             'username': 'Misago Admin',
             'username': 'Misago Admin',
-            'first_name': u'Błob',
+            'first_name': 'Błob',
         }
         }
         result = get_username(None, details, None)
         result = get_username(None, details, None)
         self.assertEqual(result, {'clean_username': 'Blob'})
         self.assertEqual(result, {'clean_username': 'Blob'})
@@ -625,7 +624,7 @@ class GetUsernameTests(PipelineTestCase):
         Ban.objects.create(banned_value='*Admin*', check_type=Ban.USERNAME)
         Ban.objects.create(banned_value='*Admin*', check_type=Ban.USERNAME)
         details = {
         details = {
             'username': 'Misago Admin',
             'username': 'Misago Admin',
-            'full_name': u'Błob Błopo',
+            'full_name': 'Błob Błopo',
         }
         }
         result = get_username(None, details, None)
         result = get_username(None, details, None)
         self.assertEqual(result, {'clean_username': 'BlobBlopo'})
         self.assertEqual(result, {'clean_username': 'BlobBlopo'})
@@ -633,7 +632,7 @@ class GetUsernameTests(PipelineTestCase):
     def test_resolve_to_cut_name(self):
     def test_resolve_to_cut_name(self):
         """pipeline will resolve cut too long name on second pass"""
         """pipeline will resolve cut too long name on second pass"""
         details = {
         details = {
-            'username': u'Abrakadabrapokuskonstantynopolitańczykowianeczkatrzy',
+            'username': 'Abrakadabrapokuskonstantynopolitańczykowianeczkatrzy',
         }
         }
         result = get_username(None, details, None)
         result = get_username(None, details, None)
         self.assertEqual(result, {'clean_username': 'Abrakadabrapok'})
         self.assertEqual(result, {'clean_username': 'Abrakadabrapok'})
@@ -641,7 +640,7 @@ class GetUsernameTests(PipelineTestCase):
 
 
 class RequireActivationTests(PipelineTestCase):
 class RequireActivationTests(PipelineTestCase):
     def setUp(self):
     def setUp(self):
-        super(RequireActivationTests, self).setUp()
+        super().setUp()
 
 
         self.user.requires_activation = UserModel.ACTIVATION_ADMIN
         self.user.requires_activation = UserModel.ACTIVATION_ADMIN
         self.user.save()
         self.user.save()

+ 7 - 8
misago/users/tests/test_twitter_profilefield.py

@@ -1,6 +1,5 @@
 from django.contrib.auth import get_user_model
 from django.contrib.auth import get_user_model
 from django.urls import reverse
 from django.urls import reverse
-from django.utils import six
 
 
 from misago.admin.testutils import AdminTestCase
 from misago.admin.testutils import AdminTestCase
 
 
@@ -10,7 +9,7 @@ UserModel = get_user_model()
 
 
 class TwitterProfileFieldTests(AdminTestCase):
 class TwitterProfileFieldTests(AdminTestCase):
     def setUp(self):
     def setUp(self):
-        super(TwitterProfileFieldTests, self).setUp()
+        super().setUp()
 
 
         self.test_link = reverse(
         self.test_link = reverse(
             'misago:admin:users:accounts:edit',
             'misago:admin:users:accounts:edit',
@@ -36,8 +35,8 @@ class TwitterProfileFieldTests(AdminTestCase):
             self.test_link,
             self.test_link,
             data={
             data={
                 'username': 'Edited',
                 'username': 'Edited',
-                'rank': six.text_type(self.user.rank_id),
-                'roles': six.text_type(self.user.roles.all()[0].pk),
+                'rank': str(self.user.rank_id),
+                'roles': str(self.user.roles.all()[0].pk),
                 'email': 'reg@stered.com',
                 'email': 'reg@stered.com',
                 'new_password': '',
                 'new_password': '',
                 'signature': '',
                 'signature': '',
@@ -61,8 +60,8 @@ class TwitterProfileFieldTests(AdminTestCase):
             self.test_link,
             self.test_link,
             data={
             data={
                 'username': 'Edited',
                 'username': 'Edited',
-                'rank': six.text_type(self.user.rank_id),
-                'roles': six.text_type(self.user.roles.all()[0].pk),
+                'rank': str(self.user.rank_id),
+                'roles': str(self.user.roles.all()[0].pk),
                 'email': 'reg@stered.com',
                 'email': 'reg@stered.com',
                 'twitter': 'lorem!ipsum',
                 'twitter': 'lorem!ipsum',
                 'new_password': '',
                 'new_password': '',
@@ -85,8 +84,8 @@ class TwitterProfileFieldTests(AdminTestCase):
             self.test_link,
             self.test_link,
             data={
             data={
                 'username': 'Edited',
                 'username': 'Edited',
-                'rank': six.text_type(self.user.rank_id),
-                'roles': six.text_type(self.user.roles.all()[0].pk),
+                'rank': str(self.user.rank_id),
+                'roles': str(self.user.roles.all()[0].pk),
                 'email': 'reg@stered.com',
                 'email': 'reg@stered.com',
                 'twitter': 'lorem_ipsum',
                 'twitter': 'lorem_ipsum',
                 'new_password': '',
                 'new_password': '',

+ 6 - 7
misago/users/tests/test_user_avatar_api.py

@@ -1,7 +1,6 @@
 import json
 import json
 import os
 import os
-
-from path import Path
+from pathlib import Path
 
 
 from django.contrib.auth import get_user_model
 from django.contrib.auth import get_user_model
 
 
@@ -22,7 +21,7 @@ class UserAvatarTests(AuthenticatedUserTestCase):
     """tests for user avatar RPC (/api/users/1/avatar/)"""
     """tests for user avatar RPC (/api/users/1/avatar/)"""
 
 
     def setUp(self):
     def setUp(self):
-        super(UserAvatarTests, self).setUp()
+        super().setUp()
         self.link = '/api/users/%s/avatar/' % self.user.pk
         self.link = '/api/users/%s/avatar/' % self.user.pk
         self.client.post(self.link, data={'avatar': 'generated'})
         self.client.post(self.link, data={'avatar': 'generated'})
 
 
@@ -142,7 +141,7 @@ class UserAvatarTests(AuthenticatedUserTestCase):
 
 
         avatar = Path(self.get_current_user().avatar_tmp.path)
         avatar = Path(self.get_current_user().avatar_tmp.path)
         self.assertTrue(avatar.exists())
         self.assertTrue(avatar.exists())
-        self.assertTrue(avatar.isfile())
+        self.assertTrue(avatar.is_file())
 
 
         response = self.client.post(
         response = self.client.post(
             self.link,
             self.link,
@@ -168,7 +167,7 @@ class UserAvatarTests(AuthenticatedUserTestCase):
 
 
         avatar = Path(self.get_current_user().avatar_src.path)
         avatar = Path(self.get_current_user().avatar_src.path)
         self.assertTrue(avatar.exists())
         self.assertTrue(avatar.exists())
-        self.assertTrue(avatar.isfile())
+        self.assertTrue(avatar.is_file())
 
 
         response = self.client.post(
         response = self.client.post(
             self.link,
             self.link,
@@ -210,7 +209,7 @@ class UserAvatarTests(AuthenticatedUserTestCase):
 
 
         avatar = Path(self.get_current_user().avatar_src.path)
         avatar = Path(self.get_current_user().avatar_src.path)
         self.assertFalse(avatar.exists())
         self.assertFalse(avatar.exists())
-        self.assertFalse(avatar.isfile())
+        self.assertFalse(avatar.is_file())
 
 
     def test_gallery_set_empty_gallery(self):
     def test_gallery_set_empty_gallery(self):
         """gallery handles set avatar on empty gallery"""
         """gallery handles set avatar on empty gallery"""
@@ -297,7 +296,7 @@ class UserAvatarModerationTests(AuthenticatedUserTestCase):
     """tests for moderate user avatar RPC (/api/users/1/moderate-avatar/)"""
     """tests for moderate user avatar RPC (/api/users/1/moderate-avatar/)"""
 
 
     def setUp(self):
     def setUp(self):
-        super(UserAvatarModerationTests, self).setUp()
+        super().setUp()
 
 
         self.other_user = UserModel.objects.create_user("OtherUser", "other@user.com", "pass123")
         self.other_user = UserModel.objects.create_user("OtherUser", "other@user.com", "pass123")
 
 

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

@@ -12,7 +12,7 @@ class UserChangeEmailTests(AuthenticatedUserTestCase):
     """tests for user change email RPC (/api/users/1/change-email/)"""
     """tests for user change email RPC (/api/users/1/change-email/)"""
 
 
     def setUp(self):
     def setUp(self):
-        super(UserChangeEmailTests, self).setUp()
+        super().setUp()
         self.link = '/api/users/%s/change-email/' % self.user.pk
         self.link = '/api/users/%s/change-email/' % self.user.pk
 
 
     def test_unsupported_methods(self):
     def test_unsupported_methods(self):

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

@@ -8,7 +8,7 @@ class UserChangePasswordTests(AuthenticatedUserTestCase):
     """tests for user change password RPC (/api/users/1/change-password/)"""
     """tests for user change password RPC (/api/users/1/change-password/)"""
 
 
     def setUp(self):
     def setUp(self):
-        super(UserChangePasswordTests, self).setUp()
+        super().setUp()
         self.link = '/api/users/%s/change-password/' % self.user.pk
         self.link = '/api/users/%s/change-password/' % self.user.pk
 
 
     def test_unsupported_methods(self):
     def test_unsupported_methods(self):

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

@@ -16,7 +16,7 @@ class UserCreateTests(UserTestCase):
     """tests for new user registration (POST to /api/users/)"""
     """tests for new user registration (POST to /api/users/)"""
 
 
     def setUp(self):
     def setUp(self):
-        super(UserCreateTests, self).setUp()
+        super().setUp()
         
         
         Agreement.objects.invalidate_cache()
         Agreement.objects.invalidate_cache()
 
 

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

@@ -4,7 +4,7 @@ from misago.users.testutils import AuthenticatedUserTestCase
 
 
 class UserDataDownloadsApiTests(AuthenticatedUserTestCase):
 class UserDataDownloadsApiTests(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(UserDataDownloadsApiTests, self).setUp()
+        super().setUp()
         self.link = '/api/users/%s/data-downloads/' % self.user.pk
         self.link = '/api/users/%s/data-downloads/' % self.user.pk
 
 
     def test_get_other_user_exports_anonymous(self):
     def test_get_other_user_exports_anonymous(self):

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

@@ -11,7 +11,7 @@ UserModel = get_user_model()
 
 
 class UserEditDetailsApiTests(AuthenticatedUserTestCase):
 class UserEditDetailsApiTests(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(UserEditDetailsApiTests, self).setUp()
+        super().setUp()
 
 
         self.api_link = reverse(
         self.api_link = reverse(
             'misago:api:user-edit-details',
             'misago:api:user-edit-details',

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

@@ -6,7 +6,7 @@ from misago.threads.tests.test_threads_api import ThreadsApiTestCase
 
 
 class UserThreadsApiTests(ThreadsApiTestCase):
 class UserThreadsApiTests(ThreadsApiTestCase):
     def setUp(self):
     def setUp(self):
-        super(UserThreadsApiTests, self).setUp()
+        super().setUp()
 
 
         self.api_link = reverse(
         self.api_link = reverse(
             'misago:api:user-threads', kwargs={
             'misago:api:user-threads', kwargs={
@@ -92,7 +92,7 @@ class UserThreadsApiTests(ThreadsApiTestCase):
 
 
 class UserPostsApiTests(ThreadsApiTestCase):
 class UserPostsApiTests(ThreadsApiTestCase):
     def setUp(self):
     def setUp(self):
-        super(UserPostsApiTests, self).setUp()
+        super().setUp()
 
 
         self.api_link = reverse(
         self.api_link = reverse(
             'misago:api:user-posts', kwargs={
             'misago:api:user-posts', kwargs={

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

@@ -7,7 +7,7 @@ from misago.users.testutils import AuthenticatedUserTestCase
 
 
 class UserMiddlewareTest(AuthenticatedUserTestCase):
 class UserMiddlewareTest(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(UserMiddlewareTest, self).setUp()
+        super().setUp()
 
 
         self.api_link = reverse('misago:api:auth')
         self.api_link = reverse('misago:api:auth')
         self.test_link = reverse('misago:index')
         self.test_link = reverse('misago:index')

+ 6 - 7
misago/users/tests/test_user_model.py

@@ -1,5 +1,4 @@
-# -*- coding: utf-8 -*-
-from path import Path
+from pathlib import Path
 
 
 from django.core.exceptions import ValidationError
 from django.core.exceptions import ValidationError
 from django.test import TestCase
 from django.test import TestCase
@@ -75,13 +74,13 @@ class UserManagerTests(TestCase):
     def test_getters_unicode_handling(self):
     def test_getters_unicode_handling(self):
         """get_by_ methods handle unicode"""
         """get_by_ methods handle unicode"""
         with self.assertRaises(User.DoesNotExist):
         with self.assertRaises(User.DoesNotExist):
-            User.objects.get_by_username(u'łóć')
+            User.objects.get_by_username('łóć')
 
 
         with self.assertRaises(User.DoesNotExist):
         with self.assertRaises(User.DoesNotExist):
-            User.objects.get_by_email(u'łóć@polskimail.pl')
+            User.objects.get_by_email('łóć@polskimail.pl')
 
 
         with self.assertRaises(User.DoesNotExist):
         with self.assertRaises(User.DoesNotExist):
-            User.objects.get_by_username_or_email(u'łóć@polskimail.pl')
+            User.objects.get_by_username_or_email('łóć@polskimail.pl')
 
 
 
 
 class UserModelTests(TestCase):
 class UserModelTests(TestCase):
@@ -103,7 +102,7 @@ class UserModelTests(TestCase):
         for avatar in user.avatar_set.all():
         for avatar in user.avatar_set.all():
             avatar_path = Path(avatar.image.path)
             avatar_path = Path(avatar.image.path)
             self.assertTrue(avatar_path.exists())
             self.assertTrue(avatar_path.exists())
-            self.assertTrue(avatar_path.isfile())
+            self.assertTrue(avatar_path.is_file())
             user_avatars.append(avatar)
             user_avatars.append(avatar)
         self.assertNotEqual(user_avatars, [])
         self.assertNotEqual(user_avatars, [])
         
         
@@ -112,7 +111,7 @@ class UserModelTests(TestCase):
         for removed_avatar in user_avatars:
         for removed_avatar in user_avatars:
             avatar_path = Path(removed_avatar.image.path)
             avatar_path = Path(removed_avatar.image.path)
             self.assertFalse(avatar_path.exists())
             self.assertFalse(avatar_path.exists())
-            self.assertFalse(avatar_path.isfile())
+            self.assertFalse(avatar_path.is_file())
 
 
             with self.assertRaises(Avatar.DoesNotExist):
             with self.assertRaises(Avatar.DoesNotExist):
                 Avatar.objects.get(pk=removed_avatar.pk)
                 Avatar.objects.get(pk=removed_avatar.pk)

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

@@ -6,7 +6,7 @@ from misago.users.testutils import AuthenticatedUserTestCase
 
 
 class UserRequestDataDownload(AuthenticatedUserTestCase):
 class UserRequestDataDownload(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(UserRequestDataDownload, self).setUp()
+        super().setUp()
         self.link = '/api/users/%s/request-data-download/' % self.user.pk
         self.link = '/api/users/%s/request-data-download/' % self.user.pk
 
 
     def test_request_other_user_download_anonymous(self):
     def test_request_other_user_download_anonymous(self):

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

@@ -6,7 +6,7 @@ class UserSignatureTests(AuthenticatedUserTestCase):
     """tests for user signature RPC (POST to /api/users/1/signature/)"""
     """tests for user signature RPC (POST to /api/users/1/signature/)"""
 
 
     def setUp(self):
     def setUp(self):
-        super(UserSignatureTests, self).setUp()
+        super().setUp()
         self.link = '/api/users/%s/signature/' % self.user.pk
         self.link = '/api/users/%s/signature/' % self.user.pk
 
 
     def test_signature_no_permission(self):
     def test_signature_no_permission(self):

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

@@ -14,7 +14,7 @@ class UserUsernameTests(AuthenticatedUserTestCase):
     """tests for user change name RPC (POST to /api/users/1/username/)"""
     """tests for user change name RPC (POST to /api/users/1/username/)"""
 
 
     def setUp(self):
     def setUp(self):
-        super(UserUsernameTests, self).setUp()
+        super().setUp()
         self.link = '/api/users/%s/username/' % self.user.pk
         self.link = '/api/users/%s/username/' % self.user.pk
 
 
     def test_get_change_username_options(self):
     def test_get_change_username_options(self):
@@ -109,7 +109,7 @@ class UserUsernameModerationTests(AuthenticatedUserTestCase):
     """tests for moderate username RPC (/api/users/1/moderate-username/)"""
     """tests for moderate username RPC (/api/users/1/moderate-username/)"""
 
 
     def setUp(self):
     def setUp(self):
-        super(UserUsernameModerationTests, self).setUp()
+        super().setUp()
 
 
         self.other_user = UserModel.objects.create_user("OtherUser", "other@user.com", "pass123")
         self.other_user = UserModel.objects.create_user("OtherUser", "other@user.com", "pass123")
 
 

+ 30 - 31
misago/users/tests/test_useradmin_views.py

@@ -1,7 +1,6 @@
 from django.contrib.auth import get_user_model
 from django.contrib.auth import get_user_model
 from django.core import mail
 from django.core import mail
 from django.urls import reverse
 from django.urls import reverse
-from django.utils import six
 
 
 from misago.acl.models import Role
 from misago.acl.models import Role
 from misago.admin.testutils import AdminTestCase
 from misago.admin.testutils import AdminTestCase
@@ -453,8 +452,8 @@ class UserAdminViewsTests(AdminTestCase):
             reverse('misago:admin:users:accounts:new'),
             reverse('misago:admin:users:accounts:new'),
             data={
             data={
                 'username': 'Bawww',
                 'username': 'Bawww',
-                'rank': six.text_type(default_rank.pk),
-                'roles': six.text_type(authenticated_role.pk),
+                'rank': str(default_rank.pk),
+                'roles': str(authenticated_role.pk),
                 'email': 'reg@stered.com',
                 'email': 'reg@stered.com',
                 'new_password': 'pass123',
                 'new_password': 'pass123',
                 'staff_level': '0',
                 'staff_level': '0',
@@ -479,8 +478,8 @@ class UserAdminViewsTests(AdminTestCase):
             reverse('misago:admin:users:accounts:new'),
             reverse('misago:admin:users:accounts:new'),
             data={
             data={
                 'username': 'Bawww',
                 'username': 'Bawww',
-                'rank': six.text_type(default_rank.pk),
-                'roles': six.text_type(authenticated_role.pk),
+                'rank': str(default_rank.pk),
+                'roles': str(authenticated_role.pk),
                 'email': 'reg@stered.com',
                 'email': 'reg@stered.com',
                 'new_password': ' pass123 ',
                 'new_password': ' pass123 ',
                 'staff_level': '0',
                 'staff_level': '0',
@@ -509,8 +508,8 @@ class UserAdminViewsTests(AdminTestCase):
             test_link,
             test_link,
             data={
             data={
                 'username': 'Bawww',
                 'username': 'Bawww',
-                'rank': six.text_type(test_user.rank_id),
-                'roles': six.text_type(test_user.roles.all()[0].pk),
+                'rank': str(test_user.rank_id),
+                'roles': str(test_user.roles.all()[0].pk),
                 'email': 'reg@stered.com',
                 'email': 'reg@stered.com',
                 'new_password': 'newpass123',
                 'new_password': 'newpass123',
                 'staff_level': '0',
                 'staff_level': '0',
@@ -554,8 +553,8 @@ class UserAdminViewsTests(AdminTestCase):
             test_link,
             test_link,
             data={
             data={
                 'username': 'Bob',
                 'username': 'Bob',
-                'rank': six.text_type(test_user.rank_id),
-                'roles': six.text_type(test_user.roles.all()[0].pk),
+                'rank': str(test_user.rank_id),
+                'roles': str(test_user.roles.all()[0].pk),
                 'email': 'reg@stered.com',
                 'email': 'reg@stered.com',
                 'new_password': 'pass123',
                 'new_password': 'pass123',
                 'signature': 'Hello world!',
                 'signature': 'Hello world!',
@@ -591,8 +590,8 @@ class UserAdminViewsTests(AdminTestCase):
             test_link,
             test_link,
             data={
             data={
                 'username': 'Bawww',
                 'username': 'Bawww',
-                'rank': six.text_type(test_user.rank_id),
-                'roles': six.text_type(test_user.roles.all()[0].pk),
+                'rank': str(test_user.rank_id),
+                'roles': str(test_user.roles.all()[0].pk),
                 'email': 'reg@stered.com',
                 'email': 'reg@stered.com',
                 'new_password': ' newpass123 ',
                 'new_password': ' newpass123 ',
                 'staff_level': '0',
                 'staff_level': '0',
@@ -633,8 +632,8 @@ class UserAdminViewsTests(AdminTestCase):
             test_link,
             test_link,
             data={
             data={
                 'username': 'Bawww',
                 'username': 'Bawww',
-                'rank': six.text_type(test_user.rank_id),
-                'roles': six.text_type(test_user.roles.all()[0].pk),
+                'rank': str(test_user.rank_id),
+                'roles': str(test_user.roles.all()[0].pk),
                 'email': 'reg@stered.com',
                 'email': 'reg@stered.com',
                 'new_password': 'pass123',
                 'new_password': 'pass123',
                 'is_staff': '1',
                 'is_staff': '1',
@@ -672,8 +671,8 @@ class UserAdminViewsTests(AdminTestCase):
             test_link,
             test_link,
             data={
             data={
                 'username': 'Bawww',
                 'username': 'Bawww',
-                'rank': six.text_type(test_user.rank_id),
-                'roles': six.text_type(test_user.roles.all()[0].pk),
+                'rank': str(test_user.rank_id),
+                'roles': str(test_user.roles.all()[0].pk),
                 'email': 'reg@stered.com',
                 'email': 'reg@stered.com',
                 'new_password': 'pass123',
                 'new_password': 'pass123',
                 'is_staff': '0',
                 'is_staff': '0',
@@ -718,8 +717,8 @@ class UserAdminViewsTests(AdminTestCase):
             test_link,
             test_link,
             data={
             data={
                 'username': 'Bawww',
                 'username': 'Bawww',
-                'rank': six.text_type(test_user.rank_id),
-                'roles': six.text_type(test_user.roles.all()[0].pk),
+                'rank': str(test_user.rank_id),
+                'roles': str(test_user.roles.all()[0].pk),
                 'email': 'reg@stered.com',
                 'email': 'reg@stered.com',
                 'new_password': 'pass123',
                 'new_password': 'pass123',
                 'is_staff': '0',
                 'is_staff': '0',
@@ -760,8 +759,8 @@ class UserAdminViewsTests(AdminTestCase):
             test_link,
             test_link,
             data={
             data={
                 'username': 'Bawww',
                 'username': 'Bawww',
-                'rank': six.text_type(test_user.rank_id),
-                'roles': six.text_type(test_user.roles.all()[0].pk),
+                'rank': str(test_user.rank_id),
+                'roles': str(test_user.roles.all()[0].pk),
                 'email': 'reg@stered.com',
                 'email': 'reg@stered.com',
                 'new_password': 'pass123',
                 'new_password': 'pass123',
                 'is_staff': '1',
                 'is_staff': '1',
@@ -802,8 +801,8 @@ class UserAdminViewsTests(AdminTestCase):
             test_link,
             test_link,
             data={
             data={
                 'username': 'Bawww',
                 'username': 'Bawww',
-                'rank': six.text_type(test_user.rank_id),
-                'roles': six.text_type(test_user.roles.all()[0].pk),
+                'rank': str(test_user.rank_id),
+                'roles': str(test_user.roles.all()[0].pk),
                 'email': 'reg@stered.com',
                 'email': 'reg@stered.com',
                 'new_password': 'pass123',
                 'new_password': 'pass123',
                 'is_staff': '0',
                 'is_staff': '0',
@@ -850,8 +849,8 @@ class UserAdminViewsTests(AdminTestCase):
             test_link,
             test_link,
             data={
             data={
                 'username': 'Bawww',
                 'username': 'Bawww',
-                'rank': six.text_type(test_user.rank_id),
-                'roles': six.text_type(test_user.roles.all()[0].pk),
+                'rank': str(test_user.rank_id),
+                'roles': str(test_user.roles.all()[0].pk),
                 'email': 'reg@stered.com',
                 'email': 'reg@stered.com',
                 'new_password': 'pass123',
                 'new_password': 'pass123',
                 'is_staff': '1',
                 'is_staff': '1',
@@ -898,8 +897,8 @@ class UserAdminViewsTests(AdminTestCase):
             test_link,
             test_link,
             data={
             data={
                 'username': 'Bawww',
                 'username': 'Bawww',
-                'rank': six.text_type(test_user.rank_id),
-                'roles': six.text_type(test_user.roles.all()[0].pk),
+                'rank': str(test_user.rank_id),
+                'roles': str(test_user.roles.all()[0].pk),
                 'email': 'reg@stered.com',
                 'email': 'reg@stered.com',
                 'new_password': 'pass123',
                 'new_password': 'pass123',
                 'is_staff': '1',
                 'is_staff': '1',
@@ -941,8 +940,8 @@ class UserAdminViewsTests(AdminTestCase):
             test_link,
             test_link,
             data={
             data={
                 'username': 'Bawww',
                 'username': 'Bawww',
-                'rank': six.text_type(test_user.rank_id),
-                'roles': six.text_type(test_user.roles.all()[0].pk),
+                'rank': str(test_user.rank_id),
+                'roles': str(test_user.roles.all()[0].pk),
                 'email': 'reg@stered.com',
                 'email': 'reg@stered.com',
                 'new_password': 'pass123',
                 'new_password': 'pass123',
                 'is_staff': '1',
                 'is_staff': '1',
@@ -982,8 +981,8 @@ class UserAdminViewsTests(AdminTestCase):
             test_link,
             test_link,
             data={
             data={
                 'username': 'Bawww',
                 'username': 'Bawww',
-                'rank': six.text_type(test_user.rank_id),
-                'roles': six.text_type(test_user.roles.all()[0].pk),
+                'rank': str(test_user.rank_id),
+                'roles': str(test_user.roles.all()[0].pk),
                 'email': 'reg@stered.com',
                 'email': 'reg@stered.com',
                 'new_password': 'pass123',
                 'new_password': 'pass123',
                 'is_staff': '1',
                 'is_staff': '1',
@@ -1022,8 +1021,8 @@ class UserAdminViewsTests(AdminTestCase):
             test_link,
             test_link,
             data={
             data={
                 'username': 'Bawww',
                 'username': 'Bawww',
-                'rank': six.text_type(test_user.rank_id),
-                'roles': six.text_type(test_user.roles.all()[0].pk),
+                'rank': str(test_user.rank_id),
+                'roles': str(test_user.roles.all()[0].pk),
                 'email': 'reg@stered.com',
                 'email': 'reg@stered.com',
                 'is_staff': '1',
                 'is_staff': '1',
                 'is_superuser': '0',
                 'is_superuser': '0',

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

@@ -4,7 +4,7 @@ from misago.users.testutils import AuthenticatedUserTestCase
 
 
 class UsernameChangesApiTests(AuthenticatedUserTestCase):
 class UsernameChangesApiTests(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(UsernameChangesApiTests, self).setUp()
+        super().setUp()
         self.link = '/api/username-changes/'
         self.link = '/api/username-changes/'
 
 
     def test_user_can_always_see_his_name_changes(self):
     def test_user_can_always_see_his_name_changes(self):

+ 11 - 11
misago/users/tests/test_users_api.py

@@ -24,7 +24,7 @@ class ActivePostersListTests(AuthenticatedUserTestCase):
     """tests for active posters list (GET /users/?list=active)"""
     """tests for active posters list (GET /users/?list=active)"""
 
 
     def setUp(self):
     def setUp(self):
-        super(ActivePostersListTests, self).setUp()
+        super().setUp()
         self.link = '/api/users/?list=active'
         self.link = '/api/users/?list=active'
 
 
         cache.clear()
         cache.clear()
@@ -71,7 +71,7 @@ class FollowersListTests(AuthenticatedUserTestCase):
     """tests for generic list (GET /users/) filtered by followers"""
     """tests for generic list (GET /users/) filtered by followers"""
 
 
     def setUp(self):
     def setUp(self):
-        super(FollowersListTests, self).setUp()
+        super().setUp()
         self.link = '/api/users/%s/followers/'
         self.link = '/api/users/%s/followers/'
 
 
     def test_nonexistent_user(self):
     def test_nonexistent_user(self):
@@ -117,7 +117,7 @@ class FollowsListTests(AuthenticatedUserTestCase):
     """tests for generic list (GET /users/) filtered by follows"""
     """tests for generic list (GET /users/) filtered by follows"""
 
 
     def setUp(self):
     def setUp(self):
-        super(FollowsListTests, self).setUp()
+        super().setUp()
         self.link = '/api/users/%s/follows/'
         self.link = '/api/users/%s/follows/'
 
 
     def test_nonexistent_user(self):
     def test_nonexistent_user(self):
@@ -163,7 +163,7 @@ class RankListTests(AuthenticatedUserTestCase):
     """tests for generic list (GET /users/) filtered by rank"""
     """tests for generic list (GET /users/) filtered by rank"""
 
 
     def setUp(self):
     def setUp(self):
-        super(RankListTests, self).setUp()
+        super().setUp()
         self.link = '/api/users/?rank=%s'
         self.link = '/api/users/?rank=%s'
 
 
     def test_nonexistent_rank(self):
     def test_nonexistent_rank(self):
@@ -237,7 +237,7 @@ class SearchNamesListTests(AuthenticatedUserTestCase):
     """tests for generic list (GET /users/) filtered by username disallowing searches"""
     """tests for generic list (GET /users/) filtered by username disallowing searches"""
 
 
     def setUp(self):
     def setUp(self):
-        super(SearchNamesListTests, self).setUp()
+        super().setUp()
         self.link = '/api/users/?&name='
         self.link = '/api/users/?&name='
 
 
     def test_empty_list(self):
     def test_empty_list(self):
@@ -253,7 +253,7 @@ class SearchNamesListTests(AuthenticatedUserTestCase):
 
 
 class UserRetrieveTests(AuthenticatedUserTestCase):
 class UserRetrieveTests(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(UserRetrieveTests, self).setUp()
+        super().setUp()
 
 
         self.test_user = UserModel.objects.create_user('Tyrael', 't123@test.com', 'pass123')
         self.test_user = UserModel.objects.create_user('Tyrael', 't123@test.com', 'pass123')
         self.link = reverse(
         self.link = reverse(
@@ -289,7 +289,7 @@ class UserForumOptionsTests(AuthenticatedUserTestCase):
     """tests for user forum options RPC (POST to /api/users/1/forum-options/)"""
     """tests for user forum options RPC (POST to /api/users/1/forum-options/)"""
 
 
     def setUp(self):
     def setUp(self):
-        super(UserForumOptionsTests, self).setUp()
+        super().setUp()
         self.link = '/api/users/%s/forum-options/' % self.user.pk
         self.link = '/api/users/%s/forum-options/' % self.user.pk
 
 
     def test_empty_request(self):
     def test_empty_request(self):
@@ -397,7 +397,7 @@ class UserFollowTests(AuthenticatedUserTestCase):
     """tests for user follow RPC (POST to /api/users/1/follow/)"""
     """tests for user follow RPC (POST to /api/users/1/follow/)"""
 
 
     def setUp(self):
     def setUp(self):
-        super(UserFollowTests, self).setUp()
+        super().setUp()
 
 
         self.other_user = UserModel.objects.create_user("OtherUser", "other@user.com", "pass123")
         self.other_user = UserModel.objects.create_user("OtherUser", "other@user.com", "pass123")
 
 
@@ -461,7 +461,7 @@ class UserBanTests(AuthenticatedUserTestCase):
     """tests for ban endpoint (GET to /api/users/1/ban/)"""
     """tests for ban endpoint (GET to /api/users/1/ban/)"""
 
 
     def setUp(self):
     def setUp(self):
-        super(UserBanTests, self).setUp()
+        super().setUp()
 
 
         self.other_user = UserModel.objects.create_user("OtherUser", "other@user.com", "pass123")
         self.other_user = UserModel.objects.create_user("OtherUser", "other@user.com", "pass123")
 
 
@@ -503,7 +503,7 @@ class UserBanTests(AuthenticatedUserTestCase):
 class UserDeleteOwnAccountTests(AuthenticatedUserTestCase):
 class UserDeleteOwnAccountTests(AuthenticatedUserTestCase):
     """tests for user request own account delete RPC (POST to /api/users/1/delete-own-account/)"""
     """tests for user request own account delete RPC (POST to /api/users/1/delete-own-account/)"""
     def setUp(self):
     def setUp(self):
-        super(UserDeleteOwnAccountTests, self).setUp()
+        super().setUp()
         self.api_link = '/api/users/%s/delete-own-account/' % self.user.pk
         self.api_link = '/api/users/%s/delete-own-account/' % self.user.pk
 
 
     @override_settings(MISAGO_ENABLE_DELETE_OWN_ACCOUNT=False)
     @override_settings(MISAGO_ENABLE_DELETE_OWN_ACCOUNT=False)
@@ -576,7 +576,7 @@ class UserDeleteTests(AuthenticatedUserTestCase):
     """tests for user delete RPC (POST to /api/users/1/delete/)"""
     """tests for user delete RPC (POST to /api/users/1/delete/)"""
 
 
     def setUp(self):
     def setUp(self):
-        super(UserDeleteTests, self).setUp()
+        super().setUp()
 
 
         self.other_user = UserModel.objects.create_user("OtherUser", "other@user.com", "pass123")
         self.other_user = UserModel.objects.create_user("OtherUser", "other@user.com", "pass123")
 
 

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

@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 from django.test import TestCase
 from django.test import TestCase
 
 
 from misago.users.utils import hash_email
 from misago.users.utils import hash_email
@@ -11,4 +10,4 @@ class HashEmailTests(TestCase):
 
 
     def test_handles_unicode(self):
     def test_handles_unicode(self):
         """util works with unicode strings"""
         """util works with unicode strings"""
-        self.assertEqual(hash_email(u'łóć@test.com'), hash_email(u'ŁÓĆ@tEst.cOm'))
+        self.assertEqual(hash_email('łóć@test.com'), hash_email('ŁÓĆ@tEst.cOm'))

+ 2 - 3
misago/users/tests/test_validators.py

@@ -1,4 +1,3 @@
-#-*- coding: utf-8 -*-
 from django.contrib.auth import get_user_model
 from django.contrib.auth import get_user_model
 from django.core.exceptions import ValidationError
 from django.core.exceptions import ValidationError
 from django.test import TestCase
 from django.test import TestCase
@@ -110,9 +109,9 @@ class ValidateUsernameContentTests(TestCase):
         with self.assertRaises(ValidationError):
         with self.assertRaises(ValidationError):
             validate_username_content('Bob Boberson')
             validate_username_content('Bob Boberson')
         with self.assertRaises(ValidationError):
         with self.assertRaises(ValidationError):
-            validate_username_content(u'Rafał')
+            validate_username_content('Rafał')
         with self.assertRaises(ValidationError):
         with self.assertRaises(ValidationError):
-            validate_username_content(u'初音 ミク')
+            validate_username_content('初音 ミク')
 
 
 
 
 class ValidateUsernameLengthTests(TestCase):
 class ValidateUsernameLengthTests(TestCase):

+ 1 - 1
misago/users/testutils.py

@@ -13,7 +13,7 @@ class UserTestCase(MisagoTestCase):
     USER_IP = '127.0.0.1'
     USER_IP = '127.0.0.1'
 
 
     def setUp(self):
     def setUp(self):
-        super(UserTestCase, self).setUp()
+        super().setUp()
         self.get_initial_user()
         self.get_initial_user()
 
 
     def get_initial_user(self):
     def get_initial_user(self):

+ 1 - 2
misago/users/tokens.py

@@ -12,7 +12,6 @@ from hashlib import sha256
 from time import time
 from time import time
 
 
 from django.conf import settings
 from django.conf import settings
-from django.utils import six
 from django.utils.encoding import force_bytes
 from django.utils.encoding import force_bytes
 
 
 
 
@@ -54,7 +53,7 @@ def _make_hash(user, token_type):
         settings.SECRET_KEY,
         settings.SECRET_KEY,
     ]
     ]
 
 
-    return sha256(force_bytes('+'.join([six.text_type(s) for s in seeds]))).hexdigest()[:8]
+    return sha256(force_bytes('+'.join([str(s) for s in seeds]))).hexdigest()[:8]
 
 
 
 
 def _days_since_epoch():
 def _days_since_epoch():

+ 1 - 1
misago/users/validators.py

@@ -100,7 +100,7 @@ def validate_username(value, exclude=None):
 
 
 
 
 # New account validators
 # New account validators
-SFS_API_URL = u'http://api.stopforumspam.org/api?email=%(email)s&ip=%(ip)s&f=json&confidence'  # noqa
+SFS_API_URL = 'http://api.stopforumspam.org/api?email=%(email)s&ip=%(ip)s&f=json&confidence'  # noqa
 
 
 
 
 def validate_with_sfs(request, cleaned_data, add_error):
 def validate_with_sfs(request, cleaned_data, add_error):

+ 1 - 1
misago/users/views/admin/bans.py

@@ -14,7 +14,7 @@ class BanAdmin(generic.AdminBaseMixin):
     message_404 = _("Requested ban does not exist.")
     message_404 = _("Requested ban does not exist.")
 
 
     def handle_form(self, form, request, target):
     def handle_form(self, form, request, target):
-        super(BanAdmin, self).handle_form(form, request, target)
+        super().handle_form(form, request, target)
         Ban.objects.invalidate_cache()
         Ban.objects.invalidate_cache()
 
 
 
 

+ 1 - 1
misago/users/views/admin/datadownloads.py

@@ -38,7 +38,7 @@ class DataDownloadsList(DataDownloadAdmin, generic.ListView):
     ]
     ]
 
 
     def get_queryset(self):
     def get_queryset(self):
-        qs = super(DataDownloadsList, self).get_queryset()
+        qs = super().get_queryset()
         return qs.select_related('user', 'requester')
         return qs.select_related('user', 'requester')
         
         
     def get_search_form(self, request):
     def get_search_form(self, request):

+ 1 - 1
misago/users/views/admin/ranks.py

@@ -21,7 +21,7 @@ class RankAdmin(generic.AdminBaseMixin):
             target.roles.add(*roles)
             target.roles.add(*roles)
 
 
     def handle_form(self, form, request, target):
     def handle_form(self, form, request, target):
-        super(RankAdmin, self).handle_form(form, request, target)
+        super().handle_form(form, request, target)
         self.update_roles(target, form.cleaned_data['roles'])
         self.update_roles(target, form.cleaned_data['roles'])
 
 
 
 

+ 2 - 2
misago/users/views/admin/users.py

@@ -97,7 +97,7 @@ class UsersList(UserAdmin, generic.ListView):
     ]
     ]
 
 
     def get_queryset(self):
     def get_queryset(self):
-        qs = super(UsersList, self).get_queryset()
+        qs = super().get_queryset()
         return qs.select_related('rank')
         return qs.select_related('rank')
 
 
     def get_search_form(self, request):
     def get_search_form(self, request):
@@ -278,7 +278,7 @@ class EditUser(UserAdmin, generic.ModelFormView):
     def real_dispatch(self, request, target):
     def real_dispatch(self, request, target):
         target.old_username = target.username
         target.old_username = target.username
         target.old_is_avatar_locked = target.is_avatar_locked
         target.old_is_avatar_locked = target.is_avatar_locked
-        return super(EditUser, self).real_dispatch(request, target)
+        return super().real_dispatch(request, target)
 
 
     def initialize_form(self, form, request, target):
     def initialize_form(self, form, request, target):
         if request.method == 'POST':
         if request.method == 'POST':

+ 5 - 4
misago/users/views/auth.py

@@ -1,9 +1,10 @@
+from urllib.parse import urlparse
+
 from django.conf import settings
 from django.conf import settings
 from django.contrib import auth
 from django.contrib import auth
 from django.shortcuts import redirect
 from django.shortcuts import redirect
 from django.urls import NoReverseMatch
 from django.urls import NoReverseMatch
 from django.utils.http import is_safe_url
 from django.utils.http import is_safe_url
-from django.utils.six.moves.urllib.parse import urlparse
 from django.views.decorators.cache import never_cache
 from django.views.decorators.cache import never_cache
 from django.views.decorators.csrf import csrf_protect
 from django.views.decorators.csrf import csrf_protect
 from django.views.decorators.debug import sensitive_post_parameters
 from django.views.decorators.debug import sensitive_post_parameters
@@ -24,10 +25,10 @@ def login(request):
             if is_redirect_safe:
             if is_redirect_safe:
                 redirect_to_path = urlparse(redirect_to).path
                 redirect_to_path = urlparse(redirect_to).path
                 if '?' not in redirect_to_path:
                 if '?' not in redirect_to_path:
-                    redirect_to_path = u'{}?'.format(redirect_to_path)
+                    redirect_to_path = '{}?'.format(redirect_to_path)
                 else:
                 else:
-                    redirect_to_path = u'{}&'.format(redirect_to_path)
-                redirect_to_path = u'{}ref=login'.format(redirect_to_path)
+                    redirect_to_path = '{}&'.format(redirect_to_path)
+                redirect_to_path = '{}ref=login'.format(redirect_to_path)
                 try:
                 try:
                     return redirect(redirect_to_path)
                     return redirect(redirect_to_path)
                 except NoReverseMatch:
                 except NoReverseMatch:

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

@@ -1,6 +1,5 @@
 from django.shortcuts import get_object_or_404, redirect, render
 from django.shortcuts import get_object_or_404, redirect, render
 from django.urls import reverse
 from django.urls import reverse
-from django.utils import six
 from django.views import View
 from django.views import View
 
 
 from misago.core.utils import format_plaintext_for_html
 from misago.core.utils import format_plaintext_for_html
@@ -24,7 +23,7 @@ class ListView(View):
         for page in sections:
         for page in sections:
             page['reversed_link'] = reverse(page['link'])
             page['reversed_link'] = reverse(page['link'])
             request.frontend_context['USERS_LISTS'].append({
             request.frontend_context['USERS_LISTS'].append({
-                'name': six.text_type(page['name']),
+                'name': str(page['name']),
                 'component': page['component'],
                 'component': page['component'],
             })
             })
 
 

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

@@ -1,7 +1,6 @@
 from django.contrib.auth import update_session_auth_hash
 from django.contrib.auth import update_session_auth_hash
 from django.db import IntegrityError
 from django.db import IntegrityError
 from django.shortcuts import render
 from django.shortcuts import render
-from django.utils import six
 from django.utils.translation import ugettext as _
 from django.utils.translation import ugettext as _
 
 
 from misago.users.credentialchange import read_new_credential
 from misago.users.credentialchange import read_new_credential
@@ -14,7 +13,7 @@ def index(request, *args, **kwargs):
     user_options = []
     user_options = []
     for section in usercp.get_sections(request):
     for section in usercp.get_sections(request):
         user_options.append({
         user_options.append({
-            'name': six.text_type(section['name']),
+            'name': str(section['name']),
             'icon': section['icon'],
             'icon': section['icon'],
             'component': section['component'],
             'component': section['component'],
         })
         })

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

@@ -1,7 +1,6 @@
 from django.contrib.auth import get_user_model
 from django.contrib.auth import get_user_model
 from django.http import Http404
 from django.http import Http404
 from django.shortcuts import get_object_or_404, redirect, render
 from django.shortcuts import get_object_or_404, redirect, render
-from django.utils import six
 from django.views import View
 from django.views import View
 
 
 from misago.acl import add_acl
 from misago.acl import add_acl
@@ -61,7 +60,7 @@ class ProfileView(View):
         request.frontend_context['PROFILE_PAGES'] = []
         request.frontend_context['PROFILE_PAGES'] = []
         for section in sections:
         for section in sections:
             request.frontend_context['PROFILE_PAGES'].append({
             request.frontend_context['PROFILE_PAGES'].append({
-                'name': six.text_type(section['name']),
+                'name': str(section['name']),
                 'icon': section['icon'],
                 'icon': section['icon'],
                 'meta': section.get('metadata'),
                 'meta': section.get('metadata'),
                 'component': section['component'],
                 'component': section['component'],

+ 0 - 1
requirements.in

@@ -10,7 +10,6 @@ Faker<0.9
 html5lib==0.999999999
 html5lib==0.999999999
 markdown<2.7
 markdown<2.7
 misago-social-auth-app-django
 misago-social-auth-app-django
-path.py<10.4
 pillow<4.2
 pillow<4.2
 psycopg2-binary<2.8
 psycopg2-binary<2.8
 pytz
 pytz

+ 0 - 1
requirements.txt

@@ -22,7 +22,6 @@ markdown==2.6.11
 misago-social-auth-app-django==2.1.0
 misago-social-auth-app-django==2.1.0
 oauthlib==2.1.0           # via requests-oauthlib, social-auth-core
 oauthlib==2.1.0           # via requests-oauthlib, social-auth-core
 olefile==0.45.1           # via pillow
 olefile==0.45.1           # via pillow
-path.py==10.3.1
 pillow==4.1.1
 pillow==4.1.1
 psycopg2-binary==2.7.5
 psycopg2-binary==2.7.5
 pyjwt==1.6.4              # via social-auth-core
 pyjwt==1.6.4              # via social-auth-core

+ 0 - 3
setup.py

@@ -54,9 +54,6 @@ setup(
         'License :: OSI Approved :: GNU General Public License v2 (GPLv2)',
         'License :: OSI Approved :: GNU General Public License v2 (GPLv2)',
         'Operating System :: OS Independent',
         'Operating System :: OS Independent',
         'Programming Language :: Python',
         'Programming Language :: Python',
-        'Programming Language :: Python :: 2.7',
-        'Programming Language :: Python :: 3.4',
-        'Programming Language :: Python :: 3.5',
         'Programming Language :: Python :: 3.6',
         'Programming Language :: Python :: 3.6',
         'Topic :: Internet :: WWW/HTTP',
         'Topic :: Internet :: WWW/HTTP',
         'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
         'Topic :: Internet :: WWW/HTTP :: Dynamic Content',