gmodules.py 4.9 KB

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