Skip to content

type_bridge.attribute.flags

flags

Flag system for TypeDB attribute annotations.

TypeNameCase

Bases: Enum

Type name case formatting options for Entity and Relation types.

Options

LOWERCASE: Convert class name to lowercase (default) Example: PersonName → personname CLASS_NAME: Keep class name as-is (PascalCase) Example: PersonName → PersonName SNAKE_CASE: Convert class name to snake_case Example: PersonName → person_name

TypeFlags dataclass

TypeFlags(name=None, abstract=False, base=False, case=CLASS_NAME)

Metadata flags for Entity and Relation classes.

Parameters:

Name Type Description Default
name str | None

TypeDB type name (if None, uses class name with case formatting)

None
abstract bool

Whether this is an abstract type

False
base bool

Whether this is a Python base class that should not appear in TypeDB schema

False
case TypeNameCase

Case formatting for auto-generated type names (default: CLASS_NAME)

CLASS_NAME
Example

class Person(Entity): flags = TypeFlags(name="person") name: Name

class PersonName(Entity): flags = TypeFlags() # → PersonName (default CLASS_NAME) name: Name

class PersonName(Entity): flags = TypeFlags(case=TypeNameCase.SNAKE_CASE) # → person_name name: Name

class AbstractPerson(Entity): flags = TypeFlags(abstract=True) name: Name

class BaseEntity(Entity): flags = TypeFlags(base=True) # Python base class only # Children skip this in TypeDB hierarchy

Initialize TypeFlags.

Parameters:

Name Type Description Default
name str | None

TypeDB type name (if None, uses class name with case formatting)

None
abstract bool

Whether this is an abstract type

False
base bool

Whether this is a Python base class that should not appear in TypeDB schema

False
case TypeNameCase

Case formatting for auto-generated type names (default: CLASS_NAME)

CLASS_NAME
Source code in type_bridge/attribute/flags.py
def __init__(
    self,
    name: str | None = None,
    abstract: bool = False,
    base: bool = False,
    case: TypeNameCase = TypeNameCase.CLASS_NAME,
):
    """Initialize TypeFlags.

    Args:
        name: TypeDB type name (if None, uses class name with case formatting)
        abstract: Whether this is an abstract type
        base: Whether this is a Python base class that should not appear in TypeDB schema
        case: Case formatting for auto-generated type names (default: CLASS_NAME)
    """
    self.name = name
    self.abstract = abstract
    self.base = base
    self.case = case

Card

Card(*args, min=None, max=None)

Cardinality marker for multi-value attribute ownership.

IMPORTANT: Card() should only be used with list[Type] annotations. For optional single values, use Optional[Type] instead.

Parameters:

Name Type Description Default
min int | None

Minimum cardinality (default: None, which means unspecified)

None
max int | None

Maximum cardinality (default: None, which means unbounded)

None

Examples:

tags: list[Tag] = Flag(Card(min=2)) # @card(2..) - at least two jobs: list[Job] = Flag(Card(1, 5)) # @card(1..5) - one to five ids: list[ID] = Flag(Key, Card(min=1)) # @key @card(1..)

INCORRECT - use Optional[Type] instead:

age: Age = Flag(Card(min=0, max=1)) # ❌ Wrong!

age: Optional[Age] # ✓ Correct

Initialize cardinality marker.

Supports both positional and keyword arguments: - Card(1, 5) → min=1, max=5 - Card(min=2) → min=2, max=None (unbounded) - Card(max=5) → min=0, max=5 (defaults min to 0) - Card(min=0, max=10) → min=0, max=10

Source code in type_bridge/attribute/flags.py
def __init__(self, *args: int, min: int | None = None, max: int | None = None):
    """Initialize cardinality marker.

    Supports both positional and keyword arguments:
    - Card(1, 5) → min=1, max=5
    - Card(min=2) → min=2, max=None (unbounded)
    - Card(max=5) → min=0, max=5 (defaults min to 0)
    - Card(min=0, max=10) → min=0, max=10
    """
    self.min: int | None = None
    self.max: int | None = None
    if args:
        # Positional arguments: Card(1, 5) or Card(2)
        if len(args) == 1:
            self.min = args[0]
            self.max = max  # Use keyword arg if provided
        elif len(args) == 2:
            self.min = args[0]
            self.max = args[1]
        else:
            raise ValueError("Card accepts at most 2 positional arguments")
    else:
        # Keyword arguments only
        # If only max is specified, default min to 0
        if min is None and max is not None:
            self.min = 0
            self.max = max
        else:
            self.min = min
            self.max = max

AttributeFlags dataclass

AttributeFlags(is_key=False, is_unique=False, card_min=None, card_max=None, has_explicit_card=False, name=None, case=None)

Metadata for attribute ownership and type configuration.

Represents TypeDB ownership annotations like @key, @card(min..max), @unique, and allows overriding the attribute type name with explicit name or case formatting.

Example

class Person(Entity): name: Name = Flag(Key) # @key (implies @card(1..1)) email: Email = Flag(Unique) # @unique @card(1..1) age: Optional[Age] # @card(0..1) - no Flag needed tags: list[Tag] = Flag(Card(min=2)) # @card(2..) jobs: list[Job] = Flag(Card(1, 5)) # @card(1..5)

Override attribute type name explicitly

class Name(String): flags = AttributeFlags(name="name")

Or use case formatting

class PersonName(String): flags = AttributeFlags(case=TypeNameCase.SNAKE_CASE) # -> person_name

to_typeql_annotations

to_typeql_annotations()

Convert to TypeQL annotations like @key, @card(0..5).

Rules: - @key implies @card(1..1), so never output @card with @key - @unique with @card(1..1) is redundant, so omit @card in that case - Otherwise, always output @card if cardinality is specified

