[damned-lies] Removed Python 2 support



commit 7d78e0383f1c2efd608d307dee12d23708e69996
Author: Claude Paroz <claude 2xlibre net>
Date:   Wed Feb 22 15:45:35 2017 +0100

    Removed Python 2 support

 README                                             |   23 +++--------
 common/context_processors.py                       |    1 -
 common/fields.py                                   |   14 +++----
 common/models.py                                   |    1 -
 common/templatetags/list_to_columns.py             |    2 -
 common/tests.py                                    |    6 +--
 common/utils.py                                    |    2 -
 common/views.py                                    |    3 -
 damnedlies/settings.py                             |    3 +-
 damnedlies/urls.py                                 |    1 -
 feeds/urls.py                                      |    1 -
 languages/admin.py                                 |    1 -
 languages/management/commands/load-plurals.py      |    1 -
 languages/migrations/0001_initial.py               |    3 -
 languages/models.py                                |    3 -
 languages/tests.py                                 |    2 -
 languages/urls.py                                  |    1 -
 languages/views.py                                 |    2 -
 people/admin.py                                    |    1 -
 people/forms.py                                    |   39 +++++++++----------
 people/migrations/0001_initial.py                  |    3 -
 .../0002_set_use_gravatar_verbose_name.py          |    3 -
 people/models.py                                   |    8 +---
 people/templatetags/people.py                      |    2 -
 people/tests.py                                    |    4 --
 people/urls.py                                     |    1 -
 people/views.py                                    |   14 ++----
 stats/admin.py                                     |   12 ++---
 stats/doap.py                                      |    3 +-
 stats/forms.py                                     |    7 +--
 stats/management/commands/archive-release.py       |    1 -
 stats/management/commands/compile-trans.py         |    1 -
 stats/management/commands/copy-release.py          |    2 -
 stats/management/commands/migrate-to-git.py        |    1 -
 stats/management/commands/run-maintenance.py       |    1 -
 stats/management/commands/update-from-doap.py      |    1 -
 stats/management/commands/update-stats.py          |    1 -
 stats/management/commands/update-trans.py          |    1 -
 stats/migrations/0001_initial.py                   |    3 -
 stats/migrations/0002_add_category_name.py         |    3 -
 stats/migrations/0003_migrate_category_names.py    |    3 -
 stats/migrations/0004_remove_old_cat_name.py       |    3 -
 stats/migrations/0005_update_module_name_field.py  |    3 -
 stats/migrations/0006_add_domain_branch_from_to.py |    3 -
 stats/migrations/0007_extend_bugs_base.py          |    3 -
 stats/migrations/0008_domain_extra_its_dirs.py     |    3 -
 stats/models.py                                    |   40 +++++++-------------
 stats/potdiff.py                                   |    2 -
 stats/signals.py                                   |    1 -
 stats/templatetags/stats_extras.py                 |   19 ++++-----
 stats/tests/fixture_factory.py                     |    1 -
 stats/tests/tests.py                               |   28 ++++++-------
 stats/utils.py                                     |   11 +----
 stats/views.py                                     |   20 ++++-----
 teams/admin.py                                     |    3 +-
 teams/forms.py                                     |    9 +---
 teams/migrations/0001_initial.py                   |    3 -
 teams/models.py                                    |   10 +---
 teams/tests.py                                     |    9 +---
 teams/urls.py                                      |    1 -
 teams/views.py                                     |    2 -
 vertimus/admin.py                                  |    1 -
 vertimus/feeds.py                                  |    3 -
 vertimus/forms.py                                  |    7 +--
 vertimus/migrations/0001_initial.py                |    3 -
 vertimus/migrations/0002_state_person_blank.py     |    3 -
 vertimus/migrations/0003_add_action_sent_to_ml.py  |    3 -
 vertimus/models.py                                 |   18 +++------
 vertimus/tests/tests.py                            |   18 ++++-----
 vertimus/urls.py                                   |    1 -
 vertimus/views.py                                  |    3 -
 71 files changed, 123 insertions(+), 296 deletions(-)
---
diff --git a/README b/README
index db7ae7f..daa6cbd 100644
--- a/README
+++ b/README
@@ -10,7 +10,7 @@ Requirements
 
 1 - Django > 1.8.X
 
-2 - Python 2.7 (minimal)
+2 - Python 3.4 (minimal)
     pillow (python-pillow) for hackergotchi checks.
     Markdown (python-markdown) for Team presentation markup rendering.
 
@@ -52,13 +52,10 @@ Installation
     console:
     EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
 
-2 - Run the following command to create the tables in the database:
-    ./manage.py syncdb
-
-3 - Run the following command to execute the migrations:
+2 - Run the following command to execute the database migrations:
     ./manage.py migrate
 
-4 - In production, you will have to run the following command:
+3 - In production, you will have to run the following command:
     ./manage.py collectstatic
 
     Then configure your Web server to statically serve this directory. For
@@ -66,24 +63,19 @@ Installation
 
     Alias /static /absolute/path/to/djamnedlies/static
 
-5 - (optional) If you want to populate the database with sample data, run:
+4 - (optional) If you want to populate the database with sample data, run:
     ./manage.py loaddata sample_data
 
-6 - You should now be able to launch the server to check if all is running well:
+5 - You should now be able to launch the server to check if all is running well:
     ./manage.py runserver
 
-7 - Configure Sites in admin interface ('View on site' link, site address in
+6 - Configure Sites in admin interface ('View on site' link, site address in
     sent mail).
 
 Running tests
 =============
 
-To run the tests, you need to install the mock package (in addition to the
-runtime dependencies):
-
-    pip install mock
-
-And then, run the tests with this command:
+To execute the tests, run this command:
 
     python manage.py test [optional path to run partial tests]
 
@@ -149,7 +141,6 @@ DATABASES = {
         'HOST' = '/var/run/mysqld/mysqld.sock',
         'OPTIONS' = {
             'read_default_file': '/etc/mysql/my.cnf',
-            'init_command': 'SET storage_engine=INNODB'
         }
     }
 }
diff --git a/common/context_processors.py b/common/context_processors.py
index 1f2b481..1c962b7 100644
--- a/common/context_processors.py
+++ b/common/context_processors.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 from django.conf import settings
 
 def utils(request):
diff --git a/common/fields.py b/common/fields.py
index b1fe741..9023deb 100644
--- a/common/fields.py
+++ b/common/fields.py
@@ -1,4 +1,3 @@
-#-*- coding: utf-8 -*-
 # From: https://djangosnippets.org/snippets/1979/
 import json
 
@@ -6,7 +5,6 @@ from django import forms
 from django.core import exceptions
 from django.core.serializers.json import DjangoJSONEncoder
 from django.db import models
-from django.utils import six
 
 
 class DictionaryField(models.Field):
@@ -20,7 +18,7 @@ class DictionaryField(models.Field):
             return None
         elif value == "":
             return {}
-        elif isinstance(value, six.string_types):
+        elif isinstance(value, str):
             try:
                 return dict(json.loads(value))
             except (ValueError, TypeError):
@@ -34,7 +32,7 @@ class DictionaryField(models.Field):
     def get_prep_value(self, value):
         if not value:
             return ""
-        if isinstance(value, six.string_types):
+        if isinstance(value, str):
             return value
         else:
             return json.dumps(value)
@@ -44,13 +42,13 @@ class DictionaryField(models.Field):
         return self.get_prep_value(value)
 
     def clean(self, value, model_instance):
-        value = super(DictionaryField, self).clean(value, model_instance)
+        value = super().clean(value, model_instance)
         return self.get_prep_value(value)
 
     def formfield(self, **kwargs):
         defaults = {'widget': forms.Textarea}
         defaults.update(kwargs)
-        return super(DictionaryField, self).formfield(**defaults)
+        return super().formfield(**defaults)
 
 
 # From https://djangosnippets.org/snippets/1478/
@@ -66,7 +64,7 @@ class JSONField(models.TextField):
             return None
 
         try:
-            if isinstance(value, six.string_types):
+            if isinstance(value, str):
                 return json.loads(value)
         except ValueError:
             pass
@@ -82,7 +80,7 @@ class JSONField(models.TextField):
         if isinstance(value, (dict, list)):
             value = json.dumps(value, cls=DjangoJSONEncoder)
 
-        return super(JSONField, self).get_db_prep_save(value, connection=connection)
+        return super().get_db_prep_save(value, connection=connection)
 
     def value_to_string(self, obj):
         return json.dumps(self._get_val_from_obj(obj), cls=DjangoJSONEncoder)
diff --git a/common/models.py b/common/models.py
index 8c3750f..864a180 100644
--- a/common/models.py
+++ b/common/models.py
@@ -1,2 +1 @@
-# -*- coding: utf-8 -*-
 # This empty file is necessary to run the tests
diff --git a/common/templatetags/list_to_columns.py b/common/templatetags/list_to_columns.py
index 2ba5e40..c9ebc2c 100644
--- a/common/templatetags/list_to_columns.py
+++ b/common/templatetags/list_to_columns.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-#
 # Based on https://www.djangosnippets.org/snippets/889/
 # Copyright (c) 2008 Stéphane Raimbault <stephane raimbault gmail com>
 #
diff --git a/common/tests.py b/common/tests.py
index cf5723e..20453cd 100644
--- a/common/tests.py
+++ b/common/tests.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-#
 # Copyright (c) 2010 Adorilson Bezerra <adorilson gmail com>
 # Copyright (c) 2010 Claude Paroz <claude 2xlibre net>
 #
@@ -18,14 +16,14 @@
 # You should have received a copy of the GNU General Public License
 # along with this program; if not, see <http://www.gnu.org/licenses/>.
 
-from datetime import datetime, timedelta
 import operator
+from datetime import datetime, timedelta
 from unittest import skipUnless
+from unittest.mock import MagicMock, patch
 
 from django.core.urlresolvers import reverse
 from django.test import TestCase
 from django.utils import translation
-from mock import MagicMock, patch
 
 from people.models import Person
 from teams.models import Team, Role
diff --git a/common/utils.py b/common/utils.py
index 2a5ebc2..0010919 100644
--- a/common/utils.py
+++ b/common/utils.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-#
 # Copyright (c) 2006-2007 Danilo Segan <danilo gnome org>.
 # Copyright (c) 2008 Claude Paroz <claude 2xlibre net>.
 #
diff --git a/common/views.py b/common/views.py
index 79cb13e..ffe8e61 100644
--- a/common/views.py
+++ b/common/views.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-#
 # Copyright (c) 2008-2011 Claude Paroz <claude 2xlibre net>.
 #
 # This file is part of Damned Lies.
@@ -16,7 +14,6 @@
 #
 # You should have received a copy of the GNU General Public License
 # along with this program; if not, see <http://www.gnu.org/licenses/>.
-from __future__ import unicode_literals
 
 from django.conf import settings
 from django.contrib.auth import login, authenticate, logout
diff --git a/damnedlies/settings.py b/damnedlies/settings.py
index 1b5c2aa..6edf659 100644
--- a/damnedlies/settings.py
+++ b/damnedlies/settings.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 # Django settings for djamnedlies project.
 import os
 from django.conf import global_settings
@@ -78,7 +77,7 @@ POTDIR = os.path.join(SCRATCHDIR, "POT")
 
 # The regex is used to determine if the module is in the standard VCS of the project
 VCS_HOME_REGEX = "git\.gnome\.org"
