Skip to content

Commit

Permalink
New function flat_fields_iterator
Browse files Browse the repository at this point in the history
  • Loading branch information
avalentino committed Jun 5, 2023
1 parent a98e3a6 commit c105922
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 5 deletions.
36 changes: 34 additions & 2 deletions bpack/descriptors.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import types
import warnings
import dataclasses
from typing import Iterable, Optional, Sequence, Type, Union
from typing import Iterator, Optional, Sequence, Type, Union

import bpack.utils
import bpack.typing
Expand Down Expand Up @@ -628,7 +628,7 @@ def bitorder(obj) -> Union[EBitOrder, None]:

def field_descriptors(
descriptor, pad: bool = False
) -> Iterable[BinFieldDescriptor]:
) -> Iterator[BinFieldDescriptor]:
"""Return the list of field descriptors for the input record descriptor.
Items are instances of the :class:`BinFieldDescriptor` class describing
Expand Down Expand Up @@ -659,3 +659,35 @@ def field_descriptors(
else:
for field_ in fields(descriptor):
yield get_field_descriptor(field_)


def flat_fields_iterator(desctiptor, offset: int = 0) -> Iterator[Field]:
"""Recursively iterate on fields of a descriptor.
The behaviour of this function is similar to the one of
:func:`bpack.descriptors.fileds` if the input descriptor do not contain
fileds that are desctipors (nested).
The main difference is that this one is an iterator while
:func:`bpack.descriptors.fileds` returns a tuple.
If the input desctiptor is nested (i.e. has fields that are descriptors),
then a the it is visited recursively to return oll the fields belonging
to the main decriptor and to the nested ones.
The nested descriptors are replaced by their fields and the returned
sequence of fields is *flat*.
.. note:: please note that in case of nested descriptors, the returned
fields are copy of the original ones, with the `offset` attribute
adjusted to the relative to the beginning of the root desctiptor.
"""
for field_ in bpack.fields(desctiptor):
fd = get_field_descriptor(field_)
fd.offset += offset

if bpack.is_descriptor(field_.type):
yield from flat_fields_iterator(field_.type, offset=fd.offset)
else:
field_ = copy.copy(field_)
set_field_descriptor(field_, fd)
yield field_
39 changes: 37 additions & 2 deletions bpack/tests/test_desctiptor_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,13 @@
import pytest

import bpack
from bpack import EBaseUnits, EByteOrder, EBitOrder
from bpack.descriptors import BinFieldDescriptor, METADATA_KEY
from bpack import EBaseUnits, EByteOrder, EBitOrder, T
from bpack.descriptors import (
METADATA_KEY,
BinFieldDescriptor,
flat_fields_iterator,
get_field_descriptor,
)


def test_is_descriptor():
Expand Down Expand Up @@ -450,3 +455,33 @@ class Record:
assert bpack.astuple(record) == values
assert type(bpack.astuple(record)) is tuple
assert type(bpack.astuple(record, tuple_factory=list)) is list


def test_flat_fields_iterator():
@bpack.descriptor(baseunits=EBaseUnits.BITS, size=8)
class Record:
x: T["u3"] = 0 # noqa: F821
y: T["u3"] = bpack.field(offset=4, default=3) # noqa: F821

@bpack.descriptor(baseunits=EBaseUnits.BITS)
class NestedRecord:
a: T["u7"] = 32 # noqa: F821
b: Record = bpack.field(default_factory=Record, offset=8)
c: T["f16"] = -9999 # noqa: F821

@bpack.descriptor(baseunits=EBaseUnits.BITS)
class FlatRecord:
a: T["u7"] = 32 # noqa: F821
x: T["u3"] = bpack.field(offset=8, default=0) # noqa: F821
y: T["u3"] = bpack.field(offset=12, default=3) # noqa: F821
c: T["f16"] = bpack.field(offset=16, default=-9999) # noqa: F821

for firld_, ref_field in zip(
flat_fields_iterator(NestedRecord),
flat_fields_iterator(FlatRecord),
):
assert get_field_descriptor(firld_) == get_field_descriptor(ref_field)
assert firld_.name == ref_field.name
assert firld_.type == ref_field.type
assert firld_.default == ref_field.default
assert firld_.default_factory == ref_field.default_factory
4 changes: 3 additions & 1 deletion docs/release_notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ bpack v1.2.0 (UNRELEASED)
* Improved documentation and fixed typos.
* flake8_ configuration moved to a dedicated file.
* Do not test the :mod:`bpack.ba` backend in PyPy3_.
* New :func:`bpack.typing.type_params_to_str` function.
* New functions:
- :func:`bpack.typing.type_params_to_str`
- :func:`bpack.descriptors.flat_fields_iterator`

.. _flake8: https://github.com/pycqa/flake8
.. _PyPy3: https://www.pypy.org
Expand Down

0 comments on commit c105922

Please sign in to comment.