bytefield


Namebytefield JSON
Version 1.0.2 PyPI version JSON
download
home_pagehttps://github.com/donadigo/bytefield
SummaryParse binary data using declarative field layout and native Python properties
upload_time2023-01-29 20:31:01
maintainer
docs_urlNone
authordonadigo
requires_python
licenseMIT
keywords struct bytefield bytearray
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            ![Build Status](https://github.com/donadigo/bytefield/workflows/Build/badge.svg)
# ByteField
A Python library for parsing/manipulating binary data with easily accessible Python properties inspired by Django. The library is still in development. ByteField supports:
* Variable length fields
* Nested structures
* Parsing only accessed fields

## Quick example
ByteField allows to define binary data layout declaratively which then maps to underlying bytes:
```py
from bytefield import *

class Header(ByteStruct):
    magic = StringField(length=5)
    length = IntegerField()
    array = ArrayField(shape=None, elem_field_type=IntegerField)
    floating = FloatField()

header = Header(magic='bytes', floating=3.14)
header.length = 3
header.array = list(range(1, header.length + 1))
print(header.data)
```

### Output:
```py
bytearray(b'bytes\x03\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\xc3\xf5H@')`
```

## Example: parse a JPEG header
You can embed other structure declarations inside structures:
```py
from bytefield import *

class RGB(ByteStruct):
    r = IntegerField(signed=False, size=1)
    g = IntegerField(signed=False, size=1)
    b = IntegerField(signed=False, size=1)

class Marker(ByteStruct):
    marker = IntegerField(size=2, signed=False)
    length = IntegerField(size=2, signed=False)
    identifier = StringField(length=5, encoding='ascii')
    version = IntegerField(size=2, signed=False)
    density = IntegerField(size=1, signed=False)
    x_density = IntegerField(size=2, signed=False)
    y_density = IntegerField(size=2, signed=False)
    x_thumbnail = IntegerField(size=2, signed=False)
    y_thumbnail = IntegerField(size=2, signed=False)
    thumb_data = ArrayField(shape=None, elem_field_type=RGB)

class JPEGHeader(ByteStruct):
    soi = IntegerField(size=2, signed=False)
    marker = StructField(Marker)

with open('image.jpg', 'rb') as f:
    # Parse the JPEG header
    header = JPEGHeader(f.read())

    # Resize the thumbnail data
    header.marker.resize(
        Marker.thumb_data_field, header.marker.x_thumbnail * header.marker.y_thumbnail
    )

    # Display the thumbnail
    display_thumbnail(header.marker.thumb_data)
```

## Writing custom struct logic
You can create high-level structures which define their own behavior depending on the data contained within the struct:
```py
from bytefield import *

class DynamicFloatArray(ByteStruct):
    length = IntegerField(signed=False)
    array_data = ArrayField(None, FloatField)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # When instantiated, resize the array according to its length
        self.resize(DynamicFloatArray.array_data_field, self.length)

data = bytearray(b'\x03\x00\x00\x00\x00\x00\x80?\x00\x00\x00@\x00\x00@@')
print(DynamicFloatArray(data))
```

### Output:
```py
[DynamicFloatArray object at 0x1c88e709e50]
length (int): 3
array_data (ndarray): [1.0 2.0 3.0]
```

## Variable fields
Bytefield supports fields with unknown type/size:
```py
from bytefiel import *

TYPE_INTEGER = 0
TYPE_FLOAT = 1
TYPE_STRING = 2

class DynamicString(ByteStruct):
    length = IntegerField(signed=False)
    str = StringField(None)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.resize(DynamicString.str_field, self.length)

class Content(ByteStruct):
    content_type = IntegerField(signed=False, size=2)
    content_data = VariableField()  # a variable field that will be resized when parsing the struct

    def __init__(self, data: bytearray = None, *args, **kwargs):
        super().__init__(data, *args, **kwargs)
        resize_bytes = not bool(data)
        if self.content_type == TYPE_INTEGER:
            self.resize(Content.content_data_field, IntegerField(), resize_bytes=resize_bytes)
        elif self.content_type == TYPE_FLOAT:
            self.resize(Content.content_data_field, FloatField(), resize_bytes=resize_bytes)
        elif self.content_type == TYPE_STRING:
            self.resize(Content.content_data_field, StructField(DynamicString), resize_bytes=resize_bytes)

write = Content()
write.content_type = TYPE_STRING
write.resize(Content.content_data_field, StructField(DynamicString), resize_bytes=True)
write.content_data.str = 'content'
write.content_data.length = len(write.content_data.str)

read = Content(write.data)
print(f'{write.data} is parsed to:\n{read}')
```

### Output
```
bytearray(b'\x02\x00\x07\x00\x00\x00content') is parsed to:
[Content object at 0x1c1846888b0]
content_type (int): 2
content_data (DynamicString):
        length (int): 7
        str (str): content
```


            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/donadigo/bytefield",
    "name": "bytefield",
    "maintainer": "",
    "docs_url": null,
    "requires_python": "",
    "maintainer_email": "",
    "keywords": "struct,bytefield,bytearray",
    "author": "donadigo",
    "author_email": "donadigo@gmail.com",
    "download_url": "https://files.pythonhosted.org/packages/11/26/0df7b59f208d76f610589b634c2255555862d37649bb788af856360954c1/bytefield-1.0.2.tar.gz",
    "platform": null,
    "description": "![Build Status](https://github.com/donadigo/bytefield/workflows/Build/badge.svg)\r\n# ByteField\r\nA Python library for parsing/manipulating binary data with easily accessible Python properties inspired by Django. The library is still in development. ByteField supports:\r\n* Variable length fields\r\n* Nested structures\r\n* Parsing only accessed fields\r\n\r\n## Quick example\r\nByteField allows to define binary data layout declaratively which then maps to underlying bytes:\r\n```py\r\nfrom bytefield import *\r\n\r\nclass Header(ByteStruct):\r\n    magic = StringField(length=5)\r\n    length = IntegerField()\r\n    array = ArrayField(shape=None, elem_field_type=IntegerField)\r\n    floating = FloatField()\r\n\r\nheader = Header(magic='bytes', floating=3.14)\r\nheader.length = 3\r\nheader.array = list(range(1, header.length + 1))\r\nprint(header.data)\r\n```\r\n\r\n### Output:\r\n```py\r\nbytearray(b'bytes\\x03\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\xc3\\xf5H@')`\r\n```\r\n\r\n## Example: parse a JPEG header\r\nYou can embed other structure declarations inside structures:\r\n```py\r\nfrom bytefield import *\r\n\r\nclass RGB(ByteStruct):\r\n    r = IntegerField(signed=False, size=1)\r\n    g = IntegerField(signed=False, size=1)\r\n    b = IntegerField(signed=False, size=1)\r\n\r\nclass Marker(ByteStruct):\r\n    marker = IntegerField(size=2, signed=False)\r\n    length = IntegerField(size=2, signed=False)\r\n    identifier = StringField(length=5, encoding='ascii')\r\n    version = IntegerField(size=2, signed=False)\r\n    density = IntegerField(size=1, signed=False)\r\n    x_density = IntegerField(size=2, signed=False)\r\n    y_density = IntegerField(size=2, signed=False)\r\n    x_thumbnail = IntegerField(size=2, signed=False)\r\n    y_thumbnail = IntegerField(size=2, signed=False)\r\n    thumb_data = ArrayField(shape=None, elem_field_type=RGB)\r\n\r\nclass JPEGHeader(ByteStruct):\r\n    soi = IntegerField(size=2, signed=False)\r\n    marker = StructField(Marker)\r\n\r\nwith open('image.jpg', 'rb') as f:\r\n    # Parse the JPEG header\r\n    header = JPEGHeader(f.read())\r\n\r\n    # Resize the thumbnail data\r\n    header.marker.resize(\r\n        Marker.thumb_data_field, header.marker.x_thumbnail * header.marker.y_thumbnail\r\n    )\r\n\r\n    # Display the thumbnail\r\n    display_thumbnail(header.marker.thumb_data)\r\n```\r\n\r\n## Writing custom struct logic\r\nYou can create high-level structures which define their own behavior depending on the data contained within the struct:\r\n```py\r\nfrom bytefield import *\r\n\r\nclass DynamicFloatArray(ByteStruct):\r\n    length = IntegerField(signed=False)\r\n    array_data = ArrayField(None, FloatField)\r\n\r\n    def __init__(self, *args, **kwargs):\r\n        super().__init__(*args, **kwargs)\r\n        # When instantiated, resize the array according to its length\r\n        self.resize(DynamicFloatArray.array_data_field, self.length)\r\n\r\ndata = bytearray(b'\\x03\\x00\\x00\\x00\\x00\\x00\\x80?\\x00\\x00\\x00@\\x00\\x00@@')\r\nprint(DynamicFloatArray(data))\r\n```\r\n\r\n### Output:\r\n```py\r\n[DynamicFloatArray object at 0x1c88e709e50]\r\nlength (int): 3\r\narray_data (ndarray): [1.0 2.0 3.0]\r\n```\r\n\r\n## Variable fields\r\nBytefield supports fields with unknown type/size:\r\n```py\r\nfrom bytefiel import *\r\n\r\nTYPE_INTEGER = 0\r\nTYPE_FLOAT = 1\r\nTYPE_STRING = 2\r\n\r\nclass DynamicString(ByteStruct):\r\n    length = IntegerField(signed=False)\r\n    str = StringField(None)\r\n\r\n    def __init__(self, *args, **kwargs):\r\n        super().__init__(*args, **kwargs)\r\n        self.resize(DynamicString.str_field, self.length)\r\n\r\nclass Content(ByteStruct):\r\n    content_type = IntegerField(signed=False, size=2)\r\n    content_data = VariableField()  # a variable field that will be resized when parsing the struct\r\n\r\n    def __init__(self, data: bytearray = None, *args, **kwargs):\r\n        super().__init__(data, *args, **kwargs)\r\n        resize_bytes = not bool(data)\r\n        if self.content_type == TYPE_INTEGER:\r\n            self.resize(Content.content_data_field, IntegerField(), resize_bytes=resize_bytes)\r\n        elif self.content_type == TYPE_FLOAT:\r\n            self.resize(Content.content_data_field, FloatField(), resize_bytes=resize_bytes)\r\n        elif self.content_type == TYPE_STRING:\r\n            self.resize(Content.content_data_field, StructField(DynamicString), resize_bytes=resize_bytes)\r\n\r\nwrite = Content()\r\nwrite.content_type = TYPE_STRING\r\nwrite.resize(Content.content_data_field, StructField(DynamicString), resize_bytes=True)\r\nwrite.content_data.str = 'content'\r\nwrite.content_data.length = len(write.content_data.str)\r\n\r\nread = Content(write.data)\r\nprint(f'{write.data} is parsed to:\\n{read}')\r\n```\r\n\r\n### Output\r\n```\r\nbytearray(b'\\x02\\x00\\x07\\x00\\x00\\x00content') is parsed to:\r\n[Content object at 0x1c1846888b0]\r\ncontent_type (int): 2\r\ncontent_data (DynamicString):\r\n        length (int): 7\r\n        str (str): content\r\n```\r\n\r\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Parse binary data using declarative field layout and native Python properties",
    "version": "1.0.2",
    "split_keywords": [
        "struct",
        "bytefield",
        "bytearray"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "11260df7b59f208d76f610589b634c2255555862d37649bb788af856360954c1",
                "md5": "ee9c00dd76834f374e5e68b48f52e536",
                "sha256": "eb5f8c8d51de54d8764041531521e072b4e6208acd3504f1f2260c0d6377599e"
            },
            "downloads": -1,
            "filename": "bytefield-1.0.2.tar.gz",
            "has_sig": false,
            "md5_digest": "ee9c00dd76834f374e5e68b48f52e536",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": null,
            "size": 17067,
            "upload_time": "2023-01-29T20:31:01",
            "upload_time_iso_8601": "2023-01-29T20:31:01.353739Z",
            "url": "https://files.pythonhosted.org/packages/11/26/0df7b59f208d76f610589b634c2255555862d37649bb788af856360954c1/bytefield-1.0.2.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-01-29 20:31:01",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "github_user": "donadigo",
    "github_project": "bytefield",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "requirements": [],
    "lcname": "bytefield"
}
        
Elapsed time: 0.03661s