Entity relationship diagrams for Python data model classes like Pydantic.
—
Erdantic defines several exception classes that provide specific error information when operations fail. These exceptions help identify and troubleshoot issues with model analysis, plugin registration, and diagram generation.
class ErdanticException(Exception):
"""Base class for all exceptions from erdantic library."""
class UnknownModelTypeError(ValueError, ErdanticException):
"""Raised when a given model does not match known model types from loaded plugins.
Attributes:
model (type): The model class that was not recognized.
available_plugins (list[str]): List of plugin keys that were available.
"""
class PluginNotFoundError(KeyError, ErdanticException):
"""Raised when specified plugin key does not match a registered plugin.
Attributes:
key (str): The plugin key that was not found.
"""
class ModelOrModuleNotFoundError(ImportError, ErdanticException):
"""Raised when specified fully qualified name of model class or module cannot be imported."""
class UnresolvableForwardRefError(NameError, ErdanticException):
"""Raised when a forward reference in a type annotation cannot be resolved automatically.
Attributes:
name (str): The string representation of the unresolvable forward reference.
model_full_name (FullyQualifiedName): The fully qualified name of the model with the forward reference.
"""
class UnevaluatedForwardRefError(ErdanticException):
"""Raised when a field's type declaration has an unevaluated forward reference.
Attributes:
model_full_name (FullyQualifiedName): The fully qualified name of the model with the field
with the unevaluated forward reference.
field_name (str): The name of the field with the unevaluated forward reference.
forward_ref (str): The string representation of the unevaluated forward reference.
"""
class FieldNotFoundError(AttributeError, ErdanticException):
"""Raised trying to access a field name that does not match any fields returned by the
field extractor function for a model.
Attributes:
name (str): The name of the field that was not found.
obj (object): The model object that the field was being accessed on.
model_full_name (FullyQualifiedName): The fully qualified name of the model.
"""from erdantic.exceptions import (
ErdanticException, UnknownModelTypeError, PluginNotFoundError,
ModelOrModuleNotFoundError, UnresolvableForwardRefError,
UnevaluatedForwardRefError, FieldNotFoundError
)
from erdantic.core import FullyQualifiedNamefrom erdantic import create
from erdantic.exceptions import UnknownModelTypeError
class MyCustomClass:
"""Not a data model class - will cause error."""
name: str
try:
diagram = create(MyCustomClass)
except UnknownModelTypeError as e:
print(f"Model not recognized: {e.model}")
print(f"Available plugins: {e.available_plugins}")
print("Make sure your class inherits from a supported data model base class")from erdantic.plugins import get_predicate_fn
from erdantic.exceptions import PluginNotFoundError
try:
predicate = get_predicate_fn("nonexistent_plugin")
except PluginNotFoundError as e:
print(f"Plugin '{e.key}' not found")
# Show available plugins
from erdantic import list_plugins
print(f"Available plugins: {list_plugins()}")from erdantic.cli import import_object_from_name
from erdantic.exceptions import ModelOrModuleNotFoundError
try:
model_class = import_object_from_name("nonexistent.module.Model")
except ModelOrModuleNotFoundError as e:
print(f"Could not import: {e}")
print("Check that the module path is correct and the module is installed")from erdantic import create
from erdantic.exceptions import UnresolvableForwardRefError, UnevaluatedForwardRefError
try:
diagram = create(MyModelWithForwardRefs)
except UnresolvableForwardRefError as e:
print(f"Cannot resolve forward reference '{e.name}' in model {e.model_full_name}")
print("Make sure all referenced types are properly imported or defined")
except UnevaluatedForwardRefError as e:
print(f"Unevaluated forward reference '{e.forward_ref}' in field '{e.field_name}'")
print(f"Model: {e.model_full_name}")
print("This usually indicates a plugin issue - consider reporting as a bug")from erdantic.core import FieldInfo, FullyQualifiedName
from erdantic.exceptions import FieldNotFoundError
# This would occur when accessing field information programmatically
try:
# Simulate field access that fails
field_info = FieldInfo(
model_full_name=FullyQualifiedName.from_object(MyModel),
name="nonexistent_field",
type_name="str"
)
raw_type = field_info.raw_type # This accesses the field
except FieldNotFoundError as e:
print(f"Field '{e.name}' not found on model {e.model_full_name}")
print("Check that the field name is correct")from erdantic import draw
from erdantic.exceptions import (
ErdanticException, UnknownModelTypeError, PluginNotFoundError,
ModelOrModuleNotFoundError, UnresolvableForwardRefError,
UnevaluatedForwardRefError, FieldNotFoundError
)
def safe_draw_diagram(models_or_modules, output_path):
"""Draw diagram with comprehensive error handling."""
try:
draw(*models_or_modules, out=output_path)
print(f"Diagram successfully created: {output_path}")
except UnknownModelTypeError as e:
print(f"❌ Unknown model type: {e.model.__name__}")
print(f" Available plugins: {e.available_plugins}")
return False
except PluginNotFoundError as e:
print(f"❌ Plugin not found: {e.key}")
return False
except ModelOrModuleNotFoundError as e:
print(f"❌ Import error: {e}")
return False
except (UnresolvableForwardRefError, UnevaluatedForwardRefError) as e:
print(f"❌ Forward reference error: {e}")
return False
except FieldNotFoundError as e:
print(f"❌ Field access error: {e}")
return False
except ErdanticException as e:
print(f"❌ Erdantic error: {type(e).__name__}: {e}")
return False
except Exception as e:
print(f"❌ Unexpected error: {type(e).__name__}: {e}")
return False
return True
# Usage
success = safe_draw_diagram([MyModel1, MyModel2], "diagram.png")
if not success:
print("See error messages above for troubleshooting guidance")from erdantic import create, list_plugins
from erdantic.exceptions import UnknownModelTypeError, PluginNotFoundError
def create_diagram_with_fallback(models):
"""Create diagram with fallback strategies for common errors."""
# Strategy 1: Try creating diagram normally
try:
return create(*models)
except UnknownModelTypeError as e:
print(f"Model {e.model} not supported by current plugins")
# Strategy 2: Check if we need additional plugins
available = e.available_plugins
if "attrs" not in available:
print("Consider installing: pip install attrs")
if "msgspec" not in available:
print("Consider installing: pip install msgspec")
# Strategy 3: Filter to only supported models
supported_models = []
for model in models:
try:
create(model) # Test individual model
supported_models.append(model)
except UnknownModelTypeError:
print(f"Skipping unsupported model: {model.__name__}")
if supported_models:
print(f"Creating diagram with {len(supported_models)} supported models")
return create(*supported_models)
else:
print("No supported models found")
return None
# Usage
diagram = create_diagram_with_fallback([Model1, Model2, UnsupportedModel])
if diagram:
diagram.draw("filtered_diagram.png")When erdantic cannot find support for your model types:
# Error: UnknownModelTypeError with attrs model
# Solution: pip install attrs
# Error: UnknownModelTypeError with msgspec model
# Solution: pip install msgspec
# Error: All models show as unsupported
# Solution: Check that models inherit from proper base classesWhen models contain forward references that cannot be resolved:
# Common causes:
# 1. Missing __future__ import: from __future__ import annotations
# 2. Circular imports between modules
# 3. Type hints referencing classes not yet defined
# 4. Missing imports for referenced types
# Solutions:
# 1. Add proper imports
# 2. Restructure to avoid circular dependencies
# 3. Use string literals for forward references
# 4. Ensure all referenced types are accessibleWhen using the CLI with module or class paths:
# Error: ModelOrModuleNotFoundError
# Common causes:
# 1. Typo in module/class path
# 2. Module not installed or not in PYTHONPATH
# 3. Class doesn't exist in specified module
# Solutions:
# 1. Verify spelling and case sensitivity
# 2. Check module installation: python -c "import mymodule"
# 3. Check class exists: python -c "from mymodule import MyClass"Install with Tessl CLI
npx tessl i tessl/pypi-erdantic