DPDK-dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 3/6] bpf/arm64: fix offset type to allow a negative jump
From: Stephen Hemminger @ 2026-06-18 20:47 UTC (permalink / raw)
  To: dev
  Cc: Christophe Fontaine, stable, Stephen Hemminger,
	Wathsala Vithanage, Konstantin Ananyev, Marat Khalili,
	Jerin Jacob
In-Reply-To: <20260618210026.430288-1-stephen@networkplumber.org>

From: Christophe Fontaine <cfontain@redhat.com>

The DPDK BPF JIT standalone test test_ld_mbuf1 fails on arm64.
It does:
	r6 = r1                    // mbuf
	r0 = *(u8 *)pkt[0]         // BPF_ABS
	if ((r0 & 0xf0) == 0x40)
		goto parse
	r0 = 0
	exit                       // epilogue E0
parse:
	r0 = *(u8 *)pkt[r0 + 3]    // BPF_IND
	...
	exit

emit_return_zero_if_src_zero() returns 0 by branching to a function
epilogue. The target maybe a previous epilogue so branch
might be backwards; therefore the offset needs to be negative.

The offset was stored in a uint16_t, so a negative value wrapped to a
large positive number; emit_b() then branched past the end of the
program and faulted at run time.

Fixes: 111e2a747a4f ("bpf/arm: add basic arithmetic operations")
Cc: stable@dpdk.org

Signed-off-by: Christophe Fontaine <cfontain@redhat.com>
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
 lib/bpf/bpf_jit_arm64.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/lib/bpf/bpf_jit_arm64.c b/lib/bpf/bpf_jit_arm64.c
index a04ef33a9c..67e42015de 100644
--- a/lib/bpf/bpf_jit_arm64.c
+++ b/lib/bpf/bpf_jit_arm64.c
@@ -957,10 +957,12 @@ static void
 emit_return_zero_if_src_zero(struct a64_jit_ctx *ctx, bool is64, uint8_t src)
 {
 	uint8_t r0 = ebpf_to_a64_reg(ctx, EBPF_REG_0);
-	uint16_t jump_to_epilogue;
+	int32_t jump_to_epilogue;
 
 	emit_cbnz(ctx, is64, src, 3);
 	emit_mov_imm(ctx, is64, r0, 0);
+
+	/* maybe backwards branch to earlier epilogue */
 	jump_to_epilogue = (ctx->program_start + ctx->program_sz) - ctx->idx;
 	emit_b(ctx, jump_to_epilogue);
 }
-- 
2.53.0


^ permalink raw reply related

* [PATCH v2 2/6] test/bpf: add JSET test with small immediate
From: Stephen Hemminger @ 2026-06-18 20:47 UTC (permalink / raw)
  To: dev; +Cc: Stephen Hemminger, Konstantin Ananyev, Marat Khalili
In-Reply-To: <20260618210026.430288-1-stephen@networkplumber.org>

The existing jump test only used a 32-bit JSET mask,
so the broken imm8 encoding of TEST in the x86 JIT was never exercised.
Add a case with a byte-sized mask;
run_test() runs it through the interpreter and the JIT.

Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
 app/test/test_bpf.c | 82 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 82 insertions(+)

diff --git a/app/test/test_bpf.c b/app/test/test_bpf.c
index dd24722450..e70dea736f 100644
--- a/app/test/test_bpf.c
+++ b/app/test/test_bpf.c
@@ -3158,7 +3158,89 @@ static const struct ebpf_insn test_ld_mbuf3_prog[] = {
 };
 
 /* all bpf test cases */
