erdantic.base¶
Attributes¶
model_adapter_registry: Dict[str, Type[erdantic.base.Model]]
¶
Registry of concrete Model
adapter subclasses. A concrete Model
subclass must be registered for it to be available to the diagram creation workflow.
Classes¶
Field (ABC, Generic)
¶
Abstract base class that adapts a field object of a data model class to work with erdantic. Concrete implementations should subclass and implement abstract methods.
Attributes:
Name | Type | Description |
---|---|---|
field |
FT |
Field object on a data model class associated with this adapter |
Source code in erdantic/base.py
class Field(ABC, Generic[FT]):
"""Abstract base class that adapts a field object of a data model class to work with erdantic.
Concrete implementations should subclass and implement abstract methods.
Attributes:
field (FT): Field object on a data model class associated with this adapter
"""
@abstractmethod
def __init__(self, field: FT):
"""Initialize Field adapter instance.
Args:
field: Field object to associate with this adapter instance
"""
self.field: Final[FT] = field
@property
@abstractmethod
def name(self) -> str: # pragma: no cover
"""Name of this field on the parent data model."""
@property
@abstractmethod
def type_obj(self) -> Union[type, GenericAlias]:
"""Python type object for this field."""
pass
@abstractmethod
def is_many(self) -> bool: # pragma: no cover
"""Check whether this field represents a one-to-one or one-to-many relationship.
Returns:
bool: True if one-to-many relationship, else False.
"""
pass
@abstractmethod
def is_nullable(self) -> bool: # pragma: no cover
"""Check whether this field is nullable, i.e., can be `None`.
Returns:
bool: True if nullable, else False.
"""
pass
@property
def type_name(self) -> str: # pragma: no cover
"""String representation of the Python type annotation for this field."""
return repr_type(self.type_obj)
def dot_row(self) -> str:
"""Returns the DOT language "HTML-like" syntax specification of a row detailing this field
that is part of a table describing the field's parent data model. It is used as part the
`label` attribute of data model's node in the graph's DOT representation.
Returns:
str: DOT language for table row
"""
return _row_template.format(name=self.name, type_name=self.type_name)
def __eq__(self, other: Any) -> bool:
return isinstance(other, type(self)) and hash(self) == hash(other)
def __hash__(self) -> int:
return id(self.field)
def __repr__(self) -> str:
return f"<{type(self).__name__}: '{self.name}', {self.type_name}>"
Attributes¶
name: str
property
readonly
¶
Name of this field on the parent data model.
type_name: str
property
readonly
¶
String representation of the Python type annotation for this field.
type_obj: Union[type, _GenericAlias]
property
readonly
¶
Python type object for this field.
Methods¶
__init__(self, field: +FT)
special
¶
Initialize Field adapter instance.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
field |
+FT |
Field object to associate with this adapter instance |
required |
Source code in erdantic/base.py
@abstractmethod
def __init__(self, field: FT):
"""Initialize Field adapter instance.
Args:
field: Field object to associate with this adapter instance
"""
self.field: Final[FT] = field
dot_row(self) -> str
¶
Returns the DOT language "HTML-like" syntax specification of a row detailing this field
that is part of a table describing the field's parent data model. It is used as part the
label
attribute of data model's node in the graph's DOT representation.
Returns:
Type | Description |
---|---|
str |
DOT language for table row |
Source code in erdantic/base.py
def dot_row(self) -> str:
"""Returns the DOT language "HTML-like" syntax specification of a row detailing this field
that is part of a table describing the field's parent data model. It is used as part the
`label` attribute of data model's node in the graph's DOT representation.
Returns:
str: DOT language for table row
"""
return _row_template.format(name=self.name, type_name=self.type_name)
is_many(self) -> bool
¶
Check whether this field represents a one-to-one or one-to-many relationship.
Returns:
Type | Description |
---|---|
bool |
True if one-to-many relationship, else False. |
Source code in erdantic/base.py
@abstractmethod
def is_many(self) -> bool: # pragma: no cover
"""Check whether this field represents a one-to-one or one-to-many relationship.
Returns:
bool: True if one-to-many relationship, else False.
"""
pass
is_nullable(self) -> bool
¶
Check whether this field is nullable, i.e., can be None
.
Returns:
Type | Description |
---|---|
bool |
True if nullable, else False. |
Source code in erdantic/base.py
@abstractmethod
def is_nullable(self) -> bool: # pragma: no cover
"""Check whether this field is nullable, i.e., can be `None`.
Returns:
bool: True if nullable, else False.
"""
pass
Model (ABC, Generic)
¶
Abstract base class that adapts a data model class to work with erdantic. Instances represent a node in our entity relationship diagram graph. Concrete implementations should subclass and implement abstract methods.
Attributes:
Name | Type | Description |
---|---|---|
model |
MT |
Data model class associated with this adapter |
forward_ref_help |
Optional[str] |
Instructions for how to resolve an unevaluated forward reference in a field's type declaration. |
Source code in erdantic/base.py
class Model(ABC, Generic[MT]):
"""Abstract base class that adapts a data model class to work with erdantic. Instances
represent a node in our entity relationship diagram graph. Concrete implementations should
subclass and implement abstract methods.
Attributes:
model (MT): Data model class associated with this adapter
forward_ref_help (Optional[str]): Instructions for how to resolve an unevaluated forward
reference in a field's type declaration.
"""
forward_ref_help: Optional[str] = None
@abstractmethod
def __init__(self, model: MT):
"""Initialize model adapter instance.
Args:
model: Data model class to associate with this adapter instance
"""
self.model: Final[MT] = model
@property
@abstractmethod
def fields(self) -> List[Field]: # pragma: no cover
"""List of fields defined on this data model."""
pass
@staticmethod
@abstractmethod
def is_model_type(obj: Any) -> bool: # pragma: no cover
"""Check if object is the type of data model class that this model adapter works with."""
pass
@property
def name(self) -> str: # pragma: no cover
"""Name of this data model."""
return self.model.__name__
@property
def docstring(self) -> str:
"""Docstring for this data model."""
out = f"{self.model.__module__}.{self.model.__qualname__}"
docstring = inspect.getdoc(self.model)
if docstring:
out += "\n\n" + docstring + "\n"
return out
@property
def key(self) -> str:
"""Human-readable unique identifier for this data model. Should be stable across
sessions."""
return f"{self.model.__module__}.{self.model.__qualname__}"
def dot_label(self) -> str:
"""Returns the DOT language "HTML-like" syntax specification of a table for this data
model. It is used as the `label` attribute of data model's node in the graph's DOT
representation.
Returns:
str: DOT language for table
"""
rows = "\n".join(field.dot_row() for field in self.fields)
return _table_template.format(name=self.name, rows=rows).replace("\n", "")
def __eq__(self, other) -> bool:
return isinstance(other, type(self)) and hash(self) == hash(other)
def __hash__(self) -> int:
return hash(self.key)
def __lt__(self, other) -> bool:
if isinstance(other, Model):
return self.key < other.key
return NotImplemented
def __repr__(self) -> str:
return f"{type(self).__name__}({self.name})"
Attributes¶
docstring: str
property
readonly
¶
Docstring for this data model.
fields: List[erdantic.base.Field]
property
readonly
¶
List of fields defined on this data model.
forward_ref_help: Optional[str]
¶
key: str
property
readonly
¶
Human-readable unique identifier for this data model. Should be stable across sessions.
name: str
property
readonly
¶
Name of this data model.
Methods¶
__init__(self, model: +MT)
special
¶
Initialize model adapter instance.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
model |
+MT |
Data model class to associate with this adapter instance |
required |
Source code in erdantic/base.py
@abstractmethod
def __init__(self, model: MT):
"""Initialize model adapter instance.
Args:
model: Data model class to associate with this adapter instance
"""
self.model: Final[MT] = model
dot_label(self) -> str
¶
Returns the DOT language "HTML-like" syntax specification of a table for this data
model. It is used as the label
attribute of data model's node in the graph's DOT
representation.
Returns:
Type | Description |
---|---|
str |
DOT language for table |
Source code in erdantic/base.py
def dot_label(self) -> str:
"""Returns the DOT language "HTML-like" syntax specification of a table for this data
model. It is used as the `label` attribute of data model's node in the graph's DOT
representation.
Returns:
str: DOT language for table
"""
rows = "\n".join(field.dot_row() for field in self.fields)
return _table_template.format(name=self.name, rows=rows).replace("\n", "")
is_model_type(obj: Any) -> bool
staticmethod
¶
Check if object is the type of data model class that this model adapter works with.
Source code in erdantic/base.py
@staticmethod
@abstractmethod
def is_model_type(obj: Any) -> bool: # pragma: no cover
"""Check if object is the type of data model class that this model adapter works with."""
pass
Functions¶
register_model_adapter(type_name: str) -> Callable[[Type[erdantic.base.Model]], Type[erdantic.base.Model]]
¶
Create decorator to register a concrete Model
adapter subclass
that will be identified under the key type_name
. A concrete Model
subclass must be
registered for it to be available to the diagram creation workflow.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
type_name |
str |
Key used to identify concrete |
required |
Returns:
Type | Description |
---|---|
Callable[[Type[Model]], Type[Model]] |
A registration decorator for a concrete |
Source code in erdantic/base.py
def register_model_adapter(type_name: str) -> Callable[[Type[Model]], Type[Model]]:
"""Create decorator to register a concrete [`Model`][erdantic.base.Model] adapter subclass
that will be identified under the key `type_name`. A concrete `Model` subclass must be
registered for it to be available to the diagram creation workflow.
Args:
type_name (str): Key used to identify concrete `Model` adapter subclass
Returns:
Callable[[Type[Model]], Type[Model]]: A registration decorator for a concrete `Model`
adapter subclass
"""
def decorator(cls: type) -> type:
global model_adapter_registry
if not issubclass(cls, Model):
raise InvalidModelAdapterError(
"Only subclasses of erdantic.base.Model can be "
"registered as erdantic model adapters."
)
model_adapter_registry[type_name] = cls
return cls
return decorator