Suggestions for the Python HLA API

The high-level analyzer feature and measurements is a fantastic feature, absolute game changer and very easy to get started with.

With that in mind, I would like to suggest making the API a bit more structured by using classes and enums rather than dicts and strings (of course, if you guys intend to stick with the dictionaries, it would not be too hard for someone in the community to make a wrapper library to provide this kind of functionality).

What I mean is that get_capabilities returns a Capabilities object rather than a dict, set_settings receives a UserSettings object rather than a dict, and so on.
Furthermore, strings that can only have a fixed set of values (such as the “type” field for I2C frames) are represented by some sort of enumerated type.

Benefits:

  • IDEs like PyCharm will show warnings if there is a typo or if the wrong types are used
  • IDEs like PyCharm will provide autocomplete
  • You can look up the source code to see what kind of data types are expected
  • Class docstrings can be extracted by e.g. Sphinx and used to generate up-to-date documentation

Downsides:

  • Increased code size in some cases (probably mostly the example cases)
  • Examples might look a bit more intimidating to some users

Below is an example of how a HLA implementation might look with such an API (names are just made up of course).

import saleae_logic_high_level_analyzers as hla

class MyAnalyzer(object):
    def get_capabilities(self) -> hla.Capabilities:
        return hla.Capabilities(
            settings=[
                hla.UserInput(
                    label="First setting label",
                    type=hla.String()
                ),
                hla.UserInput(
                    label="Another setting",
                    type=hla.Number(minimum=1e-6, maximum=1e4)
                ),
                hla.UserInput(
                    label="Pick one of the following",
                    type=hla.Choices(["Option A", "Option B"])
                ),
            ]
        )

    def set_settings(self, settings: hla.UserSettings) -> hla.AnalyzerSettings:
        self.setting_1 = settings.get("First setting label", default="")
        self.setting_2 = settings.get("Another setting", default=1)
        self.setting_3 = settings.get("Pick one of the following", default="Option A")

        return hla.AnalyzerSettings(
            result_types=[
                hla.ResultType(
                    type="error",
                    format="Error!"
                ),
                hla.ResultType(
                    type="my_type",
                    format="Field 1: {{data.field_1}}, Field 2: {{data.field_2}}"
                ),
            ]
        )

    def decode(self, input_data: hla.I2CFrame):
        if input_data.type == hla.I2CStart:
            self.handle_start(input_data)
        elif input_data.type == hla.I2CStop:
            self.handle_stop(input_data)
        elif input_data.type == hla.I2CAddress:
            self.handle_address(input_data)
        elif input_data.type == hla.I2CData:
            self.handle_data(input_data)

        return self.get_frames()

    # ... definitions for handle_start, handle_stop, handle_address, handle_data, get_frames etc
1 Like

I really second this! It is also useful to note the developers of the existing protocol decoders are familiar with C++ which is a more typed language than Python. And for e.g. for me it is always easier to get used to something which is more typed.

@jonathan.gjertsen sorry for not responding earlier. I somehow missed your post until @martonmiklosqdev_sal replied…

We wanted to reduce the friction as much as possible and make the example less intimidating, as you also mentioned. We’ll release some new features next month and we can definitely consider changing the API a bit (or extending it to support both). I’ll keep you posted.

Thanks a lot for the feedback :slight_smile: