Skip to content

List Provider API

AniBridge list provider base classes package.

ListEntity dataclass

Bases: ABC

Base class for list entities.

Source code in .venv/lib/python3.14/site-packages/anibridge/list/base.py
@dataclass(slots=True, eq=False)
class ListEntity[ProviderT: ListProvider](ABC):
    """Base class for list entities."""

    _provider: ProviderT = field(repr=False, compare=False)
    _key: str
    _title: str = field(compare=False)

    @property
    def key(self) -> str:
        """Return the unique key for this entity."""
        return self._key

    def provider(self) -> ProviderT:
        """Return the provider associated with this entity."""
        return self._provider

    @property
    def title(self) -> str:
        """Return the title of this entity."""
        return self._title

    def __hash__(self) -> int:
        """Compute a hash based on the provider namespace, class name, and key."""
        return hash((self._provider.NAMESPACE, self.__class__.__name__, self.key))

    def __eq__(self, other: object) -> bool:
        """Check equality with another ListEntity based on provider and key."""
        if other.__class__ is not self.__class__:
            return NotImplemented
        other_ent = cast(ListEntity, other)
        return (
            self._provider.NAMESPACE == other_ent._provider.NAMESPACE
            and self.key == other_ent.key
        )

    def __repr__(self) -> str:
        """Return a string representation of the ListEntity."""
        return (
            f"<{self.__class__.__name__}:{self._provider.NAMESPACE}:{self.key}:"
            f"{self.title[:32]}>"
        )

key property

Return the unique key for this entity.

title property

Return the title of this entity.

__eq__(other)

Check equality with another ListEntity based on provider and key.

Source code in .venv/lib/python3.14/site-packages/anibridge/list/base.py
def __eq__(self, other: object) -> bool:
    """Check equality with another ListEntity based on provider and key."""
    if other.__class__ is not self.__class__:
        return NotImplemented
    other_ent = cast(ListEntity, other)
    return (
        self._provider.NAMESPACE == other_ent._provider.NAMESPACE
        and self.key == other_ent.key
    )

__hash__()

Compute a hash based on the provider namespace, class name, and key.

Source code in .venv/lib/python3.14/site-packages/anibridge/list/base.py
def __hash__(self) -> int:
    """Compute a hash based on the provider namespace, class name, and key."""
    return hash((self._provider.NAMESPACE, self.__class__.__name__, self.key))

__repr__()

Return a string representation of the ListEntity.

Source code in .venv/lib/python3.14/site-packages/anibridge/list/base.py
def __repr__(self) -> str:
    """Return a string representation of the ListEntity."""
    return (
        f"<{self.__class__.__name__}:{self._provider.NAMESPACE}:{self.key}:"
        f"{self.title[:32]}>"
    )

provider()

Return the provider associated with this entity.

Source code in .venv/lib/python3.14/site-packages/anibridge/list/base.py
def provider(self) -> ProviderT:
    """Return the provider associated with this entity."""
    return self._provider

ListEntry dataclass

Bases: ListEntity[ProviderT], ABC

Base class for list entries for a given media item.

Source code in .venv/lib/python3.14/site-packages/anibridge/list/base.py
class ListEntry[ProviderT: ListProvider](ListEntity[ProviderT], ABC):
    """Base class for list entries for a given media item."""

    @property
    @abstractmethod
    def progress(self) -> int | None:
        """Progress integer (e.g., episodes watched)."""
        ...

    @progress.setter
    @abstractmethod
    def progress(self, value: int | None) -> None: ...

    @property
    @abstractmethod
    def repeats(self) -> int | None:
        """Repeat count for the entry."""
        ...

    @repeats.setter
    @abstractmethod
    def repeats(self, value: int | None) -> None: ...

    @property
    @abstractmethod
    def review(self) -> str | None:
        """User review text, if any."""
        ...

    @review.setter
    @abstractmethod
    def review(self, value: str | None) -> None: ...

    @property
    @abstractmethod
    def status(self) -> ListStatus | None:
        """Watch status for the entry."""
        ...

    @status.setter
    @abstractmethod
    def status(self, value: ListStatus | None) -> None:
        """Update the status for the entry."""
        ...

    @property
    @abstractmethod
    def user_rating(self) -> int | None:
        """User rating on a 0-100 scale."""
        ...

    @user_rating.setter
    @abstractmethod
    def user_rating(self, value: int | None) -> None:
        """Update the user rating for the entry."""
        ...

    @property
    @abstractmethod
    def started_at(self) -> datetime | None:
        """Timestamp when the user started the entry (timezone-aware)."""
        ...

    @started_at.setter
    @abstractmethod
    def started_at(self, value: datetime | None) -> None:
        """Update the started_at timestamp for the entry."""
        ...

    @property
    @abstractmethod
    def finished_at(self) -> datetime | None:
        """Timestamp when the user first completed the entry (timezone-aware)."""
        ...

    @finished_at.setter
    @abstractmethod
    def finished_at(self, value: datetime | None) -> None:
        """Update the finished_at timestamp for the entry."""
        ...

    @abstractmethod
    def media(self) -> ListMedia[ProviderT]:
        """Get the media item associated with this entry.

        For an efficient implementation, this should return a cached media item if
        possible, rather than fetching it anew each time.

        Returns:
            ListMedia: The media item associated with this entry.
        """
        ...

finished_at abstractmethod property writable

Timestamp when the user first completed the entry (timezone-aware).

key property

Return the unique key for this entity.

progress abstractmethod property writable

Progress integer (e.g., episodes watched).

repeats abstractmethod property writable

Repeat count for the entry.

review abstractmethod property writable

User review text, if any.

started_at abstractmethod property writable

Timestamp when the user started the entry (timezone-aware).

status abstractmethod property writable

Watch status for the entry.

title property

Return the title of this entity.

user_rating abstractmethod property writable

User rating on a 0-100 scale.

__eq__(other)

Check equality with another ListEntity based on provider and key.

Source code in .venv/lib/python3.14/site-packages/anibridge/list/base.py
def __eq__(self, other: object) -> bool:
    """Check equality with another ListEntity based on provider and key."""
    if other.__class__ is not self.__class__:
        return NotImplemented
    other_ent = cast(ListEntity, other)
    return (
        self._provider.NAMESPACE == other_ent._provider.NAMESPACE
        and self.key == other_ent.key
    )

__hash__()

Compute a hash based on the provider namespace, class name, and key.

Source code in .venv/lib/python3.14/site-packages/anibridge/list/base.py
def __hash__(self) -> int:
    """Compute a hash based on the provider namespace, class name, and key."""
    return hash((self._provider.NAMESPACE, self.__class__.__name__, self.key))

__repr__()

Return a string representation of the ListEntity.

Source code in .venv/lib/python3.14/site-packages/anibridge/list/base.py
def __repr__(self) -> str:
    """Return a string representation of the ListEntity."""
    return (
        f"<{self.__class__.__name__}:{self._provider.NAMESPACE}:{self.key}:"
        f"{self.title[:32]}>"
    )

media() abstractmethod

Get the media item associated with this entry.

For an efficient implementation, this should return a cached media item if possible, rather than fetching it anew each time.

Returns:

Name Type Description
ListMedia ListMedia[ProviderT]

The media item associated with this entry.

Source code in .venv/lib/python3.14/site-packages/anibridge/list/base.py
@abstractmethod
def media(self) -> ListMedia[ProviderT]:
    """Get the media item associated with this entry.

    For an efficient implementation, this should return a cached media item if
    possible, rather than fetching it anew each time.

    Returns:
        ListMedia: The media item associated with this entry.
    """
    ...

provider()

Return the provider associated with this entity.

Source code in .venv/lib/python3.14/site-packages/anibridge/list/base.py
def provider(self) -> ProviderT:
    """Return the provider associated with this entity."""
    return self._provider

ListMedia dataclass

Bases: ListEntity[ProviderT], ABC

Base class for media items in a provider list.

Subclasses should call the base constructor and may override properties if they need custom behaviour; defaults store values provided at init time.

Source code in .venv/lib/python3.14/site-packages/anibridge/list/base.py
class ListMedia[ProviderT: ListProvider](ListEntity[ProviderT], ABC):
    """Base class for media items in a provider list.

    Subclasses should call the base constructor and may override properties if
    they need custom behaviour; defaults store values provided at init time.
    """

    @property
    def external_url(self) -> str | None:
        """URL to the provider's media item, if available."""
        return None

    @property
    def labels(self) -> Sequence[str]:
        """Display labels such as season or release year."""
        return ()

    @property
    @abstractmethod
    def media_type(self) -> ListMediaType:
        """Type of media (e.g., TV, MOVIE)."""
        ...

    @property
    def poster_image(self) -> str | None:
        """Poster or cover image URL, if provided by the provider."""
        return None

    @property
    @abstractmethod
    def total_units(self) -> int | None:
        """Total number of units (episodes/chapters) for the media."""
        ...

external_url property

URL to the provider's media item, if available.

key property

Return the unique key for this entity.

labels property

Display labels such as season or release year.

media_type abstractmethod property

Type of media (e.g., TV, MOVIE).

poster_image property

Poster or cover image URL, if provided by the provider.

title property

Return the title of this entity.

total_units abstractmethod property

Total number of units (episodes/chapters) for the media.

__eq__(other)

Check equality with another ListEntity based on provider and key.

Source code in .venv/lib/python3.14/site-packages/anibridge/list/base.py
def __eq__(self, other: object) -> bool:
    """Check equality with another ListEntity based on provider and key."""
    if other.__class__ is not self.__class__:
        return NotImplemented
    other_ent = cast(ListEntity, other)
    return (
        self._provider.NAMESPACE == other_ent._provider.NAMESPACE
        and self.key == other_ent.key
    )

__hash__()

Compute a hash based on the provider namespace, class name, and key.

Source code in .venv/lib/python3.14/site-packages/anibridge/list/base.py
def __hash__(self) -> int:
    """Compute a hash based on the provider namespace, class name, and key."""
    return hash((self._provider.NAMESPACE, self.__class__.__name__, self.key))

__repr__()

Return a string representation of the ListEntity.

Source code in .venv/lib/python3.14/site-packages/anibridge/list/base.py
def __repr__(self) -> str:
    """Return a string representation of the ListEntity."""
    return (
        f"<{self.__class__.__name__}:{self._provider.NAMESPACE}:{self.key}:"
        f"{self.title[:32]}>"
    )

provider()

Return the provider associated with this entity.

Source code in .venv/lib/python3.14/site-packages/anibridge/list/base.py
def provider(self) -> ProviderT:
    """Return the provider associated with this entity."""
    return self._provider

ListMediaType

Bases: StrEnum

Supported media types in a list.

Source code in .venv/lib/python3.14/site-packages/anibridge/list/base.py
class ListMediaType(StrEnum):
    """Supported media types in a list."""

    TV = "TV"
    MOVIE = "MOVIE"

ListProvider

Bases: ABC

Abstract base provider that exposes a user media list.

Source code in .venv/lib/python3.14/site-packages/anibridge/list/base.py
class ListProvider(ABC):
    """Abstract base provider that exposes a user media list."""

    NAMESPACE: ClassVar[str]
    MAPPING_PROVIDERS: ClassVar[frozenset[str]]

    def __init__(self, *, config: dict | None = None) -> None:
        """Initialize the provider with optional configuration.

        Args:
            config (dict | None): Any configuration options that were detected with the
                provider's namespace as a prefix.
        """
        return None

    async def initialize(self) -> None:
        """Asynchronous initialization hook.

        Put any async logic that should be run after construction here.
        """
        return None

    async def backup_list(self) -> str:
        """Backup the entire list from the provider.

        It is up to the implementation to decide the format of the backup data. Whatever
        format, it should be serializable/deserializable as a string.

        Backup capabilities are optional. If a provider does not support backups, this
        method should raise a NotImplementedError.

        Returns:
            str: A serialized string representation of all list entries.
        """
        raise NotImplementedError(f"{self.NAMESPACE} does not support backup_list()")

    async def clear_cache(self) -> None:
        """Clear any cached data held by the provider.

        For more efficient implementations, it is a good idea to cache data
        fetched from the provider to minimize network requests. AniBridge uses
        this method occasionally to clear such caches.
        """
        return None

    async def close(self) -> None:
        """Close the provider and release resources."""
        return None

    @abstractmethod
    async def delete_entry(self, key: str) -> None:
        """Delete a list entry by its media key.

        Args:
            key (str): The unique key of the media item to delete.
        """
        ...

    @abstractmethod
    async def resolve_mapping_descriptors(
        self, descriptors: Sequence[MappingDescriptor]
    ) -> Sequence[ListTarget]:
        """Resolve mapping descriptors into list media keys.

        Implementations should return one ListTarget for each mapping descriptor that
        can be resolved into a list media key. Multiple list keys may be returned for a
        single descriptor.

        Args:
            descriptors (Sequence[MappingDescriptor]): Mapping descriptors to resolve.

        Returns:
            Sequence[ListTarget]: The resolved descriptor/key pairs.
        """
        ...

    @abstractmethod
    async def get_entry(self, key: str) -> ListEntry[Self] | None:
        """Retrieve a list entry by its media key.

        Only return None if the media item does not exist on the provider.

        Args:
            key (str): The unique key of the media item to retrieve.

        Returns:
            ListEntry | None: The list entry if found, otherwise None.
        """
        ...

    async def get_entries_batch(
        self, keys: Sequence[str]
    ) -> Sequence[ListEntry[Self] | None]:
        """Retrieve multiple list entries by their media keys.

        The order of the returned sequence must match the order of the input keys.

        Args:
            keys (Sequence[str]): The unique keys of the media items to retrieve.

        Returns:
            Sequence[ListEntry | None]: A sequence of list entries, with None for any
                not found.
        """
        entries: list[ListEntry[Self] | None] = []
        for key in keys:
            try:
                entry = await self.get_entry(key)
            except Exception:
                entry = None
            entries.append(entry)
        return entries

    async def restore_list(self, backup: str) -> None:
        """Restore list entries from a backup string.

        The format of the backup string is determined by the implementation
        of `backup_list`.

        Args:
            backup (str): The serialized string representation of the list entries.
        """
        return None

    async def search(self, query: str) -> Sequence[ListEntry[Self]]:
        """Search the provider for entries matching the query.

        Args:
            query (str): The search query string.

        Returns:
            Sequence[ListEntry]: A sequence of matching list entries.
        """
        return []

    @abstractmethod
    async def update_entry(
        self, key: str, entry: ListEntry[Self]
    ) -> ListEntry[Self] | None:
        """Update a list entry with new information.

        Args:
            key (str): The unique key of the media item to update.
            entry (ListEntry): The updated entry information.

        Returns:
            ListEntry | None: The updated list entry, or None if the update failed.
        """
        ...

    async def update_entries_batch(
        self, entries: Sequence[ListEntry[Self]]
    ) -> Sequence[ListEntry[Self] | None]:
        """Update multiple list entries in a single operation.

        Args:
            entries (Sequence[ListEntry]): The list entries to update.

        Returns:
            Sequence[ListEntry | None]: A sequence of updated list entries, with None
                for any that could not be updated.
        """
        updated_entries: list[ListEntry[Self] | None] = []
        for entry in entries:
            try:
                updated_entry = await self.update_entry(entry.media().key, entry)
            except Exception:
                updated_entry = None
            updated_entries.append(updated_entry)
        return updated_entries

    @abstractmethod
    def user(self) -> ListUser | None:
        """Return the associated user object, if any.

        Returns:
            User | None: The associated user object, if any.
        """
        ...

__init__(*, config=None)

Initialize the provider with optional configuration.

Parameters:

Name Type Description Default
config dict | None

Any configuration options that were detected with the provider's namespace as a prefix.

None
Source code in .venv/lib/python3.14/site-packages/anibridge/list/base.py
def __init__(self, *, config: dict | None = None) -> None:
    """Initialize the provider with optional configuration.

    Args:
        config (dict | None): Any configuration options that were detected with the
            provider's namespace as a prefix.
    """
    return None

backup_list() async

Backup the entire list from the provider.

It is up to the implementation to decide the format of the backup data. Whatever format, it should be serializable/deserializable as a string.

Backup capabilities are optional. If a provider does not support backups, this method should raise a NotImplementedError.

Returns:

Name Type Description
str str

A serialized string representation of all list entries.

Source code in .venv/lib/python3.14/site-packages/anibridge/list/base.py
async def backup_list(self) -> str:
    """Backup the entire list from the provider.

    It is up to the implementation to decide the format of the backup data. Whatever
    format, it should be serializable/deserializable as a string.

    Backup capabilities are optional. If a provider does not support backups, this
    method should raise a NotImplementedError.

    Returns:
        str: A serialized string representation of all list entries.
    """
    raise NotImplementedError(f"{self.NAMESPACE} does not support backup_list()")

clear_cache() async

Clear any cached data held by the provider.

For more efficient implementations, it is a good idea to cache data fetched from the provider to minimize network requests. AniBridge uses this method occasionally to clear such caches.

Source code in .venv/lib/python3.14/site-packages/anibridge/list/base.py
async def clear_cache(self) -> None:
    """Clear any cached data held by the provider.

    For more efficient implementations, it is a good idea to cache data
    fetched from the provider to minimize network requests. AniBridge uses
    this method occasionally to clear such caches.
    """
    return None

close() async

Close the provider and release resources.

Source code in .venv/lib/python3.14/site-packages/anibridge/list/base.py
async def close(self) -> None:
    """Close the provider and release resources."""
    return None

delete_entry(key) abstractmethod async

Delete a list entry by its media key.

Parameters:

Name Type Description Default
key str

The unique key of the media item to delete.

required
Source code in .venv/lib/python3.14/site-packages/anibridge/list/base.py
@abstractmethod
async def delete_entry(self, key: str) -> None:
    """Delete a list entry by its media key.

    Args:
        key (str): The unique key of the media item to delete.
    """
    ...

get_entries_batch(keys) async

Retrieve multiple list entries by their media keys.

The order of the returned sequence must match the order of the input keys.

Parameters:

Name Type Description Default
keys Sequence[str]

The unique keys of the media items to retrieve.

required

Returns:

Type Description
Sequence[ListEntry[Self] | None]

Sequence[ListEntry | None]: A sequence of list entries, with None for any not found.

Source code in .venv/lib/python3.14/site-packages/anibridge/list/base.py
async def get_entries_batch(
    self, keys: Sequence[str]
) -> Sequence[ListEntry[Self] | None]:
    """Retrieve multiple list entries by their media keys.

    The order of the returned sequence must match the order of the input keys.

    Args:
        keys (Sequence[str]): The unique keys of the media items to retrieve.

    Returns:
        Sequence[ListEntry | None]: A sequence of list entries, with None for any
            not found.
    """
    entries: list[ListEntry[Self] | None] = []
    for key in keys:
        try:
            entry = await self.get_entry(key)
        except Exception:
            entry = None
        entries.append(entry)
    return entries

get_entry(key) abstractmethod async

Retrieve a list entry by its media key.

Only return None if the media item does not exist on the provider.

Parameters:

Name Type Description Default
key str

The unique key of the media item to retrieve.

required

Returns:

Type Description
ListEntry[Self] | None

ListEntry | None: The list entry if found, otherwise None.

Source code in .venv/lib/python3.14/site-packages/anibridge/list/base.py
@abstractmethod
async def get_entry(self, key: str) -> ListEntry[Self] | None:
    """Retrieve a list entry by its media key.

    Only return None if the media item does not exist on the provider.

    Args:
        key (str): The unique key of the media item to retrieve.

    Returns:
        ListEntry | None: The list entry if found, otherwise None.
    """
    ...

initialize() async

Asynchronous initialization hook.

Put any async logic that should be run after construction here.

Source code in .venv/lib/python3.14/site-packages/anibridge/list/base.py
async def initialize(self) -> None:
    """Asynchronous initialization hook.

    Put any async logic that should be run after construction here.
    """
    return None

resolve_mapping_descriptors(descriptors) abstractmethod async

Resolve mapping descriptors into list media keys.

Implementations should return one ListTarget for each mapping descriptor that can be resolved into a list media key. Multiple list keys may be returned for a single descriptor.

Parameters:

Name Type Description Default
descriptors Sequence[MappingDescriptor]

Mapping descriptors to resolve.

required

Returns:

Type Description
Sequence[ListTarget]

Sequence[ListTarget]: The resolved descriptor/key pairs.

Source code in .venv/lib/python3.14/site-packages/anibridge/list/base.py
@abstractmethod
async def resolve_mapping_descriptors(
    self, descriptors: Sequence[MappingDescriptor]
) -> Sequence[ListTarget]:
    """Resolve mapping descriptors into list media keys.

    Implementations should return one ListTarget for each mapping descriptor that
    can be resolved into a list media key. Multiple list keys may be returned for a
    single descriptor.

    Args:
        descriptors (Sequence[MappingDescriptor]): Mapping descriptors to resolve.

    Returns:
        Sequence[ListTarget]: The resolved descriptor/key pairs.
    """
    ...

restore_list(backup) async

Restore list entries from a backup string.

The format of the backup string is determined by the implementation of backup_list.

Parameters:

Name Type Description Default
backup str

The serialized string representation of the list entries.

required
Source code in .venv/lib/python3.14/site-packages/anibridge/list/base.py
async def restore_list(self, backup: str) -> None:
    """Restore list entries from a backup string.

    The format of the backup string is determined by the implementation
    of `backup_list`.

    Args:
        backup (str): The serialized string representation of the list entries.
    """
    return None

search(query) async

Search the provider for entries matching the query.

Parameters:

Name Type Description Default
query str

The search query string.

required

Returns:

Type Description
Sequence[ListEntry[Self]]

Sequence[ListEntry]: A sequence of matching list entries.

Source code in .venv/lib/python3.14/site-packages/anibridge/list/base.py
async def search(self, query: str) -> Sequence[ListEntry[Self]]:
    """Search the provider for entries matching the query.

    Args:
        query (str): The search query string.

    Returns:
        Sequence[ListEntry]: A sequence of matching list entries.
    """
    return []

update_entries_batch(entries) async

Update multiple list entries in a single operation.

Parameters:

Name Type Description Default
entries Sequence[ListEntry]

The list entries to update.

required

Returns:

Type Description
Sequence[ListEntry[Self] | None]

Sequence[ListEntry | None]: A sequence of updated list entries, with None for any that could not be updated.

Source code in .venv/lib/python3.14/site-packages/anibridge/list/base.py
async def update_entries_batch(
    self, entries: Sequence[ListEntry[Self]]
) -> Sequence[ListEntry[Self] | None]:
    """Update multiple list entries in a single operation.

    Args:
        entries (Sequence[ListEntry]): The list entries to update.

    Returns:
        Sequence[ListEntry | None]: A sequence of updated list entries, with None
            for any that could not be updated.
    """
    updated_entries: list[ListEntry[Self] | None] = []
    for entry in entries:
        try:
            updated_entry = await self.update_entry(entry.media().key, entry)
        except Exception:
            updated_entry = None
        updated_entries.append(updated_entry)
    return updated_entries

update_entry(key, entry) abstractmethod async

Update a list entry with new information.

Parameters:

Name Type Description Default
key str

The unique key of the media item to update.

required
entry ListEntry

The updated entry information.

required

Returns:

Type Description
ListEntry[Self] | None

ListEntry | None: The updated list entry, or None if the update failed.

Source code in .venv/lib/python3.14/site-packages/anibridge/list/base.py
@abstractmethod
async def update_entry(
    self, key: str, entry: ListEntry[Self]
) -> ListEntry[Self] | None:
    """Update a list entry with new information.

    Args:
        key (str): The unique key of the media item to update.
        entry (ListEntry): The updated entry information.

    Returns:
        ListEntry | None: The updated list entry, or None if the update failed.
    """
    ...

user() abstractmethod

Return the associated user object, if any.

Returns:

Type Description
ListUser | None

User | None: The associated user object, if any.

Source code in .venv/lib/python3.14/site-packages/anibridge/list/base.py
@abstractmethod
def user(self) -> ListUser | None:
    """Return the associated user object, if any.

    Returns:
        User | None: The associated user object, if any.
    """
    ...

ListProviderRegistry

Registry for ListProvider implementations.

Source code in .venv/lib/python3.14/site-packages/anibridge/list/registry.py
class ListProviderRegistry:
    """Registry for `ListProvider` implementations."""

    def __init__(self) -> None:
        """Initialize an empty registry."""
        self._providers: dict[str, type[ListProvider]] = {}

    def clear(self) -> None:
        """Remove all provider registrations."""
        self._providers.clear()

    def create(self, namespace: str, *, config: dict | None = None) -> ListProvider:
        """Instantiate the provider registered under `namespace`.

        Args:
            namespace (str): The namespace identifier to create.
            config (dict | None): Optional configuration dictionary to pass to the
                provider constructor.

        Returns:
            ListProvider: An instance of the registered provider.
        """
        provider_cls = self.get(namespace)
        return provider_cls(config=config)

    def get(self, namespace: str) -> type[ListProvider]:
        """Return the provider class registered under `namespace`.

        Args:
            namespace (str): The namespace identifier to look up.

        Returns:
            type[ListProvider]: The registered provider class.
        """
        try:
            return self._providers[namespace]
        except KeyError as exc:
            raise LookupError(
                f"No provider registered for namespace '{namespace}'."
            ) from exc

    def namespaces(self) -> tuple[str, ...]:
        """Return a tuple of registered namespace identifiers.

        Returns:
            tuple[str, ...]: The registered namespace identifiers.
        """
        return tuple(self._providers)

    @overload
    def register(
        self,
        provider_cls: type[LP],
        *,
        namespace: str | None = None,
    ) -> type[LP]: ...

    @overload
    def register(
        self,
        provider_cls: None = None,
        *,
        namespace: str | None = None,
    ) -> Callable[[type[LP]], type[LP]]: ...

    def register(
        self,
        provider_cls: type[LP] | None = None,
        *,
        namespace: str | None = None,
    ) -> type[LP] | Callable[[type[LP]], type[LP]]:
        """Register a provider class, optionally used as a decorator.

        Args:
            provider_cls (type[LP] | None): The provider class to register. If `None`,
                the method acts as a decorator factory.
            namespace (str | None): Explicit namespace override. Defaults to the class'
                `NAMESPACE` attribute.

        Returns:
            type[LP] | Callable[[type[LP]], type[LP]]: The registered provider class, or
                a decorator that registers the class.
        """

        def decorator(cls: type[LP]) -> type[LP]:
            """Register `cls` as a provider."""
            resolved_namespace = namespace or getattr(cls, "NAMESPACE", None)
            if not isinstance(resolved_namespace, str) or not resolved_namespace:
                raise ValueError(
                    "List providers must define a non-empty string `NAMESPACE` "
                    "attribute or pass `namespace=` when registering."
                )

            existing = self._providers.get(resolved_namespace)
            if existing is not None and existing is not cls:
                raise ValueError(
                    f"A provider is already registered for namespace "
                    f"'{resolved_namespace}'."
                )

            self._providers[resolved_namespace] = cls
            return cls

        if provider_cls is None:
            return decorator
        return decorator(provider_cls)

    def unregister(self, namespace: str) -> None:
        """Remove a provider registration if it exists.

        Args:
            namespace (str): The namespace identifier to unregister.
        """
        self._providers.pop(namespace, None)

    def __contains__(self, namespace: object) -> bool:
        """Check if a provider is registered under `namespace`."""
        return isinstance(namespace, str) and namespace in self._providers

    def __iter__(self) -> Iterator[tuple[str, type[ListProvider]]]:
        """Iterate over `(namespace, provider_class)` pairs."""
        return iter(self._providers.items())

__contains__(namespace)

Check if a provider is registered under namespace.

Source code in .venv/lib/python3.14/site-packages/anibridge/list/registry.py
def __contains__(self, namespace: object) -> bool:
    """Check if a provider is registered under `namespace`."""
    return isinstance(namespace, str) and namespace in self._providers

__init__()

Initialize an empty registry.

Source code in .venv/lib/python3.14/site-packages/anibridge/list/registry.py
def __init__(self) -> None:
    """Initialize an empty registry."""
    self._providers: dict[str, type[ListProvider]] = {}

__iter__()

Iterate over (namespace, provider_class) pairs.

Source code in .venv/lib/python3.14/site-packages/anibridge/list/registry.py
def __iter__(self) -> Iterator[tuple[str, type[ListProvider]]]:
    """Iterate over `(namespace, provider_class)` pairs."""
    return iter(self._providers.items())

clear()

Remove all provider registrations.

Source code in .venv/lib/python3.14/site-packages/anibridge/list/registry.py
def clear(self) -> None:
    """Remove all provider registrations."""
    self._providers.clear()

create(namespace, *, config=None)

Instantiate the provider registered under namespace.

Parameters:

Name Type Description Default
namespace str

The namespace identifier to create.

required
config dict | None

Optional configuration dictionary to pass to the provider constructor.

None

Returns:

Name Type Description
ListProvider ListProvider

An instance of the registered provider.

Source code in .venv/lib/python3.14/site-packages/anibridge/list/registry.py
def create(self, namespace: str, *, config: dict | None = None) -> ListProvider:
    """Instantiate the provider registered under `namespace`.

    Args:
        namespace (str): The namespace identifier to create.
        config (dict | None): Optional configuration dictionary to pass to the
            provider constructor.

    Returns:
        ListProvider: An instance of the registered provider.
    """
    provider_cls = self.get(namespace)
    return provider_cls(config=config)

