From 2109feffd2f0e4e764bd85b8c6a467b4366d003a Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Wed, 10 May 2017 18:29:17 +0200 Subject: [PATCH] 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 )