-
Notifications
You must be signed in to change notification settings - Fork 200
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
Python: to_i on a enum may fail, if a enum is not parsed as known #815
Comments
I've just hit this issue when porting serialization support to Python. If an enum value cannot be converted to an integer, it cannot be serialized. And the current override def enumToInt(v: Ast.expr, et: EnumType): String =
s"${translate(v)}.value" When the KS-generated code parses an enum field, it first reads the integer value, which goes through the override def doEnumById(enumTypeAbs: List[String], id: String): String =
s"${PythonCompiler.kstreamName}.resolve_enum(${PythonCompiler.types2class(enumTypeAbs)}, $id)" So in case of Python, the @staticmethod
def resolve_enum(enum_obj, value):
"""Resolves value using enum: if the value is not found in the map,
we'll just use literal value per se. Works around problem with Python
enums throwing an exception when encountering unknown value.
"""
try:
return enum_obj(value)
except ValueError:
return value As a result, the value of an "enum attribute" in KS terms can in Python be represented either as an object instance of the enum class, or a plain integer (
class EnumForUnknownId(ReadWriteKaitaiStruct):
# ...
def _read(self):
self.one = KaitaiStream.resolve_enum(EnumForUnknownId.Animal, self._io.read_u1())
def _write__seq(self, io=None):
# ...
self._io.write_u1(self.one.value) |
|
I'm going to change the base class for Python enums to [`enum.IntEnum`](https://docs.python.org/3/library/enum.html#enum.IntEnum), as suggested in kaitai-io/kaitai_struct#815. An `enum.IntEnum` object behaves almost like an `int` in many cases, but not all. For example, if you have a enum def like this: ```python class Animal(enum.IntEnum): DOG = 4 CAT = 7 ``` ...then `Animal.DOG == 4` is `True`. Consequently, if you do `self.assertEqual(Animal.DOG, 4)` in a unit test, it will pass. However, the result of `str(Animal.DOG)` depends on the version of Python: - before Python 3.11: `str(Animal.DOG) == 'Animal.DOG'` - since Python 3.11: `str(Animal.DOG) == '4'` So at least as long as we support older Pythons than 3.11, a KS expression `animal::dog.to_i.to_s` has to be translated as `str(int(Animal.DOG))` (not just `str(Animal.DOG)`, because that won't yield the correct result `'4'` in Python 3.10 and older). Therefore, this commit adds an assertion to protect against this potential mistake.
Proposed fix is to replace Enum to IntEnum in
https://github.com/kaitai-io/kaitai_struct_compiler/blob/8d5308dc6591f82cd7e1bd0233af2772b5f68db8/shared/src/main/scala/io/kaitai/struct/languages/PythonCompiler.scala#L462
https://github.com/kaitai-io/kaitai_struct_compiler/blob/8d5308dc6591f82cd7e1bd0233af2772b5f68db8/shared/src/main/scala/io/kaitai/struct/languages/PythonCompiler.scala#L465
and use
in
https://github.com/kaitai-io/kaitai_struct_compiler/blob/61a577cb67e1fb7e3d9c6603554c36e7c6f47849/shared/src/main/scala/io/kaitai/struct/translators/PythonTranslator.scala#L84L85
Also,
IntEnum
s can be interacted to numbers without explicit casting at all - they inheritint
. Should we allow such for all the targets and determine if casting is needed automatically and apply it implicitly?UPD: thanks for fixing this in a timely fashion ;) I had to implement a workaround. https://github.com/kaitaiStructCompile/kaitaiStructCompile.py/blob/16eef312efea66593b5a1e1b07bab6fd33b1b3b8/kaitaiStructCompile/postprocessors.py#L12-L132
The text was updated successfully, but these errors were encountered: