From mboxrd@z Thu Jan  1 00:00:00 1970
Return-Path: <dev-bounces@dpdk.org>
Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124])
	by inbox.dpdk.org (Postfix) with ESMTP id 2AE74433AC;
	Thu, 23 Nov 2023 16:15:29 +0100 (CET)
Received: from mails.dpdk.org (localhost [127.0.0.1])
	by mails.dpdk.org (Postfix) with ESMTP id 4DDE243067;
	Thu, 23 Nov 2023 16:14:09 +0100 (CET)
Received: from mail-wm1-f42.google.com (mail-wm1-f42.google.com
 [209.85.128.42]) by mails.dpdk.org (Postfix) with ESMTP id E27E942FB9
 for <dev@dpdk.org>; Thu, 23 Nov 2023 16:14:01 +0100 (CET)
Received: by mail-wm1-f42.google.com with SMTP id
 5b1f17b1804b1-4083cd3917eso6267285e9.3
 for <dev@dpdk.org>; Thu, 23 Nov 2023 07:14:01 -0800 (PST)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
 d=pantheon.tech; s=google; t=1700752441; x=1701357241; darn=dpdk.org;
 h=content-transfer-encoding:mime-version:references:in-reply-to
 :message-id:date:subject:cc:to:from:from:to:cc:subject:date
 :message-id:reply-to;
 bh=P0Nblz39VHMsl/sn7WugqAoH0RlFKnbia6lvcHGMAig=;
 b=TIxmYH2jyurT+oXl2/hsFSAIGm8C1jH5wePTEwSRUj916U5XsAymhP39gEtEnglQkw
 j4YWMq44lCFtdiXiVQWFhqAEdFpsb2ZfHtg7LiPFh2W5KDUzsAUel6HK0uTyqaEM36+J
 4pvAyaL3wETpm6GqRUw/q2J6yqV2aui3I285HM7myRGnUpsgS5vdNZmtFaw1QJwWXhh/
 mH0b5pxduUy5ERyu4DSeWuYCqzGeGLd2cIv4xl5WtnIIc6oTij6XzuvMgeyX95Iqwwkj
 mP8/2Ff9/1sbygDKgSgCXqK7xuTWmVj+eoqIxGciiOr0+SbFzJIINYftTO0hIv3lusqx
 PDww==
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
 d=1e100.net; s=20230601; t=1700752441; x=1701357241;
 h=content-transfer-encoding:mime-version:references:in-reply-to
 :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc
 :subject:date:message-id:reply-to;
 bh=P0Nblz39VHMsl/sn7WugqAoH0RlFKnbia6lvcHGMAig=;
 b=v1gGFLx9JYKe/+QuxJ/HIt5D7md+9IaQklHvwRppdyX+GUMjJnoxwFrtElnbfr6TbK
 vp/EssQZXPtz8AoiCOcKQuEuawnlkecyyiTaYXcH7bHC1e9Gv9I5bN4XzcZ4gZBYC7Gu
 MBNepuTE93iEW/AY9MBUfzjat6jqMHqpfF9O2L7PXiSdhISSPzM33o93gD/2dmKEoiit
 80WYLM/OScUT7Lv1/QFFR2w6Vdk5zwTSE1CjAkrbguLAUstwo6gxVsMhogt/cezY3bvQ
 eUFzDa9Ss+Z1Xm4Q/V8YbT9FRFeKxKSJxhgmqC2a7mwY8qhDkXSSz/QfA0ZCVwP+873s
 lmRQ==
X-Gm-Message-State: AOJu0YxiMyRrEeGJNu5o8xdQiodw5xYDIZdfjTuCyS7wc0mpwMtbIzPB
 szAEXJZM3Cv4k7BpAOEzj/NmFw==
X-Google-Smtp-Source: AGHT+IFwjiDvbuAMRWMP0KrUGm4xHQRIZS+4fwy2rPx9QPvIOwL2WXze0wh+ygvlD3vBTnxMXm26DA==
X-Received: by 2002:adf:a447:0:b0:32f:aaff:96dd with SMTP id
 e7-20020adfa447000000b0032faaff96ddmr3462509wra.4.1700752440614; 
 Thu, 23 Nov 2023 07:14:00 -0800 (PST)
Received: from jlinkes-PT-Latitude-5530.. ([84.245.121.10])
 by smtp.gmail.com with ESMTPSA id
 q4-20020adfea04000000b003296b488961sm1870143wrm.31.2023.11.23.07.13.59
 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);
 Thu, 23 Nov 2023 07:14:00 -0800 (PST)