get(namespace)

Return the provider class registered under namespace.

Parameters:

Name Type Description Default
namespace str

The namespace identifier to look up.

required

Returns:

Type Description
type[ListProvider]

type[ListProvider]: The registered provider class.

Source code in .venv/lib/python3.14/site-packages/anibridge/list/registry.py
def get(self, namespace: str) -> type[ListProvider]:
    """Return the provider class registered under `namespace`.

    Args:
        namespace (str): The namespace identifier to look up.

    Returns:
        type[ListProvider]: The registered provider class.
    """
    try:
        return self._providers[namespace]
    except KeyError as exc:
        raise LookupError(
            f"No provider registered for namespace '{namespace}'."
        ) from exc

namespaces()

Return a tuple of registered namespace identifiers.

Returns:

Type Description
tuple[str, ...]

tuple[str, ...]: The registered namespace identifiers.

Source code in .venv/lib/python3.14/site-packages/anibridge/list/registry.py
def namespaces(self) -> tuple[str, ...]:
    """Return a tuple of registered namespace identifiers.

    Returns:
        tuple[str, ...]: The registered namespace identifiers.
    """
    return tuple(self._providers)

register(provider_cls=None, *, namespace=None)

register(provider_cls: type[LP], *, namespace: str | None = None) -> type[LP]
register(provider_cls: None = None, *, namespace: str | None = None) -> Callable[[type[LP]], type[LP]]

Register a provider class, optionally used as a decorator.

Parameters:

Name Type Description Default
provider_cls type[LP] | None

The provider class to register. If None, the method acts as a decorator factory.

None
namespace str | None

Explicit namespace override. Defaults to the class' NAMESPACE attribute.

None

Returns:

Type Description
type[LP] | Callable[[type[LP]], type[LP]]

type[LP] | Callable[[type[LP]], type[LP]]: The registered provider class, or a decorator that registers the class.

Source code in .venv/lib/python3.14/site-packages/anibridge/list/registry.py
def register(
    self,
    provider_cls: type[LP] | None = None,
    *,
    namespace: str | None = None,
) -> type[LP] | Callable[[type[LP]], type[LP]]:
    """Register a provider class, optionally used as a decorator.

    Args:
        provider_cls (type[LP] | None): The provider class to register. If `None`,
            the method acts as a decorator factory.
        namespace (str | None): Explicit namespace override. Defaults to the class'
            `NAMESPACE` attribute.

    Returns:
        type[LP] | Callable[[type[LP]], type[LP]]: The registered provider class, or
            a decorator that registers the class.
    """

    def decorator(cls: type[LP]) -> type[LP]:
        """Register `cls` as a provider."""
        resolved_namespace = namespace or getattr(cls, "NAMESPACE", None)
        if not isinstance(resolved_namespace, str) or not resolved_namespace:
            raise ValueError(
                "List providers must define a non-empty string `NAMESPACE` "
                "attribute or pass `namespace=` when registering."
            )

        existing = self._providers.get(resolved_namespace)
        if existing is not None and existing is not cls:
            raise ValueError(
                f"A provider is already registered for namespace "
                f"'{resolved_namespace}'."
            )

        self._providers[resolved_namespace] = cls
        return cls

    if provider_cls is None:
        return decorator
    return decorator(provider_cls)

unregister(namespace)

Remove a provider registration if it exists.

Parameters:

Name Type Description Default
namespace str

The namespace identifier to unregister.

required
Source code in .venv/lib/python3.14/site-packages/anibridge/list/registry.py
def unregister(self, namespace: str) -> None:
    """Remove a provider registration if it exists.

    Args:
        namespace (str): The namespace identifier to unregister.
    """
    self._providers.pop(namespace, None)

ListStatus

Bases: StrEnum

Supported statuses for media items in a list.

Source code in .venv/lib/python3.14/site-packages/anibridge/list/base.py
class ListStatus(StrEnum):
    """Supported statuses for media items in a list."""

    COMPLETED = "completed"
    CURRENT = "current"
    DROPPED = "dropped"
    PAUSED = "paused"
    PLANNING = "planning"
    REPEATING = "repeating"

    __PRIORITY: ClassVar[dict[str, int]] = {
        "completed": 3,
        "repeating": 3,
        "current": 2,
        "paused": 2,
        "dropped": 2,
        "planning": 1,
    }

    @property
    def priority(self) -> int:
        """Get the priority of the ListStatus for comparison purposes."""
        return self.__PRIORITY[self.value]

    def __eq__(self, other: object) -> bool:
        """Check equality with another ListStatus (not based on priority)."""
        if not isinstance(other, ListStatus):
            return NotImplemented
        return self.value == other.value

    def __lt__(self, other: object) -> bool:
        """Check if this ListStatus has lower priority than another."""
        if not isinstance(other, ListStatus):
            return NotImplemented
        return self.priority < other.priority

    def __le__(self, other: object) -> bool:
        """Check if this ListStatus has lower or equal priority than another."""
        if not isinstance(other, ListStatus):
            return NotImplemented
        return self.priority <= other.priority

    def __gt__(self, other: object) -> bool:
        """Check if this ListStatus has higher priority than another."""
        if not isinstance(other, ListStatus):
            return NotImplemented
        return self.priority > other.priority

    def __ge__(self, other: object) -> bool:
        """Check if this ListStatus has higher or equal priority than another."""
        if not isinstance(other, ListStatus):
            return NotImplemented
        return self.priority >= other.priority

