Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/patches/129575/?format=api
http://patchwork.dpdk.org/api/patches/129575/?format=api", "web_url": "http://patchwork.dpdk.org/project/dpdk/patch/20230717110709.39220-5-juraj.linkes@pantheon.tech/", "project": { "id": 1, "url": "http://patchwork.dpdk.org/api/projects/1/?format=api", "name": "DPDK", "link_name": "dpdk", "list_id": "dev.dpdk.org", "list_email": "dev@dpdk.org", "web_url": "http://core.dpdk.org", "scm_url": "git://dpdk.org/dpdk", "webscm_url": "http://git.dpdk.org/dpdk", "list_archive_url": "https://inbox.dpdk.org/dev", "list_archive_url_format": "https://inbox.dpdk.org/dev/{}", "commit_url_format": "" }, "msgid": "<20230717110709.39220-5-juraj.linkes@pantheon.tech>", "list_archive_url": "https://inbox.dpdk.org/dev/20230717110709.39220-5-juraj.linkes@pantheon.tech", "date": "2023-07-17T11:07:07", "name": "[v2,4/6] dts: add python remote interactive shell", "commit_ref": null, "pull_url": null, "state": "superseded", "archived": true, "hash": "3de83e3250a404e737546579791e28255390749c", "submitter": { "id": 1626, "url": "http://patchwork.dpdk.org/api/people/1626/?format=api", "name": "Juraj Linkeš", "email": "juraj.linkes@pantheon.tech" }, "delegate": { "id": 1, "url": "http://patchwork.dpdk.org/api/users/1/?format=api", "username": "tmonjalo", "first_name": "Thomas", "last_name": "Monjalon", "email": "thomas@monjalon.net" }, "mbox": "http://patchwork.dpdk.org/project/dpdk/patch/20230717110709.39220-5-juraj.linkes@pantheon.tech/mbox/", "series": [ { "id": 28953, "url": "http://patchwork.dpdk.org/api/series/28953/?format=api", "web_url": "http://patchwork.dpdk.org/project/dpdk/list/?series=28953", "date": "2023-07-17T11:07:03", "name": "dts: tg abstractions and scapy tg", "version": 2, "mbox": "http://patchwork.dpdk.org/series/28953/mbox/" } ], "comments": "http://patchwork.dpdk.org/api/patches/129575/comments/", "check": "success", "checks": "http://patchwork.dpdk.org/api/patches/129575/checks/", "tags": {}, "related": [], "headers": { "Return-Path": "<dev-bounces@dpdk.org>", "X-Original-To": "patchwork@inbox.dpdk.org", "Delivered-To": "patchwork@inbox.dpdk.org", "Received": [ "from mails.dpdk.org (mails.dpdk.org [217.70.189.124])\n\tby inbox.dpdk.org (Postfix) with ESMTP id 1AB0342E9B;\n\tMon, 17 Jul 2023 13:07:50 +0200 (CEST)", "from mails.dpdk.org (localhost [127.0.0.1])\n\tby mails.dpdk.org (Postfix) with ESMTP id A6A5742D52;\n\tMon, 17 Jul 2023 13:07:21 +0200 (CEST)", "from mail-ej1-f44.google.com (mail-ej1-f44.google.com\n [209.85.218.44]) by mails.dpdk.org (Postfix) with ESMTP id 7780942D38\n for <dev@dpdk.org>; Mon, 17 Jul 2023 13:07:18 +0200 (CEST)", "by mail-ej1-f44.google.com with SMTP id\n a640c23a62f3a-9939fbb7191so897994066b.0\n for <dev@dpdk.org>; Mon, 17 Jul 2023 04:07:18 -0700 (PDT)", "from jlinkes-PT-Latitude-5530.. (ip-46.34.239.87.o2inet.sk.\n [46.34.239.87]) by smtp.gmail.com with ESMTPSA id\n s21-20020a170906355500b0098de7d28c34sm8995051eja.193.2023.07.17.04.07.16\n (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n Mon, 17 Jul 2023 04:07:17 -0700 (PDT)" ], "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=pantheon.tech; s=google; t=1689592038; x=1692184038;\n h=content-transfer-encoding:mime-version:references:in-reply-to\n :message-id:date:subject:cc:to:from:from:to:cc:subject:date\n :message-id:reply-to;\n bh=WurjEEDhKXmw0OmdqHjUU44DZFn2qClO/dDEcstQhXI=;\n b=qKoGbUsV3NeQd2sh3cnZDdVWIaSOB2pzVGNtClAXoLQBDeBtUMtXWOvygK46rmTnkF\n GjNsE6IpYExXqp0/6PxDLfsELhSPYgpzCaQkTig7FEkxrpnPakQj2HSbawLO4bc3Wj8G\n SKgmU6RgjxZ1GRNPKiLaHG9ygy93XKyhdwqmXs5jxi0mHkXvPDoXC5HF1vO87agzQ58a\n Jphnt9p5B1V1e4mS52/BDSjhFcThzb9b84wtTzvQyTVV/zBkOjnUDe8P/wur47zKmPHq\n 93WwBvinGf+dByhoz0DCGt/4zsd6hcYmTl5aibtEEvl6LAB5O9qfWX9m0C3973CsDgxT\n mbBQ==", "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20221208; t=1689592038; x=1692184038;\n h=content-transfer-encoding:mime-version:references:in-reply-to\n :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc\n :subject:date:message-id:reply-to;\n bh=WurjEEDhKXmw0OmdqHjUU44DZFn2qClO/dDEcstQhXI=;\n b=F5cUK50XaUTbfJzoHp3u80LYXUNmeslGbVAhiNmf0niZs6tY0NViArmv1f0dR+I4ft\n MkUktpYBju8rimhsqvhjece69Fw/Fp739ACEEVb/u4OWC2N7mfesaiHpitjWkMpqX0nK\n XsE5rG5U6iAFf15ggcPgHDhSui0wIgZlfZ7cEFwVv7MouuVg2D6xmMgvijEKgnoLX6Nk\n xckeFJYro1eqmfC8JrTWrXV80FdCFhIvigmp00YhwWzl3DkqDq4A+kw8LTaqjzvhMo9K\n 7QfA98lEHdIgmb+xVo4ElRru/CUHW+PG3Hv4cHcjAWKWOECf15svrs/N2Jg4vv8ihoRX\n gdJw==", "X-Gm-Message-State": "ABy/qLYfb3ueT+sPTeigUmdJKl9yt/QhhWCCiE/ETEhB4BF7/mMheQBk\n M7E75yfaSaSVxjoNp74UcO0lRg==", "X-Google-Smtp-Source": "\n APBJJlHGH4Jl+GwKNenLsV0RwFWq8pYiw4wCRD6W9QL8hZNe9ukxM7tOgjd6Y9PqWtljyRqCGJbOJA==", "X-Received": "by 2002:a17:906:73d1:b0:992:1005:928d with SMTP id\n n17-20020a17090673d100b009921005928dmr10654069ejl.8.1689592038090;\n Mon, 17 Jul 2023 04:07:18 -0700 (PDT)", "From": "=?utf-8?q?Juraj_Linke=C5=A1?= <juraj.linkes@pantheon.tech>", "To": "thomas@monjalon.net, Honnappa.Nagarahalli@arm.com, lijuan.tu@intel.com,\n jspewock@iol.unh.edu, probb@iol.unh.edu", "Cc": "dev@dpdk.org, =?utf-8?q?Juraj_Linke=C5=A1?= <juraj.linkes@pantheon.tech>", "Subject": "[PATCH v2 4/6] dts: add python remote interactive shell", "Date": "Mon, 17 Jul 2023 13:07:07 +0200", "Message-Id": "<20230717110709.39220-5-juraj.linkes@pantheon.tech>", "X-Mailer": "git-send-email 2.34.1", "In-Reply-To": "<20230717110709.39220-1-juraj.linkes@pantheon.tech>", "References": "<20230420093109.594704-1-juraj.linkes@pantheon.tech>\n <20230717110709.39220-1-juraj.linkes@pantheon.tech>", "MIME-Version": "1.0", "Content-Type": "text/plain; charset=UTF-8", "Content-Transfer-Encoding": "8bit", "X-BeenThere": "dev@dpdk.org", "X-Mailman-Version": "2.1.29", "Precedence": "list", "List-Id": "DPDK patches and discussions <dev.dpdk.org>", "List-Unsubscribe": "<https://mails.dpdk.org/options/dev>,\n <mailto:dev-request@dpdk.org?subject=unsubscribe>", "List-Archive": "<http://mails.dpdk.org/archives/dev/>", "List-Post": "<mailto:dev@dpdk.org>", "List-Help": "<mailto:dev-request@dpdk.org?subject=help>", "List-Subscribe": "<https://mails.dpdk.org/listinfo/dev>,\n <mailto:dev-request@dpdk.org?subject=subscribe>", "Errors-To": "dev-bounces@dpdk.org" }, "content": "The shell can be used to remotely run any Python code interactively.\n\nSigned-off-by: Juraj Linkeš <juraj.linkes@pantheon.tech>\n---\n dts/framework/config/__init__.py | 28 +-----------\n dts/framework/remote_session/__init__.py | 2 +-\n dts/framework/remote_session/os_session.py | 42 +++++++++---------\n .../remote/interactive_shell.py | 18 +++++---\n .../remote_session/remote/python_shell.py | 24 +++++++++++\n .../remote_session/remote/testpmd_shell.py | 33 +++-----------\n dts/framework/testbed_model/node.py | 35 ++++++++++++++-\n dts/framework/testbed_model/sut_node.py | 43 ++++++++-----------\n dts/tests/TestSuite_smoke_tests.py | 6 +--\n 9 files changed, 119 insertions(+), 112 deletions(-)\n create mode 100644 dts/framework/remote_session/remote/python_shell.py", "diff": "diff --git a/dts/framework/config/__init__.py b/dts/framework/config/__init__.py\nindex 72aa021b97..b5830f6301 100644\n--- a/dts/framework/config/__init__.py\n+++ b/dts/framework/config/__init__.py\n@@ -11,8 +11,7 @@\n import os.path\n import pathlib\n from dataclasses import dataclass\n-from enum import Enum, auto, unique\n-from pathlib import PurePath\n+from enum import auto, unique\n from typing import Any, TypedDict, Union\n \n import warlock # type: ignore\n@@ -331,28 +330,3 @@ def load_config() -> Configuration:\n \n \n CONFIGURATION = load_config()\n-\n-\n-@unique\n-class InteractiveApp(Enum):\n- \"\"\"An enum that represents different supported interactive applications.\n-\n- The values in this enum must all be set to objects that have a key called\n- \"default_path\" where \"default_path\" represents a PurePath object for the path\n- to the application. This default path will be passed into the handler class\n- for the application so that it can start the application.\n- \"\"\"\n-\n- testpmd = {\"default_path\": PurePath(\"app\", \"dpdk-testpmd\")}\n-\n- @property\n- def path(self) -> PurePath:\n- \"\"\"Default path of the application.\n-\n- For DPDK apps, this will be appended to the DPDK build directory.\n- \"\"\"\n- return self.value[\"default_path\"]\n-\n- @path.setter\n- def path(self, path: PurePath) -> None:\n- self.value[\"default_path\"] = path\ndiff --git a/dts/framework/remote_session/__init__.py b/dts/framework/remote_session/__init__.py\nindex 2c408c2557..1155dd8318 100644\n--- a/dts/framework/remote_session/__init__.py\n+++ b/dts/framework/remote_session/__init__.py\n@@ -17,7 +17,7 @@\n from framework.logger import DTSLOG\n \n from .linux_session import LinuxSession\n-from .os_session import OSSession\n+from .os_session import InteractiveShellType, OSSession\n from .remote import (\n CommandResult,\n InteractiveRemoteSession,\ndiff --git a/dts/framework/remote_session/os_session.py b/dts/framework/remote_session/os_session.py\nindex 633d06eb5d..c17a17a267 100644\n--- a/dts/framework/remote_session/os_session.py\n+++ b/dts/framework/remote_session/os_session.py\n@@ -5,11 +5,11 @@\n from abc import ABC, abstractmethod\n from collections.abc import Iterable\n from pathlib import PurePath\n-from typing import Union\n+from typing import Type, TypeVar\n \n-from framework.config import Architecture, InteractiveApp, NodeConfiguration, NodeInfo\n+from framework.config import Architecture, NodeConfiguration, NodeInfo\n from framework.logger import DTSLOG\n-from framework.remote_session.remote import InteractiveShell, TestPmdShell\n+from framework.remote_session.remote import InteractiveShell\n from framework.settings import SETTINGS\n from framework.testbed_model import LogicalCore\n from framework.testbed_model.hw.port import Port\n@@ -23,6 +23,8 @@\n create_remote_session,\n )\n \n+InteractiveShellType = TypeVar(\"InteractiveShellType\", bound=InteractiveShell)\n+\n \n class OSSession(ABC):\n \"\"\"\n@@ -81,30 +83,26 @@ def send_command(\n \n def create_interactive_shell(\n self,\n- shell_type: InteractiveApp,\n- path_to_app: PurePath,\n+ shell_cls: Type[InteractiveShellType],\n eal_parameters: str,\n timeout: float,\n- ) -> Union[InteractiveShell, TestPmdShell]:\n+ privileged: bool,\n+ ) -> InteractiveShellType:\n \"\"\"\n See \"create_interactive_shell\" in SutNode\n \"\"\"\n- match (shell_type):\n- case InteractiveApp.testpmd:\n- return TestPmdShell(\n- self.interactive_session.session,\n- self._logger,\n- path_to_app,\n- timeout=timeout,\n- eal_flags=eal_parameters,\n- )\n- case _:\n- self._logger.info(\n- f\"Unhandled app type {shell_type.name}, defaulting to shell.\"\n- )\n- return InteractiveShell(\n- self.interactive_session.session, self._logger, path_to_app, timeout\n- )\n+ app_command = (\n+ self._get_privileged_command(str(shell_cls.path))\n+ if privileged\n+ else str(shell_cls.path)\n+ )\n+ return shell_cls(\n+ self.interactive_session.session,\n+ self._logger,\n+ app_command,\n+ eal_parameters,\n+ timeout,\n+ )\n \n @abstractmethod\n def _get_privileged_command(self, command: str) -> str:\ndiff --git a/dts/framework/remote_session/remote/interactive_shell.py b/dts/framework/remote_session/remote/interactive_shell.py\nindex 2cabe9edca..1211d91aa9 100644\n--- a/dts/framework/remote_session/remote/interactive_shell.py\n+++ b/dts/framework/remote_session/remote/interactive_shell.py\n@@ -17,13 +17,18 @@ class InteractiveShell:\n _ssh_channel: Channel\n _logger: DTSLOG\n _timeout: float\n- _path_to_app: PurePath\n+ _startup_command: str\n+ _app_args: str\n+ _default_prompt: str = \"\"\n+ path: PurePath\n+ dpdk_app: bool = False\n \n def __init__(\n self,\n interactive_session: SSHClient,\n logger: DTSLOG,\n- path_to_app: PurePath,\n+ startup_command: str,\n+ app_args: str = \"\",\n timeout: float = SETTINGS.timeout,\n ) -> None:\n self._interactive_session = interactive_session\n@@ -34,16 +39,19 @@ def __init__(\n self._ssh_channel.set_combine_stderr(True) # combines stdout and stderr streams\n self._logger = logger\n self._timeout = timeout\n- self._path_to_app = path_to_app\n+ self._startup_command = startup_command\n+ self._app_args = app_args\n self._start_application()\n \n def _start_application(self) -> None:\n- \"\"\"Starts a new interactive application based on _path_to_app.\n+ \"\"\"Starts a new interactive application based on _startup_command.\n \n This method is often overridden by subclasses as their process for\n starting may look different.\n \"\"\"\n- self.send_command_get_output(f\"{self._path_to_app}\", \"\")\n+ self.send_command_get_output(\n+ f\"{self._startup_command} {self._app_args}\", self._default_prompt\n+ )\n \n def send_command_get_output(self, command: str, prompt: str) -> str:\n \"\"\"Send a command and get all output before the expected ending string.\ndiff --git a/dts/framework/remote_session/remote/python_shell.py b/dts/framework/remote_session/remote/python_shell.py\nnew file mode 100644\nindex 0000000000..66d5787c86\n--- /dev/null\n+++ b/dts/framework/remote_session/remote/python_shell.py\n@@ -0,0 +1,24 @@\n+# SPDX-License-Identifier: BSD-3-Clause\n+# Copyright(c) 2023 PANTHEON.tech s.r.o.\n+\n+from pathlib import PurePath\n+\n+from .interactive_shell import InteractiveShell\n+\n+\n+class PythonShell(InteractiveShell):\n+ _startup_command: str\n+ _default_prompt: str = \">>>\"\n+ path: PurePath = PurePath(\"python3\")\n+\n+ def _start_application(self) -> None:\n+ self._startup_command = f\"{self._startup_command}\\n\"\n+ super()._start_application()\n+\n+ def send_command(self, command: str, prompt: str = _default_prompt) -> str:\n+ \"\"\"Specific way of handling the command for python\n+\n+ An extra newline character is consumed in order to force the current line into\n+ the stdout buffer.\n+ \"\"\"\n+ return self.send_command_get_output(f\"{command}\\n\", prompt)\ndiff --git a/dts/framework/remote_session/remote/testpmd_shell.py b/dts/framework/remote_session/remote/testpmd_shell.py\nindex c0261c00f6..1288cfd10c 100644\n--- a/dts/framework/remote_session/remote/testpmd_shell.py\n+++ b/dts/framework/remote_session/remote/testpmd_shell.py\n@@ -3,11 +3,6 @@\n \n from pathlib import PurePath\n \n-from paramiko import SSHClient # type: ignore\n-\n-from framework.logger import DTSLOG\n-from framework.settings import SETTINGS\n-\n from .interactive_shell import InteractiveShell\n \n \n@@ -22,34 +17,18 @@ def __str__(self) -> str:\n \n \n class TestPmdShell(InteractiveShell):\n- expected_prompt: str = \"testpmd>\"\n+ path: PurePath = PurePath(\"app\", \"dpdk-testpmd\")\n+ dpdk_app: bool = True\n+ _default_prompt: str = \"testpmd>\"\n _eal_flags: str\n \n- def __init__(\n- self,\n- interactive_session: SSHClient,\n- logger: DTSLOG,\n- path_to_testpmd: PurePath,\n- eal_flags: str,\n- timeout: float = SETTINGS.timeout,\n- ) -> None:\n- \"\"\"Initializes an interactive testpmd session using specified parameters.\"\"\"\n- self._eal_flags = eal_flags\n-\n- super(TestPmdShell, self).__init__(\n- interactive_session,\n- logger=logger,\n- path_to_app=path_to_testpmd,\n- timeout=timeout,\n- )\n-\n def _start_application(self) -> None:\n- \"\"\"Starts a new interactive testpmd shell using _path_to_app.\"\"\"\n+ \"\"\"Starts a new interactive testpmd shell using _startup_command.\"\"\"\n self.send_command(\n- f\"{self._path_to_app} {self._eal_flags} -- -i\",\n+ f\"{self._startup_command} {self._app_args} -- -i\",\n )\n \n- def send_command(self, command: str, prompt: str = expected_prompt) -> str:\n+ def send_command(self, command: str, prompt: str = _default_prompt) -> str:\n \"\"\"Specific way of handling the command for testpmd\n \n An extra newline character is consumed in order to force the current line into\ndiff --git a/dts/framework/testbed_model/node.py b/dts/framework/testbed_model/node.py\nindex e09931cedf..f70e4d5ce6 100644\n--- a/dts/framework/testbed_model/node.py\n+++ b/dts/framework/testbed_model/node.py\n@@ -7,7 +7,7 @@\n A node is a generic host that DTS connects to and manages.\n \"\"\"\n \n-from typing import Any, Callable\n+from typing import Any, Callable, Type\n \n from framework.config import (\n BuildTargetConfiguration,\n@@ -15,7 +15,7 @@\n NodeConfiguration,\n )\n from framework.logger import DTSLOG, getLogger\n-from framework.remote_session import OSSession, create_session\n+from framework.remote_session import InteractiveShellType, OSSession, create_session\n from framework.settings import SETTINGS\n \n from .hw import (\n@@ -138,6 +138,37 @@ def create_session(self, name: str) -> OSSession:\n self._other_sessions.append(connection)\n return connection\n \n+ def create_interactive_shell(\n+ self,\n+ shell_cls: Type[InteractiveShellType],\n+ timeout: float = SETTINGS.timeout,\n+ privileged: bool = False,\n+ app_args: str = \"\",\n+ ) -> InteractiveShellType:\n+ \"\"\"Create a handler for an interactive session.\n+\n+ Instantiate shell_cls according to the remote OS specifics.\n+\n+ Args:\n+ shell_cls: The class of the shell.\n+ timeout: Timeout for reading output from the SSH channel. If you are\n+ reading from the buffer and don't receive any data within the timeout\n+ it will throw an error.\n+ privileged: Whether to run the shell with administrative privileges.\n+ app_args: The arguments to be passed to the application.\n+ Returns:\n+ Instance of the desired interactive application.\n+ \"\"\"\n+ if not shell_cls.dpdk_app:\n+ shell_cls.path = self.main_session.join_remote_path(shell_cls.path)\n+\n+ return self.main_session.create_interactive_shell(\n+ shell_cls,\n+ app_args,\n+ timeout,\n+ privileged,\n+ )\n+\n def filter_lcores(\n self,\n filter_specifier: LogicalCoreCount | LogicalCoreList,\ndiff --git a/dts/framework/testbed_model/sut_node.py b/dts/framework/testbed_model/sut_node.py\nindex bcad364435..f0b017a383 100644\n--- a/dts/framework/testbed_model/sut_node.py\n+++ b/dts/framework/testbed_model/sut_node.py\n@@ -7,21 +7,15 @@\n import tarfile\n import time\n from pathlib import PurePath\n-from typing import Union\n+from typing import Type\n \n from framework.config import (\n BuildTargetConfiguration,\n BuildTargetInfo,\n- InteractiveApp,\n NodeInfo,\n SutNodeConfiguration,\n )\n-from framework.remote_session import (\n- CommandResult,\n- InteractiveShell,\n- OSSession,\n- TestPmdShell,\n-)\n+from framework.remote_session import CommandResult, InteractiveShellType, OSSession\n from framework.settings import SETTINGS\n from framework.utils import MesonArgs\n \n@@ -359,23 +353,24 @@ def run_dpdk_app(\n \n def create_interactive_shell(\n self,\n- shell_type: InteractiveApp,\n+ shell_cls: Type[InteractiveShellType],\n timeout: float = SETTINGS.timeout,\n- eal_parameters: EalParameters | None = None,\n- ) -> Union[InteractiveShell, TestPmdShell]:\n- \"\"\"Create a handler for an interactive session.\n+ privileged: bool = False,\n+ eal_parameters: EalParameters | str | None = None,\n+ ) -> InteractiveShellType:\n+ \"\"\"Factory method for creating a handler for an interactive session.\n \n- This method is a factory that calls a method in OSSession to create shells for\n- different DPDK applications.\n+ Instantiate shell_cls according to the remote OS specifics.\n \n Args:\n- shell_type: Enum value representing the desired application.\n+ shell_cls: The class of the shell.\n timeout: Timeout for reading output from the SSH channel. If you are\n reading from the buffer and don't receive any data within the timeout\n it will throw an error.\n+ privileged: Whether to run the shell with administrative privileges.\n eal_parameters: List of EAL parameters to use to launch the app. If this\n- isn't provided, it will default to calling create_eal_parameters().\n- This is ignored for base \"shell\" types.\n+ isn't provided or an empty string is passed, it will default to calling\n+ create_eal_parameters().\n Returns:\n Instance of the desired interactive application.\n \"\"\"\n@@ -383,11 +378,11 @@ def create_interactive_shell(\n eal_parameters = self.create_eal_parameters()\n \n # We need to append the build directory for DPDK apps\n- shell_type.path = self.remote_dpdk_build_dir.joinpath(shell_type.path)\n- default_path = self.main_session.join_remote_path(shell_type.path)\n- return self.main_session.create_interactive_shell(\n- shell_type,\n- default_path,\n- str(eal_parameters),\n- timeout,\n+ if shell_cls.dpdk_app:\n+ shell_cls.path = self.main_session.join_remote_path(\n+ self.remote_dpdk_build_dir, shell_cls.path\n+ )\n+\n+ return super().create_interactive_shell(\n+ shell_cls, timeout, privileged, str(eal_parameters)\n )\ndiff --git a/dts/tests/TestSuite_smoke_tests.py b/dts/tests/TestSuite_smoke_tests.py\nindex 9cf547205f..e73d015bc7 100644\n--- a/dts/tests/TestSuite_smoke_tests.py\n+++ b/dts/tests/TestSuite_smoke_tests.py\n@@ -3,7 +3,7 @@\n \n import re\n \n-from framework.config import InteractiveApp, PortConfig\n+from framework.config import PortConfig\n from framework.remote_session import TestPmdDevice, TestPmdShell\n from framework.settings import SETTINGS\n from framework.test_suite import TestSuite\n@@ -67,9 +67,7 @@ def test_devices_listed_in_testpmd(self) -> None:\n Test:\n Uses testpmd driver to verify that devices have been found by testpmd.\n \"\"\"\n- testpmd_driver = self.sut_node.create_interactive_shell(InteractiveApp.testpmd)\n- # We know it should always be a TestPmdShell but mypy doesn't\n- assert isinstance(testpmd_driver, TestPmdShell)\n+ testpmd_driver = self.sut_node.create_interactive_shell(TestPmdShell)\n dev_list: list[TestPmdDevice] = testpmd_driver.get_devices()\n for nic in self.nics_in_node:\n self.verify(\n", "prefixes": [ "v2", "4/6" ] }{ "id": 129575, "url": "