From: =?UTF-8?q?Juraj=20Linke=C5=A1?= <juraj.linkes@pantheon.tech>
To: thomas@monjalon.net, Honnappa.Nagarahalli@arm.com, jspewock@iol.unh.edu,
 probb@iol.unh.edu, paul.szczepanek@arm.com, yoan.picchi@foss.arm.com,
 Luca.Vizzarro@arm.com
Cc: dev@dpdk.org, =?UTF-8?q?Juraj=20Linke=C5=A1?= <juraj.linkes@pantheon.tech>
Subject: [PATCH v8 11/21] dts: remote session docstring update
Date: Thu, 23 Nov 2023 16:13:34 +0100
Message-Id: <20231123151344.162812-12-juraj.linkes@pantheon.tech>
X-Mailer: git-send-email 2.34.1
In-Reply-To: <20231123151344.162812-1-juraj.linkes@pantheon.tech>
References: <20231115130959.39420-1-juraj.linkes@pantheon.tech>
 <20231123151344.162812-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>,
 <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>,
 <mailto:dev-request@dpdk.org?subject=subscribe>
Errors-To: dev-bounces@dpdk.org

Format according to the Google format and PEP257, with slight
deviations.

Signed-off-by: Juraj Linkeš <juraj.linkes@pantheon.tech>
---
 dts/framework/remote_session/__init__.py      |  39 +++++-
 .../remote_session/remote_session.py          | 130 +++++++++++++-----
 dts/framework/remote_session/ssh_session.py   |  16 +--
 3 files changed, 137 insertions(+), 48 deletions(-)

diff --git a/dts/framework/remote_session/__init__.py b/dts/framework/remote_session/__init__.py
index 5e7ddb2b05..51a01d6b5e 100644
--- a/dts/framework/remote_session/__init__.py
+++ b/dts/framework/remote_session/__init__.py
@@ -2,12 +2,14 @@
 # Copyright(c) 2023 PANTHEON.tech s.r.o.
 # Copyright(c) 2023 University of New Hampshire
 
-"""
-The package provides modules for managing remote connections to a remote host (node),
-differentiated by OS.
-The package provides a factory function, create_session, that returns the appropriate
-remote connection based on the passed configuration. The differences are in the
-underlying transport protocol (e.g. SSH) and remote OS (e.g. Linux).
+"""Remote interactive and non-interactive sessions.
+
+This package provides modules for managing remote connections to a remote host (node).
+
+The non-interactive sessions send commands and return their output and exit code.
+
+The interactive sessions open an interactive shell which is continuously open,
+allowing it to send and receive data within that particular shell.
 """
 
 # pylama:ignore=W0611
@@ -26,10 +28,35 @@
 def create_remote_session(
     node_config: NodeConfiguration, name: str, logger: DTSLOG
 ) -> RemoteSession:
+    """Factory for non-interactive remote sessions.
+
+    The function returns an SSH session, but will be extended if support
+    for other protocols is added.
+
+    Args:
+        node_config: The test run configuration of the node to connect to.
+        name: The name of the session.
+        logger: The logger instance this session will use.
+
+    Returns:
+        The SSH remote session.
+    """
     return SSHSession(node_config, name, logger)
 
 
 def create_interactive_session(
     node_config: NodeConfiguration, logger: DTSLOG
 ) -> InteractiveRemoteSession:
+    """Factory for interactive remote sessions.
+
+    The function returns an interactive SSH session, but will be extended if support
+    for other protocols is added.
+
+    Args:
+        node_config: The test run configuration of the node to connect to.
+        logger: The logger instance this session will use.
+
+    Returns:
+        The interactive SSH remote session.
+    """
     return InteractiveRemoteSession(node_config, logger)
diff --git a/dts/framework/remote_session/remote_session.py b/dts/framework/remote_session/remote_session.py
index 719f7d1ef7..2059f9a981 100644
--- a/dts/framework/remote_session/remote_session.py
+++ b/dts/framework/remote_session/remote_session.py
@@ -3,6 +3,13 @@
 # Copyright(c) 2022-2023 PANTHEON.tech s.r.o.
 # Copyright(c) 2022-2023 University of New Hampshire
 
+"""Base remote session.
+
+This module contains the abstract base class for remote sessions and defines
+the structure of the result of a command execution.
+"""
+
+
 import dataclasses
 from abc import ABC, abstractmethod
 from pathlib import PurePath
@@ -15,8 +22,14 @@
 
 @dataclasses.dataclass(slots=True, frozen=True)
 class CommandResult:
-    """
-    The result of remote execution of a command.
+    """The result of remote execution of a command.
+
+    Attributes:
+        name: The name of the session that executed the command.
+        command: The executed command.
+        stdout: The standard output the command produced.
+        stderr: The standard error output the command produced.
+        return_code: The return code the command exited with.
     """
 
     name: str