priority property

Get the priority of the ListStatus for comparison purposes.

__eq__(other)

Check equality with another ListStatus (not based on priority).

Source code in .venv/lib/python3.14/site-packages/anibridge/list/base.py
def __eq__(self, other: object) -> bool:
    """Check equality with another ListStatus (not based on priority)."""
    if not isinstance(other, ListStatus):
        return NotImplemented
    return self.value == other.value

__ge__(other)

Check if this ListStatus has higher or equal priority than another.

Source code in .venv/lib/python3.14/site-packages/anibridge/list/base.py
def __ge__(self, other: object) -> bool:
    """Check if this ListStatus has higher or equal priority than another."""
    if not isinstance(other, ListStatus):
        return NotImplemented
    return self.priority >= other.priority

__gt__(other)

Check if this ListStatus has higher priority than another.

Source code in .venv/lib/python3.14/site-packages/anibridge/list/base.py
def __gt__(self, other: object) -> bool:
    """Check if this ListStatus has higher priority than another."""
    if not isinstance(other, ListStatus):
        return NotImplemented
    return self.priority > other.priority

__le__(other)

Check if this ListStatus has lower or equal priority than another.

Source code in .venv/lib/python3.14/site-packages/anibridge/list/base.py
def __le__(self, other: object) -> bool:
    """Check if this ListStatus has lower or equal priority than another."""
    if not isinstance(other, ListStatus):
        return NotImplemented
    return self.priority <= other.priority

__lt__(other)

Check if this ListStatus has lower priority than another.

Source code in .venv/lib/python3.14/site-packages/anibridge/list/base.py
def __lt__(self, other: object) -> bool:
    """Check if this ListStatus has lower priority than another."""
    if not isinstance(other, ListStatus):
        return NotImplemented
    return self.priority < other.priority

ListTarget dataclass

Resolved mapping descriptor to a list media key.

Source code in .venv/lib/python3.14/site-packages/anibridge/list/base.py
@dataclass(frozen=True, slots=True)
class ListTarget:
    """Resolved mapping descriptor to a list media key."""

    descriptor: MappingDescriptor
    media_key: str

ListUser dataclass

User information for a list provider.

Source code in .venv/lib/python3.14/site-packages/anibridge/list/base.py
@dataclass(frozen=True, slots=True)
class ListUser:
    """User information for a list provider."""

    key: str
    title: str = field(compare=False)

list_provider(cls=None, *, namespace=None, registry=None)

list_provider(cls: type[LP], *, namespace: str | None = None, registry: ListProviderRegistry | None = None) -> type[LP]
list_provider(cls: None = None, *, namespace: str | None = None, registry: ListProviderRegistry | None = None) -> Callable[[type[LP]], type[LP]]

Class decorator that registers ListProvider implementations.

This helper lets third-party providers register themselves declaratively:

```
from anibridge.list import list_provider


@list_provider(namespace="anilist")
class AnilistListProvider:
    NAMESPACE = "anilist"
    ...
```

Parameters:

Name Type Description Default
cls type[LP] | None

The provider class to register. If None, the function acts as a decorator factory.

None
namespace str | None

Explicit namespace override. Defaults to the class' NAMESPACE attribute.

None
registry ListProviderRegistry | None

Alternate registry to insert into. Defaults to the module-level one.

None

Returns:

Type Description
type[LP] | Callable[[type[LP]], type[LP]]

type[LP] | Callable[[type[LP]], type[LP]]: The registered provider class, or a decorator that registers the class.

Source code in .venv/lib/python3.14/site-packages/anibridge/list/registry.py
def list_provider[LP: ListProvider](
    cls: type[LP] | None = None,
    *,
    namespace: str | None = None,
    registry: ListProviderRegistry | None = None,
) -> type[LP] | Callable[[type[LP]], type[LP]]:
    """Class decorator that registers `ListProvider` implementations.

    This helper lets third-party providers register themselves declaratively:

        ```
        from anibridge.list import list_provider


        @list_provider(namespace="anilist")
        class AnilistListProvider:
            NAMESPACE = "anilist"
            ...
        ```

    Args:
        cls (type[LP] | None): The provider class to register. If `None`, the function
            acts as a decorator factory.
        namespace (str | None): Explicit namespace override. Defaults to the class'
            `NAMESPACE` attribute.
        registry (ListProviderRegistry | None): Alternate registry to insert into.
            Defaults to the module-level one.

    Returns:
        type[LP] | Callable[[type[LP]], type[LP]]: The registered provider class, or
            a decorator that registers the class.
    """
    active_registry = registry or provider_registry
    return active_registry.register(cls, namespace=namespace)