Source code for grass.gunittest.gmodules

"""
Specialized interfaces for invoking modules for testing framework

SPDX-FileCopyrightText: 2014 GRASS Development Team
SPDX-License-Identifier: GPL-2.0-or-later

:authors: Vaclav Petras, Soeren Gebbert
"""

from __future__ import annotations

import subprocess
from grass.script.core import start_command
from grass.script.utils import decode
from grass.exceptions import CalledModuleError
from grass.pygrass.modules import Module

from .utils import do_doctest_gettext_workaround


[docs] class SimpleModule(Module): r"""Simple wrapper around pygrass.modules.Module to make sure that run\_, finish\_, stdout and stderr are set correctly. >>> mapcalc = SimpleModule("r.mapcalc", expression="test_a = 1", overwrite=True) >>> mapcalc.run() Module('r.mapcalc') >>> mapcalc.returncode 0 >>> colors = SimpleModule("r.colors", map="test_a", rules="-", stdin_="1 red") >>> colors.run() Module('r.colors') >>> colors.returncode 0 >>> str(colors.inputs.stdin) '1 red' >>> str(colors.outputs.stdout) '' >>> colors.outputs.stderr.strip() "Color table for raster map <test_a> set to 'rules'" """ def __init__(self, cmd, *args, **kargs): for banned in ["stdout_", "stderr_", "finish_", "run_"]: if banned in kargs: raise ValueError( "Do not set %s parameter, it would be overridden" % banned ) kargs["stdout_"] = subprocess.PIPE kargs["stderr_"] = subprocess.PIPE kargs["finish_"] = True kargs["run_"] = False Module.__init__(self, cmd, *args, **kargs)
[docs] def call_module( module, stdin=None, merge_stderr: bool = False, capture_stdout: bool = True, capture_stderr: bool = True, **kwargs, ) -> str | None: r"""Run module with parameters given in `kwargs` and return its output. >>> print(call_module("g.region", flags="pg")) # doctest: +ELLIPSIS projection=... zone=... n=... s=... w=... >>> call_module("m.proj", flags="i", input="-", stdin="50.0 41.5") '8642890.65|6965155.61|0.00\n' >>> call_module( ... "g.region", aabbbccc="notexist" ... ) # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... CalledModuleError: Module run g.region ... ended with error If `stdin` is not set and `kwargs` contains ``input`` with value set to ``-`` (dash), the function raises an error. Note that ``input`` nor ``output`` parameters are used by this function itself, these are usually module parameters which this function just passes to it. However, when ``input`` is in parameters the function checks if its values is correct considering value of ``stdin`` parameter. :param str module: module name :param stdin: string to be used as module standard input (stdin) or `None` :param merge_stderr: if the standard error output should be merged with stdout :param kwargs: module parameters :returns: module standard output (stdout) as string or None if capture_stdout is False :raises ~grass.exceptions.CalledModuleError: if module return code is non-zero :raises ValueError: if the parameters are not correct .. note:: The data read is buffered in memory, so do not use this method if the data size is large or unlimited. """ # noqa: E501 # TODO: remove this: do_doctest_gettext_workaround() # implementation inspired by subprocess.check_output() function if stdin: if "input" in kwargs and kwargs["input"] != "-": raise ValueError(_("input='-' must be used when stdin is specified")) if stdin == subprocess.PIPE: raise ValueError(_("stdin must be string or buffer, not PIPE")) kwargs["stdin"] = subprocess.PIPE # to be able to send data to stdin elif "input" in kwargs and kwargs["input"] == "-": raise ValueError(_("stdin must be used when input='-'")) if merge_stderr and not (capture_stdout and capture_stderr): raise ValueError(_("You cannot merge stdout and stderr and not capture them")) if "stdout" in kwargs: raise TypeError(_("stdout argument not allowed, it could be overridden")) if "stderr" in kwargs: raise TypeError(_("stderr argument not allowed, it could be overridden")) if capture_stdout: kwargs["stdout"] = subprocess.PIPE if capture_stderr: if merge_stderr: kwargs["stderr"] = subprocess.STDOUT else: kwargs["stderr"] = subprocess.PIPE with start_command(module, **kwargs) as process: # input=None means no stdin (our default) # for no stdout, output is None which is out interface # for stderr=STDOUT or no stderr, errors is None # which is fine for CalledModuleError output, errors = process.communicate(input=decode(stdin) if stdin else None) returncode = process.poll() if returncode: raise CalledModuleError(module, kwargs, returncode, errors) return decode(output) if output else None