@@ -26,6 +39,7 @@ class CommandResult:
     return_code: int
 
     def __str__(self) -> str:
+        """Format the command outputs."""
         return (
             f"stdout: '{self.stdout}'\n"
             f"stderr: '{self.stderr}'\n"
@@ -34,13 +48,24 @@ def __str__(self) -> str:
 
 
 class RemoteSession(ABC):
-    """
-    The base class for defining which methods must be implemented in order to connect
-    to a remote host (node) and maintain a remote session. The derived classes are
-    supposed to implement/use some underlying transport protocol (e.g. SSH) to
-    implement the methods. On top of that, it provides some basic services common to
-    all derived classes, such as keeping history and logging what's being executed
-    on the remote node.
+    """Non-interactive remote session.
+
+    The abstract methods must be implemented in order to connect to a remote host (node)
+    and maintain a remote session.
+    The subclasses must use (or implement) some underlying transport protocol (e.g. SSH)
+    to implement the methods. On top of that, it provides some basic services common to all
+    subclasses, such as keeping history and logging what's being executed on the remote node.
+
+    Attributes:
+        name: The name of the session.
+        hostname: The node's hostname. Could be an IP (possibly with port, separated by a colon)
+            or a domain name.
+        ip: The IP address of the node or a domain name, whichever was used in `hostname`.
+        port: The port of the node, if given in `hostname`.
+        username: The username used in the connection.
+        password: The password used in the connection. Most frequently empty,
+            as the use of passwords is discouraged.
+        history: The executed commands during this session.
     """
 
     name: str
@@ -59,6 +84,16 @@ def __init__(
         session_name: str,
         logger: DTSLOG,
     ):
+        """Connect to the node during initialization.
+
+        Args:
+            node_config: The test run configuration of the node to connect to.
+            session_name: The name of the session.
+            logger: The logger instance this session will use.
+
+        Raises:
+            SSHConnectionError: If the connection to the node was not successful.
+        """
         self._node_config = node_config
 
         self.name = session_name
@@ -79,8 +114,13 @@ def __init__(
 
     @abstractmethod
     def _connect(self) -> None:
-        """
-        Create connection to assigned node.
+        """Create a connection to the node.
+
+        The implementation must assign the established session to self.session.
+
+        The implementation must except all exceptions and convert them to an SSHConnectionError.
+
+        The implementation may optionally implement retry attempts.
         """
 
     def send_command(
@@ -90,11 +130,24 @@ def send_command(
         verify: bool = False,
         env: dict | None = None,
     ) -> CommandResult:
-        """
-        Send a command to the connected node using optional env vars
-        and return CommandResult.
-        If verify is True, check the return code of the executed command
-        and raise a RemoteCommandExecutionError if the command failed.
+        """Send `command` to the connected node.
+
+        The :option:`--timeout` command line argument and the :envvar:`DTS_TIMEOUT`
+        environment variable configure the timeout of command execution.
+
+        Args:
+            command: The command to execute.
+            timeout: Wait at most this long in seconds for `command` execution to complete.
+            verify: If :data:`True`, will check the exit code of `command`.
+            env: A dictionary with environment variables to be used with `command` execution.
+
+        Raises:
+            SSHSessionDeadError: If the session isn't alive when sending `command`.
+            SSHTimeoutError: If `command` execution timed out.
+            RemoteCommandExecutionError: If verify is :data:`True` and `command` execution failed.
+
+        Returns:
+            The output of the command along with the return code.
         """
         self._logger.info(f"Sending: '{command}'" + (f" with env vars: '{env}'" if env else ""))
         result = self._send_command(command, timeout, env)
@@ -111,29 +164,38 @@ def send_command(
 
     @abstractmethod
     def _send_command(self, command: str, timeout: float, env: dict | None) -> CommandResult:
-        """
-        Use the underlying protocol to execute the command using optional env vars
-        and return CommandResult.
+        """Send a command to the connected node.
+
+        The implementation must execute the command remotely with `env` environment variables
+        and return the result.
+
+        The implementation must except all exceptions and raise:
+
+            * SSHSessionDeadError if the session is not alive,
+            * SSHTimeoutError if the command execution times out.
         """
 
     def close(self, force: bool = False) -> None:
-        """
-        Close the remote session and free all used resources.
+        """Close the remote session and free all used resources.
+
+        Args:
+            force: Force the closure of the connection. This may not clean up all resources.
         """
         self._logger.logger_exit()
         self._close(force)
 
     @abstractmethod
     def _close(self, force: bool = False) -> None:
-        """
-        Execute protocol specific steps needed to close the session properly.
+        """Protocol specific steps needed to close the session properly.
+
+        Args:
+            force: Force the closure of the connection. This may not clean up all resources.
+                This doesn't have to be implemented in the overloaded method.
         """
 
     @abstractmethod
     def is_alive(self) -> bool:
-        """
-        Check whether the remote session is still responding.
-        """
+        """Check whether the remote session is still responding."""
 
     @abstractmethod
     def copy_from(
@@ -143,12 +205,12 @@ def copy_from(
     ) -> None:
         """Copy a file from the remote Node to the local filesystem.
 
-        Copy source_file from the remote Node associated with this remote
-        session to destination_file on the local filesystem.
+        Copy `source_file` from the remote Node associated with this remote session
+        to `destination_file` on the local filesystem.
 
         Args:
-            source_file: the file on the remote Node.
-            destination_file: a file or directory path on the local filesystem.
+            source_file: The file on the remote Node.
+            destination_file: A file or directory path on the local filesystem.
         """
 
     @abstractmethod
@@ -159,10 +221,10 @@ def copy_to(
     ) -> None:
         """Copy a file from local filesystem to the remote Node.
 
-        Copy source_file from local filesystem to destination_file
-        on the remote Node associated with this remote session.
+        Copy `source_file` from local filesystem to `destination_file` on the remote Node
+        associated with this remote session.
 
         Args:
-            source_file: the file on the local filesystem.
-            destination_file: a file or directory path on the remote Node.
+            source_file: The file on the local filesystem.
+            destination_file: A file or directory path on the remote Node.
         """
diff --git a/dts/framework/remote_session/ssh_session.py b/dts/framework/remote_session/ssh_session.py
index a467033a13..782220092c 100644
--- a/dts/framework/remote_session/ssh_session.py
+++ b/dts/framework/remote_session/ssh_session.py
@@ -1,6 +1,8 @@
 # SPDX-License-Identifier: BSD-3-Clause
 # Copyright(c) 2023 PANTHEON.tech s.r.o.
 
+"""SSH remote session."""
+
 import socket
 import traceback
 from pathlib import PurePath
@@ -26,13 +28,8 @@
 class SSHSession(RemoteSession):
     """A persistent SSH connection to a remote Node.
 
-    The connection is implemented with the Fabric Python library.
-
-    Args:
-        node_config: The configuration of the Node to connect to.
-        session_name: The name of the session.
-        logger: The logger used for logging.
-            This should be passed from the parent OSSession.
+    The connection is implemented with
+    `the Fabric Python library <https://docs.fabfile.org/en/latest/>`_.
 
     Attributes:
         session: The underlying Fabric SSH connection.
@@ -78,6 +75,7 @@ def _connect(self) -> None:
             raise SSHConnectionError(self.hostname, errors)
 
     def is_alive(self) -> bool:
+        """Overrides :meth:`~.remote_session.RemoteSession.is_alive`."""
         return self.session.is_connected
 
     def _send_command(self, command: str, timeout: float, env: dict | None) -> CommandResult:
@@ -85,7 +83,7 @@ def _send_command(self, command: str, timeout: float, env: dict | None) -> Comma
 
         Args:
             command: The command to execute.
-            timeout: Wait at most this many seconds for the execution to complete.
+            timeout: Wait at most this long in seconds for the command execution to complete.
             env: Extra environment variables that will be used in command execution.
 
         Raises:
@@ -110,6 +108,7 @@ def copy_from(
         source_file: str | PurePath,
         destination_file: str | PurePath,
     ) -> None:
+        """Overrides :meth:`~.remote_session.RemoteSession.copy_from`."""
         self.session.get(str(destination_file), str(source_file))
 
     def copy_to(
@@ -117,6 +116,7 @@ def copy_to(
         source_file: str | PurePath,
         destination_file: str | PurePath,
     ) -> None:
+        """Overrides :meth:`~.remote_session.RemoteSession.copy_to`."""
         self.session.put(str(source_file), str(destination_file))
 
     def _close(self, force: bool = False) -> None:
-- 
2.34.1