Source code for fairdm.registry.exceptions
"""Exceptions raised by the registry."""
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from django.db import models
[docs]
class RegistryError(Exception):
"""Base exception for all registry errors.
All registry-specific exceptions inherit from this class,
allowing catching of all registry errors with a single except block.
Example:
try:
@register
class BadConfig(ModelConfiguration):
model = BadModel # Missing Sample/Measurement inheritance
except RegistryError as e:
logger.error(f"Registration failed: {e}")
"""
[docs]
class ConfigurationError(RegistryError):
"""Invalid ModelConfiguration setup.
Raised when:
- Model doesn't inherit from Sample or Measurement
- Required model attribute is missing
- Custom class doesn't inherit from expected base
- Invalid configuration combination
The message is prefixed with the model's or configuration class's name when
either is given.
Args:
message: Error description.
model: The Django model class, if known.
config_class: The ModelConfiguration class, if known.
Example:
raise ConfigurationError(
"model attribute is required",
config_class=RockSampleConfig
)
"""
def __init__(
self,
message: str,
model: type["models.Model"] | None = None,
config_class: type | None = None,
):
self.model = model
self.config_class = config_class
if model:
message = f"{model.__name__}: {message}"
elif config_class:
message = f"{config_class.__name__}: {message}"
super().__init__(message)
[docs]
class FieldValidationError(RegistryError):
"""A field name in a configuration does not resolve on the model.
The message names the four things a portal developer needs in order to fix it:
the model, the attribute that declared the name, the name itself, and either a
close match or the reason the path stopped resolving.
Args:
field_name: The name or path that failed.
model: The Django model it was declared against.
attribute: The configuration attribute that declared it.
suggestion: A comma-separated list of close matches, if any.
reason: Why the path stopped resolving, where that is not a missing name.
Example:
raise FieldValidationError(
"loction", RockSample, attribute="fields", suggestion="location"
)
# "Invalid field 'loction' in RockSample.fields: no such field on
# RockSample. Did you mean: location?"
"""
def __init__(
self,
field_name: str,
model: type["models.Model"],
attribute: str | None = None,
suggestion: str | None = None,
reason: str | None = None,
):
self.field_name = field_name
self.model = model
self.attribute = attribute
self.suggestion = suggestion
self.reason = reason
where = f"{model.__name__}.{attribute}" if attribute else model.__name__
why = reason or f"no such field on {model.__name__}"
message = f"Invalid field {field_name!r} in {where}: {why}"
if suggestion:
message += f". Did you mean: {suggestion}?"
super().__init__(message)
[docs]
class NotRegisteredError(RegistryError, KeyError):
"""The configuration of a model that was never registered was requested.
Subclasses ``KeyError`` so that callers written against the registry's earlier
behaviour keep working, while the message names the model rather than repeating
its label as a bare key.
Args:
model: The model class, or its ``app_label.ModelName`` label.
"""
def __init__(self, model: "type[models.Model] | str"):
self.model = model
name = model if isinstance(model, str) else model._meta.label
super().__init__(
f"{name} is not registered with the FairDM registry. Register it with "
f"@fairdm.register, or use registry.is_registered() to ask without "
f"raising."
)
def __str__(self) -> str:
"""Return the message unquoted."""
# KeyError repr()s its argument, which would quote the whole sentence.
return str(self.args[0])
[docs]
class DuplicateRegistrationError(RegistryError):
"""Model registered multiple times.
Raised when attempting to register a model that's already registered.
Each model can only be registered once.
Args:
model: The Django model class.
original_location: Module path where the model was first registered.
new_location: Module path of the duplicate registration attempt.
Example:
raise DuplicateRegistrationError(
RockSample,
original_location='myapp.registry',
new_location='myapp.another_registry'
)
# "RockSample already registered at myapp.registry. Each model can only
# be registered once. Attempted duplicate registration from
# myapp.another_registry."
"""
def __init__(
self,
model: type["models.Model"],
original_location: str,
new_location: str | None = None,
):
self.model = model
self.original_location = original_location
self.new_location = new_location
message = f"{model.__name__} already registered at {original_location}. Each model can only be registered once."
if new_location:
message += f" Attempted duplicate registration from {new_location}."
super().__init__(message)