diff --git a/modoboa/admin/api/v1/serializers.py b/modoboa/admin/api/v1/serializers.py
index 48f9c037c..bd840b952 100644
--- a/modoboa/admin/api/v1/serializers.py
+++ b/modoboa/admin/api/v1/serializers.py
@@ -178,7 +178,7 @@ class DomainAliasSerializer(serializers.ModelSerializer):
object_type="domain_aliases",
)
except lib_exceptions.ModoboaException as inst:
- raise serializers.ValidationError({"domain": force_str(inst)})
+ raise serializers.ValidationError({"domain": force_str(inst)}) from None
domain_alias.save(creator=creator)
return domain_alias
@@ -311,7 +311,7 @@ class AccountPasswordSerializer(serializers.ModelSerializer):
try:
password_validation.validate_password(value, self.instance)
except ValidationError as exc:
- raise serializers.ValidationError(exc.messages[0])
+ raise serializers.ValidationError(exc.messages[0]) from None
return value
def update(self, instance, validated_data):
@@ -363,7 +363,7 @@ class WritableAccountSerializer(AccountSerializer):
try:
validators.UTF8EmailValidator()(username)
except ValidationError as err:
- raise ValidationError({"username": err.message})
+ raise ValidationError({"username": err.message}) from None
mailbox = data.get("mailbox")
if mailbox is None:
if not self.instance:
@@ -396,7 +396,9 @@ class WritableAccountSerializer(AccountSerializer):
object_type="mailboxes",
)
except lib_exceptions.ModoboaException as inst:
- raise serializers.ValidationError({"mailbox": force_str(inst)})
+ raise serializers.ValidationError(
+ {"mailbox": force_str(inst)}
+ ) from None
condition = not data.get("random_password") and (
data.get("password") or not self.partial
)
@@ -408,7 +410,9 @@ class WritableAccountSerializer(AccountSerializer):
data["password"], self.instance
)
except ValidationError as exc:
- raise serializers.ValidationError({"password": exc.messages[0]})
+ raise serializers.ValidationError(
+ {"password": exc.messages[0]}
+ ) from None
elif not self.instance:
raise serializers.ValidationError(
{"password": _("This field is required.")}
@@ -523,9 +527,9 @@ class AliasSerializer(serializers.ModelSerializer):
value, self.context["request"].user, instance=self.instance
)
except ValidationError as err:
- raise serializers.ValidationError(err)
+ raise serializers.ValidationError(err) from None
except AliasExists:
- raise serializers.ValidationError(_("This alias already exists"))
+ raise serializers.ValidationError(_("This alias already exists")) from None
return value.lower()
def create(self, validated_data):
diff --git a/modoboa/admin/api/v1/viewsets.py b/modoboa/admin/api/v1/viewsets.py
index f6ed5804f..c97a2512c 100644
--- a/modoboa/admin/api/v1/viewsets.py
+++ b/modoboa/admin/api/v1/viewsets.py
@@ -135,7 +135,7 @@ class AccountViewSet(
try:
user = core_models.User.objects.get(pk=pk)
except core_models.User.DoesNotExist:
- raise http.Http404
+ raise http.Http404 from None
serializer = self.get_serializer(user, data=request.data)
if serializer.is_valid():
serializer.save()
diff --git a/modoboa/admin/api/v2/serializers.py b/modoboa/admin/api/v2/serializers.py
index 547c3995e..c38f4ab77 100644
--- a/modoboa/admin/api/v2/serializers.py
+++ b/modoboa/admin/api/v2/serializers.py
@@ -94,7 +94,7 @@ class DomainSerializer(v1_serializers.DomainSerializer):
return domain
# 1. Create a domain administrator
- username = "%s@%s" % (domain_admin["username"], domain.name)
+ username = "{}@{}".format(domain_admin["username"], domain.name)
try:
da = core_models.User.objects.get(username=username)
except core_models.User.DoesNotExist:
@@ -244,7 +244,7 @@ class AdminGlobalParametersSerializer(serializers.Serializer):
except ValueError:
raise serializers.ValidationError(
_("This field only allows valid IP addresses (or networks)")
- )
+ ) from None
return value
def validate_auto_create_domain_and_mailbox(self, value):
@@ -479,7 +479,7 @@ class WritableAccountSerializer(v1_serializers.WritableAccountSerializer):
try:
validators.UTF8EmailValidator()(username)
except ValidationError as err:
- raise ValidationError({"username": err.message})
+ raise ValidationError({"username": err.message}) from None
mailbox = data.get("mailbox")
if mailbox is None:
if not self.instance:
@@ -501,7 +501,7 @@ class WritableAccountSerializer(v1_serializers.WritableAccountSerializer):
object_type="mailboxes",
)
except lib_exceptions.ModoboaException as inst:
- raise serializers.ValidationError({"mailbox": str(inst)})
+ raise serializers.ValidationError({"mailbox": str(inst)}) from None
if len(self.address) > 64:
raise serializers.ValidationError(
{
@@ -518,7 +518,9 @@ class WritableAccountSerializer(v1_serializers.WritableAccountSerializer):
data["password"], self.instance
)
except ValidationError as exc:
- raise serializers.ValidationError({"password": exc.messages[0]})
+ raise serializers.ValidationError(
+ {"password": exc.messages[0]}
+ ) from None
elif not self.instance:
raise serializers.ValidationError(
{"password": _("This field is required.")}
diff --git a/modoboa/admin/app_settings.py b/modoboa/admin/app_settings.py
index 36581f9b6..8f27d0269 100644
--- a/modoboa/admin/app_settings.py
+++ b/modoboa/admin/app_settings.py
@@ -212,7 +212,7 @@ class AdminParametersForm(param_forms.AdminParametersForm):
dpath = fpath
if dpath:
try:
- code, version = exec_cmd("%s --version" % dpath)
+ code, version = exec_cmd(f"{dpath} --version")
except OSError:
hide_fields = True
else:
@@ -259,12 +259,12 @@ class AdminParametersForm(param_forms.AdminParametersForm):
except ValueError:
raise forms.ValidationError(
gettext_lazy("This field only allows valid IP addresses (or networks)")
- )
+ ) from None
return value
def clean(self):
"""Check MX options."""
- cleaned_data = super(AdminParametersForm, self).clean()
+ cleaned_data = super().clean()
condition = (
cleaned_data.get("enable_mx_checks")
and cleaned_data.get("domains_must_have_authorized_mx")
diff --git a/modoboa/admin/forms/account.py b/modoboa/admin/forms/account.py
index 39dcb78c7..f43c1979a 100644
--- a/modoboa/admin/forms/account.py
+++ b/modoboa/admin/forms/account.py
@@ -71,7 +71,7 @@ class AccountFormGeneral(forms.ModelForm):
labels = {"is_active": gettext_lazy("Enabled")}
def __init__(self, user, *args, **kwargs):
- super(AccountFormGeneral, self).__init__(*args, **kwargs)
+ super().__init__(*args, **kwargs)
self.fields = OrderedDict(
(key, self.fields[key])
for key in [
@@ -163,7 +163,7 @@ class AccountFormGeneral(forms.ModelForm):
def clean(self):
"""Check master user mode."""
- super(AccountFormGeneral, self).clean()
+ super().clean()
if self.errors:
return self.cleaned_data
condition = (
@@ -200,7 +200,7 @@ class AccountFormGeneral(forms.ModelForm):
return self.cleaned_data
def save(self, commit=True):
- account = super(AccountFormGeneral, self).save(commit=False)
+ account = super().save(commit=False)
if self.user == account and not self.cleaned_data["is_active"]:
raise lib_exceptions.PermDeniedException(
_("You can't disable your own account")
@@ -355,7 +355,7 @@ class AccountFormMail(forms.Form, DynamicForm):
Check if quota is >= 0 only when the domain value is not used.
"""
- cleaned_data = super(AccountFormMail, self).clean()
+ cleaned_data = super().clean()
use_default_domain_quota = cleaned_data["quota_act"]
condition = (
not use_default_domain_quota
@@ -510,13 +510,13 @@ class AccountPermissionsForm(forms.Form, DynamicForm):
self.account = kwargs["instance"]
del kwargs["instance"]
- super(AccountPermissionsForm, self).__init__(*args, **kwargs)
+ super().__init__(*args, **kwargs)
if not hasattr(self, "account") or self.account is None:
return
qset = models.Domain.objects.get_for_admin(self.account)
for pos, dom in enumerate(qset):
- name = "domains_%d" % (pos + 1)
+ name = f"domains_{pos + 1}"
self._create_field(lib_fields.DomainNameField, name, dom.name)
if len(args) and isinstance(args[0], QueryDict):
self._load_from_qdict(args[0], "domains", lib_fields.DomainNameField)
@@ -580,7 +580,7 @@ class AccountForm(TabForms):
cbargs["account"] = kwargs["instances"]["general"]
results = signals.extra_account_forms.send(sender=self.__class__, **cbargs)
self.forms += reduce(lambda a, b: a + b, [result[1] for result in results])
- super(AccountForm, self).__init__(request, *args, **kwargs)
+ super().__init__(request, *args, **kwargs)
def extra_context(self, context):
account = self.instances["general"]
@@ -604,8 +604,8 @@ class AccountForm(TabForms):
if form["id"] == "general":
return True
- if hasattr(self, "check_%s" % form["id"]):
- if not getattr(self, "check_%s" % form["id"])(self.account):
+ if hasattr(self, "check_{}".format(form["id"])):
+ if not getattr(self, "check_{}".format(form["id"]))(self.account):
return False
return True
@@ -620,9 +620,9 @@ class AccountForm(TabForms):
def is_valid(self):
"""Two steps validation."""
self.instances["general"].oldgroup = self.instances["general"].role
- if super(AccountForm, self).is_valid(mandatory_only=True):
+ if super().is_valid(mandatory_only=True):
self.account = self.forms[0]["instance"].save()
- return super(AccountForm, self).is_valid(optional_only=True)
+ return super().is_valid(optional_only=True)
return False
def save(self):
@@ -646,7 +646,7 @@ class AccountWizard(WizardForm):
"""Account creation wizard."""
def __init__(self, request):
- super(AccountWizard, self).__init__(request)
+ super().__init__(request)
self.add_step(
WizardStep(
"general", AccountFormGeneral, _("General"), new_args=[request.user]
diff --git a/modoboa/admin/forms/alias.py b/modoboa/admin/forms/alias.py
index 70636a72e..ab95bf1a9 100644
--- a/modoboa/admin/forms/alias.py
+++ b/modoboa/admin/forms/alias.py
@@ -49,7 +49,7 @@ class AliasForm(forms.ModelForm, DynamicForm):
def __init__(self, user, *args, **kwargs):
self.user = user
- super(AliasForm, self).__init__(*args, **kwargs)
+ super().__init__(*args, **kwargs)
self.fields = OrderedDict(
(key, self.fields[key])
for key in [
@@ -80,7 +80,7 @@ class AliasForm(forms.ModelForm, DynamicForm):
self.fields["enabled"].widget.attrs["disabled"] = "disabled"
cpt = 1
for rcpt in alias.aliasrecipient_set.filter(alias__internal=False):
- name = "recipients_%d" % cpt
+ name = f"recipients_{cpt}"
self._create_field(forms.EmailField, name, rcpt.address, 2)
cpt += 1
@@ -92,7 +92,7 @@ class AliasForm(forms.ModelForm, DynamicForm):
try:
domain = Domain.objects.get(name=domname)
except Domain.DoesNotExist:
- raise forms.ValidationError(_("Domain does not exist"))
+ raise forms.ValidationError(_("Domain does not exist")) from None
if not self.user.can_access(domain):
raise forms.ValidationError(_("You don't have access to this domain"))
if not self.instance.pk:
@@ -101,12 +101,12 @@ class AliasForm(forms.ModelForm, DynamicForm):
sender=self.__class__, context=domain, object_type="mailbox_aliases"
)
except lib_exceptions.ModoboaException as inst:
- raise forms.ValidationError(inst)
+ raise forms.ValidationError(inst) from None
return self.cleaned_data["address"].lower()
def clean(self):
"""Check it there is at least one recipient."""
- super(AliasForm, self).clean()
+ super().clean()
random_address = self.cleaned_data.get("random_address")
if not random_address:
if not self.cleaned_data.get("address"):
@@ -121,7 +121,7 @@ class AliasForm(forms.ModelForm, DynamicForm):
def save(self, commit=True):
"""Custom save method."""
- alias = super(AliasForm, self).save(commit=False)
+ alias = super().save(commit=False)
if self.cleaned_data.get("random_address"):
alias.address = f"{Alias.generate_random_address()}@{alias.domain}"
else:
diff --git a/modoboa/admin/forms/export.py b/modoboa/admin/forms/export.py
index ba0863664..6d9c9d925 100644
--- a/modoboa/admin/forms/export.py
+++ b/modoboa/admin/forms/export.py
@@ -13,7 +13,7 @@ class ExportDataForm(forms.Form):
)
def __init__(self, *args, **kwargs):
- super(ExportDataForm, self).__init__(*args, **kwargs)
+ super().__init__(*args, **kwargs)
self.fields["sepchar"].widget.attrs = {"class": "col-md-1 form-control"}
def clean_sepchar(self):
diff --git a/modoboa/admin/forms/import_.py b/modoboa/admin/forms/import_.py
index a038d1e35..86f53dbaf 100644
--- a/modoboa/admin/forms/import_.py
+++ b/modoboa/admin/forms/import_.py
@@ -21,7 +21,7 @@ class ImportDataForm(forms.Form):
)
def __init__(self, *args, **kwargs):
- super(ImportDataForm, self).__init__(*args, **kwargs)
+ super().__init__(*args, **kwargs)
self.fields["sepchar"].widget.attrs = {"class": "col-md-1 form-control"}
def clean_sepchar(self):
diff --git a/modoboa/admin/lib.py b/modoboa/admin/lib.py
index f03da82ad..9715c06c7 100644
--- a/modoboa/admin/lib.py
+++ b/modoboa/admin/lib.py
@@ -253,7 +253,7 @@ def get_dns_records(name, typ, resolver=None):
exc_info=e,
)
except dns.name.NameTooLong as e:
- logger.error(_("DNS name is too long: %s" % name), exc_info=e)
+ logger.error(_("DNS name is too long: {}".format(name)), exc_info=e)
else:
return dns_answers
return None
@@ -359,7 +359,7 @@ def import_data(user, file_object, options: dict):
raise Conflict(
_("Object already exists: %s")
% options["sepchar"].join(row[:2])
- )
+ ) from None
cpt += 1
msg = _("%d objects imported successfully") % cpt
return True, msg
diff --git a/modoboa/admin/models/alias.py b/modoboa/admin/models/alias.py
index afdad2870..1fb857ee8 100644
--- a/modoboa/admin/models/alias.py
+++ b/modoboa/admin/models/alias.py
@@ -54,7 +54,7 @@ def validate_alias_address(
object_type="mailbox_aliases",
)
except ModoboaException as inst:
- raise ValidationError(str(inst))
+ raise ValidationError(str(inst)) from None
return local_part, domain
@@ -132,7 +132,7 @@ class Alias(AdminObject):
return "---"
rcpts = self.recipients
if rcpts_count > 1:
- return "%s, ..." % rcpts[0]
+ return f"{rcpts[0]}, ..."
return rcpts[0]
@property
@@ -150,7 +150,7 @@ class Alias(AdminObject):
def post_create(self, creator):
from modoboa.lib.permissions import grant_access_to_object
- super(Alias, self).post_create(creator)
+ super().post_create(creator)
if creator.is_superuser:
for admin in self.domain.admins:
grant_access_to_object(admin, self)
@@ -174,7 +174,7 @@ class Alias(AdminObject):
address, return_extension=True
)
if domname is None:
- raise BadRequest("%s %s" % (_("Invalid address"), address))
+ raise BadRequest("{} {}".format(_("Invalid address"), address))
domain = Domain.objects.filter(name=domname).first()
kwargs = {"address": address, "alias": self}
if (domain is not None) and (
@@ -189,7 +189,7 @@ class Alias(AdminObject):
rcpt = Mailbox.objects.filter(domain=domain, address=local_part).first()
if rcpt is None:
rcpt = Alias.objects.filter(
- address="%s@%s" % (local_part, domname)
+ address=f"{local_part}@{domname}"
).first()
if rcpt is None:
raise NotFound(
diff --git a/modoboa/admin/models/base.py b/modoboa/admin/models/base.py
index 0fc6ef7d3..06b71029b 100644
--- a/modoboa/admin/models/base.py
+++ b/modoboa/admin/models/base.py
@@ -62,10 +62,10 @@ class AdminObject(models.Model):
def save(self, *args, **kwargs):
creator = kwargs.pop("creator", None)
- super(AdminObject, self).save(*args, **kwargs)
+ super().save(*args, **kwargs)
if creator is not None:
self.post_create(creator)
def delete(self):
ungrant_access_to_object(self)
- super(AdminObject, self).delete()
+ super().delete()
diff --git a/modoboa/admin/models/domain.py b/modoboa/admin/models/domain.py
index ab1f515b0..6680c97f9 100644
--- a/modoboa/admin/models/domain.py
+++ b/modoboa/admin/models/domain.py
@@ -271,17 +271,17 @@ class Domain(mixins.MessageLimitMixin, AdminObject):
@property
def bind_format_dkim_public_key(self):
"""Return DKIM public key using Bind format."""
- record = "v=DKIM1;k=rsa;p=%s" % self.dkim_public_key
+ record = f"v=DKIM1;k=rsa;p={self.dkim_public_key}"
# TXT records longer than 255 characters need split into chunks
# split record into 74 character chunks (+2 for indent, +2 for quotes(")
# == 78 characters) to make more readable in text editors.
split_record = []
while record:
if len(record) > 74:
- split_record.append(' "%s"' % record[:74])
+ split_record.append(f' "{record[:74]}"')
record = record[74:]
else:
- split_record.append(' "%s"' % record)
+ split_record.append(f' "{record}"')
break
record = "\n".join(split_record)
return f"{self.dkim_key_selector}._domainkey.{self.name}. IN TXT (\n{record})"
@@ -343,7 +343,7 @@ class Domain(mixins.MessageLimitMixin, AdminObject):
if param_tools.get_global_parameter("auto_account_removal"):
User.objects.filter(mailbox__domain=self).delete()
elif self.mailbox_set.count():
- Quota.objects.filter(username__contains="@%s" % self.name).delete()
+ Quota.objects.filter(username__contains=f"@{self.name}").delete()
ungrant_access_to_objects(self.mailbox_set.all())
super().delete()
@@ -378,7 +378,9 @@ class Domain(mixins.MessageLimitMixin, AdminObject):
try:
self.quota = int(row[2].strip())
except ValueError:
- raise BadRequest(_("{}: invalid quota value for domain").format(self.name))
+ raise BadRequest(
+ _("{}: invalid quota value for domain").format(self.name)
+ ) from None
try:
self.default_mailbox_quota = int(row[3].strip())
except ValueError:
@@ -386,7 +388,7 @@ class Domain(mixins.MessageLimitMixin, AdminObject):
_("{}: invalid default mailbox quota value for domain").format(
self.name
)
- )
+ ) from None
if self.quota != 0 and self.default_mailbox_quota > self.quota:
raise BadRequest(
_(
diff --git a/modoboa/admin/models/domain_alias.py b/modoboa/admin/models/domain_alias.py
index 32a00b7f9..218795fbc 100644
--- a/modoboa/admin/models/domain_alias.py
+++ b/modoboa/admin/models/domain_alias.py
@@ -76,7 +76,7 @@ class DomainAlias(AdminObject):
try:
self.target = Domain.objects.get(name=domname)
except Domain.DoesNotExist:
- raise BadRequest(_("Unknown domain %s") % domname)
+ raise BadRequest(_("Unknown domain %s") % domname) from None
core_signals.can_create_object.send(
sender="import", context=self.target, object_type="domain_aliases"
)
diff --git a/modoboa/admin/models/mailbox.py b/modoboa/admin/models/mailbox.py
index 17403ef97..f1acaeb3c 100644
--- a/modoboa/admin/models/mailbox.py
+++ b/modoboa/admin/models/mailbox.py
@@ -114,7 +114,7 @@ class Mailbox(mixins.MessageLimitMixin, AdminObject):
return smart_str(self.full_address)
def __full_address(self, localpart):
- return "%s@%s" % (localpart, self.domain.name)
+ return f"{localpart}@{self.domain.name}"
@property
def full_address(self):
@@ -147,7 +147,7 @@ class Mailbox(mixins.MessageLimitMixin, AdminObject):
if not admin_params.get("handle_mailboxes"):
return None
if self.__mail_home is None:
- code, output = doveadm_cmd("user -f home %s" % self.full_address)
+ code, output = doveadm_cmd(f"user -f home {self.full_address}")
if code:
raise lib_exceptions.InternalError(
_("Failed to retrieve mailbox location (%s)") % output
@@ -384,5 +384,5 @@ class MailboxOperation(models.Model):
def __str__(self):
if self.type == "rename":
- return "Rename %s -> %s" % (self.argument, self.mailbox.mail_home)
- return "Delete %s" % self.argument
+ return f"Rename {self.argument} -> {self.mailbox.mail_home}"
+ return f"Delete {self.argument}"
diff --git a/modoboa/admin/templatetags/admin_tags.py b/modoboa/admin/templatetags/admin_tags.py
index 0da358765..43c5c9757 100644
--- a/modoboa/admin/templatetags/admin_tags.py
+++ b/modoboa/admin/templatetags/admin_tags.py
@@ -151,7 +151,7 @@ def domain_actions(user, domain):
actions = [
{
"name": "listidentities",
- "url": "{0}#list/?searchquery=@{1}".format(
+ "url": "{}#list/?searchquery=@{}".format(
reverse("admin:identity_list"), domain.name
),
"title": _("View the domain's identities"),
@@ -268,7 +268,7 @@ def domain_aliases(domain):
return "---"
res = ""
for alias in domain.aliases.all():
- res += "%s
" % alias.name
+ res += f"{alias.name}
"
return mark_safe(res)
@@ -286,7 +286,7 @@ def identity_modify_link(identity, active_tab="default"):
linkdef = {"label": identity.identity, "modal": True}
if identity.__class__.__name__ == "User":
linkdef["url"] = reverse("admin:account_change", args=[identity.id])
- linkdef["url"] += "?active_tab=%s" % active_tab
+ linkdef["url"] += f"?active_tab={active_tab}"
linkdef["modalcb"] = "admin.editaccount_cb"
else:
linkdef["url"] = reverse("admin:alias_change", args=[identity.id])
@@ -299,7 +299,7 @@ def domadmin_actions(daid, domid):
actions = [
{
"name": "removeperm",
- "url": "{0}?domid={1}&daid={2}".format(
+ "url": "{}?domid={}&daid={}".format(
reverse("admin:permission_remove"), domid, daid
),
"img": "fa fa-trash",
diff --git a/modoboa/admin/tests/test_account.py b/modoboa/admin/tests/test_account.py
index 7f0f38ff1..79d44a694 100644
--- a/modoboa/admin/tests/test_account.py
+++ b/modoboa/admin/tests/test_account.py
@@ -17,7 +17,7 @@ class AuthenticationTestCase(ModoTestCase):
@classmethod
def setUpTestData(cls): # NOQA:N802
"""Create test data."""
- super(AuthenticationTestCase, cls).setUpTestData()
+ super().setUpTestData()
cls.mb = factories.MailboxFactory(
domain__name="test.com",
address="user",
@@ -42,7 +42,7 @@ class AccountTestCase(ModoTestCase):
@classmethod
def setUpTestData(cls): # NOQA:N802
"""Create test data."""
- super(AccountTestCase, cls).setUpTestData()
+ super().setUpTestData()
factories.populate_database()
def test_crud(self):
@@ -612,7 +612,7 @@ class PermissionsTestCase(ModoTestCase):
@classmethod
def setUpTestData(cls): # NOQA:N802
"""Create test data."""
- super(PermissionsTestCase, cls).setUpTestData()
+ super().setUpTestData()
parameters = {}
for name, _definition in limits_utils.get_user_limit_templates():
parameters[f"deflt_user_{name}_limit"] = 2
@@ -625,7 +625,7 @@ class PermissionsTestCase(ModoTestCase):
def setUp(self):
"""Initiate test context."""
- super(PermissionsTestCase, self).setUp()
+ super().setUp()
self.values = {
"username": self.user.username,
"role": "DomainAdmins",
diff --git a/modoboa/admin/tests/test_alias.py b/modoboa/admin/tests/test_alias.py
index dafa6b2cd..f2984bdd1 100644
--- a/modoboa/admin/tests/test_alias.py
+++ b/modoboa/admin/tests/test_alias.py
@@ -13,7 +13,7 @@ class AliasTestCase(ModoTestCase):
@classmethod
def setUpTestData(cls): # NOQA:N802
"""Create test data."""
- super(AliasTestCase, cls).setUpTestData()
+ super().setUpTestData()
factories.populate_database()
def test_alias(self):
@@ -173,7 +173,7 @@ class AliasTestCase(ModoTestCase):
self.ajax_post(reverse("admin:alias_change", args=[fwd.id]), values)
self.assertEqual(fwd.recipients_count, 1)
- self.ajax_delete(reverse("admin:alias_delete") + "?selection=%d" % fwd.id, {})
+ self.ajax_delete(reverse("admin:alias_delete") + f"?selection={fwd.id}", {})
self.assertRaises(
Alias.DoesNotExist, Alias.objects.get, address="forward2@test.com"
)
diff --git a/modoboa/admin/tests/test_domain.py b/modoboa/admin/tests/test_domain.py
index b3df2ebc3..1b3859276 100644
--- a/modoboa/admin/tests/test_domain.py
+++ b/modoboa/admin/tests/test_domain.py
@@ -32,7 +32,7 @@ class DomainTestCase(ModoTestCase):
@classmethod
def setUpTestData(cls): # NOQA:N802
"""Create test data."""
- super(DomainTestCase, cls).setUpTestData()
+ super().setUpTestData()
factories.populate_database()
def test_create(self):
@@ -431,7 +431,7 @@ class DKIMTestCase(ModoTestCase):
@classmethod
def setUpTestData(cls): # NOQA:N802
"""Create test data."""
- super(DKIMTestCase, cls).setUpTestData()
+ super().setUpTestData()
factories.populate_database()
def setUp(self):
diff --git a/modoboa/admin/tests/test_domain_alias.py b/modoboa/admin/tests/test_domain_alias.py
index 9952cba6a..883e0a061 100644
--- a/modoboa/admin/tests/test_domain_alias.py
+++ b/modoboa/admin/tests/test_domain_alias.py
@@ -10,7 +10,7 @@ class DomainAliasTestCase(ModoTestCase):
@classmethod
def setUpTestData(cls): # NOQA:N802
"""Create test data."""
- super(DomainAliasTestCase, cls).setUpTestData()
+ super().setUpTestData()
factories.populate_database()
cls.dom = Domain.objects.get(name="test.com")
diff --git a/modoboa/admin/tests/test_export.py b/modoboa/admin/tests/test_export.py
index 0de2728e0..ae4c8df90 100644
--- a/modoboa/admin/tests/test_export.py
+++ b/modoboa/admin/tests/test_export.py
@@ -17,7 +17,7 @@ class ExportTestCase(ModoTestCase):
@classmethod
def setUpTestData(cls): # NOQA:N802
"""Create test data."""
- super(ExportTestCase, cls).setUpTestData()
+ super().setUpTestData()
factories.populate_database()
def __export_domains(self, domfilter=""):
@@ -31,7 +31,7 @@ class ExportTestCase(ModoTestCase):
def __export_identities(self, idtfilter="", grpfilter=""):
self.client.get(
reverse("admin:_identity_list")
- + "?grpfilter=%s&idtfilter=%s" % (grpfilter, idtfilter)
+ + f"?grpfilter={grpfilter}&idtfilter={idtfilter}"
)
return self.client.post(
reverse("admin:identity_export"), {"filename": "test.csv"}
diff --git a/modoboa/admin/tests/test_mailbox_operations.py b/modoboa/admin/tests/test_mailbox_operations.py
index 2cf2b7d5d..84a45a0de 100644
--- a/modoboa/admin/tests/test_mailbox_operations.py
+++ b/modoboa/admin/tests/test_mailbox_operations.py
@@ -20,12 +20,12 @@ class MailboxOperationTestCase(ModoTestCase):
@classmethod
def setUpTestData(cls): # NOQA:N802
"""Create test data."""
- super(MailboxOperationTestCase, cls).setUpTestData()
+ super().setUpTestData()
factories.populate_database()
def setUp(self):
"""Initiate test env."""
- super(MailboxOperationTestCase, self).setUp()
+ super().setUp()
self.workdir = tempfile.mkdtemp()
path = f"{self.workdir}/test.com/admin"
os.makedirs(path)
diff --git a/modoboa/admin/tests/test_mx.py b/modoboa/admin/tests/test_mx.py
index 902490cd2..a2e967b2c 100644
--- a/modoboa/admin/tests/test_mx.py
+++ b/modoboa/admin/tests/test_mx.py
@@ -23,7 +23,7 @@ class MXTestCase(ModoTestCase):
@classmethod
def setUpTestData(cls): # NOQA:N802
"""Create some data."""
- super(MXTestCase, cls).setUpTestData()
+ super().setUpTestData()
cls.domain = factories.DomainFactory(name="modoboa.org")
# should not exist
cls.bad_domain = factories.DomainFactory(name="does-not-exist.example.com")
@@ -297,7 +297,7 @@ class DNSChecksTestCase(ModoTestCase):
@classmethod
def setUpTestData(cls): # NOQA:N802
"""Create some data."""
- super(DNSChecksTestCase, cls).setUpTestData()
+ super().setUpTestData()
cls.domain = factories.DomainFactory(name="dns-checks.com")
@mock.patch("gevent.socket.gethostbyname")
diff --git a/modoboa/admin/tests/test_password_schemes.py b/modoboa/admin/tests/test_password_schemes.py
index de48eda3f..7a5d56fdc 100644
--- a/modoboa/admin/tests/test_password_schemes.py
+++ b/modoboa/admin/tests/test_password_schemes.py
@@ -10,7 +10,7 @@ class PasswordSchemesTestCase(ModoTestCase):
@classmethod
def setUpTestData(cls): # NOQA:N802
"""Create test data."""
- super(PasswordSchemesTestCase, cls).setUpTestData()
+ super().setUpTestData()
factories.populate_database()
def _create_account(self):
diff --git a/modoboa/admin/tests/test_repair.py b/modoboa/admin/tests/test_repair.py
index 6a6a1e6bb..3a23901c3 100644
--- a/modoboa/admin/tests/test_repair.py
+++ b/modoboa/admin/tests/test_repair.py
@@ -13,7 +13,7 @@ class RepairTestCase(ModoTestCase):
@classmethod
def setUpTestData(cls): # NOQA:N802
"""Create some data."""
- super(RepairTestCase, cls).setUpTestData()
+ super().setUpTestData()
factories.populate_database()
def test_management_command(self):
diff --git a/modoboa/admin/tests/test_user.py b/modoboa/admin/tests/test_user.py
index f4614aab4..b5096ff92 100644
--- a/modoboa/admin/tests/test_user.py
+++ b/modoboa/admin/tests/test_user.py
@@ -12,7 +12,7 @@ class ForwardTestCase(ModoTestCase):
"""User forward test cases."""
def setUp(self):
- super(ForwardTestCase, self).setUp()
+ super().setUp()
populate_database()
def test_set_forward(self):
diff --git a/modoboa/admin/views/alias.py b/modoboa/admin/views/alias.py
index ac491811d..8b3e5c109 100644
--- a/modoboa/admin/views/alias.py
+++ b/modoboa/admin/views/alias.py
@@ -27,7 +27,7 @@ def _validate_alias(request, form, successmsg, callback=None):
try:
alias = form.save()
except IntegrityError:
- raise Conflict(_("Alias with this name already exists"))
+ raise Conflict(_("Alias with this name already exists")) from None
if callback:
callback(request.user, alias)
return render_to_json_response(successmsg)
@@ -110,13 +110,13 @@ class AliasDetailView(auth_mixins.PermissionRequiredMixin, generic.DetailView):
def has_permission(self):
"""Check object-level access."""
- result = super(AliasDetailView, self).has_permission()
+ result = super().has_permission()
if not result:
return result
return self.request.user.can_access(self.get_object())
def get_context_data(self, **kwargs):
"""Add information to context."""
- context = super(AliasDetailView, self).get_context_data(**kwargs)
+ context = super().get_context_data(**kwargs)
context["selection"] = "identities"
return context
diff --git a/modoboa/admin/views/dns.py b/modoboa/admin/views/dns.py
index cfdde03eb..778f24571 100644
--- a/modoboa/admin/views/dns.py
+++ b/modoboa/admin/views/dns.py
@@ -16,15 +16,11 @@ class MXDomainDetailView(auth_mixins.PermissionRequiredMixin, generic.DetailView
def get_queryset(self):
"""Add some prefetching."""
- return (
- super(MXDomainDetailView, self)
- .get_queryset()
- .prefetch_related("mxrecord_set")
- )
+ return super().get_queryset().prefetch_related("mxrecord_set")
def get_context_data(self, **kwargs):
"""Add extra variables."""
- context = super(MXDomainDetailView, self).get_context_data(**kwargs)
+ context = super().get_context_data(**kwargs)
context.update({"title": _("MX records of {}").format(self.object.name)})
return context
@@ -38,14 +34,10 @@ class DNSBLDomainDetailView(auth_mixins.PermissionRequiredMixin, generic.DetailV
def get_queryset(self):
"""Add some prefetching."""
- return (
- super(DNSBLDomainDetailView, self)
- .get_queryset()
- .prefetch_related("dnsblresult_set")
- )
+ return super().get_queryset().prefetch_related("dnsblresult_set")
def get_context_data(self, **kwargs):
"""Add extra variables."""
- context = super(DNSBLDomainDetailView, self).get_context_data(**kwargs)
+ context = super().get_context_data(**kwargs)
context.update({"title": _("DNSBL summary for {}").format(self.object.name)})
return context
diff --git a/modoboa/admin/views/identity.py b/modoboa/admin/views/identity.py
index 09aa11a8f..985c8b528 100644
--- a/modoboa/admin/views/identity.py
+++ b/modoboa/admin/views/identity.py
@@ -75,7 +75,7 @@ def list_quotas(request):
)
mboxes = mboxes.exclude(quota=0)
if sort_order in ["address", "quota"]:
- mboxes = mboxes.order_by("%s%s" % (sort_dir, sort_order))
+ mboxes = mboxes.order_by(f"{sort_dir}{sort_order}")
elif sort_order in ("quota_value__bytes", "quota_usage"):
db_type = db_type()
if db_type == "mysql":
@@ -85,9 +85,9 @@ def list_quotas(request):
if sort_order == "quota_value__bytes":
mboxes = mboxes.extra(
select={"quota_value__bytes": "admin_quota.bytes"},
- where=["admin_quota.username=%s" % where],
+ where=[f"admin_quota.username={where}"],
tables=["admin_quota", "admin_domain"],
- order_by=["%s%s" % (sort_dir, sort_order)],
+ order_by=[f"{sort_dir}{sort_order}"],
)
else:
if db_type == "postgres":
@@ -102,9 +102,9 @@ def list_quotas(request):
)
mboxes = mboxes.extra(
select={"quota_usage": select},
- where=["admin_quota.username=%s" % where],
+ where=[f"admin_quota.username={where}"],
tables=["admin_quota", "admin_domain"],
- order_by=["%s%s" % (sort_dir, sort_order)],
+ order_by=[f"{sort_dir}{sort_order}"],
)
else:
raise BadRequest(_("Invalid request"))
@@ -199,7 +199,7 @@ def remove_permission(request):
account = User.objects.get(pk=daid)
domain = Domain.objects.get(pk=domid)
except (User.DoesNotExist, Domain.DoesNotExist):
- raise BadRequest(_("Invalid request"))
+ raise BadRequest(_("Invalid request")) from None
if not request.user.can_access(account) or not request.user.can_access(domain):
raise PermDeniedException
domain.remove_admin(account)
@@ -215,14 +215,14 @@ class AccountDetailView(auth_mixins.PermissionRequiredMixin, generic.DetailView)
def has_permission(self):
"""Check object-level access."""
- result = super(AccountDetailView, self).has_permission()
+ result = super().has_permission()
if not result:
return result
return self.request.user.can_access(self.get_object())
def get_context_data(self, **kwargs):
"""Add information to context."""
- context = super(AccountDetailView, self).get_context_data(**kwargs)
+ context = super().get_context_data(**kwargs)
del context["user"]
result = signals.extra_account_dashboard_widgets.send(
self.__class__, user=self.request.user, account=self.object
diff --git a/modoboa/calendars/factories.py b/modoboa/calendars/factories.py
index b8b0b32f5..ac3b37e46 100644
--- a/modoboa/calendars/factories.py
+++ b/modoboa/calendars/factories.py
@@ -12,7 +12,7 @@ class UserCalendarFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.UserCalendar
- name = factory.Sequence(lambda n: "User calendar %s" % n)
+ name = factory.Sequence(lambda n: f"User calendar {n}")
mailbox = factory.SubFactory(admin_factories.MailboxFactory)
@@ -21,7 +21,7 @@ class SharedCalendarFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.SharedCalendar
- name = factory.Sequence(lambda n: "Shared calendar %s" % n)
+ name = factory.Sequence(lambda n: f"Shared calendar {n}")
domain = factory.SubFactory(admin_factories.DomainFactory)
diff --git a/modoboa/calendars/models.py b/modoboa/calendars/models.py
index ec0c88ba3..35cd954ac 100644
--- a/modoboa/calendars/models.py
+++ b/modoboa/calendars/models.py
@@ -162,6 +162,7 @@ class AccessRule(models.Model):
access = "r" if self.read else ""
access += "w" if self.write else ""
return smart_str(
- "%s access rule to %s -> %s"
- % (self.mailbox, self.calendar, access if access else "no access")
+ "{} access rule to {} -> {}".format(
+ self.mailbox, self.calendar, access if access else "no access"
+ )
)
diff --git a/modoboa/contacts/management/commands/import_contacts.py b/modoboa/contacts/management/commands/import_contacts.py
index f1fffc527..d13aa822b 100644
--- a/modoboa/contacts/management/commands/import_contacts.py
+++ b/modoboa/contacts/management/commands/import_contacts.py
@@ -44,7 +44,7 @@ class Command(BaseCommand):
).first()
if not addressbook:
raise CommandError(
- "Address Book for email '%s' not found" % options["email"]
+ "Address Book for email '{}' not found".format(options["email"])
)
try:
import_csv_file(
@@ -55,5 +55,5 @@ class Command(BaseCommand):
options.get("carddav_password"),
)
except RuntimeError as err:
- raise CommandError(err)
+ raise CommandError(err) from None
self.stdout.write(self.style.SUCCESS("File was imported successfuly"))
diff --git a/modoboa/contacts/tests.py b/modoboa/contacts/tests.py
index 0535c4d93..9225399ce 100644
--- a/modoboa/contacts/tests.py
+++ b/modoboa/contacts/tests.py
@@ -24,7 +24,7 @@ class TestDataMixin:
@classmethod
def setUpTestData(cls):
"""Create test data."""
- super(TestDataMixin, cls).setUpTestData()
+ super().setUpTestData()
admin_factories.populate_database()
cls.user = core_models.User.objects.get(username="user@test.com")
cls.addressbook = cls.user.addressbook_set.first()
diff --git a/modoboa/core/api/v1/viewsets.py b/modoboa/core/api/v1/viewsets.py
index 6cf22cb4f..2f4dad928 100644
--- a/modoboa/core/api/v1/viewsets.py
+++ b/modoboa/core/api/v1/viewsets.py
@@ -45,7 +45,7 @@ class AccountViewSet(GetThrottleViewsetMixin, viewsets.ViewSet):
device = StaticDevice.objects.create(
user=request.user, name=f"{request.user} static device"
)
- for cpt in range(10):
+ for _cpt in range(10):
token = StaticToken.random_token()
device.token_set.create(token=token)
django_otp.login(self.request, request.user.totpdevice_set.first())
@@ -82,7 +82,7 @@ class AccountViewSet(GetThrottleViewsetMixin, viewsets.ViewSet):
if device is None:
return response.Response({"error": _("2FA is not enabled")}, status=403)
device.token_set.all().delete()
- for cpt in range(10):
+ for _cpt in range(10):
token = StaticToken.random_token()
device.token_set.create(token=token)
return response.Response(
diff --git a/modoboa/core/api/v2/serializers.py b/modoboa/core/api/v2/serializers.py
index a25196ecd..80527b975 100644
--- a/modoboa/core/api/v2/serializers.py
+++ b/modoboa/core/api/v2/serializers.py
@@ -180,21 +180,21 @@ class CoreGlobalParametersSerializer(serializers.Serializer):
try:
value % {"user": "toto"}
except (KeyError, ValueError):
- raise serializers.ValidationError(_("Invalid syntax"))
+ raise serializers.ValidationError(_("Invalid syntax")) from None
return value
def validate_ldap_sync_account_dn_template(self, value):
try:
value % {"user": "toto"}
except (KeyError, ValueError):
- raise serializers.ValidationError(_("Invalid syntax"))
+ raise serializers.ValidationError(_("Invalid syntax")) from None
return value
def validate_ldap_search_filter(self, value):
try:
value % {"user": "toto"}
except (KeyError, ValueError, TypeError):
- raise serializers.ValidationError(_("Invalid syntax"))
+ raise serializers.ValidationError(_("Invalid syntax")) from None
return value
def validate_rounds_number(self, value):
@@ -416,7 +416,7 @@ class PasswordRecoverySmsConfirmSerializer(serializers.Serializer):
except KeyError:
raise CustomValidationError(
{"reason": "totp secret not set in session"}, 403
- )
+ ) from None
if not oath.accept_totp(self.context["totp_secret"], data["sms_totp"])[0]:
raise CustomValidationError({"reason": "Wrong totp"}, 400)
@@ -445,7 +445,7 @@ class PasswordRecoverySmsResendSerializer(serializers.Serializer):
except KeyError:
raise CustomValidationError(
{"reason": "totp secret not set in session"}, status.HTTP_403_FORBIDDEN
- )
+ ) from None
# Attempt to get user, will raise an error if pk is not valid
self.context["user"] = User.objects.filter(
@@ -501,7 +501,7 @@ class PasswordRecoveryConfirmSerializer(serializers.Serializer):
try:
password_validation.validate_password(data["new_password1"], user)
except djangoValidationError as e:
- raise PasswordRequirementsFailure(e.messages)
+ raise PasswordRequirementsFailure(e.messages) from None
self.context["user"] = user
return super().validate(data)
diff --git a/modoboa/core/app_settings.py b/modoboa/core/app_settings.py
index a3b200990..a63f8112a 100644
--- a/modoboa/core/app_settings.py
+++ b/modoboa/core/app_settings.py
@@ -512,7 +512,7 @@ class GeneralParametersForm(param_forms.AdminParametersForm):
}
def __init__(self, *args, **kwargs):
- super(GeneralParametersForm, self).__init__(*args, **kwargs)
+ super().__init__(*args, **kwargs)
self.fields["default_top_redirection"].choices = enabled_applications()
self._add_visibilty_rules(sms_backends.get_all_backend_visibility_rules())
self.fields["password_scheme"].choices = get_password_scheme()
@@ -532,7 +532,7 @@ class GeneralParametersForm(param_forms.AdminParametersForm):
try:
tpl % {"user": "toto"}
except (KeyError, ValueError):
- raise forms.ValidationError(_("Invalid syntax"))
+ raise forms.ValidationError(_("Invalid syntax")) from None
return tpl
def clean_ldap_sync_account_dn_template(self):
@@ -540,7 +540,7 @@ class GeneralParametersForm(param_forms.AdminParametersForm):
try:
tpl % {"user": "toto"}
except (KeyError, ValueError):
- raise forms.ValidationError(_("Invalid syntax"))
+ raise forms.ValidationError(_("Invalid syntax")) from None
return tpl
def clean_ldap_search_filter(self):
@@ -548,7 +548,7 @@ class GeneralParametersForm(param_forms.AdminParametersForm):
try:
ldap_filter % {"user": "toto"}
except (KeyError, ValueError, TypeError):
- raise forms.ValidationError(_("Invalid syntax"))
+ raise forms.ValidationError(_("Invalid syntax")) from None
return ldap_filter
def clean_rounds_number(self):
@@ -613,10 +613,7 @@ class GeneralParametersForm(param_forms.AdminParametersForm):
{"first_name": "givenName", "email": "mail", "last_name": "sn"},
)
ldap_uri = "ldaps://" if values["ldap_secured"] == "ssl" else "ldap://"
- ldap_uri += "%s:%s" % (
- values[backend.srv_address_setting_name],
- values[backend.srv_port_setting_name],
- )
+ ldap_uri += f"{values[backend.srv_address_setting_name]}:{values[backend.srv_port_setting_name]}"
setattr(settings, backend.setting_fullname("SERVER_URI"), ldap_uri)
if values["ldap_secured"] == "starttls":
setattr(settings, backend.setting_fullname("START_TLS"), True)
diff --git a/modoboa/core/commands/__init__.py b/modoboa/core/commands/__init__.py
index 4759b0666..049ee7120 100644
--- a/modoboa/core/commands/__init__.py
+++ b/modoboa/core/commands/__init__.py
@@ -25,7 +25,7 @@ class Command:
{"BACKEND": ("django.template.backends.django.DjangoTemplates")}
]
)
- self._templates_dir = "%s/templates" % os.path.dirname(__file__)
+ self._templates_dir = f"{os.path.dirname(__file__)}/templates"
def _render_template(self, tplfile, env):
"""Render an HTML template."""
@@ -70,7 +70,7 @@ def scan_for_commands(dirname=""):
else:
cmdclassname = cmdname.capitalize()
try:
- cmdclass = getattr(cmdmod, "%sCommand" % cmdclassname)
+ cmdclass = getattr(cmdmod, f"{cmdclassname}Command")
except AttributeError:
continue
result[cmdname] = cmdclass
@@ -83,9 +83,10 @@ def handle_command_line():
parser = argparse.ArgumentParser(
description="A set of utilities to ease the installation of Modoboa.",
epilog="""Available commands:
-%s
-"""
- % "\n".join(["\t%s" % c for c in sorted(commands)]),
+{}
+""".format(
+ "\n".join([f"\t{c}" for c in sorted(commands)])
+ ),
)
parser.add_argument(
"--verbose", action="store_true", help="Activate verbose output"
@@ -94,7 +95,7 @@ def handle_command_line():
(args, remaining) = parser.parse_known_args()
if args.command not in commands:
- print("Unknown command '%s'" % args.command, file=sys.stderr)
+ print(f"Unknown command '{args.command}'", file=sys.stderr)
sys.exit(1)
commands[args.command](commands, verbose=args.verbose).run(remaining)
diff --git a/modoboa/core/commands/help.py b/modoboa/core/commands/help.py
index 2c7049b8f..bd5514711 100644
--- a/modoboa/core/commands/help.py
+++ b/modoboa/core/commands/help.py
@@ -8,14 +8,14 @@ class HelpCommand(Command):
help = "Display the help message associated to a specific command" # NOQA:A003
def __init__(self, *args, **kwargs):
- super(HelpCommand, self).__init__(*args, **kwargs)
+ super().__init__(*args, **kwargs)
self._parser.add_argument("name", type=str, help="A command name")
def handle(self, parsed_args):
if parsed_args.name not in self._commands:
- print("Unknown command: %s" % parsed_args.name, file=sys.stderr)
+ print(f"Unknown command: {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
+ cmd._parser.prog = f"modoboa-admin.py {parsed_args.name}"
cmd._parser.description = cmd.help
cmd._parser.print_help()
diff --git a/modoboa/core/forms.py b/modoboa/core/forms.py
index fdc80c42c..25a646304 100644
--- a/modoboa/core/forms.py
+++ b/modoboa/core/forms.py
@@ -133,7 +133,7 @@ class APIAccessForm(forms.Form):
def __init__(self, *args, **kwargs):
"""Initialize form."""
user = kwargs.pop("user")
- super(APIAccessForm, self).__init__(*args, **kwargs)
+ super().__init__(*args, **kwargs)
self.fields["enable_api_access"].initial = hasattr(user, "auth_token")
diff --git a/modoboa/core/management/commands/cleanlogs.py b/modoboa/core/management/commands/cleanlogs.py
index b7332a6e5..f44d6dabd 100644
--- a/modoboa/core/management/commands/cleanlogs.py
+++ b/modoboa/core/management/commands/cleanlogs.py
@@ -38,7 +38,7 @@ class Command(BaseCommand):
self.verbose = options["verbose"]
log_maximum_age = param_tools.get_global_parameter("log_maximum_age")
- self.__vprint("Deleting logs older than %d days..." % log_maximum_age)
+ self.__vprint(f"Deleting logs older than {log_maximum_age} days...")
limit = timezone.now() - datetime.timedelta(log_maximum_age)
Log.objects.filter(date_created__lt=limit).delete()
self.__vprint("Done.")
diff --git a/modoboa/core/models.py b/modoboa/core/models.py
index b88c4c4d7..351fbb904 100644
--- a/modoboa/core/models.py
+++ b/modoboa/core/models.py
@@ -373,15 +373,17 @@ class User(AbstractUser):
if len(row) < 8 or not row[7].strip():
raise BadRequest(
_(
- "The simple user '%s' must have a valid email address"
- % self.username
+ "The simple user '{}' must have a valid email address".format(
+ self.username
+ )
)
)
if self.username != row[7].strip():
raise BadRequest(
_(
- "username and email fields must not differ for '%s'"
- % self.username
+ "username and email fields must not differ for '{}'".format(
+ self.username
+ )
)
)
diff --git a/modoboa/core/password_hashers/__init__.py b/modoboa/core/password_hashers/__init__.py
index 379732224..b5c1ce167 100644
--- a/modoboa/core/password_hashers/__init__.py
+++ b/modoboa/core/password_hashers/__init__.py
@@ -28,7 +28,7 @@ def get_password_hasher(scheme: str) -> type[PasswordHasher]:
"""
try:
scheme = scheme.replace("-", "")
- hasher = globals()["%sHasher" % scheme.upper()]
+ hasher = globals()[f"{scheme.upper()}Hasher"]
except KeyError:
hasher = PLAINHasher
return hasher
diff --git a/modoboa/core/password_hashers/advanced.py b/modoboa/core/password_hashers/advanced.py
index 8b44d0367..12366b9b1 100644
--- a/modoboa/core/password_hashers/advanced.py
+++ b/modoboa/core/password_hashers/advanced.py
@@ -148,7 +148,7 @@ if argon2_hasher is not None:
def __init__(
self,
):
- super(ARGON2IDHasher, self).__init__()
+ super().__init__()
parameters = dict()
diff --git a/modoboa/core/password_hashers/base.py b/modoboa/core/password_hashers/base.py
index e1ff5056d..76314dfa4 100644
--- a/modoboa/core/password_hashers/base.py
+++ b/modoboa/core/password_hashers/base.py
@@ -80,7 +80,7 @@ class PasswordHasher(metaclass=MetaHasher):
:return: encrypted password
"""
pwhash = self._b64encode(self._encrypt(force_str(clearvalue)))
- return "%s%s" % (self.scheme, force_str(pwhash))
+ return f"{self.scheme}{force_str(pwhash)}"
def verify(self, clearvalue, hashed_value):
"""Verify a password against a hashed value.
diff --git a/modoboa/core/templatetags/core_tags.py b/modoboa/core/templatetags/core_tags.py
index 948f524ba..714a6043d 100644
--- a/modoboa/core/templatetags/core_tags.py
+++ b/modoboa/core/templatetags/core_tags.py
@@ -181,7 +181,7 @@ def colorize_level(level):
}
if level not in classes:
return level
- return "
%s
" % (classes[level], level) + return f"{level}
" @register.filter @@ -238,7 +238,7 @@ def connected_users(parser, token): except ValueError: raise template.TemplateSyntaxError( "connected_users usage: {% connected_users as users %}" - ) + ) from None return ConnectedUsers(varname) @@ -279,14 +279,13 @@ def display_messages(msgs): timeout = "undefined" return mark_safe( - """ + f""" """ - % (level, text, timeout) ) diff --git a/modoboa/core/tests/test_core.py b/modoboa/core/tests/test_core.py index a7be45f93..9b5f35c71 100644 --- a/modoboa/core/tests/test_core.py +++ b/modoboa/core/tests/test_core.py @@ -21,7 +21,7 @@ class AuthenticationTestCase(ModoTestCase): @classmethod def setUpTestData(cls): # NOQA:N802 """Create test data.""" - super(AuthenticationTestCase, cls).setUpTestData() + super().setUpTestData() cls.account = factories.UserFactory( username="user@test.com", groups=("SimpleUsers",) ) @@ -115,7 +115,7 @@ class ProfileTestCase(ModoTestCase): @classmethod def setUpTestData(cls): # NOQA:N802 """Create test data.""" - super(ProfileTestCase, cls).setUpTestData() + super().setUpTestData() cls.account = factories.UserFactory( username="user@test.com", groups=("SimpleUsers",) ) @@ -231,7 +231,7 @@ class APIAccessFormTestCase(ModoTestCase): @classmethod def setUpTestData(cls): # NOQA:N802 """Create test data.""" - super(APIAccessFormTestCase, cls).setUpTestData() + super().setUpTestData() cls.account = factories.UserFactory( username="user@test.com", groups=("SimpleUsers",) ) diff --git a/modoboa/core/tests/test_ldap.py b/modoboa/core/tests/test_ldap.py index 4a4b88619..21c6ae290 100644 --- a/modoboa/core/tests/test_ldap.py +++ b/modoboa/core/tests/test_ldap.py @@ -84,7 +84,7 @@ class LDAPAuthenticationTestCase(LDAPTestCaseMixin, ModoTestCase): def setUp(self): """Create test data.""" - super(LDAPAuthenticationTestCase, self).setUp() + super().setUp() self.activate_ldap_authentication() def check_created_user(self, username, group="SimpleUsers", with_mb=True): @@ -142,7 +142,7 @@ class ProfileTestCase(LDAPTestCaseMixin, ModoTestCase): @classmethod def setUpTestData(cls): # NOQA:N802 """Create test data.""" - super(ProfileTestCase, cls).setUpTestData() + super().setUpTestData() cls.account = factories.UserFactory( username="user@test.com", groups=("SimpleUsers",) ) diff --git a/modoboa/core/tests/test_views.py b/modoboa/core/tests/test_views.py index 7ba41f9bc..eb3cb61b6 100644 --- a/modoboa/core/tests/test_views.py +++ b/modoboa/core/tests/test_views.py @@ -148,7 +148,7 @@ class DashboardTestCase(ModoTestCase): @classmethod def setUpTestData(cls): # NOQA:N802 """Create some data.""" - super(DashboardTestCase, cls).setUpTestData() + super().setUpTestData() cls.dadmin = factories.UserFactory( username="admin@test.com", groups=("DomainAdmins",) ) @@ -317,13 +317,13 @@ class PreferencesTestCase(ModoTestCase): @classmethod def setUpClass(cls): """Register user form.""" - super(PreferencesTestCase, cls).setUpClass() + super().setUpClass() param_tools.registry.add("user", UserSettings, "Test") @classmethod def tearDownClass(cls): """Remove user class.""" - super(PreferencesTestCase, cls).tearDownClass() + super().tearDownClass() del param_tools.registry._registry["user"]["core"] def test_get_user_index(self): diff --git a/modoboa/core/views/admin.py b/modoboa/core/views/admin.py index a2fe6b3df..27d441458 100644 --- a/modoboa/core/views/admin.py +++ b/modoboa/core/views/admin.py @@ -88,7 +88,7 @@ def get_logs_page(request, page_id=None): if page_id is None: return None return get_listing_page( - Log.objects.all().order_by("%s%s" % (sort_dir, sort_order)), page_id + Log.objects.all().order_by(f"{sort_dir}{sort_order}"), page_id ) diff --git a/modoboa/core/views/auth.py b/modoboa/core/views/auth.py index 058603293..1faa11c2b 100644 --- a/modoboa/core/views/auth.py +++ b/modoboa/core/views/auth.py @@ -141,11 +141,10 @@ class LoginView(LoginViewMixin, auth_views.LoginView): def form_invalid(self, form): # FIXME: should we return a 401 as before ? self.logger.warning( - "Failed connection attempt from '%(addr)s' as user '%(user)s'" - % { - "addr": self.request.META["REMOTE_ADDR"], - "user": escape(form.cleaned_data["username"]), - } + "Failed connection attempt from '{addr}' as user '{user}'".format( + addr=self.request.META["REMOTE_ADDR"], + user=escape(form.cleaned_data["username"]), + ) ) return self.render_to_response(self.get_context_data(form=form), status=401) @@ -220,7 +219,7 @@ class VerifySMSCodeView(generic.FormView): try: kwargs.update({"totp_secret": self.request.session["totp_secret"]}) except KeyError: - raise Http404 + raise Http404 from None return kwargs def form_valid(self, form): @@ -241,11 +240,11 @@ class ResendSMSCodeView(generic.View): "sms_password_recovery" ) if not sms_password_recovery: - raise Http404 + raise Http404 from None try: user = models.User._default_manager.get(pk=self.request.session["user_pk"]) except KeyError: - raise Http404 + raise Http404 from None backend = sms_backends.get_active_backend(self.request.localconfig.parameters) secret = cryptutils.random_hex_key(20) code = oath.totp(secret) @@ -255,7 +254,7 @@ class ResendSMSCodeView(generic.View): ) ) if not backend.send(text, [user.phone_number]): - raise Http404 + raise Http404 from None self.request.session["totp_secret"] = secret return JsonResponse({"status": "ok"}) diff --git a/modoboa/dmarc/tests/test_management_command.py b/modoboa/dmarc/tests/test_management_command.py index 1a23be9ed..6631c37d7 100644 --- a/modoboa/dmarc/tests/test_management_command.py +++ b/modoboa/dmarc/tests/test_management_command.py @@ -12,7 +12,7 @@ class ManagementCommandTestCase(mixins.CallCommandMixin, ModoTestCase): @classmethod def setUpTestData(cls): - super(ManagementCommandTestCase, cls).setUpTestData() + super().setUpTestData() cls.domain = admin_factories.DomainFactory(name="ngyn.org") def test_import_from_archive(self): diff --git a/modoboa/dmarc/views.py b/modoboa/dmarc/views.py index ca2c06d5f..666b79a51 100644 --- a/modoboa/dmarc/views.py +++ b/modoboa/dmarc/views.py @@ -78,7 +78,7 @@ class DomainReportView(auth_mixins.PermissionRequiredMixin, generic.TemplateView def get_context_data(self, *args, **kwargs): """Extra context data.""" - context = super(DomainReportView, self).get_context_data(*args, **kwargs) + context = super().get_context_data(*args, **kwargs) qset = self.get_queryset() stats = {"total": 0, "aligned": 0, "trusted": 0, "forwarded": 0, "failed": 0} aligned = {} diff --git a/modoboa/dnstools/lib.py b/modoboa/dnstools/lib.py index cd4cfdc41..67ef8e257 100644 --- a/modoboa/dnstools/lib.py +++ b/modoboa/dnstools/lib.py @@ -83,7 +83,7 @@ def check_spf_ip4(value): try: ipaddress.ip_network(parts[1], False) except ValueError: - raise DNSSyntaxError(_("Wrong IPv4 address format")) + raise DNSSyntaxError(_("Wrong IPv4 address format")) from None def check_spf_ip6(value): @@ -94,7 +94,7 @@ def check_spf_ip6(value): try: ipaddress.ip_network(value, False) except ValueError: - raise DNSSyntaxError(_("Wrong IPv6 address format")) + raise DNSSyntaxError(_("Wrong IPv6 address format")) from None def _check_domain_and_mask(value, mechanism): @@ -229,7 +229,7 @@ def check_dmarc_tag(tag, value): try: value = int(value) except ValueError: - raise DNSSyntaxError(error + _(" not an integer")) + raise DNSSyntaxError(error + _(" not an integer")) from None if "min_value" in tdef and value < tdef["min_value"]: raise DNSSyntaxError(error + _(" less than {}").format(tdef["min_value"])) if "max_value" in tdef and value > tdef["max_value"]: diff --git a/modoboa/dnstools/tests.py b/modoboa/dnstools/tests.py index a3e57430d..1b2bf7411 100644 --- a/modoboa/dnstools/tests.py +++ b/modoboa/dnstools/tests.py @@ -133,7 +133,7 @@ class ViewsTestCase(ModoTestCase): @classmethod def setUpTestData(cls): """Create some records.""" - super(ViewsTestCase, cls).setUpTestData() + super().setUpTestData() cls.spf_rec = factories.DNSRecordFactory( type="spf", value="v=SPF1 mx -all", is_valid=True, domain__name="test.com" ) diff --git a/modoboa/dnstools/views.py b/modoboa/dnstools/views.py index c8871c783..74472d9a3 100644 --- a/modoboa/dnstools/views.py +++ b/modoboa/dnstools/views.py @@ -16,7 +16,7 @@ class DomainAccessRequiredMixin(auth_mixins.AccessMixin): def dispatch(self, request, *args, **kwargs): if not request.user.can_access(self.get_object()): return self.handle_no_permission() - return super(DomainAccessRequiredMixin, self).dispatch(request, *args, **kwargs) + return super().dispatch(request, *args, **kwargs) class DNSRecordDetailView( @@ -29,7 +29,7 @@ class DNSRecordDetailView( def get_context_data(self, **kwargs): """Add extra variables.""" - context = super(DNSRecordDetailView, self).get_context_data(**kwargs) + context = super().get_context_data(**kwargs) context.update( { "title": _("{} record of {}").format( @@ -59,7 +59,7 @@ class AutoConfigRecordsStatusView( def get_context_data(self, **kwargs): """Add extra data to context.""" - context = super(AutoConfigRecordsStatusView, self).get_context_data(**kwargs) + context = super().get_context_data(**kwargs) context.update( {"title": _("Auto configuration records for {}").format(self.object.name)} ) @@ -76,6 +76,6 @@ class DomainDNSConfigurationView( def get_context_data(self, **kwargs): """Add extra variables.""" - context = super(DomainDNSConfigurationView, self).get_context_data(**kwargs) + context = super().get_context_data(**kwargs) context.update({"title": _("DNS configuration for {}").format(self.object)}) return context diff --git a/modoboa/imap_migration/tests.py b/modoboa/imap_migration/tests.py index ac36ccf65..9e3129ad1 100644 --- a/modoboa/imap_migration/tests.py +++ b/modoboa/imap_migration/tests.py @@ -26,7 +26,7 @@ class DataMixin: @classmethod def setUpTestData(cls): - super(DataMixin, cls).setUpTestData() + super().setUpTestData() admin_factories.populate_database() cls.mb = admin_models.Mailbox.objects.get(user__username="user@test.com") cls.migration = factories.MigrationFactory( @@ -110,7 +110,7 @@ class ManagementCommandTestCase(DataMixin, ModoTestCase): """Management command test cases.""" def setUp(self): - super(ManagementCommandTestCase, self).setUp() + super().setUp() self.workdir = tempfile.mkdtemp() def tearDown(self): diff --git a/modoboa/imap_migration/views.py b/modoboa/imap_migration/views.py index 334518a21..d6ac2468e 100644 --- a/modoboa/imap_migration/views.py +++ b/modoboa/imap_migration/views.py @@ -18,4 +18,4 @@ class IndexView( def get_context_data(self, **kwargs): """Set menu selection.""" kwargs.update({"selection": "imap_migrations"}) - return super(IndexView, self).get_context_data(**kwargs) + return super().get_context_data(**kwargs) diff --git a/modoboa/lib/connections.py b/modoboa/lib/connections.py index d830d509c..aabf460fa 100644 --- a/modoboa/lib/connections.py +++ b/modoboa/lib/connections.py @@ -9,7 +9,7 @@ class ConnectionsManager(type): """ def __init__(self, name, bases, ctx): - super(ConnectionsManager, self).__init__(name, bases, ctx) + super().__init__(name, bases, ctx) self.instances = {} def __call__(self, **kwargs): @@ -24,7 +24,7 @@ class ConnectionsManager(type): kwargs["password"] = decrypt(kwargs["password"]) if self.instances[key] is None: - self.instances[key] = super(ConnectionsManager, self).__call__(**kwargs) + self.instances[key] = super().__call__(**kwargs) else: self.instances[key].refresh(key, kwargs["password"]) return self.instances[key] diff --git a/modoboa/lib/db_utils.py b/modoboa/lib/db_utils.py index 6c3f0e575..c817394c8 100644 --- a/modoboa/lib/db_utils.py +++ b/modoboa/lib/db_utils.py @@ -19,7 +19,7 @@ def db_type(cname="default"): :return: a string or None """ if cname not in settings.DATABASES: - raise InternalError(_("Connection to database %s not configured" % cname)) + raise InternalError(_("Connection to database {} not configured".format(cname))) for t in ["postgres", "mysql", "sqlite"]: if settings.DATABASES[cname]["ENGINE"].find(t) != -1: return t diff --git a/modoboa/lib/email_utils.py b/modoboa/lib/email_utils.py index 78e868948..6c2501dfb 100644 --- a/modoboa/lib/email_utils.py +++ b/modoboa/lib/email_utils.py @@ -113,7 +113,7 @@ class Email: def body(self): if self._body is None: self._parse(self.msg) - self._body = getattr(self, "viewmail_%s" % self.dformat)() + self._body = getattr(self, f"viewmail_{self.dformat}")() return self._body @@ -215,7 +215,7 @@ class Email: # Duplicate Content-ID return - self._images[cid] = "data:%s;base64,%s" % ( + self._images[cid] = "data:{};base64,{}".format( content_type, "".join(msg.get_payload().splitlines(False)), ) @@ -263,7 +263,7 @@ class Email: contents = self.contents["plain"] else: contents = conditional_escape(contents) - return "%s" % contents + return f"
{contents}"
def viewmail_html(self, contents=None, **kwargs):
# contents and **kwargs parameters to maintain compatibility with
@@ -385,7 +385,7 @@ def set_email_headers(msg, subject, sender, rcpt):
msg["From"] = sender
msg["To"] = prepare_addresses(rcpt)
msg["Message-ID"] = make_msgid()
- msg["User-Agent"] = "Modoboa %s" % (
+ msg["User-Agent"] = "Modoboa {}".format(
pkg_resources.get_distribution("modoboa").version
)
msg["Date"] = formatdate(time.time(), True)
@@ -409,7 +409,7 @@ def _sendmail(sender, rcpt, msgstring, server="localhost", port=25):
s.sendmail(sender, [rcpt], msgstring)
s.quit()
except smtplib.SMTPException as e:
- return False, "SMTP error: %s" % str(e)
+ return False, f"SMTP error: {str(e)}"
return True, None
@@ -449,12 +449,9 @@ def sendmail_fromfile(sender, rcpt, fname):
except OSError as e:
return False, str(e)
- content = """From: %s
-To: %s
-""" % (
- sender,
- rcpt,
- )
+ content = f"""From: {sender}
+To: {rcpt}
+"""
content += fp.read()
fp.close()
diff --git a/modoboa/lib/exceptions.py b/modoboa/lib/exceptions.py
index 786c05d06..1ddfb8ecd 100644
--- a/modoboa/lib/exceptions.py
+++ b/modoboa/lib/exceptions.py
@@ -82,5 +82,5 @@ class PermDeniedException(ModoboaException):
def __str__(self):
if self.msg:
- return _("Permission denied: %s" % self.msg)
+ return _("Permission denied: {}".format(self.msg))
return _("Permission denied")
diff --git a/modoboa/lib/fields.py b/modoboa/lib/fields.py
index 65fa08ac0..82e9a82f9 100644
--- a/modoboa/lib/fields.py
+++ b/modoboa/lib/fields.py
@@ -17,7 +17,7 @@ class DomainNameField(forms.fields.CharField):
def clean(self, value):
value = self.to_python(value).strip()
- return super(DomainNameField, self).clean(value)
+ return super().clean(value)
class UTF8EmailField(forms.fields.EmailField):
@@ -41,7 +41,7 @@ class DRFEmailFieldUTF8(serializers.CharField):
default_error_messages = {"invalid": gettext_lazy("Enter a valid email address.")}
def __init__(self, **kwargs):
- super(DRFEmailFieldUTF8, self).__init__(**kwargs)
+ super().__init__(**kwargs)
validator = validators.UTF8EmailValidator(
message=self.error_messages["invalid"]
)
@@ -54,7 +54,7 @@ class DRFEmailFieldUTF8AndEmptyUser(serializers.CharField):
default_error_messages = {"invalid": gettext_lazy("Enter a valid email address.")}
def __init__(self, **kwargs):
- super(DRFEmailFieldUTF8AndEmptyUser, self).__init__(**kwargs)
+ super().__init__(**kwargs)
validator = validators.UTF8AndEmptyUserEmailValidator(
message=self.error_messages["invalid"]
)
diff --git a/modoboa/lib/form_utils.py b/modoboa/lib/form_utils.py
index 06ff5a9e2..9c645bcc6 100644
--- a/modoboa/lib/form_utils.py
+++ b/modoboa/lib/form_utils.py
@@ -218,7 +218,7 @@ class DynamicForm:
:param string pattern: pattern used to find field instances
:param typ: a form field class
"""
- expr = re.compile(r"%s_\d+" % pattern)
+ expr = re.compile(rf"{pattern}_\d+")
values = []
for k, v in list(qdict.items()):
if k == pattern or expr.match(k):
@@ -258,7 +258,7 @@ class TabForms:
args.append(request.POST)
if instances is not None:
self.instances = instances
- mname = "check_%s" % fd["id"]
+ mname = "check_{}".format(fd["id"])
if hasattr(self, mname):
if not getattr(self, mname)(instances[fd["id"]]):
to_remove += [fd]
@@ -326,8 +326,7 @@ class TabForms:
return self.forward()
def forward(self):
- for form in self.forms:
- yield form
+ yield from self.forms
def extra_context(self, context):
""" "Provide additional information to template's context."""
@@ -387,7 +386,7 @@ class SeparatorField(Field):
def __init__(self, *args, **kwargs):
kwargs["required"] = False
- super(SeparatorField, self).__init__(*args, **kwargs)
+ super().__init__(*args, **kwargs)
class YesNoField(TypedChoiceField):
@@ -402,4 +401,4 @@ class YesNoField(TypedChoiceField):
"coerce": lambda x: x == "True",
}
)
- super(YesNoField, self).__init__(*args, **kwargs)
+ super().__init__(*args, **kwargs)
diff --git a/modoboa/lib/ldap_utils.py b/modoboa/lib/ldap_utils.py
index 1afbfa360..2c14f432d 100644
--- a/modoboa/lib/ldap_utils.py
+++ b/modoboa/lib/ldap_utils.py
@@ -112,7 +112,7 @@ class LDAPAuthBackend:
self.connect_to_server(user, password)
user_dn = self._find_user_dn(user)
if self.ldap_ad:
- newpassword = ('"%s"' % newpassword).encode("utf-16").lstrip("\377\376")
+ newpassword = (f'"{newpassword}"').encode("utf-16").lstrip("\377\376")
ldif = [
(
ldap.MOD_REPLACE,
diff --git a/modoboa/lib/singleton.py b/modoboa/lib/singleton.py
index e49acc309..c1447ced6 100644
--- a/modoboa/lib/singleton.py
+++ b/modoboa/lib/singleton.py
@@ -4,7 +4,7 @@ class _Singleton(type):
def __call__(self, *args, **kwargs):
if self not in self._instances:
- self._instances[self] = super(_Singleton, self).__call__(*args, **kwargs)
+ self._instances[self] = super().__call__(*args, **kwargs)
return self._instances[self]
diff --git a/modoboa/lib/sysutils.py b/modoboa/lib/sysutils.py
index 64c79647b..f97c0a0f8 100644
--- a/modoboa/lib/sysutils.py
+++ b/modoboa/lib/sysutils.py
@@ -27,7 +27,7 @@ def exec_cmd(cmd, sudo_user=None, pinput=None, capture_output=True, **kwargs):
:return: return code, command output
"""
if sudo_user is not None:
- cmd = "sudo -u %s %s" % (sudo_user, cmd)
+ cmd = f"sudo -u {sudo_user} {cmd}"
kwargs["shell"] = True
if pinput is not None:
kwargs["stdin"] = subprocess.PIPE
diff --git a/modoboa/lib/templatetags/form_tags.py b/modoboa/lib/templatetags/form_tags.py
index f346f8e7a..69f37155f 100644
--- a/modoboa/lib/templatetags/form_tags.py
+++ b/modoboa/lib/templatetags/form_tags.py
@@ -20,7 +20,7 @@ def render_form(form, tpl=None):
ret = ""
for field in form:
- ret += "%s\n" % render_field(field)
+ ret += f"{render_field(field)}\n"
return mark_safe(ret)
@@ -42,7 +42,7 @@ def render_field(field, help_display_mode="tooltip", label_width="col-sm-4", **o
from modoboa.core.templatetags.core_tags import visirule
if isinstance(field.field, SeparatorField):
- return "