-VCS_HOME_WARNING = gettext_noop(u"This module is not part of the GNOME Git repository. Please check the 
module’s web page to see where to send translations.")
+VCS_HOME_WARNING = gettext_noop("This module is not part of the GNOME Git repository. Please check the 
module’s web page to see where to send translations.")
 
 # By default, Django stores files locally, using the MEDIA_ROOT and MEDIA_URL settings
 UPLOAD_DIR = 'upload'
diff --git a/damnedlies/urls.py b/damnedlies/urls.py
index e35f67d..552760a 100644
--- a/damnedlies/urls.py
+++ b/damnedlies/urls.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 from django.conf.urls import include, url
 from django.conf import settings
 from django.contrib import admin
diff --git a/feeds/urls.py b/feeds/urls.py
index 72cd282..107c67a 100644
--- a/feeds/urls.py
+++ b/feeds/urls.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 from django.conf.urls import url
 from vertimus.feeds import LatestActionsByLanguage, LatestActionsByTeam
 
diff --git a/languages/admin.py b/languages/admin.py
index 0b20da6..1214e94 100644
--- a/languages/admin.py
+++ b/languages/admin.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 from django.contrib import admin
 from languages.models import Language
 
diff --git a/languages/management/commands/load-plurals.py b/languages/management/commands/load-plurals.py
index 835f66c..62cc91a 100644
--- a/languages/management/commands/load-plurals.py
+++ b/languages/management/commands/load-plurals.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 from django.core.management.base import BaseCommand
 from languages.models import Language
 
diff --git a/languages/migrations/0001_initial.py b/languages/migrations/0001_initial.py
index 81bca76..2f780d5 100644
--- a/languages/migrations/0001_initial.py
+++ b/languages/migrations/0001_initial.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import models, migrations
 
 
diff --git a/languages/models.py b/languages/models.py
index bccb165..443b6fa 100644
--- a/languages/models.py
+++ b/languages/models.py
@@ -1,13 +1,10 @@
-# -*- coding: utf-8 -*-
 from django.core.urlresolvers import NoReverseMatch
 from django.db import models
 from django.db.models import Q
-from django.utils.encoding import python_2_unicode_compatible
 from django.utils.translation import ugettext as _
 from teams.models import Team, FakeTeam
 
 
-@python_2_unicode_compatible
 class Language(models.Model):
     name = models.CharField(max_length=50, unique=True)
     locale = models.CharField(max_length=15, unique=True)
diff --git a/languages/tests.py b/languages/tests.py
index d1c7960..d5906ea 100644
--- a/languages/tests.py
+++ b/languages/tests.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-#
 # Copyright (c) 2011 Claude Paroz <claude 2xlibre net>
 #
 # This file is part of Damned Lies.
diff --git a/languages/urls.py b/languages/urls.py
index fd7a68d..8ee85ae 100644
--- a/languages/urls.py
+++ b/languages/urls.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 from django.conf.urls import url
 
 from languages import views
diff --git a/languages/views.py b/languages/views.py
index a46a89e..c5ca58e 100644
--- a/languages/views.py
+++ b/languages/views.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-#
 # Copyright (c) 2008 Stéphane Raimbault <stephane raimbault gmail com>
 # Copyright (c) 2008-2011 Claude Paroz <claude 2xlibre net>
 #
diff --git a/people/admin.py b/people/admin.py
index 801863b..659182a 100644
--- a/people/admin.py
+++ b/people/admin.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 from django.contrib import admin
 from django.contrib.auth.admin import UserAdmin
 from django.contrib.auth.models import User
diff --git a/people/forms.py b/people/forms.py
index e040550..f928ad9 100644
--- a/people/forms.py
+++ b/people/forms.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 import hashlib, random
 
 from django import forms
@@ -15,16 +14,16 @@ from people.models import Person
 class RegistrationForm(forms.Form):
     """ Form for user registration """
     username = forms.RegexField(max_length=30, regex=r'^\w+$',
-                               label=ugettext_lazy(u'Choose a username:'),
-                               help_text=ugettext_lazy(u'May contain only letters, numbers, underscores or 
hyphens'))
-    email = forms.EmailField(label=ugettext_lazy(u'Email:'))
-    openid_url = forms.URLField(label=ugettext_lazy(u'OpenID:'),
+                               label=ugettext_lazy('Choose a username:'),
+                               help_text=ugettext_lazy('May contain only letters, numbers, underscores or 
hyphens'))
+    email = forms.EmailField(label=ugettext_lazy('Email:'))
+    openid_url = forms.URLField(label=ugettext_lazy('OpenID:'),
                                 required=False)
     password1 = forms.CharField(widget=forms.PasswordInput(render_value=False),
-                                label=ugettext_lazy(u'Password:'), required=False, min_length=7,
-                                help_text=ugettext_lazy(u'At least 7 characters'))
+                                label=ugettext_lazy('Password:'), required=False, min_length=7,
+                                help_text=ugettext_lazy('At least 7 characters'))
     password2 = forms.CharField(widget=forms.PasswordInput(render_value=False),
-                                label=ugettext_lazy(u'Confirm password:'), required=False)
+                                label=ugettext_lazy('Confirm password:'), required=False)
 
     def clean_username(self):
         """  Validate the username (correctness and uniqueness)"""
@@ -32,7 +31,7 @@ class RegistrationForm(forms.Form):
             user = Person.objects.get(username__iexact=self.cleaned_data['username'])
         except Person.DoesNotExist:
             return self.cleaned_data['username']
-        raise forms.ValidationError(_(u'This username is already taken. Please choose another.'))
+        raise forms.ValidationError(_('This username is already taken. Please choose another.'))
 
     def clean_openid_url(self):
         """ Check openid url is not already linked to any existing user """
@@ -42,7 +41,7 @@ class RegistrationForm(forms.Form):
                 oid = UserOpenID.objects.get(claimed_id=self.cleaned_data['openid_url'])
             except UserOpenID.DoesNotExist:
                 return self.cleaned_data['openid_url']
-            raise forms.ValidationError(_(u'This OpenID URL is already taken by a registered user'))
+            raise forms.ValidationError(_('This OpenID URL is already taken by a registered user'))
         else:
             return self.cleaned_data['openid_url']
 
@@ -52,10 +51,10 @@ class RegistrationForm(forms.Form):
         password2 = cleaned_data.get('password2')
         openid_url = cleaned_data.get('openid_url')
         if not password1 and not openid_url:
-            raise forms.ValidationError(_(u'You must either provide an OpenID or a password'))
+            raise forms.ValidationError(_('You must either provide an OpenID or a password'))
 
         if password1 and password1 != password2:
-            raise forms.ValidationError(_(u'The passwords do not match'))
+            raise forms.ValidationError(_('The passwords do not match'))
         return cleaned_data
 
     def save(self, request):
@@ -78,10 +77,10 @@ class RegistrationForm(forms.Form):
         new_user.save()
         # Send activation email
         current_site = get_current_site(request)
-        subject = settings.EMAIL_SUBJECT_PREFIX + _(u'Account activation')
-        message = _(u"This is a confirmation that your registration on %s succeeded. To activate your 
account, please click on the link below or copy and paste it in a browser.") % current_site.name
+        subject = settings.EMAIL_SUBJECT_PREFIX + _('Account activation')
+        message = _("This is a confirmation that your registration on %s succeeded. To activate your 
account, please click on the link below or copy and paste it in a browser.") % current_site.name
         message += "\n\nhttps://%s%s\n\n"; % (current_site.domain, str(reverse("register_activation", 
kwargs={'key': activation_key})))
-        message += _(u"Administrators of %s" % current_site.name)
+        message += _("Administrators of %s" % current_site.name)
 
         send_mail(subject, message, settings.DEFAULT_FROM_EMAIL,
                   (email,), fail_silently=False)
@@ -100,13 +99,13 @@ class DetailForm(forms.ModelForm):
             return
         size = get_image_size(url)
         if size[0]>100 or size[1]>100:
-            raise forms.ValidationError(_(u"Image too high or too wide (%(width)d×%(height)d, maximum is 
100×100 pixels)") % {
+            raise forms.ValidationError(_("Image too high or too wide (%(width)d×%(height)d, maximum is 
100×100 pixels)") % {
                 'width': size[0], 'height': size[1]})
         return url
 
 class TeamJoinForm(forms.Form):
     def __init__(self, *args, **kwargs):
-        super(TeamJoinForm, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
         # FIXME: exclude team to which user is already member
         self.fields['teams'] = forms.ModelChoiceField(queryset=Team.objects.all())
 
@@ -120,7 +119,7 @@ def get_image_size(url):
     try:
         file = urllib.urlopen(url)
     except (IOError, UnicodeError, InvalidURL, EOFError):
-        raise forms.ValidationError(_(u"The URL you provided is not valid"))
+        raise forms.ValidationError(_("The URL you provided is not valid"))
     size = None
     p = ImageFile.Parser()
     try:
@@ -133,8 +132,8 @@ def get_image_size(url):
                 size = p.image.size
                 break
     except Exception as e:
-        raise forms.ValidationError(u"Sorry, an error occurred while trying to get image size (%s)" % str(e))
+        raise forms.ValidationError("Sorry, an error occurred while trying to get image size (%s)" % str(e))
     file.close()
     if not size:
-        raise forms.ValidationError(_(u"The URL you provided seems not to correspond to a valid image"))
+        raise forms.ValidationError(_("The URL you provided seems not to correspond to a valid image"))
     return size
diff --git a/people/migrations/0001_initial.py b/people/migrations/0001_initial.py
index 50818c9..4e748c7 100644
--- a/people/migrations/0001_initial.py
+++ b/people/migrations/0001_initial.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import models, migrations
 from django.conf import settings
 import django.contrib.auth.models
diff --git a/people/migrations/0002_set_use_gravatar_verbose_name.py 
b/people/migrations/0002_set_use_gravatar_verbose_name.py
index bec0f5f..7737f78 100644
--- a/people/migrations/0002_set_use_gravatar_verbose_name.py
+++ b/people/migrations/0002_set_use_gravatar_verbose_name.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 import django.contrib.auth.models
 
diff --git a/people/models.py b/people/models.py
index 03c3097..7476c79 100644
--- a/people/models.py
+++ b/people/models.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-#
 # Copyright (c) 2008 Claude Paroz <claude 2xlibre net>.
 # Copyright (c) 2008 Stéphane Raimbault <stephane raimbault gmail com>.
 #
@@ -18,14 +16,11 @@
 # You should have received a copy of the GNU General Public License
 # along with this program; if not, see <http://www.gnu.org/licenses/>.
 
-from __future__ import unicode_literals
-
 import datetime
 import re
 from django.core.exceptions import ObjectDoesNotExist
 from django.core.urlresolvers import reverse
 from django.db import models
-from django.utils.encoding import python_2_unicode_compatible
 from django.utils.html import escape
 from django.utils.safestring import mark_safe
 from django.utils.translation import ugettext_lazy as _
@@ -40,7 +35,6 @@ def obfuscate_email(email):
     return ''
 
 
-@python_2_unicode_compatible
 class Person(User):
     """ The User class of D-L. """
 
@@ -112,7 +106,7 @@ class Person(User):
         if not self.password or self.password == "!":
             self.password = None
             self.set_unusable_password()
-        super(User, self).save(*args, **kwargs)
+        super().save(*args, **kwargs)
 
     def activate(self):
         self.activation_key = None
diff --git a/people/templatetags/people.py b/people/templatetags/people.py
index da3a5b8..09ce262 100644
--- a/people/templatetags/people.py
+++ b/people/templatetags/people.py
@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
 import hashlib
 
 from django import template
diff --git a/people/tests.py b/people/tests.py
index 8e778f4..63eafa9 100644
--- a/people/tests.py
+++ b/people/tests.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-#
 # Copyright (c) 2010-2011 Claude Paroz <claude 2xlibre net>
 #
 # This file is part of Damned Lies.
@@ -17,8 +15,6 @@
 # You should have received a copy of the GNU General Public License
 # along with this program; if not, see <http://www.gnu.org/licenses/>.
 
-from __future__ import unicode_literals
-
 import datetime
 from unittest import skipUnless
 
diff --git a/people/urls.py b/people/urls.py
index fce4773..da0c1dd 100644
--- a/people/urls.py
+++ b/people/urls.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 from django.conf.urls import url
 from django.contrib.auth.decorators import login_required
 
diff --git a/people/views.py b/people/views.py
index f732f5d..9a6c878 100644
--- a/people/views.py
+++ b/people/views.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-#
 # Copyright (c) 2008 Claude Paroz <claude 2xlibre net>.
 #
 # This file is part of Damned Lies.
@@ -16,8 +14,6 @@
 #
 # You should have received a copy of the GNU General Public License
 # along with this program; if not, see <http://www.gnu.org/licenses/>.
-from __future__ import unicode_literals
-
 from operator import itemgetter
 
 from django.conf import settings
@@ -44,7 +40,7 @@ class PeopleListView(ListView):
     model = Person
 
     def get_context_data(self, **kwargs):
-        context = super(PeopleListView, self).get_context_data(**kwargs)
+        context = super().get_context_data(**kwargs)
         context['pageSection'] = "teams"
         return context
 
@@ -54,7 +50,7 @@ class PersonDetailView(DetailView):
     context_object_name = 'person'
 
     def get_context_data(self, **kwargs):
-        context = super(PersonDetailView, self).get_context_data(**kwargs)
+        context = super().get_context_data(**kwargs)
         states = State.objects.filter(action__person=self.object).distinct()
         all_languages = [(lg[0], LANG_INFO.get(lg[0], {'name_local': lg[1]})['name_local']) for lg in 
settings.LANGUAGES]
         all_languages = lc_sorted(all_languages, key=itemgetter(1))
@@ -74,17 +70,17 @@ class PersonEditView(UpdateView):
 
     def get_object(self):
         self.kwargs['slug'] = self.request.user.username
-        return super(PersonEditView, self).get_object()
+        return super().get_object()
 
     def get_context_data(self, **kwargs):
-        context = super(PersonEditView, self).get_context_data(**kwargs)
+        context = super().get_context_data(**kwargs)
         context['pageSection'] = "teams"
         context['on_own_page'] = self.object.username == self.request.user.username,
         return context
 
     def form_invalid(self, form):
         messages.error(self.request, _("Sorry, the form is not valid."))
-        return super(PersonEditView, self).form_invalid(form)
+        return super().form_invalid(form)
 
 @login_required
 def person_team_join(request):
diff --git a/stats/admin.py b/stats/admin.py
index d90ad1e..a024580 100644
--- a/stats/admin.py
+++ b/stats/admin.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-#
 # Copyright (c) 2008-2011 Claude Paroz <claude 2xlibre net>.
 #
 # This file is part of Damned Lies.
@@ -49,7 +47,7 @@ class DomainInline(admin.StackedInline):
     def get_formset(self, request, obj=None, **kwargs):
         # Hack! Store parent obj for formfield_for_foreignkey
         self.parent_obj = obj
-        return super(DomainInline, self).get_formset(request, obj, **kwargs)
+        return super().get_formset(request, obj, **kwargs)
 
     def formfield_for_dbfield(self, db_field, **kwargs):
         if db_field.name == 'description':
@@ -58,12 +56,12 @@ class DomainInline(admin.StackedInline):
             kwargs['widget'] = forms.TextInput(attrs={'size':'20'})
         elif db_field.name in ('red_filter', 'extra_its_dirs'):
             kwargs['widget'] = forms.Textarea(attrs={'rows':'1', 'cols':'40'})
-        return super(DomainInline, self).formfield_for_dbfield(db_field, **kwargs)
+        return super().formfield_for_dbfield(db_field, **kwargs)
 
     def formfield_for_foreignkey(self, db_field, request, **kwargs):
         if db_field.name in ('branch_from', 'branch_to') and hasattr(self, 'parent_obj') and self.parent_obj:
             kwargs['queryset'] = self.parent_obj.branch_set.all()
-        return super(DomainInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
+        return super().formfield_for_foreignkey(db_field, request, **kwargs)
 
 
 class ModuleForm(forms.ModelForm):
@@ -78,7 +76,7 @@ class ModuleForm(forms.ModelForm):
             # Delete checkout(s)
             for branch in old_module.get_branches(reverse=True):  # head branch last
                 branch.delete_checkout()
-        instance = super(ModuleForm, self).save(**kwargs)
+        instance = super().save(**kwargs)
         if must_renew_checkout:
             for branch in instance.get_branches():
                 # Force checkout and updating stats
@@ -102,7 +100,7 @@ class ModuleAdmin(admin.ModelAdmin):
     list_filter = ('archived',)
 
     def formfield_for_dbfield(self, db_field, **kwargs):
-        field = super(ModuleAdmin, self).formfield_for_dbfield(db_field, **kwargs)
+        field = super().formfield_for_dbfield(db_field, **kwargs)
         if db_field.name == 'description':
             field.widget.attrs['rows'] = '1'
         elif db_field.name == 'comment':
diff --git a/stats/doap.py b/stats/doap.py
index 57e73bf..8147c5d 100644
--- a/stats/doap.py
+++ b/stats/doap.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
 import os
 import re
+from urllib.parse import unquote
 from xml.etree.ElementTree import ParseError, parse
 
 from django.utils.encoding import force_text
-from django.utils.six.moves.urllib.parse import unquote
 
 from people.models import Person
 
diff --git a/stats/forms.py b/stats/forms.py
index 873fce2..2c00409 100644
--- a/stats/forms.py
+++ b/stats/forms.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 from django import forms
 from django.utils.translation import ugettext as _
 
@@ -8,13 +7,13 @@ from stats.models import Branch, Category, CategoryName, Release
 class ReleaseField(forms.ModelChoiceField):
     def __init__(self, *args, **kwargs):
         kwargs['required'] = False
-        super(ReleaseField, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
         if 'label' in kwargs:
             self.is_branch = True
 
 class ModuleBranchForm(forms.Form):
     def __init__(self, module, *args, **kwargs):
-        super(ModuleBranchForm, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
         self.module = module
         self.branch_fields = []
         default_cat_name = None
@@ -55,7 +54,7 @@ class ModuleBranchForm(forms.Form):
             yield (self[rel_field], self[cat_field])
 
     def clean(self):
-        cleaned_data = super(ModuleBranchForm, self).clean()
+        cleaned_data = super().clean()
         for field_name in cleaned_data.keys():
             if (field_name.endswith('_cat') and cleaned_data[field_name] is None
                     and cleaned_data[field_name[:-4]] is not None):
diff --git a/stats/management/commands/archive-release.py b/stats/management/commands/archive-release.py
index 8fd75cc..1d2a30d 100644
--- a/stats/management/commands/archive-release.py
+++ b/stats/management/commands/archive-release.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 import csv
 import StringIO
 from django.core.management.base import BaseCommand, CommandError
diff --git a/stats/management/commands/compile-trans.py b/stats/management/commands/compile-trans.py
index 85e4d28..6539bdb 100644
--- a/stats/management/commands/compile-trans.py
+++ b/stats/management/commands/compile-trans.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 import os
 import shutil
 
diff --git a/stats/management/commands/copy-release.py b/stats/management/commands/copy-release.py
index 5e8c220..e1724a6 100644
--- a/stats/management/commands/copy-release.py
+++ b/stats/management/commands/copy-release.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-#
 # Copyright (c) 2008 Claude Paroz <claude 2xlibre net>.
 #
 # This file is part of Damned Lies.
diff --git a/stats/management/commands/migrate-to-git.py b/stats/management/commands/migrate-to-git.py
index 09326c1..797b95b 100644
--- a/stats/management/commands/migrate-to-git.py
+++ b/stats/management/commands/migrate-to-git.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 import shutil
 from django.core.management.base import BaseCommand
 from stats.models import Module, Branch
diff --git a/stats/management/commands/run-maintenance.py b/stats/management/commands/run-maintenance.py
index eee498f..994a47b 100644
--- a/stats/management/commands/run-maintenance.py
+++ b/stats/management/commands/run-maintenance.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 from django.core.management.base import BaseCommand
 
 from people.models import Person
diff --git a/stats/management/commands/update-from-doap.py b/stats/management/commands/update-from-doap.py
index 407e978..8f3e298 100644
--- a/stats/management/commands/update-from-doap.py
+++ b/stats/management/commands/update-from-doap.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 import sys
 from django.core.management.base import BaseCommand
 
diff --git a/stats/management/commands/update-stats.py b/stats/management/commands/update-stats.py
index 7ee0165..c882387 100644
--- a/stats/management/commands/update-stats.py
+++ b/stats/management/commands/update-stats.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 import traceback
 
 from django.core.management.base import BaseCommand, CommandError
diff --git a/stats/management/commands/update-trans.py b/stats/management/commands/update-trans.py
index 0c68ef0..a72cca2 100644
--- a/stats/management/commands/update-trans.py
+++ b/stats/management/commands/update-trans.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 import os
 import re
 import shutil
diff --git a/stats/migrations/0001_initial.py b/stats/migrations/0001_initial.py
index 2e39c69..b1fc213 100644
--- a/stats/migrations/0001_initial.py
+++ b/stats/migrations/0001_initial.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import models, migrations
 import django.db.models.deletion
 import common.fields
diff --git a/stats/migrations/0002_add_category_name.py b/stats/migrations/0002_add_category_name.py
index d1ef0a2..2f8bed4 100644
--- a/stats/migrations/0002_add_category_name.py
+++ b/stats/migrations/0002_add_category_name.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import models, migrations
 import django.db.models.deletion
 
diff --git a/stats/migrations/0003_migrate_category_names.py b/stats/migrations/0003_migrate_category_names.py
index 1cac78e..2d105aa 100644
--- a/stats/migrations/0003_migrate_category_names.py
+++ b/stats/migrations/0003_migrate_category_names.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import models, migrations
 
 CATEGORY_CHOICES = (
diff --git a/stats/migrations/0004_remove_old_cat_name.py b/stats/migrations/0004_remove_old_cat_name.py
index 54afdcd..ec8e296 100644
--- a/stats/migrations/0004_remove_old_cat_name.py
+++ b/stats/migrations/0004_remove_old_cat_name.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import models, migrations
 import django.db.models.deletion
 
diff --git a/stats/migrations/0005_update_module_name_field.py 
b/stats/migrations/0005_update_module_name_field.py
index f08b31b..19234fa 100644
--- a/stats/migrations/0005_update_module_name_field.py
+++ b/stats/migrations/0005_update_module_name_field.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import models, migrations
 import re
 import django.core.validators
diff --git a/stats/migrations/0006_add_domain_branch_from_to.py 
b/stats/migrations/0006_add_domain_branch_from_to.py
index 288d88b..232b86c 100644
--- a/stats/migrations/0006_add_domain_branch_from_to.py
+++ b/stats/migrations/0006_add_domain_branch_from_to.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import models, migrations
 import django.db.models.deletion
 
diff --git a/stats/migrations/0007_extend_bugs_base.py b/stats/migrations/0007_extend_bugs_base.py
index 52a9b12..b233d82 100644
--- a/stats/migrations/0007_extend_bugs_base.py
+++ b/stats/migrations/0007_extend_bugs_base.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/stats/migrations/0008_domain_extra_its_dirs.py b/stats/migrations/0008_domain_extra_its_dirs.py
index 635ce1b..a04bf14 100644
--- a/stats/migrations/0008_domain_extra_its_dirs.py
+++ b/stats/migrations/0008_domain_extra_its_dirs.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/stats/models.py b/stats/models.py
index 1e092af..d2f54a7 100644
--- a/stats/models.py
+++ b/stats/models.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-#
 # Copyright (c) 2008-2014 Claude Paroz <claude 2xlibre net>.
 # Copyright (c) 2008 Stephane Raimbault <stephane raimbault gmail com>.
 #
@@ -18,8 +16,6 @@
 # You should have received a copy of the GNU General Public License
 # along with this program; if not, see <http://www.gnu.org/licenses/>.
 
-from __future__ import division
-
 from collections import Counter, OrderedDict
 import fnmatch
 import logging
@@ -29,15 +25,15 @@ import threading
 from datetime import datetime
 from functools import total_ordering
 from time import sleep
+from urllib import request
+from urllib.error import URLError
 
 from django.conf import settings
 from django.core.exceptions import ValidationError
 from django.core.urlresolvers import reverse
 from django.core.validators import RegexValidator
-from django.utils.encoding import force_text, python_2_unicode_compatible
+from django.utils.encoding import force_text
 from django.utils.functional import cached_property
-from django.utils.six.moves.urllib.error import URLError
-from django.utils.six.moves.urllib import request
 from django.utils.translation import ungettext, ugettext as _, ugettext_noop
 from django.utils import dateformat
 from django.db import models
@@ -72,7 +68,6 @@ BRANCH_HEAD_NAMES = (
     'master'
 )
 
-@python_2_unicode_compatible
 class Module(models.Model):
     name = models.CharField(max_length=50, unique=True, validators=[validate_slug])
     homepage    = models.URLField(null=True, blank=True,
@@ -194,7 +189,6 @@ class ModuleLock(object):
         return os.path.exists(self.dirpath)
 
 
-@python_2_unicode_compatible
 @total_ordering
 class Branch(models.Model):
     """ Branch of a module """
@@ -216,7 +210,7 @@ class Branch(models.Model):
         unique_together = ('name', 'module')
 
     def __init__(self, *args, **kwargs):
-        super(Branch, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
         self._ui_stats = None
         self._doc_stats = None
 
@@ -232,7 +226,7 @@ class Branch(models.Model):
                 raise ValidationError("Branch not valid: error while checking out the branch (%s)." % 
sys.exc_info()[1])
 
     def save(self, update_statistics=True, **kwargs):
-        super(Branch, self).save(**kwargs)
+        super().save(**kwargs)
         if update_statistics and not self.module.archived:
             # The update command is launched asynchronously in a separate thread
             upd_thread = threading.Thread(target=self.update_stats, kwargs={'force':True})
@@ -240,7 +234,7 @@ class Branch(models.Model):
 
     def delete(self):
         self.delete_checkout()
-        super(Branch, self).delete()
+        super().delete()
 
     def delete_checkout(self):
         # Remove the repo checkout
@@ -288,7 +282,7 @@ class Branch(models.Model):
 
     def warnings(self):
         if self.releases.count() < 1:
-            return _(u"This branch is not linked from any release")
+            return _("This branch is not linked from any release")
         return ""
 
     def file_changed(self, rel_path):
@@ -738,7 +732,6 @@ DOMAIN_TYPE_CHOICES = (
     ('doc', 'Documentation')
 )
 
-@python_2_unicode_compatible
 class Domain(models.Model):
     module = models.ForeignKey(Module)
     name = models.CharField(max_length=50)
@@ -918,7 +911,6 @@ RELEASE_STATUS_CHOICES = (
     ('unofficial', 'Unofficial'),
     ('xternal', 'External')
 )
-@python_2_unicode_compatible
 class Release(models.Model):
     name = models.SlugField(max_length=20)
     description = models.CharField(max_length=50)
@@ -1177,7 +1169,6 @@ class Release(models.Model):
         return last_modif_date, lang_files
 
 
-@python_2_unicode_compatible
 class CategoryName(models.Model):
     name = models.CharField(max_length=30, unique=True)
 
@@ -1188,7 +1179,6 @@ class CategoryName(models.Model):
         return self.name
 
 
-@python_2_unicode_compatible
 class Category(models.Model):
     release = models.ForeignKey(Release)
     branch = models.ForeignKey(Branch)
@@ -1203,7 +1193,6 @@ class Category(models.Model):
         return "%s (%s, %s)" % (self.name, self.release, self.branch)
 
 
-@python_2_unicode_compatible
 class PoFile(models.Model):
     # File type fields of Django may not be flexible enough for our use case
     path         = models.CharField(max_length=255, blank=True, null=True)
@@ -1296,7 +1285,6 @@ class PoFile(models.Model):
         self.save()
 
 
-@python_2_unicode_compatible
 class Statistics(models.Model):
     branch = models.ForeignKey(Branch)
     domain = models.ForeignKey(Domain)
@@ -1312,7 +1300,7 @@ class Statistics(models.Model):
         unique_together = ('branch', 'domain', 'language')
 
     def __init__(self, *args, **kwargs):
-        super(Statistics, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
         self.partial_po = False # True if part of a multiple po module
         self.info_list = []
 
@@ -1420,18 +1408,18 @@ class Statistics(models.Model):
         pot_words_size = self.full_po.pot_size(words=True)
         fig_count = self.full_po.fig_count()
         """ Return stat table header: 'POT file (n messages) - updated on ??-??-???? tz' """
-        msg_text = ungettext(u"%(count)s message", "%(count)s messages", pot_size) % {'count': pot_size}
-        upd_text = _(u"updated on %(date)s") % {
+        msg_text = ungettext("%(count)s message", "%(count)s messages", pot_size) % {'count': pot_size}
+        upd_text = _("updated on %(date)s") % {
                         # Date format syntax is similar to PHP http://www.php.net/date
                         'date': dateformat.format(self.full_po.updated, _("Y-m-d g:i a O"))
                         }
-        words_text = ungettext(u"%(count)s word", "%(count)s words", pot_words_size) % {'count': 
pot_words_size}
+        words_text = ungettext("%(count)s word", "%(count)s words", pot_words_size) % {'count': 
pot_words_size}
         if fig_count:
-            fig_text = ungettext(u"%(count)s figure", "%(count)s figures", fig_count) % {'count': fig_count}
-            text = _(u"POT file (%(messages)s — %(words)s, %(figures)s) — %(updated)s") % \
+            fig_text = ungettext("%(count)s figure", "%(count)s figures", fig_count) % {'count': fig_count}
+            text = _("POT file (%(messages)s — %(words)s, %(figures)s) — %(updated)s") % \
                               {'messages': msg_text, 'figures': fig_text, 'updated': upd_text, 'words': 
words_text }
         else:
-            text = _(u"POT file (%(messages)s — %(words)s) — %(updated)s") % \
+            text = _("POT file (%(messages)s — %(words)s) — %(updated)s") % \
                               {'messages': msg_text, 'updated': upd_text, 'words': words_text }
         return text
 
diff --git a/stats/potdiff.py b/stats/potdiff.py
index 7fa0d1e..1e6b221 100644
--- a/stats/potdiff.py
+++ b/stats/potdiff.py
@@ -1,5 +1,4 @@
 #!/usr/bin/env python
-# -*- coding: utf-8 -*-
 #
 # Copyright (c) 2006-2007 Danilo Segan <danilo gnome org>.
 #
@@ -19,7 +18,6 @@
 # along with this program; if not, see <http://www.gnu.org/licenses/>.
 
 # Output differences between two POT files
-from __future__ import unicode_literals
 
 USE_DIFFLIB = 0
 
diff --git a/stats/signals.py b/stats/signals.py
index 59fca64..d59285f 100644
--- a/stats/signals.py
+++ b/stats/signals.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 import django.dispatch
 
 pot_has_changed = django.dispatch.Signal(providing_args=["potfile", "branch", "domain"])
diff --git a/stats/templatetags/stats_extras.py b/stats/templatetags/stats_extras.py
index fc63985..2867b79 100644
--- a/stats/templatetags/stats_extras.py
+++ b/stats/templatetags/stats_extras.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 import re
 
 from django import template
@@ -218,10 +215,10 @@ def is_video(fig):
 
 @register.filter
 def as_tr(field):
-    help_html = u''
+    help_html = ''
     if field.help_text:
-        help_html = u'<br><span class="help">%s</span>' % field.help_text
-    help_link = u''
+        help_html = '<br><span class="help">%s</span>' % field.help_text
+    help_link = ''
     # This is a custom attribute possibly set in forms.py
     if hasattr(field.field, 'help_link'):
         help_link = '<span class="help_link">'
@@ -229,12 +226,12 @@ def as_tr(field):
         help_link += '<img src="%simg/help.png" alt="help icon">'
         help_link += '</a></span>'
         help_link = help_link % (field.field.help_link, settings.STATIC_URL)
-    errors_html = u''
+    errors_html = ''
     if field.errors:
-        errors = u''.join(["<li>%s</li>" % err for err in field.errors])
-        errors_html = u'<ul class="errorlist">%s</ul>' % errors
+        errors = ''.join(["<li>%s</li>" % err for err in field.errors])
+        errors_html = '<ul class="errorlist">%s</ul>' % errors
 
-    return mark_safe(u'<tr class="tr_%s"><th>%s</th><td>%s%s%s%s</td></tr>' % (
+    return mark_safe('<tr class="tr_%s"><th>%s</th><td>%s%s%s%s</td></tr>' % (
         field.name, field.label_tag(), errors_html, field.as_widget(), help_link, help_html)
     )
 
@@ -254,7 +251,7 @@ def bugzilla_linkify(text):
     reference is found and transforms it to a link to that bug"""
     # Should not catch encoded entities (&#39;)
     bug_id = re.compile('(?<!&)(?P<id>#[0-9]+)')
-    repl = u'<a rel="nofollow" href="https://bugzilla.gnome.org/show_bug.cgi?id=\g<id>">\g<id></a>'
+    repl = '<a rel="nofollow" href="https://bugzilla.gnome.org/show_bug.cgi?id=\g<id>">\g<id></a>'
 
     result = bug_id.sub(repl, text)
     result = result.replace('.cgi?id=#', '.cgi?id=')
diff --git a/stats/tests/fixture_factory.py b/stats/tests/fixture_factory.py
index 1d2ac86..e636a52 100644
--- a/stats/tests/fixture_factory.py
+++ b/stats/tests/fixture_factory.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 from tempfile import NamedTemporaryFile
 
 from django.core.management import call_command
diff --git a/stats/tests/tests.py b/stats/tests/tests.py
index 0139951..3b8cc37 100644
--- a/stats/tests/tests.py
+++ b/stats/tests/tests.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-#
 # Copyright (c) 2008-2014 Claude Paroz <claude 2xlibre net>
 #
 # This file is part of Damned Lies.
@@ -102,7 +100,7 @@ class ModuleTestCase(TestCase):
         ('gnome-doc-utils', 'xml2po'),
     )
     def __init__(self, name):
-        super(ModuleTestCase, self).__init__(name)
+        super().__init__(name)
         for package, prog in self.SYS_DEPENDENCIES:
             if not utils.check_program_presence(prog):
                 raise Exception("You are missing a required system package needed by Damned Lies (%s)" % 
package)
@@ -179,9 +177,9 @@ class ModuleTestCase(TestCase):
         fr_doc_stat = Statistics.objects.get(branch=self.branch, domain__name='help', language__locale='fr')
         self.assertEqual(fr_doc_stat.translated(), 41)
         self.assertEqual(len(pot_doc_stat.full_po.figures), len(fr_doc_stat.full_po.figures))
-        self.assertEqual(fr_po_stat.po_url(), u"/POT/gnome-hello.master/gnome-hello.master.fr.po")
-        self.assertEqual(fr_po_stat.pot_url(), u"/POT/gnome-hello.master/gnome-hello.master.pot")
-        self.assertEqual(fr_doc_stat.po_url(), u"/POT/gnome-hello.master/docs/gnome-hello-help.master.fr.po")
+        self.assertEqual(fr_po_stat.po_url(), "/POT/gnome-hello.master/gnome-hello.master.fr.po")
+        self.assertEqual(fr_po_stat.pot_url(), "/POT/gnome-hello.master/gnome-hello.master.pot")
+        self.assertEqual(fr_doc_stat.po_url(), "/POT/gnome-hello.master/docs/gnome-hello-help.master.fr.po")
         with self.assertRaises(Statistics.DoesNotExist):
             Statistics.objects.get(pk=ghost_stat.pk)
 
@@ -191,7 +189,7 @@ class ModuleTestCase(TestCase):
         self.assertEqual(stats['help'][-1].language.locale, 'xxx')  # Fake stat last
 
         if utils.has_toolkit:
-            self.assertEqual(fr_po_stat.po_url(reduced=True), 
u"/POT/gnome-hello.master/gnome-hello.master.fr.reduced.po")
+            self.assertEqual(fr_po_stat.po_url(reduced=True), 
"/POT/gnome-hello.master/gnome-hello.master.fr.reduced.po")
             self.assertEqual(fr_po_stat.translated(scope='part'), 44)
 
     @test_scratchdir
@@ -625,11 +623,11 @@ class OtherTests(TestCase):
 
     def test_bugzilla_linkify(self):
         from stats.templatetags.stats_extras import bugzilla_linkify
-        self.assertEqual(bugzilla_linkify(u"Sample normal text should not be altered."),
-            u"Sample normal text should not be altered.")
-        self.assertEqual(bugzilla_linkify(u"Bugzilla bug numbers like #669240 should be linkified"),
-            u'Bugzilla bug numbers like <a rel="nofollow" 
href="https://bugzilla.gnome.org/show_bug.cgi?id=669240";>#669240</a> should be linkified')
-        self.assertEqual(bugzilla_linkify(u"#669240 start or finish #669240"),
-            u'<a rel="nofollow" href="https://bugzilla.gnome.org/show_bug.cgi?id=669240";>#669240</a> start 
or finish <a rel="nofollow" href="https://bugzilla.gnome.org/show_bug.cgi?id=669240";>#669240</a>')
-        self.assertEqual(bugzilla_linkify(u"Test with encoded entities (rock&#39;n roll) should not."),
-            u"Test with encoded entities (rock&#39;n roll) should not.")
+        self.assertEqual(bugzilla_linkify("Sample normal text should not be altered."),
+            "Sample normal text should not be altered.")
+        self.assertEqual(bugzilla_linkify("Bugzilla bug numbers like #669240 should be linkified"),
+            'Bugzilla bug numbers like <a rel="nofollow" 
href="https://bugzilla.gnome.org/show_bug.cgi?id=669240";>#669240</a> should be linkified')
+        self.assertEqual(bugzilla_linkify("#669240 start or finish #669240"),
+            '<a rel="nofollow" href="https://bugzilla.gnome.org/show_bug.cgi?id=669240";>#669240</a> start or 
finish <a rel="nofollow" href="https://bugzilla.gnome.org/show_bug.cgi?id=669240";>#669240</a>')
+        self.assertEqual(bugzilla_linkify("Test with encoded entities (rock&#39;n roll) should not."),
+            "Test with encoded entities (rock&#39;n roll) should not.")
diff --git a/stats/utils.py b/stats/utils.py
index e0875a5..c433c8c 100644
--- a/stats/utils.py
+++ b/stats/utils.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-#
 # Copyright (c) 2006-2007 Danilo Segan <danilo gnome org>.
 # Copyright (c) 2008-2010 Claude Paroz <claude 2xlibre net>.
 #
@@ -17,8 +15,6 @@
 #
 # You should have received a copy of the GNU General Public License
 # along with this program; if not, see <http://www.gnu.org/licenses/>.
-from __future__ import unicode_literals
-
 import errno
 import hashlib
 import logging
@@ -41,7 +37,6 @@ from django.contrib.sites.models import Site
 from django.core.files.base import File
 from django.core.mail import send_mail
 from django.template.loader import get_template
-from django.utils import six
 from django.utils.encoding import force_bytes, force_text
 from django.utils.translation import ugettext_noop
 
@@ -137,7 +132,7 @@ def run_shell_command(cmd, input_data=None, raise_on_error=False, env=None, **po
     if env is not None:
         env = dict(os.environ, **env)
     shell = not isinstance(cmd, list)
-    if isinstance(cmd, six.text_type):
+    if isinstance(cmd, str):
         cmd = cmd.encode('utf-8')
     elif isinstance(cmd, list):
         cmd = [c.encode('utf-8') for c in cmd]
@@ -162,7 +157,7 @@ def check_program_presence(prog_name):
     return status == 0
 
 def po_grep(in_file, out_file, filter_):
-    if not has_toolkit or filter_ == u"-":
+    if not has_toolkit or filter_ == "-":
         return
     if not filter_:
         filter_loc, filter_str = "locations", "gschema.xml.in|schemas.in"
@@ -344,7 +339,7 @@ def po_file_stats(pofile, msgfmt_checks=True):
         }
     c_env = {"LC_ALL": "C", "LANG": "C", "LANGUAGE": "C"}
 
-    if isinstance(pofile, six.string_types):
+    if isinstance(pofile, str):
         # pofile is a filesystem path
         filename = os.path.basename(pofile)
         if not os.access(pofile, os.R_OK):
diff --git a/stats/views.py b/stats/views.py
index be1e1fe..fff21c4 100644
--- a/stats/views.py
+++ b/stats/views.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-#
 # Copyright (c) 2008-2013 Claude Paroz <claude 2xlibre net>.
 #
 # This file is part of Damned Lies.
@@ -226,7 +224,7 @@ def dynamic_po(request, module_name, domain, branch_name, filename):
     if not os.access(file_path, os.R_OK):
         raise Http404
 
-    dyn_content = u"""# %(lang)s translation for %(pack)s.
+    dyn_content = """# %(lang)s translation for %(pack)s.
 # Copyright (C) %(year)s %(pack)s's COPYRIGHT HOLDER
 # This file is distributed under the same license as the %(pack)s package.\n""" % {
         'lang': language.name,
@@ -235,13 +233,13 @@ def dynamic_po(request, module_name, domain, branch_name, filename):
     }
     if request.user.is_authenticated():
         person = Person.get_by_user(request.user)
-        dyn_content += u"# %(name)s <%(email)s>, %(year)s.\n#\n" % {
+        dyn_content += "# %(name)s <%(email)s>, %(year)s.\n#\n" % {
             'name' : person.name,
             'email': person.email,
             'year' : date.today().year,
         }
     else:
-        dyn_content += u"# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.\n#\n"
+        dyn_content += "# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.\n#\n"
 
     command = "msginit --locale=%s --no-translator --input=%s --output-file=-" % (locale, file_path)
     status, output, err = utils.run_shell_command(command, raise_on_error=True)
@@ -254,14 +252,14 @@ def dynamic_po(request, module_name, domain, branch_name, filename):
             continue
         # Transformations
         new_line = {
-            '"Project-Id-': u"\"Project-Id-Version: %s %s\\n\"" % (module_name, branch_name),
-            '"Last-Transl': u"\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"",
-            '"Language-Te': u"\"Language-Team: %s <%s>\\n\"" % (
+            '"Project-Id-': "\"Project-Id-Version: %s %s\\n\"" % (module_name, branch_name),
+            '"Last-Transl': "\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"",
+            '"Language-Te': "\"Language-Team: %s <%s>\\n\"" % (
                 language.name, language.team and language.team.mailing_list or "%s li org" % locale),
-            '"Content-Typ': u"\"Content-Type: text/plain; charset=UTF-8\\n\"",
-            '"Plural-Form': u"\"Plural-Forms: %s;\\n\"" % (language.plurals or "nplurals=INTEGER; 
plural=EXPRESSION"),
+            '"Content-Typ': "\"Content-Type: text/plain; charset=UTF-8\\n\"",
+            '"Plural-Form': "\"Plural-Forms: %s;\\n\"" % (language.plurals or "nplurals=INTEGER; 
plural=EXPRESSION"),
         }.get(line[:12], line)
-        if line != new_line and line[-3:] != u"\\n\"":
+        if line != new_line and line[-3:] != "\\n\"":
             # Skip the wrapped part of the replaced line
             skip_next_line = True
         dyn_content += new_line + "\n"
diff --git a/teams/admin.py b/teams/admin.py
index 482118f..00cddca 100644
--- a/teams/admin.py
+++ b/teams/admin.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 from django.contrib import admin
 from teams.models import Team, Role
 from languages.models import Language
@@ -15,7 +14,7 @@ class TeamAdmin(admin.ModelAdmin):
 
     def formfield_for_dbfield(self, db_field, **kwargs):
         # Reduced text area for aliases
-        field = super(TeamAdmin, self).formfield_for_dbfield(db_field, **kwargs)
+        field = super().formfield_for_dbfield(db_field, **kwargs)
         if db_field.name == 'description':
             field.widget.attrs['rows'] = '4'
 
diff --git a/teams/forms.py b/teams/forms.py
index 024948b..69d3204 100644
--- a/teams/forms.py
+++ b/teams/forms.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django import forms
 from django.conf import settings
 from django.contrib.sites.shortcuts import get_current_site
@@ -22,7 +19,7 @@ class EditTeamDetailsForm(forms.ModelForm):
         }
 
     def __init__(self, user, *args, **kwargs):
-        super(EditTeamDetailsForm, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
         self.user = user
         if is_site_admin(user):
             # Add coordinatorship dropdown
@@ -40,7 +37,7 @@ class EditTeamDetailsForm(forms.ModelForm):
             )
 
     def save(self, *args, **kwargs):
-        super(EditTeamDetailsForm, self).save(*args, **kwargs)
+        super().save(*args, **kwargs)
         if 'coordinatorship' in self.changed_data and is_site_admin(self.user):
             # Change coordinator
             try:
@@ -59,7 +56,7 @@ class EditTeamDetailsForm(forms.ModelForm):
 class EditMemberRoleForm(forms.Form):
 
     def __init__(self, roles, *args, **kwargs):
-        super(EditMemberRoleForm, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
         choices = list(filter(lambda x:x[0]!='coordinator', ROLE_CHOICES))
         choices.append(('inactivate', _("Mark as Inactive")))
         choices.append(('remove', _("Remove From Team")))
diff --git a/teams/migrations/0001_initial.py b/teams/migrations/0001_initial.py
index 1e59d6f..4d536c2 100644
--- a/teams/migrations/0001_initial.py
+++ b/teams/migrations/0001_initial.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import models, migrations
 
 
diff --git a/teams/models.py b/teams/models.py
index 008f148..0dd0be8 100644
--- a/teams/models.py
+++ b/teams/models.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-#
 # Copyright (c) 2008 Stéphane Raimbault <stephane raimbault gmail com>.
 # Copyright (c) 2009 Claude Paroz <claude 2xlibre net>
 #
@@ -24,7 +22,7 @@ from django.db import models
 from django.core import mail
 from django.core.urlresolvers import reverse
 from django.utils import translation
-from django.utils.encoding import force_text, python_2_unicode_compatible
+from django.utils.encoding import force_text
 from django.utils.translation import ugettext_lazy, ugettext as _
 from django.conf import settings
 from django.contrib.sites.models import Site
@@ -81,7 +79,6 @@ class TeamManager(models.Manager):
         return teams
 
 
-@python_2_unicode_compatible
 class Team(models.Model):
     """The lang_code is generally used for the name of the team."""
 
@@ -199,8 +196,8 @@ class Team(models.Model):
         if not recipients:
             return
         with translation.override(self.language_set.all()[0].locale):
-            message = u"%s\n--\n" % (message % messagekw,)
-            message += _(u"This is an automated message sent from %s.") % Site.objects.get_current()
+            message = "%s\n--\n" % (message % messagekw,)
+            message += _("This is an automated message sent from %s.") % Site.objects.get_current()
             mail.send_mail(
                 force_text(subject),
                 message,
@@ -243,7 +240,6 @@ ROLE_CHOICES = (
     ('coordinator', ugettext_lazy('Coordinator')),
 )
 
-@python_2_unicode_compatible
 class Role(models.Model):
     """
     This is the intermediary class between Person and Team to attribute roles to
diff --git a/teams/tests.py b/teams/tests.py
index 2b7674f..da10003 100644
--- a/teams/tests.py
+++ b/teams/tests.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from datetime import datetime, timedelta
 
 from django.core.urlresolvers import reverse
@@ -52,7 +49,7 @@ class TeamsAndRolesTests(TestCase):
 
 class TeamTest(TeamsAndRolesTests):
     def setUp(self):
-        super(TeamTest, self).setUp()
+        super().setUp()
 
     def test_get_members_by_role_exact(self):
         members = self.t.get_members_by_role_exact('committer')
@@ -208,7 +205,7 @@ class TeamTest(TeamsAndRolesTests):
 
 class JSONTeamsTest(TeamsAndRolesTests):
     def setUp(self):
-        super(JSONTeamsTest, self).setUp()
+        super().setUp()
         t3 = Team.objects.create(name='gl', description='Galician')
         coor1 = Person.objects.create(first_name='Marcos', last_name='Coordinator',
             email='marc imthebigboss fr', username='marcos', svn_account='thesvnaccount')
@@ -255,7 +252,7 @@ class JSONTeamsTest(TeamsAndRolesTests):
 class RoleTest(TeamsAndRolesTests):
 
     def setUp(self):
-        super(RoleTest, self).setUp()
+        super().setUp()
 
         self.pt.last_login = datetime.now()-timedelta(days=10) # active person
         self.pt.save()
diff --git a/teams/urls.py b/teams/urls.py
index e2e2b9a..dc13269 100644
--- a/teams/urls.py
+++ b/teams/urls.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 from django.conf.urls import url
 
 from teams import views
diff --git a/teams/views.py b/teams/views.py
index aa67bd7..17c335a 100644
--- a/teams/views.py
+++ b/teams/views.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-#
 # Copyright (c) 2008-2011 Claude Paroz <claude 2xlibre net>.
 # Copyright (c) 2008 Stéphane Raimbault <stephane raimbault gmail com>.
 #
diff --git a/vertimus/admin.py b/vertimus/admin.py
index 9c28a2d..91eb590 100644
--- a/vertimus/admin.py
+++ b/vertimus/admin.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 from django.contrib import admin
 
 from vertimus.models import State, Action
diff --git a/vertimus/feeds.py b/vertimus/feeds.py
index d9fcc0b..98520f1 100644
--- a/vertimus/feeds.py
+++ b/vertimus/feeds.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-#
 # Copyright (c) 2009 Stéphane Raimbault <stephane raimbault gmail com>
 #
 # This file is part of Damned Lies.
@@ -16,7 +14,6 @@
 #
 # You should have received a copy of the GNU General Public License
 # along with this program; if not, see <http://www.gnu.org/licenses/>.
-from __future__ import unicode_literals
 
 from itertools import islice
 from django.core import urlresolvers
diff --git a/vertimus/forms.py b/vertimus/forms.py
index 302a9f2..cb10178 100644
--- a/vertimus/forms.py
+++ b/vertimus/forms.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-#
 # Copyright (c) 2008 Stéphane Raimbault <stephane raimbault gmail com>
 # Copyright (c) 2008 Claude Paroz <claude 2xlibre net>.
 #
@@ -17,7 +15,6 @@
 #
 # You should have received a copy of the GNU General Public License
 # along with this program; if not, see <http://www.gnu.org/licenses/>.
-from __future__ import unicode_literals
 
 import os
 
@@ -35,7 +32,7 @@ class ActionWidget(forms.Select):
     def render_options(self, choices, selected_choices):
         if selected_choices == ['']:
             selected_choices = []
-        options = super(ActionWidget, self).render_options(choices, selected_choices)
+        options = super().render_options(choices, selected_choices)
         options = options.replace('<option value="">--------</option>',
                                   '<option disabled="disabled">--------</option>')
         return options
@@ -83,7 +80,7 @@ class ActionForm(forms.Form):
     send_to_ml = forms.BooleanField(label=_("Send message to the team mailing list"), required=False)
 
     def __init__(self, state, actions, has_mailing_list, *args, **kwargs):
-        super(ActionForm, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
         self.actions = actions
         self.fields['action'].choices = [
             (act.name, act.description) for act in actions
diff --git a/vertimus/migrations/0001_initial.py b/vertimus/migrations/0001_initial.py
index e0779b8..8432b5d 100644
--- a/vertimus/migrations/0001_initial.py
+++ b/vertimus/migrations/0001_initial.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import models, migrations
 import vertimus.models
 
diff --git a/vertimus/migrations/0002_state_person_blank.py b/vertimus/migrations/0002_state_person_blank.py
index 7ad3d27..844745e 100644
--- a/vertimus/migrations/0002_state_person_blank.py
+++ b/vertimus/migrations/0002_state_person_blank.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/vertimus/migrations/0003_add_action_sent_to_ml.py 
b/vertimus/migrations/0003_add_action_sent_to_ml.py
index 7676d44..e1bccf9 100644
--- a/vertimus/migrations/0003_add_action_sent_to_ml.py
+++ b/vertimus/migrations/0003_add_action_sent_to_ml.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/vertimus/models.py b/vertimus/models.py
index a190c45..88e2eb5 100644
--- a/vertimus/models.py
+++ b/vertimus/models.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-#
 # Copyright (c) 2008-2009 Stéphane Raimbault <stephane raimbault gmail com>
 # Copyright (c) 2011-2013 Claude Paroz <claude 2xlibre net>
 #
@@ -17,7 +15,6 @@
 #
 # You should have received a copy of the GNU General Public License
 # along with this program; if not, see <http://www.gnu.org/licenses/>.
-from __future__ import unicode_literals
 
 import os, sys
 import shutil
@@ -31,7 +28,6 @@ from django.db import models
 from django.db.models import Max
 from django.db.models.signals import post_save, pre_delete
 from django.dispatch import receiver
-from django.utils.encoding import python_2_unicode_compatible
 from django.utils.translation import override, ugettext, ugettext_noop, ugettext_lazy as _
 
 from stats.models import Branch, Domain, Statistics, PoFile
@@ -51,7 +47,6 @@ class SendMailFailed(Exception):
 # States
 #
 
-@python_2_unicode_compatible
 class State(models.Model):
     """State of a module translation"""
     branch = models.ForeignKey(Branch)
@@ -68,7 +63,7 @@ class State(models.Model):
         unique_together = ('branch', 'domain', 'language')
 
     def __init__(self, *args, **kwargs):
-        super(State, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
         if self.name == 'None' and getattr(self.__class__, 'name', 'None') != 'None':
             self.name = self.__class__.name
         self.__class__ = {
@@ -348,7 +343,6 @@ def generate_upload_filename(instance, filename):
     return "%s/%s" % (settings.UPLOAD_DIR, new_filename)
 
 
-@python_2_unicode_compatible
 class ActionAbstract(models.Model):
     """ Common model for Action and ActionArchived """
     state_db = models.ForeignKey(State)
@@ -434,7 +428,7 @@ class Action(ActionAbstract):
         verbose_name = 'action'
 
     def __init__(self, *args, **kwargs):
-        super(Action, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
         if self.name:
             self.__class__ = eval('Action' + self.name)
         else:
@@ -448,7 +442,7 @@ class Action(ActionAbstract):
     def save(self, *args, **kwargs):
         if not self.id and not self.created:
             self.created = datetime.today()
-        super(Action, self).save(*args, **kwargs)
+        super().save(*args, **kwargs)
 
     def update_state(self):
         if self.target_state is not None:
@@ -631,7 +625,7 @@ class ActionTC(Action):
         proxy = True
 
     def apply_on(self, state, form_data):
-        super(ActionTC, self).apply_on(state, form_data)
+        super().apply_on(state, form_data)
         # Send an email to all committers of the team
         committers = [c.email for c in state.language.team.get_committers()]
         self.send_mail_new_state(state, committers)
@@ -665,7 +659,7 @@ class ActionCI(Action):
             else:
                 msg += ugettext(" However, the synchronization with the master branch failed.")
 
-        super(ActionCI, self).apply_on(state, form_data)  # Mail sent in super
+        super().apply_on(state, form_data)  # Mail sent in super
         return msg
 
 class ActionRC(Action):
@@ -701,7 +695,7 @@ class ActionAA(Action):
         proxy = True
 
     def apply_on(self, state, form_data):
-        super(ActionAA, self).apply_on(state, form_data)
+        super().apply_on(state, form_data)
         all_actions = Action.objects.filter(state_db=state).order_by('id').all()
 
         sequence = None
diff --git a/vertimus/tests/tests.py b/vertimus/tests/tests.py
index 2413b17..8cdbce7 100644
--- a/vertimus/tests/tests.py
+++ b/vertimus/tests/tests.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-#
 # Copyright (c) 2008 Stéphane Raimbault <stephane raimbault gmail com>
 #
 # This file is part of Damned Lies.
@@ -43,7 +41,7 @@ MEDIA_ROOT = tempfile.mkdtemp()
 class VertimusTest(TeamsAndRolesTests):
 
     def setUp(self):
-        super(VertimusTest, self).setUp()
+        super().setUp()
 
         self.m = Module.objects.create(name='gedit', description='GNOME Editor',
             bugs_base="https://bugzilla.gnome.org/";,
@@ -246,7 +244,7 @@ class VertimusTest(TeamsAndRolesTests):
         self.assertTrue(action.sent_to_ml)
         self.assertEqual(len(mail.outbox), 1)
         self.assertEqual(mail.outbox[0].recipients(), [self.l.team.mailing_list])
-        self.assertIn(u"Hi again!", mail.outbox[0].body)
+        self.assertIn("Hi again!", mail.outbox[0].body)
 
     def test_action_rt(self):
         state = StateNone(branch=self.b, domain=self.d, language=self.l)
@@ -364,10 +362,10 @@ class VertimusTest(TeamsAndRolesTests):
         action = Action.new_by_name('WC', person=self.pt)
         action.apply_on(state, {'send_to_ml': action.send_mail_to_ml, 'comment': "Hi!"})
         # Adding users with incomplete profiles
-        pers_no_full_name = Person.objects.create(username=u'ûsername')
+        pers_no_full_name = Person.objects.create(username='ûsername')
         action = Action.new_by_name('WC', person=pers_no_full_name)
         action.apply_on(state, {'send_to_ml': action.send_mail_to_ml, 'comment': "Hello!"})
-        pers_no_email = Person.objects.create(username=u'noemail', first_name="Sven", last_name="Brkc")
+        pers_no_email = Person.objects.create(username='noemail', first_name="Sven", last_name="Brkc")
         action = Action.new_by_name('WC', person=pers_no_email)
         action.apply_on(state, {'send_to_ml': action.send_mail_to_ml, 'comment': "Hello!"})
 
@@ -385,7 +383,7 @@ class VertimusTest(TeamsAndRolesTests):
         self.assertEqual(form.fields['author'].initial, self.pr)
         rendered_form = form.as_p()
         self.assertIn(
-            u'<option value="%d" disabled>ûsername (full name missing)</option>' % pers_no_full_name.pk,
+            '<option value="%d" disabled>ûsername (full name missing)</option>' % pers_no_full_name.pk,
             rendered_form
         )
         self.assertIn(
@@ -447,7 +445,7 @@ class VertimusTest(TeamsAndRolesTests):
         self.assertEqual(len(mail.outbox), 1)
         self.assertEqual(mail.outbox[0].recipients(), [self.l.team.mailing_list])
         # Team is French (but translations may not be compiled/up-to-date)
-        self.assertTrue(u'Commité' in mail.outbox[0].body or "Committed" in mail.outbox[0].body)
+        self.assertTrue('Commité' in mail.outbox[0].body or "Committed" in mail.outbox[0].body)
 
         self.assertTrue(isinstance(state, StateNone))
         self.assertTrue(not os.access(file_path, os.F_OK), "%s not deleted" % file_path)
@@ -573,9 +571,9 @@ class VertimusTest(TeamsAndRolesTests):
 
         response = self.client.post(url, data={
             'action': 'WC',
-            'comment': u'Graçias', 'send_to_ml': '', 'author': str(self.pn.pk)
+            'comment': 'Graçias', 'send_to_ml': '', 'author': str(self.pn.pk)
         }, follow=True)
-        self.assertContains(response, u'Graçias')
+        self.assertContains(response, 'Graçias')
 
     def test_vertimus_view_on_going_activities(self):
         master = Branch(name='master', module=self.m)
diff --git a/vertimus/urls.py b/vertimus/urls.py
index aae24da..1d907e6 100644
--- a/vertimus/urls.py
+++ b/vertimus/urls.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 from django.conf.urls import url
 
 from vertimus import views
diff --git a/vertimus/views.py b/vertimus/views.py
index a04c9a6..c81ee33 100644
--- a/vertimus/views.py
+++ b/vertimus/views.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-#
 # Copyright (c) 2008 Stéphane Raimbault <stephane raimbault gmail com>
 # Copyright (c) 2011 Claude Paroz <claude 2xlibre net>
 #
@@ -17,7 +15,6 @@
 #
 # You should have received a copy of the GNU General Public License
 # along with this program; if not, see <http://www.gnu.org/licenses/>.
-from __future__ import unicode_literals
 
 import difflib
 import io


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]