get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/129575/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 129575,
    "url": "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"
    ]
}