From: Nicholas Pratte <npratte@iol.unh.edu>
To: Luca Vizzarro <luca.vizzarro@arm.com>
Cc: dev@dpdk.org, "Juraj Linkeš" <juraj.linkes@pantheon.tech>,
"Jeremy Spewock" <jspewock@iol.unh.edu>,
"Paul Szczepanek" <paul.szczepanek@arm.com>
Subject: Re: [PATCH v2 1/8] dts: add params manipulation module
Date: Tue, 28 May 2024 11:40:50 -0400 [thread overview]
Message-ID: <CAKXZ7ehtmKPC7sW6N+Db2oA8THm+3kO_TwR+wp2bv97g6Y8FtQ@mail.gmail.com> (raw)
In-Reply-To: <20240509112057.1167947-2-luca.vizzarro@arm.com>
Tested-by: Nicholas Pratte <npratte@iol.unh.edu>
Reviewed-by: Nicholas Pratte <npratte@iol.unh.edu>
On Thu, May 9, 2024 at 7:21 AM Luca Vizzarro <luca.vizzarro@arm.com> wrote:
>
> This commit introduces a new "params" module, which adds a new way
> to manage command line parameters. The provided Params dataclass
> is able to read the fields of its child class and produce a string
> representation to supply to the command line. Any data structure
> that is intended to represent command line parameters can inherit it.
>
> The main purpose is to make it easier to represent data structures that
> map to parameters. Aiding quicker development, while minimising code
> bloat.
>
> Signed-off-by: Luca Vizzarro <luca.vizzarro@arm.com>
> Reviewed-by: Paul Szczepanek <paul.szczepanek@arm.com>
> ---
> dts/framework/params/__init__.py | 274 +++++++++++++++++++++++++++++++
> 1 file changed, 274 insertions(+)
> create mode 100644 dts/framework/params/__init__.py
>
> diff --git a/dts/framework/params/__init__.py b/dts/framework/params/__init__.py
> new file mode 100644
> index 0000000000..aa27e34357
> --- /dev/null
> +++ b/dts/framework/params/__init__.py
> @@ -0,0 +1,274 @@
> +# SPDX-License-Identifier: BSD-3-Clause
> +# Copyright(c) 2024 Arm Limited
> +
> +"""Parameter manipulation module.
> +
> +This module provides :class:`Params` which can be used to model any data structure
> +that is meant to represent any command parameters.
> +"""
> +
> +from dataclasses import dataclass, fields
> +from enum import Flag
> +from typing import Any, Callable, Iterable, Literal, Reversible, TypedDict, cast
> +
> +from typing_extensions import Self
> +
> +#: Type for a function taking one argument.
> +FnPtr = Callable[[Any], Any]
> +#: Type for a switch parameter.
> +Switch = Literal[True, None]
> +#: Type for a yes/no switch parameter.
> +YesNoSwitch = Literal[True, False, None]
> +
> +
> +def _reduce_functions(funcs: Reversible[FnPtr]) -> FnPtr:
> + """Reduces an iterable of :attr:`FnPtr` from end to start to a composite function.
> +
> + If the iterable is empty, the created function just returns its fed value back.
> + """
> +
> + def composite_function(value: Any):
> + for fn in reversed(funcs):
> + value = fn(value)
> + return value
> +
> + return composite_function
> +
> +
> +def convert_str(*funcs: FnPtr):
> + """Decorator that makes the ``__str__`` method a composite function created from its arguments.
> +
> + The :attr:`FnPtr`s fed to the decorator are executed from right to left
> + in the arguments list order.
> +
> + Example:
> + .. code:: python
> +
> + @convert_str(hex_from_flag_value)
> + class BitMask(enum.Flag):
> + A = auto()
> + B = auto()
> +
> + will allow ``BitMask`` to render as a hexadecimal value.
> + """
> +
> + def _class_decorator(original_class):
> + original_class.__str__ = _reduce_functions(funcs)
> + return original_class
> +
> + return _class_decorator
> +
> +
> +def comma_separated(values: Iterable[Any]) -> str:
> + """Converts an iterable in a comma-separated string."""
> + return ",".join([str(value).strip() for value in values if value is not None])
> +
> +
> +def bracketed(value: str) -> str:
> + """Adds round brackets to the input."""
> + return f"({value})"
> +
> +
> +def str_from_flag_value(flag: Flag) -> str:
> + """Returns the value from a :class:`enum.Flag` as a string."""
> + return str(flag.value)
> +
> +
> +def hex_from_flag_value(flag: Flag) -> str:
> + """Returns the value from a :class:`enum.Flag` converted to hexadecimal."""
> + return hex(flag.value)
> +
> +
> +class ParamsModifier(TypedDict, total=False):
> + """Params modifiers dict compatible with the :func:`dataclasses.field` metadata parameter."""
> +
> + #:
> + Params_value_only: bool
> + #:
> + Params_short: str
> + #:
> + Params_long: str
> + #:
> + Params_multiple: bool
> + #:
> + Params_convert_value: Reversible[FnPtr]
> +
> +
> +@dataclass
> +class Params:
> + """Dataclass that renders its fields into command line arguments.
> +
> + The parameter name is taken from the field name by default. The following:
> +
> + .. code:: python
> +
> + name: str | None = "value"
> +
> + is rendered as ``--name=value``.
> + Through :func:`dataclasses.field` the resulting parameter can be manipulated by applying
> + this class' metadata modifier functions.
> +
> + To use fields as switches, set the value to ``True`` to render them. If you
> + use a yes/no switch you can also set ``False`` which would render a switch
> + prefixed with ``--no-``. Examples:
> +
> + .. code:: python
> +
> + interactive: Switch = True # renders --interactive
> + numa: YesNoSwitch = False # renders --no-numa
> +
> + Setting ``None`` will prevent it from being rendered. The :attr:`~Switch` type alias is provided
> + for regular switches, whereas :attr:`~YesNoSwitch` is offered for yes/no ones.
> +
> + An instance of a dataclass inheriting ``Params`` can also be assigned to an attribute,
> + this helps with grouping parameters together.
> + The attribute holding the dataclass will be ignored and the latter will just be rendered as
> + expected.
> + """
> +
> + _suffix = ""
> + """Holder of the plain text value of Params when called directly. A suffix for child classes."""
> +
> + """========= BEGIN FIELD METADATA MODIFIER FUNCTIONS ========"""
> +
> + @staticmethod
> + def value_only() -> ParamsModifier:
> + """Injects the value of the attribute as-is without flag.
> +
> + Metadata modifier for :func:`dataclasses.field`.
> + """
> + return ParamsModifier(Params_value_only=True)
> +
> + @staticmethod
> + def short(name: str) -> ParamsModifier:
> + """Overrides any parameter name with the given short option.
> +
> + Metadata modifier for :func:`dataclasses.field`.
> +
> + Example:
> + .. code:: python
> +
> + logical_cores: str | None = field(default="1-4", metadata=Params.short("l"))
> +
> + will render as ``-l=1-4`` instead of ``--logical-cores=1-4``.
> + """
> + return ParamsModifier(Params_short=name)
> +
> + @staticmethod
> + def long(name: str) -> ParamsModifier:
> + """Overrides the inferred parameter name to the specified one.
> +
> + Metadata modifier for :func:`dataclasses.field`.
> +
> + Example:
> + .. code:: python
> +
> + x_name: str | None = field(default="y", metadata=Params.long("x"))
> +
> + will render as ``--x=y``, but the field is accessed and modified through ``x_name``.
> + """
> + return ParamsModifier(Params_long=name)
> +
> + @staticmethod
> + def multiple() -> ParamsModifier:
> + """Specifies that this parameter is set multiple times. Must be a list.
> +
> + Metadata modifier for :func:`dataclasses.field`.
> +
> + Example:
> + .. code:: python
> +
> + ports: list[int] | None = field(
> + default_factory=lambda: [0, 1, 2],
> + metadata=Params.multiple() | Params.long("port")
> + )
> +
> + will render as ``--port=0 --port=1 --port=2``. Note that modifiers can be chained like
> + in this example.
> + """
> + return ParamsModifier(Params_multiple=True)
> +
> + @classmethod
> + def convert_value(cls, *funcs: FnPtr) -> ParamsModifier:
> + """Takes in a variable number of functions to convert the value text representation.
> +
> + Metadata modifier for :func:`dataclasses.field`.
> +
> + The ``metadata`` keyword argument can be used to chain metadata modifiers together.
> +
> + Functions can be chained together, executed from right to left in the arguments list order.
> +
> + Example:
> + .. code:: python
> +
> + hex_bitmask: int | None = field(
> + default=0b1101,
> + metadata=Params.convert_value(hex) | Params.long("mask")
> + )
> +
> + will render as ``--mask=0xd``.
> + """
> + return ParamsModifier(Params_convert_value=funcs)
> +
> + """========= END FIELD METADATA MODIFIER FUNCTIONS ========"""
> +
> + def append_str(self, text: str) -> None:
> + """Appends a string at the end of the string representation."""
> + self._suffix += text
> +
> + def __iadd__(self, text: str) -> Self:
> + """Appends a string at the end of the string representation."""
> + self.append_str(text)
> + return self
> +
> + @classmethod
> + def from_str(cls, text: str) -> Self:
> + """Creates a plain Params object from a string."""
> + obj = cls()
> + obj.append_str(text)
> + return obj
> +
> + @staticmethod
> + def _make_switch(
> + name: str, is_short: bool = False, is_no: bool = False, value: str | None = None
> + ) -> str:
> + prefix = f"{'-' if is_short else '--'}{'no-' if is_no else ''}"
> + name = name.replace("_", "-")
> + value = f"{' ' if is_short else '='}{value}" if value else ""
> + return f"{prefix}{name}{value}"
> +
> + def __str__(self) -> str:
> + """Returns a string of command-line-ready arguments from the class fields."""
> + arguments: list[str] = []
> +
> + for field in fields(self):
> + value = getattr(self, field.name)
> + modifiers = cast(ParamsModifier, field.metadata)
> +
> + if value is None:
> + continue
> +
> + value_only = modifiers.get("Params_value_only", False)
> + if isinstance(value, Params) or value_only:
> + arguments.append(str(value))
> + continue
> +
> + # take the short modifier, or the long modifier, or infer from field name
> + switch_name = modifiers.get("Params_short", modifiers.get("Params_long", field.name))
> + is_short = "Params_short" in modifiers
> +
> + if isinstance(value, bool):
> + arguments.append(self._make_switch(switch_name, is_short, is_no=(not value)))
> + continue
> +
> + convert = _reduce_functions(modifiers.get("Params_convert_value", []))
> + multiple = modifiers.get("Params_multiple", False)
> +
> + values = value if multiple else [value]
> + for value in values:
> + arguments.append(self._make_switch(switch_name, is_short, value=convert(value)))
> +
> + if self._suffix:
> + arguments.append(self._suffix)
> +
> + return " ".join(arguments)
> --
> 2.34.1
>
next prev parent reply other threads:[~2024-05-28 15:41 UTC|newest]
Thread overview: 159+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-03-26 19:04 [PATCH 0/6] dts: add testpmd params and statefulness Luca Vizzarro
2024-03-26 19:04 ` [PATCH 1/6] dts: add parameters data structure Luca Vizzarro
2024-03-28 16:48 ` Jeremy Spewock
2024-04-09 15:52 ` Luca Vizzarro
2024-04-09 12:10 ` Juraj Linkeš
2024-04-09 16:28 ` Luca Vizzarro
2024-04-10 9:15 ` Juraj Linkeš
2024-04-10 9:51 ` Luca Vizzarro
2024-04-10 10:04 ` Juraj Linkeš
2024-03-26 19:04 ` [PATCH 2/6] dts: use Params for interactive shells Luca Vizzarro
2024-03-28 16:48 ` Jeremy Spewock
2024-04-09 14:56 ` Juraj Linkeš
2024-04-10 9:34 ` Luca Vizzarro
2024-04-10 9:58 ` Juraj Linkeš
2024-05-28 15:43 ` Nicholas Pratte
2024-03-26 19:04 ` [PATCH 3/6] dts: add testpmd shell params Luca Vizzarro
2024-03-28 16:48 ` Jeremy Spewock
2024-04-09 16:37 ` Juraj Linkeš
2024-04-10 10:49 ` Luca Vizzarro
2024-04-10 13:17 ` Juraj Linkeš
2024-03-26 19:04 ` [PATCH 4/6] dts: use testpmd params for scatter test suite Luca Vizzarro
2024-04-09 19:12 ` Juraj Linkeš
2024-04-10 10:53 ` Luca Vizzarro
2024-04-10 13:18 ` Juraj Linkeš
2024-04-26 18:06 ` Jeremy Spewock
2024-04-29 7:45 ` Juraj Linkeš
2024-03-26 19:04 ` [PATCH 5/6] dts: add statefulness to InteractiveShell Luca Vizzarro
2024-03-28 16:48 ` Jeremy Spewock
2024-04-10 6:53 ` Juraj Linkeš
2024-04-10 11:27 ` Luca Vizzarro
2024-04-10 13:35 ` Juraj Linkeš
2024-04-10 14:07 ` Luca Vizzarro
2024-04-12 12:33 ` Juraj Linkeš
2024-04-29 14:48 ` Jeremy Spewock
2024-03-26 19:04 ` [PATCH 6/6] dts: add statefulness to TestPmdShell Luca Vizzarro
2024-03-28 16:48 ` Jeremy Spewock
2024-04-10 7:41 ` Juraj Linkeš
2024-04-10 11:35 ` Luca Vizzarro
2024-04-11 10:30 ` Juraj Linkeš
2024-04-11 11:47 ` Luca Vizzarro
2024-04-11 12:13 ` Juraj Linkeš
2024-04-11 13:59 ` Luca Vizzarro
2024-04-26 18:06 ` Jeremy Spewock
2024-04-29 12:06 ` Juraj Linkeš
2024-04-10 7:50 ` Juraj Linkeš
2024-04-10 11:37 ` Luca Vizzarro
2024-05-09 11:20 ` [PATCH v2 0/8] dts: add testpmd params Luca Vizzarro
2024-05-09 11:20 ` [PATCH v2 1/8] dts: add params manipulation module Luca Vizzarro
2024-05-28 15:40 ` Nicholas Pratte [this message]
2024-05-28 21:08 ` Jeremy Spewock
2024-06-06 9:19 ` Juraj Linkeš
2024-06-17 11:44 ` Luca Vizzarro
2024-06-18 8:55 ` Juraj Linkeš
2024-05-09 11:20 ` [PATCH v2 2/8] dts: use Params for interactive shells Luca Vizzarro
2024-05-28 17:43 ` Nicholas Pratte
2024-05-28 21:04 ` Jeremy Spewock
2024-06-06 13:14 ` Juraj Linkeš
2024-05-09 11:20 ` [PATCH v2 3/8] dts: refactor EalParams Luca Vizzarro
2024-05-28 15:44 ` Nicholas Pratte
2024-05-28 21:05 ` Jeremy Spewock
2024-06-06 13:17 ` Juraj Linkeš
2024-05-09 11:20 ` [PATCH v2 4/8] dts: remove module-wide imports Luca Vizzarro
2024-05-28 15:45 ` Nicholas Pratte
2024-05-28 21:08 ` Jeremy Spewock
2024-06-06 13:21 ` Juraj Linkeš
2024-05-09 11:20 ` [PATCH v2 5/8] dts: add testpmd shell params Luca Vizzarro
2024-05-28 15:53 ` Nicholas Pratte
2024-05-28 21:05 ` Jeremy Spewock
2024-05-29 15:59 ` Luca Vizzarro
2024-05-29 17:11 ` Jeremy Spewock
2024-05-09 11:20 ` [PATCH v2 6/8] dts: use testpmd params for scatter test suite Luca Vizzarro
2024-05-28 15:49 ` Nicholas Pratte
2024-05-28 21:06 ` Jeremy Spewock
2024-05-09 11:20 ` [PATCH v2 7/8] dts: rework interactive shells Luca Vizzarro
2024-05-28 15:50 ` Nicholas Pratte
2024-05-28 21:07 ` Jeremy Spewock
2024-05-29 15:57 ` Luca Vizzarro
2024-05-09 11:20 ` [PATCH v2 8/8] dts: use Unpack for type checking and hinting Luca Vizzarro
2024-05-28 15:50 ` Nicholas Pratte
2024-05-28 21:08 ` Jeremy Spewock
2024-05-22 15:59 ` [PATCH v2 0/8] dts: add testpmd params Nicholas Pratte
2024-05-30 15:24 ` [PATCH v3 " Luca Vizzarro
2024-05-30 15:24 ` [PATCH v3 1/8] dts: add params manipulation module Luca Vizzarro
2024-05-30 20:12 ` Jeremy Spewock
2024-05-31 15:19 ` Nicholas Pratte
2024-05-30 15:24 ` [PATCH v3 2/8] dts: use Params for interactive shells Luca Vizzarro
2024-05-30 20:12 ` Jeremy Spewock
2024-05-31 15:20 ` Nicholas Pratte
2024-05-30 15:25 ` [PATCH v3 3/8] dts: refactor EalParams Luca Vizzarro
2024-05-30 20:12 ` Jeremy Spewock
2024-05-31 15:21 ` Nicholas Pratte
2024-05-30 15:25 ` [PATCH v3 4/8] dts: remove module-wide imports Luca Vizzarro
2024-05-30 20:12 ` Jeremy Spewock
2024-05-31 15:21 ` Nicholas Pratte
2024-05-30 15:25 ` [PATCH v3 5/8] dts: add testpmd shell params Luca Vizzarro
2024-05-30 20:12 ` Jeremy Spewock
2024-05-31 15:20 ` Nicholas Pratte
2024-06-06 14:37 ` Juraj Linkeš
2024-05-30 15:25 ` [PATCH v3 6/8] dts: use testpmd params for scatter test suite Luca Vizzarro
2024-05-30 20:13 ` Jeremy Spewock
2024-05-31 15:22 ` Nicholas Pratte
2024-06-06 14:38 ` Juraj Linkeš
2024-05-30 15:25 ` [PATCH v3 7/8] dts: rework interactive shells Luca Vizzarro
2024-05-30 20:13 ` Jeremy Spewock
2024-05-31 15:22 ` Nicholas Pratte
2024-06-06 18:03 ` Juraj Linkeš
2024-06-17 12:13 ` Luca Vizzarro
2024-06-18 9:18 ` Juraj Linkeš
2024-05-30 15:25 ` [PATCH v3 8/8] dts: use Unpack for type checking and hinting Luca Vizzarro
2024-05-30 20:13 ` Jeremy Spewock
2024-05-31 15:21 ` Nicholas Pratte
2024-06-06 18:05 ` Juraj Linkeš
2024-06-17 14:42 ` [PATCH v4 0/8] dts: add testpmd params and statefulness Luca Vizzarro
2024-06-17 14:42 ` [PATCH v4 1/8] dts: add params manipulation module Luca Vizzarro
2024-06-17 14:42 ` [PATCH v4 2/8] dts: use Params for interactive shells Luca Vizzarro
2024-06-17 14:42 ` [PATCH v4 3/8] dts: refactor EalParams Luca Vizzarro
2024-06-17 14:42 ` [PATCH v4 4/8] dts: remove module-wide imports Luca Vizzarro
2024-06-17 14:42 ` [PATCH v4 5/8] dts: add testpmd shell params Luca Vizzarro
2024-06-17 14:42 ` [PATCH v4 6/8] dts: use testpmd params for scatter test suite Luca Vizzarro
2024-06-17 14:42 ` [PATCH v4 7/8] dts: rework interactive shells Luca Vizzarro
2024-06-17 14:42 ` [PATCH v4 8/8] dts: use Unpack for type checking and hinting Luca Vizzarro
2024-06-17 14:54 ` [PATCH v5 0/8] dts: add testpmd params Luca Vizzarro
2024-06-17 14:54 ` [PATCH v5 1/8] dts: add params manipulation module Luca Vizzarro
2024-06-17 15:22 ` Nicholas Pratte
2024-06-17 14:54 ` [PATCH v5 2/8] dts: use Params for interactive shells Luca Vizzarro
2024-06-17 15:23 ` Nicholas Pratte
2024-06-17 14:54 ` [PATCH v5 3/8] dts: refactor EalParams Luca Vizzarro
2024-06-17 15:23 ` Nicholas Pratte
2024-06-17 14:54 ` [PATCH v5 4/8] dts: remove module-wide imports Luca Vizzarro
2024-06-17 15:23 ` Nicholas Pratte
2024-06-17 14:54 ` [PATCH v5 5/8] dts: add testpmd shell params Luca Vizzarro
2024-06-17 15:24 ` Nicholas Pratte
2024-06-17 14:54 ` [PATCH v5 6/8] dts: use testpmd params for scatter test suite Luca Vizzarro
2024-06-17 15:24 ` Nicholas Pratte
2024-06-17 14:54 ` [PATCH v5 7/8] dts: rework interactive shells Luca Vizzarro
2024-06-17 15:25 ` Nicholas Pratte
2024-06-17 14:54 ` [PATCH v5 8/8] dts: use Unpack for type checking and hinting Luca Vizzarro
2024-06-17 15:25 ` Nicholas Pratte
2024-06-19 10:23 ` [PATCH v6 0/8] dts: add testpmd params Luca Vizzarro
2024-06-19 10:23 ` [PATCH v6 1/8] dts: add params manipulation module Luca Vizzarro
2024-06-19 12:45 ` Juraj Linkeš
2024-06-19 10:23 ` [PATCH v6 2/8] dts: use Params for interactive shells Luca Vizzarro
2024-06-19 10:23 ` [PATCH v6 3/8] dts: refactor EalParams Luca Vizzarro
2024-06-19 10:23 ` [PATCH v6 4/8] dts: remove module-wide imports Luca Vizzarro
2024-06-19 10:23 ` [PATCH v6 5/8] dts: add testpmd shell params Luca Vizzarro
2024-06-19 10:23 ` [PATCH v6 6/8] dts: use testpmd params for scatter test suite Luca Vizzarro
2024-06-19 10:23 ` [PATCH v6 7/8] dts: rework interactive shells Luca Vizzarro
2024-06-19 12:49 ` Juraj Linkeš
2024-06-19 10:23 ` [PATCH v6 8/8] dts: use Unpack for type checking and hinting Luca Vizzarro
2024-06-19 14:02 ` [PATCH v7 0/8] dts: add testpmd params Luca Vizzarro
2024-06-19 14:02 ` [PATCH v7 1/8] dts: add params manipulation module Luca Vizzarro
2024-06-19 14:02 ` [PATCH v7 2/8] dts: use Params for interactive shells Luca Vizzarro
2024-06-19 14:03 ` [PATCH v7 3/8] dts: refactor EalParams Luca Vizzarro
2024-06-19 14:03 ` [PATCH v7 4/8] dts: remove module-wide imports Luca Vizzarro
2024-06-19 14:03 ` [PATCH v7 5/8] dts: add testpmd shell params Luca Vizzarro
2024-06-19 14:03 ` [PATCH v7 6/8] dts: use testpmd params for scatter test suite Luca Vizzarro
2024-06-19 14:03 ` [PATCH v7 7/8] dts: rework interactive shells Luca Vizzarro
2024-06-19 14:03 ` [PATCH v7 8/8] dts: use Unpack for type checking and hinting Luca Vizzarro
2024-06-20 3:36 ` [PATCH v7 0/8] dts: add testpmd params Thomas Monjalon
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=CAKXZ7ehtmKPC7sW6N+Db2oA8THm+3kO_TwR+wp2bv97g6Y8FtQ@mail.gmail.com \
--to=npratte@iol.unh.edu \
--cc=dev@dpdk.org \
--cc=jspewock@iol.unh.edu \
--cc=juraj.linkes@pantheon.tech \
--cc=luca.vizzarro@arm.com \
--cc=paul.szczepanek@arm.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).