diff --git a/modoboa/admin/management/commands/subcommands/_export.py b/modoboa/admin/management/commands/subcommands/_export.py index 02fb35800..91c40d929 100644 --- a/modoboa/admin/management/commands/subcommands/_export.py +++ b/modoboa/admin/management/commands/subcommands/_export.py @@ -1,20 +1,14 @@ """Django management command to export admin objects.""" -import sys +import csv from django.core.management.base import BaseCommand -from django.utils import six from django.utils.encoding import smart_text from modoboa.core.extensions import exts_pool from modoboa.core.models import User from .... import models -if six.PY2: - from backports import csv -else: - import csv - class ExportCommand(BaseCommand): """Command class.""" diff --git a/modoboa/admin/management/commands/subcommands/_import.py b/modoboa/admin/management/commands/subcommands/_import.py index eafec60d0..d4b7054db 100644 --- a/modoboa/admin/management/commands/subcommands/_import.py +++ b/modoboa/admin/management/commands/subcommands/_import.py @@ -1,5 +1,6 @@ """Django management command to import admin objects.""" +import csv import io import os @@ -8,7 +9,6 @@ from chardet.universaldetector import UniversalDetector from django.core.management.base import BaseCommand, CommandError from django.db import transaction -from django.utils import six from django.utils.translation import ugettext as _ from modoboa.core import models as core_models @@ -16,11 +16,6 @@ from modoboa.core.extensions import exts_pool from modoboa.lib.exceptions import Conflict from .... import signals -if six.PY2: - from backports import csv -else: - import csv - class ImportCommand(BaseCommand): """Command class.""" @@ -31,7 +26,7 @@ class ImportCommand(BaseCommand): def add_arguments(self, parser): """Add extra arguments to command.""" parser.add_argument( - "--sepchar", type=six.text_type, default=";", + "--sepchar", type=str, default=";", help="Separator used in file.") parser.add_argument( "--continue-if-exists", action="store_true", @@ -41,7 +36,7 @@ class ImportCommand(BaseCommand): "--crypt-password", action="store_true", default=False, help="Encrypt provided passwords.") parser.add_argument( - "files", type=six.text_type, nargs="+", help="CSV files to import.") + "files", type=str, nargs="+", help="CSV files to import.") def _import(self, filename, options, encoding="utf-8"): """Import domains or identities.""" @@ -117,10 +112,7 @@ class ImportCommand(BaseCommand): encoding=detector.result["encoding"] ) except UnicodeDecodeError as exc: - six.raise_from( - CommandError( - _("Unable to decode CSV file using %(encoding)s " - "encoding") % detector.result - ), - exc - ) + raise CommandError( + _("Unable to decode CSV file using %(encoding)s " + "encoding") % detector.result + ) from exc diff --git a/modoboa/admin/tests/test_export.py b/modoboa/admin/tests/test_export.py index fa2c70303..830dd6eab 100644 --- a/modoboa/admin/tests/test_export.py +++ b/modoboa/admin/tests/test_export.py @@ -1,8 +1,7 @@ """Export related test cases.""" import sys - -from six.moves import StringIO +from io import StringIO from django.core.management import call_command from django.urls import reverse diff --git a/modoboa/admin/views/export.py b/modoboa/admin/views/export.py index 62f91880e..5d1764a87 100644 --- a/modoboa/admin/views/export.py +++ b/modoboa/admin/views/export.py @@ -1,5 +1,8 @@ """Export related views.""" +import csv +from io import StringIO + from rfc6266 import build_header from django.contrib.auth.decorators import ( @@ -8,17 +11,11 @@ from django.contrib.auth.decorators import ( from django.http import HttpResponse from django.shortcuts import render from django.urls import reverse -from django.utils import six from django.utils.translation import ugettext as _ from ..forms import ExportDomainsForm, ExportIdentitiesForm from ..lib import get_domains, get_identities -if six.PY2: - from backports import csv -else: - import csv - def _export(content, filename): """Export a csv file's content @@ -50,7 +47,7 @@ def export_identities(request): if request.method == "POST": form = ExportIdentitiesForm(request.POST) form.is_valid() - fp = six.StringIO() + fp = StringIO() csvwriter = csv.writer(fp, delimiter=form.cleaned_data["sepchar"]) identities = get_identities( request.user, **request.session["identities_filters"]) @@ -78,7 +75,7 @@ def export_domains(request): if request.method == "POST": form = ExportDomainsForm(request.POST) form.is_valid() - fp = six.StringIO() + fp = StringIO() csvwriter = csv.writer(fp, delimiter=form.cleaned_data["sepchar"]) for dom in get_domains(request.user, **request.session["domains_filters"]): diff --git a/modoboa/admin/views/import_.py b/modoboa/admin/views/import_.py index 1a2bd2711..b40e4ce28 100644 --- a/modoboa/admin/views/import_.py +++ b/modoboa/admin/views/import_.py @@ -1,5 +1,6 @@ """Import related views.""" +import csv import io from reversion import revisions as reversion @@ -10,7 +11,6 @@ from django.contrib.auth.decorators import ( from django.db import transaction from django.shortcuts import render from django.urls import reverse -from django.utils import six from django.utils.encoding import smart_text from django.utils.translation import ugettext as _ @@ -18,11 +18,6 @@ from modoboa.lib.exceptions import Conflict, ModoboaException from .. import signals from ..forms import ImportDataForm, ImportIdentitiesForm -if six.PY2: - from backports import csv -else: - import csv - @reversion.create_revision() def importdata(request, formclass=ImportDataForm): diff --git a/modoboa/core/commands/deploy.py b/modoboa/core/commands/deploy.py index 2ff0cbfef..d750616a6 100644 --- a/modoboa/core/commands/deploy.py +++ b/modoboa/core/commands/deploy.py @@ -14,7 +14,6 @@ import django from django.core import management from django.template import Context, Template from django.utils.encoding import smart_str -from django.utils.six.moves import input from modoboa.core.commands import Command from modoboa.lib.api_client import ModoAPIClient diff --git a/modoboa/core/management/commands/clean_inactive_accounts.py b/modoboa/core/management/commands/clean_inactive_accounts.py index dd151e882..eb5e58778 100644 --- a/modoboa/core/management/commands/clean_inactive_accounts.py +++ b/modoboa/core/management/commands/clean_inactive_accounts.py @@ -1,7 +1,6 @@ """Inactive accounts cleanup tool.""" from dateutil.relativedelta import relativedelta -from six.moves import input from django.core.management.base import BaseCommand from django.utils import timezone diff --git a/modoboa/core/password_hashers/base.py b/modoboa/core/password_hashers/base.py index d91bd9be6..689ffb82a 100644 --- a/modoboa/core/password_hashers/base.py +++ b/modoboa/core/password_hashers/base.py @@ -13,8 +13,6 @@ from random import Random from django.utils.crypto import constant_time_compare from django.utils.encoding import force_bytes, force_text -from six import with_metaclass - class MetaHasher(type): """ @@ -32,7 +30,7 @@ class MetaHasher(type): return cls.name if not cls._weak else "{} (weak)".format(cls.name) -class PasswordHasher(with_metaclass(MetaHasher, object)): +class PasswordHasher(metaclass=MetaHasher): """ Base class of all hashers. """ diff --git a/modoboa/core/tests/test_core.py b/modoboa/core/tests/test_core.py index 82a3e42a8..14a04e50a 100644 --- a/modoboa/core/tests/test_core.py +++ b/modoboa/core/tests/test_core.py @@ -1,8 +1,9 @@ """Tests for core application.""" +from io import StringIO + import httmock from dateutil.relativedelta import relativedelta -from six import StringIO from django.core import management from django.test import TestCase diff --git a/modoboa/ldapsync/tests.py b/modoboa/ldapsync/tests.py index 2e975a09b..e5a633307 100644 --- a/modoboa/ldapsync/tests.py +++ b/modoboa/ldapsync/tests.py @@ -2,7 +2,6 @@ from unittest import skipIf -from django.utils import six from django.utils.encoding import force_bytes, force_str from modoboa.core import factories as core_factories @@ -36,8 +35,7 @@ class LDAPSyncTestCase(ModoTestCase): def reset_ldap_directory(self): try: - tmp_dn = force_bytes(self.dn) if six.PY2 else self.dn - self.conn.delete_s(tmp_dn) + self.conn.delete_s(self.dn) except ldap.NO_SUCH_OBJECT: pass diff --git a/modoboa/lib/email_utils.py b/modoboa/lib/email_utils.py index b9d09ae8f..8e0d3c544 100644 --- a/modoboa/lib/email_utils.py +++ b/modoboa/lib/email_utils.py @@ -12,7 +12,6 @@ from lxml.html import defs from lxml.html.clean import Cleaner from django.template.loader import render_to_string -from django.utils import six from django.utils.encoding import smart_str, smart_text from django.utils.html import conditional_escape, escape from django.utils.translation import ugettext as _ @@ -130,7 +129,7 @@ class Email(object): continue if encoding: value = smart_text(value, encoding=encoding) - elif isinstance(value, six.binary_type): + elif isinstance(value, bytes): # SMTPUTF8 fallback (most of the time) # Address contains non ASCII chars but is not RFC2047 # encoded... @@ -138,12 +137,9 @@ class Email(object): try: value = value.decode(encoding["encoding"], "replace") except (TypeError, UnicodeDecodeError) as exc: - six.raise_from( - InternalError( - _("unable to determine encoding of string") - ), - exc - ) + raise InternalError( + _("unable to determine encoding of string") + ) from exc decoded_values.append(value) return "".join(decoded_values) return "" @@ -289,8 +285,8 @@ class Email(object): def split_address(address): """Split an e-mail address into local part and domain.""" - assert isinstance(address, six.text_type),\ - "address should be of type %s" % six.text_type.__name__ + assert isinstance(address, str),\ + "address should be of type str" if "@" not in address: local_part = address domain = None @@ -301,10 +297,10 @@ def split_address(address): def split_local_part(local_part, delimiter=None): """Split a local part into local part and extension.""" - assert isinstance(local_part, six.text_type),\ - "local_part should be of type %s" % six.text_type.__name__ - assert isinstance(delimiter, six.text_type) or delimiter is None,\ - "delimiter should be of type %s" % six.text_type.__name__ + assert isinstance(local_part, str),\ + "local_part should be of type str" + assert isinstance(delimiter, str) or delimiter is None,\ + "delimiter should be of type str" extension = None if local_part.lower() in ["mailer-daemon", "double-bounce"]: # never split these special addresses @@ -346,8 +342,8 @@ def split_mailbox(mailbox, return_extension=False): def decode(value_bytes, encoding, append_to_error=""): """Try to decode the given string.""" - assert isinstance(value_bytes, six.binary_type),\ - "value_bytes should be of type %s" % six.binary_type.__name__ + assert isinstance(value_bytes, bytes),\ + "value_bytes should be of type bytes" if len(value_bytes) == 0: # short circuit for empty strings return "" @@ -360,13 +356,10 @@ def decode(value_bytes, encoding, append_to_error=""): encoding["encoding"], "replace" ) except (TypeError, UnicodeDecodeError) as exc: - six.raise_from( - InternalError( - _("unable to determine encoding of string") + - append_to_error - ), - exc - ) + raise InternalError( + _("unable to determine encoding of string") + + append_to_error + ) from exc return value @@ -378,7 +371,7 @@ def prepare_addresses(addresses, usage="header"): :return: a string or a list depending on usage value """ result = [] - if isinstance(addresses, six.text_type): + if isinstance(addresses, str): addresses = [addresses] for name, address in getaddresses(addresses): if not address: diff --git a/modoboa/lib/ldap_utils.py b/modoboa/lib/ldap_utils.py index 32d3272dd..c981c25ab 100644 --- a/modoboa/lib/ldap_utils.py +++ b/modoboa/lib/ldap_utils.py @@ -34,7 +34,6 @@ Extracted from `this blog import ldap from django.conf import settings -from django.utils import six from django.utils.encoding import force_bytes, force_str from django.utils.translation import ugettext as _ @@ -66,7 +65,7 @@ class LDAPAuthBackend(object): def _get_conn(self, dn, password): """Get a connection from the server.""" - conn = ldap.initialize(self.server_uri, bytes_mode=six.PY2) + conn = ldap.initialize(self.server_uri) conn.set_option(ldap.OPT_X_TLS_DEMAND, True) conn.set_option(ldap.OPT_DEBUG_LEVEL, 255) conn.simple_bind_s(