Skip to content

Enum deserialization still fails for tuple, nested-tuple, and list values after #160 #161

Description

@marcosfrenkel

Background

When the server returns an Enum from a parameter get, it serializes it as {"value": member.value, "_class_type": "pkg.mod.EnumClass"} (_convert_enum_to_dict, added in #140). The client rebuilds the member in _convert_dict_to_obj. JSON has no tuple type, so any tuple inside value reaches the client as a list.

#160 fixed this for namedtuple-valued enums (the QDAC1 Mode enum): when value is a list, it takes the value type of the first enum member and rebuilds it with ValueType(*value). That works for namedtuples, but some cases are still broken, and one used to work and no longer does.

Remaining problems

Enum values Before #160 After #160
namedtuple, e.g. X = P(1, 2) ValueError works
plain tuple, e.g. X = (1, 2) ValueError TypeError: tuple expected at most 1 argument, got 2
list, e.g. X = [1, 2] works TypeError: list expected at most 1 argument, got 2 (regression)
nested tuple, e.g. X = ((1, 2), 3) ValueError fails, inner list never converted
mixed value types across members ValueError for list values may rebuild with the wrong type, since it uses the first member's type

List-valued enums are rare (list values can't be hashed), so the regression probably doesn't hit anyone in practice. None of the enums in this repo (Operation, ParameterTypes, LogLevels, the test StatusFlag) have tuple or list values.

Proposed fix

Look up the value as it arrives first, and only if that fails, convert lists to tuples recursively and look it up again. A namedtuple compares and hashes equal to a plain tuple with the same items, so a plain-tuple lookup finds namedtuple members without knowing their type.

def _lists_to_tuples(v):
    if isinstance(v, list):
        return tuple(_lists_to_tuples(x) for x in v)
    return v

if isinstance(cls, type) and issubclass(cls, Enum):
    value = item_dict["value"]
    try:
        return cls(value)
    except ValueError:
        if isinstance(value, list):
            return cls(_lists_to_tuples(value))
        raise

With this, list-valued enums go back to working, plain-tuple and nested-tuple enums start working, and the code no longer guesses the value type from the first member.

Tests

#160 added no tests. Add round-trip cases (serialize a ServerResponse whose message is the enum member, then deserialize_obj) to test/pytest/test_enum_serialization.py for enums with:

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions