ソースを参照

Simplify grass init script (#5)

* shutil.rmtree removes directories recursively and has ignore errors, so no need for the wrapper
function/classes

* lib/init/grass: Simplify encode()/decode() functions.

* lib/init/grass: Move all the imports at the beginning of the file.

* lib/init/grass: Simplify clean_env().

"clean_env()" is only being called once. Even if it gets called multiple
times in the future, it is unlikely that it will be used with a different
"gisrc".

* Just keeping globals and the initialization stuff at the beginning of the
module

* lib/init/grass: Group the logging functions together.

* lib/init/grass: Move the info message out of the "show_info()" function.

This was messing up with the vim code folding.

* lib/init/grass: Remove unused constants

* lib/init/grass: Remove old comment about removed nested session test

The comment is from 2015. If there were problems, they would have surfaced
since then.

* lib/init/grass: Move all the gisbase stuff in a single place.

* lib/init/grass: Capitalize all constants

* lib/init/grass: Simplify setting a value to CONFIG_PROJSHARE

* lib/init/grass: Add comments about GISBASE

* lib/init/grass: Validate cmdline in a separate function

* lib/init/grass: Inline variable substitution inside "help_message()"

The main motivation here is to avoid making "_()" calls at the module
level which will hopefully let us remove the early "gettext.install()"
call in a later commit.

* lib/init/grass: Refuse to start if ~/.grass7 exists but is not a directory

* lib/init/grass: Ensure that the init process only happens inside main()

1. we remove the "gettext.install()" call from the module level
2. we make sure that "set_language()" is the first step of the init process.

* lib/init/grass: Use six for cross version Python compatibility

Six is being pulled by matplotlib anyway, but for good measure,
I also added it to the explicit requirements.
pmav99 5 年 前
コミット
2246f5b6ac
3 ファイル変更202 行追加255 行削除
  1. 4 0
      REQUIREMENTS.html
  2. 1 0
      Vagrantfile
  3. 197 255
      lib/init/grass.py

+ 4 - 0
REQUIREMENTS.html

@@ -185,6 +185,10 @@ newer versions are named "python-pillow"
 <a href="https://pypi.python.org/pypi/termcolor">https://pypi.python.org/pypi/termcolor</a>
 </li>
 
+<li><b>six</b> (needed for cross-version Python compatibility)<br>
+<a href="https://pypi.python.org/pypi/six">https://pypi.python.org/pypi/six</a>
+</li>
+
 <li><b>FFMPEG or alternative</b> (for wxGUI Animation tool - g.gui.module),
     specifically ffmpeg tool<br>
 <a href="http://ffmpeg.org">http://ffmpeg.org</a>

+ 1 - 0
Vagrantfile

@@ -71,6 +71,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
       "python-numpy",
       "python-ply",
       "python-pil",
+      "python-six",
       "libnetcdf-dev",
       "netcdf-bin",
       "libblas-dev",

+ 197 - 255
lib/init/grass.py

@@ -40,48 +40,85 @@ is not safe, i.e. it has side effects (this should be changed in the future).
 from __future__ import print_function
 import sys
 import os
+import errno
 import atexit
+import gettext
+import shutil
+import signal
 import string
 import subprocess
+import types
 import re
+import six
 import platform
 import tempfile
 import locale
 
 
-# ----+- Python 3 compatibility start -+----
-PY2 = sys.version[0] == '2'
+# mechanism meant for debugging this script (only)
+# private global to store if we are debugging
+_DEBUG = None
+
+# for wxpath
+_WXPYTHON_BASE = None
+
 ENCODING = locale.getdefaultlocale()[1]
 if ENCODING is None:
     ENCODING = 'UTF-8'
     print("Default locale not found, using UTF-8")  # intentionally not translatable
 
+# The "@...@" variables are being substituted during build process
+#
+# TODO: should GISBASE be renamed to something like GRASS_PATH?
+# GISBASE marks complete runtime, so no need to get it here when
+# setting it up, possible scenario: existing runtime and starting
+# GRASS in that, we want to overwrite the settings, not to take it
+# possibly same for GRASS_PROJSHARE and others but maybe not
+#
+# We need to simultaneously make sure that:
+# - we get GISBASE from os.environ if it is defined (doesn't this mean that we are already
+#   inside a GRASS session? If we are, why do we need to run this script again???).
+# - GISBASE exists as an ENV variable
+#
+# pmav99: Ugly as hell, but that's what the code before the refactoring was doing.
+if 'GISBASE' in os.environ and len(os.getenv('GISBASE')) > 0:
+    GISBASE = os.path.normpath(os.environ["GISBASE"])
+else:
+    GISBASE = os.path.normpath("@GISBASE@")
+    os.environ['GISBASE'] = GISBASE
+CMD_NAME = "@START_UP@"
+GRASS_VERSION = "@GRASS_VERSION_NUMBER@"
+LD_LIBRARY_PATH_VAR = '@LD_LIBRARY_PATH_VAR@'
+CONFIG_PROJSHARE = os.environ.get('GRASS_PROJSHARE', "@CONFIG_PROJSHARE@")
+
+# Get the system name
+WINDOWS = sys.platform == 'win32'
+CYGWIN = "cygwin" in sys.platform
+MACOSX = "darwin" in sys.platform
+
 
-def decode(bytes_, encoding=None):
+def decode(bytes_, encoding=ENCODING):
     """Decode bytes with default locale and return (unicode) string
     Adapted from lib/python/core/utils.py
 
     No-op if parameter is not bytes (assumed unicode string).
 
     :param bytes bytes_: the bytes to decode
-    :param encoding: encoding to be used, default value is None
+    :param encoding: encoding to be used, default value is the system's default
+        encoding or, if that cannot be determined, 'UTF-8'.
     """
     if sys.version_info.major >= 3:
         unicode = str
     if isinstance(bytes_, unicode):
         return bytes_
     elif isinstance(bytes_, bytes):
-        if encoding is None:
-            enc = ENCODING
-        else:
-            enc = encoding
-        return bytes_.decode(enc)
+        return bytes_.decode(encoding)
     else:
         # if something else than text
         raise TypeError("can only accept types str and bytes")
 
 
-def encode(string, encoding=None):
+def encode(string, encoding=ENCODING):
     """Encode string with default locale and return bytes with that encoding
     Adapted from lib/python/core/utils.py
 
@@ -89,7 +126,8 @@ def encode(string, encoding=None):
     This ensures garbage in, garbage out.
 
     :param str string: the string to encode
-    :param encoding: encoding to be used, default value is None
+    :param encoding: encoding to be used, default value is the system's default
+        encoding or, if that cannot be determined, 'UTF-8'.
     """
     if sys.version_info.major >= 3:
         unicode = str
@@ -97,20 +135,16 @@ def encode(string, encoding=None):
         return string
     # this also tests str in Py3:
     elif isinstance(string, unicode):
-        if encoding is None:
-            enc = ENCODING
-        else:
-            enc = encoding
-        return string.encode(enc)
+        return string.encode(encoding)
     else:
         # if something else than text
         raise TypeError("can only accept types str and bytes")
 
 
-# currently not used, see https://trac.osgeo.org/grass/ticket/3508
+# see https://trac.osgeo.org/grass/ticket/3508
 def to_text_string(obj, encoding=ENCODING):
     """Convert `obj` to (unicode) text string"""
-    if PY2:
+    if six.PY2:
         # Python 2
         return encode(obj, encoding=encoding)
     else:
@@ -118,53 +152,6 @@ def to_text_string(obj, encoding=ENCODING):
         return decode(obj, encoding=encoding)
 
 
-if PY2:
-    import types
-    string_types = basestring,
-    integer_types = (int, long)
-    class_types = (type, types.ClassType)
-    text_type = unicode
-    binary_type = str
-else:
-    string_types = str,
-    integer_types = int,
-    class_types = type,
-    text_type = str
-    binary_type = bytes
-    MAXSIZE = sys.maxsize
-
-# ----+- Python 3 compatibility end -+----
-
-# Variables substituted during build process
-if 'GISBASE' in os.environ and len(os.getenv('GISBASE')) > 0:
-    # TODO: should this be something like GRASS_PATH?
-    # GISBASE marks complete runtime, so no need to get it here when
-    # setting it up, possible scenario: existing runtime and starting
-    # GRASS in that, we want to overwrite the settings, not to take it
-    # possibly same for GRASS_PROJSHARE and others but maybe not
-    gisbase = os.environ['GISBASE']
-else:
-    gisbase = "@GISBASE@"
-cmd_name = "@START_UP@"
-grass_version = "@GRASS_VERSION_NUMBER@"
-ld_library_path_var = '@LD_LIBRARY_PATH_VAR@'
-if 'GRASS_PROJSHARE' in os.environ:
-    config_projshare = os.environ['GRASS_PROJSHARE']
-else:
-    config_projshare = "@CONFIG_PROJSHARE@"
-
-gisbase = os.path.normpath(gisbase)
-
-# i18N
-import gettext
-# TODO: is this needed or even desirable when we have set_language()?
-gettext.install('grasslibs', os.path.join(gisbase, 'locale'))
-
-
-def warning(text):
-    sys.stderr.write(_("WARNING") + ': ' + text + os.linesep)
-
-
 def try_remove(path):
     try:
         os.remove(path)
@@ -172,65 +159,17 @@ def try_remove(path):
         pass
 
 
-def try_rmdir(path):
-    try:
-        os.rmdir(path)
-    except:
-        pass
-
-
-def clean_env(gisrc):
+def clean_env():
+    gisrc = os.environ['GISRC']
     env_curr = read_gisrc(gisrc)
     env_new = {}
     for k, v in env_curr.items():
         if k.endswith('PID') or k.startswith('MONITOR'):
             continue
         env_new[k] = v
-
     write_gisrc(env_new, gisrc)
 
 
-def cleanup_dir(path):
-    if not path:
-        return
-
-    for root, dirs, files in os.walk(path, topdown=False):
-        for name in files:
-            try_remove(os.path.join(root, name))
-        for name in dirs:
-            try_rmdir(os.path.join(root, name))
-
-
-class Cleaner(object):  # pylint: disable=R0903
-    """Holds directories and files which needs to be cleaned or deleted"""
-    def __init__(self):
-        self.tmpdir = None
-
-    def cleanup(self):
-        """This can be registered with atexit
-
-        Object can then still change and add or remove directories to clean"""
-        # all exits after setting up tmp dirs (system/location) should
-        # also tidy it up
-        cleanup_dir(self.tmpdir)
-        try_rmdir(self.tmpdir)
-
-
-def fatal(msg):
-    sys.stderr.write("%s: " % _('ERROR') + msg + os.linesep)
-    sys.exit(_("Exiting..."))
-
-
-def message(msg):
-    sys.stderr.write(msg + "\n")
-    sys.stderr.flush()
-
-
-# mechanism meant for debugging this script (only)
-# private global to store if we are debugging
-_DEBUG = None
-
-
 def is_debug():
     """Returns True if we are in debug mode
 
@@ -258,6 +197,20 @@ def debug(msg):
         sys.stderr.flush()
 
 
+def message(msg):
+    sys.stderr.write(msg + "\n")
+    sys.stderr.flush()
+
+
+def warning(text):
+    sys.stderr.write(_("WARNING") + ': ' + text + os.linesep)
+
+
+def fatal(msg):
+    sys.stderr.write("%s: " % _('ERROR') + msg + os.linesep)
+    sys.exit(_("Exiting..."))
+
+
 def readfile(path):
     debug("Reading %s" % path)
     f = open(path, 'r')
@@ -275,14 +228,14 @@ def writefile(path, s):
 
 def call(cmd, **kwargs):
     """Wrapper for subprocess.call to deal with platform-specific issues"""
-    if windows:
+    if WINDOWS:
         kwargs['shell'] = True
     return subprocess.call(cmd, **kwargs)
 
 
 def Popen(cmd, **kwargs):  # pylint: disable=C0103
     """Wrapper for subprocess.Popen to deal with platform-specific issues"""
-    if windows:
+    if WINDOWS:
         kwargs['shell'] = True
     return subprocess.Popen(cmd, **kwargs)
 
@@ -290,33 +243,29 @@ def Popen(cmd, **kwargs):  # pylint: disable=C0103
 def gpath(*args):
     """Costruct path to file or directory in GRASS GIS installation
 
-    Can be called only after gisbase was set.
+    Can be called only after GISBASE was set.
     """
-    return os.path.join(gisbase, *args)
-
-
-# for wxpath
-_WXPYTHON_BASE = None
+    return os.path.join(GISBASE, *args)
 
 
 def wxpath(*args):
     """Costruct path to file or directory in GRASS wxGUI
 
-    Can be called only after gisbase was set.
+    Can be called only after GISBASE was set.
 
     This function does not check if the directories exist or if GUI works
     this must be done by the caller if needed.
     """
     global _WXPYTHON_BASE
     if not _WXPYTHON_BASE:
-        # this can be called only after gisbase was set
+        # this can be called only after GISBASE was set
         _WXPYTHON_BASE = gpath("gui", "wxpython")
     return os.path.join(_WXPYTHON_BASE, *args)
 
 
 # using format for most but leaving usage of template for the dynamic ones
 # two different methods are easy way to implement two phase construction
-help_text = r"""GRASS GIS $VERSION_NUMBER
+HELP_TEXT = r"""GRASS GIS $VERSION_NUMBER
 Geographic Resources Analysis Support System (GRASS GIS).
 
 {usage}:
@@ -365,49 +314,51 @@ Geographic Resources Analysis Support System (GRASS GIS).
   GRASS_ADDON_BASE               {addon_base_var}
   GRASS_BATCH_JOB                {batch_var}
   GRASS_PYTHON                   {python_var}
-""".format(
-    usage=_("Usage"),
-    flags=_("Flags"),
-    help_flag=_("print this help message"),
-    version_flag=_("show version information and exit"),
-    create=_("create given database, location or mapset if it doesn't exist"),
-    exit_after=_("exit after creation of location or mapset. Only with -c flag"),
-    force_removal=_("force removal of .gislock if exists (use with care!). Only with --text flag"),
-    text=_("use text based interface (skip graphical welcome screen)"),
-    text_detail=_("and set as default"),
-    gtext=_("use text based interface (show graphical welcome screen)"),
-    gtext_detail=_("and set as default"),
-    gui=_("use $DEFAULT_GUI graphical user interface"),
-    gui_detail=_("and set as default"),
-    config=_("print GRASS configuration parameters"),
-    config_detail=_("options: arch,build,compiler,path,revision,svn_revision,version"),
-    params=_("Parameters"),
-    gisdbase=_("initial GRASS database directory"),
-    gisdbase_detail=_("directory containing Locations"),
-    location=_("initial GRASS Location"),
-    location_detail=_("directory containing Mapsets with one common coordinate system (projection)"),
-    mapset=_("initial GRASS Mapset"),
-    full_mapset=_("fully qualified initial Mapset directory"),
-    env_vars=_("Environment variables relevant for startup"),
-    gui_var=_("select GUI (text, gui, gtext)"),
-    html_var=_("set html web browser for help pages"),
-    addon_path_var=_("set additional path(s) to local GRASS modules or user scripts"),
-    addon_base_var=_("set additional GISBASE for locally installed GRASS Addons"),
-    batch_var=_("shell script to be processed as batch job"),
-    python_var=_("set Python interpreter name to override 'python'"),
-    exec_=_("execute GRASS module or script"),
-    exec_detail=_("provided executable will be executed in GRASS session"),
-    executable=_("GRASS module, script or any other executable"),
-    executable_params=_("parameters of the executable"),
-    standard_flags=_("standard flags"),
-    tmp_location=_("create temporary location (use with the --exec flag)"),
-    )
+"""
 
 
 def help_message(default_gui):
-    t = string.Template(help_text)
-    s = t.substitute(CMD_NAME=cmd_name, DEFAULT_GUI=default_gui,
-                     VERSION_NUMBER=grass_version)
+    t = string.Template(
+        HELP_TEXT.format(
+            usage=_("Usage"),
+            flags=_("Flags"),
+            help_flag=_("print this help message"),
+            version_flag=_("show version information and exit"),
+            create=_("create given database, location or mapset if it doesn't exist"),
+            exit_after=_("exit after creation of location or mapset. Only with -c flag"),
+            force_removal=_("force removal of .gislock if exists (use with care!). Only with --text flag"),
+            text=_("use text based interface (skip graphical welcome screen)"),
+            text_detail=_("and set as default"),
+            gtext=_("use text based interface (show graphical welcome screen)"),
+            gtext_detail=_("and set as default"),
+            gui=_("use $DEFAULT_GUI graphical user interface"),
+            gui_detail=_("and set as default"),
+            config=_("print GRASS configuration parameters"),
+            config_detail=_("options: arch,build,compiler,path,revision,svn_revision,version"),
+            params=_("Parameters"),
+            gisdbase=_("initial GRASS database directory"),
+            gisdbase_detail=_("directory containing Locations"),
+            location=_("initial GRASS Location"),
+            location_detail=_("directory containing Mapsets with one common coordinate system (projection)"),
+            mapset=_("initial GRASS Mapset"),
+            full_mapset=_("fully qualified initial Mapset directory"),
+            env_vars=_("Environment variables relevant for startup"),
+            gui_var=_("select GUI (text, gui, gtext)"),
+            html_var=_("set html web browser for help pages"),
+            addon_path_var=_("set additional path(s) to local GRASS modules or user scripts"),
+            addon_base_var=_("set additional GISBASE for locally installed GRASS Addons"),
+            batch_var=_("shell script to be processed as batch job"),
+            python_var=_("set Python interpreter name to override 'python'"),
+            exec_=_("execute GRASS module or script"),
+            exec_detail=_("provided executable will be executed in GRASS session"),
+            executable=_("GRASS module, script or any other executable"),
+            executable_params=_("parameters of the executable"),
+            standard_flags=_("standard flags"),
+            tmp_location=_("create temporary location (use with the --exec flag)"),
+        )
+    )
+    s = t.substitute(CMD_NAME=CMD_NAME, DEFAULT_GUI=default_gui,
+                     VERSION_NUMBER=GRASS_VERSION)
     sys.stderr.write(s)
 
 
@@ -434,12 +385,12 @@ def get_grass_config_dir():
     else:
         grass_config_dirname = ".grass7"
         directory = os.path.join(os.getenv('HOME'), grass_config_dirname)
-    if not os.path.exists(directory):
+    if not os.path.isdir(directory) :
         try:
             os.mkdir(directory)
         except OSError as e:
             # Can happen as a race condition
-            if not e.errno == 17:
+            if not e.errno == errno.EEXIST or not os.path.isdir(directory):
                 fatal(
                     _("Failed to create configuration directory '%s' with error: %s")
                     % (directory, e.strerror))
@@ -629,12 +580,12 @@ def set_paths(grass_config_dir):
     if not addon_base:
         addon_base = os.path.join(grass_config_dir, 'addons')
         os.environ['GRASS_ADDON_BASE'] = addon_base
-    if not windows:
+    if not WINDOWS:
         path_prepend(os.path.join(addon_base, 'scripts'), 'PATH')
     path_prepend(os.path.join(addon_base, 'bin'), 'PATH')
 
     # standard installation
-    if not windows:
+    if not WINDOWS:
         path_prepend(gpath('scripts'), 'PATH')
     path_prepend(gpath('bin'), 'PATH')
 
@@ -676,7 +627,7 @@ def set_paths(grass_config_dir):
 
     # Set LD_LIBRARY_PATH (etc) to find GRASS shared libraries
     # this works for subprocesses but won't affect the current process
-    path_prepend(gpath("lib"), ld_library_path_var)
+    path_prepend(gpath("lib"), LD_LIBRARY_PATH_VAR)
 
 
 def find_exe(pgm):
@@ -694,7 +645,7 @@ def set_defaults():
             pager = "more"
         elif find_exe("less"):
             pager = "less"
-        elif windows:
+        elif WINDOWS:
             pager = "more"
         else:
             pager = "cat"
@@ -702,7 +653,7 @@ def set_defaults():
 
     # GRASS_PYTHON
     if not os.getenv('GRASS_PYTHON'):
-        if windows:
+        if WINDOWS:
             os.environ['GRASS_PYTHON'] = "python.exe"
         else:
             os.environ['GRASS_PYTHON'] = "python"
@@ -713,7 +664,7 @@ def set_defaults():
 
     # GRASS_PROJSHARE
     if not os.getenv('GRASS_PROJSHARE'):
-        os.environ['GRASS_PROJSHARE'] = config_projshare
+        os.environ['GRASS_PROJSHARE'] = CONFIG_PROJSHARE
 
 
 def set_display_defaults():
@@ -728,15 +679,15 @@ def set_browser():
     # GRASS_HTML_BROWSER
     browser = os.getenv('GRASS_HTML_BROWSER')
     if not browser:
-        if macosx:
+        if MACOSX:
             # OSX doesn't execute browsers from the shell PATH - route through a
             # script
             browser = gpath('etc', "html_browser_mac.sh")
             os.environ['GRASS_HTML_BROWSER_MACOSX'] = "-b com.apple.helpviewer"
 
-        if windows:
+        if WINDOWS:
             browser = "start"
-        elif cygwin:
+        elif CYGWIN:
             browser = "explorer"
         else:
             # the usual suspects
@@ -748,7 +699,7 @@ def set_browser():
                     browser = b
                     break
 
-    elif macosx:
+    elif MACOSX:
         # OSX doesn't execute browsers from the shell PATH - route through a
         # script
         os.environ['GRASS_HTML_BROWSER_MACOSX'] = "-b %s" % browser
@@ -763,7 +714,7 @@ def set_browser():
 
 def ensure_home():
     """Set HOME if not set on MS Windows"""
-    if windows and not os.getenv('HOME'):
+    if WINDOWS and not os.getenv('HOME'):
         os.environ['HOME'] = os.path.join(os.getenv('HOMEDRIVE'),
                                           os.getenv('HOMEPATH'))
 
@@ -780,7 +731,7 @@ MAPSET: <UNKNOWN>
 def check_gui(expected_gui):
     grass_gui = expected_gui
     # Check if we are running X windows by checking the DISPLAY variable
-    if os.getenv('DISPLAY') or windows or macosx:
+    if os.getenv('DISPLAY') or WINDOWS or MACOSX:
         # Check if python is working properly
         if expected_gui in ('wxpython', 'gtext'):
             nul = open(os.devnull, 'w')
@@ -1139,7 +1090,7 @@ def gui_startup(grass_gui):
                 "Use '--help' for further options\n"
                 "     {cmd_name} --help\n"
                 "See also: https://grass.osgeo.org/{cmd_name}/manuals/helptext.html").format(
-                    cmd_name=cmd_name))
+                    cmd_name=CMD_NAME))
     elif ret == 5:  # defined in gui/wxpython/gis_set.py
         # User wants to exit from GRASS
         message(_("Exit was requested in GUI.\nGRASS GIS will not start. Bye."))
@@ -1193,11 +1144,11 @@ def load_gisrc(gisrc, gisrcrc):
     mapset_settings.mapset = kv.get('MAPSET')
     if not mapset_settings.is_valid():
         fatal(_("Error reading data path information from g.gisenv.\n"
-                "GISDBASE={gisbase}\n"
+                "GISDBASE={gisdbase}\n"
                 "LOCATION_NAME={location}\n"
                 "MAPSET={mapset}\n\n"
                 "Check the <{file}> file.").format(
-                    gisbase=mapset_settings.gisdbase,
+                    gisdbase=mapset_settings.gisdbase,
                     location=mapset_settings.location,
                     mapset=mapset_settings.mapset,
                     file=gisrcrc))
@@ -1481,7 +1432,7 @@ def ensure_db_connected(mapset):
 def get_shell():
     # set SHELL on ms windowns
     # this was at the very beginning of the script but it can be anywhere
-    if windows:
+    if WINDOWS:
         if os.getenv('GRASS_SH'):
             os.environ['SHELL'] = os.getenv('GRASS_SH')
         if not os.getenv('SHELL'):
@@ -1490,7 +1441,7 @@ def get_shell():
     # cygwin has many problems with the shell setup
     # below, so i hardcoded everything here.
     if sys.platform == 'cygwin':
-        sh = "cygwin"
+        sh = "CYGWIN"
         shellname = "GNU Bash (Cygwin)"
         os.environ['SHELL'] = "/usr/bin/bash.exe"
         os.environ['OSTYPE'] = "cygwin"
@@ -1503,7 +1454,7 @@ def get_shell():
             sh = 'sh'
             os.environ['SHELL'] = sh
 
-        if windows and sh:
+        if WINDOWS and sh:
             sh = os.path.splitext(sh)[0]
 
         if sh == "ksh":
@@ -1578,11 +1529,11 @@ def run_batch_job(batch_job):
     :param batch_job: executable and parameters in a list or a string
     """
     batch_job_string = batch_job
-    if not isinstance(batch_job, string_types):
+    if not isinstance(batch_job, six.string_types):
         # for messages only
         batch_job_string = ' '.join(batch_job)
     message(_("Executing <%s> ...") % batch_job_string)
-    if isinstance(batch_job, string_types):
+    if isinstance(batch_job, six.string_types):
         # shell=True is keeping the original GRASS_BATCH_JOB behavior
         def quote(string):
             if '"' in string:
@@ -1622,7 +1573,6 @@ def close_gui():
     env = gcore.gisenv()
     if 'GUI_PID' not in env:
         return
-    import signal
     for pid in env['GUI_PID'].split(','):
         debug("Exiting GUI with pid={0}".format(pid))
         try:
@@ -1645,8 +1595,8 @@ def show_banner():
 
 def say_hello():
     """Write welcome to stderr including Subversion revision if in svn copy"""
-    sys.stderr.write(_("Welcome to GRASS GIS %s") % grass_version)
-    if grass_version.endswith('svn'):
+    sys.stderr.write(_("Welcome to GRASS GIS %s") % GRASS_VERSION)
+    if GRASS_VERSION.endswith('svn'):
         try:
             filerev = open(gpath('etc', 'VERSIONNUMBER'))
             linerev = filerev.readline().rstrip('\n')
@@ -1658,22 +1608,25 @@ def say_hello():
             pass
 
 
-def show_info(shellname, grass_gui, default_gui):
-    """Write basic info about GRASS GIS and GRASS session to stderr"""
-    sys.stderr.write(
-r"""
+INFO_TEXT = r"""\
 %-41shttps://grass.osgeo.org
 %-41s%s (%s)
 %-41sg.manual -i
 %-41sg.version -c
 %-41sg.version -x
-""" % (_("GRASS GIS homepage:"),
+"""
+
+
+def show_info(shellname, grass_gui, default_gui):
+    """Write basic info about GRASS GIS and GRASS session to stderr"""
+    sys.stderr.write(INFO_TEXT % (
+        _("GRASS GIS homepage:"),
         # GTC Running through: SHELL NAME
-       _("This version running through:"),
-       shellname, os.getenv('SHELL'),
-       _("Help is available with the command:"),
-       _("See the licence terms with:"),
-       _("See citation options with:")))
+        _("This version running through:"),
+        shellname, os.getenv('SHELL'),
+        _("Help is available with the command:"),
+        _("See the licence terms with:"),
+        _("See citation options with:")))
 
     if grass_gui == 'wxpython':
         message("%-41sg.gui wxpython" % _("If required, restart the GUI with:"))