+/*
+ * JSET with a byte-sized mask: exercises the imm8 path of the TEST
+ * encoding in the x86 JIT (a 32-bit mask takes a different path).
+ */
+static const struct ebpf_insn test_jset1_prog[] = {
+	{
+		.code = (BPF_ALU | EBPF_MOV | BPF_K),
+		.dst_reg = EBPF_REG_0,
+		.imm = 0,
+	},
+	{
+		.code = (BPF_LDX | BPF_MEM | BPF_B),
+		.dst_reg = EBPF_REG_2,
+		.src_reg = EBPF_REG_1,
+		.off = offsetof(struct dummy_offset, u8),
+	},
+	/* bit 0 is set in the input: branch is taken */
+	{
+		.code = (BPF_JMP | BPF_JSET | BPF_K),
+		.dst_reg = EBPF_REG_2,
+		.imm = 0x1,
+		.off = 1,
+	},
+	{
+		.code = (BPF_JMP | BPF_JA),
+		.off = 1,
+	},
+	{
+		.code = (EBPF_ALU64 | BPF_OR | BPF_K),
+		.dst_reg = EBPF_REG_0,
+		.imm = 0x1,
+	},
+	/* bit 1 is clear in the input: branch is not taken */
+	{
+		.code = (BPF_JMP | BPF_JSET | BPF_K),
+		.dst_reg = EBPF_REG_2,
+		.imm = 0x2,
+		.off = 1,
+	},
+	{
+		.code = (BPF_JMP | BPF_JA),
+		.off = 1,
+	},
+	{
+		.code = (EBPF_ALU64 | BPF_OR | BPF_K),
+		.dst_reg = EBPF_REG_0,
+		.imm = 0x2,
+	},
+	{
+		.code = (BPF_JMP | EBPF_EXIT),
+	},
+};
+
+static void
+test_jset1_prepare(void *arg)
+{
+	struct dummy_offset *df = arg;
+
+	memset(df, 0, sizeof(*df));
+	df->u8 = 0x1;	/* bit 0 set, bit 1 clear */
+}
+
+static int
+test_jset1_check(uint64_t rc, const void *arg)
+{
+	return cmp_res(__func__, 0x1, rc, arg, arg, 0);
+}
+
 static const struct bpf_test tests[] = {
+	{
+		.name = "test_jset1",
+		.arg_sz = sizeof(struct dummy_offset),
+		.prm = {
+			.ins = test_jset1_prog,
+			.nb_ins = RTE_DIM(test_jset1_prog),
+			.prog_arg = {
+				.type = RTE_BPF_ARG_PTR,
+				.size = sizeof(struct dummy_offset),
+			},
+		},
+		.prepare = test_jset1_prepare,
+		.check_result = test_jset1_check,
+	},
 	{
 		.name = "test_store1",
 		.arg_sz = sizeof(struct dummy_offset),
-- 
2.53.0


^ permalink raw reply related

* [PATCH v2 1/6] bpf/x86: fix JIT encoding of BPF_JSET with immediate
From: Stephen Hemminger @ 2026-06-18 20:47 UTC (permalink / raw)
  To: dev
  Cc: Stephen Hemminger, stable, Konstantin Ananyev, Marat Khalili,
	Ferruh Yigit
In-Reply-To: <20260618210026.430288-1-stephen@networkplumber.org>

emit_tst_imm() emits TEST (0xF7 /0) but sized the immediate with
imm_size(), which can return 1 byte. TEST has no imm8 form; it always
takes imm32. A small mask like BPF_JSET | BPF_K #0x1 then produced a
4-byte instruction the CPU decodes as 7, swallowing the following Jcc
and crashing.

Always emit a 32-bit immediate for TEST.

Bugzilla ID: 1959
Fixes: cc752e43e079 ("bpf: add JIT compilation for x86_64 ISA")
Cc: stable@dpdk.org

Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
 lib/bpf/bpf_jit_x86.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/bpf/bpf_jit_x86.c b/lib/bpf/bpf_jit_x86.c
index 88b1b5aeab..0ffe3783ff 100644
--- a/lib/bpf/bpf_jit_x86.c
+++ b/lib/bpf/bpf_jit_x86.c
@@ -921,7 +921,7 @@ emit_tst_imm(struct bpf_jit_state *st, uint32_t op, uint32_t dreg, uint32_t imm)
 	emit_rex(st, op, 0, dreg);
 	emit_bytes(st, &ops, sizeof(ops));
 	emit_modregrm(st, MOD_DIRECT, mods, dreg);
-	emit_imm(st, imm, imm_size(imm));
+	emit_imm(st, imm, sizeof(int32_t));
 }
 
 static void
-- 
2.53.0


^ permalink raw reply related

* [PATCH v2 0/6] bpf: JIT related bug fixes
From: Stephen Hemminger @ 2026-06-18 20:47 UTC (permalink / raw)
  To: dev; +Cc: Stephen Hemminger
In-Reply-To: <20260608203322.1116296-1-stephen@networkplumber.org>

While implementing JIT for packet capture ran into several issues:
  1. x86 JIT had pre-existing bug which would crash
  2. ARM64 BPF JIT was missing instructions for packet access.
     Which had been discovered previously [1]
  3. Tests related to JIT were not being run or missing coverage.

Fixed all of these. Patches are ordered so that most urgent fix
is first, follwed by the test that should have caught the problem.

The arm64 epilogue branch fix (patch 3) was originally posted by Christophe
Fontaine [1]; that series stalled, so it is carried here with his
authorship.

Changes since v1:
 - add x86 BPF_JSET encoding fix and a regression test (patches 1-2),
   found once the convert test ran generated code through the JIT
 - carry Christophe's arm64 epilogue fix with his sign-off (patch 3)
 - convert test now runs the converted filters through the JIT, not just
   loads them (patch 6)
 - kept Marat's ack (patch 4)
   Since tests change enough, decided to drop his ack for that part.

[1] https://inbox.dpdk.org/dev/20260319114500.9757-2-cfontain@redhat.com/

Christophe Fontaine (1):
  bpf/arm64: fix offset type to allow a negative jump

Stephen Hemminger (5):
  bpf/x86: fix JIT encoding of BPF_JSET with immediate
  test/bpf: add JSET test with small immediate
  test/bpf: check that JIT was generated
  bpf/arm64: add BPF_ABS/BPF_IND packet load support
  test/bpf: check that bpf_convert can be JIT'd

 app/test/test_bpf.c     | 184 ++++++++++++++++++++++++++++++++++++----
 lib/bpf/bpf_jit_arm64.c | 153 ++++++++++++++++++++++++++++++++-
 lib/bpf/bpf_jit_x86.c   |   2 +-
 3 files changed, 321 insertions(+), 18 deletions(-)

-- 
2.53.0


^ permalink raw reply

* [PATCH v2] dts: update dts check format script and resolve errors
From: Koushik Bhargav Nimoji @ 2026-06-18 20:45 UTC (permalink / raw)
  To: luca.vizzarro, patrickrobb1997
  Cc: dev, abailey, ahassick, lylavoie, Koushik Bhargav Nimoji
In-Reply-To: <20260609144501.1441468-1-knimoji@iol.unh.edu>

This patch updates the tool versions used in the dts-check-format.sh
script; by doing so, formatting and type hinting errors that weren't
previously visible have now appeared. This patch also resolves those
new formatting and type hinting errors.

Signed-off-by: Koushik Bhargav Nimoji <knimoji@iol.unh.edu>
---
v2:
    *Fixed type hinting issues stemming from ParamSpec
     issues.
    *Fixed tarball write type parameter for copying
     directories from one node to another. 
---
 dts/api/packet.py                             |  15 +-
 dts/api/testpmd/__init__.py                   |  12 +-
 dts/api/testpmd/types.py                      |   6 +-
 dts/framework/config/__init__.py              |  24 +-
 dts/framework/config/test_run.py              |  12 +-
 dts/framework/context.py                      |   8 +-
 .../interactive_remote_session.py             |   2 +-
 .../remote_session/interactive_shell.py       |   6 +-
 .../remote_session/remote_session.py          |   4 +-
 dts/framework/remote_session/shell_pool.py    |   2 +-
 dts/framework/settings.py                     |   8 +-
 dts/framework/testbed_model/cpu.py            |   2 +-
 .../testbed_model/traffic_generator/scapy.py  |   6 +-
 .../testbed_model/traffic_generator/trex.py   |   6 +-
 dts/framework/utils.py                        |  13 +-
 dts/poetry.lock                               | 368 ++++++++++++++----
 dts/pyproject.toml                            |   8 +-
 dts/tests/TestSuite_cryptodev_throughput.py   |   4 +-
 dts/tests/TestSuite_port_control.py           |   3 +-
 .../TestSuite_single_core_forward_perf.py     |   2 +-
 20 files changed, 372 insertions(+), 139 deletions(-)

diff --git a/dts/api/packet.py b/dts/api/packet.py
index 094a1b7a9d..3dda18e781 100644
--- a/dts/api/packet.py
+++ b/dts/api/packet.py
@@ -87,9 +87,9 @@ def send_packets_and_capture(
         CapturingTrafficGenerator,
     )
 
-    assert isinstance(
-        get_ctx().func_tg, CapturingTrafficGenerator
-    ), "Cannot capture with a non-capturing traffic generator"
+    assert isinstance(get_ctx().func_tg, CapturingTrafficGenerator), (
+        "Cannot capture with a non-capturing traffic generator"
+    )
     tg: CapturingTrafficGenerator = cast(CapturingTrafficGenerator, get_ctx().func_tg)
     # TODO: implement @requires for types of traffic generator
     packets = adjust_addresses(packets)
@@ -308,8 +308,7 @@ def _verify_l2_frame(received_packet: Ether, contains_l3: bool) -> bool:
     if contains_l3:
         expected_src_mac = get_ctx().topology.sut_port_egress.mac_address
     log_debug(
-        f"Comparing received src mac '{received_packet.src}' "
-        f"with expected '{expected_src_mac}'."
+        f"Comparing received src mac '{received_packet.src}' with expected '{expected_src_mac}'."
     )
     if received_packet.src != expected_src_mac:
         return False
@@ -344,9 +343,9 @@ def assess_performance_by_packet(
         PerformanceTrafficGenerator,
     )
 
-    assert isinstance(
-        get_ctx().perf_tg, PerformanceTrafficGenerator
-    ), "Cannot send performance traffic with non-performance traffic generator"
+    assert isinstance(get_ctx().perf_tg, PerformanceTrafficGenerator), (
+        "Cannot send performance traffic with non-performance traffic generator"
+    )
     tg: PerformanceTrafficGenerator = cast(PerformanceTrafficGenerator, get_ctx().perf_tg)
     # TODO: implement @requires for types of traffic generator
     return tg.calculate_traffic_and_stats(packet, duration, send_mpps)
diff --git a/dts/api/testpmd/__init__.py b/dts/api/testpmd/__init__.py
index e9187440bb..f4767c6aeb 100644
--- a/dts/api/testpmd/__init__.py
+++ b/dts/api/testpmd/__init__.py
@@ -76,7 +76,9 @@ def _requires_stopped_ports(func: TestPmdMethod) -> TestPmdMethod:
     """
 
     @functools.wraps(func)
-    def _wrapper(self: "TestPmd", *args: P.args, **kwargs: P.kwargs) -> Any:
+    def _wrapper(
+        self: "TestPmd", *args: P.args, **kwargs: P.kwargs
+    ) -> Callable[Concatenate["TestPmd", P], Any]:
         if self.ports_started:
             self._logger.debug("Ports need to be stopped to continue.")
             self.stop_all_ports()
@@ -100,7 +102,9 @@ def _requires_started_ports(func: TestPmdMethod) -> TestPmdMethod:
     """
 
     @functools.wraps(func)
-    def _wrapper(self: "TestPmd", *args: P.args, **kwargs: P.kwargs) -> Any:
+    def _wrapper(
+        self: "TestPmd", *args: P.args, **kwargs: P.kwargs
+    ) -> Callable[Concatenate["TestPmd", P], Any]:
         if not self.ports_started:
             self._logger.debug("Ports need to be started to continue.")
             self.start_all_ports()
@@ -128,7 +132,9 @@ def _add_remove_mtu(mtu: int = 1500) -> Callable[[TestPmdMethod], TestPmdMethod]
 
     def decorator(func: TestPmdMethod) -> TestPmdMethod:
         @functools.wraps(func)
-        def wrapper(self: "TestPmd", *args: P.args, **kwargs: P.kwargs) -> Any:
+        def wrapper(
+            self: "TestPmd", *args: P.args, **kwargs: P.kwargs
+        ) -> Callable[Concatenate["TestPmd", P], Any]:
             original_mtu = self.ports[0].mtu
             self.set_port_mtu_all(mtu=mtu, verify=False)
             retval = func(self, *args, **kwargs)
diff --git a/dts/api/testpmd/types.py b/dts/api/testpmd/types.py
index 0d322aece2..af3263682e 100644
--- a/dts/api/testpmd/types.py
+++ b/dts/api/testpmd/types.py
@@ -279,7 +279,7 @@ def from_list_string(cls, names: str) -> Self:
         Returns:
             An instance of this flag.
         """
-        flag = cls(0)
+        flag: RSSOffloadTypesFlag = cls(0)
         for name in names.split():
             flag |= cls.from_str(name)
         return flag
@@ -960,7 +960,7 @@ def from_list_string(cls, names: str) -> Self:
         Returns:
             An instance of this flag.
         """
-        flag = cls(0)
+        flag: PacketOffloadFlag = cls(0)
         for name in names.split():
             flag |= cls.from_str(name)
         return flag
@@ -1168,7 +1168,7 @@ def from_list_string(cls, names: str) -> Self:
         Returns:
             An instance of this flag.
         """
-        flag = cls(0)
+        flag: RtePTypes = cls(0)
         for name in names.split():
             flag |= cls.from_str(name)
         return flag
diff --git a/dts/framework/config/__init__.py b/dts/framework/config/__init__.py
index d2f0138e4a..a8861894b7 100644
--- a/dts/framework/config/__init__.py
+++ b/dts/framework/config/__init__.py
@@ -85,9 +85,9 @@ def validate_port_links(self) -> Self:
             sut_node_port_peer = existing_port_links.get(
                 (self.test_run.system_under_test_node, link.sut_port), None
             )
-            assert (
-                sut_node_port_peer is not None
-            ), f"Invalid SUT node port specified for link port_topology.{link_idx}."
+            assert sut_node_port_peer is not None, (
+                f"Invalid SUT node port specified for link port_topology.{link_idx}."
+            )
 
             assert sut_node_port_peer is False or sut_node_port_peer == link.right, (
                 f"The SUT node port for link port_topology.{link_idx} is "
@@ -97,9 +97,9 @@ def validate_port_links(self) -> Self:
             tg_node_port_peer = existing_port_links.get(
                 (self.test_run.traffic_generator_node, link.tg_port), None
             )
-            assert (
-                tg_node_port_peer is not None
-            ), f"Invalid TG node port specified for link port_topology.{link_idx}."
+            assert tg_node_port_peer is not None, (
+                f"Invalid TG node port specified for link port_topology.{link_idx}."
+            )
 
             assert tg_node_port_peer is False or sut_node_port_peer == link.left, (
                 f"The TG node port for link port_topology.{link_idx} is "
@@ -117,16 +117,16 @@ def validate_test_run_against_nodes(self) -> Self:
         sut_node_name = self.test_run.system_under_test_node
         sut_node = next((n for n in self.nodes if n.name == sut_node_name), None)
 
-        assert (
-            sut_node is not None
-        ), f"The system_under_test_node {sut_node_name} is not a valid node name."
+        assert sut_node is not None, (
+            f"The system_under_test_node {sut_node_name} is not a valid node name."
+        )
 
         tg_node_name = self.test_run.traffic_generator_node
         tg_node = next((n for n in self.nodes if n.name == tg_node_name), None)
 
-        assert (
-            tg_node is not None
-        ), f"The traffic_generator_name {tg_node_name} is not a valid node name."
+        assert tg_node is not None, (
+            f"The traffic_generator_name {tg_node_name} is not a valid node name."
+        )
 
         return self
 
diff --git a/dts/framework/config/test_run.py b/dts/framework/config/test_run.py
index 76e24d1785..3cd643981d 100644
--- a/dts/framework/config/test_run.py
+++ b/dts/framework/config/test_run.py
@@ -233,9 +233,9 @@ def test_suite_spec(self) -> "TestSuiteSpec":
         from framework.test_suite import find_by_name
 
         test_suite_spec = find_by_name(self.test_suite_name)
-        assert (
-            test_suite_spec is not None
-        ), f"{self.test_suite_name} is not a valid test suite module name."
+        assert test_suite_spec is not None, (
+            f"{self.test_suite_name} is not a valid test suite module name."
+        )
         return test_suite_spec
 
     @cached_property
@@ -384,9 +384,9 @@ def convert_from_string(cls, data: Any) -> Any:
     @model_validator(mode="after")
     def verify_distinct_nodes(self) -> Self:
         """Verify that each side of the link has distinct nodes."""
-        assert (
-            self.left.node_type != self.right.node_type
-        ), "Linking ports of the same node is unsupported."
+        assert self.left.node_type != self.right.node_type, (
+            "Linking ports of the same node is unsupported."
+        )
         return self
 
 
diff --git a/dts/framework/context.py b/dts/framework/context.py
index 8f1021dc96..efe9af0645 100644
--- a/dts/framework/context.py
+++ b/dts/framework/context.py
@@ -60,9 +60,9 @@ def reset(self) -> None:
                 else _field.default
             )
 
-            assert (
-                default is not MISSING
-            ), "{LocalContext.__name__} must have defaults on all fields!"
+            assert default is not MISSING, (
+                "{LocalContext.__name__} must have defaults on all fields!"
+            )
 
             setattr(self, _field.name, default)
 
@@ -108,7 +108,7 @@ def filter_cores(
 ) -> Callable[[type["TestProtocol"]], Callable]:
     """Decorates functions that require a temporary update to the lcore specifier."""
 
-    def decorator(func: type["TestProtocol"]) -> Callable:
+    def decorator(func: type["TestProtocol"]) -> Callable[P, type["TestProtocol"]]:
         @functools.wraps(func)
         def wrapper(*args: P.args, **kwargs: P.kwargs) -> Any:
             local_ctx = get_ctx().local
diff --git a/dts/framework/remote_session/interactive_remote_session.py b/dts/framework/remote_session/interactive_remote_session.py
index c8156b4345..fc42e862bc 100644
--- a/dts/framework/remote_session/interactive_remote_session.py
+++ b/dts/framework/remote_session/interactive_remote_session.py
@@ -109,7 +109,7 @@ def _connect(self) -> None:
                 self._logger.debug(traceback.format_exc())
                 self._logger.warning(e)
                 self._logger.info(
-                    f"Retrying interactive session connection: retry number {retry_attempt +1}"
+                    f"Retrying interactive session connection: retry number {retry_attempt + 1}"
                 )
             else:
                 break
diff --git a/dts/framework/remote_session/interactive_shell.py b/dts/framework/remote_session/interactive_shell.py
index a65cbce209..6bba58a4f6 100644
--- a/dts/framework/remote_session/interactive_shell.py
+++ b/dts/framework/remote_session/interactive_shell.py
@@ -50,7 +50,9 @@
 def only_active(func: InteractiveShellMethod) -> InteractiveShellMethod:
     """This decorator will skip running the method if the SSH channel is not active."""
 
-    def _wrapper(self: "InteractiveShell", *args: P.args, **kwargs: P.kwargs) -> R | None:
+    def _wrapper(
+        self: "InteractiveShell", *args: P.args, **kwargs: P.kwargs
+    ) -> Callable[P, "InteractiveShell"] | None:
         if self._ssh_channel.active:
             return func(self, *args, **kwargs)
         return None
@@ -167,7 +169,7 @@ def start_application(self, prompt: str | None = None, add_to_shell_pool: bool =
                 break
             except InteractiveSSHTimeoutError:
                 self._logger.info(
-                    f"Interactive shell failed to start (attempt {attempt+1} out of "
+                    f"Interactive shell failed to start (attempt {attempt + 1} out of "
                     f"{self._init_attempts})"
                 )
         else:
diff --git a/dts/framework/remote_session/remote_session.py b/dts/framework/remote_session/remote_session.py
index 158325bb7f..fb5f6fedf5 100644
--- a/dts/framework/remote_session/remote_session.py
+++ b/dts/framework/remote_session/remote_session.py
@@ -72,9 +72,7 @@ def __post_init__(self, init_stdout: str, init_stderr: str) -> None:
     def __str__(self) -> str:
         """Format the command outputs."""
         return (
-            f"stdout: '{self.stdout}'\n"
-            f"stderr: '{self.stderr}'\n"
-            f"return_code: '{self.return_code}'"
+            f"stdout: '{self.stdout}'\nstderr: '{self.stderr}'\nreturn_code: '{self.return_code}'"
         )
 
 
diff --git a/dts/framework/remote_session/shell_pool.py b/dts/framework/remote_session/shell_pool.py
index 241737eab3..710107c6cb 100644
--- a/dts/framework/remote_session/shell_pool.py
+++ b/dts/framework/remote_session/shell_pool.py
@@ -74,7 +74,7 @@ def unregister_shell(self, shell: "InteractiveShell") -> None:
 
     def start_new_pool(self) -> None:
         """Start a new shell pool."""
-        self._logger.debug(f"Starting new shell pool and advancing to level {self.pool_level+1}.")
+        self._logger.debug(f"Starting new shell pool and advancing to level {self.pool_level + 1}.")
         self._pools.append(set())
 
     def terminate_current_pool(self) -> None:
diff --git a/dts/framework/settings.py b/dts/framework/settings.py
index b08373b7ea..f329677804 100644
--- a/dts/framework/settings.py
+++ b/dts/framework/settings.py
@@ -249,9 +249,9 @@ def error(self, message) -> NoReturn:
             if _is_from_env(action):
                 action_name = _get_action_name(action)
                 env_var_name = _get_env_var_name(action)
-                assert (
-                    env_var_name is not None
-                ), "Action was set from environment, but no environment variable name was found."
+                assert env_var_name is not None, (
+                    "Action was set from environment, but no environment variable name was found."
+                )
                 env_var_value = os.environ.get(env_var_name)
 
                 message = message.replace(
@@ -260,7 +260,7 @@ def error(self, message) -> NoReturn:
                 )
 
         print(f"{self.prog}: error: {message}\n", file=sys.stderr)
-        self.exit(2, "For help and usage, " "run the command with the --help flag.\n")
+        self.exit(2, "For help and usage, run the command with the --help flag.\n")
 
 
 class _EnvVarHelpFormatter(ArgumentDefaultsHelpFormatter):
diff --git a/dts/framework/testbed_model/cpu.py b/dts/framework/testbed_model/cpu.py
index 6e2ecca080..a9471709dd 100644
--- a/dts/framework/testbed_model/cpu.py
+++ b/dts/framework/testbed_model/cpu.py
@@ -105,7 +105,7 @@ def __init__(self, lcore_list: list[int] | list[str] | list[LogicalCore] | str)
 
         # the input lcores may not be sorted
         self._lcore_list.sort()
-        self._lcore_str = f'{",".join(self._get_consecutive_lcores_range(self._lcore_list))}'
+        self._lcore_str = f"{','.join(self._get_consecutive_lcores_range(self._lcore_list))}"
 
     @property
     def lcore_list(self) -> list[int]:
diff --git a/dts/framework/testbed_model/traffic_generator/scapy.py b/dts/framework/testbed_model/traffic_generator/scapy.py
index c6e9006205..62853a34e4 100644
--- a/dts/framework/testbed_model/traffic_generator/scapy.py
+++ b/dts/framework/testbed_model/traffic_generator/scapy.py
@@ -314,9 +314,9 @@ def __init__(self, tg_node: Node, config: ScapyTrafficGeneratorConfig, **kwargs:
             kwargs: Additional keyword arguments. Supported arguments correspond to the parameters
                 of :meth:`PythonShell.__init__` in this case.
         """
-        assert (
-            tg_node.config.os == OS.linux
-        ), "Linux is the only supported OS for scapy traffic generation"
+        assert tg_node.config.os == OS.linux, (
+            "Linux is the only supported OS for scapy traffic generation"
+        )
 
         super().__init__(tg_node=tg_node, config=config, **kwargs)
 
diff --git a/dts/framework/testbed_model/traffic_generator/trex.py b/dts/framework/testbed_model/traffic_generator/trex.py
index 2abfbfc5ab..d53791b0a6 100644
--- a/dts/framework/testbed_model/traffic_generator/trex.py
+++ b/dts/framework/testbed_model/traffic_generator/trex.py
@@ -95,9 +95,9 @@ def __init__(self, tg_node: Node, config: TrexTrafficGeneratorConfig) -> None:
             tg_node: TG node the TRex instance is operating on.
             config: Traffic generator config provided for TRex instance.
         """
-        assert (
-            tg_node.config.os == OS.linux
-        ), "Linux is the only supported OS for trex traffic generation"
+        assert tg_node.config.os == OS.linux, (
+            "Linux is the only supported OS for trex traffic generation"
+        )
 
         super().__init__(tg_node=tg_node, config=config)
         self._tg_node_config = tg_node.config
diff --git a/dts/framework/utils.py b/dts/framework/utils.py
index 9917ffbfaa..5753c1b7fe 100644
--- a/dts/framework/utils.py
+++ b/dts/framework/utils.py
@@ -21,7 +21,7 @@
 import tarfile
 from enum import Enum, Flag
 from pathlib import Path
-from typing import Any, Callable
+from typing import Any, Callable, Literal, TypeAlias, cast
 
 from scapy.layers.inet import IP, TCP, UDP, Ether
 from scapy.packet import Packet
@@ -39,6 +39,8 @@
     rf"(?:(sut|tg)\.)?({REGEX_FOR_IDENTIFIER})"  # right side
 )
 
+Tar_modes: TypeAlias = Literal["w:gz", "w:bz2", "w:xz", "w:tar"]
+
 
 def expand_range(range_str: str) -> list[int]:
     """Process `range_str` into a list of integers.
@@ -154,7 +156,11 @@ def extension(self) -> str:
         For other compression formats, the extension will be in the format
         'tar.{compression format}'.
         """
-        return f"{self.value}" if self == self.none else f"{type(self).none.value}.{self.value}"
+        return (
+            f"{self.value}"
+            if self == self.none
+            else f"{TarCompressionFormat.none.value}.{self.value}"
+        )
 
 
 def convert_to_list_of_string(value: Any | list[Any]) -> list[str]:
@@ -207,7 +213,8 @@ def filter_func(tarinfo: tarfile.TarInfo) -> tarfile.TarInfo | None:
         return None
 
     target_tarball_path = dir_path.with_suffix(f".{compress_format.extension}")
-    with tarfile.open(target_tarball_path, f"w:{compress_format.value}") as tar:
+    tarball_mode = cast(Tar_modes, f"w:{compress_format.value}")
+    with tarfile.open(target_tarball_path, tarball_mode) as tar:
         tar.add(dir_path, arcname=dir_path.name, filter=create_filter_function(exclude))
 
     return target_tarball_path
diff --git a/dts/poetry.lock b/dts/poetry.lock
index 0ad5d32b85..eda7cc2fec 100644
--- a/dts/poetry.lock
+++ b/dts/poetry.lock
@@ -1,4 +1,4 @@
-# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
+# This file is automatically @generated by Poetry 2.3.2 and should not be changed by hand.
 
 [[package]]
 name = "aenum"
@@ -6,6 +6,7 @@ version = "3.1.15"
 description = "Advanced Enumerations (compatible with Python's stdlib Enum), NamedTuples, and NamedConstants"
 optional = false
 python-versions = "*"
+groups = ["main"]
 files = [
     {file = "aenum-3.1.15-py2-none-any.whl", hash = "sha256:27b1710b9d084de6e2e695dab78fe9f269de924b51ae2850170ee7e1ca6288a5"},
     {file = "aenum-3.1.15-py3-none-any.whl", hash = "sha256:e0dfaeea4c2bd362144b87377e2c61d91958c5ed0b4daf89cb6f45ae23af6288"},
@@ -18,17 +19,62 @@ version = "0.7.0"
 description = "Reusable constraint types to use with typing.Annotated"
 optional = false
 python-versions = ">=3.8"
+groups = ["main"]
 files = [
     {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"},
     {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"},
 ]
 
+[[package]]
+name = "ast-serialize"
+version = "0.5.0"
+description = "Python bindings for mypy AST serialization"
+optional = false
+python-versions = ">=3.7"
+groups = ["dev"]
+files = [
+    {file = "ast_serialize-0.5.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8f5c14f169eb0972c0c21bada5358b23d6047c76583b005234f865b11f1fa00a"},
+    {file = "ast_serialize-0.5.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7d1a2de9de5be04652f0ed60738356ef94f66db37924a9499fffe98dc491aa0b"},
+    {file = "ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be5173fb66f9b49026d9d5a2ff0fc7c7009077107c0eb285b2d60fdf1fe10bd1"},
+    {file = "ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f8015cd071ac1339924ee2b8098c93e00e155f30a16f40ec9816fcf84f4753f6"},
+    {file = "ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5499e8797edff2a9186aa313ed382c6b422e798e9332d9953badcee6e69a88f2"},
+    {file = "ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6848f2a093fb5548751a9a09bff8fcd229e2bbeb0e3331f391b6ae6d26cd9903"},
+    {file = "ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:832d4c998e0b091fd60a6d6bceee535483c4d490de9ba85003af835225719261"},
+    {file = "ast_serialize-0.5.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:16db7c62ec0b8efe1d7afd283a388d8f74f2605d56032e5a37747d2de8dba027"},
+    {file = "ast_serialize-0.5.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:baf5eb061eb5bccade4128ad42da33787d72f6013809cd1b590376ece8b3c937"},
+    {file = "ast_serialize-0.5.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:104e4a35bd7c124173c41760ef9aaea17ddb3f86c65cb643671d59afbe3ee94c"},
+    {file = "ast_serialize-0.5.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:36be371028fc1675acb38a331bde160dbab7ff907fdf00b67eb6911aa106951b"},
+    {file = "ast_serialize-0.5.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:061ee58bdb52341c8201a6df41182a977736bae3b7ded87ca7176ca25a8a47ab"},
+    {file = "ast_serialize-0.5.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b15219e9cdc9f53f6f4cb51c009203507228226148c05c5e8fe451c28b435eb3"},
+    {file = "ast_serialize-0.5.0-cp314-cp314t-win32.whl", hash = "sha256:842d1c004bb466c7df036f95fabef789570541922b10976b12f5592a69cf0b38"},
+    {file = "ast_serialize-0.5.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b0c06d760909b095cc466356dfccd05a1c7233a6ca191c020dca2c6a6f16c24c"},
+    {file = "ast_serialize-0.5.0-cp314-cp314t-win_arm64.whl", hash = "sha256:787baedb0262cc49e8ce37cc15c00ae818e46a165a3b36f5e21ed174998104cb"},
+    {file = "ast_serialize-0.5.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:0668aa9459cfa8c9c49ddd2163ebcf43088ba045ef7492af6fe22e0098303101"},
+    {file = "ast_serialize-0.5.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:bf683d6363edf2b39eed6b6d4fe22d34b6203867a67e27134d9e2a2680c4bc4a"},
+    {file = "ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cc22cf0c9be65e71cf88fda130af60d61eb4a79370ad4cfe7900d48a4aa2211"},
+    {file = "ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f66173891548c9f2726bf27957b41cabce12fa679dc6da505ddbde4d4b3b31cf"},
+    {file = "ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e42d729ef2be96a14efbad355093284739e3670ece3e534f82cc8832790911d9"},
+    {file = "ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b725026bafa801dbd7310eb13a75f0a2e370e7e51b2cb225f9d21fcfadf919ee"},
+    {file = "ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b54f60c1d78767a53b67eaa663f0dfac3afe606aa07f1301572f588b73d64809"},
+    {file = "ast_serialize-0.5.0-cp39-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:27d51654fc240a1e87e742d353d98eb45b75f62f129086b3596ab53df2ac2a43"},
+    {file = "ast_serialize-0.5.0-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c36237c46dd1674542f2109740ea5ea485a169bf1431939ada0434e17934"},
+    {file = "ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1943db345233cc7194a470f13afa9c59772c0b123dea0c9414c4d4ca54369759"},
+    {file = "ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:df1c00022cbbcb064bfaa505aa9c9295362443ce5dacb459d1331d3da353f887"},
+    {file = "ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:cae65289fc456fde04af979a2be09302ef5d8ab92ef23e596d6746dc267ada27"},
+    {file = "ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:239a4c354e8d676e9d94631d1d4a64edc6b266f86ff3a5a80aedd344f342c01d"},
+    {file = "ast_serialize-0.5.0-cp39-abi3-win32.whl", hash = "sha256:143a4ef63285a075871908fda3672dc21864b83a8ec3ee12304aa3e4c5387b9a"},
+    {file = "ast_serialize-0.5.0-cp39-abi3-win_amd64.whl", hash = "sha256:cf25572c526add400f26a4750dc6ce0c3bb93fc1f75e7ae0cad4ce4f2cd5c590"},
+    {file = "ast_serialize-0.5.0-cp39-abi3-win_arm64.whl", hash = "sha256:92a31c9c20d25a076edaeec76b128a3535d74a24f340b9a8a7e96c9b86dc9642"},
+    {file = "ast_serialize-0.5.0.tar.gz", hash = "sha256:5880091bfe6f4f986f22866375c2e884843e7a0b6343ae41aeea659613d879b6"},
+]
+
 [[package]]
 name = "bcrypt"
 version = "4.2.1"
 description = "Modern password hashing for your software and your servers"
 optional = false
 python-versions = ">=3.7"
+groups = ["main"]
 files = [
     {file = "bcrypt-4.2.1-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:1340411a0894b7d3ef562fb233e4b6ed58add185228650942bdc885362f32c17"},
     {file = "bcrypt-4.2.1-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1ee315739bc8387aa36ff127afc99120ee452924e0df517a8f3e4c0187a0f5f"},
@@ -67,6 +113,7 @@ version = "1.17.1"
 description = "Foreign Function Interface for Python calling C code."
 optional = false
 python-versions = ">=3.8"
+groups = ["main", "dev"]
 files = [
     {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"},
     {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"},
@@ -136,6 +183,7 @@ files = [
     {file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"},
     {file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"},
 ]
+markers = {dev = "platform_python_implementation != \"PyPy\""}
 
 [package.dependencies]
 pycparser = "*"
@@ -146,6 +194,7 @@ version = "44.0.0"
 description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
 optional = false
 python-versions = "!=3.9.0,!=3.9.1,>=3.7"
+groups = ["main", "dev"]
 files = [
     {file = "cryptography-44.0.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:84111ad4ff3f6253820e6d3e58be2cc2a00adb29335d4cacb5ab4d4d34f2a123"},
     {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b15492a11f9e1b62ba9d73c210e2416724633167de94607ec6069ef724fad092"},
@@ -180,10 +229,10 @@ files = [
 cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""}
 
 [package.extras]
-docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=3.0.0)"]
+docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=3.0.0) ; python_version >= \"3.8\""]
 docstest = ["pyenchant (>=3)", "readme-renderer (>=30.0)", "sphinxcontrib-spelling (>=7.3.1)"]
-nox = ["nox (>=2024.4.15)", "nox[uv] (>=2024.3.2)"]
-pep8test = ["check-sdist", "click (>=8.0.1)", "mypy (>=1.4)", "ruff (>=0.3.6)"]
+nox = ["nox (>=2024.4.15)", "nox[uv] (>=2024.3.2) ; python_version >= \"3.8\""]
+pep8test = ["check-sdist ; python_version >= \"3.8\"", "click (>=8.0.1)", "mypy (>=1.4)", "ruff (>=0.3.6)"]
 sdist = ["build (>=1.0.0)"]
 ssh = ["bcrypt (>=3.1.5)"]
 test = ["certifi (>=2024)", "cryptography-vectors (==44.0.0)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"]
@@ -195,6 +244,7 @@ version = "5.1.1"
 description = "Decorators for Humans"
 optional = false
 python-versions = ">=3.5"
+groups = ["main"]
 files = [
     {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"},
     {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"},
@@ -206,6 +256,7 @@ version = "1.2.18"
 description = "Python @deprecated decorator to deprecate old python classes, functions or methods."
 optional = false
 python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7"
+groups = ["main"]
 files = [
     {file = "Deprecated-1.2.18-py2.py3-none-any.whl", hash = "sha256:bd5011788200372a32418f888e326a09ff80d0214bd961147cfed01b5c018eec"},
     {file = "deprecated-1.2.18.tar.gz", hash = "sha256:422b6f6d859da6f2ef57857761bfb392480502a64c3028ca9bbe86085d72115d"},
@@ -215,7 +266,7 @@ files = [
 wrapt = ">=1.10,<2"
 
 [package.extras]
-dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "setuptools", "tox"]
+dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "setuptools ; python_version >= \"3.12\"", "tox"]
 
 [[package]]
 name = "fabric"
@@ -223,6 +274,7 @@ version = "3.2.2"
 description = "High level SSH command execution"
 optional = false
 python-versions = "*"
+groups = ["main"]
 files = [
     {file = "fabric-3.2.2-py3-none-any.whl", hash = "sha256:91c47c0be68b14936c88b34da8a1f55e5710fd28397dac5d4ff2e21558113a6f"},
     {file = "fabric-3.2.2.tar.gz", hash = "sha256:8783ca42e3b0076f08b26901aac6b9d9b1f19c410074e7accfab902c184ff4a3"},
@@ -243,56 +295,177 @@ version = "2.2.0"
 description = "Pythonic task execution"
 optional = false
 python-versions = ">=3.6"
+groups = ["main"]
 files = [
     {file = "invoke-2.2.0-py3-none-any.whl", hash = "sha256:6ea924cc53d4f78e3d98bc436b08069a03077e6f85ad1ddaa8a116d7dad15820"},
     {file = "invoke-2.2.0.tar.gz", hash = "sha256:ee6cbb101af1a859c7fe84f2a264c059020b0cb7fe3535f9424300ab568f6bd5"},
 ]
 
+[[package]]
+name = "librt"
+version = "0.11.0"
+description = "Mypyc runtime library"
+optional = false
+python-versions = ">=3.9"
+groups = ["dev"]
+markers = "platform_python_implementation != \"PyPy\""
+files = [
+    {file = "librt-0.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6e94ebfcfa2d5e9926d6c3b9aa4617ffc42a845b4321fb84021b872358c82a0f"},
+    {file = "librt-0.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ae627397a2f351560440d872d6f7c8dbb4072e57868e7b2fc5b8b430fe489d45"},
+    {file = "librt-0.11.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc329359321b67d24efdf4bc69012b0597001649544db662c001db5a0184794c"},
+    {file = "librt-0.11.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:7e82e642ab0f7608ce2fe53d76ca2280a9ee33a1b06556142c7c6fe80a86fc33"},
+    {file = "librt-0.11.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:88145c15c67731d54283d135b03244028c750cc9edc334a96a4f5950ebdb2884"},
+    {file = "librt-0.11.0-cp310-cp310-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9d36a51b3d93320b686588e27123f4995804dbf1bce81df78c02fc3c6eea9280"},
+    {file = "librt-0.11.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d00f3ac06a2a8b246327f11e186a53a100a4d5c7ed52346367e5ec751d51586c"},
+    {file = "librt-0.11.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:461bbceede621f1ffb8839755f8663e886087ee7af16294cab7fb4d782c62eeb"},
+    {file = "librt-0.11.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:0cad8a4d6a8ff03c9b76f9414caccd78e7cfbc8a2e12fa334d8e1d9932753783"},
+    {file = "librt-0.11.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f37aa505b3cf60701562eddb32df74b12a9e380c207fd8b06dd157a943ac7ea0"},
+    {file = "librt-0.11.0-cp310-cp310-win32.whl", hash = "sha256:94663a21534637f0e787ec2a2a756022df6e5b7b2335a5cdd7d8e33d68a2af89"},
+    {file = "librt-0.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:dec7db73758c2b54953fd8b7fe348c45188fe26b39ee18446196edd08453a5d4"},
+    {file = "librt-0.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:93d95bd45b7d58343d8b90d904450a545144eec19a002511163426f8ab1fae29"},
+    {file = "librt-0.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ee278c769a713638cdacd4c0436d72156e75df3ebc0166ab2b9dc43acc386c9"},
+    {file = "librt-0.11.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f230cb1cbc9faaa616f9a678f530ebcf186e414b6bcbd88b960e4ba1b92428d5"},
+    {file = "librt-0.11.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:5d63c855d86938d9de93e265c9bd8c705b51ec494de5738340ee93767a686e4b"},
+    {file = "librt-0.11.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:993f028be9e96a08d31df3479ac80d99be374d17f3b78e4796b3fd3c913d4e89"},
+    {file = "librt-0.11.0-cp311-cp311-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:258d73a0aa66a055e65b2e4d1b8cdb23b9d132c5bb915d9547d804fcaed116cc"},
+    {file = "librt-0.11.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0827efe7854718f04aaddf6496e96960a956e676fe1d0f04eb41511fd8ad06d5"},
+    {file = "librt-0.11.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7753e57d6e12d019c0d8786f1c09c709f4c3fcc57c3887b24e36e6c06ec938b7"},
+    {file = "librt-0.11.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:11bd19822431cc21af9f27374e7ae2e58103c7d98bda823536a6c47f6bb2bb3d"},
+    {file = "librt-0.11.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:22bdf239b219d3993761a148ffa134b19e52e9989c84f845d5d7b71d70a17412"},
+    {file = "librt-0.11.0-cp311-cp311-win32.whl", hash = "sha256:46c60b61e308eb535fbd6fa622b1ee1bb2815691c1ad9c98bf7b84952ec3bc8d"},
+    {file = "librt-0.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:902e546ff044f579ff1c953ff5fce97b636fe9e3943996b2177710c6ef076f73"},
+    {file = "librt-0.11.0-cp311-cp311-win_arm64.whl", hash = "sha256:65ac3bc20f78aa0ee5ae84baa68917f89fef4af63e941084dd019a0d0e749f0c"},
+    {file = "librt-0.11.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b87504f1690a23b9a2cca841191a04f83895d4fc2dd04df91d82b1a04ca2ad46"},
+    {file = "librt-0.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40071fc5fe0ce8daa6de616702314a01e1250711682b0523d6ab8d4525910cb3"},
+    {file = "librt-0.11.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:137e79445c896a0ea7b265f52d23954e05b64222ee1af69e2cb34219067cbb67"},
+    {file = "librt-0.11.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:cca6644054e78746d8d4ef238681f9c34ff8b584fe6b988ecebb8db3b15e622a"},
+    {file = "librt-0.11.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d5b0eea49f5562861ee8d757a32ef7d559c1d35be2aaaa1ec28941d74c9ffc8a"},
+    {file = "librt-0.11.0-cp312-cp312-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0d1029d7e1ae1a7e647ed6fb5df8c4ce2dffefb7a9f5fd1376a4554d96dac09f"},
+    {file = "librt-0.11.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bc3ce6b33c5828d9e80592011a5c584cb2ce86edbc4088405f70da47dc1d1b3b"},
+    {file = "librt-0.11.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:936c5995f3514a42111f20099397d8177c79b4d7e70961e396c6f5a0a3566766"},
+    {file = "librt-0.11.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:9bc0ca6ad9381cbe8e4aa6e5726e4c80c78115a6e9723c599ed1d73e092bc49d"},
+    {file = "librt-0.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:070aa8c26c0a74774317a72df8851facc7f0f012a5b406557ac56992d92e1ec8"},
+    {file = "librt-0.11.0-cp312-cp312-win32.whl", hash = "sha256:6bf14feb84b05ae945277395451998c89c54d0def4070eb5c08de544930b245a"},
+    {file = "librt-0.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:75672f0bc524ede266287d532d7923dbce94c7514ad07627bac3d0c6d92cc4d9"},
+    {file = "librt-0.11.0-cp312-cp312-win_arm64.whl", hash = "sha256:2f10cf143e4a9bb0f4f5af568a00df94a2d69ef41c2579584454bb0fe5cc642c"},
+    {file = "librt-0.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:78dc31f7fdfe9c9d0eb0e8f42d139db230e826415bbcabd9f0e9faaaee909894"},
+    {file = "librt-0.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fa475675db22290c3158e1d42326d0f5a65f04f44a0e68c3630a25b53560fb9c"},
+    {file = "librt-0.11.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:621db29691044bdeda22e789e482e1b0f3a985d90e3426c9c6d17606416205ea"},
+    {file = "librt-0.11.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:a9010e2ed5b3a9e158c5fd966b3ab7e834bb3d3aacc8f66c91dd4b57a3799230"},
+    {file = "librt-0.11.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7c39513d8b7477a2e1ed8c43fc21c524e8d5a0f8d4e8b7b074dbdbe7820a08e2"},
+    {file = "librt-0.11.0-cp313-cp313-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7aef3cf1d5af86e770ab04bfd993dfc4ae8b8c17f66fb77dd4a7d50de7bbb1a3"},
+    {file = "librt-0.11.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:557183ddc36babe46b27dd60facbd5adb4492181a5be887587d57cda6e092f21"},
+    {file = "librt-0.11.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:83d3e1f72bd42f6c5c0b7daec530c3f829bd02db42c70b8ddf0c2d90a2459930"},
+    {file = "librt-0.11.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:4ce1f21fbe589bc1afd7872dece84fb0e1144f794a288e58a10d2c54a55c43be"},
+    {file = "librt-0.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:970b09f7044ea2b64c9da42fd3d335666518cfd1c6e8a182c95da73d0214b41e"},
+    {file = "librt-0.11.0-cp313-cp313-win32.whl", hash = "sha256:78fddc31cd4d3caa897ad5d31f856b1faadc9474021ad6cb182b9018793e254e"},
+    {file = "librt-0.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:8ca8aa88751a775870b764e93bad5135385f563cb8dcee399abf034ea4d3cb47"},
+    {file = "librt-0.11.0-cp313-cp313-win_arm64.whl", hash = "sha256:96f044bb325fd9cf1a723015638c219e9143f0dfbc0ca54c565df2b7fc748b44"},
+    {file = "librt-0.11.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4a017a95e5837dc15a8c5661d60e05daa96b90908b1aa6b7acdf443cd25c8ebd"},
+    {file = "librt-0.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b1ecbd9819deccc39b7542bf4d2a740d8a620694d39989e58661d3763458f8d4"},
+    {file = "librt-0.11.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7da327dacd7be8f8ec36547373550744a3cc0e536d54665cd83f8bcd961200e8"},
+    {file = "librt-0.11.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:0dc56b1f8d06e60db362cc3fdae206681817f86ce4725d34511473487f12a34b"},
+    {file = "librt-0.11.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05fb8fb2ab90e21c8d12ea240d744ad514da9baf381ebfa70d91d20d21713175"},
+    {file = "librt-0.11.0-cp314-cp314-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cae74872be221df4374d10fec61f93ed1513b9546ea84f2c0bf73ab3e9bd0b03"},
+    {file = "librt-0.11.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:32bcc918c0148eb7e3d57385125bac7e5f9e4359d05f07448b09f6f778c2f31c"},
+    {file = "librt-0.11.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:f9743fc99135d5f78d2454435615f6dec0473ca507c26ce9d92b10b562a280d3"},
+    {file = "librt-0.11.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:5ba067f4aadae8fda802d91d2124c90c42195ff32d9161d3549e6d05cfe26f96"},
+    {file = "librt-0.11.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:de3bf945454d032f9e390b85c4072e0a0570bf825421c8be0e71209fa65e1abe"},
+    {file = "librt-0.11.0-cp314-cp314-win32.whl", hash = "sha256:d2277a05f6dcb9fd13db9566aac4fabd68c3ea1ea46ee5567d4eef8efa495a2f"},
+    {file = "librt-0.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:ab73e8db5e3f564d812c1f5c3a175930a5f9bc96ccb5e3b22a34d7858b401cf7"},
+    {file = "librt-0.11.0-cp314-cp314-win_arm64.whl", hash = "sha256:aea3caa317752e3a466fa8af45d91ee0ea8c7fdd96e42b0a8dd9b76a7931eba1"},
+    {file = "librt-0.11.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d1b36540d7aaf9b9101b3a6f376c8d8e9f7a9aec93ed05918f2c69d493ffef72"},
+    {file = "librt-0.11.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:efbb343ab2ce3540f4ecbe6315d677ed70f37cd9a72b1e58066c918ca83acbaa"},
+    {file = "librt-0.11.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa0dd688aab3f7914d3e6e5e3554978e0383312fb8e771d84be008a35b9ee548"},
+    {file = "librt-0.11.0-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:f5fb36b8c6c63fdcbb1d526d94c0d1331610d43f4118cc1beb4efef4f3faacb2"},
+    {file = "librt-0.11.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4a9a237d13addb93715b6fee74023d5ee3469b53fce527626c0e088aa585805f"},
+    {file = "librt-0.11.0-cp314-cp314t-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5ddd17bd87b2c56ddd60e546a7984a2e64c4e8eab92fb4cf3830a48ad5469d51"},
+    {file = "librt-0.11.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bd43992b4473d42f12ff9e68326079f0696d9d4e6000e8f39a0238d482ba6ee2"},
+    {file = "librt-0.11.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:f8e3e8056dd674e279741485e2e512d6e9a751c7455809d0114e6ebf8d781085"},
+    {file = "librt-0.11.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:c1f708d8ae9c56cf38a903c44297243d2ec83fd82b396b977e0144a3e76217e3"},
+    {file = "librt-0.11.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0add982e0e7b9fc14cf4b33789d5f13f66581889b88c2f58099f6ce8f92617bd"},
+    {file = "librt-0.11.0-cp314-cp314t-win32.whl", hash = "sha256:2b481d846ac894c4e8403c5fd0e87c5d11d6499e404b474602508a224ff531c8"},
+    {file = "librt-0.11.0-cp314-cp314t-win_amd64.whl", hash = "sha256:28edb433edde181112a908c78907af28f964eabc15f4dd16c9d66c834302677c"},
+    {file = "librt-0.11.0-cp314-cp314t-win_arm64.whl", hash = "sha256:dee008f20b542e3cd162ba338a7f9ec0f6d23d395f66fe8aeeec3c9d067ea253"},
+    {file = "librt-0.11.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6bd72d903911d995ab666dbd1871f8b1e80925a699af8063fbf50053329fb05f"},
+    {file = "librt-0.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0ef69ac715f3cd8e5cd252cb2aebfa72c015492aacc339d5d7bf8fef3c62c677"},
+    {file = "librt-0.11.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:624a40c4a4ad7773315c287276cd024509b2c66ff5904f504bfc08d2c70293ab"},
+    {file = "librt-0.11.0-cp39-cp39-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:41dc19fe150b69716c8ece4f76773a9e8813fe3e35e032a58b4d46423fb8d7c0"},
+    {file = "librt-0.11.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4e8bd98ea9c47ae90b319a087ab28dac493f1ffbc1ecd1f28fcdbf3b7e1108d1"},
+    {file = "librt-0.11.0-cp39-cp39-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:84308fc49423ce6475d1c5d1985cd69a8ca9f0325fc7d5f81bb690a3f3625d4e"},
+    {file = "librt-0.11.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ff0fbaf5f44a21beeb0110f2ab64f45135a9536a834b79c0d1ef018f2786bbfa"},
+    {file = "librt-0.11.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:9c028a9442a18e266955d364ce42259136e79a7ba14d773e0d778d5f70cd56f1"},
+    {file = "librt-0.11.0-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:9f1692105a02bcf853f355032a5fdc5494358ef83d8fd22d16de375c85cec3f5"},
+    {file = "librt-0.11.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7a80a71e1fda83cc752a9141e87aae7fef279538597564d670e9ce513f286192"},
+    {file = "librt-0.11.0-cp39-cp39-win32.whl", hash = "sha256:140695816ddf3c86eb972981a26f35efd871c44b0c3aed44c8cd01749386617f"},
+    {file = "librt-0.11.0-cp39-cp39-win_amd64.whl", hash = "sha256:92f7ff819c197fc30473190a12c2856f325ac90aabfccbeb2072d28cc2e234e3"},
+    {file = "librt-0.11.0.tar.gz", hash = "sha256:075dc3ef4458a278e0195cbf6ac9d38808d9b906c5a6c7f7f79c3888276a3fb1"},
+]
+
 [[package]]
 name = "mypy"
-version = "1.13.0"
+version = "2.1.0"
 description = "Optional static typing for Python"
 optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.10"
+groups = ["dev"]
 files = [
-    {file = "mypy-1.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6607e0f1dd1fb7f0aca14d936d13fd19eba5e17e1cd2a14f808fa5f8f6d8f60a"},
-    {file = "mypy-1.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8a21be69bd26fa81b1f80a61ee7ab05b076c674d9b18fb56239d72e21d9f4c80"},
-    {file = "mypy-1.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b2353a44d2179846a096e25691d54d59904559f4232519d420d64da6828a3a7"},
-    {file = "mypy-1.13.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0730d1c6a2739d4511dc4253f8274cdd140c55c32dfb0a4cf8b7a43f40abfa6f"},
-    {file = "mypy-1.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:c5fc54dbb712ff5e5a0fca797e6e0aa25726c7e72c6a5850cfd2adbc1eb0a372"},
-    {file = "mypy-1.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:581665e6f3a8a9078f28d5502f4c334c0c8d802ef55ea0e7276a6e409bc0d82d"},
-    {file = "mypy-1.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3ddb5b9bf82e05cc9a627e84707b528e5c7caaa1c55c69e175abb15a761cec2d"},
-    {file = "mypy-1.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:20c7ee0bc0d5a9595c46f38beb04201f2620065a93755704e141fcac9f59db2b"},
-    {file = "mypy-1.13.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3790ded76f0b34bc9c8ba4def8f919dd6a46db0f5a6610fb994fe8efdd447f73"},
-    {file = "mypy-1.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:51f869f4b6b538229c1d1bcc1dd7d119817206e2bc54e8e374b3dfa202defcca"},
-    {file = "mypy-1.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5c7051a3461ae84dfb5dd15eff5094640c61c5f22257c8b766794e6dd85e72d5"},
-    {file = "mypy-1.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:39bb21c69a5d6342f4ce526e4584bc5c197fd20a60d14a8624d8743fffb9472e"},
-    {file = "mypy-1.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:164f28cb9d6367439031f4c81e84d3ccaa1e19232d9d05d37cb0bd880d3f93c2"},
-    {file = "mypy-1.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a4c1bfcdbce96ff5d96fc9b08e3831acb30dc44ab02671eca5953eadad07d6d0"},
-    {file = "mypy-1.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:a0affb3a79a256b4183ba09811e3577c5163ed06685e4d4b46429a271ba174d2"},
-    {file = "mypy-1.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a7b44178c9760ce1a43f544e595d35ed61ac2c3de306599fa59b38a6048e1aa7"},
-    {file = "mypy-1.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5d5092efb8516d08440e36626f0153b5006d4088c1d663d88bf79625af3d1d62"},
-    {file = "mypy-1.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2904956dac40ced10931ac967ae63c5089bd498542194b436eb097a9f77bc8"},
-    {file = "mypy-1.13.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:7bfd8836970d33c2105562650656b6846149374dc8ed77d98424b40b09340ba7"},
-    {file = "mypy-1.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:9f73dba9ec77acb86457a8fc04b5239822df0c14a082564737833d2963677dbc"},
-    {file = "mypy-1.13.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:100fac22ce82925f676a734af0db922ecfea991e1d7ec0ceb1e115ebe501301a"},
-    {file = "mypy-1.13.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7bcb0bb7f42a978bb323a7c88f1081d1b5dee77ca86f4100735a6f541299d8fb"},
-    {file = "mypy-1.13.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bde31fc887c213e223bbfc34328070996061b0833b0a4cfec53745ed61f3519b"},
-    {file = "mypy-1.13.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:07de989f89786f62b937851295ed62e51774722e5444a27cecca993fc3f9cd74"},
-    {file = "mypy-1.13.0-cp38-cp38-win_amd64.whl", hash = "sha256:4bde84334fbe19bad704b3f5b78c4abd35ff1026f8ba72b29de70dda0916beb6"},
-    {file = "mypy-1.13.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0246bcb1b5de7f08f2826451abd947bf656945209b140d16ed317f65a17dc7dc"},
-    {file = "mypy-1.13.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7f5b7deae912cf8b77e990b9280f170381fdfbddf61b4ef80927edd813163732"},
-    {file = "mypy-1.13.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7029881ec6ffb8bc233a4fa364736789582c738217b133f1b55967115288a2bc"},
-    {file = "mypy-1.13.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3e38b980e5681f28f033f3be86b099a247b13c491f14bb8b1e1e134d23bb599d"},
-    {file = "mypy-1.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:a6789be98a2017c912ae6ccb77ea553bbaf13d27605d2ca20a76dfbced631b24"},
-    {file = "mypy-1.13.0-py3-none-any.whl", hash = "sha256:9c250883f9fd81d212e0952c92dbfcc96fc237f4b7c92f56ac81fd48460b3e5a"},
-    {file = "mypy-1.13.0.tar.gz", hash = "sha256:0291a61b6fbf3e6673e3405cfcc0e7650bebc7939659fdca2702958038bd835e"},
+    {file = "mypy-2.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:11a6beb180257a805961aea9ec591bbd0bd17f1e18d35b8456d57aee5bedfedc"},
+    {file = "mypy-2.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8ef78c1d306bbf9a8a12f526c44902c9c28dffd6c52c52bf6a72641ce18d3849"},
+    {file = "mypy-2.1.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c209a90853081ff01d01ee895cafe10f7db1474e0d95beaeef0f6c1db9119bbd"},
+    {file = "mypy-2.1.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:47cebf61abde7c088a4e27718a8b13a81655686b2e9c251f5c0915a802248166"},
+    {file = "mypy-2.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d57a90ae5e872138a425ec328edbc9b235d1934c4377881a33ec05b341acc9a8"},
+    {file = "mypy-2.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:aea7f7a8a55b459c34275fc468ada6ca7c173a5e43a68f5dbe588a563d8a06b8"},
+    {file = "mypy-2.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:c989640253f0d76843e9c6c1bbf4bd48c5e85ada61bde4beb37cb3eca035685e"},
+    {file = "mypy-2.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a683016b16fe2f572dc04c72be7ee0504ac1605a265d0200f5cea695fb788f41"},
+    {file = "mypy-2.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1a293c534adb55271fef24a26da04b855540a8c13cc07bc5917b9fd2c394f2ca"},
+    {file = "mypy-2.1.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7406f4d048e71e576f5356d317e5b0a9e666dfd966bd99f9d14ca06e1a341538"},
+    {file = "mypy-2.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e0210d626fc8b31ccc90233754c7bc90e1f43205e85d96387f7db1285b55c398"},
+    {file = "mypy-2.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3712c20deed54e814eaaa825603bada8ea1c390670a397c95b98405347acc563"},
+    {file = "mypy-2.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fcaa0e479066e31f7cceb6a3bea39cb22b2ff51a6b2f24f193d19179ba17c389"},
+    {file = "mypy-2.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:0b1a5260c95aa443083f9ed3592662941951bca3d4ca224a5dc517c38b7cf666"},
+    {file = "mypy-2.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:244358bf1c0da7722230bce60683d52e8e9fd030554926f15b747a84efb5b3af"},
+    {file = "mypy-2.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4ec7c57657493c7a75534df2751c8ae2cda383c16ecc55d2106c54476b1b16f6"},
+    {file = "mypy-2.1.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8161b6ff4392410023224f0969d17db93e1e154bc3e4ba62598e720723ae211"},
+    {file = "mypy-2.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bf03e12003084a67395184d3eb8cbd6a489dc3655b5664b28c210a9e2403ab0b"},
+    {file = "mypy-2.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:20509760fd791c51579d573153407d226385ec1f8bcce55d730b354f3336bc22"},
+    {file = "mypy-2.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:6753d0c1fdd6b1a23b9e4f283ce80b2153b724adcb2653b20b85a8a28ac6436b"},
+    {file = "mypy-2.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:98ebb6589bb3b6d0c6f0c459d53ca55b8091fbc13d277c4041c885392e8195e8"},
+    {file = "mypy-2.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:35aac3bb114e03888f535d5eb51b8bafbb3266586b599da1940f9b1be3ec5bd5"},
+    {file = "mypy-2.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8de55a8c861f2a49331f807be98d90caeceeef520bde13d43a160207f8af613e"},
+    {file = "mypy-2.1.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5fdf2941a07434af755837d9880f7d7d25f1dacb1af9dcd4b9b66f2220a3024e"},
+    {file = "mypy-2.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e195b817c13f02352a9c124301f9f30f078405444679b6753c1b96b6eed37285"},
+    {file = "mypy-2.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5431d42af987ebd92ba2f71d45c85ed41d8e6ca9f5fd209a69f68f707d2469e5"},
+    {file = "mypy-2.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:767fe8c66dc3e01e19e1737d4c38ebefead16125e1b8e58ad421903b376f5c65"},
+    {file = "mypy-2.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:ecfe70d43775ab99562ab128ce49854a362044c9f894961f68f898c23cb7429d"},
+    {file = "mypy-2.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:7354c5a7f69d9345c3d6e69921d57088eea3ddeeb6b20d34c1b3855b02c36ec2"},
+    {file = "mypy-2.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:49890d4f76ac9e06ec117f9e09f3174da70a620a0c300953d8595c926e80947f"},
+    {file = "mypy-2.1.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:761be68e023ef5d94678772396a8af1220030f80837a3afd8d0aef3b419666f4"},
+    {file = "mypy-2.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c90345fc182dc363b891350457ec69c35140858538f38b4540845afcc32b1aef"},
+    {file = "mypy-2.1.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b84802e7b5a6daf1f5e15bc9fcd7ddae77be13981ffab037f1c67bb84d67d135"},
+    {file = "mypy-2.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:022c771234936ceac541ebaf836fe9e2abeb3f5e09aff21588fe543ff006fe21"},
+    {file = "mypy-2.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:498207db725cec88829a6a5c2fc771205fd043719ef98bc49aba8fb9fc4e6d57"},
+    {file = "mypy-2.1.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7d5e5cad0efeba72b93cd17490cc0d69c5ac9ca132994fe3fb0314808aeeb83e"},
+    {file = "mypy-2.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ff715050c127d724fd260a2e666e7747fdd83511c0c47d449d98238970aef780"},
+    {file = "mypy-2.1.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:82208da9e09414d520e912d3e462d454854bed0810b71540bb016dcbca7308fd"},
+    {file = "mypy-2.1.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e79ebc1b904b84f0310dff7469655a9c36c7a68bddb37bdd42b67a332df61d08"},
+    {file = "mypy-2.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e583edc957cfb0deb142079162ae826f58449b116c1d442f2d91c69d9fced081"},
+    {file = "mypy-2.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b33b6cd332695bba180d55e717a79d3038e479a2c49cc5eb3d53603409b9a5d7"},
+    {file = "mypy-2.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:4f910fe825376a7b66ef7ca8c98e5a149e8cd64c19ae71d84047a74ee060d4e6"},
+    {file = "mypy-2.1.0-py3-none-any.whl", hash = "sha256:a663814603a5c563fb87a4f96fb473eeb30d1f5a4885afcf44f9db000a366289"},
+    {file = "mypy-2.1.0.tar.gz", hash = "sha256:81e76ad12c2d804512e9b13240d1588316531bfba07558286078bfbce9613633"},
 ]
 
 [package.dependencies]
-mypy-extensions = ">=1.0.0"
+ast-serialize = ">=0.3.0,<1.0.0"
+librt = {version = ">=0.11.0", markers = "platform_python_implementation != \"PyPy\""}
+mypy_extensions = ">=1.0.0"
+pathspec = ">=1.0.0"
 tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
-typing-extensions = ">=4.6.0"
+typing_extensions = [
+    {version = ">=4.6.0", markers = "python_version < \"3.15\""},
+    {version = ">=4.14.0", markers = "python_version >= \"3.15\""},
+]
 
 [package.extras]
 dmypy = ["psutil (>=4.0)"]
@@ -307,6 +480,7 @@ version = "1.0.0"
 description = "Type system extensions for programs checked with the mypy type checker."
 optional = false
 python-versions = ">=3.5"
+groups = ["dev"]
 files = [
     {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"},
     {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"},
@@ -318,6 +492,7 @@ version = "3.5.0"
 description = "SSH2 protocol library"
 optional = false
 python-versions = ">=3.6"
+groups = ["main"]
 files = [
     {file = "paramiko-3.5.0-py3-none-any.whl", hash = "sha256:1fedf06b085359051cd7d0d270cebe19e755a8a921cc2ddbfa647fb0cd7d68f9"},
     {file = "paramiko-3.5.0.tar.gz", hash = "sha256:ad11e540da4f55cedda52931f1a3f812a8238a7af7f62a60de538cd80bb28124"},
@@ -329,20 +504,39 @@ cryptography = ">=3.3"
 pynacl = ">=1.5"
 
 [package.extras]
-all = ["gssapi (>=1.4.1)", "invoke (>=2.0)", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1.8)"]
-gssapi = ["gssapi (>=1.4.1)", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1.8)"]
+all = ["gssapi (>=1.4.1) ; platform_system != \"Windows\"", "invoke (>=2.0)", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1.8) ; platform_system == \"Windows\""]
+gssapi = ["gssapi (>=1.4.1) ; platform_system != \"Windows\"", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1.8) ; platform_system == \"Windows\""]
 invoke = ["invoke (>=2.0)"]
 
+[[package]]
+name = "pathspec"
+version = "1.1.1"
+description = "Utility library for gitignore style pattern matching of file paths."
+optional = false
+python-versions = ">=3.9"
+groups = ["dev"]
+files = [
+    {file = "pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189"},
+    {file = "pathspec-1.1.1.tar.gz", hash = "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a"},
+]
+
+[package.extras]
+hyperscan = ["hyperscan (>=0.7)"]
+optional = ["typing-extensions (>=4)"]
+re2 = ["google-re2 (>=1.1)"]
+
 [[package]]
 name = "pycparser"
 version = "2.22"
 description = "C parser in Python"
 optional = false
 python-versions = ">=3.8"
+groups = ["main", "dev"]
 files = [
     {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"},
     {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"},
 ]
+markers = {dev = "platform_python_implementation != \"PyPy\""}
 
 [[package]]
 name = "pydantic"
@@ -350,6 +544,7 @@ version = "2.10.3"
 description = "Data validation using Python type hints"
 optional = false
 python-versions = ">=3.8"
+groups = ["main"]
 files = [
     {file = "pydantic-2.10.3-py3-none-any.whl", hash = "sha256:be04d85bbc7b65651c5f8e6b9976ed9c6f41782a55524cef079a34a0bb82144d"},
     {file = "pydantic-2.10.3.tar.gz", hash = "sha256:cb5ac360ce894ceacd69c403187900a02c4b20b693a9dd1d643e1effab9eadf9"},
@@ -362,7 +557,7 @@ typing-extensions = ">=4.12.2"
 
 [package.extras]
 email = ["email-validator (>=2.0.0)"]
-timezone = ["tzdata"]
+timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows\""]
 
 [[package]]
 name = "pydantic-core"
@@ -370,6 +565,7 @@ version = "2.27.1"
 description = "Core functionality for Pydantic validation and serialization"
 optional = false
 python-versions = ">=3.8"
+groups = ["main"]
 files = [
     {file = "pydantic_core-2.27.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:71a5e35c75c021aaf400ac048dacc855f000bdfed91614b4a726f7432f1f3d6a"},
     {file = "pydantic_core-2.27.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f82d068a2d6ecfc6e054726080af69a6764a10015467d7d7b9f66d6ed5afa23b"},
@@ -482,6 +678,7 @@ version = "1.5.0"
 description = "Python binding to the Networking and Cryptography (NaCl) library"
 optional = false
 python-versions = ">=3.6"
+groups = ["main"]
 files = [
     {file = "PyNaCl-1.5.0-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:401002a4aaa07c9414132aaed7f6836ff98f59277a234704ff66878c2ee4a0d1"},
     {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:52cb72a79269189d4e0dc537556f4740f7f0a9ec41c1322598799b0bdad4ef92"},
@@ -508,6 +705,7 @@ version = "6.0.2"
 description = "YAML parser and emitter for Python"
 optional = false
 python-versions = ">=3.8"
+groups = ["main"]
 files = [
     {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"},
     {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"},
@@ -566,29 +764,30 @@ files = [
 
 [[package]]
 name = "ruff"
-version = "0.8.2"
+version = "0.15.16"
 description = "An extremely fast Python linter and code formatter, written in Rust."
 optional = false
 python-versions = ">=3.7"
+groups = ["dev"]
 files = [
-    {file = "ruff-0.8.2-py3-none-linux_armv6l.whl", hash = "sha256:c49ab4da37e7c457105aadfd2725e24305ff9bc908487a9bf8d548c6dad8bb3d"},
-    {file = "ruff-0.8.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ec016beb69ac16be416c435828be702ee694c0d722505f9c1f35e1b9c0cc1bf5"},
-    {file = "ruff-0.8.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:f05cdf8d050b30e2ba55c9b09330b51f9f97d36d4673213679b965d25a785f3c"},
-    {file = "ruff-0.8.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:60f578c11feb1d3d257b2fb043ddb47501ab4816e7e221fbb0077f0d5d4e7b6f"},
-    {file = "ruff-0.8.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cbd5cf9b0ae8f30eebc7b360171bd50f59ab29d39f06a670b3e4501a36ba5897"},
-    {file = "ruff-0.8.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b402ddee3d777683de60ff76da801fa7e5e8a71038f57ee53e903afbcefdaa58"},
-    {file = "ruff-0.8.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:705832cd7d85605cb7858d8a13d75993c8f3ef1397b0831289109e953d833d29"},
-    {file = "ruff-0.8.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:32096b41aaf7a5cc095fa45b4167b890e4c8d3fd217603f3634c92a541de7248"},
-    {file = "ruff-0.8.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e769083da9439508833cfc7c23e351e1809e67f47c50248250ce1ac52c21fb93"},
-    {file = "ruff-0.8.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fe716592ae8a376c2673fdfc1f5c0c193a6d0411f90a496863c99cd9e2ae25d"},
-    {file = "ruff-0.8.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:81c148825277e737493242b44c5388a300584d73d5774defa9245aaef55448b0"},
-    {file = "ruff-0.8.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d261d7850c8367704874847d95febc698a950bf061c9475d4a8b7689adc4f7fa"},
-    {file = "ruff-0.8.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1ca4e3a87496dc07d2427b7dd7ffa88a1e597c28dad65ae6433ecb9f2e4f022f"},
-    {file = "ruff-0.8.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:729850feed82ef2440aa27946ab39c18cb4a8889c1128a6d589ffa028ddcfc22"},
-    {file = "ruff-0.8.2-py3-none-win32.whl", hash = "sha256:ac42caaa0411d6a7d9594363294416e0e48fc1279e1b0e948391695db2b3d5b1"},
-    {file = "ruff-0.8.2-py3-none-win_amd64.whl", hash = "sha256:2aae99ec70abf43372612a838d97bfe77d45146254568d94926e8ed5bbb409ea"},
-    {file = "ruff-0.8.2-py3-none-win_arm64.whl", hash = "sha256:fb88e2a506b70cfbc2de6fae6681c4f944f7dd5f2fe87233a7233d888bad73e8"},
-    {file = "ruff-0.8.2.tar.gz", hash = "sha256:b84f4f414dda8ac7f75075c1fa0b905ac0ff25361f42e6d5da681a465e0f78e5"},
+    {file = "ruff-0.15.16-py3-none-linux_armv6l.whl", hash = "sha256:6ac3c0b3969cc6cf6b158c4e2f8f682acb58e7d700d8a44b65ecdc72d66ab0b2"},
+    {file = "ruff-0.15.16-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:197c207ed75ffba54a0dec23db4aa939a27a3053073e085e0042433cbdc58e4a"},
+    {file = "ruff-0.15.16-py3-none-macosx_11_0_arm64.whl", hash = "sha256:3a39fec45ab316cc23e7558f23fea4a70403ddb5648ea9a4a3854a16973d0071"},
+    {file = "ruff-0.15.16-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba93191d79003116b95128c9d306e045200fdbd0bccb782b110f3cd1d4abc5cf"},
+    {file = "ruff-0.15.16-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c6ee4b90520630120ef032aa5cc10db483852dff950e78b1d717e2993a61ac8d"},
+    {file = "ruff-0.15.16-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e4215bc938bc3c8215c1472c1aa437e310fee20cd427335fec9d7e609563628"},
+    {file = "ruff-0.15.16-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7c8d26be963b090f10e29abc8b3e74a2a321f6fa34e02424e30b5af89350ecbb"},
+    {file = "ruff-0.15.16-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f198cf4123602a2280ed46c307bcbafe41758d6fee5b456b6b6058ca1514b3b4"},
+    {file = "ruff-0.15.16-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb27515fa6240fb586ae82b901a59e67d24acff86f2190b433dc542fe0435aeb"},
+    {file = "ruff-0.15.16-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:a267c46ba1593fc26b8eecbea050b39d40c0b6bb7781ee11c90a02cd10032951"},
+    {file = "ruff-0.15.16-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:528c68f39a91498a8d50e91ff5985df3d105782bab49cc378e73ac26bff083e8"},
+    {file = "ruff-0.15.16-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:7ed55c58950df60589a9a7a5d2f8fa5f54ebd287163be805adfe6ee95a9de123"},
+    {file = "ruff-0.15.16-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d482feaf51512b50f9790ceb417a56a61dd1e9d9bf967662b9ed27c01b34f53a"},
+    {file = "ruff-0.15.16-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:1e15bc8c94513dae2a40cc9ef07c94fdd4ecc9e29dabebeebe170f952322c9e3"},
+    {file = "ruff-0.15.16-py3-none-win32.whl", hash = "sha256:580378f7bd4aa25f72e74aa54948a9622f142b1e509521dd10902e886681cc1e"},
+    {file = "ruff-0.15.16-py3-none-win_amd64.whl", hash = "sha256:408256017284eddf98fff77b29aa4fb30f586042d535b2d9befc6512f400aaec"},
+    {file = "ruff-0.15.16-py3-none-win_arm64.whl", hash = "sha256:8cd61783afb39638a7133ef0d2dfb1e91277593962f81b5a8423eb0b888a6121"},
+    {file = "ruff-0.15.16.tar.gz", hash = "sha256:d05e78d38c78caf020b03789e25106c93017db5a0cb6e2819885018c61343b78"},
 ]
 
 [[package]]
@@ -597,6 +796,7 @@ version = "2.6.1"
 description = "Scapy: interactive packet manipulation tool"
 optional = false
 python-versions = "<4,>=3.7"
+groups = ["main"]
 files = [
     {file = "scapy-2.6.1-py3-none-any.whl", hash = "sha256:88a998572049b511a1f3e44f4aa7c62dd39c6ea2aa1bb58434f503956641789d"},
     {file = "scapy-2.6.1.tar.gz", hash = "sha256:7600d7e2383c853e5c3a6e05d37e17643beebf2b3e10d7914dffcc3bc3c6e6c5"},
@@ -613,6 +813,7 @@ version = "0.10.2"
 description = "Python Library for Tom's Obvious, Minimal Language"
 optional = false
 python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
+groups = ["dev"]
 files = [
     {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
     {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
@@ -624,6 +825,8 @@ version = "2.2.1"
 description = "A lil' TOML parser"
 optional = false
 python-versions = ">=3.8"
+groups = ["dev"]
+markers = "python_version == \"3.10\""
 files = [
     {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"},
     {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"},
@@ -665,6 +868,7 @@ version = "2.0.0.10"
 description = "Typing stubs for invoke"
 optional = false
 python-versions = ">=3.7"
+groups = ["dev"]
 files = [
     {file = "types-invoke-2.0.0.10.tar.gz", hash = "sha256:a54d7ecdc19e0c22cd2786ef2e64c2631715c78eba8a1bf40b511d0608f33a88"},
     {file = "types_invoke-2.0.0.10-py3-none-any.whl", hash = "sha256:2404e4279601fa96e14ef68321fd10a660a828677aabdcaeef6a5189778084ef"},
@@ -672,13 +876,14 @@ files = [
 
 [[package]]
 name = "types-paramiko"
-version = "3.5.0.20240928"
+version = "4.0.0.20260518"
 description = "Typing stubs for paramiko"
 optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.10"
+groups = ["dev"]
 files = [
-    {file = "types-paramiko-3.5.0.20240928.tar.gz", hash = "sha256:79dd9b2ee510b76a3b60d8ac1f3f348c45fcecf01347ca79e14db726bbfc442d"},
-    {file = "types_paramiko-3.5.0.20240928-py3-none-any.whl", hash = "sha256:cda0aff4905fe8efe4b5448331a80e943d42a796bd4beb77a3eed3485bc96a85"},
+    {file = "types_paramiko-4.0.0.20260518-py3-none-any.whl", hash = "sha256:0ffaf1a6eb796833a49653cba4c7be13af51c8269d75234972d6239763dda270"},
+    {file = "types_paramiko-4.0.0.20260518.tar.gz", hash = "sha256:286f6830945cba63797eedf375ed87138d93198121253afe66c5d6dbcf91318d"},
 ]
 
 [package.dependencies]
@@ -686,13 +891,14 @@ cryptography = ">=37.0.0"
 
 [[package]]
 name = "types-pyyaml"
-version = "6.0.12.20240917"
+version = "6.0.12.20260518"
 description = "Typing stubs for PyYAML"
 optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.10"
+groups = ["dev"]
 files = [
-    {file = "types-PyYAML-6.0.12.20240917.tar.gz", hash = "sha256:d1405a86f9576682234ef83bcb4e6fff7c9305c8b1fbad5e0bcd4f7dbdc9c587"},
-    {file = "types_PyYAML-6.0.12.20240917-py3-none-any.whl", hash = "sha256:392b267f1c0fe6022952462bf5d6523f31e37f6cea49b14cee7ad634b6301570"},
+    {file = "types_pyyaml-6.0.12.20260518-py3-none-any.whl", hash = "sha256:d2150f75a231c9fe9c7463bd29487d93e60bac90400287351384bc2284eba7cd"},
+    {file = "types_pyyaml-6.0.12.20260518.tar.gz", hash = "sha256:d917f83fb38462550338c1297faedd860b3ec83912b96b1e3d73255f7473e466"},
 ]
 
 [[package]]
@@ -701,17 +907,33 @@ version = "4.12.2"
 description = "Backported and Experimental Type Hints for Python 3.8+"
 optional = false
 python-versions = ">=3.8"
+groups = ["main", "dev"]
+markers = "python_version < \"3.15\""
 files = [
     {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"},
     {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
 ]
 
+[[package]]
+name = "typing-extensions"
+version = "4.15.0"
+description = "Backported and Experimental Type Hints for Python 3.9+"
+optional = false
+python-versions = ">=3.9"
+groups = ["main", "dev"]
+markers = "python_version >= \"3.15\""
+files = [
+    {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"},
+    {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"},
+]
+
 [[package]]
 name = "wrapt"
 version = "1.17.2"
 description = "Module for decorators, wrappers and monkey patching."
 optional = false
 python-versions = ">=3.8"
+groups = ["main"]
 files = [
     {file = "wrapt-1.17.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3d57c572081fed831ad2d26fd430d565b76aa277ed1d30ff4d40670b1c0dd984"},
     {file = "wrapt-1.17.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b5e251054542ae57ac7f3fba5d10bfff615b6c2fb09abeb37d2f1463f841ae22"},
@@ -795,6 +1017,6 @@ files = [
 ]
 
 [metadata]
-lock-version = "2.0"
+lock-version = "2.1"
 python-versions = "^3.10"
-content-hash = "aa6dff54827602c89ee125019291965de50d7a471ca48add889ea23aa5fd9b2f"
+content-hash = "1ca3cdf5bf98c528845b5e22169322db8912fe437988635f4ebea088b2079463"
diff --git a/dts/pyproject.toml b/dts/pyproject.toml
index 8b061c3cee..b639af65b6 100644
--- a/dts/pyproject.toml
+++ b/dts/pyproject.toml
@@ -28,12 +28,12 @@ aenum = "^3.1.15"
 pydantic = "^2.9.2"
 
 [tool.poetry.group.dev.dependencies]
-mypy = "^1.13.0"
+mypy = "^2.1.0"
 toml = "^0.10.2"
-ruff = "^0.8.1"
-types-paramiko = "^3.5.0.20240928"
+ruff = "^0.15.16"
+types-paramiko = "^4.0.0.20260518"
 types-invoke = "^2.0.0.10"
-types-pyyaml = "^6.0.12.20240917"
+types-pyyaml = "^6.0.12.20260518"
 
 [build-system]
 requires = ["poetry-core>=1.0.0"]
diff --git a/dts/tests/TestSuite_cryptodev_throughput.py b/dts/tests/TestSuite_cryptodev_throughput.py
index af0a5680ab..2fc0d8779a 100644
--- a/dts/tests/TestSuite_cryptodev_throughput.py
+++ b/dts/tests/TestSuite_cryptodev_throughput.py
@@ -101,12 +101,12 @@ def _print_stats(self, test_vals: list[dict[str, int | float | str]]) -> None:
         print(f"{'Throughput Results'.center(border_len)}\n{'=' * border_len}")
         for k, v in test_vals[0].items():
             print(f"|{k.title():<{element_len}}", end="")
-        print(f"|\n{'='*border_len}")
+        print(f"|\n{'=' * border_len}")
 
         for test_val in test_vals:
             for k, v in test_val.items():
                 print(f"|{v:<{element_len}}", end="")
-            print(f"|\n{'='*border_len}")
+            print(f"|\n{'=' * border_len}")
 
     def _verify_throughput(
         self,
diff --git a/dts/tests/TestSuite_port_control.py b/dts/tests/TestSuite_port_control.py
index 6be47838d0..b51fdc2959 100644
--- a/dts/tests/TestSuite_port_control.py
+++ b/dts/tests/TestSuite_port_control.py
@@ -44,8 +44,7 @@ def _send_packets_and_verify(self) -> None:
         recv_pakts = [
             p
             for p in recv_pakts
-            if
-            (
+            if (
                 # Remove padding from the bytes.
                 hasattr(p, "load") and p.load.decode("utf-8").replace("\x00", "") == payload
             )
diff --git a/dts/tests/TestSuite_single_core_forward_perf.py b/dts/tests/TestSuite_single_core_forward_perf.py
index 1e7ab7b036..acdf8ae2f6 100644
--- a/dts/tests/TestSuite_single_core_forward_perf.py
+++ b/dts/tests/TestSuite_single_core_forward_perf.py
@@ -144,7 +144,7 @@ def single_core_forward_perf(self) -> None:
         for params in self.test_parameters:
             verify(
                 params["pass"] is True,
-                f"""Packets forwarded is less than {(1 -self.delta_tolerance)*100}%
+                f"""Packets forwarded is less than {(1 - self.delta_tolerance) * 100}%
                 of the expected baseline.
                 Measured MPPS = {params["measured_mpps"]}
                 Expected MPPS = {params["expected_mpps"]}""",
-- 
2.54.0


^ permalink raw reply related

* Re: [PATCH] examples/ptp_tap_relay_sw: forbid shadowed variables
From: Stephen Hemminger @ 2026-06-18 20:05 UTC (permalink / raw)
  To: Thomas Monjalon; +Cc: dev, Rajesh Kumar
In-Reply-To: <20260618142526.191340-1-thomas@monjalon.net>

On Thu, 18 Jun 2026 16:25:26 +0200
Thomas Monjalon <thomas@monjalon.net> wrote:

> By removing the compilation flag no_shadow_cflag,
> it becomes forbidden to shadow a variable.
> 
> Signed-off-by: Thomas Monjalon <thomas@monjalon.net>
> ---

Thanks, example did not exist when the first pass was done.

Acked-by: Stephen Hemminger <stephen@networkplumber.org>

^ permalink raw reply

* [DPDK/core Bug 1959] BPF JIT produces bad code
From: bugzilla @ 2026-06-18 19:52 UTC (permalink / raw)
  To: dev

http://bugs.dpdk.org/show_bug.cgi?id=1959

            Bug ID: 1959
           Summary: BPF JIT produces bad code
           Product: DPDK
           Version: 22.03
          Hardware: All
                OS: All
            Status: UNCONFIRMED
          Severity: major
          Priority: Normal
         Component: core
          Assignee: dev@dpdk.org
          Reporter: stephen@networkplumber.org
  Target Milestone: ---

While testing bpf convert code for capture; discovered that the following BPF
instructions are JIT'd in a way that causes a SEGV when executed.

bpf convert for "ether[0] & 1 = 0 and ip[16] >= 224" produced:
cBPF program (8 insns)
(000) ldb      [0]
(001) jset     #0x1             jt 7    jf 2
(002) ldh      [12]
(003) jeq      #0x800           jt 4    jf 7
(004) ldb      [30]
(005) jge      #0xe0            jt 6    jf 7
(006) ret      #262144
(007) ret      #0

eBPF program (14 insns)
 L0:    xor r0, r0
 L1:    xor r7, r7
 L2:    mov r6, r1
 L3:    ldb r0, [0]
 L4:    jset r0, #0x1, L12
 L5:    ldh r0, [12]
 L6:    jne r0, #0x800, L12
 L7:    ldb r0, [30]
 L8:    jge r0, #0xe0, L10
 L9:    ja L12
 L10:   mov32 r0, #0x40000
 L11:   exit
 L12:   mov32 r0, #0x0
 L13:   exit
JIT code at [0x7fb6dbebf000], len=340
00000000: 48 83 EC 18 48 89 5C 24 00 4C 89 6C 24 08 48 89 | H...H.\$.L.l$.H.
00000010: 6C 24 10 48 89 E5 48 83 EC 08 48 83 E4 F0 48 31 | l$.H..H...H...H1
00000020: C0 4D 31 ED 48 89 FB 48 31 F6 48 0F B7 53 28 48 | .M1.H..H1.H..S(H
00000030: 29 F2 48 83 FA 01 7C 11 48 0F B7 53 10 48 8B 43 | ).H...|.H..S.H.C
00000040: 00 48 01 D0 48 01 F0 EB 26 48 C7 C2 01 00 00 00 | .H..H...&H......
00000050: 48 89 E9 48 83 E9 08 48 89 DF 48 B8 30 7E C6 A5 | H..H...H..H.0~..
00000060: 01 56 00 00 FF D0 48 85 C0 0F 84 CA 00 00 00 48 | .V....H........H
00000070: 0F B6 40 00 48 F7 C0 01 0F 85 D2 00 00 00 48 C7 | ..@.H.........H.
00000080: C6 0C 00 00 00 48 0F B7 53 28 48 29 F2 48 83 FA | .....H..S(H).H..
00000090: 02 7C 11 48 0F B7 53 10 48 8B 43 00 48 01 D0 48 | .|.H..S.H.C.H..H
000000A0: 01 F0 EB 22 48 C7 C2 02 00 00 00 48 89 E9 48 83 | ..."H......H..H.
000000B0: E9 08 48 89 DF 48 B8 30 7E C6 A5 01 56 00 00 FF | ..H..H.0~...V...
000000C0: D0 48 85 C0 74 73 48 0F B7 40 00 66 C1 C8 08 0F | .H..tsH..@.f....
000000D0: B7 C0 48 81 F8 00 08 00 00 75 75 48 C7 C6 1E 00 | ..H......uuH....
000000E0: 00 00 48 0F B7 53 28 48 29 F2 48 83 FA 01 7C 11 | ..H..S(H).H...|.
000000F0: 48 0F B7 53 10 48 8B 43 00 48 01 D0 48 01 F0 EB | H..S.H.C.H..H...
00000100: 22 48 C7 C2 01 00 00 00 48 89 E9 48 83 E9 08 48 | "H......H..H...H
00000110: 89 DF 48 B8 30 7E C6 A5 01 56 00 00 FF D0 48 85 | ..H.0~...V....H.
00000120: C0 74 16 48 0F B6 40 00 48 81 F8 E0 00 00 00 73 | .t.H..@.H......s
00000130: 02 EB 1D C7 C0 00 00 04 00 48 89 EC 48 8B 5C 24 | .........H..H.\$
00000140: 00 4C 8B 6C 24 08 48 8B 6C 24 10 48 83 C4 18 C3 | .L.l$.H.l$.H....
00000150: 31 C0 EB E5                                     | 1...
Segmentation fault         DPDK_TEST=bpf_convert_autotest ./build/app/dpdk-test

-- 
You are receiving this mail because:
You are the assignee for the bug.

^ permalink raw reply

* Re: [PATCH] examples: Fix vm_power_manager scratch area to /run/dpdk/powermonitor
From: Sudheendra S @ 2026-06-18 19:24 UTC (permalink / raw)
  To: Bruce Richardson
  Cc: Stephen Hemminger, dev@dpdk.org, Anatoly Burakov,
	Sivaprasad Tummala
In-Reply-To: <ahm4Qr5gLBf6sxof@bricha3-mobl1.ger.corp.intel.com>

[-- Attachment #1: Type: text/plain, Size: 4160 bytes --]

*Note:  Changed subject line from /run/dpdk/powermanager ->
/run/dpdk/powermonitor*

Hi All,

Apologies for the delay.

So, I am following the above thread and below are what I understand are the
changes that are needed :


   1. I see that rte_eal_get_runtime_dir() is RTE_EXPORTED_SYMBOL.  I will
   verify that channel_manager has access to this symbol.
   2. Upon successful creation of /run/dpdk, I can mkdir
   CHANNEL_MGR_SOCKET_PATH (/var/run/dpdk/powermonitor) and opendir() that for
   subsequent processing.
   3. Fixup the documentation to reflect correct powermonitor path

Please let me know if I am missing anything or if I misinterpreting any
part of the email exchange.

Thanks for all your help and support.

-Sudheendra G Sampath

On Fri, May 29, 2026 at 9:01 AM Bruce Richardson <bruce.richardson@intel.com>
wrote:

> On Fri, May 29, 2026 at 08:23:24AM -0700, Stephen Hemminger wrote:
> > On Fri, 29 May 2026 09:01:34 +0100
> > Bruce Richardson <bruce.richardson@intel.com> wrote:
> >
> > > On Thu, May 28, 2026 at 07:04:48PM +0000, Sudheendra Sampath wrote:
> > > > This patch for bug 1832 will do the following:
> > > > 1.  If /run/dpdk is not present, it will create it first with and
> > > >     then create powermanager directory underneath it.
> > > > 2.  If /run/dpdk is present, it will verify it is actually a
> directory
> > > >     before creating subdirectory, powermanager.
> > > >
> > > I would suggest using $XDG_RUNTIME_DIR for the directory path, rather
> than
> > > hardcoding it by default. If XDG_RUNTIME_DIR is not set, then maybe
> > > consider using /run/dpdk. However, rather than /run/dpdk, I'd suggest
> using
> > > the normal runtime dir path on most distros as the default:
> > > /run/user/<uid>.
> > >
> > > /Bruce
> >
> > The login in EAL is a little more detailed.
> > The choice is from systemd conventions which follows filesystem
> hierarchy.
> >
> >
> > int eal_create_runtime_dir(void)
> > {
> >       const char *directory;
> >       char run_dir[PATH_MAX];
> >       char tmp[PATH_MAX];
> >       int ret;
> >
> >       /* from RuntimeDirectory= see systemd.exec */
> >       directory = getenv("RUNTIME_DIRECTORY");
> >       if (directory == NULL) {
> >               /*
> >                * Used standard convention defined in
> >                * XDG Base Directory Specification and
> >                * Filesystem Hierarchy Standard.
> >                */
> >               if (getuid() == 0)
> >                       directory = "/var/run";
> >               else
> >                       directory = getenv("XDG_RUNTIME_DIR") ? : "/tmp";
> >       }
> >
> >       /* create DPDK subdirectory under runtime dir */
> >       ret = snprintf(tmp, sizeof(tmp), "%s/dpdk", directory);
> >       if (ret < 0 || ret == sizeof(tmp)) {
> >               EAL_LOG(ERR, "Error creating DPDK runtime path name");
> >               return -1;
> >       }
> >
> >       /* create prefix-specific subdirectory under DPDK runtime dir */
> >       ret = snprintf(run_dir, sizeof(run_dir), "%s/%s",
> >                       tmp, eal_get_hugefile_prefix());
> >       if (ret < 0 || ret == sizeof(run_dir)) {
> >               EAL_LOG(ERR, "Error creating prefix-specific runtime path
> name");
> >               return -1;
> >       }
> >
> >       /* create the path if it doesn't exist. no "mkdir -p" here, so do
> it
> >        * step by step.
> >        */
> >       ret = mkdir(tmp, 0700);
> >       if (ret < 0 && errno != EEXIST) {
> >               EAL_LOG(ERR, "Error creating '%s': %s",
> >                       tmp, strerror(errno));
> >               return -1;
> >       }
> >
> >       ret = mkdir(run_dir, 0700);
> >       if (ret < 0 && errno != EEXIST) {
> >               EAL_LOG(ERR, "Error creating '%s': %s",
> >                       run_dir, strerror(errno));
> >               return -1;
> >       }
>
> Yes. Can the power manager call the rte_eal_get_runtime_dir() API and use
> that as a basis for its working directory? Save duplicating all this logic.
>
> /Bruce
>

[-- Attachment #2: Type: text/html, Size: 5594 bytes --]

^ permalink raw reply

* Re: [PATCH v2 2/2] dts: add build arguments to test run configuration
From: Koushik Bhargav Nimoji @ 2026-06-18 18:59 UTC (permalink / raw)
  To: luca.vizzarro, patrickrobb1997; +Cc: dev, abailey, ahassick, lylavoie
In-Reply-To: <20260609143647.1434076-2-knimoji@iol.unh.edu>

[-- Attachment #1: Type: text/plain, Size: 6479 bytes --]

Recheck-request: github-robot: build

On Tue, Jun 9, 2026 at 10:36 AM Koushik Bhargav Nimoji <knimoji@iol.unh.edu>
wrote:

> This patch adds the ability to specify build arguments when building DPDK
> through DTS. Doing so allows users to build DPDK with the desired build
> arguments, which allows for a more configurable DTS run.
>
> Signed-off-by: Koushik Bhargav Nimoji <knimoji@iol.unh.edu>
> ---
>  dts/configurations/test_run.example.yaml | 13 +++++++++++++
>  dts/framework/config/test_run.py         |  2 ++
>  dts/framework/remote_session/dpdk.py     | 12 ++++++++----
>  dts/framework/utils.py                   | 21 ++++++++++++++++++++-
>  4 files changed, 43 insertions(+), 5 deletions(-)
>
> diff --git a/dts/configurations/test_run.example.yaml
> b/dts/configurations/test_run.example.yaml
> index ee641f5dce..0bd5151801 100644
> --- a/dts/configurations/test_run.example.yaml
> +++ b/dts/configurations/test_run.example.yaml
> @@ -16,6 +16,8 @@
>  #       `precompiled_build_dir` or `build_options` can be defined, but
> not both.
>  #   `compiler_wrapper`:
>  #       Optional, adds a compiler wrapper if present.
> +#   `build_args`:
> +#       The additional build arguments to be used when building DPDK.
>  #   `func_traffic_generator` & `perf_traffic_generator`:
>  #       Define `func_traffic_generator` when `func` set to true.
>  #       Define `perf_traffic_generator` when `perf` set to true.
> @@ -40,6 +42,17 @@ dpdk:
>        # the combination of the following two makes CC="ccache gcc"
>        compiler: gcc
>        compiler_wrapper: ccache # see `Optional Fields`
> +      # arguments to be used when building DPDK
> +      # build_args:
> +      #   c_args:
> +      #     - O3
> +      #     - g
> +      #   b_coverage:
> +      #     - "true"
> +      #   buildtype:
> +      #     - release
> +      #   flags:
> +      #     - strip
>  func_traffic_generator:
>    type: SCAPY
>  # perf_traffic_generator:
> diff --git a/dts/framework/config/test_run.py
> b/dts/framework/config/test_run.py
> index 76e24d1785..eab12041fc 100644
> --- a/dts/framework/config/test_run.py
> +++ b/dts/framework/config/test_run.py
> @@ -191,6 +191,8 @@ class DPDKBuildOptionsConfiguration(FrozenModel):
>      #: This string will be put in front of the compiler when executing
> the build. Useful for adding
>      #: wrapper commands, such as ``ccache``.
>      compiler_wrapper: str = ""
> +    #: The build arguments to build dpdk with
> +    build_args: dict[str, list[str]] = {}
>
>
>  class DPDKUncompiledBuildConfiguration(BaseDPDKBuildConfiguration):
> diff --git a/dts/framework/remote_session/dpdk.py
> b/dts/framework/remote_session/dpdk.py
> index 865f97f6ca..4dc0ceeaaf 100644
> --- a/dts/framework/remote_session/dpdk.py
> +++ b/dts/framework/remote_session/dpdk.py
> @@ -100,8 +100,8 @@ def setup(self) -> None:
>          match self.config:
>              case
> DPDKPrecompiledBuildConfiguration(precompiled_build_dir=build_dir):
>                  self._set_remote_dpdk_build_dir(build_dir)
> -            case
> DPDKUncompiledBuildConfiguration(build_options=build_options):
> -                self._configure_dpdk_build(build_options)
> +            case DPDKUncompiledBuildConfiguration():
> +                self._configure_dpdk_build(self.config.build_options)
>                  self._build_dpdk()
>
>      def teardown(self) -> None:
> @@ -277,16 +277,20 @@ def _build_dpdk(self) -> None:
>          `remote_dpdk_tree_path` has already been set on the SUT node.
>          """
>          ctx = get_ctx()
> +        build_options = getattr(self.config, "build_options")
>          # If the SUT is an ice driver device, make sure to build with 16B
> descriptors.
>          if (
>              ctx.topology.sut_port_ingress
>              and ctx.topology.sut_port_ingress.config.os_driver == "ice"
>          ):
>              meson_args = MesonArgs(
> -                default_library="static", libdir="lib",
> c_args="-DRTE_NET_INTEL_USE_16BYTE_DESC"
> +                build_options.build_args,
> +                default_library="static",
> +                libdir="lib",
> +                c_args="-DRTE_NET_INTEL_USE_16BYTE_DESC",
>              )
>          else:
> -            meson_args = MesonArgs(default_library="static", libdir="lib")
> +            meson_args = MesonArgs(build_options.build_args,
> default_library="static", libdir="lib")
>
>          if SETTINGS.code_coverage:
>              meson_args._add_arg("-Db_coverage=true")
> diff --git a/dts/framework/utils.py b/dts/framework/utils.py
> index 38da88cd9c..e0ed35066c 100644
> --- a/dts/framework/utils.py
> +++ b/dts/framework/utils.py
> @@ -99,10 +99,16 @@ class MesonArgs:
>
>      _default_library: str
>
> -    def __init__(self, default_library: str | None = None, **dpdk_args:
> str | bool):
> +    def __init__(
> +        self,
> +        dpdk_build_args: dict[str, list[str]],
> +        default_library: str | None = None,
> +        **dpdk_args: str | bool,
> +    ):
>          """Initialize the meson arguments.
>
>          Args:
> +            dpdk_build_args: The DPDK build arguments specified in the
> test run configuration file.
>              default_library: The default library type, Meson supports
> ``shared``, ``static`` and
>                  ``both``. Defaults to :data:`None`, in which case the
> argument won't be used.
>              dpdk_args: The arguments found in ``meson_options.txt`` in
> root DPDK directory.
> @@ -121,6 +127,19 @@ def __init__(self, default_library: str | None =
> None, **dpdk_args: str | bool):
>              )
>          )
>
> +        arguments = []
> +        for option, value in dpdk_build_args.items():
> +            if option == "c_args":
> +                values = " ".join(f"-{val}" for val in value)
> +                arguments.append(f'-D{option}="{values}"')
> +            elif option == "flags":
> +                values = " ".join(f"--{val}" for val in value)
> +                arguments.append(values)
> +            else:
> +                arguments.append(f" -D{option}={value[0]}")
> +
> +        self._dpdk_args = " ".join(arguments)
> +
>      def __str__(self) -> str:
>          """The actual args."""
>          return " ".join(f"{self._default_library}
> {self._dpdk_args}".split())
> --
> 2.54.0
>
>

[-- Attachment #2: Type: text/html, Size: 8042 bytes --]

^ permalink raw reply

* Re: [PATCH v1] dts: update dts check format script and resolve errors
From: Koushik Bhargav Nimoji @ 2026-06-18 18:59 UTC (permalink / raw)
  To: luca.vizzarro, patrickrobb1997; +Cc: abailey, ahassick, lylavoie, dev
In-Reply-To: <20260609144501.1441468-1-knimoji@iol.unh.edu>

[-- Attachment #1: Type: text/plain, Size: 80962 bytes --]

Recheck-request: github-robot: build

On Tue, Jun 9, 2026 at 10:45 AM Koushik Bhargav Nimoji <knimoji@iol.unh.edu>
wrote:

> This patch updates the tool versions used in the dts-check-format.sh
> script;by doing so, formatting and type hinting errors that weren't
> previously visible have now appeared. This patch also resolves those
> new formatting and type hinting errors.
>
> Signed-off-by: Koushik Bhargav Nimoji <knimoji@iol.unh.edu>
> ---
>  dts/api/packet.py                             |  15 +-
>  dts/api/testpmd/__init__.py                   |  12 +-
>  dts/api/testpmd/types.py                      |   6 +-
>  dts/framework/config/__init__.py              |  24 +-
>  dts/framework/config/test_run.py              |  12 +-
>  dts/framework/context.py                      |   8 +-
>  .../interactive_remote_session.py             |   2 +-
>  .../remote_session/interactive_shell.py       |   6 +-
>  .../remote_session/remote_session.py          |   4 +-
>  dts/framework/remote_session/shell_pool.py    |   2 +-
>  dts/framework/settings.py                     |   8 +-
>  dts/framework/testbed_model/cpu.py            |   2 +-
>  .../testbed_model/traffic_generator/scapy.py  |   6 +-
>  .../testbed_model/traffic_generator/trex.py   |   6 +-
>  dts/framework/utils.py                        |   9 +-
>  dts/poetry.lock                               | 368 ++++++++++++++----
>  dts/pyproject.toml                            |   8 +-
>  dts/tests/TestSuite_cryptodev_throughput.py   |   4 +-
>  dts/tests/TestSuite_port_control.py           |   3 +-
>  .../TestSuite_single_core_forward_perf.py     |   2 +-
>  20 files changed, 369 insertions(+), 138 deletions(-)
>
> diff --git a/dts/api/packet.py b/dts/api/packet.py
> index 094a1b7a9d..3dda18e781 100644
> --- a/dts/api/packet.py
> +++ b/dts/api/packet.py
> @@ -87,9 +87,9 @@ def send_packets_and_capture(
>          CapturingTrafficGenerator,
>      )
>
> -    assert isinstance(
> -        get_ctx().func_tg, CapturingTrafficGenerator
> -    ), "Cannot capture with a non-capturing traffic generator"
> +    assert isinstance(get_ctx().func_tg, CapturingTrafficGenerator), (
> +        "Cannot capture with a non-capturing traffic generator"
> +    )
>      tg: CapturingTrafficGenerator = cast(CapturingTrafficGenerator,
> get_ctx().func_tg)
>      # TODO: implement @requires for types of traffic generator
>      packets = adjust_addresses(packets)
> @@ -308,8 +308,7 @@ def _verify_l2_frame(received_packet: Ether,
> contains_l3: bool) -> bool:
>      if contains_l3:
>          expected_src_mac = get_ctx().topology.sut_port_egress.mac_address
>      log_debug(
> -        f"Comparing received src mac '{received_packet.src}' "
> -        f"with expected '{expected_src_mac}'."
> +        f"Comparing received src mac '{received_packet.src}' with
> expected '{expected_src_mac}'."
>      )
>      if received_packet.src != expected_src_mac:
>          return False
> @@ -344,9 +343,9 @@ def assess_performance_by_packet(
>          PerformanceTrafficGenerator,
>      )
>
> -    assert isinstance(
> -        get_ctx().perf_tg, PerformanceTrafficGenerator
> -    ), "Cannot send performance traffic with non-performance traffic
> generator"
> +    assert isinstance(get_ctx().perf_tg, PerformanceTrafficGenerator), (
> +        "Cannot send performance traffic with non-performance traffic
> generator"
> +    )
>      tg: PerformanceTrafficGenerator = cast(PerformanceTrafficGenerator,
> get_ctx().perf_tg)
>      # TODO: implement @requires for types of traffic generator
>      return tg.calculate_traffic_and_stats(packet, duration, send_mpps)
> diff --git a/dts/api/testpmd/__init__.py b/dts/api/testpmd/__init__.py
> index e9187440bb..1bde66d876 100644
> --- a/dts/api/testpmd/__init__.py
> +++ b/dts/api/testpmd/__init__.py
> @@ -76,7 +76,9 @@ def _requires_stopped_ports(func: TestPmdMethod) ->
> TestPmdMethod:
>      """
>
>      @functools.wraps(func)
> -    def _wrapper(self: "TestPmd", *args: P.args, **kwargs: P.kwargs) ->
> Any:
> +    def _wrapper(
> +        self: "TestPmd", *args: P.args, **kwargs: P.kwargs
> +    ) -> Callable[P, "TestPmd"] | Any:
>          if self.ports_started:
>              self._logger.debug("Ports need to be stopped to continue.")
>              self.stop_all_ports()
> @@ -100,7 +102,9 @@ def _requires_started_ports(func: TestPmdMethod) ->
> TestPmdMethod:
>      """
>
>      @functools.wraps(func)
> -    def _wrapper(self: "TestPmd", *args: P.args, **kwargs: P.kwargs) ->
> Any:
> +    def _wrapper(
> +        self: "TestPmd", *args: P.args, **kwargs: P.kwargs
> +    ) -> Callable[P, "TestPmd"] | Any:
>          if not self.ports_started:
>              self._logger.debug("Ports need to be started to continue.")
>              self.start_all_ports()
> @@ -128,7 +132,9 @@ def _add_remove_mtu(mtu: int = 1500) ->
> Callable[[TestPmdMethod], TestPmdMethod]
>
>      def decorator(func: TestPmdMethod) -> TestPmdMethod:
>          @functools.wraps(func)
> -        def wrapper(self: "TestPmd", *args: P.args, **kwargs: P.kwargs)
> -> Any:
> +        def wrapper(
> +            self: "TestPmd", *args: P.args, **kwargs: P.kwargs
> +        ) -> Callable[P, TestPmd] | Any:
>              original_mtu = self.ports[0].mtu
>              self.set_port_mtu_all(mtu=mtu, verify=False)
>              retval = func(self, *args, **kwargs)
> diff --git a/dts/api/testpmd/types.py b/dts/api/testpmd/types.py
> index 0d322aece2..af3263682e 100644
> --- a/dts/api/testpmd/types.py
> +++ b/dts/api/testpmd/types.py
> @@ -279,7 +279,7 @@ def from_list_string(cls, names: str) -> Self:
>          Returns:
>              An instance of this flag.
>          """
> -        flag = cls(0)
> +        flag: RSSOffloadTypesFlag = cls(0)
>          for name in names.split():
>              flag |= cls.from_str(name)
>          return flag
> @@ -960,7 +960,7 @@ def from_list_string(cls, names: str) -> Self:
>          Returns:
>              An instance of this flag.
>          """
> -        flag = cls(0)
> +        flag: PacketOffloadFlag = cls(0)
>          for name in names.split():
>              flag |= cls.from_str(name)
>          return flag
> @@ -1168,7 +1168,7 @@ def from_list_string(cls, names: str) -> Self:
>          Returns:
>              An instance of this flag.
>          """
> -        flag = cls(0)
> +        flag: RtePTypes = cls(0)
>          for name in names.split():
>              flag |= cls.from_str(name)
>          return flag
> diff --git a/dts/framework/config/__init__.py
> b/dts/framework/config/__init__.py
> index d2f0138e4a..a8861894b7 100644
> --- a/dts/framework/config/__init__.py
> +++ b/dts/framework/config/__init__.py
> @@ -85,9 +85,9 @@ def validate_port_links(self) -> Self:
>              sut_node_port_peer = existing_port_links.get(
>                  (self.test_run.system_under_test_node, link.sut_port),
> None
>              )
> -            assert (
> -                sut_node_port_peer is not None
> -            ), f"Invalid SUT node port specified for link
> port_topology.{link_idx}."
> +            assert sut_node_port_peer is not None, (
> +                f"Invalid SUT node port specified for link
> port_topology.{link_idx}."
> +            )
>
>              assert sut_node_port_peer is False or sut_node_port_peer ==
> link.right, (
>                  f"The SUT node port for link port_topology.{link_idx} is "
> @@ -97,9 +97,9 @@ def validate_port_links(self) -> Self:
>              tg_node_port_peer = existing_port_links.get(
>                  (self.test_run.traffic_generator_node, link.tg_port), None
>              )
> -            assert (
> -                tg_node_port_peer is not None
> -            ), f"Invalid TG node port specified for link
> port_topology.{link_idx}."
> +            assert tg_node_port_peer is not None, (
> +                f"Invalid TG node port specified for link
> port_topology.{link_idx}."
> +            )
>
>              assert tg_node_port_peer is False or sut_node_port_peer ==
> link.left, (
>                  f"The TG node port for link port_topology.{link_idx} is "
> @@ -117,16 +117,16 @@ def validate_test_run_against_nodes(self) -> Self:
>          sut_node_name = self.test_run.system_under_test_node
>          sut_node = next((n for n in self.nodes if n.name ==
> sut_node_name), None)
>
> -        assert (
> -            sut_node is not None
> -        ), f"The system_under_test_node {sut_node_name} is not a valid
> node name."
> +        assert sut_node is not None, (
> +            f"The system_under_test_node {sut_node_name} is not a valid
> node name."
> +        )
>
>          tg_node_name = self.test_run.traffic_generator_node
>          tg_node = next((n for n in self.nodes if n.name ==
> tg_node_name), None)
>
> -        assert (
> -            tg_node is not None
> -        ), f"The traffic_generator_name {tg_node_name} is not a valid
> node name."
> +        assert tg_node is not None, (
> +            f"The traffic_generator_name {tg_node_name} is not a valid
> node name."
> +        )
>
>          return self
>
> diff --git a/dts/framework/config/test_run.py
> b/dts/framework/config/test_run.py
> index 76e24d1785..3cd643981d 100644
> --- a/dts/framework/config/test_run.py
> +++ b/dts/framework/config/test_run.py
> @@ -233,9 +233,9 @@ def test_suite_spec(self) -> "TestSuiteSpec":
>          from framework.test_suite import find_by_name
>
>          test_suite_spec = find_by_name(self.test_suite_name)
> -        assert (
> -            test_suite_spec is not None
> -        ), f"{self.test_suite_name} is not a valid test suite module
> name."
> +        assert test_suite_spec is not None, (
> +            f"{self.test_suite_name} is not a valid test suite module
> name."
> +        )
>          return test_suite_spec
>
>      @cached_property
> @@ -384,9 +384,9 @@ def convert_from_string(cls, data: Any) -> Any:
>      @model_validator(mode="after")
>      def verify_distinct_nodes(self) -> Self:
>          """Verify that each side of the link has distinct nodes."""
> -        assert (
> -            self.left.node_type != self.right.node_type
> -        ), "Linking ports of the same node is unsupported."
> +        assert self.left.node_type != self.right.node_type, (
> +            "Linking ports of the same node is unsupported."
> +        )
>          return self
>
>
> diff --git a/dts/framework/context.py b/dts/framework/context.py
> index 8f1021dc96..efe9af0645 100644
> --- a/dts/framework/context.py
> +++ b/dts/framework/context.py
> @@ -60,9 +60,9 @@ def reset(self) -> None:
>                  else _field.default
>              )
>
> -            assert (
> -                default is not MISSING
> -            ), "{LocalContext.__name__} must have defaults on all fields!"
> +            assert default is not MISSING, (
> +                "{LocalContext.__name__} must have defaults on all
> fields!"
> +            )
>
>              setattr(self, _field.name, default)
>
> @@ -108,7 +108,7 @@ def filter_cores(
>  ) -> Callable[[type["TestProtocol"]], Callable]:
>      """Decorates functions that require a temporary update to the lcore
> specifier."""
>
> -    def decorator(func: type["TestProtocol"]) -> Callable:
> +    def decorator(func: type["TestProtocol"]) -> Callable[P,
> type["TestProtocol"]]:
>          @functools.wraps(func)
>          def wrapper(*args: P.args, **kwargs: P.kwargs) -> Any:
>              local_ctx = get_ctx().local
> diff --git a/dts/framework/remote_session/interactive_remote_session.py
> b/dts/framework/remote_session/interactive_remote_session.py
> index c8156b4345..fc42e862bc 100644
> --- a/dts/framework/remote_session/interactive_remote_session.py
> +++ b/dts/framework/remote_session/interactive_remote_session.py
> @@ -109,7 +109,7 @@ def _connect(self) -> None:
>                  self._logger.debug(traceback.format_exc())
>                  self._logger.warning(e)
>                  self._logger.info(
> -                    f"Retrying interactive session connection: retry
> number {retry_attempt +1}"
> +                    f"Retrying interactive session connection: retry
> number {retry_attempt + 1}"
>                  )
>              else:
>                  break
> diff --git a/dts/framework/remote_session/interactive_shell.py
> b/dts/framework/remote_session/interactive_shell.py
> index a65cbce209..6bba58a4f6 100644
> --- a/dts/framework/remote_session/interactive_shell.py
> +++ b/dts/framework/remote_session/interactive_shell.py
> @@ -50,7 +50,9 @@
>  def only_active(func: InteractiveShellMethod) -> InteractiveShellMethod:
>      """This decorator will skip running the method if the SSH channel is
> not active."""
>
> -    def _wrapper(self: "InteractiveShell", *args: P.args, **kwargs:
> P.kwargs) -> R | None:
> +    def _wrapper(
> +        self: "InteractiveShell", *args: P.args, **kwargs: P.kwargs
> +    ) -> Callable[P, "InteractiveShell"] | None:
>          if self._ssh_channel.active:
>              return func(self, *args, **kwargs)
>          return None
> @@ -167,7 +169,7 @@ def start_application(self, prompt: str | None = None,
> add_to_shell_pool: bool =
>                  break
>              except InteractiveSSHTimeoutError:
>                  self._logger.info(
> -                    f"Interactive shell failed to start (attempt
> {attempt+1} out of "
> +                    f"Interactive shell failed to start (attempt {attempt
> + 1} out of "
>                      f"{self._init_attempts})"
>                  )
>          else:
> diff --git a/dts/framework/remote_session/remote_session.py
> b/dts/framework/remote_session/remote_session.py
> index 158325bb7f..fb5f6fedf5 100644
> --- a/dts/framework/remote_session/remote_session.py
> +++ b/dts/framework/remote_session/remote_session.py
> @@ -72,9 +72,7 @@ def __post_init__(self, init_stdout: str, init_stderr:
> str) -> None:
>      def __str__(self) -> str:
>          """Format the command outputs."""
>          return (
> -            f"stdout: '{self.stdout}'\n"
> -            f"stderr: '{self.stderr}'\n"
> -            f"return_code: '{self.return_code}'"
> +            f"stdout: '{self.stdout}'\nstderr:
> '{self.stderr}'\nreturn_code: '{self.return_code}'"
>          )
>
>
> diff --git a/dts/framework/remote_session/shell_pool.py
> b/dts/framework/remote_session/shell_pool.py
> index 241737eab3..710107c6cb 100644
> --- a/dts/framework/remote_session/shell_pool.py
> +++ b/dts/framework/remote_session/shell_pool.py
> @@ -74,7 +74,7 @@ def unregister_shell(self, shell: "InteractiveShell") ->
> None:
>
>      def start_new_pool(self) -> None:
>          """Start a new shell pool."""
> -        self._logger.debug(f"Starting new shell pool and advancing to
> level {self.pool_level+1}.")
> +        self._logger.debug(f"Starting new shell pool and advancing to
> level {self.pool_level + 1}.")
>          self._pools.append(set())
>
>      def terminate_current_pool(self) -> None:
> diff --git a/dts/framework/settings.py b/dts/framework/settings.py
> index b08373b7ea..f329677804 100644
> --- a/dts/framework/settings.py
> +++ b/dts/framework/settings.py
> @@ -249,9 +249,9 @@ def error(self, message) -> NoReturn:
>              if _is_from_env(action):
>                  action_name = _get_action_name(action)
>                  env_var_name = _get_env_var_name(action)
> -                assert (
> -                    env_var_name is not None
> -                ), "Action was set from environment, but no environment
> variable name was found."
> +                assert env_var_name is not None, (
> +                    "Action was set from environment, but no environment
> variable name was found."
> +                )
>                  env_var_value = os.environ.get(env_var_name)
>
>                  message = message.replace(
> @@ -260,7 +260,7 @@ def error(self, message) -> NoReturn:
>                  )
>
>          print(f"{self.prog}: error: {message}\n", file=sys.stderr)
> -        self.exit(2, "For help and usage, " "run the command with the
> --help flag.\n")
> +        self.exit(2, "For help and usage, run the command with the --help
> flag.\n")
>
>
>  class _EnvVarHelpFormatter(ArgumentDefaultsHelpFormatter):
> diff --git a/dts/framework/testbed_model/cpu.py
> b/dts/framework/testbed_model/cpu.py
> index 6e2ecca080..a9471709dd 100644
> --- a/dts/framework/testbed_model/cpu.py
> +++ b/dts/framework/testbed_model/cpu.py
> @@ -105,7 +105,7 @@ def __init__(self, lcore_list: list[int] | list[str] |
> list[LogicalCore] | str)
>
>          # the input lcores may not be sorted
>          self._lcore_list.sort()
> -        self._lcore_str =
> f'{",".join(self._get_consecutive_lcores_range(self._lcore_list))}'
> +        self._lcore_str =
> f"{','.join(self._get_consecutive_lcores_range(self._lcore_list))}"
>
>      @property
>      def lcore_list(self) -> list[int]:
> diff --git a/dts/framework/testbed_model/traffic_generator/scapy.py
> b/dts/framework/testbed_model/traffic_generator/scapy.py
> index c6e9006205..62853a34e4 100644
> --- a/dts/framework/testbed_model/traffic_generator/scapy.py
> +++ b/dts/framework/testbed_model/traffic_generator/scapy.py
> @@ -314,9 +314,9 @@ def __init__(self, tg_node: Node, config:
> ScapyTrafficGeneratorConfig, **kwargs:
>              kwargs: Additional keyword arguments. Supported arguments
> correspond to the parameters
>                  of :meth:`PythonShell.__init__` in this case.
>          """
> -        assert (
> -            tg_node.config.os == OS.linux
> -        ), "Linux is the only supported OS for scapy traffic generation"
> +        assert tg_node.config.os == OS.linux, (
> +            "Linux is the only supported OS for scapy traffic generation"
> +        )
>
>          super().__init__(tg_node=tg_node, config=config, **kwargs)
>
> diff --git a/dts/framework/testbed_model/traffic_generator/trex.py
> b/dts/framework/testbed_model/traffic_generator/trex.py
> index 22cd20dea9..0314dfc881 100644
> --- a/dts/framework/testbed_model/traffic_generator/trex.py
> +++ b/dts/framework/testbed_model/traffic_generator/trex.py
> @@ -94,9 +94,9 @@ def __init__(self, tg_node: Node, config:
> TrexTrafficGeneratorConfig) -> None:
>              tg_node: TG node the TRex instance is operating on.
>              config: Traffic generator config provided for TRex instance.
>          """
> -        assert (
> -            tg_node.config.os == OS.linux
> -        ), "Linux is the only supported OS for trex traffic generation"
> +        assert tg_node.config.os == OS.linux, (
> +            "Linux is the only supported OS for trex traffic generation"
> +        )
>
>          super().__init__(tg_node=tg_node, config=config)
>          self._tg_node_config = tg_node.config
> diff --git a/dts/framework/utils.py b/dts/framework/utils.py
> index 9917ffbfaa..d16580ec85 100644
> --- a/dts/framework/utils.py
> +++ b/dts/framework/utils.py
> @@ -154,7 +154,11 @@ def extension(self) -> str:
>          For other compression formats, the extension will be in the format
>          'tar.{compression format}'.
>          """
> -        return f"{self.value}" if self == self.none else
> f"{type(self).none.value}.{self.value}"
> +        return (
> +            f"{self.value}"
> +            if self == self.none
> +            else f"{TarCompressionFormat.none.value}.{self.value}"
> +        )
>
>
>  def convert_to_list_of_string(value: Any | list[Any]) -> list[str]:
> @@ -207,7 +211,8 @@ def filter_func(tarinfo: tarfile.TarInfo) ->
> tarfile.TarInfo | None:
>          return None
>
>      target_tarball_path =
> dir_path.with_suffix(f".{compress_format.extension}")
> -    with tarfile.open(target_tarball_path, f"w:{compress_format.value}")
> as tar:
> +    compression_format: Any = f"w:{compress_format.value}"
> +    with tarfile.open(target_tarball_path, compression_format) as tar:
>          tar.add(dir_path, arcname=dir_path.name,
> filter=create_filter_function(exclude))
>
>      return target_tarball_path
> diff --git a/dts/poetry.lock b/dts/poetry.lock
> index 0ad5d32b85..eda7cc2fec 100644
> --- a/dts/poetry.lock
> +++ b/dts/poetry.lock
> @@ -1,4 +1,4 @@
> -# This file is automatically @generated by Poetry 1.8.3 and should not be
> changed by hand.
> +# This file is automatically @generated by Poetry 2.3.2 and should not be
> changed by hand.
>
>  [[package]]
>  name = "aenum"
> @@ -6,6 +6,7 @@ version = "3.1.15"
>  description = "Advanced Enumerations (compatible with Python's stdlib
> Enum), NamedTuples, and NamedConstants"
>  optional = false
>  python-versions = "*"
> +groups = ["main"]
>  files = [
>      {file = "aenum-3.1.15-py2-none-any.whl", hash =
> "sha256:27b1710b9d084de6e2e695dab78fe9f269de924b51ae2850170ee7e1ca6288a5"},
>      {file = "aenum-3.1.15-py3-none-any.whl", hash =
> "sha256:e0dfaeea4c2bd362144b87377e2c61d91958c5ed0b4daf89cb6f45ae23af6288"},
> @@ -18,17 +19,62 @@ version = "0.7.0"
>  description = "Reusable constraint types to use with typing.Annotated"
>  optional = false
>  python-versions = ">=3.8"
> +groups = ["main"]
>  files = [
>      {file = "annotated_types-0.7.0-py3-none-any.whl", hash =
> "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"},
>      {file = "annotated_types-0.7.0.tar.gz", hash =
> "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"},
>  ]
>
> +[[package]]
> +name = "ast-serialize"
> +version = "0.5.0"
> +description = "Python bindings for mypy AST serialization"
> +optional = false
> +python-versions = ">=3.7"
> +groups = ["dev"]
> +files = [
> +    {file = "ast_serialize-0.5.0-cp314-cp314t-macosx_10_12_x86_64.whl",
> hash =
> "sha256:8f5c14f169eb0972c0c21bada5358b23d6047c76583b005234f865b11f1fa00a"},
> +    {file = "ast_serialize-0.5.0-cp314-cp314t-macosx_11_0_arm64.whl",
> hash =
> "sha256:7d1a2de9de5be04652f0ed60738356ef94f66db37924a9499fffe98dc491aa0b"},
> +    {file =
> "ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
> hash =
> "sha256:be5173fb66f9b49026d9d5a2ff0fc7c7009077107c0eb285b2d60fdf1fe10bd1"},
> +    {file =
> "ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl",
> hash =
> "sha256:f8015cd071ac1339924ee2b8098c93e00e155f30a16f40ec9816fcf84f4753f6"},
> +    {file =
> "ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl",
> hash =
> "sha256:5499e8797edff2a9186aa313ed382c6b422e798e9332d9953badcee6e69a88f2"},
> +    {file =
> "ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl",
> hash =
> "sha256:6848f2a093fb5548751a9a09bff8fcd229e2bbeb0e3331f391b6ae6d26cd9903"},
> +    {file =
> "ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
> hash =
> "sha256:832d4c998e0b091fd60a6d6bceee535483c4d490de9ba85003af835225719261"},
> +    {file =
> "ast_serialize-0.5.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash =
> "sha256:16db7c62ec0b8efe1d7afd283a388d8f74f2605d56032e5a37747d2de8dba027"},
> +    {file =
> "ast_serialize-0.5.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl",
> hash =
> "sha256:baf5eb061eb5bccade4128ad42da33787d72f6013809cd1b590376ece8b3c937"},
> +    {file = "ast_serialize-0.5.0-cp314-cp314t-musllinux_1_2_aarch64.whl",
> hash =
> "sha256:104e4a35bd7c124173c41760ef9aaea17ddb3f86c65cb643671d59afbe3ee94c"},
> +    {file = "ast_serialize-0.5.0-cp314-cp314t-musllinux_1_2_armv7l.whl",
> hash =
> "sha256:36be371028fc1675acb38a331bde160dbab7ff907fdf00b67eb6911aa106951b"},
> +    {file = "ast_serialize-0.5.0-cp314-cp314t-musllinux_1_2_i686.whl",
> hash =
> "sha256:061ee58bdb52341c8201a6df41182a977736bae3b7ded87ca7176ca25a8a47ab"},
> +    {file = "ast_serialize-0.5.0-cp314-cp314t-musllinux_1_2_x86_64.whl",
> hash =
> "sha256:b15219e9cdc9f53f6f4cb51c009203507228226148c05c5e8fe451c28b435eb3"},
> +    {file = "ast_serialize-0.5.0-cp314-cp314t-win32.whl", hash =
> "sha256:842d1c004bb466c7df036f95fabef789570541922b10976b12f5592a69cf0b38"},
> +    {file = "ast_serialize-0.5.0-cp314-cp314t-win_amd64.whl", hash =
> "sha256:b0c06d760909b095cc466356dfccd05a1c7233a6ca191c020dca2c6a6f16c24c"},
> +    {file = "ast_serialize-0.5.0-cp314-cp314t-win_arm64.whl", hash =
> "sha256:787baedb0262cc49e8ce37cc15c00ae818e46a165a3b36f5e21ed174998104cb"},
> +    {file = "ast_serialize-0.5.0-cp39-abi3-macosx_10_12_x86_64.whl", hash
> =
> "sha256:0668aa9459cfa8c9c49ddd2163ebcf43088ba045ef7492af6fe22e0098303101"},
> +    {file = "ast_serialize-0.5.0-cp39-abi3-macosx_11_0_arm64.whl", hash =
> "sha256:bf683d6363edf2b39eed6b6d4fe22d34b6203867a67e27134d9e2a2680c4bc4a"},
> +    {file =
> "ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
> hash =
> "sha256:9cc22cf0c9be65e71cf88fda130af60d61eb4a79370ad4cfe7900d48a4aa2211"},
> +    {file =
> "ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl",
> hash =
> "sha256:f66173891548c9f2726bf27957b41cabce12fa679dc6da505ddbde4d4b3b31cf"},
> +    {file =
> "ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl",
> hash =
> "sha256:e42d729ef2be96a14efbad355093284739e3670ece3e534f82cc8832790911d9"},
> +    {file =
> "ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl",
> hash =
> "sha256:b725026bafa801dbd7310eb13a75f0a2e370e7e51b2cb225f9d21fcfadf919ee"},
> +    {file =
> "ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
> hash =
> "sha256:b54f60c1d78767a53b67eaa663f0dfac3afe606aa07f1301572f588b73d64809"},
> +    {file = "ast_serialize-0.5.0-cp39-abi3-manylinux_2_31_riscv64.whl",
> hash =
> "sha256:27d51654fc240a1e87e742d353d98eb45b75f62f129086b3596ab53df2ac2a43"},
> +    {file =
> "ast_serialize-0.5.0-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.whl",
> hash =
> "sha256:2782c36237c46dd1674542f2109740ea5ea485a169bf1431939ada0434e17934"},
> +    {file = "ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_aarch64.whl",
> hash =
> "sha256:1943db345233cc7194a470f13afa9c59772c0b123dea0c9414c4d4ca54369759"},
> +    {file = "ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_armv7l.whl",
> hash =
> "sha256:df1c00022cbbcb064bfaa505aa9c9295362443ce5dacb459d1331d3da353f887"},
> +    {file = "ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_i686.whl", hash
> =
> "sha256:cae65289fc456fde04af979a2be09302ef5d8ab92ef23e596d6746dc267ada27"},
> +    {file = "ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_x86_64.whl",
> hash =
> "sha256:239a4c354e8d676e9d94631d1d4a64edc6b266f86ff3a5a80aedd344f342c01d"},
> +    {file = "ast_serialize-0.5.0-cp39-abi3-win32.whl", hash =
> "sha256:143a4ef63285a075871908fda3672dc21864b83a8ec3ee12304aa3e4c5387b9a"},
> +    {file = "ast_serialize-0.5.0-cp39-abi3-win_amd64.whl", hash =
> "sha256:cf25572c526add400f26a4750dc6ce0c3bb93fc1f75e7ae0cad4ce4f2cd5c590"},
> +    {file = "ast_serialize-0.5.0-cp39-abi3-win_arm64.whl", hash =
> "sha256:92a31c9c20d25a076edaeec76b128a3535d74a24f340b9a8a7e96c9b86dc9642"},
> +    {file = "ast_serialize-0.5.0.tar.gz", hash =
> "sha256:5880091bfe6f4f986f22866375c2e884843e7a0b6343ae41aeea659613d879b6"},
> +]
> +
>  [[package]]
>  name = "bcrypt"
>  version = "4.2.1"
>  description = "Modern password hashing for your software and your servers"
>  optional = false
>  python-versions = ">=3.7"
> +groups = ["main"]
>  files = [
>      {file = "bcrypt-4.2.1-cp37-abi3-macosx_10_12_universal2.whl", hash =
> "sha256:1340411a0894b7d3ef562fb233e4b6ed58add185228650942bdc885362f32c17"},
>      {file =
> "bcrypt-4.2.1-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
> hash =
> "sha256:b1ee315739bc8387aa36ff127afc99120ee452924e0df517a8f3e4c0187a0f5f"},
> @@ -67,6 +113,7 @@ version = "1.17.1"
>  description = "Foreign Function Interface for Python calling C code."
>  optional = false
>  python-versions = ">=3.8"
> +groups = ["main", "dev"]
>  files = [
>      {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash =
> "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"},
>      {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash =
> "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"},
> @@ -136,6 +183,7 @@ files = [
>      {file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash =
> "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"},
>      {file = "cffi-1.17.1.tar.gz", hash =
> "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"},
>  ]
> +markers = {dev = "platform_python_implementation != \"PyPy\""}
>
>  [package.dependencies]
>  pycparser = "*"
> @@ -146,6 +194,7 @@ version = "44.0.0"
>  description = "cryptography is a package which provides cryptographic
> recipes and primitives to Python developers."
>  optional = false
>  python-versions = "!=3.9.0,!=3.9.1,>=3.7"
> +groups = ["main", "dev"]
>  files = [
>      {file = "cryptography-44.0.0-cp37-abi3-macosx_10_9_universal2.whl",
> hash =
> "sha256:84111ad4ff3f6253820e6d3e58be2cc2a00adb29335d4cacb5ab4d4d34f2a123"},
>      {file =
> "cryptography-44.0.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
> hash =
> "sha256:b15492a11f9e1b62ba9d73c210e2416724633167de94607ec6069ef724fad092"},
> @@ -180,10 +229,10 @@ files = [
>  cffi = {version = ">=1.12", markers = "platform_python_implementation !=
> \"PyPy\""}
>
>  [package.extras]
> -docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=3.0.0)"]
> +docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=3.0.0) ; python_version
> >= \"3.8\""]
>  docstest = ["pyenchant (>=3)", "readme-renderer (>=30.0)",
> "sphinxcontrib-spelling (>=7.3.1)"]
> -nox = ["nox (>=2024.4.15)", "nox[uv] (>=2024.3.2)"]
> -pep8test = ["check-sdist", "click (>=8.0.1)", "mypy (>=1.4)", "ruff
> (>=0.3.6)"]
> +nox = ["nox (>=2024.4.15)", "nox[uv] (>=2024.3.2) ; python_version >=
> \"3.8\""]
> +pep8test = ["check-sdist ; python_version >= \"3.8\"", "click (>=8.0.1)",
> "mypy (>=1.4)", "ruff (>=0.3.6)"]
>  sdist = ["build (>=1.0.0)"]
>  ssh = ["bcrypt (>=3.1.5)"]
>  test = ["certifi (>=2024)", "cryptography-vectors (==44.0.0)", "pretend
> (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov
> (>=2.10.1)", "pytest-xdist (>=3.5.0)"]
> @@ -195,6 +244,7 @@ version = "5.1.1"
>  description = "Decorators for Humans"
>  optional = false
>  python-versions = ">=3.5"
> +groups = ["main"]
>  files = [
>      {file = "decorator-5.1.1-py3-none-any.whl", hash =
> "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"},
>      {file = "decorator-5.1.1.tar.gz", hash =
> "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"},
> @@ -206,6 +256,7 @@ version = "1.2.18"
>  description = "Python @deprecated decorator to deprecate old python
> classes, functions or methods."
>  optional = false
>  python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7"
> +groups = ["main"]
>  files = [
>      {file = "Deprecated-1.2.18-py2.py3-none-any.whl", hash =
> "sha256:bd5011788200372a32418f888e326a09ff80d0214bd961147cfed01b5c018eec"},
>      {file = "deprecated-1.2.18.tar.gz", hash =
> "sha256:422b6f6d859da6f2ef57857761bfb392480502a64c3028ca9bbe86085d72115d"},
> @@ -215,7 +266,7 @@ files = [
>  wrapt = ">=1.10,<2"
>
>  [package.extras]
> -dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "setuptools", "tox"]
> +dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "setuptools ;
> python_version >= \"3.12\"", "tox"]
>
>  [[package]]
>  name = "fabric"
> @@ -223,6 +274,7 @@ version = "3.2.2"
>  description = "High level SSH command execution"
>  optional = false
>  python-versions = "*"
> +groups = ["main"]
>  files = [
>      {file = "fabric-3.2.2-py3-none-any.whl", hash =
> "sha256:91c47c0be68b14936c88b34da8a1f55e5710fd28397dac5d4ff2e21558113a6f"},
>      {file = "fabric-3.2.2.tar.gz", hash =
> "sha256:8783ca42e3b0076f08b26901aac6b9d9b1f19c410074e7accfab902c184ff4a3"},
> @@ -243,56 +295,177 @@ version = "2.2.0"
>  description = "Pythonic task execution"
>  optional = false
>  python-versions = ">=3.6"
> +groups = ["main"]
>  files = [
>      {file = "invoke-2.2.0-py3-none-any.whl", hash =
> "sha256:6ea924cc53d4f78e3d98bc436b08069a03077e6f85ad1ddaa8a116d7dad15820"},
>      {file = "invoke-2.2.0.tar.gz", hash =
> "sha256:ee6cbb101af1a859c7fe84f2a264c059020b0cb7fe3535f9424300ab568f6bd5"},
>  ]
>
> +[[package]]
> +name = "librt"
> +version = "0.11.0"
> +description = "Mypyc runtime library"
> +optional = false
> +python-versions = ">=3.9"
> +groups = ["dev"]
> +markers = "platform_python_implementation != \"PyPy\""
> +files = [
> +    {file = "librt-0.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash =
> "sha256:6e94ebfcfa2d5e9926d6c3b9aa4617ffc42a845b4321fb84021b872358c82a0f"},
> +    {file = "librt-0.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash =
> "sha256:ae627397a2f351560440d872d6f7c8dbb4072e57868e7b2fc5b8b430fe489d45"},
> +    {file =
> "librt-0.11.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl",
> hash =
> "sha256:dc329359321b67d24efdf4bc69012b0597001649544db662c001db5a0184794c"},
> +    {file =
> "librt-0.11.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl",
> hash =
> "sha256:7e82e642ab0f7608ce2fe53d76ca2280a9ee33a1b06556142c7c6fe80a86fc33"},
> +    {file =
> "librt-0.11.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl",
> hash =
> "sha256:88145c15c67731d54283d135b03244028c750cc9edc334a96a4f5950ebdb2884"},
> +    {file =
> "librt-0.11.0-cp310-cp310-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl",
> hash =
> "sha256:9d36a51b3d93320b686588e27123f4995804dbf1bce81df78c02fc3c6eea9280"},
> +    {file = "librt-0.11.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash =
> "sha256:d00f3ac06a2a8b246327f11e186a53a100a4d5c7ed52346367e5ec751d51586c"},
> +    {file = "librt-0.11.0-cp310-cp310-musllinux_1_2_i686.whl", hash =
> "sha256:461bbceede621f1ffb8839755f8663e886087ee7af16294cab7fb4d782c62eeb"},
> +    {file = "librt-0.11.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash =
> "sha256:0cad8a4d6a8ff03c9b76f9414caccd78e7cfbc8a2e12fa334d8e1d9932753783"},
> +    {file = "librt-0.11.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash =
> "sha256:f37aa505b3cf60701562eddb32df74b12a9e380c207fd8b06dd157a943ac7ea0"},
> +    {file = "librt-0.11.0-cp310-cp310-win32.whl", hash =
> "sha256:94663a21534637f0e787ec2a2a756022df6e5b7b2335a5cdd7d8e33d68a2af89"},
> +    {file = "librt-0.11.0-cp310-cp310-win_amd64.whl", hash =
> "sha256:dec7db73758c2b54953fd8b7fe348c45188fe26b39ee18446196edd08453a5d4"},
> +    {file = "librt-0.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash =
> "sha256:93d95bd45b7d58343d8b90d904450a545144eec19a002511163426f8ab1fae29"},
> +    {file = "librt-0.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash =
> "sha256:4ee278c769a713638cdacd4c0436d72156e75df3ebc0166ab2b9dc43acc386c9"},
> +    {file =
> "librt-0.11.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl",
> hash =
> "sha256:f230cb1cbc9faaa616f9a678f530ebcf186e414b6bcbd88b960e4ba1b92428d5"},
> +    {file =
> "librt-0.11.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl",
> hash =
> "sha256:5d63c855d86938d9de93e265c9bd8c705b51ec494de5738340ee93767a686e4b"},
> +    {file =
> "librt-0.11.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl",
> hash =
> "sha256:993f028be9e96a08d31df3479ac80d99be374d17f3b78e4796b3fd3c913d4e89"},
> +    {file =
> "librt-0.11.0-cp311-cp311-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl",
> hash =
> "sha256:258d73a0aa66a055e65b2e4d1b8cdb23b9d132c5bb915d9547d804fcaed116cc"},
> +    {file = "librt-0.11.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash =
> "sha256:0827efe7854718f04aaddf6496e96960a956e676fe1d0f04eb41511fd8ad06d5"},
> +    {file = "librt-0.11.0-cp311-cp311-musllinux_1_2_i686.whl", hash =
> "sha256:7753e57d6e12d019c0d8786f1c09c709f4c3fcc57c3887b24e36e6c06ec938b7"},
> +    {file = "librt-0.11.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash =
> "sha256:11bd19822431cc21af9f27374e7ae2e58103c7d98bda823536a6c47f6bb2bb3d"},
> +    {file = "librt-0.11.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash =
> "sha256:22bdf239b219d3993761a148ffa134b19e52e9989c84f845d5d7b71d70a17412"},
> +    {file = "librt-0.11.0-cp311-cp311-win32.whl", hash =
> "sha256:46c60b61e308eb535fbd6fa622b1ee1bb2815691c1ad9c98bf7b84952ec3bc8d"},
> +    {file = "librt-0.11.0-cp311-cp311-win_amd64.whl", hash =
> "sha256:902e546ff044f579ff1c953ff5fce97b636fe9e3943996b2177710c6ef076f73"},
> +    {file = "librt-0.11.0-cp311-cp311-win_arm64.whl", hash =
> "sha256:65ac3bc20f78aa0ee5ae84baa68917f89fef4af63e941084dd019a0d0e749f0c"},
> +    {file = "librt-0.11.0-cp312-cp312-macosx_10_13_x86_64.whl", hash =
> "sha256:b87504f1690a23b9a2cca841191a04f83895d4fc2dd04df91d82b1a04ca2ad46"},
> +    {file = "librt-0.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash =
> "sha256:40071fc5fe0ce8daa6de616702314a01e1250711682b0523d6ab8d4525910cb3"},
> +    {file =
> "librt-0.11.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl",
> hash =
> "sha256:137e79445c896a0ea7b265f52d23954e05b64222ee1af69e2cb34219067cbb67"},
> +    {file =
> "librt-0.11.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl",
> hash =
> "sha256:cca6644054e78746d8d4ef238681f9c34ff8b584fe6b988ecebb8db3b15e622a"},
> +    {file =
> "librt-0.11.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl",
> hash =
> "sha256:d5b0eea49f5562861ee8d757a32ef7d559c1d35be2aaaa1ec28941d74c9ffc8a"},
> +    {file =
> "librt-0.11.0-cp312-cp312-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl",
> hash =
> "sha256:0d1029d7e1ae1a7e647ed6fb5df8c4ce2dffefb7a9f5fd1376a4554d96dac09f"},
> +    {file = "librt-0.11.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash =
> "sha256:bc3ce6b33c5828d9e80592011a5c584cb2ce86edbc4088405f70da47dc1d1b3b"},
> +    {file = "librt-0.11.0-cp312-cp312-musllinux_1_2_i686.whl", hash =
> "sha256:936c5995f3514a42111f20099397d8177c79b4d7e70961e396c6f5a0a3566766"},
> +    {file = "librt-0.11.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash =
> "sha256:9bc0ca6ad9381cbe8e4aa6e5726e4c80c78115a6e9723c599ed1d73e092bc49d"},
> +    {file = "librt-0.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash =
> "sha256:070aa8c26c0a74774317a72df8851facc7f0f012a5b406557ac56992d92e1ec8"},
> +    {file = "librt-0.11.0-cp312-cp312-win32.whl", hash =
> "sha256:6bf14feb84b05ae945277395451998c89c54d0def4070eb5c08de544930b245a"},
> +    {file = "librt-0.11.0-cp312-cp312-win_amd64.whl", hash =
> "sha256:75672f0bc524ede266287d532d7923dbce94c7514ad07627bac3d0c6d92cc4d9"},
> +    {file = "librt-0.11.0-cp312-cp312-win_arm64.whl", hash =
> "sha256:2f10cf143e4a9bb0f4f5af568a00df94a2d69ef41c2579584454bb0fe5cc642c"},
> +    {file = "librt-0.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash =
> "sha256:78dc31f7fdfe9c9d0eb0e8f42d139db230e826415bbcabd9f0e9faaaee909894"},
> +    {file = "librt-0.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash =
> "sha256:fa475675db22290c3158e1d42326d0f5a65f04f44a0e68c3630a25b53560fb9c"},
> +    {file =
> "librt-0.11.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl",
> hash =
> "sha256:621db29691044bdeda22e789e482e1b0f3a985d90e3426c9c6d17606416205ea"},
> +    {file =
> "librt-0.11.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl",
> hash =
> "sha256:a9010e2ed5b3a9e158c5fd966b3ab7e834bb3d3aacc8f66c91dd4b57a3799230"},
> +    {file =
> "librt-0.11.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl",
> hash =
> "sha256:7c39513d8b7477a2e1ed8c43fc21c524e8d5a0f8d4e8b7b074dbdbe7820a08e2"},
> +    {file =
> "librt-0.11.0-cp313-cp313-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl",
> hash =
> "sha256:7aef3cf1d5af86e770ab04bfd993dfc4ae8b8c17f66fb77dd4a7d50de7bbb1a3"},
> +    {file = "librt-0.11.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash =
> "sha256:557183ddc36babe46b27dd60facbd5adb4492181a5be887587d57cda6e092f21"},
> +    {file = "librt-0.11.0-cp313-cp313-musllinux_1_2_i686.whl", hash =
> "sha256:83d3e1f72bd42f6c5c0b7daec530c3f829bd02db42c70b8ddf0c2d90a2459930"},
> +    {file = "librt-0.11.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash =
> "sha256:4ce1f21fbe589bc1afd7872dece84fb0e1144f794a288e58a10d2c54a55c43be"},
> +    {file = "librt-0.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash =
> "sha256:970b09f7044ea2b64c9da42fd3d335666518cfd1c6e8a182c95da73d0214b41e"},
> +    {file = "librt-0.11.0-cp313-cp313-win32.whl", hash =
> "sha256:78fddc31cd4d3caa897ad5d31f856b1faadc9474021ad6cb182b9018793e254e"},
> +    {file = "librt-0.11.0-cp313-cp313-win_amd64.whl", hash =
> "sha256:8ca8aa88751a775870b764e93bad5135385f563cb8dcee399abf034ea4d3cb47"},
> +    {file = "librt-0.11.0-cp313-cp313-win_arm64.whl", hash =
> "sha256:96f044bb325fd9cf1a723015638c219e9143f0dfbc0ca54c565df2b7fc748b44"},
> +    {file = "librt-0.11.0-cp314-cp314-macosx_10_13_x86_64.whl", hash =
> "sha256:4a017a95e5837dc15a8c5661d60e05daa96b90908b1aa6b7acdf443cd25c8ebd"},
> +    {file = "librt-0.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash =
> "sha256:b1ecbd9819deccc39b7542bf4d2a740d8a620694d39989e58661d3763458f8d4"},
> +    {file =
> "librt-0.11.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl",
> hash =
> "sha256:7da327dacd7be8f8ec36547373550744a3cc0e536d54665cd83f8bcd961200e8"},
> +    {file =
> "librt-0.11.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl",
> hash =
> "sha256:0dc56b1f8d06e60db362cc3fdae206681817f86ce4725d34511473487f12a34b"},
> +    {file =
> "librt-0.11.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl",
> hash =
> "sha256:05fb8fb2ab90e21c8d12ea240d744ad514da9baf381ebfa70d91d20d21713175"},
> +    {file =
> "librt-0.11.0-cp314-cp314-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl",
> hash =
> "sha256:cae74872be221df4374d10fec61f93ed1513b9546ea84f2c0bf73ab3e9bd0b03"},
> +    {file = "librt-0.11.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash =
> "sha256:32bcc918c0148eb7e3d57385125bac7e5f9e4359d05f07448b09f6f778c2f31c"},
> +    {file = "librt-0.11.0-cp314-cp314-musllinux_1_2_i686.whl", hash =
> "sha256:f9743fc99135d5f78d2454435615f6dec0473ca507c26ce9d92b10b562a280d3"},
> +    {file = "librt-0.11.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash =
> "sha256:5ba067f4aadae8fda802d91d2124c90c42195ff32d9161d3549e6d05cfe26f96"},
> +    {file = "librt-0.11.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash =
> "sha256:de3bf945454d032f9e390b85c4072e0a0570bf825421c8be0e71209fa65e1abe"},
> +    {file = "librt-0.11.0-cp314-cp314-win32.whl", hash =
> "sha256:d2277a05f6dcb9fd13db9566aac4fabd68c3ea1ea46ee5567d4eef8efa495a2f"},
> +    {file = "librt-0.11.0-cp314-cp314-win_amd64.whl", hash =
> "sha256:ab73e8db5e3f564d812c1f5c3a175930a5f9bc96ccb5e3b22a34d7858b401cf7"},
> +    {file = "librt-0.11.0-cp314-cp314-win_arm64.whl", hash =
> "sha256:aea3caa317752e3a466fa8af45d91ee0ea8c7fdd96e42b0a8dd9b76a7931eba1"},
> +    {file = "librt-0.11.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash =
> "sha256:d1b36540d7aaf9b9101b3a6f376c8d8e9f7a9aec93ed05918f2c69d493ffef72"},
> +    {file = "librt-0.11.0-cp314-cp314t-macosx_11_0_arm64.whl", hash =
> "sha256:efbb343ab2ce3540f4ecbe6315d677ed70f37cd9a72b1e58066c918ca83acbaa"},
> +    {file =
> "librt-0.11.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl",
> hash =
> "sha256:aa0dd688aab3f7914d3e6e5e3554978e0383312fb8e771d84be008a35b9ee548"},
> +    {file =
> "librt-0.11.0-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl",
> hash =
> "sha256:f5fb36b8c6c63fdcbb1d526d94c0d1331610d43f4118cc1beb4efef4f3faacb2"},
> +    {file =
> "librt-0.11.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl",
> hash =
> "sha256:4a9a237d13addb93715b6fee74023d5ee3469b53fce527626c0e088aa585805f"},
> +    {file =
> "librt-0.11.0-cp314-cp314t-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl",
> hash =
> "sha256:5ddd17bd87b2c56ddd60e546a7984a2e64c4e8eab92fb4cf3830a48ad5469d51"},
> +    {file = "librt-0.11.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash =
> "sha256:bd43992b4473d42f12ff9e68326079f0696d9d4e6000e8f39a0238d482ba6ee2"},
> +    {file = "librt-0.11.0-cp314-cp314t-musllinux_1_2_i686.whl", hash =
> "sha256:f8e3e8056dd674e279741485e2e512d6e9a751c7455809d0114e6ebf8d781085"},
> +    {file = "librt-0.11.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash =
> "sha256:c1f708d8ae9c56cf38a903c44297243d2ec83fd82b396b977e0144a3e76217e3"},
> +    {file = "librt-0.11.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash =
> "sha256:0add982e0e7b9fc14cf4b33789d5f13f66581889b88c2f58099f6ce8f92617bd"},
> +    {file = "librt-0.11.0-cp314-cp314t-win32.whl", hash =
> "sha256:2b481d846ac894c4e8403c5fd0e87c5d11d6499e404b474602508a224ff531c8"},
> +    {file = "librt-0.11.0-cp314-cp314t-win_amd64.whl", hash =
> "sha256:28edb433edde181112a908c78907af28f964eabc15f4dd16c9d66c834302677c"},
> +    {file = "librt-0.11.0-cp314-cp314t-win_arm64.whl", hash =
> "sha256:dee008f20b542e3cd162ba338a7f9ec0f6d23d395f66fe8aeeec3c9d067ea253"},
> +    {file = "librt-0.11.0-cp39-cp39-macosx_10_9_x86_64.whl", hash =
> "sha256:6bd72d903911d995ab666dbd1871f8b1e80925a699af8063fbf50053329fb05f"},
> +    {file = "librt-0.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash =
> "sha256:0ef69ac715f3cd8e5cd252cb2aebfa72c015492aacc339d5d7bf8fef3c62c677"},
> +    {file =
> "librt-0.11.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl",
> hash =
> "sha256:624a40c4a4ad7773315c287276cd024509b2c66ff5904f504bfc08d2c70293ab"},
> +    {file =
> "librt-0.11.0-cp39-cp39-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl",
> hash =
> "sha256:41dc19fe150b69716c8ece4f76773a9e8813fe3e35e032a58b4d46423fb8d7c0"},
> +    {file =
> "librt-0.11.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl",
> hash =
> "sha256:4e8bd98ea9c47ae90b319a087ab28dac493f1ffbc1ecd1f28fcdbf3b7e1108d1"},
> +    {file =
> "librt-0.11.0-cp39-cp39-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl",
> hash =
> "sha256:84308fc49423ce6475d1c5d1985cd69a8ca9f0325fc7d5f81bb690a3f3625d4e"},
> +    {file = "librt-0.11.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash =
> "sha256:ff0fbaf5f44a21beeb0110f2ab64f45135a9536a834b79c0d1ef018f2786bbfa"},
> +    {file = "librt-0.11.0-cp39-cp39-musllinux_1_2_i686.whl", hash =
> "sha256:9c028a9442a18e266955d364ce42259136e79a7ba14d773e0d778d5f70cd56f1"},
> +    {file = "librt-0.11.0-cp39-cp39-musllinux_1_2_riscv64.whl", hash =
> "sha256:9f1692105a02bcf853f355032a5fdc5494358ef83d8fd22d16de375c85cec3f5"},
> +    {file = "librt-0.11.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash =
> "sha256:7a80a71e1fda83cc752a9141e87aae7fef279538597564d670e9ce513f286192"},
> +    {file = "librt-0.11.0-cp39-cp39-win32.whl", hash =
> "sha256:140695816ddf3c86eb972981a26f35efd871c44b0c3aed44c8cd01749386617f"},
> +    {file = "librt-0.11.0-cp39-cp39-win_amd64.whl", hash =
> "sha256:92f7ff819c197fc30473190a12c2856f325ac90aabfccbeb2072d28cc2e234e3"},
> +    {file = "librt-0.11.0.tar.gz", hash =
> "sha256:075dc3ef4458a278e0195cbf6ac9d38808d9b906c5a6c7f7f79c3888276a3fb1"},
> +]
> +
>  [[package]]
>  name = "mypy"
> -version = "1.13.0"
> +version = "2.1.0"
>  description = "Optional static typing for Python"
>  optional = false
> -python-versions = ">=3.8"
> +python-versions = ">=3.10"
> +groups = ["dev"]
>  files = [
> -    {file = "mypy-1.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash =
> "sha256:6607e0f1dd1fb7f0aca14d936d13fd19eba5e17e1cd2a14f808fa5f8f6d8f60a"},
> -    {file = "mypy-1.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash =
> "sha256:8a21be69bd26fa81b1f80a61ee7ab05b076c674d9b18fb56239d72e21d9f4c80"},
> -    {file =
> "mypy-1.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl",
> hash =
> "sha256:7b2353a44d2179846a096e25691d54d59904559f4232519d420d64da6828a3a7"},
> -    {file = "mypy-1.13.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash =
> "sha256:0730d1c6a2739d4511dc4253f8274cdd140c55c32dfb0a4cf8b7a43f40abfa6f"},
> -    {file = "mypy-1.13.0-cp310-cp310-win_amd64.whl", hash =
> "sha256:c5fc54dbb712ff5e5a0fca797e6e0aa25726c7e72c6a5850cfd2adbc1eb0a372"},
> -    {file = "mypy-1.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash =
> "sha256:581665e6f3a8a9078f28d5502f4c334c0c8d802ef55ea0e7276a6e409bc0d82d"},
> -    {file = "mypy-1.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash =
> "sha256:3ddb5b9bf82e05cc9a627e84707b528e5c7caaa1c55c69e175abb15a761cec2d"},
> -    {file =
> "mypy-1.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl",
> hash =
> "sha256:20c7ee0bc0d5a9595c46f38beb04201f2620065a93755704e141fcac9f59db2b"},
> -    {file = "mypy-1.13.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash =
> "sha256:3790ded76f0b34bc9c8ba4def8f919dd6a46db0f5a6610fb994fe8efdd447f73"},
> -    {file = "mypy-1.13.0-cp311-cp311-win_amd64.whl", hash =
> "sha256:51f869f4b6b538229c1d1bcc1dd7d119817206e2bc54e8e374b3dfa202defcca"},
> -    {file = "mypy-1.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash =
> "sha256:5c7051a3461ae84dfb5dd15eff5094640c61c5f22257c8b766794e6dd85e72d5"},
> -    {file = "mypy-1.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash =
> "sha256:39bb21c69a5d6342f4ce526e4584bc5c197fd20a60d14a8624d8743fffb9472e"},
> -    {file =
> "mypy-1.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl",
> hash =
> "sha256:164f28cb9d6367439031f4c81e84d3ccaa1e19232d9d05d37cb0bd880d3f93c2"},
> -    {file = "mypy-1.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash =
> "sha256:a4c1bfcdbce96ff5d96fc9b08e3831acb30dc44ab02671eca5953eadad07d6d0"},
> -    {file = "mypy-1.13.0-cp312-cp312-win_amd64.whl", hash =
> "sha256:a0affb3a79a256b4183ba09811e3577c5163ed06685e4d4b46429a271ba174d2"},
> -    {file = "mypy-1.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash =
> "sha256:a7b44178c9760ce1a43f544e595d35ed61ac2c3de306599fa59b38a6048e1aa7"},
> -    {file = "mypy-1.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash =
> "sha256:5d5092efb8516d08440e36626f0153b5006d4088c1d663d88bf79625af3d1d62"},
> -    {file =
> "mypy-1.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl",
> hash =
> "sha256:de2904956dac40ced10931ac967ae63c5089bd498542194b436eb097a9f77bc8"},
> -    {file = "mypy-1.13.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash =
> "sha256:7bfd8836970d33c2105562650656b6846149374dc8ed77d98424b40b09340ba7"},
> -    {file = "mypy-1.13.0-cp313-cp313-win_amd64.whl", hash =
> "sha256:9f73dba9ec77acb86457a8fc04b5239822df0c14a082564737833d2963677dbc"},
> -    {file = "mypy-1.13.0-cp38-cp38-macosx_10_9_x86_64.whl", hash =
> "sha256:100fac22ce82925f676a734af0db922ecfea991e1d7ec0ceb1e115ebe501301a"},
> -    {file = "mypy-1.13.0-cp38-cp38-macosx_11_0_arm64.whl", hash =
> "sha256:7bcb0bb7f42a978bb323a7c88f1081d1b5dee77ca86f4100735a6f541299d8fb"},
> -    {file =
> "mypy-1.13.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl",
> hash =
> "sha256:bde31fc887c213e223bbfc34328070996061b0833b0a4cfec53745ed61f3519b"},
> -    {file = "mypy-1.13.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash =
> "sha256:07de989f89786f62b937851295ed62e51774722e5444a27cecca993fc3f9cd74"},
> -    {file = "mypy-1.13.0-cp38-cp38-win_amd64.whl", hash =
> "sha256:4bde84334fbe19bad704b3f5b78c4abd35ff1026f8ba72b29de70dda0916beb6"},
> -    {file = "mypy-1.13.0-cp39-cp39-macosx_10_9_x86_64.whl", hash =
> "sha256:0246bcb1b5de7f08f2826451abd947bf656945209b140d16ed317f65a17dc7dc"},
> -    {file = "mypy-1.13.0-cp39-cp39-macosx_11_0_arm64.whl", hash =
> "sha256:7f5b7deae912cf8b77e990b9280f170381fdfbddf61b4ef80927edd813163732"},
> -    {file =
> "mypy-1.13.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl",
> hash =
> "sha256:7029881ec6ffb8bc233a4fa364736789582c738217b133f1b55967115288a2bc"},
> -    {file = "mypy-1.13.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash =
> "sha256:3e38b980e5681f28f033f3be86b099a247b13c491f14bb8b1e1e134d23bb599d"},
> -    {file = "mypy-1.13.0-cp39-cp39-win_amd64.whl", hash =
> "sha256:a6789be98a2017c912ae6ccb77ea553bbaf13d27605d2ca20a76dfbced631b24"},
> -    {file = "mypy-1.13.0-py3-none-any.whl", hash =
> "sha256:9c250883f9fd81d212e0952c92dbfcc96fc237f4b7c92f56ac81fd48460b3e5a"},
> -    {file = "mypy-1.13.0.tar.gz", hash =
> "sha256:0291a61b6fbf3e6673e3405cfcc0e7650bebc7939659fdca2702958038bd835e"},
> +    {file = "mypy-2.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash =
> "sha256:11a6beb180257a805961aea9ec591bbd0bd17f1e18d35b8456d57aee5bedfedc"},
> +    {file = "mypy-2.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash =
> "sha256:8ef78c1d306bbf9a8a12f526c44902c9c28dffd6c52c52bf6a72641ce18d3849"},
> +    {file =
> "mypy-2.1.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl",
> hash =
> "sha256:c209a90853081ff01d01ee895cafe10f7db1474e0d95beaeef0f6c1db9119bbd"},
> +    {file =
> "mypy-2.1.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl",
> hash =
> "sha256:47cebf61abde7c088a4e27718a8b13a81655686b2e9c251f5c0915a802248166"},
> +    {file = "mypy-2.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash =
> "sha256:d57a90ae5e872138a425ec328edbc9b235d1934c4377881a33ec05b341acc9a8"},
> +    {file = "mypy-2.1.0-cp310-cp310-win_amd64.whl", hash =
> "sha256:aea7f7a8a55b459c34275fc468ada6ca7c173a5e43a68f5dbe588a563d8a06b8"},
> +    {file = "mypy-2.1.0-cp310-cp310-win_arm64.whl", hash =
> "sha256:c989640253f0d76843e9c6c1bbf4bd48c5e85ada61bde4beb37cb3eca035685e"},
> +    {file = "mypy-2.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash =
> "sha256:a683016b16fe2f572dc04c72be7ee0504ac1605a265d0200f5cea695fb788f41"},
> +    {file = "mypy-2.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash =
> "sha256:1a293c534adb55271fef24a26da04b855540a8c13cc07bc5917b9fd2c394f2ca"},
> +    {file =
> "mypy-2.1.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl",
> hash =
> "sha256:7406f4d048e71e576f5356d317e5b0a9e666dfd966bd99f9d14ca06e1a341538"},
> +    {file =
> "mypy-2.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl",
> hash =
> "sha256:e0210d626fc8b31ccc90233754c7bc90e1f43205e85d96387f7db1285b55c398"},
> +    {file = "mypy-2.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash =
> "sha256:3712c20deed54e814eaaa825603bada8ea1c390670a397c95b98405347acc563"},
> +    {file = "mypy-2.1.0-cp311-cp311-win_amd64.whl", hash =
> "sha256:fcaa0e479066e31f7cceb6a3bea39cb22b2ff51a6b2f24f193d19179ba17c389"},
> +    {file = "mypy-2.1.0-cp311-cp311-win_arm64.whl", hash =
> "sha256:0b1a5260c95aa443083f9ed3592662941951bca3d4ca224a5dc517c38b7cf666"},
> +    {file = "mypy-2.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash =
> "sha256:244358bf1c0da7722230bce60683d52e8e9fd030554926f15b747a84efb5b3af"},
> +    {file = "mypy-2.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash =
> "sha256:4ec7c57657493c7a75534df2751c8ae2cda383c16ecc55d2106c54476b1b16f6"},
> +    {file =
> "mypy-2.1.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl",
> hash =
> "sha256:d8161b6ff4392410023224f0969d17db93e1e154bc3e4ba62598e720723ae211"},
> +    {file =
> "mypy-2.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl",
> hash =
> "sha256:bf03e12003084a67395184d3eb8cbd6a489dc3655b5664b28c210a9e2403ab0b"},
> +    {file = "mypy-2.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash =
> "sha256:20509760fd791c51579d573153407d226385ec1f8bcce55d730b354f3336bc22"},
> +    {file = "mypy-2.1.0-cp312-cp312-win_amd64.whl", hash =
> "sha256:6753d0c1fdd6b1a23b9e4f283ce80b2153b724adcb2653b20b85a8a28ac6436b"},
> +    {file = "mypy-2.1.0-cp312-cp312-win_arm64.whl", hash =
> "sha256:98ebb6589bb3b6d0c6f0c459d53ca55b8091fbc13d277c4041c885392e8195e8"},
> +    {file = "mypy-2.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash =
> "sha256:35aac3bb114e03888f535d5eb51b8bafbb3266586b599da1940f9b1be3ec5bd5"},
> +    {file = "mypy-2.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash =
> "sha256:8de55a8c861f2a49331f807be98d90caeceeef520bde13d43a160207f8af613e"},
> +    {file =
> "mypy-2.1.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl",
> hash =
> "sha256:5fdf2941a07434af755837d9880f7d7d25f1dacb1af9dcd4b9b66f2220a3024e"},
> +    {file =
> "mypy-2.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl",
> hash =
> "sha256:e195b817c13f02352a9c124301f9f30f078405444679b6753c1b96b6eed37285"},
> +    {file = "mypy-2.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash =
> "sha256:5431d42af987ebd92ba2f71d45c85ed41d8e6ca9f5fd209a69f68f707d2469e5"},
> +    {file = "mypy-2.1.0-cp313-cp313-win_amd64.whl", hash =
> "sha256:767fe8c66dc3e01e19e1737d4c38ebefead16125e1b8e58ad421903b376f5c65"},
> +    {file = "mypy-2.1.0-cp313-cp313-win_arm64.whl", hash =
> "sha256:ecfe70d43775ab99562ab128ce49854a362044c9f894961f68f898c23cb7429d"},
> +    {file = "mypy-2.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash =
> "sha256:7354c5a7f69d9345c3d6e69921d57088eea3ddeeb6b20d34c1b3855b02c36ec2"},
> +    {file = "mypy-2.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash =
> "sha256:49890d4f76ac9e06ec117f9e09f3174da70a620a0c300953d8595c926e80947f"},
> +    {file =
> "mypy-2.1.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl",
> hash =
> "sha256:761be68e023ef5d94678772396a8af1220030f80837a3afd8d0aef3b419666f4"},
> +    {file =
> "mypy-2.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl",
> hash =
> "sha256:c90345fc182dc363b891350457ec69c35140858538f38b4540845afcc32b1aef"},
> +    {file = "mypy-2.1.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash =
> "sha256:b84802e7b5a6daf1f5e15bc9fcd7ddae77be13981ffab037f1c67bb84d67d135"},
> +    {file = "mypy-2.1.0-cp314-cp314-win_amd64.whl", hash =
> "sha256:022c771234936ceac541ebaf836fe9e2abeb3f5e09aff21588fe543ff006fe21"},
> +    {file = "mypy-2.1.0-cp314-cp314-win_arm64.whl", hash =
> "sha256:498207db725cec88829a6a5c2fc771205fd043719ef98bc49aba8fb9fc4e6d57"},
> +    {file = "mypy-2.1.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash =
> "sha256:7d5e5cad0efeba72b93cd17490cc0d69c5ac9ca132994fe3fb0314808aeeb83e"},
> +    {file = "mypy-2.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash =
> "sha256:ff715050c127d724fd260a2e666e7747fdd83511c0c47d449d98238970aef780"},
> +    {file =
> "mypy-2.1.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl",
> hash =
> "sha256:82208da9e09414d520e912d3e462d454854bed0810b71540bb016dcbca7308fd"},
> +    {file =
> "mypy-2.1.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl",
> hash =
> "sha256:e79ebc1b904b84f0310dff7469655a9c36c7a68bddb37bdd42b67a332df61d08"},
> +    {file = "mypy-2.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash =
> "sha256:e583edc957cfb0deb142079162ae826f58449b116c1d442f2d91c69d9fced081"},
> +    {file = "mypy-2.1.0-cp314-cp314t-win_amd64.whl", hash =
> "sha256:b33b6cd332695bba180d55e717a79d3038e479a2c49cc5eb3d53603409b9a5d7"},
> +    {file = "mypy-2.1.0-cp314-cp314t-win_arm64.whl", hash =
> "sha256:4f910fe825376a7b66ef7ca8c98e5a149e8cd64c19ae71d84047a74ee060d4e6"},
> +    {file = "mypy-2.1.0-py3-none-any.whl", hash =
> "sha256:a663814603a5c563fb87a4f96fb473eeb30d1f5a4885afcf44f9db000a366289"},
> +    {file = "mypy-2.1.0.tar.gz", hash =
> "sha256:81e76ad12c2d804512e9b13240d1588316531bfba07558286078bfbce9613633"},
>  ]
>
>  [package.dependencies]
> -mypy-extensions = ">=1.0.0"
> +ast-serialize = ">=0.3.0,<1.0.0"
> +librt = {version = ">=0.11.0", markers = "platform_python_implementation
> != \"PyPy\""}
> +mypy_extensions = ">=1.0.0"
> +pathspec = ">=1.0.0"
>  tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
> -typing-extensions = ">=4.6.0"
> +typing_extensions = [
> +    {version = ">=4.6.0", markers = "python_version < \"3.15\""},
> +    {version = ">=4.14.0", markers = "python_version >= \"3.15\""},
> +]
>
>  [package.extras]
>  dmypy = ["psutil (>=4.0)"]
> @@ -307,6 +480,7 @@ version = "1.0.0"
>  description = "Type system extensions for programs checked with the mypy
> type checker."
>  optional = false
>  python-versions = ">=3.5"
> +groups = ["dev"]
>  files = [
>      {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash =
> "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"},
>      {file = "mypy_extensions-1.0.0.tar.gz", hash =
> "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"},
> @@ -318,6 +492,7 @@ version = "3.5.0"
>  description = "SSH2 protocol library"
>  optional = false
>  python-versions = ">=3.6"
> +groups = ["main"]
>  files = [
>      {file = "paramiko-3.5.0-py3-none-any.whl", hash =
> "sha256:1fedf06b085359051cd7d0d270cebe19e755a8a921cc2ddbfa647fb0cd7d68f9"},
>      {file = "paramiko-3.5.0.tar.gz", hash =
> "sha256:ad11e540da4f55cedda52931f1a3f812a8238a7af7f62a60de538cd80bb28124"},
> @@ -329,20 +504,39 @@ cryptography = ">=3.3"
>  pynacl = ">=1.5"
>
>  [package.extras]
> -all = ["gssapi (>=1.4.1)", "invoke (>=2.0)", "pyasn1 (>=0.1.7)", "pywin32
> (>=2.1.8)"]
> -gssapi = ["gssapi (>=1.4.1)", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1.8)"]
> +all = ["gssapi (>=1.4.1) ; platform_system != \"Windows\"", "invoke
> (>=2.0)", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1.8) ; platform_system ==
> \"Windows\""]
> +gssapi = ["gssapi (>=1.4.1) ; platform_system != \"Windows\"", "pyasn1
> (>=0.1.7)", "pywin32 (>=2.1.8) ; platform_system == \"Windows\""]
>  invoke = ["invoke (>=2.0)"]
>
> +[[package]]
> +name = "pathspec"
> +version = "1.1.1"
> +description = "Utility library for gitignore style pattern matching of
> file paths."
> +optional = false
> +python-versions = ">=3.9"
> +groups = ["dev"]
> +files = [
> +    {file = "pathspec-1.1.1-py3-none-any.whl", hash =
> "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189"},
> +    {file = "pathspec-1.1.1.tar.gz", hash =
> "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a"},
> +]
> +
> +[package.extras]
> +hyperscan = ["hyperscan (>=0.7)"]
> +optional = ["typing-extensions (>=4)"]
> +re2 = ["google-re2 (>=1.1)"]
> +
>  [[package]]
>  name = "pycparser"
>  version = "2.22"
>  description = "C parser in Python"
>  optional = false
>  python-versions = ">=3.8"
> +groups = ["main", "dev"]
>  files = [
>      {file = "pycparser-2.22-py3-none-any.whl", hash =
> "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"},
>      {file = "pycparser-2.22.tar.gz", hash =
> "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"},
>  ]
> +markers = {dev = "platform_python_implementation != \"PyPy\""}
>
>  [[package]]
>  name = "pydantic"
> @@ -350,6 +544,7 @@ version = "2.10.3"
>  description = "Data validation using Python type hints"
>  optional = false
>  python-versions = ">=3.8"
> +groups = ["main"]
>  files = [
>      {file = "pydantic-2.10.3-py3-none-any.whl", hash =
> "sha256:be04d85bbc7b65651c5f8e6b9976ed9c6f41782a55524cef079a34a0bb82144d"},
>      {file = "pydantic-2.10.3.tar.gz", hash =
> "sha256:cb5ac360ce894ceacd69c403187900a02c4b20b693a9dd1d643e1effab9eadf9"},
> @@ -362,7 +557,7 @@ typing-extensions = ">=4.12.2"
>
>  [package.extras]
>  email = ["email-validator (>=2.0.0)"]
> -timezone = ["tzdata"]
> +timezone = ["tzdata ; python_version >= \"3.9\" and platform_system ==
> \"Windows\""]
>
>  [[package]]
>  name = "pydantic-core"
> @@ -370,6 +565,7 @@ version = "2.27.1"
>  description = "Core functionality for Pydantic validation and
> serialization"
>  optional = false
>  python-versions = ">=3.8"
> +groups = ["main"]
>  files = [
>      {file = "pydantic_core-2.27.1-cp310-cp310-macosx_10_12_x86_64.whl",
> hash =
> "sha256:71a5e35c75c021aaf400ac048dacc855f000bdfed91614b4a726f7432f1f3d6a"},
>      {file = "pydantic_core-2.27.1-cp310-cp310-macosx_11_0_arm64.whl",
> hash =
> "sha256:f82d068a2d6ecfc6e054726080af69a6764a10015467d7d7b9f66d6ed5afa23b"},
> @@ -482,6 +678,7 @@ version = "1.5.0"
>  description = "Python binding to the Networking and Cryptography (NaCl)
> library"
>  optional = false
>  python-versions = ">=3.6"
> +groups = ["main"]
>  files = [
>      {file = "PyNaCl-1.5.0-cp36-abi3-macosx_10_10_universal2.whl", hash =
> "sha256:401002a4aaa07c9414132aaed7f6836ff98f59277a234704ff66878c2ee4a0d1"},
>      {file =
> "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl",
> hash =
> "sha256:52cb72a79269189d4e0dc537556f4740f7f0a9ec41c1322598799b0bdad4ef92"},
> @@ -508,6 +705,7 @@ version = "6.0.2"
>  description = "YAML parser and emitter for Python"
>  optional = false
>  python-versions = ">=3.8"
> +groups = ["main"]
>  files = [
>      {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash =
> "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"},
>      {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash =
> "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"},
> @@ -566,29 +764,30 @@ files = [
>
>  [[package]]
>  name = "ruff"
> -version = "0.8.2"
> +version = "0.15.16"
>  description = "An extremely fast Python linter and code formatter,
> written in Rust."
>  optional = false
>  python-versions = ">=3.7"
> +groups = ["dev"]
>  files = [
> -    {file = "ruff-0.8.2-py3-none-linux_armv6l.whl", hash =
> "sha256:c49ab4da37e7c457105aadfd2725e24305ff9bc908487a9bf8d548c6dad8bb3d"},
> -    {file = "ruff-0.8.2-py3-none-macosx_10_12_x86_64.whl", hash =
> "sha256:ec016beb69ac16be416c435828be702ee694c0d722505f9c1f35e1b9c0cc1bf5"},
> -    {file = "ruff-0.8.2-py3-none-macosx_11_0_arm64.whl", hash =
> "sha256:f05cdf8d050b30e2ba55c9b09330b51f9f97d36d4673213679b965d25a785f3c"},
> -    {file =
> "ruff-0.8.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
> hash =
> "sha256:60f578c11feb1d3d257b2fb043ddb47501ab4816e7e221fbb0077f0d5d4e7b6f"},
> -    {file =
> "ruff-0.8.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash
> =
> "sha256:cbd5cf9b0ae8f30eebc7b360171bd50f59ab29d39f06a670b3e4501a36ba5897"},
> -    {file =
> "ruff-0.8.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash =
> "sha256:b402ddee3d777683de60ff76da801fa7e5e8a71038f57ee53e903afbcefdaa58"},
> -    {file =
> "ruff-0.8.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash =
> "sha256:705832cd7d85605cb7858d8a13d75993c8f3ef1397b0831289109e953d833d29"},
> -    {file =
> "ruff-0.8.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl",
> hash =
> "sha256:32096b41aaf7a5cc095fa45b4167b890e4c8d3fd217603f3634c92a541de7248"},
> -    {file =
> "ruff-0.8.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash =
> "sha256:e769083da9439508833cfc7c23e351e1809e67f47c50248250ce1ac52c21fb93"},
> -    {file =
> "ruff-0.8.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash
> =
> "sha256:5fe716592ae8a376c2673fdfc1f5c0c193a6d0411f90a496863c99cd9e2ae25d"},
> -    {file = "ruff-0.8.2-py3-none-musllinux_1_2_aarch64.whl", hash =
> "sha256:81c148825277e737493242b44c5388a300584d73d5774defa9245aaef55448b0"},
> -    {file = "ruff-0.8.2-py3-none-musllinux_1_2_armv7l.whl", hash =
> "sha256:d261d7850c8367704874847d95febc698a950bf061c9475d4a8b7689adc4f7fa"},
> -    {file = "ruff-0.8.2-py3-none-musllinux_1_2_i686.whl", hash =
> "sha256:1ca4e3a87496dc07d2427b7dd7ffa88a1e597c28dad65ae6433ecb9f2e4f022f"},
> -    {file = "ruff-0.8.2-py3-none-musllinux_1_2_x86_64.whl", hash =
> "sha256:729850feed82ef2440aa27946ab39c18cb4a8889c1128a6d589ffa028ddcfc22"},
> -    {file = "ruff-0.8.2-py3-none-win32.whl", hash =
> "sha256:ac42caaa0411d6a7d9594363294416e0e48fc1279e1b0e948391695db2b3d5b1"},
> -    {file = "ruff-0.8.2-py3-none-win_amd64.whl", hash =
> "sha256:2aae99ec70abf43372612a838d97bfe77d45146254568d94926e8ed5bbb409ea"},
> -    {file = "ruff-0.8.2-py3-none-win_arm64.whl", hash =
> "sha256:fb88e2a506b70cfbc2de6fae6681c4f944f7dd5f2fe87233a7233d888bad73e8"},
> -    {file = "ruff-0.8.2.tar.gz", hash =
> "sha256:b84f4f414dda8ac7f75075c1fa0b905ac0ff25361f42e6d5da681a465e0f78e5"},
> +    {file = "ruff-0.15.16-py3-none-linux_armv6l.whl", hash =
> "sha256:6ac3c0b3969cc6cf6b158c4e2f8f682acb58e7d700d8a44b65ecdc72d66ab0b2"},
> +    {file = "ruff-0.15.16-py3-none-macosx_10_12_x86_64.whl", hash =
> "sha256:197c207ed75ffba54a0dec23db4aa939a27a3053073e085e0042433cbdc58e4a"},
> +    {file = "ruff-0.15.16-py3-none-macosx_11_0_arm64.whl", hash =
> "sha256:3a39fec45ab316cc23e7558f23fea4a70403ddb5648ea9a4a3854a16973d0071"},
> +    {file =
> "ruff-0.15.16-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
> hash =
> "sha256:ba93191d79003116b95128c9d306e045200fdbd0bccb782b110f3cd1d4abc5cf"},
> +    {file =
> "ruff-0.15.16-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl",
> hash =
> "sha256:c6ee4b90520630120ef032aa5cc10db483852dff950e78b1d717e2993a61ac8d"},
> +    {file =
> "ruff-0.15.16-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash =
> "sha256:4e4215bc938bc3c8215c1472c1aa437e310fee20cd427335fec9d7e609563628"},
> +    {file =
> "ruff-0.15.16-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl",
> hash =
> "sha256:7c8d26be963b090f10e29abc8b3e74a2a321f6fa34e02424e30b5af89350ecbb"},
> +    {file =
> "ruff-0.15.16-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash
> =
> "sha256:f198cf4123602a2280ed46c307bcbafe41758d6fee5b456b6b6058ca1514b3b4"},
> +    {file =
> "ruff-0.15.16-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
> hash =
> "sha256:bb27515fa6240fb586ae82b901a59e67d24acff86f2190b433dc542fe0435aeb"},
> +    {file = "ruff-0.15.16-py3-none-manylinux_2_31_riscv64.whl", hash =
> "sha256:a267c46ba1593fc26b8eecbea050b39d40c0b6bb7781ee11c90a02cd10032951"},
> +    {file = "ruff-0.15.16-py3-none-musllinux_1_2_aarch64.whl", hash =
> "sha256:528c68f39a91498a8d50e91ff5985df3d105782bab49cc378e73ac26bff083e8"},
> +    {file = "ruff-0.15.16-py3-none-musllinux_1_2_armv7l.whl", hash =
> "sha256:7ed55c58950df60589a9a7a5d2f8fa5f54ebd287163be805adfe6ee95a9de123"},
> +    {file = "ruff-0.15.16-py3-none-musllinux_1_2_i686.whl", hash =
> "sha256:d482feaf51512b50f9790ceb417a56a61dd1e9d9bf967662b9ed27c01b34f53a"},
> +    {file = "ruff-0.15.16-py3-none-musllinux_1_2_x86_64.whl", hash =
> "sha256:1e15bc8c94513dae2a40cc9ef07c94fdd4ecc9e29dabebeebe170f952322c9e3"},
> +    {file = "ruff-0.15.16-py3-none-win32.whl", hash =
> "sha256:580378f7bd4aa25f72e74aa54948a9622f142b1e509521dd10902e886681cc1e"},
> +    {file = "ruff-0.15.16-py3-none-win_amd64.whl", hash =
> "sha256:408256017284eddf98fff77b29aa4fb30f586042d535b2d9befc6512f400aaec"},
> +    {file = "ruff-0.15.16-py3-none-win_arm64.whl", hash =
> "sha256:8cd61783afb39638a7133ef0d2dfb1e91277593962f81b5a8423eb0b888a6121"},
> +    {file = "ruff-0.15.16.tar.gz", hash =
> "sha256:d05e78d38c78caf020b03789e25106c93017db5a0cb6e2819885018c61343b78"},
>  ]
>
>  [[package]]
> @@ -597,6 +796,7 @@ version = "2.6.1"
>  description = "Scapy: interactive packet manipulation tool"
>  optional = false
>  python-versions = "<4,>=3.7"
> +groups = ["main"]
>  files = [
>      {file = "scapy-2.6.1-py3-none-any.whl", hash =
> "sha256:88a998572049b511a1f3e44f4aa7c62dd39c6ea2aa1bb58434f503956641789d"},
>      {file = "scapy-2.6.1.tar.gz", hash =
> "sha256:7600d7e2383c853e5c3a6e05d37e17643beebf2b3e10d7914dffcc3bc3c6e6c5"},
> @@ -613,6 +813,7 @@ version = "0.10.2"
>  description = "Python Library for Tom's Obvious, Minimal Language"
>  optional = false
>  python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
> +groups = ["dev"]
>  files = [
>      {file = "toml-0.10.2-py2.py3-none-any.whl", hash =
> "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
>      {file = "toml-0.10.2.tar.gz", hash =
> "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
> @@ -624,6 +825,8 @@ version = "2.2.1"
>  description = "A lil' TOML parser"
>  optional = false
>  python-versions = ">=3.8"
> +groups = ["dev"]
> +markers = "python_version == \"3.10\""
>  files = [
>      {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash =
> "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"},
>      {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash =
> "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"},
> @@ -665,6 +868,7 @@ version = "2.0.0.10"
>  description = "Typing stubs for invoke"
>  optional = false
>  python-versions = ">=3.7"
> +groups = ["dev"]
>  files = [
>      {file = "types-invoke-2.0.0.10.tar.gz", hash =
> "sha256:a54d7ecdc19e0c22cd2786ef2e64c2631715c78eba8a1bf40b511d0608f33a88"},
>      {file = "types_invoke-2.0.0.10-py3-none-any.whl", hash =
> "sha256:2404e4279601fa96e14ef68321fd10a660a828677aabdcaeef6a5189778084ef"},
> @@ -672,13 +876,14 @@ files = [
>
>  [[package]]
>  name = "types-paramiko"
> -version = "3.5.0.20240928"
> +version = "4.0.0.20260518"
>  description = "Typing stubs for paramiko"
>  optional = false
> -python-versions = ">=3.8"
> +python-versions = ">=3.10"
> +groups = ["dev"]
>  files = [
> -    {file = "types-paramiko-3.5.0.20240928.tar.gz", hash =
> "sha256:79dd9b2ee510b76a3b60d8ac1f3f348c45fcecf01347ca79e14db726bbfc442d"},
> -    {file = "types_paramiko-3.5.0.20240928-py3-none-any.whl", hash =
> "sha256:cda0aff4905fe8efe4b5448331a80e943d42a796bd4beb77a3eed3485bc96a85"},
> +    {file = "types_paramiko-4.0.0.20260518-py3-none-any.whl", hash =
> "sha256:0ffaf1a6eb796833a49653cba4c7be13af51c8269d75234972d6239763dda270"},
> +    {file = "types_paramiko-4.0.0.20260518.tar.gz", hash =
> "sha256:286f6830945cba63797eedf375ed87138d93198121253afe66c5d6dbcf91318d"},
>  ]
>
>  [package.dependencies]
> @@ -686,13 +891,14 @@ cryptography = ">=37.0.0"
>
>  [[package]]
>  name = "types-pyyaml"
> -version = "6.0.12.20240917"
> +version = "6.0.12.20260518"
>  description = "Typing stubs for PyYAML"
>  optional = false
> -python-versions = ">=3.8"
> +python-versions = ">=3.10"
> +groups = ["dev"]
>  files = [
> -    {file = "types-PyYAML-6.0.12.20240917.tar.gz", hash =
> "sha256:d1405a86f9576682234ef83bcb4e6fff7c9305c8b1fbad5e0bcd4f7dbdc9c587"},
> -    {file = "types_PyYAML-6.0.12.20240917-py3-none-any.whl", hash =
> "sha256:392b267f1c0fe6022952462bf5d6523f31e37f6cea49b14cee7ad634b6301570"},
> +    {file = "types_pyyaml-6.0.12.20260518-py3-none-any.whl", hash =
> "sha256:d2150f75a231c9fe9c7463bd29487d93e60bac90400287351384bc2284eba7cd"},
> +    {file = "types_pyyaml-6.0.12.20260518.tar.gz", hash =
> "sha256:d917f83fb38462550338c1297faedd860b3ec83912b96b1e3d73255f7473e466"},
>  ]
>
>  [[package]]
> @@ -701,17 +907,33 @@ version = "4.12.2"
>  description = "Backported and Experimental Type Hints for Python 3.8+"
>  optional = false
>  python-versions = ">=3.8"
> +groups = ["main", "dev"]
> +markers = "python_version < \"3.15\""
>  files = [
>      {file = "typing_extensions-4.12.2-py3-none-any.whl", hash =
> "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"},
>      {file = "typing_extensions-4.12.2.tar.gz", hash =
> "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
>  ]
>
> +[[package]]
> +name = "typing-extensions"
> +version = "4.15.0"
> +description = "Backported and Experimental Type Hints for Python 3.9+"
> +optional = false
> +python-versions = ">=3.9"
> +groups = ["main", "dev"]
> +markers = "python_version >= \"3.15\""
> +files = [
> +    {file = "typing_extensions-4.15.0-py3-none-any.whl", hash =
> "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"},
> +    {file = "typing_extensions-4.15.0.tar.gz", hash =
> "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"},
> +]
> +
>  [[package]]
>  name = "wrapt"
>  version = "1.17.2"
>  description = "Module for decorators, wrappers and monkey patching."
>  optional = false
>  python-versions = ">=3.8"
> +groups = ["main"]
>  files = [
>      {file = "wrapt-1.17.2-cp310-cp310-macosx_10_9_universal2.whl", hash =
> "sha256:3d57c572081fed831ad2d26fd430d565b76aa277ed1d30ff4d40670b1c0dd984"},
>      {file = "wrapt-1.17.2-cp310-cp310-macosx_10_9_x86_64.whl", hash =
> "sha256:b5e251054542ae57ac7f3fba5d10bfff615b6c2fb09abeb37d2f1463f841ae22"},
> @@ -795,6 +1017,6 @@ files = [
>  ]
>
>  [metadata]
> -lock-version = "2.0"
> +lock-version = "2.1"
>  python-versions = "^3.10"
> -content-hash =
> "aa6dff54827602c89ee125019291965de50d7a471ca48add889ea23aa5fd9b2f"
> +content-hash =
> "1ca3cdf5bf98c528845b5e22169322db8912fe437988635f4ebea088b2079463"
> diff --git a/dts/pyproject.toml b/dts/pyproject.toml
> index 8b061c3cee..b639af65b6 100644
> --- a/dts/pyproject.toml
> +++ b/dts/pyproject.toml
> @@ -28,12 +28,12 @@ aenum = "^3.1.15"
>  pydantic = "^2.9.2"
>
>  [tool.poetry.group.dev.dependencies]
> -mypy = "^1.13.0"
> +mypy = "^2.1.0"
>  toml = "^0.10.2"
> -ruff = "^0.8.1"
> -types-paramiko = "^3.5.0.20240928"
> +ruff = "^0.15.16"
> +types-paramiko = "^4.0.0.20260518"
>  types-invoke = "^2.0.0.10"
> -types-pyyaml = "^6.0.12.20240917"
> +types-pyyaml = "^6.0.12.20260518"
>
>  [build-system]
>  requires = ["poetry-core>=1.0.0"]
> diff --git a/dts/tests/TestSuite_cryptodev_throughput.py
> b/dts/tests/TestSuite_cryptodev_throughput.py
> index af0a5680ab..2fc0d8779a 100644
> --- a/dts/tests/TestSuite_cryptodev_throughput.py
> +++ b/dts/tests/TestSuite_cryptodev_throughput.py
> @@ -101,12 +101,12 @@ def _print_stats(self, test_vals: list[dict[str, int
> | float | str]]) -> None:
>          print(f"{'Throughput Results'.center(border_len)}\n{'=' *
> border_len}")
>          for k, v in test_vals[0].items():
>              print(f"|{k.title():<{element_len}}", end="")
> -        print(f"|\n{'='*border_len}")
> +        print(f"|\n{'=' * border_len}")
>
>          for test_val in test_vals:
>              for k, v in test_val.items():
>                  print(f"|{v:<{element_len}}", end="")
> -            print(f"|\n{'='*border_len}")
> +            print(f"|\n{'=' * border_len}")
>
>      def _verify_throughput(
>          self,
> diff --git a/dts/tests/TestSuite_port_control.py
> b/dts/tests/TestSuite_port_control.py
> index 6be47838d0..b51fdc2959 100644
> --- a/dts/tests/TestSuite_port_control.py
> +++ b/dts/tests/TestSuite_port_control.py
> @@ -44,8 +44,7 @@ def _send_packets_and_verify(self) -> None:
>          recv_pakts = [
>              p
>              for p in recv_pakts
> -            if
> -            (
> +            if (
>                  # Remove padding from the bytes.
>                  hasattr(p, "load") and
> p.load.decode("utf-8").replace("\x00", "") == payload
>              )
> diff --git a/dts/tests/TestSuite_single_core_forward_perf.py
> b/dts/tests/TestSuite_single_core_forward_perf.py
> index 1e7ab7b036..acdf8ae2f6 100644
> --- a/dts/tests/TestSuite_single_core_forward_perf.py
> +++ b/dts/tests/TestSuite_single_core_forward_perf.py
> @@ -144,7 +144,7 @@ def single_core_forward_perf(self) -> None:
>          for params in self.test_parameters:
>              verify(
>                  params["pass"] is True,
> -                f"""Packets forwarded is less than {(1
> -self.delta_tolerance)*100}%
> +                f"""Packets forwarded is less than {(1 -
> self.delta_tolerance) * 100}%
>                  of the expected baseline.
>                  Measured MPPS = {params["measured_mpps"]}
>                  Expected MPPS = {params["expected_mpps"]}""",
> --
> 2.54.0
>
>

[-- Attachment #2: Type: text/html, Size: 95456 bytes --]

^ permalink raw reply

* RE: [PATCH v1 0/5] prefix lcore role enum values
From: Morten Brørup @ 2026-06-18 18:52 UTC (permalink / raw)
  To: Thomas Monjalon, Huisong Li
  Cc: dev, andrew.rybchenko, zhanjie9, David Marchand, Bruce Richardson
In-Reply-To: <SdThFrO9S62xAryFCraiCQ@monjalon.net>

> From: Thomas Monjalon [mailto:thomas@monjalon.net]
> Sent: Thursday, 18 June 2026 18.20
> 
> 17/06/2026 13:48, Morten Brørup:
> > > From: Huisong Li [mailto:lihuisong@huawei.com]
> > > Sent: Wednesday, 17 June 2026 12.28
> > >
> > > Add the RTE_LCORE_ prefix to the lcore role enum values in
> > > rte_lcore_role_t
> > > to follow DPDK naming conventions.
> > >
> > > - ROLE_RTE      -> RTE_LCORE_ROLE_RTE
> > > - ROLE_OFF      -> RTE_LCORE_ROLE_OFF
> > > - ROLE_SERVICE  -> RTE_LCORE_ROLE_SERVICE
> > > - ROLE_NON_EAL  -> RTE_LCORE_ROLE_NON_EAL
> > >
> > > Old names are kept as macros aliasing to the new names to preserve
> > > backward compatibility.
> > >
> >
> > Series-Acked-by: Morten Brørup <mb@smartsharesystems.com>
> 
> Squashed and applied, thanks.
> 
> I have a doubt about RTE_LCORE_ROLE_RTE: could we find a better name?

Yes, it made more sense when lcores were either used by DPDK or not.
Note that it's used by both workers and the main lcore.

> 
> Also we should probably add some comments to explain each role.

+1

BTW, I thought the Non-EAL role was for registered control plane threads. But then I looked into Grout.
Grout uses the Non-EAL role for its dataplane threads. And the RTE role for its main thread, which is handling the control plane (which I suppose is not unusual).

Lcore roles and CPU core isolation is very flexible in DPDK. Maybe too flexible.


^ permalink raw reply

* Re: [PATCH v3 1/1] pcapng: add user-supplied timestamp support
From: Stephen Hemminger @ 2026-06-18 18:51 UTC (permalink / raw)
  To: Morten Brørup; +Cc: Dawid Wesierski, dev, thomas, Marek Kasiewicz
In-Reply-To: <98CBD80474FA8B44BF855DF32C47DC35F65924@smartserver.smartshare.dk>

On Thu, 18 Jun 2026 18:45:19 +0200
Morten Brørup <mb@smartsharesystems.com> wrote:

> > From: Stephen Hemminger [mailto:stephen@networkplumber.org]
> > Sent: Thursday, 18 June 2026 17.23
> > 
> > On Thu, 18 Jun 2026 10:38:15 -0400
> > Dawid Wesierski <dawid.wesierski@intel.com> wrote:
> >   
> > > + * @param ts
> > > + *   Packet timestamp in nanoseconds since the Unix epoch. If zero,  
> > the  
> > > + *   current TSC is captured and converted to epoch ns by
> > > + *   rte_pcapng_write_packets() when the packet is written.
> > >   *  
> > 
> > It might help users if a helper rte_tsc_to_epoch() was exposed.  
> 
> +1
> 
> Please note that such a helper would need to compensate for rte_rdtsc() drift.
> 
> Simplified:
> 
> int64_t rte_tsc_to_ns(tsc)
> {
>   struct timespec ts = clock_gettime(CLOCK_REALTIME);
>   int64_t now_ns = ts.tv_sec * NS_PER_S + ts.tv_nsec;
>   int64_t now_tsc = rte_rdtsc();
>   int64_t diff_tsc = tsc - tsc_now;
>   return now_ns + diff_tsc * NS_PER_S / rte_get_tsc_hz();
> }
> 
> A performance optimized version would take "now_ns" and "now_tsc" as parameters.
> And with "now_ns" passed as a parameter, the function also works with other clocks, such as CLOCK_MONOTONIC.
> 
> Also see this discussion on the Grout mailing list:
> https://inbox.dpdk.org/grout/98CBD80474FA8B44BF855DF32C47DC35F6590E@smartserver.smartshare.dk/T/#m465a04ca2e8219612dd9c3efb4198d23d5813422
> 
> 

Inside rte_pcapng is already all the necessary state and setup.
Just not exposed to user. It does it without divide operation

^ permalink raw reply

* [PATCH v3 4/4] net/axgbe: fix descriptor status out-of-bounds access
From: Stephen Hemminger @ 2026-06-18 17:21 UTC (permalink / raw)
  To: dev
  Cc: Stephen Hemminger, stable, Selwin Sebastian,
	Amaranath Somalapuram, Ravi Kumar
In-Reply-To: <20260618172544.338758-1-stephen@networkplumber.org>

Both axgbe_dev_rx_descriptor_status() and
axgbe_dev_tx_descriptor_status() compute the descriptor address as
desc[idx + offset] where idx is the masked ring position. When
idx + offset >= nb_desc, this reads past the end of the
descriptor ring buffer.

Fix by incorporating the offset into the index before masking,
using AXGBE_GET_DESC_IDX() which wraps with (nb_desc - 1).

Fixes: 0962b6055c08 ("net/axgbe: support descriptor status")
Cc: stable@dpdk.org

Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
 drivers/net/axgbe/axgbe_rxtx.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/net/axgbe/axgbe_rxtx.c b/drivers/net/axgbe/axgbe_rxtx.c
index 51a1aeb0b9..6f750d6ede 100644
--- a/drivers/net/axgbe/axgbe_rxtx.c
+++ b/drivers/net/axgbe/axgbe_rxtx.c
@@ -1205,8 +1205,8 @@ axgbe_dev_rx_descriptor_status(void *rx_queue, uint16_t offset)
 	if (offset >= rxq->nb_desc - rxq->dirty)
 		return RTE_ETH_RX_DESC_UNAVAIL;
 
-	idx = AXGBE_GET_DESC_IDX(rxq, rxq->cur);
-	desc = &rxq->desc[idx + offset];
+	idx = AXGBE_GET_DESC_IDX(rxq, rxq->cur + offset);
+	desc = &rxq->desc[idx];
 
 	if (!AXGMAC_GET_BITS_LE(desc->write.desc3, RX_NORMAL_DESC3, OWN))
 		return RTE_ETH_RX_DESC_DONE;
@@ -1228,8 +1228,8 @@ axgbe_dev_tx_descriptor_status(void *tx_queue, uint16_t offset)
 	if (offset >= txq->nb_desc - txq->dirty)
 		return RTE_ETH_TX_DESC_UNAVAIL;
 
-	idx = AXGBE_GET_DESC_IDX(txq, txq->dirty + txq->free_batch_cnt - 1);
-	desc = &txq->desc[idx + offset];
+	idx = AXGBE_GET_DESC_IDX(txq, txq->dirty + txq->free_batch_cnt - 1 + offset);
+	desc = &txq->desc[idx];
 
 	if (!AXGMAC_GET_BITS_LE(desc->desc3, TX_NORMAL_DESC3, OWN))
 		return RTE_ETH_TX_DESC_DONE;
-- 
2.53.0


^ permalink raw reply related

* [PATCH v3 3/4] net/axgbe: destroy mutexes on device close
From: Stephen Hemminger @ 2026-06-18 17:21 UTC (permalink / raw)
  To: dev; +Cc: Stephen Hemminger, stable, Selwin Sebastian, Ravi Kumar
In-Reply-To: <20260618172544.338758-1-stephen@networkplumber.org>

Four pthread mutexes (xpcs_mutex, i2c_mutex, an_mutex, phy_mutex)
are initialized in eth_axgbe_dev_init() but never destroyed in
axgbe_dev_close(). This leaks kernel resources on every device
close/reopen cycle.

Add pthread_mutex_destroy() calls to the close path.

Fixes: 572890ef6625 ("net/axgbe: add structs for MAC init and reset")
Cc: stable@dpdk.org

Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
 drivers/net/axgbe/axgbe_ethdev.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/drivers/net/axgbe/axgbe_ethdev.c b/drivers/net/axgbe/axgbe_ethdev.c
index 415fc6d739..a4b5e9425f 100644
--- a/drivers/net/axgbe/axgbe_ethdev.c
+++ b/drivers/net/axgbe/axgbe_ethdev.c
@@ -2481,6 +2481,11 @@ axgbe_dev_close(struct rte_eth_dev *eth_dev)
 	/* Disable all interrupts in the hardware */
 	XP_IOWRITE(pdata, XP_INT_EN, 0x0);
 
+	pthread_mutex_destroy(&pdata->xpcs_mutex);
+	pthread_mutex_destroy(&pdata->i2c_mutex);
+	pthread_mutex_destroy(&pdata->an_mutex);
+	pthread_mutex_destroy(&pdata->phy_mutex);
+
 	return 0;
 }
 
-- 
2.53.0


^ permalink raw reply related

* [PATCH v3 2/4] net/axgbe: fix Rx queue leak on descriptor init failure
From: Stephen Hemminger @ 2026-06-18 17:21 UTC (permalink / raw)
  To: dev; +Cc: Stephen Hemminger, stable, Selwin Sebastian, Ravi Kumar
In-Reply-To: <20260618172544.338758-1-stephen@networkplumber.org>

When wrapper_rx_desc_init() fails to allocate an mbuf for queue i,
only queue i is released. Queues 0 through i-1 have already been
fully populated with mbufs assigned to sw_ring entries and
programmed into hardware descriptors, but are never cleaned up.
This leaks all mbufs from the previously initialized queues.

Fix by allocating all needed buffers ahead of time with
rte_pktmbuf_alloc_bulk().

Fixes: 7c4158a5b592 ("net/axgbe: add DMA programming and start/stop")
Cc: stable@dpdk.org

Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
 drivers/net/axgbe/axgbe_dev.c | 43 +++++++++++++++++++----------------
 1 file changed, 24 insertions(+), 19 deletions(-)

diff --git a/drivers/net/axgbe/axgbe_dev.c b/drivers/net/axgbe/axgbe_dev.c
index 482d3d8062..10ddecf968 100644
--- a/drivers/net/axgbe/axgbe_dev.c
+++ b/drivers/net/axgbe/axgbe_dev.c
@@ -1011,13 +1011,29 @@ static void wrapper_tx_desc_init(struct axgbe_port *pdata)
 
 static int wrapper_rx_desc_init(struct axgbe_port *pdata)
 {
-	struct axgbe_rx_queue *rxq;
-	struct rte_mbuf *mbuf;
 	volatile union axgbe_rx_desc *desc;
-	unsigned int i, j;
+	unsigned int i, j, k;
 
+	/* Allocate all receive buffers up front. */
 	for (i = 0; i < pdata->eth_dev->data->nb_rx_queues; i++) {
-		rxq = pdata->eth_dev->data->rx_queues[i];
+		struct axgbe_rx_queue *rxq = pdata->eth_dev->data->rx_queues[i];
+
+		if (rte_pktmbuf_alloc_bulk(rxq->mb_pool, rxq->sw_ring, rxq->nb_desc) != 0) {
+			PMD_DRV_LOG_LINE(ERR, "RX mbuf alloc failed queue_id = %u, nb_desc = %u",
+					 i, rxq->nb_desc);
+			for (k = 0; k < i; k++) {
+				rxq = pdata->eth_dev->data->rx_queues[k];
+
+				rte_pktmbuf_free_bulk(rxq->sw_ring, rxq->nb_desc);
+				memset(rxq->sw_ring, 0, rxq->nb_desc * sizeof(*rxq->sw_ring));
+			}
+			return -ENOMEM;
+		}
+	}
+
+	/* Buffers are available; publish them to the hardware */
+	for (i = 0; i < pdata->eth_dev->data->nb_rx_queues; i++) {
+		struct axgbe_rx_queue *rxq = pdata->eth_dev->data->rx_queues[i];
 
 		/* Initialize software ring entries */
 		rxq->mbuf_alloc = 0;
@@ -1026,22 +1042,11 @@ static int wrapper_rx_desc_init(struct axgbe_port *pdata)
 		desc = AXGBE_GET_DESC_PT(rxq, 0);
 
 		for (j = 0; j < rxq->nb_desc; j++) {
-			mbuf = rte_mbuf_raw_alloc(rxq->mb_pool);
-			if (mbuf == NULL) {
-				PMD_DRV_LOG_LINE(ERR, "RX mbuf alloc failed queue_id = %u, idx = %d",
-					    (unsigned int)rxq->queue_id, j);
-				axgbe_dev_rx_queue_release(pdata->eth_dev, i);
-				return -ENOMEM;
-			}
-			rxq->sw_ring[j] = mbuf;
-			/* Mbuf populate */
-			mbuf->next = NULL;
-			mbuf->data_off = RTE_PKTMBUF_HEADROOM;
-			mbuf->nb_segs = 1;
+			struct rte_mbuf *mbuf = rxq->sw_ring[j];
+
+			/* mbuf is in reset state (nb_segs = 1, headroom, etc)  */
 			mbuf->port = rxq->port_id;
-			desc->read.baddr =
-				rte_cpu_to_le_64(
-					rte_mbuf_data_iova_default(mbuf));
+			desc->read.baddr = rte_cpu_to_le_64(rte_mbuf_data_iova_default(mbuf));
 			rte_wmb();
 			AXGMAC_SET_BITS_LE(desc->read.desc3,
 						RX_NORMAL_DESC3, OWN, 1);
-- 
2.53.0


^ permalink raw reply related

* [PATCH v3 1/4] net/axgbe: fix resource leaks in device init error paths
From: Stephen Hemminger @ 2026-06-18 17:21 UTC (permalink / raw)
  To: dev
  Cc: Stephen Hemminger, stable, Selwin Sebastian, Chandubabu Namburu,
	Ravi Kumar
In-Reply-To: <20260618172544.338758-1-stephen@networkplumber.org>

Several error paths in eth_axgbe_dev_init() fail to release
previously allocated resources:

 - When hash_mac_addrs allocation fails, mac_addrs is leaked.
 - When phy_init() fails, hash_mac_addrs is leaked.
 - When phy_init() or the interrupt registration fails, the four
   mutexes initialized just before phy_init() are leaked.
 - The return value of rte_intr_callback_register() is not
   checked, so a failure leaves the driver without interrupt
   handling but continuing as if everything is functional.
 - When the DMA software reset via hw_if.exit() fails, the
   error is only logged and initialization continues with the
   hardware in an undefined state.

Replace the scattered cleanups with a single goto unwind so each
error path releases exactly the resources allocated up to that
point: the hw_if.exit() failure runs before the mutexes are
initialized and so skips the mutex teardown, while the phy_init()
and interrupt-registration failures destroy the mutexes as well.
Also check the interrupt registration return value and propagate
the DMA reset failure.

Fixes: e01d9b2e980b ("net/axgbe: support unicast hash table for MAC address")
Cc: stable@dpdk.org

Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
 drivers/net/axgbe/axgbe_ethdev.c | 35 +++++++++++++++++++++++---------
 1 file changed, 25 insertions(+), 10 deletions(-)

diff --git a/drivers/net/axgbe/axgbe_ethdev.c b/drivers/net/axgbe/axgbe_ethdev.c
index 61725d55ca..415fc6d739 100644
--- a/drivers/net/axgbe/axgbe_ethdev.c
+++ b/drivers/net/axgbe/axgbe_ethdev.c
@@ -2360,7 +2360,8 @@ eth_axgbe_dev_init(struct rte_eth_dev *eth_dev)
 		PMD_INIT_LOG(ERR,
 			     "Failed to allocate %d bytes needed to "
 			     "store MAC addresses", len);
-		return -ENOMEM;
+		ret = -ENOMEM;
+		goto err_mac;
 	}
 
 	if (!rte_is_valid_assigned_ether_addr(&pdata->mac_addr))
@@ -2406,8 +2407,10 @@ eth_axgbe_dev_init(struct rte_eth_dev *eth_dev)
 					  pdata->vdata->rx_max_fifo_size);
 	/* Issue software reset to DMA */
 	ret = pdata->hw_if.exit(pdata);
-	if (ret)
+	if (ret) {
 		PMD_DRV_LOG_LINE(ERR, "hw_if->exit EBUSY error");
+		goto err_hash;
+	}
 
 	/* Set default configuration data */
 	axgbe_default_config(pdata);
@@ -2426,20 +2429,32 @@ eth_axgbe_dev_init(struct rte_eth_dev *eth_dev)
 	rte_thread_mutex_init_shared(&pdata->phy_mutex);
 
 	ret = pdata->phy_if.phy_init(pdata);
-	if (ret) {
-		rte_free(eth_dev->data->mac_addrs);
-		eth_dev->data->mac_addrs = NULL;
-		return ret;
-	}
+	if (ret)
+		goto err_mutex;
 
-	rte_intr_callback_register(pci_dev->intr_handle,
-				   axgbe_dev_interrupt_handler,
-				   (void *)eth_dev);
+	ret = rte_intr_callback_register(pci_dev->intr_handle,
+					 axgbe_dev_interrupt_handler,
+					 (void *)eth_dev);
+	if (ret)
+		goto err_mutex;
 	PMD_INIT_LOG(DEBUG, "port %d vendorID=0x%x deviceID=0x%x",
 		     eth_dev->data->port_id, pci_dev->id.vendor_id,
 		     pci_dev->id.device_id);
 
 	return 0;
+
+err_mutex:
+	pthread_mutex_destroy(&pdata->phy_mutex);
+	pthread_mutex_destroy(&pdata->an_mutex);
+	pthread_mutex_destroy(&pdata->i2c_mutex);
+	pthread_mutex_destroy(&pdata->xpcs_mutex);
+err_hash:
+	rte_free(eth_dev->data->hash_mac_addrs);
+	eth_dev->data->hash_mac_addrs = NULL;
+err_mac:
+	rte_free(eth_dev->data->mac_addrs);
+	eth_dev->data->mac_addrs = NULL;
+	return ret;
 }
 
 static int
-- 
2.53.0


^ permalink raw reply related

* [PATCH v3 0/4] net/axgbe: fix resource leaks and OOB access
From: Stephen Hemminger @ 2026-06-18 17:21 UTC (permalink / raw)
  To: dev; +Cc: Stephen Hemminger
In-Reply-To: <20260218164324.915065-1-stephen@networkplumber.org>

A code review of the axgbe PMD turned up several correctness bugs,
most of them in resource management on the device initialization
error paths, plus an out-of-bounds descriptor ring access.

v3:
  - Patch 1: consolidate the error-path cleanup 
  - Patch 2: split Rx buffer setup into two passes

Stephen Hemminger (4):
  net/axgbe: fix resource leaks in device init error paths
  net/axgbe: fix Rx queue leak on descriptor init failure
  net/axgbe: destroy mutexes on device close
  net/axgbe: fix descriptor status out-of-bounds access

 drivers/net/axgbe/axgbe_dev.c    | 43 ++++++++++++++++++--------------
 drivers/net/axgbe/axgbe_ethdev.c | 40 +++++++++++++++++++++--------
 drivers/net/axgbe/axgbe_rxtx.c   |  8 +++---
 3 files changed, 58 insertions(+), 33 deletions(-)

-- 
2.53.0


^ permalink raw reply

* RE: [PATCH v3 1/1] pcapng: add user-supplied timestamp support
From: Morten Brørup @ 2026-06-18 16:45 UTC (permalink / raw)
  To: Stephen Hemminger, Dawid Wesierski; +Cc: dev, thomas, Marek Kasiewicz
In-Reply-To: <20260618082240.517e60cb@phoenix.local>

> From: Stephen Hemminger [mailto:stephen@networkplumber.org]
> Sent: Thursday, 18 June 2026 17.23
> 
> On Thu, 18 Jun 2026 10:38:15 -0400
> Dawid Wesierski <dawid.wesierski@intel.com> wrote:
> 
> > + * @param ts
> > + *   Packet timestamp in nanoseconds since the Unix epoch. If zero,
> the
> > + *   current TSC is captured and converted to epoch ns by
> > + *   rte_pcapng_write_packets() when the packet is written.
> >   *
> 
> It might help users if a helper rte_tsc_to_epoch() was exposed.

+1

Please note that such a helper would need to compensate for rte_rdtsc() drift.

Simplified:

int64_t rte_tsc_to_ns(tsc)
{
  struct timespec ts = clock_gettime(CLOCK_REALTIME);
  int64_t now_ns = ts.tv_sec * NS_PER_S + ts.tv_nsec;
  int64_t now_tsc = rte_rdtsc();
  int64_t diff_tsc = tsc - tsc_now;
  return now_ns + diff_tsc * NS_PER_S / rte_get_tsc_hz();
}

A performance optimized version would take "now_ns" and "now_tsc" as parameters.
And with "now_ns" passed as a parameter, the function also works with other clocks, such as CLOCK_MONOTONIC.

Also see this discussion on the Grout mailing list:
https://inbox.dpdk.org/grout/98CBD80474FA8B44BF855DF32C47DC35F6590E@smartserver.smartshare.dk/T/#m465a04ca2e8219612dd9c3efb4198d23d5813422



^ permalink raw reply

* Re: [PATCH v2 1/7] ethdev: add header split mbuf callback API
From: Thomas Monjalon @ 2026-06-18 16:26 UTC (permalink / raw)
  To: Marek Kasiewicz, Dawid Wesierski
  Cc: dev, david.marchand, Stephen Hemminger, andrew.rybchenko
In-Reply-To: <20260618144442.312844-2-dawid.wesierski@intel.com>

Hello,

Please use --cc-cmd devtools/get-maintainer.sh so maintainers are Cc'ed.

18/06/2026 16:44, Dawid Wesierski:
> From: Marek Kasiewicz <marek.kasiewicz@intel.com>
> 
> Add rte_eth_hdrs_set_mbuf_callback() that allows applications to
> register a callback providing custom payload mbufs for header split RX
> mode. When registered, a PMD that supports header split is expected to
> call this callback at mbuf allocation points to obtain user-provided
> payload buffers instead of allocating from the mempool.

What is not working with the current API?
You can already use an external buffer.

> This enables zero-copy RX for header split: the NIC DMAs the payload
> directly into application-managed buffers (e.g., mapped frame buffers
> with known IOVA), bypassing an extra memcpy from the mempool mbuf.

Do you know the flag RTE_PKTMBUF_POOL_F_PINNED_EXT_BUF?

> A new struct rte_eth_hdrs_mbuf describes the payload buffer (virtual
> address and IOVA), and the new dev_ops hook hdrs_mbuf_set_cb lets each
> PMD wire the callback to its receive queue state.
> 
> The API is marked experimental and exported with version 26.07.

I think this API is not necessary.



^ permalink raw reply

* Re: [PATCH v1 0/5] prefix lcore role enum values
From: Thomas Monjalon @ 2026-06-18 16:19 UTC (permalink / raw)
  To: Huisong Li
  Cc: dev, andrew.rybchenko, zhanjie9, Morten Brørup,
	David Marchand
In-Reply-To: <98CBD80474FA8B44BF855DF32C47DC35F65917@smartserver.smartshare.dk>

17/06/2026 13:48, Morten Brørup:
> > From: Huisong Li [mailto:lihuisong@huawei.com]
> > Sent: Wednesday, 17 June 2026 12.28
> > 
> > Add the RTE_LCORE_ prefix to the lcore role enum values in
> > rte_lcore_role_t
> > to follow DPDK naming conventions.
> > 
> > - ROLE_RTE      -> RTE_LCORE_ROLE_RTE
> > - ROLE_OFF      -> RTE_LCORE_ROLE_OFF
> > - ROLE_SERVICE  -> RTE_LCORE_ROLE_SERVICE
> > - ROLE_NON_EAL  -> RTE_LCORE_ROLE_NON_EAL
> > 
> > Old names are kept as macros aliasing to the new names to preserve
> > backward compatibility.
> > 
> 
> Series-Acked-by: Morten Brørup <mb@smartsharesystems.com>

Squashed and applied, thanks.

I have a doubt about RTE_LCORE_ROLE_RTE: could we find a better name?

Also we should probably add some comments to explain each role.




^ permalink raw reply

* Re: [PATCH 0/2] eal: notify secondaries on primary exit
From: Stephen Hemminger @ 2026-06-18 16:09 UTC (permalink / raw)
  To: dev
In-Reply-To: <20260507210152.410419-1-stephen@networkplumber.org>

On Thu,  7 May 2026 13:58:35 -0700
Stephen Hemminger <stephen@networkplumber.org> wrote:

> Bugzilla 1942: when a primary process exits cleanly, secondary
> processes other than testpmd do not get notified.  The notification
> mechanism added in 25.11 was placed in testpmd and used
> rte_mp_request_sync() with a testpmd-specific action name, so any
> non-testpmd secondary (dpdk-dumpcap, dpdk-pdump, dpdk-procinfo, or
> out-of-tree consumers) would log "Cannot find action: mp_testpmd"
> and the primary would block on the 5 second request timeout.
> 
> Putting application-specific IPC actions on a broadcast request path
> is the wrong layer.  Notification of primary exit is something every
> secondary needs and should come from EAL, not from each application.
> 
> Patch 1 reverts the testpmd-side mechanism (commit f96273c8e9d3).
> The secondary-side primary alive monitor (enable_primary_monitor)
> is preserved and continues to handle detection of primary exit via
> the existing alarm-based polling of rte_eal_primary_proc_alive().
> 
> Patch 2 adds a generic EAL-level notification.  On primary cleanup,
> rte_mp_channel_cleanup() broadcasts an MP_REQ_QUIT message to all
> known secondaries via rte_mp_sendmsg().  Secondaries register an
> internal action handler that tears down their own MP channel on
> receipt.  No new public API; no application changes required for
> any secondary, in-tree or out.
> 
> This is the minimum fix suitable for backport to 25.11 stable.
> It addresses the clean exit case.  The crash case (primary killed
> or signaled) continues to be handled by the existing
> rte_eal_primary_proc_alive() polling on the secondary side, which
> detects the primary's release of the config file lock.
> 
> A more complete solution using a connected socket type (SOCK_SEQPACKET)
> is planned since that can handle both planned and forced exiting
> of the primary.
> 
> Tested with testpmd (primary) and dpdk-dumpcap, dpdk-pdump,
> dpdk-procinfo (secondaries).
> 
> 
> Stephen Hemminger (2):
>   Revert "app/testpmd: stop forwarding in secondary process"
>   eal: notify secondary on primary exit
> 
>  app/test-pmd/testpmd.c           | 103 ++-----------------------------
>  lib/eal/common/eal_common_proc.c |  51 ++++++++++++++-
>  2 files changed, 53 insertions(+), 101 deletions(-)
> 

This is a real bug fix. Even if AI is being overly wordy in describing
it; what it does is move the notification from being testpmd -> testpmd only
to a more general primary -> secondary mechanism.

Without it can demonstrate the bug rather trivially with packet
capture tools (pdump or dumpcap)

Could this get reviewed?

^ permalink raw reply

* Re: [PATCH v2 0/4] net/bond: fixes and cleanup
From: Stephen Hemminger @ 2026-06-18 16:00 UTC (permalink / raw)
  To: Chas Williams, Min Hu (Connor); +Cc: dev
In-Reply-To: <20260529000157.235931-1-stephen@networkplumber.org>

On Thu, 28 May 2026 16:59:12 -0700
Stephen Hemminger <stephen@networkplumber.org> wrote:

> Automated analysis of the bonding found a few minor things.
> The bug fix is in patch 3 for secondary process crash does rx/tx. 
> 
> The cleanups are in handling of 8023ad mode setting
> and the logging macros.
> 
> v2 - feedback about the mode setting and log messages
> 
> Stephen Hemminger (4):
>   net/bonding: make 8023ad enable function void
>   net/bonding: check mode before setting dedicated queues
>   net/bonding: prevent crash on Rx/Tx from secondary process
>   net/bonding: remove redundant function names from log
> 
>  drivers/net/bonding/eth_bond_8023ad_private.h | 17 +----
>  drivers/net/bonding/rte_eth_bond_8023ad.c     | 16 ++--
>  drivers/net/bonding/rte_eth_bond_api.c        |  4 +-
>  drivers/net/bonding/rte_eth_bond_pmd.c        | 73 ++++++++++++++-----
>  4 files changed, 67 insertions(+), 43 deletions(-)
> 

Ping. Need review and ack for these patches

^ permalink raw reply

* Re: [PATCH v1 00/17] net/dpaa: bug fixes for bus, net and fmlib drivers
From: Stephen Hemminger @ 2026-06-18 15:56 UTC (permalink / raw)
  To: Hemant Agrawal; +Cc: david.marchand, dev
In-Reply-To: <20260618141151.3990283-1-hemant.agrawal@nxp.com>

On Thu, 18 Jun 2026 19:41:34 +0530
Hemant Agrawal <hemant.agrawal@nxp.com> wrote:

> This series contains bug fixes for the DPAA PMD (bus/dpaa, net/dpaa,
> net/dpaa/fmlib and dma/dpaa):
> 
> - Fix error handling in qman_create_fq and qman_query
> - Fix fqid endianness in qman_fq_flow_control
> - Fix CGR index usage in dpaa_modify_cgr
> - Add null check in fmlib scheme delete
> - Fix BMI RX stats register offset
> - Fix file descriptor leak after CCSR mmap
> - Fix device probe regression on LS1043A
> - Fix double-close in device remove path
> - Fix incorrect condition in interrupt unregister
> - Fix Coverity-reported issues in dpaa_flow and dpaa_qdma
> - Fix xstat name for tx undersized counter
> - Fix xstat string typos in BMI stats table
> - Remove duplicate ptype entries
> - Fix wrong buffer in xstats get by id
> - Fix null l3_len check in checksum offload
> - Fix mbuf leak in SG fd creation
> 
> All patches are bug fixes tagged with Fixes: and Cc: stable@dpdk.org.
> 
> Gagandeep Singh (3):
>   bus/dpaa: fix device probe issue
>   net/dpaa: fix device remove
>   net/dpaa: fix invalid check on interrupt unregister
> 
> Hemant Agrawal (11):
>   bus/dpaa: fix error handling of qman_create_fq
>   bus/dpaa: fix fqid endianness
>   bus/dpaa: fix error handling in qman_query
>   net/dpaa: fix modify cgr to use index
>   bus/dpaa: fix fd leak for ccsr mmap
>   net/dpaa: fix xstat name for tx undersized counter
>   net/dpaa: fix xstat string typos in BMI stats table
>   net/dpaa: remove duplicate ptype entries
>   net/dpaa: fix wrong buffer in xstats get by id
>   net/dpaa: fix null l3_len check in checksum offload
>   net/dpaa: fix mbuf leak in SG fd creation
> 
> Jun Yang (1):
>   bus/dpaa: fix BMI RX stats register offset
> 
> Prashant Gupta (1):
>   net/dpaa/fmlib: add null check in scheme delete
> 
> Vanshika Shukla (1):
>   net/dpaa: fix coverity reported issues
> 
>  drivers/bus/dpaa/base/qbman/bman_driver.c |  3 ++-
>  drivers/bus/dpaa/base/qbman/qman.c        | 11 +++++----
>  drivers/bus/dpaa/base/qbman/qman_driver.c |  6 ++---
>  drivers/bus/dpaa/dpaa_bus.c               |  6 ++---
>  drivers/bus/dpaa/include/fman.h           |  6 ++---
>  drivers/dma/dpaa/dpaa_qdma.c              |  7 +++++-
>  drivers/net/dpaa/dpaa_ethdev.c            | 27 +++++++++++------------
>  drivers/net/dpaa/dpaa_flow.c              |  4 ++++
>  drivers/net/dpaa/dpaa_rxtx.c              |  3 +++
>  drivers/net/dpaa/fmlib/fm_lib.c           |  3 +++
>  10 files changed, 46 insertions(+), 30 deletions(-)
> 

Looks good but there are some warnings from more detailed AI review that
need addressing.

Review of [PATCH v1 00/17] bus/dpaa, net/dpaa fixes

Reviewed against current main. No errors found; the series is a
solid set of bug fixes. A few warnings and notes below, mostly
about undocumented side effects and Fixes: accuracy. Patches not
listed (01, 02, 03, 04, 06, 07, 12, 14, 15, 16, 17) look correct.


[PATCH v1 05/17] net/dpaa/fmlib: add null check in scheme delete

Info: the commit body refers to FM_PCD_MatchTableSchemeDelete(),
but the function actually changed is fm_pcd_kg_scheme_delete().
The NULL guard and E_NO_DEVICE return are correct and match the
sibling functions in fm_lib.c; only the message names the wrong
API.


[PATCH v1 08/17] bus/dpaa: fix device probe issue

The early "return 0" was indeed skipping device-list creation, so
removing it fixes probe on LS1043A. Good.

Warning: that early return did two things -- it forced
max_push_rxq_num = 0 AND skipped the DPAA_PUSH_QUEUES_NUMBER
override that follows. With the return gone, execution now falls
through to:

	penv = getenv("DPAA_PUSH_QUEUES_NUMBER");
	if (penv)
		dpaa_bus.max_push_rxq_num = atoi(penv);

so on LS1043A the env var can now raise the push-queue count back
above zero, which the original code deliberately prevented (the
comment is "Disabling the default push mode for LS1043A", and the
SoC has the FMAN push-mode errata handled in dpaa_rxtx.c). If
LS1043A must keep push mode disabled regardless of the env var,
guard the override, e.g.:

	if (dpaa_bus.svr_ver == SVR_LS1043A_FAMILY) {
		dpaa_bus.max_push_rxq_num = 0;
	} else {
		penv = getenv("DPAA_PUSH_QUEUES_NUMBER");
		...
	}

If the override is intended to apply to LS1043A, please say so in
the commit message.


[PATCH v1 09/17] net/dpaa: fix device remove

The RTE_ETH_DEV_UNUSED guard against double close/release is
correct, and "int ret = 0" is now required because the assignment
is conditional.

Warning: the patch also drops the dpaa_finish() call, which the
commit message does not mention. dpaa_finish() is registered as a
destructor (RTE_FINI_PRIO(dpaa_finish, 103)), so it still runs at
process exit, but the previous explicit call ran at last-device
remove (!dpaa_valid_dev). Removing it moves the global teardown
(dpaa_fm_term, per-queue portal close, is_global_init = 0) from
last-remove time to exit time. For run-then-exit this is
equivalent, but for remove-all-then-continue (e.g. re-probe in a
running process) is_global_init now stays 1 and portals stay open
until exit. Please call this change out in the commit message and
confirm re-probe still behaves.


[PATCH v1 10/17] net/dpaa: fix invalid check on interrupt unregister

The fix is correct: rte_intr_callback_unregister() returns the
number of callbacks removed (>=1) on success and a negative value
on failure, so "if (ret)" logged a spurious warning on every
successful unregister; "if (ret < 0)" is right.

Warning: the Fixes: tag points at

	9c99878aa1 ("log: introduce logtype register macro")

which is unrelated to interrupt unregistration and looks like a
copy/paste error. An incorrect Fixes: will misdirect the stable
backport -- please point it at the commit that introduced the
"if (ret)" check.


[PATCH v1 11/17] net/dpaa: fix coverity reported issues

Both fixes are correct. The dpaa_qdma.c bound (num == 0 ||
num > FSL_QDMA_SG_MAX_ENTRY) prevents the desc_ssge[num - 1]
underflow when pending_num is 0. The dpaa_flow.c port_handle
close fixes the continue-path leak without introducing a
double-close: the success path nulls port_handle inside
dpaa_fm_deconfig(), and the error/continue paths legitimately
leave it set for the new guard to close.

Info: this bundles two unrelated Coverity fixes across
drivers/dma/dpaa and drivers/net/dpaa under a single net/dpaa
Fixes: tag (e7665de896). The qdma OOB almost certainly has a
different origin commit. Splitting into two patches (or at least
carrying the correct Fixes: for the qdma change) would make the
stable backport cleaner.


[PATCH v1 13/17] net/dpaa: fix xstat string typos in BMI stats table

Both typo fixes are correct.

Info: the very next entry on the same table is also misspelled --
"rx_buf_diallocate" (fmbm_rbdc, "Rx Buffers Deallocate Counter")
should be "rx_buf_deallocate". Worth fixing in the same pass since
this patch is specifically cleaning up these names.

^ permalink raw reply

* Re: [PATCH v2 0/7] Intel network drivers enhancements
From: Stephen Hemminger @ 2026-06-18 15:46 UTC (permalink / raw)
  To: Dawid Wesierski; +Cc: dev, thomas, david.marchand, Marek Kasiewicz
In-Reply-To: <20260618144442.312844-1-dawid.wesierski@intel.com>

On Thu, 18 Jun 2026 10:44:35 -0400
Dawid Wesierski <dawid.wesierski@intel.com> wrote:

> From: Marek Kasiewicz <marek.kasiewicz@intel.com>
> 
> This series introduces several improvements to Intel iavf and ice
> drivers, including a new ethdev API for header split mbuf callbacks,
> increased ring descriptors, and improved PTP timestamping.
> 
> Marek Kasiewicz (7):
>   ethdev: add header split mbuf callback API
>   net/iavf: increase max ring descriptors to hardware limit
>   net/iavf: allow runtime queue rate limit configuration
>   net/ice/base: reduce default scheduler burst size
>   net/ice: timestamp all received packets when PTP is enabled
>   net/iavf: disable runtime queue setup capability
>   net/intel: support header split mbuf callback
> 
>  drivers/net/intel/common/rx.h         |  2 +
>  drivers/net/intel/iavf/iavf_ethdev.c  |  3 --
>  drivers/net/intel/iavf/iavf_rxtx.h    |  2 +-
>  drivers/net/intel/iavf/iavf_tm.c      | 11 ++--
>  drivers/net/intel/ice/base/ice_type.h |  2 +-
>  drivers/net/intel/ice/ice_ethdev.c    |  1 +
>  drivers/net/intel/ice/ice_rxtx.c      | 72 ++++++++++++++++++++++++---
>  drivers/net/intel/ice/ice_rxtx.h      |  2 +
>  lib/ethdev/ethdev_driver.h            | 15 +++++++
>  lib/ethdev/rte_ethdev.c               | 51 ++++++++++++++++++++++
>  lib/ethdev/rte_ethdev.h               |  7 +++
>  11 files changed, 153 insertions(+), 15 deletions(-)
> 

AI review found lots of problems with the implementation as well.

Series composition

This v2 series bundles seven patches under one cover letter, but only
1/7 (ethdev API) and 7/7 (net/intel) implement the header-split mbuf
callback. Patches 2/7 (iavf max ring desc), 3/7 (iavf queue rate
limit), 4/7 (ice base burst size), 5/7 (ice PTP timestamp), and 6/7
(iavf runtime queue setup) are unrelated driver changes. Please split
these into a separate series so each can be reviewed and merged on its
own merits; mixing them obscures the dependency and complicates bisect.


[PATCH v2 1/7] ethdev: add header split mbuf callback API

Warning: struct rte_eth_hdrs_mbuf carries only buf_addr and buf_iova,
no length. The callback therefore cannot tell the PMD how large the
application buffer is. In buffer-split mode the NIC DMAs up to the
queue's configured payload buffer size; if the application buffer is
smaller, the hardware writes past it. Add a length field and have the
PMD (and/or ethdev) validate it against the configured payload size.

Warning: the buffer lifetime and ownership are undocumented. The
Doxygen does not state who owns the buffer after the payload mbuf is
freed, whether the address is consumed as-is or with headroom applied
(the 7/7 implementation adds RTE_PKTMBUF_HEADROOM to the IOVA -- see
below), or that the callback must return a distinct buffer per call.
These are contract details an application cannot guess.

Warning: new experimental API with no testpmd hook and no functional
test in app/test. Both are required for new ethdev API.

Warning: new experimental public API but no release notes entry
(doc/guides/rel_notes/release_26_07.rst). The series touches only the
three code files.

Info: rte_eth_hdrs_set_mbuf_callback() validates port_id and the
hdrs_mbuf_set_cb hook, but passes rx_queue_id to the PMD without
checking it against dev->data->nb_rx_queues. Sibling per-queue ethdev
calls (e.g. rte_eth_rx_queue_setup) validate the queue id at the
ethdev layer. The ICE PMD does check it in 7/7, but validating in the
wrapper would be consistent and would protect future PMDs that forget.


[PATCH v2 7/7] net/intel: support header split mbuf callback

Error: overwriting buf_addr/buf_iova on a pool mbuf corrupts the
payload mempool. At all three sites (ice_alloc_rx_queue_mbufs,
ice_rx_alloc_bufs, ice_recv_pkts) the payload mbuf is allocated from
rxq->rxseg[1].mp and then has its buf_addr/buf_iova rewritten to point
at application memory:

	if (ret >= 0) {
		mbuf_pay->buf_addr = hdrs_mbuf.buf_addr;
		mbuf_pay->buf_iova = hdrs_mbuf.buf_iova;
	}

DPDK never restores buf_addr/buf_iova on free. When these mbufs are
returned to rxseg[1].mp (via rte_pktmbuf_free of the received chain,
or _ice_rx_queue_release_mbufs on queue stop), the pool objects retain
the foreign buffer pointer, and buf_len still describes the original
mempool buffer. The next rte_mbuf_raw_alloc()/raw_alloc_bulk() from
that pool hands back an mbuf pointing at application memory. The
correct mechanism for pointing an mbuf at non-pool memory is
rte_pktmbuf_attach_extbuf() (sets buf_addr/buf_iova/buf_len, the
EXTERNAL flag, and a free callback), or a dedicated external-buffer
mempool. As written the payload pool is progressively poisoned.

Error: the callback's failure return is swallowed. On ret < 0 the code
skips the address update but still programs the descriptor from the
mbuf's current buf_iova:

	rxdp->read.pkt_addr =
		rte_cpu_to_le_64(rte_mbuf_data_iova_default(nmb_pay));

Because of the pool corruption above, a recycled mbuf's buf_iova may
point at a buffer the application already reclaimed, so a failed
callback silently arms the NIC to DMA into stale/foreign memory. A
callback failure should abort the refill (or fall back to a known-good
pool buffer), not fall through. Separately, the documented contract is
"0 on success, negative on failure"; the test should be ret == 0, not
ret >= 0.

Warning: RTE_PKTMBUF_HEADROOM is silently applied to the supplied
IOVA. The descriptor is programmed with rte_mbuf_data_iova_default(),
i.e. buf_iova + data_off (128), but the API documents buf_iova as "the
IOVA of the payload buffer". The NIC therefore writes 128 bytes into
the application buffer, and the tail of a buffer sized to the payload
overruns by the headroom. Either document that the headroom offset is
applied, or program pkt_addr from the raw buf_iova for callback-
provided buffers.


^ permalink raw reply

* Re: [PATCH v2 0/7] Intel network drivers enhancements
From: Stephen Hemminger @ 2026-06-18 15:45 UTC (permalink / raw)
  To: Dawid Wesierski; +Cc: dev, thomas, david.marchand, Marek Kasiewicz
In-Reply-To: <20260618144442.312844-1-dawid.wesierski@intel.com>

On Thu, 18 Jun 2026 10:44:35 -0400
Dawid Wesierski <dawid.wesierski@intel.com> wrote:

> From: Marek Kasiewicz <marek.kasiewicz@intel.com>
> 
> This series introduces several improvements to Intel iavf and ice
> drivers, including a new ethdev API for header split mbuf callbacks,
> increased ring descriptors, and improved PTP timestamping.
> 
> Marek Kasiewicz (7):
>   ethdev: add header split mbuf callback API
>   net/iavf: increase max ring descriptors to hardware limit
>   net/iavf: allow runtime queue rate limit configuration
>   net/ice/base: reduce default scheduler burst size
>   net/ice: timestamp all received packets when PTP is enabled
>   net/iavf: disable runtime queue setup capability
>   net/intel: support header split mbuf callback
> 
>  drivers/net/intel/common/rx.h         |  2 +
>  drivers/net/intel/iavf/iavf_ethdev.c  |  3 --
>  drivers/net/intel/iavf/iavf_rxtx.h    |  2 +-
>  drivers/net/intel/iavf/iavf_tm.c      | 11 ++--
>  drivers/net/intel/ice/base/ice_type.h |  2 +-
>  drivers/net/intel/ice/ice_ethdev.c    |  1 +
>  drivers/net/intel/ice/ice_rxtx.c      | 72 ++++++++++++++++++++++++---
>  drivers/net/intel/ice/ice_rxtx.h      |  2 +
>  lib/ethdev/ethdev_driver.h            | 15 +++++++
>  lib/ethdev/rte_ethdev.c               | 51 ++++++++++++++++++++++
>  lib/ethdev/rte_ethdev.h               |  7 +++
>  11 files changed, 153 insertions(+), 15 deletions(-)
> 

This looks interesting but I don't understand the motivation
or use case for the new code. There is no documentation, examples
or test integration.  At this point the patch is in pure RFC state.

More wordy answer from AI...

Documentation and motivation

This adds a new public ethdev RX mechanism but ships no prose
rationale, no documentation, and no worked example. For a new API
the series needs to make the case before the code can be evaluated.

Missing documentation:

- No prog_guide section. A new RX model -- header split where the
  application supplies the payload buffer -- needs a write-up (e.g.
  in doc/guides/prog_guide) covering the buffer-lifetime contract,
  the IOVA/headroom semantics, which offloads must be enabled
  (RTE_ETH_RX_OFFLOAD_BUFFER_SPLIT), and what happens on a PMD that
  does not implement the hook.

- No example or testpmd integration. There is no end-to-end flow a
  reviewer can read or run: register the callback, supply buffers,
  receive, consume, recycle. Without one there is no demonstrated
  use case and no way to validate the design (the mempool-corruption
  and headroom issues in 7/7 would likely have surfaced in a working
  example).

Missing motivation:

- The actual use case is not named. "Zero-copy RX into mapped frame
  buffers with known IOVA" describes a mechanism, not a user. Who
  needs this -- an AF_XDP-style external umem, a GPU/DMA target
  (gpudev), something else? State it.

- The benefit is unquantified. The claim is avoiding "an extra
  memcpy from the mempool mbuf," but the patch replaces that with a
  per-allocation indirect callback on the Rx fast path
  (ice_recv_pkts, ice_rx_alloc_bufs). The rationale should show the
  memcpy actually exists in the path being optimized and that the
  indirect call per buffer is a net win.

- It does not explain why existing mechanisms are insufficient. DPDK
  already lets an application own payload memory via
  rte_pktmbuf_attach_extbuf() and via external-buffer mempools
  (pinned external memory passed to the Rx mempool). The series must
  argue why a new callback API is needed instead of those -- this is
  the same alternative raised against the 7/7 implementation, so the
  motivation and the correctness fix point at the same question.

- Constraints are undocumented: PMD support scope, fast-path
  threading and reentrancy of the callback, and interaction with the
  buffer-split configuration.

Until there is a documented model and at least one example showing
the buffer lifecycle, it is hard to tell whether the callback API is
the right shape -- or whether attach_extbuf / an external-mempool
already covers the use case.

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox