New fixes

This commit is contained in:
Antoine Nguyen
2025-02-18 15:54:59 +01:00
parent f202b702df
commit db0b7eb79d
133 changed files with 405 additions and 1165 deletions
+9 -9
View File
@@ -161,7 +161,7 @@ class DomainAliasAPITestCase(ModoAPITestCase):
@classmethod
def setUpTestData(cls): # NOQA:N802
"""Create test data."""
super(DomainAliasAPITestCase, cls).setUpTestData()
super().setUpTestData()
factories.populate_database()
cls.dom_alias1 = factories.DomainAliasFactory(
name="dalias1.com", target__name="test.com"
@@ -185,7 +185,7 @@ class DomainAliasAPITestCase(ModoAPITestCase):
self.assertEqual(response.data["name"], "dalias1.com")
url = reverse("v1:domain_alias-list")
response = self.client.get("{}?domain=test.com".format(url))
response = self.client.get(f"{url}?domain=test.com")
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data), 1)
@@ -246,7 +246,7 @@ class AccountAPITestCase(ModoAPITestCase):
@classmethod
def setUpTestData(cls): # NOQA:N802
"""Create test data."""
super(AccountAPITestCase, cls).setUpTestData()
super().setUpTestData()
factories.populate_database()
cls.da_token = Token.objects.create(
user=core_models.User.objects.get(username="admin@test.com")
@@ -271,17 +271,17 @@ class AccountAPITestCase(ModoAPITestCase):
response = response.json()
self.assertEqual(len(response), 5)
response = self.client.get("{}?domain=test.com".format(url))
response = self.client.get(f"{url}?domain=test.com")
self.assertEqual(response.status_code, 200)
response = response.json()
self.assertEqual(len(response), 2)
response = self.client.get("{}?domain=pouet.com".format(url))
response = self.client.get(f"{url}?domain=pouet.com")
self.assertEqual(response.status_code, 200)
response = response.json()
self.assertEqual(len(response), 0)
response = self.client.get("{}?search=user@test.com".format(url))
response = self.client.get(f"{url}?search=user@test.com")
self.assertEqual(response.status_code, 200)
response = response.json()
self.assertEqual(len(response), 1)
@@ -608,7 +608,7 @@ class AliasAPITestCase(ModoAPITestCase):
@classmethod
def setUpTestData(cls): # NOQA:N802
"""Create test data."""
super(AliasAPITestCase, cls).setUpTestData()
super().setUpTestData()
cls.localconfig.parameters.set_value("enable_admin_limits", False, app="limits")
cls.localconfig.save()
factories.populate_database()
@@ -624,7 +624,7 @@ class AliasAPITestCase(ModoAPITestCase):
response = response.json()
self.assertEqual(len(response), 3)
response = self.client.get("{}?domain=test.com".format(url))
response = self.client.get(f"{url}?domain=test.com")
self.assertEqual(response.status_code, 200)
response = response.json()
self.assertEqual(len(response), 3)
@@ -744,7 +744,7 @@ class SenderAddressAPITestCase(ModoAPITestCase):
@classmethod
def setUpTestData(cls): # NOQA:N802
"""Create test data."""
super(SenderAddressAPITestCase, cls).setUpTestData()
super().setUpTestData()
cls.localconfig.parameters.set_value("enable_admin_limits", False, app="limits")
cls.localconfig.save()
factories.populate_database()
+2 -4
View File
@@ -136,7 +136,7 @@ class DomainSerializer(v1_serializers.DomainSerializer):
core_signals.can_create_object.send(
self.__class__, context=user, klass=models.Alias
)
address = "postmaster@{}".format(domain.name)
address = f"postmaster@{domain.name}"
alias = models.Alias.objects.create(
address=address, domain=domain, enabled=True
)
@@ -364,9 +364,7 @@ class AccountMeSerializer(v1_serializers.AccountSerializer):
permissions = (
user.user_permissions.all() | Permission.objects.filter(group__user=user)
).select_related("content_type")
return [
"{}.{}".format(p.content_type.app_label, p.codename) for p in permissions
]
return [f"{p.content_type.app_label}.{p.codename}" for p in permissions]
class AccountSerializer(v1_serializers.AccountSerializer):
+1 -1
View File
@@ -15,7 +15,7 @@ class DomainFactory(PermissionFactory):
model = models.Domain
django_get_or_create = ("name",)
name = factory.Sequence(lambda n: "domain{}.test".format(n))
name = factory.Sequence(lambda n: f"domain{n}.test")
type = "domain" # NOQA:A003
quota = 0
default_mailbox_quota = 10
+4 -4
View File
@@ -292,12 +292,12 @@ class AccountFormMail(forms.Form, DynamicForm):
self.fields["email"].required = True
qset = self.mb.aliasrecipient_set.filter(alias__internal=False)
for cpt, ralias in enumerate(qset):
name = "aliases_{}".format(cpt + 1)
name = f"aliases_{cpt + 1}"
self._create_field(
lib_fields.UTF8AndEmptyUserEmailField, name, ralias.alias.address
)
for cpt, saddress in enumerate(self.mb.senderaddress_set.all()):
name = "senderaddress_{}".format(cpt + 1)
name = f"senderaddress_{cpt + 1}"
self._create_field(
lib_fields.UTF8AndEmptyUserEmailField, name, saddress.address
)
@@ -336,14 +336,14 @@ class AccountFormMail(forms.Form, DynamicForm):
try:
self.domain = models.Domain.objects.get(name=domname)
except models.Domain.DoesNotExist:
raise forms.ValidationError(_("Domain does not exist"))
raise forms.ValidationError(_("Domain does not exist")) from None
if not self.mb:
try:
core_signals.can_create_object.send(
sender=self.__class__, context=self.domain, object_type="mailboxes"
)
except lib_exceptions.ModoboaException as inst:
raise forms.ValidationError(inst)
raise forms.ValidationError(inst) from None
return email
def clean_quota(self):
+1 -3
View File
@@ -123,9 +123,7 @@ class AliasForm(forms.ModelForm, DynamicForm):
"""Custom save method."""
alias = super(AliasForm, self).save(commit=False)
if self.cleaned_data.get("random_address"):
alias.address = "{}@{}".format(
Alias.generate_random_address(), alias.domain
)
alias.address = f"{Alias.generate_random_address()}@{alias.domain}"
else:
local_part, domname = split_mailbox(self.cleaned_data["address"])
alias.domain = Domain.objects.get(name=domname)
+11 -11
View File
@@ -80,7 +80,7 @@ class DomainFormGeneral(forms.ModelForm, DynamicForm):
if "instance" in kwargs:
self.old_dkim_key_length = kwargs["instance"].dkim_key_length
self.oldname = kwargs["instance"].name
super(DomainFormGeneral, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
params = dict(param_tools.get_global_parameters("admin"))
self.fields["quota"].initial = params["default_domain_quota"]
if params["default_domain_message_limit"] is not None:
@@ -97,7 +97,7 @@ class DomainFormGeneral(forms.ModelForm, DynamicForm):
elif "instance" in kwargs:
d = kwargs["instance"]
for pos, dalias in enumerate(d.domainalias_set.all()):
name = "aliases_%d" % (pos + 1)
name = f"aliases_{pos + 1}"
self._create_field(forms.CharField, name, dalias.name, 3)
def clean_name(self):
@@ -148,7 +148,7 @@ class DomainFormGeneral(forms.ModelForm, DynamicForm):
The validation way is not very smart...
"""
cleaned_data = super(DomainFormGeneral, self).clean()
cleaned_data = super().clean()
if self._errors:
return cleaned_data
condition = (
@@ -203,7 +203,7 @@ class DomainFormGeneral(forms.ModelForm, DynamicForm):
quotas update.
"""
d = super(DomainFormGeneral, self).save(commit=False)
d = super().save(commit=False)
core_signals.can_create_object.send(
sender=self.__class__, context=user, klass=Domain, instance=d
)
@@ -280,7 +280,7 @@ class DomainFormOptions(forms.Form):
)
def __init__(self, user, *args, **kwargs):
super(DomainFormOptions, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
results = core_signals.user_can_set_role.send(
sender=self.__class__, user=user, role="DomainAdmins"
)
@@ -296,7 +296,7 @@ class DomainFormOptions(forms.Form):
def clean(self):
"""Check required values."""
cleaned_data = super(DomainFormOptions, self).clean()
cleaned_data = super().clean()
if cleaned_data.get("create_dom_admin"):
if not cleaned_data.get("dom_admin_username"):
self.add_error("dom_admin_username", _("This field is required."))
@@ -311,7 +311,7 @@ class DomainFormOptions(forms.Form):
return
user = kwargs.pop("user")
domain = kwargs.pop("domain")
username = "%s@%s" % (self.cleaned_data["dom_admin_username"], domain.name)
username = f"{self.cleaned_data['dom_admin_username']}@{domain.name}"
try:
da = User.objects.get(username=username)
except User.DoesNotExist:
@@ -349,7 +349,7 @@ class DomainFormOptions(forms.Form):
core_signals.can_create_object.send(
self.__class__, context=user, klass=Alias
)
address = "postmaster@{}".format(domain.name)
address = f"postmaster@{domain.name}"
alias = Alias.objects.create(
address=address, domain=domain, enabled=True
)
@@ -386,7 +386,7 @@ class DomainForm(TabForms):
self.forms += reduce(lambda a, b: a + b, [result[1] for result in results])
if not self.forms:
self.active_id = "admins"
super(DomainForm, self).__init__(request, *args, **kwargs)
super().__init__(request, *args, **kwargs)
def extra_context(self, context):
"""Add information to template context."""
@@ -409,7 +409,7 @@ class DomainForm(TabForms):
if "general" in self.instances:
instance = self.instances["general"]
instance.oldname = instance.name
return super(DomainForm, self).is_valid()
return super().is_valid()
def save(self):
"""Custom save method.
@@ -437,7 +437,7 @@ class DomainWizard(WizardForm):
"""Domain creation wizard."""
def __init__(self, request):
super(DomainWizard, self).__init__(request)
super().__init__(request)
self.add_step(
WizardStep(
"general",
+6 -10
View File
@@ -27,13 +27,9 @@ def update_domain_mxs_and_mailboxes(sender, instance, **kwargs):
)
if instance.old_mail_homes is None:
return
qset = models.Quota.objects.filter(
username__contains="@{}".format(instance.oldname)
)
qset = models.Quota.objects.filter(username__contains=f"@{instance.oldname}")
for q in qset:
username = q.username.replace(
"@{}".format(instance.oldname), "@{}".format(instance.name)
)
username = q.username.replace(f"@{instance.oldname}", f"@{instance.name}")
models.Quota.objects.create(
username=username, bytes=q.bytes, messages=q.messages
)
@@ -62,17 +58,17 @@ def create_alias_for_domainalias(sender, instance, **kwargs):
if not kwargs.get("created"):
return
alias = models.Alias.objects.create(
address="@{}".format(instance.name), enabled=True, internal=True
address=f"@{instance.name}", enabled=True, internal=True
)
models.AliasRecipient.objects.create(
address="@{}".format(instance.target.name), alias=alias
address=f"@{instance.target.name}", alias=alias
)
@receiver(signals.post_delete, sender=models.DomainAlias)
def remove_alias_for_domainalias(sender, instance, **kwargs):
"""Remove the alias associated to domain alias."""
models.Alias.objects.filter(address="@{}".format(instance.name)).delete()
models.Alias.objects.filter(address=f"@{instance.name}").delete()
@receiver(signals.post_save, sender=models.Mailbox)
@@ -256,7 +252,7 @@ def import_account_mailbox(sender, user, account, row, **kwargs):
raise exceptions.BadRequest(
_("Account import failed (%s): wrong quota value")
% account.username
)
) from None
use_domain_quota = True if not quota else False
mb = models.Mailbox(
address=mailbox,
@@ -25,7 +25,7 @@ class Command(BaseCommand):
help = "Handles rename and delete operations on mailboxes" # NOQA:A003
def __init__(self, *args, **kwargs):
super(Command, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
self.logger = logging.getLogger("modoboa.admin")
def add_arguments(self, parser):
@@ -44,9 +44,9 @@ class Command(BaseCommand):
if not os.path.exists(dirname):
try:
os.makedirs(dirname)
except os.error as e:
raise OperationError(str(e))
code, output = exec_cmd("mv %s %s" % (operation.argument, new_mail_home))
except OSError as e:
raise OperationError(str(e)) from None
code, output = exec_cmd(f"mv {operation.argument} {new_mail_home}")
if code:
raise OperationError(output)
@@ -57,7 +57,7 @@ class Command(BaseCommand):
def onerror(function, path, excinfo):
"""Handle errors."""
self.logger.critical("delete failed (reason: {})".format(excinfo))
self.logger.critical(f"delete failed (reason: {excinfo})")
shutil.rmtree(operation.argument, False, onerror)
@@ -71,7 +71,7 @@ class Command(BaseCommand):
with open(path) as fp:
pid = fp.read().strip()
code, output = exec_cmd(
"grep handle_mailbox_operations /proc/%s/cmdline" % pid
f"grep handle_mailbox_operations /proc/{pid}/cmdline"
)
if not code:
return False
@@ -88,7 +88,7 @@ class Command(BaseCommand):
return
for ope in MailboxOperation.objects.all():
try:
f = getattr(self, "%s_mailbox" % ope.type)
f = getattr(self, f"{ope.type}_mailbox")
except AttributeError:
continue
try:
@@ -1,7 +1,6 @@
"""Django management command to import admin objects."""
import csv
import io
import os
import progressbar
@@ -49,12 +48,12 @@ class ImportCommand(BaseCommand):
if not os.path.isfile(filename):
raise CommandError("File not found")
num_lines = sum(1 for line in io.open(filename, encoding=encoding) if line)
num_lines = sum(1 for line in open(filename, encoding=encoding) if line)
pbar = progressbar.ProgressBar(
widgets=[progressbar.Percentage(), progressbar.Bar(), progressbar.ETA()],
maxval=num_lines,
).start()
with io.open(filename, encoding=encoding, newline="") as f:
with open(filename, encoding=encoding, newline="") as f:
reader = csv.reader(f, delimiter=options["sepchar"])
i = 0
for row in reader:
@@ -76,7 +75,7 @@ class ImportCommand(BaseCommand):
"Object already exists at line {}: {}".format(
i + 1, options["sepchar"].join(row[:2])
)
)
) from None
i += 1
pbar.update(i)
@@ -101,7 +100,7 @@ class ImportCommand(BaseCommand):
)
)
detector = UniversalDetector()
with io.open(filename, "rb") as fp:
with open(filename, "rb") as fp:
for line in fp:
detector.feed(line)
if detector.done:
@@ -19,7 +19,7 @@ class ManageDKIMKeys(BaseCommand):
def create_new_dkim_key(self, domain):
"""Create a new DKIM key."""
storage_dir = param_tools.get_global_parameter("dkim_keys_storage_dir")
pkey_path = os.path.join(storage_dir, "{}.pem".format(domain.name))
pkey_path = os.path.join(storage_dir, f"{domain.name}.pem")
alarm_qset = domain.alarms.filter(internal_name=DKIM_WRITE_ERROR)
if not os.access(storage_dir, os.W_OK):
@@ -43,26 +43,20 @@ class ManageDKIMKeys(BaseCommand):
if domain.dkim_key_length
else self.default_key_length
)
code, output = sysutils.exec_cmd(
"openssl genrsa -out {} {}".format(pkey_path, key_size)
)
code, output = sysutils.exec_cmd(f"openssl genrsa -out {pkey_path} {key_size}")
if code:
print(
"Failed to generate DKIM private key for domain {}: {}".format(
domain.name, smart_str(output)
)
f"Failed to generate DKIM private key for domain {domain.name}: {smart_str(output)}"
)
domain.alarms.create(
title=_("Failed to generate DKIM private key"), internal_name=DKIM_ERROR
)
return
domain.dkim_private_key_path = pkey_path
code, output = sysutils.exec_cmd("openssl rsa -in {} -pubout".format(pkey_path))
code, output = sysutils.exec_cmd(f"openssl rsa -in {pkey_path} -pubout")
if code:
print(
"Failed to generate DKIM public key for domain {}: {}".format(
domain.name, smart_str(output)
)
f"Failed to generate DKIM public key for domain {domain.name}: {smart_str(output)}"
)
domain.alarms.create(
title=_("Failed to generate DKIM public key"), internal_name=DKIM_ERROR
@@ -87,7 +87,7 @@ class CheckMXRecords(BaseCommand):
else:
delim = "." if ip.version == 4 else ":"
reverse = delim.join(ip.exploded.split(delim)[::-1])
pattern = "{}.{}.".format(reverse, provider)
pattern = f"{reverse}.{provider}."
try:
result = socket.gethostbyname(pattern)
# result from dnsbl is in ipv4 format
@@ -128,7 +128,7 @@ class CheckMXRecords(BaseCommand):
dnsbl_result.save()
if not dnsbl_result.status and result:
trigger = True
alarm_name = "domain_mx_in_dnsbl_{}".format(provider)
alarm_name = f"domain_mx_in_dnsbl_{provider}"
if trigger:
title = _("MX {} listed by DNSBL provider {}").format(mx.name, provider)
domain.alarms.create(
@@ -165,7 +165,7 @@ class CheckMXRecords(BaseCommand):
"""
alerts = []
check = False
mxs = [(mx, ipaddress.ip_address("%s" % mx.address)) for mx in mx_list]
mxs = [(mx, ipaddress.ip_address(str(mx.address))) for mx in mx_list]
valid_mxs = self.valid_mxs
if not mxs:
alarm, created = domain.alarms.get_or_create(
@@ -187,7 +187,7 @@ class CheckMXRecords(BaseCommand):
mx.managed = check = True
mx.save()
if check is False:
mx_names = ["{0.name} ({0.address})".format(mx) for mx in mx_list]
mx_names = [f"{mx.name} ({mx.address})" for mx in mx_list]
alarm, created = domain.alarms.get_or_create(
internal_name="domain_invalid_mx",
status=constants.ALARM_OPENED,
@@ -76,18 +76,16 @@ def sometimes_mailbox_have_no_alias(**options):
)
if created:
alias_created += 1
log("Alias {0} created".format(alias), **options)
log(f"Alias {alias} created", **options)
recipient, created = models.AliasRecipient.objects.get_or_create(
alias=alias, address=instance.full_address, r_mailbox=instance
)
if created:
recipient_created += 1
log("AliasRecipient {0} created".format(recipient), **options)
log(f"AliasRecipient {recipient} created", **options)
if alias_created or recipient_created:
log(
"{0} alias created. {1} alias recipient created".format(
alias_created, recipient_created
),
f"{alias_created} alias created. {recipient_created} alias recipient created",
**options,
)
@@ -129,5 +127,5 @@ class Repair(BaseCommand):
for func in known_problems:
title = func.__doc__.strip()
log("", **options)
log("Checking for... {}...".format(title), **options)
log(f"Checking for... {title}...", **options)
func(**options)
+1 -3
View File
@@ -48,9 +48,7 @@ class Alarm(models.Model):
ordering = ["created"]
def __str__(self):
return "[{}] {} - {}".format(
self.created, self.domain, self.get_status_display()
)
return f"[{self.created}] {self.domain} - {self.get_status_display()}"
def close(self):
"""Close this alarm."""
+1 -1
View File
@@ -306,4 +306,4 @@ class AliasRecipient(models.Model):
def __str__(self):
"""Return alias and recipient."""
return smart_str("{} -> {}".format(self.alias.address, self.address))
return smart_str(f"{self.alias.address} -> {self.address}")
+1 -3
View File
@@ -284,9 +284,7 @@ class Domain(mixins.MessageLimitMixin, AdminObject):
split_record.append(' "%s"' % record)
break
record = "\n".join(split_record)
return "{}._domainkey.{}. IN TXT (\n{})".format(
self.dkim_key_selector, self.name, record
)
return f"{self.dkim_key_selector}._domainkey.{self.name}. IN TXT (\n{record})"
def add_admin(self, account):
"""Add a new administrator to this domain.
+1 -1
View File
@@ -25,7 +25,7 @@ class QuotaManager(models.Manager):
def get_domain_usage(self, domain):
"""Return current usage for domain."""
qset = self.get_queryset().filter(username__endswith="@{}".format(domain.name))
qset = self.get_queryset().filter(username__endswith=f"@{domain.name}")
result = qset.aggregate(usage=models.Sum("bytes")).get("usage", 0)
if result is None:
result = 0
+2 -2
View File
@@ -45,7 +45,7 @@ class MXRecordManager(models.Manager):
record = self.get_queryset().create(
domain=domain,
name="{}".format(mx_addr.strip(".")),
address="{}".format(mx_ip_addr),
address=f"{mx_ip_addr}",
updated=now + delta,
)
yield record
@@ -68,7 +68,7 @@ class MXRecord(models.Model):
return bool(param_tools.get_global_parameter("valid_mxs").strip())
def __str__(self):
return "{0.name} ({0.address}) for {0.domain} ".format(self)
return f"{self.name} ({self.address}) for {self.domain} "
class DNSBLQuerySet(models.QuerySet):
+5 -5
View File
@@ -1,7 +1,7 @@
"""Map file definitions for postfix."""
class DomainsMap(object):
class DomainsMap:
"""Map to list all domains."""
filename = "sql-domains.cf"
@@ -18,7 +18,7 @@ class DomainsMap(object):
)
class DomainsAliasesMap(object):
class DomainsAliasesMap:
"""Map to list all domain aliases."""
filename = "sql-domain-aliases.cf"
@@ -39,7 +39,7 @@ class DomainsAliasesMap(object):
)
class AliasesMap(object):
class AliasesMap:
"""A map to list all mailbox aliases."""
filename = "sql-aliases.cf"
@@ -63,7 +63,7 @@ class AliasesMap(object):
)
class MaintainMap(object):
class MaintainMap:
"""Map files to list non available mailboxes."""
filename = "sql-maintain.cf"
@@ -90,7 +90,7 @@ class MaintainMap(object):
)
class SenderLoginMap(object):
class SenderLoginMap:
"""Map file to list authorized sender addresses for a given account:
* Its mailbox
* Its aliases
+4 -4
View File
@@ -576,14 +576,14 @@ class AccountTestCase(ModoTestCase):
self.assertIn("user@test.com", response["rows"])
old_rows = response["rows"]
response = self.ajax_get("{}?sort_order=-quota_value__bytes".format(url))
response = self.ajax_get(f"{url}?sort_order=-quota_value__bytes")
self.assertNotEqual(old_rows, response["rows"])
old_rows = response["rows"]
response = self.ajax_get("{}?sort_order=-quota_usage".format(url))
response = self.ajax_get(f"{url}?sort_order=-quota_usage")
self.assertEqual(old_rows, response["rows"])
response = self.ajax_get("{}?sort_order=-unknown".format(url), status=400)
response = self.ajax_get(f"{url}?sort_order=-unknown", status=400)
@skipIf(NO_LDAP, "No ldap module installed")
@@ -615,7 +615,7 @@ class PermissionsTestCase(ModoTestCase):
super(PermissionsTestCase, cls).setUpTestData()
parameters = {}
for name, _definition in limits_utils.get_user_limit_templates():
parameters["deflt_user_{0}_limit".format(name)] = 2
parameters[f"deflt_user_{name}_limit"] = 2
cls.localconfig.parameters.set_values(parameters, app="limits")
cls.localconfig.save()
factories.populate_database()
+5 -5
View File
@@ -364,10 +364,10 @@ class DomainTestCase(ModoTestCase):
self.assertIn("test.com", response["rows"])
old_rows = response["rows"]
response = self.ajax_get("{}?sort_order=-name".format(url))
response = self.ajax_get(f"{url}?sort_order=-name")
self.assertNotEqual(old_rows, response["rows"])
response = self.ajax_get("{}?sort_order=allocated_quota".format(url))
response = self.ajax_get(f"{url}?sort_order=allocated_quota")
self.assertNotEqual(old_rows, response["rows"])
def test_domain_logs_list_view(self):
@@ -386,10 +386,10 @@ class DomainTestCase(ModoTestCase):
self.assertIn("ID1", response["rows"])
old_rows = response["rows"]
response = self.ajax_get("{}?sort_order=-sender".format(url))
response = self.ajax_get(f"{url}?sort_order=-sender")
self.assertNotEqual(old_rows, response["rows"])
response = self.ajax_get("{}?searchquery=ID1".format(url))
response = self.ajax_get(f"{url}?searchquery=ID1")
self.assertNotEqual(old_rows, response["rows"])
admin = User.objects.get(username="admin@test.com")
@@ -421,7 +421,7 @@ class DomainTestCase(ModoTestCase):
response = self.ajax_get(url)
self.assertIn("handle_mailboxes", response)
self.assertIn("listalarms", response["rows"])
response = self.ajax_get("{}?objtype=quota".format(url))
response = self.ajax_get(f"{url}?objtype=quota")
self.assertIn("progress-bar", response["rows"])
+2 -4
View File
@@ -21,15 +21,13 @@ class DomainAliasTestCase(ModoTestCase):
domal.target = dom
domal.save()
self.assertEqual(dom.domainalias_count, 1)
self.assertTrue(Alias.objects.filter(address="@{}".format(domal.name)).exists())
self.assertTrue(Alias.objects.filter(address=f"@{domal.name}").exists())
domal.name = "domalias.org"
domal.save()
domal.delete()
self.assertFalse(
Alias.objects.filter(address="@{}".format(domal.name)).exists()
)
self.assertFalse(Alias.objects.filter(address=f"@{domal.name}").exists())
def test_form(self):
dom = Domain.objects.get(name="test.com")
+2 -4
View File
@@ -21,7 +21,7 @@ class ImportTestCase(ModoTestCase):
@classmethod
def setUpTestData(cls): # NOQA:N802
"""Create test data."""
super(ImportTestCase, cls).setUpTestData()
super().setUpTestData()
cls.localconfig.parameters.set_value("enable_admin_limits", False, app="limits")
cls.localconfig.save()
factories.populate_database()
@@ -326,9 +326,7 @@ alias;user@test.com;True;admin@test.com
f = ContentFile(
"""domain; dómªin1.com; 1000; 100; True
dómain; dómªin2.com; 1000; 100; True
""".encode(
"utf8"
),
""".encode(),
name="dómains.csv",
)
self.client.post(reverse("admin:domain_import"), {"sourcefile": f})
@@ -13,7 +13,7 @@ from modoboa.lib.tests import ModoTestCase
from .. import factories, models
@override_settings(DOVECOT_LOOKUP_PATH=["{}/dovecot".format(os.path.dirname(__file__))])
@override_settings(DOVECOT_LOOKUP_PATH=[f"{os.path.dirname(__file__)}/dovecot"])
class MailboxOperationTestCase(ModoTestCase):
"""Test management command."""
@@ -27,7 +27,7 @@ class MailboxOperationTestCase(ModoTestCase):
"""Initiate test env."""
super(MailboxOperationTestCase, self).setUp()
self.workdir = tempfile.mkdtemp()
path = "{}/test.com/admin".format(self.workdir)
path = f"{self.workdir}/test.com/admin"
os.makedirs(path)
self.set_global_parameter("handle_mailboxes", True)
self.set_global_parameter("enable_admin_limits", False, app="limits")
@@ -39,7 +39,7 @@ class MailboxOperationTestCase(ModoTestCase):
@mock.patch("modoboa.admin.models.Mailbox.mail_home")
def test_delete_account(self, mail_home_mock):
"""Check delete operation."""
path = "{}/test.com/admin".format(self.workdir)
path = f"{self.workdir}/test.com/admin"
mail_home_mock.__get__ = mock.Mock(return_value=path)
mb = models.Mailbox.objects.select_related("user").get(
address="admin", domain__name="test.com"
@@ -52,7 +52,7 @@ class MailboxOperationTestCase(ModoTestCase):
@mock.patch("modoboa.admin.models.Mailbox.mail_home")
def test_rename_account(self, mail_home_mock):
"""Check rename operation."""
path = "{}/test.com/admin".format(self.workdir)
path = f"{self.workdir}/test.com/admin"
mail_home_mock.__get__ = mock.Mock(return_value=path)
mb = models.Mailbox.objects.select_related("user").get(
address="admin", domain__name="test.com"
@@ -67,7 +67,7 @@ class MailboxOperationTestCase(ModoTestCase):
"content": "content",
}
self.ajax_post(reverse("admin:account_change", args=[mb.user.pk]), values)
path = "{}/test.com/admin2".format(self.workdir)
path = f"{self.workdir}/test.com/admin2"
mail_home_mock.__get__ = mock.Mock(return_value=path)
call_command("handle_mailbox_operations")
self.assertFalse(models.MailboxOperation.objects.exists())
@@ -76,7 +76,7 @@ class MailboxOperationTestCase(ModoTestCase):
@mock.patch("modoboa.admin.models.Mailbox.mail_home")
def test_delete_domain(self, mail_home_mock):
"""Check delete operation."""
path = "{}/test.com/admin".format(self.workdir)
path = f"{self.workdir}/test.com/admin"
mail_home_mock.__get__ = mock.Mock(return_value=path)
domain = models.Domain.objects.get(name="test.com")
self.ajax_post(reverse("admin:domain_delete", args=[domain.pk]))
+1 -1
View File
@@ -110,7 +110,7 @@ class MXTestCase(ModoTestCase):
mb = factories.MailboxFactory(
address="admin",
domain=domain,
user__username="admin@{}".format(domain.name),
user__username=f"admin@{domain.name}",
user__groups=("DomainAdmins",),
)
domain.add_admin(mb.user)
+2 -2
View File
@@ -11,13 +11,13 @@ from dns.rdtypes.ANY.TXT import TXT
from dns.resolver import NXDOMAIN, NoAnswer, NoNameservers, Timeout
class RRset(object):
class RRset:
def __init__(self):
self.address = "192.0.2.1"
class RRsetInvalid(object):
class RRsetInvalid:
def __init__(self):
self.address = "000.0.0.0"
+4 -4
View File
@@ -150,10 +150,10 @@ def list_quotas(request):
domains = Domain.objects.get_for_admin(request.user)
domains = domains.exclude(quota=0)
if sort_order in ["name", "quota"]:
domains = domains.order_by("{}{}".format(sort_dir, sort_order))
domains = domains.order_by(f"{sort_dir}{sort_order}")
elif sort_order == "allocated_quota":
domains = domains.annotate(allocated_quota=Sum("mailbox__quota")).order_by(
"{}{}".format(sort_dir, sort_order)
f"{sort_dir}{sort_order}"
)
page = get_listing_page(domains, request.GET.get("page", 1))
context = {
@@ -182,7 +182,7 @@ def list_logs(request):
)
else:
logs = ml_models.Maillog.objects.all()
logs = logs.order_by("{}{}".format(sort_dir, sort_order))
logs = logs.order_by(f"{sort_dir}{sort_order}")
if search:
logs = logs.filter(
Q(sender__icontains=search)
@@ -266,7 +266,7 @@ class DomainDetailView(auth_mixins.PermissionRequiredMixin, generic.DetailView):
def get_context_data(self, **kwargs):
"""Include extra widgets."""
context = super(DomainDetailView, self).get_context_data(**kwargs)
context = super().get_context_data(**kwargs)
result = signals.extra_domain_dashboard_widgets.send(
self.__class__, user=self.request.user, domain=self.object
)
+1 -1
View File
@@ -27,7 +27,7 @@ def _export(content, filename):
resp = HttpResponse(content)
resp["Content-Type"] = "text/csv"
resp["Content-Length"] = len(content)
resp["Content-Disposition"] = 'attachment; filename="{}"'.format(filename)
resp["Content-Disposition"] = f'attachment; filename="{filename}"'
return resp
+3 -3
View File
@@ -3,7 +3,7 @@
from importlib import import_module
class CalendarBackend(object):
class CalendarBackend:
"""Base backend class."""
def __init__(self, calendar=None):
@@ -29,8 +29,8 @@ class CalendarBackend(object):
def get_backend(name, *args, **kwargs):
"""Return a backend instance."""
module = import_module("modoboa.calendars.backends.{}".format(name))
return getattr(module, "{}Backend".format(name.capitalize()))(*args, **kwargs)
module = import_module(f"modoboa.calendars.backends.{name}")
return getattr(module, f"{name.capitalize()}Backend")(*args, **kwargs)
def get_backend_from_request(name, request, calendar=None):
+4 -4
View File
@@ -94,7 +94,7 @@ class Caldav_Backend(CalendarBackend):
def update_event(self, uid, original_data):
"""Update an existing event."""
data = dict(original_data)
url = "{}/{}.ics".format(self.remote_cal.url.geturl(), uid)
url = f"{self.remote_cal.url.geturl()}/{uid}.ics"
cal = self.remote_cal.event_by_url(url)
orig_evt = cal.vobject_instance.vevent
if "title" in data:
@@ -125,7 +125,7 @@ class Caldav_Backend(CalendarBackend):
# Calendar has been changed, remove old event first.
self.remote_cal.client.delete(url)
remote_cal = Calendar(self.client, data["calendar"].encoded_path)
url = "{}/{}.ics".format(remote_cal.url.geturl(), uid)
url = f"{remote_cal.url.geturl()}/{uid}.ics"
else:
remote_cal = self.remote_cal
remote_cal.add_event(cal.instance)
@@ -133,7 +133,7 @@ class Caldav_Backend(CalendarBackend):
def get_event(self, uid):
"""Retrieve and event using its uid."""
url = "{}/{}.ics".format(self.remote_cal.url.geturl(), uid)
url = f"{self.remote_cal.url.geturl()}/{uid}.ics"
event = self.remote_cal.event_by_url(url)
return self._serialize_event(event)
@@ -147,7 +147,7 @@ class Caldav_Backend(CalendarBackend):
def delete_event(self, uid):
"""Delete an event using its uid."""
url = "{}/{}.ics".format(self.remote_cal.url.geturl(), uid)
url = f"{self.remote_cal.url.geturl()}/{uid}.ics"
self.remote_cal.client.delete(url)
def import_events(self, fp):
+2 -2
View File
@@ -48,7 +48,7 @@ def set_user_calendar_path(sender, instance, **kwargs):
"""Set path at creation."""
if instance.pk:
return
instance._path = "{}/{}".format(instance.mailbox.full_address, instance.name)
instance._path = f"{instance.mailbox.full_address}/{instance.name}"
if not instance.access_token:
instance.access_token = token_hex(16)
@@ -58,6 +58,6 @@ def set_shared_calendar_path(sender, instance, **kwargs):
"""Set path at creation."""
if instance.pk:
return
instance._path = "{}/{}".format(instance.domain.name, instance.name)
instance._path = f"{instance.domain.name}/{instance.name}"
if not instance.access_token:
instance.access_token = token_hex(16)
@@ -30,21 +30,20 @@ class Command(BaseCommand):
def _generate_acr(self, name, user, collection, perm="rw", comment=None):
"""Write a new access control rule to the config file."""
if comment is not None:
self._cfgfile.write("\n# %s" % comment)
self._cfgfile.write(f"\n# {comment}")
self._cfgfile.write(
"""
[%s]
user = %s
collection = %s
permissions = %s
f"""
[{name}]
user = {user}
collection = {collection}
permissions = {perm}
"""
% (name, user, collection, perm)
)
def _user_access_rules(self):
"""Create user access rules."""
for acr in models.AccessRule.objects.select_related().all():
section = "%s-to-%s-acr" % (acr.mailbox, acr.calendar)
section = f"{acr.mailbox}-to-{acr.calendar}-acr"
permission = ""
if acr.read:
permission += "r"
@@ -61,7 +60,7 @@ permissions = %s
"""Create token access rules."""
for model in [models.UserCalendar, models.SharedCalendar]:
for calendar in model.objects.all():
section = "token-{}-access".format(calendar._path)
section = f"token-{calendar._path}-access"
self._generate_acr(
section,
calendar.access_token,
@@ -73,15 +72,15 @@ permissions = %s
def _super_admin_rules(self):
"""Generate access rules for super administrators."""
for sa in User.objects.filter(is_superuser=True):
section = "sa-%s-acr" % sa.username
section = f"sa-{sa.username}-acr"
self._generate_acr(section, sa.username, ".*")
def _domain_admin_rules(self):
"""Generate access rules for domain adminstrators."""
for da in User.objects.filter(groups__name="DomainAdmins"):
for domain in Domain.objects.get_for_admin(da):
section = "da-%s-to-%s-acr" % (da.email, domain.name)
self._generate_acr(section, da.email, "%s/user/.*" % domain.name)
section = f"da-{da.email}-to-{domain.name}-acr"
self._generate_acr(section, da.email, f"{domain.name}/user/.*")
def _generate_file(self, target):
"""
@@ -89,11 +88,10 @@ permissions = %s
"""
self._cfgfile = open(target, "w")
self._cfgfile.write(
"""# Rights management file for Radicale
# This file was generated by Modoboa on %s
f"""# Rights management file for Radicale
# This file was generated by Modoboa on {datetime.datetime.today()}
# DO NOT EDIT MANUALLY!
"""
% datetime.datetime.today()
)
allow_calendars_administration = param_tools.get_global_parameter(
+2 -2
View File
@@ -31,7 +31,7 @@ END:VCALENDAR
"""
class Url(object):
class Url:
def __init__(self, path):
self.path = path
@@ -85,5 +85,5 @@ class DAVClientMock:
def proppatch(self, url, body, dummy=None):
return Response(200)
def request(self, url, method="GET", body="", headers={}):
def request(self, url, method="GET", body="", headers=None):
return Response(200)
+1 -1
View File
@@ -53,7 +53,7 @@ class Calendar(models.Model):
@property
def share_url(self):
"""Return calendar share url."""
return "{}?token={}".format(self.full_url, self.access_token)
return f"{self.full_url}?token={self.access_token}"
@property
def encoded_url(self):
+16 -18
View File
@@ -1,5 +1,3 @@
# coding: utf-8
"""Radicale extension unit tests."""
import os
@@ -24,13 +22,13 @@ from . import models
from . import mocks
class TestDataMixin(object):
class TestDataMixin:
"""Create some data."""
@classmethod
def setUpTestData(cls):
"""Create test data."""
super(TestDataMixin, cls).setUpTestData()
super().setUpTestData()
admin_factories.populate_database()
cls.account = core_models.User.objects.get(username="user@test.com")
cls.calendar = factories.UserCalendarFactory(mailbox=cls.account.mailbox)
@@ -60,12 +58,12 @@ class AccessRuleTestCase(ModoTestCase):
@classmethod
def setUpTestData(cls):
"""Create test data."""
super(AccessRuleTestCase, cls).setUpTestData()
super().setUpTestData()
populate_database()
def setUp(self):
"""Initialize tests."""
super(AccessRuleTestCase, self).setUp()
super().setUp()
self.rights_file_path = tempfile.mktemp()
self.set_global_parameter(
"rights_file_path", self.rights_file_path, app="calendars"
@@ -394,12 +392,12 @@ class EventViewSetTestCase(TestDataMixin, ModoAPITestCase):
self.client.post(reverse("core:login"), data)
# FIXME: drf nested routers does not handle reverse() properly
url = "/api/v2/user-calendars/{}/events/{}/".format(self.calendar.pk, 1234)
url = f"/api/v2/user-calendars/{self.calendar.pk}/events/{1234}/"
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertTrue(response.json()["title"].startswith("Bastille"))
url = "/api/v2/user-calendars/{}/events/".format(self.calendar.pk)
url = f"/api/v2/user-calendars/{self.calendar.pk}/events/"
url = "{}?start={}&end={}".format(url, "20060712T182145Z", "20070712T182145Z")
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
@@ -411,12 +409,12 @@ class EventViewSetTestCase(TestDataMixin, ModoAPITestCase):
self.client.post(reverse("core:login"), data)
# FIXME: drf nested routers does not handle reverse() properly
url = "/api/v2/shared-calendars/{}/events/{}/".format(self.scalendar.pk, 1234)
url = f"/api/v2/shared-calendars/{self.scalendar.pk}/events/{1234}/"
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertTrue(response.json()["title"].startswith("Bastille"))
url = "/api/v2/shared-calendars/{}/events/".format(self.scalendar.pk)
url = f"/api/v2/shared-calendars/{self.scalendar.pk}/events/"
url = "{}?start={}&end={}".format(url, "20060712T182145Z", "20070712T182145Z")
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
@@ -427,7 +425,7 @@ class EventViewSetTestCase(TestDataMixin, ModoAPITestCase):
data = {"username": "user@test.com", "password": "toto"}
self.client.post(reverse("core:login"), data)
url = "/api/v2/user-calendars/{}/events/".format(self.calendar.pk)
url = f"/api/v2/user-calendars/{self.calendar.pk}/events/"
data = {
"title": "Test event",
"start": "2018-03-27T00:00:00Z",
@@ -441,7 +439,7 @@ class EventViewSetTestCase(TestDataMixin, ModoAPITestCase):
self.assertEqual(response.status_code, 201)
self.assertIn("id", response.json())
url = "/api/v2/shared-calendars/{}/events/".format(self.scalendar.pk)
url = f"/api/v2/shared-calendars/{self.scalendar.pk}/events/"
data = {
"title": "Test event 2",
"start": "2018-03-27T00:00:00Z",
@@ -460,7 +458,7 @@ class EventViewSetTestCase(TestDataMixin, ModoAPITestCase):
data = {"username": "user@test.com", "password": "toto"}
self.client.post(reverse("core:login"), data)
url = "/api/v2/user-calendars/{}/events/1234/".format(self.calendar.pk)
url = f"/api/v2/user-calendars/{self.calendar.pk}/events/1234/"
data = {
"title": "Test event",
"start": "2018-03-27T00:00:00Z",
@@ -483,7 +481,7 @@ class EventViewSetTestCase(TestDataMixin, ModoAPITestCase):
data = {"username": "user@test.com", "password": "toto"}
self.client.post(reverse("core:login"), data)
url = "/api/v2/user-calendars/{}/events/1234/".format(self.calendar.pk)
url = f"/api/v2/user-calendars/{self.calendar.pk}/events/1234/"
data = {
"start": "2018-03-27T00:00:00Z",
"end": "2018-03-28T00:00:00Z",
@@ -506,7 +504,7 @@ class EventViewSetTestCase(TestDataMixin, ModoAPITestCase):
"calendar": self.scalendar.pk,
"new_calendar_type": "shared",
}
url = "/api/v2/user-calendars/{}/events/1234/".format(self.calendar.pk)
url = f"/api/v2/user-calendars/{self.calendar.pk}/events/1234/"
response = self.client.put(url, data=data, format="json")
self.assertEqual(response.status_code, 200)
@@ -515,7 +513,7 @@ class EventViewSetTestCase(TestDataMixin, ModoAPITestCase):
data = {"username": "user@test.com", "password": "toto"}
self.client.post(reverse("core:login"), data)
url = "/api/v2/shared-calendars/{}/events/1234/".format(self.scalendar.pk)
url = f"/api/v2/shared-calendars/{self.scalendar.pk}/events/1234/"
response = self.client.delete(url)
self.assertEqual(response.status_code, 200)
@@ -546,7 +544,7 @@ class AttendeeViewSetTestCase(ModoAPITestCase):
@classmethod
def setUpTestData(cls):
super(AttendeeViewSetTestCase, cls).setUpTestData()
super().setUpTestData()
populate_database()
cls.account = core_models.User.objects.get(username="user@test.com")
@@ -567,7 +565,7 @@ class MailboxViewSetTestCase(ModoAPITestCase):
@classmethod
def setUpTestData(cls):
super(MailboxViewSetTestCase, cls).setUpTestData()
super().setUpTestData()
populate_database()
cls.account = core_models.User.objects.get(username="user@test.com")
+1 -3
View File
@@ -51,9 +51,7 @@ class ContactFactory(factory.django.DjangoModelFactory):
first_name = "Homer"
last_name = "Simpson"
display_name = factory.LazyAttribute(
lambda c: "{}{}".format(c.first_name, c.last_name)
)
display_name = factory.LazyAttribute(lambda c: f"{c.first_name}{c.last_name}")
@factory.post_generation
def categories(self, create, extracted, **dummy_kwargs):
+3 -3
View File
@@ -53,9 +53,9 @@ nsmap = {
def ns(prefix, tag=None):
name = "{%s}" % nsmap[prefix]
name = f"{{{nsmap[prefix]}}}"
if tag is not None:
name = "%s%s" % (name, tag)
name = f"{name}{tag}"
return name
@@ -91,7 +91,7 @@ class SyncCollectionQuery(elements.base.BaseElement):
tag = ns("D", "sync-collection")
class PyCardDAV(object):
class PyCardDAV:
"""class for interacting with a CardDAV server
Since PyCardDAV relies heavily on Requests [1] its SSL verification is also
+3 -5
View File
@@ -98,10 +98,10 @@ def report_mock(url, request):
def get_mock(url, request):
"""Simulate a GET request."""
uid = url.path.split("/")[-1]
content = """
content = f"""
BEGIN:VCARD
VERSION:3.0
UID:{}
UID:{uid}
N:Gump;Forrest
FN:Forrest Gump
ORG:Bubba Gump Shrimp Co.
@@ -111,7 +111,5 @@ TEL;TYPE=HOME;VOICE:(404) 555-1212
ADR;TYPE=HOME:;;42 Plantation St.;Baytown;LA;30314;United States of America
EMAIL;TYPE=PREF,INTERNET:forrestgump@example.com
END:VCARD
""".format(
uid
)
"""
return {"status_code": 200, "content": content}
+4 -6
View File
@@ -82,18 +82,16 @@ class Contact(models.Model):
"""Set uid for new object."""
super().__init__(*args, **kwargs)
if not self.pk:
self.uid = "{}.vcf".format(uuid.uuid4())
self.uid = f"{uuid.uuid4()}.vcf"
@property
def url(self):
return "{}/{}.vcf".format(self.addressbook.url, self.uid)
return f"{self.addressbook.url}/{self.uid}.vcf"
def to_vcard(self):
"""Convert this contact to a vCard."""
card = vobject.vCard()
card.add("prodid").value = "-//Modoboa//Contacts plugin {}//EN".format(
__version__
)
card.add("prodid").value = f"-//Modoboa//Contacts plugin {__version__}//EN"
card.add("uid").value = self.uid
card.add("n").value = vobject.vcard.Name(
family=self.last_name, given=self.first_name
@@ -193,4 +191,4 @@ class PhoneNumber(models.Model):
db_table = "modoboa_contacts_phonenumber"
def __str__(self):
return "{}: {}".format(self.type, self.number)
return f"{self.type}: {self.number}"
+1 -1
View File
@@ -38,7 +38,7 @@ class EmailAddressWithNameSerializer(serializers.ModelSerializer):
"""Return display name."""
if obj.contact.display_name:
return obj.contact.display_name
return "{} {}".format(obj.contact.first_name, obj.contact.last_name)
return f"{obj.contact.first_name} {obj.contact.last_name}"
class PhoneNumberSerializer(serializers.ModelSerializer):
+5 -6
View File
@@ -1,4 +1,3 @@
# coding: utf-8
"""Contacts backend tests."""
import os
@@ -172,14 +171,14 @@ class ContactViewSetTestCase(TestDataMixin, ModoAPITestCase):
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data), 3)
response = self.client.get("{}?search=homer".format(url))
response = self.client.get(f"{url}?search=homer")
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data), 1)
def test_filter_by_category(self):
"""Check filtering."""
url = reverse("api:contact-list")
response = self.client.get("{}?category={}".format(url, self.category.name))
response = self.client.get(f"{url}?category={self.category.name}")
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data), 1)
@@ -300,15 +299,15 @@ class EmailAddressViewSetTestCase(TestDataMixin, ModoAPITestCase):
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data), 3)
response = self.client.get("{}?search=homer".format(url))
response = self.client.get(f"{url}?search=homer")
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data), 1)
response = self.client.get("{}?search=Marge".format(url))
response = self.client.get(f"{url}?search=Marge")
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data), 1)
response = self.client.get("{}?search=Simpson".format(url))
response = self.client.get(f"{url}?search=Simpson")
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data), 3)
+2 -2
View File
@@ -29,7 +29,7 @@ class AccountViewSet(GetThrottleViewsetMixin, viewsets.ViewSet):
def tfa_setup(self, request):
"""Initiate TFA setup."""
instance, created = TOTPDevice.objects.get_or_create(
user=request.user, defaults={"name": "{} TOTP device".format(request.user)}
user=request.user, defaults={"name": f"{request.user} TOTP device"}
)
return response.Response()
@@ -43,7 +43,7 @@ class AccountViewSet(GetThrottleViewsetMixin, viewsets.ViewSet):
serializer.is_valid(raise_exception=True)
# create static device for recovery purposes
device = StaticDevice.objects.create(
user=request.user, name="{} static device".format(request.user)
user=request.user, name=f"{request.user} static device"
)
for cpt in range(10):
token = StaticToken.random_token()
+2 -2
View File
@@ -32,12 +32,12 @@ def create_static_tokens(request):
device = request.user.staticdevice_set.first()
if device is None:
device = StaticDevice.objects.create(
user=request.user, name="{} static device".format(request.user)
user=request.user, name=f"{request.user} static device"
)
elif device is not None:
return None
tokens = []
for cpt in range(10):
for _cpt in range(10):
token = StaticToken.random_token()
device.token_set.create(token=token)
tokens.append(token)
+1 -1
View File
@@ -7,7 +7,7 @@ from django.template import Context, Template
from django.utils.encoding import smart_str
class Command(object):
class Command:
"""Base command class.
A valid administrative command must inherit from this class.
+12 -12
View File
@@ -117,7 +117,7 @@ class DeployCommand(Command):
"\n".join([line.decode() for line in output if line is not None]),
file=sys.stderr,
)
print("%s failed, check your configuration" % cmd, file=sys.stderr)
print(f"{cmd} failed, check your configuration", file=sys.stderr)
def ask_db_info(self, name="default"):
"""Prompt the user for database information
@@ -127,7 +127,7 @@ class DeployCommand(Command):
:param name: the connection name
"""
print("Configuring database connection: %s" % name)
print(f"Configuring database connection: {name}")
info = {
"conn_name": name,
"ENGINE": input("Database type (mysql, postgres or sqlite3): "),
@@ -137,7 +137,7 @@ class DeployCommand(Command):
if info["ENGINE"] == "sqlite3":
info["ENGINE"] = "django.db.backends.sqlite3"
info["NAME"] = "%s.db" % name
info["NAME"] = f"{name}.db"
return info
if info["ENGINE"] == "postgres":
info["ENGINE"] = "django.db.backends.postgresql"
@@ -146,7 +146,7 @@ class DeployCommand(Command):
info["ENGINE"] = "django.db.backends.mysql"
default_port = 3306
info["HOST"] = input("Database host (default: 'localhost'): ")
info["PORT"] = input("Database port (default: '%s'): " % default_port)
info["PORT"] = input(f"Database port (default: '{default_port}'): ")
# leave port setting empty, if default value is supplied and
# leave it to django
if info["PORT"] == default_port:
@@ -181,7 +181,7 @@ class DeployCommand(Command):
for extension in extensions:
module = __import__(extension[1], locals(), globals(), [])
basedir = os.path.dirname(module.__file__)
if not os.path.exists("{0}/settings.py".format(basedir)):
if not os.path.exists(f"{basedir}/settings.py"):
continue
extra_settings.append(extension[1])
return extra_settings
@@ -189,7 +189,7 @@ class DeployCommand(Command):
def handle(self, parsed_args):
django.setup()
management.call_command("startproject", parsed_args.name, verbosity=False)
path = "%(name)s/%(name)s" % {"name": parsed_args.name}
path = f"{parsed_args.name}/{parsed_args.name}"
sys.path.append(parsed_args.name)
conn_tpl = Template(DBCONN_TPL)
@@ -240,7 +240,7 @@ class DeployCommand(Command):
mod = __import__(parsed_args.name, globals(), locals(), [smart_str("settings")])
tpl = self._render_template(
"%s/settings.py.tpl" % self._templates_dir,
f"{self._templates_dir}/settings.py.tpl",
{
"db_connections": connections,
"secret_key": management.utils.get_random_secret_key(),
@@ -254,15 +254,15 @@ class DeployCommand(Command):
"extra_settings": extra_settings,
},
)
with open("%s/settings.py" % path, "w") as fp:
with open(f"{path}/settings.py", "w") as fp:
fp.write(tpl)
generate_rsa_private_key(parsed_args.name)
shutil.copyfile("%s/urls.py.tpl" % self._templates_dir, "%s/urls.py" % path)
os.mkdir("%s/media" % parsed_args.name)
shutil.copyfile(f"{self._templates_dir}/urls.py.tpl", f"{path}/urls.py")
os.mkdir(f"{parsed_args.name}/media")
if isfile("%s/settings.pyc" % path):
os.unlink("%s/settings.pyc" % path)
if isfile(f"{path}/settings.pyc"):
os.unlink(f"{path}/settings.pyc")
self._exec_django_command("migrate", parsed_args.name, "--noinput")
self._exec_django_command(
"load_initial_data",
+1 -1
View File
@@ -124,7 +124,7 @@ class ExtensionsPool:
root = ""
pattern = "{}.urls_api"
else:
root = r"^{}/".format(ext.get_url())
root = rf"^{ext.get_url()}/"
pattern = "{}.urls"
try:
result.append(re_path(root, include(pattern.format(ext_name))))
+4 -4
View File
@@ -10,7 +10,7 @@ from . import models
class PermissionFactory(factory.django.DjangoModelFactory):
"""A base factory to handle permissions."""
class Meta(object):
class Meta:
abstract = True
@factory.post_generation
@@ -23,7 +23,7 @@ class PermissionFactory(factory.django.DjangoModelFactory):
class GroupFactory(factory.django.DjangoModelFactory):
"""A factory to create Group instances."""
class Meta(object):
class Meta:
model = Group
name = "DefaultGroup"
@@ -32,7 +32,7 @@ class GroupFactory(factory.django.DjangoModelFactory):
class UserFactory(PermissionFactory):
"""A factory to create User instances."""
class Meta(object):
class Meta:
model = models.User
django_get_or_create = ("username",)
@@ -52,7 +52,7 @@ class UserFactory(PermissionFactory):
class LogFactory(factory.django.DjangoModelFactory):
"""Factory for Log."""
class Meta(object):
class Meta:
model = models.Log
logger = "modoboa.admin"
@@ -72,7 +72,7 @@ class Command(BaseCommand):
return
action = "delete" if options["delete"] else "disable"
if not options["silent"]:
answer = input("Do you want to {} those accounts? (y/N) ".format(action))
answer = input(f"Do you want to {action} those accounts? (y/N) ")
if not answer.lower().startswith("y"):
return
if action == "disable":
@@ -126,9 +126,7 @@ query = {{ query|safe }}
if os.path.exists(fullpath) and not force_overwrite:
if not self.__check_file(fullpath):
print(
"Cannot upgrade '{}' map because it has been modified.".format(
mapobject.filename
)
f"Cannot upgrade '{mapobject.filename}' map because it has been modified."
)
return self.__checksums[mapobject.filename]
mapcontent = utils.parse_map_file(fullpath)
@@ -91,7 +91,7 @@ class Command(BaseCommand):
extension.load_initial_data()
except Exception as e:
self.stderr.write(
"Unable to load initial data for '{}' ({}).".format(extname, str(e))
f"Unable to load initial data for '{extname}' ({str(e)})."
)
else:
signals.initial_data_loaded.send(sender=self.__class__, extname=extname)
@@ -141,7 +141,7 @@ class Command(BaseCommand):
base_frontend_dir = os.path.join(
os.path.dirname(__file__), "../../../frontend_dist/"
)
frontend_target_dir = "{}/frontend".format(settings.BASE_DIR)
frontend_target_dir = f"{settings.BASE_DIR}/frontend"
if os.path.exists(base_frontend_dir):
shutil.rmtree(frontend_target_dir, ignore_errors=True)
shutil.copytree(base_frontend_dir, frontend_target_dir)
+1 -1
View File
@@ -517,4 +517,4 @@ class ExtensionUpdateHistory(models.Model):
unique_together = [("extension", "version")]
def __str__(self):
return "{}: {}".format(self.extension, self.name)
return f"{self.extension}: {self.name}"
+1 -1
View File
@@ -58,7 +58,7 @@ def get_dovecot_schemes():
internal_name=DOVEADM_PASS_SCHEME_ALARM,
)
return ["{{{}}}".format(smart_str(scheme)) for scheme in schemes.split()], status
return [f"{{{smart_str(scheme)}}}" for scheme in schemes.split()], status
def cache_available_password_hasher(bypass_cache=False):
+1 -1
View File
@@ -4,7 +4,7 @@ from django.core.exceptions import ValidationError
from django.utils.translation import gettext as _, ngettext
class ComplexityValidator(object):
class ComplexityValidator:
"""Check password contains at least a few things."""
def __init__(self, upper=1, lower=1, digits=1, specials=1):
+2 -2
View File
@@ -24,9 +24,9 @@ class SMSBackend:
def get_backend_class(name):
"""Return class for given backend."""
backend_module = import_module("modoboa.core.sms_backends.{}".format(name))
backend_module = import_module(f"modoboa.core.sms_backends.{name}")
try:
backend_class = getattr(backend_module, "{}Backend".format(name.upper()))
backend_class = getattr(backend_module, f"{name.upper()}Backend")
except AttributeError:
return None
else:
+1 -1
View File
@@ -148,7 +148,7 @@ class OVHBackend(SMSBackend):
"""Send a new SMS to given recipients."""
services = self.client.get("/sms")
result = self.client.post(
"/sms/{}/jobs".format(services[0]),
f"/sms/{services[0]}/jobs",
message=text,
receivers=recipients,
priority="high",
+2 -2
View File
@@ -295,5 +295,5 @@ def currencyfmt(amount):
"""Simple temp. filter to replace babel."""
lang = get_language()
if lang == "fr":
return "{} €".format(amount)
return "€{}".format(amount)
return f"{amount} €"
return f"€{amount}"
+3 -3
View File
@@ -19,7 +19,7 @@ from modoboa.core.password_hashers.utils import get_dovecot_schemes
from modoboa.lib.tests import NO_SMTP, ModoTestCase
from .. import factories, models
DOVEADM_TEST_PATH = "{}/doveadm".format(os.path.dirname(__file__))
DOVEADM_TEST_PATH = f"{os.path.dirname(__file__)}/doveadm"
class MockedAttestedCredentialData:
@@ -38,7 +38,7 @@ class AuthenticationTestCase(ModoTestCase):
@classmethod
def setUpTestData(cls): # NOQA:N802
"""Create test data."""
super(AuthenticationTestCase, cls).setUpTestData()
super().setUpTestData()
from modoboa.admin.factories import MailboxFactory
mailbox = MailboxFactory(
@@ -322,7 +322,7 @@ class PasswordResetTestCase(ModoTestCase):
@classmethod
def setUpTestData(cls): # NOQA:N802
"""Create test data."""
super(PasswordResetTestCase, cls).setUpTestData()
super().setUpTestData()
cls.account_ok = factories.UserFactory(
username="user@test.com",
secondary_email="test@ext.com",
+1 -1
View File
@@ -13,7 +13,7 @@ from .. import factories, models
@skipIf(NO_LDAP, "No ldap module installed")
class LDAPTestCaseMixin(object):
class LDAPTestCaseMixin:
"""Set of methods used to test LDAP features."""
@cached_property
+1 -1
View File
@@ -236,7 +236,7 @@ class SettingsTestCase(ModoTestCase):
url = reverse("core:parameters")
response = self.ajax_get(url)
for app in ["core", "admin", "limits"]:
self.assertIn('data-app="{}"'.format(app), response["content"])
self.assertIn(f'data-app="{app}"', response["content"])
def test_save_settings(self):
"""Test settings save."""
+1 -1
View File
@@ -2,4 +2,4 @@ import os
def get_doveadm_test_path():
return "{}/doveadm".format(os.path.dirname(__file__))
return f"{os.path.dirname(__file__)}/doveadm"
+2 -2
View File
@@ -37,7 +37,7 @@ class DashboardView(auth_mixins.AccessMixin, generic.TemplateView):
lang = "en"
context.update({"selection": "dashboard"})
feed_url = "{}{}/weblog/feeds/".format(MODOBOA_WEBSITE_URL, lang)
feed_url = f"{MODOBOA_WEBSITE_URL}{lang}/weblog/feeds/"
if self.request.user.role != "SuperAdmins":
custom_feed_url = self.request.localconfig.parameters.get_value(
"rss_feed_url"
@@ -57,7 +57,7 @@ class DashboardView(auth_mixins.AccessMixin, generic.TemplateView):
"hide_features_widget"
)
if self.request.user.is_superuser or not hide_features_widget:
url = "{}{}/api/projects/?featured=true".format(MODOBOA_WEBSITE_URL, lang)
url = f"{MODOBOA_WEBSITE_URL}{lang}/api/projects/?featured=true"
features = []
if not settings.DISABLE_DASHBOARD_EXTERNAL_QUERIES:
try:
+5 -5
View File
@@ -129,7 +129,7 @@ def import_report(content):
else:
print(f"Report skipped because of malformed data (empty {attr})")
return
setattr(report, "policy_{}".format(attr), node.text)
setattr(report, f"policy_{attr}", node.text)
report.save()
for record in root.findall("record"):
import_record(record, report)
@@ -174,7 +174,7 @@ def import_report_from_email(content):
fpo.seek(0)
if file_type in FILE_TYPES:
import_archive(fpo, content_type=part.get_content_type())
except (OSError, IOError):
except OSError:
print("Error: the attachment does not match the mimetype")
err = True
else:
@@ -219,8 +219,8 @@ def week_range(year, weeknumber):
"""Return start and end dates of a given week."""
tz = timezone.get_current_timezone()
fmt = "%Y-%W-%w"
start_week = datetime.datetime.strptime("{}-{}-{}".format(year, weeknumber, 1), fmt)
end_week = datetime.datetime.strptime("{}-{}-{}".format(year, weeknumber, 0), fmt)
start_week = datetime.datetime.strptime(f"{year}-{weeknumber}-{1}", fmt)
end_week = datetime.datetime.strptime(f"{year}-{weeknumber}-{0}", fmt)
return start_week.replace(tzinfo=tz), end_week.replace(tzinfo=tz)
@@ -237,7 +237,7 @@ def insert_record(target: dict, record, name: str) -> None:
}
target[name][record.source_ip]["total"] += record.count
for typ in ["spf", "dkim"]:
result = getattr(record, "{}_result".format(typ))
result = getattr(record, f"{typ}_result")
key = "success" if result == "pass" else "failure"
target[name][record.source_ip][typ][key] += record.count
@@ -1,7 +1,5 @@
"""Import a DMARC aggregated report."""
from __future__ import print_function
from django.core.management.base import BaseCommand
from ... import lib
+2 -2
View File
@@ -39,7 +39,7 @@ class Reporter(models.Model):
def __str__(self):
"""Return name and email."""
return "{} <{}>".format(self.org_name, self.email)
return f"{self.org_name} <{self.email}>"
class Report(models.Model):
@@ -63,7 +63,7 @@ class Report(models.Model):
def __str__(self):
"""Display provider and dates."""
return "{}: {} -> {}".format(self.reporter, self.start_date, self.end_date)
return f"{self.reporter}: {self.start_date} -> {self.end_date}"
class Record(models.Model):
+9 -15
View File
@@ -16,20 +16,16 @@ def next_period(period):
"""Return next period."""
current = period.split("-")
if int(current[1]) == 52:
week0 = datetime.datetime.strptime(
"{}-0-1".format(int(current[0]) + 1), "%Y-%W-%w"
)
week1 = datetime.datetime.strptime(
"{}-1-1".format(int(current[0]) + 1), "%Y-%W-%w"
)
week52 = datetime.datetime.strptime("{}-52-1".format(current[0]), "%Y-%W-%w")
week0 = datetime.datetime.strptime(f"{int(current[0]) + 1}-0-1", "%Y-%W-%w")
week1 = datetime.datetime.strptime(f"{int(current[0]) + 1}-1-1", "%Y-%W-%w")
week52 = datetime.datetime.strptime(f"{current[0]}-52-1", "%Y-%W-%w")
if week0 == week52 or week0 == week1:
parts = (int(current[0]) + 1, 1)
else:
parts = (int(current[0]) + 1, 0)
else:
parts = (current[0], int(current[1]) + 1)
return mark_safe("{}-{}".format(parts[0], parts[1]))
return mark_safe(f"{parts[0]}-{parts[1]}")
@register.simple_tag
@@ -37,24 +33,22 @@ def previous_period(period):
"""Return previous period."""
current = period.split("-")
if int(current[1]) == 1:
week0 = datetime.datetime.strptime("{}-0-1".format(current[0]), "%Y-%W-%w")
week1 = datetime.datetime.strptime("{}-1-1".format(current[0]), "%Y-%W-%w")
week0 = datetime.datetime.strptime(f"{current[0]}-0-1", "%Y-%W-%w")
week1 = datetime.datetime.strptime(f"{current[0]}-1-1", "%Y-%W-%w")
if week0 == week1:
parts = (int(current[0]) - 1, 52)
else:
parts = (current[0], 0)
elif int(current[1]) == 0:
week0 = datetime.datetime.strptime("{}-0-1".format(current[0]), "%Y-%W-%w")
week52 = datetime.datetime.strptime(
"{}-52-1".format(int(current[0]) - 1), "%Y-%W-%w"
)
week0 = datetime.datetime.strptime(f"{current[0]}-0-1", "%Y-%W-%w")
week52 = datetime.datetime.strptime(f"{int(current[0]) - 1}-52-1", "%Y-%W-%w")
if week0 == week52:
parts = (int(current[0]) - 1, 51)
else:
parts = (int(current[0]) - 1, 52)
else:
parts = (current[0], int(current[1]) - 1)
return mark_safe("{}-{}".format(parts[0], parts[1]))
return mark_safe(f"{parts[0]}-{parts[1]}")
@register.filter
+3 -3
View File
@@ -7,12 +7,12 @@ import sys
from django.core.management import call_command
class CallCommandMixin(object):
class CallCommandMixin:
"""A mixin to provide command execution shortcuts."""
def setUp(self):
def setUp(self, *args, **kwargs):
"""Replace stdin"""
super(CallCommandMixin, self).setUp()
super().setUp(*args, **kwargs)
self.stdin = sys.stdin
def tearDown(self):
+6 -6
View File
@@ -15,7 +15,7 @@ class DMARCViewsTestCase(mixins.CallCommandMixin, ModoTestCase):
@classmethod
def setUpTestData(cls):
"""Create test data."""
super(DMARCViewsTestCase, cls).setUpTestData()
super().setUpTestData()
cls.domain = admin_factories.DomainFactory(name="ngyn.org")
def test_domainlist_view(self):
@@ -34,7 +34,7 @@ class DMARCViewsTestCase(mixins.CallCommandMixin, ModoTestCase):
user = core_models.User.objects.get(username="admin")
self.client.force_login(user)
url = reverse("dmarc:domain_report", args=[self.domain.pk])
response = self.client.get("{}?period=2015-26".format(url))
response = self.client.get(f"{url}?period=2015-26")
self.assertContains(response, "'Failed', 100.0")
def test_domainreport_view_week0(self):
@@ -43,7 +43,7 @@ class DMARCViewsTestCase(mixins.CallCommandMixin, ModoTestCase):
user = core_models.User.objects.get(username="admin")
self.client.force_login(user)
url = reverse("dmarc:domain_report", args=[self.domain.pk])
response = self.client.get("{}?period=2019-0".format(url))
response = self.client.get(f"{url}?period=2019-0")
self.assertContains(response, "Dec. 31, 2018")
def test_domainreport_view_week1(self):
@@ -52,7 +52,7 @@ class DMARCViewsTestCase(mixins.CallCommandMixin, ModoTestCase):
user = core_models.User.objects.get(username="admin")
self.client.force_login(user)
url = reverse("dmarc:domain_report", args=[self.domain.pk])
response = self.client.get("{}?period=2019-1".format(url))
response = self.client.get(f"{url}?period=2019-1")
self.assertContains(response, "Jan. 7, 2019")
def test_domainreport_view_week52(self):
@@ -61,7 +61,7 @@ class DMARCViewsTestCase(mixins.CallCommandMixin, ModoTestCase):
user = core_models.User.objects.get(username="admin")
self.client.force_login(user)
url = reverse("dmarc:domain_report", args=[self.domain.pk])
response = self.client.get("{}?period=2018-52".format(url))
response = self.client.get(f"{url}?period=2018-52")
self.assertContains(response, "Dec. 30, 2018")
def test_domainreport_view_arc(self):
@@ -70,7 +70,7 @@ class DMARCViewsTestCase(mixins.CallCommandMixin, ModoTestCase):
user = core_models.User.objects.get(username="admin")
self.client.force_login(user)
url = reverse("dmarc:domain_report", args=[self.domain.pk])
response = self.client.get("{}?period=2021-22".format(url))
response = self.client.get(f"{url}?period=2021-22")
self.assertContains(response, "'Fully aligned', 86.0")
self.assertContains(response, "'Partially aligned', 8.0")
self.assertContains(response, "'Forwarded', 4.0")
+4 -4
View File
@@ -33,7 +33,7 @@ def insert_record(target, record, name):
}
target[name][record.source_ip]["total"] += record.count
for typ in ["spf", "dkim"]:
result = getattr(record, "{}_result".format(typ))
result = getattr(record, f"{typ}_result")
target[name][record.source_ip][typ][result] += record.count
@@ -41,8 +41,8 @@ def week_range(year, weeknumber):
"""Return start and end dates of a given week."""
tz = timezone.get_current_timezone()
fmt = "%Y-%W-%w"
start_week = datetime.datetime.strptime("{}-{}-{}".format(year, weeknumber, 1), fmt)
end_week = datetime.datetime.strptime("{}-{}-{}".format(year, weeknumber, 0), fmt)
start_week = datetime.datetime.strptime(f"{year}-{weeknumber}-{1}", fmt)
end_week = datetime.datetime.strptime(f"{year}-{weeknumber}-{0}", fmt)
return start_week.replace(tzinfo=tz), end_week.replace(tzinfo=tz)
@@ -58,7 +58,7 @@ class DomainReportView(auth_mixins.PermissionRequiredMixin, generic.TemplateView
if not self.period:
year, week, day = timezone.now().isocalendar()
week -= 1
self.period = "{}-{}".format(year, week)
self.period = f"{year}-{week}"
else:
year, week = self.period.split("-")
+6 -6
View File
@@ -17,7 +17,7 @@ def _get_record_type_value(records, rr_type):
# Multiple strings are separated by a space as described in:
# https://tools.ietf.org/html/rfc4408#section-3.1.3
value = str(record).replace('" "', "").strip('"')
if value.startswith("v={}".format(rr_type)):
if value.startswith(f"v={rr_type}"):
return value
return None
@@ -30,14 +30,14 @@ def get_spf_record(domain):
def get_dkim_record(domain, selector):
"""Return DKIM records form domain (if any)."""
name = "{}._domainkey.{}".format(selector, domain)
name = f"{selector}._domainkey.{domain}"
records = admin_lib.get_dns_records(name, "TXT")
return _get_record_type_value(records, "DKIM1")
def get_dmarc_record(domain):
"""Return DMARC record for domain (if any)."""
name = "_dmarc.{}".format(domain)
name = f"_dmarc.{domain}"
records = admin_lib.get_dns_records(name, "TXT")
return _get_record_type_value(records, "DMARC1")
@@ -61,12 +61,12 @@ def _get_simple_record(name):
def get_autoconfig_record(domain):
"""Return autoconfig record for domain (if any)."""
return _get_simple_record("autoconfig.{}".format(domain))
return _get_simple_record(f"autoconfig.{domain}")
def get_autodiscover_record(domain):
"""Return autodiscover record for domain (if any)."""
return _get_simple_record("autodiscover.{}".format(domain))
return _get_simple_record(f"autodiscover.{domain}")
class DNSSyntaxError(Exception):
@@ -167,7 +167,7 @@ def check_spf_syntax(record):
continue
for mechanism in constants.SPF_MECHANISMS:
if part.startswith(mechanism):
globals()["check_spf_{}".format(mechanism)](part)
globals()[f"check_spf_{mechanism}"](part)
mechanisms.append(mechanism)
break
else:
+3 -3
View File
@@ -50,7 +50,7 @@ class DNSRecord(models.Model):
objects = DNSRecordManager()
def __str__(self):
return "{} ({}): {}".format(self.domain, self.type, self.value)
return f"{self.domain} ({self.type}): {self.value}"
def get_dns_record(self):
"""Retrieve corresponding DNS record."""
@@ -59,13 +59,13 @@ class DNSRecord(models.Model):
self.domain.name, self.domain.dkim_key_selector
)
else:
func = getattr(lib, "get_{}_record".format(self.type))
func = getattr(lib, f"get_{self.type}_record")
self.value = func(self.domain.name)
def check_syntax(self, ttl=7200):
"""Check record syntax."""
try:
func = getattr(lib, "check_{}_syntax".format(self.type))
func = getattr(lib, f"check_{self.type}_syntax")
result = func(self.value)
except lib.DNSSyntaxError as err:
self.error = str(err)[:50]
-4
View File
@@ -1,9 +1,5 @@
# -*- coding: utf-8 -*-
"""A plugin to migrate mailboxes using IMAP."""
from __future__ import unicode_literals
from pkg_resources import get_distribution, DistributionNotFound
+1 -2
View File
@@ -1,7 +1,6 @@
"""Viewsets for API V2 for imap_migration."""
import imaplib
import socket
import ssl
from rest_framework import response, filters, mixins, permissions, viewsets
@@ -38,7 +37,7 @@ class EmailProviderViewSet(GetThrottleViewsetMixin, viewsets.ModelViewSet):
imaplib.IMAP4_SSL(address, port)
else:
imaplib.IMAP4(address, port)
except (socket.error, imaplib.IMAP4.error, ssl.SSLError):
except (OSError, imaplib.IMAP4.error, ssl.SSLError):
return response.Response({"status": "failed"}, 400)
return response.Response(status=200)
+5 -6
View File
@@ -1,7 +1,6 @@
"""IMAP authentication backend for Django."""
import imaplib
import socket
import ssl
from django.utils.encoding import smart_bytes, smart_str
@@ -45,8 +44,10 @@ class IMAPBackend:
conn = imaplib.IMAP4_SSL(address, port)
else:
conn = imaplib.IMAP4(address, port)
except (socket.error, imaplib.IMAP4.error, ssl.SSLError) as error:
raise ModoboaException(_("Connection to IMAP server failed: %s") % error)
except (OSError, imaplib.IMAP4.error, ssl.SSLError) as error:
raise ModoboaException(
_(f"Connection to IMAP server failed: {error}")
) from None
try:
typ, data = conn.login(smart_bytes(username), smart_str(password))
@@ -68,9 +69,7 @@ class IMAPBackend:
orig_username = username
# Check if old addresses must be converted
if self.provider_domain.new_domain:
username = "{}@{}".format(
self.address, self.provider_domain.new_domain.name
)
username = f"{self.address}@{self.provider_domain.new_domain.name}"
user, created = core_models.User.objects.get_or_create(
username__iexact=username,
defaults={"username": username.lower(), "email": username.lower()},
+1 -1
View File
@@ -13,7 +13,7 @@ class EmailProviderFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.EmailProvider
name = factory.Sequence(lambda n: "Provider {}".format(n))
name = factory.Sequence(lambda n: f"Provider {n}")
address = "127.0.0.1"
port = 143
@@ -33,7 +33,7 @@ class Command(BaseCommand):
"imap_create_folders": conf["create_folders"],
"imap_folder_filter_exclude": conf["folder_filter_exclude"],
"imap_folder_filter_include": ", ".join(
"'{0}'".format(w) for w in conf["folder_filter_include"].split(",")
f"'{w}'" for w in conf["folder_filter_include"].split(",")
),
"migrations": Migration.objects.select_related(
"provider", "mailbox__domain"
+1 -1
View File
@@ -21,7 +21,7 @@ from . import factories
from . import models
class DataMixin(object):
class DataMixin:
"""A mixin to provide test data."""
@classmethod
+8 -15
View File
@@ -14,7 +14,7 @@ from modoboa.lib.exceptions import InternalError
def create_connection(srv_address, srv_port, config, username, password):
"""Create a new connection with given server."""
uri = "{}:{}".format(srv_address, srv_port)
uri = f"{srv_address}:{srv_port}"
uri = "{}://{}".format("ldaps" if config["ldap_secured"] == "ssl" else "ldap", uri)
conn = ldap.initialize(uri)
conn.protocol_version = 3
@@ -152,7 +152,7 @@ def find_user_groups(conn, config, dn, entry):
or config["ldap_group_type"] == "groupofnames"
)
if condition:
flt = "(member={})".format(dn)
flt = f"(member={dn})"
elif config["ldap_group_type"] == "posixgroup":
flt = "(memberUid={})".format(force_str(entry["uid"][0]))
@@ -204,7 +204,7 @@ def import_accounts_from_ldap(config):
break
if email is None:
if grp == "SimpleUsers":
print("Skipping {} because no email found".format(dn))
print(f"Skipping {dn} because no email found")
continue
else:
username = email
@@ -235,8 +235,8 @@ def build_ldap_uri(config, node=""):
node += "_"
return "{}://{}:{}".format(
"ldaps" if config["ldap_secured"] == "ssl" else "ldap",
config["ldap_{}server_address".format(node)],
config["ldap_{}server_port".format(node)],
config[f"ldap_{node}server_address"],
config[f"ldap_{node}server_port"],
)
@@ -259,18 +259,11 @@ def update_dovecot_config_file(config):
with open(conf_file, "w") as fp:
fp.write(
"""uris = {uris}
f"""uris = {uris}
dn = "{bind_dn}"
dnpass = '{bind_pwd}'
base = {base}
user_filter = {user_filter}
pass_filter = {pass_filter}
""".format(
uris=uris,
bind_dn=bind_dn,
bind_pwd=bind_pwd,
base=base,
user_filter=user_filter,
pass_filter=user_filter,
)
pass_filter = {user_filter}
"""
)
+5 -5
View File
@@ -12,7 +12,7 @@ from django.utils.translation import gettext as _
logger = logging.getLogger("modoboa.admin")
class ModoAPIClient(object):
class ModoAPIClient:
"""A simple client for the public API."""
def __init__(self, api_url=None):
@@ -57,10 +57,10 @@ class ModoAPIClient(object):
def register_instance(self, hostname):
"""Register this instance."""
url = "{}instances/search/?hostname={}".format(self._api_url, hostname)
url = f"{self._api_url}instances/search/?hostname={hostname}"
instance = self.__send_request(url)
if instance is None:
url = "{}instances/".format(self._api_url)
url = f"{self._api_url}instances/"
data = {"hostname": hostname, "known_version": self.local_core_version}
try:
response = requests.post(url, data=data)
@@ -76,7 +76,7 @@ class ModoAPIClient(object):
def update_instance(self, pk, data):
"""Update instance and send stats."""
url = "{}instances/{}/".format(self._api_url, pk)
url = f"{self._api_url}instances/{pk}/"
try:
response = requests.put(url, data=data)
except RequestException as err:
@@ -89,5 +89,5 @@ class ModoAPIClient(object):
def versions(self):
"""Fetch core and extension versions."""
url = "{}versions/".format(self._api_url)
url = f"{self._api_url}versions/"
return self.__send_request(url)
+2 -2
View File
@@ -9,7 +9,7 @@ from modoboa.lib.email_utils import split_mailbox
from modoboa.parameters import tools as param_tools
class SMTPBackend(object):
class SMTPBackend:
"""A backend to authenticate against an SMTP server."""
def authenticate(self, request, username=None, password=None):
@@ -126,7 +126,7 @@ try:
@classmethod
def setting_fullname(cls, setting):
"""Return fullname for given setting."""
return "{}{}".format(cls.settings_prefix, setting)
return f"{cls.settings_prefix}{setting}"
class LDAPBackend(LDAPBackendBase):
"""Primary LDAP backend."""
+2 -2
View File
@@ -31,7 +31,7 @@ class EmailAddress:
def __init__(self, address):
self.name, self.address = u2u_decode.decode_address(address)
if self.name:
self.fulladdress = "{} <{}>".format(self.name, self.address)
self.fulladdress = f"{self.name} <{self.address}>"
else:
self.fulladdress = self.address
@@ -446,7 +446,7 @@ def sendmail_fromfile(sender, rcpt, fname):
"""
try:
fp = open(fname)
except IOError as e:
except OSError as e:
return False, str(e)
content = """From: %s
+3 -3
View File
@@ -17,7 +17,7 @@ from modoboa.lib.web_utils import render_to_json_response
ABC = abc.ABCMeta(force_str("ABC"), (object,), {})
class WizardStep(object):
class WizardStep:
"""A wizard step."""
def __init__(self, uid, formclass, title, formtpl=None, new_args=None):
@@ -188,7 +188,7 @@ class WizardForm(ABC):
"""
class DynamicForm(object):
class DynamicForm:
"""
A form which accepts dynamic fields.
@@ -234,7 +234,7 @@ class DynamicForm(object):
self.data = ndata
class TabForms(object):
class TabForms:
"""
Simple forms container.
+2 -2
View File
@@ -3,7 +3,7 @@ Custom pagination tools.
"""
class Page(object):
class Page:
"""Class to represent a page."""
def __init__(self, paginator, pageid, id_start, id_stop, baseurl=None):
@@ -66,7 +66,7 @@ class Page(object):
return lid
class Paginator(object):
class Paginator:
"""Pagination class."""
def __init__(self, total, elems_per_page):
+1 -1
View File
@@ -75,7 +75,7 @@ def doveadm_cmd(params, pinput=None, capture_output=True, **kwargs):
sudo_user = dovecot_user if curuser != dovecot_user else None
if dpath:
return exec_cmd(
"{} {}".format(dpath, params),
f"{dpath} {params}",
sudo_user=sudo_user,
pinput=pinput,
capture_output=capture_output,
+1 -1
View File
@@ -94,4 +94,4 @@ def render_field_width(field):
width = form.field_widths[field.name]
else:
width = 5
return "col-sm-{0}".format(width)
return f"col-sm-{width}"
+3 -3
View File
@@ -7,7 +7,7 @@ import tempfile
from django.core.management import call_command
class MapFilesTestCaseMixin(object):
class MapFilesTestCaseMixin:
"""A generic test case to check map files generation."""
MAP_FILES = None
@@ -21,13 +21,13 @@ class MapFilesTestCaseMixin(object):
shutil.rmtree(self.workdir)
def _test_maps_generation(self, engine):
dburl = "{0}://user:password@localhost/testdb".format(engine)
dburl = f"{engine}://user:password@localhost/testdb"
call_command(
"generate_postfix_maps", "--dburl", dburl, "--destdir", self.workdir
)
for mapfile in self.MAP_FILES:
path = "{0}/{1}".format(self.workdir, mapfile)
path = f"{self.workdir}/{mapfile}"
self.assertTrue(os.path.exists(path))
with open(path) as fpo:
content = fpo.read()
+5 -5
View File
@@ -19,7 +19,7 @@ try:
s = socket.create_connection(("127.0.0.1", 25))
s.close()
NO_SMTP = False
except socket.error:
except OSError:
NO_SMTP = True
try:
@@ -36,7 +36,7 @@ class ParametersMixin:
@classmethod
def setUpTestData(cls): # noqa
"""Set LocalConfig instance."""
super(ParametersMixin, cls).setUpTestData()
super().setUpTestData()
cls.localconfig = core_models.LocalConfig.objects.first()
def set_global_parameter(self, name, value, app=None):
@@ -64,7 +64,7 @@ class ModoTestCase(ParametersMixin, TestCase):
@classmethod
def setUpTestData(cls): # noqa
"""Create a default user."""
super(ModoTestCase, cls).setUpTestData()
super().setUpTestData()
management.call_command("load_initial_data", "--no-frontend")
def setUp(self, username="admin", password="password"):
@@ -101,7 +101,7 @@ class ModoAPITestCase(ParametersMixin, APITestCase):
@classmethod
def setUpTestData(cls): # noqa
"""Create a default user."""
super(ModoAPITestCase, cls).setUpTestData()
super().setUpTestData()
management.call_command("load_initial_data")
cls.token = Token.objects.create(
user=core_models.User.objects.get(username="admin")
@@ -109,7 +109,7 @@ class ModoAPITestCase(ParametersMixin, APITestCase):
def setUp(self):
"""Setup."""
super(ModoAPITestCase, self).setUp()
super().setUp()
self.client.credentials(HTTP_AUTHORIZATION="Token " + self.token.key)
self.workdir = tempfile.mkdtemp()
self.set_global_parameter("storage_dir", self.workdir, app="pdfcredentials")
+1 -1
View File
@@ -7,7 +7,7 @@ from django.core.validators import EmailValidator, URLValidator
from django.utils.translation import gettext_lazy
class HostnameValidator(object):
class HostnameValidator:
"""Validator for fqdn."""
message = gettext_lazy("Enter a valid domain name")
+2 -2
View File
@@ -138,11 +138,11 @@ def size2integer(value, output_unit="B"):
if m.group(2)[0] in ["G", "g"]:
return int(m.group(1)) * 2**10
else:
raise ValueError("Unsupported output unit {}".format(output_unit))
raise ValueError(f"Unsupported output unit {output_unit}")
return 0
class NavigationParameters(object):
class NavigationParameters:
"""
Just a simple object to manipulate navigation parameters.
"""
+7 -9
View File
@@ -22,7 +22,7 @@ class APIAdminLimitsTestCase(lib_tests.ModoAPITestCase):
"""Create test data."""
super(APIAdminLimitsTestCase, cls).setUpTestData()
for name, _definition in utils.get_user_limit_templates():
cls.localconfig.parameters.set_value("deflt_user_{0}_limit".format(name), 2)
cls.localconfig.parameters.set_value(f"deflt_user_{name}_limit", 2)
cls.localconfig.save()
populate_database()
cls.user = User.objects.get(username="admin@test.com")
@@ -192,9 +192,7 @@ class APIDomainLimitsTestCase(lib_tests.ModoAPITestCase):
super(APIDomainLimitsTestCase, cls).setUpTestData()
cls.localconfig.parameters.set_value("enable_domain_limits", True)
for name, _definition in utils.get_domain_limit_templates():
cls.localconfig.parameters.set_value(
"deflt_domain_{0}_limit".format(name), 2
)
cls.localconfig.parameters.set_value(f"deflt_domain_{name}_limit", 2)
cls.localconfig.save()
populate_database()
@@ -255,7 +253,7 @@ class ResourcesAPITestCase(lib_tests.ModoAPITestCase):
"""Create test data."""
super(ResourcesAPITestCase, cls).setUpTestData()
for name, _definition in utils.get_user_limit_templates():
cls.localconfig.parameters.set_value("deflt_user_{0}_limit".format(name), 2)
cls.localconfig.parameters.set_value(f"deflt_user_{name}_limit", 2)
cls.localconfig.save()
populate_database()
cls.user = User.objects.get(username="admin@test.com")
@@ -282,12 +280,12 @@ class ResourcesAPITestCase(lib_tests.ModoAPITestCase):
compare(expected, response.data)
# As reseller => fails
self.client.credentials(HTTP_AUTHORIZATION="Token {}".format(self.r_token.key))
self.client.credentials(HTTP_AUTHORIZATION=f"Token {self.r_token.key}")
response = self.client.get(url)
self.assertEqual(response.status_code, 404)
# As domain admin => fails
self.client.credentials(HTTP_AUTHORIZATION="Token {}".format(self.da_token.key))
self.client.credentials(HTTP_AUTHORIZATION=f"Token {self.da_token.key}")
response = self.client.get(url)
self.assertEqual(response.status_code, 404)
@@ -304,7 +302,7 @@ class ResourcesAPITestCase(lib_tests.ModoAPITestCase):
)
# As domain admin => fails
self.client.credentials(HTTP_AUTHORIZATION="Token {}".format(self.da_token.key))
self.client.credentials(HTTP_AUTHORIZATION=f"Token {self.da_token.key}")
resources.update({"domains": 2, "mailboxes": 2})
url = reverse("v1:resources-detail", args=[self.user.pk])
response = self.client.put(url, resources)
@@ -312,7 +310,7 @@ class ResourcesAPITestCase(lib_tests.ModoAPITestCase):
# As reseller => ok
permissions.grant_access_to_object(self.reseller, self.user, True)
self.client.credentials(HTTP_AUTHORIZATION="Token {}".format(self.r_token.key))
self.client.credentials(HTTP_AUTHORIZATION=f"Token {self.r_token.key}")
resources.update({"domains": 500, "mailboxes": 500})
url = reverse("v1:resources-detail", args=[self.user.pk])
response = self.client.put(url, resources)
+4 -4
View File
@@ -22,7 +22,7 @@ class ResourcePoolForm(forms.Form):
)
if condition:
continue
self.fields["{}_limit".format(name)] = forms.IntegerField(
self.fields[f"{name}_limit"] = forms.IntegerField(
label=tpl["label"], help_text=tpl["help"]
)
if self.account:
@@ -49,7 +49,7 @@ class ResourcePoolForm(forms.Form):
def save(self):
owner = get_object_owner(self.account)
for name, _definition in utils.get_user_limit_templates():
fieldname = "{}_limit".format(name)
fieldname = f"{name}_limit"
if fieldname not in self.cleaned_data:
continue
limit = self.account.userobjectlimit_set.get(name=name)
@@ -67,7 +67,7 @@ class DomainLimitsForm(forms.Form):
self.domain = kwargs.pop("instance", None)
super(DomainLimitsForm, self).__init__(*args, **kwargs)
for name, tpl in utils.get_domain_limit_templates():
self.fields["{}_limit".format(name)] = forms.IntegerField(
self.fields[f"{name}_limit"] = forms.IntegerField(
label=tpl["label"], help_text=tpl["help"]
)
if not self.domain:
@@ -91,7 +91,7 @@ class DomainLimitsForm(forms.Form):
def save(self, user, **kwargs):
"""Set limits."""
for name, _definition in utils.get_domain_limit_templates():
fieldname = "{}_limit".format(name)
fieldname = f"{name}_limit"
if fieldname not in self.cleaned_data:
continue
limit = self.domain.domainobjectlimit_set.get(name=name)
+2 -2
View File
@@ -51,7 +51,7 @@ def create_user_limits(sender, instance, **kwargs):
max_value = 0
# creator can be None if user was created by a factory
if not creator or creator.is_superuser:
max_value = global_params["deflt_user_{0}_limit".format(name)]
max_value = global_params[f"deflt_user_{name}_limit"]
models.UserObjectLimit.objects.create(
user=instance, name=name, content_type=ct, max_value=max_value
)
@@ -64,7 +64,7 @@ def create_domain_limits(sender, instance, **kwargs):
return
global_params = dict(param_tools.get_global_parameters("limits"))
for name, _definition in utils.get_domain_limit_templates():
max_value = global_params["deflt_domain_{0}_limit".format(name)]
max_value = global_params[f"deflt_domain_{name}_limit"]
models.DomainObjectLimit.objects.create(
domain=instance, name=name, max_value=max_value
)
+6 -6
View File
@@ -9,7 +9,7 @@ from modoboa.core import models as core_models
from . import lib, utils
class ObjectLimitMixin(object):
class ObjectLimitMixin:
"""Common methods."""
@property
@@ -54,7 +54,7 @@ class ObjectLimitMixin(object):
"""Display current usage."""
if self.max_value == -1:
return _("unlimited")
return "{}%".format(self.usage)
return f"{self.usage}%"
class UserObjectLimit(ObjectLimitMixin, models.Model):
@@ -67,7 +67,7 @@ class UserObjectLimit(ObjectLimitMixin, models.Model):
)
max_value = models.IntegerField(default=0)
class Meta(object):
class Meta:
unique_together = (("user", "name"),)
@property
@@ -117,7 +117,7 @@ class UserObjectLimit(ObjectLimitMixin, models.Model):
"""Display current usage."""
if self.max_value == -1:
return _("unlimited")
return "{}%".format(self.usage)
return f"{self.usage}%"
class DomainObjectLimit(ObjectLimitMixin, models.Model):
@@ -127,7 +127,7 @@ class DomainObjectLimit(ObjectLimitMixin, models.Model):
name = models.CharField(max_length=254)
max_value = models.IntegerField(default=0)
class Meta(object):
class Meta:
unique_together = (("domain", "name"),)
@property
@@ -143,7 +143,7 @@ class DomainObjectLimit(ObjectLimitMixin, models.Model):
"""Return the current number of objects."""
definition = self.definition
if not definition:
raise RuntimeError("Bad limit {}".format(self.name))
raise RuntimeError(f"Bad limit {self.name}")
relation = getattr(self.domain, definition["relation"])
if "extra_filters" in definition:
relation = relation.filter(**definition["extra_filters"])
+2 -4
View File
@@ -16,14 +16,12 @@ class DomainLimitsTestCase(lib_tests.ModoTestCase):
@classmethod
def setUpTestData(cls): # NOQA:N802
"""Create test data."""
super(DomainLimitsTestCase, cls).setUpTestData()
super().setUpTestData()
cls.localconfig.parameters.set_values(
{"enable_admin_limits": False, "enable_domain_limits": True}
)
for name, _definition in utils.get_domain_limit_templates():
cls.localconfig.parameters.set_value(
"deflt_domain_{0}_limit".format(name), 2
)
cls.localconfig.parameters.set_value(f"deflt_domain_{name}_limit", 2)
cls.localconfig.save()
admin_factories.populate_database()
+23 -35
View File
@@ -27,18 +27,16 @@ class LimitImportTestCase(ModoTestCase):
self.client.force_login(self.reseller)
self.assertFalse(limit.is_exceeded())
f = ContentFile(
"""domainalias; domalias1.com; {domain}; True
domainalias; domalias2.com; {domain}; True
""".format(
domain=self.domain
),
f"""domainalias; domalias1.com; {self.domain}; True
domainalias; domalias2.com; {self.domain}; True
""",
name="domains.csv",
)
self.client.post(reverse("admin:domain_import"), {"sourcefile": f})
self.assertTrue(limit.is_exceeded())
f = ContentFile(
"domainalias; domalias3.com; {}; True".format(self.domain),
f"domainalias; domalias3.com; {self.domain}; True",
name="domains.csv",
)
response = self.client.post(reverse("admin:domain_import"), {"sourcefile": f})
@@ -51,20 +49,16 @@ domainalias; domalias2.com; {domain}; True
content = ""
for cpt in range(initial_count, 2):
content += (
"account; admin{cpt}@{domain}; toto; User; One; True; "
"DomainAdmins; user{cpt}@{domain}; 5; {domain}\n".format(
domain=self.domain, cpt=cpt
)
f"account; admin{cpt}@{self.domain}; toto; User; One; True; "
f"DomainAdmins; user{cpt}@{self.domain}; 5; {self.domain}\n"
)
f = ContentFile(content, name="domain_admins.csv")
response = self.client.post(reverse("admin:identity_import"), {"sourcefile": f})
self.assertTrue(limit.is_exceeded())
f = ContentFile(
"""account; admin3@{domain}; toto; User; One; True; DomainAdmins; admin3@{domain}; 5; {domain}
""".format(
domain=self.domain
),
f"""account; admin3@{self.domain}; toto; User; One; True; DomainAdmins; admin3@{self.domain}; 5; {self.domain}
""",
name="domain_admins.csv",
) # NOQA:E501
response = self.client.post(reverse("admin:identity_import"), {"sourcefile": f})
@@ -75,21 +69,19 @@ domainalias; domalias2.com; {domain}; True
self.client.force_login(self.dadmin)
self.assertFalse(limit.is_exceeded())
f = ContentFile(
"account; user1@{domain}; toto; User; One; True; SimpleUsers; "
"user1@{domain}; 5\r\n"
"account; truc@{domain}; toto; René; Truc; True; SimpleUsers; "
"truc@{domain}; 5\r\n".format(domain=self.domain),
f"account; user1@{self.domain}; toto; User; One; True; SimpleUsers; "
f"user1@{self.domain}; 5\r\n"
f"account; truc@{self.domain}; toto; René; Truc; True; SimpleUsers; "
f"truc@{self.domain}; 5\r\n",
name="domains.csv",
)
response = self.client.post(reverse("admin:identity_import"), {"sourcefile": f})
self.assertTrue(limit.is_exceeded())
f = ContentFile(
"""
account; user3@{domain}; toto; User; One; True; SimpleUsers; user3@{domain}; 5
""".format(
domain=self.domain
),
f"""
account; user3@{self.domain}; toto; User; One; True; SimpleUsers; user3@{self.domain}; 5
""",
name="domains.csv",
)
response = self.client.post(reverse("admin:identity_import"), {"sourcefile": f})
@@ -100,22 +92,18 @@ account; user3@{domain}; toto; User; One; True; SimpleUsers; user3@{domain}; 5
self.client.force_login(self.dadmin)
self.assertFalse(limit.is_exceeded())
f = ContentFile(
"""alias; alias1@{domain}; True; user@{domain}
alias; alias2@{domain}; True; user@{domain}
""".format(
domain=self.domain
),
f"""alias; alias1@{self.domain}; True; user@{self.domain}
alias; alias2@{self.domain}; True; user@{self.domain}
""",
name="aliases.csv",
)
self.client.post(reverse("admin:identity_import"), {"sourcefile": f})
self.assertTrue(limit.is_exceeded())
f = ContentFile(
"""
alias; alias3@{domain}; True; user@{domain}
""".format(
domain=self.domain
),
f"""
alias; alias3@{self.domain}; True; user@{self.domain}
""",
name="aliases.csv",
)
with self.assertRaises(exceptions.ValidationError):
@@ -129,7 +117,7 @@ class UserLimitImportTestCase(LimitImportTestCase):
"""Create test data."""
localconfig = core_models.LocalConfig.objects.first()
for name, _definition in utils.get_user_limit_templates():
localconfig.parameters.set_value("deflt_user_{0}_limit".format(name), 2)
localconfig.parameters.set_value(f"deflt_user_{name}_limit", 2)
localconfig.save()
super(UserLimitImportTestCase, cls).setUpTestData()
@@ -191,7 +179,7 @@ class DomainLimitImportTestCase(LimitImportTestCase):
{"enable_domain_limits": True, "enable_admin_limits": False}
)
for name, _definition in utils.get_domain_limit_templates():
localconfig.parameters.set_value("deflt_domain_{0}_limit".format(name), 2)
localconfig.parameters.set_value(f"deflt_domain_{name}_limit", 2)
localconfig.save()
super(DomainLimitImportTestCase, cls).setUpTestData()
mb = admin_factories.MailboxFactory(
+1 -1
View File
@@ -54,7 +54,7 @@ class ResourceTestCase(lib_tests.ModoTestCase):
{"enable_admin_limits": True, "enable_domain_limits": False}
)
for name, _definition in utils.get_user_limit_templates():
cls.localconfig.parameters.set_value("deflt_user_{0}_limit".format(name), 2)
cls.localconfig.parameters.set_value(f"deflt_user_{name}_limit", 2)
cls.localconfig.save()
populate_database()
+1 -1
View File
@@ -41,7 +41,7 @@ class StatisticsViewSet(GetThrottleViewsetMixin, viewsets.ViewSet):
end = int(time.mktime(serializer.validated_data["end"].timetuple()))
else:
end = int(time.mktime(time.localtime()))
start = "-1{}".format(period)
start = f"-1{period}"
graphs = graph_sets[gset].export(
fname, start, end, serializer.validated_data.get("graphic")
)
+3 -3
View File
@@ -15,10 +15,10 @@ class MaillogFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.Maillog
queue_id = factory.Sequence(lambda n: "ID{}".format(n))
queue_id = factory.Sequence(lambda n: f"ID{n}")
date = timezone.now()
sender = factory.Sequence(lambda n: "sender{}@example.test".format(n))
rcpt = factory.Sequence(lambda n: "rcpt{}@example2.test".format(n))
sender = factory.Sequence(lambda n: f"sender{n}@example.test")
rcpt = factory.Sequence(lambda n: f"rcpt{n}@example2.test")
size = 100
status = "sent"
from_domain = factory.SubFactory(admin_factories.DomainFactory)
+2 -4
View File
@@ -102,9 +102,7 @@ class Graphic:
cmdargs += curve.to_rrd_command_args(rrdfile)
code = 0
cmd = "{} xport --json -t --start {} --end {} ".format(
self.rrdtool_binary, str(start), str(end)
)
cmd = f"{self.rrdtool_binary} xport --json -t --start {str(start)} --end {str(end)} "
cmd += " ".join(cmdargs)
code, output = exec_cmd(smart_bytes(cmd))
if code:
@@ -122,7 +120,7 @@ class Graphic:
return result
class GraphicSet(object):
class GraphicSet:
"""A set of graphics."""
domain_selector = False
-1
View File
@@ -1,4 +1,3 @@
# coding: utf-8
import sys
import time
@@ -1,5 +1,3 @@
# coding: utf-8
"""
Postfix log parser.
@@ -230,7 +228,7 @@ class LogParser:
def add_point_to_rrd(self, fname, tpl, values, ts=None):
"""Try to add a new point to RRD file."""
if ts:
values = "{}:{}".format(ts, values)
values = f"{ts}:{values}"
if self.verbose:
print("[rrd] VERBOSE update -t %s %s" % (tpl, values))
try:
@@ -564,11 +562,11 @@ class LogParser:
host, prog, subprog, pid, log = m.groups()
try:
parser = getattr(self, "_parse_{}".format(prog))
parser = getattr(self, f"_parse_{prog}")
if not parser(log, host, pid, subprog):
self._dprint("[parser] ignoring %r log: %r" % (prog, log))
except AttributeError:
self._dprint('[parser] no log handler for "{}": {}'.format(prog, log))
self._dprint(f'[parser] no log handler for "{prog}": {log}')
def process(self):
"""Process the log file.
@@ -580,7 +578,7 @@ class LogParser:
with open(self.logfile, encoding="utf-8", errors="ignore") as fp:
for line in fp:
self._parse_line(line)
except IOError as errno:
except OSError as errno:
self._dprint("%s" % errno)
sys.exit(1)
@@ -29,7 +29,7 @@ class Command(BaseCommand):
"""Create RRD file."""
step = 3600
ds_name = "new_accounts"
params = ["DS:{}:ABSOLUTE:{}:0:U".format(ds_name, step * 2)]
params = [f"DS:{ds_name}:ABSOLUTE:{step * 2}:0:U"]
params += [
"RRA:AVERAGE:0.5:1:48", # 48 hours with a 1h granularity
"RRA:AVERAGE:0.5:24:31", # 31 days with a 1d granularity

Some files were not shown because too many files have changed in this diff Show More