@@ -1702,7 +1655,7 @@ def csh_startup(location, location_name, mapset, grass_env_file):
 
     f.write("set prompt = '\\\n")
     f.write("Mapset <%s> in Location <%s> \\\n" % (mapset, location_name))
-    f.write("GRASS GIS %s > '\n" % grass_version)
+    f.write("GRASS GIS %s > '\n" % GRASS_VERSION)
     f.write("set BOGUS=``;unset BOGUS\n")
 
     # csh shell rc file left for backward compatibility
@@ -1758,8 +1711,8 @@ def bash_startup(location, location_name, grass_env_file):
         grass_name = "ISIS-GRASS"
     else:
         grass_name = "GRASS"
-    f.write("PS1='{name} {version} ({location}):\\W > '\n".format(
-        name=grass_name, version=grass_version, location=location_name))
+    f.write("PS1='{name} {version} ({location}):\\w > '\n".format(
+        name=grass_name, version=GRASS_VERSION, location=location_name))
 
     # TODO: have a function and/or module to test this
     mask2d_test = 'test -f "$MAPSET_PATH/cell/MASK"'
@@ -1795,7 +1748,7 @@ PROMPT_COMMAND=grass_prompt\n""".format(
         for line in readfile(env_file).splitlines():
             # Bug related to OS X "SIP", see
             # https://trac.osgeo.org/grass/ticket/3462#comment:13
-            if macosx or not line.startswith('export'):
+            if MACOSX or not line.startswith('export'):
                 f.write(line + '\n')
 
     f.write("export PATH=\"%s\"\n" % os.getenv('PATH'))
@@ -1808,12 +1761,12 @@ PROMPT_COMMAND=grass_prompt\n""".format(
 
 
 def default_startup(location, location_name):
-    if windows:
-        os.environ['PS1'] = "GRASS %s> " % (grass_version)
+    if WINDOWS:
+        os.environ['PS1'] = "GRASS %s> " % (GRASS_VERSION)
         # "$ETC/run" doesn't work at all???
         process = subprocess.Popen([os.getenv('SHELL')])
     else:
-        os.environ['PS1'] = "GRASS %s (%s):\\W > " % (grass_version, location_name)
+        os.environ['PS1'] = "GRASS %s (%s):\\w > " % (GRASS_VERSION, location_name)
         process = Popen([gpath("etc", "run"), os.getenv('SHELL')])
 
     return process
@@ -1842,7 +1795,7 @@ def clean_all():
     clean_temp()
     # save 'last used' GISRC after removing variables which shouldn't
     # be saved, e.g. d.mon related
-    clean_env(os.environ['GISRC'])
+    clean_env()
 
 
 def grep(pattern, lines):
@@ -1870,7 +1823,7 @@ def print_params():
 
     for arg in params:
         if arg == 'path':
-            sys.stdout.write("%s\n" % gisbase)
+            sys.stdout.write("%s\n" % GISBASE)
         elif arg == 'arch':
             val = grep('ARCH', linesplat)
             sys.stdout.write("%s\n" % val[0].split('=')[1].strip())
@@ -1901,14 +1854,14 @@ def print_params():
             except:
                sys.stdout.write("No SVN revision defined\n")
         elif arg == 'version':
-            sys.stdout.write("%s\n" % grass_version)
+            sys.stdout.write("%s\n" % GRASS_VERSION)
         else:
             message(_("Parameter <%s> not supported") % arg)
 
 
 def get_username():
     """Get name of the current user"""
-    if windows:
+    if WINDOWS:
         user = os.getenv('USERNAME')
         if not user:
             user = "user_name"
@@ -1952,7 +1905,7 @@ def parse_cmdline(argv, default_gui):
     for i in argv:
         # Check if the user asked for the version
         if i in ["-v", "--version"]:
-            message("GRASS GIS %s" % grass_version)
+            message("GRASS GIS %s" % GRASS_VERSION)
             message('\n' + readfile(gpath("etc", "license")))
             sys.exit()
         # Check if the user asked for help
@@ -1998,24 +1951,24 @@ def parse_cmdline(argv, default_gui):
     return params
 
 
-# The main script starts here
-
-# Get the system name
-windows = sys.platform == 'win32'
-cygwin = "cygwin" in sys.platform
-macosx = "darwin" in sys.platform
-
-# TODO: it is OK to remove this?
-# at the beginning of this file were are happily getting GISBASE
-# from the environment and we don't care about inconsistencies it might cause
-# The following was commented out because of breaking winGRASS
-# if 'GISBASE' in os.environ:
-#     sys.exit(_("ERROR: GRASS GIS is already running "
-#                "(environmental variable GISBASE found)"))
-# this is not really an issue, we should be able to overpower another session
-
-# Set GISBASE
-os.environ['GISBASE'] = gisbase
+def validate_cmdline(params):
+    """ Validate the cmdline params and exit if necessary. """
+    if params.exit_grass and not params.create_new:
+        fatal(_("Flag -e requires also flag -c"))
+    if params.tmp_location and not params.geofile:
+        fatal(
+            _(
+                "Coordinate reference system argument (e.g. EPSG)"
+                " is needed for --tmp-location"
+            )
+        )
+    if params.tmp_location and params.mapset:
+        fatal(
+            _(
+                "Only one argument (e.g. EPSG) is needed for"
+                " --tmp-location, mapset name <{}> provided"
+            ).format(params.mapset)
+        )
 
 
 def main():
@@ -2023,6 +1976,13 @@ def main():
 
     Only few things are set on the module level.
     """
+    # Set language
+    # This has to be called before any _() function call!
+    # Subsequent functions are using _() calls and
+    # thus must be called only after Language has been set.
+    grass_config_dir = get_grass_config_dir()
+    set_language(grass_config_dir)
+
     # Set default GUI
     default_gui = "wxpython"
 
@@ -2034,14 +1994,12 @@ def main():
 
     # Set GRASS version number for R interface etc
     # (must be an env var for MS Windows)
-    os.environ['GRASS_VERSION'] = grass_version
+    os.environ['GRASS_VERSION'] = GRASS_VERSION
 
     # Set the GIS_LOCK variable to current process id
     gis_lock = str(os.getpid())
     os.environ['GIS_LOCK'] = gis_lock
 
-    grass_config_dir = get_grass_config_dir()
-
     batch_job = get_batch_job_from_env_variable()
 
     # Parse the command-line options and set several global variables
@@ -2054,15 +2012,7 @@ def main():
         params = parse_cmdline(clean_argv, default_gui=default_gui)
     except ValueError:
         params = parse_cmdline(sys.argv[1:], default_gui=default_gui)
-    if params.exit_grass and not params.create_new:
-        fatal(_("Flag -e requires also flag -c"))
-    if params.tmp_location and not params.geofile:
-        fatal(_("Coordinate reference system argument (e.g. EPSG)"
-                " is needed for --tmp-location"))
-    if params.tmp_location and params.mapset:
-        fatal(_("Only one argument (e.g. EPSG) is needed for"
-                " --tmp-location, mapset name <{}> provided").format(
-                    params.mapset))
+    validate_cmdline(params)
     # For now, we allow, but not advertise/document, --tmp-location
     # without --exec (usefulness to be evaluated).
 
@@ -2076,13 +2026,6 @@ def main():
     # Set the username
     user = get_username()
 
-    # TODO: this might need to be moved before processing of parameters
-    # and getting batch job
-    # Set language
-    # This has to be called before any _() function call!
-    # Subsequent functions are using _() calls and
-    # thus must be called only after Language has been set.
-    set_language(grass_config_dir)
 
     # Set shell (needs to be called before load_env())
     sh, shellname = get_shell()
@@ -2095,10 +2038,9 @@ def main():
     # Create the temporary directory and session grassrc file
     tmpdir = create_tmp(user, gis_lock)
 
-    cleaner = Cleaner()
-    cleaner.tmpdir = tmpdir
-    # object is not destroyed when its method is registered
-    atexit.register(cleaner.cleanup)
+    # Remove the tmpdir
+    # The removal will be executed when the python process terminates.
+    atexit.register(lambda: shutil.rmtree(tmpdir, ignore_errors=True))
 
     # Create the session grassrc file
     gisrc = create_gisrc(tmpdir, gisrcrc)
@@ -2130,7 +2072,7 @@ def main():
                     " - Use '--help' for further options\n"
                     "     {cmd_name} --help\n"
                     "See also: https://grass.osgeo.org/{cmd_name}/manuals/helptext.html").format(
-                        cmd_name=cmd_name, gisrcrc=gisrcrc))
+                        cmd_name=CMD_NAME, gisrcrc=gisrcrc))
         create_initial_gisrc(gisrc)
 
     message(_("Starting GRASS GIS..."))