sematic icon indicating copy to clipboard operation
sematic copied to clipboard

JSONEncodableMixin has unexpected behaviors with strings/enums

Open augray opened this issue 2 years ago • 0 comments

Consider this test:

from enum import Enum, unique

from sqlalchemy import Column, types

# Sematic
from sematic.db.models.run import Run
from sematic.db.models.base import Base
from sematic.db.tests.fixtures import persisted_run, run, test_db  # noqa: F401
from sematic.db.models.json_encodable_mixin import JSONEncodableMixin, ENUM_KEY

@unique
class CardSuit(Enum):
    HEARTS = "HEARTS"
    SPADES = "SPADES"
    DIAMONDS = "DIAMONDS"
    CLUBS = "CLUBS"

class ExampleEncodable(Base, JSONEncodableMixin):
    __tablename__ = "fake"
    id: str = Column(  # type: ignore
        types.String(), nullable=False, primary_key=True,
    )
    suit: CardSuit = Column(  # type: ignore
        types.String(), nullable=False, info={ENUM_KEY: CardSuit}
    )


def test_encode_decode():
    expected_id = "abc123"
    expected_suit = CardSuit.SPADES.value

    # ideally this would fail since we set suit with a str rather than an Enum instance
    example = ExampleEncodable(id=expected_id, suit=expected_suit)

    # if the above didn't fail, then hopefully this would, since the suit field is of the
    # wrong type
    encodable = example.to_json_encodable()

    # Decoding also succeeds without error
    decoded = ExampleEncodable.from_json_encodable(encodable)
    assert decoded.id == expected_id

    # this assert fails, though given that everything above has worked this is surprising.
    # In practice, this behavior leads to difficult to debug issues.
    assert decoded.suit == expected_suit

As noted in the code, the current behavior makes it easy to make difficult-to-debug mistakes. So much so that the Runs code has a mistake that everybody is working around: future_state is labeled as a FutureState, but will actually always return a string when accessed.

augray avatar Dec 23 '22 18:12 augray