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 "%s" % (visirule(field), smart_str(field.label)) + return f"{smart_str(field.label)}" configure_field_classes(field) context = { "field": field, @@ -64,7 +64,7 @@ def render_fields_group(form, pattern): cpt = 1 haserror = len(first.errors) != 0 while True: - fname = "%s_%d" % (pattern, cpt) + fname = f"{pattern}_{cpt}" if fname not in form.fields: break bfield = forms.BoundField(form, form.fields[fname], fname) diff --git a/modoboa/lib/templatetags/lib_tags.py b/modoboa/lib/templatetags/lib_tags.py index 64e931f15..3214e457e 100644 --- a/modoboa/lib/templatetags/lib_tags.py +++ b/modoboa/lib/templatetags/lib_tags.py @@ -18,13 +18,13 @@ def join(items, sep=","): for k, v in list(items.items()): if res != "": res += sep - res += "%s : '%s'" % (k, v) + res += f"{k} : '{v}'" return res @register.simple_tag def tolist(values): - return mark_safe("[%s]" % ",".join(['"%s"' % v for v in values])) + return mark_safe("[{}]".format(",".join([f'"{v}"' for v in values]))) @register.simple_tag diff --git a/modoboa/lib/tests/test_email_utils.py b/modoboa/lib/tests/test_email_utils.py index 1c6844ccc..a3202462d 100644 --- a/modoboa/lib/tests/test_email_utils.py +++ b/modoboa/lib/tests/test_email_utils.py @@ -15,8 +15,8 @@ SAMPLES_DIR = os.path.realpath( class EmailTestImplementation(Email): def _fetch_message(self): - message_path = os.path.join(SAMPLES_DIR, "%s-input.txt" % self.mailid) - assert os.path.isfile(message_path), "%s does not exist." % message_path + message_path = os.path.join(SAMPLES_DIR, f"{self.mailid}-input.txt") + assert os.path.isfile(message_path), f"{message_path} does not exist." with open(message_path, "rb") as fp: mail_text = smart_bytes(fp.read()) @@ -38,7 +38,7 @@ class EmailTests(SimpleTestCase): ext = kwargs["dformat"] if "dformat" in kwargs else "plain" ext += "_links" if "links" in kwargs and kwargs["links"] else "_nolinks" message_path = os.path.join(SAMPLES_DIR, f"{message_id}-output-{ext}.txt") - assert os.path.isfile(message_path), "%s does not exist." % message_path + assert os.path.isfile(message_path), f"{message_path} does not exist." with open(message_path, "rb") as fp: # output should always be unicode (py2) or str (py3) diff --git a/modoboa/lib/web_utils.py b/modoboa/lib/web_utils.py index 9ba0f31db..1f9d4cf1d 100644 --- a/modoboa/lib/web_utils.py +++ b/modoboa/lib/web_utils.py @@ -16,7 +16,7 @@ from django.template.loader import render_to_string def _render_error(request, errortpl="error", user_context=None): if user_context is None: user_context = {} - return render(request, "common/%s.html" % errortpl, user_context) + return render(request, f"common/{errortpl}.html", user_context) def render_actions(actions): @@ -102,7 +102,7 @@ def static_url(path): """ if path.startswith("/"): path = path[1:] - return "%s%s" % (settings.STATIC_URL, path) + return f"{settings.STATIC_URL}{path}" def size2integer(value, output_unit="B"): diff --git a/modoboa/limits/api/v1/tests.py b/modoboa/limits/api/v1/tests.py index ffa446fb1..9c0705fdb 100644 --- a/modoboa/limits/api/v1/tests.py +++ b/modoboa/limits/api/v1/tests.py @@ -20,7 +20,7 @@ class APIAdminLimitsTestCase(lib_tests.ModoAPITestCase): @classmethod def setUpTestData(cls): # NOQA:N802 """Create test data.""" - super(APIAdminLimitsTestCase, cls).setUpTestData() + super().setUpTestData() for name, _definition in utils.get_user_limit_templates(): cls.localconfig.parameters.set_value(f"deflt_user_{name}_limit", 2) cls.localconfig.save() @@ -189,7 +189,7 @@ class APIDomainLimitsTestCase(lib_tests.ModoAPITestCase): @classmethod def setUpTestData(cls): # NOQA:N802 """Create test data.""" - super(APIDomainLimitsTestCase, cls).setUpTestData() + super().setUpTestData() cls.localconfig.parameters.set_value("enable_domain_limits", True) for name, _definition in utils.get_domain_limit_templates(): cls.localconfig.parameters.set_value(f"deflt_domain_{name}_limit", 2) @@ -251,7 +251,7 @@ class ResourcesAPITestCase(lib_tests.ModoAPITestCase): @classmethod def setUpTestData(cls): # NOQA:N802 """Create test data.""" - super(ResourcesAPITestCase, cls).setUpTestData() + super().setUpTestData() for name, _definition in utils.get_user_limit_templates(): cls.localconfig.parameters.set_value(f"deflt_user_{name}_limit", 2) cls.localconfig.save() diff --git a/modoboa/limits/forms.py b/modoboa/limits/forms.py index 13d201a85..433aa8513 100644 --- a/modoboa/limits/forms.py +++ b/modoboa/limits/forms.py @@ -13,7 +13,7 @@ class ResourcePoolForm(forms.Form): def __init__(self, *args, **kwargs): self.account = kwargs.pop("instance", None) - super(ResourcePoolForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) for name, tpl in utils.get_user_limit_templates(): if "required_role" in tpl: condition = ( @@ -30,7 +30,7 @@ class ResourcePoolForm(forms.Form): def clean(self): """Ensure limit values are correct.""" - cleaned_data = super(ResourcePoolForm, self).clean() + cleaned_data = super().clean() if self.errors: return cleaned_data for lname in list(self.fields.keys()): @@ -65,7 +65,7 @@ class DomainLimitsForm(forms.Form): def __init__(self, *args, **kwargs): """Define limits as fields.""" self.domain = kwargs.pop("instance", None) - super(DomainLimitsForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) for name, tpl in utils.get_domain_limit_templates(): self.fields[f"{name}_limit"] = forms.IntegerField( label=tpl["label"], help_text=tpl["help"] @@ -80,7 +80,7 @@ class DomainLimitsForm(forms.Form): def clean(self): """Ensure limit values are correct.""" - cleaned_data = super(DomainLimitsForm, self).clean() + cleaned_data = super().clean() if self.errors: return cleaned_data for lname in list(self.fields.keys()): diff --git a/modoboa/limits/tests/test_import.py b/modoboa/limits/tests/test_import.py index 72d5f5b14..049949877 100644 --- a/modoboa/limits/tests/test_import.py +++ b/modoboa/limits/tests/test_import.py @@ -14,7 +14,7 @@ class LimitImportTestCase(ModoTestCase): @classmethod def setUpTestData(cls): # NOQA:N802 """Create test data.""" - super(LimitImportTestCase, cls).setUpTestData() + super().setUpTestData() admin_factories.populate_database() cls.reseller = core_factories.UserFactory( username="reseller", groups=("Resellers",) @@ -119,7 +119,7 @@ class UserLimitImportTestCase(LimitImportTestCase): for name, _definition in utils.get_user_limit_templates(): localconfig.parameters.set_value(f"deflt_user_{name}_limit", 2) localconfig.save() - super(UserLimitImportTestCase, cls).setUpTestData() + super().setUpTestData() def test_domains_import(self): """Check domains limit.""" @@ -181,7 +181,7 @@ class DomainLimitImportTestCase(LimitImportTestCase): for name, _definition in utils.get_domain_limit_templates(): localconfig.parameters.set_value(f"deflt_domain_{name}_limit", 2) localconfig.save() - super(DomainLimitImportTestCase, cls).setUpTestData() + super().setUpTestData() mb = admin_factories.MailboxFactory( user__username="user@test4.com", domain__name="test4.com", address="user" ) diff --git a/modoboa/limits/tests/test_user_limits.py b/modoboa/limits/tests/test_user_limits.py index a8ecb0f1e..d0d39fe17 100644 --- a/modoboa/limits/tests/test_user_limits.py +++ b/modoboa/limits/tests/test_user_limits.py @@ -15,7 +15,7 @@ class PermissionsTestCase(lib_tests.ModoTestCase): @classmethod def setUpTestData(cls): # NOQA:N802 """Create test data.""" - super(PermissionsTestCase, cls).setUpTestData() + super().setUpTestData() populate_database() def test_domainadmin_deletes_reseller(self): @@ -49,7 +49,7 @@ class ResourceTestCase(lib_tests.ModoTestCase): @classmethod def setUpTestData(cls): # NOQA:N802 """Custom setUpTestData method.""" - super(ResourceTestCase, cls).setUpTestData() + super().setUpTestData() cls.localconfig.parameters.set_values( {"enable_admin_limits": True, "enable_domain_limits": False} ) @@ -116,7 +116,7 @@ class ResourceTestCase(lib_tests.ModoTestCase): else: aliases.remove(name) for cpt, alias in enumerate(aliases): - fname = "aliases" if not cpt else "aliases_%d" % cpt + fname = "aliases" if not cpt else f"aliases_{cpt}" values[fname] = alias self.ajax_post(reverse("admin:domain_change", args=[dom.id]), values, status) @@ -131,7 +131,7 @@ class DomainAdminTestCase(ResourceTestCase): @classmethod def setUpTestData(cls): # NOQA:N802 """Create test data.""" - super(DomainAdminTestCase, cls).setUpTestData() + super().setUpTestData() cls.user = User.objects.get(username="admin@test.com") cls.user.userobjectlimit_set.filter( name__in=["mailboxes", "mailbox_aliases"] @@ -139,7 +139,7 @@ class DomainAdminTestCase(ResourceTestCase): def setUp(self): """Test initialization.""" - super(DomainAdminTestCase, self).setUp() + super().setUp() self.client.force_login(self.user) def test_mailboxes_limit(self): @@ -173,7 +173,7 @@ class DomainAdminTestCase(ResourceTestCase): self._check_limit("mailbox_aliases", 3, -1) self.ajax_delete( reverse("admin:alias_delete") - + "?selection=%d" % Alias.objects.get(address="alias2@test.com").id + + f"?selection={Alias.objects.get(address='alias2@test.com').id}" ) self._check_limit("mailbox_aliases", 2, -1) @@ -201,14 +201,14 @@ class ResellerTestCase(ResourceTestCase): @classmethod def setUpTestData(cls): # NOQA:N802 """Create test data.""" - super(ResellerTestCase, cls).setUpTestData() + super().setUpTestData() cls.localconfig.parameters.set_value("deflt_user_quota_limit", 1000) cls.localconfig.save() cls.user = UserFactory(username="reseller", groups=("Resellers",)) def setUp(self): """Test initialization.""" - super(ResellerTestCase, self).setUp() + super().setUp() self.client.force_login(self.user) def test_domains_limit(self): @@ -380,7 +380,7 @@ class ResellerTestCase(ResourceTestCase): self.client.logout() self.client.login(username="admin", password="password") self.ajax_delete( - "{0}?domid={1}&daid={2}".format( + "{}?domid={}&daid={}".format( reverse("admin:permission_remove"), dom.id, self.user.id ), {}, diff --git a/modoboa/maillog/graphics.py b/modoboa/maillog/graphics.py index 51c272b01..1a632e90a 100644 --- a/modoboa/maillog/graphics.py +++ b/modoboa/maillog/graphics.py @@ -36,12 +36,12 @@ class Curve: :return: a list """ rrdfile = os.path.join( - param_tools.get_global_parameter("rrd_rootdir"), "%s.rrd" % rrdfile + param_tools.get_global_parameter("rrd_rootdir"), f"{rrdfile}.rrd" ) return [ - "DEF:%s=%s:%s:%s" % (self.dsname, rrdfile, self.dsname, self.cfunc), - "CDEF:%(ds)spm=%(ds)s,UN,0,%(ds)s,IF,60,*" % {"ds": self.dsname}, - 'XPORT:%spm:"%s"' % (self.dsname, self.legend), + f"DEF:{self.dsname}={rrdfile}:{self.dsname}:{self.cfunc}", + f"CDEF:{self.dsname}pm={self.dsname},UN,0,{self.dsname},IF,60,*", + f'XPORT:{self.dsname}pm:"{self.legend}"', ] diff --git a/modoboa/maillog/lib.py b/modoboa/maillog/lib.py index 180dea71f..e91feee40 100644 --- a/modoboa/maillog/lib.py +++ b/modoboa/maillog/lib.py @@ -11,7 +11,7 @@ def date_to_timestamp(timetuple): :return: an integer """ date = " ".join( - [("%d" % elem) if isinstance(elem, int) else elem for elem in timetuple] + [str(elem) if isinstance(elem, int) else elem for elem in timetuple] ) fmt = "%Y %m %d %H %M %S" if timetuple[1].isdigit() else "%Y %b %d %H %M %S" try: diff --git a/modoboa/maillog/management/commands/logparser.py b/modoboa/maillog/management/commands/logparser.py index fe3e73fac..ba5cf492b 100644 --- a/modoboa/maillog/management/commands/logparser.py +++ b/modoboa/maillog/management/commands/logparser.py @@ -204,12 +204,12 @@ class LogParser: # Set up data sources for our RRD params = [] for v in variables: - params += ["DS:%s:%s:%s:0:U" % (v, ds_type, rrdstep * 2)] + params += [f"DS:{v}:{ds_type}:{rrdstep * 2}:0:U"] # Set up RRD to archive data for cf in ["AVERAGE", "MAX"]: for step in [day_steps, week_steps, month_steps, year_steps]: - params += ["RRA:%s:0.5:%s:%s" % (cf, step, realrows)] + params += [f"RRA:{cf}:0.5:{step}:{realrows}"] # With those setup, we can now created the RRD rrdtool.create(str(fname), "--start", str(m), "--step", str(rrdstep), *params) @@ -221,16 +221,16 @@ class LogParser: Add missing Data Sources (DS) to existing Round Robin Archive (RRA): See init_rrd for details. """ - ds_def = "DS:%s:ABSOLUTE:%s:0:U" % (dsname, rrdstep * 2) + ds_def = f"DS:{dsname}:ABSOLUTE:{rrdstep * 2}:0:U" rrdtool.tune(fname, ds_def) - self._dprint("[rrd] added DS %s to %s" % (dsname, fname)) + self._dprint(f"[rrd] added DS {dsname} to {fname}") def add_point_to_rrd(self, fname, tpl, values, ts=None): """Try to add a new point to RRD file.""" if ts: values = f"{ts}:{values}" if self.verbose: - print("[rrd] VERBOSE update -t %s %s" % (tpl, values)) + print(f"[rrd] VERBOSE update -t {tpl} {values}") try: rrdtool.update(str(fname), "-t", tpl, values) except rrdtool.OperationalError as e: @@ -249,20 +249,20 @@ class LogParser: False : syslog may have probably been already recorded or something wrong """ - fname = "%s/%s.rrd" % (self.workdir, dom) + fname = f"{self.workdir}/{dom}.rrd" m = t - (t % rrdstep) - self._dprint("[rrd] updating %s" % fname) + self._dprint(f"[rrd] updating {fname}") if not os.path.exists(fname): self.lupdates[fname] = self.init_rrd(fname, m) - self._dprint("[rrd] create new RRD file %s" % fname) + self._dprint(f"[rrd] create new RRD file {fname}") else: if fname not in self.lupdates: self.lupdates[fname] = rrdtool.last(str(fname)) if m <= self.lupdates[fname]: if self.verbose: - print("[rrd] VERBOSE events at %s already recorded in RRD" % m) + print(f"[rrd] VERBOSE events at {m} already recorded in RRD") return False tpl = "" @@ -281,7 +281,7 @@ class LogParser: for p in range(self.lupdates[fname] + rrdstep, m, rrdstep): self.add_point_to_rrd(fname, tpl, values, ts=p) - values = "%s" % m + values = f"{m}" tpl = "" for v in variables: values += ":" @@ -356,7 +356,7 @@ class LogParser: m = self._srs_regex["reverse_srs1"].match(mail_address) if m is None else m if m is not None: - return "%s@%s" % m.group(2, 1) + return "{}@{}".format(*m.group(2, 1)) else: return mail_address @@ -474,12 +474,10 @@ class LogParser: return False (msg_to, msg_status) = m.groups() if queue_id not in self.workdict: - self._dprint( - "[parser] inconsistent mail (%s: %s), skipping" % (queue_id, msg_to) - ) + self._dprint(f"[parser] inconsistent mail ({queue_id}: {msg_to}), skipping") return True if msg_status not in variables: - self._dprint("[parser] unsupported status %s, skipping" % msg_status) + self._dprint(f"[parser] unsupported status {msg_status}, skipping") return True # orig_to is optional. @@ -564,7 +562,7 @@ class LogParser: try: parser = getattr(self, f"_parse_{prog}") if not parser(log, host, pid, subprog): - self._dprint("[parser] ignoring %r log: %r" % (prog, log)) + self._dprint(f"[parser] ignoring {prog!r} log: {log!r}") except AttributeError: self._dprint(f'[parser] no log handler for "{prog}": {log}') @@ -579,11 +577,11 @@ class LogParser: for line in fp: self._parse_line(line) except OSError as errno: - self._dprint("%s" % errno) + self._dprint(f"{errno}") sys.exit(1) for dom, data in self.data.items(): - self._dprint("[rrd] dealing with domain %s" % dom) + self._dprint(f"[rrd] dealing with domain {dom}") for t in sorted(data.keys()): self.update_rrd(dom, t) diff --git a/modoboa/maillog/parser.py b/modoboa/maillog/parser.py index 3bacbd93b..3b47a1b75 100644 --- a/modoboa/maillog/parser.py +++ b/modoboa/maillog/parser.py @@ -162,7 +162,7 @@ class MaillogParser: m = self._srs_regex["reverse_srs1"].match(mail_address) if m is None else m if m is not None: - return "%s@%s" % m.group(2, 1) + return "{}@{}".format(*m.group(2, 1)) return mail_address def new_domain_event(self, domain, name, size=None): @@ -221,9 +221,7 @@ class MaillogParser: return False (msg_to, msg_status) = m.groups() if queue_id not in self.workdict: - self._dprint( - "[parser] inconsistent mail (%s: %s), skipping" % (queue_id, msg_to) - ) + self._dprint(f"[parser] inconsistent mail ({queue_id}: {msg_to}), skipping") return True # orig_to is optional. @@ -270,16 +268,16 @@ class MaillogParser: try: parser = getattr(self, f"_parse_{prog}") if not parser(log, host, pid, subprog): - self._dprint("[parser] ignoring %r log: %r" % (prog, log)) + self._dprint(f"[parser] ignoring {prog!r} log: {log!r}") except AttributeError: - self._dprint('[parser] no log handler for "%r": %r' % (prog, log)) + self._dprint(f'[parser] no log handler for "{prog!r}": {log!r}') def parse(self, logfile): """Process the log file.""" try: fp = open(logfile, encoding="utf-8") except OSError as errno: - self._dprint("%s" % errno) + self._dprint(f"{errno}") return for line in fp.readlines(): diff --git a/modoboa/maillog/tests/test_views.py b/modoboa/maillog/tests/test_views.py index 6d6fa1ef6..9544895d5 100644 --- a/modoboa/maillog/tests/test_views.py +++ b/modoboa/maillog/tests/test_views.py @@ -19,7 +19,7 @@ class RunCommandsMixin: """Mixin to run management commands.""" def setUp(self): - super(RunCommandsMixin, self).setUp() + super().setUp() self.workdir = tempfile.mkdtemp() self.set_global_parameter("rrd_rootdir", self.workdir) @@ -54,12 +54,12 @@ class ViewsTestCase(RunCommandsMixin, ModoTestCase): @classmethod def setUpTestData(cls): # noqa - super(ViewsTestCase, cls).setUpTestData() + super().setUpTestData() admin_factories.populate_database() cls.da = core_models.User.objects.get(username="admin@test.com") def tearDown(self): - super(ViewsTestCase, self).tearDown() + super().tearDown() self.set_global_parameter("greylist", False) def test_index(self): @@ -139,7 +139,7 @@ class ManagementCommandsTestCase(RunCommandsMixin, ModoTestCase): @classmethod def setUpTestData(cls): # noqa - super(ManagementCommandsTestCase, cls).setUpTestData() + super().setUpTestData() admin_factories.populate_database() def test_logparser(self): diff --git a/modoboa/maillog/views.py b/modoboa/maillog/views.py index 132b7b955..e366590c0 100644 --- a/modoboa/maillog/views.py +++ b/modoboa/maillog/views.py @@ -75,12 +75,12 @@ def graphs(request): start = request.GET["start"] end = request.GET["end"] expr = re.compile(r"[:\- ]") - period_name = "%s_%s" % (expr.sub("", start), expr.sub("", end)) + period_name = "{}_{}".format(expr.sub("", start), expr.sub("", end)) start = date_to_timestamp(expr.split(start)) end = date_to_timestamp(expr.split(end)) else: end = int(time.mktime(time.localtime())) - start = "-1%s" % period + start = f"-1{period}" period_name = period tplvars["domain_selector"] = graph_sets[gset].domain_selector diff --git a/modoboa/parameters/forms.py b/modoboa/parameters/forms.py index 49a40eba4..5b587d89d 100644 --- a/modoboa/parameters/forms.py +++ b/modoboa/parameters/forms.py @@ -21,7 +21,7 @@ class GenericParametersForm(forms.Form): kwargs["prefix"] = self.app load_values_from_db = kwargs.pop("load_values_from_db", True) - super(GenericParametersForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self._add_dynamic_fields() @@ -36,8 +36,8 @@ class GenericParametersForm(forms.Form): """Add visibility rules to this form.""" for key, rule in list(rules.items()): field, value = rule.split("=") - visibility = {"field": "id_%s-%s" % (self.app, field), "value": value} - self.visirules["%s-%s" % (self.app, key)] = visibility + visibility = {"field": f"id_{self.app}-{field}", "value": value} + self.visirules[f"{self.app}-{key}"] = visibility def _add_dynamic_fields(self): """Add dynamic fields to this form.""" @@ -60,7 +60,7 @@ class AdminParametersForm(GenericParametersForm): def __init__(self, *args, **kwargs): """Store LocalConfig instance.""" self.localconfig = kwargs.pop("localconfig", None) - super(AdminParametersForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def _load_initial_values(self): """Load form initial values from database.""" @@ -92,7 +92,7 @@ class UserParametersForm(GenericParametersForm): def __init__(self, *args, **kwargs): self.user = kwargs.pop("user", None) - super(UserParametersForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def _load_initial_values(self): """Load initial values from User instance.""" diff --git a/modoboa/pdfcredentials/app_settings.py b/modoboa/pdfcredentials/app_settings.py index 8f39db239..6e44736b6 100644 --- a/modoboa/pdfcredentials/app_settings.py +++ b/modoboa/pdfcredentials/app_settings.py @@ -308,7 +308,7 @@ class ParametersForm(param_forms.AdminParametersForm): def __init__(self, *args, **kwargs): """Set initial values.""" - super(ParametersForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) if not self.fields["webpanel_url"].initial: url = f"https://{self.hostname}{settings.LOGIN_URL}" self.fields["webpanel_url"].initial = url diff --git a/modoboa/pdfcredentials/tests/test_views.py b/modoboa/pdfcredentials/tests/test_views.py index 548d9293a..b79972db8 100644 --- a/modoboa/pdfcredentials/tests/test_views.py +++ b/modoboa/pdfcredentials/tests/test_views.py @@ -19,12 +19,12 @@ class EventsTestCase(ModoTestCase): @classmethod def setUpTestData(cls): """Create some data.""" - super(EventsTestCase, cls).setUpTestData() + super().setUpTestData() admin_factories.DomainFactory(name="test.com") def setUp(self): """Create temp. directory to store files.""" - super(EventsTestCase, self).setUp() + super().setUp() self.workdir = tempfile.mkdtemp() self.set_global_parameter("storage_dir", self.workdir) diff --git a/modoboa/postfix_autoreply/api/v2/tests.py b/modoboa/postfix_autoreply/api/v2/tests.py index 7a913a600..0fd554432 100644 --- a/modoboa/postfix_autoreply/api/v2/tests.py +++ b/modoboa/postfix_autoreply/api/v2/tests.py @@ -14,7 +14,7 @@ class ARMessageViewSetTestCase(ModoAPITestCase): @classmethod def setUpTestData(cls): - super(ARMessageViewSetTestCase, cls).setUpTestData() + super().setUpTestData() admin_factories.populate_database() cls.account = User.objects.get(username="user@test.com") cls.account2 = User.objects.get(username="user@test2.com") diff --git a/modoboa/postfix_autoreply/forms.py b/modoboa/postfix_autoreply/forms.py index a01370a1e..f43f60ac8 100644 --- a/modoboa/postfix_autoreply/forms.py +++ b/modoboa/postfix_autoreply/forms.py @@ -150,7 +150,7 @@ Best regards, try: tpl % {"name": "Antoine Nguyen"} except (KeyError, ValueError): - raise forms.ValidationError(gettext_lazy("Invalid syntax")) + raise forms.ValidationError(gettext_lazy("Invalid syntax")) from None return tpl diff --git a/modoboa/postfix_autoreply/handlers.py b/modoboa/postfix_autoreply/handlers.py index 83629ab49..6bd741ac1 100644 --- a/modoboa/postfix_autoreply/handlers.py +++ b/modoboa/postfix_autoreply/handlers.py @@ -92,15 +92,13 @@ def extra_js(sender, caller, st_type, user, **kwargs): """Add static content.""" if caller != "user_index" or st_type != "js": return "" - return """function autoreply_cb() { - $('.datefield').datetimepicker({ + return f"""function autoreply_cb() {{ + $('.datefield').datetimepicker({{ format: 'YYYY-MM-DD HH:mm:ss', - locale: '%(lang)s' - }); -} -""" % { - "lang": user.language - } + locale: '{user.language}' + }}); +}} +""" @receiver(core_signals.extra_user_menu_entries) diff --git a/modoboa/postfix_autoreply/tests.py b/modoboa/postfix_autoreply/tests.py index 64f1cea74..2e1cf00d9 100644 --- a/modoboa/postfix_autoreply/tests.py +++ b/modoboa/postfix_autoreply/tests.py @@ -111,7 +111,7 @@ class EventsTestCase(ModoTestCase): @classmethod def setUpTestData(cls): # NOQA:N802 """Create test data.""" - super(EventsTestCase, cls).setUpTestData() + super().setUpTestData() admin_factories.populate_database() def test_domain_created_event(self): @@ -241,12 +241,12 @@ class FormTestCase(ModoTestCase): @classmethod def setUpTestData(cls): # NOQA:N802 """Create test data.""" - super(FormTestCase, cls).setUpTestData() + super().setUpTestData() admin_factories.populate_database() def setUp(self): """Initialize tests.""" - super(FormTestCase, self).setUp() + super().setUp() self.client.logout() self.client.login(username="user@test.com", password="toto") @@ -314,7 +314,7 @@ class RepairTestCase(ModoTestCase): @classmethod def setUpTestData(cls): # NOQA:N802 """Create some data.""" - super(RepairTestCase, cls).setUpTestData() + super().setUpTestData() admin_factories.populate_database() def test_management_command(self): @@ -342,13 +342,13 @@ class ManagementCommandTestCase(ModoTestCase): @classmethod def setUpTestData(cls): # NOQA:N802 """Create some data.""" - super(ManagementCommandTestCase, cls).setUpTestData() + super().setUpTestData() admin_factories.populate_database() cls.account = User.objects.get(username="user@test.com") def setUp(self): """Replace stdin.""" - super(ManagementCommandTestCase, self).setUp() + super().setUp() self.stdin = sys.stdin sys.stdin = StringIO(SIMPLE_EMAIL_CONTENT.strip()) self.arm = factories.ARmessageFactory(mbox=self.account.mailbox) diff --git a/modoboa/relaydomains/api/v1/tests.py b/modoboa/relaydomains/api/v1/tests.py index e16dc59f2..1c05bdf08 100644 --- a/modoboa/relaydomains/api/v1/tests.py +++ b/modoboa/relaydomains/api/v1/tests.py @@ -14,7 +14,7 @@ class DataMixin: @classmethod def setUpTestData(cls): # NOQA:N802 - super(DataMixin, cls).setUpTestData() + super().setUpTestData() transport = tr_factories.TransportFactory( pattern="test.com", _settings={ diff --git a/modoboa/relaydomains/forms.py b/modoboa/relaydomains/forms.py index 565da74f5..b55fe4329 100644 --- a/modoboa/relaydomains/forms.py +++ b/modoboa/relaydomains/forms.py @@ -52,7 +52,7 @@ class RelayDomainFormGeneral(tr_forms.TransportForm): # We don't want to recreate the transport we just deleted it # (post_save signal). return None - instance = super(RelayDomainFormGeneral, self).save() + instance = super().save() instance.pattern = domain.name instance.save() if not domain.transport: diff --git a/modoboa/relaydomains/tests.py b/modoboa/relaydomains/tests.py index 7323a837f..9b3b722b1 100644 --- a/modoboa/relaydomains/tests.py +++ b/modoboa/relaydomains/tests.py @@ -48,7 +48,7 @@ class Operations: else: aliases.remove(name) for cpt, alias in enumerate(aliases): - fname = "aliases" if not cpt else "aliases_%d" % cpt + fname = "aliases" if not cpt else f"aliases_{cpt}" values[fname] = alias return self.ajax_post( reverse("admin:domain_change", args=[domain.id]), values, status @@ -65,7 +65,7 @@ class RelayDomainsTestCase(ModoTestCase, Operations): @classmethod def setUpTestData(cls): # NOQA:N802 """Create test data.""" - super(RelayDomainsTestCase, cls).setUpTestData() + super().setUpTestData() admin_factories.populate_database() cls.transport = tr_factories.TransportFactory( pattern="relaydomain.tld", @@ -277,7 +277,7 @@ class LimitsTestCase(ModoTestCase, Operations): @classmethod def setUpTestData(cls): # NOQA:N802 """Create test data.""" - super(LimitsTestCase, cls).setUpTestData() + super().setUpTestData() for name, _definition in limits_utils.get_user_limit_templates(): cls.localconfig.parameters.set_value( f"deflt_user_{name}_limit", 2, app="limits" @@ -287,7 +287,7 @@ class LimitsTestCase(ModoTestCase, Operations): def setUp(self): """Initialize test.""" - super(LimitsTestCase, self).setUp() + super().setUp() self.client.force_login(self.user) def test_relay_domains_limit(self): diff --git a/modoboa/sievefilters/lib.py b/modoboa/sievefilters/lib.py index beb033a22..eaec2f733 100644 --- a/modoboa/sievefilters/lib.py +++ b/modoboa/sievefilters/lib.py @@ -76,7 +76,7 @@ class SieveClient: def pushscript(self, name: str, content: str, active: bool = False) -> None: if not self.msc.havespace(name, len(content)): - error = "%s (%s)" % (_("Not enough space on server"), self.msc.errmsg) + error = "{} ({})".format(_("Not enough space on server"), self.msc.errmsg) raise SieveClientError(error) if not self.msc.putscript(name, content): raise SieveClientError(self.msc.errmsg.decode()) diff --git a/modoboa/sievefilters/rfc6266.py b/modoboa/sievefilters/rfc6266.py index bbb973fd0..b79ea2b95 100644 --- a/modoboa/sievefilters/rfc6266.py +++ b/modoboa/sievefilters/rfc6266.py @@ -82,25 +82,25 @@ def build_header(filename, disposition="attachment", filename_compat=None): rv = disposition if is_token(filename): - rv += "; filename=%s" % (filename,) + rv += f"; filename={filename}" return rv elif is_ascii(filename) and is_lws_safe(filename): qd_filename = qd_quote(filename) - rv += '; filename="%s"' % (qd_filename,) + rv += f'; filename="{qd_filename}"' if qd_filename == filename: # RFC 6266 claims some implementations are iffy on qdtext's # backslash-escaping, we'll include filename* in that case. return rv elif filename_compat: if is_token(filename_compat): - rv += "; filename=%s" % (filename_compat,) + rv += f"; filename={filename_compat}" else: assert is_lws_safe(filename_compat) - rv += '; filename="%s"' % (qd_quote(filename_compat),) + rv += f'; filename="{qd_quote(filename_compat)}"' # alnum are already considered always-safe, but the rest isn't. # Python encodes ~ when it shouldn't, for example. - rv += "; filename*=utf-8''%s" % ( + rv += "; filename*=utf-8''{}".format( percent_encode(filename, safe=attr_chars_nonalnum, encoding="utf-8"), ) diff --git a/pyproject.toml b/pyproject.toml index 142caf645..203d00998 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -120,4 +120,4 @@ version = {attr = "modoboa.version.get_version"} exclude = ["migrations"] [tool.ruff.lint] -select = ["E4", "E7", "E9", "F", "Q"] +select = ["E4", "E7", "E9", "F", "B", "Q", "UP"]