15049 add missing gettext to error strings

This commit is contained in:
Arthur 2024-02-14 10:31:45 -08:00
parent 4416657681
commit 3f305d199d
29 changed files with 124 additions and 68 deletions

View File

@ -102,7 +102,7 @@ class GitBackend(DataBackend):
try: try:
porcelain.clone(self.url, local_path.name, **clone_args) porcelain.clone(self.url, local_path.name, **clone_args)
except BaseException as e: except BaseException as e:
raise SyncError(f"Fetching remote data failed ({type(e).__name__}): {e}") raise SyncError(_("Fetching remote data failed ({name}): {e}").format(name=type(e).__name__, e=e))
yield local_path.name yield local_path.name

View File

@ -181,7 +181,7 @@ class Job(models.Model):
""" """
valid_statuses = JobStatusChoices.TERMINAL_STATE_CHOICES valid_statuses = JobStatusChoices.TERMINAL_STATE_CHOICES
if status not in valid_statuses: if status not in valid_statuses:
raise ValueError(f"Invalid status for job termination. Choices are: {', '.join(valid_statuses)}") raise ValueError(_("Invalid status for job termination. Choices are: {choices}").format(choices=', '.join(valid_statuses)))
# Mark the job as completed # Mark the job as completed
self.status = status self.status = status

View File

@ -1,6 +1,7 @@
from django.contrib.postgres.fields import ArrayField from django.contrib.postgres.fields import ArrayField
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.db import models from django.db import models
from django.utils.translation import gettext as _
from netaddr import AddrFormatError, EUI, eui64_unix_expanded, mac_unix_expanded from netaddr import AddrFormatError, EUI, eui64_unix_expanded, mac_unix_expanded
from .lookups import PathContains from .lookups import PathContains
@ -41,7 +42,7 @@ class MACAddressField(models.Field):
try: try:
return EUI(value, version=48, dialect=mac_unix_expanded_uppercase) return EUI(value, version=48, dialect=mac_unix_expanded_uppercase)
except AddrFormatError: except AddrFormatError:
raise ValidationError(f"Invalid MAC address format: {value}") raise ValidationError(_("Invalid MAC address format: {value}").format(value=value))
def db_type(self, connection): def db_type(self, connection):
return 'macaddr' return 'macaddr'
@ -67,7 +68,7 @@ class WWNField(models.Field):
try: try:
return EUI(value, version=64, dialect=eui64_unix_expanded_uppercase) return EUI(value, version=64, dialect=eui64_unix_expanded_uppercase)
except AddrFormatError: except AddrFormatError:
raise ValidationError(f"Invalid WWN format: {value}") raise ValidationError(_("Invalid WWN format: {value}").format(value=value))
def db_type(self, connection): def db_type(self, connection):
return 'macaddr8' return 'macaddr8'

View File

@ -870,7 +870,9 @@ class InterfaceImportForm(NetBoxModelImportForm):
def clean_vdcs(self): def clean_vdcs(self):
for vdc in self.cleaned_data['vdcs']: for vdc in self.cleaned_data['vdcs']:
if vdc.device != self.cleaned_data['device']: if vdc.device != self.cleaned_data['device']:
raise forms.ValidationError(f"VDC {vdc} is not assigned to device {self.cleaned_data['device']}") raise forms.ValidationError(
_("VDC {vdc} is not assigned to device {device}").format(
vdc=vdc, device=self.cleaned_data['device']))
return self.cleaned_data['vdcs'] return self.cleaned_data['vdcs']
@ -1075,7 +1077,8 @@ class InventoryItemImportForm(NetBoxModelImportForm):
component = model.objects.get(device=device, name=component_name) component = model.objects.get(device=device, name=component_name)
self.instance.component = component self.instance.component = component
except ObjectDoesNotExist: except ObjectDoesNotExist:
raise forms.ValidationError(f"Component not found: {device} - {component_name}") raise forms.ValidationError(_("Component not found: {device} - {component_name}").format(
device=device, component_name=component_name))
# #
@ -1193,9 +1196,13 @@ class CableImportForm(NetBoxModelImportForm):
else: else:
termination_object = model.objects.get(device=device, name=name) termination_object = model.objects.get(device=device, name=name)
if termination_object.cable is not None and termination_object.cable != self.instance: if termination_object.cable is not None and termination_object.cable != self.instance:
raise forms.ValidationError(f"Side {side.upper()}: {device} {termination_object} is already connected") raise forms.ValidationError(
_("Side {side_upper}: {device} {termination_object} is already connected").format(
side_upper=side.upper(), device=device, termination_object=termination_object))
except ObjectDoesNotExist: except ObjectDoesNotExist:
raise forms.ValidationError(f"{side.upper()} side termination not found: {device} {name}") raise forms.ValidationError(
_("{side_upper} side termination not found: {device} {name}").format(
side_upper=side.upper(), device=device, name=name))
setattr(self.instance, f'{side}_terminations', [termination_object]) setattr(self.instance, f'{side}_terminations', [termination_object])
return termination_object return termination_object

View File

@ -1,4 +1,5 @@
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.utils.translation import gettext as _
from drf_spectacular.utils import extend_schema_field from drf_spectacular.utils import extend_schema_field
from drf_spectacular.types import OpenApiTypes from drf_spectacular.types import OpenApiTypes
from rest_framework.fields import Field from rest_framework.fields import Field
@ -88,7 +89,7 @@ class CustomFieldsDataField(Field):
if serializer.is_valid(): if serializer.is_valid():
data[cf.name] = [obj['id'] for obj in serializer.data] if many else serializer.data['id'] data[cf.name] = [obj['id'] for obj in serializer.data] if many else serializer.data['id']
else: else:
raise ValidationError(f"Unknown related object(s): {data[cf.name]}") raise ValidationError(_("Unknown related object(s): {name}").format(name=data[cf.name]))
# If updating an existing instance, start with existing custom_field_data # If updating an existing instance, start with existing custom_field_data
if self.parent.instance: if self.parent.instance:

View File

@ -1,5 +1,6 @@
import functools import functools
import re import re
from django.utils.translation import gettext as _
__all__ = ( __all__ = (
'Condition', 'Condition',
@ -50,11 +51,12 @@ class Condition:
def __init__(self, attr, value, op=EQ, negate=False): def __init__(self, attr, value, op=EQ, negate=False):
if op not in self.OPERATORS: if op not in self.OPERATORS:
raise ValueError(f"Unknown operator: {op}. Must be one of: {', '.join(self.OPERATORS)}") raise ValueError(_("Unknown operator: {op}. Must be one of: {operators}").format(
op=op, operators=', '.join(self.OPERATORS)))
if type(value) not in self.TYPES: if type(value) not in self.TYPES:
raise ValueError(f"Unsupported value type: {type(value)}") raise ValueError(_("Unsupported value type: {value}").format(value=type(value)))
if op not in self.TYPES[type(value)]: if op not in self.TYPES[type(value)]:
raise ValueError(f"Invalid type for {op} operation: {type(value)}") raise ValueError(_("Invalid type for {op} operation: {value}").format(op=op, value=type(value)))
self.attr = attr self.attr = attr
self.value = value self.value = value
@ -131,14 +133,16 @@ class ConditionSet:
""" """
def __init__(self, ruleset): def __init__(self, ruleset):
if type(ruleset) is not dict: if type(ruleset) is not dict:
raise ValueError(f"Ruleset must be a dictionary, not {type(ruleset)}.") raise ValueError(_("Ruleset must be a dictionary, not {ruleset}.").format(ruleset=type(ruleset)))
if len(ruleset) != 1: if len(ruleset) != 1:
raise ValueError(f"Ruleset must have exactly one logical operator (found {len(ruleset)})") raise ValueError(_("Ruleset must have exactly one logical operator (found {ruleset})").format(
ruleset=len(ruleset)))
# Determine the logic type # Determine the logic type
logic = list(ruleset.keys())[0] logic = list(ruleset.keys())[0]
if type(logic) is not str or logic.lower() not in (AND, OR): if type(logic) is not str or logic.lower() not in (AND, OR):
raise ValueError(f"Invalid logic type: {logic} (must be '{AND}' or '{OR}')") raise ValueError(_("Invalid logic type: {logic} (must be '{op_and}' or '{op_or}')").format(
logic=logic, op_and=AND, op_or=OR))
self.logic = logic.lower() self.logic = logic.lower()
# Compile the set of Conditions # Compile the set of Conditions

View File

@ -2,6 +2,7 @@ import uuid
from django.conf import settings from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.utils.translation import gettext as _
from netbox.registry import registry from netbox.registry import registry
from extras.constants import DEFAULT_DASHBOARD from extras.constants import DEFAULT_DASHBOARD
@ -32,7 +33,7 @@ def get_widget_class(name):
try: try:
return registry['widgets'][name] return registry['widgets'][name]
except KeyError: except KeyError:
raise ValueError(f"Unregistered widget class: {name}") raise ValueError(_("Unregistered widget class: {name}").format(name=name))
def get_dashboard(user): def get_dashboard(user):

View File

@ -112,7 +112,8 @@ class DashboardWidget:
Params: Params:
request: The current request request: The current request
""" """
raise NotImplementedError(f"{self.__class__} must define a render() method.") raise NotImplementedError(_("{class_name} must define a render() method.").format(
class_name=self.__class__))
@property @property
def name(self): def name(self):

View File

@ -6,6 +6,7 @@ from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.utils import timezone from django.utils import timezone
from django.utils.module_loading import import_string from django.utils.module_loading import import_string
from django.utils.translation import gettext as _
from django_rq import get_queue from django_rq import get_queue
from core.models import Job from core.models import Job
@ -129,7 +130,8 @@ def process_event_rules(event_rules, model_name, event, data, username=None, sna
) )
else: else:
raise ValueError(f"Unknown action type for an event rule: {event_rule.action_type}") raise ValueError(_("Unknown action type for an event rule: {action_type}").format(
action_type=event_rule.action_type))
def process_event_queue(events): def process_event_queue(events):
@ -175,4 +177,4 @@ def flush_events(queue):
func = import_string(name) func = import_string(name)
func(queue) func(queue)
except Exception as e: except Exception as e:
logger.error(f"Cannot import events pipeline {name} error: {e}") logger.error(_("Cannot import events pipeline {name} error: {e}").format(name=name, e=e))

View File

@ -202,7 +202,7 @@ class EventRuleImportForm(NetBoxModelImportForm):
try: try:
webhook = Webhook.objects.get(name=action_object) webhook = Webhook.objects.get(name=action_object)
except Webhook.DoesNotExist: except Webhook.DoesNotExist:
raise forms.ValidationError(f"Webhook {action_object} not found") raise forms.ValidationError(_("Webhook {action_object} not found").format(action_object=action_object))
self.instance.action_object = webhook self.instance.action_object = webhook
# Script # Script
elif action_type == EventRuleActionChoices.SCRIPT: elif action_type == EventRuleActionChoices.SCRIPT:
@ -211,7 +211,7 @@ class EventRuleImportForm(NetBoxModelImportForm):
try: try:
module, script = get_module_and_script(module_name, script_name) module, script = get_module_and_script(module_name, script_name)
except ObjectDoesNotExist: except ObjectDoesNotExist:
raise forms.ValidationError(f"Script {action_object} not found") raise forms.ValidationError(_("Script {action_object} not found").format(action_object=action_object))
self.instance.action_object = module self.instance.action_object = module
self.instance.action_object_type = ContentType.objects.get_for_model(module, for_concrete_model=False) self.instance.action_object_type = ContentType.objects.get_for_model(module, for_concrete_model=False)
self.instance.action_parameters = { self.instance.action_parameters = {

View File

@ -83,7 +83,7 @@ class ChoiceField(serializers.Field):
except TypeError: # Input is an unhashable type except TypeError: # Input is an unhashable type
pass pass
raise ValidationError(f"{data} is not a valid choice.") raise ValidationError(_("{data} is not a valid choice.").format(data=data))
@property @property
def choices(self): def choices(self):

View File

@ -1,4 +1,5 @@
from django.core.exceptions import FieldError, MultipleObjectsReturned, ObjectDoesNotExist from django.core.exceptions import FieldError, MultipleObjectsReturned, ObjectDoesNotExist
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers from rest_framework import serializers
from rest_framework.exceptions import ValidationError from rest_framework.exceptions import ValidationError
@ -30,9 +31,10 @@ class WritableNestedSerializer(BaseModelSerializer):
try: try:
return queryset.get(**params) return queryset.get(**params)
except ObjectDoesNotExist: except ObjectDoesNotExist:
raise ValidationError(f"Related object not found using the provided attributes: {params}") raise ValidationError(
_("Related object not found using the provided attributes: {params}").format(params=params))
except MultipleObjectsReturned: except MultipleObjectsReturned:
raise ValidationError(f"Multiple objects match the provided attributes: {params}") raise ValidationError(_("Multiple objects match the provided attributes: {params}").format(params=params))
except FieldError as e: except FieldError as e:
raise ValidationError(e) raise ValidationError(e)
@ -42,15 +44,14 @@ class WritableNestedSerializer(BaseModelSerializer):
pk = int(data) pk = int(data)
except (TypeError, ValueError): except (TypeError, ValueError):
raise ValidationError( raise ValidationError(
f"Related objects must be referenced by numeric ID or by dictionary of attributes. Received an " _("Related objects must be referenced by numeric ID or by dictionary of attributes. Received an unrecognized value: {data}").format(data=data)
f"unrecognized value: {data}"
) )
# Look up object by PK # Look up object by PK
try: try:
return self.Meta.model.objects.get(pk=pk) return self.Meta.model.objects.get(pk=pk)
except ObjectDoesNotExist: except ObjectDoesNotExist:
raise ValidationError(f"Related object not found using the provided numeric ID: {pk}") raise ValidationError(_("Related object not found using the provided numeric ID: {pk}").format(pk=pk))
# Declared here for use by PrimaryModelSerializer, but should be imported from extras.api.nested_serializers # Declared here for use by PrimaryModelSerializer, but should be imported from extras.api.nested_serializers

View File

@ -7,6 +7,7 @@ from django.contrib.auth.backends import ModelBackend, RemoteUserBackend as _Rem
from django.contrib.auth.models import Group, AnonymousUser from django.contrib.auth.models import Group, AnonymousUser
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.db.models import Q from django.db.models import Q
from django.utils.translation import gettext_lazy as _
from users.constants import CONSTRAINT_TOKEN_USER from users.constants import CONSTRAINT_TOKEN_USER
from users.models import ObjectPermission from users.models import ObjectPermission
@ -132,7 +133,7 @@ class ObjectPermissionMixin:
# Sanity check: Ensure that the requested permission applies to the specified object # Sanity check: Ensure that the requested permission applies to the specified object
model = obj._meta.concrete_model model = obj._meta.concrete_model
if model._meta.label_lower != '.'.join((app_label, model_name)): if model._meta.label_lower != '.'.join((app_label, model_name)):
raise ValueError(f"Invalid permission {perm} for model {model}") raise ValueError(_("Invalid permission {perm} for model {model}").format(perm=perm, model=model))
# Compile a QuerySet filter that matches all instances of the specified model # Compile a QuerySet filter that matches all instances of the specified model
tokens = { tokens = {

View File

@ -4,6 +4,7 @@ import threading
from django.conf import settings from django.conf import settings
from django.core.cache import cache from django.core.cache import cache
from django.db.utils import DatabaseError from django.db.utils import DatabaseError
from django.utils.translation import gettext_lazy as _
from .parameters import PARAMS from .parameters import PARAMS
@ -63,7 +64,7 @@ class Config:
if item in self.defaults: if item in self.defaults:
return self.defaults[item] return self.defaults[item]
raise AttributeError(f"Invalid configuration parameter: {item}") raise AttributeError(_("Invalid configuration parameter: {item}").format(item=item))
def _populate_from_cache(self): def _populate_from_cache(self):
"""Populate config data from Redis cache""" """Populate config data from Redis cache"""

View File

@ -35,7 +35,8 @@ class CustomFieldsMixin:
Return the ContentType of the form's model. Return the ContentType of the form's model.
""" """
if not getattr(self, 'model', None): if not getattr(self, 'model', None):
raise NotImplementedError(f"{self.__class__.__name__} must specify a model class.") raise NotImplementedError(_("{class_name} must specify a model class.").format(
class_name=self.__class__.__name__))
return ContentType.objects.get_for_model(self.model) return ContentType.objects.get_for_model(self.model)
def _get_custom_fields(self, content_type): def _get_custom_fields(self, content_type):

View File

@ -275,16 +275,18 @@ class CustomFieldsMixin(models.Model):
# Validate all field values # Validate all field values
for field_name, value in self.custom_field_data.items(): for field_name, value in self.custom_field_data.items():
if field_name not in custom_fields: if field_name not in custom_fields:
raise ValidationError(f"Unknown field name '{field_name}' in custom field data.") raise ValidationError(_("Unknown field name '{field_name}' in custom field data.").format(
field_name=field_name))
try: try:
custom_fields[field_name].validate(value) custom_fields[field_name].validate(value)
except ValidationError as e: except ValidationError as e:
raise ValidationError(f"Invalid value for custom field '{field_name}': {e.message}") raise ValidationError(_("Invalid value for custom field '{field_name}': {message}").format(
field_name=field_name, message=e.message))
# Check for missing required values # Check for missing required values
for cf in custom_fields.values(): for cf in custom_fields.values():
if cf.required and cf.name not in self.custom_field_data: if cf.required and cf.name not in self.custom_field_data:
raise ValidationError(f"Missing required custom field '{cf.name}'.") raise ValidationError(_("Missing required custom field '{name}'.").format(name=cf.name))
class CustomLinksMixin(models.Model): class CustomLinksMixin(models.Model):
@ -547,7 +549,8 @@ class SyncedDataMixin(models.Model):
Inheriting models must override this method with specific logic to copy data from the assigned DataFile Inheriting models must override this method with specific logic to copy data from the assigned DataFile
to the local instance. This method should *NOT* call save() on the instance. to the local instance. This method should *NOT* call save() on the instance.
""" """
raise NotImplementedError(f"{self.__class__} must implement a sync_data() method.") raise NotImplementedError(_("{class_name} must implement a sync_data() method.").format(
class_name=self.__class__))
# #

View File

@ -1,5 +1,6 @@
import inspect import inspect
from django.utils.translation import gettext_lazy as _
from netbox.registry import registry from netbox.registry import registry
from .navigation import PluginMenu, PluginMenuButton, PluginMenuItem from .navigation import PluginMenu, PluginMenuButton, PluginMenuItem
from .templates import PluginTemplateExtension from .templates import PluginTemplateExtension
@ -20,18 +21,24 @@ def register_template_extensions(class_list):
# Validation # Validation
for template_extension in class_list: for template_extension in class_list:
if not inspect.isclass(template_extension): if not inspect.isclass(template_extension):
raise TypeError(f"PluginTemplateExtension class {template_extension} was passed as an instance!") raise TypeError(
_("PluginTemplateExtension class {template_extension} was passed as an instance!").format(
template_extension=template_extension))
if not issubclass(template_extension, PluginTemplateExtension): if not issubclass(template_extension, PluginTemplateExtension):
raise TypeError(f"{template_extension} is not a subclass of netbox.plugins.PluginTemplateExtension!") raise TypeError(
_("{template_extension} is not a subclass of netbox.plugins.PluginTemplateExtension!").format(
template_extension=template_extension))
if template_extension.model is None: if template_extension.model is None:
raise TypeError(f"PluginTemplateExtension class {template_extension} does not define a valid model!") raise TypeError(
_("PluginTemplateExtension class {template_extension} does not define a valid model!").format(
template_extension=template_extension))
registry['plugins']['template_extensions'][template_extension.model].append(template_extension) registry['plugins']['template_extensions'][template_extension.model].append(template_extension)
def register_menu(menu): def register_menu(menu):
if not isinstance(menu, PluginMenu): if not isinstance(menu, PluginMenu):
raise TypeError(f"{menu} must be an instance of netbox.plugins.PluginMenu") raise TypeError(_("{menu} must be an instance of netbox.plugins.PluginMenu").format(menu=menu))
registry['plugins']['menus'].append(menu) registry['plugins']['menus'].append(menu)
@ -42,10 +49,12 @@ def register_menu_items(section_name, class_list):
# Validation # Validation
for menu_link in class_list: for menu_link in class_list:
if not isinstance(menu_link, PluginMenuItem): if not isinstance(menu_link, PluginMenuItem):
raise TypeError(f"{menu_link} must be an instance of netbox.plugins.PluginMenuItem") raise TypeError(_("{menu_link} must be an instance of netbox.plugins.PluginMenuItem").format(
menu_link=menu_link))
for button in menu_link.buttons: for button in menu_link.buttons:
if not isinstance(button, PluginMenuButton): if not isinstance(button, PluginMenuButton):
raise TypeError(f"{button} must be an instance of netbox.plugins.PluginMenuButton") raise TypeError(_("{button} must be an instance of netbox.plugins.PluginMenuButton").format(
button=button))
registry['plugins']['menu_items'][section_name] = class_list registry['plugins']['menu_items'][section_name] = class_list

View File

@ -11,7 +11,7 @@ class Registry(dict):
try: try:
return super().__getitem__(key) return super().__getitem__(key)
except KeyError: except KeyError:
raise KeyError(f"Invalid store: {key}") raise KeyError(_("Invalid store: {key}").format(key=key))
def __setitem__(self, key, value): def __setitem__(self, key, value):
raise TypeError(_("Cannot add stores to registry after initialization")) raise TypeError(_("Cannot add stores to registry after initialization"))

View File

@ -14,6 +14,7 @@ from django.http import HttpResponse
from django.shortcuts import get_object_or_404, redirect, render from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse from django.urls import reverse
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import gettext as _
from django_tables2.export import TableExport from django_tables2.export import TableExport
from extras.models import ExportTemplate from extras.models import ExportTemplate
@ -390,7 +391,7 @@ class BulkImportView(GetReturnURLMixin, BaseMultiObjectView):
try: try:
instance = prefetched_objects[object_id] instance = prefetched_objects[object_id]
except KeyError: except KeyError:
form.add_error('data', f"Row {i}: Object with ID {object_id} does not exist") form.add_error('data', _("Row {i}: Object with ID {object_id} does not exist").format(i=i, object_id=object_id))
raise ValidationError('') raise ValidationError('')
# Take a snapshot for change logging # Take a snapshot for change logging

View File

@ -11,6 +11,7 @@ from django.shortcuts import redirect, render
from django.urls import reverse from django.urls import reverse
from django.utils.html import escape from django.utils.html import escape
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import gettext as _
from extras.signals import clear_events from extras.signals import clear_events
from utilities.error_handlers import handle_protectederror from utilities.error_handlers import handle_protectederror
@ -101,7 +102,8 @@ class ObjectChildrenView(ObjectView, ActionsMixin, TableMixin):
request: The current request request: The current request
parent: The parent object parent: The parent object
""" """
raise NotImplementedError(f'{self.__class__.__name__} must implement get_children()') raise NotImplementedError(_('{class_name} must implement get_children()').format(
class_name=self.__class__.__name__))
def prep_table_data(self, request, queryset, parent): def prep_table_data(self, request, queryset, parent):
""" """

View File

@ -78,7 +78,7 @@ class BulkImportForm(BootstrapMixin, SyncedDataMixin, forms.Form):
elif format == ImportFormatChoices.YAML: elif format == ImportFormatChoices.YAML:
self.cleaned_data['data'] = self._clean_yaml(data) self.cleaned_data['data'] = self._clean_yaml(data)
else: else:
raise forms.ValidationError(f"Unknown data format: {format}") raise forms.ValidationError(_("Unknown data format: {format}").format(format=format))
def _detect_format(self, data): def _detect_format(self, data):
""" """

View File

@ -2,6 +2,7 @@ import re
from django import forms from django import forms
from django.forms.models import fields_for_model from django.forms.models import fields_for_model
from django.utils.translation import gettext as _
from utilities.choices import unpack_grouped_choices from utilities.choices import unpack_grouped_choices
from utilities.querysets import RestrictedQuerySet from utilities.querysets import RestrictedQuerySet
@ -38,7 +39,7 @@ def parse_numeric_range(string, base=10):
try: try:
begin, end = int(begin.strip(), base=base), int(end.strip(), base=base) + 1 begin, end = int(begin.strip(), base=base), int(end.strip(), base=base) + 1
except ValueError: except ValueError:
raise forms.ValidationError(f'Range "{dash_range}" is invalid.') raise forms.ValidationError(_('Range "{dash_range}" is invalid.').format(dash_range=dash_range))
values.extend(range(begin, end)) values.extend(range(begin, end))
return sorted(set(values)) return sorted(set(values))
@ -61,7 +62,7 @@ def parse_alphanumeric_range(string):
begin, end = dash_range, dash_range begin, end = dash_range, dash_range
if begin.isdigit() and end.isdigit(): if begin.isdigit() and end.isdigit():
if int(begin) >= int(end): if int(begin) >= int(end):
raise forms.ValidationError(f'Range "{dash_range}" is invalid.') raise forms.ValidationError(_('Range "{dash_range}" is invalid.').format(dash_range=dash_range))
for n in list(range(int(begin), int(end) + 1)): for n in list(range(int(begin), int(end) + 1)):
values.append(n) values.append(n)
@ -73,10 +74,10 @@ def parse_alphanumeric_range(string):
else: else:
# Not a valid range (more than a single character) # Not a valid range (more than a single character)
if not len(begin) == len(end) == 1: if not len(begin) == len(end) == 1:
raise forms.ValidationError(f'Range "{dash_range}" is invalid.') raise forms.ValidationError(_('Range "{dash_range}" is invalid.').format(dash_range=dash_range))
if ord(begin) >= ord(end): if ord(begin) >= ord(end):
raise forms.ValidationError(f'Range "{dash_range}" is invalid.') raise forms.ValidationError(_('Range "{dash_range}" is invalid.').format(dash_range=dash_range))
for n in list(range(ord(begin), ord(end) + 1)): for n in list(range(ord(begin), ord(end) + 1)):
values.append(chr(n)) values.append(chr(n))
@ -221,18 +222,21 @@ def parse_csv(reader):
if '.' in header: if '.' in header:
field, to_field = header.split('.', 1) field, to_field = header.split('.', 1)
if field in headers: if field in headers:
raise forms.ValidationError(f'Duplicate or conflicting column header for "{field}"') raise forms.ValidationError(_('Duplicate or conflicting column header for "{field}"').format(
field=field))
headers[field] = to_field headers[field] = to_field
else: else:
if header in headers: if header in headers:
raise forms.ValidationError(f'Duplicate or conflicting column header for "{header}"') raise forms.ValidationError(_('Duplicate or conflicting column header for "{header}"').format(
header=header))
headers[header] = None headers[header] = None
# Parse CSV rows into a list of dictionaries mapped from the column headers. # Parse CSV rows into a list of dictionaries mapped from the column headers.
for i, row in enumerate(reader, start=1): for i, row in enumerate(reader, start=1):
if len(row) != len(headers): if len(row) != len(headers):
raise forms.ValidationError( raise forms.ValidationError(
f"Row {i}: Expected {len(headers)} columns but found {len(row)}" _("Row {i}: Expected {len_headers} columns but found {len_row}").format(
len_headers=len(headers), len_row=len(row))
) )
row = [col.strip() for col in row] row = [col.strip() for col in row]
record = dict(zip(headers.keys(), row)) record = dict(zip(headers.keys(), row))
@ -253,14 +257,16 @@ def validate_csv(headers, fields, required_fields):
is_update = True is_update = True
continue continue
if field not in fields: if field not in fields:
raise forms.ValidationError(f'Unexpected column header "{field}" found.') raise forms.ValidationError(_('Unexpected column header "{field}" found.').format(field=field))
if to_field and not hasattr(fields[field], 'to_field_name'): if to_field and not hasattr(fields[field], 'to_field_name'):
raise forms.ValidationError(f'Column "{field}" is not a related object; cannot use dots') raise forms.ValidationError(_('Column "{field}" is not a related object; cannot use dots').format(
field=field))
if to_field and not hasattr(fields[field].queryset.model, to_field): if to_field and not hasattr(fields[field].queryset.model, to_field):
raise forms.ValidationError(f'Invalid related object attribute for column "{field}": {to_field}') raise forms.ValidationError(_('Invalid related object attribute for column "{field}": {to_field}').format(
field=field, to_field=to_field))
# Validate required fields (if not an update) # Validate required fields (if not an update)
if not is_update: if not is_update:
for f in required_fields: for f in required_fields:
if f not in headers: if f not in headers:
raise forms.ValidationError(f'Required column header "{f}" not found.') raise forms.ValidationError(_('Required column header "{f}" not found.').format(f=f))

View File

@ -3,6 +3,7 @@ from typing import Dict, List, Tuple
from django import forms from django import forms
from django.conf import settings from django.conf import settings
from django.utils.translation import gettext_lazy as _
__all__ = ( __all__ = (
'APISelect', 'APISelect',
@ -119,7 +120,9 @@ class APISelect(forms.Select):
update = [{'fieldName': f, 'queryParam': q} for (f, q) in self.dynamic_params.items()] update = [{'fieldName': f, 'queryParam': q} for (f, q) in self.dynamic_params.items()]
self._serialize_params(key, update) self._serialize_params(key, update)
except IndexError as error: except IndexError as error:
raise RuntimeError(f"Missing required value for dynamic query param: '{self.dynamic_params}'") from error raise RuntimeError(
_("Missing required value for dynamic query param: '{dynamic_params}'").format(
dynamic_params=self.dynamic_params)) from error
def _add_static_params(self): def _add_static_params(self):
""" """
@ -132,7 +135,9 @@ class APISelect(forms.Select):
update = [{'queryParam': k, 'queryValue': v} for (k, v) in self.static_params.items()] update = [{'queryParam': k, 'queryValue': v} for (k, v) in self.static_params.items()]
self._serialize_params(key, update) self._serialize_params(key, update)
except IndexError as error: except IndexError as error:
raise RuntimeError(f"Missing required value for static query param: '{self.static_params}'") from error raise RuntimeError(
_("Missing required value for static query param: '{static_params}'").format(
static_params=self.static_params)) from error
def add_query_params(self, query_params): def add_query_params(self, query_params):
""" """

View File

@ -1,6 +1,7 @@
from django.conf import settings from django.conf import settings
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.db.models import Q from django.db.models import Q
from django.utils.translation import gettext_lazy as _
__all__ = ( __all__ = (
'get_permission_for_model', 'get_permission_for_model',
@ -36,7 +37,7 @@ def resolve_permission(name):
action, model_name = codename.rsplit('_', 1) action, model_name = codename.rsplit('_', 1)
except ValueError: except ValueError:
raise ValueError( raise ValueError(
f"Invalid permission name: {name}. Must be in the format <app_label>.<action>_<model>" _("Invalid permission name: {name}. Must be in the format <app_label>.<action>_<model>").format(name=name)
) )
return app_label, action, model_name return app_label, action, model_name
@ -53,7 +54,7 @@ def resolve_permission_ct(name):
try: try:
content_type = ContentType.objects.get(app_label=app_label, model=model_name) content_type = ContentType.objects.get(app_label=app_label, model=model_name)
except ContentType.DoesNotExist: except ContentType.DoesNotExist:
raise ValueError(f"Unknown app_label/model_name for {name}") raise ValueError(_("Unknown app_label/model_name for {name}").format(name=name))
return content_type, action return content_type, action

View File

@ -1,3 +1,4 @@
from django.utils.translation import gettext_lazy as _
from netaddr import AddrFormatError, IPAddress from netaddr import AddrFormatError, IPAddress
from urllib.parse import urlparse from urllib.parse import urlparse
@ -29,7 +30,7 @@ def get_client_ip(request, additional_headers=()):
return IPAddress(ip) return IPAddress(ip)
except AddrFormatError: except AddrFormatError:
# We did our best # We did our best
raise ValueError(f"Invalid IP address set for {header}: {ip}") raise ValueError(_("Invalid IP address set for {header}: {ip}").format(header=header, ip=ip))
# Could not determine the client IP address from request headers # Could not determine the client IP address from request headers
return None return None

View File

@ -1,3 +1,4 @@
from django.utils.translation import gettext_lazy as _
from netbox.registry import registry from netbox.registry import registry
__all__ = ( __all__ = (
@ -43,5 +44,6 @@ def register_table_column(column, name, *tables):
for table in tables: for table in tables:
reg = registry['tables'][table] reg = registry['tables'][table]
if name in reg: if name in reg:
raise ValueError(f"A column named {name} is already defined for table {table.__name__}") raise ValueError(_("A column named {name} is already defined for table {table_name}").format(
name=name, table_name=table.__name__))
reg[name] = column reg[name] = column

View File

@ -2,6 +2,7 @@ import re
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.core.validators import BaseValidator, RegexValidator, URLValidator, _lazy_re_compile from django.core.validators import BaseValidator, RegexValidator, URLValidator, _lazy_re_compile
from django.utils.translation import gettext_lazy as _
from netbox.config import get_config from netbox.config import get_config
@ -61,4 +62,4 @@ def validate_regex(value):
try: try:
re.compile(value) re.compile(value)
except re.error: except re.error:
raise ValidationError(f"{value} is not a valid regular expression.") raise ValidationError(_("{value} is not a valid regular expression.").format(value=value))

View File

@ -2,6 +2,7 @@ from django.contrib.auth.mixins import AccessMixin
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.urls import reverse from django.urls import reverse
from django.urls.exceptions import NoReverseMatch from django.urls.exceptions import NoReverseMatch
from django.utils.translation import gettext_lazy as _
from netbox.registry import registry from netbox.registry import registry
from .permissions import resolve_permission from .permissions import resolve_permission
@ -34,7 +35,8 @@ class ContentTypePermissionRequiredMixin(AccessMixin):
""" """
Return the specific permission necessary to perform the requested action on an object. Return the specific permission necessary to perform the requested action on an object.
""" """
raise NotImplementedError(f"{self.__class__.__name__} must implement get_required_permission()") raise NotImplementedError(_("{self.__class__.__name__} must implement get_required_permission()").format(
class_name=self.__class__.__name__))
def has_permission(self): def has_permission(self):
user = self.request.user user = self.request.user
@ -68,7 +70,8 @@ class ObjectPermissionRequiredMixin(AccessMixin):
""" """
Return the specific permission necessary to perform the requested action on an object. Return the specific permission necessary to perform the requested action on an object.
""" """
raise NotImplementedError(f"{self.__class__.__name__} must implement get_required_permission()") raise NotImplementedError(_("{class_name} must implement get_required_permission()").format(
class_name=self.__class__.__name__))
def has_permission(self): def has_permission(self):
user = self.request.user user = self.request.user
@ -89,8 +92,8 @@ class ObjectPermissionRequiredMixin(AccessMixin):
if not hasattr(self, 'queryset'): if not hasattr(self, 'queryset'):
raise ImproperlyConfigured( raise ImproperlyConfigured(
'{} has no queryset defined. ObjectPermissionRequiredMixin may only be used on views which define ' _('{} has no queryset defined. ObjectPermissionRequiredMixin may only be used on views which define a base queryset').format(
'a base queryset'.format(self.__class__.__name__) self.__class__.__name__)
) )
if not self.has_permission(): if not self.has_permission():

View File

@ -1,4 +1,5 @@
from decimal import Decimal from decimal import Decimal
from django.utils.translation import gettext_lazy as _
from .choices import WirelessChannelChoices from .choices import WirelessChannelChoices
@ -12,7 +13,7 @@ def get_channel_attr(channel, attr):
Return the specified attribute of a given WirelessChannelChoices value. Return the specified attribute of a given WirelessChannelChoices value.
""" """
if channel not in WirelessChannelChoices.values(): if channel not in WirelessChannelChoices.values():
raise ValueError(f"Invalid channel value: {channel}") raise ValueError(_("Invalid channel value: {channel}").format(channel=channel))
channel_values = channel.split('-') channel_values = channel.split('-')
attrs = { attrs = {
@ -22,6 +23,6 @@ def get_channel_attr(channel, attr):
'width': Decimal(channel_values[3]), 'width': Decimal(channel_values[3]),
} }
if attr not in attrs: if attr not in attrs:
raise ValueError(f"Invalid channel attribute: {attr}") raise ValueError(_("Invalid channel attribute: {attr}").format(attr=attr))
return attrs[attr] return attrs[attr]