gmodules.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. """
  2. Specialized interfaces for invoking modules for testing framework
  3. Copyright (C) 2014 by the GRASS Development Team
  4. This program is free software under the GNU General Public
  5. License (>=v2). Read the file COPYING that comes with GRASS GIS
  6. for details.
  7. :authors: Vaclav Petras, Soeren Gebbert
  8. """
  9. import subprocess
  10. from grass.script.core import start_command
  11. from grass.script.utils import encode, decode
  12. from grass.exceptions import CalledModuleError
  13. from grass.pygrass.modules import Module
  14. from .utils import do_doctest_gettext_workaround
  15. class SimpleModule(Module):
  16. """Simple wrapper around pygrass.modules.Module to make sure that
  17. run\_, finish\_, stdout and stderr are set correctly.
  18. >>> mapcalc = SimpleModule('r.mapcalc', expression='test_a = 1',
  19. ... overwrite=True)
  20. >>> mapcalc.run()
  21. Module('r.mapcalc')
  22. >>> mapcalc.returncode
  23. 0
  24. >>> colors = SimpleModule('r.colors',
  25. ... map='test_a', rules='-', stdin_='1 red')
  26. >>> colors.run()
  27. Module('r.colors')
  28. >>> colors.returncode
  29. 0
  30. >>> str(colors.inputs.stdin)
  31. '1 red'
  32. >>> str(colors.outputs.stdout)
  33. ''
  34. >>> colors.outputs.stderr.strip()
  35. "Color table for raster map <test_a> set to 'rules'"
  36. """
  37. def __init__(self, cmd, *args, **kargs):
  38. for banned in ["stdout_", "stderr_", "finish_", "run_"]:
  39. if banned in kargs:
  40. raise ValueError(
  41. "Do not set %s parameter" ", it would be overriden" % banned
  42. )
  43. kargs["stdout_"] = subprocess.PIPE
  44. kargs["stderr_"] = subprocess.PIPE
  45. kargs["finish_"] = True
  46. kargs["run_"] = False
  47. Module.__init__(self, cmd, *args, **kargs)
  48. def call_module(
  49. module,
  50. stdin=None,
  51. merge_stderr=False,
  52. capture_stdout=True,
  53. capture_stderr=True,
  54. **kwargs,
  55. ):
  56. r"""Run module with parameters given in `kwargs` and return its output.
  57. >>> print (call_module('g.region', flags='pg')) # doctest: +ELLIPSIS
  58. projection=...
  59. zone=...
  60. n=...
  61. s=...
  62. w=...
  63. >>> call_module('m.proj', flags='i', input='-', stdin="50.0 41.5")
  64. '8642890.65|6965155.61|0.00\n'
  65. >>> call_module('g.region', aabbbccc='notexist') # doctest: +IGNORE_EXCEPTION_DETAIL
  66. Traceback (most recent call last):
  67. ...
  68. CalledModuleError: Module run g.region ... ended with error
  69. If `stdin` is not set and `kwargs` contains ``input`` with value set
  70. to ``-`` (dash), the function raises an error.
  71. Note that ``input`` nor ``output`` parameters are used by this
  72. function itself, these are usually module parameters which this
  73. function just passes to it. However, when ``input`` is in parameters
  74. the function checks if its values is correct considering value of
  75. ``stdin`` parameter.
  76. :param str module: module name
  77. :param stdin: string to be used as module standard input (stdin) or `None`
  78. :param merge_stderr: if the standard error output should be merged with stdout
  79. :param kwargs: module parameters
  80. :returns: module standard output (stdout) as string or None
  81. if capture_stdout is False
  82. :raises CalledModuleError: if module return code is non-zero
  83. :raises ValueError: if the parameters are not correct
  84. .. note::
  85. The data read is buffered in memory, so do not use this method
  86. if the data size is large or unlimited.
  87. """
  88. # TODO: remove this:
  89. do_doctest_gettext_workaround()
  90. # implementation inspired by subprocess.check_output() function
  91. if stdin:
  92. if "input" in kwargs and kwargs["input"] != "-":
  93. raise ValueError(_("input='-' must be used when stdin is specified"))
  94. if stdin == subprocess.PIPE:
  95. raise ValueError(_("stdin must be string or buffer, not PIPE"))
  96. kwargs["stdin"] = subprocess.PIPE # to be able to send data to stdin
  97. elif "input" in kwargs and kwargs["input"] == "-":
  98. raise ValueError(_("stdin must be used when input='-'"))
  99. if merge_stderr and not (capture_stdout and capture_stderr):
  100. raise ValueError(_("You cannot merge stdout and stderr and not capture them"))
  101. if "stdout" in kwargs:
  102. raise TypeError(_("stdout argument not allowed, it could be overridden"))
  103. if "stderr" in kwargs:
  104. raise TypeError(_("stderr argument not allowed, it could be overridden"))
  105. if capture_stdout:
  106. kwargs["stdout"] = subprocess.PIPE
  107. if capture_stderr:
  108. if merge_stderr:
  109. kwargs["stderr"] = subprocess.STDOUT
  110. else:
  111. kwargs["stderr"] = subprocess.PIPE
  112. process = start_command(module, **kwargs)
  113. # input=None means no stdin (our default)
  114. # for no stdout, output is None which is out interface
  115. # for stderr=STDOUT or no stderr, errors is None
  116. # which is fine for CalledModuleError
  117. output, errors = process.communicate(input=encode(decode(stdin)) if stdin else None)
  118. returncode = process.poll()
  119. if returncode:
  120. raise CalledModuleError(returncode, module, kwargs, errors)
  121. return decode(output) if output else None