Explorar el Código

pythonlib: Use more specific language in CalledModuleError (#1525)

The error message coming from CalledModuleError was not sufficient for users to find
the error output of a module. This now explicitly says to look above traceback (case
for user scripts) while mentioning the more general case of testing framework and
(Jupyter) notebooks with default (GRASS) settings where stderr shows in a different
place than the traceback.

The module name is now included only if provided and if not already present avoiding
the `None r.univar` and `r.univar r.univar` cases. Backticks are used to mark the code,
although a) the code formatting is still unresolved (e.g., a list will render as just
Python list) and b) backticks are not common in GRASS (but fit perfectly this context
in general).

This also rewrites the code with format, constructs the message without plus, and
extends documentation (in-code and docstrings).
Vaclav Petras hace 4 años
padre
commit
8b2bf04628
Se han modificado 1 ficheros con 30 adiciones y 12 borrados
  1. 30 12
      python/grass/exceptions/__init__.py

+ 30 - 12
python/grass/exceptions/__init__.py

@@ -58,25 +58,43 @@ class Usage(Exception):
 class CalledModuleError(subprocess.CalledProcessError):
     """Raised when a called module ends with error (non-zero return code)
 
-    :param module: module name
-    :param code: some code snipped which contains parameters
-    :param rc: process returncode
-    :param error: errors provided by the module (stderr)
+    Used for failures of modules called as subprocesses from Python code.
     """
 
     def __init__(self, module, code, returncode, errors=None):
+        """Create an exception with a full error message based on the parameters.
+
+        :param module: module name
+        :param code: some code snipped which contains parameters
+        :param returncode: process returncode (assuming non-zero)
+        :param errors: errors provided by the module (e.g., stderr)
+        """
         # CalledProcessError has undocumented constructor
         super(CalledModuleError, self).__init__(returncode, module)
-        msg = _("Module run %s %s ended with error") % (module, code)
-        msg += _("\nProcess ended with non-zero return code %s") % returncode
+        if not module or module in code:
+            # No need to include module name if it is directly in code
+            # of if it is not set.
+            executed = code
+        else:
+            # Make sure module name is there if provided and not in code.
+            executed = f"{module} {code}"
         if errors:
-            msg += _(". See the following errors:\n%s") % errors
+            # We assume actual errors, e.g., captured stderr.
+            err = _("See the following errors:\n{errors}").format(errors=errors)
         else:
-            # here could be written "above" but it wouldn't work in some cases
-            # e.g., for testing framework
-            msg += _(". See errors in the (error) output.")
-        self.msg = msg
-        # TODO: handle other parameters
+            # In command line, the errors will be above, but in testing framework
+            # or notebooks, the errors will be somewhere else than the traceback.
+            err = _("See errors above the traceback or in the error output.")
+        # The full message
+        self.msg = _(
+            "Module run `{executed}` ended with an error.\n"
+            "The subprocess ended with a non-zero return code: {returncode}."
+            " {see_errors}"
+        ).format(
+            executed=executed,
+            returncode=returncode,
+            see_errors=err,
+        )
 
     def __str__(self):
         return self.msg