def test_valid_mapset(self): """Test that different parameter combinations work and return true""" db_path = call_module("g.gisenv", get="GISDBASE").strip() loc_name = call_module("g.gisenv", get="LOCATION_NAME").strip() mapset_name = call_module("g.gisenv", get="MAPSET").strip() self.assertTrue(is_mapset_valid(db_path, loc_name, mapset_name)) self.assertTrue( is_mapset_valid(os.path.join(db_path, loc_name, mapset_name))) self.assertTrue(is_mapset_valid(Path(db_path), loc_name, mapset_name)) self.assertTrue(is_mapset_valid( Path(db_path) / loc_name / mapset_name))
def switch_mapset(self, path, location=None, mapset=None): """Switch to a mapset provided as a name or path. The mapset can be provided as a name, as a path, or as database, location, and mapset. Specifically, the *path* positional-only parameter can be either name of a mapset in the current location or a full path to a mapset. When location and mapset are provided using the additional parameters, the *path* parameter is path to a database. Raises ValueError if the mapset does not exist (e.g., when the name is misspelled or the mapset is invalid). """ # The method could be a function, but this is more general (would work even for # a non-global session). # pylint: disable=no-self-use # Functions needed only here. # pylint: disable=import-outside-toplevel from grass.grassdb.checks import ( get_mapset_invalid_reason, is_mapset_valid, mapset_exists, ) from grass.grassdb.manage import resolve_mapset_path # For only one parameter, try if it is a mapset in the current location to # support switching only by its name. gisenv = gs.gisenv() if ( not location and not mapset and mapset_exists( path=gisenv["GISDBASE"], location=gisenv["LOCATION_NAME"], mapset=path ) ): gs.run_command("g.gisenv", set=f"MAPSET={path}") return mapset_path = resolve_mapset_path(path=path, location=location, mapset=mapset) if not is_mapset_valid(mapset_path): raise ValueError( _("Mapset {path} is not valid: {reason}").format( path=mapset_path.path, reason=get_mapset_invalid_reason( mapset_path.directory, mapset_path.location, mapset_path.mapset ), ) ) # This requires direct session file modification using g.gisenv because # g.mapset locks the mapset which is not how init and finish behave. # For code simplicity, we just change all even when only mapset is changed. gs.run_command("g.gisenv", set=f"GISDBASE={mapset_path.directory}") gs.run_command("g.gisenv", set=f"LOCATION_NAME={mapset_path.location}") gs.run_command("g.gisenv", set=f"MAPSET={mapset_path.mapset}")
def init(path, location=None, mapset=None, grass_path=None): """Initialize system variables to run GRASS modules This function is for running GRASS GIS without starting it with the standard main executable grass. No GRASS modules shall be called before call of this function but any module or user script can be called afterwards because a GRASS session has been set up. GRASS Python libraries are usable as well in general but the ones using C libraries through ``ctypes`` are not (which is caused by library path not being updated for the current process which is a common operating system limitation). When the path or specified mapset does not exist, ValueError is raised. The :func:`get_install_path` function is used to determine where the rest of GRASS files is installed. The *grass_path* parameter is passed to it if provided. If the path cannot be determined, ValueError is raised. Exceptions from the underlying function are propagated. To create a GRASS session a session file (aka gisrc file) is created. Caller is responsible for deleting the file which is normally done with the function :func:`finish`. Basic usage:: # ... setup GISBASE and sys.path before import import grass.script as gs gs.setup.init( "~/grassdata/nc_spm_08/user1", grass_path="/usr/lib/grass", ) # ... use GRASS modules here # end the session gs.setup.finish() :param path: path to GRASS database :param location: location name :param mapset: mapset within given location (default: 'PERMANENT') :param grass_path: path to GRASS installation or executable :returns: path to ``gisrc`` file (may change in future versions) """ grass_path = get_install_path(grass_path) if not grass_path: raise ValueError( _("Parameter grass_path or GISBASE environmental variable must be set") ) # We reduce the top-level imports because this is initialization code. # pylint: disable=import-outside-toplevel from grass.grassdb.checks import get_mapset_invalid_reason, is_mapset_valid from grass.grassdb.manage import resolve_mapset_path # Support ~ in the path for user home directory. path = Path(path).expanduser() # A simple existence test. The directory, whatever it is, should exist. if not path.exists(): raise ValueError(_("Path '{path}' does not exist").format(path=path)) # A specific message when it exists, but it is a file. if path.is_file(): raise ValueError( _("Path '{path}' is a file, but a directory is needed").format(path=path) ) mapset_path = resolve_mapset_path(path=path, location=location, mapset=mapset) if not is_mapset_valid(mapset_path): raise ValueError( _("Mapset {path} is not valid: {reason}").format( path=mapset_path.path, reason=get_mapset_invalid_reason( mapset_path.directory, mapset_path.location, mapset_path.mapset ), ) ) setup_runtime_env(grass_path) # TODO: lock the mapset? os.environ["GIS_LOCK"] = str(os.getpid()) os.environ["GISRC"] = write_gisrc( mapset_path.directory, mapset_path.location, mapset_path.mapset ) return os.environ["GISRC"]
def resolve_mapset_path(path, location=None, mapset=None): """Resolve full path to mapset from given combination of parameters. Full or relative path to mapset can be provided as *path*. If the *path* points to a valid location instead of a valid mapset, the mapset defaults to PERMANENT. Alternatively, location and mapset can be provided separately. In that case, location and mapset are added to the path. If location is provided and mapset is not, mapset defaults to PERMANENT. Home represented by ``~`` (tilde) and relative paths are resolved and the result contains absolute paths. The function does not enforce the existence of the directory or that it is a mapset. It only manipulates the paths except for internal checks which help to determine the result in some cases. On Windows, if the path does not exist and ``..`` is present in the path, it will be not be resolved due to path resolution limitation in the Python pathlib package. Returns a MapsetPath object. """ # We reduce the top-level imports because this is initialization code. # pylint: disable=import-outside-toplevel path = Path(path).expanduser() if not sys.platform.startswith("win") or path.exists(): # The resolve function works just fine on Windows when the path exists # and everywhere even if it does not. # This also resolves symlinks which may or may not be desired. path = path.resolve() else: # On Windows when the path does not exist, resolve does not work. # This does not resolve `..` which is not desired. path = Path.cwd() / path default_mapset = grass.grassdb.config.permanent_mapset if location and mapset: directory = str(path) path = path / location / mapset elif location: mapset = default_mapset directory = str(path) path = path / location / mapset elif mapset: # mapset, but not location raise ValueError( _("Provide only path, or path and location, " "or path, location, and mapset, but not mapset without location") ) else: from grass.grassdb.checks import is_mapset_valid if not is_mapset_valid(path) and is_mapset_valid( path / default_mapset): path = path / default_mapset parts = path.parts if len(parts) < 3: raise ValueError( _("Parameter path needs to be 'path/to/location/mapset' " "or location and mapset need to be set")) directory, location, mapset = split_mapset_path(path) return MapsetPath(path=path, directory=directory, location=location, mapset=mapset)