Skip to content

Dataclass-based ASCS Packets #688

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open

Dataclass-based ASCS Packets #688

wants to merge 1 commit into from

Conversation

zxzxwu
Copy link
Collaborator

@zxzxwu zxzxwu commented May 10, 2025

This has been used in Navi for a while. Recently I resolved several critical issues and I think it's a time to bring it into Bumble upstream.

An obvious benefit to use dataclasses is IDE and type checker integration.

image

Unlike #430, this design properly utilize the existing effort, especially field specs. For nested fields, we may need to set another field in metadata like Annotated[list, 1, '[' or ']' ] as stack operator, though usually we only support up to 2 layers.

return_parameters_fields cannot be benifit from this, because current type checker cannot associate two classes. But BTW, return_parameters_fields is actually not necessary to be inside decorator, so later we may have a PR to move them from decorators to classes.

@zxzxwu zxzxwu force-pushed the hci2 branch 5 times, most recently from 664d285 to 94d132f Compare May 10, 2025 16:18
@zxzxwu zxzxwu marked this pull request as draft May 10, 2025 17:15
@zxzxwu zxzxwu requested a review from barbibulle May 10, 2025 17:15
@zxzxwu
Copy link
Collaborator Author

zxzxwu commented May 10, 2025

I believe there are still a lot of places to polish, so this is just an initial draft for reviews.

@barbibulle
Copy link
Collaborator

I like the idea. Having support for proper typechecking via dataclasses is a great way to go (easier that designing type-checking plugins). We'll need to make sure that we keep things still compatible with Python >= 3.9.

@zxzxwu
Copy link
Collaborator Author

zxzxwu commented May 13, 2025

Thanks! typing.Annotated is introduced in Python 3.9, so compatability shouldn't be a problem (and even under 3.9 we can import it from typing_extensions). But of course we will need some test cases to handle them.

I will work on #686 first, and them continue here.

@zxzxwu
Copy link
Collaborator Author

zxzxwu commented May 14, 2025

A problem here is that with from __future__ import annotations, Annotations including metadata will become string in Runtime. Evaluating them from HCI module is not a good idea because it's not always possible to get the symbols.

There are 2 solutions to do this:

  1. Avoid using from __future__ import annotations when dataclass-based packets are used. It seems PEP 563 will never become mandatory (replaced by PEP 649), so it should be safe, but it requires some rework to remove this import.
  2. Using dataclasses.field(metadata={...}) to store field info, but this will be a little bit long-winded.

@zxzxwu zxzxwu force-pushed the hci2 branch 2 times, most recently from 0183d3a to ff73adb Compare May 14, 2025 08:49
@zxzxwu zxzxwu changed the title [Proposal] Dataclass-based HCI Packets Dataclass-based ASCS Packets May 21, 2025
@zxzxwu zxzxwu marked this pull request as ready for review May 23, 2025 07:37
@zxzxwu
Copy link
Collaborator Author

zxzxwu commented May 23, 2025

This PR is ready for review. Let's get started from ASCS packets since the test is more comprehensive here.

@barbibulle
Copy link
Collaborator

This PR is ready for review. Let's get started from ASCS packets since the test is more comprehensive here.

I'll take a look shortly. I need to refresh my memory on deferred type annotation evaluation.

@barbibulle
Copy link
Collaborator

I think there may be a slight variant on this model that may be a bit simpler, a bit easier to read, and would still let us use from __future__ import annotations. Instead of using Annotated to change the type annotation, we can usedataclasses.field, via a helper function. In a first step, we can keep the existing 'spec' syntax exactly the way it is (makes it easy to just convert 1:1 to the new scheme without having to worry about compatibility). The only change being how list of fields are represented.
I'm thinking of something like this:

def hci_field(spec, **kw_args):
    return dataclasses.field(
        metadata={"bumble.hci": {"spec": spec, "options": kw_args}}
    )

@dataclasses.dataclass
class Bar:
    # equivalent of:
    #  ('my_field1', 1)
    my_field1: int = hci_field(1)

    # equivalent of:
    #  ('my_field2', '*')
    my_field2: bytes = hci_field("*")

    # equivalent of:
    #  ('my_field3', Address.parse_address)
    my_field3: Address = hci_field(Address.parse_address)

    # equivalent of:
    #  ('my_field4', STATUS_SPEC)
    my_field4: int = hci_field(STATUS_SPEC)

    # equivalent of:
    # [
    #   ('my_field5', 1),
    #   ('my_field6', 1)
    #   ('my_field7', 1)
    # ]
    my_field5: list[int] = hci_field(1, start_list=True)
    my_field6: list[int] = hci_field(1)
    my_field7: list[int] = hci_field(1, end_list=True)


bar = Bar(
    my_field1=7,
    my_field2=b"hello",
    my_field3=Address("AA:BB:CC:DD:EE:FF"),
    my_field4=55,
    my_field5=[1],
    my_field6=[2],
    my_field7=[3],
)
print(dataclasses.fields(bar))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants