From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id BC18E43E2C; Tue, 9 Apr 2024 18:28:16 +0200 (CEST) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 4855A402C7; Tue, 9 Apr 2024 18:28:16 +0200 (CEST) Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by mails.dpdk.org (Postfix) with ESMTP id 8112D4025D for ; Tue, 9 Apr 2024 18:28:14 +0200 (CEST) Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 116A8139F; Tue, 9 Apr 2024 09:28:44 -0700 (PDT) Received: from [10.1.38.52] (FVFG51LCQ05N.cambridge.arm.com [10.1.38.52]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 5B6EB3F6C4; Tue, 9 Apr 2024 09:28:13 -0700 (PDT) Message-ID: Date: Tue, 9 Apr 2024 17:28:12 +0100 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Subject: Re: [PATCH 1/6] dts: add parameters data structure Content-Language: en-GB To: =?UTF-8?Q?Juraj_Linke=C5=A1?= Cc: dev@dpdk.org, Jack Bond-Preston , Honnappa Nagarahalli References: <20240326190422.577028-1-luca.vizzarro@arm.com> <20240326190422.577028-2-luca.vizzarro@arm.com> From: Luca Vizzarro In-Reply-To: Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Thank you so much for your review Juraj! On 09/04/2024 13:10, Juraj Linkeš wrote: > We shouldn't list what the patch does, but rather explain the choices > made within. It seems to me the patch is here to give devs an API > instead of them having to construct strings - explaining why this > approach improves the old one should be in the commit message. > Ack. >> +META_VALUE_ONLY = "value_only" >> +META_OPTIONS_END = "options_end" >> +META_SHORT_NAME = "short_name" >> +META_LONG_NAME = "long_name" >> +META_MULTIPLE = "multiple" >> +META_MIXINS = "mixins" > > These are only used in the Params class (but not set), so I'd either > move them there or make them private. > Ack. >> + >> +def value_only(metadata: dict[str, Any] = {}) -> dict[str, Any]: >> + """Injects the value of the attribute as-is without flag. Metadata modifier for :func:`dataclasses.field`.""" >> + return {**metadata, META_VALUE_ONLY: True} > > These methods, on the other hand, are used outside this module, so it > makes sense to have them outside Params. It could be better to have > them inside as static methods, as they're closely related. Looking at > how they're used, we should unite the imports: > 1. In testpmd_shell, they're imported directly (from framework.params > import long) > 2. In sut_node, just the params module is imported (from framework > import params and then accessed via it: metadata=params.short("l")) > Having a shorter version may look less verbose. I agree that we can make everything a static method of Params, but then having to do Params.short etc everytime will make it look more verbose. So what option do we prefer? The functions do belong to the params module nonetheless, and therefore are meant to be used in conjunction with the Params class. > If we move these to Params, we could import Params and use them > similarly to 2. Not sure which one is better. > >> +def field_mixins(*mixins: Mixin, metadata: dict[str, Any] = {}) -> dict[str, Any]: > > Any reason why mixins are plural? I've only seen this used with one > argument, do you anticipate we'd need to use more than one? We could > make this singular and if we ever need to do two things, we could just > pass a function that does those two things (or calls two different > functions). Also, I'd just rename the mixin the func or something like > that. > > The default of an argument should not be mutable, here's a quick > explanation: https://docs.python-guide.org/writing/gotchas/#mutable-default-arguments Indeed the reason for which I create dictionaries, as I am treating them as read only. I wanted to avoid to bloat the code with lots of `is None` checks. But we can sacrifice this optimization for better code. > I don't really like the name. The positional arguments are applied to > the value and that should be reflected in the name - I'd like to see > something like convert in the name. What this does is effectively just add the mixins to dataclass.field metadata, hence "field"_mixins. This is meant to represent a chain of mixins, in my original code this appeared more often. Not so much in this post, as I made more optimisations. Which takes me to the plural bit, for testpmd specifically this plurality appears only when decorating another class using `str_mixins`, see TestPmdRingNUMAConfig. So for consistency I kept both to ingest a chain of "mixins", as maybe it'll be needed in the future. Are you suggesting to just make the name as singular? But still take multiple function pointers? The term "mixin" specifically just means a middleware that manipulates the output value when using __str__. Here we are just chaining them for reusability. Do you have any better name in mind? >> + return {**metadata, META_MIXINS: mixins} > > metadata | {META_MIXINS: mixins} also creates a new dictionary with > values from both and I think that would be more readable (since it's > mentioned in docs: > https://docs.python.org/3/library/stdtypes.html#mapping-types-dict). If we were to use `None` as default to the arguments, then this would no longer be needed. But great shout, wasn't aware of this feature added in 3.9. >> +def _reduce_mixins(mixins: Reversible[Mixin], value: Any) -> str: >> + for mixin in reversed(mixins): >> + value = mixin(value) >> + return value >> + >> + >> +def str_mixins(*mixins: Mixin): > > Again the name, this doesn't strike me as a decorator name. A > decorator modifies the thing it's decorating so it should contain a > verb describing what it's doing. > > Maybe we could also do singular mixin here, as I described above. It > may result in cleaner API. Great point. Yes making the function name sound like a verb is definitely the right way. Thank you for pointing it out. >> +@dataclass >> +class Params: >> + """Helper abstract dataclass that renders its fields into command line arguments. > > Let's make the abstract class explicitly abstract with > https://docs.python.org/3/library/abc.html#abc.ABC. It won't be a full > abstract class since it won't have any abstract method or properties, > but I think it'll be better this way. No problem. I'm not sure if applying ABC to a dataclass may complicate things. But can definitely do. >> + To use fields as option switches set the value to ``True`` to enable them. If you > > There should be a comma between switches and set. Ack. >> + use a yes/no option switch you can also set ``False`` which would enable an option >> + prefixed with ``--no-``. Examples: >> + >> + .. code:: python >> + >> + interactive: Option = True # renders --interactive >> + numa: BooleanOption = False # renders --no-numa > > I'm wondering whether these could be simplified, but it's pretty good > this way. I'd change the names though: > Option -> Switch > BooleanOption -> NoSwitch (or YesNoSwitch? NegativeSwitch? Not sure > about this one) > > All options (short, long, etc.) are options so that's where it's > confusing. The BooleanOption doesn't really capture what we're doing > with it (prepending no-) so I want a name that captures it. > > There could also be some confusion with this being different from the > rest of the options API. This only requires us to set the type, the > rest must be passed in dataclasses.field. It's probably not that big > of a deal though. > I am glad you are bringing this up. I am also perplexed on the choice of name here. I decided to use whatever libc getopts uses. But your suggestion sounds nice. Will use it. >> + >> + Setting ``None`` will disable any option. > > I'd reword this to "Setting an option to ``None`` will prevent it from > being rendered." or something similar. Disabling an option is kinda > ambiguous. > Ack. >> + options_end = field.metadata.get(META_OPTIONS_END, False) >> + if options_end: >> + arguments.append("--") > > This is a confusing way to separate the options. The separator itself > is an option (I'd rename it to sep or separator instead of end), so > I'd make a separate field for it. We could pass init=False to field() > in the field definition, but that relies on fields being ordered, so > we'd need to also pass order=True to the dataclass constructor. Ack.