construct-typing icon indicating copy to clipboard operation
construct-typing copied to clipboard

!!! EXPERIMENTAL VERSION !!!

Open kevinheavey opened this issue 3 years ago • 5 comments

The README section for construct_typed has this big !!! EXPERIMENTAL VERSION !!! health warning on it, and I'm curious what that entails. For example, is it saying it might not work? Or perhaps that it might change a lot?

kevinheavey avatar Nov 01 '21 01:11 kevinheavey

The warning refers mostly to the fact that there may still be conceptual changes to DataclassStruct, DataclassBitStruct, TEnum and TFlagsEnum. But I also can't rule out bugs in principle, because this library is not tested well enough yet. Currently I am testing the following two changes. If you have other suggestions/tips they are welcome.

1. Combine dataclass and construct-format What still bothers me in my current implementation is that the dataclass and the construct-format are two independent objects. I am currently experimenting how to combine the two like this:

Current usage (dataclass and the construct-format are independent):

@dataclasses.dataclass
class Image(DataclassMixin):
    width: int = csfield(Int8ub)
    height: int = csfield(Int8ub)
    pixels: t.List[int] = csfield(Array(this.width * this.height, Byte))

format = DataclassStruct(Image)
print(format.parse(b"BMP\x01\x03\x02\x07\x08\t\x0b\x0c\r"))

My prefered usage (dataclass and the construct-format are combined in one object):

@construct_typed.struct
class Image:
    width: int = csfield(Int8ub)
    height: int = csfield(Int8ub)
    pixels: t.List[int] = csfield(Array(this.width * this.height, Byte))

print(Image.parse(b"BMP\x01\x03\x02\x07\x08\t\x0b\x0c\r"))

But my prefered usage is tricky together with static type checkers like pyright/pylance which I use the most of the time.

2. Change dataclasses to attrs Also I am experimenting if it makes sense to switch from dataclasses to attrs, because attrs offers a few more features. What I am missing the most in dataclasses is kw_only which maybe helps with Default or Const constructs. This is only available from Python 3.10. But in attrs it is available for a longer time.

timrid avatar Nov 01 '21 09:11 timrid

Wouldn't something like this work? (of course I totally did not think about typing issues here :))

class EnhancedDataclassMixin(DataclassMixin):
    @classmethod
    def _get_format(cls):
        return DataclassStruct(cls)

    @classmethod
    def build(cls, obj):
        return cls._get_format().build(obj)

    @classmethod
    def parse(cls, data):
        return cls._get_format().parse(data)

@dataclasses.dataclass
class Image(EnhancedDataclassMixin):
    width: int = csfield(Int8ub)
    height: int = csfield(Int8ub)
    pixels: t.List[int] = csfield(Array(this.width * this.height, Byte))

raw = b'\x01\x03\x01\x02\x03'
obj = Image.parse(raw)
assert Image.build(obj) == raw

waszil avatar Feb 07 '22 16:02 waszil

Yes, I had also tried out something like that. The problem is that if you want to embed Image in another structure you always have to write Image._get_format(). I would prefer that you only have to write Image.

That's why I already tried to make the metaclass of EnhancedDataclassMixin derived from the class Construct, so that the class type itself is a Construct. But unfortunately I didn't manage to do that as I had imagined...

timrid avatar Feb 07 '22 19:02 timrid

Embedding would work as well, I have implemented a small mod for example for this on a branch: https://github.com/waszil/construct-typing/commit/479b51344bfd95149596a75ee574ac2e63c032df

waszil avatar Feb 11 '22 09:02 waszil

In your particular case your implementation works. But this is not a general solution for the problem. If you embed e.g. SubStruct from your test into another construct like Array then it doesn't work like that anymore:

@dataclasses.dataclass
class MainStruct(EnhancedDataclassMixin):
	a: int = csfield(Int8ub)
	b: int = csfield(Int8ub)
	# here, we can simply use `SubStruct`, as csfield will recognize it and get the DataclassStruct(...) format.
	sub_struct: List[SubStruct] = csfield(Array(2, SubStruct))

timrid avatar Feb 11 '22 20:02 timrid