From 2109feffd2f0e4e764bd85b8c6a467b4366d003a Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Wed, 10 May 2017 18:29:17 +0200 Subject: [PATCH 01/23] Initial python3 support. --- modoboa/admin/forms/account.py | 1 + modoboa/admin/forms/domain.py | 2 + modoboa/admin/handlers.py | 2 +- .../commands/handle_mailbox_operations.py | 4 +- .../management/commands/subcommands/_mx.py | 3 +- .../commands/subcommands/_repair.py | 2 + modoboa/admin/models/domain.py | 1 + modoboa/admin/templatetags/admin_tags.py | 2 + modoboa/admin/views/domain.py | 2 + modoboa/admin/views/export.py | 6 +-- modoboa/core/commands/__init__.py | 5 +- modoboa/core/commands/deploy.py | 31 ++++++------ modoboa/core/commands/help.py | 4 +- modoboa/core/context_processors.py | 2 + modoboa/core/extensions.py | 6 ++- modoboa/core/factories.py | 10 ++-- modoboa/core/forms.py | 4 +- modoboa/core/handlers.py | 5 +- modoboa/core/management/commands/cleanlogs.py | 4 +- .../commands/generate_postfix_maps.py | 5 +- .../management/commands/load_initial_data.py | 6 ++- modoboa/core/middleware.py | 2 + modoboa/core/models.py | 24 +++++----- modoboa/core/password_hashers/base.py | 7 ++- modoboa/core/templatetags/core_tags.py | 4 +- modoboa/core/tests/test_ldap.py | 2 + modoboa/core/views/admin.py | 2 + modoboa/lib/email_utils.py | 14 +++--- modoboa/lib/form_utils.py | 17 +++---- modoboa/lib/imap_utf7.py | 48 ++++++++++--------- modoboa/lib/ldap_utils.py | 3 ++ modoboa/lib/middleware.py | 9 ++-- modoboa/lib/paginator.py | 2 + modoboa/lib/templatetags/form_tags.py | 3 +- modoboa/lib/templatetags/lib_tags.py | 2 +- modoboa/lib/test_utils.py | 2 + modoboa/lib/tests.py | 1 + modoboa/lib/u2u_decode.py | 4 +- modoboa/lib/validators.py | 1 + modoboa/lib/web_utils.py | 6 ++- modoboa/limits/forms.py | 8 ++-- modoboa/limits/models.py | 6 ++- modoboa/limits/serializers.py | 2 +- modoboa/limits/tests/test_import.py | 5 +- modoboa/limits/utils.py | 4 +- modoboa/parameters/forms.py | 6 +-- modoboa/parameters/tools.py | 10 ++-- modoboa/relaydomains/forms.py | 4 +- modoboa/relaydomains/models.py | 6 ++- modoboa/relaydomains/postfix_maps.py | 2 + modoboa/relaydomains/tests.py | 3 +- modoboa/relaydomains/views.py | 2 + 52 files changed, 201 insertions(+), 117 deletions(-) diff --git a/modoboa/admin/forms/account.py b/modoboa/admin/forms/account.py index e34c5f537..1fa51628f 100644 --- a/modoboa/admin/forms/account.py +++ b/modoboa/admin/forms/account.py @@ -1,6 +1,7 @@ """Forms related to accounts management.""" from collections import OrderedDict +from functools import reduce from django import forms from django.core.urlresolvers import reverse diff --git a/modoboa/admin/forms/domain.py b/modoboa/admin/forms/domain.py index 9bcf1ce13..3dab2282a 100644 --- a/modoboa/admin/forms/domain.py +++ b/modoboa/admin/forms/domain.py @@ -1,5 +1,7 @@ """Forms related to domains management.""" +from functools import reduce + from django import forms from django.core.urlresolvers import reverse from django.http import QueryDict diff --git a/modoboa/admin/handlers.py b/modoboa/admin/handlers.py index 35ed3ee9b..afdb82ee8 100644 --- a/modoboa/admin/handlers.py +++ b/modoboa/admin/handlers.py @@ -174,7 +174,7 @@ def account_auto_created(sender, user, **kwargs): @receiver(core_signals.account_exported) def export_admin_domains(sender, user, **kwargs): """Export administered domains too.""" - result = [user.mailbox.quota] if hasattr(user, "mailbox") else [""] + result = [user.mailbox.quota] if hasattr(user, "mailbox") else [u""] if user.role != "DomainAdmins": return result return result + [ diff --git a/modoboa/admin/management/commands/handle_mailbox_operations.py b/modoboa/admin/management/commands/handle_mailbox_operations.py index 16eae65f4..1632a1a3a 100644 --- a/modoboa/admin/management/commands/handle_mailbox_operations.py +++ b/modoboa/admin/management/commands/handle_mailbox_operations.py @@ -1,5 +1,7 @@ """A management command to apply mailbox operations.""" +from __future__ import print_function + import logging import os import shutil @@ -79,7 +81,7 @@ class Command(BaseCommand): if not code: return False with open(path, 'w') as fp: - print >> fp, os.getpid() + print(os.getpid(), file=fp) return True def handle(self, *args, **options): diff --git a/modoboa/admin/management/commands/subcommands/_mx.py b/modoboa/admin/management/commands/subcommands/_mx.py index 38cac067f..4ee1e5ba9 100644 --- a/modoboa/admin/management/commands/subcommands/_mx.py +++ b/modoboa/admin/management/commands/subcommands/_mx.py @@ -1,6 +1,7 @@ """Management command to check defined domains.""" -from datetime import datetime +from __future__ import print_function + from datetime import timedelta import ipaddress diff --git a/modoboa/admin/management/commands/subcommands/_repair.py b/modoboa/admin/management/commands/subcommands/_repair.py index 29af55e65..27cc9705d 100644 --- a/modoboa/admin/management/commands/subcommands/_repair.py +++ b/modoboa/admin/management/commands/subcommands/_repair.py @@ -1,5 +1,7 @@ """Management command to check and fix known problems.""" +from __future__ import print_function + from django.conf import settings from django.core.management.base import BaseCommand diff --git a/modoboa/admin/models/domain.py b/modoboa/admin/models/domain.py index 16a59ab6d..c894b9e01 100644 --- a/modoboa/admin/models/domain.py +++ b/modoboa/admin/models/domain.py @@ -1,6 +1,7 @@ """Models related to domains management.""" import datetime +from functools import reduce from django.db import models from django.db.models.manager import Manager diff --git a/modoboa/admin/templatetags/admin_tags.py b/modoboa/admin/templatetags/admin_tags.py index 38948c6a7..af301a446 100644 --- a/modoboa/admin/templatetags/admin_tags.py +++ b/modoboa/admin/templatetags/admin_tags.py @@ -1,5 +1,7 @@ """Admin extension tags.""" +from functools import reduce + from django import template from django.core.urlresolvers import reverse from django.template.loader import render_to_string diff --git a/modoboa/admin/views/domain.py b/modoboa/admin/views/domain.py index 3348ec93d..26c6a9126 100644 --- a/modoboa/admin/views/domain.py +++ b/modoboa/admin/views/domain.py @@ -1,5 +1,7 @@ """Domain related views.""" +from functools import reduce + from django.core.urlresolvers import reverse from django.http import HttpResponseRedirect from django.shortcuts import render diff --git a/modoboa/admin/views/export.py b/modoboa/admin/views/export.py index 4e9430aa0..a9c78920e 100644 --- a/modoboa/admin/views/export.py +++ b/modoboa/admin/views/export.py @@ -1,7 +1,7 @@ """Export related views.""" -import cStringIO import csv +import io from rfc6266 import build_header @@ -47,7 +47,7 @@ def export_identities(request): if request.method == "POST": form = ExportIdentitiesForm(request.POST) form.is_valid() - fp = cStringIO.StringIO() + fp = io.BytesIO() csvwriter = csv.writer(fp, delimiter=form.cleaned_data["sepchar"]) identities = get_identities( request.user, **request.session['identities_filters']) @@ -75,7 +75,7 @@ def export_domains(request): if request.method == "POST": form = ExportDomainsForm(request.POST) form.is_valid() - fp = cStringIO.StringIO() + fp = io.BytesIO() csvwriter = csv.writer(fp, delimiter=form.cleaned_data["sepchar"]) for dom in get_domains(request.user, **request.session['domains_filters']): diff --git a/modoboa/core/commands/__init__.py b/modoboa/core/commands/__init__.py index f767830b3..3f582aaee 100644 --- a/modoboa/core/commands/__init__.py +++ b/modoboa/core/commands/__init__.py @@ -1,5 +1,8 @@ # coding: utf-8 +from __future__ import print_function +from builtins import object + import argparse import os import sys @@ -94,7 +97,7 @@ def handle_command_line(): (args, remaining) = parser.parse_known_args() if args.command not in commands: - print >> sys.stderr, "Unknown command '%s'" % args.command + print("Unknown command '%s'" % args.command, file=sys.stderr) sys.exit(1) commands[args.command](commands, verbose=args.verbose).run(remaining) diff --git a/modoboa/core/commands/deploy.py b/modoboa/core/commands/deploy.py index 7cfa145b9..de971b7e1 100644 --- a/modoboa/core/commands/deploy.py +++ b/modoboa/core/commands/deploy.py @@ -2,6 +2,9 @@ """A shortcut to deploy a fresh modoboa instance.""" +from __future__ import print_function + +from builtins import input import getpass import os import shutil @@ -105,9 +108,9 @@ class DeployCommand(Command): output = None if p.returncode: if output: - print >> sys.stderr, "\n".join( - [l for l in output if l is not None]) - print >> sys.stderr, "%s failed, check your configuration" % cmd + print("\n".join( + [l for l in output if l is not None]), file=sys.stderr) + print("%s failed, check your configuration" % cmd, file=sys.stderr) def ask_db_info(self, name='default'): """Prompt the user for database information @@ -117,10 +120,11 @@ class DeployCommand(Command): :param name: the connection name """ - print "Configuring database connection: %s" % name + print("Configuring database connection: %s" % name) info = { 'conn_name': name, - 'ENGINE': raw_input('Database type (mysql, postgres or sqlite3): ') + 'ENGINE': eval( + input('Database type (mysql, postgres or sqlite3): ')) } if info['ENGINE'] not in ['mysql', 'postgres', 'sqlite3']: raise RuntimeError('Unsupported database engine') @@ -135,15 +139,15 @@ class DeployCommand(Command): else: info['ENGINE'] = 'django.db.backends.mysql' default_port = 3306 - info['HOST'] = raw_input("Database host (default: 'localhost'): ") - info['PORT'] = raw_input( - "Database port (default: '%s'): " % default_port) + info['HOST'] = eval(input("Database host (default: 'localhost'): ")) + info['PORT'] = eval( + input("Database port (default: '%s'): " % default_port)) # leave port setting empty, if default value is supplied and # leave it to django if info['PORT'] == default_port: info['PORT'] = '' - info['NAME'] = raw_input('Database name: ') - info['USER'] = raw_input('Username: ') + info['NAME'] = eval(input('Database name: ')) + info['USER'] = eval(input('Username: ')) info['PASSWORD'] = getpass.getpass('Password: ') return info @@ -192,7 +196,7 @@ class DeployCommand(Command): # In case the user fails to supply a valid database url, # fallback to manual mode if not info: - print "There was a problem with your database-url. \n" + print("There was a problem with your database-url. \n") info = self.ask_db_info(conn_name) # If we set this earlier, our fallback method will never # be triggered @@ -205,9 +209,8 @@ class DeployCommand(Command): if parsed_args.domain: allowed_host = parsed_args.domain else: - allowed_host = raw_input( - 'What will be the hostname used to access Modoboa? ' - ) + allowed_host = eval( + input('What will be the hostname used to access Modoboa? ')) if not allowed_host: allowed_host = "localhost" extra_settings = [] diff --git a/modoboa/core/commands/help.py b/modoboa/core/commands/help.py index ab6960d00..adcac4887 100644 --- a/modoboa/core/commands/help.py +++ b/modoboa/core/commands/help.py @@ -1,5 +1,7 @@ # coding: utf-8 +from __future__ import print_function + import sys from . import Command @@ -16,7 +18,7 @@ class HelpCommand(Command): def handle(self, parsed_args): if parsed_args.name not in self._commands: - print >>sys.stderr, "Unknown command: %s" % parsed_args.name + print("Unknown command: %s" % parsed_args.name, file=sys.stderr) sys.exit(1) cmd = self._commands[parsed_args.name](self._commands) cmd._parser.prog = "modoboa-admin.py %s" % parsed_args.name diff --git a/modoboa/core/context_processors.py b/modoboa/core/context_processors.py index 7c081699c..3bd8a3c92 100644 --- a/modoboa/core/context_processors.py +++ b/modoboa/core/context_processors.py @@ -1,5 +1,7 @@ """Custom context processors.""" +from functools import reduce + from . import signals diff --git a/modoboa/core/extensions.py b/modoboa/core/extensions.py index 4303ff07d..d6641c611 100644 --- a/modoboa/core/extensions.py +++ b/modoboa/core/extensions.py @@ -1,5 +1,7 @@ """Extension management.""" +from builtins import object + from django.conf import settings from django.conf.urls import include, url @@ -100,7 +102,7 @@ class ExtensionsPool(object): def get_urls(self, category="app"): """Get all urls defined by extensions.""" result = [] - for ext_name in self.extensions.keys(): + for ext_name in list(self.extensions.keys()): ext = self.get_extension(ext_name) options = {} if category == "api": @@ -122,7 +124,7 @@ class ExtensionsPool(object): def list_all(self): """List all defined extensions.""" result = [] - for extname, extdef in self.extensions.iteritems(): + for extname, extdef in list(self.extensions.items()): if not extdef["show"]: continue infos = self.get_extension_infos(extname) diff --git a/modoboa/core/factories.py b/modoboa/core/factories.py index 61f224763..a88bb24fc 100644 --- a/modoboa/core/factories.py +++ b/modoboa/core/factories.py @@ -1,5 +1,7 @@ """Factories for core application.""" +from builtins import object + import factory from django.contrib.auth.models import Group @@ -11,7 +13,7 @@ class PermissionFactory(factory.DjangoModelFactory): """A base factory to handle permissions.""" - class Meta: + class Meta(object): abstract = True @factory.post_generation @@ -25,7 +27,7 @@ class GroupFactory(factory.DjangoModelFactory): """A factory to create Group instances.""" - class Meta: + class Meta(object): model = Group name = "DefaultGroup" @@ -35,7 +37,7 @@ class UserFactory(PermissionFactory): """A factory to create User instances.""" - class Meta: + class Meta(object): model = models.User django_get_or_create = ("username", ) @@ -55,7 +57,7 @@ class UserFactory(PermissionFactory): class LogFactory(factory.DjangoModelFactory): """Factory for Log.""" - class Meta: + class Meta(object): model = models.Log logger = "modoboa.admin" diff --git a/modoboa/core/forms.py b/modoboa/core/forms.py index 09ccd9ee5..95e1b8398 100644 --- a/modoboa/core/forms.py +++ b/modoboa/core/forms.py @@ -2,6 +2,8 @@ """Core forms.""" +from builtins import object + from django import forms from django.utils.translation import ugettext as _, ugettext_lazy @@ -44,7 +46,7 @@ class ProfileForm(forms.ModelForm): widget=forms.PasswordInput(attrs={"class": "form-control"}) ) - class Meta: + class Meta(object): model = User fields = ("first_name", "last_name", "language", "phone_number", "secondary_email") diff --git a/modoboa/core/handlers.py b/modoboa/core/handlers.py index 3be6517d2..9b904de36 100644 --- a/modoboa/core/handlers.py +++ b/modoboa/core/handlers.py @@ -5,6 +5,7 @@ import logging from django.core.urlresolvers import reverse from django.db.models import signals from django.dispatch import receiver +from django.utils.encoding import smart_text from django.utils.translation import ugettext as _ from django.contrib.sites import models as sites_models @@ -44,7 +45,7 @@ def post_revision_commit(sender, **kwargs): action = _("modified") level = "warning" message = _("%(object)s '%(name)s' %(action)s by user %(user)s") % { - "object": unicode(version.content_type).capitalize(), + "object": smart_text(version.content_type).capitalize(), "name": version.object_repr, "action": action, "user": current_user } @@ -68,7 +69,7 @@ def log_object_removal(sender, instance, **kwargs): return logger = logging.getLogger("modoboa.admin") msg = _("%(object)s '%(name)s' %(action)s by user %(user)s") % { - "object": unicode(version.content_type).capitalize(), + "object": smart_text(version.content_type).capitalize(), "name": version.object_repr, "action": _("deleted"), "user": get_request().user.username } diff --git a/modoboa/core/management/commands/cleanlogs.py b/modoboa/core/management/commands/cleanlogs.py index 754cfe264..16c0e0b60 100644 --- a/modoboa/core/management/commands/cleanlogs.py +++ b/modoboa/core/management/commands/cleanlogs.py @@ -1,5 +1,7 @@ # coding: utf-8 +from __future__ import print_function + import datetime import logging @@ -27,7 +29,7 @@ class Command(BaseCommand): def __vprint(self, msg): if not self.verbose: return - print msg + print(msg) def handle(self, *args, **options): if options["debug"]: diff --git a/modoboa/core/management/commands/generate_postfix_maps.py b/modoboa/core/management/commands/generate_postfix_maps.py index 65d85ac49..fbce3dc97 100644 --- a/modoboa/core/management/commands/generate_postfix_maps.py +++ b/modoboa/core/management/commands/generate_postfix_maps.py @@ -1,4 +1,5 @@ """Management command to generate/update postfix map files.""" +from __future__ import print_function import copy import hashlib @@ -134,7 +135,7 @@ query = {{ query|safe }} context["dbhost"] = mapcontent["hosts"] content = self.get_template(context["dbtype"]).render( Context( - dict(context.items(), + dict(list(context.items()), query=getattr(mapobject, context["dbtype"])) ) ) @@ -160,6 +161,6 @@ query = {{ query|safe }} force_overwrite=options["force_overwrite"]) checksums[mapobject.filename] = checksum with open(self.__checksums_file, "w") as fp: - for fname, checksum in checksums.items(): + for fname, checksum in list(checksums.items()): fp.write("{}:{}:{}\n".format( fname, context["dbtype"], checksum)) diff --git a/modoboa/core/management/commands/load_initial_data.py b/modoboa/core/management/commands/load_initial_data.py index dff31da7c..c3d82064d 100644 --- a/modoboa/core/management/commands/load_initial_data.py +++ b/modoboa/core/management/commands/load_initial_data.py @@ -5,6 +5,8 @@ """ +from functools import reduce + from django.contrib.auth.models import Group from django.core.management.base import BaseCommand @@ -57,7 +59,7 @@ class Command(BaseCommand): extensions.exts_pool.load_all() - groups = constants.PERMISSIONS.keys() + groups = list(constants.PERMISSIONS.keys()) for groupname in groups: group, created = Group.objects.get_or_create(name=groupname) results = signals.extra_role_permissions.send( @@ -70,7 +72,7 @@ class Command(BaseCommand): continue add_permissions_to_group(group, permissions) - for extname in extensions.exts_pool.extensions.keys(): + for extname in list(extensions.exts_pool.extensions.keys()): extension = extensions.exts_pool.get_extension(extname) extension.load_initial_data() signals.initial_data_loaded.send( diff --git a/modoboa/core/middleware.py b/modoboa/core/middleware.py index c1463be56..7ebd3bdb8 100644 --- a/modoboa/core/middleware.py +++ b/modoboa/core/middleware.py @@ -1,5 +1,7 @@ """Core middlewares.""" +from builtins import object + from . import models diff --git a/modoboa/core/models.py b/modoboa/core/models.py index fed83e3f7..bd32a128f 100644 --- a/modoboa/core/models.py +++ b/modoboa/core/models.py @@ -1,12 +1,14 @@ """Core models.""" +from builtins import object from email.header import Header import re from django.core.urlresolvers import reverse from django.db import models from django.utils import timezone -from django.utils.encoding import python_2_unicode_compatible, smart_text +from django.utils.encoding import ( + python_2_unicode_compatible, smart_bytes, smart_text) from django.utils.functional import cached_property from django.utils.translation import ugettext as _, ugettext_lazy @@ -91,7 +93,7 @@ class User(PermissionsMixin): USERNAME_FIELD = 'username' REQUIRED_FIELDS = ['email'] - class Meta: + class Meta(object): ordering = ["username"] index_together = [ ['email', 'is_active'] @@ -119,8 +121,7 @@ class User(PermissionsMixin): load_core_settings() scheme = param_tools.get_global_parameter( "password_scheme", raise_exception=False) - if isinstance(raw_value, unicode): - raw_value = raw_value.encode("utf-8") + raw_value = smart_bytes(raw_value) return get_password_hasher(scheme.upper())().encrypt(raw_value) def set_password(self, raw_value, curvalue=None): @@ -152,8 +153,7 @@ class User(PermissionsMixin): match = self.password_expr.match(self.password) if match is None: return False - if isinstance(raw_value, unicode): - raw_value = raw_value.encode("utf-8") + raw_value = smart_bytes(raw_value) scheme = match.group(1) val2 = match.group(2) hasher = get_password_hasher(scheme) @@ -410,13 +410,13 @@ class User(PermissionsMixin): """ row = [ "account", - self.username.encode("utf-8"), - self.password.encode("utf-8"), - self.first_name.encode("utf-8"), - self.last_name.encode("utf-8"), + smart_bytes(self.username), + smart_bytes(self.password), + smart_bytes(self.first_name), + smart_bytes(self.last_name), self.is_active, self.role, - self.email.encode("utf-8") + smart_bytes(self.email) ] results = signals.account_exported.send( sender=self.__class__, user=self) @@ -455,7 +455,7 @@ class ObjectAccess(models.Model): content_object = GenericForeignKey('content_type', 'object_id') is_owner = models.BooleanField(default=False) - class Meta: + class Meta(object): unique_together = (("user", "content_type", "object_id"),) def __unicode__(self): diff --git a/modoboa/core/password_hashers/base.py b/modoboa/core/password_hashers/base.py index b0902c926..67c1d0e4b 100644 --- a/modoboa/core/password_hashers/base.py +++ b/modoboa/core/password_hashers/base.py @@ -3,11 +3,14 @@ Base password hashers. Contains weak hashers (the original ones) available with Modoboa. """ + +import base64 +from builtins import object import crypt import hashlib -import base64 -import string from random import Random +import string + from django.utils.crypto import constant_time_compare diff --git a/modoboa/core/templatetags/core_tags.py b/modoboa/core/templatetags/core_tags.py index 6d3518728..70b32fc4a 100644 --- a/modoboa/core/templatetags/core_tags.py +++ b/modoboa/core/templatetags/core_tags.py @@ -1,6 +1,7 @@ # coding: utf-8 """Custom tags for Core application.""" +from functools import reduce import os import pkg_resources import re @@ -10,6 +11,7 @@ from django.conf import settings from django.core.urlresolvers import reverse from django.template.loader import render_to_string from django.utils import timezone +from django.utils.encoding import smart_text from django.utils.safestring import mark_safe from django.utils.translation import get_language, ugettext as _ @@ -241,7 +243,7 @@ def display_messages(msgs): level = "info" for m in msgs: level = m.tags - text += unicode(m) + "\\\n" + text += smart_text(m) + "\\\n" if level == "info": level = "success" diff --git a/modoboa/core/tests/test_ldap.py b/modoboa/core/tests/test_ldap.py index 182fae6dd..d87e175b6 100644 --- a/modoboa/core/tests/test_ldap.py +++ b/modoboa/core/tests/test_ldap.py @@ -1,5 +1,7 @@ """Tests for core application.""" +from builtins import object + from unittest import skipIf from django.core.urlresolvers import reverse diff --git a/modoboa/core/views/admin.py b/modoboa/core/views/admin.py index d1bdba05b..e224decd7 100644 --- a/modoboa/core/views/admin.py +++ b/modoboa/core/views/admin.py @@ -1,5 +1,7 @@ """Views available to super administrators only.""" +from functools import reduce + from django.contrib.auth.decorators import ( login_required, user_passes_test ) diff --git a/modoboa/lib/email_utils.py b/modoboa/lib/email_utils.py index be8a6a411..22b8e414f 100644 --- a/modoboa/lib/email_utils.py +++ b/modoboa/lib/email_utils.py @@ -1,13 +1,15 @@ # coding: utf-8 -import re -import smtplib -import time +from builtins import object from email.header import Header, decode_header from email.mime.text import MIMEText from email.utils import make_msgid, formatdate, parseaddr +import re +import smtplib +import time from django.conf import settings +from django.utils.encoding import smart_text from django.template.loader import render_to_string from modoboa.lib import u2u_decode @@ -79,7 +81,7 @@ class Email(object): decoded = decode_header(fname) value = ( decoded[0][0] if decoded[0][1] is None - else unicode(decoded[0][0], decoded[0][1]) + else smart_text(decoded[0][0], decoded[0][1]) ) else: value = "part_%s" % level @@ -321,7 +323,7 @@ def __sendmail(sender, rcpt, msgstring, server='localhost', port=25): s = smtplib.SMTP(server, port) s.sendmail(sender, [rcpt], msgstring) s.quit() - except smtplib.SMTPException, e: + except smtplib.SMTPException as e: return False, "SMTP error: %s" % str(e) return True, None @@ -360,7 +362,7 @@ def sendmail_fromfile(sender, rcpt, fname): """ try: fp = open(fname) - except IOError, e: + except IOError as e: return False, str(e) content = """From: %s diff --git a/modoboa/lib/form_utils.py b/modoboa/lib/form_utils.py index f04c6f5fc..a5acd2573 100644 --- a/modoboa/lib/form_utils.py +++ b/modoboa/lib/form_utils.py @@ -3,6 +3,7 @@ """Form management utilities.""" import abc +from builtins import object, range from collections import OrderedDict import re @@ -19,6 +20,8 @@ from django.utils.translation import ugettext as _, ugettext_lazy from modoboa.lib.exceptions import BadRequest from modoboa.lib.web_utils import render_to_json_response +ABC = abc.ABCMeta("ABC", (object,), {}) # compatible with Python 2 *and* 3 + class WizardStep(object): """A wizard step.""" @@ -64,11 +67,9 @@ class WizardStep(object): self.form = self._cls(*args) -class WizardForm(object): +class WizardForm(ABC): """Custom wizard.""" - __metaclass__ = abc.ABCMeta - template_name = "common/wizard_forms.html" def __init__(self, request, submit_button_label=None): @@ -86,7 +87,7 @@ class WizardForm(object): def errors(self): result = {} for step in self.steps: - for name, value in step.form.errors.items(): + for name, value in list(step.form.errors.items()): if name == '__all__': continue result[name] = value @@ -139,7 +140,7 @@ class WizardForm(object): stepid = self._get_step_id() self.create_forms(self.request.POST) statuses = [] - for cpt in xrange(0, stepid): + for cpt in range(0, stepid): if self.steps[cpt].check_access(self): statuses.append(self.steps[cpt].form.is_valid()) if False in statuses: @@ -201,7 +202,7 @@ class DynamicForm(object): if value is not None: self.fields[name].initial = value if pos: - order = self.fields.keys() + order = list(self.fields.keys()) order.remove(name) order.insert(pos, name) self.fields = OrderedDict((key, self.fields[key]) for key in order) @@ -215,7 +216,7 @@ class DynamicForm(object): """ expr = re.compile(r'%s_\d+' % pattern) values = [] - for k, v in qdict.iteritems(): + for k, v in list(qdict.items()): if k == pattern or expr.match(k): values.append((k, v)) @@ -280,7 +281,7 @@ class TabForms(object): """ result = {} for f in self.forms: - for name, value in f['instance'].errors.items(): + for name, value in list(f['instance'].errors.items()): if name == '__all__': continue result[name] = value diff --git a/modoboa/lib/imap_utf7.py b/modoboa/lib/imap_utf7.py index 2da57f0e9..28fccacc8 100644 --- a/modoboa/lib/imap_utf7.py +++ b/modoboa/lib/imap_utf7.py @@ -51,6 +51,8 @@ Found here: http://svn.plone.org/svn/collective/mxmImapClient/trunk/imapUTF7.py """ +from __future__ import print_function + import binascii import codecs @@ -172,29 +174,29 @@ if __name__ == '__main__': # print u'båx'.encode('imap4-utf-7') # print 'expected b&AOU-x' - print '#######' - print 'bøx' + print('#######') + print('bøx') e = imapUTF7Encode(u'bøx') - print e - print imapUTF7Decode(e).encode('latin-1') + print(e) + print(imapUTF7Decode(e).encode('latin-1')) - print '#######' - print 'båx' + print('#######') + print('båx') e = imapUTF7Encode(u'båx') - print e - print imapUTF7Decode(e).encode('latin-1') + print(e) + print(imapUTF7Decode(e).encode('latin-1')) - print '#######' - print '~/bågø' + print('#######') + print('~/bågø') e = imapUTF7Encode(u'~/bågø') - print e - print imapUTF7Decode(e).encode('latin-1') + print(e) + print(imapUTF7Decode(e).encode('latin-1')) - print '#######' - print 'Ting & Såger' + print('#######') + print('Ting & Såger') e = imapUTF7Encode(u'Ting & Såger') - print e - print imapUTF7Decode(e).encode('latin-1') + print(e) + print(imapUTF7Decode(e).encode('latin-1')) # e = imapUTF7Decode('b&AOU-x') # print e.encode('latin-1') @@ -202,13 +204,13 @@ if __name__ == '__main__': # e = imapUTF7Decode('b&APg-x') # print e.encode('latin-1') - print '#######' - print '~/Følder/mailbåx & stuff + more' + print('#######') + print('~/Følder/mailbåx & stuff + more') n = u'~/Følder/mailbåx & stuff + more' e = imapUTF7Encode(n) - print e - print imapUTF7Decode(e).encode('latin-1') + print(e) + print(imapUTF7Decode(e).encode('latin-1')) - print '#######' - print '~peter/mail/&ZeVnLIqe-/&U,BTFw-' - print imapUTF7Decode('~peter/mail/&ZeVnLIqe-/&U,BTFw-').encode('utf-8') + print('#######') + print('~peter/mail/&ZeVnLIqe-/&U,BTFw-') + print(imapUTF7Decode('~peter/mail/&ZeVnLIqe-/&U,BTFw-').encode('utf-8')) diff --git a/modoboa/lib/ldap_utils.py b/modoboa/lib/ldap_utils.py index 8fc210eb2..89789f34c 100644 --- a/modoboa/lib/ldap_utils.py +++ b/modoboa/lib/ldap_utils.py @@ -32,6 +32,9 @@ Extracted from `this blog one instance to another. """ + +from builtins import object + import ldap from django.conf import settings diff --git a/modoboa/lib/middleware.py b/modoboa/lib/middleware.py index 365d12f68..e9193c0b3 100644 --- a/modoboa/lib/middleware.py +++ b/modoboa/lib/middleware.py @@ -2,7 +2,10 @@ """Custom middlewares.""" +from builtins import object + from django.http import HttpResponseRedirect +from django.utils.encoding import smart_text from modoboa.lib.exceptions import ModoboaException from modoboa.lib import signals as lib_signals @@ -30,14 +33,14 @@ class CommonExceptionCatcher(object): if request.is_ajax() or "/api/" in request.path: if exception.http_code is None: return ajax_response( - request, status="ko", respmsg=unicode(exception), + request, status="ko", respmsg=smart_text(exception), norefresh=True ) return render_to_json_response( - unicode(exception), status=exception.http_code + smart_text(exception), status=exception.http_code ) return _render_error( - request, user_context=dict(error=unicode(exception)) + request, user_context=dict(error=smart_text(exception)) ) diff --git a/modoboa/lib/paginator.py b/modoboa/lib/paginator.py index 79ecf94dd..34120ec82 100644 --- a/modoboa/lib/paginator.py +++ b/modoboa/lib/paginator.py @@ -2,6 +2,8 @@ Custom pagination tools. """ +from builtins import object + class Page(object): diff --git a/modoboa/lib/templatetags/form_tags.py b/modoboa/lib/templatetags/form_tags.py index 213dd17ad..c993f0506 100644 --- a/modoboa/lib/templatetags/form_tags.py +++ b/modoboa/lib/templatetags/form_tags.py @@ -4,6 +4,7 @@ Form rendering tags. from django import forms from django import template +from django.utils.encoding import smart_text from django.utils.safestring import mark_safe from django.template.loader import render_to_string @@ -43,7 +44,7 @@ def render_field( from modoboa.core.templatetags.core_tags import visirule if type(field.field) is SeparatorField: - return "%s" % (visirule(field), unicode(field.label)) + return "%s" % (visirule(field), smart_text(field.label)) configure_field_classes(field) context = { "field": field, "help_display_mode": help_display_mode, diff --git a/modoboa/lib/templatetags/lib_tags.py b/modoboa/lib/templatetags/lib_tags.py index 530c272bf..1342f27c4 100644 --- a/modoboa/lib/templatetags/lib_tags.py +++ b/modoboa/lib/templatetags/lib_tags.py @@ -16,7 +16,7 @@ register = template.Library() @register.simple_tag def join(items, sep=','): res = "" - for k, v in items.iteritems(): + for k, v in list(items.items()): if res != "": res += sep res += "%s : '%s'" % (k, v) diff --git a/modoboa/lib/test_utils.py b/modoboa/lib/test_utils.py index e4bb1e4d9..22f48ee2b 100644 --- a/modoboa/lib/test_utils.py +++ b/modoboa/lib/test_utils.py @@ -1,5 +1,7 @@ """Tools for top level testing (ie. when ran by Travis).""" +from builtins import object + import os import shutil import tempfile diff --git a/modoboa/lib/tests.py b/modoboa/lib/tests.py index c2544717c..7fe724c88 100644 --- a/modoboa/lib/tests.py +++ b/modoboa/lib/tests.py @@ -1,6 +1,7 @@ # coding: utf-8 """Testing utilities.""" +from builtins import object import json from django.core import management diff --git a/modoboa/lib/u2u_decode.py b/modoboa/lib/u2u_decode.py index 085e714e8..f453f6768 100644 --- a/modoboa/lib/u2u_decode.py +++ b/modoboa/lib/u2u_decode.py @@ -5,6 +5,8 @@ from email.header import decode_header, make_header import re +from django.utils.encoding import smart_text + # check spaces between encoded_words (and strip them) sre = re.compile(r'\?=[ \t]+=\?') # re pat for MIME encoded_word (without trailing spaces) @@ -24,7 +26,7 @@ def decode_mime(m): """Substitute matching encoded_word with unicode equiv.""" h = decode_header(clean_spaces(m)) try: - u = unicode(make_header(h)) + u = smart_text(make_header(h)) except (LookupError, UnicodeDecodeError): return m.group(0) return u diff --git a/modoboa/lib/validators.py b/modoboa/lib/validators.py index 86fbbc85b..ee4cda012 100644 --- a/modoboa/lib/validators.py +++ b/modoboa/lib/validators.py @@ -1,5 +1,6 @@ """Field validators.""" +from builtins import object import re from django.core.exceptions import ValidationError diff --git a/modoboa/lib/web_utils.py b/modoboa/lib/web_utils.py index 1099b6dac..8ac234a76 100644 --- a/modoboa/lib/web_utils.py +++ b/modoboa/lib/web_utils.py @@ -4,6 +4,8 @@ This module contains extra functions/shortcuts used to render HTML. """ +from builtins import object + import json import re import sys @@ -36,7 +38,7 @@ def getctx(status, level=1, callback=None, **kwargs): else: callername = callback ctx = {"status": status, "callback": callername} - for kw, v in kwargs.iteritems(): + for kw, v in list(kwargs.items()): ctx[kw] = v return ctx @@ -60,7 +62,7 @@ def ajax_response(request, status="ok", respmsg=None, :param kwargs: dict used for template rendering """ ctx = {} - for k, v in kwargs.iteritems(): + for k, v in list(kwargs.items()): ctx[k] = v if template is not None: content = render_to_string(template, ctx, request) diff --git a/modoboa/limits/forms.py b/modoboa/limits/forms.py index 2958b201c..bc4182684 100644 --- a/modoboa/limits/forms.py +++ b/modoboa/limits/forms.py @@ -36,14 +36,14 @@ class ResourcePoolForm(forms.Form): cleaned_data = super(ResourcePoolForm, self).clean() if self.errors: return cleaned_data - for lname in self.fields.keys(): + for lname in list(self.fields.keys()): if cleaned_data[lname] < -1: self.add_error(lname, _("Invalid limit")) return cleaned_data def load_from_user(self, user): """Load limit values from given user.""" - for fieldname in self.fields.keys(): + for fieldname in list(self.fields.keys()): lname = fieldname.replace("_limit", "") self.fields[fieldname].initial = ( user.userobjectlimit_set.get(name=lname).max_value) @@ -75,7 +75,7 @@ class DomainLimitsForm(forms.Form): ) if not self.domain: return - for fieldname in self.fields.keys(): + for fieldname in list(self.fields.keys()): lname = fieldname.replace("_limit", "") self.fields[fieldname].initial = ( self.domain.domainobjectlimit_set.get(name=lname).max_value) @@ -85,7 +85,7 @@ class DomainLimitsForm(forms.Form): cleaned_data = super(DomainLimitsForm, self).clean() if self.errors: return cleaned_data - for lname in self.fields.keys(): + for lname in list(self.fields.keys()): if cleaned_data[lname] < -1: self.add_error(lname, _("Invalid limit")) return cleaned_data diff --git a/modoboa/limits/models.py b/modoboa/limits/models.py index 58e3e8c0e..f93f0afd6 100644 --- a/modoboa/limits/models.py +++ b/modoboa/limits/models.py @@ -2,6 +2,8 @@ """Models for the limits extensions.""" +from builtins import object + from django.conf import settings from django.db import models from django.utils.encoding import python_2_unicode_compatible @@ -72,7 +74,7 @@ class UserObjectLimit(ObjectLimitMixin, models.Model): content_type = models.ForeignKey("contenttypes.ContentType") max_value = models.IntegerField(default=0) - class Meta: + class Meta(object): unique_together = (("user", "name"), ) @property @@ -134,7 +136,7 @@ class DomainObjectLimit(ObjectLimitMixin, models.Model): name = models.CharField(max_length=254) max_value = models.IntegerField(default=0) - class Meta: + class Meta(object): unique_together = (("domain", "name"), ) @property diff --git a/modoboa/limits/serializers.py b/modoboa/limits/serializers.py index c11205a92..a4e63b2a6 100644 --- a/modoboa/limits/serializers.py +++ b/modoboa/limits/serializers.py @@ -24,7 +24,7 @@ class ResourcesSerializer(serializers.Serializer): def update(self, instance, validated_data): """Update limits.""" user = self.context["request"].user - for name, max_value in validated_data.items(): + for name, max_value in list(validated_data.items()): limit = instance.userobjectlimit_set.get(name=name) if not user.is_superuser: lib.allocate_resources_from_user(limit, user, max_value) diff --git a/modoboa/limits/tests/test_import.py b/modoboa/limits/tests/test_import.py index 0cc11e39a..f07bc99b4 100644 --- a/modoboa/limits/tests/test_import.py +++ b/modoboa/limits/tests/test_import.py @@ -1,4 +1,7 @@ # coding: utf-8 + +from builtins import range + from django.core.files.base import ContentFile from django.core.urlresolvers import reverse @@ -53,7 +56,7 @@ domainalias; domalias2.com; {domain}; True self.client.force_login(self.reseller) self.assertFalse(limit.is_exceeded()) content = "" - for cpt in xrange(initial_count, 2): + for cpt in range(initial_count, 2): content += ( "account; admin{cpt}@{domain}; toto; User; One; True; " "DomainAdmins; user{cpt}@{domain}; 5; {domain}\n" diff --git a/modoboa/limits/utils.py b/modoboa/limits/utils.py index 64b3358ba..692cafd01 100644 --- a/modoboa/limits/utils.py +++ b/modoboa/limits/utils.py @@ -5,12 +5,12 @@ from . import constants def get_user_limit_templates(): """Return defined templates.""" - return constants.DEFAULT_USER_LIMITS.items() + return list(constants.DEFAULT_USER_LIMITS.items()) def get_domain_limit_templates(): """Return defined templates.""" - return constants.DEFAULT_DOMAIN_LIMITS.items() + return list(constants.DEFAULT_DOMAIN_LIMITS.items()) def move_pool_resource(owner, user): diff --git a/modoboa/parameters/forms.py b/modoboa/parameters/forms.py index 2996e96e5..f456dae16 100644 --- a/modoboa/parameters/forms.py +++ b/modoboa/parameters/forms.py @@ -25,7 +25,7 @@ class GenericParametersForm(forms.Form): self.visirules = {} if self.visibility_rules is not None: - for key, rule in self.visibility_rules.items(): + for key, rule in list(self.visibility_rules.items()): field, value = rule.split("=") visibility = { "field": "id_%s-%s" % (self.app, field), "value": value @@ -68,7 +68,7 @@ class AdminParametersForm(GenericParametersForm): def save(self): """Save parameters to database.""" parameters = {} - for name, value in self.cleaned_data.items(): + for name, value in list(self.cleaned_data.items()): if type(self.fields[name]) is form_utils.SeparatorField: continue parameters[name] = value @@ -99,7 +99,7 @@ class UserParametersForm(GenericParametersForm): def save(self): """Save new values.""" parameters = {} - for name, value in self.cleaned_data.items(): + for name, value in list(self.cleaned_data.items()): if type(self.fields[name]) is form_utils.SeparatorField: continue parameters[name] = value diff --git a/modoboa/parameters/tools.py b/modoboa/parameters/tools.py index 1be26d468..eb11d845d 100644 --- a/modoboa/parameters/tools.py +++ b/modoboa/parameters/tools.py @@ -1,5 +1,6 @@ """Parameters management.""" +from builtins import object import copy from modoboa.lib import exceptions @@ -42,9 +43,9 @@ class Registry(object): def _load_default_values(self, level): """Load default values.""" - for app, data in self._registry[level].items(): + for app, data in list(self._registry[level].items()): form = data["formclass"](load_values_from_db=False) - for name, field in form.fields.items(): + for name, field in list(form.fields.items()): if type(field) is form_utils.SeparatorField: continue data["defaults"][name] = field.initial @@ -56,7 +57,8 @@ class Registry(object): if first_app in self._registry[level]: sorted_apps.append(first_app) sorted_apps += sorted( - [a for a in self._registry[level].keys() if a != first_app], + [a for a in list(self._registry[level].keys()) + if a != first_app], key=lambda a: self._registry[level][a]["label"] ) result = [] @@ -126,7 +128,7 @@ class Manager(object): if app is None: app = guess_extension_name() values = registry.get_defaults(self._level, app) - for parameter, value in values.iteritems(): + for parameter, value in list(values.items()): if app in self._parameters: value = self._parameters[app].get(parameter, value) yield (parameter, value) diff --git a/modoboa/relaydomains/forms.py b/modoboa/relaydomains/forms.py index abd3652b1..38007e4de 100644 --- a/modoboa/relaydomains/forms.py +++ b/modoboa/relaydomains/forms.py @@ -1,5 +1,7 @@ """Postfix relay domains extension forms.""" +from builtins import object + from django import forms from django.core import validators from django.utils.translation import ugettext as _ @@ -22,7 +24,7 @@ class RelayDomainWizardStep(WizardStep): class RelayDomainFormGeneral(forms.ModelForm): """RelayDomain form.""" - class Meta: + class Meta(object): model = RelayDomain exclude = ["domain", "creation", "last_modification"] widgets = { diff --git a/modoboa/relaydomains/models.py b/modoboa/relaydomains/models.py index 628e7cd54..ba3b31041 100644 --- a/modoboa/relaydomains/models.py +++ b/modoboa/relaydomains/models.py @@ -1,5 +1,7 @@ """Relay domain related models.""" +from builtins import object + from django.db import models from django.db.models.manager import Manager from django.utils.translation import ugettext as _, ugettext_lazy @@ -50,7 +52,7 @@ class Service(models.Model): objects = ServiceManager() - class Meta: + class Meta(object): db_table = "postfix_relay_domains_service" def __str__(self): @@ -84,7 +86,7 @@ class RelayDomain(admin_models.AdminObject): default=False ) - class Meta: + class Meta(object): ordering = ["domain__name"] db_table = "postfix_relay_domains_relaydomain" diff --git a/modoboa/relaydomains/postfix_maps.py b/modoboa/relaydomains/postfix_maps.py index e91444e7f..2372b05c0 100644 --- a/modoboa/relaydomains/postfix_maps.py +++ b/modoboa/relaydomains/postfix_maps.py @@ -1,5 +1,7 @@ """Map file definitions for postfix.""" +from builtins import object + class RelayDomainsMap(object): diff --git a/modoboa/relaydomains/tests.py b/modoboa/relaydomains/tests.py index 1ca7e151f..dd256f373 100644 --- a/modoboa/relaydomains/tests.py +++ b/modoboa/relaydomains/tests.py @@ -1,5 +1,6 @@ -"""modoboa-admin-relaydomains unit tests.""" +"""relaydomains unit tests.""" +from builtins import object import json from django.core.files.base import ContentFile diff --git a/modoboa/relaydomains/views.py b/modoboa/relaydomains/views.py index 6e30d2ce9..64c8372a4 100644 --- a/modoboa/relaydomains/views.py +++ b/modoboa/relaydomains/views.py @@ -1,5 +1,7 @@ """Custom views.""" +from builtins import str + from django.contrib.auth.decorators import ( login_required, permission_required ) From 6644ca3a55d30c58141c06505650a8b4771fa130 Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Wed, 10 May 2017 19:09:02 +0200 Subject: [PATCH 02/23] Added python 3.4 to test matrix. see #755 --- .travis.yml | 1 + requirements.txt | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 0a580df42..9a6c02647 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,7 @@ language: python cache: pip python: - "2.7" + - "3.4" addons: apt: packages: diff --git a/requirements.txt b/requirements.txt index ea5733c8a..48c0f433b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -21,7 +21,7 @@ gevent==1.2.1 ipaddress jsonfield==1.0.3 passlib==1.6.5 -progressbar==2.3 +progressbar33==2.4 py-dateutil==2.2 pycrypto pytz From 297a9aed94bea1ae6e38e304b74640380c553c82 Mon Sep 17 00:00:00 2001 From: Arthur Vuillard Date: Wed, 10 May 2017 23:31:41 +0200 Subject: [PATCH 03/23] make modoboa-admin deploy work with python 3.4 --- modoboa/core/commands/deploy.py | 11 ++++++++--- modoboa/lib/cryptutils.py | 6 ++++-- modoboa/lib/form_utils.py | 8 +++++--- modoboa/lib/permissions.py | 4 +++- modoboa/lib/u2u_decode.py | 2 +- modoboa/lib/validators.py | 6 +++--- 6 files changed, 24 insertions(+), 13 deletions(-) diff --git a/modoboa/core/commands/deploy.py b/modoboa/core/commands/deploy.py index de971b7e1..04457e013 100644 --- a/modoboa/core/commands/deploy.py +++ b/modoboa/core/commands/deploy.py @@ -11,6 +11,8 @@ import shutil import subprocess import sys +from os.path import isfile + try: import pip except ImportError: @@ -108,8 +110,10 @@ class DeployCommand(Command): output = None if p.returncode: if output: - print("\n".join( - [l for l in output if l is not None]), file=sys.stderr) + print( + "\n".join([l.decode() for l in output if l is not None]), + file=sys.stderr + ) print("%s failed, check your configuration" % cmd, file=sys.stderr) def ask_db_info(self, name='default'): @@ -250,7 +254,8 @@ class DeployCommand(Command): ) os.mkdir("%s/media" % parsed_args.name) - os.unlink("%s/settings.pyc" % path) + if isfile("%s/settings.pyc" % path): + os.unlink("%s/settings.pyc" % path) self._exec_django_command( "migrate", parsed_args.name, '--noinput' ) diff --git a/modoboa/lib/cryptutils.py b/modoboa/lib/cryptutils.py index f0401ded7..16e8a82ca 100644 --- a/modoboa/lib/cryptutils.py +++ b/modoboa/lib/cryptutils.py @@ -1,6 +1,8 @@ # coding: utf-8 """Crypto related utilities.""" +from __future__ import unicode_literals + import base64 import random import string @@ -17,7 +19,7 @@ def random_key(l=16): :return: a string """ punctuation = """!#$%&'()*+,-./:;<=>?@[]^_`{|}~""" - population = string.digits + string.letters + punctuation + population = string.digits + string.ascii_letters + punctuation while True: key = "".join(random.sample(population * l, l)) if len(key) == l: @@ -27,7 +29,7 @@ def random_key(l=16): def encrypt(clear): key = param_tools.get_global_parameter("secret_key", app="core") obj = AES.new(key, AES.MODE_ECB) - if type(clear) is unicode: + if type(clear) is str: clear = clear.encode("utf-8") if len(clear) % AES.block_size: clear += " " * (AES.block_size - len(clear) % AES.block_size) diff --git a/modoboa/lib/form_utils.py b/modoboa/lib/form_utils.py index a5acd2573..7d67c8bb7 100644 --- a/modoboa/lib/form_utils.py +++ b/modoboa/lib/form_utils.py @@ -2,6 +2,8 @@ """Form management utilities.""" +from __future__ import unicode_literals + import abc from builtins import object, range from collections import OrderedDict @@ -12,7 +14,7 @@ from django.forms.fields import Field from django.forms.widgets import RadioSelect from django.forms.widgets import RadioChoiceInput from django.shortcuts import render -from django.utils.encoding import force_unicode +from django.utils.encoding import force_text from django.utils.html import conditional_escape from django.utils.safestring import mark_safe from django.utils.translation import ugettext as _, ugettext_lazy @@ -376,7 +378,7 @@ class CustomRadioInput(RadioChoiceInput): label_for = ' for="%s"' % self.attrs["id"] else: label_for = "" - choice_label = conditional_escape(force_unicode(self.choice_label)) + choice_label = conditional_escape(force_text(self.choice_label)) return mark_safe( u"" % (label_for, self.tag(), choice_label) @@ -394,7 +396,7 @@ class InlineRadioRenderer(RadioSelect.renderer): def render(self): return mark_safe( - u"\n".join([u"%s\n" % force_unicode(w) for w in self]) + u"\n".join([u"%s\n" % force_text(w) for w in self]) ) diff --git a/modoboa/lib/permissions.py b/modoboa/lib/permissions.py index 202dc9496..d27bcdf2b 100644 --- a/modoboa/lib/permissions.py +++ b/modoboa/lib/permissions.py @@ -1,6 +1,8 @@ # coding: utf-8 """Object level permissions.""" +from __future__ import unicode_literals + from django.contrib.auth.models import Group, Permission from django.contrib.contenttypes.models import ContentType @@ -143,7 +145,7 @@ def get_object_owner(obj): def add_permissions_to_group(group, permissions): """Add the specified permissions to a django group.""" - if isinstance(group, basestring): + if isinstance(group, str): group = Group.objects.get(name=group) for appname, modelname, permname in permissions: diff --git a/modoboa/lib/u2u_decode.py b/modoboa/lib/u2u_decode.py index f453f6768..cf4880c62 100644 --- a/modoboa/lib/u2u_decode.py +++ b/modoboa/lib/u2u_decode.py @@ -33,7 +33,7 @@ def decode_mime(m): def u2u_decode(s): - ur"""utility function for (final) decoding of mime header + """utility function for (final) decoding of mime header note: resulting string is in one line (no \n within) note2: spaces between enc_words are stripped (see RFC2047) diff --git a/modoboa/lib/validators.py b/modoboa/lib/validators.py index ee4cda012..54dbd2180 100644 --- a/modoboa/lib/validators.py +++ b/modoboa/lib/validators.py @@ -45,9 +45,9 @@ class UTF8EmailValidator(EmailValidator): ascii_set = u"-!#$%&'*+/=?^_`{}|~0-9A-Z" user_regex_raw = ( # dot-atom - ur"^[" + ascii_set + ul + "]+(\.[" + ascii_set + ul + "]+)*\Z" + r"^[" + ascii_set + ul + "]+(\.[" + ascii_set + ul + "]+)*\Z" # quoted-string - ur'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013\014\016-\177])*"\Z)' + r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013\014\016-\177])*"\Z)' ) user_regex = re.compile("(" + user_regex_raw, re.IGNORECASE) @@ -59,6 +59,6 @@ class UTF8AndEmptyUserEmailValidator(UTF8EmailValidator): """Same as upper + allows empty local part.""" user_regex = re.compile( - ur"(^$|" + UTF8EmailValidator.user_regex_raw, re.IGNORECASE) + r"(^$|" + UTF8EmailValidator.user_regex_raw, re.IGNORECASE) validate_utf8_and_empty_user_email = UTF8AndEmptyUserEmailValidator() From 9a5c293d520bb925f55d8edee479b7e2cb2ec542 Mon Sep 17 00:00:00 2001 From: Arthur Vuillard Date: Thu, 11 May 2017 00:02:00 +0200 Subject: [PATCH 04/23] remove useless builtins imports --- modoboa/core/commands/__init__.py | 1 - modoboa/core/commands/deploy.py | 1 - modoboa/core/extensions.py | 2 -- modoboa/core/factories.py | 2 -- modoboa/core/forms.py | 2 -- modoboa/core/middleware.py | 2 -- modoboa/core/models.py | 1 - modoboa/core/password_hashers/base.py | 1 - modoboa/core/tests/test_ldap.py | 2 -- modoboa/lib/email_utils.py | 1 - modoboa/lib/form_utils.py | 3 +-- modoboa/lib/ldap_utils.py | 2 -- modoboa/lib/middleware.py | 2 -- modoboa/lib/paginator.py | 2 -- modoboa/lib/test_utils.py | 2 -- modoboa/lib/tests.py | 1 - modoboa/lib/validators.py | 1 - modoboa/lib/web_utils.py | 2 -- modoboa/limits/models.py | 2 -- modoboa/limits/tests/test_import.py | 2 -- modoboa/parameters/tools.py | 1 - modoboa/relaydomains/forms.py | 2 -- modoboa/relaydomains/models.py | 2 -- modoboa/relaydomains/postfix_maps.py | 2 -- modoboa/relaydomains/tests.py | 1 - modoboa/relaydomains/views.py | 2 -- 26 files changed, 1 insertion(+), 43 deletions(-) diff --git a/modoboa/core/commands/__init__.py b/modoboa/core/commands/__init__.py index 3f582aaee..1a53c3120 100644 --- a/modoboa/core/commands/__init__.py +++ b/modoboa/core/commands/__init__.py @@ -1,7 +1,6 @@ # coding: utf-8 from __future__ import print_function -from builtins import object import argparse import os diff --git a/modoboa/core/commands/deploy.py b/modoboa/core/commands/deploy.py index 04457e013..61eb4eab8 100644 --- a/modoboa/core/commands/deploy.py +++ b/modoboa/core/commands/deploy.py @@ -4,7 +4,6 @@ from __future__ import print_function -from builtins import input import getpass import os import shutil diff --git a/modoboa/core/extensions.py b/modoboa/core/extensions.py index d6641c611..3df7f0fea 100644 --- a/modoboa/core/extensions.py +++ b/modoboa/core/extensions.py @@ -1,7 +1,5 @@ """Extension management.""" -from builtins import object - from django.conf import settings from django.conf.urls import include, url diff --git a/modoboa/core/factories.py b/modoboa/core/factories.py index a88bb24fc..c375d09c5 100644 --- a/modoboa/core/factories.py +++ b/modoboa/core/factories.py @@ -1,7 +1,5 @@ """Factories for core application.""" -from builtins import object - import factory from django.contrib.auth.models import Group diff --git a/modoboa/core/forms.py b/modoboa/core/forms.py index 95e1b8398..26dd71847 100644 --- a/modoboa/core/forms.py +++ b/modoboa/core/forms.py @@ -2,8 +2,6 @@ """Core forms.""" -from builtins import object - from django import forms from django.utils.translation import ugettext as _, ugettext_lazy diff --git a/modoboa/core/middleware.py b/modoboa/core/middleware.py index 7ebd3bdb8..c1463be56 100644 --- a/modoboa/core/middleware.py +++ b/modoboa/core/middleware.py @@ -1,7 +1,5 @@ """Core middlewares.""" -from builtins import object - from . import models diff --git a/modoboa/core/models.py b/modoboa/core/models.py index bd32a128f..66b1ea954 100644 --- a/modoboa/core/models.py +++ b/modoboa/core/models.py @@ -1,6 +1,5 @@ """Core models.""" -from builtins import object from email.header import Header import re diff --git a/modoboa/core/password_hashers/base.py b/modoboa/core/password_hashers/base.py index 67c1d0e4b..b9050f4ca 100644 --- a/modoboa/core/password_hashers/base.py +++ b/modoboa/core/password_hashers/base.py @@ -5,7 +5,6 @@ Contains weak hashers (the original ones) available with Modoboa. """ import base64 -from builtins import object import crypt import hashlib from random import Random diff --git a/modoboa/core/tests/test_ldap.py b/modoboa/core/tests/test_ldap.py index d87e175b6..182fae6dd 100644 --- a/modoboa/core/tests/test_ldap.py +++ b/modoboa/core/tests/test_ldap.py @@ -1,7 +1,5 @@ """Tests for core application.""" -from builtins import object - from unittest import skipIf from django.core.urlresolvers import reverse diff --git a/modoboa/lib/email_utils.py b/modoboa/lib/email_utils.py index 22b8e414f..50790187c 100644 --- a/modoboa/lib/email_utils.py +++ b/modoboa/lib/email_utils.py @@ -1,6 +1,5 @@ # coding: utf-8 -from builtins import object from email.header import Header, decode_header from email.mime.text import MIMEText from email.utils import make_msgid, formatdate, parseaddr diff --git a/modoboa/lib/form_utils.py b/modoboa/lib/form_utils.py index 7d67c8bb7..e1c85a416 100644 --- a/modoboa/lib/form_utils.py +++ b/modoboa/lib/form_utils.py @@ -5,7 +5,6 @@ from __future__ import unicode_literals import abc -from builtins import object, range from collections import OrderedDict import re @@ -22,7 +21,7 @@ from django.utils.translation import ugettext as _, ugettext_lazy from modoboa.lib.exceptions import BadRequest from modoboa.lib.web_utils import render_to_json_response -ABC = abc.ABCMeta("ABC", (object,), {}) # compatible with Python 2 *and* 3 +ABC = abc.ABCMeta(b"ABC", (object,), {}) # compatible with Python 2 *and* 3 class WizardStep(object): diff --git a/modoboa/lib/ldap_utils.py b/modoboa/lib/ldap_utils.py index 89789f34c..2988ee889 100644 --- a/modoboa/lib/ldap_utils.py +++ b/modoboa/lib/ldap_utils.py @@ -33,8 +33,6 @@ Extracted from `this blog """ -from builtins import object - import ldap from django.conf import settings diff --git a/modoboa/lib/middleware.py b/modoboa/lib/middleware.py index e9193c0b3..ee2f074c3 100644 --- a/modoboa/lib/middleware.py +++ b/modoboa/lib/middleware.py @@ -2,8 +2,6 @@ """Custom middlewares.""" -from builtins import object - from django.http import HttpResponseRedirect from django.utils.encoding import smart_text diff --git a/modoboa/lib/paginator.py b/modoboa/lib/paginator.py index 34120ec82..79ecf94dd 100644 --- a/modoboa/lib/paginator.py +++ b/modoboa/lib/paginator.py @@ -2,8 +2,6 @@ Custom pagination tools. """ -from builtins import object - class Page(object): diff --git a/modoboa/lib/test_utils.py b/modoboa/lib/test_utils.py index 22f48ee2b..e4bb1e4d9 100644 --- a/modoboa/lib/test_utils.py +++ b/modoboa/lib/test_utils.py @@ -1,7 +1,5 @@ """Tools for top level testing (ie. when ran by Travis).""" -from builtins import object - import os import shutil import tempfile diff --git a/modoboa/lib/tests.py b/modoboa/lib/tests.py index 7fe724c88..c2544717c 100644 --- a/modoboa/lib/tests.py +++ b/modoboa/lib/tests.py @@ -1,7 +1,6 @@ # coding: utf-8 """Testing utilities.""" -from builtins import object import json from django.core import management diff --git a/modoboa/lib/validators.py b/modoboa/lib/validators.py index 54dbd2180..83c569ad1 100644 --- a/modoboa/lib/validators.py +++ b/modoboa/lib/validators.py @@ -1,6 +1,5 @@ """Field validators.""" -from builtins import object import re from django.core.exceptions import ValidationError diff --git a/modoboa/lib/web_utils.py b/modoboa/lib/web_utils.py index 8ac234a76..b7607d75c 100644 --- a/modoboa/lib/web_utils.py +++ b/modoboa/lib/web_utils.py @@ -4,8 +4,6 @@ This module contains extra functions/shortcuts used to render HTML. """ -from builtins import object - import json import re import sys diff --git a/modoboa/limits/models.py b/modoboa/limits/models.py index f93f0afd6..0986eb756 100644 --- a/modoboa/limits/models.py +++ b/modoboa/limits/models.py @@ -2,8 +2,6 @@ """Models for the limits extensions.""" -from builtins import object - from django.conf import settings from django.db import models from django.utils.encoding import python_2_unicode_compatible diff --git a/modoboa/limits/tests/test_import.py b/modoboa/limits/tests/test_import.py index f07bc99b4..e6702a773 100644 --- a/modoboa/limits/tests/test_import.py +++ b/modoboa/limits/tests/test_import.py @@ -1,7 +1,5 @@ # coding: utf-8 -from builtins import range - from django.core.files.base import ContentFile from django.core.urlresolvers import reverse diff --git a/modoboa/parameters/tools.py b/modoboa/parameters/tools.py index eb11d845d..8d3b1131f 100644 --- a/modoboa/parameters/tools.py +++ b/modoboa/parameters/tools.py @@ -1,6 +1,5 @@ """Parameters management.""" -from builtins import object import copy from modoboa.lib import exceptions diff --git a/modoboa/relaydomains/forms.py b/modoboa/relaydomains/forms.py index 38007e4de..1da259ebb 100644 --- a/modoboa/relaydomains/forms.py +++ b/modoboa/relaydomains/forms.py @@ -1,7 +1,5 @@ """Postfix relay domains extension forms.""" -from builtins import object - from django import forms from django.core import validators from django.utils.translation import ugettext as _ diff --git a/modoboa/relaydomains/models.py b/modoboa/relaydomains/models.py index ba3b31041..715e4ba3f 100644 --- a/modoboa/relaydomains/models.py +++ b/modoboa/relaydomains/models.py @@ -1,7 +1,5 @@ """Relay domain related models.""" -from builtins import object - from django.db import models from django.db.models.manager import Manager from django.utils.translation import ugettext as _, ugettext_lazy diff --git a/modoboa/relaydomains/postfix_maps.py b/modoboa/relaydomains/postfix_maps.py index 2372b05c0..e91444e7f 100644 --- a/modoboa/relaydomains/postfix_maps.py +++ b/modoboa/relaydomains/postfix_maps.py @@ -1,7 +1,5 @@ """Map file definitions for postfix.""" -from builtins import object - class RelayDomainsMap(object): diff --git a/modoboa/relaydomains/tests.py b/modoboa/relaydomains/tests.py index dd256f373..672c5dda5 100644 --- a/modoboa/relaydomains/tests.py +++ b/modoboa/relaydomains/tests.py @@ -1,6 +1,5 @@ """relaydomains unit tests.""" -from builtins import object import json from django.core.files.base import ContentFile diff --git a/modoboa/relaydomains/views.py b/modoboa/relaydomains/views.py index 64c8372a4..6e30d2ce9 100644 --- a/modoboa/relaydomains/views.py +++ b/modoboa/relaydomains/views.py @@ -1,7 +1,5 @@ """Custom views.""" -from builtins import str - from django.contrib.auth.decorators import ( login_required, permission_required ) From bcb3fe0c1fe142cc98a9552e79d5209acb16cc21 Mon Sep 17 00:00:00 2001 From: Arthur Vuillard Date: Thu, 11 May 2017 00:07:22 +0200 Subject: [PATCH 05/23] use pyldap in place of python-ldap in order to have python3 compatibility --- ldap-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ldap-requirements.txt b/ldap-requirements.txt index 926c4e036..a74d0b79e 100644 --- a/ldap-requirements.txt +++ b/ldap-requirements.txt @@ -1,2 +1,2 @@ -python-ldap +pyldap django-auth-ldap From 19e0c7d0962f1f72030b2cec2a6ae4f1c6a2e418 Mon Sep 17 00:00:00 2001 From: Arthur Vuillard Date: Thu, 11 May 2017 00:21:30 +0200 Subject: [PATCH 06/23] add python 3.4 in tox conf --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index ed4170764..370af28f5 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py27 +envlist = py27,py34 [testenv] changedir=test_project From 1ad280e2c9d0e243df515573bf3318b9a06923d0 Mon Sep 17 00:00:00 2001 From: Arthur Vuillard Date: Thu, 11 May 2017 00:22:04 +0200 Subject: [PATCH 07/23] make deploy command work in both python2 and python3 --- modoboa/lib/form_utils.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/modoboa/lib/form_utils.py b/modoboa/lib/form_utils.py index e1c85a416..31b6dc421 100644 --- a/modoboa/lib/form_utils.py +++ b/modoboa/lib/form_utils.py @@ -13,6 +13,7 @@ from django.forms.fields import Field from django.forms.widgets import RadioSelect from django.forms.widgets import RadioChoiceInput from django.shortcuts import render +from django.utils import six from django.utils.encoding import force_text from django.utils.html import conditional_escape from django.utils.safestring import mark_safe @@ -21,7 +22,11 @@ from django.utils.translation import ugettext as _, ugettext_lazy from modoboa.lib.exceptions import BadRequest from modoboa.lib.web_utils import render_to_json_response -ABC = abc.ABCMeta(b"ABC", (object,), {}) # compatible with Python 2 *and* 3 +if six.PY2: + meta_name = b"ABC" +else: + meta_name = "ABC" +ABC = abc.ABCMeta(meta_name, (object,), {}) # compatible with Python 2 *and* 3 class WizardStep(object): From 799e06bc2f486f8f45ae299eb5a4f131c58fb696 Mon Sep 17 00:00:00 2001 From: Arthur Vuillard Date: Thu, 11 May 2017 00:27:39 +0200 Subject: [PATCH 08/23] add dirs created while testing to gitignoe --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 530d709e6..a018c094f 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,5 @@ DEV .idea .ropeproject modoboa.iml +.tox +modoboa_test From 66730da5eb334763f152fdb1695afb30a17c665c Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Thu, 11 May 2017 09:17:30 +0200 Subject: [PATCH 09/23] Fixed str issues. see #755 --- modoboa/core/tests/test_views.py | 10 ++++------ modoboa/lib/authbackends.py | 5 ----- modoboa/lib/cryptutils.py | 7 +++---- modoboa/lib/form_utils.py | 6 +++--- modoboa/lib/permissions.py | 2 +- modoboa/lib/tests.py | 3 +-- modoboa/lib/u2u_decode.py | 2 +- modoboa/lib/validators.py | 6 +++--- 8 files changed, 16 insertions(+), 25 deletions(-) diff --git a/modoboa/core/tests/test_views.py b/modoboa/core/tests/test_views.py index 88ca94b41..7396866c8 100644 --- a/modoboa/core/tests/test_views.py +++ b/modoboa/core/tests/test_views.py @@ -1,7 +1,5 @@ """Core views test cases.""" -import json - from testfixtures import compare from django import forms @@ -29,7 +27,7 @@ class LoginTestCase(ModoTestCase): signals.get_announcements.connect(announcement) response = self.client.get(reverse("core:login")) self.assertEqual(response.status_code, 200) - self.assertIn("This is an annoucement!", response.content) + self.assertIn("This is an annoucement!", str(response.content)) class DashboardTestCase(ModoTestCase): @@ -51,12 +49,12 @@ class DashboardTestCase(ModoTestCase): url = reverse("core:dashboard") response = self.client.get(url) self.assertEqual(response.status_code, 200) - self.assertIn("Latest news", response.content) + self.assertIn("Latest news", str(response.content)) self.client.logout() self.client.login(username=self.dadmin.username, password="toto") response = self.client.get(url) self.assertEqual(response.status_code, 200) - self.assertIn("Latest news", response.content) + self.assertIn("Latest news", str(response.content)) self.client.logout() self.client.login(username=self.user.username, password="toto") response = self.client.get(url) @@ -100,7 +98,7 @@ class DashboardTestCase(ModoTestCase): url = reverse("core:top_notifications_check") response = self.client.get(url) self.assertEqual(response.status_code, 200) - self.assertEqual(len(json.loads(response.content)), 1) + self.assertEqual(len(response.json()), 1) class SettingsTestCase(ModoTestCase): diff --git a/modoboa/lib/authbackends.py b/modoboa/lib/authbackends.py index c3c1c3f8b..f70acdb5c 100644 --- a/modoboa/lib/authbackends.py +++ b/modoboa/lib/authbackends.py @@ -14,11 +14,6 @@ class SMTPBackend(object): def authenticate(self, username=None, password=None): """Check the username/password and return a User.""" - if type(username) is unicode: - username = username.encode("utf-8") - if type(password) is unicode: - password = password.encode("utf-8") - host = getattr(settings, "AUTH_SMTP_SERVER_ADDRESS", "localhost") port = getattr(settings, "AUTH_SMTP_SERVER_PORT", 25) secured_mode = getattr(settings, "AUTH_SMTP_SECURED_MODE", None) diff --git a/modoboa/lib/cryptutils.py b/modoboa/lib/cryptutils.py index f0401ded7..4a6a22d5a 100644 --- a/modoboa/lib/cryptutils.py +++ b/modoboa/lib/cryptutils.py @@ -17,7 +17,7 @@ def random_key(l=16): :return: a string """ punctuation = """!#$%&'()*+,-./:;<=>?@[]^_`{|}~""" - population = string.digits + string.letters + punctuation + population = string.digits + string.ascii_letters + punctuation while True: key = "".join(random.sample(population * l, l)) if len(key) == l: @@ -27,13 +27,12 @@ def random_key(l=16): def encrypt(clear): key = param_tools.get_global_parameter("secret_key", app="core") obj = AES.new(key, AES.MODE_ECB) - if type(clear) is unicode: - clear = clear.encode("utf-8") + clear = str(clear) if len(clear) % AES.block_size: clear += " " * (AES.block_size - len(clear) % AES.block_size) ciph = obj.encrypt(clear) ciph = base64.b64encode(ciph) - return ciph + return str(ciph) def decrypt(ciph): diff --git a/modoboa/lib/form_utils.py b/modoboa/lib/form_utils.py index a5acd2573..c9c2b295f 100644 --- a/modoboa/lib/form_utils.py +++ b/modoboa/lib/form_utils.py @@ -12,7 +12,7 @@ from django.forms.fields import Field from django.forms.widgets import RadioSelect from django.forms.widgets import RadioChoiceInput from django.shortcuts import render -from django.utils.encoding import force_unicode +from django.utils.encoding import force_text from django.utils.html import conditional_escape from django.utils.safestring import mark_safe from django.utils.translation import ugettext as _, ugettext_lazy @@ -376,7 +376,7 @@ class CustomRadioInput(RadioChoiceInput): label_for = ' for="%s"' % self.attrs["id"] else: label_for = "" - choice_label = conditional_escape(force_unicode(self.choice_label)) + choice_label = conditional_escape(force_text(self.choice_label)) return mark_safe( u"" % (label_for, self.tag(), choice_label) @@ -394,7 +394,7 @@ class InlineRadioRenderer(RadioSelect.renderer): def render(self): return mark_safe( - u"\n".join([u"%s\n" % force_unicode(w) for w in self]) + u"\n".join([u"%s\n" % force_text(w) for w in self]) ) diff --git a/modoboa/lib/permissions.py b/modoboa/lib/permissions.py index 202dc9496..06bb09883 100644 --- a/modoboa/lib/permissions.py +++ b/modoboa/lib/permissions.py @@ -143,7 +143,7 @@ def get_object_owner(obj): def add_permissions_to_group(group, permissions): """Add the specified permissions to a django group.""" - if isinstance(group, basestring): + if isinstance(group, str): group = Group.objects.get(name=group) for appname, modelname, permname in permissions: diff --git a/modoboa/lib/tests.py b/modoboa/lib/tests.py index 7fe724c88..4e7822795 100644 --- a/modoboa/lib/tests.py +++ b/modoboa/lib/tests.py @@ -2,7 +2,6 @@ """Testing utilities.""" from builtins import object -import json from django.core import management from django.test import TestCase @@ -65,7 +64,7 @@ class ModoTestCase(ParametersMixin, TestCase): response = getattr(self.client, method)( url, params, HTTP_X_REQUESTED_WITH='XMLHttpRequest') self.assertEqual(response.status_code, status) - return json.loads(response.content) + return response.json() def ajax_post(self, *args, **kwargs): return self.ajax_request('post', *args, **kwargs) diff --git a/modoboa/lib/u2u_decode.py b/modoboa/lib/u2u_decode.py index f453f6768..cf4880c62 100644 --- a/modoboa/lib/u2u_decode.py +++ b/modoboa/lib/u2u_decode.py @@ -33,7 +33,7 @@ def decode_mime(m): def u2u_decode(s): - ur"""utility function for (final) decoding of mime header + """utility function for (final) decoding of mime header note: resulting string is in one line (no \n within) note2: spaces between enc_words are stripped (see RFC2047) diff --git a/modoboa/lib/validators.py b/modoboa/lib/validators.py index ee4cda012..e86adeed3 100644 --- a/modoboa/lib/validators.py +++ b/modoboa/lib/validators.py @@ -45,9 +45,9 @@ class UTF8EmailValidator(EmailValidator): ascii_set = u"-!#$%&'*+/=?^_`{}|~0-9A-Z" user_regex_raw = ( # dot-atom - ur"^[" + ascii_set + ul + "]+(\.[" + ascii_set + ul + "]+)*\Z" + u"^[" + ascii_set + ul + "]+(\.[" + ascii_set + ul + "]+)*\Z" # quoted-string - ur'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013\014\016-\177])*"\Z)' + u'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013\014\016-\177])*"\Z)' ) user_regex = re.compile("(" + user_regex_raw, re.IGNORECASE) @@ -59,6 +59,6 @@ class UTF8AndEmptyUserEmailValidator(UTF8EmailValidator): """Same as upper + allows empty local part.""" user_regex = re.compile( - ur"(^$|" + UTF8EmailValidator.user_regex_raw, re.IGNORECASE) + u"(^$|" + UTF8EmailValidator.user_regex_raw, re.IGNORECASE) validate_utf8_and_empty_user_email = UTF8AndEmptyUserEmailValidator() From 615f73f272363bc1e19ea8561215fa1a4b83bae7 Mon Sep 17 00:00:00 2001 From: Arthur Vuillard Date: Thu, 11 May 2017 09:25:42 +0200 Subject: [PATCH 10/23] unicode_literals everywhere and modoboa-admin deploy fixes --- bin/modoboa-admin.py | 2 ++ modoboa/__init__.py | 1 + modoboa/admin/__init__.py | 2 ++ modoboa/admin/api.py | 2 ++ modoboa/admin/app_settings.py | 2 ++ modoboa/admin/apps.py | 2 ++ modoboa/admin/constants.py | 2 ++ modoboa/admin/factories.py | 3 +++ modoboa/admin/forms/__init__.py | 2 ++ modoboa/admin/forms/account.py | 2 ++ modoboa/admin/forms/alias.py | 2 ++ modoboa/admin/forms/domain.py | 2 ++ modoboa/admin/forms/export.py | 2 ++ modoboa/admin/forms/forward.py | 2 ++ modoboa/admin/forms/import_.py | 2 ++ modoboa/admin/handlers.py | 2 ++ modoboa/admin/lib.py | 2 ++ modoboa/admin/management/__init__.py | 1 + modoboa/admin/management/commands/__init__.py | 1 + .../management/commands/handle_mailbox_operations.py | 2 +- modoboa/admin/management/commands/modo.py | 2 ++ .../admin/management/commands/subcommands/__init__.py | 1 + modoboa/admin/management/commands/subcommands/_export.py | 2 ++ modoboa/admin/management/commands/subcommands/_import.py | 2 ++ modoboa/admin/management/commands/subcommands/_mx.py | 2 +- modoboa/admin/management/commands/subcommands/_repair.py | 2 +- modoboa/admin/migrations/__init__.py | 1 + modoboa/admin/models/__init__.py | 2 ++ modoboa/admin/models/alias.py | 2 ++ modoboa/admin/models/base.py | 2 ++ modoboa/admin/models/domain.py | 2 ++ modoboa/admin/models/domain_alias.py | 2 ++ modoboa/admin/models/mailbox.py | 2 ++ modoboa/admin/postfix_maps.py | 2 ++ modoboa/admin/serializers.py | 2 ++ modoboa/admin/signals.py | 2 ++ modoboa/admin/templatetags/__init__.py | 1 + modoboa/admin/templatetags/admin_tags.py | 2 ++ modoboa/admin/tests/__init__.py | 1 + modoboa/admin/tests/test_account.py | 2 ++ modoboa/admin/tests/test_alias.py | 2 ++ modoboa/admin/tests/test_api.py | 2 ++ modoboa/admin/tests/test_domain.py | 2 ++ modoboa/admin/tests/test_domain_alias.py | 2 ++ modoboa/admin/tests/test_export.py | 2 ++ modoboa/admin/tests/test_import_.py | 3 +++ modoboa/admin/tests/test_mailbox_operations.py | 2 ++ modoboa/admin/tests/test_mapfiles.py | 2 ++ modoboa/admin/tests/test_mx.py | 2 ++ modoboa/admin/tests/test_password_schemes.py | 2 ++ modoboa/admin/tests/test_repair.py | 3 +++ modoboa/admin/tests/test_user.py | 2 ++ modoboa/admin/urls.py | 2 ++ modoboa/admin/urls_api.py | 2 ++ modoboa/admin/views/__init__.py | 2 ++ modoboa/admin/views/alias.py | 2 ++ modoboa/admin/views/dns.py | 2 ++ modoboa/admin/views/domain.py | 2 ++ modoboa/admin/views/export.py | 2 ++ modoboa/admin/views/identity.py | 2 ++ modoboa/admin/views/import_.py | 2 ++ modoboa/admin/views/user.py | 2 ++ modoboa/core/__init__.py | 2 ++ modoboa/core/app_settings.py | 3 +++ modoboa/core/apps.py | 2 ++ modoboa/core/commands/__init__.py | 9 +++++++-- modoboa/core/commands/deploy.py | 9 +++++++-- modoboa/core/commands/help.py | 2 +- modoboa/core/constants.py | 2 ++ modoboa/core/context_processors.py | 2 ++ modoboa/core/dev_settings.py | 3 +++ modoboa/core/extensions.py | 9 ++++++++- modoboa/core/factories.py | 2 ++ modoboa/core/forms.py | 2 ++ modoboa/core/handlers.py | 2 ++ modoboa/core/loggers.py | 2 ++ modoboa/core/management/__init__.py | 1 + modoboa/core/management/commands/__init__.py | 1 + modoboa/core/management/commands/cleanlogs.py | 2 +- .../management/commands/communicate_with_public_api.py | 2 ++ .../core/management/commands/generate_postfix_maps.py | 2 +- modoboa/core/management/commands/load_initial_data.py | 2 ++ modoboa/core/management/commands/set_default_site.py | 2 ++ modoboa/core/middleware.py | 2 ++ modoboa/core/migrations/__init__.py | 1 + modoboa/core/mocks.py | 2 ++ modoboa/core/models.py | 2 ++ modoboa/core/password_hashers/__init__.py | 3 +++ modoboa/core/password_hashers/advanced.py | 2 ++ modoboa/core/password_hashers/base.py | 2 ++ modoboa/core/signals.py | 2 ++ modoboa/core/templatetags/__init__.py | 1 + modoboa/core/templatetags/core_tags.py | 2 ++ modoboa/core/tests/__init__.py | 1 + modoboa/core/tests/stupid_extension_1/__init__.py | 1 + modoboa/core/tests/stupid_extension_1/modo_extension.py | 2 ++ modoboa/core/tests/stupid_extension_2/__init__.py | 1 + modoboa/core/tests/stupid_extension_2/modo_extension.py | 3 +++ modoboa/core/tests/stupid_extension_2/urls.py | 2 ++ modoboa/core/tests/stupid_extension_2/views.py | 2 ++ modoboa/core/tests/test_authentication.py | 2 ++ modoboa/core/tests/test_core.py | 2 ++ modoboa/core/tests/test_extensions.py | 3 ++- modoboa/core/tests/test_ldap.py | 2 ++ modoboa/core/tests/test_views.py | 2 ++ modoboa/core/urls.py | 2 ++ modoboa/core/utils.py | 2 ++ modoboa/core/views/__init__.py | 2 ++ modoboa/core/views/admin.py | 2 ++ modoboa/core/views/auth.py | 2 ++ modoboa/core/views/base.py | 2 ++ modoboa/core/views/dashboard.py | 2 ++ modoboa/core/views/user.py | 2 ++ modoboa/lib/__init__.py | 1 + modoboa/lib/api_client.py | 2 ++ modoboa/lib/authbackends.py | 2 ++ modoboa/lib/connections.py | 2 ++ modoboa/lib/db_utils.py | 2 ++ modoboa/lib/email_utils.py | 2 ++ modoboa/lib/exceptions.py | 3 +++ modoboa/lib/fields.py | 2 ++ modoboa/lib/imap_utf7.py | 2 +- modoboa/lib/ldap_utils.py | 2 ++ modoboa/lib/listing.py | 2 ++ modoboa/lib/middleware.py | 2 ++ modoboa/lib/migrations/__init__.py | 1 + modoboa/lib/paginator.py | 2 ++ modoboa/lib/signals.py | 2 ++ modoboa/lib/singleton.py | 2 +- modoboa/lib/sysutils.py | 3 +++ modoboa/lib/templatetags/__init__.py | 1 + modoboa/lib/templatetags/form_tags.py | 2 ++ modoboa/lib/templatetags/lib_tags.py | 2 ++ modoboa/lib/test_utils.py | 2 ++ modoboa/lib/tests.py | 2 ++ modoboa/lib/u2u_decode.py | 2 ++ modoboa/lib/validators.py | 2 ++ modoboa/lib/web_utils.py | 2 ++ modoboa/limits/__init__.py | 2 ++ modoboa/limits/app_settings.py | 2 ++ modoboa/limits/apps.py | 2 ++ modoboa/limits/constants.py | 2 ++ modoboa/limits/forms.py | 2 ++ modoboa/limits/handlers.py | 2 ++ modoboa/limits/lib.py | 3 +++ modoboa/limits/migrations/__init__.py | 1 + modoboa/limits/models.py | 2 ++ modoboa/limits/serializers.py | 2 ++ modoboa/limits/tests/__init__.py | 1 + modoboa/limits/tests/test_api.py | 2 ++ modoboa/limits/tests/test_domain_limits.py | 2 ++ modoboa/limits/tests/test_import.py | 2 ++ modoboa/limits/tests/test_user_limits.py | 2 ++ modoboa/limits/urls_api.py | 2 ++ modoboa/limits/utils.py | 2 ++ modoboa/limits/views.py | 2 ++ modoboa/limits/viewsets.py | 2 ++ modoboa/parameters/__init__.py | 1 + modoboa/parameters/forms.py | 2 ++ modoboa/parameters/tests.py | 2 ++ modoboa/parameters/tools.py | 2 ++ modoboa/relaydomains/__init__.py | 2 ++ modoboa/relaydomains/app_settings.py | 2 ++ modoboa/relaydomains/apps.py | 2 ++ modoboa/relaydomains/constants.py | 2 ++ modoboa/relaydomains/forms.py | 2 ++ modoboa/relaydomains/handlers.py | 2 ++ modoboa/relaydomains/lib.py | 2 ++ modoboa/relaydomains/migrations/__init__.py | 1 + modoboa/relaydomains/models.py | 2 ++ modoboa/relaydomains/postfix_maps.py | 2 ++ modoboa/relaydomains/tests.py | 2 ++ modoboa/relaydomains/urls.py | 2 ++ modoboa/relaydomains/views.py | 2 ++ modoboa/test_settings.py | 3 +++ modoboa/urls.py | 4 ++++ modoboa/urls_api.py | 2 ++ test_project/manage.py | 3 +++ test_project/test_project/__init__.py | 1 + test_project/test_project/settings.py | 2 ++ test_project/test_project/urls.py | 2 ++ test_project/test_project/wsgi.py | 2 ++ 182 files changed, 364 insertions(+), 14 deletions(-) diff --git a/bin/modoboa-admin.py b/bin/modoboa-admin.py index 9309b6cc8..a637e52c6 100644 --- a/bin/modoboa-admin.py +++ b/bin/modoboa-admin.py @@ -1,6 +1,8 @@ #!/usr/bin/env python # coding: utf-8 +from __future__ import unicode_literals + from modoboa.core.commands import handle_command_line if __name__ == "__main__": diff --git a/modoboa/__init__.py b/modoboa/__init__.py index e69de29bb..baffc4882 100644 --- a/modoboa/__init__.py +++ b/modoboa/__init__.py @@ -0,0 +1 @@ +from __future__ import unicode_literals diff --git a/modoboa/admin/__init__.py b/modoboa/admin/__init__.py index 4002eb9dd..5882e6d25 100644 --- a/modoboa/admin/__init__.py +++ b/modoboa/admin/__init__.py @@ -1 +1,3 @@ +from __future__ import unicode_literals + default_app_config = "modoboa.admin.apps.AdminConfig" diff --git a/modoboa/admin/api.py b/modoboa/admin/api.py index 27432a663..b4a5c491d 100644 --- a/modoboa/admin/api.py +++ b/modoboa/admin/api.py @@ -1,5 +1,7 @@ """Admin API.""" +from __future__ import unicode_literals + from django import http from django.contrib.contenttypes.models import ContentType diff --git a/modoboa/admin/app_settings.py b/modoboa/admin/app_settings.py index 433a21618..611601d66 100644 --- a/modoboa/admin/app_settings.py +++ b/modoboa/admin/app_settings.py @@ -1,5 +1,7 @@ """Modoboa admin settings.""" +from __future__ import unicode_literals + import os from django import forms diff --git a/modoboa/admin/apps.py b/modoboa/admin/apps.py index 281d2d5ba..f799fa177 100644 --- a/modoboa/admin/apps.py +++ b/modoboa/admin/apps.py @@ -1,5 +1,7 @@ """AppConfig for admin.""" +from __future__ import unicode_literals + from django.apps import AppConfig from .app_settings import load_admin_settings diff --git a/modoboa/admin/constants.py b/modoboa/admin/constants.py index 944da5e2f..7a78b2cc0 100644 --- a/modoboa/admin/constants.py +++ b/modoboa/admin/constants.py @@ -1,5 +1,7 @@ """Admin constants.""" +from __future__ import unicode_literals + DNSBL_PROVIDERS = [ "aspews.ext.sorbs.net", "b.barracudacentral.org", diff --git a/modoboa/admin/factories.py b/modoboa/admin/factories.py index ad7eb13a3..2aa5112c9 100644 --- a/modoboa/admin/factories.py +++ b/modoboa/admin/factories.py @@ -1,6 +1,9 @@ """ Fixtures factories. """ + +from __future__ import unicode_literals + import factory from modoboa.core.factories import PermissionFactory, UserFactory diff --git a/modoboa/admin/forms/__init__.py b/modoboa/admin/forms/__init__.py index dbb5a36ef..b7811a0d9 100644 --- a/modoboa/admin/forms/__init__.py +++ b/modoboa/admin/forms/__init__.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + from .domain import ( DomainFormGeneral, DomainFormOptions, DomainForm, DomainWizard ) diff --git a/modoboa/admin/forms/account.py b/modoboa/admin/forms/account.py index 1fa51628f..53e2ff5fe 100644 --- a/modoboa/admin/forms/account.py +++ b/modoboa/admin/forms/account.py @@ -1,5 +1,7 @@ """Forms related to accounts management.""" +from __future__ import unicode_literals + from collections import OrderedDict from functools import reduce diff --git a/modoboa/admin/forms/alias.py b/modoboa/admin/forms/alias.py index 6c7641145..12b306442 100644 --- a/modoboa/admin/forms/alias.py +++ b/modoboa/admin/forms/alias.py @@ -1,5 +1,7 @@ """Forms related to aliases management.""" +from __future__ import unicode_literals + from collections import OrderedDict from django import forms diff --git a/modoboa/admin/forms/domain.py b/modoboa/admin/forms/domain.py index 3dab2282a..a51a912b3 100644 --- a/modoboa/admin/forms/domain.py +++ b/modoboa/admin/forms/domain.py @@ -1,5 +1,7 @@ """Forms related to domains management.""" +from __future__ import unicode_literals + from functools import reduce from django import forms diff --git a/modoboa/admin/forms/export.py b/modoboa/admin/forms/export.py index f1f8a868b..049b451cb 100644 --- a/modoboa/admin/forms/export.py +++ b/modoboa/admin/forms/export.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + from django import forms from django.utils.translation import ugettext_lazy diff --git a/modoboa/admin/forms/forward.py b/modoboa/admin/forms/forward.py index 5cd7b1b7c..ddccba0f4 100644 --- a/modoboa/admin/forms/forward.py +++ b/modoboa/admin/forms/forward.py @@ -1,5 +1,7 @@ """Forms related to forwards management.""" +from __future__ import unicode_literals + from django import forms from django.core import validators from django.utils.translation import ugettext_lazy diff --git a/modoboa/admin/forms/import_.py b/modoboa/admin/forms/import_.py index 0091e4ba8..19fc7d360 100644 --- a/modoboa/admin/forms/import_.py +++ b/modoboa/admin/forms/import_.py @@ -1,5 +1,7 @@ """Forms related to import operations.""" +from __future__ import unicode_literals + from django import forms from django.utils.translation import ugettext_lazy diff --git a/modoboa/admin/handlers.py b/modoboa/admin/handlers.py index afdb82ee8..ff9d7c954 100644 --- a/modoboa/admin/handlers.py +++ b/modoboa/admin/handlers.py @@ -1,5 +1,7 @@ """Django signal handlers for admin.""" +from __future__ import unicode_literals + from django.core.urlresolvers import reverse from django.db.models import signals from django.dispatch import receiver diff --git a/modoboa/admin/lib.py b/modoboa/admin/lib.py index 368757300..4ad7a9d67 100644 --- a/modoboa/admin/lib.py +++ b/modoboa/admin/lib.py @@ -2,6 +2,8 @@ """Internal library for admin.""" +from __future__ import unicode_literals + from functools import wraps from itertools import chain diff --git a/modoboa/admin/management/__init__.py b/modoboa/admin/management/__init__.py index e69de29bb..baffc4882 100644 --- a/modoboa/admin/management/__init__.py +++ b/modoboa/admin/management/__init__.py @@ -0,0 +1 @@ +from __future__ import unicode_literals diff --git a/modoboa/admin/management/commands/__init__.py b/modoboa/admin/management/commands/__init__.py index e69de29bb..baffc4882 100644 --- a/modoboa/admin/management/commands/__init__.py +++ b/modoboa/admin/management/commands/__init__.py @@ -0,0 +1 @@ +from __future__ import unicode_literals diff --git a/modoboa/admin/management/commands/handle_mailbox_operations.py b/modoboa/admin/management/commands/handle_mailbox_operations.py index 1632a1a3a..8888cb9eb 100644 --- a/modoboa/admin/management/commands/handle_mailbox_operations.py +++ b/modoboa/admin/management/commands/handle_mailbox_operations.py @@ -1,6 +1,6 @@ """A management command to apply mailbox operations.""" -from __future__ import print_function +from __future__ import unicode_literals, print_function import logging import os diff --git a/modoboa/admin/management/commands/modo.py b/modoboa/admin/management/commands/modo.py index 4205f2e28..5088ace23 100644 --- a/modoboa/admin/management/commands/modo.py +++ b/modoboa/admin/management/commands/modo.py @@ -1,5 +1,7 @@ """Modoboa main management command.""" +from __future__ import unicode_literals + from subcommand.base import SubcommandCommand from .subcommands._mx import CheckMXRecords diff --git a/modoboa/admin/management/commands/subcommands/__init__.py b/modoboa/admin/management/commands/subcommands/__init__.py index e69de29bb..baffc4882 100644 --- a/modoboa/admin/management/commands/subcommands/__init__.py +++ b/modoboa/admin/management/commands/subcommands/__init__.py @@ -0,0 +1 @@ +from __future__ import unicode_literals diff --git a/modoboa/admin/management/commands/subcommands/_export.py b/modoboa/admin/management/commands/subcommands/_export.py index 4786e3462..6a5f0c121 100644 --- a/modoboa/admin/management/commands/subcommands/_export.py +++ b/modoboa/admin/management/commands/subcommands/_export.py @@ -1,5 +1,7 @@ """Django management command to export admin objects.""" +from __future__ import unicode_literals + import csv import sys diff --git a/modoboa/admin/management/commands/subcommands/_import.py b/modoboa/admin/management/commands/subcommands/_import.py index b5fb3c49c..8e199eabf 100644 --- a/modoboa/admin/management/commands/subcommands/_import.py +++ b/modoboa/admin/management/commands/subcommands/_import.py @@ -1,5 +1,7 @@ """Django management command to import admin objects.""" +from __future__ import unicode_literals + import csv import os diff --git a/modoboa/admin/management/commands/subcommands/_mx.py b/modoboa/admin/management/commands/subcommands/_mx.py index 4ee1e5ba9..740f514d4 100644 --- a/modoboa/admin/management/commands/subcommands/_mx.py +++ b/modoboa/admin/management/commands/subcommands/_mx.py @@ -1,6 +1,6 @@ """Management command to check defined domains.""" -from __future__ import print_function +from __future__ import print_function, unicode_literals from datetime import timedelta import ipaddress diff --git a/modoboa/admin/management/commands/subcommands/_repair.py b/modoboa/admin/management/commands/subcommands/_repair.py index 27cc9705d..b7198bbd6 100644 --- a/modoboa/admin/management/commands/subcommands/_repair.py +++ b/modoboa/admin/management/commands/subcommands/_repair.py @@ -1,6 +1,6 @@ """Management command to check and fix known problems.""" -from __future__ import print_function +from __future__ import print_function, unicode_literals from django.conf import settings from django.core.management.base import BaseCommand diff --git a/modoboa/admin/migrations/__init__.py b/modoboa/admin/migrations/__init__.py index e69de29bb..baffc4882 100644 --- a/modoboa/admin/migrations/__init__.py +++ b/modoboa/admin/migrations/__init__.py @@ -0,0 +1 @@ +from __future__ import unicode_literals diff --git a/modoboa/admin/models/__init__.py b/modoboa/admin/models/__init__.py index 07a1569ea..35e093302 100644 --- a/modoboa/admin/models/__init__.py +++ b/modoboa/admin/models/__init__.py @@ -1,5 +1,7 @@ """Admin models.""" +from __future__ import unicode_literals + from .base import AdminObject from .domain import Domain, MXRecord, DNSBLResult from .domain_alias import DomainAlias diff --git a/modoboa/admin/models/alias.py b/modoboa/admin/models/alias.py index 0c3223d18..81274b538 100644 --- a/modoboa/admin/models/alias.py +++ b/modoboa/admin/models/alias.py @@ -1,5 +1,7 @@ """Models related to aliases management.""" +from __future__ import unicode_literals + import hashlib import random diff --git a/modoboa/admin/models/base.py b/modoboa/admin/models/base.py index b58ab912a..f3fb46848 100644 --- a/modoboa/admin/models/base.py +++ b/modoboa/admin/models/base.py @@ -1,5 +1,7 @@ """Base admin models.""" +from __future__ import unicode_literals + from django.db import models from django.utils import timezone diff --git a/modoboa/admin/models/domain.py b/modoboa/admin/models/domain.py index c894b9e01..a521418b7 100644 --- a/modoboa/admin/models/domain.py +++ b/modoboa/admin/models/domain.py @@ -1,5 +1,7 @@ """Models related to domains management.""" +from __future__ import unicode_literals + import datetime from functools import reduce diff --git a/modoboa/admin/models/domain_alias.py b/modoboa/admin/models/domain_alias.py index 516be06fc..c9047886d 100644 --- a/modoboa/admin/models/domain_alias.py +++ b/modoboa/admin/models/domain_alias.py @@ -1,5 +1,7 @@ """Models related to domain aliases management.""" +from __future__ import unicode_literals + from django.db import models from django.utils.encoding import python_2_unicode_compatible, smart_text from django.utils.translation import ugettext as _, ugettext_lazy diff --git a/modoboa/admin/models/mailbox.py b/modoboa/admin/models/mailbox.py index b527ab0d0..bd50a3363 100644 --- a/modoboa/admin/models/mailbox.py +++ b/modoboa/admin/models/mailbox.py @@ -1,5 +1,7 @@ """Models related to mailboxes management.""" +from __future__ import unicode_literals + import os import pwd diff --git a/modoboa/admin/postfix_maps.py b/modoboa/admin/postfix_maps.py index a6e5a4c4a..1af7112fa 100644 --- a/modoboa/admin/postfix_maps.py +++ b/modoboa/admin/postfix_maps.py @@ -1,5 +1,7 @@ """Map file definitions for postfix.""" +from __future__ import unicode_literals + class DomainsMap(object): diff --git a/modoboa/admin/serializers.py b/modoboa/admin/serializers.py index 5a0608874..3827ec139 100644 --- a/modoboa/admin/serializers.py +++ b/modoboa/admin/serializers.py @@ -1,5 +1,7 @@ """Admin serializers.""" +from __future__ import unicode_literals + from django.core.exceptions import ValidationError from django.shortcuts import get_object_or_404 from django.utils.translation import ugettext as _, ugettext_lazy diff --git a/modoboa/admin/signals.py b/modoboa/admin/signals.py index c915f0c18..79f2ed116 100644 --- a/modoboa/admin/signals.py +++ b/modoboa/admin/signals.py @@ -1,5 +1,7 @@ """Modoboa admin signals.""" +from __future__ import unicode_literals + import django.dispatch check_extra_account_form = django.dispatch.Signal( diff --git a/modoboa/admin/templatetags/__init__.py b/modoboa/admin/templatetags/__init__.py index e69de29bb..baffc4882 100644 --- a/modoboa/admin/templatetags/__init__.py +++ b/modoboa/admin/templatetags/__init__.py @@ -0,0 +1 @@ +from __future__ import unicode_literals diff --git a/modoboa/admin/templatetags/admin_tags.py b/modoboa/admin/templatetags/admin_tags.py index af301a446..60285fa0a 100644 --- a/modoboa/admin/templatetags/admin_tags.py +++ b/modoboa/admin/templatetags/admin_tags.py @@ -1,5 +1,7 @@ """Admin extension tags.""" +from __future__ import unicode_literals + from functools import reduce from django import template diff --git a/modoboa/admin/tests/__init__.py b/modoboa/admin/tests/__init__.py index e69de29bb..baffc4882 100644 --- a/modoboa/admin/tests/__init__.py +++ b/modoboa/admin/tests/__init__.py @@ -0,0 +1 @@ +from __future__ import unicode_literals diff --git a/modoboa/admin/tests/test_account.py b/modoboa/admin/tests/test_account.py index b2ae52cdc..86ad607d5 100644 --- a/modoboa/admin/tests/test_account.py +++ b/modoboa/admin/tests/test_account.py @@ -1,5 +1,7 @@ # coding: utf-8 +from __future__ import unicode_literals + from unittest import skipIf from django.core.urlresolvers import reverse diff --git a/modoboa/admin/tests/test_alias.py b/modoboa/admin/tests/test_alias.py index 4ad07d09d..62dc19bd2 100644 --- a/modoboa/admin/tests/test_alias.py +++ b/modoboa/admin/tests/test_alias.py @@ -1,5 +1,7 @@ # coding: utf-8 +from __future__ import unicode_literals + """Admin test cases.""" from django.core.urlresolvers import reverse diff --git a/modoboa/admin/tests/test_api.py b/modoboa/admin/tests/test_api.py index 8a824b08b..2b35c54e4 100644 --- a/modoboa/admin/tests/test_api.py +++ b/modoboa/admin/tests/test_api.py @@ -1,6 +1,8 @@ # coding: utf-8 """Admin API related tests.""" +from __future__ import unicode_literals + import copy import json diff --git a/modoboa/admin/tests/test_domain.py b/modoboa/admin/tests/test_domain.py index e71368f4f..907472906 100644 --- a/modoboa/admin/tests/test_domain.py +++ b/modoboa/admin/tests/test_domain.py @@ -1,5 +1,7 @@ # coding: utf-8 +from __future__ import unicode_literals + """Domain related test cases.""" import json diff --git a/modoboa/admin/tests/test_domain_alias.py b/modoboa/admin/tests/test_domain_alias.py index f5f21efcc..76f92fb16 100644 --- a/modoboa/admin/tests/test_domain_alias.py +++ b/modoboa/admin/tests/test_domain_alias.py @@ -1,5 +1,7 @@ # coding: utf-8 +from __future__ import unicode_literals + from django.core.urlresolvers import reverse from modoboa.lib.tests import ModoTestCase diff --git a/modoboa/admin/tests/test_export.py b/modoboa/admin/tests/test_export.py index f35eb8a9f..1b47bb906 100644 --- a/modoboa/admin/tests/test_export.py +++ b/modoboa/admin/tests/test_export.py @@ -1,5 +1,7 @@ """Export related test cases.""" +from __future__ import unicode_literals + from django.core.urlresolvers import reverse from modoboa.lib.tests import ModoTestCase diff --git a/modoboa/admin/tests/test_import_.py b/modoboa/admin/tests/test_import_.py index d468144f6..c495ea61a 100644 --- a/modoboa/admin/tests/test_import_.py +++ b/modoboa/admin/tests/test_import_.py @@ -1,4 +1,7 @@ # coding: utf-8 + +from __future__ import unicode_literals + from django.core.files.base import ContentFile from django.core.urlresolvers import reverse diff --git a/modoboa/admin/tests/test_mailbox_operations.py b/modoboa/admin/tests/test_mailbox_operations.py index d27d99233..bfcf9491e 100644 --- a/modoboa/admin/tests/test_mailbox_operations.py +++ b/modoboa/admin/tests/test_mailbox_operations.py @@ -1,5 +1,7 @@ """Management command tests.""" +from __future__ import unicode_literals + import os import shutil import tempfile diff --git a/modoboa/admin/tests/test_mapfiles.py b/modoboa/admin/tests/test_mapfiles.py index 255968353..ff0bd2ba1 100644 --- a/modoboa/admin/tests/test_mapfiles.py +++ b/modoboa/admin/tests/test_mapfiles.py @@ -1,5 +1,7 @@ """Test map files generation.""" +from __future__ import unicode_literals + import os from django.conf import settings diff --git a/modoboa/admin/tests/test_mx.py b/modoboa/admin/tests/test_mx.py index 1aa59656e..aa455f500 100644 --- a/modoboa/admin/tests/test_mx.py +++ b/modoboa/admin/tests/test_mx.py @@ -1,5 +1,7 @@ """DNSBL related tests.""" +from __future__ import unicode_literals + from django.core import management from django.core.urlresolvers import reverse from django.test import override_settings diff --git a/modoboa/admin/tests/test_password_schemes.py b/modoboa/admin/tests/test_password_schemes.py index 113c5d90b..160d1e0b2 100644 --- a/modoboa/admin/tests/test_password_schemes.py +++ b/modoboa/admin/tests/test_password_schemes.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + from django.core.urlresolvers import reverse from modoboa.core.models import User diff --git a/modoboa/admin/tests/test_repair.py b/modoboa/admin/tests/test_repair.py index f18178f3f..aa4ce47fd 100644 --- a/modoboa/admin/tests/test_repair.py +++ b/modoboa/admin/tests/test_repair.py @@ -1,4 +1,7 @@ """Repair command tests""" + +from __future__ import unicode_literals + from django.core import management from modoboa.lib.permissions import get_object_owner diff --git a/modoboa/admin/tests/test_user.py b/modoboa/admin/tests/test_user.py index 25eebb8c5..65a6efc06 100644 --- a/modoboa/admin/tests/test_user.py +++ b/modoboa/admin/tests/test_user.py @@ -1,5 +1,7 @@ """User related tests.""" +from __future__ import unicode_literals + import json from django.core.urlresolvers import reverse diff --git a/modoboa/admin/urls.py b/modoboa/admin/urls.py index 1934ced58..63958d202 100644 --- a/modoboa/admin/urls.py +++ b/modoboa/admin/urls.py @@ -1,6 +1,8 @@ # pylint: disable=invalid-name, protected-access """Admin urls.""" +from __future__ import unicode_literals + from django.conf.urls import url from . import views diff --git a/modoboa/admin/urls_api.py b/modoboa/admin/urls_api.py index 3c1051aed..1f0f9f0d4 100644 --- a/modoboa/admin/urls_api.py +++ b/modoboa/admin/urls_api.py @@ -1,5 +1,7 @@ """Admin API urls.""" +from __future__ import unicode_literals + from rest_framework import routers from . import api diff --git a/modoboa/admin/views/__init__.py b/modoboa/admin/views/__init__.py index e63b5b5e1..8c499c185 100644 --- a/modoboa/admin/views/__init__.py +++ b/modoboa/admin/views/__init__.py @@ -1,5 +1,7 @@ """Shortcuts.""" +from __future__ import unicode_literals + from .alias import AliasDetailView from .identity import AccountDetailView from .dns import DNSBLDomainDetailView, MXDomainDetailView diff --git a/modoboa/admin/views/alias.py b/modoboa/admin/views/alias.py index 1ec4aaf23..20b1c4635 100644 --- a/modoboa/admin/views/alias.py +++ b/modoboa/admin/views/alias.py @@ -1,5 +1,7 @@ """Alias related views.""" +from __future__ import unicode_literals + from django.core.urlresolvers import reverse from django.db import IntegrityError from django.shortcuts import render diff --git a/modoboa/admin/views/dns.py b/modoboa/admin/views/dns.py index c7381d0a6..591f3f3e5 100644 --- a/modoboa/admin/views/dns.py +++ b/modoboa/admin/views/dns.py @@ -1,5 +1,7 @@ """DNSBL related views.""" +from __future__ import unicode_literals + from django.views import generic from django.utils.translation import ugettext as _ diff --git a/modoboa/admin/views/domain.py b/modoboa/admin/views/domain.py index 26c6a9126..39eba12f2 100644 --- a/modoboa/admin/views/domain.py +++ b/modoboa/admin/views/domain.py @@ -1,5 +1,7 @@ """Domain related views.""" +from __future__ import unicode_literals + from functools import reduce from django.core.urlresolvers import reverse diff --git a/modoboa/admin/views/export.py b/modoboa/admin/views/export.py index a9c78920e..57edc87bf 100644 --- a/modoboa/admin/views/export.py +++ b/modoboa/admin/views/export.py @@ -1,5 +1,7 @@ """Export related views.""" +from __future__ import unicode_literals + import csv import io diff --git a/modoboa/admin/views/identity.py b/modoboa/admin/views/identity.py index b77773c5b..a38bc430d 100644 --- a/modoboa/admin/views/identity.py +++ b/modoboa/admin/views/identity.py @@ -1,5 +1,7 @@ """Identity related views.""" +from __future__ import unicode_literals + from django.shortcuts import render from django.template.loader import render_to_string from django.utils.translation import ugettext as _, ungettext diff --git a/modoboa/admin/views/import_.py b/modoboa/admin/views/import_.py index bf749f78f..1bf5f78d2 100644 --- a/modoboa/admin/views/import_.py +++ b/modoboa/admin/views/import_.py @@ -1,5 +1,7 @@ """Import related views.""" +from __future__ import unicode_literals + import csv from django.contrib.auth.decorators import ( diff --git a/modoboa/admin/views/user.py b/modoboa/admin/views/user.py index 07af14f4e..424111919 100644 --- a/modoboa/admin/views/user.py +++ b/modoboa/admin/views/user.py @@ -1,5 +1,7 @@ """SimpleUsers views.""" +from __future__ import unicode_literals + from django.template.loader import render_to_string from django.utils.translation import ugettext as _ diff --git a/modoboa/core/__init__.py b/modoboa/core/__init__.py index 0f5234285..c9be3cc2e 100644 --- a/modoboa/core/__init__.py +++ b/modoboa/core/__init__.py @@ -1,3 +1,5 @@ """App config for core.""" +from __future__ import unicode_literals + default_app_config = "modoboa.core.apps.CoreConfig" diff --git a/modoboa/core/app_settings.py b/modoboa/core/app_settings.py index b0f9b9f43..47742a503 100644 --- a/modoboa/core/app_settings.py +++ b/modoboa/core/app_settings.py @@ -1,4 +1,7 @@ # coding: utf-8 + +from __future__ import unicode_literals + from django import forms from django.conf import settings from django.utils.translation import ugettext as _, ugettext_lazy diff --git a/modoboa/core/apps.py b/modoboa/core/apps.py index a3d6e5dab..d676eb44a 100644 --- a/modoboa/core/apps.py +++ b/modoboa/core/apps.py @@ -1,5 +1,7 @@ """Core config for admin.""" +from __future__ import unicode_literals + from django.apps import AppConfig from django.db.models import signals from django.utils.translation import ugettext_lazy diff --git a/modoboa/core/commands/__init__.py b/modoboa/core/commands/__init__.py index 1a53c3120..a45b85a9d 100644 --- a/modoboa/core/commands/__init__.py +++ b/modoboa/core/commands/__init__.py @@ -1,6 +1,6 @@ # coding: utf-8 -from __future__ import print_function +from __future__ import unicode_literals, print_function import argparse import os @@ -8,6 +8,7 @@ import sys from django.conf import settings from django.template import Context, Template +from django.utils import six class Command(object): @@ -65,8 +66,12 @@ def scan_for_commands(dirname=""): if os.path.isfile(f): continue cmdname = f.replace(".py", "") + if six.PY2: + fromlist = [cmdname.encode()] + else: + fromlist = [cmdname] cmdmod = __import__("modoboa.core.commands", globals(), locals(), - [cmdname]) + fromlist) cmdmod = getattr(cmdmod, cmdname) if "_" in cmdname: cmdclassname = "".join( diff --git a/modoboa/core/commands/deploy.py b/modoboa/core/commands/deploy.py index 61eb4eab8..6f07ef462 100644 --- a/modoboa/core/commands/deploy.py +++ b/modoboa/core/commands/deploy.py @@ -2,7 +2,7 @@ """A shortcut to deploy a fresh modoboa instance.""" -from __future__ import print_function +from __future__ import unicode_literals, print_function import getpass import os @@ -21,6 +21,7 @@ except ImportError: import django from django.core import management from django.template import Context, Template +from django.utils import six import dj_database_url from modoboa.core.commands import Command @@ -231,7 +232,11 @@ class DeployCommand(Command): os.path.join(os.path.dirname(__file__), "../../bower_components") ) - mod = __import__(parsed_args.name, globals(), locals(), ['settings']) + if six.PY2: + fromlist = ['settings'.encode()] + else: + fromlist = ['settings'] + mod = __import__(parsed_args.name, globals(), locals(), fromlist) tpl = self._render_template( "%s/settings.py.tpl" % self._templates_dir, { 'db_connections': connections, diff --git a/modoboa/core/commands/help.py b/modoboa/core/commands/help.py index adcac4887..88c7efc0b 100644 --- a/modoboa/core/commands/help.py +++ b/modoboa/core/commands/help.py @@ -1,6 +1,6 @@ # coding: utf-8 -from __future__ import print_function +from __future__ import unicode_literals, print_function import sys diff --git a/modoboa/core/constants.py b/modoboa/core/constants.py index 6a9d2be6e..a024e5fa3 100644 --- a/modoboa/core/constants.py +++ b/modoboa/core/constants.py @@ -2,6 +2,8 @@ """Core application constants.""" +from __future__ import unicode_literals + from django.utils.translation import ugettext_lazy SIMPLEUSERS_ROLE = ("SimpleUsers", ugettext_lazy("Simple user")) diff --git a/modoboa/core/context_processors.py b/modoboa/core/context_processors.py index 3bd8a3c92..72d4ba435 100644 --- a/modoboa/core/context_processors.py +++ b/modoboa/core/context_processors.py @@ -1,5 +1,7 @@ """Custom context processors.""" +from __future__ import unicode_literals + from functools import reduce from . import signals diff --git a/modoboa/core/dev_settings.py b/modoboa/core/dev_settings.py index ed1a3b6cb..cbc4b1b54 100644 --- a/modoboa/core/dev_settings.py +++ b/modoboa/core/dev_settings.py @@ -1,4 +1,7 @@ # Development settings + +from __future__ import unicode_literals + import os BOWER_COMPONENTS_ROOT = os.path.join( diff --git a/modoboa/core/extensions.py b/modoboa/core/extensions.py index 3df7f0fea..5b4732204 100644 --- a/modoboa/core/extensions.py +++ b/modoboa/core/extensions.py @@ -1,7 +1,10 @@ """Extension management.""" +from __future__ import unicode_literals + from django.conf import settings from django.conf.urls import include, url +from django.utils import six class ModoExtension(object): @@ -76,7 +79,11 @@ class ExtensionsPool(object): def load_extension(self, name): """Load a registered extension.""" - __import__(name, locals(), globals(), ["modo_extension"]) + if six.PY2: + fromlist = ["modo_extension".encode()] + else: + fromlist = ["modo_extension"] + __import__(name, locals(), globals(), fromlist) extinstance = self.get_extension(name) if extinstance is None: return None diff --git a/modoboa/core/factories.py b/modoboa/core/factories.py index c375d09c5..9624ab0d5 100644 --- a/modoboa/core/factories.py +++ b/modoboa/core/factories.py @@ -1,5 +1,7 @@ """Factories for core application.""" +from __future__ import unicode_literals + import factory from django.contrib.auth.models import Group diff --git a/modoboa/core/forms.py b/modoboa/core/forms.py index 26dd71847..c2a021f1f 100644 --- a/modoboa/core/forms.py +++ b/modoboa/core/forms.py @@ -2,6 +2,8 @@ """Core forms.""" +from __future__ import unicode_literals + from django import forms from django.utils.translation import ugettext as _, ugettext_lazy diff --git a/modoboa/core/handlers.py b/modoboa/core/handlers.py index 9b904de36..a953cdc20 100644 --- a/modoboa/core/handlers.py +++ b/modoboa/core/handlers.py @@ -1,5 +1,7 @@ """Modoboa core signal handlers.""" +from __future__ import unicode_literals + import logging from django.core.urlresolvers import reverse diff --git a/modoboa/core/loggers.py b/modoboa/core/loggers.py index 35efe0397..c6dd24a05 100644 --- a/modoboa/core/loggers.py +++ b/modoboa/core/loggers.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + import logging diff --git a/modoboa/core/management/__init__.py b/modoboa/core/management/__init__.py index e69de29bb..baffc4882 100644 --- a/modoboa/core/management/__init__.py +++ b/modoboa/core/management/__init__.py @@ -0,0 +1 @@ +from __future__ import unicode_literals diff --git a/modoboa/core/management/commands/__init__.py b/modoboa/core/management/commands/__init__.py index e69de29bb..baffc4882 100644 --- a/modoboa/core/management/commands/__init__.py +++ b/modoboa/core/management/commands/__init__.py @@ -0,0 +1 @@ +from __future__ import unicode_literals diff --git a/modoboa/core/management/commands/cleanlogs.py b/modoboa/core/management/commands/cleanlogs.py index 16c0e0b60..1513819c3 100644 --- a/modoboa/core/management/commands/cleanlogs.py +++ b/modoboa/core/management/commands/cleanlogs.py @@ -1,6 +1,6 @@ # coding: utf-8 -from __future__ import print_function +from __future__ import print_function, unicode_literals import datetime import logging diff --git a/modoboa/core/management/commands/communicate_with_public_api.py b/modoboa/core/management/commands/communicate_with_public_api.py index dcd003a54..39dbdde28 100644 --- a/modoboa/core/management/commands/communicate_with_public_api.py +++ b/modoboa/core/management/commands/communicate_with_public_api.py @@ -1,5 +1,7 @@ """Management command to communicate with public API.""" +from __future__ import unicode_literals + from django.core.management.base import BaseCommand, CommandError from modoboa.admin import models as admin_models diff --git a/modoboa/core/management/commands/generate_postfix_maps.py b/modoboa/core/management/commands/generate_postfix_maps.py index fbce3dc97..5fc15510f 100644 --- a/modoboa/core/management/commands/generate_postfix_maps.py +++ b/modoboa/core/management/commands/generate_postfix_maps.py @@ -1,5 +1,5 @@ """Management command to generate/update postfix map files.""" -from __future__ import print_function +from __future__ import print_function, unicode_literals import copy import hashlib diff --git a/modoboa/core/management/commands/load_initial_data.py b/modoboa/core/management/commands/load_initial_data.py index c3d82064d..5c746481d 100644 --- a/modoboa/core/management/commands/load_initial_data.py +++ b/modoboa/core/management/commands/load_initial_data.py @@ -5,6 +5,8 @@ """ +from __future__ import unicode_literals + from functools import reduce from django.contrib.auth.models import Group diff --git a/modoboa/core/management/commands/set_default_site.py b/modoboa/core/management/commands/set_default_site.py index 78db35a1f..d80268a10 100644 --- a/modoboa/core/management/commands/set_default_site.py +++ b/modoboa/core/management/commands/set_default_site.py @@ -5,6 +5,8 @@ A simple managemenent command to update the default site. See `https://docs.djangoproject.com/en/dev/ref/contrib/sites/`_. """ +from __future__ import unicode_literals + from django.core.management.base import BaseCommand, CommandError from django.contrib.sites.models import Site diff --git a/modoboa/core/middleware.py b/modoboa/core/middleware.py index c1463be56..f8f66b403 100644 --- a/modoboa/core/middleware.py +++ b/modoboa/core/middleware.py @@ -1,5 +1,7 @@ """Core middlewares.""" +from __future__ import unicode_literals + from . import models diff --git a/modoboa/core/migrations/__init__.py b/modoboa/core/migrations/__init__.py index e69de29bb..baffc4882 100644 --- a/modoboa/core/migrations/__init__.py +++ b/modoboa/core/migrations/__init__.py @@ -0,0 +1 @@ +from __future__ import unicode_literals diff --git a/modoboa/core/mocks.py b/modoboa/core/mocks.py index 7c8f245f7..e593bfc3f 100644 --- a/modoboa/core/mocks.py +++ b/modoboa/core/mocks.py @@ -1,5 +1,7 @@ """Mocks used for testing.""" +from __future__ import unicode_literals + import httmock diff --git a/modoboa/core/models.py b/modoboa/core/models.py index 66b1ea954..928c0c873 100644 --- a/modoboa/core/models.py +++ b/modoboa/core/models.py @@ -1,5 +1,7 @@ """Core models.""" +from __future__ import unicode_literals + from email.header import Header import re diff --git a/modoboa/core/password_hashers/__init__.py b/modoboa/core/password_hashers/__init__.py index 653b0dd56..6269c666c 100644 --- a/modoboa/core/password_hashers/__init__.py +++ b/modoboa/core/password_hashers/__init__.py @@ -1,6 +1,9 @@ """ Password hashers for Modoboa. """ + +from __future__ import unicode_literals + from modoboa.core.password_hashers.base import ( PLAINHasher, CRYPTHasher, MD5Hasher, SHA256Hasher ) diff --git a/modoboa/core/password_hashers/advanced.py b/modoboa/core/password_hashers/advanced.py index c14245e29..ad27ae865 100644 --- a/modoboa/core/password_hashers/advanced.py +++ b/modoboa/core/password_hashers/advanced.py @@ -4,6 +4,8 @@ Advanced (ie. stronger) password hashers. This module relies on `passlib` to provide more secure hashers. """ +from __future__ import unicode_literals + from passlib.hash import bcrypt, md5_crypt, sha256_crypt, sha512_crypt from modoboa.parameters import tools as param_tools diff --git a/modoboa/core/password_hashers/base.py b/modoboa/core/password_hashers/base.py index b9050f4ca..3f4b0c8d6 100644 --- a/modoboa/core/password_hashers/base.py +++ b/modoboa/core/password_hashers/base.py @@ -4,6 +4,8 @@ Base password hashers. Contains weak hashers (the original ones) available with Modoboa. """ +from __future__ import unicode_literals + import base64 import crypt import hashlib diff --git a/modoboa/core/signals.py b/modoboa/core/signals.py index 409dc2e0b..56b640ec9 100644 --- a/modoboa/core/signals.py +++ b/modoboa/core/signals.py @@ -1,5 +1,7 @@ """Modoboa core signals.""" +from __future__ import unicode_literals + import django.dispatch account_auto_created = django.dispatch.Signal(providing_args=["user"]) diff --git a/modoboa/core/templatetags/__init__.py b/modoboa/core/templatetags/__init__.py index e69de29bb..baffc4882 100644 --- a/modoboa/core/templatetags/__init__.py +++ b/modoboa/core/templatetags/__init__.py @@ -0,0 +1 @@ +from __future__ import unicode_literals diff --git a/modoboa/core/templatetags/core_tags.py b/modoboa/core/templatetags/core_tags.py index 70b32fc4a..44d0735a4 100644 --- a/modoboa/core/templatetags/core_tags.py +++ b/modoboa/core/templatetags/core_tags.py @@ -1,6 +1,8 @@ # coding: utf-8 """Custom tags for Core application.""" +from __future__ import unicode_literals + from functools import reduce import os import pkg_resources diff --git a/modoboa/core/tests/__init__.py b/modoboa/core/tests/__init__.py index e69de29bb..baffc4882 100644 --- a/modoboa/core/tests/__init__.py +++ b/modoboa/core/tests/__init__.py @@ -0,0 +1 @@ +from __future__ import unicode_literals diff --git a/modoboa/core/tests/stupid_extension_1/__init__.py b/modoboa/core/tests/stupid_extension_1/__init__.py index e69de29bb..baffc4882 100644 --- a/modoboa/core/tests/stupid_extension_1/__init__.py +++ b/modoboa/core/tests/stupid_extension_1/__init__.py @@ -0,0 +1 @@ +from __future__ import unicode_literals diff --git a/modoboa/core/tests/stupid_extension_1/modo_extension.py b/modoboa/core/tests/stupid_extension_1/modo_extension.py index 4cc7f4121..4115447d0 100644 --- a/modoboa/core/tests/stupid_extension_1/modo_extension.py +++ b/modoboa/core/tests/stupid_extension_1/modo_extension.py @@ -1,5 +1,7 @@ """A stupid extension used for tests.""" +from __future__ import unicode_literals + from modoboa.core import extensions diff --git a/modoboa/core/tests/stupid_extension_2/__init__.py b/modoboa/core/tests/stupid_extension_2/__init__.py index e69de29bb..baffc4882 100644 --- a/modoboa/core/tests/stupid_extension_2/__init__.py +++ b/modoboa/core/tests/stupid_extension_2/__init__.py @@ -0,0 +1 @@ +from __future__ import unicode_literals diff --git a/modoboa/core/tests/stupid_extension_2/modo_extension.py b/modoboa/core/tests/stupid_extension_2/modo_extension.py index 47824e924..e2adace26 100644 --- a/modoboa/core/tests/stupid_extension_2/modo_extension.py +++ b/modoboa/core/tests/stupid_extension_2/modo_extension.py @@ -1,5 +1,7 @@ """A stupid extension used for tests.""" +from __future__ import unicode_literals + from modoboa.core import extensions @@ -11,4 +13,5 @@ class StupidExtension2(extensions.ModoExtension): version = "1.0.0" description = "A stupid extension" + extensions.exts_pool.register_extension(StupidExtension2, show=False) diff --git a/modoboa/core/tests/stupid_extension_2/urls.py b/modoboa/core/tests/stupid_extension_2/urls.py index a7996a92b..43f39f251 100644 --- a/modoboa/core/tests/stupid_extension_2/urls.py +++ b/modoboa/core/tests/stupid_extension_2/urls.py @@ -1,5 +1,7 @@ """Custom urls.""" +from __future__ import unicode_literals + from django.conf.urls import url from . import views diff --git a/modoboa/core/tests/stupid_extension_2/views.py b/modoboa/core/tests/stupid_extension_2/views.py index 4a5f6dd70..25d1b2268 100644 --- a/modoboa/core/tests/stupid_extension_2/views.py +++ b/modoboa/core/tests/stupid_extension_2/views.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + from django import http diff --git a/modoboa/core/tests/test_authentication.py b/modoboa/core/tests/test_authentication.py index 764dadaf0..e3e9a47d0 100644 --- a/modoboa/core/tests/test_authentication.py +++ b/modoboa/core/tests/test_authentication.py @@ -1,5 +1,7 @@ """Tests for core application.""" +from __future__ import unicode_literals + import smtplib from mock import patch diff --git a/modoboa/core/tests/test_core.py b/modoboa/core/tests/test_core.py index 2fae9c971..217e379a3 100644 --- a/modoboa/core/tests/test_core.py +++ b/modoboa/core/tests/test_core.py @@ -1,5 +1,7 @@ """Tests for core application.""" +from __future__ import unicode_literals + import httmock from dateutil.relativedelta import relativedelta diff --git a/modoboa/core/tests/test_extensions.py b/modoboa/core/tests/test_extensions.py index d220f3318..2ceee2307 100644 --- a/modoboa/core/tests/test_extensions.py +++ b/modoboa/core/tests/test_extensions.py @@ -1,12 +1,13 @@ """Extensions management related test cases.""" +from __future__ import unicode_literals + import os import sys from testfixtures import compare from django.core import management -from django.core.urlresolvers import reverse from django.test import TestCase, override_settings from .. import extensions diff --git a/modoboa/core/tests/test_ldap.py b/modoboa/core/tests/test_ldap.py index 182fae6dd..d7b166594 100644 --- a/modoboa/core/tests/test_ldap.py +++ b/modoboa/core/tests/test_ldap.py @@ -1,5 +1,7 @@ """Tests for core application.""" +from __future__ import unicode_literals + from unittest import skipIf from django.core.urlresolvers import reverse diff --git a/modoboa/core/tests/test_views.py b/modoboa/core/tests/test_views.py index 88ca94b41..aa09458de 100644 --- a/modoboa/core/tests/test_views.py +++ b/modoboa/core/tests/test_views.py @@ -1,5 +1,7 @@ """Core views test cases.""" +from __future__ import unicode_literals + import json from testfixtures import compare diff --git a/modoboa/core/urls.py b/modoboa/core/urls.py index 1d086effd..d3f34ba00 100644 --- a/modoboa/core/urls.py +++ b/modoboa/core/urls.py @@ -1,5 +1,7 @@ """Core urls.""" +from __future__ import unicode_literals + from django.conf.urls import url from . import views diff --git a/modoboa/core/utils.py b/modoboa/core/utils.py index 371e5b33a..43af52c8d 100644 --- a/modoboa/core/utils.py +++ b/modoboa/core/utils.py @@ -1,5 +1,7 @@ """Utility functions.""" +from __future__ import unicode_literals + from django.utils.translation import ugettext as _ from versionfield.constants import DEFAULT_NUMBER_BITS diff --git a/modoboa/core/views/__init__.py b/modoboa/core/views/__init__.py index 6df7980e4..45177a4e3 100644 --- a/modoboa/core/views/__init__.py +++ b/modoboa/core/views/__init__.py @@ -1,5 +1,7 @@ """Core views.""" +from __future__ import unicode_literals + from .admin import ( check_top_notifications, information, logs, logs_page, parameters, viewsettings) diff --git a/modoboa/core/views/admin.py b/modoboa/core/views/admin.py index e224decd7..6771dedb1 100644 --- a/modoboa/core/views/admin.py +++ b/modoboa/core/views/admin.py @@ -1,5 +1,7 @@ """Views available to super administrators only.""" +from __future__ import unicode_literals + from functools import reduce from django.contrib.auth.decorators import ( diff --git a/modoboa/core/views/auth.py b/modoboa/core/views/auth.py index 5ca158c1d..2b049b676 100644 --- a/modoboa/core/views/auth.py +++ b/modoboa/core/views/auth.py @@ -1,6 +1,8 @@ # coding: utf-8 """Core authentication views.""" +from __future__ import unicode_literals + import logging from django.core.urlresolvers import reverse diff --git a/modoboa/core/views/base.py b/modoboa/core/views/base.py index 0129de9ac..2470adfa4 100644 --- a/modoboa/core/views/base.py +++ b/modoboa/core/views/base.py @@ -1,5 +1,7 @@ """Base core views.""" +from __future__ import unicode_literals + from django.core.urlresolvers import reverse from django.views import generic diff --git a/modoboa/core/views/dashboard.py b/modoboa/core/views/dashboard.py index a6c9ceb00..9731096d4 100644 --- a/modoboa/core/views/dashboard.py +++ b/modoboa/core/views/dashboard.py @@ -1,5 +1,7 @@ """Core dashboard views.""" +from __future__ import unicode_literals + from dateutil import parser import feedparser import requests diff --git a/modoboa/core/views/user.py b/modoboa/core/views/user.py index feae310d0..dd01f270b 100644 --- a/modoboa/core/views/user.py +++ b/modoboa/core/views/user.py @@ -1,5 +1,7 @@ """Simple user views.""" +from __future__ import unicode_literals + from django.shortcuts import render from django.template.loader import render_to_string from django.utils import translation diff --git a/modoboa/lib/__init__.py b/modoboa/lib/__init__.py index e69de29bb..baffc4882 100644 --- a/modoboa/lib/__init__.py +++ b/modoboa/lib/__init__.py @@ -0,0 +1 @@ +from __future__ import unicode_literals diff --git a/modoboa/lib/api_client.py b/modoboa/lib/api_client.py index ea44bcf59..098e5f8d6 100644 --- a/modoboa/lib/api_client.py +++ b/modoboa/lib/api_client.py @@ -1,5 +1,7 @@ """A client for Modoboa's public API.""" +from __future__ import unicode_literals + import os import pkg_resources diff --git a/modoboa/lib/authbackends.py b/modoboa/lib/authbackends.py index c3c1c3f8b..59d46678e 100644 --- a/modoboa/lib/authbackends.py +++ b/modoboa/lib/authbackends.py @@ -1,5 +1,7 @@ """Custom authentication backends.""" +from __future__ import unicode_literals + import smtplib from django.conf import settings diff --git a/modoboa/lib/connections.py b/modoboa/lib/connections.py index 3cb4f175a..09287c5e8 100644 --- a/modoboa/lib/connections.py +++ b/modoboa/lib/connections.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + from modoboa.lib.cryptutils import decrypt diff --git a/modoboa/lib/db_utils.py b/modoboa/lib/db_utils.py index 42c8dd55f..8f6999aad 100644 --- a/modoboa/lib/db_utils.py +++ b/modoboa/lib/db_utils.py @@ -1,5 +1,7 @@ # coding: utf-8 +from __future__ import unicode_literals + from django.conf import settings from django.db import connection from django.utils.translation import ugettext as _ diff --git a/modoboa/lib/email_utils.py b/modoboa/lib/email_utils.py index 50790187c..ed1015e8a 100644 --- a/modoboa/lib/email_utils.py +++ b/modoboa/lib/email_utils.py @@ -1,5 +1,7 @@ # coding: utf-8 +from __future__ import unicode_literals + from email.header import Header, decode_header from email.mime.text import MIMEText from email.utils import make_msgid, formatdate, parseaddr diff --git a/modoboa/lib/exceptions.py b/modoboa/lib/exceptions.py index 82c1135f0..4bdd15896 100644 --- a/modoboa/lib/exceptions.py +++ b/modoboa/lib/exceptions.py @@ -4,6 +4,9 @@ ----------------------------------------------- """ + +from __future__ import unicode_literals + from django.utils.translation import ugettext as _ diff --git a/modoboa/lib/fields.py b/modoboa/lib/fields.py index c5e81ff11..4dcaf2998 100644 --- a/modoboa/lib/fields.py +++ b/modoboa/lib/fields.py @@ -1,5 +1,7 @@ """Custom form fields.""" +from __future__ import unicode_literals + from django import forms from django.utils.translation import ugettext_lazy diff --git a/modoboa/lib/imap_utf7.py b/modoboa/lib/imap_utf7.py index 28fccacc8..611729a53 100644 --- a/modoboa/lib/imap_utf7.py +++ b/modoboa/lib/imap_utf7.py @@ -51,7 +51,7 @@ Found here: http://svn.plone.org/svn/collective/mxmImapClient/trunk/imapUTF7.py """ -from __future__ import print_function +from __future__ import print_function, unicode_literals import binascii import codecs diff --git a/modoboa/lib/ldap_utils.py b/modoboa/lib/ldap_utils.py index 2988ee889..26d29fb65 100644 --- a/modoboa/lib/ldap_utils.py +++ b/modoboa/lib/ldap_utils.py @@ -33,6 +33,8 @@ Extracted from `this blog """ +from __future__ import unicode_literals + import ldap from django.conf import settings diff --git a/modoboa/lib/listing.py b/modoboa/lib/listing.py index e8f38f84d..c77e7dcf1 100644 --- a/modoboa/lib/listing.py +++ b/modoboa/lib/listing.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from modoboa.parameters import tools as param_tools diff --git a/modoboa/lib/middleware.py b/modoboa/lib/middleware.py index ee2f074c3..6fd7c77f2 100644 --- a/modoboa/lib/middleware.py +++ b/modoboa/lib/middleware.py @@ -2,6 +2,8 @@ """Custom middlewares.""" +from __future__ import unicode_literals + from django.http import HttpResponseRedirect from django.utils.encoding import smart_text diff --git a/modoboa/lib/migrations/__init__.py b/modoboa/lib/migrations/__init__.py index e69de29bb..baffc4882 100644 --- a/modoboa/lib/migrations/__init__.py +++ b/modoboa/lib/migrations/__init__.py @@ -0,0 +1 @@ +from __future__ import unicode_literals diff --git a/modoboa/lib/paginator.py b/modoboa/lib/paginator.py index 79ecf94dd..2c5658a54 100644 --- a/modoboa/lib/paginator.py +++ b/modoboa/lib/paginator.py @@ -2,6 +2,8 @@ Custom pagination tools. """ +from __future__ import unicode_literals + class Page(object): diff --git a/modoboa/lib/signals.py b/modoboa/lib/signals.py index 2a114e8c3..f203de7c3 100644 --- a/modoboa/lib/signals.py +++ b/modoboa/lib/signals.py @@ -1,5 +1,7 @@ """Custom signals.""" +from __future__ import unicode_literals + from threading import local _local_store = local() diff --git a/modoboa/lib/singleton.py b/modoboa/lib/singleton.py index 3fd8f0c17..da4210794 100644 --- a/modoboa/lib/singleton.py +++ b/modoboa/lib/singleton.py @@ -1,4 +1,4 @@ - +from __future__ import unicode_literals # works in Python 2 & 3 class _Singleton(type): diff --git a/modoboa/lib/sysutils.py b/modoboa/lib/sysutils.py index 9fc62187b..aff56e97a 100644 --- a/modoboa/lib/sysutils.py +++ b/modoboa/lib/sysutils.py @@ -4,6 +4,9 @@ This module extra functions/shortcuts to communicate with the system (executing commands, etc.) """ + +from __future__ import unicode_literals + import subprocess import inspect import re diff --git a/modoboa/lib/templatetags/__init__.py b/modoboa/lib/templatetags/__init__.py index e69de29bb..baffc4882 100644 --- a/modoboa/lib/templatetags/__init__.py +++ b/modoboa/lib/templatetags/__init__.py @@ -0,0 +1 @@ +from __future__ import unicode_literals diff --git a/modoboa/lib/templatetags/form_tags.py b/modoboa/lib/templatetags/form_tags.py index c993f0506..11db3bf90 100644 --- a/modoboa/lib/templatetags/form_tags.py +++ b/modoboa/lib/templatetags/form_tags.py @@ -2,6 +2,8 @@ Form rendering tags. """ +from __future__ import unicode_literals + from django import forms from django import template from django.utils.encoding import smart_text diff --git a/modoboa/lib/templatetags/lib_tags.py b/modoboa/lib/templatetags/lib_tags.py index 1342f27c4..ef7a9da2d 100644 --- a/modoboa/lib/templatetags/lib_tags.py +++ b/modoboa/lib/templatetags/lib_tags.py @@ -1,6 +1,8 @@ # coding: utf-8 """Custom template tags.""" +from __future__ import unicode_literals + from datetime import datetime from django import template diff --git a/modoboa/lib/test_utils.py b/modoboa/lib/test_utils.py index e4bb1e4d9..640c199f0 100644 --- a/modoboa/lib/test_utils.py +++ b/modoboa/lib/test_utils.py @@ -1,5 +1,7 @@ """Tools for top level testing (ie. when ran by Travis).""" +from __future__ import unicode_literals + import os import shutil import tempfile diff --git a/modoboa/lib/tests.py b/modoboa/lib/tests.py index c2544717c..99d6186d1 100644 --- a/modoboa/lib/tests.py +++ b/modoboa/lib/tests.py @@ -1,6 +1,8 @@ # coding: utf-8 """Testing utilities.""" +from __future__ import unicode_literals + import json from django.core import management diff --git a/modoboa/lib/u2u_decode.py b/modoboa/lib/u2u_decode.py index cf4880c62..39c498977 100644 --- a/modoboa/lib/u2u_decode.py +++ b/modoboa/lib/u2u_decode.py @@ -2,6 +2,8 @@ """Unstructured rfc2047 header to unicode.""" +from __future__ import unicode_literals + from email.header import decode_header, make_header import re diff --git a/modoboa/lib/validators.py b/modoboa/lib/validators.py index 83c569ad1..c440eb805 100644 --- a/modoboa/lib/validators.py +++ b/modoboa/lib/validators.py @@ -1,5 +1,7 @@ """Field validators.""" +from __future__ import unicode_literals + import re from django.core.exceptions import ValidationError diff --git a/modoboa/lib/web_utils.py b/modoboa/lib/web_utils.py index b7607d75c..555a1ebf4 100644 --- a/modoboa/lib/web_utils.py +++ b/modoboa/lib/web_utils.py @@ -4,6 +4,8 @@ This module contains extra functions/shortcuts used to render HTML. """ +from __future__ import unicode_literals + import json import re import sys diff --git a/modoboa/limits/__init__.py b/modoboa/limits/__init__.py index 1816137f6..990d4c137 100644 --- a/modoboa/limits/__init__.py +++ b/modoboa/limits/__init__.py @@ -1 +1,3 @@ +from __future__ import unicode_literals + default_app_config = "modoboa.limits.apps.LimitsConfig" diff --git a/modoboa/limits/app_settings.py b/modoboa/limits/app_settings.py index cf77296b3..9a724e671 100644 --- a/modoboa/limits/app_settings.py +++ b/modoboa/limits/app_settings.py @@ -1,5 +1,7 @@ """Custom settings.""" +from __future__ import unicode_literals + from django import forms from django.utils.translation import ugettext_lazy as _ diff --git a/modoboa/limits/apps.py b/modoboa/limits/apps.py index f91abd69a..4716d1f19 100644 --- a/modoboa/limits/apps.py +++ b/modoboa/limits/apps.py @@ -1,5 +1,7 @@ """App config for limits.""" +from __future__ import unicode_literals + from django.apps import AppConfig from django.utils.translation import ugettext as _ diff --git a/modoboa/limits/constants.py b/modoboa/limits/constants.py index 069729e24..f12d5818f 100644 --- a/modoboa/limits/constants.py +++ b/modoboa/limits/constants.py @@ -1,5 +1,7 @@ """Modoboa limits constants.""" +from __future__ import unicode_literals + import collections from django.utils.translation import ugettext_lazy as _ diff --git a/modoboa/limits/forms.py b/modoboa/limits/forms.py index bc4182684..a30d86f88 100644 --- a/modoboa/limits/forms.py +++ b/modoboa/limits/forms.py @@ -2,6 +2,8 @@ """Custom forms.""" +from __future__ import unicode_literals + from django import forms from django.utils.translation import ugettext as _ diff --git a/modoboa/limits/handlers.py b/modoboa/limits/handlers.py index 29bb1b776..8602f2bc6 100644 --- a/modoboa/limits/handlers.py +++ b/modoboa/limits/handlers.py @@ -1,5 +1,7 @@ """Django signal handlers for limits.""" +from __future__ import unicode_literals + from django.db.models import signals from django.dispatch import receiver from django.template.loader import render_to_string diff --git a/modoboa/limits/lib.py b/modoboa/limits/lib.py index 35af3b584..500cdf454 100644 --- a/modoboa/limits/lib.py +++ b/modoboa/limits/lib.py @@ -4,6 +4,9 @@ ------------------------------- """ + +from __future__ import unicode_literals + from django.utils.translation import ugettext as _ from modoboa.lib.exceptions import ModoboaException diff --git a/modoboa/limits/migrations/__init__.py b/modoboa/limits/migrations/__init__.py index e69de29bb..baffc4882 100644 --- a/modoboa/limits/migrations/__init__.py +++ b/modoboa/limits/migrations/__init__.py @@ -0,0 +1 @@ +from __future__ import unicode_literals diff --git a/modoboa/limits/models.py b/modoboa/limits/models.py index 0986eb756..0797a7557 100644 --- a/modoboa/limits/models.py +++ b/modoboa/limits/models.py @@ -2,6 +2,8 @@ """Models for the limits extensions.""" +from __future__ import unicode_literals + from django.conf import settings from django.db import models from django.utils.encoding import python_2_unicode_compatible diff --git a/modoboa/limits/serializers.py b/modoboa/limits/serializers.py index a4e63b2a6..8b447ad71 100644 --- a/modoboa/limits/serializers.py +++ b/modoboa/limits/serializers.py @@ -1,5 +1,7 @@ """Limits serializers.""" +from __future__ import unicode_literals + from rest_framework import serializers from . import lib diff --git a/modoboa/limits/tests/__init__.py b/modoboa/limits/tests/__init__.py index e69de29bb..baffc4882 100644 --- a/modoboa/limits/tests/__init__.py +++ b/modoboa/limits/tests/__init__.py @@ -0,0 +1 @@ +from __future__ import unicode_literals diff --git a/modoboa/limits/tests/test_api.py b/modoboa/limits/tests/test_api.py index 5917c777f..b13a2246f 100644 --- a/modoboa/limits/tests/test_api.py +++ b/modoboa/limits/tests/test_api.py @@ -1,6 +1,8 @@ # coding: utf-8 """Test cases for the limits extension.""" +from __future__ import unicode_literals + from testfixtures import compare from django.core.urlresolvers import reverse diff --git a/modoboa/limits/tests/test_domain_limits.py b/modoboa/limits/tests/test_domain_limits.py index dede7d468..0122c418f 100644 --- a/modoboa/limits/tests/test_domain_limits.py +++ b/modoboa/limits/tests/test_domain_limits.py @@ -1,6 +1,8 @@ # coding: utf-8 """Test cases for the limits extension.""" +from __future__ import unicode_literals + from django.core.urlresolvers import reverse from modoboa.admin import factories as admin_factories diff --git a/modoboa/limits/tests/test_import.py b/modoboa/limits/tests/test_import.py index e6702a773..7f8e4603f 100644 --- a/modoboa/limits/tests/test_import.py +++ b/modoboa/limits/tests/test_import.py @@ -1,5 +1,7 @@ # coding: utf-8 +from __future__ import unicode_literals + from django.core.files.base import ContentFile from django.core.urlresolvers import reverse diff --git a/modoboa/limits/tests/test_user_limits.py b/modoboa/limits/tests/test_user_limits.py index c871e0b58..e57618755 100644 --- a/modoboa/limits/tests/test_user_limits.py +++ b/modoboa/limits/tests/test_user_limits.py @@ -1,6 +1,8 @@ # coding: utf-8 """Test cases for the limits extension.""" +from __future__ import unicode_literals + from django.core.urlresolvers import reverse from modoboa.admin.factories import populate_database diff --git a/modoboa/limits/urls_api.py b/modoboa/limits/urls_api.py index de97d5718..dad167523 100644 --- a/modoboa/limits/urls_api.py +++ b/modoboa/limits/urls_api.py @@ -1,5 +1,7 @@ """Limits API urls.""" +from __future__ import unicode_literals + from rest_framework import routers from . import viewsets diff --git a/modoboa/limits/utils.py b/modoboa/limits/utils.py index 692cafd01..265a579ca 100644 --- a/modoboa/limits/utils.py +++ b/modoboa/limits/utils.py @@ -1,5 +1,7 @@ """Modoboa limits utilities.""" +from __future__ import unicode_literals + from . import constants diff --git a/modoboa/limits/views.py b/modoboa/limits/views.py index 57d631c3f..e17c22b7f 100644 --- a/modoboa/limits/views.py +++ b/modoboa/limits/views.py @@ -1 +1,3 @@ # coding: utf-8 + +from __future__ import unicode_literals diff --git a/modoboa/limits/viewsets.py b/modoboa/limits/viewsets.py index 1b7fff120..2d792ca75 100644 --- a/modoboa/limits/viewsets.py +++ b/modoboa/limits/viewsets.py @@ -1,5 +1,7 @@ """Limits API.""" +from __future__ import unicode_literals + from django.contrib.contenttypes.models import ContentType from rest_framework import mixins, viewsets diff --git a/modoboa/parameters/__init__.py b/modoboa/parameters/__init__.py index e69de29bb..baffc4882 100644 --- a/modoboa/parameters/__init__.py +++ b/modoboa/parameters/__init__.py @@ -0,0 +1 @@ +from __future__ import unicode_literals diff --git a/modoboa/parameters/forms.py b/modoboa/parameters/forms.py index f456dae16..d26996e13 100644 --- a/modoboa/parameters/forms.py +++ b/modoboa/parameters/forms.py @@ -1,5 +1,7 @@ """Parameters forms.""" +from __future__ import unicode_literals + from django import forms from modoboa.lib import db_utils diff --git a/modoboa/parameters/tests.py b/modoboa/parameters/tests.py index 7ce503c2d..36fcdafd1 100644 --- a/modoboa/parameters/tests.py +++ b/modoboa/parameters/tests.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + from django.test import TestCase # Create your tests here. diff --git a/modoboa/parameters/tools.py b/modoboa/parameters/tools.py index 8d3b1131f..383b17442 100644 --- a/modoboa/parameters/tools.py +++ b/modoboa/parameters/tools.py @@ -1,5 +1,7 @@ """Parameters management.""" +from __future__ import unicode_literals + import copy from modoboa.lib import exceptions diff --git a/modoboa/relaydomains/__init__.py b/modoboa/relaydomains/__init__.py index 0b2987e99..c9afa7d7b 100644 --- a/modoboa/relaydomains/__init__.py +++ b/modoboa/relaydomains/__init__.py @@ -1 +1,3 @@ +from __future__ import unicode_literals + default_app_config = "modoboa.relaydomains.apps.RelayDomainsConfig" diff --git a/modoboa/relaydomains/app_settings.py b/modoboa/relaydomains/app_settings.py index a6cf7fb4d..ac892234e 100644 --- a/modoboa/relaydomains/app_settings.py +++ b/modoboa/relaydomains/app_settings.py @@ -1,5 +1,7 @@ """Relay domains settings definition.""" +from __future__ import unicode_literals + from django import forms from django.utils.translation import ugettext_lazy diff --git a/modoboa/relaydomains/apps.py b/modoboa/relaydomains/apps.py index 34b229a03..937456f2b 100644 --- a/modoboa/relaydomains/apps.py +++ b/modoboa/relaydomains/apps.py @@ -1,5 +1,7 @@ """AppConfig for relaydomains.""" +from __future__ import unicode_literals + from django.apps import AppConfig from django.utils.translation import ugettext as _ diff --git a/modoboa/relaydomains/constants.py b/modoboa/relaydomains/constants.py index 951bfa1af..1e4b24230 100644 --- a/modoboa/relaydomains/constants.py +++ b/modoboa/relaydomains/constants.py @@ -1,5 +1,7 @@ """Relaydomains app constants.""" +from __future__ import unicode_literals + PERMISSIONS = { "Resellers": [ ("relaydomains", "relaydomain", "add_relaydomain"), diff --git a/modoboa/relaydomains/forms.py b/modoboa/relaydomains/forms.py index 1da259ebb..de872f917 100644 --- a/modoboa/relaydomains/forms.py +++ b/modoboa/relaydomains/forms.py @@ -1,5 +1,7 @@ """Postfix relay domains extension forms.""" +from __future__ import unicode_literals + from django import forms from django.core import validators from django.utils.translation import ugettext as _ diff --git a/modoboa/relaydomains/handlers.py b/modoboa/relaydomains/handlers.py index 87b8e36e3..2bdd168a9 100644 --- a/modoboa/relaydomains/handlers.py +++ b/modoboa/relaydomains/handlers.py @@ -1,5 +1,7 @@ """Django signal handlers for relaydomains.""" +from __future__ import unicode_literals + from django.conf import settings from django.db.models import signals from django.dispatch import receiver diff --git a/modoboa/relaydomains/lib.py b/modoboa/relaydomains/lib.py index eaa2295de..b32a710ca 100644 --- a/modoboa/relaydomains/lib.py +++ b/modoboa/relaydomains/lib.py @@ -1,5 +1,7 @@ """Internal library.""" +from __future__ import unicode_literals + from .models import RelayDomain diff --git a/modoboa/relaydomains/migrations/__init__.py b/modoboa/relaydomains/migrations/__init__.py index e69de29bb..baffc4882 100644 --- a/modoboa/relaydomains/migrations/__init__.py +++ b/modoboa/relaydomains/migrations/__init__.py @@ -0,0 +1 @@ +from __future__ import unicode_literals diff --git a/modoboa/relaydomains/models.py b/modoboa/relaydomains/models.py index 715e4ba3f..4347a2a13 100644 --- a/modoboa/relaydomains/models.py +++ b/modoboa/relaydomains/models.py @@ -1,5 +1,7 @@ """Relay domain related models.""" +from __future__ import unicode_literals + from django.db import models from django.db.models.manager import Manager from django.utils.translation import ugettext as _, ugettext_lazy diff --git a/modoboa/relaydomains/postfix_maps.py b/modoboa/relaydomains/postfix_maps.py index e91444e7f..fdb084a61 100644 --- a/modoboa/relaydomains/postfix_maps.py +++ b/modoboa/relaydomains/postfix_maps.py @@ -1,5 +1,7 @@ """Map file definitions for postfix.""" +from __future__ import unicode_literals + class RelayDomainsMap(object): diff --git a/modoboa/relaydomains/tests.py b/modoboa/relaydomains/tests.py index 672c5dda5..957a165c1 100644 --- a/modoboa/relaydomains/tests.py +++ b/modoboa/relaydomains/tests.py @@ -1,5 +1,7 @@ """relaydomains unit tests.""" +from __future__ import unicode_literals + import json from django.core.files.base import ContentFile diff --git a/modoboa/relaydomains/urls.py b/modoboa/relaydomains/urls.py index e8dcd2cbd..e71b7b830 100644 --- a/modoboa/relaydomains/urls.py +++ b/modoboa/relaydomains/urls.py @@ -1,5 +1,7 @@ """Relay domains urls.""" +from __future__ import unicode_literals + from django.conf.urls import url from . import views diff --git a/modoboa/relaydomains/views.py b/modoboa/relaydomains/views.py index 6e30d2ce9..d115dc90c 100644 --- a/modoboa/relaydomains/views.py +++ b/modoboa/relaydomains/views.py @@ -1,5 +1,7 @@ """Custom views.""" +from __future__ import unicode_literals + from django.contrib.auth.decorators import ( login_required, permission_required ) diff --git a/modoboa/test_settings.py b/modoboa/test_settings.py index 06c111971..ba67caaf9 100644 --- a/modoboa/test_settings.py +++ b/modoboa/test_settings.py @@ -1,4 +1,7 @@ # -*- coding: utf-8 -*- + +from __future__ import unicode_literals + import os # Database diff --git a/modoboa/urls.py b/modoboa/urls.py index 5d0d350f8..34635fea7 100644 --- a/modoboa/urls.py +++ b/modoboa/urls.py @@ -1,3 +1,7 @@ +from __future__ import unicode_literals + +from functools import reduce + from django.conf import settings from django.conf.urls import include, url from django.views.i18n import JavaScriptCatalog diff --git a/modoboa/urls_api.py b/modoboa/urls_api.py index 64d6aaa2f..ef93a9914 100644 --- a/modoboa/urls_api.py +++ b/modoboa/urls_api.py @@ -1,5 +1,7 @@ """External API urls.""" +from __future__ import unicode_literals + from django.conf.urls import include, url from modoboa.core.extensions import exts_pool diff --git a/test_project/manage.py b/test_project/manage.py index 0fc36a34c..7fae8c557 100755 --- a/test_project/manage.py +++ b/test_project/manage.py @@ -1,4 +1,7 @@ #!/usr/bin/env python + +from __future__ import unicode_literals + import os import sys diff --git a/test_project/test_project/__init__.py b/test_project/test_project/__init__.py index e69de29bb..baffc4882 100644 --- a/test_project/test_project/__init__.py +++ b/test_project/test_project/__init__.py @@ -0,0 +1 @@ +from __future__ import unicode_literals diff --git a/test_project/test_project/settings.py b/test_project/test_project/settings.py index d19515f9a..7493ab588 100644 --- a/test_project/test_project/settings.py +++ b/test_project/test_project/settings.py @@ -8,6 +8,8 @@ For the full list of settings and their values, see https://docs.djangoproject.com/en/1.6/ref/settings/ """ +from __future__ import unicode_literals + # Build paths inside the project like this: os.path.join(BASE_DIR, ...) import os from logging.handlers import SysLogHandler diff --git a/test_project/test_project/urls.py b/test_project/test_project/urls.py index 1a4d5cea7..1317f7c7b 100644 --- a/test_project/test_project/urls.py +++ b/test_project/test_project/urls.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + from django.conf.urls import include, url urlpatterns = [ diff --git a/test_project/test_project/wsgi.py b/test_project/test_project/wsgi.py index 5ce7da76a..e63b59d9e 100644 --- a/test_project/test_project/wsgi.py +++ b/test_project/test_project/wsgi.py @@ -7,6 +7,8 @@ For more information on this file, see https://docs.djangoproject.com/en/1.7/howto/deployment/wsgi/ """ +from __future__ import unicode_literals + import os os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test_project.settings") From c97e5a703d0162d7ab53e249b9148abe65083ec5 Mon Sep 17 00:00:00 2001 From: Arthur Vuillard Date: Thu, 11 May 2017 15:17:20 +0200 Subject: [PATCH 11/23] fix tests for modoboa.core in python2 --- modoboa/admin/forms/import_.py | 2 +- modoboa/core/tests/test_views.py | 6 ++---- modoboa/lib/ldap_utils.py | 5 +++-- modoboa/lib/tests.py | 2 -- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/modoboa/admin/forms/import_.py b/modoboa/admin/forms/import_.py index 19fc7d360..790260919 100644 --- a/modoboa/admin/forms/import_.py +++ b/modoboa/admin/forms/import_.py @@ -29,7 +29,7 @@ class ImportDataForm(forms.Form): def clean_sepchar(self): if self.cleaned_data["sepchar"] == "": return ";" - return str(self.cleaned_data["sepchar"]) + return self.cleaned_data["sepchar"] class ImportIdentitiesForm(ImportDataForm): diff --git a/modoboa/core/tests/test_views.py b/modoboa/core/tests/test_views.py index 904754fc3..177ca940d 100644 --- a/modoboa/core/tests/test_views.py +++ b/modoboa/core/tests/test_views.py @@ -2,8 +2,6 @@ from __future__ import unicode_literals -import json - from testfixtures import compare from django import forms @@ -53,12 +51,12 @@ class DashboardTestCase(ModoTestCase): url = reverse("core:dashboard") response = self.client.get(url) self.assertEqual(response.status_code, 200) - self.assertIn("Latest news", str(response.content)) + self.assertIn("Latest news", response.content.decode('utf-8')) self.client.logout() self.client.login(username=self.dadmin.username, password="toto") response = self.client.get(url) self.assertEqual(response.status_code, 200) - self.assertIn("Latest news", str(response.content)) + self.assertIn("Latest news", response.content.decode('utf-8')) self.client.logout() self.client.login(username=self.user.username, password="toto") response = self.client.get(url) diff --git a/modoboa/lib/ldap_utils.py b/modoboa/lib/ldap_utils.py index 26d29fb65..6ec1316fe 100644 --- a/modoboa/lib/ldap_utils.py +++ b/modoboa/lib/ldap_utils.py @@ -39,6 +39,7 @@ import ldap from django.conf import settings from django.utils.translation import ugettext as _ +from django.utils.encoding import smart_bytes from modoboa.core.password_hashers import get_password_hasher from modoboa.lib.exceptions import InternalError @@ -119,8 +120,8 @@ class LDAPAuthBackend(object): ('"%s"' % newpassword).encode('utf-16').lstrip('\377\376') ) ldif = [(ldap.MOD_REPLACE, - self.pwd_attr, - self._crypt_password(newpassword))] + smart_bytes(self.pwd_attr), + smart_bytes(self._crypt_password(newpassword)))] try: self.conn.modify_s(user_dn, ldif) except ldap.LDAPError as e: diff --git a/modoboa/lib/tests.py b/modoboa/lib/tests.py index 7cb122345..e86d6a405 100644 --- a/modoboa/lib/tests.py +++ b/modoboa/lib/tests.py @@ -3,8 +3,6 @@ from __future__ import unicode_literals -import json - from django.core import management from django.test import TestCase From 742d948e76c8128540d21b792b1c3b74fba7da4f Mon Sep 17 00:00:00 2001 From: Arthur Vuillard Date: Thu, 11 May 2017 15:29:03 +0200 Subject: [PATCH 12/23] fix tests for modoboa.admin in python2 --- modoboa/admin/forms/export.py | 4 ++-- modoboa/admin/management/commands/subcommands/_repair.py | 3 ++- modoboa/admin/tests/test_domain.py | 7 ++++--- modoboa/admin/views/export.py | 9 +++++++-- modoboa/admin/views/import_.py | 7 +++++-- 5 files changed, 20 insertions(+), 10 deletions(-) diff --git a/modoboa/admin/forms/export.py b/modoboa/admin/forms/export.py index 049b451cb..25e9402a3 100644 --- a/modoboa/admin/forms/export.py +++ b/modoboa/admin/forms/export.py @@ -25,12 +25,12 @@ class ExportDataForm(forms.Form): def clean_sepchar(self): if self.cleaned_data["sepchar"] == "": return ";" - return str(self.cleaned_data["sepchar"]) + return self.cleaned_data["sepchar"] def clean_filename(self): if self.cleaned_data["filename"] == "": return self.fields["filename"].initial - return str(self.cleaned_data["filename"]) + return self.cleaned_data["filename"] class ExportDomainsForm(ExportDataForm): diff --git a/modoboa/admin/management/commands/subcommands/_repair.py b/modoboa/admin/management/commands/subcommands/_repair.py index b7198bbd6..1406b683d 100644 --- a/modoboa/admin/management/commands/subcommands/_repair.py +++ b/modoboa/admin/management/commands/subcommands/_repair.py @@ -4,6 +4,7 @@ from __future__ import print_function, unicode_literals from django.conf import settings from django.core.management.base import BaseCommand +from django.utils.encoding import smart_str from modoboa.lib.permissions import grant_access_to_object from modoboa.lib.permissions import get_object_owner @@ -112,7 +113,7 @@ class Repair(BaseCommand): # Load known problems from extensions. for ext in settings.MODOBOA_APPS: try: - __import__(ext, locals(), globals(), ["known_problems"]) + __import__(ext, locals(), globals(), [smart_str("known_problems")]) except ImportError: pass for func in known_problems: diff --git a/modoboa/admin/tests/test_domain.py b/modoboa/admin/tests/test_domain.py index 907472906..0a7d64f57 100644 --- a/modoboa/admin/tests/test_domain.py +++ b/modoboa/admin/tests/test_domain.py @@ -236,11 +236,12 @@ class DomainTestCase(ModoTestCase): url = reverse("core:dashboard") response = self.client.get(url) self.assertEqual(response.status_code, 200) - self.assertIn("Global statistics", response.content) + self.assertIn("Global statistics", response.content.decode('utf-8')) self.client.force_login( User.objects.get(username="admin@test.com")) response = self.client.get(url) self.assertEqual(response.status_code, 200) - self.assertNotIn("Global statistics", response.content) - self.assertIn("Per-domain statistics", response.content) + content = response.content.decode('utf-8') + self.assertNotIn("Global statistics", content) + self.assertIn("Per-domain statistics", content) diff --git a/modoboa/admin/views/export.py b/modoboa/admin/views/export.py index 57edc87bf..1044a9e29 100644 --- a/modoboa/admin/views/export.py +++ b/modoboa/admin/views/export.py @@ -14,6 +14,7 @@ from django.core.urlresolvers import reverse from django.http import HttpResponse from django.shortcuts import render from django.utils.translation import ugettext as _ +from django.utils.encoding import smart_str from ..forms import ExportIdentitiesForm, ExportDomainsForm from ..lib import get_domains, get_identities @@ -50,7 +51,9 @@ def export_identities(request): form = ExportIdentitiesForm(request.POST) form.is_valid() fp = io.BytesIO() - csvwriter = csv.writer(fp, delimiter=form.cleaned_data["sepchar"]) + csvwriter = csv.writer( + fp, delimiter=smart_str(form.cleaned_data["sepchar"]) + ) identities = get_identities( request.user, **request.session['identities_filters']) for ident in identities: @@ -78,7 +81,9 @@ def export_domains(request): form = ExportDomainsForm(request.POST) form.is_valid() fp = io.BytesIO() - csvwriter = csv.writer(fp, delimiter=form.cleaned_data["sepchar"]) + csvwriter = csv.writer( + fp, delimiter=smart_str(form.cleaned_data["sepchar"]) + ) for dom in get_domains(request.user, **request.session['domains_filters']): dom.to_csv(csvwriter) diff --git a/modoboa/admin/views/import_.py b/modoboa/admin/views/import_.py index 1bf5f78d2..951ae5940 100644 --- a/modoboa/admin/views/import_.py +++ b/modoboa/admin/views/import_.py @@ -11,6 +11,7 @@ from django.core.urlresolvers import reverse from django.db import transaction from django.shortcuts import render from django.utils.translation import ugettext as _ +from django.utils.encoding import smart_str from reversion import revisions as reversion @@ -36,8 +37,10 @@ def importdata(request, formclass=ImportDataForm): form = formclass(request.POST, request.FILES) if form.is_valid(): try: - reader = csv.reader(request.FILES['sourcefile'], - delimiter=form.cleaned_data['sepchar']) + reader = csv.reader( + request.FILES['sourcefile'], + delimiter=smart_str(form.cleaned_data['sepchar']) + ) except csv.Error as inst: error = str(inst) From b5617479e3e26b41b9f0899ca68f51c6b22cb118 Mon Sep 17 00:00:00 2001 From: Arthur Vuillard Date: Thu, 11 May 2017 15:30:33 +0200 Subject: [PATCH 13/23] remove useless module in test command for travis --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 9a6c02647..9896b753c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -43,7 +43,7 @@ before_script: script: - python ./tests.py - cd test_project - - coverage run --source ../modoboa manage.py test modoboa.core modoboa.admin.tests modoboa.limits modoboa.relaydomains + - coverage run --source ../modoboa manage.py test modoboa.core modoboa.admin modoboa.limits modoboa.relaydomains after_success: - codecov From 3ffa3a539668c470bc6648adc53d09f06f784b88 Mon Sep 17 00:00:00 2001 From: Arthur Vuillard Date: Thu, 11 May 2017 16:37:19 +0200 Subject: [PATCH 14/23] improvements in using six ; tests in modoboa.core in both python 2 and 3 --- .travis.yml | 2 +- modoboa/core/commands/__init__.py | 8 ++------ modoboa/core/commands/deploy.py | 10 ++++------ modoboa/core/extensions.py | 9 +++------ modoboa/core/views/user.py | 6 ++++-- modoboa/lib/cryptutils.py | 6 +++++- modoboa/lib/form_utils.py | 10 +++------- modoboa/lib/ldap_utils.py | 8 ++++---- modoboa/limits/tests/test_api.py | 4 +++- modoboa/relaydomains/tests.py | 4 +--- tox.ini | 2 +- 11 files changed, 31 insertions(+), 38 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9896b753c..fbfb621d2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -43,7 +43,7 @@ before_script: script: - python ./tests.py - cd test_project - - coverage run --source ../modoboa manage.py test modoboa.core modoboa.admin modoboa.limits modoboa.relaydomains + - coverage run --source ../modoboa manage.py test modoboa.core modoboa.lib modoboa.admin modoboa.limits modoboa.relaydomains after_success: - codecov diff --git a/modoboa/core/commands/__init__.py b/modoboa/core/commands/__init__.py index a45b85a9d..f21b36fa7 100644 --- a/modoboa/core/commands/__init__.py +++ b/modoboa/core/commands/__init__.py @@ -8,7 +8,7 @@ import sys from django.conf import settings from django.template import Context, Template -from django.utils import six +from django.utils.encoding import smart_str class Command(object): @@ -66,12 +66,8 @@ def scan_for_commands(dirname=""): if os.path.isfile(f): continue cmdname = f.replace(".py", "") - if six.PY2: - fromlist = [cmdname.encode()] - else: - fromlist = [cmdname] cmdmod = __import__("modoboa.core.commands", globals(), locals(), - fromlist) + [smart_str(cmdname)]) cmdmod = getattr(cmdmod, cmdname) if "_" in cmdname: cmdclassname = "".join( diff --git a/modoboa/core/commands/deploy.py b/modoboa/core/commands/deploy.py index 6f07ef462..5a21e4fbf 100644 --- a/modoboa/core/commands/deploy.py +++ b/modoboa/core/commands/deploy.py @@ -21,7 +21,7 @@ except ImportError: import django from django.core import management from django.template import Context, Template -from django.utils import six +from django.utils.encoding import smart_str import dj_database_url from modoboa.core.commands import Command @@ -232,11 +232,9 @@ class DeployCommand(Command): os.path.join(os.path.dirname(__file__), "../../bower_components") ) - if six.PY2: - fromlist = ['settings'.encode()] - else: - fromlist = ['settings'] - mod = __import__(parsed_args.name, globals(), locals(), fromlist) + mod = __import__( + parsed_args.name, globals(), locals(), [smart_str('settings')] + ) tpl = self._render_template( "%s/settings.py.tpl" % self._templates_dir, { 'db_connections': connections, diff --git a/modoboa/core/extensions.py b/modoboa/core/extensions.py index 5b4732204..a6ea04909 100644 --- a/modoboa/core/extensions.py +++ b/modoboa/core/extensions.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals from django.conf import settings from django.conf.urls import include, url -from django.utils import six +from django.utils.encoding import smart_str class ModoExtension(object): @@ -79,11 +79,7 @@ class ExtensionsPool(object): def load_extension(self, name): """Load a registered extension.""" - if six.PY2: - fromlist = ["modo_extension".encode()] - else: - fromlist = ["modo_extension"] - __import__(name, locals(), globals(), fromlist) + __import__(name, locals(), globals(), [smart_str('modo_extension')]) extinstance = self.get_extension(name) if extinstance is None: return None @@ -137,4 +133,5 @@ class ExtensionsPool(object): result += [infos] return sorted(result, key=lambda i: i["name"]) + exts_pool = ExtensionsPool() diff --git a/modoboa/core/views/user.py b/modoboa/core/views/user.py index dd01f270b..91acaff35 100644 --- a/modoboa/core/views/user.py +++ b/modoboa/core/views/user.py @@ -6,6 +6,7 @@ from django.shortcuts import render from django.template.loader import render_to_string from django.utils import translation from django.utils.translation import ugettext as _ +from django.utils.encoding import force_text from django.contrib.auth.decorators import login_required, user_passes_test @@ -41,8 +42,9 @@ def profile(request, tplname='core/user_profile.html'): if form.is_valid(): form.save() if update_password and form.cleaned_data["confirmation"] != "": - request.session["password"] = encrypt( - form.cleaned_data["confirmation"]) + request.session["password"] = force_text(encrypt( + form.cleaned_data["confirmation"] + )) translation.activate(request.user.language) request.session[translation.LANGUAGE_SESSION_KEY] = ( request.user.language) diff --git a/modoboa/lib/cryptutils.py b/modoboa/lib/cryptutils.py index 16e8a82ca..717ef7f73 100644 --- a/modoboa/lib/cryptutils.py +++ b/modoboa/lib/cryptutils.py @@ -7,6 +7,8 @@ import base64 import random import string +from django.utils.encoding import force_bytes + from Crypto.Cipher import AES from modoboa.parameters import tools as param_tools @@ -32,7 +34,9 @@ def encrypt(clear): if type(clear) is str: clear = clear.encode("utf-8") if len(clear) % AES.block_size: - clear += " " * (AES.block_size - len(clear) % AES.block_size) + clear += ( + force_bytes(" ") * (AES.block_size - len(clear) % AES.block_size) + ) ciph = obj.encrypt(clear) ciph = base64.b64encode(ciph) return ciph diff --git a/modoboa/lib/form_utils.py b/modoboa/lib/form_utils.py index 31b6dc421..e2b7144b1 100644 --- a/modoboa/lib/form_utils.py +++ b/modoboa/lib/form_utils.py @@ -13,8 +13,7 @@ from django.forms.fields import Field from django.forms.widgets import RadioSelect from django.forms.widgets import RadioChoiceInput from django.shortcuts import render -from django.utils import six -from django.utils.encoding import force_text +from django.utils.encoding import force_text, force_str from django.utils.html import conditional_escape from django.utils.safestring import mark_safe from django.utils.translation import ugettext as _, ugettext_lazy @@ -22,11 +21,8 @@ from django.utils.translation import ugettext as _, ugettext_lazy from modoboa.lib.exceptions import BadRequest from modoboa.lib.web_utils import render_to_json_response -if six.PY2: - meta_name = b"ABC" -else: - meta_name = "ABC" -ABC = abc.ABCMeta(meta_name, (object,), {}) # compatible with Python 2 *and* 3 + +ABC = abc.ABCMeta(force_str('ABC'), (object,), {}) class WizardStep(object): diff --git a/modoboa/lib/ldap_utils.py b/modoboa/lib/ldap_utils.py index 6ec1316fe..2952f0198 100644 --- a/modoboa/lib/ldap_utils.py +++ b/modoboa/lib/ldap_utils.py @@ -39,7 +39,7 @@ import ldap from django.conf import settings from django.utils.translation import ugettext as _ -from django.utils.encoding import smart_bytes +from django.utils.encoding import force_bytes from modoboa.core.password_hashers import get_password_hasher from modoboa.lib.exceptions import InternalError @@ -69,7 +69,7 @@ class LDAPAuthBackend(object): def _get_conn(self, dn, password): """Get a connection from the server.""" - conn = ldap.initialize(self.server_uri) + conn = ldap.initialize(self.server_uri, bytes_mode=False) conn.set_option(ldap.OPT_X_TLS_DEMAND, True) conn.set_option(ldap.OPT_DEBUG_LEVEL, 255) conn.simple_bind_s(dn, password) @@ -120,8 +120,8 @@ class LDAPAuthBackend(object): ('"%s"' % newpassword).encode('utf-16').lstrip('\377\376') ) ldif = [(ldap.MOD_REPLACE, - smart_bytes(self.pwd_attr), - smart_bytes(self._crypt_password(newpassword)))] + self.pwd_attr, + force_bytes(self._crypt_password(newpassword)))] try: self.conn.modify_s(user_dn, ldif) except ldap.LDAPError as e: diff --git a/modoboa/limits/tests/test_api.py b/modoboa/limits/tests/test_api.py index b13a2246f..d671c0c2c 100644 --- a/modoboa/limits/tests/test_api.py +++ b/modoboa/limits/tests/test_api.py @@ -103,7 +103,9 @@ class APIAdminLimitsTestCase(lib_tests.ModoAPITestCase): data["name"] = "test5.com" response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, 403) - self.assertEqual(response.content, '"Domains: limit reached"') + self.assertEqual( + response.content.decode('utf-8'), '"Domains: limit reached"' + ) self.client.delete( reverse("api:domain-detail", diff --git a/modoboa/relaydomains/tests.py b/modoboa/relaydomains/tests.py index 957a165c1..fe773212f 100644 --- a/modoboa/relaydomains/tests.py +++ b/modoboa/relaydomains/tests.py @@ -2,8 +2,6 @@ from __future__ import unicode_literals -import json - from django.core.files.base import ContentFile from django.core.urlresolvers import reverse from django.test import TestCase @@ -88,7 +86,7 @@ class RelayDomainsTestCase(ModoTestCase, Operations): url = reverse("admin:_domain_list") response = self.client.get(url) self.assertEqual(response.status_code, 200) - content = json.loads(response.content) + content = response.json() self.assertIn("relaydomain.tld", content["rows"]) self.assertIn("Relay Domain", content["rows"]) diff --git a/tox.ini b/tox.ini index 370af28f5..6d4ef5c08 100644 --- a/tox.ini +++ b/tox.ini @@ -5,7 +5,7 @@ envlist = py27,py34 changedir=test_project commands = python ../tests.py - coverage run --source modoboa manage.py test modoboa.core modoboa.lib modoboa.admin.tests modoboa.limits modoboa.relaydomains + coverage run --source modoboa manage.py test modoboa.core modoboa.lib modoboa.admin modoboa.limits modoboa.relaydomains coverage report --show-missing deps = -r{toxinidir}/test-requirements.txt From 20e34bc19929d4518674cb2d043ed3d9eab2bea7 Mon Sep 17 00:00:00 2001 From: Arthur Vuillard Date: Thu, 11 May 2017 17:56:16 +0200 Subject: [PATCH 15/23] checkpoint for modoboa.admin tests in both python 2 and 3 --- modoboa/admin/models/alias.py | 2 +- modoboa/admin/tests/test_account.py | 8 ++--- modoboa/admin/tests/test_alias.py | 4 +-- modoboa/admin/tests/test_api.py | 34 +++++++++---------- modoboa/admin/tests/test_domain.py | 4 +-- modoboa/admin/tests/test_import_.py | 9 ++--- modoboa/admin/tests/test_user.py | 6 ++-- modoboa/admin/views/import_.py | 4 ++- .../commands/generate_postfix_maps.py | 5 +-- modoboa/core/password_hashers/base.py | 2 +- requirements.txt | 2 +- tox.ini | 3 +- 12 files changed, 41 insertions(+), 42 deletions(-) diff --git a/modoboa/admin/models/alias.py b/modoboa/admin/models/alias.py index 81274b538..5232468c8 100644 --- a/modoboa/admin/models/alias.py +++ b/modoboa/admin/models/alias.py @@ -63,7 +63,7 @@ class Alias(AdminObject): def generate_random_address(cls): """Generate a random address (local part).""" m = hashlib.md5() - for x in random.sample(xrange(10000000), 60): + for x in random.sample(range(10000000), 60): m.update(str(x)) return m.hexdigest()[:20] diff --git a/modoboa/admin/tests/test_account.py b/modoboa/admin/tests/test_account.py index 86ad607d5..b0927329b 100644 --- a/modoboa/admin/tests/test_account.py +++ b/modoboa/admin/tests/test_account.py @@ -243,12 +243,12 @@ class AccountTestCase(ModoTestCase): url = reverse("admin:account_detail", args=[account.pk]) response = self.client.get(url) self.assertEqual(response.status_code, 200) - self.assertIn("Summary", response.content) - self.assertIn("Administered domains", response.content) - self.assertNotIn("Resources usage", response.content) + self.assertIn("Summary", response.content.decode()) + self.assertIn("Administered domains", response.content.decode()) + self.assertNotIn("Resources usage", response.content.decode()) self.set_global_parameter("enable_admin_limits", True, app="limits") response = self.client.get(url) - self.assertIn("Resources usage", response.content) + self.assertIn("Resources usage", response.content.decode()) @skipIf(NO_LDAP, "No ldap module installed") diff --git a/modoboa/admin/tests/test_alias.py b/modoboa/admin/tests/test_alias.py index 62dc19bd2..b75310b24 100644 --- a/modoboa/admin/tests/test_alias.py +++ b/modoboa/admin/tests/test_alias.py @@ -265,8 +265,8 @@ class AliasTestCase(ModoTestCase): url = reverse("admin:alias_detail", args=[account.pk]) response = self.client.get(url) self.assertEqual(response.status_code, 200) - self.assertIn("Summary", response.content) - self.assertIn("Recipients", response.content) + self.assertIn("Summary", response.content.decode()) + self.assertIn("Recipients", response.content.decode()) def test_domainadmin_restrictions(self): """Check that restrictions are applied.""" diff --git a/modoboa/admin/tests/test_api.py b/modoboa/admin/tests/test_api.py index 2b35c54e4..44f146bcb 100644 --- a/modoboa/admin/tests/test_api.py +++ b/modoboa/admin/tests/test_api.py @@ -148,7 +148,7 @@ class DomainAliasAPITestCase(ModoAPITestCase): response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, 201) - dalias = json.loads(response.content) + dalias = response.json() dalias = models.DomainAlias.objects.filter( pk=dalias["pk"]).first() self.assertEqual(dalias.target, target) @@ -217,17 +217,17 @@ class AccountAPITestCase(ModoAPITestCase): url = reverse("api:account-list") response = self.client.get(url) self.assertEqual(response.status_code, 200) - response = json.loads(response.content) + response = response.json() self.assertEqual(len(response), 5) response = self.client.get("{}?domain=test.com".format(url)) self.assertEqual(response.status_code, 200) - response = json.loads(response.content) + response = response.json() self.assertEqual(len(response), 2) response = self.client.get("{}?domain=pouet.com".format(url)) self.assertEqual(response.status_code, 200) - response = json.loads(response.content) + response = response.json() self.assertEqual(len(response), 0) def test_create_account(self): @@ -236,7 +236,7 @@ class AccountAPITestCase(ModoAPITestCase): response = self.client.post(url, self.ACCOUNT_DATA, format="json") self.assertEqual(response.status_code, 201) - account = json.loads(response.content) + account = response.json() user = core_models.User.objects.filter(pk=account["pk"]).first() self.assertIsNot(user, None) self.assertIsNot(user.mailbox, None) @@ -260,7 +260,7 @@ class AccountAPITestCase(ModoAPITestCase): self.assertEqual(response.status_code, 201) domain = admin_models.Domain.objects.get(name="test.com") admin = core_models.User.objects.get( - pk=json.loads(response.content)["pk"]) + pk=response.json()["pk"]) self.assertIn(admin, domain.admins) data["username"] = "domain_admin" @@ -275,7 +275,7 @@ class AccountAPITestCase(ModoAPITestCase): url = reverse("api:account-list") response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, 201) - account = json.loads(response.content) + account = response.json() user = core_models.User.objects.filter(pk=account["pk"]).first() self.assertIsNot(user, None) self.assertIsNot(user.mailbox, None) @@ -302,7 +302,7 @@ class AccountAPITestCase(ModoAPITestCase): url = reverse("api:account-list") response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, 400) - errors = json.loads(response.content) + errors = response.json() self.assertIn("password", errors) def test_create_account_as_domadmin(self): @@ -320,7 +320,7 @@ class AccountAPITestCase(ModoAPITestCase): url = reverse("api:account-list") response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, 400) - errors = json.loads(response.content) + errors = response.json() self.assertIn("domain", errors) def test_create_account_bad_master_user(self): @@ -330,7 +330,7 @@ class AccountAPITestCase(ModoAPITestCase): url = reverse("api:account-list") response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, 400) - errors = json.loads(response.content) + errors = response.json() self.assertIn("master_user", errors) def test_update_account(self): @@ -432,12 +432,12 @@ class AccountAPITestCase(ModoAPITestCase): response = self.client.get( "{}?email={}".format(url, "user@test.com")) self.assertEqual(response.status_code, 200) - content = json.loads(response.content) + content = response.json() self.assertTrue(content["exists"]) response = self.client.get( "{}?email={}".format(url, "pipo@test.com")) self.assertEqual(response.status_code, 200) - content = json.loads(response.content) + content = response.json() self.assertFalse(content["exists"]) response = self.client.get(url) self.assertEqual(response.status_code, 400) @@ -487,12 +487,12 @@ class AliasAPITestCase(ModoAPITestCase): url = reverse("api:alias-list") response = self.client.get(url) self.assertEqual(response.status_code, 200) - response = json.loads(response.content) + response = response.json() self.assertEqual(len(response), 3) response = self.client.get("{}?domain=test.com".format(url)) self.assertEqual(response.status_code, 200) - response = json.loads(response.content) + response = response.json() self.assertEqual(len(response), 3) def test_get_alias(self): @@ -501,7 +501,7 @@ class AliasAPITestCase(ModoAPITestCase): url = reverse("api:alias-detail", args=[al.pk]) response = self.client.get(url) self.assertEqual(response.status_code, 200) - response = json.loads(response.content) + response = response.json() self.assertEqual(response["recipients"], ["user@test.com"]) def test_create_alias(self): @@ -510,7 +510,7 @@ class AliasAPITestCase(ModoAPITestCase): response = self.client.post(url, self.ALIAS_DATA, format="json") self.assertEqual(response.status_code, 201) - alias = json.loads(response.content) + alias = json.loads(response.content.decode('utf-8')) alias = models.Alias.objects.filter(pk=alias["pk"]).first() domadmin = core_models.User.objects.get(username="admin@test.com") self.assertTrue(domadmin.can_access(alias)) @@ -541,7 +541,7 @@ class AliasAPITestCase(ModoAPITestCase): data["address"] = "alias_fromapi@test2.com" response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, 400) - errors = json.loads(response.content) + errors = response.json() self.assertIn("address", errors) def test_update_alias(self): diff --git a/modoboa/admin/tests/test_domain.py b/modoboa/admin/tests/test_domain.py index 0a7d64f57..597860fc0 100644 --- a/modoboa/admin/tests/test_domain.py +++ b/modoboa/admin/tests/test_domain.py @@ -4,8 +4,6 @@ from __future__ import unicode_literals """Domain related test cases.""" -import json - from django.core.urlresolvers import reverse from modoboa.core.models import User @@ -213,7 +211,7 @@ class DomainTestCase(ModoTestCase): """Test the 'domain_flat_list' view.""" response = self.client.get(reverse("admin:domain_flat_list")) self.assertEqual(response.status_code, 200) - content = json.loads(response.content) + content = response.json() self.assertIn("test.com", content) self.assertIn("test2.com", content) diff --git a/modoboa/admin/tests/test_import_.py b/modoboa/admin/tests/test_import_.py index c495ea61a..ac3252245 100644 --- a/modoboa/admin/tests/test_import_.py +++ b/modoboa/admin/tests/test_import_.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals from django.core.files.base import ContentFile from django.core.urlresolvers import reverse +from django.utils.encoding import force_bytes from modoboa.core.models import User from modoboa.lib.tests import ModoTestCase @@ -26,7 +27,7 @@ class ImportTestCase(ModoTestCase): def test_domains_import(self): response = self.client.get(reverse("admin:domain_import")) self.assertEqual(response.status_code, 200) - self.assertIn("Provide a CSV", response.content) + self.assertIn("Provide a CSV", response.content.decode()) f = ContentFile(b"""domain; domain1.com; 1000; 100; True domain; domain2.com; 1000; 200; False @@ -88,7 +89,7 @@ domainalias;test.alias;test.com;True def test_identities_import(self): response = self.client.get(reverse("admin:identity_import")) self.assertEqual(response.status_code, 200) - self.assertIn("Provide a CSV", response.content) + self.assertIn("Provide a CSV", response.content.decode()) f = ContentFile(b""" account; user1@test.com; toto; User; One; True; SimpleUsers; user1@test.com; 0 @@ -179,7 +180,7 @@ account; user1@test.com; toto; User; One; True; SimpleUsers; user1@test.com; ; t reverse("admin:identity_import"), {"sourcefile": f, "crypt_password": True} ) - self.assertIn('wrong quota value', resp.content) + self.assertIn('wrong quota value', resp.content.decode()) def test_import_domain_by_domainadmin(self): """Check if a domain admin is not allowed to import a domain.""" @@ -213,7 +214,7 @@ account; user1@test.com; toto; User; One; True; SimpleUsers; user1@test.com; 40 reverse("admin:identity_import"), {"sourcefile": f, "crypt_password": True} ) - self.assertIn("Domain quota exceeded", resp.content) + self.assertIn("Domain quota exceeded", resp.content.decode()) def test_import_missing_quota(self): f = ContentFile(b""" diff --git a/modoboa/admin/tests/test_user.py b/modoboa/admin/tests/test_user.py index 65a6efc06..2eccfc900 100644 --- a/modoboa/admin/tests/test_user.py +++ b/modoboa/admin/tests/test_user.py @@ -2,8 +2,6 @@ from __future__ import unicode_literals -import json - from django.core.urlresolvers import reverse from modoboa.core.models import User @@ -30,7 +28,7 @@ class ForwardTestCase(ModoTestCase): ) response = self.client.get(url) self.assertEqual(response.status_code, 200) - content = json.loads(response.content) + content = response.json() self.assertIn("user@extdomain.com", content["content"]) self.assertNotIn("user@test.com", content["content"]) forward = Alias.objects.get(address="user@test.com", internal=False) @@ -43,5 +41,5 @@ class ForwardTestCase(ModoTestCase): self.ajax_post(url, {"dest": "", "keepcopies": False}) response = self.client.get(url) self.assertEqual(response.status_code, 200) - content = json.loads(response.content) + content = response.json() self.assertNotIn("user@extdomain.com", content["content"]) diff --git a/modoboa/admin/views/import_.py b/modoboa/admin/views/import_.py index 951ae5940..a157e71f9 100644 --- a/modoboa/admin/views/import_.py +++ b/modoboa/admin/views/import_.py @@ -4,6 +4,8 @@ from __future__ import unicode_literals import csv +from io import StringIO + from django.contrib.auth.decorators import ( login_required, permission_required, user_passes_test ) @@ -11,7 +13,7 @@ from django.core.urlresolvers import reverse from django.db import transaction from django.shortcuts import render from django.utils.translation import ugettext as _ -from django.utils.encoding import smart_str +from django.utils.encoding import smart_str, force_text from reversion import revisions as reversion diff --git a/modoboa/core/management/commands/generate_postfix_maps.py b/modoboa/core/management/commands/generate_postfix_maps.py index 5fc15510f..2e0f7fc05 100644 --- a/modoboa/core/management/commands/generate_postfix_maps.py +++ b/modoboa/core/management/commands/generate_postfix_maps.py @@ -10,6 +10,7 @@ from django.conf import settings from django.core.management.base import BaseCommand from django.template import Context, Template from django.utils import timezone +from django.utils.encoding import force_bytes import dj_database_url @@ -68,7 +69,7 @@ class Command(BaseCommand): fname not in self.__checksums) if condition: return True - with open(path) as fp: + with open(path, mode="rb") as fp: checksum = hashlib.md5(fp.read()).hexdigest() return checksum == self.__checksums[fname]["checksum"] @@ -142,7 +143,7 @@ query = {{ query|safe }} fullpath = os.path.join(destdir, mapobject.filename) with open(fullpath, "w") as fp: fp.write(content) - return hashlib.md5(content).hexdigest() + return hashlib.md5(force_bytes(content)).hexdigest() def handle(self, *args, **options): """Command entry point.""" diff --git a/modoboa/core/password_hashers/base.py b/modoboa/core/password_hashers/base.py index 3f4b0c8d6..73c4b237a 100644 --- a/modoboa/core/password_hashers/base.py +++ b/modoboa/core/password_hashers/base.py @@ -92,7 +92,7 @@ class CRYPTHasher(PasswordHasher): def _encrypt(self, clearvalue, salt=None): if salt is None: - salt = "".join(Random().sample(string.letters + string.digits, 2)) + salt = "".join(Random().sample(string.ascii_letters + string.digits, 2)) return crypt.crypt(clearvalue, salt) diff --git a/requirements.txt b/requirements.txt index 48c0f433b..08dcfb547 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,7 +15,7 @@ djangorestframework==3.6.2 argparse # bcrypt requires libffi-dev bcrypt==2.0.0 -dnspython==1.12.0 +dnspython==1.15.0 feedparser==5.2.1 gevent==1.2.1 ipaddress diff --git a/tox.ini b/tox.ini index 6d4ef5c08..70d428bdb 100644 --- a/tox.ini +++ b/tox.ini @@ -4,8 +4,7 @@ envlist = py27,py34 [testenv] changedir=test_project commands = - python ../tests.py - coverage run --source modoboa manage.py test modoboa.core modoboa.lib modoboa.admin modoboa.limits modoboa.relaydomains + coverage run --source modoboa manage.py test modoboa.admin coverage report --show-missing deps = -r{toxinidir}/test-requirements.txt From 67729a8f30e0c2e33e50baa569e14daddec12447 Mon Sep 17 00:00:00 2001 From: Arthur Vuillard Date: Thu, 11 May 2017 17:58:02 +0200 Subject: [PATCH 16/23] just comment in tox.ini --- tox.ini | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tox.ini b/tox.ini index 70d428bdb..8f39cd949 100644 --- a/tox.ini +++ b/tox.ini @@ -4,7 +4,10 @@ envlist = py27,py34 [testenv] changedir=test_project commands = +; python ../tests.py +; coverage run --source modoboa manage.py test modoboa.core modoboa.lib modoboa.admin modoboa.limits modoboa.relaydomains coverage run --source modoboa manage.py test modoboa.admin + coverage report --show-missing deps = -r{toxinidir}/test-requirements.txt From 4d109a68579d18e5e8c59376bcc5bd09668bce13 Mon Sep 17 00:00:00 2001 From: Arthur Vuillard Date: Thu, 11 May 2017 18:19:33 +0200 Subject: [PATCH 17/23] fix a test in python3 --- modoboa/core/models.py | 5 +++-- modoboa/core/password_hashers/base.py | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/modoboa/core/models.py b/modoboa/core/models.py index 928c0c873..bfd3a173b 100644 --- a/modoboa/core/models.py +++ b/modoboa/core/models.py @@ -9,7 +9,7 @@ from django.core.urlresolvers import reverse from django.db import models from django.utils import timezone from django.utils.encoding import ( - python_2_unicode_compatible, smart_bytes, smart_text) + python_2_unicode_compatible, smart_bytes, smart_text, force_str) from django.utils.functional import cached_property from django.utils.translation import ugettext as _, ugettext_lazy @@ -154,7 +154,7 @@ class User(PermissionsMixin): match = self.password_expr.match(self.password) if match is None: return False - raw_value = smart_bytes(raw_value) + raw_value = force_str(raw_value) scheme = match.group(1) val2 = match.group(2) hasher = get_password_hasher(scheme) @@ -425,6 +425,7 @@ class User(PermissionsMixin): row += result[1] csvwriter.writerow(row) + reversion.register(User) diff --git a/modoboa/core/password_hashers/base.py b/modoboa/core/password_hashers/base.py index 73c4b237a..586ff6efd 100644 --- a/modoboa/core/password_hashers/base.py +++ b/modoboa/core/password_hashers/base.py @@ -13,6 +13,7 @@ from random import Random import string from django.utils.crypto import constant_time_compare +from django.utils.encoding import force_text class PasswordHasher(object): @@ -35,7 +36,7 @@ class PasswordHasher(object): """ if self._target == 'ldap': return base64.b64encode(pwhash) - return pwhash + return force_text(pwhash) def encrypt(self, clearvalue): """Encrypt a password. From 588318e6383f5a960030c15ccf16533d98f8b940 Mon Sep 17 00:00:00 2001 From: Arthur Vuillard Date: Thu, 11 May 2017 18:23:47 +0200 Subject: [PATCH 18/23] fix a test in python3 --- modoboa/admin/app_settings.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modoboa/admin/app_settings.py b/modoboa/admin/app_settings.py index 611601d66..889e4755c 100644 --- a/modoboa/admin/app_settings.py +++ b/modoboa/admin/app_settings.py @@ -7,6 +7,7 @@ import os from django import forms from django.conf import settings from django.utils.translation import ugettext_lazy +from django.utils.encoding import force_text from modoboa.lib.form_utils import YesNoField, SeparatorField from modoboa.lib.sysutils import exec_cmd @@ -129,6 +130,7 @@ class AdminParametersForm(param_forms.AdminParametersForm): except OSError: hide_fields = True else: + version = force_text(version) if code or not version.strip().startswith("2"): hide_fields = True else: From 2f5394a0f1a55a7d3ec7fa2494cc7eedea0c5899 Mon Sep 17 00:00:00 2001 From: Arthur Vuillard Date: Thu, 11 May 2017 21:26:26 +0200 Subject: [PATCH 19/23] fix python ldap connection on python2 --- ldap-requirements.txt | 1 - modoboa/lib/ldap_utils.py | 5 ++++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ldap-requirements.txt b/ldap-requirements.txt index a74d0b79e..212b582a2 100644 --- a/ldap-requirements.txt +++ b/ldap-requirements.txt @@ -1,2 +1 @@ -pyldap django-auth-ldap diff --git a/modoboa/lib/ldap_utils.py b/modoboa/lib/ldap_utils.py index 2952f0198..b25335c17 100644 --- a/modoboa/lib/ldap_utils.py +++ b/modoboa/lib/ldap_utils.py @@ -69,7 +69,10 @@ class LDAPAuthBackend(object): def _get_conn(self, dn, password): """Get a connection from the server.""" - conn = ldap.initialize(self.server_uri, bytes_mode=False) + kwargs = {} + if six.PY3: + kwargs['bytes_mode'] = False + conn = ldap.initialize(self.server_uri, **kwargs) conn.set_option(ldap.OPT_X_TLS_DEMAND, True) conn.set_option(ldap.OPT_DEBUG_LEVEL, 255) conn.simple_bind_s(dn, password) From 6fe26028d496b6e703f3d692dad012c2f4666747 Mon Sep 17 00:00:00 2001 From: Arthur Vuillard Date: Thu, 11 May 2017 23:22:22 +0200 Subject: [PATCH 20/23] tests for modoboa.admin for python 2 and 3 --- modoboa/admin/forms/domain.py | 8 +++++--- modoboa/admin/models/alias.py | 10 ++++++---- modoboa/admin/models/domain.py | 10 ++++++++-- modoboa/admin/tests/test_domain.py | 8 ++++---- modoboa/admin/tests/test_export.py | 9 +++++---- modoboa/admin/tests/test_import_.py | 11 +++++------ modoboa/admin/views/export.py | 6 +++--- modoboa/admin/views/import_.py | 9 ++++++++- modoboa/core/models.py | 17 +++++++++-------- modoboa/core/password_hashers/base.py | 12 ++++++------ modoboa/lib/ldap_utils.py | 1 + tox.ini | 5 ++--- 12 files changed, 62 insertions(+), 44 deletions(-) diff --git a/modoboa/admin/forms/domain.py b/modoboa/admin/forms/domain.py index a51a912b3..bac68ec72 100644 --- a/modoboa/admin/forms/domain.py +++ b/modoboa/admin/forms/domain.py @@ -8,6 +8,7 @@ from django import forms from django.core.urlresolvers import reverse from django.http import QueryDict from django.utils.translation import ugettext as _, ugettext_lazy +from django.utils.encoding import force_text from modoboa.core import signals as core_signals from modoboa.core.models import User @@ -98,7 +99,7 @@ class DomainFormGeneral(forms.ModelForm, DynamicForm): if label is not None: self.add_error( "name", _("A %s with this name already exists") - % unicode(label) + % force_text(label) ) condition = ( self.cleaned_data["quota"] != 0 and @@ -120,7 +121,8 @@ class DomainFormGeneral(forms.ModelForm, DynamicForm): if default_mailbox_quota == 0: self.add_error("default_mailbox_quota", msg) self.aliases = [] - for k in cleaned_data.keys(): + copied_data = cleaned_data.copy() + for k in copied_data.keys(): if not k.startswith("aliases"): continue if cleaned_data[k] == "": @@ -137,7 +139,7 @@ class DomainFormGeneral(forms.ModelForm, DynamicForm): if label is not None: self.add_error( k, _("A %s with this name already exists") - % unicode(label) + % force_text(label) ) else: self.aliases.append(cleaned_data[k]) diff --git a/modoboa/admin/models/alias.py b/modoboa/admin/models/alias.py index 5232468c8..b137dfa08 100644 --- a/modoboa/admin/models/alias.py +++ b/modoboa/admin/models/alias.py @@ -7,7 +7,9 @@ import random from django.core.urlresolvers import reverse from django.db import models -from django.utils.encoding import python_2_unicode_compatible, smart_text +from django.utils.encoding import ( + python_2_unicode_compatible, smart_text, force_str, force_bytes +) from django.utils.translation import ugettext as _, ugettext_lazy from reversion import revisions as reversion @@ -64,7 +66,7 @@ class Alias(AdminObject): """Generate a random address (local part).""" m = hashlib.md5() for x in random.sample(range(10000000), 60): - m.update(str(x)) + m.update(force_bytes(x)) return m.hexdigest()[:20] @property @@ -111,7 +113,6 @@ class Alias(AdminObject): is defined on the associated domain """ - to_create = [] for address in set(address_list): if not address: continue @@ -195,10 +196,11 @@ class Alias(AdminObject): self.post_create(user) def to_csv(self, csvwriter): - row = ["alias", self.address.encode("utf-8"), self.enabled] + row = ["alias", force_str(self.address), self.enabled] row += self.recipients csvwriter.writerow(row) + reversion.register(Alias) diff --git a/modoboa/admin/models/domain.py b/modoboa/admin/models/domain.py index a521418b7..516378605 100644 --- a/modoboa/admin/models/domain.py +++ b/modoboa/admin/models/domain.py @@ -8,7 +8,9 @@ from functools import reduce from django.db import models from django.db.models.manager import Manager from django.utils import timezone -from django.utils.encoding import python_2_unicode_compatible, smart_text +from django.utils.encoding import ( + python_2_unicode_compatible, smart_text, force_text +) from django.utils.functional import cached_property from django.utils.translation import ugettext as _, ugettext_lazy @@ -281,7 +283,10 @@ class Domain(AdminObject): def to_csv(self, csvwriter): """Export domain and domain aliases to CSV format.""" csvwriter.writerow([ - "domain", self.name, self.quota, self.default_mailbox_quota, + "domain", + force_text(self.name), + self.quota, + self.default_mailbox_quota, self.enabled ]) for dalias in self.domainalias_set.all(): @@ -296,6 +301,7 @@ class Domain(AdminObject): for domalias in self.domainalias_set.all(): domalias.post_create(creator) + reversion.register(Domain) diff --git a/modoboa/admin/tests/test_domain.py b/modoboa/admin/tests/test_domain.py index 597860fc0..f069ca13d 100644 --- a/modoboa/admin/tests/test_domain.py +++ b/modoboa/admin/tests/test_domain.py @@ -222,12 +222,12 @@ class DomainTestCase(ModoTestCase): url = reverse("admin:domain_detail", args=[domain.pk]) response = self.client.get(url) self.assertEqual(response.status_code, 200) - self.assertIn("Summary", response.content) - self.assertIn("Administrators", response.content) - self.assertNotIn("Resources usage", response.content) + self.assertIn("Summary", response.content.decode()) + self.assertIn("Administrators", response.content.decode()) + self.assertNotIn("Resources usage", response.content.decode()) self.set_global_parameter("enable_domain_limits", True, app="limits") response = self.client.get(url) - self.assertIn("Resources usage", response.content) + self.assertIn("Resources usage", response.content.decode()) def test_statitics_widget(self): """Test statistics display in dashboard.""" diff --git a/modoboa/admin/tests/test_export.py b/modoboa/admin/tests/test_export.py index 1b47bb906..5c5f7debd 100644 --- a/modoboa/admin/tests/test_export.py +++ b/modoboa/admin/tests/test_export.py @@ -3,6 +3,7 @@ from __future__ import unicode_literals from django.core.urlresolvers import reverse +from django.utils.encoding import force_text from modoboa.lib.tests import ModoTestCase @@ -38,8 +39,8 @@ class ExportTestCase(ModoTestCase): ) def assertListEqual(self, list1, list2): - list1 = list1.split("\r\n") - list2 = list2.split("\r\n") + list1 = force_text(list1).split("\r\n") + list2 = force_text(list2).split("\r\n") self.assertEqual(len(list1), len(list2)) for entry in list1: if not entry: @@ -85,7 +86,7 @@ class ExportTestCase(ModoTestCase): response = self.__export_identities( idtfilter="account", grpfilter="SuperAdmins" ) - elements = response.content.strip().split(";") + elements = response.content.decode().strip().split(";") self.assertEqual(len(elements), 9) elements[2] = "" self.assertEqual( @@ -104,6 +105,6 @@ class ExportTestCase(ModoTestCase): def test_export_aliases(self): response = self.__export_identities(idtfilter="alias") self.assertEqual( - response.content.strip(), + response.content.decode().strip(), "alias;alias@test.com;True;user@test.com\r\nalias;forward@test.com;True;user@external.com\r\nalias;postmaster@test.com;True;test@truc.fr;toto@titi.com" ) diff --git a/modoboa/admin/tests/test_import_.py b/modoboa/admin/tests/test_import_.py index ac3252245..dfb612bd3 100644 --- a/modoboa/admin/tests/test_import_.py +++ b/modoboa/admin/tests/test_import_.py @@ -4,7 +4,6 @@ from __future__ import unicode_literals from django.core.files.base import ContentFile from django.core.urlresolvers import reverse -from django.utils.encoding import force_bytes from modoboa.core.models import User from modoboa.lib.tests import ModoTestCase @@ -84,14 +83,14 @@ domainalias;test.alias;test.com;True "sourcefile": f } ) - self.assertIn('Object already exists: domainalias', resp.content) + self.assertIn('Object already exists: domainalias', resp.content.decode()) def test_identities_import(self): response = self.client.get(reverse("admin:identity_import")) self.assertEqual(response.status_code, 200) self.assertIn("Provide a CSV", response.content.decode()) - f = ContentFile(b""" + f = ContentFile(""" account; user1@test.com; toto; User; One; True; SimpleUsers; user1@test.com; 0 account; Truc@test.com; toto; René; Truc; True; DomainAdmins; truc@test.com; 5; test.com alias; alias1@test.com; True; user1@test.com @@ -193,7 +192,7 @@ domain; domain2.com; 1000; 200; False reverse("admin:identity_import"), {"sourcefile": f, "crypt_password": True} ) - self.assertIn("You are not allowed to import domains", resp.content) + self.assertIn("You are not allowed to import domains", resp.content.decode()) f = ContentFile(b""" domainalias; domalias1.com; test.com; True """, name="identities.csv") @@ -202,7 +201,7 @@ domainalias; domalias1.com; test.com; True {"sourcefile": f, "crypt_password": True} ) self.assertIn( - "You are not allowed to import domain aliases", resp.content) + "You are not allowed to import domain aliases", resp.content.decode()) def test_import_quota_too_big(self): self.client.logout() @@ -231,7 +230,7 @@ account; user1@test.com; toto; User; One; True; SimpleUsers; user1@test.com ) def test_import_duplicate(self): - f = ContentFile(b""" + f = ContentFile(""" account; admin@test.com; toto; Admin; ; True; DomainAdmins; admin@test.com; 0; test.com account; truc@test.com; toto; René; Truc; True; DomainAdmins; truc@test.com; 0; test.com """, name="identities.csv") diff --git a/modoboa/admin/views/export.py b/modoboa/admin/views/export.py index 1044a9e29..89e6dabb0 100644 --- a/modoboa/admin/views/export.py +++ b/modoboa/admin/views/export.py @@ -3,7 +3,6 @@ from __future__ import unicode_literals import csv -import io from rfc6266 import build_header @@ -15,6 +14,7 @@ from django.http import HttpResponse from django.shortcuts import render from django.utils.translation import ugettext as _ from django.utils.encoding import smart_str +from django.utils import six from ..forms import ExportIdentitiesForm, ExportDomainsForm from ..lib import get_domains, get_identities @@ -50,7 +50,7 @@ def export_identities(request): if request.method == "POST": form = ExportIdentitiesForm(request.POST) form.is_valid() - fp = io.BytesIO() + fp = six.StringIO() csvwriter = csv.writer( fp, delimiter=smart_str(form.cleaned_data["sepchar"]) ) @@ -80,7 +80,7 @@ def export_domains(request): if request.method == "POST": form = ExportDomainsForm(request.POST) form.is_valid() - fp = io.BytesIO() + fp = six.StringIO() csvwriter = csv.writer( fp, delimiter=smart_str(form.cleaned_data["sepchar"]) ) diff --git a/modoboa/admin/views/import_.py b/modoboa/admin/views/import_.py index a157e71f9..44921471c 100644 --- a/modoboa/admin/views/import_.py +++ b/modoboa/admin/views/import_.py @@ -14,6 +14,7 @@ from django.db import transaction from django.shortcuts import render from django.utils.translation import ugettext as _ from django.utils.encoding import smart_str, force_text +from django.utils import six from reversion import revisions as reversion @@ -39,8 +40,14 @@ def importdata(request, formclass=ImportDataForm): form = formclass(request.POST, request.FILES) if form.is_valid(): try: + if six.PY2: + infile = request.FILES['sourcefile'] + else: + infile = StringIO( + force_text(request.FILES['sourcefile'].read()) + ) reader = csv.reader( - request.FILES['sourcefile'], + infile, delimiter=smart_str(form.cleaned_data['sepchar']) ) except csv.Error as inst: diff --git a/modoboa/core/models.py b/modoboa/core/models.py index bfd3a173b..baba71d07 100644 --- a/modoboa/core/models.py +++ b/modoboa/core/models.py @@ -9,7 +9,8 @@ from django.core.urlresolvers import reverse from django.db import models from django.utils import timezone from django.utils.encoding import ( - python_2_unicode_compatible, smart_bytes, smart_text, force_str) + python_2_unicode_compatible, smart_bytes, smart_text, force_str, force_text +) from django.utils.functional import cached_property from django.utils.translation import ugettext as _, ugettext_lazy @@ -411,13 +412,13 @@ class User(PermissionsMixin): """ row = [ "account", - smart_bytes(self.username), - smart_bytes(self.password), - smart_bytes(self.first_name), - smart_bytes(self.last_name), - self.is_active, - self.role, - smart_bytes(self.email) + force_text(self.username), + force_text(self.password), + force_text(self.first_name), + force_text(self.last_name), + force_text(self.is_active), + force_text(self.role), + force_text(self.email) ] results = signals.account_exported.send( sender=self.__class__, user=self) diff --git a/modoboa/core/password_hashers/base.py b/modoboa/core/password_hashers/base.py index 586ff6efd..a449c8088 100644 --- a/modoboa/core/password_hashers/base.py +++ b/modoboa/core/password_hashers/base.py @@ -13,7 +13,7 @@ from random import Random import string from django.utils.crypto import constant_time_compare -from django.utils.encoding import force_text +from django.utils.encoding import force_text, force_bytes class PasswordHasher(object): @@ -36,7 +36,7 @@ class PasswordHasher(object): """ if self._target == 'ldap': return base64.b64encode(pwhash) - return force_text(pwhash) + return pwhash def encrypt(self, clearvalue): """Encrypt a password. @@ -51,8 +51,8 @@ class PasswordHasher(object): :rtype: str :return: encrypted password """ - pwhash = self._b64encode(self._encrypt(clearvalue)) - return '%s%s' % (self.scheme, pwhash) + pwhash = self._b64encode(self._encrypt(force_text(clearvalue))) + return '%s%s' % (self.scheme, force_text(pwhash)) def verify(self, clearvalue, hashed_value): """Verify a password against a hashed value. @@ -109,7 +109,7 @@ class MD5Hasher(PasswordHasher): return '{MD5}' def _encrypt(self, clearvalue, salt=None): - obj = hashlib.md5(clearvalue) + obj = hashlib.md5(force_bytes(clearvalue)) return obj.hexdigest() @@ -125,7 +125,7 @@ class SHA256Hasher(PasswordHasher): return '{SHA256}' def _encrypt(self, clearvalue, salt=None): - return hashlib.sha256(clearvalue).digest() + return hashlib.sha256(force_bytes(clearvalue)).digest() def _b64encode(self, pwhash): """Encode :keyword:`pwhash` using base64. diff --git a/modoboa/lib/ldap_utils.py b/modoboa/lib/ldap_utils.py index b25335c17..57767b69f 100644 --- a/modoboa/lib/ldap_utils.py +++ b/modoboa/lib/ldap_utils.py @@ -40,6 +40,7 @@ import ldap from django.conf import settings from django.utils.translation import ugettext as _ from django.utils.encoding import force_bytes +from django.utils import six from modoboa.core.password_hashers import get_password_hasher from modoboa.lib.exceptions import InternalError diff --git a/tox.ini b/tox.ini index 8f39cd949..1f949ddfd 100644 --- a/tox.ini +++ b/tox.ini @@ -4,9 +4,8 @@ envlist = py27,py34 [testenv] changedir=test_project commands = -; python ../tests.py -; coverage run --source modoboa manage.py test modoboa.core modoboa.lib modoboa.admin modoboa.limits modoboa.relaydomains - coverage run --source modoboa manage.py test modoboa.admin + python ../tests.py + coverage run --source modoboa manage.py test modoboa.core modoboa.lib modoboa.admin modoboa.limits modoboa.relaydomains coverage report --show-missing deps = From 2c66b529554c5b1449913bf25583743cec0f7496 Mon Sep 17 00:00:00 2001 From: Arthur Vuillard Date: Thu, 11 May 2017 23:30:29 +0200 Subject: [PATCH 21/23] tests for modoboa.limits for python 2 and 3 --- modoboa/admin/serializers.py | 7 ++++--- modoboa/limits/tests/test_api.py | 2 +- modoboa/limits/tests/test_import.py | 6 +++--- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/modoboa/admin/serializers.py b/modoboa/admin/serializers.py index 3827ec139..878df8b05 100644 --- a/modoboa/admin/serializers.py +++ b/modoboa/admin/serializers.py @@ -5,6 +5,7 @@ from __future__ import unicode_literals from django.core.exceptions import ValidationError from django.shortcuts import get_object_or_404 from django.utils.translation import ugettext as _, ugettext_lazy +from django.utils.encoding import force_text from rest_framework import serializers from passwords import validators @@ -77,7 +78,7 @@ class DomainAliasSerializer(serializers.ModelSerializer): object_type="domain_aliases") except lib_exceptions.ModoboaException as inst: raise serializers.ValidationError({ - "domain": unicode(inst)}) + "domain": force_text(inst)}) domain_alias.save(creator=creator) return domain_alias @@ -254,7 +255,7 @@ class WritableAccountSerializer(AccountSerializer): object_type="mailboxes") except lib_exceptions.ModoboaException as inst: raise serializers.ValidationError({ - "domain": unicode(inst)}) + "domain": force_text(inst)}) quota = data.pop("quota", None) mb = admin_models.Mailbox( user=account, address=address, domain=domain, **data) @@ -347,7 +348,7 @@ class AliasSerializer(serializers.ModelSerializer): sender=self.__class__, context=self.domain, object_type="mailbox_aliases") except lib_exceptions.ModoboaException as inst: - raise serializers.ValidationError(unicode(inst)) + raise serializers.ValidationError(force_text(inst)) recipients = validated_data.pop("recipients", None) alias = admin_models.Alias(domain=self.domain, **validated_data) alias.save(creator=creator) diff --git a/modoboa/limits/tests/test_api.py b/modoboa/limits/tests/test_api.py index d671c0c2c..ecfbe9d8f 100644 --- a/modoboa/limits/tests/test_api.py +++ b/modoboa/limits/tests/test_api.py @@ -114,7 +114,7 @@ class APIAdminLimitsTestCase(lib_tests.ModoAPITestCase): response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, 400) self.assertEqual( - response.content, + response.content.decode(), '"You\'re not allowed to define unlimited values"') def test_domain_aliases_limit(self): diff --git a/modoboa/limits/tests/test_import.py b/modoboa/limits/tests/test_import.py index 7f8e4603f..dc743b438 100644 --- a/modoboa/limits/tests/test_import.py +++ b/modoboa/limits/tests/test_import.py @@ -31,7 +31,7 @@ class LimitImportTestCase(ModoTestCase): """Check domain aliases limit.""" self.client.force_login(self.reseller) self.assertFalse(limit.is_exceeded()) - f = ContentFile(b"""domainalias; domalias1.com; {domain}; True + f = ContentFile("""domainalias; domalias1.com; {domain}; True domainalias; domalias2.com; {domain}; True """.format(domain=self.domain), name="domains.csv") self.client.post( @@ -70,7 +70,7 @@ domainalias; domalias2.com; {domain}; True ) self.assertTrue(limit.is_exceeded()) - f = ContentFile(b"""account; admin3@{domain}; toto; User; One; True; DomainAdmins; admin3@{domain}; 5; {domain} + f = ContentFile("""account; admin3@{domain}; toto; User; One; True; DomainAdmins; admin3@{domain}; 5; {domain} """.format(domain=self.domain), name="domain_admins.csv") response = self.client.post( reverse("admin:identity_import"), { @@ -111,7 +111,7 @@ account; user3@{domain}; toto; User; One; True; SimpleUsers; user3@{domain}; 5 """Check mailbox aliases limit.""" self.client.force_login(self.dadmin) self.assertFalse(limit.is_exceeded()) - f = ContentFile(b"""alias; alias1@{domain}; True; user@{domain} + f = ContentFile("""alias; alias1@{domain}; True; user@{domain} alias; alias2@{domain}; True; user@{domain} """.format(domain=self.domain), name="aliases.csv") response = self.client.post( From b782cfde5de607ec92c0673b22b2ff51f7dd21cc Mon Sep 17 00:00:00 2001 From: Arthur Vuillard Date: Thu, 11 May 2017 23:36:21 +0200 Subject: [PATCH 22/23] remove obvious calls to unicode() --- modoboa/lib/email_utils.py | 3 ++- modoboa/lib/imap_utf7.py | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/modoboa/lib/email_utils.py b/modoboa/lib/email_utils.py index ed1015e8a..a0a3aaa74 100644 --- a/modoboa/lib/email_utils.py +++ b/modoboa/lib/email_utils.py @@ -12,6 +12,7 @@ import time from django.conf import settings from django.utils.encoding import smart_text from django.template.loader import render_to_string +from django.utils import six from modoboa.lib import u2u_decode @@ -77,7 +78,7 @@ class Email(object): """ fname = msg.get_filename() if fname is not None: - if type(fname) is unicode: + if type(fname) is six.text_type: fname = fname.encode("utf-8") decoded = decode_header(fname) value = ( diff --git a/modoboa/lib/imap_utf7.py b/modoboa/lib/imap_utf7.py index 611729a53..f90b13a51 100644 --- a/modoboa/lib/imap_utf7.py +++ b/modoboa/lib/imap_utf7.py @@ -53,6 +53,8 @@ http://svn.plone.org/svn/collective/mxmImapClient/trunk/imapUTF7.py """ from __future__ import print_function, unicode_literals +from django.utils.encoding import force_text + import binascii import codecs @@ -91,7 +93,7 @@ def encoder(s): def modified_unbase64(s): b = binascii.a2b_base64(s.replace(',', '/') + '===') - return unicode(b, 'utf-16be') + return force_text(b, encoding='utf-16be') def decoder(s): @@ -129,6 +131,8 @@ class StreamWriter(codecs.StreamWriter): def imap4_utf_7(name): if name == 'imap4-utf-7': return (encoder, decoder, StreamReader, StreamWriter) + + codecs.register(imap4_utf_7) From daa82cc0422661f18c5b71373f82332f36f584cc Mon Sep 17 00:00:00 2001 From: Arthur Vuillard Date: Fri, 12 May 2017 09:14:49 +0200 Subject: [PATCH 23/23] final fix ? --- modoboa/lib/cryptutils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modoboa/lib/cryptutils.py b/modoboa/lib/cryptutils.py index 717ef7f73..1cfcd5089 100644 --- a/modoboa/lib/cryptutils.py +++ b/modoboa/lib/cryptutils.py @@ -7,7 +7,7 @@ import base64 import random import string -from django.utils.encoding import force_bytes +from django.utils.encoding import force_bytes, force_text from Crypto.Cipher import AES @@ -39,7 +39,7 @@ def encrypt(clear): ) ciph = obj.encrypt(clear) ciph = base64.b64encode(ciph) - return ciph + return force_text(ciph) def decrypt(ciph):