Skip to content

[Feature]: Enforce Abstract↔Concrete type-hint coherence via a metaclass #149

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
adamamer20 opened this issue Apr 24, 2025 · 1 comment
Labels
feature New functionality added to the project.

Comments

@adamamer20
Copy link
Member

🤔 Problem Description

After merging PR #143, we will have Beartype on every method, but nothing automatically verifies that each concrete class still honors the abstract class’s type hints.
A small drift—e.g. changing an add(...) signature—can silently break downstream code or introduce subtle variance bugs that only fail at runtime.

💡 Proposed Solution

  1. Add a new metaclass (e.g. InterfaceMeta) in a util module (say mesa_frames.utils.interface_meta) that, in its __init__, walks every ABC base and uses Beartype’s is_subhint to compare each abstract method’s type hints against the override in the subclass—raising a TypeError at class‐definition time on mismatch.
  2. Switch our ABCs (like AgentContainer) to use metaclass=InterfaceMeta instead of plain ABCMeta. Any drop-in mismatch in AgentSetPolars (or any other subclass) will now fail immediately when Python imports the module, giving a clear error message.

Example in mesa-frames

# mesa_frames/abstract/agents.py
from abc      import abstractmethod
from typing  import get_type_hints
from typing_extensions import Self
from mesa_frames.types_ import DataFrameInput
from collections.abc import Collectionclass AgentSetDF(AgentContainer, DataFrameMixin):
    @abstractmethod
    def add(
        self,
        agents: DataFrameInput,
        inplace: bool = True,
    ) -> Self:
        """Add agents to the container."""
# mesa_frames/concrete/agentset.py
import polars as pl
from typing      import Sequence, Any
from mesa_frames.abstract.agents import AgentSetDF
from mesa_frames.utils.interface_meta import InterfaceMeta

class AgentSetPolars(AgentSetDF, PolarsMixin, metaclass=InterfaceMeta):
    def add(
        self,
        agents: pl.DataFrame | Sequence[Any] | dict[str, Any],
        inplace: bool = True,
    ) -> Self:
        """Polars-backed add implementation."""

Here, the metaclass check would ensure that the concrete add’s agents: hint (pl.DataFrame | Sequence[Any] | dict[str,Any]) is a valid superhint of the abstract DataFrameInput alias (dict[str, Any] | Sequence[Sequence] | pl.DataFrame), and would error out at class‐definition time if someone ever drifted one side out of alignment.

🔄 Alternatives Considered

  • Decorator (opt-in per class), but easy to forget.
  • CI test to compare signatures, but that only catches errors later in testing.

➕ Additional Context

@adamamer20 adamamer20 added the feature New functionality added to the project. label Apr 24, 2025
@Ben-geo
Copy link
Collaborator

Ben-geo commented Apr 26, 2025

Really like this idea. Catching mismatches early will save us a lot of headaches later. love a clean and lightweight way to do it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature New functionality added to the project.
Projects
None yet
Development

No branches or pull requests

2 participants