Returns:

Type Description
list[str]

List of TypeQL annotation strings

Source code in type_bridge/attribute/flags.py
def to_typeql_annotations(self) -> list[str]:
    """Convert to TypeQL annotations like @key, @card(0..5).

    Rules:
    - @key implies @card(1..1), so never output @card with @key
    - @unique with @card(1..1) is redundant, so omit @card in that case
    - Otherwise, always output @card if cardinality is specified

    Returns:
        List of TypeQL annotation strings
    """
    from type_bridge.typeql.annotations import format_card_annotation

    annotations = []
    if self.is_key:
        annotations.append("@key")
    if self.is_unique:
        annotations.append("@unique")

    # Only output @card if:
    # 1. Not a @key (since @key always implies @card(1..1))
    # 2. Not (@unique with default @card(1..1))
    should_output_card = self.card_min is not None or self.card_max is not None

    if should_output_card and not self.is_key:
        # Check if it's @unique with default (1,1) - if so, omit @card
        is_default_card = self.card_min == 1 and self.card_max == 1
        if not (self.is_unique and is_default_card):
            card_annotation = format_card_annotation(self.card_min, self.card_max)
            if card_annotation:
                annotations.append(card_annotation)

    return annotations

format_type_name

format_type_name(class_name, case)

Format a class name according to the specified case style.

Parameters:

Name Type Description Default
class_name str

The Python class name

required
case TypeNameCase

The case formatting style to apply

required

Returns:

Type Description
str

The formatted type name

Examples:

>>> format_type_name("PersonName", TypeNameCase.LOWERCASE)
'personname'
>>> format_type_name("PersonName", TypeNameCase.CLASS_NAME)
'PersonName'
>>> format_type_name("PersonName", TypeNameCase.SNAKE_CASE)
'person_name'
Source code in type_bridge/attribute/flags.py
def format_type_name(class_name: str, case: TypeNameCase) -> str:
    """Format a class name according to the specified case style.

    Args:
        class_name: The Python class name
        case: The case formatting style to apply

    Returns:
        The formatted type name

    Examples:
        >>> format_type_name("PersonName", TypeNameCase.LOWERCASE)
        'personname'
        >>> format_type_name("PersonName", TypeNameCase.CLASS_NAME)
        'PersonName'
        >>> format_type_name("PersonName", TypeNameCase.SNAKE_CASE)
        'person_name'
    """
    if case == TypeNameCase.LOWERCASE:
        return class_name.lower()
    elif case == TypeNameCase.CLASS_NAME:
        return class_name
    elif case == TypeNameCase.SNAKE_CASE:
        return _to_snake_case(class_name)
    else:
        # Default to lowercase for unknown cases
        return class_name.lower()

Flag

Flag(*annotations)

Create attribute flags for Key, Unique, and Card markers.

Usage

field: Type = Flag(Key) # @key (implies @card(1..1)) field: Type = Flag(Unique) # @unique @card(1..1) field: list[Type] = Flag(Card(min=2)) # @card(2..) field: list[Type] = Flag(Card(1, 5)) # @card(1..5) field: Type = Flag(Key, Unique) # @key @unique field: list[Type] = Flag(Key, Card(min=1)) # @key @card(1..)

For optional single values, use Optional[Type] instead: field: Optional[Type] # @card(0..1) - no Flag needed

Parameters:

Name Type Description Default
*annotations Any

Variable number of Key, Unique, or Card marker instances

()

Returns:

Type Description
Annotated[Any, AttributeFlags]

AttributeFlags instance with the specified flags

Example

class Person(Entity): flags = TypeFlags(name="person") name: Name = Flag(Key) # @key (implies @card(1..1)) email: Email = Flag(Key, Unique) # @key @unique age: Optional[Age] # @card(0..1) tags: list[Tag] = Flag(Card(min=2)) # @card(2..) jobs: list[Job] = Flag(Card(1, 5)) # @card(1..5)

Source code in type_bridge/attribute/flags.py
def Flag(*annotations: Any) -> Annotated[Any, AttributeFlags]:
    """Create attribute flags for Key, Unique, and Card markers.

    Usage:
        field: Type = Flag(Key)                   # @key (implies @card(1..1))
        field: Type = Flag(Unique)                # @unique @card(1..1)
        field: list[Type] = Flag(Card(min=2))     # @card(2..)
        field: list[Type] = Flag(Card(1, 5))      # @card(1..5)
        field: Type = Flag(Key, Unique)           # @key @unique
        field: list[Type] = Flag(Key, Card(min=1)) # @key @card(1..)

    For optional single values, use Optional[Type] instead:
        field: Optional[Type]  # @card(0..1) - no Flag needed

    Args:
        *annotations: Variable number of Key, Unique, or Card marker instances

    Returns:
        AttributeFlags instance with the specified flags

    Example:
        class Person(Entity):
            flags = TypeFlags(name="person")
            name: Name = Flag(Key)                    # @key (implies @card(1..1))
            email: Email = Flag(Key, Unique)          # @key @unique
            age: Optional[Age]                        # @card(0..1)
            tags: list[Tag] = Flag(Card(min=2))       # @card(2..)
            jobs: list[Job] = Flag(Card(1, 5))        # @card(1..5)
    """
    flags = AttributeFlags()
    has_card = False

    for ann in annotations:
        if ann is Key:
            flags.is_key = True
        elif ann is Unique:
            flags.is_unique = True
        elif isinstance(ann, Card):
            # Extract cardinality from Card instance
            flags.card_min = ann.min
            flags.card_max = ann.max
            flags.has_explicit_card = True
            has_card = True

    # If Key was used but no Card, set default card(1,1)
    if flags.is_key and not has_card:
        flags.card_min = 1
        flags.card_max = 1

    return flags