Explorar o código

Apply Black to grass package, use Python 3 style everywhere (#1382)

This applies Black 20.8b1 formatting to grass Python package and all related tests.
ctypes is not formatted by Black and two additional files are ignored because
of bug in Black which breaks strings with escape sequence for tab.

This adds pyproject.toml file with Black configuration for the project.
In the CI, instead of mutliple Black runs, just run once for the whole tree and ignore directories which are not formatted yet.
Black is very fast (so no need for multiple job) and only one configuration is needed (unlike Flake8)

Newly, only Python 3 versions are specified as targets for Black,
so also string literals starting with u (Python 2) are replaced by
simple strings.

This also applies Python 3 targeted Black to already formatted code.
This replaces unicode literals (Python 2) by plain Python 3 strings and adds commas to kwargs in function calls.

Update Flake8 config for use with Black. Enable Flake8 whitespace checks and fix remaining issues not
touched by Black. Ignore many E226 in images2gif.py which is now ignored by Black.
Enable long line warning in Flake8. Fix or ignore lines not fixed by Black.
Vaclav Petras %!s(int64=4) %!d(string=hai) anos
pai
achega
1aab3bbcff
Modificáronse 100 ficheiros con 5773 adicións e 4170 borrados
  1. 1 10
      .github/workflows/black.yml
  2. 12 12
      man/sphinx/conf.py
  3. 39 0
      pyproject.toml
  4. 17 20
      python/grass/.flake8
  5. 7 7
      python/grass/__init__.py
  6. 37 46
      python/grass/bandref/reader.py
  7. 159 102
      python/grass/docs/conf.py
  8. 5 5
      python/grass/exceptions/__init__.py
  9. 5 6
      python/grass/exceptions/testsuite/test_ScriptError.py
  10. 63 46
      python/grass/grassdb/checks.py
  11. 545 363
      python/grass/gunittest/case.py
  12. 96 74
      python/grass/gunittest/checkers.py
  13. 26 19
      python/grass/gunittest/gmodules.py
  14. 19 12
      python/grass/gunittest/gutils.py
  15. 130 87
      python/grass/gunittest/invoker.py
  16. 53 38
      python/grass/gunittest/loader.py
  17. 103 62
      python/grass/gunittest/main.py
  18. 340 176
      python/grass/gunittest/multireport.py
  19. 82 43
      python/grass/gunittest/multirunner.py
  20. 446 397
      python/grass/gunittest/reporters.py
  21. 57 47
      python/grass/gunittest/runner.py
  22. 6 6
      python/grass/gunittest/testsuite/data/samplecode/submodule_errors/subsubmodule_errors/testsuite/test_error.py
  23. 8 6
      python/grass/gunittest/testsuite/data/samplecode/submodule_errors/subsubmodule_errors/testsuite/test_import_error.py
  24. 1 1
      python/grass/gunittest/testsuite/data/samplecode/submodule_errors/subsubmodule_exiting/testsuite/test_gfatalerror.py
  25. 1 1
      python/grass/gunittest/testsuite/data/samplecode/submodule_errors/subsubmodule_exiting/testsuite/test_osexit_one.py
  26. 1 1
      python/grass/gunittest/testsuite/data/samplecode/submodule_errors/subsubmodule_exiting/testsuite/test_osexit_zero.py
  27. 3 3
      python/grass/gunittest/testsuite/data/samplecode/submodule_errors/subsubmodule_exiting/testsuite/test_segfaut.py
  28. 1 1
      python/grass/gunittest/testsuite/data/samplecode/submodule_errors/subsubmodule_exiting/testsuite/test_sysexit_one.py
  29. 1 1
      python/grass/gunittest/testsuite/data/samplecode/submodule_errors/subsubmodule_exiting/testsuite/test_sysexit_zero.py
  30. 1 1
      python/grass/gunittest/testsuite/data/samplecode/submodule_test_fail/testsuite/test_fail.py
  31. 3 2
      python/grass/gunittest/testsuite/data/samplecode/testsuite/test_good_and_bad.py
  32. 2 1
      python/grass/gunittest/testsuite/data/samplecode/testsuite/test_python_unittest.py
  33. 2 1
      python/grass/gunittest/testsuite/data/samplecode/testsuite/test_success.py
  34. 206 164
      python/grass/gunittest/testsuite/test_assertions.py
  35. 114 83
      python/grass/gunittest/testsuite/test_assertions_rast3d.py
  36. 188 110
      python/grass/gunittest/testsuite/test_assertions_vect.py
  37. 151 122
      python/grass/gunittest/testsuite/test_checkers.py
  38. 92 53
      python/grass/gunittest/testsuite/test_gmodules.py
  39. 9 7
      python/grass/gunittest/testsuite/test_gunitest_doctests.py
  40. 23 16
      python/grass/gunittest/testsuite/test_module_assertions.py
  41. 3 1
      python/grass/gunittest/utils.py
  42. 35 21
      python/grass/imaging/images2avi.py
  43. 13 7
      python/grass/imaging/images2gif.py
  44. 15 15
      python/grass/imaging/images2ims.py
  45. 132 130
      python/grass/imaging/images2swf.py
  46. 16 11
      python/grass/imaging/operations.py
  47. 18 26
      python/grass/pydispatch/dispatcher.py
  48. 2 10
      python/grass/pydispatch/robust.py
  49. 17 17
      python/grass/pydispatch/robustapply.py
  50. 20 16
      python/grass/pydispatch/saferef.py
  51. 9 4
      python/grass/pydispatch/signal.py
  52. 9 7
      python/grass/pygrass/errors.py
  53. 98 70
      python/grass/pygrass/gis/__init__.py
  54. 248 200
      python/grass/pygrass/gis/region.py
  55. 1 2
      python/grass/pygrass/gis/testsuite/test_gis.py
  56. 17 11
      python/grass/pygrass/gis/testsuite/test_pygrass_gis_doctests.py
  57. 107 100
      python/grass/pygrass/messages/__init__.py
  58. 9 7
      python/grass/pygrass/messages/testsuite/test_pygrass_messages_doctests.py
  59. 178 121
      python/grass/pygrass/modules/grid/grid.py
  60. 28 11
      python/grass/pygrass/modules/grid/patch.py
  61. 19 10
      python/grass/pygrass/modules/grid/split.py
  62. 10 8
      python/grass/pygrass/modules/grid/testsuite/test_pygrass_modules_grid_doctests.py
  63. 5 1
      python/grass/pygrass/modules/interface/__init__.py
  64. 2 0
      python/grass/pygrass/modules/interface/docstring.py
  65. 12 8
      python/grass/pygrass/modules/interface/env.py
  66. 28 20
      python/grass/pygrass/modules/interface/flag.py
  67. 128 78
      python/grass/pygrass/modules/interface/module.py
  68. 106 67
      python/grass/pygrass/modules/interface/parameter.py
  69. 42 34
      python/grass/pygrass/modules/interface/read.py
  70. 12 12
      python/grass/pygrass/modules/interface/testsuite/test_flag.py
  71. 15 10
      python/grass/pygrass/modules/interface/testsuite/test_modules.py
  72. 227 114
      python/grass/pygrass/modules/interface/testsuite/test_parameter.py
  73. 9 7
      python/grass/pygrass/modules/interface/testsuite/test_pygrass_modules_interface_doctests.py
  74. 12 5
      python/grass/pygrass/modules/interface/typedict.py
  75. 56 48
      python/grass/pygrass/modules/shortcuts.py
  76. 19 11
      python/grass/pygrass/modules/testsuite/test_import_isolation.py
  77. 10 8
      python/grass/pygrass/modules/testsuite/test_pygrass_modules_doctests.py
  78. 40 38
      python/grass/pygrass/orderdict.py
  79. 130 107
      python/grass/pygrass/raster/__init__.py
  80. 105 65
      python/grass/pygrass/raster/abstract.py
  81. 15 12
      python/grass/pygrass/raster/buffer.py
  82. 91 83
      python/grass/pygrass/raster/history.py
  83. 25 15
      python/grass/pygrass/raster/raster_type.py
  84. 23 20
      python/grass/pygrass/raster/rowio.py
  85. 48 40
      python/grass/pygrass/raster/segment.py
  86. 16 13
      python/grass/pygrass/raster/testsuite/test_category.py
  87. 12 7
      python/grass/pygrass/raster/testsuite/test_history.py
  88. 10 8
      python/grass/pygrass/raster/testsuite/test_numpy.py
  89. 20 18
      python/grass/pygrass/raster/testsuite/test_pygrass_raster.py
  90. 26 15
      python/grass/pygrass/raster/testsuite/test_pygrass_raster_doctests.py
  91. 19 24
      python/grass/pygrass/raster/testsuite/test_raster_img.py
  92. 18 11
      python/grass/pygrass/raster/testsuite/test_raster_region.py
  93. 211 188
      python/grass/pygrass/rpc/__init__.py
  94. 44 38
      python/grass/pygrass/rpc/base.py
  95. 15 10
      python/grass/pygrass/rpc/testsuite/test_pygrass_rpc_doctests.py
  96. 22 6
      python/grass/pygrass/shell/conversion.py
  97. 1 1
      python/grass/pygrass/shell/show.py
  98. 9 7
      python/grass/pygrass/shell/testsuite/test_pygrass_shell_doctests.py
  99. 131 64
      python/grass/pygrass/tests/benchmark.py
  100. 0 0
      python/grass/pygrass/tests/set_mapset.py

+ 1 - 10
.github/workflows/black.yml

@@ -6,16 +6,8 @@ on:
 
 
 jobs:
 jobs:
   run-black:
   run-black:
-    name: ${{ matrix.directory }}
+    name: Check
     runs-on: ubuntu-20.04
     runs-on: ubuntu-20.04
-    strategy:
-      matrix:
-        directory:
-          - lib/init
-          - man
-          - scripts
-          - utils
-      fail-fast: false
 
 
     steps:
     steps:
       - uses: actions/checkout@v2
       - uses: actions/checkout@v2
@@ -32,5 +24,4 @@ jobs:
 
 
       - name: Run Black
       - name: Run Black
         run: |
         run: |
-          cd ${{ matrix.directory }}
           black --check --diff .
           black --check --diff .

+ 12 - 12
man/sphinx/conf.py

@@ -38,8 +38,8 @@ source_suffix = ".txt"
 master_doc = "index"
 master_doc = "index"
 
 
 # General information about the project.
 # General information about the project.
-project = u"GRASS 7.9 Documentation"
-copyright = u"2019, GRASS Development Team"
+project = "GRASS 7.9 Documentation"
+copyright = "2019, GRASS Development Team"
 
 
 # The version info for the project you're documenting, acts as replacement for
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the
 # |version| and |release|, also used in various other places throughout the
@@ -182,8 +182,8 @@ latex_documents = [
     (
     (
         "content",
         "content",
         "grass79Documentation.tex",
         "grass79Documentation.tex",
-        u"GRASS 7.9 Documentation",
-        u"GRASS Development Team",
+        "GRASS 7.9 Documentation",
+        "GRASS Development Team",
         "manual",
         "manual",
     ),
     ),
 ]
 ]
@@ -217,8 +217,8 @@ man_pages = [
     (
     (
         "content",
         "content",
         "grass79documentation",
         "grass79documentation",
-        u"GRASS 7.9 Documentation",
-        [u"GRASS Development Team"],
+        "GRASS 7.9 Documentation",
+        ["GRASS Development Team"],
         1,
         1,
     )
     )
 ]
 ]
@@ -236,8 +236,8 @@ texinfo_documents = [
     (
     (
         "content",
         "content",
         "grass79Documentation",
         "grass79Documentation",
-        u"GRASS 7.9 Documentation",
-        u"GRASS Development Team",
+        "GRASS 7.9 Documentation",
+        "GRASS Development Team",
         "grass79Documentation",
         "grass79Documentation",
         "One line description of project.",
         "One line description of project.",
         "Miscellaneous",
         "Miscellaneous",
@@ -257,10 +257,10 @@ texinfo_documents = [
 # -- Options for Epub output ---------------------------------------------------
 # -- Options for Epub output ---------------------------------------------------
 
 
 # Bibliographic Dublin Core info.
 # Bibliographic Dublin Core info.
-epub_title = u"GRASS 7.9 Documentation"
-epub_author = u"GRASS Development Team"
-epub_publisher = u"GRASS Development Team"
-epub_copyright = u"2017, GRASS Development Team"
+epub_title = "GRASS 7.9 Documentation"
+epub_author = "GRASS Development Team"
+epub_publisher = "GRASS Development Team"
+epub_copyright = "2017, GRASS Development Team"
 
 
 # The language of the text. It defaults to the language option
 # The language of the text. It defaults to the language option
 # or en if the language is not set.
 # or en if the language is not set.

+ 39 - 0
pyproject.toml

@@ -0,0 +1,39 @@
+[tool.black]
+line-length = 88
+target-version = ['py36', 'py37', 'py38']
+include = '\.pyi?$'
+exclude = '''
+(
+    # exclude directories in the root of the project
+    /(
+          \.eggs
+        | \.git
+        | \.hg
+        | \.mypy_cache
+        | \.tox
+        | \.venv
+        | _build
+        | buck-out
+        | build
+        | bin\.
+        | dist\.
+    )/
+    | python/grass/ctypes
+    # Bug in Black related to tab escape prevents these from being formatted correctly.
+    # https://github.com/psf/black/issues/1970
+    | python/grass/imaging/images2gif.py
+    | python/grass/pygrass/raster/category.py
+    # Directories and files not yet under Black
+    | db
+    | doc
+    | general
+    | gui
+    | imagery
+    | lib/gis
+    | temporal
+    | raster
+    | vector
+    | docker/testdata/test_grass_session.py
+    | display/d.mon/render_cmd.py
+)
+'''

+ 17 - 20
python/grass/.flake8

@@ -1,24 +1,8 @@
 [flake8]
 [flake8]
 ignore =
 ignore =
-    E121, # continuation line under-indented for hanging indent
-    E125, # continuation line with same indent as next logical line
-    E127, # continuation line over-indented for visual indent
-    E128, # continuation line under-indented for visual indent
-    E202, # whitespace before ')'
-    E211, # whitespace before '('
-    E221, # multiple spaces before operator
-    E226, # missing whitespace around arithmetic operator
-    E231, # missing whitespace after ':'
-    E251, # unexpected spaces around keyword / parameter equals
-    E261, # at least two spaces before inline comment
-    E265, # block comment should start with '# '
+    E203,  # whitespace before ':' (Black)
+    W503,  # line break before binary operator (Black)
     E266, # too many leading '#' for block comment
     E266, # too many leading '#' for block comment
-    E271, # multiple spaces after keyword
-    E272, # multiple spaces before keyword
-    E302, # expected 2 blank lines, found 1
-    E303, # too many blank lines (3)
-    E305, # expected 2 blank lines after class or function definition, found 1
-    E501, # line too long (183 > 150 characters)
     E722, # do not use bare 'except'
     E722, # do not use bare 'except'
     E741, # ambiguous variable name 'l'
     E741, # ambiguous variable name 'l'
     F403, # 'from ctypes import *' used; unable to detect undefined names
     F403, # 'from ctypes import *' used; unable to detect undefined names
@@ -34,13 +18,26 @@ per-file-ignores =
     # TODO: Is this really needed?
     # TODO: Is this really needed?
     pygrass/vector/__init__.py: E402,
     pygrass/vector/__init__.py: E402,
     pygrass/raster/__init__.py: E402,
     pygrass/raster/__init__.py: E402,
-    pygrass/utils.py: E402,
+    # Files and directories which need fixes or specific exceptions
+    gunittest/*.py: E501  # These are mainly just todo comments
+    pygrass/vector/table.py: E501
+    pygrass/vector/__init__.py: E501, E402
+    pygrass/modules/interface/*.py: E501, F401
+    pygrass/modules/grid/*.py: E501, F401
+    pygrass/raster/*.py: E501
+    pygrass/rpc/__init__.py: E501, F401
+    pygrass/utils.py: E402, E501
+    script/db.py: E501
+    script/vector.py: E501  # Long doctest lines which need review anyway
+    temporal/*.py: E501
     # Current benchmarks/tests are changing sys.path before import.
     # Current benchmarks/tests are changing sys.path before import.
     # Possibly, a different approach should be taken there anyway.
     # Possibly, a different approach should be taken there anyway.
-    pygrass/tests/benchmark.py: E402, F401, F821
+    pygrass/tests/benchmark.py: E501, E402, F401, F821
     # Configuration file for Sphinx:
     # Configuration file for Sphinx:
     # Ignoring import/code mix and line length.
     # Ignoring import/code mix and line length.
     docs/conf.py: E402, E501,
     docs/conf.py: E402, E501,
+    # Files not managed by Black
+    imaging/images2gif.py: E226, E501
     # Unused imports
     # Unused imports
     */__init__.py: F401,
     */__init__.py: F401,
     */*/__init__.py: F401,
     */*/__init__.py: F401,

+ 7 - 7
python/grass/__init__.py

@@ -18,15 +18,15 @@ import six
 # - https://pymotw.com/2//gettext/index.html#application-vs-module-localization
 # - https://pymotw.com/2//gettext/index.html#application-vs-module-localization
 # - https://www.wefearchange.org/2012/06/the-right-way-to-internationalize-your.html
 # - https://www.wefearchange.org/2012/06/the-right-way-to-internationalize-your.html
 #
 #
-_LOCALE_DIR = os.path.join(os.getenv("GISBASE"), 'locale')
+_LOCALE_DIR = os.path.join(os.getenv("GISBASE"), "locale")
 if six.PY2:
 if six.PY2:
-    gettext.install('grasslibs', _LOCALE_DIR, unicode=True)
-    gettext.install('grassmods', _LOCALE_DIR, unicode=True)
-    gettext.install('grasswxpy', _LOCALE_DIR, unicode=True)
+    gettext.install("grasslibs", _LOCALE_DIR, unicode=True)
+    gettext.install("grassmods", _LOCALE_DIR, unicode=True)
+    gettext.install("grasswxpy", _LOCALE_DIR, unicode=True)
 else:
 else:
-    gettext.install('grasslibs', _LOCALE_DIR)
-    gettext.install('grassmods', _LOCALE_DIR)
-    gettext.install('grasswxpy', _LOCALE_DIR)
+    gettext.install("grasslibs", _LOCALE_DIR)
+    gettext.install("grassmods", _LOCALE_DIR)
+    gettext.install("grasswxpy", _LOCALE_DIR)
 
 
 
 
 __all__ = ["script", "temporal"]
 __all__ = ["script", "temporal"]

+ 37 - 46
python/grass/bandref/reader.py

@@ -14,15 +14,17 @@ from collections import OrderedDict
 # https://github.com/radiantearth/stac-spec/blob/master/extensions/eo/README.md#band-object
 # https://github.com/radiantearth/stac-spec/blob/master/extensions/eo/README.md#band-object
 # custom names must be possible
 # custom names must be possible
 
 
+
 class BandReferenceReaderError(Exception):
 class BandReferenceReaderError(Exception):
     pass
     pass
 
 
+
 class BandReferenceReader:
 class BandReferenceReader:
     """Band references reader"""
     """Band references reader"""
 
 
     def __init__(self):
     def __init__(self):
         self._json_files = glob.glob(
         self._json_files = glob.glob(
-            os.path.join(os.environ['GISBASE'], 'etc', 'g.bands', '*.json')
+            os.path.join(os.environ["GISBASE"], "etc", "g.bands", "*.json")
         )
         )
         if not self._json_files:
         if not self._json_files:
             raise BandReferenceReaderError("No band definitions found")
             raise BandReferenceReaderError("No band definitions found")
@@ -35,15 +37,11 @@ class BandReferenceReader:
         for json_file in self._json_files:
         for json_file in self._json_files:
             try:
             try:
                 with open(json_file) as fd:
                 with open(json_file) as fd:
-                    config = json.load(
-                        fd,
-                        object_pairs_hook=OrderedDict
-                    )
+                    config = json.load(fd, object_pairs_hook=OrderedDict)
             except json.decoder.JSONDecodeError as e:
             except json.decoder.JSONDecodeError as e:
                 raise BandReferenceReaderError(
                 raise BandReferenceReaderError(
-                    "Unable to parse '{}': {}".format(
-                        json_file, e
-                    ))
+                    "Unable to parse '{}': {}".format(json_file, e)
+                )
 
 
             # check if configuration is valid
             # check if configuration is valid
             self._check_config(config)
             self._check_config(config)
@@ -59,12 +57,12 @@ class BandReferenceReader:
         :param dict config: configuration to be validated
         :param dict config: configuration to be validated
         """
         """
         for items in config.values():
         for items in config.values():
-            for item in ('shortcut', 'bands'):
+            for item in ("shortcut", "bands"):
                 if item not in items.keys():
                 if item not in items.keys():
                     raise BandReferenceReaderError(
                     raise BandReferenceReaderError(
-                        "Invalid band definition: <{}> is missing".format(item
-                                                                          ))
-            if len(items['bands']) < 1:
+                        "Invalid band definition: <{}> is missing".format(item)
+                    )
+            if len(items["bands"]) < 1:
                 raise BandReferenceReaderError(
                 raise BandReferenceReaderError(
                     "Invalid band definition: no bands defined"
                     "Invalid band definition: no bands defined"
                 )
                 )
@@ -76,25 +74,24 @@ class BandReferenceReader:
         :param str band: band identifier
         :param str band: band identifier
         :param str item: items to be printed out
         :param str item: items to be printed out
         """
         """
+
         def print_kv(k, v, indent):
         def print_kv(k, v, indent):
             if isinstance(v, OrderedDict):
             if isinstance(v, OrderedDict):
-                print ('{}{}:'.format(' ' * indent * 2, k))
+                print("{}{}:".format(" " * indent * 2, k))
                 for ki, vi in v.items():
                 for ki, vi in v.items():
                     print_kv(ki, vi, indent * 2)
                     print_kv(ki, vi, indent * 2)
             else:
             else:
-                print ('{}{}: {}'.format(' ' * indent * 2, k, v))
+                print("{}{}: {}".format(" " * indent * 2, k, v))
 
 
         indent = 4
         indent = 4
-        print ('{}band: {}'.format(
-            ' ' * indent, band
-        ))
+        print("{}band: {}".format(" " * indent, band))
         for k, v in item[band].items():
         for k, v in item[band].items():
             print_kv(k, v, indent)
             print_kv(k, v, indent)
 
 
     def _print_band(self, shortcut, band, tag=None):
     def _print_band(self, shortcut, band, tag=None):
         sys.stdout.write(self._band_identifier(shortcut, band))
         sys.stdout.write(self._band_identifier(shortcut, band))
         if tag:
         if tag:
-            sys.stdout.write(' {}'.format(tag))
+            sys.stdout.write(" {}".format(tag))
         sys.stdout.write(os.linesep)
         sys.stdout.write(os.linesep)
 
 
     def print_info(self, shortcut=None, band=None, extended=False):
     def print_info(self, shortcut=None, band=None, extended=False):
@@ -110,48 +107,41 @@ class BandReferenceReader:
         for root in self.config.values():
         for root in self.config.values():
             for item in root.values():
             for item in root.values():
                 try:
                 try:
-                    if shortcut and re.match(shortcut, item['shortcut']) is None:
+                    if shortcut and re.match(shortcut, item["shortcut"]) is None:
                         continue
                         continue
                 except re.error as e:
                 except re.error as e:
-                    raise BandReferenceReaderError(
-                        "Invalid pattern: {}".format(e)
-                    )
+                    raise BandReferenceReaderError("Invalid pattern: {}".format(e))
 
 
                 found = True
                 found = True
-                if band and band not in item['bands']:
+                if band and band not in item["bands"]:
                     raise BandReferenceReaderError(
                     raise BandReferenceReaderError(
-                        "Band <{}> not found in <{}>".format(
-                            band, shortcut
-                        ))
+                        "Band <{}> not found in <{}>".format(band, shortcut)
+                    )
 
 
                 # print generic information
                 # print generic information
                 if extended:
                 if extended:
                     for subitem in item.keys():
                     for subitem in item.keys():
-                        if subitem == 'bands':
+                        if subitem == "bands":
                             # bands item is processed bellow
                             # bands item is processed bellow
                             continue
                             continue
-                        print ('{}: {}'.format(
-                            subitem, item[subitem]
-                        ))
+                        print("{}: {}".format(subitem, item[subitem]))
 
 
                     # print detailed band information
                     # print detailed band information
                     if band:
                     if band:
-                        self._print_band_extended(band, item['bands'])
+                        self._print_band_extended(band, item["bands"])
                     else:
                     else:
-                        for iband in item['bands']:
-                            self._print_band_extended(iband, item['bands'])
+                        for iband in item["bands"]:
+                            self._print_band_extended(iband, item["bands"])
                 else:
                 else:
                     # basic information only
                     # basic information only
                     if band:
                     if band:
                         self._print_band(
                         self._print_band(
-                            item['shortcut'], band,
-                            item['bands'][band].get('tag')
+                            item["shortcut"], band, item["bands"][band].get("tag")
                         )
                         )
                     else:
                     else:
-                        for iband in item['bands']:
+                        for iband in item["bands"]:
                             self._print_band(
                             self._print_band(
-                                item['shortcut'], iband,
-                                item['bands'][iband].get('tag')
+                                item["shortcut"], iband, item["bands"][iband].get("tag")
                             )
                             )
 
 
         # raise error when defined shortcut not found
         # raise error when defined shortcut not found
@@ -170,7 +160,7 @@ class BandReferenceReader:
         :return str: file basename if found or None
         :return str: file basename if found or None
         """
         """
         try:
         try:
-            shortcut, band = band_reference.split('_')
+            shortcut, band = band_reference.split("_")
         except ValueError:
         except ValueError:
             # raise BandReferenceReaderError("Invalid band identifier <{}>".format(
             # raise BandReferenceReaderError("Invalid band identifier <{}>".format(
             #    band_reference
             #    band_reference
@@ -180,8 +170,11 @@ class BandReferenceReader:
 
 
         for filename, config in self.config.items():
         for filename, config in self.config.items():
             for root in config.keys():
             for root in config.keys():
-                if config[root]['shortcut'].upper() == shortcut.upper() and \
-                   band.upper() in map(lambda x: x.upper(), config[root]['bands'].keys()):
+                if config[root][
+                    "shortcut"
+                ].upper() == shortcut.upper() and band.upper() in map(
+                    lambda x: x.upper(), config[root]["bands"].keys()
+                ):
                     return filename
                     return filename
 
 
         return None
         return None
@@ -194,12 +187,10 @@ class BandReferenceReader:
         bands = []
         bands = []
         for root in self.config.values():
         for root in self.config.values():
             for item in root.values():
             for item in root.values():
-                for band in item['bands']:
-                    bands.append(
-                        self._band_identifier(item['shortcut'], band)
-                    )
+                for band in item["bands"]:
+                    bands.append(self._band_identifier(item["shortcut"], band))
         return bands
         return bands
 
 
     @staticmethod
     @staticmethod
     def _band_identifier(shortcut, band):
     def _band_identifier(shortcut, band):
-        return '{}_{}'.format(shortcut, band)
+        return "{}_{}".format(shortcut, band)

+ 159 - 102
python/grass/docs/conf.py

@@ -21,17 +21,59 @@ from shutil import copy
 # If extensions (or modules to document with autodoc) are in another directory,
 # If extensions (or modules to document with autodoc) are in another directory,
 # add these directories to sys.path here. If the directory is relative to the
 # add these directories to sys.path here. If the directory is relative to the
 # documentation root, use os.path.abspath to make it absolute, like shown here.
 # documentation root, use os.path.abspath to make it absolute, like shown here.
-if not os.getenv('GISBASE'):
+if not os.getenv("GISBASE"):
     sys.exit("GISBASE not defined")
     sys.exit("GISBASE not defined")
-sys.path.insert(0, os.path.abspath(os.path.join(os.environ['GISBASE'], 'etc', 'python', 'grass')))
-sys.path.insert(0, os.path.abspath(os.path.join(os.environ['GISBASE'], 'etc', 'python', 'grass', 'ctypes')))
-sys.path.insert(0, os.path.abspath(os.path.join(os.environ['GISBASE'], 'etc', 'python', 'grass', 'exceptions')))
-sys.path.insert(0, os.path.abspath(os.path.join(os.environ['GISBASE'], 'etc', 'python', 'grass', 'gunittest')))
-sys.path.insert(0, os.path.abspath(os.path.join(os.environ['GISBASE'], 'etc', 'python', 'grass', 'imaging')))
-sys.path.insert(0, os.path.abspath(os.path.join(os.environ['GISBASE'], 'etc', 'python', 'grass', 'pydispatch')))
-sys.path.insert(0, os.path.abspath(os.path.join(os.environ['GISBASE'], 'etc', 'python', 'grass', 'pygrass')))
-sys.path.insert(0, os.path.abspath(os.path.join(os.environ['GISBASE'], 'etc', 'python', 'grass', 'script')))
-sys.path.insert(0, os.path.abspath(os.path.join(os.environ['GISBASE'], 'etc', 'python', 'grass', 'temporal')))
+sys.path.insert(
+    0, os.path.abspath(os.path.join(os.environ["GISBASE"], "etc", "python", "grass"))
+)
+sys.path.insert(
+    0,
+    os.path.abspath(
+        os.path.join(os.environ["GISBASE"], "etc", "python", "grass", "ctypes")
+    ),
+)
+sys.path.insert(
+    0,
+    os.path.abspath(
+        os.path.join(os.environ["GISBASE"], "etc", "python", "grass", "exceptions")
+    ),
+)
+sys.path.insert(
+    0,
+    os.path.abspath(
+        os.path.join(os.environ["GISBASE"], "etc", "python", "grass", "gunittest")
+    ),
+)
+sys.path.insert(
+    0,
+    os.path.abspath(
+        os.path.join(os.environ["GISBASE"], "etc", "python", "grass", "imaging")
+    ),
+)
+sys.path.insert(
+    0,
+    os.path.abspath(
+        os.path.join(os.environ["GISBASE"], "etc", "python", "grass", "pydispatch")
+    ),
+)
+sys.path.insert(
+    0,
+    os.path.abspath(
+        os.path.join(os.environ["GISBASE"], "etc", "python", "grass", "pygrass")
+    ),
+)
+sys.path.insert(
+    0,
+    os.path.abspath(
+        os.path.join(os.environ["GISBASE"], "etc", "python", "grass", "script")
+    ),
+)
+sys.path.insert(
+    0,
+    os.path.abspath(
+        os.path.join(os.environ["GISBASE"], "etc", "python", "grass", "temporal")
+    ),
+)
 
 
 from grass.script import core
 from grass.script import core
 
 
@@ -41,9 +83,10 @@ footer_tmpl = string.Template(
 <p><a href="../index.html">Help Index</a> | <a href="../topics.html">Topics Index</a> | <a href="../keywords.html">Keywords Index</a> | <a href="../full_index.html">Full Index</a></p>
 <p><a href="../index.html">Help Index</a> | <a href="../topics.html">Topics Index</a> | <a href="../keywords.html">Keywords Index</a> | <a href="../full_index.html">Full Index</a></p>
 <p>&copy; 2003-${year} <a href="https://grass.osgeo.org">GRASS Development Team</a>, GRASS GIS ${grass_version} Reference Manual</p>
 <p>&copy; 2003-${year} <a href="https://grass.osgeo.org">GRASS Development Team</a>, GRASS GIS ${grass_version} Reference Manual</p>
 {% endblock %}
 {% endblock %}
-""")
+"""
+)
 
 
-grass_version = core.version()['version']
+grass_version = core.version()["version"]
 today = date.today()
 today = date.today()
 
 
 copy("_templates/layout.html.template", "_templates/layout.html")
 copy("_templates/layout.html.template", "_templates/layout.html")
@@ -54,149 +97,149 @@ with open("_templates/layout.html", "a") as f:
 # -- General configuration -----------------------------------------------------
 # -- General configuration -----------------------------------------------------
 
 
 # If your documentation needs a minimal Sphinx version, state it here.
 # If your documentation needs a minimal Sphinx version, state it here.
-#needs_sphinx = '1.0'
+# needs_sphinx = '1.0'
 
 
 # Add any Sphinx extension module names here, as strings. They can be
 # Add any Sphinx extension module names here, as strings. They can be
 # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
 # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
 # ones.
 # ones.
 extensions = [
 extensions = [
-    'sphinx.ext.autodoc',
-    'sphinx.ext.doctest',
-    'sphinx.ext.todo',
-    'sphinx.ext.coverage',
-    'sphinx.ext.mathjax',
-    'sphinx.ext.ifconfig',
-    'sphinx.ext.viewcode',
+    "sphinx.ext.autodoc",
+    "sphinx.ext.doctest",
+    "sphinx.ext.todo",
+    "sphinx.ext.coverage",
+    "sphinx.ext.mathjax",
+    "sphinx.ext.ifconfig",
+    "sphinx.ext.viewcode",
 ]
 ]
 
 
 # Add any paths that contain templates here, relative to this directory.
 # Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
+templates_path = ["_templates"]
 
 
 # The suffix of source filenames.
 # The suffix of source filenames.
-source_suffix = '.rst'
+source_suffix = ".rst"
 
 
 # The encoding of source files.
 # The encoding of source files.
-#source_encoding = 'utf-8-sig'
+# source_encoding = 'utf-8-sig'
 
 
 # The master toctree document.
 # The master toctree document.
-master_doc = 'index'
+master_doc = "index"
 
 
 # General information about the project.
 # General information about the project.
-project = u'Python library documentation'
-copyright = u'2014, GRASS Development Team'
+project = "Python library documentation"
+copyright = "2014, GRASS Development Team"
 
 
 # The version info for the project you're documenting, acts as replacement for
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the
 # |version| and |release|, also used in various other places throughout the
 # built documents.
 # built documents.
 #
 #
 # The short X.Y version.
 # The short X.Y version.
-#version = '0.1'
+# version = '0.1'
 # The full version, including alpha/beta/rc tags.
 # The full version, including alpha/beta/rc tags.
-#release = '0.1'
+# release = '0.1'
 
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
 # for a list of supported languages.
 # http://sphinx-doc.org/config.html#options-for-internationalization
 # http://sphinx-doc.org/config.html#options-for-internationalization
-language = 'en'
+language = "en"
 
 
 # There are two options for replacing |today|: either, you set today to some
 # There are two options for replacing |today|: either, you set today to some
 # non-false value, then it is used:
 # non-false value, then it is used:
-#today = ''
+# today = ''
 # Else, today_fmt is used as the format for a strftime call.
 # Else, today_fmt is used as the format for a strftime call.
-#today_fmt = '%B %d, %Y'
+# today_fmt = '%B %d, %Y'
 
 
 # List of patterns, relative to source directory, that match files and
 # List of patterns, relative to source directory, that match files and
 # directories to ignore when looking for source files.
 # directories to ignore when looking for source files.
-exclude_patterns = ['_build']
+exclude_patterns = ["_build"]
 
 
 # The reST default role (used for this markup: `text`) to use for all
 # The reST default role (used for this markup: `text`) to use for all
 # documents.
 # documents.
-#default_role = None
+# default_role = None
 
 
 # If true, '()' will be appended to :func: etc. cross-reference text.
 # If true, '()' will be appended to :func: etc. cross-reference text.
-#add_function_parentheses = True
+# add_function_parentheses = True
 
 
 # If true, the current module name will be prepended to all description
 # If true, the current module name will be prepended to all description
 # unit titles (such as .. function::).
 # unit titles (such as .. function::).
-#add_module_names = True
+# add_module_names = True
 
 
 # If true, sectionauthor and moduleauthor directives will be shown in the
 # If true, sectionauthor and moduleauthor directives will be shown in the
 # output. They are ignored by default.
 # output. They are ignored by default.
-#show_authors = False
+# show_authors = False
 
 
 # The name of the Pygments (syntax highlighting) style to use.
 # The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
+pygments_style = "sphinx"
 
 
 # A list of ignored prefixes for module index sorting.
 # A list of ignored prefixes for module index sorting.
-#modindex_common_prefix = []
+# modindex_common_prefix = []
 
 
 # If true, keep warnings as "system message" paragraphs in the built documents.
 # If true, keep warnings as "system message" paragraphs in the built documents.
-#keep_warnings = False
+# keep_warnings = False
 
 
 
 
 # -- Options for HTML output ----------------------------------------------
 # -- Options for HTML output ----------------------------------------------
 
 
 # The theme to use for HTML and HTML Help pages.  See the documentation for
 # The theme to use for HTML and HTML Help pages.  See the documentation for
 # a list of builtin themes.
 # a list of builtin themes.
-html_theme = 'traditional'
+html_theme = "traditional"
 
 
 # Theme options are theme-specific and customize the look and feel of a theme
 # Theme options are theme-specific and customize the look and feel of a theme
 # further.  For a list of options available for each theme, see the
 # further.  For a list of options available for each theme, see the
 # documentation.
 # documentation.
-#html_theme_options = {}
+# html_theme_options = {}
 
 
 # Add any paths that contain custom themes here, relative to this directory.
 # Add any paths that contain custom themes here, relative to this directory.
-#html_theme_path = []
+# html_theme_path = []
 
 
 # The name for this set of Sphinx documents.  If None, it defaults to
 # The name for this set of Sphinx documents.  If None, it defaults to
 # "<project> v<release> documentation".
 # "<project> v<release> documentation".
-#html_title = None
+# html_title = None
 
 
 # A shorter title for the navigation bar.  Default is the same as html_title.
 # A shorter title for the navigation bar.  Default is the same as html_title.
-#html_short_title = None
+# html_short_title = None
 
 
 # The name of an image file (relative to this directory) to place at the top
 # The name of an image file (relative to this directory) to place at the top
 # of the sidebar.
 # of the sidebar.
-#html_logo = None
+# html_logo = None
 
 
 # The name of an image file (within the static path) to use as favicon of the
 # The name of an image file (within the static path) to use as favicon of the
 # docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
 # docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
 # pixels large.
 # pixels large.
-#html_favicon = None
+# html_favicon = None
 
 
 # Add any paths that contain custom static files (such as style sheets) here,
 # Add any paths that contain custom static files (such as style sheets) here,
 # relative to this directory. They are copied after the builtin static files,
 # relative to this directory. They are copied after the builtin static files,
 # so a file named "default.css" will overwrite the builtin "default.css".
 # so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = ['_static']
+html_static_path = ["_static"]
 
 
 # Add any extra paths that contain custom files (such as robots.txt or
 # Add any extra paths that contain custom files (such as robots.txt or
 # .htaccess) here, relative to this directory. These files are copied
 # .htaccess) here, relative to this directory. These files are copied
 # directly to the root of the documentation.
 # directly to the root of the documentation.
-#html_extra_path = []
+# html_extra_path = []
 
 
 # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
 # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
 # using the given strftime format.
 # using the given strftime format.
-#html_last_updated_fmt = '%b %d, %Y'
+# html_last_updated_fmt = '%b %d, %Y'
 
 
 # If true, SmartyPants will be used to convert quotes and dashes to
 # If true, SmartyPants will be used to convert quotes and dashes to
 # typographically correct entities.
 # typographically correct entities.
-#html_use_smartypants = True
+# html_use_smartypants = True
 
 
 # Custom sidebar templates, maps document names to template names.
 # Custom sidebar templates, maps document names to template names.
-html_sidebars = {"**":["localtoc.html",'relations.html','searchbox.html']}
+html_sidebars = {"**": ["localtoc.html", "relations.html", "searchbox.html"]}
 
 
 # Additional templates that should be rendered to pages, maps page names to
 # Additional templates that should be rendered to pages, maps page names to
 # template names.
 # template names.
-#html_additional_pages = {}
+# html_additional_pages = {}
 
 
 # If false, no module index is generated.
 # If false, no module index is generated.
-#html_domain_indices = True
+# html_domain_indices = True
 
 
 # If false, no index is generated.
 # If false, no index is generated.
 html_use_index = True
 html_use_index = True
 
 
 # If true, the index is split into individual pages for each letter.
 # If true, the index is split into individual pages for each letter.
-#html_split_index = False
+# html_split_index = False
 
 
 # If true, links to the reST sources are added to the pages.
 # If true, links to the reST sources are added to the pages.
 html_show_sourcelink = True
 html_show_sourcelink = True
@@ -210,55 +253,58 @@ html_show_copyright = True
 # If true, an OpenSearch description file will be output, and all pages will
 # If true, an OpenSearch description file will be output, and all pages will
 # contain a <link> tag referring to it.  The value of this option must be the
 # contain a <link> tag referring to it.  The value of this option must be the
 # base URL from which the finished HTML is served.
 # base URL from which the finished HTML is served.
-#html_use_opensearch = ''
+# html_use_opensearch = ''
 
 
 # This is the file name suffix for HTML files (e.g. ".xhtml").
 # This is the file name suffix for HTML files (e.g. ".xhtml").
-#html_file_suffix = None
+# html_file_suffix = None
 
 
 # Output file base name for HTML help builder.
 # Output file base name for HTML help builder.
-htmlhelp_basename = 'PythonLibdoc'
+htmlhelp_basename = "PythonLibdoc"
 
 
 
 
 # -- Options for LaTeX output ---------------------------------------------
 # -- Options for LaTeX output ---------------------------------------------
 
 
 latex_elements = {
 latex_elements = {
     # The paper size ('letterpaper' or 'a4paper').
     # The paper size ('letterpaper' or 'a4paper').
-    'papersize': 'a4paper',
-
+    "papersize": "a4paper",
     # The font size ('10pt', '11pt' or '12pt').
     # The font size ('10pt', '11pt' or '12pt').
-    'pointsize': '10pt',
-
+    "pointsize": "10pt",
     # Additional stuff for the LaTeX preamble.
     # Additional stuff for the LaTeX preamble.
-    #'preamble': '',
+    # 'preamble': '',
 }
 }
 
 
 # Grouping the document tree into LaTeX files. List of tuples
 # Grouping the document tree into LaTeX files. List of tuples
 # (source start file, target name, title,
 # (source start file, target name, title,
 #  author, documentclass [howto, manual, or own class]).
 #  author, documentclass [howto, manual, or own class]).
 latex_documents = [
 latex_documents = [
-  ('index', 'PythonLib.tex', u'Python Library Documentation',
-   u'GRASS Development Team', 'manual'),
+    (
+        "index",
+        "PythonLib.tex",
+        "Python Library Documentation",
+        "GRASS Development Team",
+        "manual",
+    ),
 ]
 ]
 
 
 # The name of an image file (relative to this directory) to place at the top of
 # The name of an image file (relative to this directory) to place at the top of
 # the title page.
 # the title page.
-#latex_logo = None
+# latex_logo = None
 
 
 # For "manual" documents, if this is true, then toplevel headings are parts,
 # For "manual" documents, if this is true, then toplevel headings are parts,
 # not chapters.
 # not chapters.
-#latex_use_parts = False
+# latex_use_parts = False
 
 
 # If true, show page references after internal links.
 # If true, show page references after internal links.
-#latex_show_pagerefs = False
+# latex_show_pagerefs = False
 
 
 # If true, show URL addresses after external links.
 # If true, show URL addresses after external links.
-#latex_show_urls = False
+# latex_show_urls = False
 
 
 # Documents to append as an appendix to all manuals.
 # Documents to append as an appendix to all manuals.
-#latex_appendices = []
+# latex_appendices = []
 
 
 # If false, no module index is generated.
 # If false, no module index is generated.
-#latex_domain_indices = True
+# latex_domain_indices = True
 
 
 
 
 # -- Options for manual page output ---------------------------------------
 # -- Options for manual page output ---------------------------------------
@@ -266,12 +312,17 @@ latex_documents = [
 # One entry per manual page. List of tuples
 # One entry per manual page. List of tuples
 # (source start file, name, description, authors, manual section).
 # (source start file, name, description, authors, manual section).
 man_pages = [
 man_pages = [
-    ('index', 'PythonLib', u'Python Library Documentation',
-     [u'GRASS Development Team'], 1)
+    (
+        "index",
+        "PythonLib",
+        "Python Library Documentation",
+        ["GRASS Development Team"],
+        1,
+    )
 ]
 ]
 
 
 # If true, show URL addresses after external links.
 # If true, show URL addresses after external links.
-#man_show_urls = False
+# man_show_urls = False
 
 
 
 
 # -- Options for Texinfo output -------------------------------------------
 # -- Options for Texinfo output -------------------------------------------
@@ -280,89 +331,95 @@ man_pages = [
 # (source start file, target name, title, author,
 # (source start file, target name, title, author,
 #  dir menu entry, description, category)
 #  dir menu entry, description, category)
 texinfo_documents = [
 texinfo_documents = [
-  ('index', 'PythonLib', u'Python Library Documentation',
-   u'GRASS Development Team', 'PythonLib', 'One line description of project.',
-   'Miscellaneous'),
+    (
+        "index",
+        "PythonLib",
+        "Python Library Documentation",
+        "GRASS Development Team",
+        "PythonLib",
+        "One line description of project.",
+        "Miscellaneous",
+    ),
 ]
 ]
 
 
 # Documents to append as an appendix to all manuals.
 # Documents to append as an appendix to all manuals.
-#texinfo_appendices = []
+# texinfo_appendices = []
 
 
 # If false, no module index is generated.
 # If false, no module index is generated.
-#texinfo_domain_indices = True
+# texinfo_domain_indices = True
 
 
 # How to display URL addresses: 'footnote', 'no', or 'inline'.
 # How to display URL addresses: 'footnote', 'no', or 'inline'.
-#texinfo_show_urls = 'footnote'
+# texinfo_show_urls = 'footnote'
 
 
 # If true, do not generate a @detailmenu in the "Top" node's menu.
 # If true, do not generate a @detailmenu in the "Top" node's menu.
-#texinfo_no_detailmenu = False
+# texinfo_no_detailmenu = False
 
 
 
 
 # -- Options for Epub output ----------------------------------------------
 # -- Options for Epub output ----------------------------------------------
 
 
 # Bibliographic Dublin Core info.
 # Bibliographic Dublin Core info.
-epub_title = u'PythonLib'
-epub_author = u'GRASS Development Team'
-epub_publisher = u'GRASS Development Team'
-epub_copyright = u'2014, GRASS Development Team'
+epub_title = "PythonLib"
+epub_author = "GRASS Development Team"
+epub_publisher = "GRASS Development Team"
+epub_copyright = "2014, GRASS Development Team"
 
 
 # The basename for the epub file. It defaults to the project name.
 # The basename for the epub file. It defaults to the project name.
-#epub_basename = u'wxGUI'
+# epub_basename = u'wxGUI'
 
 
 # The HTML theme for the epub output. Since the default themes are not optimized
 # The HTML theme for the epub output. Since the default themes are not optimized
 # for small screen space, using the same theme for HTML and epub output is
 # for small screen space, using the same theme for HTML and epub output is
 # usually not wise. This defaults to 'epub', a theme designed to save visual
 # usually not wise. This defaults to 'epub', a theme designed to save visual
 # space.
 # space.
-#epub_theme = 'epub'
+# epub_theme = 'epub'
 
 
 # The language of the text. It defaults to the language option
 # The language of the text. It defaults to the language option
 # or en if the language is not set.
 # or en if the language is not set.
-#epub_language = ''
+# epub_language = ''
 
 
 # The scheme of the identifier. Typical schemes are ISBN or URL.
 # The scheme of the identifier. Typical schemes are ISBN or URL.
-#epub_scheme = ''
+# epub_scheme = ''
 
 
 # The unique identifier of the text. This can be a ISBN number
 # The unique identifier of the text. This can be a ISBN number
 # or the project homepage.
 # or the project homepage.
-#epub_identifier = ''
+# epub_identifier = ''
 
 
 # A unique identification for the text.
 # A unique identification for the text.
-#epub_uid = ''
+# epub_uid = ''
 
 
 # A tuple containing the cover image and cover page html template filenames.
 # A tuple containing the cover image and cover page html template filenames.
-#epub_cover = ()
+# epub_cover = ()
 
 
 # A sequence of (type, uri, title) tuples for the guide element of content.opf.
 # A sequence of (type, uri, title) tuples for the guide element of content.opf.
-#epub_guide = ()
+# epub_guide = ()
 
 
 # HTML files that should be inserted before the pages created by sphinx.
 # HTML files that should be inserted before the pages created by sphinx.
 # The format is a list of tuples containing the path and title.
 # The format is a list of tuples containing the path and title.
-#epub_pre_files = []
+# epub_pre_files = []
 
 
 # HTML files shat should be inserted after the pages created by sphinx.
 # HTML files shat should be inserted after the pages created by sphinx.
 # The format is a list of tuples containing the path and title.
 # The format is a list of tuples containing the path and title.
-#epub_post_files = []
+# epub_post_files = []
 
 
 # A list of files that should not be packed into the epub file.
 # A list of files that should not be packed into the epub file.
-epub_exclude_files = ['search.html']
+epub_exclude_files = ["search.html"]
 
 
 # The depth of the table of contents in toc.ncx.
 # The depth of the table of contents in toc.ncx.
-#epub_tocdepth = 3
+# epub_tocdepth = 3
 
 
 # Allow duplicate toc entries.
 # Allow duplicate toc entries.
-#epub_tocdup = True
+# epub_tocdup = True
 
 
 # Choose between 'default' and 'includehidden'.
 # Choose between 'default' and 'includehidden'.
-#epub_tocscope = 'default'
+# epub_tocscope = 'default'
 
 
 # Fix unsupported image types using the PIL.
 # Fix unsupported image types using the PIL.
-#epub_fix_images = False
+# epub_fix_images = False
 
 
 # Scale large images.
 # Scale large images.
-#epub_max_image_width = 0
+# epub_max_image_width = 0
 
 
 # How to display URL addresses: 'footnote', 'no', or 'inline'.
 # How to display URL addresses: 'footnote', 'no', or 'inline'.
-#epub_show_urls = 'inline'
+# epub_show_urls = 'inline'
 
 
 # If false, no index is generated.
 # If false, no index is generated.
-#epub_use_index = True
+# epub_use_index = True

+ 5 - 5
python/grass/exceptions/__init__.py

@@ -35,11 +35,11 @@ class ParameterError(Exception):
 class ScriptError(Exception):
 class ScriptError(Exception):
     """Raised during script execution. ::
     """Raised during script execution. ::
 
 
-        >>> error = ScriptError('My error message!')
-        >>> error.value
-        'My error message!'
-        >>> print(error)
-        My error message!
+    >>> error = ScriptError('My error message!')
+    >>> error.value
+    'My error message!'
+    >>> print(error)
+    My error message!
     """
     """
 
 
     def __init__(self, value):
     def __init__(self, value):

+ 5 - 6
python/grass/exceptions/testsuite/test_ScriptError.py

@@ -6,15 +6,14 @@ from grass.exceptions import ScriptError
 
 
 
 
 class TestTextAssertions(TestCase):
 class TestTextAssertions(TestCase):
-
     def test_get_value(self):
     def test_get_value(self):
-        error = ScriptError('error')
-        self.assertEqual('error', error.value)
+        error = ScriptError("error")
+        self.assertEqual("error", error.value)
 
 
     def test_str(self):
     def test_str(self):
-        error = ScriptError('error')
-        self.assertEqual('error', str(error))
+        error = ScriptError("error")
+        self.assertEqual("error", str(error))
 
 
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     test()
     test()

+ 63 - 46
python/grass/grassdb/checks.py

@@ -65,17 +65,18 @@ def is_location_valid(database, location):
 
 
 def is_mapset_current(database, location, mapset):
 def is_mapset_current(database, location, mapset):
     genv = gisenv()
     genv = gisenv()
-    if (database == genv['GISDBASE'] and
-            location == genv['LOCATION_NAME'] and
-            mapset == genv['MAPSET']):
+    if (
+        database == genv["GISDBASE"]
+        and location == genv["LOCATION_NAME"]
+        and mapset == genv["MAPSET"]
+    ):
         return True
         return True
     return False
     return False
 
 
 
 
 def is_location_current(database, location):
 def is_location_current(database, location):
     genv = gisenv()
     genv = gisenv()
-    if (database == genv['GISDBASE'] and
-            location == genv['LOCATION_NAME']):
+    if database == genv["GISDBASE"] and location == genv["LOCATION_NAME"]:
         return True
         return True
     return False
     return False
 
 
@@ -90,7 +91,7 @@ def is_current_user_mapset_owner(mapset_path):
         # Mapset just needs to be accessible for writing.
         # Mapset just needs to be accessible for writing.
         return os.access(mapset_path, os.W_OK)
         return os.access(mapset_path, os.W_OK)
     # Mapset needs to be owned by user.
     # Mapset needs to be owned by user.
-    if sys.platform == 'win32':
+    if sys.platform == "win32":
         return True
         return True
     stat_info = os.stat(mapset_path)
     stat_info = os.stat(mapset_path)
     mapset_uid = stat_info.st_uid
     mapset_uid = stat_info.st_uid
@@ -105,7 +106,7 @@ def is_different_mapset_owner(mapset_path):
 def get_mapset_owner(mapset_path):
 def get_mapset_owner(mapset_path):
     """Returns mapset owner name or None if owner name unknown.
     """Returns mapset owner name or None if owner name unknown.
     On Windows it always returns None."""
     On Windows it always returns None."""
-    if sys.platform == 'win32':
+    if sys.platform == "win32":
         return None
         return None
     try:
     try:
         path = Path(mapset_path)
         path = Path(mapset_path)
@@ -115,7 +116,7 @@ def get_mapset_owner(mapset_path):
 
 
 
 
 def is_current_mapset_in_demolocation():
 def is_current_mapset_in_demolocation():
-    return gisenv()['LOCATION_NAME'] == "world_latlong_wgs84"
+    return gisenv()["LOCATION_NAME"] == "world_latlong_wgs84"
 
 
 
 
 def is_mapset_locked(mapset_path):
 def is_mapset_locked(mapset_path):
@@ -146,13 +147,14 @@ def get_mapset_lock_info(mapset_path):
     """
     """
     info = {}
     info = {}
     lock_name = ".gislock"
     lock_name = ".gislock"
-    info['lockpath'] = os.path.join(mapset_path, lock_name)
+    info["lockpath"] = os.path.join(mapset_path, lock_name)
     try:
     try:
-        info['owner'] = Path(info['lockpath']).owner()
+        info["owner"] = Path(info["lockpath"]).owner()
     except KeyError:
     except KeyError:
-        info['owner'] = None
-    info['timestamp'] = (datetime.datetime.fromtimestamp(
-        os.path.getmtime(info['lockpath']))).replace(microsecond=0)
+        info["owner"] = None
+    info["timestamp"] = (
+        datetime.datetime.fromtimestamp(os.path.getmtime(info["lockpath"]))
+    ).replace(microsecond=0)
     return info
     return info
 
 
 
 
@@ -338,18 +340,21 @@ def get_mapset_name_invalid_reason(database, location, mapset_name):
         message = _(
         message = _(
             "Name '{}' is not a valid name for location or mapset. "
             "Name '{}' is not a valid name for location or mapset. "
             "Please use only ASCII characters excluding characters {} "
             "Please use only ASCII characters excluding characters {} "
-            "and space.").format(mapset_name, '/"\'@,=*~')
+            "and space."
+        ).format(mapset_name, "/\"'@,=*~")
     # Check reserved mapset name
     # Check reserved mapset name
-    elif mapset_name.lower() == 'ogr':
+    elif mapset_name.lower() == "ogr":
         message = _(
         message = _(
             "Name '{}' is reserved for direct "
             "Name '{}' is reserved for direct "
             "read access to OGR layers. Please use "
             "read access to OGR layers. Please use "
-            "another name for your mapset.").format(mapset_name)
+            "another name for your mapset."
+        ).format(mapset_name)
     # Check whether mapset exists
     # Check whether mapset exists
     elif mapset_exists(database, location, mapset_name):
     elif mapset_exists(database, location, mapset_name):
         message = _(
         message = _(
             "Mapset  <{mapset}> already exists. Please consider using "
             "Mapset  <{mapset}> already exists. Please consider using "
-            "another name for your mapset.").format(mapset=mapset_path)
+            "another name for your mapset."
+        ).format(mapset=mapset_path)
 
 
     return message
     return message
 
 
@@ -371,12 +376,14 @@ def get_location_name_invalid_reason(grassdb, location_name):
         message = _(
         message = _(
             "Name '{}' is not a valid name for location or mapset. "
             "Name '{}' is not a valid name for location or mapset. "
             "Please use only ASCII characters excluding characters {} "
             "Please use only ASCII characters excluding characters {} "
-            "and space.").format(location_name, '/"\'@,=*~')
+            "and space."
+        ).format(location_name, "/\"'@,=*~")
     # Check whether location exists
     # Check whether location exists
     elif location_exists(grassdb, location_name):
     elif location_exists(grassdb, location_name):
         message = _(
         message = _(
             "Location  <{location}> already exists. Please consider using "
             "Location  <{location}> already exists. Please consider using "
-            "another name for your location.").format(location=location_path)
+            "another name for your location."
+        ).format(location=location_path)
 
 
     return message
     return message
 
 
@@ -386,8 +393,11 @@ def is_mapset_name_valid(database, location, mapset_name):
 
 
     Returns True if mapset name is valid, otherwise False.
     Returns True if mapset name is valid, otherwise False.
     """
     """
-    return gs.legal_name(mapset_name) and mapset_name.lower() != "ogr" and not \
-        mapset_exists(database, location, mapset_name)
+    return (
+        gs.legal_name(mapset_name)
+        and mapset_name.lower() != "ogr"
+        and not mapset_exists(database, location, mapset_name)
+    )
 
 
 
 
 def is_location_name_valid(database, location_name):
 def is_location_name_valid(database, location_name):
@@ -395,8 +405,7 @@ def is_location_name_valid(database, location_name):
 
 
     Returns True if location name is valid, otherwise False.
     Returns True if location name is valid, otherwise False.
     """
     """
-    return gs.legal_name(location_name) and not \
-        location_exists(database, location_name)
+    return gs.legal_name(location_name) and not location_exists(database, location_name)
 
 
 
 
 def get_reasons_mapsets_not_removable(mapsets, check_permanent):
 def get_reasons_mapsets_not_removable(mapsets, check_permanent):
@@ -410,8 +419,9 @@ def get_reasons_mapsets_not_removable(mapsets, check_permanent):
     """
     """
     messages = []
     messages = []
     for grassdb, location, mapset in mapsets:
     for grassdb, location, mapset in mapsets:
-        message = get_reason_mapset_not_removable(grassdb, location,
-                                                  mapset, check_permanent)
+        message = get_reason_mapset_not_removable(
+            grassdb, location, mapset, check_permanent
+        )
         if message:
         if message:
             messages.append(message)
             messages.append(message)
     return messages
     return messages
@@ -431,19 +441,21 @@ def get_reason_mapset_not_removable(grassdb, location, mapset, check_permanent):
     # Check if mapset is permanent
     # Check if mapset is permanent
     if check_permanent and mapset == "PERMANENT":
     if check_permanent and mapset == "PERMANENT":
         message = _("Mapset <{mapset}> is required for a valid location.").format(
         message = _("Mapset <{mapset}> is required for a valid location.").format(
-            mapset=mapset_path)
+            mapset=mapset_path
+        )
     # Check if mapset is current
     # Check if mapset is current
     elif is_mapset_current(grassdb, location, mapset):
     elif is_mapset_current(grassdb, location, mapset):
         message = _("Mapset <{mapset}> is the current mapset.").format(
         message = _("Mapset <{mapset}> is the current mapset.").format(
-            mapset=mapset_path)
+            mapset=mapset_path
+        )
     # Check whether mapset is in use
     # Check whether mapset is in use
     elif is_mapset_locked(mapset_path):
     elif is_mapset_locked(mapset_path):
-        message = _("Mapset <{mapset}> is in use.").format(
-            mapset=mapset_path)
+        message = _("Mapset <{mapset}> is in use.").format(mapset=mapset_path)
     # Check whether mapset is owned by different user
     # Check whether mapset is owned by different user
     elif is_different_mapset_owner(mapset_path):
     elif is_different_mapset_owner(mapset_path):
         message = _("Mapset <{mapset}> is owned by a different user.").format(
         message = _("Mapset <{mapset}> is owned by a different user.").format(
-            mapset=mapset_path)
+            mapset=mapset_path
+        )
 
 
     return message
     return message
 
 
@@ -471,20 +483,22 @@ def get_reasons_location_not_removable(grassdb, location):
 
 
     # Check if location is current
     # Check if location is current
     if is_location_current(grassdb, location):
     if is_location_current(grassdb, location):
-        messages.append(_("Location <{location}> is the current location.").format(
-            location=location_path))
+        messages.append(
+            _("Location <{location}> is the current location.").format(
+                location=location_path
+            )
+        )
         return messages
         return messages
 
 
     # Find mapsets in particular location
     # Find mapsets in particular location
-    tmp_gisrc_file, env = gs.create_environment(grassdb, location, 'PERMANENT')
-    env['GRASS_SKIP_MAPSET_OWNER_CHECK'] = '1'
+    tmp_gisrc_file, env = gs.create_environment(grassdb, location, "PERMANENT")
+    env["GRASS_SKIP_MAPSET_OWNER_CHECK"] = "1"
 
 
-    g_mapsets = gs.read_command(
-        'g.mapsets',
-        flags='l',
-        separator='comma',
-        quiet=True,
-        env=env).strip().split(',')
+    g_mapsets = (
+        gs.read_command("g.mapsets", flags="l", separator="comma", quiet=True, env=env)
+        .strip()
+        .split(",")
+    )
 
 
     # Append to the list of tuples
     # Append to the list of tuples
     mapsets = []
     mapsets = []
@@ -507,9 +521,12 @@ def get_reasons_grassdb_not_removable(grassdb):
     genv = gisenv()
     genv = gisenv()
 
 
     # Check if grassdb is current
     # Check if grassdb is current
-    if grassdb == genv['GISDBASE']:
-        messages.append(_("GRASS database <{grassdb}> is the current database.").format(
-            grassdb=grassdb))
+    if grassdb == genv["GISDBASE"]:
+        messages.append(
+            _("GRASS database <{grassdb}> is the current database.").format(
+                grassdb=grassdb
+            )
+        )
         return messages
         return messages
 
 
     g_locations = get_list_of_locations(grassdb)
     g_locations = get_list_of_locations(grassdb)
@@ -532,9 +549,9 @@ def get_list_of_locations(dbase):
     """
     """
     locations = list()
     locations = list()
     for location in glob.glob(os.path.join(dbase, "*")):
     for location in glob.glob(os.path.join(dbase, "*")):
-        if os.path.join(
-                location, "PERMANENT") in glob.glob(
-                os.path.join(location, "*")):
+        if os.path.join(location, "PERMANENT") in glob.glob(
+            os.path.join(location, "*")
+        ):
             locations.append(os.path.basename(location))
             locations.append(os.path.basename(location))
 
 
     locations.sort(key=lambda x: x.lower())
     locations.sort(key=lambda x: x.lower())

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 545 - 363
python/grass/gunittest/case.py


+ 96 - 74
python/grass/gunittest/checkers.py

@@ -50,13 +50,12 @@ def unify_projection(dic):
     """
     """
     # the lookup variable is a list of list, each list contains all the
     # the lookup variable is a list of list, each list contains all the
     # possible name for a projection system
     # possible name for a projection system
-    lookup = [['Universal Transverse Mercator',
-               'Universe Transverse Mercator']]
+    lookup = [["Universal Transverse Mercator", "Universe Transverse Mercator"]]
     dic = dict(dic)
     dic = dict(dic)
     for l in lookup:
     for l in lookup:
-        for n in range(len(dic['name'])):
-            if dic['name'][n] in l:
-                dic['name'][n] = l[0]
+        for n in range(len(dic["name"])):
+            if dic["name"][n] in l:
+                dic["name"][n] = l[0]
     return dic
     return dic
 
 
 
 
@@ -77,27 +76,32 @@ def unify_units(dic):
     """
     """
     # the lookup variable is a list of list, each list contains all the
     # the lookup variable is a list of list, each list contains all the
     # possible name for a units
     # possible name for a units
-    lookup = [['meter', 'metre'], ['meters', 'metres'],
-              ['Meter', 'Metre'], ['Meters', 'Metres'],
-              ['kilometer', 'kilometre'], ['kilometers', 'kilometres'],
-              ['Kilometer', 'Kilometre'], ['Kilometers', 'Kilometres'],
-              ]
+    lookup = [
+        ["meter", "metre"],
+        ["meters", "metres"],
+        ["Meter", "Metre"],
+        ["Meters", "Metres"],
+        ["kilometer", "kilometre"],
+        ["kilometers", "kilometres"],
+        ["Kilometer", "Kilometre"],
+        ["Kilometers", "Kilometres"],
+    ]
     dic = dict(dic)
     dic = dict(dic)
     for l in lookup:
     for l in lookup:
-        if not isinstance(dic['unit'], str):
-            for n in range(len(dic['unit'])):
-                if dic['unit'][n] in l:
-                    dic['unit'][n] = l[0]
+        if not isinstance(dic["unit"], str):
+            for n in range(len(dic["unit"])):
+                if dic["unit"][n] in l:
+                    dic["unit"][n] = l[0]
         else:
         else:
-            if dic['unit'] in l:
-                dic['unit'] = l[0]
-        if not isinstance(dic['units'], str):
-            for n in range(len(dic['units'])):
-                if dic['units'][n] in l:
-                    dic['units'][n] = l[0]
+            if dic["unit"] in l:
+                dic["unit"] = l[0]
+        if not isinstance(dic["units"], str):
+            for n in range(len(dic["units"])):
+                if dic["units"][n] in l:
+                    dic["units"][n] = l[0]
         else:
         else:
-            if dic['units'] in l:
-                dic['units'] = l[0]
+            if dic["units"] in l:
+                dic["units"] = l[0]
     return dic
     return dic
 
 
 
 
@@ -138,9 +142,15 @@ def value_from_string(value):
 
 
 
 
 # TODO: what is the default separator?
 # TODO: what is the default separator?
-def text_to_keyvalue(text, sep=":", val_sep=",", functions=None,
-                     skip_invalid=False, skip_empty=False,
-                     from_string=value_from_string):
+def text_to_keyvalue(
+    text,
+    sep=":",
+    val_sep=",",
+    functions=None,
+    skip_invalid=False,
+    skip_empty=False,
+    from_string=value_from_string,
+):
     """Convert test to key-value pairs (dictionary-like KeyValue object).
     """Convert test to key-value pairs (dictionary-like KeyValue object).
 
 
     Converts a key-value text file, where entries are separated
     Converts a key-value text file, where entries are separated
@@ -199,18 +209,23 @@ def text_to_keyvalue(text, sep=":", val_sep=",", functions=None,
                     # TODO: here should go _ for translation
                     # TODO: here should go _ for translation
                     # TODO: the error message is not really informative
                     # TODO: the error message is not really informative
                     # in case of skipping lines we may get here with no key
                     # in case of skipping lines we may get here with no key
-                    msg = ("Empty line in the parsed text.")
+                    msg = "Empty line in the parsed text."
                     if kvdict:
                     if kvdict:
                         # key is the one from previous line
                         # key is the one from previous line
-                        msg = ("Empty line in the parsed text."
-                               " Previous line's key is <%s>") % key
+                        msg = (
+                            "Empty line in the parsed text."
+                            " Previous line's key is <%s>"
+                        ) % key
                     raise ValueError(msg)
                     raise ValueError(msg)
             else:
             else:
                 # line contains something but not separator
                 # line contains something but not separator
                 if not skip_invalid:
                 if not skip_invalid:
                     # TODO: here should go _ for translation
                     # TODO: here should go _ for translation
-                    raise ValueError(("Line <{l}> does not contain"
-                                      " separator <{s}>.").format(l=line, s=sep))
+                    raise ValueError(
+                        ("Line <{l}> does not contain" " separator <{s}>.").format(
+                            l=line, s=sep
+                        )
+                    )
             # if we get here we are silently ignoring the line
             # if we get here we are silently ignoring the line
             # because it is invalid (does not contain key-value separator) or
             # because it is invalid (does not contain key-value separator) or
             # because it is empty
             # because it is empty
@@ -259,8 +274,9 @@ def values_equal(value_a, value_b, precision=0.000001):
         if abs(value_a - value_b) > precision:
         if abs(value_a - value_b) > precision:
             return False
             return False
 
 
-    elif (isinstance(value_a, float) and isinstance(value_b, int)) or \
-            (isinstance(value_b, float) and isinstance(value_a, int)):
+    elif (isinstance(value_a, float) and isinstance(value_b, int)) or (
+        isinstance(value_b, float) and isinstance(value_a, int)
+    ):
         # on is float the other is int
         # on is float the other is int
         # don't accept None
         # don't accept None
         precision = float(precision)
         precision = float(precision)
@@ -270,8 +286,12 @@ def values_equal(value_a, value_b, precision=0.000001):
         if abs(value_a - value_b) > precision:
         if abs(value_a - value_b) > precision:
             return False
             return False
 
 
-    elif isinstance(value_a, int) and isinstance(value_b, int) and \
-            precision and int(precision) > 0:
+    elif (
+        isinstance(value_a, int)
+        and isinstance(value_b, int)
+        and precision
+        and int(precision) > 0
+    ):
         # both int but precision applies for them
         # both int but precision applies for them
         if abs(value_a - value_b) > precision:
         if abs(value_a - value_b) > precision:
             return False
             return False
@@ -289,9 +309,9 @@ def values_equal(value_a, value_b, precision=0.000001):
     return True
     return True
 
 
 
 
-def keyvalue_equals(dict_a, dict_b, precision,
-                    def_equal=values_equal, key_equal=None,
-                    a_is_subset=False):
+def keyvalue_equals(
+    dict_a, dict_b, precision, def_equal=values_equal, key_equal=None, a_is_subset=False
+):
     """Compare two dictionaries.
     """Compare two dictionaries.
 
 
     .. note::
     .. note::
@@ -350,9 +370,9 @@ def keyvalue_equals(dict_a, dict_b, precision,
 
 
 # TODO: should the return depend on the a_is_subset parameter?
 # TODO: should the return depend on the a_is_subset parameter?
 # this function must have the same interface and behavior as keyvalue_equals
 # this function must have the same interface and behavior as keyvalue_equals
-def diff_keyvalue(dict_a, dict_b, precision,
-                  def_equal=values_equal, key_equal=None,
-                  a_is_subset=False):
+def diff_keyvalue(
+    dict_a, dict_b, precision, def_equal=values_equal, key_equal=None, a_is_subset=False
+):
     """Determine the difference of two dictionaries.
     """Determine the difference of two dictionaries.
 
 
     The function returns missing keys and different values for common keys::
     The function returns missing keys and different values for common keys::
@@ -410,26 +430,30 @@ def diff_keyvalue(dict_a, dict_b, precision,
 
 
 def proj_info_equals(text_a, text_b):
 def proj_info_equals(text_a, text_b):
     """Test if two PROJ_INFO texts are equal."""
     """Test if two PROJ_INFO texts are equal."""
+
     def compare_sums(list_a, list_b, precision):
     def compare_sums(list_a, list_b, precision):
         """Compare difference of sums of two list using precision"""
         """Compare difference of sums of two list using precision"""
         # derived from the code in grass.script.core
         # derived from the code in grass.script.core
         if abs(sum(list_a) - sum(list_b)) > precision:
         if abs(sum(list_a) - sum(list_b)) > precision:
             return False
             return False
-    sep = ':'
-    val_sep = ','
-    key_equal = {'+towgs84': compare_sums}
-    dict_a = text_to_keyvalue(text_a, sep=sep, val_sep=val_sep,
-                              functions=[unify_projection])
-    dict_b = text_to_keyvalue(text_b, sep=sep, val_sep=val_sep,
-                              functions=[unify_projection])
-    return keyvalue_equals(dict_a, dict_b,
-                            precision=0.000001,
-                            def_equal=values_equal,
-                            key_equal=key_equal)
+
+    sep = ":"
+    val_sep = ","
+    key_equal = {"+towgs84": compare_sums}
+    dict_a = text_to_keyvalue(
+        text_a, sep=sep, val_sep=val_sep, functions=[unify_projection]
+    )
+    dict_b = text_to_keyvalue(
+        text_b, sep=sep, val_sep=val_sep, functions=[unify_projection]
+    )
+    return keyvalue_equals(
+        dict_a, dict_b, precision=0.000001, def_equal=values_equal, key_equal=key_equal
+    )
 
 
 
 
 def proj_units_equals(text_a, text_b):
 def proj_units_equals(text_a, text_b):
     """Test if two PROJ_UNITS texts are equal."""
     """Test if two PROJ_UNITS texts are equal."""
+
     def lowercase_equals(string_a, string_b, precision=None):
     def lowercase_equals(string_a, string_b, precision=None):
         # we don't need a warning for unused precision
         # we don't need a warning for unused precision
         # pylint: disable=W0613
         # pylint: disable=W0613
@@ -438,17 +462,15 @@ def proj_units_equals(text_a, text_b):
         Precision is accepted as require by `keyvalue_equals()` but ignored.
         Precision is accepted as require by `keyvalue_equals()` but ignored.
         """
         """
         return string_a.lower() == string_b.lower()
         return string_a.lower() == string_b.lower()
-    sep = ':'
-    val_sep = ','
-    key_equal = {'unit': lowercase_equals, 'units': lowercase_equals}
-    dict_a = text_to_keyvalue(text_a, sep=sep, val_sep=val_sep,
-                              functions=[unify_units])
-    dict_b = text_to_keyvalue(text_b, sep, val_sep,
-                              functions=[unify_units])
-    return keyvalue_equals(dict_a, dict_b,
-                            precision=0.000001,
-                            def_equal=values_equal,
-                            key_equal=key_equal)
+
+    sep = ":"
+    val_sep = ","
+    key_equal = {"unit": lowercase_equals, "units": lowercase_equals}
+    dict_a = text_to_keyvalue(text_a, sep=sep, val_sep=val_sep, functions=[unify_units])
+    dict_b = text_to_keyvalue(text_b, sep, val_sep, functions=[unify_units])
+    return keyvalue_equals(
+        dict_a, dict_b, precision=0.000001, def_equal=values_equal, key_equal=key_equal
+    )
 
 
 
 
 # TODO: support also float (with E, e, inf, nan, ...?) and int (###, ##.)
 # TODO: support also float (with E, e, inf, nan, ...?) and int (###, ##.)
@@ -500,8 +522,8 @@ def check_text_ellipsis(reference, actual):
     False
     False
     """
     """
     ref_escaped = re.escape(reference)
     ref_escaped = re.escape(reference)
-    exp = re.compile(r'\\\.\\\.\\\.')  # matching escaped ...
-    ref_regexp = exp.sub('.+', ref_escaped) + "$"
+    exp = re.compile(r"\\\.\\\.\\\.")  # matching escaped ...
+    ref_regexp = exp.sub(".+", ref_escaped) + "$"
     if re.match(ref_regexp, actual, re.DOTALL):
     if re.match(ref_regexp, actual, re.DOTALL):
         return True
         return True
     else:
     else:
@@ -551,19 +573,18 @@ def check_text_ellipsis_doctest(reference, actual):
     """
     """
     # this can be also global
     # this can be also global
     checker = doctest.OutputChecker()
     checker = doctest.OutputChecker()
-    return checker.check_output(reference, actual,
-                                optionflags=doctest.ELLIPSIS)
+    return checker.check_output(reference, actual, optionflags=doctest.ELLIPSIS)
 
 
 
 
 # optimal size depends on file system and maybe on hasher.block_size
 # optimal size depends on file system and maybe on hasher.block_size
-_BUFFER_SIZE = 2**16
+_BUFFER_SIZE = 2 ** 16
 
 
 
 
 # TODO: accept also open file object
 # TODO: accept also open file object
 def file_md5(filename):
 def file_md5(filename):
     """Get MD5 (check) sum of a file."""
     """Get MD5 (check) sum of a file."""
     hasher = hashlib.md5()
     hasher = hashlib.md5()
-    with open(filename, 'rb') as f:
+    with open(filename, "rb") as f:
         buf = f.read(_BUFFER_SIZE)
         buf = f.read(_BUFFER_SIZE)
         while len(buf) > 0:
         while len(buf) > 0:
             hasher.update(buf)
             hasher.update(buf)
@@ -571,8 +592,9 @@ def file_md5(filename):
     return hasher.hexdigest()
     return hasher.hexdigest()
 
 
 
 
-def text_file_md5(filename, exclude_lines=None, exclude_re=None,
-                  prepend_lines=None, append_lines=None):
+def text_file_md5(
+    filename, exclude_lines=None, exclude_re=None, prepend_lines=None, append_lines=None
+):
     """Get a MD5 (check) sum of a text file.
     """Get a MD5 (check) sum of a text file.
 
 
     Works in the same way as `file_md5()` function but ignores newlines
     Works in the same way as `file_md5()` function but ignores newlines
@@ -594,11 +616,11 @@ def text_file_md5(filename, exclude_lines=None, exclude_re=None,
     if prepend_lines:
     if prepend_lines:
         for line in prepend_lines:
         for line in prepend_lines:
             hasher.update(line if sys.version_info[0] == 2 else encode(line))
             hasher.update(line if sys.version_info[0] == 2 else encode(line))
-    with open(filename, 'r') as f:
+    with open(filename, "r") as f:
         for line in f:
         for line in f:
             # replace platform newlines by standard newline
             # replace platform newlines by standard newline
-            if os.linesep != '\n':
-                line = line.rstrip(os.linesep) + '\n'
+            if os.linesep != "\n":
+                line = line.rstrip(os.linesep) + "\n"
             if exclude_lines and line in exclude_lines:
             if exclude_lines and line in exclude_lines:
                 continue
                 continue
             if exclude_re and regexp.match(line):
             if exclude_re and regexp.match(line):
@@ -621,5 +643,5 @@ def main():  # pragma: no cover
     return ret.failed
     return ret.failed
 
 
 
 
-if __name__ == '__main__':  # pragma: no cover
+if __name__ == "__main__":  # pragma: no cover
     sys.exit(main())
     sys.exit(main())

+ 26 - 19
python/grass/gunittest/gmodules.py

@@ -44,21 +44,27 @@ class SimpleModule(Module):
     """
     """
 
 
     def __init__(self, cmd, *args, **kargs):
     def __init__(self, cmd, *args, **kargs):
-        for banned in ['stdout_', 'stderr_', 'finish_', 'run_']:
+        for banned in ["stdout_", "stderr_", "finish_", "run_"]:
             if banned in kargs:
             if banned in kargs:
-                raise ValueError('Do not set %s parameter'
-                                 ', it would be overriden' % banned)
-        kargs['stdout_'] = subprocess.PIPE
-        kargs['stderr_'] = subprocess.PIPE
-        kargs['finish_'] = True
-        kargs['run_'] = False
+                raise ValueError(
+                    "Do not set %s parameter" ", it would be overriden" % banned
+                )
+        kargs["stdout_"] = subprocess.PIPE
+        kargs["stderr_"] = subprocess.PIPE
+        kargs["finish_"] = True
+        kargs["run_"] = False
 
 
         Module.__init__(self, cmd, *args, **kargs)
         Module.__init__(self, cmd, *args, **kargs)
 
 
 
 
-def call_module(module, stdin=None,
-                merge_stderr=False, capture_stdout=True, capture_stderr=True,
-                **kwargs):
+def call_module(
+    module,
+    stdin=None,
+    merge_stderr=False,
+    capture_stdout=True,
+    capture_stderr=True,
+    **kwargs,
+):
     r"""Run module with parameters given in `kwargs` and return its output.
     r"""Run module with parameters given in `kwargs` and return its output.
 
 
     >>> print (call_module('g.region', flags='pg'))  # doctest: +ELLIPSIS
     >>> print (call_module('g.region', flags='pg'))  # doctest: +ELLIPSIS
@@ -88,7 +94,8 @@ def call_module(module, stdin=None,
     :param merge_stderr: if the standard error output should be merged with stdout
     :param merge_stderr: if the standard error output should be merged with stdout
     :param kwargs: module parameters
     :param kwargs: module parameters
 
 
-    :returns: module standard output (stdout) as string or None if apture_stdout is False
+    :returns: module standard output (stdout) as string or None
+              if capture_stdout is False
 
 
     :raises CalledModuleError: if module return code is non-zero
     :raises CalledModuleError: if module return code is non-zero
     :raises ValueError: if the parameters are not correct
     :raises ValueError: if the parameters are not correct
@@ -101,27 +108,27 @@ def call_module(module, stdin=None,
     do_doctest_gettext_workaround()
     do_doctest_gettext_workaround()
     # implementation inspired by subprocess.check_output() function
     # implementation inspired by subprocess.check_output() function
     if stdin:
     if stdin:
-        if 'input' in kwargs and kwargs['input'] != '-':
+        if "input" in kwargs and kwargs["input"] != "-":
             raise ValueError(_("input='-' must be used when stdin is specified"))
             raise ValueError(_("input='-' must be used when stdin is specified"))
         if stdin == subprocess.PIPE:
         if stdin == subprocess.PIPE:
             raise ValueError(_("stdin must be string or buffer, not 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'] == '-':
+        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='-'"))
         raise ValueError(_("stdin must be used when input='-'"))
     if merge_stderr and not (capture_stdout and capture_stderr):
     if merge_stderr and not (capture_stdout and capture_stderr):
         raise ValueError(_("You cannot merge stdout and stderr and not capture them"))
         raise ValueError(_("You cannot merge stdout and stderr and not capture them"))
-    if 'stdout' in kwargs:
+    if "stdout" in kwargs:
         raise TypeError(_("stdout argument not allowed, it could be overridden"))
         raise TypeError(_("stdout argument not allowed, it could be overridden"))
-    if 'stderr' in kwargs:
+    if "stderr" in kwargs:
         raise TypeError(_("stderr argument not allowed, it could be overridden"))
         raise TypeError(_("stderr argument not allowed, it could be overridden"))
 
 
     if capture_stdout:
     if capture_stdout:
-        kwargs['stdout'] = subprocess.PIPE
+        kwargs["stdout"] = subprocess.PIPE
     if capture_stderr:
     if capture_stderr:
         if merge_stderr:
         if merge_stderr:
-            kwargs['stderr'] = subprocess.STDOUT
+            kwargs["stderr"] = subprocess.STDOUT
         else:
         else:
-            kwargs['stderr'] = subprocess.PIPE
+            kwargs["stderr"] = subprocess.PIPE
     process = start_command(module, **kwargs)
     process = start_command(module, **kwargs)
     # input=None means no stdin (our default)
     # input=None means no stdin (our default)
     # for no stdout, output is None which is out interface
     # for no stdout, output is None which is out interface

+ 19 - 12
python/grass/gunittest/gutils.py

@@ -18,7 +18,8 @@ from .checkers import text_to_keyvalue
 
 
 def get_current_mapset():
 def get_current_mapset():
     """Get curret mapset name as a string"""
     """Get curret mapset name as a string"""
-    return call_module('g.mapset', flags='p').strip()
+    return call_module("g.mapset", flags="p").strip()
+
 
 
 def is_map_in_mapset(name, type, mapset=None):
 def is_map_in_mapset(name, type, mapset=None):
     """Check is map is present in the mapset (current mapset by default)
     """Check is map is present in the mapset (current mapset by default)
@@ -38,22 +39,28 @@ def is_map_in_mapset(name, type, mapset=None):
     # so anything accepted by g.findfile will work but this can change in the
     # so anything accepted by g.findfile will work but this can change in the
     # future (the documentation is clear about what's legal)
     # future (the documentation is clear about what's legal)
     # supporting both short and full names
     # supporting both short and full names
-    if type == 'rast' or  type == 'raster':
-        type = 'cell'
-    elif type == 'rast3d' or type == 'raster3d':
-        type = 'grid3'
-    elif type == 'vect':
-        type = 'vector'
+    if type == "rast" or type == "raster":
+        type = "cell"
+    elif type == "rast3d" or type == "raster3d":
+        type = "grid3"
+    elif type == "vect":
+        type = "vector"
     # g.findfile returns non-zero when file was not found
     # g.findfile returns non-zero when file was not found
     # se we ignore return code and just focus on stdout
     # se we ignore return code and just focus on stdout
-    process = start_command('g.findfile', flags='n',
-                            element=type, file=name, mapset=mapset,
-                            stdout=PIPE, stderr=PIPE)
+    process = start_command(
+        "g.findfile",
+        flags="n",
+        element=type,
+        file=name,
+        mapset=mapset,
+        stdout=PIPE,
+        stderr=PIPE,
+    )
     output, errors = process.communicate()
     output, errors = process.communicate()
-    info = text_to_keyvalue(decode(output), sep='=')
+    info = text_to_keyvalue(decode(output), sep="=")
     # file is the key questioned in grass.script.core find_file()
     # file is the key questioned in grass.script.core find_file()
     # return code should be equivalent to checking the output
     # return code should be equivalent to checking the output
-    if info['file']:
+    if info["file"]:
         return True
         return True
     else:
     else:
         return False
         return False

+ 130 - 87
python/grass/gunittest/invoker.py

@@ -17,11 +17,16 @@ import subprocess
 from .checkers import text_to_keyvalue
 from .checkers import text_to_keyvalue
 
 
 from .loader import GrassTestLoader, discover_modules
 from .loader import GrassTestLoader, discover_modules
-from .reporters import (GrassTestFilesMultiReporter,
-                        GrassTestFilesTextReporter, GrassTestFilesHtmlReporter,
-                        TestsuiteDirReporter, GrassTestFilesKeyValueReporter,
-                        get_svn_path_authors,
-                        NoopFileAnonymizer, keyvalue_to_text)
+from .reporters import (
+    GrassTestFilesMultiReporter,
+    GrassTestFilesTextReporter,
+    GrassTestFilesHtmlReporter,
+    TestsuiteDirReporter,
+    GrassTestFilesKeyValueReporter,
+    get_svn_path_authors,
+    NoopFileAnonymizer,
+    keyvalue_to_text,
+)
 from .utils import silent_rmtree, ensure_dir
 from .utils import silent_rmtree, ensure_dir
 
 
 from grass.script.utils import decode, _get_encoding
 from grass.script.utils import decode, _get_encoding
@@ -43,8 +48,8 @@ import collections
 # TODO: this might be more extend then update
 # TODO: this might be more extend then update
 def update_keyval_file(filename, module, returncode):
 def update_keyval_file(filename, module, returncode):
     if os.path.exists(filename):
     if os.path.exists(filename):
-        with open(filename, 'r') as keyval_file:
-            keyval = text_to_keyvalue(keyval_file.read(), sep='=')
+        with open(filename, "r") as keyval_file:
+            keyval = text_to_keyvalue(keyval_file.read(), sep="=")
     else:
     else:
         keyval = {}
         keyval = {}
 
 
@@ -52,17 +57,17 @@ def update_keyval_file(filename, module, returncode):
     test_file_authors = get_svn_path_authors(module.abs_file_path)
     test_file_authors = get_svn_path_authors(module.abs_file_path)
     # in case that SVN is not available use empty authors
     # in case that SVN is not available use empty authors
     if test_file_authors is None:
     if test_file_authors is None:
-        test_file_authors = ''
+        test_file_authors = ""
 
 
     # always owerwrite name and status
     # always owerwrite name and status
-    keyval['name'] = module.name
-    keyval['tested_dir'] = module.tested_dir
-    if 'status' not in keyval.keys():
-        keyval['status'] = 'failed' if returncode else 'passed'
-    keyval['returncode'] = returncode
-    keyval['test_file_authors'] = test_file_authors
-
-    with open(filename, 'w') as keyval_file:
+    keyval["name"] = module.name
+    keyval["tested_dir"] = module.tested_dir
+    if "status" not in keyval.keys():
+        keyval["status"] = "failed" if returncode else "passed"
+    keyval["returncode"] = returncode
+    keyval["test_file_authors"] = test_file_authors
+
+    with open(filename, "w") as keyval_file:
         keyval_file.write(keyvalue_to_text(keyval))
         keyval_file.write(keyvalue_to_text(keyval))
     return keyval
     return keyval
 
 
@@ -74,9 +79,15 @@ class GrassTestFilesInvoker(object):
     # std stream, random outputs, saved results, profiling
     # std stream, random outputs, saved results, profiling
     # not stdout and stderr if they contain test results
     # not stdout and stderr if they contain test results
     # we can also save only failed tests, or generate only if assert fails
     # we can also save only failed tests, or generate only if assert fails
-    def __init__(self, start_dir,
-                 clean_mapsets=True, clean_outputs=True, clean_before=True,
-                 testsuite_dir='testsuite', file_anonymizer=None):
+    def __init__(
+        self,
+        start_dir,
+        clean_mapsets=True,
+        clean_outputs=True,
+        clean_before=True,
+        testsuite_dir="testsuite",
+        file_anonymizer=None,
+    ):
         """
         """
 
 
         :param bool clean_mapsets: if the mapsets should be removed
         :param bool clean_mapsets: if the mapsets should be removed
@@ -111,8 +122,8 @@ class GrassTestFilesInvoker(object):
         # replace . to get rid of unclean path
         # replace . to get rid of unclean path
         # TODO: clean paths
         # TODO: clean paths
         # note that backslash cannot be at the end of raw string
         # note that backslash cannot be at the end of raw string
-        dir_as_name = module.tested_dir.translate(maketrans(r'/\.', '___'))
-        mapset = dir_as_name + '_' + module.name
+        dir_as_name = module.tested_dir.translate(maketrans(r"/\.", "___"))
+        mapset = dir_as_name + "_" + module.name
         # TODO: use grass module to do this? but we are not in the right gisdbase
         # TODO: use grass module to do this? but we are not in the right gisdbase
         mapset_dir = os.path.join(gisdbase, location, mapset)
         mapset_dir = os.path.join(gisdbase, location, mapset)
         if self.clean_before:
         if self.clean_before:
@@ -122,21 +133,26 @@ class GrassTestFilesInvoker(object):
         # copy DEFAULT_WIND file from PERMANENT to WIND
         # copy DEFAULT_WIND file from PERMANENT to WIND
         # TODO: this should be a function in grass.script (used also in gis_set.py, PyGRASS also has its way with Mapset)
         # TODO: this should be a function in grass.script (used also in gis_set.py, PyGRASS also has its way with Mapset)
         # TODO: are premisions an issue here?
         # TODO: are premisions an issue here?
-        shutil.copy(os.path.join(gisdbase, location, 'PERMANENT', 'DEFAULT_WIND'),
-                    os.path.join(mapset_dir, 'WIND'))
+        shutil.copy(
+            os.path.join(gisdbase, location, "PERMANENT", "DEFAULT_WIND"),
+            os.path.join(mapset_dir, "WIND"),
+        )
         return mapset, mapset_dir
         return mapset, mapset_dir
 
 
     def _run_test_module(self, module, results_dir, gisdbase, location):
     def _run_test_module(self, module, results_dir, gisdbase, location):
         """Run one test file."""
         """Run one test file."""
         self.testsuite_dirs[module.tested_dir].append(module.name)
         self.testsuite_dirs[module.tested_dir].append(module.name)
         cwd = os.path.join(results_dir, module.tested_dir, module.name)
         cwd = os.path.join(results_dir, module.tested_dir, module.name)
-        data_dir = os.path.join(module.file_dir, 'data')
+        data_dir = os.path.join(module.file_dir, "data")
         if os.path.exists(data_dir):
         if os.path.exists(data_dir):
             # TODO: link dir instead of copy tree and remove link afterwads
             # TODO: link dir instead of copy tree and remove link afterwads
             # (removing is good because of testsuite dir in samplecode)
             # (removing is good because of testsuite dir in samplecode)
             # TODO: use different dir name in samplecode and test if it works
             # TODO: use different dir name in samplecode and test if it works
-            shutil.copytree(data_dir, os.path.join(cwd, 'data'),
-                            ignore=shutil.ignore_patterns('*.svn*'))
+            shutil.copytree(
+                data_dir,
+                os.path.join(cwd, "data"),
+                ignore=shutil.ignore_patterns("*.svn*"),
+            )
         ensure_dir(os.path.abspath(cwd))
         ensure_dir(os.path.abspath(cwd))
         # TODO: put this to constructor and copy here again
         # TODO: put this to constructor and copy here again
         env = os.environ.copy()
         env = os.environ.copy()
@@ -148,28 +164,28 @@ class GrassTestFilesInvoker(object):
         # will be long they should be stored somewhere separately
         # will be long they should be stored somewhere separately
 
 
         # use custom gisrc, not current session gisrc
         # use custom gisrc, not current session gisrc
-        env['GISRC'] = gisrc
+        env["GISRC"] = gisrc
         # percentage in plain format is 0...10...20... ...100
         # percentage in plain format is 0...10...20... ...100
-        env['GRASS_MESSAGE_FORMAT'] = 'plain'
+        env["GRASS_MESSAGE_FORMAT"] = "plain"
 
 
-        stdout_path = os.path.join(cwd, 'stdout.txt')
-        stderr_path = os.path.join(cwd, 'stderr.txt')
+        stdout_path = os.path.join(cwd, "stdout.txt")
+        stderr_path = os.path.join(cwd, "stderr.txt")
 
 
         self.reporter.start_file_test(module)
         self.reporter.start_file_test(module)
         # TODO: we might clean the directory here before test if non-empty
         # TODO: we might clean the directory here before test if non-empty
 
 
-        if module.file_type == 'py':
+        if module.file_type == "py":
             # ignoring shebang line to use current Python
             # ignoring shebang line to use current Python
             # and also pass parameters to it
             # and also pass parameters to it
             # add also '-Qwarn'?
             # add also '-Qwarn'?
             if sys.version_info.major >= 3:
             if sys.version_info.major >= 3:
-                args = [sys.executable, '-tt', module.abs_file_path]
+                args = [sys.executable, "-tt", module.abs_file_path]
             else:
             else:
-                args = [sys.executable, '-tt', '-3', module.abs_file_path]
-            p = subprocess.Popen(args, cwd=cwd, env=env,
-                                 stdout=subprocess.PIPE,
-                                 stderr=subprocess.PIPE)
-        elif module.file_type == 'sh':
+                args = [sys.executable, "-tt", "-3", module.abs_file_path]
+            p = subprocess.Popen(
+                args, cwd=cwd, env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE
+            )
+        elif module.file_type == "sh":
             # ignoring shebang line to pass parameters to shell
             # ignoring shebang line to pass parameters to shell
             # expecting system to have sh or something compatible
             # expecting system to have sh or something compatible
             # TODO: add some special checks for MS Windows
             # TODO: add some special checks for MS Windows
@@ -182,18 +198,24 @@ class GrassTestFilesInvoker(object):
             #                command is used to control an if, elif, while, or
             #                command is used to control an if, elif, while, or
             #                until; or if the command is the left hand operand
             #                until; or if the command is the left hand operand
             #                of an '&&' or '||' operator.
             #                of an '&&' or '||' operator.
-            p = subprocess.Popen(['sh', '-e', '-x', module.abs_file_path],
-                                 cwd=cwd, env=env,
-                                 stdout=subprocess.PIPE,
-                                 stderr=subprocess.PIPE)
+            p = subprocess.Popen(
+                ["sh", "-e", "-x", module.abs_file_path],
+                cwd=cwd,
+                env=env,
+                stdout=subprocess.PIPE,
+                stderr=subprocess.PIPE,
+            )
         else:
         else:
-            p = subprocess.Popen([module.abs_file_path],
-                                 cwd=cwd, env=env,
-                                 stdout=subprocess.PIPE,
-                                 stderr=subprocess.PIPE)
+            p = subprocess.Popen(
+                [module.abs_file_path],
+                cwd=cwd,
+                env=env,
+                stdout=subprocess.PIPE,
+                stderr=subprocess.PIPE,
+            )
         stdout, stderr = p.communicate()
         stdout, stderr = p.communicate()
         returncode = p.returncode
         returncode = p.returncode
-        encodings = [_get_encoding(), 'utf8', 'latin-1', 'ascii']
+        encodings = [_get_encoding(), "utf8", "latin-1", "ascii"]
         detected = False
         detected = False
         idx = 0
         idx = 0
         while not detected:
         while not detected:
@@ -214,33 +236,38 @@ class GrassTestFilesInvoker(object):
                 idx += 1
                 idx += 1
                 pass
                 pass
 
 
-        with open(stdout_path, 'w') as stdout_file:
+        with open(stdout_path, "w") as stdout_file:
             stdout_file.write(stdout)
             stdout_file.write(stdout)
-        with open(stderr_path, 'w') as stderr_file:
-            if type(stderr) == 'bytes':
+        with open(stderr_path, "w") as stderr_file:
+            if type(stderr) == "bytes":
                 stderr_file.write(decode(stderr))
                 stderr_file.write(decode(stderr))
             else:
             else:
                 if isinstance(stderr, str):
                 if isinstance(stderr, str):
                     stderr_file.write(stderr)
                     stderr_file.write(stderr)
                 else:
                 else:
-                    stderr_file.write(stderr.encode('utf8'))
+                    stderr_file.write(stderr.encode("utf8"))
         self._file_anonymizer.anonymize([stdout_path, stderr_path])
         self._file_anonymizer.anonymize([stdout_path, stderr_path])
 
 
         test_summary = update_keyval_file(
         test_summary = update_keyval_file(
-            os.path.join(os.path.abspath(cwd), 'test_keyvalue_result.txt'),
-            module=module, returncode=returncode)
-        self.reporter.end_file_test(module=module, cwd=cwd,
-                                    returncode=returncode,
-                                    stdout=stdout_path, stderr=stderr_path,
-                                    test_summary=test_summary)
+            os.path.join(os.path.abspath(cwd), "test_keyvalue_result.txt"),
+            module=module,
+            returncode=returncode,
+        )
+        self.reporter.end_file_test(
+            module=module,
+            cwd=cwd,
+            returncode=returncode,
+            stdout=stdout_path,
+            stderr=stderr_path,
+            test_summary=test_summary,
+        )
         # TODO: add some try-except or with for better error handling
         # TODO: add some try-except or with for better error handling
         os.remove(gisrc)
         os.remove(gisrc)
         # TODO: only if clean up
         # TODO: only if clean up
         if self.clean_mapsets:
         if self.clean_mapsets:
             shutil.rmtree(mapset_dir)
             shutil.rmtree(mapset_dir)
 
 
-    def run_in_location(self, gisdbase, location, location_type,
-                        results_dir):
+    def run_in_location(self, gisdbase, location, location_type, results_dir):
         """Run tests in a given location
         """Run tests in a given location
 
 
         Returns an object with counting attributes of GrassTestFilesCountingReporter,
         Returns an object with counting attributes of GrassTestFilesCountingReporter,
@@ -249,52 +276,68 @@ class GrassTestFilesInvoker(object):
         not to one file as these will simply contain the last executed file.
         not to one file as these will simply contain the last executed file.
         """
         """
         if os.path.abspath(results_dir) == os.path.abspath(self.start_dir):
         if os.path.abspath(results_dir) == os.path.abspath(self.start_dir):
-            raise RuntimeError("Results root directory should not be the same"
-                               " as discovery start directory")
+            raise RuntimeError(
+                "Results root directory should not be the same"
+                " as discovery start directory"
+            )
         self.reporter = GrassTestFilesMultiReporter(
         self.reporter = GrassTestFilesMultiReporter(
             reporters=[
             reporters=[
                 GrassTestFilesTextReporter(stream=sys.stderr),
                 GrassTestFilesTextReporter(stream=sys.stderr),
                 GrassTestFilesHtmlReporter(
                 GrassTestFilesHtmlReporter(
                     file_anonymizer=self._file_anonymizer,
                     file_anonymizer=self._file_anonymizer,
-                    main_page_name='testfiles.html'),
+                    main_page_name="testfiles.html",
+                ),
                 GrassTestFilesKeyValueReporter(
                 GrassTestFilesKeyValueReporter(
-                    info=dict(location=location, location_type=location_type))
-            ])
-        self.testsuite_dirs = collections.defaultdict(list)  # reset list of dirs each time
+                    info=dict(location=location, location_type=location_type)
+                ),
+            ]
+        )
+        self.testsuite_dirs = collections.defaultdict(
+            list
+        )  # reset list of dirs each time
         # TODO: move constants out of loader class or even module
         # TODO: move constants out of loader class or even module
-        modules = discover_modules(start_dir=self.start_dir,
-                                   grass_location=location_type,
-                                   file_regexp=r'.*\.(py|sh)$',
-                                   skip_dirs=GrassTestLoader.skip_dirs,
-                                   testsuite_dir=GrassTestLoader.testsuite_dir,
-                                   all_locations_value=GrassTestLoader.all_tests_value,
-                                   universal_location_value=GrassTestLoader.universal_tests_value,
-                                   import_modules=False)
+        modules = discover_modules(
+            start_dir=self.start_dir,
+            grass_location=location_type,
+            file_regexp=r".*\.(py|sh)$",
+            skip_dirs=GrassTestLoader.skip_dirs,
+            testsuite_dir=GrassTestLoader.testsuite_dir,
+            all_locations_value=GrassTestLoader.all_tests_value,
+            universal_location_value=GrassTestLoader.universal_tests_value,
+            import_modules=False,
+        )
 
 
         self.reporter.start(results_dir)
         self.reporter.start(results_dir)
         for module in modules:
         for module in modules:
-            self._run_test_module(module=module, results_dir=results_dir,
-                                  gisdbase=gisdbase, location=location)
+            self._run_test_module(
+                module=module,
+                results_dir=results_dir,
+                gisdbase=gisdbase,
+                location=location,
+            )
         self.reporter.finish()
         self.reporter.finish()
 
 
         # TODO: move this to some (new?) reporter
         # TODO: move this to some (new?) reporter
         # TODO: add basic summary of linked files so that the page is not empty
         # TODO: add basic summary of linked files so that the page is not empty
-        with open(os.path.join(results_dir, 'index.html'), 'w') as main_index:
+        with open(os.path.join(results_dir, "index.html"), "w") as main_index:
             main_index.write(
             main_index.write(
-                '<html><body>'
-                '<h1>Tests for &lt;{location}&gt;'
-                ' using &lt;{type}&gt; type tests</h1>'
-                '<ul>'
+                "<html><body>"
+                "<h1>Tests for &lt;{location}&gt;"
+                " using &lt;{type}&gt; type tests</h1>"
+                "<ul>"
                 '<li><a href="testsuites.html">Results by testsuites</a>'
                 '<li><a href="testsuites.html">Results by testsuites</a>'
-                ' (testsuite directories)</li>'
+                " (testsuite directories)</li>"
                 '<li><a href="testfiles.html">Results by test files</a></li>'
                 '<li><a href="testfiles.html">Results by test files</a></li>'
-                '<ul>'
-                '</body></html>'
-                .format(location=location, type=location_type))
+                "<ul>"
+                "</body></html>".format(location=location, type=location_type)
+            )
 
 
         testsuite_dir_reporter = TestsuiteDirReporter(
         testsuite_dir_reporter = TestsuiteDirReporter(
-            main_page_name='testsuites.html', testsuite_page_name='index.html',
-            top_level_testsuite_page_name='testsuite_index.html')
-        testsuite_dir_reporter.report_for_dirs(root=results_dir,
-                                               directories=self.testsuite_dirs)
+            main_page_name="testsuites.html",
+            testsuite_page_name="index.html",
+            top_level_testsuite_page_name="testsuite_index.html",
+        )
+        testsuite_dir_reporter.report_for_dirs(
+            root=results_dir, directories=self.testsuite_dirs
+        )
         return self.reporter
         return self.reporter

+ 53 - 38
python/grass/gunittest/loader.py

@@ -17,20 +17,25 @@ import re
 
 
 
 
 # TODO: resolve test file versus test module
 # TODO: resolve test file versus test module
-GrassTestPythonModule = collections.namedtuple('GrassTestPythonModule',
-                                               ['name', 'module',
-                                                'file_type',
-                                                'tested_dir',
-                                                'file_dir',
-                                                'abs_file_path'])
+GrassTestPythonModule = collections.namedtuple(
+    "GrassTestPythonModule",
+    ["name", "module", "file_type", "tested_dir", "file_dir", "abs_file_path"],
+)
 
 
 
 
 # TODO: implement loading without the import
 # TODO: implement loading without the import
-def discover_modules(start_dir, skip_dirs, testsuite_dir,
-                     grass_location,
-                     all_locations_value, universal_location_value,
-                     import_modules, add_failed_imports=True,
-                     file_pattern=None, file_regexp=None):
+def discover_modules(
+    start_dir,
+    skip_dirs,
+    testsuite_dir,
+    grass_location,
+    all_locations_value,
+    universal_location_value,
+    import_modules,
+    add_failed_imports=True,
+    file_pattern=None,
+    file_regexp=None,
+):
     """Find all test files (modules) in a directory tree.
     """Find all test files (modules) in a directory tree.
 
 
     The function is designed specifically for GRASS testing framework
     The function is designed specifically for GRASS testing framework
@@ -94,14 +99,14 @@ def discover_modules(start_dir, skip_dirs, testsuite_dir,
                 # otherwise we can have successful because nothing was reported
                 # otherwise we can have successful because nothing was reported
                 abspath = os.path.abspath(full)
                 abspath = os.path.abspath(full)
                 abs_file_path = os.path.join(abspath, file_name)
                 abs_file_path = os.path.join(abspath, file_name)
-                if file_name.endswith('.py'):
-                    if file_name == '__init__.py':
+                if file_name.endswith(".py"):
+                    if file_name == "__init__.py":
                         # we always ignore __init__.py
                         # we always ignore __init__.py
                         continue
                         continue
-                    file_type = 'py'
+                    file_type = "py"
                     name = file_name[:-3]
                     name = file_name[:-3]
-                elif file_name.endswith('.sh'):
-                    file_type = 'sh'
+                elif file_name.endswith(".sh"):
+                    file_type = "sh"
                     name = file_name[:-3]
                     name = file_name[:-3]
                 else:
                 else:
                     file_type = None  # alternative would be '', now equivalent
                     file_type = None  # alternative would be '', now equivalent
@@ -113,7 +118,7 @@ def discover_modules(start_dir, skip_dirs, testsuite_dir,
                         add = True
                         add = True
                     else:
                     else:
                         try:
                         try:
-                            locations = ['nc', 'stdmaps', 'all']
+                            locations = ["nc", "stdmaps", "all"]
                         except AttributeError:
                         except AttributeError:
                             add = True  # test is universal
                             add = True  # test is universal
                         else:
                         else:
@@ -127,15 +132,23 @@ def discover_modules(start_dir, skip_dirs, testsuite_dir,
                     if add_failed_imports:
                     if add_failed_imports:
                         add = True
                         add = True
                     else:
                     else:
-                        raise ImportError('Cannot import module named'
-                                          ' %s in %s (%s)'
-                                          % (name, full, e.message))
+                        raise ImportError(
+                            "Cannot import module named"
+                            " %s in %s (%s)" % (name, full, e.message)
+                        )
                         # alternative is to create TestClass which will raise
                         # alternative is to create TestClass which will raise
                         # see unittest.loader
                         # see unittest.loader
                 if add:
                 if add:
-                    modules.append(GrassTestPythonModule(
-                        name=name, module=None, tested_dir=root, file_dir=full,
-                        abs_file_path=abs_file_path, file_type=file_type))
+                    modules.append(
+                        GrassTestPythonModule(
+                            name=name,
+                            module=None,
+                            tested_dir=root,
+                            file_dir=full,
+                            abs_file_path=abs_file_path,
+                            file_type=file_type,
+                        )
+                    )
                 # in else with some verbose we could tell about skipped test
                 # in else with some verbose we could tell about skipped test
     return modules
     return modules
 
 
@@ -145,11 +158,11 @@ def discover_modules(start_dir, skip_dirs, testsuite_dir,
 class GrassTestLoader(unittest.TestLoader):
 class GrassTestLoader(unittest.TestLoader):
     """Class handles GRASS-specific loading of test modules."""
     """Class handles GRASS-specific loading of test modules."""
 
 
-    skip_dirs = ['.svn', 'dist.*', 'bin.*', 'OBJ.*']
-    testsuite_dir = 'testsuite'
-    files_in_testsuite = '*.py'
-    all_tests_value = 'all'
-    universal_tests_value = 'universal'
+    skip_dirs = [".svn", "dist.*", "bin.*", "OBJ.*"]
+    testsuite_dir = "testsuite"
+    files_in_testsuite = "*.py"
+    all_tests_value = "all"
+    universal_tests_value = "universal"
 
 
     def __init__(self, grass_location):
     def __init__(self, grass_location):
         self.grass_location = grass_location
         self.grass_location = grass_location
@@ -157,21 +170,23 @@ class GrassTestLoader(unittest.TestLoader):
     # TODO: what is the purpose of top_level_dir, can it be useful?
     # TODO: what is the purpose of top_level_dir, can it be useful?
     # probably yes, we need to know grass src or dist root
     # probably yes, we need to know grass src or dist root
     # TODO: not using pattern here
     # TODO: not using pattern here
-    def discover(self, start_dir, pattern='test*.py', top_level_dir=None):
+    def discover(self, start_dir, pattern="test*.py", top_level_dir=None):
         """Load test modules from in GRASS testing framework way."""
         """Load test modules from in GRASS testing framework way."""
-        modules = discover_modules(start_dir=start_dir,
-                                   file_pattern=self.files_in_testsuite,
-                                   skip_dirs=self.skip_dirs,
-                                   testsuite_dir=self.testsuite_dir,
-                                   grass_location=self.grass_location,
-                                   all_locations_value=self.all_tests_value,
-                                   universal_location_value=self.universal_tests_value,
-                                   import_modules=True)
+        modules = discover_modules(
+            start_dir=start_dir,
+            file_pattern=self.files_in_testsuite,
+            skip_dirs=self.skip_dirs,
+            testsuite_dir=self.testsuite_dir,
+            grass_location=self.grass_location,
+            all_locations_value=self.all_tests_value,
+            universal_location_value=self.universal_tests_value,
+            import_modules=True,
+        )
         tests = []
         tests = []
         for module in modules:
         for module in modules:
             tests.append(self.loadTestsFromModule(module.module))
             tests.append(self.loadTestsFromModule(module.module))
         return self.suiteClass(tests)
         return self.suiteClass(tests)
 
 
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     GrassTestLoader().discover()
     GrassTestLoader().discover()

+ 103 - 62
python/grass/gunittest/main.py

@@ -17,8 +17,7 @@ from unittest.main import TestProgram
 
 
 
 
 from .loader import GrassTestLoader
 from .loader import GrassTestLoader
-from .runner import (GrassTestRunner, MultiTestResult,
-                     TextTestResult, KeyValueTestResult)
+from .runner import GrassTestRunner, MultiTestResult, TextTestResult, KeyValueTestResult
 from .invoker import GrassTestFilesInvoker
 from .invoker import GrassTestFilesInvoker
 from .utils import silent_rmtree
 from .utils import silent_rmtree
 from .reporters import FileAnonymizer
 from .reporters import FileAnonymizer
@@ -29,10 +28,17 @@ import grass.script.core as gcore
 class GrassTestProgram(TestProgram):
 class GrassTestProgram(TestProgram):
     """A class to be used by individual test files (wrapped in the function)"""
     """A class to be used by individual test files (wrapped in the function)"""
 
 
-    def __init__(self, exit_at_end, grass_location, clean_outputs=True,
-                 unittest_argv=None, module=None,
-                 verbosity=1,
-                 failfast=None, catchbreak=None):
+    def __init__(
+        self,
+        exit_at_end,
+        grass_location,
+        clean_outputs=True,
+        unittest_argv=None,
+        module=None,
+        verbosity=1,
+        failfast=None,
+        catchbreak=None,
+    ):
         """Prepares the tests in GRASS way and then runs the tests.
         """Prepares the tests in GRASS way and then runs the tests.
 
 
         :param bool clean_outputs: if outputs in mapset and in ?
         :param bool clean_outputs: if outputs in mapset and in ?
@@ -45,32 +51,35 @@ class GrassTestProgram(TestProgram):
 
 
         grass_loader = GrassTestLoader(grass_location=self.grass_location)
         grass_loader = GrassTestLoader(grass_location=self.grass_location)
 
 
-        text_result = TextTestResult(stream=sys.stderr,
-                                     descriptions=True,
-                                     verbosity=verbosity)
-        keyval_file = open('test_keyvalue_result.txt', 'w')
+        text_result = TextTestResult(
+            stream=sys.stderr, descriptions=True, verbosity=verbosity
+        )
+        keyval_file = open("test_keyvalue_result.txt", "w")
         keyval_result = KeyValueTestResult(stream=keyval_file)
         keyval_result = KeyValueTestResult(stream=keyval_file)
         result = MultiTestResult(results=[text_result, keyval_result])
         result = MultiTestResult(results=[text_result, keyval_result])
 
 
-        grass_runner = GrassTestRunner(verbosity=verbosity,
-                                       failfast=failfast,
-                                       buffer=buffer_stdout_stderr,
-                                       result=result)
-        super(GrassTestProgram, self).__init__(module=module,
-                                                   argv=unittest_argv,
-                                                   testLoader=grass_loader,
-                                                   testRunner=grass_runner,
-                                                   exit=exit_at_end,
-                                                   verbosity=verbosity,
-                                                   failfast=failfast,
-                                                   catchbreak=catchbreak,
-                                                   buffer=buffer_stdout_stderr)
+        grass_runner = GrassTestRunner(
+            verbosity=verbosity,
+            failfast=failfast,
+            buffer=buffer_stdout_stderr,
+            result=result,
+        )
+        super(GrassTestProgram, self).__init__(
+            module=module,
+            argv=unittest_argv,
+            testLoader=grass_loader,
+            testRunner=grass_runner,
+            exit=exit_at_end,
+            verbosity=verbosity,
+            failfast=failfast,
+            catchbreak=catchbreak,
+            buffer=buffer_stdout_stderr,
+        )
         keyval_file.close()
         keyval_file.close()
 
 
 
 
 def test():
 def test():
-    """Run a test of a module.
-    """
+    """Run a test of a module."""
     # TODO: put the link to to the report only if available
     # TODO: put the link to to the report only if available
     # TODO: how to disable Python code coverage for module and C tests?
     # TODO: how to disable Python code coverage for module and C tests?
     # TODO: we probably need to have different test  functions for C, Python modules, and Python code
     # TODO: we probably need to have different test  functions for C, Python modules, and Python code
@@ -79,21 +88,23 @@ def test():
     # TODO: implement coverage but only when requested by invoker and only if
     # TODO: implement coverage but only when requested by invoker and only if
     # it makes sense for tests (need to know what is tested)
     # it makes sense for tests (need to know what is tested)
     # doing_coverage = False
     # doing_coverage = False
-    #try:
+    # try:
     #    import coverage
     #    import coverage
     #    doing_coverage = True
     #    doing_coverage = True
     #    cov = coverage.coverage(omit="*testsuite*")
     #    cov = coverage.coverage(omit="*testsuite*")
     #    cov.start()
     #    cov.start()
-    #except ImportError:
+    # except ImportError:
     #    pass
     #    pass
     # TODO: add some message somewhere
     # TODO: add some message somewhere
 
 
     # TODO: enable passing omit to exclude also gunittest or nothing
     # TODO: enable passing omit to exclude also gunittest or nothing
-    program = GrassTestProgram(module='__main__', exit_at_end=False, grass_location='all')
+    program = GrassTestProgram(
+        module="__main__", exit_at_end=False, grass_location="all"
+    )
     # TODO: check if we are in the directory where the test file is
     # TODO: check if we are in the directory where the test file is
     # this will ensure that data directory is available when it is requested
     # this will ensure that data directory is available when it is requested
 
 
-    #if doing_coverage:
+    # if doing_coverage:
     #    cov.stop()
     #    cov.stop()
     #    cov.html_report(directory='testcodecoverage')
     #    cov.html_report(directory='testcodecoverage')
 
 
@@ -110,7 +121,7 @@ def discovery():
         python main.py discovery [start_directory]
         python main.py discovery [start_directory]
     """
     """
 
 
-    program = GrassTestProgram(grass_location='nc', exit_at_end=False)
+    program = GrassTestProgram(grass_location="nc", exit_at_end=False)
 
 
     sys.exit(not program.result.wasSuccessful())
     sys.exit(not program.result.wasSuccessful())
 
 
@@ -119,55 +130,84 @@ def discovery():
 # TODO: create a full interface (using grass parser or argparse)
 # TODO: create a full interface (using grass parser or argparse)
 def main():
 def main():
     parser = argparse.ArgumentParser(
     parser = argparse.ArgumentParser(
-        description='Run test files in all testsuite directories starting'
-        ' from the current one'
-        ' (runs on active GRASS session)')
-    parser.add_argument('--location', dest='location', action='store',
-                        help='Name of location where to perform test', required=True)
-    parser.add_argument('--location-type', dest='location_type', action='store',
-                        default='nc',
-                        help='Type of tests which should be run'
-                             ' (tag corresponding to location)')
-    parser.add_argument('--grassdata', dest='gisdbase', action='store',
-                        default=None,
-                        help='GRASS data(base) (GISDBASE) directory'
-                        ' (current GISDBASE by default)')
-    parser.add_argument('--output', dest='output', action='store',
-                        default='testreport',
-                        help='Output directory')
-    parser.add_argument('--min-success', dest='min_success', action='store',
-                        default='90', type=int,
-                        help=("Minimum success percentage (lower percentage"
-                              " than this will result in a non-zero return code; values 0-100)"))
+        description="Run test files in all testsuite directories starting"
+        " from the current one"
+        " (runs on active GRASS session)"
+    )
+    parser.add_argument(
+        "--location",
+        dest="location",
+        action="store",
+        help="Name of location where to perform test",
+        required=True,
+    )
+    parser.add_argument(
+        "--location-type",
+        dest="location_type",
+        action="store",
+        default="nc",
+        help="Type of tests which should be run" " (tag corresponding to location)",
+    )
+    parser.add_argument(
+        "--grassdata",
+        dest="gisdbase",
+        action="store",
+        default=None,
+        help="GRASS data(base) (GISDBASE) directory" " (current GISDBASE by default)",
+    )
+    parser.add_argument(
+        "--output",
+        dest="output",
+        action="store",
+        default="testreport",
+        help="Output directory",
+    )
+    parser.add_argument(
+        "--min-success",
+        dest="min_success",
+        action="store",
+        default="90",
+        type=int,
+        help=(
+            "Minimum success percentage (lower percentage"
+            " than this will result in a non-zero return code; values 0-100)"
+        ),
+    )
     args = parser.parse_args()
     args = parser.parse_args()
     gisdbase = args.gisdbase
     gisdbase = args.gisdbase
     if gisdbase is None:
     if gisdbase is None:
         # here we already rely on being in GRASS session
         # here we already rely on being in GRASS session
-        gisdbase = gcore.gisenv()['GISDBASE']
+        gisdbase = gcore.gisenv()["GISDBASE"]
     location = args.location
     location = args.location
     location_type = args.location_type
     location_type = args.location_type
 
 
     if not gisdbase:
     if not gisdbase:
-        sys.stderr.write("GISDBASE (grassdata directory)"
-                         " cannot be empty string\n" % gisdbase)
+        sys.stderr.write(
+            "GISDBASE (grassdata directory)" " cannot be empty string\n" % gisdbase
+        )
         sys.exit(1)
         sys.exit(1)
     if not os.path.exists(gisdbase):
     if not os.path.exists(gisdbase):
-        sys.stderr.write("GISDBASE (grassdata directory) <%s>"
-                         " does not exist\n" % gisdbase)
+        sys.stderr.write(
+            "GISDBASE (grassdata directory) <%s>" " does not exist\n" % gisdbase
+        )
         sys.exit(1)
         sys.exit(1)
     if not os.path.exists(os.path.join(gisdbase, location)):
     if not os.path.exists(os.path.join(gisdbase, location)):
-        sys.stderr.write("GRASS Location <{loc}>"
-                         " does not exist in GRASS Database <{db}>\n".format(
-                             loc=location, db=gisdbase))
+        sys.stderr.write(
+            "GRASS Location <{loc}>"
+            " does not exist in GRASS Database <{db}>\n".format(
+                loc=location, db=gisdbase
+            )
+        )
         sys.exit(1)
         sys.exit(1)
     results_dir = args.output
     results_dir = args.output
     silent_rmtree(results_dir)  # TODO: too brute force?
     silent_rmtree(results_dir)  # TODO: too brute force?
 
 
-    start_dir = '.'
+    start_dir = "."
     abs_start_dir = os.path.abspath(start_dir)
     abs_start_dir = os.path.abspath(start_dir)
     invoker = GrassTestFilesInvoker(
     invoker = GrassTestFilesInvoker(
         start_dir=start_dir,
         start_dir=start_dir,
-        file_anonymizer=FileAnonymizer(paths_to_remove=[abs_start_dir]))
+        file_anonymizer=FileAnonymizer(paths_to_remove=[abs_start_dir]),
+    )
     # TODO: remove also results dir from files
     # TODO: remove also results dir from files
     # as an enhancemnt
     # as an enhancemnt
     # we can just iterate over all locations available in database
     # we can just iterate over all locations available in database
@@ -176,11 +216,12 @@ def main():
         gisdbase=gisdbase,
         gisdbase=gisdbase,
         location=location,
         location=location,
         location_type=location_type,
         location_type=location_type,
-        results_dir=results_dir
+        results_dir=results_dir,
     )
     )
     if reporter.file_pass_per >= args.min_success:
     if reporter.file_pass_per >= args.min_success:
         return 0
         return 0
     return 1
     return 1
 
 
-if __name__ == '__main__':
+
+if __name__ == "__main__":
     sys.exit(main())
     sys.exit(main())

+ 340 - 176
python/grass/gunittest/multireport.py

@@ -24,12 +24,14 @@ from grass.gunittest.reporters import success_to_html_percent
 
 
 # TODO: we should be able to work without matplotlib
 # TODO: we should be able to work without matplotlib
 import matplotlib
 import matplotlib
-matplotlib.use('Agg')
+
+matplotlib.use("Agg")
 # This counts as code already, so silence "import not at top of file".
 # This counts as code already, so silence "import not at top of file".
 # Perhaps in the future, switch_backend() could be used.
 # Perhaps in the future, switch_backend() could be used.
 import matplotlib.pyplot as plt  # noqa: E402
 import matplotlib.pyplot as plt  # noqa: E402
 from matplotlib.dates import date2num  # noqa: E402
 from matplotlib.dates import date2num  # noqa: E402
 
 
+
 class TestResultSummary(object):
 class TestResultSummary(object):
     def __init__(self):
     def __init__(self):
         self.timestamp = None
         self.timestamp = None
@@ -65,10 +67,20 @@ def plot_percents(x, xticks, xlabels, successes, failures, filename, style):
     graph = fig.add_subplot(111)
     graph = fig.add_subplot(111)
 
 
     # Plot the data as a red line with round markers
     # Plot the data as a red line with round markers
-    graph.plot(x, successes, color=style.success_color,
-               linestyle=style.linestyle, linewidth=style.linewidth)
-    graph.plot(x, failures, color=style.fail_color,
-               linestyle=style.linestyle, linewidth=style.linewidth)
+    graph.plot(
+        x,
+        successes,
+        color=style.success_color,
+        linestyle=style.linestyle,
+        linewidth=style.linewidth,
+    )
+    graph.plot(
+        x,
+        failures,
+        color=style.fail_color,
+        linestyle=style.linestyle,
+        linewidth=style.linewidth,
+    )
 
 
     fig.autofmt_xdate()
     fig.autofmt_xdate()
     graph.set_xticks(xticks)
     graph.set_xticks(xticks)
@@ -76,7 +88,7 @@ def plot_percents(x, xticks, xlabels, successes, failures, filename, style):
 
 
     percents = range(0, 110, 10)
     percents = range(0, 110, 10)
     graph.set_yticks(percents)
     graph.set_yticks(percents)
-    graph.set_yticklabels(['%d%%' % p for p in percents])
+    graph.set_yticklabels(["%d%%" % p for p in percents])
 
 
     fig.savefig(filename)
     fig.savefig(filename)
 
 
@@ -109,14 +121,15 @@ def plot_percent_successful(x, xticks, xlabels, successes, filename, style):
     smedian = median(successes)
     smedian = median(successes)
     smax = max(successes)
     smax = max(successes)
     if successes[-1] < smedian:
     if successes[-1] < smedian:
-        color = 'r'
+        color = "r"
     else:
     else:
-        color = 'g'
+        color = "g"
     # another possibility is to color according to the gradient, ideally
     # another possibility is to color according to the gradient, ideally
     # on the whole curve but that's much more complicated
     # on the whole curve but that's much more complicated
 
 
-    graph.plot(x, successes, color=color,
-               linestyle=style.linestyle, linewidth=style.linewidth)
+    graph.plot(
+        x, successes, color=color, linestyle=style.linestyle, linewidth=style.linewidth
+    )
 
 
     # rotates the xlabels
     # rotates the xlabels
     fig.autofmt_xdate()
     fig.autofmt_xdate()
@@ -128,7 +141,7 @@ def plot_percent_successful(x, xticks, xlabels, successes, filename, style):
     ymax = int(smax / step) * step
     ymax = int(smax / step) * step
     percents = range(ymin, ymax + step + 1, step)
     percents = range(ymin, ymax + step + 1, step)
     graph.set_yticks(percents)
     graph.set_yticks(percents)
-    graph.set_yticklabels(['%d%%' % p for p in percents])
+    graph.set_yticklabels(["%d%%" % p for p in percents])
 
 
     fig.savefig(filename)
     fig.savefig(filename)
 
 
@@ -143,9 +156,14 @@ def tests_successful_plot(x, xticks, xlabels, results, filename, style):
             # but we don't want any exceptions if it happens
             # but we don't want any exceptions if it happens
             successes.append(0)
             successes.append(0)
 
 
-    plot_percent_successful(x=x, xticks=xticks, xlabels=xlabels,
-                            successes=successes,
-                            filename=filename, style=style)
+    plot_percent_successful(
+        x=x,
+        xticks=xticks,
+        xlabels=xlabels,
+        successes=successes,
+        filename=filename,
+        style=style,
+    )
 
 
 
 
 def tests_plot(x, xticks, xlabels, results, filename, style):
 def tests_plot(x, xticks, xlabels, results, filename, style):
@@ -159,12 +177,27 @@ def tests_plot(x, xticks, xlabels, results, filename, style):
 
 
     graph = fig.add_subplot(111)
     graph = fig.add_subplot(111)
 
 
-    graph.plot(x, total, color=style.total_color,
-               linestyle=style.linestyle, linewidth=style.linewidth)
-    graph.plot(x, successes, color=style.success_color,
-               linestyle=style.linestyle, linewidth=style.linewidth)
-    graph.plot(x, failures, color=style.fail_color,
-               linestyle=style.linestyle, linewidth=style.linewidth)
+    graph.plot(
+        x,
+        total,
+        color=style.total_color,
+        linestyle=style.linestyle,
+        linewidth=style.linewidth,
+    )
+    graph.plot(
+        x,
+        successes,
+        color=style.success_color,
+        linestyle=style.linestyle,
+        linewidth=style.linewidth,
+    )
+    graph.plot(
+        x,
+        failures,
+        color=style.fail_color,
+        linestyle=style.linestyle,
+        linewidth=style.linewidth,
+    )
 
 
     fig.autofmt_xdate()
     fig.autofmt_xdate()
     graph.set_xticks(xticks)
     graph.set_xticks(xticks)
@@ -172,6 +205,7 @@ def tests_plot(x, xticks, xlabels, results, filename, style):
 
 
     fig.savefig(filename)
     fig.savefig(filename)
 
 
+
 def tests_percent_plot(x, xticks, xlabels, results, filename, style):
 def tests_percent_plot(x, xticks, xlabels, results, filename, style):
     successes = []
     successes = []
     failures = []
     failures = []
@@ -186,9 +220,15 @@ def tests_percent_plot(x, xticks, xlabels, results, filename, style):
             successes.append(0)
             successes.append(0)
             failures.append(0)
             failures.append(0)
 
 
-    plot_percents(x=x, xticks=xticks, xlabels=xlabels,
-                  successes=successes, failures=failures,
-                  filename=filename, style=style)
+    plot_percents(
+        x=x,
+        xticks=xticks,
+        xlabels=xlabels,
+        successes=successes,
+        failures=failures,
+        filename=filename,
+        style=style,
+    )
 
 
 
 
 def files_successful_plot(x, xticks, xlabels, results, filename, style):
 def files_successful_plot(x, xticks, xlabels, results, filename, style):
@@ -201,9 +241,14 @@ def files_successful_plot(x, xticks, xlabels, results, filename, style):
             # but we don't want any exceptions if it happens
             # but we don't want any exceptions if it happens
             successes.append(0)
             successes.append(0)
 
 
-    plot_percent_successful(x=x, xticks=xticks, xlabels=xlabels,
-                            successes=successes,
-                            filename=filename, style=style)
+    plot_percent_successful(
+        x=x,
+        xticks=xticks,
+        xlabels=xlabels,
+        successes=successes,
+        filename=filename,
+        style=style,
+    )
 
 
 
 
 def files_plot(x, xticks, xlabels, results, filename, style):
 def files_plot(x, xticks, xlabels, results, filename, style):
@@ -215,12 +260,27 @@ def files_plot(x, xticks, xlabels, results, filename, style):
 
 
     graph = fig.add_subplot(111)
     graph = fig.add_subplot(111)
 
 
-    graph.plot(x, total, color=style.total_color,
-               linestyle=style.linestyle, linewidth=style.linewidth)
-    graph.plot(x, successes, color=style.success_color,
-               linestyle=style.linestyle, linewidth=style.linewidth)
-    graph.plot(x, failures, color=style.fail_color,
-               linestyle=style.linestyle, linewidth=style.linewidth)
+    graph.plot(
+        x,
+        total,
+        color=style.total_color,
+        linestyle=style.linestyle,
+        linewidth=style.linewidth,
+    )
+    graph.plot(
+        x,
+        successes,
+        color=style.success_color,
+        linestyle=style.linestyle,
+        linewidth=style.linewidth,
+    )
+    graph.plot(
+        x,
+        failures,
+        color=style.fail_color,
+        linestyle=style.linestyle,
+        linewidth=style.linewidth,
+    )
 
 
     fig.autofmt_xdate()
     fig.autofmt_xdate()
     graph.set_xticks(xticks)
     graph.set_xticks(xticks)
@@ -242,9 +302,15 @@ def files_percent_plot(x, xticks, xlabels, results, filename, style):
             successes.append(0)
             successes.append(0)
             failures.append(0)
             failures.append(0)
 
 
-    plot_percents(x=x, xticks=xticks, xlabels=xlabels,
-                  successes=successes, failures=failures,
-                  filename=filename, style=style)
+    plot_percents(
+        x=x,
+        xticks=xticks,
+        xlabels=xlabels,
+        successes=successes,
+        failures=failures,
+        filename=filename,
+        style=style,
+    )
 
 
 
 
 def info_plot(x, xticks, xlabels, results, filename, style):
 def info_plot(x, xticks, xlabels, results, filename, style):
@@ -259,17 +325,41 @@ def info_plot(x, xticks, xlabels, results, filename, style):
 
 
     graph = fig.add_subplot(111)
     graph = fig.add_subplot(111)
 
 
-    graph.plot(x, names, color='b', label="Test files",
-               linestyle=style.linestyle, linewidth=style.linewidth)
-    graph.plot(x, modules, color='g', label="Tested modules",
-               linestyle=style.linestyle, linewidth=style.linewidth)
+    graph.plot(
+        x,
+        names,
+        color="b",
+        label="Test files",
+        linestyle=style.linestyle,
+        linewidth=style.linewidth,
+    )
+    graph.plot(
+        x,
+        modules,
+        color="g",
+        label="Tested modules",
+        linestyle=style.linestyle,
+        linewidth=style.linewidth,
+    )
     # dirs == testsuites
     # dirs == testsuites
-    graph.plot(x, dirs, color='orange', label="Tested directories",
-               linestyle=style.linestyle, linewidth=style.linewidth)
-    graph.plot(x, authors, color='r', label="Test authors",
-               linestyle=style.linestyle, linewidth=style.linewidth)
+    graph.plot(
+        x,
+        dirs,
+        color="orange",
+        label="Tested directories",
+        linestyle=style.linestyle,
+        linewidth=style.linewidth,
+    )
+    graph.plot(
+        x,
+        authors,
+        color="r",
+        label="Test authors",
+        linestyle=style.linestyle,
+        linewidth=style.linewidth,
+    )
 
 
-    graph.legend(loc='best', shadow=False)
+    graph.legend(loc="best", shadow=False)
 
 
     fig.autofmt_xdate()
     fig.autofmt_xdate()
     graph.set_xticks(xticks)
     graph.set_xticks(xticks)
@@ -279,25 +369,25 @@ def info_plot(x, xticks, xlabels, results, filename, style):
 
 
 
 
 # TODO: solve the directory inconsitencies, implement None
 # TODO: solve the directory inconsitencies, implement None
-def main_page(results, filename, images, captions, title='Test reports',
-              directory=None):
+def main_page(
+    results, filename, images, captions, title="Test reports", directory=None
+):
     filename = os.path.join(directory, filename)
     filename = os.path.join(directory, filename)
-    with open(filename, 'w') as page:
+    with open(filename, "w") as page:
         page.write(
         page.write(
-            '<html><body>'
-            '<h1>{title}</h1>'
-            '<table>'
-            '<thead><tr>'
-            '<th>Date (timestamp)</th><th>SVN revision</th><th>Name</th>'
-            '<th>Successful files</th><th>Successful tests</th>'
-            '</tr></thead>'
-            '<tbody>'
-            .format(title=title)
+            "<html><body>"
+            "<h1>{title}</h1>"
+            "<table>"
+            "<thead><tr>"
+            "<th>Date (timestamp)</th><th>SVN revision</th><th>Name</th>"
+            "<th>Successful files</th><th>Successful tests</th>"
+            "</tr></thead>"
+            "<tbody>".format(title=title)
         )
         )
         for result in reversed(results):
         for result in reversed(results):
             # TODO: include name to summary file
             # TODO: include name to summary file
             # now using location or test report directory as name
             # now using location or test report directory as name
-            if result.location != 'unknown':
+            if result.location != "unknown":
                 name = result.location
                 name = result.location
             else:
             else:
                 name = os.path.basename(result.report)
                 name = os.path.basename(result.report)
@@ -308,40 +398,62 @@ def main_page(results, filename, images, captions, title='Test reports',
                             name = d
                             name = d
                             break
                             break
             per_test = success_to_html_percent(
             per_test = success_to_html_percent(
-                total=result.total, successes=result.successes)
+                total=result.total, successes=result.successes
+            )
             per_file = success_to_html_percent(
             per_file = success_to_html_percent(
-                total=result.files_total, successes=result.files_successes)
+                total=result.files_total, successes=result.files_successes
+            )
             report_path = os.path.relpath(path=result.report, start=directory)
             report_path = os.path.relpath(path=result.report, start=directory)
             page.write(
             page.write(
-                '<tr>'
-                '<td><a href={report_path}/index.html>{result.timestamp}</a></td>'
-                '<td>{result.svn_revision}</td>'
-                '<td><a href={report_path}/index.html>{name}</a></td>'
-                '<td>{pfiles}</td><td>{ptests}</td>'
-                '</tr>'
-                .format(result=result, name=name, report_path=report_path,
-                        pfiles=per_file, ptests=per_test))
-        page.write('</tbody></table>')
+                "<tr>"
+                "<td><a href={report_path}/index.html>{result.timestamp}</a></td>"
+                "<td>{result.svn_revision}</td>"
+                "<td><a href={report_path}/index.html>{name}</a></td>"
+                "<td>{pfiles}</td><td>{ptests}</td>"
+                "</tr>".format(
+                    result=result,
+                    name=name,
+                    report_path=report_path,
+                    pfiles=per_file,
+                    ptests=per_test,
+                )
+            )
+        page.write("</tbody></table>")
         for image, caption in itertools.izip(images, captions):
         for image, caption in itertools.izip(images, captions):
             page.write(
             page.write(
-                '<h3>{caption}<h3>'
-                '<img src="{image}" alt="{caption}" title="{caption}">'
-                .format(image=image, caption=caption))
-        page.write('</body></html>')
+                "<h3>{caption}<h3>"
+                '<img src="{image}" alt="{caption}" title="{caption}">'.format(
+                    image=image, caption=caption
+                )
+            )
+        page.write("</body></html>")
 
 
 
 
 def main():
 def main():
 
 
     parser = argparse.ArgumentParser(
     parser = argparse.ArgumentParser(
-        description='Create overall report from several individual test reports')
-    parser.add_argument('reports', metavar='report_directory',
-                        type=str, nargs='+',
-                        help='Directories with reports')
-    parser.add_argument('--output', dest='output', action='store',
-                        default='testreports_summary',
-                        help='Output directory')
-    parser.add_argument('--timestamps', dest='timestamps', action='store_true',
-                        help='Use file timestamp instead of date in test summary')
+        description="Create overall report from several individual test reports"
+    )
+    parser.add_argument(
+        "reports",
+        metavar="report_directory",
+        type=str,
+        nargs="+",
+        help="Directories with reports",
+    )
+    parser.add_argument(
+        "--output",
+        dest="output",
+        action="store",
+        default="testreports_summary",
+        help="Output directory",
+    )
+    parser.add_argument(
+        "--timestamps",
+        dest="timestamps",
+        action="store_true",
+        help="Use file timestamp instead of date in test summary",
+    )
 
 
     args = parser.parse_args()
     args = parser.parse_args()
     output = args.output
     output = args.output
@@ -355,40 +467,46 @@ def main():
 
 
     for report in reports:
     for report in reports:
         try:
         try:
-            summary_file = os.path.join(report, 'test_keyvalue_result.txt')
+            summary_file = os.path.join(report, "test_keyvalue_result.txt")
             if not os.path.exists(summary_file):
             if not os.path.exists(summary_file):
-                sys.stderr.write('WARNING: Key-value summary not available in'
-                                 ' report <%s>, skipping.\n' % summary_file)
+                sys.stderr.write(
+                    "WARNING: Key-value summary not available in"
+                    " report <%s>, skipping.\n" % summary_file
+                )
                 # skipping incomplete reports
                 # skipping incomplete reports
                 # use only results list for further processing
                 # use only results list for further processing
                 continue
                 continue
-            summary = text_to_keyvalue(open(summary_file).read(), sep='=')
+            summary = text_to_keyvalue(open(summary_file).read(), sep="=")
             if use_timestamps:
             if use_timestamps:
-                test_timestamp = datetime.datetime.fromtimestamp(os.path.getmtime(summary_file))
+                test_timestamp = datetime.datetime.fromtimestamp(
+                    os.path.getmtime(summary_file)
+                )
             else:
             else:
-                test_timestamp = datetime.datetime.strptime(summary['timestamp'], "%Y-%m-%d %H:%M:%S")
+                test_timestamp = datetime.datetime.strptime(
+                    summary["timestamp"], "%Y-%m-%d %H:%M:%S"
+                )
 
 
             result = TestResultSummary()
             result = TestResultSummary()
             result.timestamp = test_timestamp
             result.timestamp = test_timestamp
-            result.total = summary['total']
-            result.successes = summary['successes']
-            result.failures = summary['failures']
-            result.errors = summary['errors']
-
-            result.files_total = summary['files_total']
-            result.files_successes = summary['files_successes']
-            result.files_failures = summary['files_failures']
-
-            result.svn_revision = str(summary['svn_revision'])
-            result.tested_modules = summary['tested_modules']
-            result.names = summary['names']
-            result.test_files_authors = summary['test_files_authors']
-            result.tested_dirs = summary['tested_dirs']
+            result.total = summary["total"]
+            result.successes = summary["successes"]
+            result.failures = summary["failures"]
+            result.errors = summary["errors"]
+
+            result.files_total = summary["files_total"]
+            result.files_successes = summary["files_successes"]
+            result.files_failures = summary["files_failures"]
+
+            result.svn_revision = str(summary["svn_revision"])
+            result.tested_modules = summary["tested_modules"]
+            result.names = summary["names"]
+            result.test_files_authors = summary["test_files_authors"]
+            result.tested_dirs = summary["tested_dirs"]
             result.report = report
             result.report = report
 
 
             # let's consider no location as valid state and use 'unknown'
             # let's consider no location as valid state and use 'unknown'
-            result.location = summary.get('location', 'unknown')
-            result.location_type = summary.get('location_type', 'unknown')
+            result.location = summary.get("location", "unknown")
+            result.location_type = summary.get("location_type", "unknown")
             # grouping according to location types
             # grouping according to location types
             # this can cause that two actual locations tested at the same time
             # this can cause that two actual locations tested at the same time
             # will end up together, this is not ideal but testing with
             # will end up together, this is not ideal but testing with
@@ -400,105 +518,151 @@ def main():
             all_results.append(result)
             all_results.append(result)
             del result
             del result
         except KeyError as e:
         except KeyError as e:
-            print('File %s does not have right values (%s)' % (report, e.message))
+            print("File %s does not have right values (%s)" % (report, e.message))
 
 
-    locations_main_page = open(os.path.join(output, 'index.html'), 'w')
+    locations_main_page = open(os.path.join(output, "index.html"), "w")
     locations_main_page.write(
     locations_main_page.write(
-        '<html><body>'
-        '<h1>Test reports grouped by location type</h1>'
-        '<table>'
-        '<thead><tr>'
-        '<th>Location</th>'
-        '<th>Successful files</th><th>Successful tests</th>'
-        '</tr></thead>'
-        '<tbody>'
+        "<html><body>"
+        "<h1>Test reports grouped by location type</h1>"
+        "<table>"
+        "<thead><tr>"
+        "<th>Location</th>"
+        "<th>Successful files</th><th>Successful tests</th>"
+        "</tr></thead>"
+        "<tbody>"
     )
     )
 
 
-    PlotStyle = namedtuple('PlotStyle',
-                           ['linestyle', 'linewidth',
-                           'success_color', 'fail_color', 'total_color'])
-    plot_style = PlotStyle(linestyle='-', linewidth=4.0,
-                           success_color='g', fail_color='r', total_color='b')
+    PlotStyle = namedtuple(
+        "PlotStyle",
+        ["linestyle", "linewidth", "success_color", "fail_color", "total_color"],
+    )
+    plot_style = PlotStyle(
+        linestyle="-", linewidth=4.0, success_color="g", fail_color="r", total_color="b"
+    )
 
 
     for location_type, results in results_in_locations.items():
     for location_type, results in results_in_locations.items():
-        results = sorted(results, key=operator.attrgetter('timestamp'))
+        results = sorted(results, key=operator.attrgetter("timestamp"))
         # TODO: document: location type must be a valid dir name
         # TODO: document: location type must be a valid dir name
         directory = os.path.join(output, location_type)
         directory = os.path.join(output, location_type)
         ensure_dir(directory)
         ensure_dir(directory)
 
 
-        if location_type == 'unknown':
-            title = 'Test reports'
+        if location_type == "unknown":
+            title = "Test reports"
         else:
         else:
-            title = ('Test reports for &lt;{type}&gt; location type'
-                     .format(type=location_type))
+            title = "Test reports for &lt;{type}&gt; location type".format(
+                type=location_type
+            )
 
 
         x = [date2num(result.timestamp) for result in results]
         x = [date2num(result.timestamp) for result in results]
         # the following would be an alternative but it does not work with
         # the following would be an alternative but it does not work with
         # labels and automatic axis limits even after removing another date fun
         # labels and automatic axis limits even after removing another date fun
         # x = [result.svn_revision for result in results]
         # x = [result.svn_revision for result in results]
-        xlabels = [result.timestamp.strftime("%Y-%m-%d") + ' (r' + result.svn_revision + ')' for result in results]
+        xlabels = [
+            result.timestamp.strftime("%Y-%m-%d") + " (r" + result.svn_revision + ")"
+            for result in results
+        ]
         step = len(x) / 10
         step = len(x) / 10
         xticks = x[step::step]
         xticks = x[step::step]
         xlabels = xlabels[step::step]
         xlabels = xlabels[step::step]
-        tests_successful_plot(x=x, xticks=xticks, xlabels=xlabels, results=results,
-                              filename=os.path.join(directory, 'tests_successful_plot.png'),
-                              style=plot_style)
-        files_successful_plot(x=x, xticks=xticks, xlabels=xlabels, results=results,
-                              filename=os.path.join(directory, 'files_successful_plot.png'),
-                              style=plot_style)
-        tests_plot(x=x, xticks=xticks, xlabels=xlabels, results=results,
-                   filename=os.path.join(directory, 'tests_plot.png'),
-                   style=plot_style)
-        tests_percent_plot(x=x, xticks=xticks, xlabels=xlabels, results=results,
-                           filename=os.path.join(directory, 'tests_percent_plot.png'),
-                           style=plot_style)
-        files_plot(x=x, xticks=xticks, xlabels=xlabels, results=results,
-                   filename=os.path.join(directory, 'files_plot.png'),
-                   style=plot_style)
-        files_percent_plot(x=x, xticks=xticks, xlabels=xlabels, results=results,
-                           filename=os.path.join(directory, 'files_percent_plot.png'),
-                           style=plot_style)
-        info_plot(x=x, xticks=xticks, xlabels=xlabels, results=results,
-                  filename=os.path.join(directory, 'info_plot.png'),
-                   style=plot_style)
-
-        main_page(results=results, filename='index.html',
-                  images=['tests_successful_plot.png',
-                          'files_successful_plot.png',
-                          'tests_plot.png',
-                          'files_plot.png',
-                          'tests_percent_plot.png',
-                          'files_percent_plot.png',
-                          'info_plot.png'],
-                  captions=['Success of individual tests in percents',
-                            'Success of test files in percents',
-                            'Successes, failures and number of individual tests',
-                            'Successes, failures and number of test files',
-                            'Successes and failures of individual tests in percent',
-                            'Successes and failures of test files in percents',
-                            'Additional information'],
-                  directory=directory,
-                  title=title)
+        tests_successful_plot(
+            x=x,
+            xticks=xticks,
+            xlabels=xlabels,
+            results=results,
+            filename=os.path.join(directory, "tests_successful_plot.png"),
+            style=plot_style,
+        )
+        files_successful_plot(
+            x=x,
+            xticks=xticks,
+            xlabels=xlabels,
+            results=results,
+            filename=os.path.join(directory, "files_successful_plot.png"),
+            style=plot_style,
+        )
+        tests_plot(
+            x=x,
+            xticks=xticks,
+            xlabels=xlabels,
+            results=results,
+            filename=os.path.join(directory, "tests_plot.png"),
+            style=plot_style,
+        )
+        tests_percent_plot(
+            x=x,
+            xticks=xticks,
+            xlabels=xlabels,
+            results=results,
+            filename=os.path.join(directory, "tests_percent_plot.png"),
+            style=plot_style,
+        )
+        files_plot(
+            x=x,
+            xticks=xticks,
+            xlabels=xlabels,
+            results=results,
+            filename=os.path.join(directory, "files_plot.png"),
+            style=plot_style,
+        )
+        files_percent_plot(
+            x=x,
+            xticks=xticks,
+            xlabels=xlabels,
+            results=results,
+            filename=os.path.join(directory, "files_percent_plot.png"),
+            style=plot_style,
+        )
+        info_plot(
+            x=x,
+            xticks=xticks,
+            xlabels=xlabels,
+            results=results,
+            filename=os.path.join(directory, "info_plot.png"),
+            style=plot_style,
+        )
+
+        main_page(
+            results=results,
+            filename="index.html",
+            images=[
+                "tests_successful_plot.png",
+                "files_successful_plot.png",
+                "tests_plot.png",
+                "files_plot.png",
+                "tests_percent_plot.png",
+                "files_percent_plot.png",
+                "info_plot.png",
+            ],
+            captions=[
+                "Success of individual tests in percents",
+                "Success of test files in percents",
+                "Successes, failures and number of individual tests",
+                "Successes, failures and number of test files",
+                "Successes and failures of individual tests in percent",
+                "Successes and failures of test files in percents",
+                "Additional information",
+            ],
+            directory=directory,
+            title=title,
+        )
 
 
         files_successes = sum(result.files_successes for result in results)
         files_successes = sum(result.files_successes for result in results)
         files_total = sum(result.files_total for result in results)
         files_total = sum(result.files_total for result in results)
         successes = sum(result.successes for result in results)
         successes = sum(result.successes for result in results)
         total = sum(result.total for result in results)
         total = sum(result.total for result in results)
-        per_test = success_to_html_percent(
-            total=total, successes=successes)
-        per_file = success_to_html_percent(
-            total=files_total, successes=files_successes)
+        per_test = success_to_html_percent(total=total, successes=successes)
+        per_file = success_to_html_percent(total=files_total, successes=files_successes)
         locations_main_page.write(
         locations_main_page.write(
-            '<tr>'
-            '<td><a href={location}/index.html>{location}</a></td>'
-            '<td>{pfiles}</td><td>{ptests}</td>'
-            '</tr>'
-            .format(location=location_type,
-                    pfiles=per_file, ptests=per_test))
-    locations_main_page.write('</tbody></table>')
-    locations_main_page.write('</body></html>')
+            "<tr>"
+            "<td><a href={location}/index.html>{location}</a></td>"
+            "<td>{pfiles}</td><td>{ptests}</td>"
+            "</tr>".format(location=location_type, pfiles=per_file, ptests=per_test)
+        )
+    locations_main_page.write("</tbody></table>")
+    locations_main_page.write("</body></html>")
     locations_main_page.close()
     locations_main_page.close()
     return 0
     return 0
 
 
-if __name__ == '__main__':
+
+if __name__ == "__main__":
     sys.exit(main())
     sys.exit(main())

+ 82 - 43
python/grass/gunittest/multirunner.py

@@ -29,7 +29,7 @@ if sys.version_info.major >= 3:
 def _get_encoding():
 def _get_encoding():
     encoding = locale.getdefaultlocale()[1]
     encoding = locale.getdefaultlocale()[1]
     if not encoding:
     if not encoding:
-        encoding = 'UTF-8'
+        encoding = "UTF-8"
     return encoding
     return encoding
 
 
 
 
@@ -49,9 +49,9 @@ def encode(string, encoding=None):
 
 
 def text_to_string(text):
 def text_to_string(text):
     """Convert text to str. Useful when passing text into environments,
     """Convert text to str. Useful when passing text into environments,
-       in Python 2 it needs to be bytes on Windows, in Python 3 in needs unicode.
+    in Python 2 it needs to be bytes on Windows, in Python 3 in needs unicode.
     """
     """
-    if sys.version[0] == '2':
+    if sys.version[0] == "2":
         # Python 2
         # Python 2
         return encode(text)
         return encode(text)
     else:
     else:
@@ -59,28 +59,45 @@ def text_to_string(text):
         return decode(text)
         return decode(text)
 
 
 
 
-
 def main():
 def main():
-    parser = argparse.ArgumentParser(
-        description='Run tests with new')
-    parser.add_argument('--location', '-l', required=True, action='append',
-                        dest='locations', metavar='LOCATION',
-                        help='Directories with reports')
-    parser.add_argument('--location-type', '-t', action='append',
-                        dest='location_types',
-                    default=[], metavar='TYPE',
-                    help='Add repeated values to a list',
-                        )
-    parser.add_argument('--grassbin', required=True,
-                        help='Use file timestamp instead of date in test summary')
+    parser = argparse.ArgumentParser(description="Run tests with new")
+    parser.add_argument(
+        "--location",
+        "-l",
+        required=True,
+        action="append",
+        dest="locations",
+        metavar="LOCATION",
+        help="Directories with reports",
+    )
+    parser.add_argument(
+        "--location-type",
+        "-t",
+        action="append",
+        dest="location_types",
+        default=[],
+        metavar="TYPE",
+        help="Add repeated values to a list",
+    )
+    parser.add_argument(
+        "--grassbin",
+        required=True,
+        help="Use file timestamp instead of date in test summary",
+    )
     # TODO: rename since every src can be used?
     # TODO: rename since every src can be used?
-    parser.add_argument('--grasssrc', required=True,
-                        help='GRASS GIS source code (to take tests from)')
-    parser.add_argument('--grassdata', required=True,
-                        help='GRASS GIS data base (GISDBASE)')
-    parser.add_argument('--create-main-report',
-                        help='Create also main report for all tests',
-                        action="store_true", default=False, dest='main_report')
+    parser.add_argument(
+        "--grasssrc", required=True, help="GRASS GIS source code (to take tests from)"
+    )
+    parser.add_argument(
+        "--grassdata", required=True, help="GRASS GIS data base (GISDBASE)"
+    )
+    parser.add_argument(
+        "--create-main-report",
+        help="Create also main report for all tests",
+        action="store_true",
+        default=False,
+        dest="main_report",
+    )
 
 
     args = parser.parse_args()
     args = parser.parse_args()
     gisdb = args.grassdata
     gisdb = args.grassdata
@@ -89,33 +106,39 @@ def main():
 
 
     # TODO: if locations empty or just one we can suppose the same all the time
     # TODO: if locations empty or just one we can suppose the same all the time
     if len(locations) != len(locations_types):
     if len(locations) != len(locations_types):
-        print("ERROR: Number of locations and their tags must be the same", file=sys.stderr)
+        print(
+            "ERROR: Number of locations and their tags must be the same",
+            file=sys.stderr,
+        )
         return 1
         return 1
 
 
-
     main_report = args.main_report
     main_report = args.main_report
     grasssrc = args.grasssrc  # TODO: can be guessed from dist
     grasssrc = args.grasssrc  # TODO: can be guessed from dist
     # TODO: create directory according to date and revision and create reports there
     # TODO: create directory according to date and revision and create reports there
 
 
     # some predefined variables, name of the GRASS launch script + location/mapset
     # some predefined variables, name of the GRASS launch script + location/mapset
-    #grass7bin = 'C:\Program Files (x86)\GRASS GIS 7.9.git\grass79dev.bat'
+    # grass7bin = 'C:\Program Files (x86)\GRASS GIS 7.9.git\grass79dev.bat'
     grass7bin = args.grassbin  # TODO: can be used if pressent
     grass7bin = args.grassbin  # TODO: can be used if pressent
 
 
     ########### SOFTWARE
     ########### SOFTWARE
     # query GRASS 7 itself for its GISBASE
     # query GRASS 7 itself for its GISBASE
     # we assume that GRASS GIS' start script is available and in the PATH
     # we assume that GRASS GIS' start script is available and in the PATH
     # the shell=True is here because of MS Windows? (code taken from wiki)
     # the shell=True is here because of MS Windows? (code taken from wiki)
-    startcmd = grass7bin + ' --config path'
-    p = subprocess.Popen(startcmd, shell=True,
-                         stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+    startcmd = grass7bin + " --config path"
+    p = subprocess.Popen(
+        startcmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE
+    )
     out, err = p.communicate()
     out, err = p.communicate()
     if p.returncode != 0:
     if p.returncode != 0:
-        print("ERROR: Cannot find GRASS GIS 7 start script (%s):\n%s" % (startcmd, err), file=sys.stderr)
+        print(
+            "ERROR: Cannot find GRASS GIS 7 start script (%s):\n%s" % (startcmd, err),
+            file=sys.stderr,
+        )
         return 1
         return 1
     gisbase = decode(out.strip())
     gisbase = decode(out.strip())
 
 
     # set GISBASE environment variable
     # set GISBASE environment variable
-    os.environ['GISBASE'] = text_to_string(gisbase)
+    os.environ["GISBASE"] = text_to_string(gisbase)
     # define GRASS Python environment
     # define GRASS Python environment
     grass_python_dir = os.path.join(gisbase, "etc", "python")
     grass_python_dir = os.path.join(gisbase, "etc", "python")
     sys.path.append(grass_python_dir)
     sys.path.append(grass_python_dir)
@@ -124,7 +147,7 @@ def main():
     # define GRASS DATABASE
     # define GRASS DATABASE
 
 
     # Set GISDBASE environment variable
     # Set GISDBASE environment variable
-    os.environ['GISDBASE'] = text_to_string(gisdb)
+    os.environ["GISDBASE"] = text_to_string(gisdb)
 
 
     # import GRASS Python package for initialization
     # import GRASS Python package for initialization
     import grass.script.setup as gsetup
     import grass.script.setup as gsetup
@@ -132,28 +155,43 @@ def main():
     # launch session
     # launch session
     # we need some location and mapset here
     # we need some location and mapset here
     # TODO: can init work without it or is there some demo location in dist?
     # TODO: can init work without it or is there some demo location in dist?
-    location = locations[0].split(':')[0]
-    mapset = 'PERMANENT'
+    location = locations[0].split(":")[0]
+    mapset = "PERMANENT"
     gsetup.init(gisbase, gisdb, location, mapset)
     gsetup.init(gisbase, gisdb, location, mapset)
 
 
     reports = []
     reports = []
     for location, location_type in zip(locations, locations_types):
     for location, location_type in zip(locations, locations_types):
         # here it is quite a good place to parallelize
         # here it is quite a good place to parallelize
         # including also type to make it unique and preserve it for sure
         # including also type to make it unique and preserve it for sure
-        report = 'report_for_' + location + '_' + location_type
+        report = "report_for_" + location + "_" + location_type
         absreport = os.path.abspath(report)
         absreport = os.path.abspath(report)
-        p = subprocess.Popen([sys.executable, '-tt',
-                              '-m', 'grass.gunittest.main',
-                              '--grassdata', gisdb, '--location', location,
-                              '--location-type', location_type,
-                              '--output', absreport],
-                              cwd=grasssrc)
+        p = subprocess.Popen(
+            [
+                sys.executable,
+                "-tt",
+                "-m",
+                "grass.gunittest.main",
+                "--grassdata",
+                gisdb,
+                "--location",
+                location,
+                "--location-type",
+                location_type,
+                "--output",
+                absreport,
+            ],
+            cwd=grasssrc,
+        )
         returncode = p.wait()
         returncode = p.wait()
         reports.append(report)
         reports.append(report)
 
 
     if main_report:
     if main_report:
         # TODO: solve the path to source code (work now only for grass source code)
         # TODO: solve the path to source code (work now only for grass source code)
-        arguments = [sys.executable, grasssrc + '/python/grass/gunittest/' + 'multireport.py', '--timestapms']
+        arguments = [
+            sys.executable,
+            grasssrc + "/python/grass/gunittest/" + "multireport.py",
+            "--timestapms",
+        ]
         arguments.extend(reports)
         arguments.extend(reports)
         p = subprocess.Popen(arguments)
         p = subprocess.Popen(arguments)
         returncode = p.wait()
         returncode = p.wait()
@@ -163,5 +201,6 @@ def main():
 
 
     return 0
     return 0
 
 
-if __name__ == '__main__':
+
+if __name__ == "__main__":
     sys.exit(main())
     sys.exit(main())

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 446 - 397
python/grass/gunittest/reporters.py


+ 57 - 47
python/grass/gunittest/runner.py

@@ -24,18 +24,18 @@ __unittest = True
 class _WritelnDecorator(object):
 class _WritelnDecorator(object):
     """Used to decorate file-like objects with a handy 'writeln' method"""
     """Used to decorate file-like objects with a handy 'writeln' method"""
 
 
-    def __init__(self,stream):
+    def __init__(self, stream):
         self.stream = stream
         self.stream = stream
 
 
     def __getattr__(self, attr):
     def __getattr__(self, attr):
-        if attr in ('stream', '__getstate__'):
+        if attr in ("stream", "__getstate__"):
             raise AttributeError(attr)
             raise AttributeError(attr)
-        return getattr(self.stream,attr)
+        return getattr(self.stream, attr)
 
 
     def writeln(self, arg=None):
     def writeln(self, arg=None):
         if arg:
         if arg:
             self.write(arg)
             self.write(arg)
-        self.write('\n') # text-mode streams translate to \r\n if needed
+        self.write("\n")  # text-mode streams translate to \r\n if needed
 
 
 
 
 class TestResult(unittest.TestResult):
 class TestResult(unittest.TestResult):
@@ -44,9 +44,9 @@ class TestResult(unittest.TestResult):
     # where are also unused, so perhaps we can remove them
     # where are also unused, so perhaps we can remove them
     # stream set to None and not included in interface, it would not make sense
     # stream set to None and not included in interface, it would not make sense
     def __init__(self, stream=None, descriptions=None, verbosity=None):
     def __init__(self, stream=None, descriptions=None, verbosity=None):
-        super(TestResult, self).__init__(stream=stream,
-                                         descriptions=descriptions,
-                                         verbosity=verbosity)
+        super(TestResult, self).__init__(
+            stream=stream, descriptions=descriptions, verbosity=verbosity
+        )
         self.successes = []
         self.successes = []
 
 
     def addSuccess(self, test):
     def addSuccess(self, test):
@@ -67,12 +67,14 @@ class TextTestResult(TestResult):
 
 
     Used by TextTestRunner.
     Used by TextTestRunner.
     """
     """
-    separator1 = '=' * 70
-    separator2 = '-' * 70
+
+    separator1 = "=" * 70
+    separator2 = "-" * 70
 
 
     def __init__(self, stream, descriptions, verbosity):
     def __init__(self, stream, descriptions, verbosity):
         super(TextTestResult, self).__init__(
         super(TextTestResult, self).__init__(
-            stream=stream, descriptions=descriptions, verbosity=verbosity)
+            stream=stream, descriptions=descriptions, verbosity=verbosity
+        )
         self.stream = _WritelnDecorator(stream)
         self.stream = _WritelnDecorator(stream)
         self.showAll = verbosity > 1
         self.showAll = verbosity > 1
         self.dots = verbosity == 1
         self.dots = verbosity == 1
@@ -85,7 +87,7 @@ class TextTestResult(TestResult):
     def getDescription(self, test):
     def getDescription(self, test):
         doc_first_line = test.shortDescription()
         doc_first_line = test.shortDescription()
         if self.descriptions and doc_first_line:
         if self.descriptions and doc_first_line:
-            return '\n'.join((str(test), doc_first_line))
+            return "\n".join((str(test), doc_first_line))
         else:
         else:
             return str(test)
             return str(test)
 
 
@@ -101,7 +103,7 @@ class TextTestResult(TestResult):
         if self.showAll:
         if self.showAll:
             self.stream.writeln("ok")
             self.stream.writeln("ok")
         elif self.dots:
         elif self.dots:
-            self.stream.write('.')
+            self.stream.write(".")
             self.stream.flush()
             self.stream.flush()
 
 
     def addError(self, test, err):
     def addError(self, test, err):
@@ -109,7 +111,7 @@ class TextTestResult(TestResult):
         if self.showAll:
         if self.showAll:
             self.stream.writeln("ERROR")
             self.stream.writeln("ERROR")
         elif self.dots:
         elif self.dots:
-            self.stream.write('E')
+            self.stream.write("E")
             self.stream.flush()
             self.stream.flush()
 
 
     def addFailure(self, test, err):
     def addFailure(self, test, err):
@@ -117,7 +119,7 @@ class TextTestResult(TestResult):
         if self.showAll:
         if self.showAll:
             self.stream.writeln("FAIL")
             self.stream.writeln("FAIL")
         elif self.dots:
         elif self.dots:
-            self.stream.write('F')
+            self.stream.write("F")
             self.stream.flush()
             self.stream.flush()
 
 
     def addSkip(self, test, reason):
     def addSkip(self, test, reason):
@@ -147,14 +149,13 @@ class TextTestResult(TestResult):
     def printErrors(self):
     def printErrors(self):
         if self.dots or self.showAll:
         if self.dots or self.showAll:
             self.stream.writeln()
             self.stream.writeln()
-        self.printErrorList('ERROR', self.errors)
-        self.printErrorList('FAIL', self.failures)
+        self.printErrorList("ERROR", self.errors)
+        self.printErrorList("FAIL", self.failures)
 
 
     def printErrorList(self, flavour, errors):
     def printErrorList(self, flavour, errors):
         for test, err in errors:
         for test, err in errors:
             self.stream.writeln(self.separator1)
             self.stream.writeln(self.separator1)
-            self.stream.writeln("%s: %s" % (flavour,
-                                            self.getDescription(test)))
+            self.stream.writeln("%s: %s" % (flavour, self.getDescription(test)))
             self.stream.writeln(self.separator2)
             self.stream.writeln(self.separator2)
             self.stream.writeln("%s" % err)
             self.stream.writeln("%s" % err)
 
 
@@ -174,9 +175,9 @@ class TextTestResult(TestResult):
         self.stream.writeln()
         self.stream.writeln()
 
 
         expectedFails = unexpectedSuccesses = skipped = 0
         expectedFails = unexpectedSuccesses = skipped = 0
-        results = map(len, (self.expectedFailures,
-                            self.unexpectedSuccesses,
-                            self.skipped))
+        results = map(
+            len, (self.expectedFailures, self.unexpectedSuccesses, self.skipped)
+        )
         expectedFails, unexpectedSuccesses, skipped = results
         expectedFails, unexpectedSuccesses, skipped = results
 
 
         infos = []
         infos = []
@@ -206,12 +207,14 @@ class KeyValueTestResult(TestResult):
 
 
     Used by TextTestRunner.
     Used by TextTestRunner.
     """
     """
-    separator1 = '=' * 70
-    separator2 = '-' * 70
+
+    separator1 = "=" * 70
+    separator2 = "-" * 70
 
 
     def __init__(self, stream, test_type=None):
     def __init__(self, stream, test_type=None):
         super(KeyValueTestResult, self).__init__(
         super(KeyValueTestResult, self).__init__(
-            stream=stream, descriptions=None, verbosity=None)
+            stream=stream, descriptions=None, verbosity=None
+        )
         self._stream = _WritelnDecorator(stream)
         self._stream = _WritelnDecorator(stream)
 
 
         self.start_time = None
         self.start_time = None
@@ -221,7 +224,7 @@ class KeyValueTestResult(TestResult):
         if test_type:
         if test_type:
             self.test_type = test_type
             self.test_type = test_type
         else:
         else:
-            self.test_type = 'not-specified'
+            self.test_type = "not-specified"
 
 
         self._grass_modules = []
         self._grass_modules = []
         self._supplementary_files = []
         self._supplementary_files = []
@@ -233,9 +236,9 @@ class KeyValueTestResult(TestResult):
 
 
     def stopTest(self, test):
     def stopTest(self, test):
         super(KeyValueTestResult, self).stopTest(test)
         super(KeyValueTestResult, self).stopTest(test)
-        if hasattr(test, 'grass_modules'):
+        if hasattr(test, "grass_modules"):
             self._grass_modules.extend(test.grass_modules)
             self._grass_modules.extend(test.grass_modules)
-        if hasattr(test, 'supplementary_files'):
+        if hasattr(test, "supplementary_files"):
             self._supplementary_files.extend(test.supplementary_files)
             self._supplementary_files.extend(test.supplementary_files)
 
 
     def stopTestRun(self):
     def stopTestRun(self):
@@ -251,19 +254,19 @@ class KeyValueTestResult(TestResult):
         # infos.append("name=%s" % 'unknown')
         # infos.append("name=%s" % 'unknown')
 
 
         infos.append("time=%.3fs" % (self.time_taken))
         infos.append("time=%.3fs" % (self.time_taken))
-#            'date={rundate}\n'
-#            'date={runtime}\n'
-#            'date={start_datetime}\n'
-#            'date={end_datetime}\n'
+        #            'date={rundate}\n'
+        #            'date={runtime}\n'
+        #            'date={start_datetime}\n'
+        #            'date={end_datetime}\n'
 
 
         failed, errored = map(len, (self.failures, self.errors))
         failed, errored = map(len, (self.failures, self.errors))
         succeeded = len(self.successes)
         succeeded = len(self.successes)
-        results = map(len, (self.expectedFailures,
-                            self.unexpectedSuccesses,
-                            self.skipped))
+        results = map(
+            len, (self.expectedFailures, self.unexpectedSuccesses, self.skipped)
+        )
         expectedFails, unexpectedSuccesses, skipped = results
         expectedFails, unexpectedSuccesses, skipped = results
 
 
-        status = 'succeeded' if self.wasSuccessful() else 'failed'
+        status = "succeeded" if self.wasSuccessful() else "failed"
         infos.append("status=%s" % status)
         infos.append("status=%s" % status)
 
 
         # if only errors occur, tests are not counted properly
         # if only errors occur, tests are not counted properly
@@ -287,8 +290,8 @@ class KeyValueTestResult(TestResult):
         infos.append("unexpected_successes=%d" % unexpectedSuccesses)
         infos.append("unexpected_successes=%d" % unexpectedSuccesses)
 
 
         # TODO: include each module just once? list good and bad modules?
         # TODO: include each module just once? list good and bad modules?
-        infos.append("tested_modules=%s" % ','.join(self._grass_modules))
-        infos.append("supplementary_files=%s" % ','.join(self._supplementary_files))
+        infos.append("tested_modules=%s" % ",".join(self._grass_modules))
+        infos.append("supplementary_files=%s" % ",".join(self._supplementary_files))
 
 
         # module, modules?, c, c++?, python
         # module, modules?, c, c++?, python
         # TODO: include also type modules?
         # TODO: include also type modules?
@@ -296,8 +299,8 @@ class KeyValueTestResult(TestResult):
         # TODO: distinguish C and Python modules?
         # TODO: distinguish C and Python modules?
         infos.append("test_type=%s" % (self.test_type))
         infos.append("test_type=%s" % (self.test_type))
 
 
-        self._stream.write('\n'.join(infos))
-        self._stream.write('\n')
+        self._stream.write("\n".join(infos))
+        self._stream.write("\n")
         self._stream.flush()
         self._stream.flush()
 
 
 
 
@@ -306,10 +309,10 @@ class MultiTestResult(TestResult):
     # included for compatibility with unittest's TestResult
     # included for compatibility with unittest's TestResult
     # where are also unused, so perhaps we can remove them
     # where are also unused, so perhaps we can remove them
     # stream set to None and not included in interface, it would not make sense
     # stream set to None and not included in interface, it would not make sense
-    def __init__(self, results, forgiving=False,
-                 descriptions=None, verbosity=None):
+    def __init__(self, results, forgiving=False, descriptions=None, verbosity=None):
         super(MultiTestResult, self).__init__(
         super(MultiTestResult, self).__init__(
-            descriptions=descriptions, verbosity=verbosity, stream=None)
+            descriptions=descriptions, verbosity=verbosity, stream=None
+        )
         self._results = results
         self._results = results
         self._forgiving = forgiving
         self._forgiving = forgiving
 
 
@@ -460,8 +463,15 @@ class MultiTestResult(TestResult):
 
 
 
 
 class GrassTestRunner(object):
 class GrassTestRunner(object):
-    def __init__(self, stream=sys.stderr, descriptions=True, verbosity=1,
-                 failfast=False, buffer=False, result=None):
+    def __init__(
+        self,
+        stream=sys.stderr,
+        descriptions=True,
+        verbosity=1,
+        failfast=False,
+        buffer=False,
+        result=None,
+    ):
         self.stream = _WritelnDecorator(stream)
         self.stream = _WritelnDecorator(stream)
         self.descriptions = descriptions
         self.descriptions = descriptions
         self.verbosity = verbosity
         self.verbosity = verbosity
@@ -476,7 +486,7 @@ class GrassTestRunner(object):
         result.failfast = self.failfast
         result.failfast = self.failfast
         result.buffer = self.buffer
         result.buffer = self.buffer
         startTime = time.time()
         startTime = time.time()
-        startTestRun = getattr(result, 'startTestRun', None)
+        startTestRun = getattr(result, "startTestRun", None)
         if startTestRun is not None:
         if startTestRun is not None:
             startTestRun()
             startTestRun()
         try:
         try:
@@ -484,10 +494,10 @@ class GrassTestRunner(object):
         finally:
         finally:
             stopTime = time.time()
             stopTime = time.time()
             timeTaken = stopTime - startTime
             timeTaken = stopTime - startTime
-            setTimes = getattr(result, 'setTimes', None)
+            setTimes = getattr(result, "setTimes", None)
             if setTimes is not None:
             if setTimes is not None:
                 setTimes(startTime, stopTime, timeTaken)
                 setTimes(startTime, stopTime, timeTaken)
-            stopTestRun = getattr(result, 'stopTestRun', None)
+            stopTestRun = getattr(result, "stopTestRun", None)
             if stopTestRun is not None:
             if stopTestRun is not None:
                 stopTestRun()
                 stopTestRun()
 
 

+ 6 - 6
python/grass/gunittest/testsuite/data/samplecode/submodule_errors/subsubmodule_errors/testsuite/test_error.py

@@ -8,7 +8,7 @@ class TestError(TestCase):
     # pylint: disable=R0904
     # pylint: disable=R0904
 
 
     def test_something(self):
     def test_something(self):
-        raise RuntimeError('Error in test function')
+        raise RuntimeError("Error in test function")
         self.assertTrue(True)
         self.assertTrue(True)
 
 
 
 
@@ -16,7 +16,7 @@ class TestErrorSetUp(TestCase):
     # pylint: disable=R0904
     # pylint: disable=R0904
 
 
     def setUp(self):
     def setUp(self):
-        raise RuntimeError('Error in setUp')
+        raise RuntimeError("Error in setUp")
 
 
     def test_something(self):
     def test_something(self):
         self.assertTrue(True)
         self.assertTrue(True)
@@ -26,7 +26,7 @@ class TestErrorTearDown(TestCase):
     # pylint: disable=R0904
     # pylint: disable=R0904
 
 
     def tearDown(self):
     def tearDown(self):
-        raise RuntimeError('Error in tearDown')
+        raise RuntimeError("Error in tearDown")
 
 
     def test_something(self):
     def test_something(self):
         self.assertTrue(True)
         self.assertTrue(True)
@@ -37,7 +37,7 @@ class TestErrorClassSetUp(TestCase):
 
 
     @classmethod
     @classmethod
     def setUpClass(cls):
     def setUpClass(cls):
-        raise RuntimeError('Error in setUpClass')
+        raise RuntimeError("Error in setUpClass")
 
 
     def test_something(self):
     def test_something(self):
         self.assertTrue(True)
         self.assertTrue(True)
@@ -48,11 +48,11 @@ class TestErrorClassTearDown(TestCase):
 
 
     @classmethod
     @classmethod
     def tearDownClass(cls):
     def tearDownClass(cls):
-        raise RuntimeError('Error in tearDownClass')
+        raise RuntimeError("Error in tearDownClass")
 
 
     def test_something(self):
     def test_something(self):
         self.assertTrue(True)
         self.assertTrue(True)
 
 
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     test()
     test()

+ 8 - 6
python/grass/gunittest/testsuite/data/samplecode/submodule_errors/subsubmodule_errors/testsuite/test_import_error.py

@@ -2,7 +2,7 @@
 
 
 # comment to get rid of the wrong import
 # comment to get rid of the wrong import
 # (if it is imported before all tests start and everything would fail)
 # (if it is imported before all tests start and everything would fail)
-#import this_module_or_package_does_not_exists__testing_import_error
+# import this_module_or_package_does_not_exists__testing_import_error
 
 
 from grass.gunittest.case import TestCase
 from grass.gunittest.case import TestCase
 from grass.gunittest.main import test
 from grass.gunittest.main import test
@@ -12,11 +12,13 @@ class TestNeverCalled(TestCase):
     # pylint: disable=R0904
     # pylint: disable=R0904
 
 
     def test_something(self):
     def test_something(self):
-        self.assertFalse("This should not be called"
-                         " if we are testing failed import."
-                         " It is all right if this fails and the wrong"
-                         " import is commented.")
+        self.assertFalse(
+            "This should not be called"
+            " if we are testing failed import."
+            " It is all right if this fails and the wrong"
+            " import is commented."
+        )
 
 
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     test()
     test()

+ 1 - 1
python/grass/gunittest/testsuite/data/samplecode/submodule_errors/subsubmodule_exiting/testsuite/test_gfatalerror.py

@@ -12,5 +12,5 @@ class TestGFatalError(TestCase):
         libgis.G_fatal_error("Testing G_fatal_error() function call")
         libgis.G_fatal_error("Testing G_fatal_error() function call")
 
 
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     test()
     test()

+ 1 - 1
python/grass/gunittest/testsuite/data/samplecode/submodule_errors/subsubmodule_exiting/testsuite/test_osexit_one.py

@@ -12,5 +12,5 @@ class TestOsExit(TestCase):
         os._exit(1)
         os._exit(1)
 
 
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     test()
     test()

+ 1 - 1
python/grass/gunittest/testsuite/data/samplecode/submodule_errors/subsubmodule_exiting/testsuite/test_osexit_zero.py

@@ -12,5 +12,5 @@ class TestOsExit(TestCase):
         os._exit(0)
         os._exit(0)
 
 
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     test()
     test()

+ 3 - 3
python/grass/gunittest/testsuite/data/samplecode/submodule_errors/subsubmodule_exiting/testsuite/test_segfaut.py

@@ -10,14 +10,14 @@ class TestSegfault(TestCase):
 
 
     def test_something(self):
     def test_something(self):
         """Crash the Python interpreter"""
         """Crash the Python interpreter"""
-        i = ctypes.c_char('a')
+        i = ctypes.c_char("a")
         j = ctypes.pointer(i)
         j = ctypes.pointer(i)
         c = 0
         c = 0
         while True:
         while True:
-            j[c] = 'a'
+            j[c] = "a"
             c += 1
             c += 1
         j
         j
 
 
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     test()
     test()

+ 1 - 1
python/grass/gunittest/testsuite/data/samplecode/submodule_errors/subsubmodule_exiting/testsuite/test_sysexit_one.py

@@ -12,5 +12,5 @@ class TestSysExit(TestCase):
         sys.exit(1)
         sys.exit(1)
 
 
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     test()
     test()

+ 1 - 1
python/grass/gunittest/testsuite/data/samplecode/submodule_errors/subsubmodule_exiting/testsuite/test_sysexit_zero.py

@@ -12,5 +12,5 @@ class TestSysExit(TestCase):
         sys.exit(0)
         sys.exit(0)
 
 
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     test()
     test()

+ 1 - 1
python/grass/gunittest/testsuite/data/samplecode/submodule_test_fail/testsuite/test_fail.py

@@ -11,5 +11,5 @@ class TestFail(TestCase):
         self.assertTrue(False)
         self.assertTrue(False)
 
 
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     test()
     test()

+ 3 - 2
python/grass/gunittest/testsuite/data/samplecode/testsuite/test_good_and_bad.py

@@ -17,8 +17,9 @@ class TestSuccessAndFailure(TestCase):
         self.assertTrue(False, msg="This failed in test_good_and_bad")
         self.assertTrue(False, msg="This failed in test_good_and_bad")
 
 
     def test_something_erroring(self):
     def test_something_erroring(self):
-        raise RuntimeError('Some error which was raised')
+        raise RuntimeError("Some error which was raised")
         self.assertTrue(True, msg="This should not fail in test_good_and_bad")
         self.assertTrue(True, msg="This should not fail in test_good_and_bad")
 
 
-if __name__ == '__main__':
+
+if __name__ == "__main__":
     test()
     test()

+ 2 - 1
python/grass/gunittest/testsuite/data/samplecode/testsuite/test_python_unittest.py

@@ -36,5 +36,6 @@ class TestUnittestSuccessVerboseClassSetUp(TestCase):
     def test_something_failing(self):
     def test_something_failing(self):
         self.assertTrue(False, msg="This should fail")
         self.assertTrue(False, msg="This should fail")
 
 
-if __name__ == '__main__':
+
+if __name__ == "__main__":
     main()
     main()

+ 2 - 1
python/grass/gunittest/testsuite/data/samplecode/testsuite/test_success.py

@@ -32,5 +32,6 @@ class TestSuccessVerboseClassSetUp(TestCase):
     def test_something(self):
     def test_something(self):
         self.assertTrue(True)
         self.assertTrue(True)
 
 
-if __name__ == '__main__':
+
+if __name__ == "__main__":
     test()
     test()

+ 206 - 164
python/grass/gunittest/testsuite/test_assertions.py

@@ -22,36 +22,37 @@ class TestTextAssertions(TestCase):
     platfrom_newline = "aaa{nl}bbb{nl}".format(nl=os.linesep)
     platfrom_newline = "aaa{nl}bbb{nl}".format(nl=os.linesep)
 
 
     def test_assertLooksLike(self):
     def test_assertLooksLike(self):
-        self.assertLooksLike("Generated map is <elevation>",
-                             "Generated map is <...>")
-        self.assertRaises(self.failureException,
-                          self.assertLooksLike,
-                          "Generated map is elevation.",
-                          "Generated map is <...>")
-        self.assertLooksLike("Projection string: '+proj=longlat +datum=WGS84'",
-                             "Projection string: ...")
+        self.assertLooksLike("Generated map is <elevation>", "Generated map is <...>")
+        self.assertRaises(
+            self.failureException,
+            self.assertLooksLike,
+            "Generated map is elevation.",
+            "Generated map is <...>",
+        )
+        self.assertLooksLike(
+            "Projection string: '+proj=longlat +datum=WGS84'", "Projection string: ..."
+        )
 
 
     def test_assertLooksLike_multiline(self):
     def test_assertLooksLike_multiline(self):
-        self.assertLooksLike("a=123\nb=456\nc=789",
-                             "a=...\nb=...\nc=...")
+        self.assertLooksLike("a=123\nb=456\nc=789", "a=...\nb=...\nc=...")
 
 
     def test_assertLooksLike_multiline_platform_dependent(self):
     def test_assertLooksLike_multiline_platform_dependent(self):
-        self.assertLooksLike("a=123\nb=456\nc=789",
-                             "a=...{nl}b=...{nl}c=...".format(nl=os.linesep))
+        self.assertLooksLike(
+            "a=123\nb=456\nc=789", "a=...{nl}b=...{nl}c=...".format(nl=os.linesep)
+        )
 
 
     def test_assertLooksLike_numbers(self):
     def test_assertLooksLike_numbers(self):
-        self.assertLooksLike("abc = 125521",
-                             "abc = 125...")
-        self.assertLooksLike("abc = 689.156",
-                             "abc = 689...")
-        self.assertLooksLike("abc = 689.159589",
-                             "abc = 689.15...")
+        self.assertLooksLike("abc = 125521", "abc = 125...")
+        self.assertLooksLike("abc = 689.156", "abc = 689...")
+        self.assertLooksLike("abc = 689.159589", "abc = 689.15...")
         # this should fail according to the implementation
         # this should fail according to the implementation
         # first three dots are considered as ellipses
         # first three dots are considered as ellipses
-        self.assertRaises(self.failureException,
-                          self.assertLooksLike,
-                          "abc = 689.159589",
-                          "abc = 689....")
+        self.assertRaises(
+            self.failureException,
+            self.assertLooksLike,
+            "abc = 689.159589",
+            "abc = 689....",
+        )
 
 
     def do_all_combidnations(self, first, second, function):
     def do_all_combidnations(self, first, second, function):
         function(first, first)
         function(first, first)
@@ -61,27 +62,30 @@ class TestTextAssertions(TestCase):
 
 
     def test_assertMultiLineEqual(self):
     def test_assertMultiLineEqual(self):
         r"""Test different combinations of ``\n`` and os.linesep"""
         r"""Test different combinations of ``\n`` and os.linesep"""
-        self.do_all_combidnations(self.std_newline, self.platfrom_newline,
-                                  function=self.assertMultiLineEqual)
+        self.do_all_combidnations(
+            self.std_newline, self.platfrom_newline, function=self.assertMultiLineEqual
+        )
 
 
     def test_assertMultiLineEqual_raises(self):
     def test_assertMultiLineEqual_raises(self):
         """Test mixed line endings"""
         """Test mixed line endings"""
-        self.assertRaises(self.failureException,
-                          self.assertMultiLineEqual,
-                          "aaa\n\rbbb\r",
-                          "aaa\nbbb\n")
+        self.assertRaises(
+            self.failureException,
+            self.assertMultiLineEqual,
+            "aaa\n\rbbb\r",
+            "aaa\nbbb\n",
+        )
 
 
     def test_assertEqual(self):
     def test_assertEqual(self):
         """Test for of newlines for strings (uses overwritten assertMultiLineEqual())"""
         """Test for of newlines for strings (uses overwritten assertMultiLineEqual())"""
-        self.do_all_combidnations(self.std_newline, self.platfrom_newline,
-                                  function=self.assertEqual)
+        self.do_all_combidnations(
+            self.std_newline, self.platfrom_newline, function=self.assertEqual
+        )
 
 
     def test_assertEqual_raises(self):
     def test_assertEqual_raises(self):
         """Test mixed line endings"""
         """Test mixed line endings"""
-        self.assertRaises(self.failureException,
-                          self.assertEqual,
-                          "aaa\n\rbbb\r",
-                          "aaa\nbbb\n")
+        self.assertRaises(
+            self.failureException, self.assertEqual, "aaa\n\rbbb\r", "aaa\nbbb\n"
+        )
 
 
 
 
 R_UNIVAR_ELEVATION_SUBSET = """n=2025000
 R_UNIVAR_ELEVATION_SUBSET = """n=2025000
@@ -102,7 +106,7 @@ datatype=FCELL
 """
 """
 
 
 # r.info -gre map=elevation
 # r.info -gre map=elevation
-ELEVATION_MAPSET_DICT = {'mapset': 'PERMANENT'}
+ELEVATION_MAPSET_DICT = {"mapset": "PERMANENT"}
 
 
 # r.univar map=elevation
 # r.univar map=elevation
 ELEVATION_MINMAX = """min=55.5787925720215
 ELEVATION_MINMAX = """min=55.5787925720215
@@ -110,17 +114,18 @@ max=156.329864501953
 """
 """
 
 
 # values rounded manually to maximal expected perecision
 # values rounded manually to maximal expected perecision
-ELEVATION_MINMAX_DICT = {'min': 55.58, 'max': 156.33}
+ELEVATION_MINMAX_DICT = {"min": 55.58, "max": 156.33}
 
 
 
 
 class TestAssertModuleKeyValue(TestCase):
 class TestAssertModuleKeyValue(TestCase):
     """Test usage of `assertModuleKeyValue` method."""
     """Test usage of `assertModuleKeyValue` method."""
+
     # pylint: disable=R0904
     # pylint: disable=R0904
 
 
     @classmethod
     @classmethod
     def setUpClass(cls):
     def setUpClass(cls):
         cls.use_temp_region()
         cls.use_temp_region()
-        cls.runModule(SimpleModule('g.region', raster='elevation'))
+        cls.runModule(SimpleModule("g.region", raster="elevation"))
 
 
     @classmethod
     @classmethod
     def tearDownClass(cls):
     def tearDownClass(cls):
@@ -128,31 +133,38 @@ class TestAssertModuleKeyValue(TestCase):
 
 
     def test_pygrass_module(self):
     def test_pygrass_module(self):
         """Test syntax with Module and required parameters as module"""
         """Test syntax with Module and required parameters as module"""
-        module = Module('r.info', map='elevation', flags='gr',
-                        run_=False, finish_=True)
-        self.assertModuleKeyValue(module,
-                                  reference=dict(min=55.58, max=156.33),
-                                  precision=0.01, sep='=')
+        module = Module("r.info", map="elevation", flags="gr", run_=False, finish_=True)
+        self.assertModuleKeyValue(
+            module, reference=dict(min=55.58, max=156.33), precision=0.01, sep="="
+        )
 
 
     def test_pygrass_simple_module(self):
     def test_pygrass_simple_module(self):
         """Test syntax with SimpleModule as module"""
         """Test syntax with SimpleModule as module"""
-        module = SimpleModule('r.info', map='elevation', flags='gr')
-        self.assertModuleKeyValue(module,
-                                  reference=dict(min=55.58, max=156.33),
-                                  precision=0.01, sep='=')
+        module = SimpleModule("r.info", map="elevation", flags="gr")
+        self.assertModuleKeyValue(
+            module, reference=dict(min=55.58, max=156.33), precision=0.01, sep="="
+        )
 
 
     def test_direct_parameters(self):
     def test_direct_parameters(self):
         """Test syntax with module and its parameters as function parameters"""
         """Test syntax with module and its parameters as function parameters"""
-        self.assertModuleKeyValue('r.info', map='elevation', flags='gr',
-                                  reference=dict(min=55.58, max=156.33),
-                                  precision=0.01, sep='=')
+        self.assertModuleKeyValue(
+            "r.info",
+            map="elevation",
+            flags="gr",
+            reference=dict(min=55.58, max=156.33),
+            precision=0.01,
+            sep="=",
+        )
 
 
     def test_parameters_parameter(self):
     def test_parameters_parameter(self):
         """Test syntax with module parameters in one parameters dictionary"""
         """Test syntax with module parameters in one parameters dictionary"""
-        self.assertModuleKeyValue(module='r.info',
-                                  parameters=dict(map='elevation', flags='gr'),
-                                  reference=dict(min=55.58, max=156.33),
-                                  precision=0.01, sep='=')
+        self.assertModuleKeyValue(
+            module="r.info",
+            parameters=dict(map="elevation", flags="gr"),
+            reference=dict(min=55.58, max=156.33),
+            precision=0.01,
+            sep="=",
+        )
 
 
 
 
 class TestRasterMapAssertions(TestCase):
 class TestRasterMapAssertions(TestCase):
@@ -162,108 +174,128 @@ class TestRasterMapAssertions(TestCase):
     def setUpClass(cls):
     def setUpClass(cls):
         cls.use_temp_region()
         cls.use_temp_region()
         # TODO: here we should actually not call self.runModule but call_module
         # TODO: here we should actually not call self.runModule but call_module
-        cls.runModule(SimpleModule('g.region', raster='elevation'))
+        cls.runModule(SimpleModule("g.region", raster="elevation"))
 
 
     @classmethod
     @classmethod
     def tearDownClass(cls):
     def tearDownClass(cls):
         cls.del_temp_region()
         cls.del_temp_region()
 
 
     def test_assertRasterFitsUnivar(self):
     def test_assertRasterFitsUnivar(self):
-        self.assertRasterFitsUnivar('elevation', R_UNIVAR_ELEVATION_SUBSET,
-                                    precision=0.01)
-        self.assertRaises(self.failureException,
-                          self.assertRasterFitsUnivar,
-                          'geology', R_UNIVAR_ELEVATION_SUBSET, precision=0.01)
-        self.assertRaises(ValueError,
-                          self.assertRasterFitsUnivar,
-                          'elevation', RANDOM_KEYVALUES)
+        self.assertRasterFitsUnivar(
+            "elevation", R_UNIVAR_ELEVATION_SUBSET, precision=0.01
+        )
+        self.assertRaises(
+            self.failureException,
+            self.assertRasterFitsUnivar,
+            "geology",
+            R_UNIVAR_ELEVATION_SUBSET,
+            precision=0.01,
+        )
+        self.assertRaises(
+            ValueError, self.assertRasterFitsUnivar, "elevation", RANDOM_KEYVALUES
+        )
 
 
     def test_assertRasterFitsInfo(self):
     def test_assertRasterFitsInfo(self):
-        self.assertRasterFitsInfo('elevation', R_INFO_ELEVATION_SUBSET)
-        self.assertRaises(self.failureException,
-                          self.assertRasterFitsInfo,
-                          'geology', R_INFO_ELEVATION_SUBSET)
-        self.assertRaises(ValueError,
-                          self.assertRasterFitsInfo,
-                          'elevation', RANDOM_KEYVALUES)
+        self.assertRasterFitsInfo("elevation", R_INFO_ELEVATION_SUBSET)
+        self.assertRaises(
+            self.failureException,
+            self.assertRasterFitsInfo,
+            "geology",
+            R_INFO_ELEVATION_SUBSET,
+        )
+        self.assertRaises(
+            ValueError, self.assertRasterFitsInfo, "elevation", RANDOM_KEYVALUES
+        )
 
 
     def test_common_values_info_univar(self):
     def test_common_values_info_univar(self):
-        self.assertRasterFitsUnivar('elevation',
-                                    ELEVATION_MINMAX, precision=0.01)
-        self.assertRasterFitsInfo('elevation',
-                                  ELEVATION_MINMAX, precision=0.01)
+        self.assertRasterFitsUnivar("elevation", ELEVATION_MINMAX, precision=0.01)
+        self.assertRasterFitsInfo("elevation", ELEVATION_MINMAX, precision=0.01)
 
 
     def test_dict_as_parameter(self):
     def test_dict_as_parameter(self):
         """This also tests if we are using r.info -e flag and that precision is
         """This also tests if we are using r.info -e flag and that precision is
         not required for strings.
         not required for strings.
         """
         """
-        self.assertRasterFitsInfo('elevation', ELEVATION_MAPSET_DICT)
+        self.assertRasterFitsInfo("elevation", ELEVATION_MAPSET_DICT)
 
 
     def test_assertRastersNoDifference(self):
     def test_assertRastersNoDifference(self):
         """Test basic usage of assertRastersNoDifference"""
         """Test basic usage of assertRastersNoDifference"""
-        self.assertRastersNoDifference(actual='elevation',
-                                       reference='elevation',
-                                       precision=0,  # this might need to be increased
-                                       msg="The same maps should have no difference")
-        self.assertRaises(self.failureException,
-                          self.assertRastersNoDifference,
-                          actual='elevation',
-                          reference='geology',
-                          precision=1,
-                          msg="Different maps should have difference")
+        self.assertRastersNoDifference(
+            actual="elevation",
+            reference="elevation",
+            precision=0,  # this might need to be increased
+            msg="The same maps should have no difference",
+        )
+        self.assertRaises(
+            self.failureException,
+            self.assertRastersNoDifference,
+            actual="elevation",
+            reference="geology",
+            precision=1,
+            msg="Different maps should have difference",
+        )
 
 
     def test_assertRastersNoDifference_mean(self):
     def test_assertRastersNoDifference_mean(self):
         """Test usage of assertRastersNoDifference with mean"""
         """Test usage of assertRastersNoDifference with mean"""
-        self.assertRastersNoDifference(actual='elevation',
-                                       reference='elevation',
-                                       precision=0,  # this might need to be increased
-                                       statistics=dict(mean=0),
-                                       msg="The difference of same maps should have small mean")
-        self.assertRaises(self.failureException,
-                          self.assertRastersNoDifference,
-                          actual='elevation',
-                          reference='geology',
-                          precision=1,
-                          statistics=dict(mean=0),
-                          msg="The difference of different maps should have huge mean")
+        self.assertRastersNoDifference(
+            actual="elevation",
+            reference="elevation",
+            precision=0,  # this might need to be increased
+            statistics=dict(mean=0),
+            msg="The difference of same maps should have small mean",
+        )
+        self.assertRaises(
+            self.failureException,
+            self.assertRastersNoDifference,
+            actual="elevation",
+            reference="geology",
+            precision=1,
+            statistics=dict(mean=0),
+            msg="The difference of different maps should have huge mean",
+        )
 
 
     def test_assertRastersEqual(self):
     def test_assertRastersEqual(self):
         """Test basic usage of assertRastersEqual"""
         """Test basic usage of assertRastersEqual"""
-        self.assertRastersEqual(actual='lakes',
-                                       reference='lakes',
-                                       precision=0,
-                                       msg="The same maps should have no difference")
-        self.assertRaises(self.failureException,
-                          self.assertRastersEqual,
-                          actual='elevation',
-                          reference='lakes',
-                          precision=1,
-                          msg="Different maps should have difference")
+        self.assertRastersEqual(
+            actual="lakes",
+            reference="lakes",
+            precision=0,
+            msg="The same maps should have no difference",
+        )
+        self.assertRaises(
+            self.failureException,
+            self.assertRastersEqual,
+            actual="elevation",
+            reference="lakes",
+            precision=1,
+            msg="Different maps should have difference",
+        )
 
 
 
 
 class TestMapExistsAssertions(TestCase):
 class TestMapExistsAssertions(TestCase):
     # pylint: disable=R0904
     # pylint: disable=R0904
 
 
-    raster_cell = 'TestMapExistsAssertions_raster_cell'
-    raster_dcell = 'TestMapExistsAssertions_raster_dcell'
-    raster3d = 'TestMapExistsAssertions_raster3D'
-    vector = 'TestMapExistsAssertions_vector'
+    raster_cell = "TestMapExistsAssertions_raster_cell"
+    raster_dcell = "TestMapExistsAssertions_raster_dcell"
+    raster3d = "TestMapExistsAssertions_raster3D"
+    vector = "TestMapExistsAssertions_vector"
 
 
     @classmethod
     @classmethod
     def setUpClass(cls):
     def setUpClass(cls):
         cls.use_temp_region()
         cls.use_temp_region()
-        cls.runModule('g.region', n=10, e=10, s=0, w=0, t=10, b=0, res=1)
-        cls.runModule('r.mapcalc', expression=cls.raster_cell + ' = 1')
-        cls.runModule('r.mapcalc', expression=cls.raster_dcell + ' = 1.0')
-        cls.runModule('r3.mapcalc', expression=cls.raster3d + ' = 1.0')
-        cls.runModule('v.edit', map=cls.vector, tool='create')
+        cls.runModule("g.region", n=10, e=10, s=0, w=0, t=10, b=0, res=1)
+        cls.runModule("r.mapcalc", expression=cls.raster_cell + " = 1")
+        cls.runModule("r.mapcalc", expression=cls.raster_dcell + " = 1.0")
+        cls.runModule("r3.mapcalc", expression=cls.raster3d + " = 1.0")
+        cls.runModule("v.edit", map=cls.vector, tool="create")
 
 
     @classmethod
     @classmethod
     def tearDownClass(cls):
     def tearDownClass(cls):
-        cls.runModule('g.remove', flags='f',
-                      type=['raster', 'raster3d', 'vector'],
-                      name=[cls.raster_cell, cls.raster_dcell,
-                            cls.raster3d, cls.vector])
+        cls.runModule(
+            "g.remove",
+            flags="f",
+            type=["raster", "raster3d", "vector"],
+            name=[cls.raster_cell, cls.raster_dcell, cls.raster3d, cls.vector],
+        )
         cls.del_temp_region()
         cls.del_temp_region()
 
 
     def test_rast_cell_exists(self):
     def test_rast_cell_exists(self):
@@ -273,34 +305,36 @@ class TestMapExistsAssertions(TestCase):
         self.assertRasterExists(self.raster_dcell)
         self.assertRasterExists(self.raster_dcell)
 
 
     def test_rast_does_not_exist(self):
     def test_rast_does_not_exist(self):
-        self.assertRaises(self.failureException,
-                          self.assertRasterExists,
-                          'does_not_exists')
+        self.assertRaises(
+            self.failureException, self.assertRasterExists, "does_not_exists"
+        )
 
 
     def test_rast3d_exists(self):
     def test_rast3d_exists(self):
         self.assertRaster3dExists(self.raster3d)
         self.assertRaster3dExists(self.raster3d)
 
 
     def test_rast3d_does_not_exist(self):
     def test_rast3d_does_not_exist(self):
-        self.assertRaises(self.failureException,
-                          self.assertRaster3dExists,
-                          'does_not_exists')
+        self.assertRaises(
+            self.failureException, self.assertRaster3dExists, "does_not_exists"
+        )
 
 
     def test_vect_exists(self):
     def test_vect_exists(self):
         self.assertVectorExists(self.vector)
         self.assertVectorExists(self.vector)
 
 
     def test_vect_does_not_exist(self):
     def test_vect_does_not_exist(self):
-        self.assertRaises(self.failureException,
-                          self.assertVectorExists,
-                          'does_not_exists')
+        self.assertRaises(
+            self.failureException, self.assertVectorExists, "does_not_exists"
+        )
 
 
     def test_rast_does_not_exist_in_current_mapset(self):
     def test_rast_does_not_exist_in_current_mapset(self):
         # expecting that there is elevation in PERMANENT
         # expecting that there is elevation in PERMANENT
         # TODO: use skip decorator
         # TODO: use skip decorator
         # TODO: add the same tests but for vect and rast3d
         # TODO: add the same tests but for vect and rast3d
-        self.assertRaises(self.failureException,
-                          self.assertRasterExists,
-                          'elevation',
-                          msg="Rasters from different mapsets should be ignored")
+        self.assertRaises(
+            self.failureException,
+            self.assertRasterExists,
+            "elevation",
+            msg="Rasters from different mapsets should be ignored",
+        )
 
 
 
 
 class TestFileAssertions(TestCase):
 class TestFileAssertions(TestCase):
@@ -310,27 +344,27 @@ class TestFileAssertions(TestCase):
     def setUpClass(cls):
     def setUpClass(cls):
         # we expect WIND to be always present
         # we expect WIND to be always present
         gisenv = gcore.gisenv()
         gisenv = gcore.gisenv()
-        cls.existing_file = os.path.join(gisenv['GISDBASE'],
-                                         gisenv['LOCATION_NAME'],
-                                         'PERMANENT', 'WIND')
-        cls.emtpy_file = cls.__name__ + '_this_is_an_empty_file'
-        open(cls.emtpy_file, 'w').close()
-        cls.file_with_md5 = cls.__name__ + '_this_is_a_file_with_known_md5'
-        file_content = 'Content of the file with known MD5.\n'
-        with open(cls.file_with_md5, 'w') as f:
+        cls.existing_file = os.path.join(
+            gisenv["GISDBASE"], gisenv["LOCATION_NAME"], "PERMANENT", "WIND"
+        )
+        cls.emtpy_file = cls.__name__ + "_this_is_an_empty_file"
+        open(cls.emtpy_file, "w").close()
+        cls.file_with_md5 = cls.__name__ + "_this_is_a_file_with_known_md5"
+        file_content = "Content of the file with known MD5.\n"
+        with open(cls.file_with_md5, "w") as f:
             f.write(file_content)
             f.write(file_content)
         # MD5 sum created using:
         # MD5 sum created using:
         # echo 'Content of the file with known MD5.' > some_file.txt
         # echo 'Content of the file with known MD5.' > some_file.txt
         # md5sum some_file.txt
         # md5sum some_file.txt
-        cls.file_md5 = '807bba4ffac4bb351bc3f27853009949'
+        cls.file_md5 = "807bba4ffac4bb351bc3f27853009949"
 
 
-        cls.file_with_same_content = cls.__name__ + '_file_with_same_content'
-        with open(cls.file_with_same_content, 'w') as f:
+        cls.file_with_same_content = cls.__name__ + "_file_with_same_content"
+        with open(cls.file_with_same_content, "w") as f:
             f.write(file_content)
             f.write(file_content)
 
 
-        cls.file_with_different_content = cls.__name__ + '_file_with_different_content'
-        with open(cls.file_with_different_content, 'w') as f:
-            f.write(file_content + ' Something else here.')
+        cls.file_with_different_content = cls.__name__ + "_file_with_different_content"
+        with open(cls.file_with_different_content, "w") as f:
+            f.write(file_content + " Something else here.")
 
 
     @classmethod
     @classmethod
     def tearDownClass(cls):
     def tearDownClass(cls):
@@ -341,30 +375,38 @@ class TestFileAssertions(TestCase):
 
 
     def test_assertFileExists(self):
     def test_assertFileExists(self):
         self.assertFileExists(filename=self.existing_file)
         self.assertFileExists(filename=self.existing_file)
-        self.assertRaises(self.failureException,
-                          self.assertFileExists,
-                          filename='this_one_does_not_exists')
+        self.assertRaises(
+            self.failureException,
+            self.assertFileExists,
+            filename="this_one_does_not_exists",
+        )
 
 
     def test_assertFileExists_empty_file(self):
     def test_assertFileExists_empty_file(self):
         self.assertFileExists(filename=self.emtpy_file, skip_size_check=True)
         self.assertFileExists(filename=self.emtpy_file, skip_size_check=True)
-        self.assertRaises(self.failureException,
-                          self.assertFileExists,
-                          filename=self.emtpy_file)
+        self.assertRaises(
+            self.failureException, self.assertFileExists, filename=self.emtpy_file
+        )
 
 
     def test_assertFileMd5(self):
     def test_assertFileMd5(self):
         self.assertFileMd5(filename=self.file_with_md5, md5=self.file_md5)
         self.assertFileMd5(filename=self.file_with_md5, md5=self.file_md5)
-        self.assertRaises(self.failureException,
-                          self.assertFileMd5,
-                          filename=self.file_with_md5, md5='wrongmd5')
+        self.assertRaises(
+            self.failureException,
+            self.assertFileMd5,
+            filename=self.file_with_md5,
+            md5="wrongmd5",
+        )
 
 
     def test_assertFilesEqualMd5(self):
     def test_assertFilesEqualMd5(self):
-        self.assertFilesEqualMd5(filename=self.file_with_md5,
-                                 reference=self.file_with_same_content)
-        self.assertRaises(self.failureException,
-                          self.assertFilesEqualMd5,
-                          filename=self.file_with_md5,
-                          reference=self.file_with_different_content)
-
-
-if __name__ == '__main__':
+        self.assertFilesEqualMd5(
+            filename=self.file_with_md5, reference=self.file_with_same_content
+        )
+        self.assertRaises(
+            self.failureException,
+            self.assertFilesEqualMd5,
+            filename=self.file_with_md5,
+            reference=self.file_with_different_content,
+        )
+
+
+if __name__ == "__main__":
     test()
     test()

+ 114 - 83
python/grass/gunittest/testsuite/test_assertions_rast3d.py

@@ -11,117 +11,148 @@ from grass.gunittest.main import test
 
 
 class TestRaster3dMapAssertions(TestCase):
 class TestRaster3dMapAssertions(TestCase):
     # pylint: disable=R0904
     # pylint: disable=R0904
-    constant_map = 'raster3d_assertions_constant'
-    rcd_increasing_map = 'raster3d_assertions_rcd_increasing'
+    constant_map = "raster3d_assertions_constant"
+    rcd_increasing_map = "raster3d_assertions_rcd_increasing"
 
 
     @classmethod
     @classmethod
     def setUpClass(cls):
     def setUpClass(cls):
         cls.use_temp_region()
         cls.use_temp_region()
         # TODO: here we should actually not call self.runModule but call_module
         # TODO: here we should actually not call self.runModule but call_module
-        cls.runModule('g.region', n=200, s=100, e=400, w=200,
-                      t=500, b=450, res3=1)
-        cls.runModule('r3.mapcalc', expression='%s = 155' % cls.constant_map)
-        cls.runModule('r3.mapcalc',
-                      expression='%s = row() + col() + depth()' % cls.rcd_increasing_map)
+        cls.runModule("g.region", n=200, s=100, e=400, w=200, t=500, b=450, res3=1)
+        cls.runModule("r3.mapcalc", expression="%s = 155" % cls.constant_map)
+        cls.runModule(
+            "r3.mapcalc",
+            expression="%s = row() + col() + depth()" % cls.rcd_increasing_map,
+        )
 
 
     @classmethod
     @classmethod
     def tearDownClass(cls):
     def tearDownClass(cls):
         cls.del_temp_region()
         cls.del_temp_region()
         # TODO: input as list does not work, why?
         # TODO: input as list does not work, why?
-        cls.runModule('g.remove', flags='f', type='raster_3d',
-                      name=','.join([cls.constant_map, cls.rcd_increasing_map]))
+        cls.runModule(
+            "g.remove",
+            flags="f",
+            type="raster_3d",
+            name=",".join([cls.constant_map, cls.rcd_increasing_map]),
+        )
 
 
     def test_assertRaster3dFitsUnivar(self):
     def test_assertRaster3dFitsUnivar(self):
-        reference = dict(n=1000000,
-                         null_cells=0,
-                         cells=1000000,
-                         min=155,
-                         max=155,
-                         range=0,
-                         mean=155,
-                         mean_of_abs=155,
-                         stddev=0,
-                         variance=0,
-                         coeff_var=0,
-                         sum=155000000)
-        self.assertRaster3dFitsUnivar(self.constant_map, reference=reference,
-                                      precision=0.000001)
-        self.assertRaises(self.failureException,
-                          self.assertRaster3dFitsUnivar,
-                          self.rcd_increasing_map,
-                          reference=reference, precision=1)
-        self.assertRaises(ValueError,
-                          self.assertRaster3dFitsUnivar,
-                          self.constant_map, reference=dict(a=4, b=5, c=6))
-        self.assertRaises(CalledModuleError,
-                          self.assertRaster3dFitsUnivar,
-                          'does_not_exists', reference=dict(a=4, b=5, c=6))
+        reference = dict(
+            n=1000000,
+            null_cells=0,
+            cells=1000000,
+            min=155,
+            max=155,
+            range=0,
+            mean=155,
+            mean_of_abs=155,
+            stddev=0,
+            variance=0,
+            coeff_var=0,
+            sum=155000000,
+        )
+        self.assertRaster3dFitsUnivar(
+            self.constant_map, reference=reference, precision=0.000001
+        )
+        self.assertRaises(
+            self.failureException,
+            self.assertRaster3dFitsUnivar,
+            self.rcd_increasing_map,
+            reference=reference,
+            precision=1,
+        )
+        self.assertRaises(
+            ValueError,
+            self.assertRaster3dFitsUnivar,
+            self.constant_map,
+            reference=dict(a=4, b=5, c=6),
+        )
+        self.assertRaises(
+            CalledModuleError,
+            self.assertRaster3dFitsUnivar,
+            "does_not_exists",
+            reference=dict(a=4, b=5, c=6),
+        )
 
 
     def test_assertRaster3dFitsInfo(self):
     def test_assertRaster3dFitsInfo(self):
-        reference = dict(north=200,
-                         south=100,
-                         east=400,
-                         west=200,
-                         bottom=450,
-                         top=500,
-                         nsres=1,
-                         ewres=1,
-                         tbres=1,
-                         rows=100,
-                         cols=200,
-                         depths=50)
+        reference = dict(
+            north=200,
+            south=100,
+            east=400,
+            west=200,
+            bottom=450,
+            top=500,
+            nsres=1,
+            ewres=1,
+            tbres=1,
+            rows=100,
+            cols=200,
+            depths=50,
+        )
         self.assertRaster3dFitsInfo(self.constant_map, reference=reference)
         self.assertRaster3dFitsInfo(self.constant_map, reference=reference)
 
 
-        reference['north'] = 500
-        self.assertRaises(self.failureException,
-                          self.assertRaster3dFitsInfo,
-                          self.constant_map, reference=reference)
-        self.assertRaises(ValueError,
-                          self.assertRaster3dFitsInfo,
-                          self.constant_map, reference=dict(a=5))
+        reference["north"] = 500
+        self.assertRaises(
+            self.failureException,
+            self.assertRaster3dFitsInfo,
+            self.constant_map,
+            reference=reference,
+        )
+        self.assertRaises(
+            ValueError,
+            self.assertRaster3dFitsInfo,
+            self.constant_map,
+            reference=dict(a=5),
+        )
 
 
     def test_common_values_info_univar(self):
     def test_common_values_info_univar(self):
         minmax = dict(min=3, max=350)
         minmax = dict(min=3, max=350)
-        self.assertRaster3dFitsUnivar(self.rcd_increasing_map,
-                                      minmax, precision=0.01)
-        self.assertRaster3dFitsInfo(self.rcd_increasing_map,
-                                    minmax, precision=0.01)
+        self.assertRaster3dFitsUnivar(self.rcd_increasing_map, minmax, precision=0.01)
+        self.assertRaster3dFitsInfo(self.rcd_increasing_map, minmax, precision=0.01)
 
 
     def test_string_as_parameter(self):
     def test_string_as_parameter(self):
-        self.assertRaster3dFitsInfo(self.constant_map,
-                                    reference="max=155", precision=1)
-        self.assertRaster3dFitsUnivar(self.rcd_increasing_map,
-                                      reference="n=1000000", precision=0)
+        self.assertRaster3dFitsInfo(self.constant_map, reference="max=155", precision=1)
+        self.assertRaster3dFitsUnivar(
+            self.rcd_increasing_map, reference="n=1000000", precision=0
+        )
 
 
     def test_assertRasters3dNoDifference(self):
     def test_assertRasters3dNoDifference(self):
         """Test basic usage of assertRastersNoDifference"""
         """Test basic usage of assertRastersNoDifference"""
         # precision might need to be increased
         # precision might need to be increased
-        self.assertRasters3dNoDifference(actual=self.rcd_increasing_map,
-                                       reference=self.rcd_increasing_map,
-                                       precision=0,
-                                       msg="The same maps should have no difference")
-        self.assertRaises(self.failureException,
-                          self.assertRasters3dNoDifference,
-                          actual=self.constant_map,
-                          reference=self.rcd_increasing_map,
-                          precision=1,
-                          msg="Different maps should have difference")
+        self.assertRasters3dNoDifference(
+            actual=self.rcd_increasing_map,
+            reference=self.rcd_increasing_map,
+            precision=0,
+            msg="The same maps should have no difference",
+        )
+        self.assertRaises(
+            self.failureException,
+            self.assertRasters3dNoDifference,
+            actual=self.constant_map,
+            reference=self.rcd_increasing_map,
+            precision=1,
+            msg="Different maps should have difference",
+        )
 
 
     def test_assertRasters3dNoDifference_mean(self):
     def test_assertRasters3dNoDifference_mean(self):
         """Test usage of assertRastersNoDifference with mean"""
         """Test usage of assertRastersNoDifference with mean"""
-        self.assertRasters3dNoDifference(actual=self.rcd_increasing_map,
-                                       reference=self.rcd_increasing_map,
-                                       precision=0,  # this might need to be increased
-                                       statistics=dict(mean=0),
-                                       msg="The difference of same maps should have small mean")
-        self.assertRaises(self.failureException,
-                          self.assertRasters3dNoDifference,
-                          actual=self.constant_map,
-                          reference=self.rcd_increasing_map,
-                          precision=1,
-                          statistics=dict(mean=0),
-                          msg="The difference of different maps should have huge mean")
+        self.assertRasters3dNoDifference(
+            actual=self.rcd_increasing_map,
+            reference=self.rcd_increasing_map,
+            precision=0,  # this might need to be increased
+            statistics=dict(mean=0),
+            msg="The difference of same maps should have small mean",
+        )
+        self.assertRaises(
+            self.failureException,
+            self.assertRasters3dNoDifference,
+            actual=self.constant_map,
+            reference=self.rcd_increasing_map,
+            precision=1,
+            statistics=dict(mean=0),
+            msg="The difference of different maps should have huge mean",
+        )
 
 
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     test()
     test()

+ 188 - 110
python/grass/gunittest/testsuite/test_assertions_vect.py

@@ -50,7 +50,7 @@ V_UNIVAR_SCHOOLS_REGION = dict(
 
 
 # v.info schools -g and reduced to minimum
 # v.info schools -g and reduced to minimum
 V_UNIVAR_SCHOOLS_EXTENDED = dict(
 V_UNIVAR_SCHOOLS_EXTENDED = dict(
-    name='schools',
+    name="schools",
     level=2,
     level=2,
     num_dblinks=1,
     num_dblinks=1,
 )
 )
@@ -58,79 +58,124 @@ V_UNIVAR_SCHOOLS_EXTENDED = dict(
 
 
 class TestVectorInfoAssertions(TestCase):
 class TestVectorInfoAssertions(TestCase):
     """Test assertions of map meta and statistics"""
     """Test assertions of map meta and statistics"""
+
     # pylint: disable=R0904
     # pylint: disable=R0904
 
 
     def test_assertVectorFitsUnivar(self):
     def test_assertVectorFitsUnivar(self):
-        self.assertVectorFitsUnivar(map='schools', column='CORECAPACI',
-                                    reference=V_UNIVAR_SCHOOLS_WIDTH_SUBSET,
-                                    precision=0.01)
-        self.assertRaises(self.failureException,
-                          self.assertVectorFitsUnivar,
-                          map='schools', column='MOBILECAPA',
-                          reference=V_UNIVAR_SCHOOLS_WIDTH_SUBSET,
-                          precision=0.01)
-        self.assertRaises(ValueError,
-                          self.assertVectorFitsUnivar,
-                          map='schools', column='CORECAPACI',
-                          reference=RANDOM_KEYVALUES)
+        self.assertVectorFitsUnivar(
+            map="schools",
+            column="CORECAPACI",
+            reference=V_UNIVAR_SCHOOLS_WIDTH_SUBSET,
+            precision=0.01,
+        )
+        self.assertRaises(
+            self.failureException,
+            self.assertVectorFitsUnivar,
+            map="schools",
+            column="MOBILECAPA",
+            reference=V_UNIVAR_SCHOOLS_WIDTH_SUBSET,
+            precision=0.01,
+        )
+        self.assertRaises(
+            ValueError,
+            self.assertVectorFitsUnivar,
+            map="schools",
+            column="CORECAPACI",
+            reference=RANDOM_KEYVALUES,
+        )
 
 
     def test_assertVectorFitsTopoInfo(self):
     def test_assertVectorFitsTopoInfo(self):
-        self.assertVectorFitsTopoInfo('schools', V_UNIVAR_SCHOOLS_TOPO)
-        self.assertRaises(self.failureException,
-                          self.assertVectorFitsTopoInfo,
-                          'hospitals',
-                          V_UNIVAR_SCHOOLS_TOPO)
-        self.assertRaises(ValueError,
-                          self.assertVectorFitsTopoInfo,
-                          'schools', RANDOM_KEYVALUES)
-        self.assertRaises(ValueError,
-                          self.assertVectorFitsTopoInfo,
-                          'schools', V_UNIVAR_SCHOOLS_REGION)
+        self.assertVectorFitsTopoInfo("schools", V_UNIVAR_SCHOOLS_TOPO)
+        self.assertRaises(
+            self.failureException,
+            self.assertVectorFitsTopoInfo,
+            "hospitals",
+            V_UNIVAR_SCHOOLS_TOPO,
+        )
+        self.assertRaises(
+            ValueError, self.assertVectorFitsTopoInfo, "schools", RANDOM_KEYVALUES
+        )
+        self.assertRaises(
+            ValueError,
+            self.assertVectorFitsTopoInfo,
+            "schools",
+            V_UNIVAR_SCHOOLS_REGION,
+        )
 
 
     def test_assertVectorFitsRegionInfo(self):
     def test_assertVectorFitsRegionInfo(self):
-        self.assertVectorFitsRegionInfo('schools', V_UNIVAR_SCHOOLS_REGION, precision=1.0)
-        self.assertRaises(self.failureException,
-                          self.assertVectorFitsRegionInfo,
-                          'hospitals', V_UNIVAR_SCHOOLS_REGION, precision=1.0)
-        self.assertRaises(ValueError,
-                          self.assertVectorFitsRegionInfo,
-                          'schools', RANDOM_KEYVALUES, precision=1.0)
-        self.assertRaises(ValueError,
-                          self.assertVectorFitsRegionInfo,
-                          'schools', V_UNIVAR_SCHOOLS_TOPO, precision=1.0)
+        self.assertVectorFitsRegionInfo(
+            "schools", V_UNIVAR_SCHOOLS_REGION, precision=1.0
+        )
+        self.assertRaises(
+            self.failureException,
+            self.assertVectorFitsRegionInfo,
+            "hospitals",
+            V_UNIVAR_SCHOOLS_REGION,
+            precision=1.0,
+        )
+        self.assertRaises(
+            ValueError,
+            self.assertVectorFitsRegionInfo,
+            "schools",
+            RANDOM_KEYVALUES,
+            precision=1.0,
+        )
+        self.assertRaises(
+            ValueError,
+            self.assertVectorFitsRegionInfo,
+            "schools",
+            V_UNIVAR_SCHOOLS_TOPO,
+            precision=1.0,
+        )
 
 
     def test_assertVectorFitsExtendedInfo(self):
     def test_assertVectorFitsExtendedInfo(self):
-        self.assertVectorFitsExtendedInfo('schools', V_UNIVAR_SCHOOLS_EXTENDED)
-        self.assertRaises(self.failureException,
-                          self.assertVectorFitsExtendedInfo,
-                          'hospitals',
-                          V_UNIVAR_SCHOOLS_EXTENDED)
-        self.assertRaises(ValueError,
-                          self.assertVectorFitsExtendedInfo,
-                          'schools',
-                          RANDOM_KEYVALUES)
-        self.assertRaises(ValueError,
-                          self.assertVectorFitsExtendedInfo,
-                          'schools',
-                          V_UNIVAR_SCHOOLS_TOPO)
+        self.assertVectorFitsExtendedInfo("schools", V_UNIVAR_SCHOOLS_EXTENDED)
+        self.assertRaises(
+            self.failureException,
+            self.assertVectorFitsExtendedInfo,
+            "hospitals",
+            V_UNIVAR_SCHOOLS_EXTENDED,
+        )
+        self.assertRaises(
+            ValueError, self.assertVectorFitsExtendedInfo, "schools", RANDOM_KEYVALUES
+        )
+        self.assertRaises(
+            ValueError,
+            self.assertVectorFitsExtendedInfo,
+            "schools",
+            V_UNIVAR_SCHOOLS_TOPO,
+        )
 
 
     def test_assertVectorInfoEqualsVectorInfo(self):
     def test_assertVectorInfoEqualsVectorInfo(self):
-        self.assertVectorInfoEqualsVectorInfo('schools', 'schools', precision=0.00000001)
-        self.assertRaises(self.failureException,
-                          self.assertVectorInfoEqualsVectorInfo,
-                          'hospitals', 'schools', precision=0.00000001)
-        self.assertRaises(CalledModuleError,
-                          self.assertVectorInfoEqualsVectorInfo,
-                          'schools', 'does_not_exist', precision=0.00000001)
+        self.assertVectorInfoEqualsVectorInfo(
+            "schools", "schools", precision=0.00000001
+        )
+        self.assertRaises(
+            self.failureException,
+            self.assertVectorInfoEqualsVectorInfo,
+            "hospitals",
+            "schools",
+            precision=0.00000001,
+        )
+        self.assertRaises(
+            CalledModuleError,
+            self.assertVectorInfoEqualsVectorInfo,
+            "schools",
+            "does_not_exist",
+            precision=0.00000001,
+        )
 
 
 
 
 class TestVectorGeometryAssertions(TestCase):
 class TestVectorGeometryAssertions(TestCase):
     """Test assertions of map geometry"""
     """Test assertions of map geometry"""
+
     # pylint: disable=R0904
     # pylint: disable=R0904
     maps_to_remove = []
     maps_to_remove = []
-    simple_base_file = 'data/simple_vector_map_ascii_4p_2l_2c_3b_dp14.txt'
-    simple_modified_file = 'data/simple_vector_map_ascii_4p_2l_2c_3b_dp14_modified.txt'
-    simple_diff_header_file = 'data/simple_vector_map_ascii_4p_2l_2c_3b_dp14_diff_header.txt'
+    simple_base_file = "data/simple_vector_map_ascii_4p_2l_2c_3b_dp14.txt"
+    simple_modified_file = "data/simple_vector_map_ascii_4p_2l_2c_3b_dp14_modified.txt"
+    simple_diff_header_file = (
+        "data/simple_vector_map_ascii_4p_2l_2c_3b_dp14_diff_header.txt"
+    )
     precision = 0.00001
     precision = 0.00001
     digits = 14
     digits = 14
 
 
@@ -141,63 +186,86 @@ class TestVectorGeometryAssertions(TestCase):
         # when invoking separately, no need to delete maps since mapset
         # when invoking separately, no need to delete maps since mapset
         # is deleted
         # is deleted
         if cls.maps_to_remove:
         if cls.maps_to_remove:
-            cls.runModule('g.remove', flags='f', type='vector',
-                          name=','.join(cls.maps_to_remove))
+            cls.runModule(
+                "g.remove", flags="f", type="vector", name=",".join(cls.maps_to_remove)
+            )
 
 
     def test_assertVectorEqualsVector_basic(self):
     def test_assertVectorEqualsVector_basic(self):
         """Check completely different maps."""
         """Check completely different maps."""
-        self.assertVectorEqualsVector(actual='schools', reference='schools',
-                                      precision=0.01, digits=15)
-        self.assertRaises(self.failureException,
-                          self.assertVectorEqualsVector,
-                          actual='schools', reference='hospitals',
-                          precision=0.01, digits=7)
-        self.assertRaises(CalledModuleError,
-                          self.assertVectorEqualsVector,
-                          actual='does_not_exist', reference='hospitals',
-                          precision=0.01, digits=7)
+        self.assertVectorEqualsVector(
+            actual="schools", reference="schools", precision=0.01, digits=15
+        )
+        self.assertRaises(
+            self.failureException,
+            self.assertVectorEqualsVector,
+            actual="schools",
+            reference="hospitals",
+            precision=0.01,
+            digits=7,
+        )
+        self.assertRaises(
+            CalledModuleError,
+            self.assertVectorEqualsVector,
+            actual="does_not_exist",
+            reference="hospitals",
+            precision=0.01,
+            digits=7,
+        )
 
 
     def test_assertVectorEqualsVector_geometry_same_header(self):
     def test_assertVectorEqualsVector_geometry_same_header(self):
         """Check small slighlty different maps with same header in ASCII."""
         """Check small slighlty different maps with same header in ASCII."""
-        amap = 'simple_vector_map_base_geom'
-        bmap = 'simple_vector_map_modified_geom'
-        self.runModule('v.in.ascii', format='standard',
-                       input=self.simple_base_file,
-                       output=amap)
+        amap = "simple_vector_map_base_geom"
+        bmap = "simple_vector_map_modified_geom"
+        self.runModule(
+            "v.in.ascii", format="standard", input=self.simple_base_file, output=amap
+        )
         self.maps_to_remove.append(amap)
         self.maps_to_remove.append(amap)
-        self.runModule('v.in.ascii', format='standard',
-                       input=self.simple_modified_file,
-                       output=bmap)
+        self.runModule(
+            "v.in.ascii",
+            format="standard",
+            input=self.simple_modified_file,
+            output=bmap,
+        )
         self.maps_to_remove.append(bmap)
         self.maps_to_remove.append(bmap)
-        self.assertVectorEqualsVector(actual=amap, reference=amap,
-                                      precision=self.precision, digits=self.digits)
-        self.assertRaises(self.failureException,
-                          self.assertVectorEqualsVector,
-                          actual=amap, reference=bmap,
-                          precision=self.precision, digits=self.digits)
+        self.assertVectorEqualsVector(
+            actual=amap, reference=amap, precision=self.precision, digits=self.digits
+        )
+        self.assertRaises(
+            self.failureException,
+            self.assertVectorEqualsVector,
+            actual=amap,
+            reference=bmap,
+            precision=self.precision,
+            digits=self.digits,
+        )
 
 
     def test_assertVectorEqualsVector_geometry(self):
     def test_assertVectorEqualsVector_geometry(self):
         """Check small slighlty different maps with different headers in ASCII."""
         """Check small slighlty different maps with different headers in ASCII."""
-        amap = 'simple_vector_map_base'
-        bmap = 'simple_vector_map_different_header'
-        self.runModule('v.in.ascii', format='standard',
-                       input=self.simple_base_file,
-                       output=amap)
+        amap = "simple_vector_map_base"
+        bmap = "simple_vector_map_different_header"
+        self.runModule(
+            "v.in.ascii", format="standard", input=self.simple_base_file, output=amap
+        )
         self.maps_to_remove.append(amap)
         self.maps_to_remove.append(amap)
-        self.runModule('v.in.ascii', format='standard',
-                       input=self.simple_diff_header_file,
-                       output=bmap)
+        self.runModule(
+            "v.in.ascii",
+            format="standard",
+            input=self.simple_diff_header_file,
+            output=bmap,
+        )
         self.maps_to_remove.append(bmap)
         self.maps_to_remove.append(bmap)
-        self.assertVectorEqualsVector(actual=amap, reference=bmap,
-                                      precision=self.precision, digits=self.digits)
+        self.assertVectorEqualsVector(
+            actual=amap, reference=bmap, precision=self.precision, digits=self.digits
+        )
 
 
     def test_assertVectorAsciiEqualsVectorAscii_diff_header(self):
     def test_assertVectorAsciiEqualsVectorAscii_diff_header(self):
         """Test ASCII files with different header.
         """Test ASCII files with different header.
 
 
         Prove that files were not deleted if not requested.
         Prove that files were not deleted if not requested.
         """
         """
-        self.assertVectorAsciiEqualsVectorAscii(actual=self.simple_base_file,
-                                                reference=self.simple_diff_header_file)
+        self.assertVectorAsciiEqualsVectorAscii(
+            actual=self.simple_base_file, reference=self.simple_diff_header_file
+        )
         self.assertFileExists(self.simple_base_file)
         self.assertFileExists(self.simple_base_file)
         self.assertFileExists(self.simple_diff_header_file)
         self.assertFileExists(self.simple_diff_header_file)
 
 
@@ -206,29 +274,39 @@ class TestVectorGeometryAssertions(TestCase):
 
 
         Prove that files were not deleted if not requested.
         Prove that files were not deleted if not requested.
         """
         """
-        self.assertRaises(self.failureException,
-                          self.assertVectorAsciiEqualsVectorAscii,
-                          actual=self.simple_base_file,
-                          reference=self.simple_modified_file)
+        self.assertRaises(
+            self.failureException,
+            self.assertVectorAsciiEqualsVectorAscii,
+            actual=self.simple_base_file,
+            reference=self.simple_modified_file,
+        )
         self.assertFileExists(self.simple_base_file)
         self.assertFileExists(self.simple_base_file)
         self.assertFileExists(self.simple_modified_file)
         self.assertFileExists(self.simple_modified_file)
 
 
     def test_assertVectorEqualsAscii_by_import(self):
     def test_assertVectorEqualsAscii_by_import(self):
-        amap = 'simple_vector_map_imported_base'
-        self.runModule('v.in.ascii', format='standard',
-                       input=self.simple_base_file,
-                       output=amap)
+        amap = "simple_vector_map_imported_base"
+        self.runModule(
+            "v.in.ascii", format="standard", input=self.simple_base_file, output=amap
+        )
         self.maps_to_remove.append(amap)
         self.maps_to_remove.append(amap)
-        self.assertVectorEqualsAscii(amap, self.simple_diff_header_file,
-                                     precision=self.precision, digits=self.digits)
-        self.assertRaises(self.failureException,
-                          self.assertVectorEqualsAscii,
-                          amap, self.simple_modified_file,
-                          precision=self.precision, digits=self.digits)
+        self.assertVectorEqualsAscii(
+            amap,
+            self.simple_diff_header_file,
+            precision=self.precision,
+            digits=self.digits,
+        )
+        self.assertRaises(
+            self.failureException,
+            self.assertVectorEqualsAscii,
+            amap,
+            self.simple_modified_file,
+            precision=self.precision,
+            digits=self.digits,
+        )
         self.assertFileExists(self.simple_base_file)
         self.assertFileExists(self.simple_base_file)
         self.assertFileExists(self.simple_modified_file)
         self.assertFileExists(self.simple_modified_file)
         self.assertFileExists(self.simple_diff_header_file)
         self.assertFileExists(self.simple_diff_header_file)
 
 
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     test()
     test()

+ 151 - 122
python/grass/gunittest/testsuite/test_checkers.py

@@ -19,14 +19,17 @@ from grass.script.utils import parse_key_val, try_remove
 from grass.gunittest.case import TestCase
 from grass.gunittest.case import TestCase
 from grass.gunittest.main import test
 from grass.gunittest.main import test
 from grass.gunittest.checkers import (
 from grass.gunittest.checkers import (
-    values_equal, text_to_keyvalue,
-    keyvalue_equals, proj_info_equals, proj_units_equals,
-    file_md5, text_file_md5)
-
+    values_equal,
+    text_to_keyvalue,
+    keyvalue_equals,
+    proj_info_equals,
+    proj_units_equals,
+    file_md5,
+    text_file_md5,
+)
 
 
 
 
 class TestValuesEqual(TestCase):
 class TestValuesEqual(TestCase):
-
     def test_floats(self):
     def test_floats(self):
         self.assertTrue(values_equal(5.0, 5.0))
         self.assertTrue(values_equal(5.0, 5.0))
         self.assertTrue(values_equal(5.1, 5.19, precision=0.1))
         self.assertTrue(values_equal(5.1, 5.19, precision=0.1))
@@ -48,33 +51,31 @@ class TestValuesEqual(TestCase):
         self.assertFalse(values_equal(5.1, 5, precision=0.01))
         self.assertFalse(values_equal(5.1, 5, precision=0.01))
 
 
     def test_strings(self):
     def test_strings(self):
-        self.assertTrue(values_equal('hello', 'hello'))
-        self.assertFalse(values_equal('Hello', 'hello'))
+        self.assertTrue(values_equal("hello", "hello"))
+        self.assertFalse(values_equal("Hello", "hello"))
 
 
     def test_lists(self):
     def test_lists(self):
         self.assertTrue(values_equal([1, 2, 3], [1, 2, 3]))
         self.assertTrue(values_equal([1, 2, 3], [1, 2, 3]))
-        self.assertTrue(values_equal([1.1, 2.0, 3.9],
-                                     [1.1, 1.95, 4.0],
-                                     precision=0.2))
-        self.assertFalse(values_equal([1, 2, 3, 4, 5],
-                                      [1, 22, 3, 4, 5],
-                                      precision=1))
+        self.assertTrue(values_equal([1.1, 2.0, 3.9], [1.1, 1.95, 4.0], precision=0.2))
+        self.assertFalse(values_equal([1, 2, 3, 4, 5], [1, 22, 3, 4, 5], precision=1))
 
 
     def test_mixed_lists(self):
     def test_mixed_lists(self):
-        self.assertTrue(values_equal([1, 'abc', 8], [1, 'abc', 8.2],
-                                     precision=0.5))
+        self.assertTrue(values_equal([1, "abc", 8], [1, "abc", 8.2], precision=0.5))
 
 
     def test_recursive_lists(self):
     def test_recursive_lists(self):
-        self.assertTrue(values_equal([1, 'abc', [5, 9.6, 9.0]],
-                                     [1, 'abc', [4.9, 9.2, 9.3]],
-                                     precision=0.5))
+        self.assertTrue(
+            values_equal(
+                [1, "abc", [5, 9.6, 9.0]], [1, "abc", [4.9, 9.2, 9.3]], precision=0.5
+            )
+        )
+
 
 
-KEYVAL_TEXT = '''s: Hello
+KEYVAL_TEXT = """s: Hello
 str: Hello world!
 str: Hello world!
 f: 1.0
 f: 1.0
 l: 1,2,3,4,5
 l: 1,2,3,4,5
 mixed: hello,8,-25,world!,4-1,5:2,0.1,-9.6
 mixed: hello,8,-25,world!,4-1,5:2,0.1,-9.6
-'''
+"""
 
 
 # file location/PERMANENT/PROJ_INFO
 # file location/PERMANENT/PROJ_INFO
 PROJ_INFO_TEXT_1 = """name: Lambert Conformal Conic
 PROJ_INFO_TEXT_1 = """name: Lambert Conformal Conic
@@ -120,78 +121,88 @@ meters: 1
 
 
 class TestTextToKeyValue(TestCase):
 class TestTextToKeyValue(TestCase):
     def test_conversion(self):
     def test_conversion(self):
-        keyvals = text_to_keyvalue(KEYVAL_TEXT, sep=':', val_sep=',')
-        expected = {'s': 'Hello',
-                    'str': 'Hello world!',
-                    'f': 1.0,
-                    'l': [1, 2, 3, 4, 5],
-                    'mixed': ['hello', 8, -25, 'world!',
-                              '4-1', '5:2', 0.1, -9.6]}
+        keyvals = text_to_keyvalue(KEYVAL_TEXT, sep=":", val_sep=",")
+        expected = {
+            "s": "Hello",
+            "str": "Hello world!",
+            "f": 1.0,
+            "l": [1, 2, 3, 4, 5],
+            "mixed": ["hello", 8, -25, "world!", "4-1", "5:2", 0.1, -9.6],
+        }
         self.assertDictEqual(expected, keyvals)
         self.assertDictEqual(expected, keyvals)
 
 
     def test_single_values(self):
     def test_single_values(self):
-        keyvals = text_to_keyvalue("a: 1.5", sep=':')
-        self.assertDictEqual({'a': 1.5}, keyvals)
-        keyvals = text_to_keyvalue("abc=1", sep='=')
-        self.assertDictEqual({'abc': 1}, keyvals)
-        keyvals = text_to_keyvalue("abc=hello", sep='=')
-        self.assertDictEqual({'abc': 'hello'}, keyvals)
+        keyvals = text_to_keyvalue("a: 1.5", sep=":")
+        self.assertDictEqual({"a": 1.5}, keyvals)
+        keyvals = text_to_keyvalue("abc=1", sep="=")
+        self.assertDictEqual({"abc": 1}, keyvals)
+        keyvals = text_to_keyvalue("abc=hello", sep="=")
+        self.assertDictEqual({"abc": "hello"}, keyvals)
 
 
     def test_strip(self):
     def test_strip(self):
-        keyvals = text_to_keyvalue("a:   2.8  ", sep=':')
-        self.assertDictEqual({'a': 2.8}, keyvals)
-        keyvals = text_to_keyvalue("a:  2  ; 2.8 ; ab cd ",
-                                   sep=':', val_sep=';')
-        self.assertDictEqual({'a': [2, 2.8, 'ab cd']}, keyvals)
-        keyvals = text_to_keyvalue("a  :  2  ; 2.8", sep=':', val_sep=';')
-        self.assertDictEqual({'a': [2, 2.8]}, keyvals)
-        keyvals = text_to_keyvalue("a  : \t 2  ;\t2.8", sep=':', val_sep=';')
-        self.assertDictEqual({'a': [2, 2.8]}, keyvals)
+        keyvals = text_to_keyvalue("a:   2.8  ", sep=":")
+        self.assertDictEqual({"a": 2.8}, keyvals)
+        keyvals = text_to_keyvalue("a:  2  ; 2.8 ; ab cd ", sep=":", val_sep=";")
+        self.assertDictEqual({"a": [2, 2.8, "ab cd"]}, keyvals)
+        keyvals = text_to_keyvalue("a  :  2  ; 2.8", sep=":", val_sep=";")
+        self.assertDictEqual({"a": [2, 2.8]}, keyvals)
+        keyvals = text_to_keyvalue("a  : \t 2  ;\t2.8", sep=":", val_sep=";")
+        self.assertDictEqual({"a": [2, 2.8]}, keyvals)
 
 
     def test_empty_list_item(self):
     def test_empty_list_item(self):
-        keyvals = text_to_keyvalue("a: 1, ,5,,", sep=':', val_sep=',')
-        self.assertDictEqual({'a': [1, '', 5, '', '']}, keyvals)
+        keyvals = text_to_keyvalue("a: 1, ,5,,", sep=":", val_sep=",")
+        self.assertDictEqual({"a": [1, "", 5, "", ""]}, keyvals)
 
 
     def test_empty_value(self):
     def test_empty_value(self):
-        keyvals = text_to_keyvalue("a: ", sep=':')
-        self.assertDictEqual({'a': ''}, keyvals)
-        keyvals = text_to_keyvalue("a:", sep=':')
-        self.assertDictEqual({'a': ''}, keyvals)
+        keyvals = text_to_keyvalue("a: ", sep=":")
+        self.assertDictEqual({"a": ""}, keyvals)
+        keyvals = text_to_keyvalue("a:", sep=":")
+        self.assertDictEqual({"a": ""}, keyvals)
 
 
     def test_wrong_lines(self):
     def test_wrong_lines(self):
         # we consider no key-value separator as invalid line
         # we consider no key-value separator as invalid line
         # and we silently ignore these
         # and we silently ignore these
-        keyvals = text_to_keyvalue("a", sep=':',
-                                   skip_invalid=True, skip_empty=False)
+        keyvals = text_to_keyvalue("a", sep=":", skip_invalid=True, skip_empty=False)
         self.assertDictEqual({}, keyvals)
         self.assertDictEqual({}, keyvals)
 
 
-        self.assertRaises(ValueError, text_to_keyvalue, "a", sep=':',
-                          skip_invalid=False, skip_empty=False)
+        self.assertRaises(
+            ValueError,
+            text_to_keyvalue,
+            "a",
+            sep=":",
+            skip_invalid=False,
+            skip_empty=False,
+        )
 
 
         # text_to_keyvalue considers the empty string as valid input
         # text_to_keyvalue considers the empty string as valid input
-        keyvals = text_to_keyvalue("", sep=':',
-                                   skip_invalid=False, skip_empty=False)
+        keyvals = text_to_keyvalue("", sep=":", skip_invalid=False, skip_empty=False)
         self.assertDictEqual({}, keyvals)
         self.assertDictEqual({}, keyvals)
 
 
-        self.assertRaises(ValueError, text_to_keyvalue, "\n", sep=':',
-                          skip_invalid=True, skip_empty=False)
+        self.assertRaises(
+            ValueError,
+            text_to_keyvalue,
+            "\n",
+            sep=":",
+            skip_invalid=True,
+            skip_empty=False,
+        )
 
 
-        keyvals = text_to_keyvalue("a\n\n", sep=':',
-                                   skip_invalid=True, skip_empty=True)
+        keyvals = text_to_keyvalue("a\n\n", sep=":", skip_invalid=True, skip_empty=True)
         self.assertDictEqual({}, keyvals)
         self.assertDictEqual({}, keyvals)
 
 
     def test_separators(self):
     def test_separators(self):
-        keyvals = text_to_keyvalue("a=a;b;c", sep='=', val_sep=';')
-        self.assertDictEqual({'a': ['a', 'b', 'c']}, keyvals)
-        keyvals = text_to_keyvalue("a 1;2;3", sep=' ', val_sep=';')
-        self.assertDictEqual({'a': [1, 2, 3]}, keyvals)
+        keyvals = text_to_keyvalue("a=a;b;c", sep="=", val_sep=";")
+        self.assertDictEqual({"a": ["a", "b", "c"]}, keyvals)
+        keyvals = text_to_keyvalue("a 1;2;3", sep=" ", val_sep=";")
+        self.assertDictEqual({"a": [1, 2, 3]}, keyvals)
         # spaces as key-value separator and values separators
         # spaces as key-value separator and values separators
         # this should work (e.g. because of : in DMS),
         # this should work (e.g. because of : in DMS),
         # although it does not support stripping (we don't merge separators)
         # although it does not support stripping (we don't merge separators)
-        keyvals = text_to_keyvalue("a 1 2 3", sep=' ', val_sep=' ')
-        self.assertDictEqual({'a': [1, 2, 3]}, keyvals)
+        keyvals = text_to_keyvalue("a 1 2 3", sep=" ", val_sep=" ")
+        self.assertDictEqual({"a": [1, 2, 3]}, keyvals)
+
+    # def test_projection_files(self):
 
 
-    #def test_projection_files(self):
 
 
 # obtained by r.univar elevation -g
 # obtained by r.univar elevation -g
 # floats removed
 # floats removed
@@ -216,23 +227,21 @@ null_cells=57995100
 cells=60020100
 cells=60020100
 """
 """
 
 
-R_UNIVAR_KEYVAL_INT_DICT = {'n': 2025000,
-                            'null_cells': 57995100, 'cells': 60020100}
+R_UNIVAR_KEYVAL_INT_DICT = {"n": 2025000, "null_cells": 57995100, "cells": 60020100}
 
 
 
 
 class TestComapreProjections(TestCase):
 class TestComapreProjections(TestCase):
-
     def test_compare_proj_info(self):
     def test_compare_proj_info(self):
         self.assertTrue(proj_info_equals(PROJ_INFO_TEXT_1, PROJ_INFO_TEXT_2))
         self.assertTrue(proj_info_equals(PROJ_INFO_TEXT_1, PROJ_INFO_TEXT_2))
         self.assertTrue(proj_units_equals(PROJ_UNITS_TEXT_1, PROJ_UNITS_TEXT_2))
         self.assertTrue(proj_units_equals(PROJ_UNITS_TEXT_1, PROJ_UNITS_TEXT_2))
 
 
 
 
 class TestParseKeyvalue(TestCase):
 class TestParseKeyvalue(TestCase):
-
     def test_shell_script_style(self):
     def test_shell_script_style(self):
 
 
-        self.assertDictEqual(parse_key_val(R_UNIVAR_KEYVAL_INT, val_type=int),
-                             R_UNIVAR_KEYVAL_INT_DICT)
+        self.assertDictEqual(
+            parse_key_val(R_UNIVAR_KEYVAL_INT, val_type=int), R_UNIVAR_KEYVAL_INT_DICT
+        )
 
 
 
 
 R_UNIVAR_ELEVATION = """n=2025000
 R_UNIVAR_ELEVATION = """n=2025000
@@ -280,50 +289,62 @@ max=156.329864501953
 
 
 
 
 class TestRasterMapComparisons(TestCase):
 class TestRasterMapComparisons(TestCase):
-
     def test_compare_univars(self):
     def test_compare_univars(self):
-        self.assertTrue(keyvalue_equals(text_to_keyvalue(R_UNIVAR_ELEVATION,
-                                                          sep='='),
-                                         text_to_keyvalue(R_UNIVAR_ELEVATION,
-                                                          sep='='),
-                                         precision=0))
-        self.assertFalse(keyvalue_equals(text_to_keyvalue(R_UNIVAR_ELEVATION,
-                                                           sep='='),
-                                          text_to_keyvalue(R_UNIVAR_ELEVATION_SUBSET,
-                                                           sep='='),
-                                          precision=0))
+        self.assertTrue(
+            keyvalue_equals(
+                text_to_keyvalue(R_UNIVAR_ELEVATION, sep="="),
+                text_to_keyvalue(R_UNIVAR_ELEVATION, sep="="),
+                precision=0,
+            )
+        )
+        self.assertFalse(
+            keyvalue_equals(
+                text_to_keyvalue(R_UNIVAR_ELEVATION, sep="="),
+                text_to_keyvalue(R_UNIVAR_ELEVATION_SUBSET, sep="="),
+                precision=0,
+            )
+        )
 
 
     def test_compare_univars_subset(self):
     def test_compare_univars_subset(self):
-        self.assertTrue(keyvalue_equals(text_to_keyvalue(R_UNIVAR_ELEVATION_SUBSET,
-                                                          sep='='),
-                                         text_to_keyvalue(R_UNIVAR_ELEVATION,
-                                                          sep='='),
-                                         a_is_subset=True, precision=0))
-        self.assertFalse(keyvalue_equals(text_to_keyvalue(R_UNIVAR_ELEVATION,
-                                                           sep='='),
-                                          text_to_keyvalue(R_UNIVAR_ELEVATION_SUBSET,
-                                                           sep='='),
-                                          a_is_subset=True, precision=0))
+        self.assertTrue(
+            keyvalue_equals(
+                text_to_keyvalue(R_UNIVAR_ELEVATION_SUBSET, sep="="),
+                text_to_keyvalue(R_UNIVAR_ELEVATION, sep="="),
+                a_is_subset=True,
+                precision=0,
+            )
+        )
+        self.assertFalse(
+            keyvalue_equals(
+                text_to_keyvalue(R_UNIVAR_ELEVATION, sep="="),
+                text_to_keyvalue(R_UNIVAR_ELEVATION_SUBSET, sep="="),
+                a_is_subset=True,
+                precision=0,
+            )
+        )
 
 
     def test_compare_univars_rounded(self):
     def test_compare_univars_rounded(self):
-        self.assertTrue(keyvalue_equals(text_to_keyvalue(R_UNIVAR_ELEVATION,
-                                                          sep='='),
-                                         text_to_keyvalue(R_UNIVAR_ELEVATION_ROUNDED,
-                                                          sep='='),
-                                         precision=0.001))
+        self.assertTrue(
+            keyvalue_equals(
+                text_to_keyvalue(R_UNIVAR_ELEVATION, sep="="),
+                text_to_keyvalue(R_UNIVAR_ELEVATION_ROUNDED, sep="="),
+                precision=0.001,
+            )
+        )
+
 
 
 CORRECT_LINES = [
 CORRECT_LINES = [
     "null_cells=57995100",
     "null_cells=57995100",
     "cells=60020100",
     "cells=60020100",
     "min=55.5787925720215",
     "min=55.5787925720215",
-    "max=156.329864501953"
+    "max=156.329864501953",
 ]
 ]
 
 
 INCORRECT_LINES = [
 INCORRECT_LINES = [
     "null_cells=579951",
     "null_cells=579951",
     "cells=60020100",
     "cells=60020100",
     "min=5.5787925720215",
     "min=5.5787925720215",
-    "max=156.329864501953"
+    "max=156.329864501953",
 ]
 ]
 
 
 
 
@@ -343,25 +364,25 @@ class TestMd5Sums(TestCase):
     9dd6c4bb9d2cf6051b12f4b5f9d70523  test.txt
     9dd6c4bb9d2cf6051b12f4b5f9d70523  test.txt
     """
     """
 
 
-    correct_md5sum = '9dd6c4bb9d2cf6051b12f4b5f9d70523'
-    correct_file_name_platform_nl = 'md5_sum_correct_file_platform_nl'
-    correct_file_name_unix_nl = 'md5_sum_correct_file_unix_nl'
-    wrong_file_name = 'md5_sum_wrong_file'
+    correct_md5sum = "9dd6c4bb9d2cf6051b12f4b5f9d70523"
+    correct_file_name_platform_nl = "md5_sum_correct_file_platform_nl"
+    correct_file_name_unix_nl = "md5_sum_correct_file_unix_nl"
+    wrong_file_name = "md5_sum_wrong_file"
 
 
     @classmethod
     @classmethod
     def setUpClass(cls):
     def setUpClass(cls):
-        with open(cls.correct_file_name_platform_nl, 'w') as f:
+        with open(cls.correct_file_name_platform_nl, "w") as f:
             for line in CORRECT_LINES:
             for line in CORRECT_LINES:
                 # \n should be converted to platform newline
                 # \n should be converted to platform newline
-                f.write(line + '\n')
-        with open(cls.correct_file_name_unix_nl, 'w') as f:
+                f.write(line + "\n")
+        with open(cls.correct_file_name_unix_nl, "w") as f:
             for line in CORRECT_LINES:
             for line in CORRECT_LINES:
                 # binary mode will write pure \n
                 # binary mode will write pure \n
-                f.write(line + '\n')
-        with open(cls.wrong_file_name, 'w') as f:
+                f.write(line + "\n")
+        with open(cls.wrong_file_name, "w") as f:
             for line in INCORRECT_LINES:
             for line in INCORRECT_LINES:
                 # \n should be converted to platform newline
                 # \n should be converted to platform newline
-                f.write(line + '\n')
+                f.write(line + "\n")
 
 
     @classmethod
     @classmethod
     def tearDownClass(cls):
     def tearDownClass(cls):
@@ -371,28 +392,36 @@ class TestMd5Sums(TestCase):
 
 
     def test_text_file_binary(self):
     def test_text_file_binary(self):
         r"""File with ``\n`` (LF) newlines as binary (MD5 has ``\n``)."""
         r"""File with ``\n`` (LF) newlines as binary (MD5 has ``\n``)."""
-        self.assertEqual(file_md5(self.correct_file_name_unix_nl),
-                         self.correct_md5sum,
-                         msg="MD5 sums different")
+        self.assertEqual(
+            file_md5(self.correct_file_name_unix_nl),
+            self.correct_md5sum,
+            msg="MD5 sums different",
+        )
 
 
     def test_text_file_platfrom(self):
     def test_text_file_platfrom(self):
         r"""Text file with platform dependent newlines"""
         r"""Text file with platform dependent newlines"""
-        self.assertEqual(text_file_md5(self.correct_file_name_platform_nl),
-                         self.correct_md5sum,
-                         msg="MD5 sums different")
+        self.assertEqual(
+            text_file_md5(self.correct_file_name_platform_nl),
+            self.correct_md5sum,
+            msg="MD5 sums different",
+        )
 
 
     def test_text_file_unix(self):
     def test_text_file_unix(self):
         r"""Text file with ``\n`` (LF) newlines"""
         r"""Text file with ``\n`` (LF) newlines"""
-        self.assertEqual(text_file_md5(self.correct_file_name_unix_nl),
-                         self.correct_md5sum,
-                         msg="MD5 sums different")
+        self.assertEqual(
+            text_file_md5(self.correct_file_name_unix_nl),
+            self.correct_md5sum,
+            msg="MD5 sums different",
+        )
 
 
     def test_text_file_different(self):
     def test_text_file_different(self):
         r"""Text file with ``\n`` (LF) newlines"""
         r"""Text file with ``\n`` (LF) newlines"""
-        self.assertNotEqual(text_file_md5(self.wrong_file_name),
-                            self.correct_md5sum,
-                            msg="MD5 sums must be different")
+        self.assertNotEqual(
+            text_file_md5(self.wrong_file_name),
+            self.correct_md5sum,
+            msg="MD5 sums must be different",
+        )
 
 
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     test()
     test()

+ 92 - 53
python/grass/gunittest/testsuite/test_gmodules.py

@@ -4,7 +4,7 @@ import subprocess
 
 
 from grass.gunittest.case import TestCase
 from grass.gunittest.case import TestCase
 from grass.gunittest.main import test
 from grass.gunittest.main import test
-from grass.gunittest.gmodules import (call_module, CalledModuleError)
+from grass.gunittest.gmodules import call_module, CalledModuleError
 
 
 G_REGION_OUTPUT = """...
 G_REGION_OUTPUT = """...
 n=...
 n=...
@@ -20,75 +20,114 @@ cells=...
 
 
 
 
 class TestCallModuleFunction(TestCase):
 class TestCallModuleFunction(TestCase):
-
     def test_output(self):
     def test_output(self):
-        output = call_module('g.region', flags='pg')
+        output = call_module("g.region", flags="pg")
         self.assertLooksLike(output, G_REGION_OUTPUT)
         self.assertLooksLike(output, G_REGION_OUTPUT)
 
 
     def test_input_output(self):
     def test_input_output(self):
-        output = call_module('m.proj', flags='i', input='-', stdin="50.0 41.5")
-        self.assertLooksLike(output, '...|...\n')
+        output = call_module("m.proj", flags="i", input="-", stdin="50.0 41.5")
+        self.assertLooksLike(output, "...|...\n")
 
 
     def test_no_output(self):
     def test_no_output(self):
-        output = call_module('m.proj', flags='i', input='-', stdin="50.0 41.5",
-                             capture_stdout=False)
+        output = call_module(
+            "m.proj", flags="i", input="-", stdin="50.0 41.5", capture_stdout=False
+        )
         self.assertIsNone(output)
         self.assertIsNone(output)
 
 
     def test_merge_stderr(self):
     def test_merge_stderr(self):
-        output = call_module('m.proj', flags='i', input='-', stdin="50.0 41.5",
-                             verbose=True,
-                             merge_stderr=True)
-        self.assertLooksLike(output, '...+proj=longlat +datum=WGS84...')
-        self.assertLooksLike(output, '...|...\n')
+        output = call_module(
+            "m.proj",
+            flags="i",
+            input="-",
+            stdin="50.0 41.5",
+            verbose=True,
+            merge_stderr=True,
+        )
+        self.assertLooksLike(output, "...+proj=longlat +datum=WGS84...")
+        self.assertLooksLike(output, "...|...\n")
 
 
     def test_merge_stderr_with_wrong_stdin_stderr(self):
     def test_merge_stderr_with_wrong_stdin_stderr(self):
-        self.assertRaises(ValueError,
-                          call_module,
-                          'm.proj', flags='i', input='-', stdin="50.0 41.5",
-                          verbose=True,
-                          merge_stderr=True, capture_stdout=False)
-        self.assertRaises(ValueError,
-                          call_module,
-                          'm.proj', flags='i', input='-', stdin="50.0 41.5",
-                          verbose=True,
-                          merge_stderr=True, capture_stderr=False)
-        self.assertRaises(ValueError,
-                          call_module,
-                          'm.proj', flags='i', input='-', stdin="50.0 41.5",
-                          verbose=True,
-                          merge_stderr=True,
-                          capture_stdout=False, capture_stderr=False)
+        self.assertRaises(
+            ValueError,
+            call_module,
+            "m.proj",
+            flags="i",
+            input="-",
+            stdin="50.0 41.5",
+            verbose=True,
+            merge_stderr=True,
+            capture_stdout=False,
+        )
+        self.assertRaises(
+            ValueError,
+            call_module,
+            "m.proj",
+            flags="i",
+            input="-",
+            stdin="50.0 41.5",
+            verbose=True,
+            merge_stderr=True,
+            capture_stderr=False,
+        )
+        self.assertRaises(
+            ValueError,
+            call_module,
+            "m.proj",
+            flags="i",
+            input="-",
+            stdin="50.0 41.5",
+            verbose=True,
+            merge_stderr=True,
+            capture_stdout=False,
+            capture_stderr=False,
+        )
 
 
     def test_wrong_module_params(self):
     def test_wrong_module_params(self):
-        self.assertRaises(CalledModuleError,
-                          call_module,
-                          'g.region', aabbbccc='notexist')
+        self.assertRaises(
+            CalledModuleError, call_module, "g.region", aabbbccc="notexist"
+        )
 
 
     def test_module_input_param_wrong(self):
     def test_module_input_param_wrong(self):
-        self.assertRaises(ValueError,
-                          call_module,
-                          'm.proj', flags='i', input='does_not_exist',
-                          stdin="50.0 41.5")
+        self.assertRaises(
+            ValueError,
+            call_module,
+            "m.proj",
+            flags="i",
+            input="does_not_exist",
+            stdin="50.0 41.5",
+        )
 
 
     def test_missing_stdin_with_input_param(self):
     def test_missing_stdin_with_input_param(self):
-        self.assertRaises(ValueError,
-                          call_module,
-                          'm.proj', flags='i', input='-')
+        self.assertRaises(ValueError, call_module, "m.proj", flags="i", input="-")
 
 
     def test_wrong_usage_of_popen_like_interface(self):
     def test_wrong_usage_of_popen_like_interface(self):
-        self.assertRaises(ValueError,
-                          call_module,
-                          'm.proj', flags='i', input='-',
-                          stdin=subprocess.PIPE)
-        self.assertRaises(TypeError,
-                          call_module,
-                          'm.proj', flags='i', input='-', stdin="50.0 41.5",
-                          stdout='any_value_or_type_here')
-        self.assertRaises(TypeError,
-                          call_module,
-                          'm.proj', flags='i', input='-', stdin="50.0 41.5",
-                          stderr='any_value_or_type_here')
-
-
-if __name__ == '__main__':
+        self.assertRaises(
+            ValueError,
+            call_module,
+            "m.proj",
+            flags="i",
+            input="-",
+            stdin=subprocess.PIPE,
+        )
+        self.assertRaises(
+            TypeError,
+            call_module,
+            "m.proj",
+            flags="i",
+            input="-",
+            stdin="50.0 41.5",
+            stdout="any_value_or_type_here",
+        )
+        self.assertRaises(
+            TypeError,
+            call_module,
+            "m.proj",
+            flags="i",
+            input="-",
+            stdin="50.0 41.5",
+            stderr="any_value_or_type_here",
+        )
+
+
+if __name__ == "__main__":
     test()
     test()

+ 9 - 7
python/grass/gunittest/testsuite/test_gunitest_doctests.py

@@ -18,12 +18,14 @@ import grass.gunittest.checkers
 # and contains doctest's methods
 # and contains doctest's methods
 # the alternative is to copy 500 from doctest and change what is needed
 # the alternative is to copy 500 from doctest and change what is needed
 # (this might be necessary anyway because of the reports and stdout and stderr)
 # (this might be necessary anyway because of the reports and stdout and stderr)
-doctest.DocFileCase = type('DocFileCase',
-                           (grass.gunittest.case.TestCase,),
-                           dict(doctest.DocFileCase.__dict__))
-doctest.SkipDocTestCase = type('SkipDocTestCase',
-                               (grass.gunittest.case.TestCase,),
-                               dict(doctest.SkipDocTestCase.__dict__))
+doctest.DocFileCase = type(
+    "DocFileCase", (grass.gunittest.case.TestCase,), dict(doctest.DocFileCase.__dict__)
+)
+doctest.SkipDocTestCase = type(
+    "SkipDocTestCase",
+    (grass.gunittest.case.TestCase,),
+    dict(doctest.SkipDocTestCase.__dict__),
+)
 
 
 
 
 def load_tests(loader, tests, ignore):
 def load_tests(loader, tests, ignore):
@@ -37,5 +39,5 @@ def load_tests(loader, tests, ignore):
     return tests
     return tests
 
 
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     grass.gunittest.main.test()
     grass.gunittest.main.test()

+ 23 - 16
python/grass/gunittest/testsuite/test_module_assertions.py

@@ -13,32 +13,39 @@ from grass.gunittest.gmodules import CalledModuleError
 
 
 class TestModuleAssertions(TestCase):
 class TestModuleAssertions(TestCase):
     """Test assertions using PyGRASS Module"""
     """Test assertions using PyGRASS Module"""
+
     # pylint: disable=R0904
     # pylint: disable=R0904
 
 
     def setUp(self):
     def setUp(self):
         """Create two Module instances one correct and one with wrong map"""
         """Create two Module instances one correct and one with wrong map"""
-        self.rinfo = Module('r.info', map='elevation', flags='g',
-                            stdout_=subprocess.PIPE, run_=False, finish_=True)
+        self.rinfo = Module(
+            "r.info",
+            map="elevation",
+            flags="g",
+            stdout_=subprocess.PIPE,
+            run_=False,
+            finish_=True,
+        )
         self.rinfo_wrong = copy.deepcopy(self.rinfo)
         self.rinfo_wrong = copy.deepcopy(self.rinfo)
-        self.wrong_map = 'does_not_exists'
-        self.rinfo_wrong.inputs['map'].value = self.wrong_map
+        self.wrong_map = "does_not_exists"
+        self.rinfo_wrong.inputs["map"].value = self.wrong_map
 
 
     def test_runModule(self):
     def test_runModule(self):
         """Correct and incorrect Module used in runModule()"""
         """Correct and incorrect Module used in runModule()"""
         self.runModule(self.rinfo)
         self.runModule(self.rinfo)
-        self.assertTrue(self.rinfo.outputs['stdout'].value)
+        self.assertTrue(self.rinfo.outputs["stdout"].value)
         self.assertRaises(CalledModuleError, self.runModule, self.rinfo_wrong)
         self.assertRaises(CalledModuleError, self.runModule, self.rinfo_wrong)
 
 
     def test_assertModule(self):
     def test_assertModule(self):
         """Correct and incorrect Module used in assertModule()"""
         """Correct and incorrect Module used in assertModule()"""
         self.assertModule(self.rinfo)
         self.assertModule(self.rinfo)
-        self.assertTrue(self.rinfo.outputs['stdout'].value)
+        self.assertTrue(self.rinfo.outputs["stdout"].value)
         self.assertRaises(self.failureException, self.assertModule, self.rinfo_wrong)
         self.assertRaises(self.failureException, self.assertModule, self.rinfo_wrong)
 
 
     def test_assertModuleFail(self):
     def test_assertModuleFail(self):
         """Correct and incorrect Module used in assertModuleFail()"""
         """Correct and incorrect Module used in assertModuleFail()"""
         self.assertModuleFail(self.rinfo_wrong)
         self.assertModuleFail(self.rinfo_wrong)
-        stderr = self.rinfo_wrong.outputs['stderr'].value
+        stderr = self.rinfo_wrong.outputs["stderr"].value
         self.assertTrue(stderr)
         self.assertTrue(stderr)
         self.assertIn(self.wrong_map, stderr)
         self.assertIn(self.wrong_map, stderr)
         self.assertRaises(self.failureException, self.assertModuleFail, self.rinfo)
         self.assertRaises(self.failureException, self.assertModuleFail, self.rinfo)
@@ -46,36 +53,36 @@ class TestModuleAssertions(TestCase):
 
 
 class TestSimpleModuleAssertions(TestCase):
 class TestSimpleModuleAssertions(TestCase):
     """Test assertions using SimpleModule"""
     """Test assertions using SimpleModule"""
+
     # pylint: disable=R0904
     # pylint: disable=R0904
 
 
     def setUp(self):
     def setUp(self):
-        """Create two SimpleModule instances one correct and one with wrong map
-        """
-        self.rinfo = SimpleModule('r.info', map='elevation', flags='g')
+        """Create two SimpleModule instances one correct and one with wrong map"""
+        self.rinfo = SimpleModule("r.info", map="elevation", flags="g")
         self.rinfo_wrong = copy.deepcopy(self.rinfo)
         self.rinfo_wrong = copy.deepcopy(self.rinfo)
-        self.wrong_map = 'does_not_exists'
-        self.rinfo_wrong.inputs['map'].value = self.wrong_map
+        self.wrong_map = "does_not_exists"
+        self.rinfo_wrong.inputs["map"].value = self.wrong_map
 
 
     def test_runModule(self):
     def test_runModule(self):
         """Correct and incorrect SimpleModule used in runModule()"""
         """Correct and incorrect SimpleModule used in runModule()"""
         self.runModule(self.rinfo)
         self.runModule(self.rinfo)
-        self.assertTrue(self.rinfo.outputs['stdout'].value)
+        self.assertTrue(self.rinfo.outputs["stdout"].value)
         self.assertRaises(CalledModuleError, self.runModule, self.rinfo_wrong)
         self.assertRaises(CalledModuleError, self.runModule, self.rinfo_wrong)
 
 
     def test_assertModule(self):
     def test_assertModule(self):
         """Correct and incorrect SimpleModule used in assertModule()"""
         """Correct and incorrect SimpleModule used in assertModule()"""
         self.assertModule(self.rinfo)
         self.assertModule(self.rinfo)
-        self.assertTrue(self.rinfo.outputs['stdout'].value)
+        self.assertTrue(self.rinfo.outputs["stdout"].value)
         self.assertRaises(self.failureException, self.assertModule, self.rinfo_wrong)
         self.assertRaises(self.failureException, self.assertModule, self.rinfo_wrong)
 
 
     def test_assertModuleFail(self):
     def test_assertModuleFail(self):
         """Correct and incorrect SimpleModule used in assertModuleFail()"""
         """Correct and incorrect SimpleModule used in assertModuleFail()"""
         self.assertModuleFail(self.rinfo_wrong)
         self.assertModuleFail(self.rinfo_wrong)
-        stderr = self.rinfo_wrong.outputs['stderr'].value
+        stderr = self.rinfo_wrong.outputs["stderr"].value
         self.assertTrue(stderr)
         self.assertTrue(stderr)
         self.assertIn(self.wrong_map, stderr)
         self.assertIn(self.wrong_map, stderr)
         self.assertRaises(self.failureException, self.assertModuleFail, self.rinfo)
         self.assertRaises(self.failureException, self.assertModuleFail, self.rinfo)
 
 
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     test()
     test()

+ 3 - 1
python/grass/gunittest/utils.py

@@ -43,6 +43,7 @@ def do_doctest_gettext_workaround():
     dummy underscore function and one other function which creates the right
     dummy underscore function and one other function which creates the right
     environment to satisfy all. This is done by this function.
     environment to satisfy all. This is done by this function.
     """
     """
+
     def new_displayhook(string):
     def new_displayhook(string):
         """A replacement for default `sys.displayhook`"""
         """A replacement for default `sys.displayhook`"""
         if string is not None:
         if string is not None:
@@ -63,6 +64,7 @@ def do_doctest_gettext_workaround():
 
 
 _MAX_LENGTH = 80
 _MAX_LENGTH = 80
 
 
+
 # taken from unittest.util (Python 2.7) since it is not part of API
 # taken from unittest.util (Python 2.7) since it is not part of API
 # but we need it for the same reason as it is used un unittest's TestCase
 # but we need it for the same reason as it is used un unittest's TestCase
 def safe_repr(obj, short=False):
 def safe_repr(obj, short=False):
@@ -72,4 +74,4 @@ def safe_repr(obj, short=False):
         result = object.__repr__(obj)
         result = object.__repr__(obj)
     if not short or len(result) < _MAX_LENGTH:
     if not short or len(result) < _MAX_LENGTH:
         return result
         return result
-    return result[:_MAX_LENGTH] + ' [truncated]...'
+    return result[:_MAX_LENGTH] + " [truncated]..."

+ 35 - 21
python/grass/imaging/images2avi.py

@@ -59,8 +59,15 @@ def _cleanDir(tempDir):
         print("Oops, could not fully clean up temporary files.")
         print("Oops, could not fully clean up temporary files.")
 
 
 
 
-def writeAvi(filename, images, duration=0.1, encoding='mpeg4',
-             inputOptions='', outputOptions='', bg_task=False):
+def writeAvi(
+    filename,
+    images,
+    duration=0.1,
+    encoding="mpeg4",
+    inputOptions="",
+    outputOptions="",
+    bg_task=False,
+):
     """Export movie to a AVI file, which is encoded with the given
     """Export movie to a AVI file, which is encoded with the given
     encoding. Hint for Windows users: the 'msmpeg4v2' codec is
     encoding. Hint for Windows users: the 'msmpeg4v2' codec is
     natively supported on Windows.
     natively supported on Windows.
@@ -89,21 +96,21 @@ def writeAvi(filename, images, duration=0.1, encoding='mpeg4',
     try:
     try:
         fps = float(1.0 / duration)
         fps = float(1.0 / duration)
     except Exception:
     except Exception:
-        raise ValueError(_('Invalid duration parameter for writeAvi.'))
+        raise ValueError(_("Invalid duration parameter for writeAvi."))
 
 
     # Determine temp dir and create images
     # Determine temp dir and create images
-    tempDir = os.path.join(os.path.expanduser('~'), '.tempIms')
-    images2ims.writeIms(os.path.join(tempDir, 'im*.png'), images)
+    tempDir = os.path.join(os.path.expanduser("~"), ".tempIms")
+    images2ims.writeIms(os.path.join(tempDir, "im*.png"), images)
 
 
     # Determine formatter
     # Determine formatter
     N = len(images)
     N = len(images)
-    formatter = '%04d'
+    formatter = "%04d"
     if N < 10:
     if N < 10:
-        formatter = '%d'
+        formatter = "%d"
     elif N < 100:
     elif N < 100:
-        formatter = '%02d'
+        formatter = "%02d"
     elif N < 1000:
     elif N < 1000:
-        formatter = '%03d'
+        formatter = "%03d"
 
 
     # Compile command to create avi
     # Compile command to create avi
     command = "ffmpeg -r %i %s " % (int(fps), inputOptions)
     command = "ffmpeg -r %i %s " % (int(fps), inputOptions)
@@ -112,8 +119,9 @@ def writeAvi(filename, images, duration=0.1, encoding='mpeg4',
     command += "output.avi"
     command += "output.avi"
 
 
     # Run ffmpeg
     # Run ffmpeg
-    S = subprocess.Popen(command, shell=True, cwd=tempDir,
-                         stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+    S = subprocess.Popen(
+        command, shell=True, cwd=tempDir, stdout=subprocess.PIPE, stderr=subprocess.PIPE
+    )
 
 
     # Show what ffmpeg has to say
     # Show what ffmpeg has to say
     outPut = S.stdout.read()
     outPut = S.stdout.read()
@@ -122,17 +130,22 @@ def writeAvi(filename, images, duration=0.1, encoding='mpeg4',
         # Clean up
         # Clean up
         _cleanDir(tempDir)
         _cleanDir(tempDir)
         if bg_task:
         if bg_task:
-            return gscript.decode(outPut) + '\n' + gscript.decode(
-                S.stderr.read()) + '\n' + _('Could not write avi.')
+            return (
+                gscript.decode(outPut)
+                + "\n"
+                + gscript.decode(S.stderr.read())
+                + "\n"
+                + _("Could not write avi.")
+            )
         else:
         else:
             # An error occurred, show
             # An error occurred, show
             print(gscript.decode(outPut))
             print(gscript.decode(outPut))
             print(gscript.decode(S.stderr.read()))
             print(gscript.decode(S.stderr.read()))
-            raise RuntimeError(_('Could not write avi.'))
+            raise RuntimeError(_("Could not write avi."))
     else:
     else:
         try:
         try:
             # Copy avi
             # Copy avi
-            shutil.copy(os.path.join(tempDir, 'output.avi'), filename)
+            shutil.copy(os.path.join(tempDir, "output.avi"), filename)
         except Exception as err:
         except Exception as err:
             # Clean up
             # Clean up
             _cleanDir(tempDir)
             _cleanDir(tempDir)
@@ -158,20 +171,21 @@ def readAvi(filename, asNumpy=True):
 
 
     # Check whether it exists
     # Check whether it exists
     if not os.path.isfile(filename):
     if not os.path.isfile(filename):
-        raise IOError('File not found: '+str(filename))
+        raise IOError("File not found: " + str(filename))
 
 
     # Determine temp dir, make sure it exists
     # Determine temp dir, make sure it exists
-    tempDir = os.path.join(os.path.expanduser('~'), '.tempIms')
+    tempDir = os.path.join(os.path.expanduser("~"), ".tempIms")
     if not os.path.isdir(tempDir):
     if not os.path.isdir(tempDir):
         os.makedirs(tempDir)
         os.makedirs(tempDir)
 
 
     # Copy movie there
     # Copy movie there
-    shutil.copy(filename, os.path.join(tempDir, 'input.avi'))
+    shutil.copy(filename, os.path.join(tempDir, "input.avi"))
 
 
     # Run ffmpeg
     # Run ffmpeg
     command = "ffmpeg -i input.avi im%d.jpg"
     command = "ffmpeg -i input.avi im%d.jpg"
-    S = subprocess.Popen(command, shell=True, cwd=tempDir,
-                         stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+    S = subprocess.Popen(
+        command, shell=True, cwd=tempDir, stdout=subprocess.PIPE, stderr=subprocess.PIPE
+    )
 
 
     # Show what mencodec has to say
     # Show what mencodec has to say
     outPut = S.stdout.read()
     outPut = S.stdout.read()
@@ -185,7 +199,7 @@ def readAvi(filename, asNumpy=True):
         raise RuntimeError("Could not read avi.")
         raise RuntimeError("Could not read avi.")
     else:
     else:
         # Read images
         # Read images
-        images = images2ims.readIms(os.path.join(tempDir, 'im*.jpg'), asNumpy)
+        images = images2ims.readIms(os.path.join(tempDir, "im*.jpg"), asNumpy)
         # Clean up
         # Clean up
         _cleanDir(tempDir)
         _cleanDir(tempDir)
 
 

+ 13 - 7
python/grass/imaging/images2gif.py

@@ -765,12 +765,15 @@ class NeuQuant:
         self.BETA = 1.0/1024.0
         self.BETA = 1.0/1024.0
         self.BETAGAMMA = self.BETA * self.GAMMA
         self.BETAGAMMA = self.BETA * self.GAMMA
 
 
-        self.network = np.empty((self.NETSIZE, 3), dtype='float64')  # The network itself
-        self.colormap = np.empty((self.NETSIZE, 4), dtype='int32')  # The network itself
+        # The network itself
+        self.network = np.empty((self.NETSIZE, 3), dtype='float64')
+        # The network itself
+        self.colormap = np.empty((self.NETSIZE, 4), dtype='int32')
 
 
-        self.netindex = np.empty(256, dtype='int32') # For network lookup - really 256
+        self.netindex = np.empty(256, dtype='int32')  # For network lookup - really 256
 
 
-        self.bias = np.empty(self.NETSIZE, dtype='float64') # Bias and freq arrays for learning
+        # Bias and freq arrays for learning
+        self.bias = np.empty(self.NETSIZE, dtype='float64')
         self.freq = np.empty(self.NETSIZE, dtype='float64')
         self.freq = np.empty(self.NETSIZE, dtype='float64')
 
 
         self.pixels = None
         self.pixels = None
@@ -872,7 +875,7 @@ class NeuQuant:
         p = self.network[lo + 1:hi]
         p = self.network[lo + 1:hi]
         p -= np.transpose(np.transpose(p - np.array([b, g, r])) * a)
         p -= np.transpose(np.transpose(p - np.array([b, g, r])) * a)
 
 
-    #def contest(self, b, g, r):
+    # def contest(self, b, g, r):
     #    """ Search for biased BGR values
     #    """ Search for biased BGR values
     #            Finds closest neuron (min dist) and updates self.freq
     #            Finds closest neuron (min dist) and updates self.freq
     #            finds best neuron (min dist-self.bias) and returns position
     #            finds best neuron (min dist-self.bias) and returns position
@@ -927,8 +930,10 @@ class NeuQuant:
         if rad <= 1:
         if rad <= 1:
             rad = 0
             rad = 0
 
 
-        print("Beginning 1D learning: samplepixels = %1.2f  rad = %i" %
-             (samplepixels, rad))
+        print(
+            "Beginning 1D learning: samplepixels = %1.2f  rad = %i"
+            % (samplepixels, rad)
+        )
         step = 0
         step = 0
         pos = 0
         pos = 0
         if lengthcount % NeuQuant.PRIME1 != 0:
         if lengthcount % NeuQuant.PRIME1 != 0:
@@ -1090,6 +1095,7 @@ class NeuQuant:
         a = np.argmin((dists * dists).sum(1))
         a = np.argmin((dists * dists).sum(1))
         return a
         return a
 
 
+
 if __name__ == '__main__':
 if __name__ == '__main__':
     im = np.zeros((200, 200), dtype=np.uint8)
     im = np.zeros((200, 200), dtype=np.uint8)
     im[10: 30, :] = 100
     im[10: 30, :] = 100

+ 15 - 15
python/grass/imaging/images2ims.py

@@ -80,19 +80,19 @@ def checkImages(images):
                 pass  # ok
                 pass  # ok
             elif im.ndim == 3:
             elif im.ndim == 3:
                 if im.shape[2] not in [3, 4]:
                 if im.shape[2] not in [3, 4]:
-                    raise ValueError('This array can not represent an image.')
+                    raise ValueError("This array can not represent an image.")
             else:
             else:
-                raise ValueError('This array can not represent an image.')
+                raise ValueError("This array can not represent an image.")
         else:
         else:
-            raise ValueError('Invalid image type: ' + str(type(im)))
+            raise ValueError("Invalid image type: " + str(type(im)))
 
 
     # Done
     # Done
     return images2
     return images2
 
 
 
 
 def _getFilenameParts(filename):
 def _getFilenameParts(filename):
-    if '*' in filename:
-        return tuple(filename.split('*', 1))
+    if "*" in filename:
+        return tuple(filename.split("*", 1))
     else:
     else:
         return os.path.splitext(filename)
         return os.path.splitext(filename)
 
 
@@ -100,13 +100,13 @@ def _getFilenameParts(filename):
 def _getFilenameWithFormatter(filename, N):
 def _getFilenameWithFormatter(filename, N):
 
 
     # Determine sequence number formatter
     # Determine sequence number formatter
-    formatter = '%04i'
+    formatter = "%04i"
     if N < 10:
     if N < 10:
-        formatter = '%i'
+        formatter = "%i"
     elif N < 100:
     elif N < 100:
-        formatter = '%02i'
+        formatter = "%02i"
     elif N < 1000:
     elif N < 1000:
-        formatter = '%03i'
+        formatter = "%03i"
 
 
     # Insert sequence number formatter
     # Insert sequence number formatter
     part1, part2 = _getFilenameParts(filename)
     part1, part2 = _getFilenameParts(filename)
@@ -115,11 +115,11 @@ def _getFilenameWithFormatter(filename, N):
 
 
 def _getSequenceNumber(filename, part1, part2):
 def _getSequenceNumber(filename, part1, part2):
     # Get string bit
     # Get string bit
-    seq = filename[len(part1):-len(part2)]
+    seq = filename[len(part1) : -len(part2)]
     # Get all numeric chars
     # Get all numeric chars
-    seq2 = ''
+    seq2 = ""
     for c in seq:
     for c in seq:
-        if c in '0123456789':
+        if c in "0123456789":
             seq2 += c
             seq2 += c
         else:
         else:
             break
             break
@@ -175,7 +175,7 @@ def writeIms(filename, images):
 
 
 
 
 def readIms(filename, asNumpy=True):
 def readIms(filename, asNumpy=True):
-    """ readIms(filename, asNumpy=True)
+    """readIms(filename, asNumpy=True)
 
 
     Read images from a series of images in a single directory. Returns a
     Read images from a series of images in a single directory. Returns a
     list of numpy arrays, or, if asNumpy is false, a list if PIL images.
     list of numpy arrays, or, if asNumpy is false, a list if PIL images.
@@ -198,7 +198,7 @@ def readIms(filename, asNumpy=True):
 
 
     # Check dir exists
     # Check dir exists
     if not os.path.isdir(dirname):
     if not os.path.isdir(dirname):
-        raise IOError('Directory not found: '+str(dirname))
+        raise IOError("Directory not found: " + str(dirname))
 
 
     # Get two parts of the filename
     # Get two parts of the filename
     part1, part2 = _getFilenameParts(filename)
     part1, part2 = _getFilenameParts(filename)
@@ -225,7 +225,7 @@ def readIms(filename, asNumpy=True):
         images = []
         images = []
         for im in images2:
         for im in images2:
             # Make without palette
             # Make without palette
-            if im.mode == 'P':
+            if im.mode == "P":
                 im = im.convert()
                 im = im.convert()
             # Make numpy array
             # Make numpy array
             a = np.asarray(im)
             a = np.asarray(im)

+ 132 - 130
python/grass/imaging/images2swf.py

@@ -86,9 +86,9 @@ except ImportError:
 # Code taken from six.py by Benjamin Peterson (MIT licensed)
 # Code taken from six.py by Benjamin Peterson (MIT licensed)
 PY3 = sys.version_info[0] == 3
 PY3 = sys.version_info[0] == 3
 
 
-string_types = str,
-integer_types = int,
-class_types = type,
+string_types = (str,)
+integer_types = (int,)
+class_types = (type,)
 text_type = str
 text_type = str
 binary_type = bytes
 binary_type = bytes
 
 
@@ -97,7 +97,7 @@ binary_type = bytes
 
 
 
 
 def checkImages(images):
 def checkImages(images):
-    """ checkImages(images)
+    """checkImages(images)
     Check numpy images and correct intensity range etc.
     Check numpy images and correct intensity range etc.
     The same for all movie formats.
     The same for all movie formats.
     """
     """
@@ -131,11 +131,11 @@ def checkImages(images):
                 pass  # ok
                 pass  # ok
             elif im.ndim == 3:
             elif im.ndim == 3:
                 if im.shape[2] not in [3, 4]:
                 if im.shape[2] not in [3, 4]:
-                    raise ValueError('This array can not represent an image.')
+                    raise ValueError("This array can not represent an image.")
             else:
             else:
-                raise ValueError('This array can not represent an image.')
+                raise ValueError("This array can not represent an image.")
         else:
         else:
-            raise ValueError('Invalid image type: ' + str(type(im)))
+            raise ValueError("Invalid image type: " + str(type(im)))
 
 
     # Done
     # Done
     return images2
     return images2
@@ -161,14 +161,14 @@ class BitArray:
         return self._len  # self.data.shape[0]
         return self._len  # self.data.shape[0]
 
 
     def __repr__(self):
     def __repr__(self):
-        return self.data[:self._len].tobytes()
+        return self.data[: self._len].tobytes()
 
 
     def _checkSize(self):
     def _checkSize(self):
         # check length... grow if necessary
         # check length... grow if necessary
         arraylen = self.data.shape[0]
         arraylen = self.data.shape[0]
         if self._len >= arraylen:
         if self._len >= arraylen:
-            tmp = np.zeros((arraylen*2,), dtype=np.uint8)
-            tmp[:self._len] = self.data[:self._len]
+            tmp = np.zeros((arraylen * 2,), dtype=np.uint8)
+            tmp[: self._len] = self.data[: self._len]
             self.data = tmp
             self.data = tmp
 
 
     def __add__(self, value):
     def __add__(self, value):
@@ -193,11 +193,11 @@ class BitArray:
 
 
     def Reverse(self):
     def Reverse(self):
         """ In-place reverse. """
         """ In-place reverse. """
-        tmp = self.data[:self._len].copy()
-        self.data[:self._len] = np.flipud(tmp)
+        tmp = self.data[: self._len].copy()
+        self.data[: self._len] = np.flipud(tmp)
 
 
     def ToBytes(self):
     def ToBytes(self):
-        """ Convert to bytes. If necessary,
+        """Convert to bytes. If necessary,
         zeros are padded to the end (right side).
         zeros are padded to the end (right side).
         """
         """
         bits = str(self)
         bits = str(self)
@@ -207,12 +207,12 @@ class BitArray:
         while nbytes * 8 < len(bits):
         while nbytes * 8 < len(bits):
             nbytes += 1
             nbytes += 1
         # pad
         # pad
-        bits = bits.ljust(nbytes * 8, '0')
+        bits = bits.ljust(nbytes * 8, "0")
 
 
         # go from bits to bytes
         # go from bits to bytes
         bb = binary_type()
         bb = binary_type()
         for i in range(nbytes):
         for i in range(nbytes):
-            tmp = int(bits[i * 8: (i + 1) * 8], 2)
+            tmp = int(bits[i * 8 : (i + 1) * 8], 2)
             bb += intToUint8(tmp)
             bb += intToUint8(tmp)
 
 
         # done
         # done
@@ -220,15 +220,19 @@ class BitArray:
 
 
 
 
 if PY3:
 if PY3:
+
     def intToUint32(i):
     def intToUint32(i):
-        return int(i).to_bytes(4, 'little')
+        return int(i).to_bytes(4, "little")
 
 
     def intToUint16(i):
     def intToUint16(i):
-        return int(i).to_bytes(2, 'little')
+        return int(i).to_bytes(2, "little")
 
 
     def intToUint8(i):
     def intToUint8(i):
-        return int(i).to_bytes(1, 'little')
+        return int(i).to_bytes(1, "little")
+
+
 else:
 else:
+
     def intToUint32(i):
     def intToUint32(i):
         number = int(i)
         number = int(i)
         n1, n2, n3, n4 = 1, 256, 256 * 256, 256 * 256 * 256
         n1, n2, n3, n4 = 1, 256, 256 * 256, 256 * 256 * 256
@@ -251,8 +255,8 @@ else:
 
 
 
 
 def intToBits(i, n=None):
 def intToBits(i, n=None):
-    """ convert int to a string of bits (0's and 1's in a string),
-    pad to n elements. Convert back using int(ss,2). """
+    """convert int to a string of bits (0's and 1's in a string),
+    pad to n elements. Convert back using int(ss,2)."""
     ii = i
     ii = i
 
 
     # make bits
     # make bits
@@ -266,7 +270,7 @@ def intToBits(i, n=None):
     if n is not None:
     if n is not None:
         if len(bb) > n:
         if len(bb) > n:
             raise ValueError("intToBits fail: len larger than padlength.")
             raise ValueError("intToBits fail: len larger than padlength.")
-        bb = str(bb).rjust(n, '0')
+        bb = str(bb).rjust(n, "0")
 
 
     # done
     # done
     return BitArray(bb)
     return BitArray(bb)
@@ -274,32 +278,32 @@ def intToBits(i, n=None):
 
 
 def bitsToInt(bb, n=8):
 def bitsToInt(bb, n=8):
     # Init
     # Init
-    value = ''
+    value = ""
 
 
     # Get value in bits
     # Get value in bits
     for i in range(len(bb)):
     for i in range(len(bb)):
-        b = bb[i:i+1]
+        b = bb[i : i + 1]
         tmp = bin(ord(b))[2:]
         tmp = bin(ord(b))[2:]
-        #value += tmp.rjust(8,'0')
-        value = tmp.rjust(8, '0') + value
+        # value += tmp.rjust(8,'0')
+        value = tmp.rjust(8, "0") + value
 
 
     # Make decimal
     # Make decimal
-    return(int(value[:n], 2))
+    return int(value[:n], 2)
 
 
 
 
 def getTypeAndLen(bb):
 def getTypeAndLen(bb):
-    """ bb should be 6 bytes at least
+    """bb should be 6 bytes at least
     Return (type, length, length_of_full_tag)
     Return (type, length, length_of_full_tag)
     """
     """
     # Init
     # Init
-    value = ''
+    value = ""
 
 
     # Get first 16 bits
     # Get first 16 bits
     for i in range(2):
     for i in range(2):
-        b = bb[i:i + 1]
+        b = bb[i : i + 1]
         tmp = bin(ord(b))[2:]
         tmp = bin(ord(b))[2:]
-        #value += tmp.rjust(8,'0')
-        value = tmp.rjust(8, '0') + value
+        # value += tmp.rjust(8,'0')
+        value = tmp.rjust(8, "0") + value
 
 
     # Get type and length
     # Get type and length
     type = int(value[:10], 2)
     type = int(value[:10], 2)
@@ -308,12 +312,12 @@ def getTypeAndLen(bb):
 
 
     # Long tag header?
     # Long tag header?
     if L == 63:  # '111111'
     if L == 63:  # '111111'
-        value = ''
+        value = ""
         for i in range(2, 6):
         for i in range(2, 6):
-            b = bb[i:i + 1]  # becomes a single-byte bytes() on both PY3 and PY2
+            b = bb[i : i + 1]  # becomes a single-byte bytes() on both PY3 and PY2
             tmp = bin(ord(b))[2:]
             tmp = bin(ord(b))[2:]
-            #value += tmp.rjust(8,'0')
-            value = tmp.rjust(8, '0') + value
+            # value += tmp.rjust(8,'0')
+            value = tmp.rjust(8, "0") + value
         L = int(value, 2)
         L = int(value, 2)
         L2 = L + 6
         L2 = L + 6
 
 
@@ -322,7 +326,7 @@ def getTypeAndLen(bb):
 
 
 
 
 def signedIntToBits(i, n=None):
 def signedIntToBits(i, n=None):
-    """ convert signed int to a string of bits (0's and 1's in a string),
+    """convert signed int to a string of bits (0's and 1's in a string),
     pad to n elements. Negative numbers are stored in 2's complement bit
     pad to n elements. Negative numbers are stored in 2's complement bit
     patterns, thus positive numbers always start with a 0.
     patterns, thus positive numbers always start with a 0.
     """
     """
@@ -341,22 +345,22 @@ def signedIntToBits(i, n=None):
     bb.Reverse()
     bb.Reverse()
 
 
     # justify
     # justify
-    bb = '0' + str(bb)  # always need the sign bit in front
+    bb = "0" + str(bb)  # always need the sign bit in front
     if n is not None:
     if n is not None:
         if len(bb) > n:
         if len(bb) > n:
             raise ValueError("signedIntToBits fail: len larger than padlength.")
             raise ValueError("signedIntToBits fail: len larger than padlength.")
-        bb = bb.rjust(n, '0')
+        bb = bb.rjust(n, "0")
 
 
     # was it negative? (then opposite bits)
     # was it negative? (then opposite bits)
     if i < 0:
     if i < 0:
-        bb = bb.replace('0', 'x').replace('1', '0').replace('x', '1')
+        bb = bb.replace("0", "x").replace("1", "0").replace("x", "1")
 
 
     # done
     # done
     return BitArray(bb)
     return BitArray(bb)
 
 
 
 
 def twitsToBits(arr):
 def twitsToBits(arr):
-    """ Given a few (signed) numbers, store them
+    """Given a few (signed) numbers, store them
     as compactly as possible in the wat specifief by the swf format.
     as compactly as possible in the wat specifief by the swf format.
     The numbers are multiplied by 20, assuming they
     The numbers are multiplied by 20, assuming they
     are twits.
     are twits.
@@ -366,7 +370,7 @@ def twitsToBits(arr):
     # first determine length using non justified bit strings
     # first determine length using non justified bit strings
     maxlen = 1
     maxlen = 1
     for i in arr:
     for i in arr:
-        tmp = len(signedIntToBits(i*20))
+        tmp = len(signedIntToBits(i * 20))
         if tmp > maxlen:
         if tmp > maxlen:
             maxlen = tmp
             maxlen = tmp
 
 
@@ -379,7 +383,7 @@ def twitsToBits(arr):
 
 
 
 
 def floatsToBits(arr):
 def floatsToBits(arr):
-    """ Given a few (signed) numbers, convert them to bits,
+    """Given a few (signed) numbers, convert them to bits,
     stored as FB (float bit values). We always use 16.16.
     stored as FB (float bit values). We always use 16.16.
     Negative numbers are not (yet) possible, because I don't
     Negative numbers are not (yet) possible, because I don't
     know how the're implemented (ambiguity).
     know how the're implemented (ambiguity).
@@ -399,7 +403,7 @@ def _readFrom(fp, n):
     bb = binary_type()
     bb = binary_type()
     try:
     try:
         while len(bb) < n:
         while len(bb) < n:
-            tmp = fp.read(n-len(bb))
+            tmp = fp.read(n - len(bb))
             bb += tmp
             bb += tmp
             if not tmp:
             if not tmp:
                 break
                 break
@@ -410,8 +414,8 @@ def _readFrom(fp, n):
 
 
 ## Base Tag
 ## Base Tag
 
 
-class Tag:
 
 
+class Tag:
     def __init__(self):
     def __init__(self):
         self.bytes = binary_type()
         self.bytes = binary_type()
         self.tagtype = -1
         self.tagtype = -1
@@ -428,7 +432,7 @@ class Tag:
         bits = intToBits(self.tagtype, 10)
         bits = intToBits(self.tagtype, 10)
 
 
         # complete header uint16 thing
         # complete header uint16 thing
-        bits += '1' * 6  # = 63 = 0x3f
+        bits += "1" * 6  # = 63 = 0x3f
         # make uint16
         # make uint16
         bb = intToUint16(int(str(bits), 2))
         bb = intToUint16(int(str(bits), 2))
 
 
@@ -440,32 +444,32 @@ class Tag:
         return bb
         return bb
 
 
     def MakeRectRecord(self, xmin, xmax, ymin, ymax):
     def MakeRectRecord(self, xmin, xmax, ymin, ymax):
-        """ Simply uses makeCompactArray to produce
-        a RECT Record. """
+        """Simply uses makeCompactArray to produce
+        a RECT Record."""
         return twitsToBits([xmin, xmax, ymin, ymax])
         return twitsToBits([xmin, xmax, ymin, ymax])
 
 
     def MakeMatrixRecord(self, scale_xy=None, rot_xy=None, trans_xy=None):
     def MakeMatrixRecord(self, scale_xy=None, rot_xy=None, trans_xy=None):
 
 
         # empty matrix?
         # empty matrix?
         if scale_xy is None and rot_xy is None and trans_xy is None:
         if scale_xy is None and rot_xy is None and trans_xy is None:
-            return "0"*8
+            return "0" * 8
 
 
         # init
         # init
         bits = BitArray()
         bits = BitArray()
 
 
         # scale
         # scale
         if scale_xy:
         if scale_xy:
-            bits += '1'
+            bits += "1"
             bits += floatsToBits([scale_xy[0], scale_xy[1]])
             bits += floatsToBits([scale_xy[0], scale_xy[1]])
         else:
         else:
-            bits += '0'
+            bits += "0"
 
 
         # rotation
         # rotation
         if rot_xy:
         if rot_xy:
-            bits += '1'
+            bits += "1"
             bits += floatsToBits([rot_xy[0], rot_xy[1]])
             bits += floatsToBits([rot_xy[0], rot_xy[1]])
         else:
         else:
-            bits += '0'
+            bits += "0"
 
 
         # translation (no flag here)
         # translation (no flag here)
         if trans_xy:
         if trans_xy:
@@ -479,6 +483,7 @@ class Tag:
 
 
 ## Control tags
 ## Control tags
 
 
+
 class ControlTag(Tag):
 class ControlTag(Tag):
     def __init__(self):
     def __init__(self):
         Tag.__init__(self)
         Tag.__init__(self)
@@ -490,7 +495,7 @@ class FileAttributesTag(ControlTag):
         self.tagtype = 69
         self.tagtype = 69
 
 
     def ProcessTag(self):
     def ProcessTag(self):
-        self.bytes = '\x00'.encode('ascii') * (1+3)
+        self.bytes = "\x00".encode("ascii") * (1 + 3)
 
 
 
 
 class ShowFrameTag(ControlTag):
 class ShowFrameTag(ControlTag):
@@ -522,7 +527,7 @@ class SetBackgroundTag(ControlTag):
 
 
 
 
 class DoActionTag(Tag):
 class DoActionTag(Tag):
-    def __init__(self, action='stop'):
+    def __init__(self, action="stop"):
         Tag.__init__(self)
         Tag.__init__(self)
         self.tagtype = 12
         self.tagtype = 12
         self.actions = [action]
         self.actions = [action]
@@ -535,10 +540,10 @@ class DoActionTag(Tag):
 
 
         for action in self.actions:
         for action in self.actions:
             action = action.lower()
             action = action.lower()
-            if action == 'stop':
-                bb += '\x07'.encode('ascii')
-            elif action == 'play':
-                bb += '\x06'.encode('ascii')
+            if action == "stop":
+                bb += "\x07".encode("ascii")
+            elif action == "play":
+                bb += "\x06".encode("ascii")
             else:
             else:
                 print("warning, unknown action: %s" % action)
                 print("warning, unknown action: %s" % action)
 
 
@@ -557,7 +562,6 @@ class DefinitionTag(Tag):
 
 
 
 
 class BitmapTag(DefinitionTag):
 class BitmapTag(DefinitionTag):
-
     def __init__(self, im):
     def __init__(self, im):
         DefinitionTag.__init__(self)
         DefinitionTag.__init__(self)
         self.tagtype = 36  # DefineBitsLossless2
         self.tagtype = 36  # DefineBitsLossless2
@@ -570,8 +574,7 @@ class BitmapTag(DefinitionTag):
 
 
         if len(im.shape) == 3:
         if len(im.shape) == 3:
             if im.shape[2] in [3, 4]:
             if im.shape[2] in [3, 4]:
-                tmp = np.ones((im.shape[0], im.shape[1], 4),
-                              dtype=np.uint8) * 255
+                tmp = np.ones((im.shape[0], im.shape[1], 4), dtype=np.uint8) * 255
                 for i in range(3):
                 for i in range(3):
                     tmp[:, :, i + 1] = im[:, :, i]
                     tmp[:, :, i + 1] = im[:, :, i]
                 if im.shape[2] == 4:
                 if im.shape[2] == 4:
@@ -580,7 +583,7 @@ class BitmapTag(DefinitionTag):
                 raise ValueError("Invalid shape to be an image.")
                 raise ValueError("Invalid shape to be an image.")
 
 
         elif len(im.shape) == 2:
         elif len(im.shape) == 2:
-            tmp = np.ones((im.shape[0], im.shape[1], 4), dtype=np.uint8)*255
+            tmp = np.ones((im.shape[0], im.shape[1], 4), dtype=np.uint8) * 255
             for i in range(3):
             for i in range(3):
                 tmp[:, :, i + 1] = im[:, :]
                 tmp[:, :, i + 1] = im[:, :]
         else:
         else:
@@ -595,11 +598,11 @@ class BitmapTag(DefinitionTag):
 
 
         # build tag
         # build tag
         bb = binary_type()
         bb = binary_type()
-        bb += intToUint16(self.id)   # CharacterID
-        bb += intToUint8(5)     # BitmapFormat
-        bb += intToUint16(self.imshape[1])   # BitmapWidth
-        bb += intToUint16(self.imshape[0])   # BitmapHeight
-        bb += self._data            # ZlibBitmapData
+        bb += intToUint16(self.id)  # CharacterID
+        bb += intToUint8(5)  # BitmapFormat
+        bb += intToUint16(self.imshape[1])  # BitmapWidth
+        bb += intToUint16(self.imshape[0])  # BitmapHeight
+        bb += self._data  # ZlibBitmapData
 
 
         self.bytes = bb
         self.bytes = bb
 
 
@@ -622,9 +625,9 @@ class PlaceObjectTag(ControlTag):
         # build PlaceObject2
         # build PlaceObject2
         bb = binary_type()
         bb = binary_type()
         if self.move:
         if self.move:
-            bb += '\x07'.encode('ascii')
+            bb += "\x07".encode("ascii")
         else:
         else:
-            bb += '\x06'.encode('ascii')  # (8 bit flags): 4:matrix, 2:character, 1:move
+            bb += "\x06".encode("ascii")  # (8 bit flags): 4:matrix, 2:character, 1:move
         bb += intToUint16(depth)  # Depth
         bb += intToUint16(depth)  # Depth
         bb += intToUint16(id)  # character id
         bb += intToUint16(id)  # character id
         bb += self.MakeMatrixRecord(trans_xy=xy).ToBytes()  # MATRIX record
         bb += self.MakeMatrixRecord(trans_xy=xy).ToBytes()  # MATRIX record
@@ -652,32 +655,33 @@ class ShapeTag(DefinitionTag):
 
 
         # first entry: FILLSTYLEARRAY with in it a single fill style
         # first entry: FILLSTYLEARRAY with in it a single fill style
         bb += intToUint8(1)  # FillStyleCount
         bb += intToUint8(1)  # FillStyleCount
-        bb += '\x41'.encode('ascii')  # FillStyleType  (0x41 or 0x43, latter is non-smoothed)
+        bb += "\x41".encode(
+            "ascii"
+        )  # FillStyleType  (0x41 or 0x43, latter is non-smoothed)
         bb += intToUint16(self.bitmapId)  # BitmapId
         bb += intToUint16(self.bitmapId)  # BitmapId
-        #bb += '\x00' # BitmapMatrix (empty matrix with leftover bits filled)
+        # bb += '\x00' # BitmapMatrix (empty matrix with leftover bits filled)
         bb += self.MakeMatrixRecord(scale_xy=(20, 20)).ToBytes()
         bb += self.MakeMatrixRecord(scale_xy=(20, 20)).ToBytes()
 
 
-#         # first entry: FILLSTYLEARRAY with in it a single fill style
-#         bb += intToUint8(1)  # FillStyleCount
-#         bb += '\x00' # solid fill
-#         bb += '\x00\x00\xff' # color
+        #         # first entry: FILLSTYLEARRAY with in it a single fill style
+        #         bb += intToUint8(1)  # FillStyleCount
+        #         bb += '\x00' # solid fill
+        #         bb += '\x00\x00\xff' # color
 
 
         # second entry: LINESTYLEARRAY with a single line style
         # second entry: LINESTYLEARRAY with a single line style
         bb += intToUint8(0)  # LineStyleCount
         bb += intToUint8(0)  # LineStyleCount
-        #bb += intToUint16(0*20) # Width
-        #bb += '\x00\xff\x00'  # Color
+        # bb += intToUint16(0*20) # Width
+        # bb += '\x00\xff\x00'  # Color
 
 
         # third and fourth entry: NumFillBits and NumLineBits (4 bits each)
         # third and fourth entry: NumFillBits and NumLineBits (4 bits each)
         # I each give them four bits, so 16 styles possible.
         # I each give them four bits, so 16 styles possible.
-        bb += '\x44'.encode('ascii')
+        bb += "\x44".encode("ascii")
 
 
         self.bytes = bb
         self.bytes = bb
 
 
         # last entries: SHAPERECORDs ... (individual shape records not aligned)
         # last entries: SHAPERECORDs ... (individual shape records not aligned)
         # STYLECHANGERECORD
         # STYLECHANGERECORD
         bits = BitArray()
         bits = BitArray()
-        bits += self.MakeStyleChangeRecord(0, 1, moveTo=(self.wh[0],
-                                                         self.wh[1]))
+        bits += self.MakeStyleChangeRecord(0, 1, moveTo=(self.wh[0], self.wh[1]))
         # STRAIGHTEDGERECORD 4x
         # STRAIGHTEDGERECORD 4x
         bits += self.MakeStraightEdgeRecord(-self.wh[0], 0)
         bits += self.MakeStraightEdgeRecord(-self.wh[0], 0)
         bits += self.MakeStraightEdgeRecord(0, -self.wh[1])
         bits += self.MakeStraightEdgeRecord(0, -self.wh[1])
@@ -690,31 +694,30 @@ class ShapeTag(DefinitionTag):
         self.bytes += bits.ToBytes()
         self.bytes += bits.ToBytes()
 
 
         # done
         # done
-        #self.bytes = bb
+        # self.bytes = bb
 
 
-    def MakeStyleChangeRecord(self, lineStyle=None, fillStyle=None,
-                              moveTo=None):
+    def MakeStyleChangeRecord(self, lineStyle=None, fillStyle=None, moveTo=None):
 
 
         # first 6 flags
         # first 6 flags
         # Note that we use FillStyle1. If we don't flash (at least 8) does not
         # Note that we use FillStyle1. If we don't flash (at least 8) does not
         # recognize the frames properly when importing to library.
         # recognize the frames properly when importing to library.
 
 
         bits = BitArray()
         bits = BitArray()
-        bits += '0'  # TypeFlag (not an edge record)
-        bits += '0'  # StateNewStyles (only for DefineShape2 and Defineshape3)
+        bits += "0"  # TypeFlag (not an edge record)
+        bits += "0"  # StateNewStyles (only for DefineShape2 and Defineshape3)
         if lineStyle:
         if lineStyle:
-            bits += '1'  # StateLineStyle
+            bits += "1"  # StateLineStyle
         else:
         else:
-            bits += '0'
+            bits += "0"
         if fillStyle:
         if fillStyle:
-            bits += '1'  # StateFillStyle1
+            bits += "1"  # StateFillStyle1
         else:
         else:
-            bits += '0'
-        bits += '0'  # StateFillStyle0
+            bits += "0"
+        bits += "0"  # StateFillStyle0
         if moveTo:
         if moveTo:
-            bits += '1'  # StateMoveTo
+            bits += "1"  # StateMoveTo
         else:
         else:
-            bits += '0'
+            bits += "0"
 
 
         # give information
         # give information
         # todo: nbits for fillStyle and lineStyle is hard coded.
         # todo: nbits for fillStyle and lineStyle is hard coded.
@@ -727,7 +730,7 @@ class ShapeTag(DefinitionTag):
             bits += intToBits(lineStyle, 4)
             bits += intToBits(lineStyle, 4)
 
 
         return bits
         return bits
-        #return bitsToBytes(bits)
+        # return bitsToBytes(bits)
 
 
     def MakeStraightEdgeRecord(self, *dxdy):
     def MakeStraightEdgeRecord(self, *dxdy):
         if len(dxdy) == 1:
         if len(dxdy) == 1:
@@ -739,23 +742,23 @@ class ShapeTag(DefinitionTag):
         nbits = max([len(xbits), len(ybits)])
         nbits = max([len(xbits), len(ybits)])
 
 
         bits = BitArray()
         bits = BitArray()
-        bits += '11'  # TypeFlag and StraightFlag
-        bits += intToBits(nbits-2, 4)
-        bits += '1'  # GeneralLineFlag
+        bits += "11"  # TypeFlag and StraightFlag
+        bits += intToBits(nbits - 2, 4)
+        bits += "1"  # GeneralLineFlag
         bits += signedIntToBits(dxdy[0] * 20, nbits)
         bits += signedIntToBits(dxdy[0] * 20, nbits)
         bits += signedIntToBits(dxdy[1] * 20, nbits)
         bits += signedIntToBits(dxdy[1] * 20, nbits)
 
 
         # note: I do not make use of vertical/horizontal only lines...
         # note: I do not make use of vertical/horizontal only lines...
 
 
         return bits
         return bits
-        #return bitsToBytes(bits)
+        # return bitsToBytes(bits)
 
 
     def MakeEndShapeRecord(self):
     def MakeEndShapeRecord(self):
         bits = BitArray()
         bits = BitArray()
-        bits += "0"     # TypeFlag: no edge
-        bits += "0"*5   # EndOfShape
+        bits += "0"  # TypeFlag: no edge
+        bits += "0" * 5  # EndOfShape
         return bits
         return bits
-        #return bitsToBytes(bits)
+        # return bitsToBytes(bits)
 
 
 
 
 ## Last few functions
 ## Last few functions
@@ -764,10 +767,10 @@ def buildFile(fp, taglist, nframes=1, framesize=(500, 500), fps=10, version=8):
 
 
     # compose header
     # compose header
     bb = binary_type()
     bb = binary_type()
-    bb += 'F'.encode('ascii')  # uncompressed
-    bb += 'WS'.encode('ascii')  # signature bytes
+    bb += "F".encode("ascii")  # uncompressed
+    bb += "WS".encode("ascii")  # signature bytes
     bb += intToUint8(version)  # version
     bb += intToUint8(version)  # version
-    bb += '0000'.encode('ascii')  # FileLength (leave open for now)
+    bb += "0000".encode("ascii")  # FileLength (leave open for now)
     bb += Tag().MakeRectRecord(0, framesize[0], 0, framesize[1]).ToBytes()
     bb += Tag().MakeRectRecord(0, framesize[0], 0, framesize[1]).ToBytes()
     bb += intToUint8(0) + intToUint8(fps)  # FrameRate
     bb += intToUint8(0) + intToUint8(fps)  # FrameRate
     bb += intToUint16(nframes)
     bb += intToUint16(nframes)
@@ -778,7 +781,7 @@ def buildFile(fp, taglist, nframes=1, framesize=(500, 500), fps=10, version=8):
         fp.write(tag.GetTag())
         fp.write(tag.GetTag())
 
 
     # finish with end tag
     # finish with end tag
-    fp.write('\x00\x00'.encode('ascii'))
+    fp.write("\x00\x00".encode("ascii"))
 
 
     # set size
     # set size
     sze = fp.tell()
     sze = fp.tell()
@@ -809,7 +812,7 @@ def writeSwf(filename, images, duration=0.1, repeat=True):
         raise ValueError("Image list is empty!")
         raise ValueError("Image list is empty!")
     for im in images:
     for im in images:
         if PIL and isinstance(im, PIL.Image.Image):
         if PIL and isinstance(im, PIL.Image.Image):
-            if im.mode == 'P':
+            if im.mode == "P":
                 im = im.convert()
                 im = im.convert()
             im = np.asarray(im)
             im = np.asarray(im)
             if len(im.shape) == 0:
             if len(im.shape) == 0:
@@ -820,7 +823,7 @@ def writeSwf(filename, images, duration=0.1, repeat=True):
     taglist = [FileAttributesTag(), SetBackgroundTag(0, 0, 0)]
     taglist = [FileAttributesTag(), SetBackgroundTag(0, 0, 0)]
 
 
     # Check duration
     # Check duration
-    if hasattr(duration, '__len__'):
+    if hasattr(duration, "__len__"):
         if len(duration) == len(images2):
         if len(duration) == len(images2):
             duration = [d for d in duration]
             duration = [d for d in duration]
         else:
         else:
@@ -830,11 +833,11 @@ def writeSwf(filename, images, duration=0.1, repeat=True):
 
 
     # Build delays list
     # Build delays list
     minDuration = float(min(duration))
     minDuration = float(min(duration))
-    delays = [round(d/minDuration) for d in duration]
+    delays = [round(d / minDuration) for d in duration]
     delays = [max(1, int(d)) for d in delays]
     delays = [max(1, int(d)) for d in delays]
 
 
     # Get FPS
     # Get FPS
-    fps = 1.0/minDuration
+    fps = 1.0 / minDuration
 
 
     # Produce series of tags for each image
     # Produce series of tags for each image
     nframes = 0
     nframes = 0
@@ -849,10 +852,10 @@ def writeSwf(filename, images, duration=0.1, repeat=True):
         nframes += 1
         nframes += 1
 
 
     if not repeat:
     if not repeat:
-        taglist.append(DoActionTag('stop'))
+        taglist.append(DoActionTag("stop"))
 
 
     # Build file
     # Build file
-    fp = open(filename, 'wb')
+    fp = open(filename, "wb")
     try:
     try:
         buildFile(fp, taglist, nframes=nframes, framesize=wh, fps=fps)
         buildFile(fp, taglist, nframes=nframes, framesize=wh, fps=fps)
     except Exception:
     except Exception:
@@ -862,21 +865,20 @@ def writeSwf(filename, images, duration=0.1, repeat=True):
 
 
 
 
 def _readPixels(bb, i, tagType, L1):
 def _readPixels(bb, i, tagType, L1):
-    """ With pf's seed after the recordheader, reads the pixeldata.
-    """
+    """With pf's seed after the recordheader, reads the pixeldata."""
 
 
     # Check Numpy
     # Check Numpy
     if np is None:
     if np is None:
         raise RuntimeError("Need Numpy to read an SWF file.")
         raise RuntimeError("Need Numpy to read an SWF file.")
 
 
     # Get info
     # Get info
-    charId = bb[i:i + 2]
+    charId = bb[i : i + 2]
     i += 2
     i += 2
-    format = ord(bb[i:i + 1])
+    format = ord(bb[i : i + 1])
     i += 1
     i += 1
-    width = bitsToInt(bb[i:i + 2], 16)
+    width = bitsToInt(bb[i : i + 2], 16)
     i += 2
     i += 2
-    height = bitsToInt(bb[i:i + 2], 16)
+    height = bitsToInt(bb[i : i + 2], 16)
     i += 2
     i += 2
 
 
     # If we can, get pixeldata and make nunmpy array
     # If we can, get pixeldata and make nunmpy array
@@ -885,7 +887,7 @@ def _readPixels(bb, i, tagType, L1):
     else:
     else:
         # Read byte data
         # Read byte data
         offset = 2 + 1 + 2 + 2  # all the info bits
         offset = 2 + 1 + 2 + 2  # all the info bits
-        bb2 = bb[i:i+(L1-offset)]
+        bb2 = bb[i : i + (L1 - offset)]
 
 
         # Decompress and make numpy array
         # Decompress and make numpy array
         data = zlib.decompress(bb2)
         data = zlib.decompress(bb2)
@@ -923,7 +925,7 @@ def readSwf(filename, asNumpy=True):
 
 
     # Check whether it exists
     # Check whether it exists
     if not os.path.isfile(filename):
     if not os.path.isfile(filename):
-        raise IOError('File not found: '+str(filename))
+        raise IOError("File not found: " + str(filename))
 
 
     # Check PIL
     # Check PIL
     if (not asNumpy) and (PIL is None):
     if (not asNumpy) and (PIL is None):
@@ -937,29 +939,29 @@ def readSwf(filename, asNumpy=True):
     images = []
     images = []
 
 
     # Open file and read all
     # Open file and read all
-    fp = open(filename, 'rb')
+    fp = open(filename, "rb")
     bb = fp.read()
     bb = fp.read()
 
 
     try:
     try:
         # Check opening tag
         # Check opening tag
-        tmp = bb[0:3].decode('ascii', 'ignore')
-        if tmp.upper() == 'FWS':
+        tmp = bb[0:3].decode("ascii", "ignore")
+        if tmp.upper() == "FWS":
             pass  # ok
             pass  # ok
-        elif tmp.upper() == 'CWS':
+        elif tmp.upper() == "CWS":
             # Decompress movie
             # Decompress movie
             bb = bb[:8] + zlib.decompress(bb[8:])
             bb = bb[:8] + zlib.decompress(bb[8:])
         else:
         else:
-            raise IOError('Not a valid SWF file: ' + str(filename))
+            raise IOError("Not a valid SWF file: " + str(filename))
 
 
         # Set filepointer at first tag (skipping framesize RECT and two uin16's
         # Set filepointer at first tag (skipping framesize RECT and two uin16's
         i = 8
         i = 8
-        nbits = bitsToInt(bb[i: i + 1], 5)  # skip FrameSize
+        nbits = bitsToInt(bb[i : i + 1], 5)  # skip FrameSize
         nbits = 5 + nbits * 4
         nbits = 5 + nbits * 4
         Lrect = nbits / 8.0
         Lrect = nbits / 8.0
         if Lrect % 1:
         if Lrect % 1:
             Lrect += 1
             Lrect += 1
         Lrect = int(Lrect)
         Lrect = int(Lrect)
-        i += Lrect+4
+        i += Lrect + 4
 
 
         # Iterate over the tags
         # Iterate over the tags
         counter = 0
         counter = 0
@@ -967,24 +969,24 @@ def readSwf(filename, asNumpy=True):
             counter += 1
             counter += 1
 
 
             # Get tag header
             # Get tag header
-            head = bb[i:i+6]
+            head = bb[i : i + 6]
             if not head:
             if not head:
                 break  # Done (we missed end tag)
                 break  # Done (we missed end tag)
 
 
             # Determine type and length
             # Determine type and length
             T, L1, L2 = getTypeAndLen(head)
             T, L1, L2 = getTypeAndLen(head)
             if not L2:
             if not L2:
-                print('Invalid tag length, could not proceed')
+                print("Invalid tag length, could not proceed")
                 break
                 break
-            #print(T, L2)
+            # print(T, L2)
 
 
             # Read image if we can
             # Read image if we can
             if T in [20, 36]:
             if T in [20, 36]:
-                im = _readPixels(bb, i+6, T, L1)
+                im = _readPixels(bb, i + 6, T, L1)
                 if im is not None:
                 if im is not None:
                     images.append(im)
                     images.append(im)
             elif T in [6, 21, 35, 90]:
             elif T in [6, 21, 35, 90]:
-                print('Ignoring JPEG image: cannot read JPEG.')
+                print("Ignoring JPEG image: cannot read JPEG.")
             else:
             else:
                 pass  # Not an image tag
                 pass  # Not an image tag
 
 

+ 16 - 11
python/grass/imaging/operations.py

@@ -58,6 +58,7 @@ for details.
 try:
 try:
     import PIL
     import PIL
     from PIL import Image
     from PIL import Image
+
     try:
     try:
         import PIL.ImageOps as ImageOps
         import PIL.ImageOps as ImageOps
     except ImportError:
     except ImportError:
@@ -83,8 +84,7 @@ def crop_image(input_file, output_file=None, format=None):
     cropped_image.save(output_file, format)
     cropped_image.save(output_file, format)
 
 
 
 
-def thumbnail_image(input_file, output_file=None, size=(200, 200),
-                    format=None):
+def thumbnail_image(input_file, output_file=None, size=(200, 200), format=None):
     """Create a thumbnail of an image
     """Create a thumbnail of an image
 
 
     The image aspect ratio is kept and its height and width are adjusted
     The image aspect ratio is kept and its height and width are adjusted
@@ -104,8 +104,9 @@ def thumbnail_image(input_file, output_file=None, size=(200, 200),
     img.save(output_file, format)
     img.save(output_file, format)
 
 
 
 
-def change_rbg_to_transparent(input_file, output_file=None, color='white',
-                              alpha=0, format=None):
+def change_rbg_to_transparent(
+    input_file, output_file=None, color="white", alpha=0, format=None
+):
     """Make a specified RGB color in the image transparent
     """Make a specified RGB color in the image transparent
 
 
     The color is specified as a RGB tuple (triplet) or string 'white'
     The color is specified as a RGB tuple (triplet) or string 'white'
@@ -121,9 +122,9 @@ def change_rbg_to_transparent(input_file, output_file=None, color='white',
     """
     """
     if PIL is None:
     if PIL is None:
         raise RuntimeError(_("Install PIL or Pillow to use this function"))
         raise RuntimeError(_("Install PIL or Pillow to use this function"))
-    if color == 'white':
+    if color == "white":
         rgb = (255, 255, 255)
         rgb = (255, 255, 255)
-    elif color == 'black':
+    elif color == "black":
         rgb = (0, 0, 0)
         rgb = (0, 0, 0)
     else:
     else:
         rgb = color  # pylint: disable=redefined-variable-type
         rgb = color  # pylint: disable=redefined-variable-type
@@ -154,22 +155,26 @@ def invert_image_colors(input_file, output_file=None, format=None):
     if PIL is None:
     if PIL is None:
         raise RuntimeError(_("Install PIL or Pillow to use this function"))
         raise RuntimeError(_("Install PIL or Pillow to use this function"))
     if ImageOps is None:
     if ImageOps is None:
-        raise RuntimeError(_("Install a newer version of PIL or Pillow to"
-                             " use this function (missing ImageOps module)"))
+        raise RuntimeError(
+            _(
+                "Install a newer version of PIL or Pillow to"
+                " use this function (missing ImageOps module)"
+            )
+        )
     if not output_file:
     if not output_file:
         output_file = input_file
         output_file = input_file
     original_img = Image.open(input_file)
     original_img = Image.open(input_file)
     # according to documentation (3.0.x) the module can work only on RGB
     # according to documentation (3.0.x) the module can work only on RGB
     # so we need to specifically take care of transparency if present
     # so we need to specifically take care of transparency if present
-    if original_img.mode == 'RGBA':
+    if original_img.mode == "RGBA":
         # split into bands
         # split into bands
         red1, green1, blue1, alpha = original_img.split()
         red1, green1, blue1, alpha = original_img.split()
-        rgb_img = Image.merge('RGB', (red1, green1, blue1))
+        rgb_img = Image.merge("RGB", (red1, green1, blue1))
         # invert RGB
         # invert RGB
         inverted_rgb_img = ImageOps.invert(rgb_img)
         inverted_rgb_img = ImageOps.invert(rgb_img)
         # put back the original alpha
         # put back the original alpha
         red2, green2, blue2 = inverted_rgb_img.split()
         red2, green2, blue2 = inverted_rgb_img.split()
-        new_image = Image.merge('RGBA', (red2, green2, blue2, alpha))
+        new_image = Image.merge("RGBA", (red2, green2, blue2, alpha))
     else:
     else:
         new_image = ImageOps.invert(original_img)
         new_image = ImageOps.invert(original_img)
     new_image.save(output_file, format)
     new_image.save(output_file, format)

+ 18 - 26
python/grass/pydispatch/dispatcher.py

@@ -49,6 +49,8 @@ class _Any(_Parameter):
     Any should react to all senders/signals, not just
     Any should react to all senders/signals, not just
     a particular sender/signal.
     a particular sender/signal.
     """
     """
+
+
 Any = _Any()
 Any = _Any()
 
 
 
 
@@ -70,6 +72,8 @@ class _Anonymous(_Parameter):
         as though there was a single sender (Anonymous)
         as though there was a single sender (Anonymous)
         being used everywhere.
         being used everywhere.
     """
     """
+
+
 Anonymous = _Anonymous()
 Anonymous = _Anonymous()
 
 
 WEAKREF_TYPES = (weakref.ReferenceType, saferef.BoundMethodWeakref)
 WEAKREF_TYPES = (weakref.ReferenceType, saferef.BoundMethodWeakref)
@@ -133,8 +137,7 @@ def connect(receiver, signal=Any, sender=Any, weak=True):
     """
     """
     if signal is None:
     if signal is None:
         raise errors.DispatcherTypeError(
         raise errors.DispatcherTypeError(
-            'Signal cannot be None (receiver=%r sender=%r)' % (receiver,
-                                                               sender)
+            "Signal cannot be None (receiver=%r sender=%r)" % (receiver, sender)
         )
         )
     if weak:
     if weak:
         receiver = saferef.safeRef(receiver, onDelete=_removeReceiver)
         receiver = saferef.safeRef(receiver, onDelete=_removeReceiver)
@@ -146,8 +149,10 @@ def connect(receiver, signal=Any, sender=Any, weak=True):
     # Keep track of senders for cleanup.
     # Keep track of senders for cleanup.
     # Is Anonymous something we want to clean up?
     # Is Anonymous something we want to clean up?
     if sender not in (None, Anonymous, Any):
     if sender not in (None, Anonymous, Any):
+
         def remove(object, senderkey=senderkey):
         def remove(object, senderkey=senderkey):
             _removeSender(senderkey=senderkey)
             _removeSender(senderkey=senderkey)
+
         # Skip objects that can not be weakly referenced, which means
         # Skip objects that can not be weakly referenced, which means
         # they won't be automatically cleaned up, but that's too bad.
         # they won't be automatically cleaned up, but that's too bad.
         try:
         try:
@@ -204,31 +209,25 @@ def disconnect(receiver, signal=Any, sender=Any, weak=True):
     """
     """
     if signal is None:
     if signal is None:
         raise errors.DispatcherTypeError(
         raise errors.DispatcherTypeError(
-            'Signal cannot be None (receiver=%r sender=%r)' % (receiver,
-                                                               sender)
+            "Signal cannot be None (receiver=%r sender=%r)" % (receiver, sender)
         )
         )
-    if weak: receiver = saferef.safeRef(receiver)
+    if weak:
+        receiver = saferef.safeRef(receiver)
     senderkey = id(sender)
     senderkey = id(sender)
     try:
     try:
         signals = connections[senderkey]
         signals = connections[senderkey]
         receivers = signals[signal]
         receivers = signals[signal]
     except KeyError:
     except KeyError:
         raise errors.DispatcherKeyError(
         raise errors.DispatcherKeyError(
-            """No receivers found for signal %r from sender %r"""  % (
-                signal,
-                sender
-            )
+            """No receivers found for signal %r from sender %r""" % (signal, sender)
         )
         )
     try:
     try:
         # also removes from receivers
         # also removes from receivers
         _removeOldBackRefs(senderkey, signal, receiver, receivers)
         _removeOldBackRefs(senderkey, signal, receiver, receivers)
     except ValueError:
     except ValueError:
         raise errors.DispatcherKeyError(
         raise errors.DispatcherKeyError(
-            """No connection to receiver %s for signal %s from sender %s"""  % (
-                receiver,
-                signal,
-                sender
-            )
+            """No connection to receiver %s for signal %s from sender %s"""
+            % (receiver, signal, sender)
         )
         )
     _cleanupConnections(senderkey, signal)
     _cleanupConnections(senderkey, signal)
 
 
@@ -343,11 +342,7 @@ def send(signal=Any, sender=Anonymous, *arguments, **named):
     responses = []
     responses = []
     for receiver in liveReceivers(getAllReceivers(sender, signal)):
     for receiver in liveReceivers(getAllReceivers(sender, signal)):
         response = robustapply.robustApply(
         response = robustapply.robustApply(
-            receiver,
-            signal=signal,
-            sender=sender,
-            *arguments,
-            **named
+            receiver, signal=signal, sender=sender, *arguments, **named
         )
         )
         responses.append((receiver, response))
         responses.append((receiver, response))
     return responses
     return responses
@@ -364,11 +359,7 @@ def sendExact(signal=Any, sender=Anonymous, *arguments, **named):
     responses = []
     responses = []
     for receiver in liveReceivers(getReceivers(sender, signal)):
     for receiver in liveReceivers(getReceivers(sender, signal)):
         response = robustapply.robustApply(
         response = robustapply.robustApply(
-            receiver,
-            signal=signal,
-            sender=sender,
-            *arguments,
-            **named
+            receiver, signal=signal, sender=sender, *arguments, **named
         )
         )
         responses.append((receiver, response))
         responses.append((receiver, response))
     return responses
     return responses
@@ -378,8 +369,8 @@ def _removeReceiver(receiver):
     """Remove receiver from connections."""
     """Remove receiver from connections."""
     if not sendersBack or not connections:
     if not sendersBack or not connections:
         # During module cleanup the objects will be replaced with None
         # During module cleanup the objects will be replaced with None
-           # The order of replacing many change, so both variables need
-           # to be checked.
+        # The order of replacing many change, so both variables need
+        # to be checked.
         return False
         return False
     backKey = id(receiver)
     backKey = id(receiver)
     try:
     try:
@@ -454,6 +445,7 @@ def _removeBackrefs(senderkey):
             for signal, set in items:
             for signal, set in items:
                 for item in set:
                 for item in set:
                     yield item
                     yield item
+
         for receiver in allReceivers():
         for receiver in allReceivers():
             _killBackref(receiver, senderkey)
             _killBackref(receiver, senderkey)
 
 

+ 2 - 10
python/grass/pydispatch/robust.py

@@ -3,11 +3,7 @@ from grass.pydispatch.dispatcher import Any, Anonymous, liveReceivers, getAllRec
 from grass.pydispatch.robustapply import robustApply
 from grass.pydispatch.robustapply import robustApply
 
 
 
 
-def sendRobust(
-    signal=Any,
-    sender=Anonymous,
-    *arguments, **named
-):
+def sendRobust(signal=Any, sender=Anonymous, *arguments, **named):
     """Send signal from sender to all connected receivers catching errors
     """Send signal from sender to all connected receivers catching errors
 
 
     signal -- (hashable) signal value, see connect for details
     signal -- (hashable) signal value, see connect for details
@@ -45,11 +41,7 @@ def sendRobust(
     for receiver in liveReceivers(getAllReceivers(sender, signal)):
     for receiver in liveReceivers(getAllReceivers(sender, signal)):
         try:
         try:
             response = robustApply(
             response = robustApply(
-                receiver,
-                signal=signal,
-                sender=sender,
-                *arguments,
-                **named
+                receiver, signal=signal, sender=sender, *arguments, **named
             )
             )
         except Exception as err:
         except Exception as err:
             responses.append((receiver, err))
             responses.append((receiver, err))

+ 17 - 17
python/grass/pydispatch/robustapply.py

@@ -6,16 +6,17 @@ and subset the given arguments to match only
 those which are acceptable.
 those which are acceptable.
 """
 """
 import sys
 import sys
+
 if sys.hexversion >= 0x3000000:
 if sys.hexversion >= 0x3000000:
-    im_func = '__func__'
-    im_self = '__self__'
-    im_code = '__code__'
-    func_code = '__code__'
+    im_func = "__func__"
+    im_self = "__self__"
+    im_code = "__code__"
+    func_code = "__code__"
 else:
 else:
-    im_func = 'im_func'
-    im_self = 'im_self'
-    im_code = 'im_code'
-    func_code = 'func_code'
+    im_func = "im_func"
+    im_self = "im_self"
+    im_code = "im_code"
+    func_code = "func_code"
 
 
 
 
 def function(receiver):
 def function(receiver):
@@ -26,26 +27,25 @@ def function(receiver):
     If fromMethod is true, then the callable already
     If fromMethod is true, then the callable already
     has its first argument bound
     has its first argument bound
     """
     """
-    if hasattr(receiver, '__call__'):
+    if hasattr(receiver, "__call__"):
         # Reassign receiver to the actual method that will be called.
         # Reassign receiver to the actual method that will be called.
-        if hasattr(receiver.__call__, im_func) or hasattr(receiver.__call__,
-                                                          im_code):
+        if hasattr(receiver.__call__, im_func) or hasattr(receiver.__call__, im_code):
             receiver = receiver.__call__
             receiver = receiver.__call__
     if hasattr(receiver, im_func):
     if hasattr(receiver, im_func):
         # an instance-method...
         # an instance-method...
         return receiver, getattr(getattr(receiver, im_func), func_code), 1
         return receiver, getattr(getattr(receiver, im_func), func_code), 1
     elif not hasattr(receiver, func_code):
     elif not hasattr(receiver, func_code):
-        raise ValueError('unknown receiver type %s %s' % (receiver,
-                                                          type(receiver)))
+        raise ValueError("unknown receiver type %s %s" % (receiver, type(receiver)))
     return receiver, getattr(receiver, func_code), 0
     return receiver, getattr(receiver, func_code), 0
 
 
 
 
 def robustApply(receiver, *arguments, **named):
 def robustApply(receiver, *arguments, **named):
-    """Call receiver with arguments and an appropriate subset of named
-    """
+    """Call receiver with arguments and an appropriate subset of named"""
     receiver, codeObject, startIndex = function(receiver)
     receiver, codeObject, startIndex = function(receiver)
-    acceptable = codeObject.co_varnames[startIndex+len(arguments):codeObject.co_argcount]
-    for name in codeObject.co_varnames[startIndex:startIndex+len(arguments)]:
+    acceptable = codeObject.co_varnames[
+        startIndex + len(arguments) : codeObject.co_argcount
+    ]
+    for name in codeObject.co_varnames[startIndex : startIndex + len(arguments)]:
         if name in named:
         if name in named:
             raise TypeError(
             raise TypeError(
                 """Argument %r specified both positionally and as a keyword"""
                 """Argument %r specified both positionally and as a keyword"""

+ 20 - 16
python/grass/pydispatch/saferef.py

@@ -6,11 +6,11 @@ import traceback
 import sys
 import sys
 
 
 if sys.hexversion >= 0x3000000:
 if sys.hexversion >= 0x3000000:
-    im_func = '__func__'
-    im_self = '__self__'
+    im_func = "__func__"
+    im_self = "__self__"
 else:
 else:
-    im_func = 'im_func'
-    im_self = 'im_self'
+    im_func = "im_func"
+    im_self = "im_self"
 
 
 
 
 def safeRef(target, onDelete=None):
 def safeRef(target, onDelete=None):
@@ -28,15 +28,12 @@ def safeRef(target, onDelete=None):
         if getattr(target, im_self) is not None:
         if getattr(target, im_self) is not None:
             # Turn a bound method into a BoundMethodWeakref instance.
             # Turn a bound method into a BoundMethodWeakref instance.
             # Keep track of these instances for lookup by disconnect().
             # Keep track of these instances for lookup by disconnect().
-            assert hasattr(target, im_func), """safeRef target %r has %s, """ \
-                                             """but no %s, don't know how """ \
-                                             """to create reference""" % (target,
-                                                                          im_self,
-                                                                          im_func)
-            reference = BoundMethodWeakref(
-                target=target,
-                onDelete=onDelete
+            assert hasattr(target, im_func), (
+                """safeRef target %r has %s, """
+                """but no %s, don't know how """
+                """to create reference""" % (target, im_self, im_func)
             )
             )
+            reference = BoundMethodWeakref(target=target, onDelete=onDelete)
             return reference
             return reference
     if onDelete is not None:
     if onDelete is not None:
         return weakref.ref(target, onDelete)
         return weakref.ref(target, onDelete)
@@ -77,6 +74,7 @@ class BoundMethodWeakref(object):
             same BoundMethodWeakref instance.
             same BoundMethodWeakref instance.
 
 
     """
     """
+
     _allInstances = weakref.WeakValueDictionary()
     _allInstances = weakref.WeakValueDictionary()
 
 
     def __new__(cls, target, onDelete=None, *arguments, **named):
     def __new__(cls, target, onDelete=None, *arguments, **named):
@@ -116,6 +114,7 @@ class BoundMethodWeakref(object):
             collected).  Should take a single argument,
             collected).  Should take a single argument,
             which will be passed a pointer to this object.
             which will be passed a pointer to this object.
         """
         """
+
         def remove(weak, self=self):
         def remove(weak, self=self):
             """Set self.isDead to true when method or instance is destroyed"""
             """Set self.isDead to true when method or instance is destroyed"""
             methods = self.deletionMethods[:]
             methods = self.deletionMethods[:]
@@ -126,15 +125,18 @@ class BoundMethodWeakref(object):
                 pass
                 pass
             for function in methods:
             for function in methods:
                 try:
                 try:
-                    if hasattr(function, '__call__'):
+                    if hasattr(function, "__call__"):
                         function(self)
                         function(self)
                 except Exception as e:
                 except Exception as e:
                     try:
                     try:
                         traceback.print_exc()
                         traceback.print_exc()
                     except AttributeError:
                     except AttributeError:
-                        print('''Exception during saferef %s cleanup '''
-                              '''function %s: %s''' % (self, function, e),
-                              file=sys.stderr)
+                        print(
+                            """Exception during saferef %s cleanup """
+                            """function %s: %s""" % (self, function, e),
+                            file=sys.stderr,
+                        )
+
         self.deletionMethods = [onDelete]
         self.deletionMethods = [onDelete]
         self.key = self.calculateKey(target)
         self.key = self.calculateKey(target)
         self.weakSelf = weakref.ref(getattr(target, im_self), remove)
         self.weakSelf = weakref.ref(getattr(target, im_self), remove)
@@ -149,6 +151,7 @@ class BoundMethodWeakref(object):
         target object and the target function respectively.
         target object and the target function respectively.
         """
         """
         return (id(getattr(target, im_self)), id(getattr(target, im_func)))
         return (id(getattr(target, im_self)), id(getattr(target, im_func)))
+
     calculateKey = classmethod(calculateKey)
     calculateKey = classmethod(calculateKey)
 
 
     def __str__(self):
     def __str__(self):
@@ -158,6 +161,7 @@ class BoundMethodWeakref(object):
             self.selfName,
             self.selfName,
             self.funcName,
             self.funcName,
         )
         )
+
     __repr__ = __str__
     __repr__ = __str__
 
 
     def __nonzero__(self):
     def __nonzero__(self):

+ 9 - 4
python/grass/pydispatch/signal.py

@@ -22,7 +22,10 @@ def _islambda(function):
     >>> _islambda(_islambda)
     >>> _islambda(_islambda)
     False
     False
     """
     """
-    return isinstance(function, type(lambda: None)) and function.__name__ == (lambda: None).__name__
+    return (
+        isinstance(function, type(lambda: None))
+        and function.__name__ == (lambda: None).__name__
+    )
 
 
 
 
 class Signal(object):
 class Signal(object):
@@ -107,6 +110,7 @@ class Signal(object):
     >>> signal2.emit(text='Hello')
     >>> signal2.emit(text='Hello')
     lambda handler: Hello
     lambda handler: Hello
     """
     """
+
     # TODO: use the name for debugging
     # TODO: use the name for debugging
 
 
     def __init__(self, name):
     def __init__(self, name):
@@ -267,11 +271,12 @@ class Signal(object):
         Traceback (most recent call last):
         Traceback (most recent call last):
         TypeError: mywrite() takes exactly 1 argument (0 given)
         TypeError: mywrite() takes exactly 1 argument (0 given)
         """
         """
-        if 'signal' in kwargs:
-            del kwargs['signal']
+        if "signal" in kwargs:
+            del kwargs["signal"]
         self.emit(*args, **kwargs)
         self.emit(*args, **kwargs)
 
 
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     import doctest
     import doctest
+
     doctest.testmod()
     doctest.testmod()

+ 9 - 7
python/grass/pygrass/errors.py

@@ -8,7 +8,6 @@ import grass.lib.gis as libgis
 
 
 
 
 def must_be_open(method):
 def must_be_open(method):
-
     @wraps(method)
     @wraps(method)
     def wrapper(self, *args, **kargs):
     def wrapper(self, *args, **kargs):
         if self.is_open():
         if self.is_open():
@@ -16,28 +15,31 @@ def must_be_open(method):
         else:
         else:
             msgr = get_msgr()
             msgr = get_msgr()
             msgr.warning(_("The map is close!"))
             msgr.warning(_("The map is close!"))
+
     return wrapper
     return wrapper
 
 
 
 
 def mapinfo_must_be_set(method):
 def mapinfo_must_be_set(method):
-
     @wraps(method)
     @wraps(method)
     def wrapper(self, *args, **kargs):
     def wrapper(self, *args, **kargs):
         if self.c_mapinfo:
         if self.c_mapinfo:
             return method(self, *args, **kargs)
             return method(self, *args, **kargs)
         else:
         else:
-            raise GrassError(_("The self.c_mapinfo pointer must be "
-                                 "correctly initiated"))
+            raise GrassError(
+                _("The self.c_mapinfo pointer must be " "correctly initiated")
+            )
+
     return wrapper
     return wrapper
 
 
-def must_be_in_current_mapset(method):
 
 
+def must_be_in_current_mapset(method):
     @wraps(method)
     @wraps(method)
     def wrapper(self, *args, **kargs):
     def wrapper(self, *args, **kargs):
         if self.mapset == libgis.G_mapset().decode():
         if self.mapset == libgis.G_mapset().decode():
             return method(self, *args, **kargs)
             return method(self, *args, **kargs)
         else:
         else:
-            raise GrassError(_("Map <{}> not found in current mapset").format(
-                self.name))
+            raise GrassError(
+                _("Map <{}> not found in current mapset").format(self.name)
+            )
 
 
     return wrapper
     return wrapper

+ 98 - 70
python/grass/pygrass/gis/__init__.py

@@ -1,8 +1,15 @@
 #!/usr/bin/env python3
 #!/usr/bin/env python3
 # -*- coding: utf-8 -*-
 # -*- coding: utf-8 -*-
 
 
-from __future__ import (nested_scopes, generators, division, absolute_import,
-                        with_statement, print_function, unicode_literals)
+from __future__ import (
+    nested_scopes,
+    generators,
+    division,
+    absolute_import,
+    with_statement,
+    print_function,
+    unicode_literals,
+)
 from os import listdir
 from os import listdir
 from os.path import join, isdir
 from os.path import join, isdir
 import shutil
 import shutil
@@ -19,21 +26,25 @@ from grass.pygrass.gis.region import Region
 test_vector_name = "Gis_test_vector"
 test_vector_name = "Gis_test_vector"
 test_raster_name = "Gis_test_raster"
 test_raster_name = "Gis_test_raster"
 
 
-libgis.G_gisinit('')
+libgis.G_gisinit("")
 
 
 
 
-ETYPE = {'raster': libgis.G_ELEMENT_RASTER,
-         'raster_3d': libgis.G_ELEMENT_RASTER3D,
-         'vector': libgis.G_ELEMENT_VECTOR,
-         'label': libgis.G_ELEMENT_LABEL,
-         'region': libgis.G_ELEMENT_REGION,
-         'group': libgis.G_ELEMENT_GROUP}
+ETYPE = {
+    "raster": libgis.G_ELEMENT_RASTER,
+    "raster_3d": libgis.G_ELEMENT_RASTER3D,
+    "vector": libgis.G_ELEMENT_VECTOR,
+    "label": libgis.G_ELEMENT_LABEL,
+    "region": libgis.G_ELEMENT_REGION,
+    "group": libgis.G_ELEMENT_GROUP,
+}
 
 
 
 
-CHECK_IS = {"GISBASE": libgis.G_is_gisbase,
-            "GISDBASE": lambda x: True,
-            "LOCATION_NAME": libgis.G_is_location,
-            "MAPSET": libgis.G_is_mapset}
+CHECK_IS = {
+    "GISBASE": libgis.G_is_gisbase,
+    "GISDBASE": lambda x: True,
+    "LOCATION_NAME": libgis.G_is_location,
+    "MAPSET": libgis.G_is_mapset,
+}
 
 
 
 
 def is_valid(value, path, type):
 def is_valid(value, path, type):
@@ -72,8 +83,9 @@ def _check_raise(value, path, type):
              if value is empty return environmental variable
              if value is empty return environmental variable
     :rtype: str
     :rtype: str
     """
     """
-    if value == '':
+    if value == "":
         from grass.pygrass.utils import getenv
         from grass.pygrass.utils import getenv
+
         return getenv(type)
         return getenv(type)
     if is_valid(value, path, type):
     if is_valid(value, path, type):
         return value
         return value
@@ -92,11 +104,11 @@ def set_current_mapset(mapset, location=None, gisdbase=None):
     :param gisdbase: Name of the gisdbase
     :param gisdbase: Name of the gisdbase
     :type gisdbase: str
     :type gisdbase: str
     """
     """
-    libgis.G_setenv('MAPSET', mapset)
+    libgis.G_setenv("MAPSET", mapset)
     if location:
     if location:
-        libgis.G_setenv('LOCATION_NAME', location)
+        libgis.G_setenv("LOCATION_NAME", location)
     if gisdbase:
     if gisdbase:
-        libgis.G_setenv('GISDBASE', gisdbase)
+        libgis.G_setenv("GISDBASE", gisdbase)
 
 
 
 
 def make_mapset(mapset, location=None, gisdbase=None):
 def make_mapset(mapset, location=None, gisdbase=None):
@@ -128,23 +140,24 @@ class Gisdbase(object):
     ..
     ..
     """
     """
 
 
-    def __init__(self, gisdbase=''):
+    def __init__(self, gisdbase=""):
         self.name = gisdbase
         self.name = gisdbase
 
 
     def _get_name(self):
     def _get_name(self):
         return self._name
         return self._name
 
 
     def _set_name(self, name):
     def _set_name(self, name):
-        self._name = _check_raise(name, '', "GISDBASE")
+        self._name = _check_raise(name, "", "GISDBASE")
 
 
-    name = property(fget=_get_name, fset=_set_name,
-                    doc="Set or obtain the name of GISDBASE")
+    name = property(
+        fget=_get_name, fset=_set_name, doc="Set or obtain the name of GISDBASE"
+    )
 
 
     def __str__(self):
     def __str__(self):
         return self.name
         return self.name
 
 
     def __repr__(self):
     def __repr__(self):
-        return 'Gisdbase(%s)' % self.name
+        return "Gisdbase(%s)" % self.name
 
 
     def __getitem__(self, location):
     def __getitem__(self, location):
         """Return a Location object. ::
         """Return a Location object. ::
@@ -161,7 +174,7 @@ class Gisdbase(object):
         if location in self.locations():
         if location in self.locations():
             return Location(location, self.name)
             return Location(location, self.name)
         else:
         else:
-            raise KeyError('Location: %s does not exist' % location)
+            raise KeyError("Location: %s does not exist" % location)
 
 
     def __iter__(self):
     def __iter__(self):
         for loc in self.locations():
         for loc in self.locations():
@@ -181,8 +194,13 @@ class Gisdbase(object):
 
 
         ..
         ..
         """
         """
-        return sorted([loc for loc in listdir(self.name)
-                       if libgis.G_is_location(encode(join(self.name, loc)))])
+        return sorted(
+            [
+                loc
+                for loc in listdir(self.name)
+                if libgis.G_is_location(encode(join(self.name, loc)))
+            ]
+        )
 
 
 
 
 class Location(object):
 class Location(object):
@@ -200,7 +218,7 @@ class Location(object):
     ..
     ..
     """
     """
 
 
-    def __init__(self, location='', gisdbase=''):
+    def __init__(self, location="", gisdbase=""):
         self.gisdbase = gisdbase
         self.gisdbase = gisdbase
         self.name = location
         self.name = location
 
 
@@ -208,10 +226,11 @@ class Location(object):
         return self._gisdb
         return self._gisdb
 
 
     def _set_gisdb(self, gisdb):
     def _set_gisdb(self, gisdb):
-        self._gisdb = _check_raise(gisdb, '', "GISDBASE")
+        self._gisdb = _check_raise(gisdb, "", "GISDBASE")
 
 
-    gisdbase = property(fget=_get_gisdb, fset=_set_gisdb,
-                        doc="Set or obtain the name of GISDBASE")
+    gisdbase = property(
+        fget=_get_gisdb, fset=_set_gisdb, doc="Set or obtain the name of GISDBASE"
+    )
 
 
     def _get_name(self):
     def _get_name(self):
         return self._name
         return self._name
@@ -219,19 +238,23 @@ class Location(object):
     def _set_name(self, name):
     def _set_name(self, name):
         self._name = _check_raise(name, self._gisdb, "LOCATION_NAME")
         self._name = _check_raise(name, self._gisdb, "LOCATION_NAME")
 
 
-    name = property(fget=_get_name, fset=_set_name,
-                    doc="Set or obtain the name of LOCATION")
+    name = property(
+        fget=_get_name, fset=_set_name, doc="Set or obtain the name of LOCATION"
+    )
 
 
     def __getitem__(self, mapset):
     def __getitem__(self, mapset):
         if mapset in self.mapsets():
         if mapset in self.mapsets():
             return Mapset(mapset)
             return Mapset(mapset)
         else:
         else:
-            raise KeyError('Mapset: %s does not exist' % mapset)
+            raise KeyError("Mapset: %s does not exist" % mapset)
 
 
     def __iter__(self):
     def __iter__(self):
         lpath = self.path()
         lpath = self.path()
-        return (m for m in listdir(lpath)
-                if (isdir(join(lpath, m)) and is_valid(m, lpath, "MAPSET")))
+        return (
+            m
+            for m in listdir(lpath)
+            if (isdir(join(lpath, m)) and is_valid(m, lpath, "MAPSET"))
+        )
 
 
     def __len__(self):
     def __len__(self):
         return len(self.mapsets())
         return len(self.mapsets())
@@ -240,7 +263,7 @@ class Location(object):
         return self.name
         return self.name
 
 
     def __repr__(self):
     def __repr__(self):
-        return 'Location(%r)' % self.name
+        return "Location(%r)" % self.name
 
 
     def mapsets(self, pattern=None, permissions=True):
     def mapsets(self, pattern=None, permissions=True):
         """Return a list of the available mapsets.
         """Return a list of the available mapsets.
@@ -261,8 +284,11 @@ class Location(object):
         """
         """
         mapsets = [mapset for mapset in self]
         mapsets = [mapset for mapset in self]
         if permissions:
         if permissions:
-            mapsets = [mapset for mapset in mapsets
-                       if libgis.G_mapset_permissions(encode(mapset))]
+            mapsets = [
+                mapset
+                for mapset in mapsets
+                if libgis.G_mapset_permissions(encode(mapset))
+            ]
         if pattern:
         if pattern:
             return fnmatch.filter(mapsets, pattern)
             return fnmatch.filter(mapsets, pattern)
         return mapsets
         return mapsets
@@ -290,7 +316,7 @@ class Mapset(object):
     ..
     ..
     """
     """
 
 
-    def __init__(self, mapset='', location='', gisdbase=''):
+    def __init__(self, mapset="", location="", gisdbase=""):
         self.gisdbase = gisdbase
         self.gisdbase = gisdbase
         self.location = location
         self.location = location
         self.name = mapset
         self.name = mapset
@@ -300,10 +326,11 @@ class Mapset(object):
         return self._gisdb
         return self._gisdb
 
 
     def _set_gisdb(self, gisdb):
     def _set_gisdb(self, gisdb):
-        self._gisdb = _check_raise(gisdb, '', "GISDBASE")
+        self._gisdb = _check_raise(gisdb, "", "GISDBASE")
 
 
-    gisdbase = property(fget=_get_gisdb, fset=_set_gisdb,
-                        doc="Set or obtain the name of GISDBASE")
+    gisdbase = property(
+        fget=_get_gisdb, fset=_set_gisdb, doc="Set or obtain the name of GISDBASE"
+    )
 
 
     def _get_loc(self):
     def _get_loc(self):
         return self._loc
         return self._loc
@@ -311,8 +338,9 @@ class Mapset(object):
     def _set_loc(self, loc):
     def _set_loc(self, loc):
         self._loc = _check_raise(loc, self._gisdb, "LOCATION_NAME")
         self._loc = _check_raise(loc, self._gisdb, "LOCATION_NAME")
 
 
-    location = property(fget=_get_loc, fset=_set_loc,
-                        doc="Set or obtain the name of LOCATION")
+    location = property(
+        fget=_get_loc, fset=_set_loc, doc="Set or obtain the name of LOCATION"
+    )
 
 
     def _get_name(self):
     def _get_name(self):
         return self._name
         return self._name
@@ -320,14 +348,15 @@ class Mapset(object):
     def _set_name(self, name):
     def _set_name(self, name):
         self._name = _check_raise(name, join(self._gisdb, self._loc), "MAPSET")
         self._name = _check_raise(name, join(self._gisdb, self._loc), "MAPSET")
 
 
-    name = property(fget=_get_name, fset=_set_name,
-                    doc="Set or obtain the name of MAPSET")
+    name = property(
+        fget=_get_name, fset=_set_name, doc="Set or obtain the name of MAPSET"
+    )
 
 
     def __str__(self):
     def __str__(self):
         return self.name
         return self.name
 
 
     def __repr__(self):
     def __repr__(self):
-        return 'Mapset(%r)' % self.name
+        return "Mapset(%r)" % self.name
 
 
     def glist(self, type, pattern=None):
     def glist(self, type, pattern=None):
         """Return a list of grass types like:
         """Return a list of grass types like:
@@ -359,9 +388,8 @@ class Mapset(object):
         """
         """
         if type not in ETYPE:
         if type not in ETYPE:
             str_err = "Type %s is not valid, valid types are: %s."
             str_err = "Type %s is not valid, valid types are: %s."
-            raise TypeError(str_err % (type, ', '.join(ETYPE.keys())))
-        clist = libgis.G_list(ETYPE[type], self.gisdbase,
-                              self.location, self.name)
+            raise TypeError(str_err % (type, ", ".join(ETYPE.keys())))
+        clist = libgis.G_list(ETYPE[type], self.gisdbase, self.location, self.name)
         elist = []
         elist = []
         for el in clist:
         for el in clist:
             el_name = ct.cast(el, ct.c_char_p).value
             el_name = ct.cast(el, ct.c_char_p).value
@@ -374,9 +402,11 @@ class Mapset(object):
 
 
     def is_current(self):
     def is_current(self):
         """Check if the MAPSET is the working MAPSET"""
         """Check if the MAPSET is the working MAPSET"""
-        return (self.name == getenv('MAPSET') and
-                self.location == getenv('LOCATION_NAME') and
-                self.gisdbase == getenv('GISDBASE'))
+        return (
+            self.name == getenv("MAPSET")
+            and self.location == getenv("LOCATION_NAME")
+            and self.gisdbase == getenv("GISDBASE")
+        )
 
 
     def current(self):
     def current(self):
         """Set the mapset as current"""
         """Set the mapset as current"""
@@ -385,7 +415,7 @@ class Mapset(object):
     def delete(self):
     def delete(self):
         """Delete the mapset"""
         """Delete the mapset"""
         if self.is_current():
         if self.is_current():
-            raise GrassError('The mapset is in use.')
+            raise GrassError("The mapset is in use.")
         shutil.rmtree(self.path())
         shutil.rmtree(self.path())
 
 
     def path(self):
     def path(self):
@@ -394,14 +424,13 @@ class Mapset(object):
 
 
 
 
 class VisibleMapset(object):
 class VisibleMapset(object):
-    """VisibleMapset object
-    """
+    """VisibleMapset object"""
 
 
-    def __init__(self, mapset, location='', gisdbase=''):
+    def __init__(self, mapset, location="", gisdbase=""):
         self.mapset = mapset
         self.mapset = mapset
         self.location = Location(location, gisdbase)
         self.location = Location(location, gisdbase)
         self._list = []
         self._list = []
-        self.spath = join(self.location.path(), self.mapset, 'SEARCH_PATH')
+        self.spath = join(self.location.path(), self.mapset, "SEARCH_PATH")
 
 
     def __repr__(self):
     def __repr__(self):
         return repr(self.read())
         return repr(self.read())
@@ -416,7 +445,9 @@ class VisibleMapset(object):
             lines = f.readlines()
             lines = f.readlines()
             if lines:
             if lines:
                 return [decode(l.strip()) for l in lines]
                 return [decode(l.strip()) for l in lines]
-        lns = [u'PERMANENT', ]
+        lns = [
+            "PERMANENT",
+        ]
         self._write(lns)
         self._write(lns)
         return lns
         return lns
 
 
@@ -428,7 +459,7 @@ class VisibleMapset(object):
         """
         """
         with open(self.spath, "wb+") as f:
         with open(self.spath, "wb+") as f:
             ms = [decode(m) for m in self.location.mapsets()]
             ms = [decode(m) for m in self.location.mapsets()]
-            f.write(b'\n'.join([encode(m) for m in mapsets if m in ms]))
+            f.write(b"\n".join([encode(m) for m in mapsets if m in ms]))
 
 
     def add(self, mapset):
     def add(self, mapset):
         """Add a mapset to the search path
         """Add a mapset to the search path
@@ -438,9 +469,9 @@ class VisibleMapset(object):
         """
         """
         if mapset not in self.read() and mapset in self.location:
         if mapset not in self.read() and mapset in self.location:
             with open(self.spath, "a+") as f:
             with open(self.spath, "a+") as f:
-                f.write('\n%s' % mapset)
+                f.write("\n%s" % mapset)
         else:
         else:
-            raise TypeError('Mapset not found')
+            raise TypeError("Mapset not found")
 
 
     def remove(self, mapset):
     def remove(self, mapset):
         """Remove mapset to the search path
         """Remove mapset to the search path
@@ -466,7 +497,7 @@ class VisibleMapset(object):
 
 
     def reset(self):
     def reset(self):
         """Reset to the original search path"""
         """Reset to the original search path"""
-        final = [self.mapset, 'PERMANENT']
+        final = [self.mapset, "PERMANENT"]
         self._write(final)
         self._write(final)
 
 
 
 
@@ -477,18 +508,15 @@ if __name__ == "__main__":
 
 
     utils.create_test_vector_map(test_vector_name)
     utils.create_test_vector_map(test_vector_name)
     run_command("g.region", n=50, s=0, e=60, w=0, res=1)
     run_command("g.region", n=50, s=0, e=60, w=0, res=1)
-    run_command("r.mapcalc", expression="%s = 1" % (test_raster_name),
-                overwrite=True)
+    run_command("r.mapcalc", expression="%s = 1" % (test_raster_name), overwrite=True)
     run_command("g.region", n=40, s=0, e=40, w=0, res=2)
     run_command("g.region", n=40, s=0, e=40, w=0, res=2)
 
 
     doctest.testmod()
     doctest.testmod()
 
 
     # Remove the generated vector map, if exist
     # Remove the generated vector map, if exist
-    mset = utils.get_mapset_vector(test_vector_name, mapset='')
+    mset = utils.get_mapset_vector(test_vector_name, mapset="")
     if mset:
     if mset:
-        run_command("g.remove", flags='f', type='vector',
-                    name=test_vector_name)
-    mset = utils.get_mapset_raster(test_raster_name, mapset='')
+        run_command("g.remove", flags="f", type="vector", name=test_vector_name)
+    mset = utils.get_mapset_raster(test_raster_name, mapset="")
     if mset:
     if mset:
-        run_command("g.remove", flags='f', type='raster',
-                    name=test_raster_name)
+        run_command("g.remove", flags="f", type="raster", name=test_raster_name)

+ 248 - 200
python/grass/pygrass/gis/region.py

@@ -4,8 +4,15 @@ Created on Fri May 25 12:57:10 2012
 
 
 @author: Pietro Zambelli
 @author: Pietro Zambelli
 """
 """
-from __future__ import (nested_scopes, generators, division, absolute_import,
-                        with_statement, print_function, unicode_literals)
+from __future__ import (
+    nested_scopes,
+    generators,
+    division,
+    absolute_import,
+    with_statement,
+    print_function,
+    unicode_literals,
+)
 import ctypes
 import ctypes
 import grass.lib.gis as libgis
 import grass.lib.gis as libgis
 import grass.lib.raster as libraster
 import grass.lib.raster as libraster
@@ -18,6 +25,7 @@ from grass.pygrass.utils import get_mapset_raster
 test_vector_name = "Region_test_vector"
 test_vector_name = "Region_test_vector"
 test_raster_name = "Region_test_raster"
 test_raster_name = "Region_test_raster"
 
 
+
 class Region(object):
 class Region(object):
     """This class is design to easily access and modify GRASS computational
     """This class is design to easily access and modify GRASS computational
     region. ::
     region. ::
@@ -114,9 +122,9 @@ class Region(object):
         return ctypes.pointer(self.c_region)
         return ctypes.pointer(self.c_region)
 
 
     def _set_param(self, key, value):
     def _set_param(self, key, value):
-        grass.run_command('g.region', **{key: value})
+        grass.run_command("g.region", **{key: value})
 
 
-    #----------LIMITS----------
+    # ----------LIMITS----------
     def _get_n(self):
     def _get_n(self):
         """Private function to obtain north value"""
         """Private function to obtain north value"""
         return self.c_region.north
         return self.c_region.north
@@ -125,8 +133,7 @@ class Region(object):
         """Private function to set north value"""
         """Private function to set north value"""
         self.c_region.north = value
         self.c_region.north = value
 
 
-    north = property(fget=_get_n, fset=_set_n,
-                     doc="Set and obtain north coordinate")
+    north = property(fget=_get_n, fset=_set_n, doc="Set and obtain north coordinate")
 
 
     def _get_s(self):
     def _get_s(self):
         """Private function to obtain south value"""
         """Private function to obtain south value"""
@@ -136,8 +143,7 @@ class Region(object):
         """Private function to set south value"""
         """Private function to set south value"""
         self.c_region.south = value
         self.c_region.south = value
 
 
-    south = property(fget=_get_s, fset=_set_s,
-                     doc="Set and obtain south coordinate")
+    south = property(fget=_get_s, fset=_set_s, doc="Set and obtain south coordinate")
 
 
     def _get_e(self):
     def _get_e(self):
         """Private function to obtain east value"""
         """Private function to obtain east value"""
@@ -147,8 +153,7 @@ class Region(object):
         """Private function to set east value"""
         """Private function to set east value"""
         self.c_region.east = value
         self.c_region.east = value
 
 
-    east = property(fget=_get_e, fset=_set_e,
-                    doc="Set and obtain east coordinate")
+    east = property(fget=_get_e, fset=_set_e, doc="Set and obtain east coordinate")
 
 
     def _get_w(self):
     def _get_w(self):
         """Private function to obtain west value"""
         """Private function to obtain west value"""
@@ -158,8 +163,7 @@ class Region(object):
         """Private function to set west value"""
         """Private function to set west value"""
         self.c_region.west = value
         self.c_region.west = value
 
 
-    west = property(fget=_get_w, fset=_set_w,
-                    doc="Set and obtain west coordinate")
+    west = property(fget=_get_w, fset=_set_w, doc="Set and obtain west coordinate")
 
 
     def _get_t(self):
     def _get_t(self):
         """Private function to obtain top value"""
         """Private function to obtain top value"""
@@ -169,8 +173,7 @@ class Region(object):
         """Private function to set top value"""
         """Private function to set top value"""
         self.c_region.top = value
         self.c_region.top = value
 
 
-    top = property(fget=_get_t, fset=_set_t,
-                   doc="Set and obtain top value")
+    top = property(fget=_get_t, fset=_set_t, doc="Set and obtain top value")
 
 
     def _get_b(self):
     def _get_b(self):
         """Private function to obtain bottom value"""
         """Private function to obtain bottom value"""
@@ -180,10 +183,9 @@ class Region(object):
         """Private function to set bottom value"""
         """Private function to set bottom value"""
         self.c_region.bottom = value
         self.c_region.bottom = value
 
 
-    bottom = property(fget=_get_b, fset=_set_b,
-                      doc="Set and obtain bottom value")
+    bottom = property(fget=_get_b, fset=_set_b, doc="Set and obtain bottom value")
 
 
-    #----------RESOLUTION----------
+    # ----------RESOLUTION----------
     def _get_rows(self):
     def _get_rows(self):
         """Private function to obtain rows value"""
         """Private function to obtain rows value"""
         return self.c_region.rows
         return self.c_region.rows
@@ -193,8 +195,7 @@ class Region(object):
         self.c_region.rows = value
         self.c_region.rows = value
         self.adjust(rows=True)
         self.adjust(rows=True)
 
 
-    rows = property(fget=_get_rows, fset=_set_rows,
-                    doc="Set and obtain number of rows")
+    rows = property(fget=_get_rows, fset=_set_rows, doc="Set and obtain number of rows")
 
 
     def _get_cols(self):
     def _get_cols(self):
         """Private function to obtain columns value"""
         """Private function to obtain columns value"""
@@ -205,8 +206,9 @@ class Region(object):
         self.c_region.cols = value
         self.c_region.cols = value
         self.adjust(cols=True)
         self.adjust(cols=True)
 
 
-    cols = property(fget=_get_cols, fset=_set_cols,
-                    doc="Set and obtain number of columns")
+    cols = property(
+        fget=_get_cols, fset=_set_cols, doc="Set and obtain number of columns"
+    )
 
 
     def _get_depths(self):
     def _get_depths(self):
         """Private function to obtain depths value"""
         """Private function to obtain depths value"""
@@ -216,8 +218,9 @@ class Region(object):
         """Private function to set depths value"""
         """Private function to set depths value"""
         self.c_region.depths = value
         self.c_region.depths = value
 
 
-    depths = property(fget=_get_depths, fset=_set_depths,
-                      doc="Set and obtain number of depths")
+    depths = property(
+        fget=_get_depths, fset=_set_depths, doc="Set and obtain number of depths"
+    )
 
 
     def _get_nsres(self):
     def _get_nsres(self):
         """Private function to obtain north-south value"""
         """Private function to obtain north-south value"""
@@ -228,8 +231,11 @@ class Region(object):
         self.c_region.ns_res = value
         self.c_region.ns_res = value
         self.adjust()
         self.adjust()
 
 
-    nsres = property(fget=_get_nsres, fset=_set_nsres,
-                     doc="Set and obtain north-south resolution value")
+    nsres = property(
+        fget=_get_nsres,
+        fset=_set_nsres,
+        doc="Set and obtain north-south resolution value",
+    )
 
 
     def _get_ewres(self):
     def _get_ewres(self):
         """Private function to obtain east-west value"""
         """Private function to obtain east-west value"""
@@ -240,8 +246,11 @@ class Region(object):
         self.c_region.ew_res = value
         self.c_region.ew_res = value
         self.adjust()
         self.adjust()
 
 
-    ewres = property(fget=_get_ewres, fset=_set_ewres,
-                     doc="Set and obtain east-west resolution value")
+    ewres = property(
+        fget=_get_ewres,
+        fset=_set_ewres,
+        doc="Set and obtain east-west resolution value",
+    )
 
 
     def _get_tbres(self):
     def _get_tbres(self):
         """Private function to obtain top-botton 3D value"""
         """Private function to obtain top-botton 3D value"""
@@ -252,19 +261,18 @@ class Region(object):
         self.c_region.tb_res = value
         self.c_region.tb_res = value
         self.adjust()
         self.adjust()
 
 
-    tbres = property(fget=_get_tbres, fset=_set_tbres,
-                     doc="Set and obtain top-bottom 3D value")
+    tbres = property(
+        fget=_get_tbres, fset=_set_tbres, doc="Set and obtain top-bottom 3D value"
+    )
 
 
     @property
     @property
     def zone(self):
     def zone(self):
-        """Return the zone of projection
-        """
+        """Return the zone of projection"""
         return self.c_region.zone
         return self.c_region.zone
 
 
     @property
     @property
     def proj(self):
     def proj(self):
-        """Return a code for projection
-        """
+        """Return a code for projection"""
         return self.c_region.proj
         return self.c_region.proj
 
 
     @property
     @property
@@ -272,18 +280,29 @@ class Region(object):
         """Return the number of cells"""
         """Return the number of cells"""
         return self.rows * self.cols
         return self.rows * self.cols
 
 
-    #----------MAGIC METHODS----------
+    # ----------MAGIC METHODS----------
     def __repr__(self):
     def __repr__(self):
-        rg = "Region(north=%g, south=%g, east=%g, west=%g, "\
-            "nsres=%g, ewres=%g, rows=%i, cols=%i, "\
+        rg = (
+            "Region(north=%g, south=%g, east=%g, west=%g, "
+            "nsres=%g, ewres=%g, rows=%i, cols=%i, "
             "cells=%i, zone=%i, proj=%i)"
             "cells=%i, zone=%i, proj=%i)"
-        return rg % (self.north, self.south, self.east, self.west,
-                     self.nsres, self.ewres, self.rows, self.cols,
-                     self.cells, self.zone, self.proj)
+        )
+        return rg % (
+            self.north,
+            self.south,
+            self.east,
+            self.west,
+            self.nsres,
+            self.ewres,
+            self.rows,
+            self.cols,
+            self.cells,
+            self.zone,
+            self.proj,
+        )
 
 
     def _repr_html_(self):
     def _repr_html_(self):
-        return dict2html(dict(self.items()), keys=self.keys(),
-                         border='1', kdec='b')
+        return dict2html(dict(self.items()), keys=self.keys(), border="1", kdec="b")
 
 
     def __unicode__(self):
     def __unicode__(self):
         return self.__repr__()
         return self.__repr__()
@@ -305,9 +324,22 @@ class Region(object):
 
 
         ..
         ..
         """
         """
-        attrs = ['north', 'south', 'west', 'east', 'top', 'bottom',
-                 'nsres', 'ewres', 'tbres', 'rows', 'cols', 'cells',
-                 'zone', 'proj']
+        attrs = [
+            "north",
+            "south",
+            "west",
+            "east",
+            "top",
+            "bottom",
+            "nsres",
+            "ewres",
+            "tbres",
+            "rows",
+            "cols",
+            "cells",
+            "zone",
+            "proj",
+        ]
         for attr in attrs:
         for attr in attrs:
             if getattr(self, attr) != getattr(reg, attr):
             if getattr(self, attr) != getattr(reg, attr):
                 return False
                 return False
@@ -328,16 +360,28 @@ class Region(object):
 
 
         ..
         ..
         """
         """
-        return ['proj', 'zone', 'north', 'south', 'west', 'east',
-                'top', 'bottom', 'nsres', 'ewres', 'tbres', 'rows',
-                'cols', 'cells']
+        return [
+            "proj",
+            "zone",
+            "north",
+            "south",
+            "west",
+            "east",
+            "top",
+            "bottom",
+            "nsres",
+            "ewres",
+            "tbres",
+            "rows",
+            "cols",
+            "cells",
+        ]
 
 
     def items(self):
     def items(self):
-        """Return a list of tuple with key and value.
-        """
+        """Return a list of tuple with key and value."""
         return [(k, self.__getattribute__(k)) for k in self.keys()]
         return [(k, self.__getattribute__(k)) for k in self.keys()]
 
 
-    #----------METHODS----------
+    # ----------METHODS----------
     def zoom(self, raster_name):
     def zoom(self, raster_name):
         """Shrink region until it meets non-NULL data from this raster map
         """Shrink region until it meets non-NULL data from this raster map
 
 
@@ -346,7 +390,7 @@ class Region(object):
         :param raster_name: the name of raster
         :param raster_name: the name of raster
         :type raster_name: str
         :type raster_name: str
         """
         """
-        self._set_param('zoom', str(raster_name))
+        self._set_param("zoom", str(raster_name))
         self.read()
         self.read()
 
 
     def align(self, raster_name):
     def align(self, raster_name):
@@ -357,7 +401,7 @@ class Region(object):
         :param raster_name: the name of raster
         :param raster_name: the name of raster
         :type raster_name: str
         :type raster_name: str
         """
         """
-        self._set_param('align', str(raster_name))
+        self._set_param("align", str(raster_name))
         self.read()
         self.read()
 
 
     def adjust(self, rows=False, cols=False):
     def adjust(self, rows=False, cols=False):
@@ -370,91 +414,90 @@ class Region(object):
     def from_vect(self, vector_name):
     def from_vect(self, vector_name):
         """Adjust bounding box of region using a vector
         """Adjust bounding box of region using a vector
 
 
-            :param vector_name: the name of vector
-            :type vector_name: str
+        :param vector_name: the name of vector
+        :type vector_name: str
 
 
-            Example ::
+        Example ::
 
 
-            >>> reg = Region()
-            >>> reg.from_vect(test_vector_name)
-            >>> reg.get_bbox()
-            Bbox(6.0, 0.0, 14.0, 0.0)
-            >>> reg.read()
-            >>> reg.get_bbox()
-            Bbox(40.0, 0.0, 40.0, 0.0)
+        >>> reg = Region()
+        >>> reg.from_vect(test_vector_name)
+        >>> reg.get_bbox()
+        Bbox(6.0, 0.0, 14.0, 0.0)
+        >>> reg.read()
+        >>> reg.get_bbox()
+        Bbox(40.0, 0.0, 40.0, 0.0)
 
 
-            ..
+        ..
         """
         """
         from grass.pygrass.vector import VectorTopo
         from grass.pygrass.vector import VectorTopo
-        with VectorTopo(vector_name, mode='r') as vect:
+
+        with VectorTopo(vector_name, mode="r") as vect:
             bbox = vect.bbox()
             bbox = vect.bbox()
             self.set_bbox(bbox)
             self.set_bbox(bbox)
 
 
     def from_rast(self, raster_name):
     def from_rast(self, raster_name):
         """Set the region from the computational region
         """Set the region from the computational region
-            of a raster map layer.
+        of a raster map layer.
 
 
-            :param raster_name: the name of raster
-            :type raster_name: str
+        :param raster_name: the name of raster
+        :type raster_name: str
 
 
-            :param mapset: the mapset of raster
-            :type mapset: str
+        :param mapset: the mapset of raster
+        :type mapset: str
 
 
-            call C function `Rast_get_cellhd`
+        call C function `Rast_get_cellhd`
 
 
-            Example ::
+        Example ::
 
 
-            >>> reg = Region()
-            >>> reg.from_rast(test_raster_name)
-            >>> reg.get_bbox()
-            Bbox(50.0, 0.0, 60.0, 0.0)
-            >>> reg.read()
-            >>> reg.get_bbox()
-            Bbox(40.0, 0.0, 40.0, 0.0)
+        >>> reg = Region()
+        >>> reg.from_rast(test_raster_name)
+        >>> reg.get_bbox()
+        Bbox(50.0, 0.0, 60.0, 0.0)
+        >>> reg.read()
+        >>> reg.get_bbox()
+        Bbox(40.0, 0.0, 40.0, 0.0)
 
 
-            ..
-           """
+        ..
+        """
         if not raster_name:
         if not raster_name:
             raise ValueError("Raster name or mapset are invalid")
             raise ValueError("Raster name or mapset are invalid")
 
 
-
         mapset = get_mapset_raster(raster_name)
         mapset = get_mapset_raster(raster_name)
 
 
         if mapset:
         if mapset:
-            libraster.Rast_get_cellhd(raster_name, mapset,
-                                      self.byref())
+            libraster.Rast_get_cellhd(raster_name, mapset, self.byref())
 
 
     def set_raster_region(self):
     def set_raster_region(self):
         """Set the computational region (window) for all raster maps in the current process.
         """Set the computational region (window) for all raster maps in the current process.
 
 
-           Attention: All raster objects must be closed or the
-                      process will be terminated.
+        Attention: All raster objects must be closed or the
+                   process will be terminated.
 
 
-           The Raster library C function Rast_set_window() is called.
+        The Raster library C function Rast_set_window() is called.
 
 
         """
         """
         libraster.Rast_set_window(self.byref())
         libraster.Rast_set_window(self.byref())
 
 
     def get_current(self):
     def get_current(self):
         """Get the current working region of this process
         """Get the current working region of this process
-           and store it into this Region object
+        and store it into this Region object
 
 
-           Previous calls to set_current() affects values returned by this function.
-           Previous calls to read() affects values returned by this function
-           only if the current working region is not initialized.
+        Previous calls to set_current() affects values returned by this function.
+        Previous calls to read() affects values returned by this function
+        only if the current working region is not initialized.
 
 
-            Example:
+         Example:
 
 
-            >>> r = Region()
-            >>> r.north
-            40.0
+         >>> r = Region()
+         >>> r.north
+         40.0
 
 
-            >>> r.north = 30
-            >>> r.north
-            30.0
-            >>> r.get_current()
-            >>> r.north
-            40.0
+         >>> r.north = 30
+         >>> r.north
+         30.0
+         >>> r.get_current()
+         >>> r.north
+         40.0
 
 
         """
         """
         libgis.G_get_set_window(self.byref())
         libgis.G_get_set_window(self.byref())
@@ -462,70 +505,70 @@ class Region(object):
     def set_current(self):
     def set_current(self):
         """Set the current working region from this region object
         """Set the current working region from this region object
 
 
-           This function adjusts the values before setting the region
-           so you don't have to call G_adjust_Cell_head().
-
-           Attention: Only the current process is affected.
-                      The GRASS computational region is not affected.
-
-            Example::
-
-            >>> r = Region()
-            >>> r.north
-            40.0
-            >>> r.south
-            0.0
-
-            >>> r.north = 30
-            >>> r.south = 20
-            >>> r.set_current()
-            >>> r.north
-            30.0
-            >>> r.south
-            20.0
-            >>> r.get_current()
-            >>> r.north
-            30.0
-            >>> r.south
-            20.0
-
-            >>> r.read(force_read=False)
-            >>> r.north
-            40.0
-            >>> r.south
-            0.0
-
-            >>> r.read(force_read=True)
-            >>> r.north
-            40.0
-            >>> r.south
-            0.0
+        This function adjusts the values before setting the region
+        so you don't have to call G_adjust_Cell_head().
+
+        Attention: Only the current process is affected.
+                   The GRASS computational region is not affected.
+
+         Example::
+
+         >>> r = Region()
+         >>> r.north
+         40.0
+         >>> r.south
+         0.0
+
+         >>> r.north = 30
+         >>> r.south = 20
+         >>> r.set_current()
+         >>> r.north
+         30.0
+         >>> r.south
+         20.0
+         >>> r.get_current()
+         >>> r.north
+         30.0
+         >>> r.south
+         20.0
+
+         >>> r.read(force_read=False)
+         >>> r.north
+         40.0
+         >>> r.south
+         0.0
+
+         >>> r.read(force_read=True)
+         >>> r.north
+         40.0
+         >>> r.south
+         0.0
 
 
         """
         """
         libgis.G_set_window(self.byref())
         libgis.G_set_window(self.byref())
 
 
     def read(self, force_read=True):
     def read(self, force_read=True):
         """
         """
-          Read the region into this region object
+        Read the region into this region object
 
 
-          Reads the region as stored in the WIND file in the user's current
-          mapset into region.
+        Reads the region as stored in the WIND file in the user's current
+        mapset into region.
 
 
-          3D values are set to defaults if not available in WIND file.  An
-          error message is printed and exit() is called if there is a problem
-          reading the region.
+        3D values are set to defaults if not available in WIND file.  An
+        error message is printed and exit() is called if there is a problem
+        reading the region.
 
 
-          <b>Note:</b> GRASS applications that read or write raster maps
-          should not use this routine since its use implies that the active
-          module region will not be used. Programs that read or write raster
-          map data (or vector data) can query the active module region using
-          Rast_window_rows() and Rast_window_cols().
+        <b>Note:</b> GRASS applications that read or write raster maps
+        should not use this routine since its use implies that the active
+        module region will not be used. Programs that read or write raster
+        map data (or vector data) can query the active module region using
+        Rast_window_rows() and Rast_window_cols().
 
 
-          :param force_read: If True the WIND file of the current mapset
-                             is re-readed, otherwise the initial region
-                             set at process start will be loaded from the internal
-                             static variables.
-          :type force_read: boolean
+        :param force_read: If True the WIND file of the current mapset
+                           is re-readed, otherwise the initial region
+                           set at process start will be loaded from the internal
+                           static variables.
+        :type force_read: boolean
 
 
         """
         """
         # Force the reading of the WIND file
         # Force the reading of the WIND file
@@ -536,52 +579,51 @@ class Region(object):
     def write(self):
     def write(self):
         """Writes the region from this region object
         """Writes the region from this region object
 
 
-           This function writes this region to the Region file (WIND)
-           in the users current mapset. This function should be
-           carefully used, since the user will ot notice if his region
-           was changed and would expect that only g.region will do this.
-
-            Example ::
-
-            >>> from copy import deepcopy
-            >>> r = Region()
-            >>> rn = deepcopy(r)
-            >>> r.north = 20
-            >>> r.south = 10
-
-            >>> r.write()
-            >>> r.read()
-            >>> r.north
-            20.0
-            >>> r.south
-            10.0
-
-            >>> rn.write()
-            >>> r.read()
-            >>> r.north
-            40.0
-            >>> r.south
-            0.0
-
-            >>> r.read_default()
-            >>> r.write()
-
-            ..
+        This function writes this region to the Region file (WIND)
+        in the users current mapset. This function should be
+        carefully used, since the user will ot notice if his region
+        was changed and would expect that only g.region will do this.
+
+         Example ::
+
+         >>> from copy import deepcopy
+         >>> r = Region()
+         >>> rn = deepcopy(r)
+         >>> r.north = 20
+         >>> r.south = 10
+
+         >>> r.write()
+         >>> r.read()
+         >>> r.north
+         20.0
+         >>> r.south
+         10.0
+
+         >>> rn.write()
+         >>> r.read()
+         >>> r.north
+         40.0
+         >>> r.south
+         0.0
+
+         >>> r.read_default()
+         >>> r.write()
+
+         ..
         """
         """
         self.adjust()
         self.adjust()
         if libgis.G_put_window(self.byref()) < 0:
         if libgis.G_put_window(self.byref()) < 0:
             raise GrassError("Cannot change region (WIND file).")
             raise GrassError("Cannot change region (WIND file).")
 
 
-
     def read_default(self):
     def read_default(self):
         """
         """
-          Get the default region
+        Get the default region
 
 
-          Reads the default region for the location in this Region object.
-          3D values are set to defaults if not available in WIND file.
+        Reads the default region for the location in this Region object.
+        3D values are set to defaults if not available in WIND file.
 
 
-          An error message is printed and exit() is called if there is a
-          problem reading the default region.
+        An error message is printed and exit() is called if there is a
+        problem reading the default region.
         """
         """
         libgis.G_get_default_window(self.byref())
         libgis.G_get_default_window(self.byref())
 
 
@@ -595,9 +637,15 @@ class Region(object):
         ..
         ..
         """
         """
         from grass.pygrass.vector.basic import Bbox
         from grass.pygrass.vector.basic import Bbox
-        return Bbox(north=self.north, south=self.south,
-                    east=self.east, west=self.west,
-                    top=self.top, bottom=self.bottom)
+
+        return Bbox(
+            north=self.north,
+            south=self.south,
+            east=self.east,
+            west=self.west,
+            top=self.top,
+            bottom=self.bottom,
+        )
 
 
     def set_bbox(self, bbox):
     def set_bbox(self, bbox):
         """Set region extent from Bbox
         """Set region extent from Bbox
@@ -622,6 +670,7 @@ class Region(object):
         self.east = bbox.east
         self.east = bbox.east
         self.west = bbox.west
         self.west = bbox.west
 
 
+
 if __name__ == "__main__":
 if __name__ == "__main__":
 
 
     import doctest
     import doctest
@@ -630,16 +679,15 @@ if __name__ == "__main__":
 
 
     utils.create_test_vector_map(test_vector_name)
     utils.create_test_vector_map(test_vector_name)
     run_command("g.region", n=50, s=0, e=60, w=0, res=1)
     run_command("g.region", n=50, s=0, e=60, w=0, res=1)
-    run_command("r.mapcalc", expression="%s = 1" % (test_raster_name),
-                             overwrite=True)
+    run_command("r.mapcalc", expression="%s = 1" % (test_raster_name), overwrite=True)
     run_command("g.region", n=40, s=0, e=40, w=0, res=2)
     run_command("g.region", n=40, s=0, e=40, w=0, res=2)
 
 
     doctest.testmod()
     doctest.testmod()
 
 
     """Remove the generated vector map, if exist"""
     """Remove the generated vector map, if exist"""
-    mset = utils.get_mapset_vector(test_vector_name, mapset='')
+    mset = utils.get_mapset_vector(test_vector_name, mapset="")
     if mset:
     if mset:
-        run_command("g.remove", flags='f', type='vector', name=test_vector_name)
-    mset = utils.get_mapset_raster(test_raster_name, mapset='')
+        run_command("g.remove", flags="f", type="vector", name=test_vector_name)
+    mset = utils.get_mapset_raster(test_raster_name, mapset="")
     if mset:
     if mset:
-        run_command("g.remove", flags='f', type='raster', name=test_raster_name)
+        run_command("g.remove", flags="f", type="raster", name=test_raster_name)

+ 1 - 2
python/grass/pygrass/gis/testsuite/test_gis.py

@@ -10,7 +10,6 @@ from grass.pygrass.gis.region import Region
 
 
 
 
 class RegionTestCase(TestCase):
 class RegionTestCase(TestCase):
-
     def test_bounds(self):
     def test_bounds(self):
         reg1 = Region()
         reg1 = Region()
         reg2 = Region()
         reg2 = Region()
@@ -21,5 +20,5 @@ class RegionTestCase(TestCase):
         reg2.north = north
         reg2.north = north
 
 
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     test()
     test()

+ 17 - 11
python/grass/pygrass/gis/testsuite/test_pygrass_gis_doctests.py

@@ -18,12 +18,14 @@ from grass.pygrass.gis import region
 # and contains doctest's methods
 # and contains doctest's methods
 # the alternative is to copy 500 from doctest and change what is needed
 # the alternative is to copy 500 from doctest and change what is needed
 # (this might be necessary anyway because of the reports and stdout and stderr)
 # (this might be necessary anyway because of the reports and stdout and stderr)
-doctest.DocFileCase = type('DocFileCase',
-                           (grass.gunittest.case.TestCase,),
-                           dict(doctest.DocFileCase.__dict__))
-doctest.SkipDocTestCase = type('SkipDocTestCase',
-                               (grass.gunittest.case.TestCase,),
-                               dict(doctest.SkipDocTestCase.__dict__))
+doctest.DocFileCase = type(
+    "DocFileCase", (grass.gunittest.case.TestCase,), dict(doctest.DocFileCase.__dict__)
+)
+doctest.SkipDocTestCase = type(
+    "SkipDocTestCase",
+    (grass.gunittest.case.TestCase,),
+    dict(doctest.SkipDocTestCase.__dict__),
+)
 
 
 
 
 def load_tests(loader, tests, ignore):
 def load_tests(loader, tests, ignore):
@@ -34,13 +36,16 @@ def load_tests(loader, tests, ignore):
 
 
     from grass.pygrass import utils
     from grass.pygrass import utils
     from grass.script.core import run_command
     from grass.script.core import run_command
+
     utils.create_test_vector_map(gis.test_vector_name)
     utils.create_test_vector_map(gis.test_vector_name)
     utils.create_test_vector_map(gis.region.test_vector_name)
     utils.create_test_vector_map(gis.region.test_vector_name)
     run_command("g.region", n=50, s=0, e=60, w=0, res=1)
     run_command("g.region", n=50, s=0, e=60, w=0, res=1)
-    run_command("r.mapcalc", expression="%s = 1" % (gis.test_raster_name),
-                             overwrite=True)
-    run_command("r.mapcalc", expression="%s = 1" % (gis.region.test_raster_name),
-                             overwrite=True)
+    run_command(
+        "r.mapcalc", expression="%s = 1" % (gis.test_raster_name), overwrite=True
+    )
+    run_command(
+        "r.mapcalc", expression="%s = 1" % (gis.region.test_raster_name), overwrite=True
+    )
     run_command("g.region", n=40, s=0, e=40, w=0, res=2)
     run_command("g.region", n=40, s=0, e=40, w=0, res=2)
 
 
     # this should be called at some top level
     # this should be called at some top level
@@ -48,5 +53,6 @@ def load_tests(loader, tests, ignore):
     tests.addTests(doctest.DocTestSuite(region))
     tests.addTests(doctest.DocTestSuite(region))
     return tests
     return tests
 
 
-if __name__ == '__main__':
+
+if __name__ == "__main__":
     grass.gunittest.main.test()
     grass.gunittest.main.test()

+ 107 - 100
python/grass/pygrass/messages/__init__.py

@@ -23,39 +23,39 @@ from grass.exceptions import FatalError
 
 
 def message_server(lock, conn):
 def message_server(lock, conn):
     """The GRASS message server function designed to be a target for
     """The GRASS message server function designed to be a target for
-       multiprocessing.Process
+    multiprocessing.Process
 
 
 
 
-       :param lock: A multiprocessing.Lock
-       :param conn: A multiprocessing.Pipe
+    :param lock: A multiprocessing.Lock
+    :param conn: A multiprocessing.Pipe
 
 
-       This function will use the G_* message C-functions from grass.lib.gis
-       to provide an interface to the GRASS C-library messaging system.
+    This function will use the G_* message C-functions from grass.lib.gis
+    to provide an interface to the GRASS C-library messaging system.
 
 
-       The data that is send through the pipe must provide an
-       identifier string to specify which C-function should be called.
+    The data that is send through the pipe must provide an
+    identifier string to specify which C-function should be called.
 
 
-       The following identifiers are supported:
+    The following identifiers are supported:
 
 
-       - "INFO"       Prints an info message, see G_message() for details
-       - "IMPORTANT"  Prints an important info message,
-                      see G_important_message() for details
-       - "VERBOSE"    Prints a verbose message if the verbosity level is
-                      set accordingly, see G_verbose_message() for details
-       - "WARNING"    Prints a warning message, see G_warning() for details
-       - "ERROR"      Prints a message with a leading "ERROR: " string,
-                      see G_important_message() for details
-       - "PERCENT"    Prints a percent value based on three integer values: n, d and s
-                      see G_percent() for details
-       - "STOP"       Stops the server function and closes the pipe
-       - "FATAL"      Calls G_fatal_error(), this functions is only for
-                      testing purpose
+    - "INFO"       Prints an info message, see G_message() for details
+    - "IMPORTANT"  Prints an important info message,
+                   see G_important_message() for details
+    - "VERBOSE"    Prints a verbose message if the verbosity level is
+                   set accordingly, see G_verbose_message() for details
+    - "WARNING"    Prints a warning message, see G_warning() for details
+    - "ERROR"      Prints a message with a leading "ERROR: " string,
+                   see G_important_message() for details
+    - "PERCENT"    Prints a percent value based on three integer values: n, d and s
+                   see G_percent() for details
+    - "STOP"       Stops the server function and closes the pipe
+    - "FATAL"      Calls G_fatal_error(), this functions is only for
+                   testing purpose
 
 
-       The that is end through the pipe must be a list of values:
+    The that is end through the pipe must be a list of values:
 
 
-       - Messages: ["INFO|VERBOSE|WARNING|ERROR|FATAL", "MESSAGE"]
-       - Debug:    ["DEBUG", level, "MESSAGE"]
-       - Percent:  ["PERCENT", n, d, s]
+    - Messages: ["INFO|VERBOSE|WARNING|ERROR|FATAL", "MESSAGE"]
+    - Debug:    ["DEBUG", level, "MESSAGE"]
+    - Percent:  ["PERCENT", n, d, s]
 
 
     """
     """
     libgis.G_debug(1, "Start messenger server")
     libgis.G_debug(1, "Start messenger server")
@@ -111,60 +111,60 @@ def message_server(lock, conn):
 class Messenger(object):
 class Messenger(object):
     """Fast and exit-safe interface to GRASS C-library message functions
     """Fast and exit-safe interface to GRASS C-library message functions
 
 
-       This class implements a fast and exit-safe interface to the GRASS
-       C-library message functions like: G_message(), G_warning(),
-       G_important_message(), G_verbose_message(), G_percent() and G_debug().
-
-       Note:
-
-       The C-library message functions a called via ctypes in a subprocess
-       using a pipe (multiprocessing.Pipe) to transfer the text messages.
-       Hence, the process that uses the Messenger interface will not be
-       exited, if a G_fatal_error() was invoked in the subprocess.
-       In this case the Messenger object will simply start a new subprocess
-       and restarts the pipeline.
-
-
-       Usage:
-
-       >>> msgr = Messenger()
-       >>> msgr.debug(0, "debug 0")
-       >>> msgr.verbose("verbose message")
-       >>> msgr.message("message")
-       >>> msgr.important("important message")
-       >>> msgr.percent(1, 1, 1)
-       >>> msgr.warning("Ohh")
-       >>> msgr.error("Ohh no")
-
-       >>> msgr = Messenger()
-       >>> msgr.fatal("Ohh no no no!")
-       Traceback (most recent call last):
-         File "__init__.py", line 239, in fatal
-           sys.exit(1)
-       SystemExit: 1
-
-       >>> msgr = Messenger(raise_on_error=True)
-       >>> msgr.fatal("Ohh no no no!")
-       Traceback (most recent call last):
-         File "__init__.py", line 241, in fatal
-           raise FatalError(message)
-       grass.exceptions.FatalError: Ohh no no no!
-
-       >>> msgr = Messenger(raise_on_error=True)
-       >>> msgr.set_raise_on_error(False)
-       >>> msgr.fatal("Ohh no no no!")
-       Traceback (most recent call last):
-         File "__init__.py", line 239, in fatal
-           sys.exit(1)
-       SystemExit: 1
-
-       >>> msgr = Messenger(raise_on_error=False)
-       >>> msgr.set_raise_on_error(True)
-       >>> msgr.fatal("Ohh no no no!")
-       Traceback (most recent call last):
-         File "__init__.py", line 241, in fatal
-           raise FatalError(message)
-       grass.exceptions.FatalError: Ohh no no no!
+    This class implements a fast and exit-safe interface to the GRASS
+    C-library message functions like: G_message(), G_warning(),
+    G_important_message(), G_verbose_message(), G_percent() and G_debug().
+
+    Note:
+
+    The C-library message functions a called via ctypes in a subprocess
+    using a pipe (multiprocessing.Pipe) to transfer the text messages.
+    Hence, the process that uses the Messenger interface will not be
+    exited, if a G_fatal_error() was invoked in the subprocess.
+    In this case the Messenger object will simply start a new subprocess
+    and restarts the pipeline.
+
+
+    Usage:
+
+    >>> msgr = Messenger()
+    >>> msgr.debug(0, "debug 0")
+    >>> msgr.verbose("verbose message")
+    >>> msgr.message("message")
+    >>> msgr.important("important message")
+    >>> msgr.percent(1, 1, 1)
+    >>> msgr.warning("Ohh")
+    >>> msgr.error("Ohh no")
+
+    >>> msgr = Messenger()
+    >>> msgr.fatal("Ohh no no no!")
+    Traceback (most recent call last):
+      File "__init__.py", line 239, in fatal
+        sys.exit(1)
+    SystemExit: 1
+
+    >>> msgr = Messenger(raise_on_error=True)
+    >>> msgr.fatal("Ohh no no no!")
+    Traceback (most recent call last):
+      File "__init__.py", line 241, in fatal
+        raise FatalError(message)
+    grass.exceptions.FatalError: Ohh no no no!
+
+    >>> msgr = Messenger(raise_on_error=True)
+    >>> msgr.set_raise_on_error(False)
+    >>> msgr.fatal("Ohh no no no!")
+    Traceback (most recent call last):
+      File "__init__.py", line 239, in fatal
+        sys.exit(1)
+    SystemExit: 1
+
+    >>> msgr = Messenger(raise_on_error=False)
+    >>> msgr.set_raise_on_error(True)
+    >>> msgr.fatal("Ohh no no no!")
+    Traceback (most recent call last):
+      File "__init__.py", line 241, in fatal
+        raise FatalError(message)
+    grass.exceptions.FatalError: Ohh no no no!
 
 
     """
     """
 
 
@@ -176,18 +176,15 @@ class Messenger(object):
         self.start_server()
         self.start_server()
 
 
     def start_server(self):
     def start_server(self):
-        """Start the messenger server and open the pipe
-        """
+        """Start the messenger server and open the pipe"""
         self.client_conn, self.server_conn = Pipe()
         self.client_conn, self.server_conn = Pipe()
         self.lock = Lock()
         self.lock = Lock()
-        self.server = Process(target=message_server, args=(self.lock,
-                                                           self.server_conn))
+        self.server = Process(target=message_server, args=(self.lock, self.server_conn))
         self.server.daemon = True
         self.server.daemon = True
         self.server.start()
         self.server.start()
 
 
     def _check_restart_server(self):
     def _check_restart_server(self):
-        """Restart the server if it was terminated
-        """
+        """Restart the server if it was terminated"""
         if self.server.is_alive() is True:
         if self.server.is_alive() is True:
             return
             return
         self.client_conn.close()
         self.client_conn.close()
@@ -295,10 +292,13 @@ class Messenger(object):
         self.client_conn.send(["PERCENT", n, d, s])
         self.client_conn.send(["PERCENT", n, d, s])
 
 
     def stop(self):
     def stop(self):
-        """Stop the messenger server and close the pipe
-        """
+        """Stop the messenger server and close the pipe"""
         if self.server is not None and self.server.is_alive():
         if self.server is not None and self.server.is_alive():
-            self.client_conn.send(["STOP", ])
+            self.client_conn.send(
+                [
+                    "STOP",
+                ]
+            )
             self.server.join(5)
             self.server.join(5)
             self.server.terminate()
             self.server.terminate()
         if self.client_conn is not None:
         if self.client_conn is not None:
@@ -307,14 +307,14 @@ class Messenger(object):
     def set_raise_on_error(self, raise_on_error=True):
     def set_raise_on_error(self, raise_on_error=True):
         """Set the fatal error behavior
         """Set the fatal error behavior
 
 
-           :param raise_on_error: if True a FatalError exception will be
-                                  raised instead of calling sys.exit(1)
-           :type raise_on_error: bool
+        :param raise_on_error: if True a FatalError exception will be
+                               raised instead of calling sys.exit(1)
+        :type raise_on_error: bool
 
 
-           - If raise_on_error == True, a FatalError exception will be raised
-             if fatal() is called
-           - If raise_on_error == False, sys.exit(1) will be invoked if
-             fatal() is called
+        - If raise_on_error == True, a FatalError exception will be raised
+          if fatal() is called
+        - If raise_on_error == False, sys.exit(1) will be invoked if
+          fatal() is called
 
 
         """
         """
         self.raise_on_error = raise_on_error
         self.raise_on_error = raise_on_error
@@ -322,21 +322,27 @@ class Messenger(object):
     def get_raise_on_error(self):
     def get_raise_on_error(self):
         """Get the fatal error behavior
         """Get the fatal error behavior
 
 
-           :returns: True if a FatalError exception will be raised or False if
-                     sys.exit(1) will be called in case of invoking fatal()
+        :returns: True if a FatalError exception will be raised or False if
+                  sys.exit(1) will be called in case of invoking fatal()
         """
         """
         return self.raise_on_error
         return self.raise_on_error
 
 
     def test_fatal_error(self, message):
     def test_fatal_error(self, message):
-        """Force the messenger server to call G_fatal_error()
-        """
+        """Force the messenger server to call G_fatal_error()"""
         import time
         import time
+
         self._check_restart_server()
         self._check_restart_server()
         self.client_conn.send(["FATAL", message])
         self.client_conn.send(["FATAL", message])
         time.sleep(1)
         time.sleep(1)
 
 
 
 
-def get_msgr(_instance=[None, ], *args, **kwargs):
+def get_msgr(
+    _instance=[
+        None,
+    ],
+    *args,
+    **kwargs,
+):
     """Return a Messenger instance.
     """Return a Messenger instance.
 
 
        :returns: the Messenger instance.
        :returns: the Messenger instance.
@@ -356,4 +362,5 @@ def get_msgr(_instance=[None, ], *args, **kwargs):
 
 
 if __name__ == "__main__":
 if __name__ == "__main__":
     import doctest
     import doctest
+
     doctest.testmod()
     doctest.testmod()

+ 9 - 7
python/grass/pygrass/messages/testsuite/test_pygrass_messages_doctests.py

@@ -17,12 +17,14 @@ import grass.pygrass.messages as gmessages
 # and contains doctest's methods
 # and contains doctest's methods
 # the alternative is to copy 500 from doctest and change what is needed
 # the alternative is to copy 500 from doctest and change what is needed
 # (this might be necessary anyway because of the reports and stdout and stderr)
 # (this might be necessary anyway because of the reports and stdout and stderr)
-doctest.DocFileCase = type('DocFileCase',
-                           (grass.gunittest.case.TestCase,),
-                           dict(doctest.DocFileCase.__dict__))
-doctest.SkipDocTestCase = type('SkipDocTestCase',
-                               (grass.gunittest.case.TestCase,),
-                               dict(doctest.SkipDocTestCase.__dict__))
+doctest.DocFileCase = type(
+    "DocFileCase", (grass.gunittest.case.TestCase,), dict(doctest.DocFileCase.__dict__)
+)
+doctest.SkipDocTestCase = type(
+    "SkipDocTestCase",
+    (grass.gunittest.case.TestCase,),
+    dict(doctest.SkipDocTestCase.__dict__),
+)
 
 
 
 
 def load_tests(loader, tests, ignore):
 def load_tests(loader, tests, ignore):
@@ -35,5 +37,5 @@ def load_tests(loader, tests, ignore):
     return tests
     return tests
 
 
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     grass.gunittest.main.test()
     grass.gunittest.main.test()

+ 178 - 121
python/grass/pygrass/modules/grid/grid.py

@@ -1,6 +1,13 @@
 # -*- coding: utf-8 -*-
 # -*- coding: utf-8 -*-
-from __future__ import (nested_scopes, generators, division, absolute_import,
-                        with_statement, print_function, unicode_literals)
+from __future__ import (
+    nested_scopes,
+    generators,
+    division,
+    absolute_import,
+    with_statement,
+    print_function,
+    unicode_literals,
+)
 import os
 import os
 import sys
 import sys
 import multiprocessing as mltp
 import multiprocessing as mltp
@@ -90,8 +97,8 @@ def copy_mapset(mapset, path):
     >>> shutil.rmtree(path)
     >>> shutil.rmtree(path)
 
 
     """
     """
-    per_old = os.path.join(mapset.gisdbase, mapset.location, 'PERMANENT')
-    per_new = os.path.join(path, 'PERMANENT')
+    per_old = os.path.join(mapset.gisdbase, mapset.location, "PERMANENT")
+    per_new = os.path.join(path, "PERMANENT")
     map_old = mapset.path()
     map_old = mapset.path()
     map_new = os.path.join(path, mapset.name)
     map_new = os.path.join(path, mapset.name)
     if not os.path.isdir(per_new):
     if not os.path.isdir(per_new):
@@ -120,10 +127,11 @@ def read_gisrc(gisrc):
     ...                                      genv['GISDBASE']))
     ...                                      genv['GISDBASE']))
     True
     True
     """
     """
-    with open(gisrc, 'r') as gfile:
-        gis = dict([(k.strip(), v.strip())
-                    for k, v in [row.split(':', 1) for row in gfile]])
-    return gis['MAPSET'], gis['LOCATION_NAME'], gis['GISDBASE']
+    with open(gisrc, "r") as gfile:
+        gis = dict(
+            [(k.strip(), v.strip()) for k, v in [row.split(":", 1) for row in gfile]]
+        )
+    return gis["MAPSET"], gis["LOCATION_NAME"], gis["GISDBASE"]
 
 
 
 
 def get_mapset(gisrc_src, gisrc_dst):
 def get_mapset(gisrc_src, gisrc_dst):
@@ -171,32 +179,33 @@ def copy_groups(groups, gisrc_src, gisrc_dst, region=None):
     """
     """
 
 
     def rmloc(r):
     def rmloc(r):
-        return r.split('@')[0] if '@' in r else r
+        return r.split("@")[0] if "@" in r else r
 
 
     env = os.environ.copy()
     env = os.environ.copy()
     # instantiate modules
     # instantiate modules
-    get_grp = Module('i.group', flags='lg', stdout_=sub.PIPE, run_=False)
-    set_grp = Module('i.group')
+    get_grp = Module("i.group", flags="lg", stdout_=sub.PIPE, run_=False)
+    set_grp = Module("i.group")
     get_grp.run_ = True
     get_grp.run_ = True
 
 
     src = read_gisrc(gisrc_src)
     src = read_gisrc(gisrc_src)
     dst = read_gisrc(gisrc_dst)
     dst = read_gisrc(gisrc_dst)
     rm = True if src[2] != dst[2] else False
     rm = True if src[2] != dst[2] else False
-    all_rasts = [r[0]
-                 for r in findmaps('raster', location=dst[1], gisdbase=dst[2])]
+    all_rasts = [r[0] for r in findmaps("raster", location=dst[1], gisdbase=dst[2])]
     for grp in groups:
     for grp in groups:
         # change gisdbase to src
         # change gisdbase to src
-        env['GISRC'] = gisrc_src
+        env["GISRC"] = gisrc_src
         get_grp(group=grp, env_=env)
         get_grp(group=grp, env_=env)
         rasts = [r for r in get_grp.outputs.stdout.split()]
         rasts = [r for r in get_grp.outputs.stdout.split()]
         # change gisdbase to dst
         # change gisdbase to dst
-        env['GISRC'] = gisrc_dst
+        env["GISRC"] = gisrc_dst
         rast2cp = [r for r in rasts if rmloc(r) not in all_rasts]
         rast2cp = [r for r in rasts if rmloc(r) not in all_rasts]
         if rast2cp:
         if rast2cp:
             copy_rasters(rast2cp, gisrc_src, gisrc_dst, region=region)
             copy_rasters(rast2cp, gisrc_src, gisrc_dst, region=region)
-        set_grp(group=grp,
-                input=[rmloc(r) for r in rasts] if rast2cp or rm else rasts,
-                env_=env)
+        set_grp(
+            group=grp,
+            input=[rmloc(r) for r in rasts] if rast2cp or rm else rasts,
+            env_=env,
+        )
 
 
 
 
 def set_region(region, gisrc_src, gisrc_dst, env):
 def set_region(region, gisrc_src, gisrc_dst, env):
@@ -214,13 +223,15 @@ def set_region(region, gisrc_src, gisrc_dst, env):
     :type env:
     :type env:
     :returns: None
     :returns: None
     """
     """
-    reg_str = "g.region n=%(north)r s=%(south)r " \
-              "e=%(east)r w=%(west)r " \
-              "nsres=%(nsres)r ewres=%(ewres)r"
+    reg_str = (
+        "g.region n=%(north)r s=%(south)r "
+        "e=%(east)r w=%(west)r "
+        "nsres=%(nsres)r ewres=%(ewres)r"
+    )
     reg_cmd = reg_str % dict(region.items())
     reg_cmd = reg_str % dict(region.items())
-    env['GISRC'] = gisrc_src
+    env["GISRC"] = gisrc_src
     sub.Popen(reg_cmd, shell=True, env=env)
     sub.Popen(reg_cmd, shell=True, env=env)
-    env['GISRC'] = gisrc_dst
+    env["GISRC"] = gisrc_dst
     sub.Popen(reg_cmd, shell=True, env=env)
     sub.Popen(reg_cmd, shell=True, env=env)
 
 
 
 
@@ -245,25 +256,25 @@ def copy_rasters(rasters, gisrc_src, gisrc_dst, region=None):
         set_region(region, gisrc_src, gisrc_dst, env)
         set_region(region, gisrc_src, gisrc_dst, env)
 
 
     path_dst = os.path.join(*read_gisrc(gisrc_dst)[::-1])
     path_dst = os.path.join(*read_gisrc(gisrc_dst)[::-1])
-    nam = "copy%d__%s" % (id(gisrc_dst), '%s')
+    nam = "copy%d__%s" % (id(gisrc_dst), "%s")
 
 
     # instantiate modules
     # instantiate modules
-    mpclc = Module('r.mapcalc')
-    rpck = Module('r.pack')
-    rupck = Module('r.unpack')
-    remove = Module('g.remove')
+    mpclc = Module("r.mapcalc")
+    rpck = Module("r.pack")
+    rupck = Module("r.unpack")
+    remove = Module("g.remove")
 
 
     for rast in rasters:
     for rast in rasters:
-        rast_clean = rast.split('@')[0] if '@' in rast else rast
+        rast_clean = rast.split("@")[0] if "@" in rast else rast
         # change gisdbase to src
         # change gisdbase to src
-        env['GISRC'] = gisrc_src
+        env["GISRC"] = gisrc_src
         name = nam % rast_clean
         name = nam % rast_clean
         mpclc(expression="%s=%s" % (name, rast), overwrite=True, env_=env)
         mpclc(expression="%s=%s" % (name, rast), overwrite=True, env_=env)
         file_dst = "%s.pack" % os.path.join(path_dst, name)
         file_dst = "%s.pack" % os.path.join(path_dst, name)
         rpck(input=name, output=file_dst, overwrite=True, env_=env)
         rpck(input=name, output=file_dst, overwrite=True, env_=env)
-        remove(flags='f', type='raster', name=name, env_=env)
+        remove(flags="f", type="raster", name=name, env_=env)
         # change gisdbase to dst
         # change gisdbase to dst
-        env['GISRC'] = gisrc_dst
+        env["GISRC"] = gisrc_dst
         rupck(input=file_dst, output=rast_clean, overwrite=True, env_=env)
         rupck(input=file_dst, output=rast_clean, overwrite=True, env_=env)
         os.remove(file_dst)
         os.remove(file_dst)
 
 
@@ -282,22 +293,22 @@ def copy_vectors(vectors, gisrc_src, gisrc_dst):
     """
     """
     env = os.environ.copy()
     env = os.environ.copy()
     path_dst = os.path.join(*read_gisrc(gisrc_dst))
     path_dst = os.path.join(*read_gisrc(gisrc_dst))
-    nam = "copy%d__%s" % (id(gisrc_dst), '%s')
+    nam = "copy%d__%s" % (id(gisrc_dst), "%s")
 
 
     # instantiate modules
     # instantiate modules
-    vpck = Module('v.pack')
-    vupck = Module('v.unpack')
-    remove = Module('g.remove')
+    vpck = Module("v.pack")
+    vupck = Module("v.unpack")
+    remove = Module("g.remove")
 
 
     for vect in vectors:
     for vect in vectors:
         # change gisdbase to src
         # change gisdbase to src
-        env['GISRC'] = gisrc_src
+        env["GISRC"] = gisrc_src
         name = nam % vect
         name = nam % vect
         file_dst = "%s.pack" % os.path.join(path_dst, name)
         file_dst = "%s.pack" % os.path.join(path_dst, name)
         vpck(input=name, output=file_dst, overwrite=True, env_=env)
         vpck(input=name, output=file_dst, overwrite=True, env_=env)
-        remove(flags='f', type='vector', name=name, env_=env)
+        remove(flags="f", type="vector", name=name, env_=env)
         # change gisdbase to dst
         # change gisdbase to dst
-        env['GISRC'] = gisrc_dst
+        env["GISRC"] = gisrc_dst
         vupck(input=file_dst, output=vect, overwrite=True, env_=env)
         vupck(input=file_dst, output=vect, overwrite=True, env_=env)
         os.remove(file_dst)
         os.remove(file_dst)
 
 
@@ -316,19 +327,33 @@ def get_cmd(cmdd):
     >>> get_cmd(slp.get_dict())  # doctest: +ELLIPSIS
     >>> get_cmd(slp.get_dict())  # doctest: +ELLIPSIS
     ['r.slope.aspect', 'elevation=ele', 'format=degrees', ..., '--o']
     ['r.slope.aspect', 'elevation=ele', 'format=degrees', ..., '--o']
     """
     """
-    cmd = [cmdd['name'], ]
-    cmd.extend(("%s=%s" % (k, v) for k, v in cmdd['inputs']
-                if not isinstance(v, list)))
-    cmd.extend(("%s=%s" % (k, ','.join(vals if isinstance(vals[0], str)
-                                       else [repr(v) for v in vals]))
-                for k, vals in cmdd['inputs']
-                if isinstance(vals, list)))
-    cmd.extend(("%s=%s" % (k, v) for k, v in cmdd['outputs']
-                if not isinstance(v, list)))
-    cmd.extend(("%s=%s" % (k, ','.join([repr(v) for v in vals]))
-                for k, vals in cmdd['outputs'] if isinstance(vals, list)))
-    cmd.extend(("-%s" % (flg) for flg in cmdd['flags'] if len(flg) == 1))
-    cmd.extend(("--%s" % (flg[0]) for flg in cmdd['flags'] if len(flg) > 1))
+    cmd = [
+        cmdd["name"],
+    ]
+    cmd.extend(("%s=%s" % (k, v) for k, v in cmdd["inputs"] if not isinstance(v, list)))
+    cmd.extend(
+        (
+            "%s=%s"
+            % (
+                k,
+                ",".join(vals if isinstance(vals[0], str) else [repr(v) for v in vals]),
+            )
+            for k, vals in cmdd["inputs"]
+            if isinstance(vals, list)
+        )
+    )
+    cmd.extend(
+        ("%s=%s" % (k, v) for k, v in cmdd["outputs"] if not isinstance(v, list))
+    )
+    cmd.extend(
+        (
+            "%s=%s" % (k, ",".join([repr(v) for v in vals]))
+            for k, vals in cmdd["outputs"]
+            if isinstance(vals, list)
+        )
+    )
+    cmd.extend(("-%s" % (flg) for flg in cmdd["flags"] if len(flg) == 1))
+    cmd.extend(("--%s" % (flg[0]) for flg in cmdd["flags"] if len(flg) > 1))
     return cmd
     return cmd
 
 
 
 
@@ -356,19 +381,21 @@ def cmd_exe(args):
     bbox, mapnames, gisrc_src, gisrc_dst, cmd, groups = args
     bbox, mapnames, gisrc_src, gisrc_dst, cmd, groups = args
     src, dst = get_mapset(gisrc_src, gisrc_dst)
     src, dst = get_mapset(gisrc_src, gisrc_dst)
     env = os.environ.copy()
     env = os.environ.copy()
-    env['GISRC'] = gisrc_dst
-    shell = True if sys.platform == 'win32' else False
+    env["GISRC"] = gisrc_dst
+    shell = True if sys.platform == "win32" else False
     if mapnames:
     if mapnames:
-        inputs = dict(cmd['inputs'])
+        inputs = dict(cmd["inputs"])
         # reset the inputs to
         # reset the inputs to
         for key in mapnames:
         for key in mapnames:
             inputs[key] = mapnames[key]
             inputs[key] = mapnames[key]
-        cmd['inputs'] = inputs.items()
+        cmd["inputs"] = inputs.items()
         # set the region to the tile
         # set the region to the tile
-        sub.Popen(['g.region', 'raster=%s' % key], shell=shell, env=env).wait()
+        sub.Popen(["g.region", "raster=%s" % key], shell=shell, env=env).wait()
     else:
     else:
         # set the computational region
         # set the computational region
-        lcmd = ['g.region', ]
+        lcmd = [
+            "g.region",
+        ]
         lcmd.extend(["%s=%s" % (k, v) for k, v in bbox.items()])
         lcmd.extend(["%s=%s" % (k, v) for k, v in bbox.items()])
         sub.Popen(lcmd, shell=shell, env=env).wait()
         sub.Popen(lcmd, shell=shell, env=env).wait()
     if groups:
     if groups:
@@ -410,11 +437,26 @@ class GridModule(object):
     >>> grd.run()
     >>> grd.run()
     """
     """
 
 
-    def __init__(self, cmd, width=None, height=None, overlap=0, processes=None,
-                 split=False, debug=False, region=None, move=None, log=False,
-                 start_row=0, start_col=0, out_prefix='', mapset_prefix=None,
-                 *args, **kargs):
-        kargs['run_'] = False
+    def __init__(
+        self,
+        cmd,
+        width=None,
+        height=None,
+        overlap=0,
+        processes=None,
+        split=False,
+        debug=False,
+        region=None,
+        move=None,
+        log=False,
+        start_row=0,
+        start_col=0,
+        out_prefix="",
+        mapset_prefix=None,
+        *args,
+        **kargs,
+    ):
+        kargs["run_"] = False
         self.mset = Mapset()
         self.mset = Mapset()
         self.module = Module(cmd, *args, **kargs)
         self.module = Module(cmd, *args, **kargs)
         self.width = width
         self.width = width
@@ -427,31 +469,31 @@ class GridModule(object):
         self.out_prefix = out_prefix
         self.out_prefix = out_prefix
         self.log = log
         self.log = log
         self.move = move
         self.move = move
-        self.gisrc_src = os.environ['GISRC']
+        self.gisrc_src = os.environ["GISRC"]
         self.n_mset, self.gisrc_dst = None, None
         self.n_mset, self.gisrc_dst = None, None
         if self.move:
         if self.move:
             self.n_mset = copy_mapset(self.mset, self.move)
             self.n_mset = copy_mapset(self.mset, self.move)
-            self.gisrc_dst = write_gisrc(self.n_mset.gisdbase,
-                                         self.n_mset.location,
-                                         self.n_mset.name)
-            rasters = [r for r in select(self.module.inputs, 'raster')]
+            self.gisrc_dst = write_gisrc(
+                self.n_mset.gisdbase, self.n_mset.location, self.n_mset.name
+            )
+            rasters = [r for r in select(self.module.inputs, "raster")]
             if rasters:
             if rasters:
-                copy_rasters(rasters, self.gisrc_src, self.gisrc_dst,
-                             region=self.region)
-            vectors = [v for v in select(self.module.inputs, 'vector')]
+                copy_rasters(
+                    rasters, self.gisrc_src, self.gisrc_dst, region=self.region
+                )
+            vectors = [v for v in select(self.module.inputs, "vector")]
             if vectors:
             if vectors:
                 copy_vectors(vectors, self.gisrc_src, self.gisrc_dst)
                 copy_vectors(vectors, self.gisrc_src, self.gisrc_dst)
-            groups = [g for g in select(self.module.inputs, 'group')]
+            groups = [g for g in select(self.module.inputs, "group")]
             if groups:
             if groups:
-                copy_groups(groups, self.gisrc_src, self.gisrc_dst,
-                            region=self.region)
-        self.bboxes = split_region_tiles(region=region,
-                                         width=width, height=height,
-                                         overlap=overlap)
+                copy_groups(groups, self.gisrc_src, self.gisrc_dst, region=self.region)
+        self.bboxes = split_region_tiles(
+            region=region, width=width, height=height, overlap=overlap
+        )
         if mapset_prefix:
         if mapset_prefix:
             self.msetstr = mapset_prefix + "_%03d_%03d"
             self.msetstr = mapset_prefix + "_%03d_%03d"
         else:
         else:
-            self.msetstr = cmd.replace('.', '') + "_%03d_%03d"
+            self.msetstr = cmd.replace(".", "") + "_%03d_%03d"
         self.inlist = None
         self.inlist = None
         if split:
         if split:
             self.split()
             self.split()
@@ -473,7 +515,7 @@ class GridModule(object):
                 self.n_mset.current()
                 self.n_mset.current()
             location = Location()
             location = Location()
 
 
-        mapsets = location.mapsets(self.msetstr.split('_')[0] + '_*')
+        mapsets = location.mapsets(self.msetstr.split("_")[0] + "_*")
         for mset in mapsets:
         for mset in mapsets:
             Mapset(mset).delete()
             Mapset(mset).delete()
         if self.n_mset and self.n_mset.is_current():
         if self.n_mset and self.n_mset.is_current():
@@ -481,15 +523,18 @@ class GridModule(object):
 
 
     def split(self):
     def split(self):
         """Split all the raster inputs using r.tile"""
         """Split all the raster inputs using r.tile"""
-        rtile = Module('r.tile')
+        rtile = Module("r.tile")
         inlist = {}
         inlist = {}
-        for inm in select(self.module.inputs, 'raster'):
-            rtile(input=inm.value, output=inm.value,
-                  width=self.width, height=self.height,
-                  overlap=self.overlap)
-            patt = '%s-*' % inm.value
-            inlist[inm.value] = sorted(self.mset.glist(type='raster',
-                                                       pattern=patt))
+        for inm in select(self.module.inputs, "raster"):
+            rtile(
+                input=inm.value,
+                output=inm.value,
+                width=self.width,
+                height=self.height,
+                overlap=self.overlap,
+            )
+            patt = "%s-*" % inm.value
+            inlist[inm.value] = sorted(self.mset.glist(type="raster", pattern=patt))
         self.inlist = inlist
         self.inlist = inlist
 
 
     def get_works(self):
     def get_works(self):
@@ -501,7 +546,7 @@ class GridModule(object):
         else:
         else:
             ldst, gdst = self.mset.location, self.mset.gisdbase
             ldst, gdst = self.mset.location, self.mset.gisdbase
         cmd = self.module.get_dict()
         cmd = self.module.get_dict()
-        groups = [g for g in select(self.module.inputs, 'group')]
+        groups = [g for g in select(self.module.inputs, "group")]
         for row, box_row in enumerate(self.bboxes):
         for row, box_row in enumerate(self.bboxes):
             for col, box in enumerate(box_row):
             for col, box in enumerate(box_row):
                 inms = None
                 inms = None
@@ -510,29 +555,34 @@ class GridModule(object):
                     cols = len(box_row)
                     cols = len(box_row)
                     for key in self.inlist:
                     for key in self.inlist:
                         indx = row * cols + col
                         indx = row * cols + col
-                        inms[key] = "%s@%s" % (self.inlist[key][indx],
-                                               self.mset.name)
+                        inms[key] = "%s@%s" % (self.inlist[key][indx], self.mset.name)
                 # set the computational region, prepare the region parameters
                 # set the computational region, prepare the region parameters
                 bbox = dict([(k[0], str(v)) for k, v in box.items()[:-2]])
                 bbox = dict([(k[0], str(v)) for k, v in box.items()[:-2]])
-                bbox['nsres'] = '%f' % reg.nsres
-                bbox['ewres'] = '%f' % reg.ewres
-                new_mset = self.msetstr % (self.start_row + row,
-                                           self.start_col + col),
-                works.append((bbox, inms,
-                              self.gisrc_src,
-                              write_gisrc(gdst, ldst, new_mset),
-                              cmd, groups))
+                bbox["nsres"] = "%f" % reg.nsres
+                bbox["ewres"] = "%f" % reg.ewres
+                new_mset = (
+                    self.msetstr % (self.start_row + row, self.start_col + col),
+                )
+                works.append(
+                    (
+                        bbox,
+                        inms,
+                        self.gisrc_src,
+                        write_gisrc(gdst, ldst, new_mset),
+                        cmd,
+                        groups,
+                    )
+                )
         return works
         return works
 
 
     def define_mapset_inputs(self):
     def define_mapset_inputs(self):
-        """Add the mapset information to the input maps
-        """
+        """Add the mapset information to the input maps"""
         for inmap in self.module.inputs:
         for inmap in self.module.inputs:
             inm = self.module.inputs[inmap]
             inm = self.module.inputs[inmap]
-            if inm.type in ('raster', 'vector') and inm.value:
-                if '@' not in inm.value:
+            if inm.type in ("raster", "vector") and inm.value:
+                if "@" not in inm.value:
                     mset = get_mapset_raster(inm.value)
                     mset = get_mapset_raster(inm.value)
-                    inm.value = inm.value + '@%s' % mset
+                    inm.value = inm.value + "@%s" % mset
 
 
     def run(self, patch=True, clean=True):
     def run(self, patch=True, clean=True):
         """Run the GRASS command
         """Run the GRASS command
@@ -559,14 +609,15 @@ class GridModule(object):
 
 
         if patch:
         if patch:
             if self.move:
             if self.move:
-                os.environ['GISRC'] = self.gisrc_dst
+                os.environ["GISRC"] = self.gisrc_dst
                 self.n_mset.current()
                 self.n_mset.current()
                 self.patch()
                 self.patch()
-                os.environ['GISRC'] = self.gisrc_src
+                os.environ["GISRC"] = self.gisrc_src
                 self.mset.current()
                 self.mset.current()
                 # copy the outputs from dst => src
                 # copy the outputs from dst => src
-                routputs = [self.out_prefix + o
-                            for o in select(self.module.outputs, 'raster')]
+                routputs = [
+                    self.out_prefix + o for o in select(self.module.outputs, "raster")
+                ]
                 copy_rasters(routputs, self.gisrc_dst, self.gisrc_src)
                 copy_rasters(routputs, self.gisrc_dst, self.gisrc_src)
             else:
             else:
                 self.patch()
                 self.patch()
@@ -574,16 +625,16 @@ class GridModule(object):
         if self.log:
         if self.log:
             # record in the temp directory
             # record in the temp directory
             from grass.lib.gis import G_tempfile
             from grass.lib.gis import G_tempfile
+
             tmp, dummy = os.path.split(G_tempfile())
             tmp, dummy = os.path.split(G_tempfile())
             tmpdir = os.path.join(tmp, self.module.name)
             tmpdir = os.path.join(tmp, self.module.name)
             for k in self.module.outputs:
             for k in self.module.outputs:
                 par = self.module.outputs[k]
                 par = self.module.outputs[k]
-                if par.typedesc == 'raster' and par.value:
+                if par.typedesc == "raster" and par.value:
                     dirpath = os.path.join(tmpdir, par.name)
                     dirpath = os.path.join(tmpdir, par.name)
                     if not os.path.isdir(dirpath):
                     if not os.path.isdir(dirpath):
                         os.makedirs(dirpath)
                         os.makedirs(dirpath)
-                    fil = open(os.path.join(dirpath,
-                                            self.out_prefix + par.value), 'w+')
+                    fil = open(os.path.join(dirpath, self.out_prefix + par.value), "w+")
                     fil.close()
                     fil.close()
 
 
         if clean:
         if clean:
@@ -595,7 +646,7 @@ class GridModule(object):
                 # rm temporary gis_rc
                 # rm temporary gis_rc
                 os.remove(self.gisrc_dst)
                 os.remove(self.gisrc_dst)
                 self.gisrc_dst = None
                 self.gisrc_dst = None
-                sht.rmtree(os.path.join(self.move, 'PERMANENT'))
+                sht.rmtree(os.path.join(self.move, "PERMANENT"))
                 sht.rmtree(os.path.join(self.move, self.mset.name))
                 sht.rmtree(os.path.join(self.move, self.mset.name))
 
 
     def patch(self):
     def patch(self):
@@ -607,22 +658,28 @@ class GridModule(object):
         noutputs = 0
         noutputs = 0
         for otmap in self.module.outputs:
         for otmap in self.module.outputs:
             otm = self.module.outputs[otmap]
             otm = self.module.outputs[otmap]
-            if otm.typedesc == 'raster' and otm.value:
-                rpatch_map(otm.value,
-                           self.mset.name, self.msetstr, bboxes,
-                           self.module.flags.overwrite,
-                           self.start_row, self.start_col, self.out_prefix)
+            if otm.typedesc == "raster" and otm.value:
+                rpatch_map(
+                    otm.value,
+                    self.mset.name,
+                    self.msetstr,
+                    bboxes,
+                    self.module.flags.overwrite,
+                    self.start_row,
+                    self.start_col,
+                    self.out_prefix,
+                )
                 noutputs += 1
                 noutputs += 1
         if noutputs < 1:
         if noutputs < 1:
-            msg = 'No raster output option defined for <{}>'.format(self.module.name)
-            if self.module.name == 'r.mapcalc':
-                msg += '. Use <{}.simple> instead'.format(self.module.name)
+            msg = "No raster output option defined for <{}>".format(self.module.name)
+            if self.module.name == "r.mapcalc":
+                msg += ". Use <{}.simple> instead".format(self.module.name)
             raise RuntimeError(msg)
             raise RuntimeError(msg)
 
 
     def rm_tiles(self):
     def rm_tiles(self):
         """Remove all the tiles."""
         """Remove all the tiles."""
         # if split, remove tiles
         # if split, remove tiles
         if self.inlist:
         if self.inlist:
-            grm = Module('g.remove')
+            grm = Module("g.remove")
             for key in self.inlist:
             for key in self.inlist:
-                grm(flags='f', type='raster', name=self.inlist[key])
+                grm(flags="f", type="raster", name=self.inlist[key])

+ 28 - 11
python/grass/pygrass/modules/grid/patch.py

@@ -4,8 +4,15 @@ Created on Tue Apr  2 18:57:42 2013
 
 
 @author: pietro
 @author: pietro
 """
 """
-from __future__ import (nested_scopes, generators, division, absolute_import,
-                        with_statement, print_function, unicode_literals)
+from __future__ import (
+    nested_scopes,
+    generators,
+    division,
+    absolute_import,
+    with_statement,
+    print_function,
+    unicode_literals,
+)
 from grass.pygrass.gis.region import Region
 from grass.pygrass.gis.region import Region
 from grass.pygrass.raster import RasterRow
 from grass.pygrass.raster import RasterRow
 from grass.pygrass.utils import coor2pixel
 from grass.pygrass.utils import coor2pixel
@@ -51,8 +58,16 @@ def rpatch_row(rast, rasts, bboxes):
         rast.put_row(rbuff)
         rast.put_row(rbuff)
 
 
 
 
-def rpatch_map(raster, mapset, mset_str, bbox_list, overwrite=False,
-               start_row=0, start_col=0, prefix=''):
+def rpatch_map(
+    raster,
+    mapset,
+    mset_str,
+    bbox_list,
+    overwrite=False,
+    start_row=0,
+    start_col=0,
+    prefix="",
+):
     # TODO is prefix useful??
     # TODO is prefix useful??
     """Patch raster using a bounding box list to trim the raster.
     """Patch raster using a bounding box list to trim the raster.
 
 
@@ -76,22 +91,24 @@ def rpatch_map(raster, mapset, mset_str, bbox_list, overwrite=False,
     # Instantiate the RasterRow input objects
     # Instantiate the RasterRow input objects
     rast = RasterRow(prefix + raster, mapset)
     rast = RasterRow(prefix + raster, mapset)
     rtype = RasterRow(name=raster, mapset=mset_str % (0, 0))
     rtype = RasterRow(name=raster, mapset=mset_str % (0, 0))
-    rtype.open('r')
-    rast.open('w', mtype=rtype.mtype, overwrite=overwrite)
+    rtype.open("r")
+    rast.open("w", mtype=rtype.mtype, overwrite=overwrite)
     rtype.close()
     rtype.close()
     rasts = []
     rasts = []
     for row, rbbox in enumerate(bbox_list):
     for row, rbbox in enumerate(bbox_list):
         rrasts = []
         rrasts = []
         for col in range(len(rbbox)):
         for col in range(len(rbbox)):
-            rrasts.append(RasterRow(name=raster,
-                                    mapset=mset_str % (start_row + row,
-                                                       start_col + col)))
-            rrasts[-1].open('r')
+            rrasts.append(
+                RasterRow(
+                    name=raster, mapset=mset_str % (start_row + row, start_col + col)
+                )
+            )
+            rrasts[-1].open("r")
         rasts.append(rrasts)
         rasts.append(rrasts)
         rpatch_row(rast, rrasts, rbbox)
         rpatch_row(rast, rrasts, rbbox)
 
 
         for rst in rrasts:
         for rst in rrasts:
             rst.close()
             rst.close()
-            del(rst)
+            del rst
 
 
     rast.close()
     rast.close()

+ 19 - 10
python/grass/pygrass/modules/grid/split.py

@@ -4,8 +4,15 @@ Created on Tue Apr  2 19:00:15 2013
 
 
 @author: pietro
 @author: pietro
 """
 """
-from __future__ import (nested_scopes, generators, division, absolute_import,
-                        with_statement, print_function, unicode_literals)
+from __future__ import (
+    nested_scopes,
+    generators,
+    division,
+    absolute_import,
+    with_statement,
+    print_function,
+    unicode_literals,
+)
 from grass.pygrass.gis.region import Region
 from grass.pygrass.gis.region import Region
 from grass.pygrass.vector.basic import Bbox
 from grass.pygrass.vector.basic import Bbox
 
 
@@ -30,10 +37,12 @@ def get_bbox(reg, row, col, width, height, overlap):
     south = reg.north - ((row + 1) * height + overlap) * reg.nsres
     south = reg.north - ((row + 1) * height + overlap) * reg.nsres
     east = reg.west + ((col + 1) * width + overlap) * reg.ewres
     east = reg.west + ((col + 1) * width + overlap) * reg.ewres
     west = reg.west + (col * width - overlap) * reg.ewres
     west = reg.west + (col * width - overlap) * reg.ewres
-    return Bbox(north=north if north <= reg.north else reg.north,
-                south=south if south >= reg.south else reg.south,
-                east=east if east <= reg.east else reg.east,
-                west=west if west >= reg.west else reg.west,)
+    return Bbox(
+        north=north if north <= reg.north else reg.north,
+        south=south if south >= reg.south else reg.south,
+        east=east if east <= reg.east else reg.east,
+        west=west if west >= reg.west else reg.west,
+    )
 
 
 
 
 def split_region_tiles(region=None, width=100, height=100, overlap=0):
 def split_region_tiles(region=None, width=100, height=100, overlap=0):
@@ -70,11 +79,11 @@ def split_region_tiles(region=None, width=100, height=100, overlap=0):
     ncols = (reg.cols + width - 1) // width
     ncols = (reg.cols + width - 1) // width
     nrows = (reg.rows + height - 1) // height
     nrows = (reg.rows + height - 1) // height
     box_list = []
     box_list = []
-    #print reg
+    # print reg
     for row in range(nrows):
     for row in range(nrows):
         row_list = []
         row_list = []
         for col in range(ncols):
         for col in range(ncols):
-            #print 'c', c, 'r', r
+            # print 'c', c, 'r', r
             row_list.append(get_bbox(reg, row, col, width, height, overlap))
             row_list.append(get_bbox(reg, row, col, width, height, overlap))
         box_list.append(row_list)
         box_list.append(row_list)
     return box_list
     return box_list
@@ -96,11 +105,11 @@ def get_overlap_region_tiles(region=None, width=100, height=100, overlap=0):
     ncols = (reg.cols + width - 1) // width
     ncols = (reg.cols + width - 1) // width
     nrows = (reg.rows + height - 1) // height
     nrows = (reg.rows + height - 1) // height
     box_list = []
     box_list = []
-    #print reg
+    # print reg
     for row in range(nrows):
     for row in range(nrows):
         row_list = []
         row_list = []
         for col in range(ncols):
         for col in range(ncols):
-            #print 'c', c, 'r', r
+            # print 'c', c, 'r', r
             row_list.append(get_bbox(reg, row, col, width, height, -overlap))
             row_list.append(get_bbox(reg, row, col, width, height, -overlap))
         box_list.append(row_list)
         box_list.append(row_list)
     return box_list
     return box_list

+ 10 - 8
python/grass/pygrass/modules/grid/testsuite/test_pygrass_modules_grid_doctests.py

@@ -17,12 +17,14 @@ import grass.pygrass.modules as gmodules
 # and contains doctest's methods
 # and contains doctest's methods
 # the alternative is to copy 500 from doctest and change what is needed
 # the alternative is to copy 500 from doctest and change what is needed
 # (this might be necessary anyway because of the reports and stdout and stderr)
 # (this might be necessary anyway because of the reports and stdout and stderr)
-doctest.DocFileCase = type('DocFileCase',
-                           (grass.gunittest.case.TestCase,),
-                           dict(doctest.DocFileCase.__dict__))
-doctest.SkipDocTestCase = type('SkipDocTestCase',
-                               (grass.gunittest.case.TestCase,),
-                               dict(doctest.SkipDocTestCase.__dict__))
+doctest.DocFileCase = type(
+    "DocFileCase", (grass.gunittest.case.TestCase,), dict(doctest.DocFileCase.__dict__)
+)
+doctest.SkipDocTestCase = type(
+    "SkipDocTestCase",
+    (grass.gunittest.case.TestCase,),
+    dict(doctest.SkipDocTestCase.__dict__),
+)
 
 
 
 
 def load_tests(loader, tests, ignore):
 def load_tests(loader, tests, ignore):
@@ -31,9 +33,9 @@ def load_tests(loader, tests, ignore):
     # for now it is the only place where it works
     # for now it is the only place where it works
     grass.gunittest.utils.do_doctest_gettext_workaround()
     grass.gunittest.utils.do_doctest_gettext_workaround()
 
 
-    #tests.addTests(doctest.DocTestSuite(gmodules.shortcuts))
+    # tests.addTests(doctest.DocTestSuite(gmodules.shortcuts))
     return tests
     return tests
 
 
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     grass.gunittest.main.test()
     grass.gunittest.main.test()

+ 5 - 1
python/grass/pygrass/modules/interface/__init__.py

@@ -10,4 +10,8 @@ from grass.pygrass.modules.interface import module
 from grass.pygrass.modules.interface import typedict
 from grass.pygrass.modules.interface import typedict
 from grass.pygrass.modules.interface import read
 from grass.pygrass.modules.interface import read
 
 
-from grass.pygrass.modules.interface.module import Module, MultiModule, ParallelModuleQueue
+from grass.pygrass.modules.interface.module import (
+    Module,
+    MultiModule,
+    ParallelModuleQueue,
+)

+ 2 - 0
python/grass/pygrass/modules/interface/docstring.py

@@ -20,8 +20,10 @@ def docstring_property(class_doc):
     >>> a.__doc__
     >>> a.__doc__
     'My value of x is 10.'
     'My value of x is 10.'
     """
     """
+
     def wrapper(fget):
     def wrapper(fget):
         return DocstringProperty(class_doc, fget)
         return DocstringProperty(class_doc, fget)
+
     return wrapper
     return wrapper
 
 
 
 

+ 12 - 8
python/grass/pygrass/modules/interface/env.py

@@ -11,18 +11,22 @@ import sys
 
 
 def get_env():
 def get_env():
     """Parse the GISRC file and return the GRASS variales"""
     """Parse the GISRC file and return the GRASS variales"""
-    gisrc = os.environ.get('GISRC')
+    gisrc = os.environ.get("GISRC")
     if gisrc is None:
     if gisrc is None:
-        raise RuntimeError('You are not in a GRASS session, GISRC not found.')
-    with open(gisrc, mode='r') as grc:
-        env = dict([(k.strip(), v.strip())
-                    for k, v in [row.split(':',1) for row in grc if row]])
+        raise RuntimeError("You are not in a GRASS session, GISRC not found.")
+    with open(gisrc, mode="r") as grc:
+        env = dict(
+            [
+                (k.strip(), v.strip())
+                for k, v in [row.split(":", 1) for row in grc if row]
+            ]
+        )
     return env
     return env
 
 
 
 
 def get_debug_level():
 def get_debug_level():
     """Return the debug level"""
     """Return the debug level"""
-    debug = get_env().get('DEBUG')
+    debug = get_env().get("DEBUG")
     return int(debug) if debug else 0
     return int(debug) if debug else 0
 
 
 
 
@@ -32,5 +36,5 @@ def G_debug(level, *msg):
     debug_level = get_debug_level()
     debug_level = get_debug_level()
     if debug_level >= level:
     if debug_level >= level:
         dfile = os.environ.get("GRASS_DEBUG_FILE")
         dfile = os.environ.get("GRASS_DEBUG_FILE")
-        fd = sys.stderr if dfile is None else open(dfile, mode='a')
-        print("D%d/%d: " % (level, debug_level), *msg, end='\n', file=fd)
+        fd = sys.stderr if dfile is None else open(dfile, mode="a")
+        print("D%d/%d: " % (level, debug_level), *msg, end="\n", file=fd)

+ 28 - 20
python/grass/pygrass/modules/interface/flag.py

@@ -1,6 +1,13 @@
 # -*- coding: utf-8 -*-
 # -*- coding: utf-8 -*-
-from __future__ import (nested_scopes, generators, division, absolute_import,
-                        with_statement, print_function, unicode_literals)
+from __future__ import (
+    nested_scopes,
+    generators,
+    division,
+    absolute_import,
+    with_statement,
+    print_function,
+    unicode_literals,
+)
 from grass.pygrass.modules.interface.docstring import docstring_property
 from grass.pygrass.modules.interface.docstring import docstring_property
 from grass.pygrass.modules.interface import read
 from grass.pygrass.modules.interface import read
 
 
@@ -28,13 +35,14 @@ class Flag(object):
     def __init__(self, xflag=None, diz=None):
     def __init__(self, xflag=None, diz=None):
         self.value = False
         self.value = False
         diz = read.element2dict(xflag) if xflag is not None else diz
         diz = read.element2dict(xflag) if xflag is not None else diz
-        self.name = diz['name']
-        self.special = True if self.name in (
-            'verbose', 'overwrite', 'quiet', 'run') else False
-        self.description = diz.get('description', None)
-        self.default = diz.get('default', None)
-        self.guisection = diz.get('guisection', None)
-        self.suppress_required = True if 'suppress_required' in diz else False
+        self.name = diz["name"]
+        self.special = (
+            True if self.name in ("verbose", "overwrite", "quiet", "run") else False
+        )
+        self.description = diz.get("description", None)
+        self.default = diz.get("default", None)
+        self.guisection = diz.get("guisection", None)
+        self.suppress_required = True if "suppress_required" in diz else False
 
 
     def get_bash(self):
     def get_bash(self):
         """Return the BASH representation of a flag.
         """Return the BASH representation of a flag.
@@ -55,11 +63,11 @@ class Flag(object):
         """
         """
         if self.value:
         if self.value:
             if self.special:
             if self.special:
-                return '--%s' % self.name[0]
+                return "--%s" % self.name[0]
             else:
             else:
-                return '-%s' % self.name
+                return "-%s" % self.name
         else:
         else:
-            return ''
+            return ""
 
 
     def get_python(self):
     def get_python(self):
         """Return the python representation of a flag.
         """Return the python representation of a flag.
@@ -79,8 +87,8 @@ class Flag(object):
         'overwrite=True'
         'overwrite=True'
         """
         """
         if self.value:
         if self.value:
-            return '%s=True' % self.name if self.special else self.name
-        return ''
+            return "%s=True" % self.name if self.special else self.name
+        return ""
 
 
     def __str__(self):
     def __str__(self):
         """Return the BASH representation of the flag."""
         """Return the BASH representation of the flag."""
@@ -116,9 +124,9 @@ class Flag(object):
             None
             None
 
 
         """
         """
-        return read.DOC['flag'].format(name=self.name,
-                                       default=repr(self.default),
-                                       description=self.description,
-                                       supress=('suppress required'
-                                                if self.suppress_required
-                                                else ''))
+        return read.DOC["flag"].format(
+            name=self.name,
+            default=repr(self.default),
+            description=self.description,
+            supress=("suppress required" if self.suppress_required else ""),
+        )

+ 128 - 78
python/grass/pygrass/modules/interface/module.py

@@ -1,6 +1,13 @@
 # -*- coding: utf-8 -*-
 # -*- coding: utf-8 -*-
-from __future__ import (nested_scopes, generators, division, absolute_import,
-                        with_statement, print_function, unicode_literals)
+from __future__ import (
+    nested_scopes,
+    generators,
+    division,
+    absolute_import,
+    with_statement,
+    print_function,
+    unicode_literals,
+)
 import sys
 import sys
 from multiprocessing import cpu_count, Process, Queue
 from multiprocessing import cpu_count, Process, Queue
 import time
 import time
@@ -20,6 +27,7 @@ if sys.version_info[0] == 2:
     from itertools import izip_longest as zip_longest
     from itertools import izip_longest as zip_longest
 else:
 else:
     from itertools import zip_longest
     from itertools import zip_longest
+
     unicode = str
     unicode = str
 
 
 
 
@@ -301,7 +309,11 @@ class ParallelModuleQueue(object):
         for proc in self._list:
         for proc in self._list:
             if proc:
             if proc:
                 if isinstance(proc, Module):
                 if isinstance(proc, Module):
-                    self._finished_modules.extend([proc.wait(),])
+                    self._finished_modules.extend(
+                        [
+                            proc.wait(),
+                        ]
+                    )
                 else:
                 else:
                     self._finished_modules.extend(proc.wait())
                     self._finished_modules.extend(proc.wait())
 
 
@@ -309,7 +321,6 @@ class ParallelModuleQueue(object):
         self._proc_count = 0
         self._proc_count = 0
 
 
 
 
-
 class Module(object):
 class Module(object):
     """This class is design to wrap/run/interact with the GRASS modules.
     """This class is design to wrap/run/interact with the GRASS modules.
 
 
@@ -538,7 +549,7 @@ class Module(object):
         tree = fromstring(self.xml)
         tree = fromstring(self.xml)
 
 
         for e in tree:
         for e in tree:
-            if e.tag not in ('parameter', 'flag'):
+            if e.tag not in ("parameter", "flag"):
                 self.__setattr__(e.tag, GETFROMTAG[e.tag](e))
                 self.__setattr__(e.tag, GETFROMTAG[e.tag](e))
 
 
         #
         #
@@ -577,18 +588,24 @@ class Module(object):
         self.stdin = None
         self.stdin = None
         self.stdout_ = None
         self.stdout_ = None
         self.stderr_ = None
         self.stderr_ = None
-        diz = {'name': 'stdin', 'required': False,
-               'multiple': False, 'type': 'all',
-               'value': None}
-        self.inputs['stdin'] = Parameter(diz=diz)
-        diz['name'] = 'stdout'
-        self.outputs['stdout'] = Parameter(diz=diz)
-        diz['name'] = 'stderr'
-        self.outputs['stderr'] = Parameter(diz=diz)
+        diz = {
+            "name": "stdin",
+            "required": False,
+            "multiple": False,
+            "type": "all",
+            "value": None,
+        }
+        self.inputs["stdin"] = Parameter(diz=diz)
+        diz["name"] = "stdout"
+        self.outputs["stdout"] = Parameter(diz=diz)
+        diz["name"] = "stderr"
+        self.outputs["stderr"] = Parameter(diz=diz)
         self.popen = None
         self.popen = None
         self.time = None
         self.time = None
-        self.start_time = None            # This variable will be set in the run() function
-        self._finished = False            # This variable is set True if wait() was successfully called
+        self.start_time = None  # This variable will be set in the run() function
+        self._finished = (
+            False  # This variable is set True if wait() was successfully called
+        )
 
 
         if args or kargs:
         if args or kargs:
             self.__call__(*args, **kargs)
             self.__call__(*args, **kargs)
@@ -607,18 +624,18 @@ class Module(object):
         #
         #
         # check for extra kargs, set attribute and remove from dictionary
         # check for extra kargs, set attribute and remove from dictionary
         #
         #
-        if 'flags' in kargs:
-            for flg in kargs['flags']:
+        if "flags" in kargs:
+            for flg in kargs["flags"]:
                 self.flags[flg].value = True
                 self.flags[flg].value = True
-            del(kargs['flags'])
+            del kargs["flags"]
 
 
         # set attributs
         # set attributs
-        for key in ('run_', 'env_', 'finish_', 'stdout_', 'stderr_', 'check_'):
+        for key in ("run_", "env_", "finish_", "stdout_", "stderr_", "check_"):
             if key in kargs:
             if key in kargs:
                 setattr(self, key, kargs.pop(key))
                 setattr(self, key, kargs.pop(key))
 
 
         # set inputs
         # set inputs
-        for key in ('stdin_', ):
+        for key in ("stdin_",):
             if key in kargs:
             if key in kargs:
                 self.inputs[key[:-1]].value = kargs.pop(key)
                 self.inputs[key[:-1]].value = kargs.pop(key)
 
 
@@ -628,7 +645,7 @@ class Module(object):
         for param, arg in zip(self.params_list, args):
         for param, arg in zip(self.params_list, args):
             param.value = arg
             param.value = arg
         for key, val in kargs.items():
         for key, val in kargs.items():
-            key = key.strip('_')
+            key = key.strip("_")
             if key in self.inputs:
             if key in self.inputs:
                 self.inputs[key].value = val
                 self.inputs[key].value = val
             elif key in self.outputs:
             elif key in self.outputs:
@@ -638,7 +655,7 @@ class Module(object):
                 # verbose and quiet) work like parameters
                 # verbose and quiet) work like parameters
                 self.flags[key].value = val
                 self.flags[key].value = val
             else:
             else:
-                raise ParameterError('%s is not a valid parameter.' % key)
+                raise ParameterError("%s is not a valid parameter." % key)
 
 
         #
         #
         # check if execute
         # check if execute
@@ -654,24 +671,32 @@ class Module(object):
 
 
     def get_bash(self):
     def get_bash(self):
         """Return a BASH representation of the Module."""
         """Return a BASH representation of the Module."""
-        return ' '.join(self.make_cmd())
+        return " ".join(self.make_cmd())
 
 
     def get_python(self):
     def get_python(self):
         """Return a Python representation of the Module."""
         """Return a Python representation of the Module."""
-        prefix = self.name.split('.')[0]
-        name = '_'.join(self.name.split('.')[1:])
-        params = ', '.join([par.get_python() for par in self.params_list
-                           if par.get_python() != ''])
-        flags = ''.join([flg.get_python()
-                         for flg in self.flags.values()
-                         if not flg.special and flg.get_python() != ''])
-        special = ', '.join([flg.get_python()
-                             for flg in self.flags.values()
-                             if flg.special and flg.get_python() != ''])
+        prefix = self.name.split(".")[0]
+        name = "_".join(self.name.split(".")[1:])
+        params = ", ".join(
+            [par.get_python() for par in self.params_list if par.get_python() != ""]
+        )
+        flags = "".join(
+            [
+                flg.get_python()
+                for flg in self.flags.values()
+                if not flg.special and flg.get_python() != ""
+            ]
+        )
+        special = ", ".join(
+            [
+                flg.get_python()
+                for flg in self.flags.values()
+                if flg.special and flg.get_python() != ""
+            ]
+        )
         #     pre name par flg special
         #     pre name par flg special
         if flags and special:
         if flags and special:
-            return "%s.%s(%s, flags=%r, %s)" % (prefix, name, params,
-                                                flags, special)
+            return "%s.%s(%s, flags=%r, %s)" % (prefix, name, params, flags, special)
         elif flags:
         elif flags:
             return "%s.%s(%s, flags=%r)" % (prefix, name, params, flags)
             return "%s.%s(%s, flags=%r)" % (prefix, name, params, flags)
         elif special:
         elif special:
@@ -681,26 +706,35 @@ class Module(object):
 
 
     def __str__(self):
     def __str__(self):
         """Return the command string that can be executed in a shell"""
         """Return the command string that can be executed in a shell"""
-        return ' '.join(self.make_cmd())
+        return " ".join(self.make_cmd())
 
 
     def __repr__(self):
     def __repr__(self):
         return "Module(%r)" % self.name
         return "Module(%r)" % self.name
 
 
     @docstring_property(__doc__)
     @docstring_property(__doc__)
     def __doc__(self):
     def __doc__(self):
-        """{cmd_name}({cmd_params})
-        """
-        head = DOC['head'].format(cmd_name=self.name,
-            cmd_params=('\n' +  # go to a new line
-             # give space under the function name
-             (' ' * (len(self.name) + 1))).join([', '.join(
-                 # transform each parameter in string
-                 [str(param) for param in line if param is not None])
-                 # make a list of parameters with only 3 param per line
-                 for line in zip_longest(*[iter(self.params_list)] * 3)]),)
-        params = '\n'.join([par.__doc__ for par in self.params_list])
+        """{cmd_name}({cmd_params})"""
+        head = DOC["head"].format(
+            cmd_name=self.name,
+            cmd_params=(
+                "\n"
+                +  # go to a new line
+                # give space under the function name
+                (" " * (len(self.name) + 1))
+            ).join(
+                [
+                    ", ".join(
+                        # transform each parameter in string
+                        [str(param) for param in line if param is not None]
+                    )
+                    # make a list of parameters with only 3 param per line
+                    for line in zip_longest(*[iter(self.params_list)] * 3)
+                ]
+            ),
+        )
+        params = "\n".join([par.__doc__ for par in self.params_list])
         flags = self.flags.__doc__
         flags = self.flags.__doc__
-        return '\n'.join([head, params, DOC['flag_head'], flags, DOC['foot']])
+        return "\n".join([head, params, DOC["flag_head"], flags, DOC["foot"]])
 
 
     def check(self):
     def check(self):
         """Check the correctness of the provide parameters"""
         """Check the correctness of the provide parameters"""
@@ -710,8 +744,9 @@ class Module(object):
                 required = False
                 required = False
         if required:
         if required:
             for k in self.required:
             for k in self.required:
-                if ((k in self.inputs and self.inputs[k].value is None) or
-                        (k in self.outputs and self.outputs[k].value is None)):
+                if (k in self.inputs and self.inputs[k].value is None) or (
+                    k in self.outputs and self.outputs[k].value is None
+                ):
                     msg = "Required parameter <%s> not set."
                     msg = "Required parameter <%s> not set."
                     raise ParameterError(msg % k)
                     raise ParameterError(msg % k)
 
 
@@ -720,12 +755,10 @@ class Module(object):
         inputs, outputs and flags
         inputs, outputs and flags
         """
         """
         dic = {}
         dic = {}
-        dic['name'] = self.name
-        dic['inputs'] = [(k, v.value) for k, v in self.inputs.items()
-                         if v.value]
-        dic['outputs'] = [(k, v.value) for k, v in self.outputs.items()
-                          if v.value]
-        dic['flags'] = [flg for flg in self.flags if self.flags[flg].value]
+        dic["name"] = self.name
+        dic["inputs"] = [(k, v.value) for k, v in self.inputs.items() if v.value]
+        dic["outputs"] = [(k, v.value) for k, v in self.outputs.items() if v.value]
+        dic["flags"] = [flg for flg in self.flags if self.flags[flg].value]
         return dic
         return dic
 
 
     def make_cmd(self):
     def make_cmd(self):
@@ -733,13 +766,23 @@ class Module(object):
 
 
         :returns: the command string
         :returns: the command string
         """
         """
-        skip = ['stdin', 'stdout', 'stderr']
-        args = [self.name, ]
+        skip = ["stdin", "stdout", "stderr"]
+        args = [
+            self.name,
+        ]
         for key in self.inputs:
         for key in self.inputs:
-            if key not in skip and self.inputs[key].value is not None and self.inputs[key].value != '':
+            if (
+                key not in skip
+                and self.inputs[key].value is not None
+                and self.inputs[key].value != ""
+            ):
                 args.append(self.inputs[key].get_bash())
                 args.append(self.inputs[key].get_bash())
         for key in self.outputs:
         for key in self.outputs:
-            if key not in skip and self.outputs[key].value is not None and self.outputs[key].value != '':
+            if (
+                key not in skip
+                and self.outputs[key].value is not None
+                and self.outputs[key].value != ""
+            ):
                 args.append(self.outputs[key].get_bash())
                 args.append(self.outputs[key].get_bash())
         for flg in self.flags:
         for flg in self.flags:
             if self.flags[flg].value:
             if self.flags[flg].value:
@@ -757,17 +800,19 @@ class Module(object):
         """
         """
         G_debug(1, self.get_bash())
         G_debug(1, self.get_bash())
         self._finished = False
         self._finished = False
-        if self.inputs['stdin'].value:
-            self.stdin = self.inputs['stdin'].value
+        if self.inputs["stdin"].value:
+            self.stdin = self.inputs["stdin"].value
             self.stdin_ = PIPE
             self.stdin_ = PIPE
 
 
         cmd = self.make_cmd()
         cmd = self.make_cmd()
         self.start_time = time.time()
         self.start_time = time.time()
-        self.popen = Popen(cmd,
-                           stdin=self.stdin_,
-                           stdout=self.stdout_,
-                           stderr=self.stderr_,
-                           env=self.env_)
+        self.popen = Popen(
+            cmd,
+            stdin=self.stdin_,
+            stdout=self.stdout_,
+            stderr=self.stderr_,
+            env=self.env_,
+        )
 
 
         if self.finish_ is True:
         if self.finish_ is True:
             self.wait()
             self.wait()
@@ -784,16 +829,19 @@ class Module(object):
             if self.stdin:
             if self.stdin:
                 self.stdin = encode(self.stdin)
                 self.stdin = encode(self.stdin)
             stdout, stderr = self.popen.communicate(input=self.stdin)
             stdout, stderr = self.popen.communicate(input=self.stdin)
-            self.outputs['stdout'].value = decode(stdout) if stdout else ''
-            self.outputs['stderr'].value = decode(stderr) if stderr else ''
+            self.outputs["stdout"].value = decode(stdout) if stdout else ""
+            self.outputs["stderr"].value = decode(stderr) if stderr else ""
             self.time = time.time() - self.start_time
             self.time = time.time() - self.start_time
 
 
             self._finished = True
             self._finished = True
 
 
             if self.popen.poll():
             if self.popen.poll():
-                raise CalledModuleError(returncode=self.popen.returncode,
-                                        code=self.get_bash(),
-                                        module=self.name, errors=stderr)
+                raise CalledModuleError(
+                    returncode=self.popen.returncode,
+                    code=self.get_bash(),
+                    module=self.name,
+                    errors=stderr,
+                )
 
 
         return self
         return self
 
 
@@ -913,13 +961,13 @@ class MultiModule(object):
         """
         """
         self.module_list = module_list
         self.module_list = module_list
         self.set_temp_region = set_temp_region
         self.set_temp_region = set_temp_region
-        self.finish_ = sync      # We use the same variable name a Module
+        self.finish_ = sync  # We use the same variable name a Module
         self.p = None
         self.p = None
         self.q = Queue()
         self.q = Queue()
 
 
     def __str__(self):
     def __str__(self):
         """Return the command string that can be executed in a shell"""
         """Return the command string that can be executed in a shell"""
-        return ' ; '.join(str(string) for string in self.module_list)
+        return " ; ".join(str(string) for string in self.module_list)
 
 
     def get_modules(self):
     def get_modules(self):
         """Return the list of modules that have been run in synchronous mode
         """Return the list of modules that have been run in synchronous mode
@@ -950,11 +998,11 @@ class MultiModule(object):
             return None
             return None
         else:
         else:
             if self.set_temp_region is True:
             if self.set_temp_region is True:
-                self.p = Process(target=run_modules_in_temp_region,
-                                 args=[self.module_list, self.q])
+                self.p = Process(
+                    target=run_modules_in_temp_region, args=[self.module_list, self.q]
+                )
             else:
             else:
-                self.p = Process(target=run_modules,
-                                 args=[self.module_list, self.q])
+                self.p = Process(target=run_modules, args=[self.module_list, self.q])
             self.p.start()
             self.p.start()
 
 
             return self.p
             return self.p
@@ -1011,8 +1059,10 @@ def run_modules(module_list, q):
     finally:
     finally:
         q.put(module_list)
         q.put(module_list)
 
 
+
 ###############################################################################
 ###############################################################################
 
 
 if __name__ == "__main__":
 if __name__ == "__main__":
     import doctest
     import doctest
+
     doctest.testmod()
     doctest.testmod()

+ 106 - 67
python/grass/pygrass/modules/interface/parameter.py

@@ -4,8 +4,15 @@ Created on Tue Apr  2 18:31:47 2013
 
 
 @author: pietro
 @author: pietro
 """
 """
-from __future__ import (nested_scopes, generators, division, absolute_import,
-                        with_statement, print_function, unicode_literals)
+from __future__ import (
+    nested_scopes,
+    generators,
+    division,
+    absolute_import,
+    with_statement,
+    print_function,
+    unicode_literals,
+)
 import re
 import re
 
 
 from grass.pygrass.modules.interface.docstring import docstring_property
 from grass.pygrass.modules.interface.docstring import docstring_property
@@ -16,9 +23,9 @@ def _check_value(param, value):
     """Function to check the correctness of a value and
     """Function to check the correctness of a value and
     return the checked value and the original.
     return the checked value and the original.
     """
     """
-    must_val = 'The Parameter <%s>, must be one of the following values: %r'
-    req = 'The Parameter <%s>, require: %s, get: %s instead: %r\n%s'
-    string = (type(b''), type(u''))
+    must_val = "The Parameter <%s>, must be one of the following values: %r"
+    req = "The Parameter <%s>, require: %s, get: %s instead: %r\n%s"
+    string = (type(b""), type(""))
 
 
     def raiseexcpet(exc, param, ptype, value):
     def raiseexcpet(exc, param, ptype, value):
         """Function to modifa the error message"""
         """Function to modifa the error message"""
@@ -37,8 +44,9 @@ def _check_value(param, value):
             if type(value) in (int, float):
             if type(value) in (int, float):
                 value = str(value)
                 value = str(value)
             if type(value) not in string:
             if type(value) not in string:
-                msg = ("The Parameter <%s> require a string,"
-                       " %s instead is provided: %r")
+                msg = (
+                    "The Parameter <%s> require a string," " %s instead is provided: %r"
+                )
                 raise ValueError(msg % (param.name, type(value), value))
                 raise ValueError(msg % (param.name, type(value), value))
         return value
         return value
 
 
@@ -49,8 +57,16 @@ def _check_value(param, value):
     # find errors with multiple parmeters
     # find errors with multiple parmeters
     if isinstance(value, (list, tuple)):
     if isinstance(value, (list, tuple)):
         if param.keydescvalues:
         if param.keydescvalues:
-            return (([value, ], value) if isinstance(value, tuple)
-                    else (value, value))
+            return (
+                (
+                    [
+                        value,
+                    ],
+                    value,
+                )
+                if isinstance(value, tuple)
+                else (value, value)
+            )
         if param.multiple:
         if param.multiple:
             # everything looks fine, so check each value
             # everything looks fine, so check each value
             try:
             try:
@@ -58,14 +74,14 @@ def _check_value(param, value):
             except Exception as exc:
             except Exception as exc:
                 raiseexcpet(exc, param, param.type, value)
                 raiseexcpet(exc, param, param.type, value)
         else:
         else:
-            msg = 'The Parameter <%s> does not accept multiple inputs'
+            msg = "The Parameter <%s> does not accept multiple inputs"
             raise TypeError(msg % param.name)
             raise TypeError(msg % param.name)
 
 
     if param.keydescvalues:
     if param.keydescvalues:
-        msg = 'The Parameter <%s> require multiple inputs in the form: %s'
+        msg = "The Parameter <%s> require multiple inputs in the form: %s"
         raise TypeError(msg % (param.name, param.keydescvalues))
         raise TypeError(msg % (param.name, param.keydescvalues))
 
 
-    if param.typedesc == 'all':
+    if param.typedesc == "all":
         return value, value
         return value, value
 
 
     # check string before trying to convert value to the correct type
     # check string before trying to convert value to the correct type
@@ -77,20 +93,30 @@ def _check_value(param, value):
         raiseexcpet(exc, param, type(value), value)
         raiseexcpet(exc, param, type(value), value)
 
 
     # check values
     # check values
-    if hasattr(param, 'values'):
+    if hasattr(param, "values"):
         if param.type in (float, int):
         if param.type in (float, int):
             # check for value in range
             # check for value in range
-            if ((param.min is not None and newvalue < param.min) or
-                    (param.max is not None and newvalue > param.max)):
-                err_str = ('The Parameter <%s>, must be between: '
-                           '%g<=value<=%g, %r is outside.')
-                raise ValueError(err_str % (param.name, param.min,
-                                            param.max, newvalue))
+            if (param.min is not None and newvalue < param.min) or (
+                param.max is not None and newvalue > param.max
+            ):
+                err_str = (
+                    "The Parameter <%s>, must be between: "
+                    "%g<=value<=%g, %r is outside."
+                )
+                raise ValueError(err_str % (param.name, param.min, param.max, newvalue))
         # check if value is in the list of valid values
         # check if value is in the list of valid values
         if param.values is not None and newvalue not in param.values:
         if param.values is not None and newvalue not in param.values:
             raise ValueError(must_val % (param.name, param.values))
             raise ValueError(must_val % (param.name, param.values))
-    return (([newvalue, ] if (param.multiple or param.keydescvalues)
-             else newvalue), value)
+    return (
+        (
+            [
+                newvalue,
+            ]
+            if (param.multiple or param.keydescvalues)
+            else newvalue
+        ),
+        value,
+    )
 
 
 
 
 # TODO add documentation
 # TODO add documentation
@@ -119,62 +145,62 @@ class Parameter(object):
         self.max = None
         self.max = None
         diz = element2dict(xparameter) if xparameter is not None else diz
         diz = element2dict(xparameter) if xparameter is not None else diz
         if diz is None:
         if diz is None:
-            raise TypeError('Xparameter or diz are required')
-        self.name = diz['name']
-        self.required = True if diz['required'] == 'yes' else False
-        self.multiple = True if diz['multiple'] == 'yes' else False
+            raise TypeError("Xparameter or diz are required")
+        self.name = diz["name"]
+        self.required = True if diz["required"] == "yes" else False
+        self.multiple = True if diz["multiple"] == "yes" else False
         # check the type
         # check the type
-        if diz['type'] in GETTYPE:
-            self.type = GETTYPE[diz['type']]
-            self.typedesc = diz['type']
+        if diz["type"] in GETTYPE:
+            self.type = GETTYPE[diz["type"]]
+            self.typedesc = diz["type"]
         else:
         else:
-            raise TypeError('New type: %s, ignored' % diz['type'])
+            raise TypeError("New type: %s, ignored" % diz["type"])
 
 
-        self.description = diz.get('description', None)
-        self.keydesc, self.keydescvalues = diz.get('keydesc', (None, None))
+        self.description = diz.get("description", None)
+        self.keydesc, self.keydescvalues = diz.get("keydesc", (None, None))
 
 
         #
         #
         # values
         # values
         #
         #
-        if 'values' in diz:
+        if "values" in diz:
             try:
             try:
                 # Check for integer ranges: "3-30" or float ranges: "0.0-1.0"
                 # Check for integer ranges: "3-30" or float ranges: "0.0-1.0"
-                isrange = re.match("(?P<min>-*\d+.*\d*)*-(?P<max>\d+.*\d*)*",
-                                   diz['values'][0])
+                isrange = re.match(
+                    "(?P<min>-*\d+.*\d*)*-(?P<max>\d+.*\d*)*", diz["values"][0]
+                )
                 if isrange:
                 if isrange:
                     mn, mx = isrange.groups()
                     mn, mx = isrange.groups()
                     self.min = None if mn is None else float(mn)
                     self.min = None if mn is None else float(mn)
                     self.max = None if mx is None else float(mx)
                     self.max = None if mx is None else float(mx)
                     self.values = None
                     self.values = None
-                    self.isrange = diz['values'][0]
+                    self.isrange = diz["values"][0]
                 # No range was found
                 # No range was found
                 else:
                 else:
-                    self.values = [self.type(i) for i in diz['values']]
+                    self.values = [self.type(i) for i in diz["values"]]
                     self.isrange = False
                     self.isrange = False
             except TypeError:
             except TypeError:
-                self.values = [self.type(i) for i in diz['values']]
+                self.values = [self.type(i) for i in diz["values"]]
                 self.isrange = False
                 self.isrange = False
 
 
         #
         #
         # default
         # default
         #
         #
-        if 'default' in diz and diz['default']:
+        if "default" in diz and diz["default"]:
             if self.multiple or self.keydescvalues:
             if self.multiple or self.keydescvalues:
-                self.default = [self.type(v)
-                                for v in diz['default'].split(',')]
+                self.default = [self.type(v) for v in diz["default"].split(",")]
             else:
             else:
-                self.default = self.type(diz['default'])
+                self.default = self.type(diz["default"])
         else:
         else:
             self.default = None
             self.default = None
         self._value, self._rawvalue = self.default, self.default
         self._value, self._rawvalue = self.default, self.default
-        self.guisection = diz.get('guisection', None)
+        self.guisection = diz.get("guisection", None)
 
 
         #
         #
         # gisprompt
         # gisprompt
         #
         #
-        if 'gisprompt' in diz and diz['gisprompt']:
-            self.typedesc = diz['gisprompt'].get('prompt', '')
-            self.input = False if diz['gisprompt']['age'] == 'new' else True
+        if "gisprompt" in diz and diz["gisprompt"]:
+            self.typedesc = diz["gisprompt"].get("prompt", "")
+            self.input = False if diz["gisprompt"]["age"] == "new" else True
         else:
         else:
             self.input = True
             self.input = True
 
 
@@ -186,8 +212,11 @@ class Parameter(object):
 
 
     # here the property function is used to transform value in an attribute
     # here the property function is used to transform value in an attribute
     # in this case we define which function must be use to get/set the value
     # in this case we define which function must be use to get/set the value
-    value = property(fget=_get_value, fset=_set_value,
-                     doc="Parameter value transformed and validated.")
+    value = property(
+        fget=_get_value,
+        fset=_set_value,
+        doc="Parameter value transformed and validated.",
+    )
 
 
     @property
     @property
     def rawvalue(self):
     def rawvalue(self):
@@ -205,11 +234,16 @@ class Parameter(object):
 
 
         ..
         ..
         """
         """
-        sep = ','
+        sep = ","
         if isinstance(self.rawvalue, (list, tuple)):
         if isinstance(self.rawvalue, (list, tuple)):
-            value = sep.join([sep.join([str(v) for v in val])
-                              if isinstance(val, tuple) else str(val)
-                              for val in self.rawvalue])
+            value = sep.join(
+                [
+                    sep.join([str(v) for v in val])
+                    if isinstance(val, tuple)
+                    else str(val)
+                    for val in self.rawvalue
+                ]
+            )
         else:
         else:
             value = str(self.rawvalue)
             value = str(self.rawvalue)
         return "%s=%s" % (self.name, value)
         return "%s=%s" % (self.name, value)
@@ -226,7 +260,7 @@ class Parameter(object):
         ..
         ..
         """
         """
         if self.value is None:
         if self.value is None:
-            return ''
+            return ""
         return """%s=%r""" % (self.name, self.value)
         return """%s=%r""" % (self.name, self.value)
 
 
     def __str__(self):
     def __str__(self):
@@ -236,11 +270,13 @@ class Parameter(object):
     def __repr__(self):
     def __repr__(self):
         """Return the python representation of the GRASS module parameter."""
         """Return the python representation of the GRASS module parameter."""
         str_repr = "Parameter <%s> (required:%s, type:%s, multiple:%s)"
         str_repr = "Parameter <%s> (required:%s, type:%s, multiple:%s)"
-        mtype = ('raster', 'vector')  # map type
-        return str_repr % (self.name,
-                           "yes" if self.required else "no",
-                           self.type if self.type in mtype else self.typedesc,
-                           "yes" if self.multiple else "no")
+        mtype = ("raster", "vector")  # map type
+        return str_repr % (
+            self.name,
+            "yes" if self.required else "no",
+            self.type if self.type in mtype else self.typedesc,
+            "yes" if self.multiple else "no",
+        )
 
 
     @docstring_property(__doc__)
     @docstring_property(__doc__)
     def __doc__(self):
     def __doc__(self):
@@ -262,19 +298,22 @@ class Parameter(object):
                 Values: 2, 4, 6, 8
                 Values: 2, 4, 6, 8
         ..
         ..
         """
         """
-        if hasattr(self, 'values'):
+        if hasattr(self, "values"):
             if self.isrange:
             if self.isrange:
                 vals = self.isrange
                 vals = self.isrange
             else:
             else:
-                vals = ', '.join([repr(val) for val in self.values])
+                vals = ", ".join([repr(val) for val in self.values])
         else:
         else:
             vals = False
             vals = False
         if self.keydescvalues:
         if self.keydescvalues:
-            keydescvals = "\n    (%s)" % ', '.join(self.keydescvalues)
-        return DOC['param'].format(name=self.name,
-                default=repr(self.default) + ', ' if self.default else '',
-            required='required, ' if self.required else 'optional, ',
-            multi='multi' if self.multiple else '',
-            ptype=self.typedesc, description=self.description,
-            values='\n    Values: {0}'.format(vals)  if vals else '',
-            keydescvalues= keydescvals if self.keydescvalues else '')
+            keydescvals = "\n    (%s)" % ", ".join(self.keydescvalues)
+        return DOC["param"].format(
+            name=self.name,
+            default=repr(self.default) + ", " if self.default else "",
+            required="required, " if self.required else "optional, ",
+            multi="multi" if self.multiple else "",
+            ptype=self.typedesc,
+            description=self.description,
+            values="\n    Values: {0}".format(vals) if vals else "",
+            keydescvalues=keydescvals if self.keydescvalues else "",
+        )

+ 42 - 34
python/grass/pygrass/modules/interface/read.py

@@ -4,8 +4,15 @@ Created on Tue Apr  2 18:30:34 2013
 
 
 @author: pietro
 @author: pietro
 """
 """
-from __future__ import (nested_scopes, generators, division, absolute_import,
-                        with_statement, print_function, unicode_literals)
+from __future__ import (
+    nested_scopes,
+    generators,
+    division,
+    absolute_import,
+    with_statement,
+    print_function,
+    unicode_literals,
+)
 
 
 
 
 def do_nothing(p):
 def do_nothing(p):
@@ -21,7 +28,7 @@ def get_dict(p):
 
 
 
 
 def get_values(p):
 def get_values(p):
-    return [e.text.strip() for e in p.findall('value/name')]
+    return [e.text.strip() for e in p.findall("value/name")]
 
 
 
 
 def read_text(p):
 def read_text(p):
@@ -30,32 +37,32 @@ def read_text(p):
 
 
 def read_keydesc(par):
 def read_keydesc(par):
     name = par.text.strip()
     name = par.text.strip()
-    items = [e.text.strip() for e in par.findall('item')]
+    items = [e.text.strip() for e in par.findall("item")]
     return name, tuple(items) if len(items) > 1 else None
     return name, tuple(items) if len(items) > 1 else None
 
 
 
 
 GETFROMTAG = {
 GETFROMTAG = {
-    'description': read_text,
-    'keydesc': read_keydesc,
-    'gisprompt': get_dict,
-    'default': read_text,
-    'values': get_values,
-    'value': get_None,
-    'guisection': read_text,
-    'label': read_text,
-    'suppress_required': get_None,
-    'keywords': read_text,
-    'guidependency': read_text,
-    'rules': get_None,
+    "description": read_text,
+    "keydesc": read_keydesc,
+    "gisprompt": get_dict,
+    "default": read_text,
+    "values": get_values,
+    "value": get_None,
+    "guisection": read_text,
+    "label": read_text,
+    "suppress_required": get_None,
+    "keywords": read_text,
+    "guidependency": read_text,
+    "rules": get_None,
 }
 }
 
 
 GETTYPE = {
 GETTYPE = {
-    'string': str,
-    'integer': int,
-    'float': float,
-    'double': float,
-    'file': str,
-    'all': do_nothing,
+    "string": str,
+    "integer": int,
+    "float": float,
+    "double": float,
+    "file": str,
+    "all": do_nothing,
 }
 }
 
 
 
 
@@ -65,37 +72,37 @@ def element2dict(xparameter):
         if p.tag in GETFROMTAG:
         if p.tag in GETFROMTAG:
             diz[p.tag] = GETFROMTAG[p.tag](p)
             diz[p.tag] = GETFROMTAG[p.tag](p)
         else:
         else:
-            print('New tag: %s, ignored' % p.tag)
+            print("New tag: %s, ignored" % p.tag)
     return diz
     return diz
 
 
 
 
 # dictionary used to create docstring for the objects
 # dictionary used to create docstring for the objects
 DOC = {
 DOC = {
-    #------------------------------------------------------------
+    # ------------------------------------------------------------
     # head
     # head
-    'head': """{cmd_name}({cmd_params})
+    "head": """{cmd_name}({cmd_params})
 
 
 Parameters
 Parameters
 ----------
 ----------
 
 
 """,
 """,
-    #------------------------------------------------------------
+    # ------------------------------------------------------------
     # param
     # param
-    'param': """{name}: {default}{required}{multi}{ptype}
+    "param": """{name}: {default}{required}{multi}{ptype}
     {description}{values}{keydescvalues}""",
     {description}{values}{keydescvalues}""",
-    #------------------------------------------------------------
+    # ------------------------------------------------------------
     # flag_head
     # flag_head
-    'flag_head': """
+    "flag_head": """
 Flags
 Flags
 ------
 ------
 """,
 """,
-    #------------------------------------------------------------
+    # ------------------------------------------------------------
     # flag
     # flag
-    'flag': """{name}: {default}, {supress}
+    "flag": """{name}: {default}, {supress}
     {description}""",
     {description}""",
-    #------------------------------------------------------------
+    # ------------------------------------------------------------
     # foot
     # foot
-    'foot': """
+    "foot": """
 Special Parameters
 Special Parameters
 ------------------
 ------------------
 
 
@@ -111,4 +118,5 @@ stdin_: PIPE, optional
     Set the standard input.
     Set the standard input.
 env_: dictionary, optional
 env_: dictionary, optional
     Set the environment variables.
     Set the environment variables.
-"""}
+""",
+}

+ 12 - 12
python/grass/pygrass/modules/interface/testsuite/test_flag.py

@@ -14,36 +14,36 @@ from grass.pygrass.modules.interface.flag import Flag
 class TestFlag(TestCase):
 class TestFlag(TestCase):
     def test_get_bash(self):
     def test_get_bash(self):
         """Test get_bash method"""
         """Test get_bash method"""
-        flag = Flag(diz=dict(name='a'))
+        flag = Flag(diz=dict(name="a"))
         self.assertFalse(flag.value)
         self.assertFalse(flag.value)
-        self.assertEqual('', flag.get_bash())
+        self.assertEqual("", flag.get_bash())
         flag.special = True
         flag.special = True
-        self.assertEqual('', flag.get_bash())
+        self.assertEqual("", flag.get_bash())
         flag.value = True
         flag.value = True
-        self.assertEqual('--a', flag.get_bash())
+        self.assertEqual("--a", flag.get_bash())
         flag.special = False
         flag.special = False
-        self.assertEqual('-a', flag.get_bash())
+        self.assertEqual("-a", flag.get_bash())
 
 
     def test_get_python(self):
     def test_get_python(self):
         """Test get_python method"""
         """Test get_python method"""
-        flag = Flag(diz=dict(name='a'))
+        flag = Flag(diz=dict(name="a"))
         self.assertFalse(flag.value)
         self.assertFalse(flag.value)
-        self.assertEqual('', flag.get_python())
+        self.assertEqual("", flag.get_python())
         flag.special = True
         flag.special = True
-        self.assertEqual('', flag.get_python())
+        self.assertEqual("", flag.get_python())
         flag.value = True
         flag.value = True
-        self.assertEqual('a=True', flag.get_python())
+        self.assertEqual("a=True", flag.get_python())
         flag.special = False
         flag.special = False
-        self.assertEqual('a', flag.get_python())
+        self.assertEqual("a", flag.get_python())
 
 
     def test_bool(self):
     def test_bool(self):
         """Test magic __bool__ method"""
         """Test magic __bool__ method"""
-        flag = Flag(diz=dict(name='a'))
+        flag = Flag(diz=dict(name="a"))
         flag.value = True
         flag.value = True
         self.assertTrue(True if flag else False)
         self.assertTrue(True if flag else False)
         flag.value = False
         flag.value = False
         self.assertFalse(True if flag else False)
         self.assertFalse(True if flag else False)
 
 
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     test()
     test()

+ 15 - 10
python/grass/pygrass/modules/interface/testsuite/test_modules.py

@@ -20,7 +20,9 @@ else:
     from io import BytesIO as StringIO
     from io import BytesIO as StringIO
 
 
 
 
-SKIP = ["g.parser", ]
+SKIP = [
+    "g.parser",
+]
 
 
 
 
 # taken from six
 # taken from six
@@ -30,24 +32,27 @@ def with_metaclass(meta, *bases):
     # metaclass for one level of class instantiation that replaces itself with
     # metaclass for one level of class instantiation that replaces itself with
     # the actual metaclass.
     # the actual metaclass.
     class metaclass(meta):
     class metaclass(meta):
-
         def __new__(cls, name, this_bases, d):
         def __new__(cls, name, this_bases, d):
             return meta(name, bases, d)
             return meta(name, bases, d)
-    return type.__new__(metaclass, 'temporary_class', (), {})
+
+    return type.__new__(metaclass, "temporary_class", (), {})
 
 
 
 
 class ModulesMeta(type):
 class ModulesMeta(type):
     def __new__(mcs, name, bases, dict):
     def __new__(mcs, name, bases, dict):
-
         def gen_test(cmd):
         def gen_test(cmd):
             def test(self):
             def test(self):
                 Module(cmd)
                 Module(cmd)
+
             return test
             return test
 
 
-        cmds = [c for c in sorted(list(get_commands()[0]))
-                if c not in SKIP and not fnmatch(c, "g.gui.*")]
+        cmds = [
+            c
+            for c in sorted(list(get_commands()[0]))
+            if c not in SKIP and not fnmatch(c, "g.gui.*")
+        ]
         for cmd in cmds:
         for cmd in cmds:
-            test_name = "test__%s" % cmd.replace('.', '_')
+            test_name = "test__%s" % cmd.replace(".", "_")
             dict[test_name] = gen_test(cmd)
             dict[test_name] = gen_test(cmd)
         return type.__new__(mcs, name, bases, dict)
         return type.__new__(mcs, name, bases, dict)
 
 
@@ -62,14 +67,14 @@ class TestModulesPickability(TestCase):
         import pickle
         import pickle
 
 
         out = StringIO()
         out = StringIO()
-        pickle.dump(Module('r.sun'), out)
+        pickle.dump(Module("r.sun"), out)
         out.close()
         out.close()
 
 
 
 
 class TestModulesCheck(TestCase):
 class TestModulesCheck(TestCase):
     def test_flags_with_suppress_required(self):
     def test_flags_with_suppress_required(self):
         """Test if flags with suppress required are handle correctly"""
         """Test if flags with suppress required are handle correctly"""
-        gextension = Module('g.extension')
+        gextension = Module("g.extension")
         # check if raise an error if required parameter are missing
         # check if raise an error if required parameter are missing
         with self.assertRaises(ParameterError):
         with self.assertRaises(ParameterError):
             gextension.check()
             gextension.check()
@@ -79,5 +84,5 @@ class TestModulesCheck(TestCase):
         self.assertIsNone(gextension.check())
         self.assertIsNone(gextension.check())
 
 
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     test()
     test()

+ 227 - 114
python/grass/pygrass/modules/interface/testsuite/test_parameter.py

@@ -11,19 +11,20 @@ from grass.gunittest.main import test
 from grass.pygrass.modules.interface.parameter import Parameter, _check_value
 from grass.pygrass.modules.interface.parameter import Parameter, _check_value
 
 
 GETTYPE = {
 GETTYPE = {
-    'string': str,
-    'integer': int,
-    'float': float,
-    'double': float,
-    'file': str,
-    'all': lambda x: x,
+    "string": str,
+    "integer": int,
+    "float": float,
+    "double": float,
+    "file": str,
+    "all": lambda x: x,
 }
 }
 
 
-class TestCheckValueFunction(TestCase):
 
 
+class TestCheckValueFunction(TestCase):
     def test_single_all(self):
     def test_single_all(self):
-        param = Parameter(diz=dict(name='int_number', required='yes',
-                                   multiple='no', type='all'))
+        param = Parameter(
+            diz=dict(name="int_number", required="yes", multiple="no", type="all")
+        )
         value = 1
         value = 1
         self.assertTupleEqual((value, value), _check_value(param, value))
         self.assertTupleEqual((value, value), _check_value(param, value))
         value = 1.2
         value = 1.2
@@ -36,9 +37,10 @@ class TestCheckValueFunction(TestCase):
             _check_value(param, (1, 2))
             _check_value(param, (1, 2))
 
 
     def test_single_float_double(self):
     def test_single_float_double(self):
-        for ptype in ('float', 'double'):
-            param = Parameter(diz=dict(name='int_number', required='yes',
-                                       multiple='no', type=ptype))
+        for ptype in ("float", "double"):
+            param = Parameter(
+                diz=dict(name="int_number", required="yes", multiple="no", type=ptype)
+            )
             value = 1
             value = 1
             self.assertTupleEqual((float(value), value), _check_value(param, value))
             self.assertTupleEqual((float(value), value), _check_value(param, value))
             value = 1.2
             value = 1.2
@@ -52,30 +54,47 @@ class TestCheckValueFunction(TestCase):
             with self.assertRaises(ValueError):
             with self.assertRaises(ValueError):
                 _check_value(param, "elev")
                 _check_value(param, "elev")
             with self.assertRaises(TypeError):
             with self.assertRaises(TypeError):
-                _check_value(param, (1., 2.))
+                _check_value(param, (1.0, 2.0))
 
 
     def test_multiple_float_double(self):
     def test_multiple_float_double(self):
-        for ptype in ('float', 'double'):
-            param = Parameter(diz=dict(name='number', required='yes',
-                                       multiple='yes', type=ptype))
+        for ptype in ("float", "double"):
+            param = Parameter(
+                diz=dict(name="number", required="yes", multiple="yes", type=ptype)
+            )
             value = (1.4, 2.3)
             value = (1.4, 2.3)
-            self.assertTupleEqual((list(value), value),
-                                  _check_value(param, value))
+            self.assertTupleEqual((list(value), value), _check_value(param, value))
             value = (1, 2)
             value = (1, 2)
-            self.assertTupleEqual(([float(v) for v in value], value),
-                                  _check_value(param, value))
+            self.assertTupleEqual(
+                ([float(v) for v in value], value), _check_value(param, value)
+            )
             value = ("1", "2")
             value = ("1", "2")
-            self.assertTupleEqual(([float(v) for v in value], value),
-                                  _check_value(param, value))
+            self.assertTupleEqual(
+                ([float(v) for v in value], value), _check_value(param, value)
+            )
             value = ("1.4", "2.3")
             value = ("1.4", "2.3")
-            self.assertTupleEqual(([float(v) for v in value], value),
-                                  _check_value(param, value))
-            value = 1.
-            self.assertTupleEqual(([value, ], value),
-                                  _check_value(param, value))
+            self.assertTupleEqual(
+                ([float(v) for v in value], value), _check_value(param, value)
+            )
+            value = 1.0
+            self.assertTupleEqual(
+                (
+                    [
+                        value,
+                    ],
+                    value,
+                ),
+                _check_value(param, value),
+            )
             value = 1
             value = 1
-            self.assertTupleEqual(([value, ], value),
-                                  _check_value(param, value))
+            self.assertTupleEqual(
+                (
+                    [
+                        value,
+                    ],
+                    value,
+                ),
+                _check_value(param, value),
+            )
 
 
             # test errors
             # test errors
             with self.assertRaises(ValueError):
             with self.assertRaises(ValueError):
@@ -84,10 +103,18 @@ class TestCheckValueFunction(TestCase):
                 _check_value(param, ("elev", "slope", "aspect"))
                 _check_value(param, ("elev", "slope", "aspect"))
 
 
     def test_range_float_double(self):
     def test_range_float_double(self):
-        for ptype in ('float', 'double'):
-            param = Parameter(diz=dict(name='int_number', required='yes',
-                                       multiple='no', type=ptype,
-                                       values=["0.0-2.5", ]))
+        for ptype in ("float", "double"):
+            param = Parameter(
+                diz=dict(
+                    name="int_number",
+                    required="yes",
+                    multiple="no",
+                    type=ptype,
+                    values=[
+                        "0.0-2.5",
+                    ],
+                )
+            )
             value = 1
             value = 1
             self.assertTupleEqual((float(value), value), _check_value(param, value))
             self.assertTupleEqual((float(value), value), _check_value(param, value))
             value = 1.2
             value = 1.2
@@ -101,15 +128,16 @@ class TestCheckValueFunction(TestCase):
             with self.assertRaises(ValueError):
             with self.assertRaises(ValueError):
                 _check_value(param, "elev")
                 _check_value(param, "elev")
             with self.assertRaises(TypeError):
             with self.assertRaises(TypeError):
-                _check_value(param, (1., 2.))
+                _check_value(param, (1.0, 2.0))
             with self.assertRaises(ValueError):
             with self.assertRaises(ValueError):
-                _check_value(param, -1.)
+                _check_value(param, -1.0)
             with self.assertRaises(ValueError):
             with self.assertRaises(ValueError):
                 _check_value(param, 2.6)
                 _check_value(param, 2.6)
 
 
     def test_single_integer(self):
     def test_single_integer(self):
-        param = Parameter(diz=dict(name='int_number', required='yes',
-                                   multiple='no', type='integer'))
+        param = Parameter(
+            diz=dict(name="int_number", required="yes", multiple="no", type="integer")
+        )
         value = 1
         value = 1
         self.assertTupleEqual((value, value), _check_value(param, value))
         self.assertTupleEqual((value, value), _check_value(param, value))
         value = 1.2
         value = 1.2
@@ -126,25 +154,50 @@ class TestCheckValueFunction(TestCase):
             _check_value(param, (1, 2))
             _check_value(param, (1, 2))
 
 
     def test_multiple_integer(self):
     def test_multiple_integer(self):
-        param = Parameter(diz=dict(name='int_number', required='yes',
-                                   multiple='yes', type='integer'))
+        param = Parameter(
+            diz=dict(name="int_number", required="yes", multiple="yes", type="integer")
+        )
         value = (1, 2)
         value = (1, 2)
-        #import ipdb; ipdb.set_trace()
+        # import ipdb; ipdb.set_trace()
         self.assertTupleEqual((list(value), value), _check_value(param, value))
         self.assertTupleEqual((list(value), value), _check_value(param, value))
         value = (1.2, 2.3)
         value = (1.2, 2.3)
-        self.assertTupleEqual(([int(v) for v in value], value),
-                              _check_value(param, value))
+        self.assertTupleEqual(
+            ([int(v) for v in value], value), _check_value(param, value)
+        )
         value = ("1", "2")
         value = ("1", "2")
-        self.assertTupleEqual(([int(v) for v in value], value),
-                              _check_value(param, value))
+        self.assertTupleEqual(
+            ([int(v) for v in value], value), _check_value(param, value)
+        )
         value = 1
         value = 1
-        self.assertTupleEqual(([1, ], value), _check_value(param, value))
+        self.assertTupleEqual(
+            (
+                [
+                    1,
+                ],
+                value,
+            ),
+            _check_value(param, value),
+        )
         value = 1.2
         value = 1.2
-        self.assertTupleEqual(([int(value), ], value),
-                              _check_value(param, value))
+        self.assertTupleEqual(
+            (
+                [
+                    int(value),
+                ],
+                value,
+            ),
+            _check_value(param, value),
+        )
         value = "1"
         value = "1"
-        self.assertTupleEqual(([int(value), ], value),
-                              _check_value(param, value))
+        self.assertTupleEqual(
+            (
+                [
+                    int(value),
+                ],
+                value,
+            ),
+            _check_value(param, value),
+        )
 
 
         # test errors
         # test errors
         with self.assertRaises(ValueError):
         with self.assertRaises(ValueError):
@@ -153,14 +206,26 @@ class TestCheckValueFunction(TestCase):
             _check_value(param, ("elev", "slope", "aspect"))
             _check_value(param, ("elev", "slope", "aspect"))
 
 
     def test_keydescvalues(self):
     def test_keydescvalues(self):
-        for ptype in ('integer', 'float'):
-            param = Parameter(diz=dict(name='int_number', required='yes',
-                                       multiple='yes',
-                                       keydesc=('range', '(min, max)'),
-                                       type='integer'))
+        for ptype in ("integer", "float"):
+            param = Parameter(
+                diz=dict(
+                    name="int_number",
+                    required="yes",
+                    multiple="yes",
+                    keydesc=("range", "(min, max)"),
+                    type="integer",
+                )
+            )
             value = (1, 2)
             value = (1, 2)
-            self.assertTupleEqual(([value, ], value),
-                                  _check_value(param, value))
+            self.assertTupleEqual(
+                (
+                    [
+                        value,
+                    ],
+                    value,
+                ),
+                _check_value(param, value),
+            )
             value = [(1, 2), (2, 3)]
             value = [(1, 2), (2, 3)]
             self.assertTupleEqual((value, value), _check_value(param, value))
             self.assertTupleEqual((value, value), _check_value(param, value))
 
 
@@ -168,9 +233,17 @@ class TestCheckValueFunction(TestCase):
                 _check_value(param, 1)
                 _check_value(param, 1)
 
 
     def test_range_integer(self):
     def test_range_integer(self):
-        param = Parameter(diz=dict(name='int_number', required='yes',
-                                   multiple='no', type='integer',
-                                   values=["0-10", ]))
+        param = Parameter(
+            diz=dict(
+                name="int_number",
+                required="yes",
+                multiple="no",
+                type="integer",
+                values=[
+                    "0-10",
+                ],
+            )
+        )
         value = 1
         value = 1
         self.assertTupleEqual((value, value), _check_value(param, value))
         self.assertTupleEqual((value, value), _check_value(param, value))
         value = 0
         value = 0
@@ -195,9 +268,15 @@ class TestCheckValueFunction(TestCase):
             _check_value(param, 11)
             _check_value(param, 11)
 
 
     def test_choice_integer(self):
     def test_choice_integer(self):
-        param = Parameter(diz=dict(name='int_number', required='yes',
-                                   multiple='no', type='integer',
-                                   values=[2, 4, 6, 8]))
+        param = Parameter(
+            diz=dict(
+                name="int_number",
+                required="yes",
+                multiple="no",
+                type="integer",
+                values=[2, 4, 6, 8],
+            )
+        )
         value = 4
         value = 4
         self.assertTupleEqual((value, value), _check_value(param, value))
         self.assertTupleEqual((value, value), _check_value(param, value))
         value = 2
         value = 2
@@ -216,39 +295,49 @@ class TestCheckValueFunction(TestCase):
             _check_value(param, 3)
             _check_value(param, 3)
 
 
     def test_single_string_file(self):
     def test_single_string_file(self):
-        for ptype in ('string', 'file'):
-            param = Parameter(diz=dict(name='name', required='yes',
-                                       multiple='no', type=ptype))
-            value = u'elev'
+        for ptype in ("string", "file"):
+            param = Parameter(
+                diz=dict(name="name", required="yes", multiple="no", type=ptype)
+            )
+            value = "elev"
             self.assertTupleEqual((value, value), _check_value(param, value))
             self.assertTupleEqual((value, value), _check_value(param, value))
             value = 10
             value = 10
-            self.assertTupleEqual((str(value), value),
-                                  _check_value(param, value))
+            self.assertTupleEqual((str(value), value), _check_value(param, value))
             value = 12.5
             value = 12.5
-            self.assertTupleEqual((str(value), value),
-                                  _check_value(param, value))
+            self.assertTupleEqual((str(value), value), _check_value(param, value))
 
 
             # test errors
             # test errors
             with self.assertRaises(TypeError):
             with self.assertRaises(TypeError):
-                _check_value(param, ('abc', 'def'))
+                _check_value(param, ("abc", "def"))
 
 
     def test_multiple_strings(self):
     def test_multiple_strings(self):
-        param = Parameter(diz=dict(name='rastnames', required='yes',
-                                   multiple='yes', type='string'))
-        value = ['elev', 'slope', 'aspect']
+        param = Parameter(
+            diz=dict(name="rastnames", required="yes", multiple="yes", type="string")
+        )
+        value = ["elev", "slope", "aspect"]
         self.assertTupleEqual((value, value), _check_value(param, value))
         self.assertTupleEqual((value, value), _check_value(param, value))
-        value = ('elev', 'slope', 'aspect')
+        value = ("elev", "slope", "aspect")
         self.assertTupleEqual((list(value), value), _check_value(param, value))
         self.assertTupleEqual((list(value), value), _check_value(param, value))
-        value = ['1.3', '2.3', '4.5']
+        value = ["1.3", "2.3", "4.5"]
         self.assertTupleEqual((value, value), _check_value(param, value))
         self.assertTupleEqual((value, value), _check_value(param, value))
         value = [1.3, 2.3, 4.5]
         value = [1.3, 2.3, 4.5]
-        self.assertTupleEqual(([str(v) for v in value], value),
-                              _check_value(param, value))
+        self.assertTupleEqual(
+            ([str(v) for v in value], value), _check_value(param, value)
+        )
         value = (1, 2, 3)
         value = (1, 2, 3)
-        self.assertTupleEqual(([str(v) for v in value], value),
-                              _check_value(param, value))
-        value = 'elev'
-        self.assertTupleEqual(([value, ], value), _check_value(param, value))
+        self.assertTupleEqual(
+            ([str(v) for v in value], value), _check_value(param, value)
+        )
+        value = "elev"
+        self.assertTupleEqual(
+            (
+                [
+                    value,
+                ],
+                value,
+            ),
+            _check_value(param, value),
+        )
 
 
         # test errors
         # test errors
         with self.assertRaises(ValueError):
         with self.assertRaises(ValueError):
@@ -256,9 +345,15 @@ class TestCheckValueFunction(TestCase):
 
 
     def test_choice_string(self):
     def test_choice_string(self):
         values = ["elev", "asp", "slp"]
         values = ["elev", "asp", "slp"]
-        param = Parameter(diz=dict(name='rastname', required='yes',
-                                   multiple='no', type='string',
-                                   values=values))
+        param = Parameter(
+            diz=dict(
+                name="rastname",
+                required="yes",
+                multiple="no",
+                type="string",
+                values=values,
+            )
+        )
         value = "asp"
         value = "asp"
         self.assertTupleEqual((value, value), _check_value(param, value))
         self.assertTupleEqual((value, value), _check_value(param, value))
 
 
@@ -275,9 +370,10 @@ class TestCheckValueFunction(TestCase):
 
 
 class TestParameterGetBash(TestCase):
 class TestParameterGetBash(TestCase):
     def test_single_float_double(self):
     def test_single_float_double(self):
-        for ptype in ('float', 'double'):
-            param = Parameter(diz=dict(name='number', required='yes',
-                                       multiple='no', type=ptype))
+        for ptype in ("float", "double"):
+            param = Parameter(
+                diz=dict(name="number", required="yes", multiple="no", type=ptype)
+            )
             # set private attributes to skip the check function
             # set private attributes to skip the check function
             param._value = 1.0
             param._value = 1.0
             param._rawvalue = 1.0
             param._rawvalue = 1.0
@@ -287,14 +383,19 @@ class TestParameterGetBash(TestCase):
             self.assertEqual("number=1.", param.get_bash())
             self.assertEqual("number=1.", param.get_bash())
 
 
     def test_multiple_float_double(self):
     def test_multiple_float_double(self):
-        for ptype in ('float', 'double'):
-            param = Parameter(diz=dict(name='number', required='yes',
-                                       multiple='yes', type=ptype))
+        for ptype in ("float", "double"):
+            param = Parameter(
+                diz=dict(name="number", required="yes", multiple="yes", type=ptype)
+            )
             # set private attributes to skip the check function
             # set private attributes to skip the check function
-            param._value = [1.0, ]
+            param._value = [
+                1.0,
+            ]
             param._rawvalue = 1.0
             param._rawvalue = 1.0
             self.assertEqual("number=1.0", param.get_bash())
             self.assertEqual("number=1.0", param.get_bash())
-            param._value = [1.0, ]
+            param._value = [
+                1.0,
+            ]
             param._rawvalue = "1."
             param._rawvalue = "1."
             self.assertEqual("number=1.", param.get_bash())
             self.assertEqual("number=1.", param.get_bash())
             param._value = [1.0, 2.0, 3.0]
             param._value = [1.0, 2.0, 3.0]
@@ -305,42 +406,54 @@ class TestParameterGetBash(TestCase):
             self.assertEqual("number=1.,2.,3.", param.get_bash())
             self.assertEqual("number=1.,2.,3.", param.get_bash())
 
 
     def test_single_string(self):
     def test_single_string(self):
-        param = Parameter(diz=dict(name='rast', required='yes',
-                                   multiple='no', type='string'))
+        param = Parameter(
+            diz=dict(name="rast", required="yes", multiple="no", type="string")
+        )
         # set private attributes to skip the check function
         # set private attributes to skip the check function
-        param._value = 'elev'
-        param._rawvalue = 'elev'
+        param._value = "elev"
+        param._rawvalue = "elev"
         self.assertEqual("rast=elev", param.get_bash())
         self.assertEqual("rast=elev", param.get_bash())
 
 
     def test_multiple_strings(self):
     def test_multiple_strings(self):
-        param = Parameter(diz=dict(name='rast', required='yes',
-                                   multiple='yes', type='string'))
+        param = Parameter(
+            diz=dict(name="rast", required="yes", multiple="yes", type="string")
+        )
         # set private attributes to skip the check function
         # set private attributes to skip the check function
-        param._value = ['elev', 'asp', 'slp']
-        param._rawvalue = ['elev', 'asp', 'slp']
+        param._value = ["elev", "asp", "slp"]
+        param._rawvalue = ["elev", "asp", "slp"]
         self.assertEqual("rast=elev,asp,slp", param.get_bash())
         self.assertEqual("rast=elev,asp,slp", param.get_bash())
-        param._value = ['elev', ]
-        param._rawvalue = 'elev'
+        param._value = [
+            "elev",
+        ]
+        param._rawvalue = "elev"
         self.assertEqual("rast=elev", param.get_bash())
         self.assertEqual("rast=elev", param.get_bash())
 
 
     def test_keydescvalues(self):
     def test_keydescvalues(self):
-        param = Parameter(diz=dict(name='range', required='yes',
-                                   multiple='yes',
-                                   keydesc=('range', '(min, max)'),
-                                   type='integer'))
+        param = Parameter(
+            diz=dict(
+                name="range",
+                required="yes",
+                multiple="yes",
+                keydesc=("range", "(min, max)"),
+                type="integer",
+            )
+        )
         # set private attributes to skip the check function
         # set private attributes to skip the check function
-        param._value = [(1., 2.), ]
-        param._rawvalue = (1., 2.)
+        param._value = [
+            (1.0, 2.0),
+        ]
+        param._rawvalue = (1.0, 2.0)
         self.assertEqual("range=1.0,2.0", param.get_bash())
         self.assertEqual("range=1.0,2.0", param.get_bash())
-        param._value = [(1., 2.), (3., 4.)]
-        param._rawvalue = [(1., 2.), (3., 4.)]
+        param._value = [(1.0, 2.0), (3.0, 4.0)]
+        param._rawvalue = [(1.0, 2.0), (3.0, 4.0)]
         self.assertEqual("range=1.0,2.0,3.0,4.0", param.get_bash())
         self.assertEqual("range=1.0,2.0,3.0,4.0", param.get_bash())
-        param._value = [(1., 2.), (3., 4.)]
-        param._rawvalue = [('1.0', '2.00'), ('3.000', '4.0000')]
+        param._value = [(1.0, 2.0), (3.0, 4.0)]
+        param._rawvalue = [("1.0", "2.00"), ("3.000", "4.0000")]
         self.assertEqual("range=1.0,2.00,3.000,4.0000", param.get_bash())
         self.assertEqual("range=1.0,2.00,3.000,4.0000", param.get_bash())
 
 
         with self.assertRaises(TypeError):
         with self.assertRaises(TypeError):
             _check_value(param, 1)
             _check_value(param, 1)
 
 
-if __name__ == '__main__':
+
+if __name__ == "__main__":
     test()
     test()

+ 9 - 7
python/grass/pygrass/modules/interface/testsuite/test_pygrass_modules_interface_doctests.py

@@ -17,12 +17,14 @@ import grass.pygrass.modules as gmodules
 # and contains doctest's methods
 # and contains doctest's methods
 # the alternative is to copy 500 from doctest and change what is needed
 # the alternative is to copy 500 from doctest and change what is needed
 # (this might be necessary anyway because of the reports and stdout and stderr)
 # (this might be necessary anyway because of the reports and stdout and stderr)
-doctest.DocFileCase = type('DocFileCase',
-                           (grass.gunittest.case.TestCase,),
-                           dict(doctest.DocFileCase.__dict__))
-doctest.SkipDocTestCase = type('SkipDocTestCase',
-                               (grass.gunittest.case.TestCase,),
-                               dict(doctest.SkipDocTestCase.__dict__))
+doctest.DocFileCase = type(
+    "DocFileCase", (grass.gunittest.case.TestCase,), dict(doctest.DocFileCase.__dict__)
+)
+doctest.SkipDocTestCase = type(
+    "SkipDocTestCase",
+    (grass.gunittest.case.TestCase,),
+    dict(doctest.SkipDocTestCase.__dict__),
+)
 
 
 
 
 def load_tests(loader, tests, ignore):
 def load_tests(loader, tests, ignore):
@@ -38,5 +40,5 @@ def load_tests(loader, tests, ignore):
     return tests
     return tests
 
 
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     grass.gunittest.main.test()
     grass.gunittest.main.test()

+ 12 - 5
python/grass/pygrass/modules/interface/typedict.py

@@ -4,9 +4,17 @@ Created on Tue Apr  2 18:37:02 2013
 
 
 @author: pietro
 @author: pietro
 """
 """
-from __future__ import (nested_scopes, generators, division, absolute_import,
-                        with_statement, print_function, unicode_literals)
+from __future__ import (
+    nested_scopes,
+    generators,
+    division,
+    absolute_import,
+    with_statement,
+    print_function,
+    unicode_literals,
+)
 from copy import deepcopy
 from copy import deepcopy
+
 try:
 try:
     from collections import OrderedDict
     from collections import OrderedDict
 except ImportError:
 except ImportError:
@@ -38,13 +46,12 @@ class TypeDict(OrderedDict):
         if isinstance(value, self._type):
         if isinstance(value, self._type):
             super(TypeDict, self).__setitem__(key, value)
             super(TypeDict, self).__setitem__(key, value)
         else:
         else:
-            str_err = 'The value: %r is not a %s instance.'
+            str_err = "The value: %r is not a %s instance."
             raise TypeError(str_err % (value, self._type.__name__))
             raise TypeError(str_err % (value, self._type.__name__))
 
 
     @docstring_property(__doc__)
     @docstring_property(__doc__)
     def __doc__(self):
     def __doc__(self):
-        return '\n'.join([self.__getitem__(obj).__doc__
-                          for obj in self.__iter__()])
+        return "\n".join([self.__getitem__(obj).__doc__ for obj in self.__iter__()])
 
 
     def __call__(self):
     def __call__(self):
         return [self.__getitem__(obj) for obj in self.__iter__()]
         return [self.__getitem__(obj) for obj in self.__iter__()]

+ 56 - 48
python/grass/pygrass/modules/shortcuts.py

@@ -1,6 +1,13 @@
 # -*- coding: utf-8 -*-
 # -*- coding: utf-8 -*-
-from __future__ import (nested_scopes, generators, division, absolute_import,
-                        with_statement, print_function, unicode_literals)
+from __future__ import (
+    nested_scopes,
+    generators,
+    division,
+    absolute_import,
+    with_statement,
+    print_function,
+    unicode_literals,
+)
 import fnmatch
 import fnmatch
 
 
 
 
@@ -14,38 +21,38 @@ _CMDS.sort()
 class MetaModule(object):
 class MetaModule(object):
     """Example how to use MetaModule
     """Example how to use MetaModule
 
 
-       >>> g = MetaModule('g')
-       >>> g_list = g.list
-       >>> g_list.name
-       'g.list'
-       >>> g_list.required
-       ['type']
-       >>> g_list.inputs.type = 'raster'
-       >>> g_list.inputs.mapset = 'PERMANENT'
-       >>> g_list.stdout_ = -1
-       >>> g_list.run()
-       Module('g.list')
-       >>> g_list.outputs.stdout                         # doctest: +ELLIPSIS
-       '...basin...elevation...'
+    >>> g = MetaModule('g')
+    >>> g_list = g.list
+    >>> g_list.name
+    'g.list'
+    >>> g_list.required
+    ['type']
+    >>> g_list.inputs.type = 'raster'
+    >>> g_list.inputs.mapset = 'PERMANENT'
+    >>> g_list.stdout_ = -1
+    >>> g_list.run()
+    Module('g.list')
+    >>> g_list.outputs.stdout                         # doctest: +ELLIPSIS
+    '...basin...elevation...'
 
 
-       >>> r = MetaModule('r')
-       >>> what = r.what
-       >>> what.description
-       'Queries raster maps on their category values and category labels.'
-       >>> what.inputs.map = 'elevation'
-       >>> what.inputs.coordinates = [640000,220500]          # doctest: +SKIP
-       >>> what.run()                                         # doctest: +SKIP
-       >>> v = MetaModule('v')
-       >>> v.import                                      # doctest: +ELLIPSIS
-       Traceback (most recent call last):
-         File ".../doctest.py", line 1315, in __run
-          compileflags, 1) in test.globs
-         File "<doctest grass.pygrass.modules.shortcuts.MetaModule[16]>", line 1
-          v.import
-               ^
-       SyntaxError: invalid syntax
-       >>> v.import_
-       Module('v.import')
+    >>> r = MetaModule('r')
+    >>> what = r.what
+    >>> what.description
+    'Queries raster maps on their category values and category labels.'
+    >>> what.inputs.map = 'elevation'
+    >>> what.inputs.coordinates = [640000,220500]          # doctest: +SKIP
+    >>> what.run()                                         # doctest: +SKIP
+    >>> v = MetaModule('v')
+    >>> v.import                                      # doctest: +ELLIPSIS
+    Traceback (most recent call last):
+      File ".../doctest.py", line 1315, in __run
+       compileflags, 1) in test.globs
+      File "<doctest grass.pygrass.modules.shortcuts.MetaModule[16]>", line 1
+       v.import
+            ^
+    SyntaxError: invalid syntax
+    >>> v.import_
+    Module('v.import')
     """
     """
 
 
     def __init__(self, prefix, cls=None):
     def __init__(self, prefix, cls=None):
@@ -53,16 +60,17 @@ class MetaModule(object):
         self.cls = cls if cls else Module
         self.cls = cls if cls else Module
 
 
     def __dir__(self):
     def __dir__(self):
-        return [mod[(len(self.prefix) + 1):].replace('.', '_')
-                for mod in fnmatch.filter(_CMDS, "%s.*" % self.prefix)]
+        return [
+            mod[(len(self.prefix) + 1) :].replace(".", "_")
+            for mod in fnmatch.filter(_CMDS, "%s.*" % self.prefix)
+        ]
 
 
     def __getattr__(self, name):
     def __getattr__(self, name):
-        return self.cls('%s.%s' % (self.prefix,
-                                   name.strip('_').replace('_', '.')))
+        return self.cls("%s.%s" % (self.prefix, name.strip("_").replace("_", ".")))
 
 
 
 
 # https://grass.osgeo.org/grass79/manuals/full_index.html
 # https://grass.osgeo.org/grass79/manuals/full_index.html
-#[ d.* | db.* | g.* | i.* | m.* | ps.* | r.* | r3.* | t.* | v.* ]
+# [ d.* | db.* | g.* | i.* | m.* | ps.* | r.* | r3.* | t.* | v.* ]
 #
 #
 #  d.*	display commands
 #  d.*	display commands
 #  db.*	database commands
 #  db.*	database commands
@@ -75,13 +83,13 @@ class MetaModule(object):
 #  t.*	temporal commands
 #  t.*	temporal commands
 #  v.*	vector commands
 #  v.*	vector commands
 
 
-display = MetaModule('d')
-database = MetaModule('db')
-general = MetaModule('g')
-imagery = MetaModule('i')
-miscellaneous = MetaModule('m')
-postscript = MetaModule('ps')
-raster = MetaModule('r')
-raster3d = MetaModule('r3')
-temporal = MetaModule('t')
-vector = MetaModule('v')
+display = MetaModule("d")
+database = MetaModule("db")
+general = MetaModule("g")
+imagery = MetaModule("i")
+miscellaneous = MetaModule("m")
+postscript = MetaModule("ps")
+raster = MetaModule("r")
+raster3d = MetaModule("r3")
+temporal = MetaModule("t")
+vector = MetaModule("v")

+ 19 - 11
python/grass/pygrass/modules/testsuite/test_import_isolation.py

@@ -20,8 +20,7 @@ from grass.gunittest.main import test
 
 
 
 
 def check(*patterns):
 def check(*patterns):
-    """Return a set of the imported libraries that soddisfies several patterns.
-    """
+    """Return a set of the imported libraries that soddisfies several patterns."""
     result = []
     result = []
     imports = sorted(sys.modules.keys())
     imports = sorted(sys.modules.keys())
     for pattern in patterns:
     for pattern in patterns:
@@ -30,24 +29,33 @@ def check(*patterns):
 
 
 
 
 class TestImportIsolation(TestCase):
 class TestImportIsolation(TestCase):
-    patterns = ['grass.lib*']
+    patterns = ["grass.lib*"]
 
 
     def test_import_isolation(self):
     def test_import_isolation(self):
         """Check that modules  classes are not using ctypes"""
         """Check that modules  classes are not using ctypes"""
         isolate = set()
         isolate = set()
-        self.assertEqual(isolate, check(*self.patterns),
-                         msg="Test isolation before any import.")
+        self.assertEqual(
+            isolate, check(*self.patterns), msg="Test isolation before any import."
+        )
         # same import done in __init__ file
         # same import done in __init__ file
         from grass.pygrass.modules.interface import Module, ParallelModuleQueue
         from grass.pygrass.modules.interface import Module, ParallelModuleQueue
         from grass.pygrass.modules import shortcuts
         from grass.pygrass.modules import shortcuts
-        self.assertEqual(isolate, check(*self.patterns),
-                         msg="Test isolation after import Module.")
+
+        self.assertEqual(
+            isolate, check(*self.patterns), msg="Test isolation after import Module."
+        )
         # test the other way round
         # test the other way round
         from grass.pygrass.vector import VectorTopo
         from grass.pygrass.vector import VectorTopo
-        self.assertNotEqual(isolate, check(*self.patterns),
-                            msg=("Test the isolation is broken, therefore "
-                                 "the defined patterns are correct"))
+
+        self.assertNotEqual(
+            isolate,
+            check(*self.patterns),
+            msg=(
+                "Test the isolation is broken, therefore "
+                "the defined patterns are correct"
+            ),
+        )
 
 
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     test()
     test()

+ 10 - 8
python/grass/pygrass/modules/testsuite/test_pygrass_modules_doctests.py

@@ -17,12 +17,14 @@ from grass.pygrass.modules import shortcuts, grid, interface
 # and contains doctest's methods
 # and contains doctest's methods
 # the alternative is to copy 500 from doctest and change what is needed
 # the alternative is to copy 500 from doctest and change what is needed
 # (this might be necessary anyway because of the reports and stdout and stderr)
 # (this might be necessary anyway because of the reports and stdout and stderr)
-doctest.DocFileCase = type('DocFileCase',
-                           (grass.gunittest.case.TestCase,),
-                           dict(doctest.DocFileCase.__dict__))
-doctest.SkipDocTestCase = type('SkipDocTestCase',
-                               (grass.gunittest.case.TestCase,),
-                               dict(doctest.SkipDocTestCase.__dict__))
+doctest.DocFileCase = type(
+    "DocFileCase", (grass.gunittest.case.TestCase,), dict(doctest.DocFileCase.__dict__)
+)
+doctest.SkipDocTestCase = type(
+    "SkipDocTestCase",
+    (grass.gunittest.case.TestCase,),
+    dict(doctest.SkipDocTestCase.__dict__),
+)
 
 
 
 
 def load_tests(loader, tests, ignore):
 def load_tests(loader, tests, ignore):
@@ -35,9 +37,9 @@ def load_tests(loader, tests, ignore):
     tests.addTests(doctest.DocTestSuite(grid.grid))
     tests.addTests(doctest.DocTestSuite(grid.grid))
     tests.addTests(doctest.DocTestSuite(grid.patch))
     tests.addTests(doctest.DocTestSuite(grid.patch))
     tests.addTests(doctest.DocTestSuite(grid.split))
     tests.addTests(doctest.DocTestSuite(grid.split))
-#    tests.addTests(doctest.DocTestSuite(shortcuts))
+    #    tests.addTests(doctest.DocTestSuite(shortcuts))
     return tests
     return tests
 
 
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     grass.gunittest.main.test()
     grass.gunittest.main.test()

+ 40 - 38
python/grass/pygrass/orderdict.py

@@ -14,7 +14,7 @@ except ImportError:
 
 
 
 
 class OrderedDict(dict):
 class OrderedDict(dict):
-    'Dictionary that remembers insertion order'
+    "Dictionary that remembers insertion order"
     # An inherited dict maps keys to values.
     # An inherited dict maps keys to values.
     # The inherited dict provides __getitem__, __len__, __contains__, and get.
     # The inherited dict provides __getitem__, __len__, __contains__, and get.
     # The remaining methods are order-aware.
     # The remaining methods are order-aware.
@@ -26,23 +26,23 @@ class OrderedDict(dict):
     # Each link is stored as a list of length three:  [PREV, NEXT, KEY].
     # Each link is stored as a list of length three:  [PREV, NEXT, KEY].
 
 
     def __init__(self, *args, **kwds):
     def __init__(self, *args, **kwds):
-        '''Initialize an ordered dictionary.  Signature is the same as for
+        """Initialize an ordered dictionary.  Signature is the same as for
         regular dictionaries, but keyword arguments are not recommended
         regular dictionaries, but keyword arguments are not recommended
         because their insertion order is arbitrary.
         because their insertion order is arbitrary.
 
 
-        '''
+        """
         if len(args) > 1:
         if len(args) > 1:
-            raise TypeError('expected at most 1 arguments, got %d' % len(args))
+            raise TypeError("expected at most 1 arguments, got %d" % len(args))
         try:
         try:
             self.__root
             self.__root
         except AttributeError:
         except AttributeError:
-            self.__root = root = []                     # sentinel node
+            self.__root = root = []  # sentinel node
             root[:] = [root, root, None]
             root[:] = [root, root, None]
             self.__map = {}
             self.__map = {}
         self.__update(*args, **kwds)
         self.__update(*args, **kwds)
 
 
     def __setitem__(self, key, value, dict_setitem=dict.__setitem__):
     def __setitem__(self, key, value, dict_setitem=dict.__setitem__):
-        'od.__setitem__(i, y) <==> od[i]=y'
+        "od.__setitem__(i, y) <==> od[i]=y"
         # Setting a new item creates a new link which goes at the end of the linked
         # Setting a new item creates a new link which goes at the end of the linked
         # list, and the inherited dictionary is updated with the new key/value pair.
         # list, and the inherited dictionary is updated with the new key/value pair.
         if key not in self:
         if key not in self:
@@ -52,7 +52,7 @@ class OrderedDict(dict):
         dict_setitem(self, key, value)
         dict_setitem(self, key, value)
 
 
     def __delitem__(self, key, dict_delitem=dict.__delitem__):
     def __delitem__(self, key, dict_delitem=dict.__delitem__):
-        'od.__delitem__(y) <==> del od[y]'
+        "od.__delitem__(y) <==> del od[y]"
         # Deleting an existing item uses self.__map to find the link which is
         # Deleting an existing item uses self.__map to find the link which is
         # then removed by updating the links in the predecessor and successor nodes.
         # then removed by updating the links in the predecessor and successor nodes.
         dict_delitem(self, key)
         dict_delitem(self, key)
@@ -61,7 +61,7 @@ class OrderedDict(dict):
         link_next[0] = link_prev
         link_next[0] = link_prev
 
 
     def __iter__(self):
     def __iter__(self):
-        'od.__iter__() <==> iter(od)'
+        "od.__iter__() <==> iter(od)"
         root = self.__root
         root = self.__root
         curr = root[1]
         curr = root[1]
         while curr is not root:
         while curr is not root:
@@ -69,7 +69,7 @@ class OrderedDict(dict):
             curr = curr[1]
             curr = curr[1]
 
 
     def __reversed__(self):
     def __reversed__(self):
-        'od.__reversed__() <==> reversed(od)'
+        "od.__reversed__() <==> reversed(od)"
         root = self.__root
         root = self.__root
         curr = root[0]
         curr = root[0]
         while curr is not root:
         while curr is not root:
@@ -77,7 +77,7 @@ class OrderedDict(dict):
             curr = curr[0]
             curr = curr[0]
 
 
     def clear(self):
     def clear(self):
-        'od.clear() -> None.  Remove all items from od.'
+        "od.clear() -> None.  Remove all items from od."
         try:
         try:
             for node in self.__map.itervalues():
             for node in self.__map.itervalues():
                 del node[:]
                 del node[:]
@@ -89,12 +89,12 @@ class OrderedDict(dict):
         dict.clear(self)
         dict.clear(self)
 
 
     def popitem(self, last=True):
     def popitem(self, last=True):
-        '''od.popitem() -> (k, v), return and remove a (key, value) pair.
+        """od.popitem() -> (k, v), return and remove a (key, value) pair.
         Pairs are returned in LIFO order if last is true or FIFO order if false.
         Pairs are returned in LIFO order if last is true or FIFO order if false.
 
 
-        '''
+        """
         if not self:
         if not self:
-            raise KeyError('dictionary is empty')
+            raise KeyError("dictionary is empty")
         root = self.__root
         root = self.__root
         if last:
         if last:
             link = root[0]
             link = root[0]
@@ -114,45 +114,47 @@ class OrderedDict(dict):
     # -- the following methods do not depend on the internal structure --
     # -- the following methods do not depend on the internal structure --
 
 
     def keys(self):
     def keys(self):
-        'od.keys() -> list of keys in od'
+        "od.keys() -> list of keys in od"
         return list(self)
         return list(self)
 
 
     def values(self):
     def values(self):
-        'od.values() -> list of values in od'
+        "od.values() -> list of values in od"
         return [self[key] for key in self]
         return [self[key] for key in self]
 
 
     def items(self):
     def items(self):
-        'od.items() -> list of (key, value) pairs in od'
+        "od.items() -> list of (key, value) pairs in od"
         return [(key, self[key]) for key in self]
         return [(key, self[key]) for key in self]
 
 
     def iterkeys(self):
     def iterkeys(self):
-        'od.iterkeys() -> an iterator over the keys in od'
+        "od.iterkeys() -> an iterator over the keys in od"
         return iter(self)
         return iter(self)
 
 
     def itervalues(self):
     def itervalues(self):
-        'od.itervalues -> an iterator over the values in od'
+        "od.itervalues -> an iterator over the values in od"
         for k in self:
         for k in self:
             yield self[k]
             yield self[k]
 
 
     def iteritems(self):
     def iteritems(self):
-        'od.iteritems -> an iterator over the (key, value) items in od'
+        "od.iteritems -> an iterator over the (key, value) items in od"
         for k in self:
         for k in self:
             yield (k, self[k])
             yield (k, self[k])
 
 
     def update(*args, **kwds):
     def update(*args, **kwds):
-        '''od.update(E, **F) -> None.  Update od from dict/iterable E and F.
+        """od.update(E, **F) -> None.  Update od from dict/iterable E and F.
 
 
         If E is a dict instance, does:           for k in E: od[k] = E[k]
         If E is a dict instance, does:           for k in E: od[k] = E[k]
         If E has a .keys() method, does:         for k in E.keys(): od[k] = E[k]
         If E has a .keys() method, does:         for k in E.keys(): od[k] = E[k]
         Or if E is an iterable of items, does:   for k, v in E: od[k] = v
         Or if E is an iterable of items, does:   for k, v in E: od[k] = v
         In either case, this is followed by:     for k, v in F.items(): od[k] = v
         In either case, this is followed by:     for k, v in F.items(): od[k] = v
 
 
-        '''
+        """
         if len(args) > 2:
         if len(args) > 2:
-            raise TypeError('update() takes at most 2 positional '
-                            'arguments (%d given)' % (len(args),))
+            raise TypeError(
+                "update() takes at most 2 positional "
+                "arguments (%d given)" % (len(args),)
+            )
         elif not args:
         elif not args:
-            raise TypeError('update() takes at least 1 argument (0 given)')
+            raise TypeError("update() takes at least 1 argument (0 given)")
         self = args[0]
         self = args[0]
         # Make progressively weaker assumptions about "other"
         # Make progressively weaker assumptions about "other"
         other = ()
         other = ()
@@ -161,7 +163,7 @@ class OrderedDict(dict):
         if isinstance(other, dict):
         if isinstance(other, dict):
             for key in other:
             for key in other:
                 self[key] = other[key]
                 self[key] = other[key]
-        elif hasattr(other, 'keys'):
+        elif hasattr(other, "keys"):
             for key in other.keys():
             for key in other.keys():
                 self[key] = other[key]
                 self[key] = other[key]
         else:
         else:
@@ -175,10 +177,10 @@ class OrderedDict(dict):
     __marker = object()
     __marker = object()
 
 
     def pop(self, key, default=__marker):
     def pop(self, key, default=__marker):
-        '''od.pop(k[,d]) -> v, remove specified key and return the corresponding value.
+        """od.pop(k[,d]) -> v, remove specified key and return the corresponding value.
         If key is not found, d is returned if given, otherwise KeyError is raised.
         If key is not found, d is returned if given, otherwise KeyError is raised.
 
 
-        '''
+        """
         if key in self:
         if key in self:
             result = self[key]
             result = self[key]
             del self[key]
             del self[key]
@@ -188,27 +190,27 @@ class OrderedDict(dict):
         return default
         return default
 
 
     def setdefault(self, key, default=None):
     def setdefault(self, key, default=None):
-        'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od'
+        "od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od"
         if key in self:
         if key in self:
             return self[key]
             return self[key]
         self[key] = default
         self[key] = default
         return default
         return default
 
 
     def __repr__(self, _repr_running={}):
     def __repr__(self, _repr_running={}):
-        'od.__repr__() <==> repr(od)'
+        "od.__repr__() <==> repr(od)"
         call_key = id(self), _get_ident()
         call_key = id(self), _get_ident()
         if call_key in _repr_running:
         if call_key in _repr_running:
-            return '...'
+            return "..."
         _repr_running[call_key] = 1
         _repr_running[call_key] = 1
         try:
         try:
             if not self:
             if not self:
-                return '%s()' % (self.__class__.__name__,)
-            return '%s(%r)' % (self.__class__.__name__, self.items())
+                return "%s()" % (self.__class__.__name__,)
+            return "%s(%r)" % (self.__class__.__name__, self.items())
         finally:
         finally:
             del _repr_running[call_key]
             del _repr_running[call_key]
 
 
     def __reduce__(self):
     def __reduce__(self):
-        'Return state information for pickling'
+        "Return state information for pickling"
         items = [[k, self[k]] for k in self]
         items = [[k, self[k]] for k in self]
         inst_dict = vars(self).copy()
         inst_dict = vars(self).copy()
         for k in vars(OrderedDict()):
         for k in vars(OrderedDict()):
@@ -218,25 +220,25 @@ class OrderedDict(dict):
         return self.__class__, (items,)
         return self.__class__, (items,)
 
 
     def copy(self):
     def copy(self):
-        'od.copy() -> a shallow copy of od'
+        "od.copy() -> a shallow copy of od"
         return self.__class__(self)
         return self.__class__(self)
 
 
     @classmethod
     @classmethod
     def fromkeys(cls, iterable, value=None):
     def fromkeys(cls, iterable, value=None):
-        '''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S
+        """OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S
         and values equal to v (which defaults to None).
         and values equal to v (which defaults to None).
 
 
-        '''
+        """
         d = cls()
         d = cls()
         for key in iterable:
         for key in iterable:
             d[key] = value
             d[key] = value
         return d
         return d
 
 
     def __eq__(self, other):
     def __eq__(self, other):
-        '''od.__eq__(y) <==> od==y.  Comparison to another OD is order-sensitive
+        """od.__eq__(y) <==> od==y.  Comparison to another OD is order-sensitive
         while comparison to a regular mapping is order-insensitive.
         while comparison to a regular mapping is order-insensitive.
 
 
-        '''
+        """
         if isinstance(other, OrderedDict):
         if isinstance(other, OrderedDict):
             return len(self) == len(other) and self.items() == other.items()
             return len(self) == len(other) and self.items() == other.items()
         return dict.__eq__(self, other)
         return dict.__eq__(self, other)

+ 130 - 107
python/grass/pygrass/raster/__init__.py

@@ -1,6 +1,13 @@
 # -*- coding: utf-8 -*-
 # -*- coding: utf-8 -*-
-from __future__ import (nested_scopes, generators, division, absolute_import,
-                        with_statement, print_function, unicode_literals)
+from __future__ import (
+    nested_scopes,
+    generators,
+    division,
+    absolute_import,
+    with_statement,
+    print_function,
+    unicode_literals,
+)
 import ctypes
 import ctypes
 import numpy as np
 import numpy as np
 
 
@@ -14,7 +21,7 @@ import grass.lib.gis as libgis
 import grass.lib.raster as libraster
 import grass.lib.raster as libraster
 import grass.lib.rowio as librowio
 import grass.lib.rowio as librowio
 
 
-libgis.G_gisinit('')
+libgis.G_gisinit("")
 
 
 #
 #
 # import pygrass modules
 # import pygrass modules
@@ -137,26 +144,26 @@ class RasterRow(RasterAbstractBase):
 
 
     """
     """
 
 
-    def __init__(self, name, mapset='', *args, **kargs):
+    def __init__(self, name, mapset="", *args, **kargs):
         super(RasterRow, self).__init__(name, mapset, *args, **kargs)
         super(RasterRow, self).__init__(name, mapset, *args, **kargs)
 
 
     # mode = "r", method = "row",
     # mode = "r", method = "row",
     @must_be_open
     @must_be_open
     def get_row(self, row, row_buffer=None):
     def get_row(self, row, row_buffer=None):
         """Private method that return the row using the read mode
         """Private method that return the row using the read mode
-            call the `Rast_get_row` C function.
+        call the `Rast_get_row` C function.
 
 
-            :param row: the number of row to obtain
-            :type row: int
-            :param row_buffer: Buffer object instance with the right dim and type
-            :type row_buffer: Buffer
+        :param row: the number of row to obtain
+        :type row: int
+        :param row_buffer: Buffer object instance with the right dim and type
+        :type row_buffer: Buffer
 
 
-            >>> elev = RasterRow(test_raster_name)
-            >>> elev.open()
-            >>> elev[0]
-            Buffer([11, 21, 31, 41], dtype=int32)
-            >>> elev.get_row(0)
-            Buffer([11, 21, 31, 41], dtype=int32)
+        >>> elev = RasterRow(test_raster_name)
+        >>> elev.open()
+        >>> elev[0]
+        Buffer([11, 21, 31, 41], dtype=int32)
+        >>> elev.get_row(0)
+        Buffer([11, 21, 31, 41], dtype=int32)
 
 
         """
         """
         if row_buffer is None:
         if row_buffer is None:
@@ -198,7 +205,7 @@ class RasterRow(RasterAbstractBase):
         self.mtype = mtype if mtype else self.mtype
         self.mtype = mtype if mtype else self.mtype
         self.overwrite = overwrite if overwrite is not None else self.overwrite
         self.overwrite = overwrite if overwrite is not None else self.overwrite
 
 
-        if self.mode == 'r':
+        if self.mode == "r":
             if self.exist():
             if self.exist():
                 self.info.read()
                 self.info.read()
                 self.cats.mtype = self.mtype
                 self.cats.mtype = self.mtype
@@ -210,18 +217,18 @@ class RasterRow(RasterAbstractBase):
             else:
             else:
                 str_err = _("The map does not exist, I can't open in 'r' mode")
                 str_err = _("The map does not exist, I can't open in 'r' mode")
                 raise OpenError(str_err)
                 raise OpenError(str_err)
-        elif self.mode == 'w':
+        elif self.mode == "w":
             if self.exist():
             if self.exist():
                 if not self.overwrite:
                 if not self.overwrite:
-                    str_err = _("Raster map <{0}> already exists"
-                                " and will be not overwritten")
+                    str_err = _(
+                        "Raster map <{0}> already exists" " and will be not overwritten"
+                    )
                     raise OpenError(str_err.format(self))
                     raise OpenError(str_err.format(self))
             if self._gtype is None:
             if self._gtype is None:
                 raise OpenError(_("Raster type not defined"))
                 raise OpenError(_("Raster type not defined"))
             self._fd = libraster.Rast_open_new(self.name, self._gtype)
             self._fd = libraster.Rast_open_new(self.name, self._gtype)
         else:
         else:
-            raise OpenError("Open mode: %r not supported,"
-                            " valid mode are: r, w")
+            raise OpenError("Open mode: %r not supported," " valid mode are: r, w")
         # read rows and cols from the active region
         # read rows and cols from the active region
         self._rows = libraster.Rast_window_rows()
         self._rows = libraster.Rast_window_rows()
         self._cols = libraster.Rast_window_cols()
         self._cols = libraster.Rast_window_cols()
@@ -312,8 +319,7 @@ class RasterSegment(RasterAbstractBase):
 
 
     """
     """
 
 
-    def __init__(self, name, srows=64, scols=64, maxmem=100,
-                 *args, **kargs):
+    def __init__(self, name, srows=64, scols=64, maxmem=100, *args, **kargs):
         self.segment = Segment(srows, scols, maxmem)
         self.segment = Segment(srows, scols, maxmem)
         super(RasterSegment, self).__init__(name, *args, **kargs)
         super(RasterSegment, self).__init__(name, *args, **kargs)
 
 
@@ -321,20 +327,20 @@ class RasterSegment(RasterAbstractBase):
         return self._mode
         return self._mode
 
 
     def _set_mode(self, mode):
     def _set_mode(self, mode):
-        if mode and mode.lower() not in ('r', 'w', 'rw'):
+        if mode and mode.lower() not in ("r", "w", "rw"):
             str_err = _("Mode type: {0} not supported ('r', 'w','rw')")
             str_err = _("Mode type: {0} not supported ('r', 'w','rw')")
             raise ValueError(str_err.format(mode))
             raise ValueError(str_err.format(mode))
         self._mode = mode
         self._mode = mode
 
 
-    mode = property(fget=_get_mode, fset=_set_mode,
-                    doc="Set or obtain the opening mode of raster")
+    mode = property(
+        fget=_get_mode, fset=_set_mode, doc="Set or obtain the opening mode of raster"
+    )
 
 
     def __setitem__(self, key, row):
     def __setitem__(self, key, row):
         """Return the row of Raster object, slice allowed."""
         """Return the row of Raster object, slice allowed."""
         if isinstance(key, slice):
         if isinstance(key, slice):
             # Get the start, stop, and step from the slice
             # Get the start, stop, and step from the slice
-            return [self.put_row(ii, row)
-                    for ii in range(*key.indices(len(self)))]
+            return [self.put_row(ii, row) for ii in range(*key.indices(len(self)))]
         elif isinstance(key, tuple):
         elif isinstance(key, tuple):
             x, y = key
             x, y = key
             return self.put(x, y, row)
             return self.put(x, y, row)
@@ -349,18 +355,15 @@ class RasterSegment(RasterAbstractBase):
 
 
     @must_be_open
     @must_be_open
     def map2segment(self):
     def map2segment(self):
-        """Transform an existing map to segment file.
-        """
+        """Transform an existing map to segment file."""
         row_buffer = Buffer((self._cols), self.mtype)
         row_buffer = Buffer((self._cols), self.mtype)
         for row in range(self._rows):
         for row in range(self._rows):
-            libraster.Rast_get_row(
-                self._fd, row_buffer.p, row, self._gtype)
+            libraster.Rast_get_row(self._fd, row_buffer.p, row, self._gtype)
             self.segment.put_row(row, row_buffer)
             self.segment.put_row(row, row_buffer)
 
 
     @must_be_open
     @must_be_open
     def segment2map(self):
     def segment2map(self):
-        """Transform the segment file to a map.
-        """
+        """Transform the segment file to a map."""
         row_buffer = Buffer((self._cols), self.mtype)
         row_buffer = Buffer((self._cols), self.mtype)
         for row in range(self._rows):
         for row in range(self._rows):
             row_buffer = self.segment.get_row(row, row_buffer)
             row_buffer = self.segment.get_row(row, row_buffer)
@@ -404,29 +407,29 @@ class RasterSegment(RasterAbstractBase):
     def put_row(self, row, row_buffer):
     def put_row(self, row, row_buffer):
         """Write the row using the `segment.put_row` method
         """Write the row using the `segment.put_row` method
 
 
-            :param row: a Row object to insert into raster
-            :type row: Buffer object
+        :param row: a Row object to insert into raster
+        :type row: Buffer object
 
 
-            Input and output must have the same type in case of row copy
+        Input and output must have the same type in case of row copy
 
 
-            >>> map_a = RasterSegment(test_raster_name)
-            >>> map_b = RasterSegment(test_raster_name + "_segment")
-            >>> map_a.open('r')
-            >>> map_b.open('w', mtype="CELL", overwrite=True)
-            >>> for row in range(map_a.info.rows):
-            ...     map_b[row] = map_a[row] + 1000
-            >>> map_a.close()
-            >>> map_b.close()
+        >>> map_a = RasterSegment(test_raster_name)
+        >>> map_b = RasterSegment(test_raster_name + "_segment")
+        >>> map_a.open('r')
+        >>> map_b.open('w', mtype="CELL", overwrite=True)
+        >>> for row in range(map_a.info.rows):
+        ...     map_b[row] = map_a[row] + 1000
+        >>> map_a.close()
+        >>> map_b.close()
 
 
-            >>> map_b = RasterSegment(test_raster_name + "_segment")
-            >>> map_b.open("r")
-            >>> for row in map_b:
-            ...         row
-            Buffer([1011, 1021, 1031, 1041], dtype=int32)
-            Buffer([1012, 1022, 1032, 1042], dtype=int32)
-            Buffer([1013, 1023, 1033, 1043], dtype=int32)
-            Buffer([1014, 1024, 1034, 1044], dtype=int32)
-            >>> map_b.close()
+        >>> map_b = RasterSegment(test_raster_name + "_segment")
+        >>> map_b.open("r")
+        >>> for row in map_b:
+        ...         row
+        Buffer([1011, 1021, 1031, 1041], dtype=int32)
+        Buffer([1012, 1022, 1032, 1042], dtype=int32)
+        Buffer([1013, 1023, 1033, 1043], dtype=int32)
+        Buffer([1014, 1024, 1034, 1044], dtype=int32)
+        >>> map_b.close()
 
 
         """
         """
         self.segment.put_row(row, row_buffer)
         self.segment.put_row(row, row_buffer)
@@ -529,8 +532,7 @@ class RasterSegment(RasterAbstractBase):
             self.cats.mtype = self.mtype
             self.cats.mtype = self.mtype
             self.cats.read()
             self.cats.read()
             self.hist.read()
             self.hist.read()
-            if ((self.mode == "w" or self.mode == "rw") and
-                    self.overwrite is False):
+            if (self.mode == "w" or self.mode == "rw") and self.overwrite is False:
                 str_err = _("Raster map <{0}> already exists. Use overwrite.")
                 str_err = _("Raster map <{0}> already exists. Use overwrite.")
                 fatal(str_err.format(self))
                 fatal(str_err.format(self))
 
 
@@ -552,12 +554,11 @@ class RasterSegment(RasterAbstractBase):
                     # warning(_(WARN_OVERWRITE.format(self)))
                     # warning(_(WARN_OVERWRITE.format(self)))
                     # Close the file descriptor and open it as new again
                     # Close the file descriptor and open it as new again
                     libraster.Rast_close(self._fd)
                     libraster.Rast_close(self._fd)
-                    self._fd = libraster.Rast_open_new(
-                        self.name, self._gtype)
+                    self._fd = libraster.Rast_open_new(self.name, self._gtype)
             # Here we simply overwrite the existing map without content copying
             # Here we simply overwrite the existing map without content copying
             elif self.mode == "w":
             elif self.mode == "w":
                 # warning(_(WARN_OVERWRITE.format(self)))
                 # warning(_(WARN_OVERWRITE.format(self)))
-                self._gtype = RTYPE[self.mtype]['grass type']
+                self._gtype = RTYPE[self.mtype]["grass type"]
                 self.segment.open(self)
                 self.segment.open(self)
                 self._fd = libraster.Rast_open_new(self.name, self._gtype)
                 self._fd = libraster.Rast_open_new(self.name, self._gtype)
         else:
         else:
@@ -565,7 +566,7 @@ class RasterSegment(RasterAbstractBase):
                 str_err = _("Raster map <{0}> does not exist")
                 str_err = _("Raster map <{0}> does not exist")
                 raise OpenError(str_err.format(self.name))
                 raise OpenError(str_err.format(self.name))
 
 
-            self._gtype = RTYPE[self.mtype]['grass type']
+            self._gtype = RTYPE[self.mtype]["grass type"]
             self.segment.open(self)
             self.segment.open(self)
             self._fd = libraster.Rast_open_new(self.name, self._gtype)
             self._fd = libraster.Rast_open_new(self.name, self._gtype)
 
 
@@ -593,9 +594,17 @@ class RasterSegment(RasterAbstractBase):
 def random_map_only_columns(mapname, mtype, overwrite=True, factor=100):
 def random_map_only_columns(mapname, mtype, overwrite=True, factor=100):
     region = Region()
     region = Region()
     random_map = RasterRow(mapname)
     random_map = RasterRow(mapname)
-    row_buf = Buffer((region.cols, ), mtype,
-                     buffer=(np.random.random(region.cols,) * factor).data)
-    random_map.open('w', mtype, overwrite)
+    row_buf = Buffer(
+        (region.cols,),
+        mtype,
+        buffer=(
+            np.random.random(
+                region.cols,
+            )
+            * factor
+        ).data,
+    )
+    random_map.open("w", mtype, overwrite)
     for _ in range(region.rows):
     for _ in range(region.rows):
         random_map.put_row(row_buf)
         random_map.put_row(row_buf)
     random_map.close()
     random_map.close()
@@ -605,62 +614,71 @@ def random_map_only_columns(mapname, mtype, overwrite=True, factor=100):
 def random_map(mapname, mtype, overwrite=True, factor=100):
 def random_map(mapname, mtype, overwrite=True, factor=100):
     region = Region()
     region = Region()
     random_map = RasterRow(mapname)
     random_map = RasterRow(mapname)
-    random_map.open('w', mtype, overwrite)
+    random_map.open("w", mtype, overwrite)
     for _ in range(region.rows):
     for _ in range(region.rows):
-        row_buf = Buffer((region.cols, ), mtype,
-                         buffer=(np.random.random(region.cols,) * factor).data)
+        row_buf = Buffer(
+            (region.cols,),
+            mtype,
+            buffer=(
+                np.random.random(
+                    region.cols,
+                )
+                * factor
+            ).data,
+        )
         random_map.put_row(row_buf)
         random_map.put_row(row_buf)
     random_map.close()
     random_map.close()
     return random_map
     return random_map
 
 
 
 
-def raster2numpy(rastname, mapset=''):
+def raster2numpy(rastname, mapset=""):
     """Return a numpy array from a raster map
     """Return a numpy array from a raster map
 
 
     :param str rastname: the name of raster map
     :param str rastname: the name of raster map
     :parar str mapset: the name of mapset containig raster map
     :parar str mapset: the name of mapset containig raster map
     """
     """
-    with RasterRow(rastname, mapset=mapset, mode='r') as rast:
+    with RasterRow(rastname, mapset=mapset, mode="r") as rast:
         return np.array(rast)
         return np.array(rast)
 
 
 
 
 def raster2numpy_img(rastname, region, color="ARGB", array=None):
 def raster2numpy_img(rastname, region, color="ARGB", array=None):
     """Convert a raster map layer into a string with
     """Convert a raster map layer into a string with
-       32Bit ARGB, 24Bit RGB or 8Bit Gray little endian encoding.
+    32Bit ARGB, 24Bit RGB or 8Bit Gray little endian encoding.
 
 
-        Return a numpy array from a raster map of type uint8
-        that contains the colored map data as 32 bit ARGB, 32Bit RGB
-        or 8 bit image
+     Return a numpy array from a raster map of type uint8
+     that contains the colored map data as 32 bit ARGB, 32Bit RGB
+     or 8 bit image
 
 
-       :param rastname: The name of raster map
-       :type rastname: string
+    :param rastname: The name of raster map
+    :type rastname: string
 
 
-       :param region: The region to be used for raster map reading
-       :type region: grass.pygrass.gis.region.Region
+    :param region: The region to be used for raster map reading
+    :type region: grass.pygrass.gis.region.Region
 
 
-       :param color: "ARGB", "RGB", "GRAY1", "GRAY2"
-                     ARGB  -> 32Bit RGB with alpha channel (0xAARRGGBB)
-                     RGB   -> 32Bit RGB (0xffRRGGBB)
-                     GRAY1 -> grey scale formular: .33R+ .5G+ .17B
-                     GRAY2 -> grey scale formular: .30R+ .59G+ .11B
-       :type color: String
+    :param color: "ARGB", "RGB", "GRAY1", "GRAY2"
+                  ARGB  -> 32Bit RGB with alpha channel (0xAARRGGBB)
+                  RGB   -> 32Bit RGB (0xffRRGGBB)
+                  GRAY1 -> grey scale formular: .33R+ .5G+ .17B
+                  GRAY2 -> grey scale formular: .30R+ .59G+ .11B
+    :type color: String
 
 
-       :param array: A numpy array (optional) to store the image,
-                     the array needs to setup as follows:
+    :param array: A numpy array (optional) to store the image,
+                  the array needs to setup as follows:
 
 
-                     array = np.ndarray((region.rows*region.cols*scale), np.uint8)
+                  array = np.ndarray((region.rows*region.cols*scale), np.uint8)
 
 
-                     scale = 4 in case of ARGB and RGB or scale = 1
-                     in case of Gray scale
-       :type array: numpy.ndarray
+                  scale = 4 in case of ARGB and RGB or scale = 1
+                  in case of Gray scale
+    :type array: numpy.ndarray
 
 
-       :return: A numpy array of size rows*cols*4 in case of ARGB, RGB and
-                rows*cols*1 in case of gray scale
+    :return: A numpy array of size rows*cols*4 in case of ARGB, RGB and
+             rows*cols*1 in case of gray scale
 
 
-       Attention: This function will change the computational raster region
-       of the current process while running.
+    Attention: This function will change the computational raster region
+    of the current process while running.
     """
     """
     from copy import deepcopy
     from copy import deepcopy
+
     region_orig = deepcopy(region)
     region_orig = deepcopy(region)
     # Set the raster region
     # Set the raster region
     region.set_raster_region()
     region.set_raster_region()
@@ -683,8 +701,9 @@ def raster2numpy_img(rastname, region, color="ARGB", array=None):
     if array is None:
     if array is None:
         array = np.ndarray((region.rows * region.cols * scale), np.uint8)
         array = np.ndarray((region.rows * region.cols * scale), np.uint8)
 
 
-    libraster.Rast_map_to_img_str(rastname, color_mode,
-                                  array.ctypes.data_as(ctypes.POINTER(ctypes.c_uint8)))
+    libraster.Rast_map_to_img_str(
+        rastname, color_mode, array.ctypes.data_as(ctypes.POINTER(ctypes.c_uint8))
+    )
     # Restore the raster region
     # Restore the raster region
     region_orig.set_raster_region()
     region_orig.set_raster_region()
 
 
@@ -703,24 +722,31 @@ def numpy2raster(array, mtype, rastname, overwrite=False):
     if (reg.rows, reg.cols) != array.shape:
     if (reg.rows, reg.cols) != array.shape:
         msg = "Region and array are different: %r != %r"
         msg = "Region and array are different: %r != %r"
         raise TypeError(msg % ((reg.rows, reg.cols), array.shape))
         raise TypeError(msg % ((reg.rows, reg.cols), array.shape))
-    with RasterRow(rastname, mode='w', mtype=mtype, overwrite=overwrite) as new:
+    with RasterRow(rastname, mode="w", mtype=mtype, overwrite=overwrite) as new:
         newrow = Buffer((array.shape[1],), mtype=mtype)
         newrow = Buffer((array.shape[1],), mtype=mtype)
         for row in array:
         for row in array:
             newrow[:] = row[:]
             newrow[:] = row[:]
             new.put_row(newrow)
             new.put_row(newrow)
 
 
+
 if __name__ == "__main__":
 if __name__ == "__main__":
 
 
     import doctest
     import doctest
     from grass.pygrass.modules import Module
     from grass.pygrass.modules import Module
+
     Module("g.region", n=40, s=0, e=40, w=0, res=10)
     Module("g.region", n=40, s=0, e=40, w=0, res=10)
-    Module("r.mapcalc",
+    Module(
+        "r.mapcalc",
         expression="%s = row() + (10 * col())" % (test_raster_name),
         expression="%s = row() + (10 * col())" % (test_raster_name),
-        overwrite=True)
-    Module("r.support", map=test_raster_name,
+        overwrite=True,
+    )
+    Module(
+        "r.support",
+        map=test_raster_name,
         title="A test map",
         title="A test map",
         history="Generated by r.mapcalc",
         history="Generated by r.mapcalc",
-        description="This is a test map")
+        description="This is a test map",
+    )
     cats = """11:A
     cats = """11:A
             12:B
             12:B
             13:C
             13:C
@@ -737,17 +763,14 @@ if __name__ == "__main__":
             42:n
             42:n
             43:O
             43:O
             44:P"""
             44:P"""
-    Module("r.category", rules="-", map=test_raster_name,
-           stdin_=cats, separator=":")
+    Module("r.category", rules="-", map=test_raster_name, stdin_=cats, separator=":")
 
 
     doctest.testmod()
     doctest.testmod()
 
 
     """Remove the generated vector map, if exist"""
     """Remove the generated vector map, if exist"""
-    mset = utils.get_mapset_raster(test_raster_name, mapset='')
+    mset = utils.get_mapset_raster(test_raster_name, mapset="")
     if mset:
     if mset:
-        Module("g.remove", flags='f', type='raster', name=test_raster_name)
-    mset = utils.get_mapset_raster(test_raster_name + "_segment",
-                                   mapset='')
+        Module("g.remove", flags="f", type="raster", name=test_raster_name)
+    mset = utils.get_mapset_raster(test_raster_name + "_segment", mapset="")
     if mset:
     if mset:
-        Module("g.remove", flags='f', type='raster',
-               name=test_raster_name + "_segment")
+        Module("g.remove", flags="f", type="raster", name=test_raster_name + "_segment")

+ 105 - 65
python/grass/pygrass/raster/abstract.py

@@ -4,8 +4,15 @@ Created on Fri Aug 17 16:05:25 2012
 
 
 @author: pietro
 @author: pietro
 """
 """
-from __future__ import (nested_scopes, generators, division, absolute_import,
-                        with_statement, print_function, unicode_literals)
+from __future__ import (
+    nested_scopes,
+    generators,
+    division,
+    absolute_import,
+    with_statement,
+    print_function,
+    unicode_literals,
+)
 import ctypes
 import ctypes
 
 
 #
 #
@@ -46,20 +53,20 @@ proj: {proj}
 
 
 
 
 class Info(object):
 class Info(object):
-    def __init__(self, name, mapset=''):
+    def __init__(self, name, mapset=""):
         """Read the information for a raster map. ::
         """Read the information for a raster map. ::
 
 
-            >>> info = Info(test_raster_name)
-            >>> info.read()
-            >>> info          # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
-            abstract_test_map@
-            rows: 4
-            cols: 4
-            north: 40.0 south: 0.0 nsres:10.0
-            east:  40.0 west: 0.0 ewres:10.0
-            range: 11, 44
-            ...
-            <BLANKLINE>
+        >>> info = Info(test_raster_name)
+        >>> info.read()
+        >>> info          # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
+        abstract_test_map@
+        rows: 4
+        cols: 4
+        north: 40.0 south: 0.0 nsres:10.0
+        east:  40.0 west: 0.0 ewres:10.0
+        range: 11, 44
+        ...
+        <BLANKLINE>
 
 
         """
         """
         self.name = name
         self.name = name
@@ -68,7 +75,7 @@ class Info(object):
         self.c_range = None
         self.c_range = None
 
 
     def _get_range(self):
     def _get_range(self):
-        if self.mtype == 'CELL':
+        if self.mtype == "CELL":
             self.c_range = ctypes.pointer(libraster.Range())
             self.c_range = ctypes.pointer(libraster.Range())
             libraster.Rast_read_range(self.name, self.mapset, self.c_range)
             libraster.Rast_read_range(self.name, self.mapset, self.c_range)
         else:
         else:
@@ -164,9 +171,9 @@ class Info(object):
         band_ref = None
         band_ref = None
         p_filename = ctypes.c_char_p()
         p_filename = ctypes.c_char_p()
         p_band_ref = ctypes.c_char_p()
         p_band_ref = ctypes.c_char_p()
-        ret = libraster.Rast_read_band_reference(self.name, self.mapset,
-                                                 ctypes.byref(p_filename),
-                                                 ctypes.byref(p_band_ref))
+        ret = libraster.Rast_read_band_reference(
+            self.name, self.mapset, ctypes.byref(p_filename), ctypes.byref(p_band_ref)
+        )
         if ret:
         if ret:
             band_ref = utils.decode(p_band_ref.value)
             band_ref = utils.decode(p_band_ref.value)
             libgis.G_free(p_filename)
             libgis.G_free(p_filename)
@@ -183,6 +190,7 @@ class Info(object):
         if band_reference:
         if band_reference:
             # assign
             # assign
             from grass.bandref import BandReferenceReader, BandReferenceReaderError
             from grass.bandref import BandReferenceReader, BandReferenceReaderError
+
             reader = BandReferenceReader()
             reader = BandReferenceReader()
             # determine filename (assuming that band_reference is unique!)
             # determine filename (assuming that band_reference is unique!)
             try:
             try:
@@ -195,9 +203,7 @@ class Info(object):
                 raise
                 raise
 
 
             # write band reference
             # write band reference
-            libraster.Rast_write_band_reference(self.name,
-                                                filename,
-                                                band_reference)
+            libraster.Rast_write_band_reference(self.name, filename, band_reference)
         else:
         else:
             libraster.Rast_remove_band_reference(self.name)
             libraster.Rast_remove_band_reference(self.name)
 
 
@@ -220,19 +226,46 @@ class Info(object):
     vdatum = property(_get_vdatum, _set_vdatum)
     vdatum = property(_get_vdatum, _set_vdatum)
 
 
     def __repr__(self):
     def __repr__(self):
-        return INFO.format(name=self.name, mapset=self.mapset,
-                           rows=self.rows, cols=self.cols,
-                           north=self.north, south=self.south,
-                           east=self.east, west=self.west,
-                           top=self.top, bottom=self.bottom,
-                           nsres=self.nsres, ewres=self.ewres,
-                           tbres=self.tbres, zone=self.zone,
-                           proj=self.proj, min=self.min, max=self.max)
+        return INFO.format(
+            name=self.name,
+            mapset=self.mapset,
+            rows=self.rows,
+            cols=self.cols,
+            north=self.north,
+            south=self.south,
+            east=self.east,
+            west=self.west,
+            top=self.top,
+            bottom=self.bottom,
+            nsres=self.nsres,
+            ewres=self.ewres,
+            tbres=self.tbres,
+            zone=self.zone,
+            proj=self.proj,
+            min=self.min,
+            max=self.max,
+        )
 
 
     def keys(self):
     def keys(self):
-        return ['name', 'mapset', 'rows', 'cols', 'north', 'south',
-                'east', 'west', 'top', 'bottom', 'nsres', 'ewres', 'tbres',
-                'zone', 'proj', 'min', 'max']
+        return [
+            "name",
+            "mapset",
+            "rows",
+            "cols",
+            "north",
+            "south",
+            "east",
+            "west",
+            "top",
+            "bottom",
+            "nsres",
+            "ewres",
+            "tbres",
+            "zone",
+            "proj",
+            "min",
+            "max",
+        ]
 
 
     def items(self):
     def items(self):
         return [(k, self.__getattribute__(k)) for k in self.keys()]
         return [(k, self.__getattribute__(k)) for k in self.keys()]
@@ -241,8 +274,7 @@ class Info(object):
         return ((k, self.__getattribute__(k)) for k in self.keys())
         return ((k, self.__getattribute__(k)) for k in self.keys())
 
 
     def _repr_html_(self):
     def _repr_html_(self):
-        return dict2html(dict(self.items()), keys=self.keys(),
-                         border='1', kdec='b')
+        return dict2html(dict(self.items()), keys=self.keys(), border="1", kdec="b")
 
 
 
 
 class RasterAbstractBase(object):
 class RasterAbstractBase(object):
@@ -295,8 +327,8 @@ class RasterAbstractBase(object):
         self.info = Info(self.name, self.mapset)
         self.info = Info(self.name, self.mapset)
         self._aopen = aopen
         self._aopen = aopen
         self._kwopen = kwopen
         self._kwopen = kwopen
-        self._mtype = 'CELL'
-        self._mode = 'r'
+        self._mtype = "CELL"
+        self._mode = "r"
         self._overwrite = False
         self._overwrite = False
 
 
     def __enter__(self):
     def __enter__(self):
@@ -312,11 +344,11 @@ class RasterAbstractBase(object):
 
 
     def _set_mtype(self, mtype):
     def _set_mtype(self, mtype):
         """Private method to change the Raster type"""
         """Private method to change the Raster type"""
-        if mtype.upper() not in ('CELL', 'FCELL', 'DCELL'):
+        if mtype.upper() not in ("CELL", "FCELL", "DCELL"):
             str_err = "Raster type: {0} not supported ('CELL','FCELL','DCELL')"
             str_err = "Raster type: {0} not supported ('CELL','FCELL','DCELL')"
             raise ValueError(_(str_err).format(mtype))
             raise ValueError(_(str_err).format(mtype))
         self._mtype = mtype
         self._mtype = mtype
-        self._gtype = RTYPE[self.mtype]['grass type']
+        self._gtype = RTYPE[self.mtype]["grass type"]
 
 
     mtype = property(fget=_get_mtype, fset=_set_mtype)
     mtype = property(fget=_get_mtype, fset=_set_mtype)
 
 
@@ -324,7 +356,7 @@ class RasterAbstractBase(object):
         return self._mode
         return self._mode
 
 
     def _set_mode(self, mode):
     def _set_mode(self, mode):
-        if mode.upper() not in ('R', 'W'):
+        if mode.upper() not in ("R", "W"):
             str_err = _("Mode type: {0} not supported ('r', 'w')")
             str_err = _("Mode type: {0} not supported ('r', 'w')")
             raise ValueError(str_err.format(mode))
             raise ValueError(str_err.format(mode))
         self._mode = mode
         self._mode = mode
@@ -391,7 +423,11 @@ class RasterAbstractBase(object):
             if key < 0:  # Handle negative indices
             if key < 0:  # Handle negative indices
                 key += self._rows
                 key += self._rows
             if key >= self._rows:
             if key >= self._rows:
-                raise IndexError("The row index {0} is out of range [0, {1}).".format(key, self._rows))
+                raise IndexError(
+                    "The row index {0} is out of range [0, {1}).".format(
+                        key, self._rows
+                    )
+                )
             return self.get_row(key)
             return self.get_row(key)
         else:
         else:
             fatal("Invalid argument type.")
             fatal("Invalid argument type.")
@@ -414,9 +450,9 @@ class RasterAbstractBase(object):
         True
         True
         """
         """
         if self.name:
         if self.name:
-            if self.mapset == '':
+            if self.mapset == "":
                 mapset = utils.get_mapset_raster(self.name, self.mapset)
                 mapset = utils.get_mapset_raster(self.name, self.mapset)
-                self.mapset = mapset if mapset else ''
+                self.mapset = mapset if mapset else ""
                 return True if mapset else False
                 return True if mapset else False
             return bool(utils.get_mapset_raster(self.name, self.mapset))
             return bool(utils.get_mapset_raster(self.name, self.mapset))
         else:
         else:
@@ -445,7 +481,7 @@ class RasterAbstractBase(object):
         """Remove the map"""
         """Remove the map"""
         if self.is_open():
         if self.is_open():
             self.close()
             self.close()
-        utils.remove(self.name, 'rast')
+        utils.remove(self.name, "rast")
 
 
     def fullname(self):
     def fullname(self):
         """Return the full name of a raster map: name@mapset"""
         """Return the full name of a raster map: name@mapset"""
@@ -468,7 +504,7 @@ class RasterAbstractBase(object):
 
 
         gis_env = gisenv()
         gis_env = gisenv()
 
 
-        if mapset and mapset != gis_env['MAPSET']:
+        if mapset and mapset != gis_env["MAPSET"]:
             return "{name}@{mapset}".format(name=name, mapset=mapset)
             return "{name}@{mapset}".format(name=name, mapset=mapset)
         else:
         else:
             return name
             return name
@@ -476,39 +512,38 @@ class RasterAbstractBase(object):
     def rename(self, newname):
     def rename(self, newname):
         """Rename the map"""
         """Rename the map"""
         if self.exist():
         if self.exist():
-            utils.rename(self.name, newname, 'rast')
+            utils.rename(self.name, newname, "rast")
         self._name = newname
         self._name = newname
 
 
-    def set_region_from_rast(self, rastname='', mapset=''):
+    def set_region_from_rast(self, rastname="", mapset=""):
         """Set the computational region from a map,
         """Set the computational region from a map,
-           if rastername and mapset is not specify, use itself.
-           This region will be used by all
-           raster map layers that are opened in the same process.
+        if rastername and mapset is not specify, use itself.
+        This region will be used by all
+        raster map layers that are opened in the same process.
 
 
-           The GRASS region settings will not be modified.
+        The GRASS region settings will not be modified.
 
 
-           call C function `Rast_get_cellhd`, `Rast_set_window`
+        call C function `Rast_get_cellhd`, `Rast_set_window`
 
 
-           """
+        """
         if self.is_open():
         if self.is_open():
             fatal("You cannot change the region if map is open")
             fatal("You cannot change the region if map is open")
             raise
             raise
         region = Region()
         region = Region()
-        if rastname == '':
+        if rastname == "":
             rastname = self.name
             rastname = self.name
-        if mapset == '':
+        if mapset == "":
             mapset = self.mapset
             mapset = self.mapset
 
 
-        libraster.Rast_get_cellhd(rastname, mapset,
-                                  region.byref())
+        libraster.Rast_get_cellhd(rastname, mapset, region.byref())
         self._set_raster_window(region)
         self._set_raster_window(region)
 
 
     def set_region(self, region):
     def set_region(self, region):
         """Set the computational region that can be different from the
         """Set the computational region that can be different from the
-           current region settings. This region will be used by all
-           raster map layers that are opened in the same process.
+        current region settings. This region will be used by all
+        raster map layers that are opened in the same process.
 
 
-           The GRASS region settings will not be modified.
+        The GRASS region settings will not be modified.
         """
         """
         if self.is_open():
         if self.is_open():
             fatal("You cannot change the region if map is open")
             fatal("You cannot change the region if map is open")
@@ -574,12 +609,12 @@ class RasterAbstractBase(object):
         self.cats.write(self)
         self.cats.write(self)
 
 
     @must_be_open
     @must_be_open
-    def read_cats_rules(self, filename, sep=':'):
+    def read_cats_rules(self, filename, sep=":"):
         """Read category from the raster map file"""
         """Read category from the raster map file"""
         self.cats.read_rules(filename, sep)
         self.cats.read_rules(filename, sep)
 
 
     @must_be_open
     @must_be_open
-    def write_cats_rules(self, filename, sep=':'):
+    def write_cats_rules(self, filename, sep=":"):
         """Write category to the raster map file"""
         """Write category to the raster map file"""
         self.cats.write_rules(filename, sep)
         self.cats.write_rules(filename, sep)
 
 
@@ -605,17 +640,22 @@ class RasterAbstractBase(object):
         """Set or update a category"""
         """Set or update a category"""
         self.cats.set_cat(index, (label, min_cat, max_cat))
         self.cats.set_cat(index, (label, min_cat, max_cat))
 
 
+
 if __name__ == "__main__":
 if __name__ == "__main__":
 
 
     import doctest
     import doctest
     from grass.pygrass.modules import Module
     from grass.pygrass.modules import Module
+
     Module("g.region", n=40, s=0, e=40, w=0, res=10)
     Module("g.region", n=40, s=0, e=40, w=0, res=10)
-    Module("r.mapcalc", expression="%s = row() + (10 * col())" % (test_raster_name),
-        overwrite=True)
+    Module(
+        "r.mapcalc",
+        expression="%s = row() + (10 * col())" % (test_raster_name),
+        overwrite=True,
+    )
 
 
     doctest.testmod()
     doctest.testmod()
 
 
     """Remove the generated vector map, if exist"""
     """Remove the generated vector map, if exist"""
-    mset = utils.get_mapset_raster(test_raster_name, mapset='')
+    mset = utils.get_mapset_raster(test_raster_name, mapset="")
     if mset:
     if mset:
-        Module("g.remove", flags='f', type='raster', name=test_raster_name)
+        Module("g.remove", flags="f", type="raster", name=test_raster_name)

+ 15 - 12
python/grass/pygrass/raster/buffer.py

@@ -4,11 +4,11 @@ import ctypes
 import numpy as np
 import numpy as np
 
 
 
 
-_CELL = ('int', 'int0', 'int8', 'int16', 'int32', 'int64')
+_CELL = ("int", "int0", "int8", "int16", "int32", "int64")
 CELL = tuple([getattr(np, attr) for attr in _CELL if hasattr(np, attr)])
 CELL = tuple([getattr(np, attr) for attr in _CELL if hasattr(np, attr)])
-_FCELL = 'float', 'float16', 'float32'
+_FCELL = "float", "float16", "float32"
 FCELL = tuple([getattr(np, attr) for attr in _FCELL if hasattr(np, attr)])
 FCELL = tuple([getattr(np, attr) for attr in _FCELL if hasattr(np, attr)])
-_DCELL = 'float64', 'float128'
+_DCELL = "float64", "float128"
 DCELL = tuple([getattr(np, attr) for attr in _DCELL if hasattr(np, attr)])
 DCELL = tuple([getattr(np, attr) for attr in _DCELL if hasattr(np, attr)])
 
 
 
 
@@ -16,31 +16,34 @@ class Buffer(np.ndarray):
     """shape, mtype='FCELL', buffer=None, offset=0,
     """shape, mtype='FCELL', buffer=None, offset=0,
     strides=None, order=None
     strides=None, order=None
     """
     """
+
     @property
     @property
     def mtype(self):
     def mtype(self):
         if self.dtype in CELL:
         if self.dtype in CELL:
-            return 'CELL'
+            return "CELL"
         elif self.dtype in FCELL:
         elif self.dtype in FCELL:
-            return 'FCELL'
+            return "FCELL"
         elif self.dtype in DCELL:
         elif self.dtype in DCELL:
             return DCELL
             return DCELL
         else:
         else:
             err = "Raster type: %r not supported by GRASS."
             err = "Raster type: %r not supported by GRASS."
             raise TypeError(err % self.dtype)
             raise TypeError(err % self.dtype)
 
 
-    def __new__(cls, shape, mtype='FCELL', buffer=None, offset=0,
-                strides=None, order=None):
-        obj = np.ndarray.__new__(cls, shape, RTYPE[mtype]['numpy'],
-                                 buffer, offset, strides, order)
-        obj.pointer_type = ctypes.POINTER(RTYPE[mtype]['ctypes'])
+    def __new__(
+        cls, shape, mtype="FCELL", buffer=None, offset=0, strides=None, order=None
+    ):
+        obj = np.ndarray.__new__(
+            cls, shape, RTYPE[mtype]["numpy"], buffer, offset, strides, order
+        )
+        obj.pointer_type = ctypes.POINTER(RTYPE[mtype]["ctypes"])
         obj.p = obj.ctypes.data_as(obj.pointer_type)
         obj.p = obj.ctypes.data_as(obj.pointer_type)
         return obj
         return obj
 
 
     def __array_finalize__(self, obj):
     def __array_finalize__(self, obj):
         if obj is None:
         if obj is None:
             return
             return
-        self.pointer_type = getattr(obj, 'pointer_type', None)
-        self.p = getattr(obj, 'p', None)
+        self.pointer_type = getattr(obj, "pointer_type", None)
+        self.p = getattr(obj, "p", None)
 
 
     def __array_wrap__(self, out_arr, context=None):
     def __array_wrap__(self, out_arr, context=None):
         """See:
         """See:

+ 91 - 83
python/grass/pygrass/raster/history.py

@@ -12,15 +12,23 @@ from grass.pygrass.utils import decode
 
 
 
 
 class History(object):
 class History(object):
-    """History class help to manage all the metadata of a raster map
-    """
-
-    def __init__(self, name, mapset='', mtype='',
-                 creator='', src1='', src2='', keyword='',
-                 date='', title=''):
+    """History class help to manage all the metadata of a raster map"""
+
+    def __init__(
+        self,
+        name,
+        mapset="",
+        mtype="",
+        creator="",
+        src1="",
+        src2="",
+        keyword="",
+        date="",
+        title="",
+    ):
         self.c_hist = ctypes.pointer(libraster.History())
         self.c_hist = ctypes.pointer(libraster.History())
         #                'Tue Nov  7 01:11:23 2006'
         #                'Tue Nov  7 01:11:23 2006'
-        self.date_fmt = '%a %b  %d %H:%M:%S %Y'
+        self.date_fmt = "%a %b  %d %H:%M:%S %Y"
         self.name = name
         self.name = name
         self.mapset = mapset
         self.mapset = mapset
         self.mtype = mtype
         self.mtype = mtype
@@ -30,12 +38,22 @@ class History(object):
         self.keyword = keyword
         self.keyword = keyword
         self.date = date
         self.date = date
         self.title = title
         self.title = title
-        self.attrs = ['name', 'mapset', 'mtype', 'creator', 'src1', 'src2',
-                      'keyword', 'date', 'title']
+        self.attrs = [
+            "name",
+            "mapset",
+            "mtype",
+            "creator",
+            "src1",
+            "src2",
+            "keyword",
+            "date",
+            "title",
+        ]
 
 
     def __repr__(self):
     def __repr__(self):
-        return "History(%s)" % ', '.join(["%s=%r" % (self.attr, getattr(self, attr))
-                                          for attr in self.attrs])
+        return "History(%s)" % ", ".join(
+            ["%s=%r" % (self.attr, getattr(self, attr)) for attr in self.attrs]
+        )
 
 
     def __del__(self):
     def __del__(self):
         """Rast_free_history"""
         """Rast_free_history"""
@@ -56,68 +74,67 @@ class History(object):
     # ----------------------------------------------------------------------
     # ----------------------------------------------------------------------
     # libraster.HIST_CREATOR
     # libraster.HIST_CREATOR
     def _get_creator(self):
     def _get_creator(self):
-        return decode(libraster.Rast_get_history(self.c_hist,
-                                                 libraster.HIST_CREATOR))
+        return decode(libraster.Rast_get_history(self.c_hist, libraster.HIST_CREATOR))
 
 
     def _set_creator(self, creator):
     def _set_creator(self, creator):
         creator = encode(creator)
         creator = encode(creator)
-        return libraster.Rast_set_history(self.c_hist,
-                                          libraster.HIST_CREATOR,
-                                          ctypes.c_char_p(creator))
+        return libraster.Rast_set_history(
+            self.c_hist, libraster.HIST_CREATOR, ctypes.c_char_p(creator)
+        )
 
 
-    creator = property(fget=_get_creator, fset=_set_creator,
-                       doc="Set or obtain the creator of map")
+    creator = property(
+        fget=_get_creator, fset=_set_creator, doc="Set or obtain the creator of map"
+    )
 
 
     # ----------------------------------------------------------------------
     # ----------------------------------------------------------------------
     # libraster.HIST_DATSRC_1
     # libraster.HIST_DATSRC_1
     def _get_src1(self):
     def _get_src1(self):
-        return decode(libraster.Rast_get_history(self.c_hist,
-                                                 libraster.HIST_DATSRC_1))
+        return decode(libraster.Rast_get_history(self.c_hist, libraster.HIST_DATSRC_1))
 
 
     def _set_src1(self, src1):
     def _set_src1(self, src1):
         src1 = encode(src1)
         src1 = encode(src1)
-        return libraster.Rast_set_history(self.c_hist,
-                                          libraster.HIST_DATSRC_1,
-                                          ctypes.c_char_p(src1))
+        return libraster.Rast_set_history(
+            self.c_hist, libraster.HIST_DATSRC_1, ctypes.c_char_p(src1)
+        )
 
 
-    src1 = property(fget=_get_src1, fset=_set_src1,
-                    doc="Set or obtain the first source of map")
+    src1 = property(
+        fget=_get_src1, fset=_set_src1, doc="Set or obtain the first source of map"
+    )
 
 
     # ----------------------------------------------------------------------
     # ----------------------------------------------------------------------
     # libraster.HIST_DATSRC_2
     # libraster.HIST_DATSRC_2
     def _get_src2(self):
     def _get_src2(self):
-        return decode(libraster.Rast_get_history(self.c_hist,
-                                                 libraster.HIST_DATSRC_2))
+        return decode(libraster.Rast_get_history(self.c_hist, libraster.HIST_DATSRC_2))
 
 
     def _set_src2(self, src2):
     def _set_src2(self, src2):
         src2 = encode(src2)
         src2 = encode(src2)
-        return libraster.Rast_set_history(self.c_hist,
-                                          libraster.HIST_DATSRC_2,
-                                          ctypes.c_char_p(src2))
+        return libraster.Rast_set_history(
+            self.c_hist, libraster.HIST_DATSRC_2, ctypes.c_char_p(src2)
+        )
 
 
-    src2 = property(fget=_get_src2, fset=_set_src2,
-                    doc="Set or obtain the second source of map")
+    src2 = property(
+        fget=_get_src2, fset=_set_src2, doc="Set or obtain the second source of map"
+    )
 
 
     # ----------------------------------------------------------------------
     # ----------------------------------------------------------------------
     # libraster.HIST_KEYWORD
     # libraster.HIST_KEYWORD
     def _get_keyword(self):
     def _get_keyword(self):
-        return decode(libraster.Rast_get_history(self.c_hist,
-                                                 libraster.HIST_KEYWRD))
+        return decode(libraster.Rast_get_history(self.c_hist, libraster.HIST_KEYWRD))
 
 
     def _set_keyword(self, keyword):
     def _set_keyword(self, keyword):
         keyword = encode(keyword)
         keyword = encode(keyword)
-        return libraster.Rast_set_history(self.c_hist,
-                                          libraster.HIST_KEYWRD,
-                                          ctypes.c_char_p(keyword))
+        return libraster.Rast_set_history(
+            self.c_hist, libraster.HIST_KEYWRD, ctypes.c_char_p(keyword)
+        )
 
 
-    keyword = property(fget=_get_keyword, fset=_set_keyword,
-                       doc="Set or obtain the keywords of map")
+    keyword = property(
+        fget=_get_keyword, fset=_set_keyword, doc="Set or obtain the keywords of map"
+    )
 
 
     # ----------------------------------------------------------------------
     # ----------------------------------------------------------------------
     # libraster.HIST_MAPID
     # libraster.HIST_MAPID
     def _get_date(self):
     def _get_date(self):
-        date_str = decode(libraster.Rast_get_history(self.c_hist,
-                                                     libraster.HIST_MAPID))
+        date_str = decode(libraster.Rast_get_history(self.c_hist, libraster.HIST_MAPID))
         if date_str:
         if date_str:
             try:
             try:
                 return datetime.datetime.strptime(date_str, self.date_fmt)
                 return datetime.datetime.strptime(date_str, self.date_fmt)
@@ -128,42 +145,41 @@ class History(object):
         if datetimeobj:
         if datetimeobj:
             date_str = datetimeobj.strftime(self.date_fmt)
             date_str = datetimeobj.strftime(self.date_fmt)
             date_str = encode(date_str)
             date_str = encode(date_str)
-            return libraster.Rast_set_history(self.c_hist,
-                                              libraster.HIST_MAPID,
-                                              ctypes.c_char_p(date_str))
+            return libraster.Rast_set_history(
+                self.c_hist, libraster.HIST_MAPID, ctypes.c_char_p(date_str)
+            )
 
 
-    date = property(fget=_get_date, fset=_set_date,
-                    doc="Set or obtain the date of map")
+    date = property(fget=_get_date, fset=_set_date, doc="Set or obtain the date of map")
 
 
     # ----------------------------------------------------------------------
     # ----------------------------------------------------------------------
     # libraster.HIST_MAPSET
     # libraster.HIST_MAPSET
     def _get_mapset(self):
     def _get_mapset(self):
-        return decode(libraster.Rast_get_history(self.c_hist,
-                                                 libraster.HIST_MAPSET))
+        return decode(libraster.Rast_get_history(self.c_hist, libraster.HIST_MAPSET))
 
 
     def _set_mapset(self, mapset):
     def _set_mapset(self, mapset):
         mapset = encode(mapset)
         mapset = encode(mapset)
-        return libraster.Rast_set_history(self.c_hist,
-                                          libraster.HIST_MAPSET,
-                                          ctypes.c_char_p(mapset))
+        return libraster.Rast_set_history(
+            self.c_hist, libraster.HIST_MAPSET, ctypes.c_char_p(mapset)
+        )
 
 
-    mapset = property(fget=_get_mapset, fset=_set_mapset,
-                      doc="Set or obtain the mapset of map")
+    mapset = property(
+        fget=_get_mapset, fset=_set_mapset, doc="Set or obtain the mapset of map"
+    )
 
 
     # ----------------------------------------------------------------------
     # ----------------------------------------------------------------------
     # libraster.HIST_MAPTYPE
     # libraster.HIST_MAPTYPE
     def _get_maptype(self):
     def _get_maptype(self):
-        return decode(libraster.Rast_get_history(self.c_hist,
-                                                 libraster.HIST_MAPTYPE))
+        return decode(libraster.Rast_get_history(self.c_hist, libraster.HIST_MAPTYPE))
 
 
     def _set_maptype(self, maptype):
     def _set_maptype(self, maptype):
         maptype = encode(maptype)
         maptype = encode(maptype)
-        return libraster.Rast_set_history(self.c_hist,
-                                          libraster.HIST_MAPTYPE,
-                                          ctypes.c_char_p(maptype))
+        return libraster.Rast_set_history(
+            self.c_hist, libraster.HIST_MAPTYPE, ctypes.c_char_p(maptype)
+        )
 
 
-    maptype = property(fget=_get_maptype, fset=_set_maptype,
-                       doc="Set or obtain the type of map")
+    maptype = property(
+        fget=_get_maptype, fset=_set_maptype, doc="Set or obtain the type of map"
+    )
 
 
     # ----------------------------------------------------------------------
     # ----------------------------------------------------------------------
     # libraster.HIST_NUM_FIELDS
     # libraster.HIST_NUM_FIELDS
@@ -183,28 +199,25 @@ class History(object):
     # ----------------------------------------------------------------------
     # ----------------------------------------------------------------------
     # libraster.HIST_TITLE
     # libraster.HIST_TITLE
     def _get_title(self):
     def _get_title(self):
-        return decode(libraster.Rast_get_history(self.c_hist,
-                                                 libraster.HIST_TITLE))
+        return decode(libraster.Rast_get_history(self.c_hist, libraster.HIST_TITLE))
 
 
     def _set_title(self, title):
     def _set_title(self, title):
         title = encode(title)
         title = encode(title)
-        return libraster.Rast_set_history(self.c_hist,
-                                          libraster.HIST_TITLE,
-                                          ctypes.c_char_p(title))
+        return libraster.Rast_set_history(
+            self.c_hist, libraster.HIST_TITLE, ctypes.c_char_p(title)
+        )
 
 
-    title = property(fget=_get_title, fset=_set_title,
-                     doc="Set or obtain the title of map")
+    title = property(
+        fget=_get_title, fset=_set_title, doc="Set or obtain the title of map"
+    )
 
 
     def append(self, obj):
     def append(self, obj):
         """Rast_append_history"""
         """Rast_append_history"""
-        libraster.Rast_append_history(self.c_hist,
-                                      ctypes.c_char_p(str(obj)))
+        libraster.Rast_append_history(self.c_hist, ctypes.c_char_p(str(obj)))
 
 
     def append_fmt(self, fmt, *args):
     def append_fmt(self, fmt, *args):
         """Rast_append_format_history"""
         """Rast_append_format_history"""
-        libraster.Rast_append_format_history(self.c_hist,
-                                             ctypes.c_char_p(fmt),
-                                             *args)
+        libraster.Rast_append_format_history(self.c_hist, ctypes.c_char_p(fmt), *args)
 
 
     def clear(self):
     def clear(self):
         """Clear the history"""
         """Clear the history"""
@@ -216,10 +229,9 @@ class History(object):
 
 
     def format(self, field, fmt, *args):
     def format(self, field, fmt, *args):
         """Rast_format_history"""
         """Rast_format_history"""
-        libraster.Rast_format_history(self.c_hist,
-                                      ctypes.c_int(field),
-                                      ctypes.c_char_p(fmt),
-                                      *args)
+        libraster.Rast_format_history(
+            self.c_hist, ctypes.c_int(field), ctypes.c_char_p(fmt), *args
+        )
 
 
     def length(self):
     def length(self):
         """Rast_history_length"""
         """Rast_history_length"""
@@ -227,8 +239,7 @@ class History(object):
 
 
     def line(self, line):
     def line(self, line):
         """Rast_history_line"""
         """Rast_history_line"""
-        return libraster.Rast_history_line(self.c_hist,
-                                           ctypes.c_int(line))
+        return libraster.Rast_history_line(self.c_hist, ctypes.c_int(line))
 
 
     def read(self):
     def read(self):
         """Read the history of map, users need to use this function to
         """Read the history of map, users need to use this function to
@@ -245,11 +256,8 @@ class History(object):
 
 
     def write(self):
     def write(self):
         """Rast_write_history"""
         """Rast_write_history"""
-        libraster.Rast_write_history(self.name,
-                                     self.c_hist)
+        libraster.Rast_write_history(self.name, self.c_hist)
 
 
     def short(self):
     def short(self):
         """Rast_short_history"""
         """Rast_short_history"""
-        libraster.Rast_short_history(self.name,
-                                     'raster',
-                                     self.c_hist)
+        libraster.Rast_short_history(self.name, "raster", self.c_hist)

+ 25 - 15
python/grass/pygrass/raster/raster_type.py

@@ -9,20 +9,30 @@ import ctypes
 import numpy as np
 import numpy as np
 
 
 ## Private dictionary to convert RASTER_TYPE into type string.
 ## Private dictionary to convert RASTER_TYPE into type string.
-RTYPE_STR = {libraster.CELL_TYPE: 'CELL',
-             libraster.FCELL_TYPE: 'FCELL',
-             libraster.DCELL_TYPE: 'DCELL'}
+RTYPE_STR = {
+    libraster.CELL_TYPE: "CELL",
+    libraster.FCELL_TYPE: "FCELL",
+    libraster.DCELL_TYPE: "DCELL",
+}
 
 
 
 
-TYPE = {'CELL': {'grass type': libraster.CELL_TYPE,
-                  'grass def': libraster.CELL,
-                  'numpy': np.int32,
-                  'ctypes': ctypes.c_int},
-        'FCELL': {'grass type': libraster.FCELL_TYPE,
-                  'grass def': libraster.FCELL,
-                  'numpy': np.float32,
-                  'ctypes': ctypes.c_float},
-        'DCELL': {'grass type': libraster.DCELL_TYPE,
-                  'grass def': libraster.DCELL,
-                  'numpy': np.float64,
-                  'ctypes': ctypes.c_double}}
+TYPE = {
+    "CELL": {
+        "grass type": libraster.CELL_TYPE,
+        "grass def": libraster.CELL,
+        "numpy": np.int32,
+        "ctypes": ctypes.c_int,
+    },
+    "FCELL": {
+        "grass type": libraster.FCELL_TYPE,
+        "grass def": libraster.FCELL,
+        "numpy": np.float32,
+        "ctypes": ctypes.c_float,
+    },
+    "DCELL": {
+        "grass type": libraster.DCELL_TYPE,
+        "grass def": libraster.DCELL,
+        "numpy": np.float64,
+        "ctypes": ctypes.c_double,
+    },
+}

+ 23 - 20
python/grass/pygrass/raster/rowio.py

@@ -13,37 +13,34 @@ from grass.pygrass.errors import GrassError
 from grass.pygrass.raster.raster_type import TYPE as RTYPE
 from grass.pygrass.raster.raster_type import TYPE as RTYPE
 
 
 
 
-CMPFUNC = ctypes.CFUNCTYPE(ctypes.c_int,
-                           ctypes.c_int, ctypes.c_void_p,
-                           ctypes.c_int, ctypes.c_int)
+CMPFUNC = ctypes.CFUNCTYPE(
+    ctypes.c_int, ctypes.c_int, ctypes.c_void_p, ctypes.c_int, ctypes.c_int
+)
 
 
 
 
 def getmaprow_CELL(fd, buf, row, l):
 def getmaprow_CELL(fd, buf, row, l):
-    librast.Rast_get_c_row(fd, ctypes.cast(buf, ctypes.POINTER(librast.CELL)),
-                           row)
+    librast.Rast_get_c_row(fd, ctypes.cast(buf, ctypes.POINTER(librast.CELL)), row)
     return 1
     return 1
 
 
 
 
 def getmaprow_FCELL(fd, buf, row, l):
 def getmaprow_FCELL(fd, buf, row, l):
-    librast.Rast_get_f_row(fd, ctypes.cast(buf, ctypes.POINTER(librast.FCELL)),
-                           row)
+    librast.Rast_get_f_row(fd, ctypes.cast(buf, ctypes.POINTER(librast.FCELL)), row)
     return 1
     return 1
 
 
 
 
 def getmaprow_DCELL(fd, buf, row, l):
 def getmaprow_DCELL(fd, buf, row, l):
-    librast.Rast_get_d_row(fd, ctypes.cast(buf, ctypes.POINTER(librast.DCELL)),
-                           row)
+    librast.Rast_get_d_row(fd, ctypes.cast(buf, ctypes.POINTER(librast.DCELL)), row)
     return 1
     return 1
 
 
+
 get_row = {
 get_row = {
-    'CELL': CMPFUNC(getmaprow_CELL),
-    'FCELL': CMPFUNC(getmaprow_FCELL),
-    'DCELL': CMPFUNC(getmaprow_DCELL),
+    "CELL": CMPFUNC(getmaprow_CELL),
+    "FCELL": CMPFUNC(getmaprow_FCELL),
+    "DCELL": CMPFUNC(getmaprow_DCELL),
 }
 }
 
 
 
 
 class RowIO(object):
 class RowIO(object):
-
     def __init__(self):
     def __init__(self):
         self.c_rowio = librowio.ROWIO()
         self.c_rowio = librowio.ROWIO()
         self.fd = None
         self.fd = None
@@ -57,13 +54,19 @@ class RowIO(object):
         self.rows = rows
         self.rows = rows
         self.cols = cols
         self.cols = cols
         self.mtype = mtype
         self.mtype = mtype
-        self.row_size = ctypes.sizeof(RTYPE[mtype]['grass def'] * cols)
-        if (librowio.Rowio_setup(ctypes.byref(self.c_rowio), self.fd,
-                                 self.rows,
-                                 self.row_size,
-                                 get_row[self.mtype],
-                                 get_row[self.mtype]) == -1):
-            raise GrassError('Fatal error, Rowio not setup correctly.')
+        self.row_size = ctypes.sizeof(RTYPE[mtype]["grass def"] * cols)
+        if (
+            librowio.Rowio_setup(
+                ctypes.byref(self.c_rowio),
+                self.fd,
+                self.rows,
+                self.row_size,
+                get_row[self.mtype],
+                get_row[self.mtype],
+            )
+            == -1
+        ):
+            raise GrassError("Fatal error, Rowio not setup correctly.")
 
 
     def release(self):
     def release(self):
         librowio.Rowio_release(ctypes.byref(self.c_rowio))
         librowio.Rowio_release(ctypes.byref(self.c_rowio))

+ 48 - 40
python/grass/pygrass/raster/segment.py

@@ -27,58 +27,72 @@ class Segment(object):
     def nseg(self):
     def nseg(self):
         rows = self.rows()
         rows = self.rows()
         cols = self.cols()
         cols = self.cols()
-        return int(((rows + self.srows - 1) / self.srows) *
-                   ((cols + self.scols - 1) / self.scols))
+        return int(
+            ((rows + self.srows - 1) / self.srows)
+            * ((cols + self.scols - 1) / self.scols)
+        )
 
 
     def segments_in_mem(self):
     def segments_in_mem(self):
         if self.maxmem > 0 and self.maxmem < 100:
         if self.maxmem > 0 and self.maxmem < 100:
             seg_in_mem = (self.maxmem * self.nseg()) / 100
             seg_in_mem = (self.maxmem * self.nseg()) / 100
         else:
         else:
-            seg_in_mem = 4 * (self.rows() / self.srows +
-                              self.cols() / self.scols + 2)
+            seg_in_mem = 4 * (self.rows() / self.srows + self.cols() / self.scols + 2)
         if seg_in_mem == 0:
         if seg_in_mem == 0:
             seg_in_mem = 1
             seg_in_mem = 1
         return seg_in_mem
         return seg_in_mem
 
 
     def open(self, mapobj):
     def open(self, mapobj):
-        """Open a segment it is necessary to pass a RasterSegment object.
-
-        """
-        self.val = RTYPE[mapobj.mtype]['grass def']()
-        size = ctypes.sizeof(RTYPE[mapobj.mtype]['ctypes'])
+        """Open a segment it is necessary to pass a RasterSegment object."""
+        self.val = RTYPE[mapobj.mtype]["grass def"]()
+        size = ctypes.sizeof(RTYPE[mapobj.mtype]["ctypes"])
         file_name = libgis.G_tempfile()
         file_name = libgis.G_tempfile()
-        libseg.Segment_open(self.c_seg, file_name,
-                            self.rows(), self.cols(),
-                            self.srows, self.scols,
-                            size,
-                            self.nseg())
+        libseg.Segment_open(
+            self.c_seg,
+            file_name,
+            self.rows(),
+            self.cols(),
+            self.srows,
+            self.scols,
+            size,
+            self.nseg(),
+        )
         self.flush()
         self.flush()
 
 
-    def format(self, mapobj, file_name='', fill=True):
+    def format(self, mapobj, file_name="", fill=True):
         """The segmentation routines require a disk file to be used for paging
         """The segmentation routines require a disk file to be used for paging
         segments in and out of memory. This routine formats the file open for
         segments in and out of memory. This routine formats the file open for
         write on file descriptor fd for use as a segment file.
         write on file descriptor fd for use as a segment file.
         """
         """
-        if file_name == '':
+        if file_name == "":
             file_name = libgis.G_tempfile()
             file_name = libgis.G_tempfile()
-        mapobj.temp_file = open(file_name, 'w')
-        size = ctypes.sizeof(RTYPE[mapobj.mtype]['ctypes'])
+        mapobj.temp_file = open(file_name, "w")
+        size = ctypes.sizeof(RTYPE[mapobj.mtype]["ctypes"])
         if fill:
         if fill:
-            libseg.Segment_format(mapobj.temp_file.fileno(), self.rows(),
-                                  self.cols(), self.srows, self.scols, size)
+            libseg.Segment_format(
+                mapobj.temp_file.fileno(),
+                self.rows(),
+                self.cols(),
+                self.srows,
+                self.scols,
+                size,
+            )
         else:
         else:
-            libseg.Segment_format_nofill(mapobj.temp_file.fileno(),
-                                         self.rows(), self.cols(),
-                                         self.srows, self.scols, size)
+            libseg.Segment_format_nofill(
+                mapobj.temp_file.fileno(),
+                self.rows(),
+                self.cols(),
+                self.srows,
+                self.scols,
+                size,
+            )
         # TODO: why should I close and then re-open it?
         # TODO: why should I close and then re-open it?
         mapobj.temp_file.close()
         mapobj.temp_file.close()
 
 
-    def init(self, mapobj, file_name=''):
-        if file_name == '':
+    def init(self, mapobj, file_name=""):
+        if file_name == "":
             file_name = mapobj.temp_file.name
             file_name = mapobj.temp_file.name
-        mapobj.temp_file = open(file_name, 'w')
-        libseg.Segment_init(self.c_seg, mapobj.temp_file.fileno(),
-                            self.segments_in_mem)
+        mapobj.temp_file = open(file_name, "w")
+        libseg.Segment_init(self.c_seg, mapobj.temp_file.fileno(), self.segments_in_mem)
 
 
     def get_row(self, row_index, buf):
     def get_row(self, row_index, buf):
         """Return the row using, the `segment` method"""
         """Return the row using, the `segment` method"""
@@ -90,23 +104,17 @@ class Segment(object):
         libseg.Segment_put_row(self.c_seg, buf.p, row_index)
         libseg.Segment_put_row(self.c_seg, buf.p, row_index)
 
 
     def get(self, row_index, col_index):
     def get(self, row_index, col_index):
-        """Return the value of the map
-        """
-        libseg.Segment_get(self.c_seg,
-                           ctypes.byref(self.val), row_index, col_index)
+        """Return the value of the map"""
+        libseg.Segment_get(self.c_seg, ctypes.byref(self.val), row_index, col_index)
         return self.val.value
         return self.val.value
 
 
     def put(self, row_index, col_index):
     def put(self, row_index, col_index):
-        """Write the value to the map
-        """
-        libseg.Segment_put(self.c_seg,
-                           ctypes.byref(self.val), row_index, col_index)
+        """Write the value to the map"""
+        libseg.Segment_put(self.c_seg, ctypes.byref(self.val), row_index, col_index)
 
 
     def get_seg_number(self, row_index, col_index):
     def get_seg_number(self, row_index, col_index):
-        """Return the segment number
-        """
-        return row_index / self.srows * self.cols / self.scols + \
-            col_index / self.scols
+        """Return the segment number"""
+        return row_index / self.srows * self.cols / self.scols + col_index / self.scols
 
 
     def flush(self):
     def flush(self):
         """Flush pending updates to disk.
         """Flush pending updates to disk.

+ 16 - 13
python/grass/pygrass/raster/testsuite/test_category.py

@@ -22,13 +22,18 @@ class RasterCategoryTestCase(TestCase):
         """Create test raster map and region"""
         """Create test raster map and region"""
         cls.use_temp_region()
         cls.use_temp_region()
         cls.runModule("g.region", n=40, s=0, e=40, w=0, res=10)
         cls.runModule("g.region", n=40, s=0, e=40, w=0, res=10)
-        cls.runModule("r.mapcalc",
+        cls.runModule(
+            "r.mapcalc",
             expression="%s = row() + (10.0 * col())" % (cls.name),
             expression="%s = row() + (10.0 * col())" % (cls.name),
-            overwrite=True)
-        cls.runModule("r.support", map=cls.name,
+            overwrite=True,
+        )
+        cls.runModule(
+            "r.support",
+            map=cls.name,
             title="A test map",
             title="A test map",
             history="Generated by r.mapcalc",
             history="Generated by r.mapcalc",
-            description="This is a test map")
+            description="This is a test map",
+        )
         cats = """11:A
         cats = """11:A
                 12:B
                 12:B
                 13:C
                 13:C
@@ -46,14 +51,12 @@ class RasterCategoryTestCase(TestCase):
                 43:O
                 43:O
                 44:P"""
                 44:P"""
 
 
-        cls.runModule("r.category", rules="-", map=cls.name,
-            stdin_=cats, separator=":")
+        cls.runModule("r.category", rules="-", map=cls.name, stdin_=cats, separator=":")
 
 
     @classmethod
     @classmethod
     def tearDownClass(cls):
     def tearDownClass(cls):
         """Remove the generated vector map, if exist"""
         """Remove the generated vector map, if exist"""
-        cls.runModule("g.remove", flags='f', type='raster',
-                      name=cls.name)
+        cls.runModule("g.remove", flags="f", type="raster", name=cls.name)
         cls.del_temp_region()
         cls.del_temp_region()
 
 
     def testCategory(self):
     def testCategory(self):
@@ -66,9 +69,9 @@ class RasterCategoryTestCase(TestCase):
         r.close()
         r.close()
 
 
     def testFirstCat(self):
     def testFirstCat(self):
-        cat0 = ('A', 11, None)
-        cat7 = ('H', 24, None)
-        cat15 = ('P', 44, None)
+        cat0 = ("A", 11, None)
+        cat7 = ("H", 24, None)
+        cat15 = ("P", 44, None)
         cats = Category(self.name)
         cats = Category(self.name)
         cats.read()
         cats.read()
         self.assertEqual(cats[0], cat0)
         self.assertEqual(cats[0], cat0)
@@ -80,8 +83,8 @@ class RasterCategoryTestCase(TestCase):
         cats = Category(self.name)
         cats = Category(self.name)
         cats.read()
         cats.read()
         cats.write_rules(tmpfile)
         cats.write_rules(tmpfile)
-        self.assertFilesEqualMd5(tmpfile, 'data/geology_cats')
+        self.assertFilesEqualMd5(tmpfile, "data/geology_cats")
 
 
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     test()
     test()

+ 12 - 7
python/grass/pygrass/raster/testsuite/test_history.py

@@ -22,19 +22,23 @@ class RasterHistoryTestCate(TestCase):
         """Create test raster map and region"""
         """Create test raster map and region"""
         cls.use_temp_region()
         cls.use_temp_region()
         cls.runModule("g.region", n=40, s=0, e=40, w=0, res=10)
         cls.runModule("g.region", n=40, s=0, e=40, w=0, res=10)
-        cls.runModule("r.mapcalc",
+        cls.runModule(
+            "r.mapcalc",
             expression="%s = row() + (10 * col())" % (cls.name),
             expression="%s = row() + (10 * col())" % (cls.name),
-            overwrite=True)
-        cls.runModule("r.support", map=cls.name,
+            overwrite=True,
+        )
+        cls.runModule(
+            "r.support",
+            map=cls.name,
             title="A test map",
             title="A test map",
             history="Generated by r.mapcalc",
             history="Generated by r.mapcalc",
-            description="This is a test map")
+            description="This is a test map",
+        )
 
 
     @classmethod
     @classmethod
     def tearDownClass(cls):
     def tearDownClass(cls):
         """Remove the generated vector map, if exist"""
         """Remove the generated vector map, if exist"""
-        cls.runModule("g.remove", flags='f', type='raster',
-                      name=cls.name)
+        cls.runModule("g.remove", flags="f", type="raster", name=cls.name)
         cls.del_temp_region()
         cls.del_temp_region()
 
 
     def testHistory(self):
     def testHistory(self):
@@ -78,5 +82,6 @@ class RasterHistoryTestCate(TestCase):
         hist1.command()
         hist1.command()
         self.assertEqual(decode(hist1.line(0)), "test_history.py")
         self.assertEqual(decode(hist1.line(0)), "test_history.py")
 
 
-if __name__ == '__main__':
+
+if __name__ == "__main__":
     test()
     test()

+ 10 - 8
python/grass/pygrass/raster/testsuite/test_numpy.py

@@ -13,7 +13,7 @@ from grass.pygrass.raster import raster2numpy, numpy2raster, RasterRow
 def check_raster(name):
 def check_raster(name):
     r = RasterRow(name)
     r = RasterRow(name)
     try:
     try:
-        r.open(mode='r')
+        r.open(mode="r")
         r.close()
         r.close()
         return True
         return True
     except:
     except:
@@ -29,20 +29,21 @@ class NumpyTestCase(TestCase):
         """Create test raster map and region"""
         """Create test raster map and region"""
         cls.use_temp_region()
         cls.use_temp_region()
         cls.runModule("g.region", n=40, s=0, e=60, w=0, res=1)
         cls.runModule("g.region", n=40, s=0, e=60, w=0, res=1)
-        cls.runModule("r.mapcalc",
+        cls.runModule(
+            "r.mapcalc",
             expression="%s = float(row() + (10.0 * col()))" % (cls.name),
             expression="%s = float(row() + (10.0 * col()))" % (cls.name),
-            overwrite=True)
+            overwrite=True,
+        )
         cls.numpy_obj = raster2numpy(cls.name)
         cls.numpy_obj = raster2numpy(cls.name)
 
 
     @classmethod
     @classmethod
     def tearDownClass(cls):
     def tearDownClass(cls):
         """Remove the generated vector map, if exist"""
         """Remove the generated vector map, if exist"""
-        cls.runModule("g.remove", flags='f', type='raster',
-                      name=cls.name)
+        cls.runModule("g.remove", flags="f", type="raster", name=cls.name)
         cls.del_temp_region()
         cls.del_temp_region()
 
 
     def test_type(self):
     def test_type(self):
-        self.assertTrue(str(self.numpy_obj.dtype), 'float32')
+        self.assertTrue(str(self.numpy_obj.dtype), "float32")
 
 
     def test_len(self):
     def test_len(self):
         self.assertTrue(len(self.numpy_obj), 40)
         self.assertTrue(len(self.numpy_obj), 40)
@@ -50,8 +51,9 @@ class NumpyTestCase(TestCase):
 
 
     def test_write(self):
     def test_write(self):
         ran = random([40, 60])
         ran = random([40, 60])
-        numpy2raster(ran, 'FCELL', self.name, True)
+        numpy2raster(ran, "FCELL", self.name, True)
         self.assertTrue(check_raster(self.name))
         self.assertTrue(check_raster(self.name))
 
 
-if __name__ == '__main__':
+
+if __name__ == "__main__":
     test()
     test()

+ 20 - 18
python/grass/pygrass/raster/testsuite/test_pygrass_raster.py

@@ -15,66 +15,68 @@ class RasterRowTestCase(TestCase):
         """Create test raster map and region"""
         """Create test raster map and region"""
         cls.use_temp_region()
         cls.use_temp_region()
         cls.runModule("g.region", n=40, s=0, e=40, w=0, res=10)
         cls.runModule("g.region", n=40, s=0, e=40, w=0, res=10)
-        cls.runModule("r.mapcalc", expression="%s = row() + (10.0 * col())" % (cls.name),
-            overwrite=True)
+        cls.runModule(
+            "r.mapcalc",
+            expression="%s = row() + (10.0 * col())" % (cls.name),
+            overwrite=True,
+        )
 
 
     @classmethod
     @classmethod
     def tearDownClass(cls):
     def tearDownClass(cls):
         """Remove the generated vector map, if exist"""
         """Remove the generated vector map, if exist"""
-        cls.runModule("g.remove", flags='f', type='raster',
-                      name=cls.name)
+        cls.runModule("g.remove", flags="f", type="raster", name=cls.name)
         cls.del_temp_region()
         cls.del_temp_region()
 
 
     def test_type(self):
     def test_type(self):
         r = RasterRow(self.name)
         r = RasterRow(self.name)
-        r.open(mode='r')
-        self.assertTrue(r.mtype, 'DCELL')
+        r.open(mode="r")
+        self.assertTrue(r.mtype, "DCELL")
         r.close()
         r.close()
 
 
     def test_isopen(self):
     def test_isopen(self):
         r = RasterRow(self.name)
         r = RasterRow(self.name)
         self.assertFalse(r.is_open())
         self.assertFalse(r.is_open())
-        r.open(mode='r')
+        r.open(mode="r")
         self.assertTrue(r.is_open())
         self.assertTrue(r.is_open())
         r.close()
         r.close()
         self.assertFalse(r.is_open())
         self.assertFalse(r.is_open())
 
 
     def test_name(self):
     def test_name(self):
         r = RasterRow(self.name)
         r = RasterRow(self.name)
-        r.open(mode='r')
+        r.open(mode="r")
         self.assertEqual(r.name, self.name)
         self.assertEqual(r.name, self.name)
         fullname = "{name}@{mapset}".format(name=r.name, mapset=r.mapset)
         fullname = "{name}@{mapset}".format(name=r.name, mapset=r.mapset)
         self.assertEqual(r.fullname(), fullname)
         self.assertEqual(r.fullname(), fullname)
         r.close()
         r.close()
 
 
     def test_exist(self):
     def test_exist(self):
-        notexist = RasterRow(self.name + 'notexist')
+        notexist = RasterRow(self.name + "notexist")
         self.assertFalse(notexist.exist())
         self.assertFalse(notexist.exist())
         exist = RasterRow(self.name)
         exist = RasterRow(self.name)
         self.assertTrue(exist.exist())
         self.assertTrue(exist.exist())
 
 
     def test_open_r(self):
     def test_open_r(self):
-        notexist = RasterRow(self.name + 'notexist')
+        notexist = RasterRow(self.name + "notexist")
         with self.assertRaises(OpenError):
         with self.assertRaises(OpenError):
             # raster does not exist
             # raster does not exist
-            notexist.open(mode='r')
+            notexist.open(mode="r")
         r = RasterRow(self.name)
         r = RasterRow(self.name)
-        r.open(mode='r', mtype='FCELL')
+        r.open(mode="r", mtype="FCELL")
         # ignore the mtype if is open in read mode
         # ignore the mtype if is open in read mode
-        self.assertEqual(r.mtype, 'DCELL')
+        self.assertEqual(r.mtype, "DCELL")
         r.close()
         r.close()
 
 
     def test_open_w(self):
     def test_open_w(self):
         r = RasterRow(self.name)
         r = RasterRow(self.name)
         with self.assertRaises(OpenError):
         with self.assertRaises(OpenError):
             # raster type is not defined!
             # raster type is not defined!
-            r.open(mode='w')
+            r.open(mode="w")
         with self.assertRaises(OpenError):
         with self.assertRaises(OpenError):
             # raster already exist
             # raster already exist
-            r.open(mode='w', mtype='DCELL')
+            r.open(mode="w", mtype="DCELL")
         # open in write mode and overwrite
         # open in write mode and overwrite
-        r.open(mode='w', mtype='DCELL', overwrite=True)
-        self.assertTrue(r.mtype, 'DCELL')
+        r.open(mode="w", mtype="DCELL", overwrite=True)
+        self.assertTrue(r.mtype, "DCELL")
         r.close()
         r.close()
 
 
     def test_row_range(self):
     def test_row_range(self):
@@ -89,5 +91,5 @@ class RasterRowTestCase(TestCase):
         r.close()
         r.close()
 
 
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     test()
     test()

+ 26 - 15
python/grass/pygrass/raster/testsuite/test_pygrass_raster_doctests.py

@@ -17,12 +17,14 @@ import grass.pygrass.raster as pgrass
 # and contains doctest's methods
 # and contains doctest's methods
 # the alternative is to copy 500 from doctest and change what is needed
 # the alternative is to copy 500 from doctest and change what is needed
 # (this might be necessary anyway because of the reports and stdout and stderr)
 # (this might be necessary anyway because of the reports and stdout and stderr)
-doctest.DocFileCase = type('DocFileCase',
-                           (grass.gunittest.case.TestCase,),
-                           dict(doctest.DocFileCase.__dict__))
-doctest.SkipDocTestCase = type('SkipDocTestCase',
-                               (grass.gunittest.case.TestCase,),
-                               dict(doctest.SkipDocTestCase.__dict__))
+doctest.DocFileCase = type(
+    "DocFileCase", (grass.gunittest.case.TestCase,), dict(doctest.DocFileCase.__dict__)
+)
+doctest.SkipDocTestCase = type(
+    "SkipDocTestCase",
+    (grass.gunittest.case.TestCase,),
+    dict(doctest.SkipDocTestCase.__dict__),
+)
 
 
 
 
 def load_tests(loader, tests, ignore):
 def load_tests(loader, tests, ignore):
@@ -33,14 +35,20 @@ def load_tests(loader, tests, ignore):
     # this should be called at some top level
     # this should be called at some top level
 
 
     from grass.pygrass.modules import Module
     from grass.pygrass.modules import Module
+
     Module("g.region", n=40, s=0, e=40, w=0, res=10)
     Module("g.region", n=40, s=0, e=40, w=0, res=10)
-    Module("r.mapcalc",
+    Module(
+        "r.mapcalc",
         expression="%s = row() + (10 * col())" % (pgrass.test_raster_name),
         expression="%s = row() + (10 * col())" % (pgrass.test_raster_name),
-        overwrite=True)
-    Module("r.support", map=pgrass.test_raster_name,
+        overwrite=True,
+    )
+    Module(
+        "r.support",
+        map=pgrass.test_raster_name,
         title="A test map",
         title="A test map",
         history="Generated by r.mapcalc",
         history="Generated by r.mapcalc",
-        description="This is a test map")
+        description="This is a test map",
+    )
     cats = """11:A
     cats = """11:A
             12:B
             12:B
             13:C
             13:C
@@ -57,17 +65,20 @@ def load_tests(loader, tests, ignore):
             42:n
             42:n
             43:O
             43:O
             44:P"""
             44:P"""
-    Module("r.category", rules="-", map=pgrass.test_raster_name,
-           stdin_=cats, separator=":")
+    Module(
+        "r.category", rules="-", map=pgrass.test_raster_name, stdin_=cats, separator=":"
+    )
 
 
-    Module("r.mapcalc",
+    Module(
+        "r.mapcalc",
         expression="%s = row() + (10 * col())" % (pgrass.abstract.test_raster_name),
         expression="%s = row() + (10 * col())" % (pgrass.abstract.test_raster_name),
-        overwrite=True)
+        overwrite=True,
+    )
 
 
     tests.addTests(doctest.DocTestSuite(pgrass))
     tests.addTests(doctest.DocTestSuite(pgrass))
     tests.addTests(doctest.DocTestSuite(pgrass.abstract))
     tests.addTests(doctest.DocTestSuite(pgrass.abstract))
     return tests
     return tests
 
 
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     grass.gunittest.main.test()
     grass.gunittest.main.test()

+ 19 - 24
python/grass/pygrass/raster/testsuite/test_raster_img.py

@@ -13,6 +13,7 @@ has_PyQt4 = False
 try:
 try:
     from PyQt4.QtCore import *
     from PyQt4.QtCore import *
     from PyQt4.QtGui import *
     from PyQt4.QtGui import *
+
     has_PyQt4 = True
     has_PyQt4 = True
 except:
 except:
     pass
     pass
@@ -27,16 +28,18 @@ class RasterRowImgTestCase(TestCase):
         """Create test raster map and region"""
         """Create test raster map and region"""
         cls.use_temp_region()
         cls.use_temp_region()
         cls.runModule("g.region", n=60, s=0, e=40, w=0, res=0.1)
         cls.runModule("g.region", n=60, s=0, e=40, w=0, res=0.1)
-        cls.runModule("r.mapcalc",
-            expression="%s = if(row() >= 10 && row() <= 60, null(), row()  + (10.0 * col()))" % (cls.name),
-            overwrite=True)
+        cls.runModule(
+            "r.mapcalc",
+            expression="%s = if(row() >= 10 && row() <= 60, null(), row()  + (10.0 * col()))"
+            % (cls.name),
+            overwrite=True,
+        )
         cls.runModule("r.colors", map=cls.name, color="elevation")
         cls.runModule("r.colors", map=cls.name, color="elevation")
 
 
     @classmethod
     @classmethod
     def tearDownClass(cls):
     def tearDownClass(cls):
         """Remove the generated vector map, if exist"""
         """Remove the generated vector map, if exist"""
-        cls.runModule("g.remove", flags='f', type='raster',
-                      name=cls.name)
+        cls.runModule("g.remove", flags="f", type="raster", name=cls.name)
         cls.del_temp_region()
         cls.del_temp_region()
 
 
     @unittest.skipIf(has_PyQt4 is False, "Require PyQt4")
     @unittest.skipIf(has_PyQt4 is False, "Require PyQt4")
@@ -53,8 +56,7 @@ class RasterRowImgTestCase(TestCase):
 
 
         a = raster2numpy_img(self.name, region)
         a = raster2numpy_img(self.name, region)
 
 
-        image = QImage(a.data, region.cols, region.rows,
-                       QImage.Format_ARGB32)
+        image = QImage(a.data, region.cols, region.rows, QImage.Format_ARGB32)
         # image.save("data/a.png")
         # image.save("data/a.png")
         image.save(tmpfile)
         image.save(tmpfile)
         self.assertFilesEqualMd5(tmpfile, "data/a.png")
         self.assertFilesEqualMd5(tmpfile, "data/a.png")
@@ -74,11 +76,9 @@ class RasterRowImgTestCase(TestCase):
         # With array as argument
         # With array as argument
         array = np.ndarray((region.rows * region.cols * 4), np.uint8)
         array = np.ndarray((region.rows * region.cols * 4), np.uint8)
 
 
-        raster2numpy_img(rastname=self.name, region=region,
-                         color="ARGB", array=array)
+        raster2numpy_img(rastname=self.name, region=region, color="ARGB", array=array)
 
 
-        image = QImage(array.data,
-                       region.cols, region.rows, QImage.Format_ARGB32)
+        image = QImage(array.data, region.cols, region.rows, QImage.Format_ARGB32)
         # image.save("data/b.png")
         # image.save("data/b.png")
         image.save(tmpfile)
         image.save(tmpfile)
         self.assertFilesEqualMd5(tmpfile, "data/b.png")
         self.assertFilesEqualMd5(tmpfile, "data/b.png")
@@ -98,11 +98,9 @@ class RasterRowImgTestCase(TestCase):
         # With array as argument
         # With array as argument
         array = np.ndarray((region.rows * region.cols * 4), np.uint8)
         array = np.ndarray((region.rows * region.cols * 4), np.uint8)
 
 
-        raster2numpy_img(rastname=self.name, region=region,
-                         color="ARGB", array=array)
+        raster2numpy_img(rastname=self.name, region=region, color="ARGB", array=array)
 
 
-        image = QImage(array.data,
-                       region.cols, region.rows, QImage.Format_ARGB32)
+        image = QImage(array.data, region.cols, region.rows, QImage.Format_ARGB32)
         # image.save("data/c.png")
         # image.save("data/c.png")
         image.save(tmpfile)
         image.save(tmpfile)
         self.assertFilesEqualMd5(tmpfile, "data/c.png")
         self.assertFilesEqualMd5(tmpfile, "data/c.png")
@@ -122,11 +120,9 @@ class RasterRowImgTestCase(TestCase):
         # With array as argument
         # With array as argument
         array = np.ndarray((region.rows * region.cols * 4), np.uint8)
         array = np.ndarray((region.rows * region.cols * 4), np.uint8)
 
 
-        raster2numpy_img(rastname=self.name, region=region,
-                         color="RGB", array=array)
+        raster2numpy_img(rastname=self.name, region=region, color="RGB", array=array)
 
 
-        image = QImage(array.data,
-                       region.cols, region.rows, QImage.Format_RGB32)
+        image = QImage(array.data, region.cols, region.rows, QImage.Format_RGB32)
         # image.save("data/d.png")
         # image.save("data/d.png")
         image.save(tmpfile)
         image.save(tmpfile)
         self.assertFilesEqualMd5(tmpfile, "data/d.png")
         self.assertFilesEqualMd5(tmpfile, "data/d.png")
@@ -143,11 +139,9 @@ class RasterRowImgTestCase(TestCase):
         tmpfile = tempfile(False)
         tmpfile = tempfile(False)
         tmpfile = tmpfile + ".png"
         tmpfile = tmpfile + ".png"
 
 
-        array = raster2numpy_img(rastname=self.name, region=region,
-                                 color="RGB")
+        array = raster2numpy_img(rastname=self.name, region=region, color="RGB")
 
 
-        image = QImage(array.data,
-                       region.cols, region.rows, QImage.Format_RGB32)
+        image = QImage(array.data, region.cols, region.rows, QImage.Format_RGB32)
         # image.save("data/e.png")
         # image.save("data/e.png")
         image.save(tmpfile)
         image.save(tmpfile)
         self.assertFilesEqualMd5(tmpfile, "data/e.png")
         self.assertFilesEqualMd5(tmpfile, "data/e.png")
@@ -196,5 +190,6 @@ class RasterRowImgTestCase(TestCase):
 
 
         self.assertEqual(len(a), region.rows * region.cols * 1)
         self.assertEqual(len(a), region.rows * region.cols * 1)
 
 
-if __name__ == '__main__':
+
+if __name__ == "__main__":
     test()
     test()

+ 18 - 11
python/grass/pygrass/raster/testsuite/test_raster_region.py

@@ -17,14 +17,16 @@ class RasterRowRegionTestCase(TestCase):
         """Create test raster map and region"""
         """Create test raster map and region"""
         cls.use_temp_region()
         cls.use_temp_region()
         cls.runModule("g.region", n=40, s=0, e=40, w=0, res=10)
         cls.runModule("g.region", n=40, s=0, e=40, w=0, res=10)
-        cls.runModule("r.mapcalc", expression="%s = row() + (10.0 * col())" % (cls.name),
-            overwrite=True)
+        cls.runModule(
+            "r.mapcalc",
+            expression="%s = row() + (10.0 * col())" % (cls.name),
+            overwrite=True,
+        )
 
 
     @classmethod
     @classmethod
     def tearDownClass(cls):
     def tearDownClass(cls):
         """Remove the generated vector map, if exist"""
         """Remove the generated vector map, if exist"""
-        cls.runModule("g.remove", flags='f', type='raster',
-                      name=cls.name)
+        cls.runModule("g.remove", flags="f", type="raster", name=cls.name)
         cls.del_temp_region()
         cls.del_temp_region()
 
 
     def test_resampling_1(self):
     def test_resampling_1(self):
@@ -41,10 +43,14 @@ class RasterRowRegionTestCase(TestCase):
 
 
         rast = RasterRow(self.name)
         rast = RasterRow(self.name)
         rast.set_region(region)
         rast.set_region(region)
-        rast.open(mode='r')
+        rast.open(mode="r")
 
 
-        six.assertCountEqual(self, rast[0].tolist(), [22, 22, 22, 22, 22, 32, 32, 32, 32, 32])
-        six.assertCountEqual(self, rast[5].tolist(), [23, 23, 23, 23, 23, 33, 33, 33, 33, 33])
+        six.assertCountEqual(
+            self, rast[0].tolist(), [22, 22, 22, 22, 22, 32, 32, 32, 32, 32]
+        )
+        six.assertCountEqual(
+            self, rast[5].tolist(), [23, 23, 23, 23, 23, 33, 33, 33, 33, 33]
+        )
 
 
         rast.close()
         rast.close()
 
 
@@ -62,7 +68,7 @@ class RasterRowRegionTestCase(TestCase):
 
 
         rast = RasterRow(self.name)
         rast = RasterRow(self.name)
         rast.set_region(region)
         rast.set_region(region)
-        rast.open(mode='r')
+        rast.open(mode="r")
 
 
         """
         """
         [nan, nan, nan, nan, nan, nan, nan, nan]
         [nan, nan, nan, nan, nan, nan, nan, nan]
@@ -75,8 +81,8 @@ class RasterRowRegionTestCase(TestCase):
         [nan, nan, nan, nan, nan, nan, nan, nan]
         [nan, nan, nan, nan, nan, nan, nan, nan]
         """
         """
 
 
-        six.assertCountEqual(self, rast[2].tolist()[2:6], [11., 21., 31., 41.])
-        six.assertCountEqual(self, rast[5].tolist()[2:6], [14., 24., 34., 44.])
+        six.assertCountEqual(self, rast[2].tolist()[2:6], [11.0, 21.0, 31.0, 41.0])
+        six.assertCountEqual(self, rast[5].tolist()[2:6], [14.0, 24.0, 34.0, 44.0])
 
 
         rast.close()
         rast.close()
 
 
@@ -110,5 +116,6 @@ class RasterRowRegionTestCase(TestCase):
 
 
         self.assertEqual(len(a), 8)
         self.assertEqual(len(a), 8)
 
 
-if __name__ == '__main__':
+
+if __name__ == "__main__":
     test()
     test()

+ 211 - 188
python/grass/pygrass/rpc/__init__.py

@@ -29,6 +29,7 @@ import logging
 ###############################################################################
 ###############################################################################
 ###############################################################################
 ###############################################################################
 
 
+
 class RPCDefs(object):
 class RPCDefs(object):
     # Function identifier and index
     # Function identifier and index
     STOP = 0
     STOP = 0
@@ -40,11 +41,11 @@ class RPCDefs(object):
 
 
 def _get_raster_image_as_np(lock, conn, data):
 def _get_raster_image_as_np(lock, conn, data):
     """Convert a raster map into an image and return
     """Convert a raster map into an image and return
-       a numpy array with RGB or Gray values.
+    a numpy array with RGB or Gray values.
 
 
-       :param lock: A multiprocessing.Lock instance
-       :param conn: A multiprocessing.Pipe instance used to send True or False
-       :param data: The list of data entries [function_id, raster_name, extent, color]
+    :param lock: A multiprocessing.Lock instance
+    :param conn: A multiprocessing.Pipe instance used to send True or False
+    :param data: The list of data entries [function_id, raster_name, extent, color]
     """
     """
     array = None
     array = None
     try:
     try:
@@ -86,12 +87,13 @@ def _get_raster_image_as_np(lock, conn, data):
     finally:
     finally:
         conn.send(array)
         conn.send(array)
 
 
+
 def _get_vector_table_as_dict(lock, conn, data):
 def _get_vector_table_as_dict(lock, conn, data):
     """Get the table of a vector map layer as dictionary
     """Get the table of a vector map layer as dictionary
 
 
-       :param lock: A multiprocessing.Lock instance
-       :param conn: A multiprocessing.Pipe instance used to send True or False
-       :param data: The list of data entries [function_id, name, mapset, where]
+    :param lock: A multiprocessing.Lock instance
+    :param conn: A multiprocessing.Pipe instance used to send True or False
+    :param data: The list of data entries [function_id, name, mapset, where]
 
 
     """
     """
     ret = None
     ret = None
@@ -124,16 +126,17 @@ def _get_vector_table_as_dict(lock, conn, data):
     finally:
     finally:
         conn.send(ret)
         conn.send(ret)
 
 
+
 def _get_vector_features_as_wkb_list(lock, conn, data):
 def _get_vector_features_as_wkb_list(lock, conn, data):
     """Return vector layer features as wkb list
     """Return vector layer features as wkb list
 
 
-       supported feature types:
-       point, centroid, line, boundary, area
+    supported feature types:
+    point, centroid, line, boundary, area
 
 
-       :param lock: A multiprocessing.Lock instance
-       :param conn: A multiprocessing.Pipe instance used to send True or False
-       :param data: The list of data entries [function_id,name,mapset,extent,
-                                              feature_type, field]
+    :param lock: A multiprocessing.Lock instance
+    :param conn: A multiprocessing.Pipe instance used to send True or False
+    :param data: The list of data entries [function_id,name,mapset,extent,
+                                           feature_type, field]
 
 
     """
     """
     wkb_list = None
     wkb_list = None
@@ -154,26 +157,30 @@ def _get_vector_features_as_wkb_list(lock, conn, data):
 
 
         if layer.exist() is True:
         if layer.exist() is True:
             if extent is not None:
             if extent is not None:
-                bbox = basic.Bbox(north=extent["north"],
-                                  south=extent["south"],
-                                  east=extent["east"],
-                                  west=extent["west"])
+                bbox = basic.Bbox(
+                    north=extent["north"],
+                    south=extent["south"],
+                    east=extent["east"],
+                    west=extent["west"],
+                )
 
 
             layer.open("r")
             layer.open("r")
             if feature_type.lower() == "area":
             if feature_type.lower() == "area":
                 wkb_list = layer.areas_to_wkb_list(bbox=bbox, field=field)
                 wkb_list = layer.areas_to_wkb_list(bbox=bbox, field=field)
             else:
             else:
-                wkb_list = layer.features_to_wkb_list(bbox=bbox,
-                                                      feature_type=feature_type,
-                                                      field=field)
+                wkb_list = layer.features_to_wkb_list(
+                    bbox=bbox, feature_type=feature_type, field=field
+                )
             layer.close()
             layer.close()
     except:
     except:
         raise
         raise
     finally:
     finally:
         conn.send(wkb_list)
         conn.send(wkb_list)
 
 
+
 ###############################################################################
 ###############################################################################
 
 
+
 def _fatal_error(lock, conn, data):
 def _fatal_error(lock, conn, data):
     """Calls G_fatal_error()"""
     """Calls G_fatal_error()"""
     libgis.G_fatal_error("Fatal Error in C library server")
     libgis.G_fatal_error("Fatal Error in C library server")
@@ -181,19 +188,22 @@ def _fatal_error(lock, conn, data):
 
 
 ###############################################################################
 ###############################################################################
 
 
+
 def _stop(lock, conn, data):
 def _stop(lock, conn, data):
     conn.close()
     conn.close()
     lock.release()
     lock.release()
     sys.exit()
     sys.exit()
 
 
+
 ###############################################################################
 ###############################################################################
 
 
+
 def data_provider_server(lock, conn):
 def data_provider_server(lock, conn):
     """The PyGRASS data provider server designed to be a target for
     """The PyGRASS data provider server designed to be a target for
-       multiprocessing.Process
+    multiprocessing.Process
 
 
-       :param lock: A multiprocessing.Lock
-       :param conn: A multiprocessing.Pipe
+    :param lock: A multiprocessing.Lock
+    :param conn: A multiprocessing.Pipe
     """
     """
 
 
     def error_handler(data):
     def error_handler(data):
@@ -215,7 +225,7 @@ def data_provider_server(lock, conn):
     libgis.G_add_error_handler(cerror_handler, None)
     libgis.G_add_error_handler(cerror_handler, None)
 
 
     # Crerate the function array
     # Crerate the function array
-    functions = [0]*15
+    functions = [0] * 15
     functions[RPCDefs.GET_VECTOR_TABLE_AS_DICT] = _get_vector_table_as_dict
     functions[RPCDefs.GET_VECTOR_TABLE_AS_DICT] = _get_vector_table_as_dict
     functions[RPCDefs.GET_VECTOR_FEATURES_AS_WKB] = _get_vector_features_as_wkb_list
     functions[RPCDefs.GET_VECTOR_FEATURES_AS_WKB] = _get_vector_features_as_wkb_list
     functions[RPCDefs.GET_RASTER_IMAGE_AS_NP] = _get_raster_image_as_np
     functions[RPCDefs.GET_RASTER_IMAGE_AS_NP] = _get_raster_image_as_np
@@ -230,223 +240,236 @@ def data_provider_server(lock, conn):
         functions[data[0]](lock, conn, data)
         functions[data[0]](lock, conn, data)
         lock.release()
         lock.release()
 
 
+
 test_vector_name = "data_provider_vector_map"
 test_vector_name = "data_provider_vector_map"
 test_raster_name = "data_provider_raster_map"
 test_raster_name = "data_provider_raster_map"
 
 
-class DataProvider(RPCServerBase):
-    """Fast and exit-safe interface to PyGRASS data delivery functions
 
 
-    """
+class DataProvider(RPCServerBase):
+    """Fast and exit-safe interface to PyGRASS data delivery functions"""
 
 
     def __init__(self):
     def __init__(self):
         RPCServerBase.__init__(self)
         RPCServerBase.__init__(self)
 
 
     def start_server(self):
     def start_server(self):
-        """This function must be re-implemented in the subclasses
-        """
+        """This function must be re-implemented in the subclasses"""
         self.client_conn, self.server_conn = Pipe(True)
         self.client_conn, self.server_conn = Pipe(True)
         self.lock = Lock()
         self.lock = Lock()
-        self.server = Process(target=data_provider_server, args=(self.lock,
-                                                             self.server_conn))
+        self.server = Process(
+            target=data_provider_server, args=(self.lock, self.server_conn)
+        )
         self.server.daemon = True
         self.server.daemon = True
         self.server.start()
         self.server.start()
 
 
     def get_raster_image_as_np(self, name, mapset=None, extent=None, color="RGB"):
     def get_raster_image_as_np(self, name, mapset=None, extent=None, color="RGB"):
         """Return the attribute table of a vector map as dictionary.
         """Return the attribute table of a vector map as dictionary.
 
 
-           See documentation of: pygrass.raster.raster2numpy_img
+        See documentation of: pygrass.raster.raster2numpy_img
 
 
-           Usage:
+        Usage:
 
 
-           .. code-block:: python
+        .. code-block:: python
 
 
-            >>> from grass.pygrass.rpc import DataProvider
-            >>> import time
-            >>> provider = DataProvider()
-            >>> ret = provider.get_raster_image_as_np(name=test_raster_name)
-            >>> len(ret)
-            64
+         >>> from grass.pygrass.rpc import DataProvider
+         >>> import time
+         >>> provider = DataProvider()
+         >>> ret = provider.get_raster_image_as_np(name=test_raster_name)
+         >>> len(ret)
+         64
 
 
-            >>> extent = {"north":30, "south":10, "east":30, "west":10,
-            ...           "rows":2, "cols":2}
-            >>> ret = provider.get_raster_image_as_np(name=test_raster_name,
-            ...                                       extent=extent)
-            >>> len(ret)
-            16
+         >>> extent = {"north":30, "south":10, "east":30, "west":10,
+         ...           "rows":2, "cols":2}
+         >>> ret = provider.get_raster_image_as_np(name=test_raster_name,
+         ...                                       extent=extent)
+         >>> len(ret)
+         16
 
 
-            >>> extent = {"rows":3, "cols":1}
-            >>> ret = provider.get_raster_image_as_np(name=test_raster_name,
-            ...                                       extent=extent)
-            >>> len(ret)
-            12
+         >>> extent = {"rows":3, "cols":1}
+         >>> ret = provider.get_raster_image_as_np(name=test_raster_name,
+         ...                                       extent=extent)
+         >>> len(ret)
+         12
 
 
-            >>> extent = {"north":100, "south":10, "east":30, "west":10,
-            ...           "rows":2, "cols":2}
-            >>> ret = provider.get_raster_image_as_np(name=test_raster_name,
-            ...                                       extent=extent)
+         >>> extent = {"north":100, "south":10, "east":30, "west":10,
+         ...           "rows":2, "cols":2}
+         >>> ret = provider.get_raster_image_as_np(name=test_raster_name,
+         ...                                       extent=extent)
 
 
-            >>> provider.stop()
-            >>> time.sleep(1)
+         >>> provider.stop()
+         >>> time.sleep(1)
 
 
-            >>> extent = {"rows":3, "cols":1}
-            >>> ret = provider.get_raster_image_as_np(name=test_raster_name,
-            ...                                       extent=extent)
-            >>> len(ret)
-            12
+         >>> extent = {"rows":3, "cols":1}
+         >>> ret = provider.get_raster_image_as_np(name=test_raster_name,
+         ...                                       extent=extent)
+         >>> len(ret)
+         12
 
 
 
 
-            ..
+         ..
         """
         """
         self.check_server()
         self.check_server()
-        self.client_conn.send([RPCDefs.GET_RASTER_IMAGE_AS_NP,
-                               name, mapset, extent, color])
+        self.client_conn.send(
+            [RPCDefs.GET_RASTER_IMAGE_AS_NP, name, mapset, extent, color]
+        )
         return self.safe_receive("get_raster_image_as_np")
         return self.safe_receive("get_raster_image_as_np")
 
 
     def get_vector_table_as_dict(self, name, mapset=None, where=None):
     def get_vector_table_as_dict(self, name, mapset=None, where=None):
         """Return the attribute table of a vector map as dictionary.
         """Return the attribute table of a vector map as dictionary.
 
 
-           See documentation of: pygrass.vector.VectorTopo::table_to_dict
-
-           Usage:
-
-           .. code-block:: python
-
-            >>> from grass.pygrass.rpc import DataProvider
-            >>> provider = DataProvider()
-            >>> ret = provider.get_vector_table_as_dict(name=test_vector_name)
-            >>> ret["table"]
-            {1: [1, 'point', 1.0], 2: [2, 'line', 2.0], 3: [3, 'centroid', 3.0]}
-            >>> ret["columns"]
-            Columns([('cat', 'INTEGER'), ('name', 'varchar(50)'), ('value', 'double precision')])
-            >>> ret = provider.get_vector_table_as_dict(name=test_vector_name,
-            ...                                           where="value > 1")
-            >>> ret["table"]
-            {2: [2, 'line', 2.0], 3: [3, 'centroid', 3.0]}
-            >>> ret["columns"]
-            Columns([('cat', 'INTEGER'), ('name', 'varchar(50)'), ('value', 'double precision')])
-            >>> provider.get_vector_table_as_dict(name="no_map",
-            ...                                   where="value > 1")
-            >>> provider.stop()
-
-            ..
+        See documentation of: pygrass.vector.VectorTopo::table_to_dict
+
+        Usage:
+
+        .. code-block:: python
+
+         >>> from grass.pygrass.rpc import DataProvider
+         >>> provider = DataProvider()
+         >>> ret = provider.get_vector_table_as_dict(name=test_vector_name)
+         >>> ret["table"]
+         {1: [1, 'point', 1.0], 2: [2, 'line', 2.0], 3: [3, 'centroid', 3.0]}
+         >>> ret["columns"]
+         Columns([('cat', 'INTEGER'), ('name', 'varchar(50)'), ('value', 'double precision')])
+         >>> ret = provider.get_vector_table_as_dict(name=test_vector_name,
+         ...                                           where="value > 1")
+         >>> ret["table"]
+         {2: [2, 'line', 2.0], 3: [3, 'centroid', 3.0]}
+         >>> ret["columns"]
+         Columns([('cat', 'INTEGER'), ('name', 'varchar(50)'), ('value', 'double precision')])
+         >>> provider.get_vector_table_as_dict(name="no_map",
+         ...                                   where="value > 1")
+         >>> provider.stop()
+
+         ..
         """
         """
         self.check_server()
         self.check_server()
-        self.client_conn.send([RPCDefs.GET_VECTOR_TABLE_AS_DICT,
-                               name, mapset, where])
+        self.client_conn.send([RPCDefs.GET_VECTOR_TABLE_AS_DICT, name, mapset, where])
         return self.safe_receive("get_vector_table_as_dict")
         return self.safe_receive("get_vector_table_as_dict")
 
 
-    def get_vector_features_as_wkb_list(self, name, mapset=None, extent=None,
-                                        feature_type="point", field=1):
+    def get_vector_features_as_wkb_list(
+        self, name, mapset=None, extent=None, feature_type="point", field=1
+    ):
         """Return the features of a vector map as wkb list.
         """Return the features of a vector map as wkb list.
 
 
-           :param extent: A dictionary of {"north":double, "south":double,
-                                           "east":double, "west":double}
-           :param feature_type: point, centroid, line, boundary or area
-
-           See documentation: pygrass.vector.VectorTopo::features_to_wkb_list
-                              pygrass.vector.VectorTopo::areas_to_wkb_list
-
-
-           Usage:
-
-           .. code-block:: python
-
-            >>> from grass.pygrass.rpc import DataProvider
-            >>> provider = DataProvider()
-            >>> wkb = provider.get_vector_features_as_wkb_list(name=test_vector_name,
-            ...                                                extent=None,
-            ...                                                feature_type="point")
-            >>> for entry in wkb:
-            ...     f_id, cat, string = entry
-            ...     print(f_id, cat, len(string))
-            1 1 21
-            2 1 21
-            3 1 21
-
-            >>> extent = {"north":6.6, "south":5.5, "east":14.5, "west":13.5}
-            >>> wkb = provider.get_vector_features_as_wkb_list(name=test_vector_name,
-            ...                                                extent=extent,
-            ...                                                feature_type="point")
-            >>> for entry in wkb:
-            ...     f_id, cat, string = entry
-            ...     print(f_id, cat, len(string))
-            3 1 21
-
-            >>> wkb = provider.get_vector_features_as_wkb_list(name=test_vector_name,
-            ...                                                extent=None,
-            ...                                                feature_type="line")
-            >>> for entry in wkb:
-            ...     f_id, cat, string = entry
-            ...     print(f_id, cat, len(string))
-            4 2 57
-            5 2 57
-            6 2 57
-
-
-            >>> wkb = provider.get_vector_features_as_wkb_list(name=test_vector_name,
-            ...                                                extent=None,
-            ...                                                feature_type="centroid")
-            >>> for entry in wkb:
-            ...     f_id, cat, string = entry
-            ...     print(f_id, cat, len(string))
-            19 3 21
-            18 3 21
-            20 3 21
-            21 3 21
-
-            >>> wkb = provider.get_vector_features_as_wkb_list(name=test_vector_name,
-            ...                                                extent=None,
-            ...                                                feature_type="area")
-            >>> for entry in wkb:
-            ...     f_id, cat, string = entry
-            ...     print(f_id, cat, len(string))
-            1 3 225
-            2 3 141
-            3 3 93
-            4 3 141
-
-            >>> wkb = provider.get_vector_features_as_wkb_list(name=test_vector_name,
-            ...                                                extent=None,
-            ...                                                feature_type="boundary")
-            >>> for entry in wkb:
-            ...     f_id, cat, string = entry
-            ...     print(f_id, cat, len(string))
-            10 None 41
-            7 None 41
-            8 None 41
-            9 None 41
-            11 None 89
-            12 None 41
-            14 None 41
-            13 None 41
-            17 None 41
-            15 None 41
-            16 None 41
-
-            >>> provider.stop()
-
-            ..
+        :param extent: A dictionary of {"north":double, "south":double,
+                                        "east":double, "west":double}
+        :param feature_type: point, centroid, line, boundary or area
+
+        See documentation: pygrass.vector.VectorTopo::features_to_wkb_list
+                           pygrass.vector.VectorTopo::areas_to_wkb_list
+
+
+        Usage:
+
+        .. code-block:: python
+
+         >>> from grass.pygrass.rpc import DataProvider
+         >>> provider = DataProvider()
+         >>> wkb = provider.get_vector_features_as_wkb_list(name=test_vector_name,
+         ...                                                extent=None,
+         ...                                                feature_type="point")
+         >>> for entry in wkb:
+         ...     f_id, cat, string = entry
+         ...     print(f_id, cat, len(string))
+         1 1 21
+         2 1 21
+         3 1 21
+
+         >>> extent = {"north":6.6, "south":5.5, "east":14.5, "west":13.5}
+         >>> wkb = provider.get_vector_features_as_wkb_list(name=test_vector_name,
+         ...                                                extent=extent,
+         ...                                                feature_type="point")
+         >>> for entry in wkb:
+         ...     f_id, cat, string = entry
+         ...     print(f_id, cat, len(string))
+         3 1 21
+
+         >>> wkb = provider.get_vector_features_as_wkb_list(name=test_vector_name,
+         ...                                                extent=None,
+         ...                                                feature_type="line")
+         >>> for entry in wkb:
+         ...     f_id, cat, string = entry
+         ...     print(f_id, cat, len(string))
+         4 2 57
+         5 2 57
+         6 2 57
+
+
+         >>> wkb = provider.get_vector_features_as_wkb_list(name=test_vector_name,
+         ...                                                extent=None,
+         ...                                                feature_type="centroid")
+         >>> for entry in wkb:
+         ...     f_id, cat, string = entry
+         ...     print(f_id, cat, len(string))
+         19 3 21
+         18 3 21
+         20 3 21
+         21 3 21
+
+         >>> wkb = provider.get_vector_features_as_wkb_list(name=test_vector_name,
+         ...                                                extent=None,
+         ...                                                feature_type="area")
+         >>> for entry in wkb:
+         ...     f_id, cat, string = entry
+         ...     print(f_id, cat, len(string))
+         1 3 225
+         2 3 141
+         3 3 93
+         4 3 141
+
+         >>> wkb = provider.get_vector_features_as_wkb_list(name=test_vector_name,
+         ...                                                extent=None,
+         ...                                                feature_type="boundary")
+         >>> for entry in wkb:
+         ...     f_id, cat, string = entry
+         ...     print(f_id, cat, len(string))
+         10 None 41
+         7 None 41
+         8 None 41
+         9 None 41
+         11 None 89
+         12 None 41
+         14 None 41
+         13 None 41
+         17 None 41
+         15 None 41
+         16 None 41
+
+         >>> provider.stop()
+
+         ..
         """
         """
         self.check_server()
         self.check_server()
-        self.client_conn.send([RPCDefs.GET_VECTOR_FEATURES_AS_WKB,
-                               name, mapset, extent, feature_type, field])
+        self.client_conn.send(
+            [
+                RPCDefs.GET_VECTOR_FEATURES_AS_WKB,
+                name,
+                mapset,
+                extent,
+                feature_type,
+                field,
+            ]
+        )
         return self.safe_receive("get_vector_features_as_wkb_list")
         return self.safe_receive("get_vector_features_as_wkb_list")
 
 
 
 
 if __name__ == "__main__":
 if __name__ == "__main__":
     import doctest
     import doctest
     from grass.pygrass.modules import Module
     from grass.pygrass.modules import Module
+
     Module("g.region", n=40, s=0, e=40, w=0, res=10)
     Module("g.region", n=40, s=0, e=40, w=0, res=10)
-    Module("r.mapcalc", expression="%s = row() + (10 * col())" % (test_raster_name),
-                             overwrite=True)
+    Module(
+        "r.mapcalc",
+        expression="%s = row() + (10 * col())" % (test_raster_name),
+        overwrite=True,
+    )
     utils.create_test_vector_map(test_vector_name)
     utils.create_test_vector_map(test_vector_name)
 
 
     doctest.testmod()
     doctest.testmod()
 
 
     """Remove the generated maps, if exist"""
     """Remove the generated maps, if exist"""
-    mset = utils.get_mapset_raster(test_raster_name, mapset='')
+    mset = utils.get_mapset_raster(test_raster_name, mapset="")
     if mset:
     if mset:
-        Module("g.remove", flags='f', type='raster', name=test_raster_name)
-    mset = utils.get_mapset_vector(test_vector_name, mapset='')
+        Module("g.remove", flags="f", type="raster", name=test_raster_name)
+    mset = utils.get_mapset_vector(test_vector_name, mapset="")
     if mset:
     if mset:
-        Module("g.remove", flags='f', type='vector', name=test_vector_name)
+        Module("g.remove", flags="f", type="vector", name=test_vector_name)

+ 44 - 38
python/grass/pygrass/rpc/base.py

@@ -20,11 +20,12 @@ import logging
 
 
 ###############################################################################
 ###############################################################################
 
 
+
 def dummy_server(lock, conn):
 def dummy_server(lock, conn):
     """Dummy server process
     """Dummy server process
 
 
-       :param lock: A multiprocessing.Lock
-       :param conn: A multiprocessing.Pipe
+    :param lock: A multiprocessing.Lock
+    :param conn: A multiprocessing.Pipe
     """
     """
 
 
     while True:
     while True:
@@ -40,44 +41,45 @@ def dummy_server(lock, conn):
             raise Exception("Server process intentionally killed by exception")
             raise Exception("Server process intentionally killed by exception")
         lock.release()
         lock.release()
 
 
+
 class RPCServerBase(object):
 class RPCServerBase(object):
     """This is the base class for send and receive RPC server
     """This is the base class for send and receive RPC server
-       It uses a Pipe for IPC.
+    It uses a Pipe for IPC.
 
 
 
 
-        >>> import grass.script as gscript
-        >>> from grass.pygrass.rpc.base import RPCServerBase
-        >>> import time
-        >>> provider = RPCServerBase()
+     >>> import grass.script as gscript
+     >>> from grass.pygrass.rpc.base import RPCServerBase
+     >>> import time
+     >>> provider = RPCServerBase()
 
 
-        >>> provider.is_server_alive()
-        True
+     >>> provider.is_server_alive()
+     True
 
 
-        >>> provider.is_check_thread_alive()
-        True
+     >>> provider.is_check_thread_alive()
+     True
 
 
-        >>> provider.stop()
-        >>> time.sleep(1)
-        >>> provider.is_server_alive()
-        False
+     >>> provider.stop()
+     >>> time.sleep(1)
+     >>> provider.is_server_alive()
+     False
 
 
-        >>> provider.is_check_thread_alive()
-        False
+     >>> provider.is_check_thread_alive()
+     False
 
 
-        >>> provider = RPCServerBase()
-        >>> provider.is_server_alive()
-        True
-        >>> provider.is_check_thread_alive()
-        True
+     >>> provider = RPCServerBase()
+     >>> provider.is_server_alive()
+     True
+     >>> provider.is_check_thread_alive()
+     True
 
 
-        Kill the server process with an exception, it should restart
+     Kill the server process with an exception, it should restart
 
 
-        >>> provider.client_conn.send([1])
-        >>> provider.is_server_alive()
-        True
+     >>> provider.client_conn.send([1])
+     >>> provider.is_server_alive()
+     True
 
 
-        >>> provider.is_check_thread_alive()
-        True
+     >>> provider.is_check_thread_alive()
+     True
 
 
     """
     """
 
 
@@ -127,14 +129,12 @@ class RPCServerBase(object):
             self.threadLock.release()
             self.threadLock.release()
 
 
     def start_server(self):
     def start_server(self):
-        """This function must be re-implemented in the subclasses
-        """
+        """This function must be re-implemented in the subclasses"""
         logging.debug("Start the libgis server")
         logging.debug("Start the libgis server")
 
 
         self.client_conn, self.server_conn = Pipe(True)
         self.client_conn, self.server_conn = Pipe(True)
         self.lock = Lock()
         self.lock = Lock()
-        self.server = Process(target=dummy_server, args=(self.lock,
-                                                         self.server_conn))
+        self.server = Process(target=dummy_server, args=(self.lock, self.server_conn))
         self.server.daemon = True
         self.server.daemon = True
         self.server.start()
         self.server.start()
 
 
@@ -142,8 +142,7 @@ class RPCServerBase(object):
         self._check_restart_server()
         self._check_restart_server()
 
 
     def _check_restart_server(self, caller="main thread"):
     def _check_restart_server(self, caller="main thread"):
-        """Restart the server if it was terminated
-        """
+        """Restart the server if it was terminated"""
         logging.debug("Check libgis server restart")
         logging.debug("Check libgis server restart")
 
 
         self.threadLock.acquire()
         self.threadLock.acquire()
@@ -155,14 +154,16 @@ class RPCServerBase(object):
         self.start_server()
         self.start_server()
 
 
         if self.stopped is not True:
         if self.stopped is not True:
-            logging.warning("Needed to restart the libgis server, caller: %s" % (caller))
+            logging.warning(
+                "Needed to restart the libgis server, caller: %s" % (caller)
+            )
 
 
         self.threadLock.release()
         self.threadLock.release()
         self.stopped = False
         self.stopped = False
 
 
     def safe_receive(self, message):
     def safe_receive(self, message):
         """Receive the data and throw a FatalError exception in case the server
         """Receive the data and throw a FatalError exception in case the server
-           process was killed and the pipe was closed by the checker thread"""
+        process was killed and the pipe was closed by the checker thread"""
         logging.debug("Receive message: {message}")
         logging.debug("Receive message: {message}")
 
 
         try:
         try:
@@ -178,13 +179,17 @@ class RPCServerBase(object):
     def stop(self):
     def stop(self):
         """Stop the check thread, the libgis server and close the pipe
         """Stop the check thread, the libgis server and close the pipe
 
 
-           This method should be called at exit using the package atexit
+        This method should be called at exit using the package atexit
         """
         """
         logging.debug("Stop libgis server")
         logging.debug("Stop libgis server")
 
 
         self.stop_checker_thread()
         self.stop_checker_thread()
         if self.server is not None and self.server.is_alive():
         if self.server is not None and self.server.is_alive():
-            self.client_conn.send([0, ])
+            self.client_conn.send(
+                [
+                    0,
+                ]
+            )
             self.server.terminate()
             self.server.terminate()
         if self.client_conn is not None:
         if self.client_conn is not None:
             self.client_conn.close()
             self.client_conn.close()
@@ -193,4 +198,5 @@ class RPCServerBase(object):
 
 
 if __name__ == "__main__":
 if __name__ == "__main__":
     import doctest
     import doctest
+
     doctest.testmod()
     doctest.testmod()

+ 15 - 10
python/grass/pygrass/rpc/testsuite/test_pygrass_rpc_doctests.py

@@ -17,12 +17,14 @@ import grass.pygrass.rpc as pygrpc
 # and contains doctest's methods
 # and contains doctest's methods
 # the alternative is to copy 500 from doctest and change what is needed
 # the alternative is to copy 500 from doctest and change what is needed
 # (this might be necessary anyway because of the reports and stdout and stderr)
 # (this might be necessary anyway because of the reports and stdout and stderr)
-doctest.DocFileCase = type('DocFileCase',
-                           (grass.gunittest.case.TestCase,),
-                           dict(doctest.DocFileCase.__dict__))
-doctest.SkipDocTestCase = type('SkipDocTestCase',
-                               (grass.gunittest.case.TestCase,),
-                               dict(doctest.SkipDocTestCase.__dict__))
+doctest.DocFileCase = type(
+    "DocFileCase", (grass.gunittest.case.TestCase,), dict(doctest.DocFileCase.__dict__)
+)
+doctest.SkipDocTestCase = type(
+    "SkipDocTestCase",
+    (grass.gunittest.case.TestCase,),
+    dict(doctest.SkipDocTestCase.__dict__),
+)
 
 
 
 
 def load_tests(loader, tests, ignore):
 def load_tests(loader, tests, ignore):
@@ -34,16 +36,19 @@ def load_tests(loader, tests, ignore):
 
 
     from grass.pygrass import utils
     from grass.pygrass import utils
     from grass.pygrass.modules import Module
     from grass.pygrass.modules import Module
+
     Module("g.region", n=40, s=0, e=40, w=0, res=10)
     Module("g.region", n=40, s=0, e=40, w=0, res=10)
-    Module("r.mapcalc", expression="%s = row() + (10 * col())" % (pygrpc.test_raster_name),
-                             overwrite=True)
+    Module(
+        "r.mapcalc",
+        expression="%s = row() + (10 * col())" % (pygrpc.test_raster_name),
+        overwrite=True,
+    )
     utils.create_test_vector_map(pygrpc.test_vector_name)
     utils.create_test_vector_map(pygrpc.test_vector_name)
 
 
-
     tests.addTests(doctest.DocTestSuite(pygrpc))
     tests.addTests(doctest.DocTestSuite(pygrpc))
     tests.addTests(doctest.DocTestSuite(pygrpc.base))
     tests.addTests(doctest.DocTestSuite(pygrpc.base))
     return tests
     return tests
 
 
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     grass.gunittest.main.test()
     grass.gunittest.main.test()

+ 22 - 6
python/grass/pygrass/shell/conversion.py

@@ -12,9 +12,17 @@ dcont = """    <tr>
     </tr>"""
     </tr>"""
 
 
 
 
-def dict2html(dic, keys=None, border='',
-              kfmt='%s', kdec='', kfun=None,
-              vfmt='%s', vdec='', vfun=None):
+def dict2html(
+    dic,
+    keys=None,
+    border="",
+    kfmt="%s",
+    kdec="",
+    kfun=None,
+    vfmt="%s",
+    vdec="",
+    vfun=None,
+):
     """Return a html repr of a dictionary.
     """Return a html repr of a dictionary.
 
 
     :param dict dic: dictionary or object with `keys` and `items` methods
     :param dict dic: dictionary or object with `keys` and `items` methods
@@ -73,6 +81,7 @@ def dict2html(dic, keys=None, border='',
         </tr>
         </tr>
     </table>
     </table>
     """
     """
+
     def fun(x):
     def fun(x):
         return x
         return x
 
 
@@ -82,6 +91,13 @@ def dict2html(dic, keys=None, border='',
     vd = "<%s>%s</%s>" % (vdec, vfmt, vdec) if vdec else vfmt
     vd = "<%s>%s</%s>" % (vdec, vfmt, vdec) if vdec else vfmt
     kfun = kfun if kfun else fun
     kfun = kfun if kfun else fun
     vfun = vfun if vfun else fun
     vfun = vfun if vfun else fun
-    content = [dcont.format(key=kd % kfun(k), value=vd % vfun(dic[k]))
-               for k in keys]
-    return '\n'.join([header, ] + content + ['</table>', ])
+    content = [dcont.format(key=kd % kfun(k), value=vd % vfun(dic[k])) for k in keys]
+    return "\n".join(
+        [
+            header,
+        ]
+        + content
+        + [
+            "</table>",
+        ]
+    )

+ 1 - 1
python/grass/pygrass/shell/show.py

@@ -8,6 +8,6 @@ import io
 
 
 
 
 def raw_figure(figpath):
 def raw_figure(figpath):
-    with io.OpenWrapper(figpath, mode='rb') as data:
+    with io.OpenWrapper(figpath, mode="rb") as data:
         res = data.read()
         res = data.read()
     return res
     return res

+ 9 - 7
python/grass/pygrass/shell/testsuite/test_pygrass_shell_doctests.py

@@ -17,12 +17,14 @@ from grass.pygrass.shell import conversion, show
 # and contains doctest's methods
 # and contains doctest's methods
 # the alternative is to copy 500 from doctest and change what is needed
 # the alternative is to copy 500 from doctest and change what is needed
 # (this might be necessary anyway because of the reports and stdout and stderr)
 # (this might be necessary anyway because of the reports and stdout and stderr)
-doctest.DocFileCase = type('DocFileCase',
-                           (grass.gunittest.case.TestCase,),
-                           dict(doctest.DocFileCase.__dict__))
-doctest.SkipDocTestCase = type('SkipDocTestCase',
-                               (grass.gunittest.case.TestCase,),
-                               dict(doctest.SkipDocTestCase.__dict__))
+doctest.DocFileCase = type(
+    "DocFileCase", (grass.gunittest.case.TestCase,), dict(doctest.DocFileCase.__dict__)
+)
+doctest.SkipDocTestCase = type(
+    "SkipDocTestCase",
+    (grass.gunittest.case.TestCase,),
+    dict(doctest.SkipDocTestCase.__dict__),
+)
 
 
 
 
 def load_tests(loader, tests, ignore):
 def load_tests(loader, tests, ignore):
@@ -36,5 +38,5 @@ def load_tests(loader, tests, ignore):
     return tests
     return tests
 
 
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     grass.gunittest.main.test()
     grass.gunittest.main.test()

+ 131 - 64
python/grass/pygrass/tests/benchmark.py

@@ -4,11 +4,19 @@ Created on Sat Jun 16 20:24:56 2012
 
 
 @author: soeren
 @author: soeren
 """
 """
-from __future__ import (nested_scopes, generators, division, absolute_import,
-                        with_statement, print_function, unicode_literals)
+from __future__ import (
+    nested_scopes,
+    generators,
+    division,
+    absolute_import,
+    with_statement,
+    print_function,
+    unicode_literals,
+)
 
 
 import optparse
 import optparse
-#import numpy as np
+
+# import numpy as np
 import time
 import time
 import collections
 import collections
 import copy
 import copy
@@ -16,6 +24,7 @@ import cProfile
 import sys
 import sys
 import os
 import os
 from jinja2 import Template
 from jinja2 import Template
+
 sys.path.append(os.getcwd())
 sys.path.append(os.getcwd())
 sys.path.append("%s/.." % (os.getcwd()))
 sys.path.append("%s/.." % (os.getcwd()))
 
 
@@ -43,6 +52,7 @@ def test__RasterSegment_value_access__if():
     test_a.close()
     test_a.close()
     test_c.close()
     test_c.close()
 
 
+
 def test__RasterSegment_value_access__add():
 def test__RasterSegment_value_access__add():
     test_a = pygrass.RasterSegment(name="test_a")
     test_a = pygrass.RasterSegment(name="test_a")
     test_a.open(mode="r")
     test_a.open(mode="r")
@@ -58,7 +68,7 @@ def test__RasterSegment_value_access__add():
 
 
     for row in range(test_a.rows):
     for row in range(test_a.rows):
         test_a.get_row(row, buff_a)
         test_a.get_row(row, buff_a)
-        test_b.get_row(row,buff_b)
+        test_b.get_row(row, buff_b)
         for col in range(test_a.cols):
         for col in range(test_a.cols):
             test_c.put(row, col, buff_a[col] + buff_b[col])
             test_c.put(row, col, buff_a[col] + buff_b[col])
 
 
@@ -66,6 +76,7 @@ def test__RasterSegment_value_access__add():
     test_b.close()
     test_b.close()
     test_c.close()
     test_c.close()
 
 
+
 def test__RasterSegment_row_access__if():
 def test__RasterSegment_row_access__if():
     test_a = pygrass.RasterSegment(name="test_a")
     test_a = pygrass.RasterSegment(name="test_a")
     test_a.open(mode="r")
     test_a.open(mode="r")
@@ -82,6 +93,7 @@ def test__RasterSegment_row_access__if():
     test_a.close()
     test_a.close()
     test_c.close()
     test_c.close()
 
 
+
 def test__RasterSegment_row_access__add():
 def test__RasterSegment_row_access__add():
     test_a = pygrass.RasterSegment(name="test_a")
     test_a = pygrass.RasterSegment(name="test_a")
     test_a.open(mode="r")
     test_a.open(mode="r")
@@ -97,13 +109,14 @@ def test__RasterSegment_row_access__add():
 
 
     for row in range(test_a.rows):
     for row in range(test_a.rows):
         test_a.get_row(row, buff_a)
         test_a.get_row(row, buff_a)
-        test_b.get_row(row,buff_b)
+        test_b.get_row(row, buff_b)
         test_c.put_row(row, buff_a + buff_b)
         test_c.put_row(row, buff_a + buff_b)
 
 
     test_a.close()
     test_a.close()
     test_b.close()
     test_b.close()
     test_c.close()
     test_c.close()
 
 
+
 def test__RasterRow_value_access__add():
 def test__RasterRow_value_access__add():
     test_a = pygrass.RasterRow(name="test_a")
     test_a = pygrass.RasterRow(name="test_a")
     test_a.open(mode="r")
     test_a.open(mode="r")
@@ -120,7 +133,7 @@ def test__RasterRow_value_access__add():
 
 
     for row in range(test_a.rows):
     for row in range(test_a.rows):
         test_a.get_row(row, buff_a)
         test_a.get_row(row, buff_a)
-        test_b.get_row(row,buff_b)
+        test_b.get_row(row, buff_b)
 
 
         for col in range(test_a.cols):
         for col in range(test_a.cols):
             buff_c[col] = buff_a[col] + buff_b[col]
             buff_c[col] = buff_a[col] + buff_b[col]
@@ -131,6 +144,7 @@ def test__RasterRow_value_access__add():
     test_b.close()
     test_b.close()
     test_c.close()
     test_c.close()
 
 
+
 def test__RasterRow_value_access__if():
 def test__RasterRow_value_access__if():
     test_a = pygrass.RasterRow(name="test_a")
     test_a = pygrass.RasterRow(name="test_a")
     test_a.open(mode="r")
     test_a.open(mode="r")
@@ -152,6 +166,7 @@ def test__RasterRow_value_access__if():
     test_a.close()
     test_a.close()
     test_c.close()
     test_c.close()
 
 
+
 def test__RasterRowIO_row_access__add():
 def test__RasterRowIO_row_access__add():
     test_a = pygrass.RasterRowIO(name="test_a")
     test_a = pygrass.RasterRowIO(name="test_a")
     test_a.open(mode="r")
     test_a.open(mode="r")
@@ -167,13 +182,14 @@ def test__RasterRowIO_row_access__add():
 
 
     for row in range(test_a.rows):
     for row in range(test_a.rows):
         test_a.get_row(row, buff_a)
         test_a.get_row(row, buff_a)
-        test_b.get_row(row,buff_b)
+        test_b.get_row(row, buff_b)
         test_c.put_row(buff_a + buff_b)
         test_c.put_row(buff_a + buff_b)
 
 
     test_a.close()
     test_a.close()
     test_b.close()
     test_b.close()
     test_c.close()
     test_c.close()
 
 
+
 def test__RasterRowIO_row_access__if():
 def test__RasterRowIO_row_access__if():
     test_a = pygrass.RasterRowIO(name="test_a")
     test_a = pygrass.RasterRowIO(name="test_a")
     test_a.open(mode="r")
     test_a.open(mode="r")
@@ -190,6 +206,7 @@ def test__RasterRowIO_row_access__if():
     test_a.close()
     test_a.close()
     test_c.close()
     test_c.close()
 
 
+
 def test__RasterRow_row_access__add():
 def test__RasterRow_row_access__add():
     test_a = pygrass.RasterRow(name="test_a")
     test_a = pygrass.RasterRow(name="test_a")
     test_a.open(mode="r")
     test_a.open(mode="r")
@@ -205,13 +222,14 @@ def test__RasterRow_row_access__add():
 
 
     for row in range(test_a.rows):
     for row in range(test_a.rows):
         test_a.get_row(row, buff_a)
         test_a.get_row(row, buff_a)
-        test_b.get_row(row,buff_b)
+        test_b.get_row(row, buff_b)
         test_c.put_row(buff_a + buff_b)
         test_c.put_row(buff_a + buff_b)
 
 
     test_a.close()
     test_a.close()
     test_b.close()
     test_b.close()
     test_c.close()
     test_c.close()
 
 
+
 def test__RasterRow_row_access__if():
 def test__RasterRow_row_access__if():
     test_a = pygrass.RasterRow(name="test_a")
     test_a = pygrass.RasterRow(name="test_a")
     test_a.open(mode="r")
     test_a.open(mode="r")
@@ -228,12 +246,15 @@ def test__RasterRow_row_access__if():
     test_a.close()
     test_a.close()
     test_c.close()
     test_c.close()
 
 
+
 def test__mapcalc__add():
 def test__mapcalc__add():
     core.mapcalc("test_c = test_a + test_b", quite=True, overwrite=True)
     core.mapcalc("test_c = test_a + test_b", quite=True, overwrite=True)
 
 
+
 def test__mapcalc__if():
 def test__mapcalc__if():
     core.mapcalc("test_c = if(test_a > 50, 1, 0)", quite=True, overwrite=True)
     core.mapcalc("test_c = if(test_a > 50, 1, 0)", quite=True, overwrite=True)
 
 
+
 def mytimer(func, runs=1):
 def mytimer(func, runs=1):
     times = []
     times = []
     t = 0.0
     t = 0.0
@@ -244,15 +265,16 @@ def mytimer(func, runs=1):
         times.append(end - start)
         times.append(end - start)
         t = t + end - start
         t = t + end - start
 
 
-    return t/runs, times
-
+    return t / runs, times
 
 
 
 
 def run_benchmark(resolution_list, runs, testdict, profile):
 def run_benchmark(resolution_list, runs, testdict, profile):
     regions = []
     regions = []
     for resolution in resolution_list:
     for resolution in resolution_list:
         core.use_temp_region()
         core.use_temp_region()
-        core.run_command('g.region', e=50, w=-50, n=50, s=-50, res=resolution, flags='p')
+        core.run_command(
+            "g.region", e=50, w=-50, n=50, s=-50, res=resolution, flags="p"
+        )
 
 
         # Adjust the computational region for this process
         # Adjust the computational region for this process
         region = libgis.Cell_head()
         region = libgis.Cell_head()
@@ -273,53 +295,61 @@ def run_benchmark(resolution_list, runs, testdict, profile):
         core.mapcalc("test_a = rand(0, 100)", quite=True, overwrite=True)
         core.mapcalc("test_a = rand(0, 100)", quite=True, overwrite=True)
         core.mapcalc("test_b = rand(0.0, 1.0)", quite=True, overwrite=True)
         core.mapcalc("test_b = rand(0.0, 1.0)", quite=True, overwrite=True)
         result = collections.OrderedDict()
         result = collections.OrderedDict()
-        result['res'] = resolution
-        result['cols'] = region.cols
-        result['rows'] = region.rows
-        result['cells'] = region.rows * region.cols
-        result['results'] = copy.deepcopy(testdict)
-        for execmode, operation in result['results'].items():
+        result["res"] = resolution
+        result["cols"] = region.cols
+        result["rows"] = region.rows
+        result["cells"] = region.rows * region.cols
+        result["results"] = copy.deepcopy(testdict)
+        for execmode, operation in result["results"].items():
             print(execmode)
             print(execmode)
             for oper, operdict in operation.items():
             for oper, operdict in operation.items():
-                operdict['time'], operdict['times'] = mytimer(operdict['func'],runs)
+                operdict["time"], operdict["times"] = mytimer(operdict["func"], runs)
                 if profile:
                 if profile:
-                    filename = '{0}_{1}_{2}'.format(execmode, oper, profile)
-                    cProfile.runctx(operdict['func'].__name__ + '()',
-                                    globals(), locals(), filename = filename)
-                print(('    {0}: {1: 40.6f}s'.format(oper, operdict['time'])))
-                del(operdict['func'])
+                    filename = "{0}_{1}_{2}".format(execmode, oper, profile)
+                    cProfile.runctx(
+                        operdict["func"].__name__ + "()",
+                        globals(),
+                        locals(),
+                        filename=filename,
+                    )
+                print(("    {0}: {1: 40.6f}s".format(oper, operdict["time"])))
+                del operdict["func"]
 
 
         regions.append(result)
         regions.append(result)
         core.del_temp_region()
         core.del_temp_region()
 
 
     return regions
     return regions
 
 
+
 def get_testlist(loc):
 def get_testlist(loc):
-    testlist = [test for test in list(loc.keys()) if 'test' in test[:5]]
+    testlist = [test for test in list(loc.keys()) if "test" in test[:5]]
     testlist.sort()
     testlist.sort()
     return testlist
     return testlist
 
 
+
 def get_testdict(testlist):
 def get_testdict(testlist):
     testdict = collections.OrderedDict()
     testdict = collections.OrderedDict()
     for testfunc in testlist:
     for testfunc in testlist:
-        #import pdb; pdb.set_trace()
-        dummy, execmode, operation = testfunc.split('__')
+        # import pdb; pdb.set_trace()
+        dummy, execmode, operation = testfunc.split("__")
         if execmode in list(testdict.keys()):
         if execmode in list(testdict.keys()):
             testdict[execmode][operation] = collections.OrderedDict()
             testdict[execmode][operation] = collections.OrderedDict()
-            testdict[execmode][operation]['func'] = loc[testfunc]
+            testdict[execmode][operation]["func"] = loc[testfunc]
         else:
         else:
             testdict[execmode] = collections.OrderedDict()
             testdict[execmode] = collections.OrderedDict()
             testdict[execmode][operation] = collections.OrderedDict()
             testdict[execmode][operation] = collections.OrderedDict()
-            testdict[execmode][operation]['func'] = loc[testfunc]
+            testdict[execmode][operation]["func"] = loc[testfunc]
     return testdict
     return testdict
 
 
+
 def print_test(testdict):
 def print_test(testdict):
     for execmode, operation in testdict.items():
     for execmode, operation in testdict.items():
         print(execmode)
         print(execmode)
         for oper, operdict in operation.items():
         for oper, operdict in operation.items():
-            print('    ', oper)
+            print("    ", oper)
             for key, value in operdict.items():
             for key, value in operdict.items():
-                print('        ', key)
+                print("        ", key)
+
 
 
 TXT = """
 TXT = """
 {% for region in regions %}
 {% for region in regions %}
@@ -346,29 +376,31 @@ CSV = """Class; Mode; Operation;
 
 
 RST = """
 RST = """
 """
 """
-#>>> txt = Template(TxT)
-#>>> txt.render(name='John Doe')
+# >>> txt = Template(TxT)
+# >>> txt.render(name='John Doe')
 
 
 
 
 def get_txt(results):
 def get_txt(results):
     txt = Template(TXT)
     txt = Template(TXT)
-    return txt.render(regions = results)
+    return txt.render(regions=results)
 
 
 
 
-#classes for required options
-strREQUIRED = 'required'
+# classes for required options
+strREQUIRED = "required"
+
 
 
 class OptionWithDefault(optparse.Option):
 class OptionWithDefault(optparse.Option):
     ATTRS = optparse.Option.ATTRS + [strREQUIRED]
     ATTRS = optparse.Option.ATTRS + [strREQUIRED]
 
 
     def __init__(self, *opts, **attrs):
     def __init__(self, *opts, **attrs):
         if attrs.get(strREQUIRED, False):
         if attrs.get(strREQUIRED, False):
-            attrs['help'] = '(Required) ' + attrs.get('help', "")
+            attrs["help"] = "(Required) " + attrs.get("help", "")
         optparse.Option.__init__(self, *opts, **attrs)
         optparse.Option.__init__(self, *opts, **attrs)
 
 
+
 class OptionParser(optparse.OptionParser):
 class OptionParser(optparse.OptionParser):
     def __init__(self, **kwargs):
     def __init__(self, **kwargs):
-        kwargs['option_class'] = OptionWithDefault
+        kwargs["option_class"] = OptionWithDefault
         optparse.OptionParser.__init__(self, **kwargs)
         optparse.OptionParser.__init__(self, **kwargs)
 
 
     def check_values(self, values, args):
     def check_values(self, values, args):
@@ -381,59 +413,94 @@ class OptionParser(optparse.OptionParser):
 
 
 def main(testdict):
 def main(testdict):
     """Main function"""
     """Main function"""
-    #usage
+    # usage
     usage = "usage: %prog [options] raster_map"
     usage = "usage: %prog [options] raster_map"
     parser = OptionParser(usage=usage)
     parser = OptionParser(usage=usage)
     # ntime
     # ntime
-    parser.add_option("-n", "--ntimes", dest="ntime",default=5, type="int",
-                      help="Number of run for each test.")
+    parser.add_option(
+        "-n",
+        "--ntimes",
+        dest="ntime",
+        default=5,
+        type="int",
+        help="Number of run for each test.",
+    )
     # res
     # res
-    parser.add_option("-r", "--resolution", action="store", type="string",
-                      dest="res", default = '1,0.25',
-                      help="Resolution list separate by comma.")
+    parser.add_option(
+        "-r",
+        "--resolution",
+        action="store",
+        type="string",
+        dest="res",
+        default="1,0.25",
+        help="Resolution list separate by comma.",
+    )
     # fmt
     # fmt
-    parser.add_option("-f", "--fmt", action="store", type="string",
-                      dest="fmt", default = 'txt',
-                      help="Choose the output format: 'txt', 'csv', 'rst'.")
+    parser.add_option(
+        "-f",
+        "--fmt",
+        action="store",
+        type="string",
+        dest="fmt",
+        default="txt",
+        help="Choose the output format: 'txt', 'csv', 'rst'.",
+    )
 
 
     # output
     # output
-    parser.add_option("-o", "--output", action="store", type="string",
-                      dest="output", help="The output filename.")
+    parser.add_option(
+        "-o",
+        "--output",
+        action="store",
+        type="string",
+        dest="output",
+        help="The output filename.",
+    )
 
 
     # store
     # store
-    parser.add_option("-s", "--store", action="store", type="string",
-                      dest="store", help="The filename of pickle obj.")
+    parser.add_option(
+        "-s",
+        "--store",
+        action="store",
+        type="string",
+        dest="store",
+        help="The filename of pickle obj.",
+    )
 
 
     # profile
     # profile
-    parser.add_option("-p", "--profile", action="store", type="string",
-                      dest="profile", help="The filename of the profile results.")
-
-    #return options and argument
+    parser.add_option(
+        "-p",
+        "--profile",
+        action="store",
+        type="string",
+        dest="profile",
+        help="The filename of the profile results.",
+    )
+
+    # return options and argument
     options, args = parser.parse_args()
     options, args = parser.parse_args()
-    res = [float(r) for r in options.res.split(',')]
-    #res = [1, 0.25, 0.1, 0.05]
+    res = [float(r) for r in options.res.split(",")]
+    # res = [1, 0.25, 0.1, 0.05]
 
 
     results = run_benchmark(res, options.ntime, testdict, options.profile)
     results = run_benchmark(res, options.ntime, testdict, options.profile)
 
 
     if options.store:
     if options.store:
         import pickle
         import pickle
-        output = open(options.store, 'wb')
+
+        output = open(options.store, "wb")
         pickle.dump(results, output)
         pickle.dump(results, output)
         output.close()
         output.close()
-    #import pdb; pdb.set_trace()
+    # import pdb; pdb.set_trace()
     print(get_txt(results))
     print(get_txt(results))
 
 
 
 
-#add options
+# add options
 if __name__ == "__main__":
 if __name__ == "__main__":
-    #import pdb; pdb.set_trace()
+    # import pdb; pdb.set_trace()
     loc = locals()
     loc = locals()
     testlist = get_testlist(loc)
     testlist = get_testlist(loc)
     testdict = get_testdict(testlist)
     testdict = get_testdict(testlist)
-    #print_test(testdict)
-
-
+    # print_test(testdict)
 
 
-    #import pdb; pdb.set_trace()
+    # import pdb; pdb.set_trace()
 
 
     main(testdict)
     main(testdict)

+ 0 - 0
python/grass/pygrass/tests/set_mapset.py


Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio