sematic
sematic copied to clipboard
JSONEncodableMixin has unexpected behaviors with strings/enums
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.