diff --git a/doc/connection.rst b/doc/connection.rst index fdd35ce..9c19c42 100644 --- a/doc/connection.rst +++ b/doc/connection.rst @@ -40,7 +40,7 @@ class that implements the following methods. :param timeout: timeout (in seconds) for the transfer; if the transfer does not complete within this period, an exception will be raised. -.. method:: execute(self, command, timeout=None, check_exit_code=False, as_root=False) +.. method:: execute(self, command, timeout=None, check_exit_code=False, as_root=False, will_succeed=False) Execute the specified command on the connected device and return its output. @@ -53,6 +53,11 @@ class that implements the following methods. raised if it is not ``0``. :param as_root: The command will be executed as root. This will fail on unrooted connected devices. + :param will_succeed: The command is assumed to always succeed, unless there is + an issue in the environment like the loss of network connectivity. That + will make the method always raise an instance of a subclass of + :class:`DevlibTransientError' when the command fails, instead of a + :class:`DevlibStableError`. .. method:: background(self, command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, as_root=False) @@ -196,13 +201,12 @@ Connection Types :param host: Host on which the gem5 simulation is running - .. note:: Even thought the input parameter for the ``host`` - will be ignored, the gem5 simulation needs to on - the same host as the user as the user is - currently on, so if the host given as input - parameter is not the same as the actual host, a - ``TargetError`` will be raised to prevent - confusion. + .. note:: Even though the input parameter for the ``host`` + will be ignored, the gem5 simulation needs to be + on the same host the user is currently on, so if + the host given as input parameter is not the + same as the actual host, a ``TargetStableError`` + will be raised to prevent confusion. :param username: Username in the simulated system :param password: No password required in gem5 so does not need to be set diff --git a/doc/overview.rst b/doc/overview.rst index 83b33b0..35d3b18 100644 --- a/doc/overview.rst +++ b/doc/overview.rst @@ -82,8 +82,14 @@ safe side, it's a good idea to call this once at the beginning of your scripts. Command Execution ~~~~~~~~~~~~~~~~~ -There are several ways to execute a command on the target. In each case, a -:class:`TargetError` will be raised if something goes wrong. In each case, it is +There are several ways to execute a command on the target. In each case, an +instance of a subclass of :class:`TargetError` will be raised if something goes +wrong. When a transient error is encountered such as the loss of the network +connectivity, it will raise a :class:`TargetTransientError`. When the command +fails, it will raise a :class:`TargetStableError` unless the +``will_succeed=True`` parameter is specified, in which case a +:class:`TargetTransientError` will be raised since it is assumed that the +command cannot fail unless there is an environment issue. In each case, it is also possible to specify ``as_root=True`` if the specified command should be executed as root. @@ -216,6 +222,66 @@ executables_directory t.push('/local/path/to/assets.tar.gz', t.get_workpath('assets.tar.gz')) +Exceptions Handling +------------------- + +Devlib custom exceptions all derive from :class:`DevlibError`. Some exceptions +are further categorized into :class:`DevlibTransientError` and +:class:`DevlibStableError`. Transient errors are raised when there is an issue +in the environment that can happen randomly such as the loss of network +connectivity. Even a properly configured environment can be subject to such +transient errors. Stable errors are related to either programming errors or +configuration issues in the broad sense. This distinction allows quicker +analysis of failures, since most transient errors can be ignored unless they +happen at an alarming rate. :class:`DevlibTransientError` usually propagates up +to the caller of devlib APIs, since it means that an operation could not +complete. Retrying it or bailing out is therefore a responsability of the caller. + +The hierarchy is as follows: + +- :class:`DevlibError` + + - :class:`WorkerThreadError` + - :class:`HostError` + - :class:`TargetError` + + - :class:`TargetStableError` + - :class:`TargetTransientError` + - :class:`TargetNotRespondingError` + + - :class:`DevlibStableError` + + - :class:`TargetStableError` + + - :class:`DevlibTransientError` + + - :class:`TimeoutError` + - :class:`TargetTransientError` + - :class:`TargetNotRespondingError` + + +Extending devlib +~~~~~~~~~~~~~~~~ + +New devlib code is likely to face the decision of raising a transient or stable +error. When it is unclear which one should be used, it can generally be assumed +that the system is properly configured and therefore, the error is linked to an +environment transient failure. If a function is somehow probing a property of a +system in the broad meaning, it can use a stable error as a way to signal a +non-expected value of that property even if it can also face transient errors. +An example are the various ``execute()`` methods where the command can generally +not be assumed to be supposed to succeed by devlib. Their failure does not +usually come from an environment random issue, but for example a permission +error. The user can use such expected failure to probe the system. Another +example is boot completion detection on Android: boot failure cannot be +distinguished from a timeout which is too small. A non-transient exception is +still raised, since assuming the timeout comes from a network failure would +either make the function useless, or force the calling code to handle a +transient exception under normal operation. The calling code would potentially +wrongly catch transient exceptions raised by other functions as well and attach +a wrong meaning to them. + + Modules ------- diff --git a/doc/target.rst b/doc/target.rst index b5c0706..1169a0f 100644 --- a/doc/target.rst +++ b/doc/target.rst @@ -232,7 +232,7 @@ Target :param timeout: timeout (in seconds) for the transfer; if the transfer does not complete within this period, an exception will be raised. -.. method:: Target.execute(command [, timeout [, check_exit_code [, as_root]]]) +.. method:: Target.execute(command [, timeout [, check_exit_code [, as_root, [will_succeed]]]]) Execute the specified command on the target device and return its output. @@ -245,6 +245,11 @@ Target raised if it is not ``0``. :param as_root: The command will be executed as root. This will fail on unrooted targets. + :param will_succeed: The command is assumed to always succeed, unless there is + an issue in the environment like the loss of network connectivity. That + will make the method always raise an instance of a subclass of + :class:`DevlibTransientError' when the command fails, instead of a + :class:`DevlibStableError`. .. method:: Target.background(command [, stdout [, stderr [, as_root]]])