Source code for grass.experimental.mapset

"Session or subsession for mapsets (subprojects)"

import shutil
import os
from pathlib import Path

import grass.script as gs
from grass.experimental.create import (
    require_create_ensure_mapset,
    create_temporary_mapset,
)


[docs]class MapsetSession: """Session in another mapset in the same location By default, it assumes that the mapset exists and raises ValueError otherwise. Use *create* to create a new mapset and add *overwrite* to delete an existing one of the same name (with all the data in it) before the new one is created. To use an existing mapset if it exist and create it if it doesn't exist, use *ensure*. Note that *ensure* will not create a new mapset if the current is invalid. Invalid mapset may mean corrupt data, so it is not clear what to do. Using create with overwrite will work on an invalid mapset because the existing mapset is always deleted with overwrite enabled. Standard use of the object is to use it as a context manager, i.e., create it using the ``with`` statement. Then use its *env* property to pass the environment variables for the session to subprocesses: >>> with MapsetSession(name, ensure=True) as session: ... run_command("r.surf.fractal", output="surface", env=session.env) This session expects an existing GRASS runtime environment. The name argument is positional-only. .. versionadded:: 8.4 """ def __init__(self, name, *, create=False, overwrite=False, ensure=False, env=None): """Starts the session and creates the mapset if requested""" self._name = name self._env = env self._session_file = None self._active = False self._start(create=create, overwrite=overwrite, ensure=ensure) @property def active(self): """True if session is active (i.e., not finished)""" return self._active @property def env(self): """Mapping object with environment variables This is suitable for subprocess which should run this mapset. """ return self._env @property def name(self): """Mapset name""" return self._name def _start(self, create, overwrite, ensure): """Start the session and create the mapset if requested""" gis_env = gs.gisenv(env=self._env) require_create_ensure_mapset( gis_env["GISDBASE"], gis_env["LOCATION_NAME"], self._name, create=create, overwrite=overwrite, ensure=ensure, ) self._session_file, self._env = gs.create_environment( gis_env["GISDBASE"], gis_env["LOCATION_NAME"], self._name, env=self._env, ) self._active = True
[docs] def finish(self): """Finish the session. If not used as a context manager, call explicitly to clean and close the mapset and finish the session. No GRASS modules can be called afterwards with the environment obtained from this object. """ if not self.active: raise ValueError("Attempt to finish an already finished session") os.remove(self._session_file) self._active = False
def __enter__(self): """Enter the context manager context. Notably, the session is activated in its *__init__* function. :returns: reference to the object (self) """ if not self.active: raise ValueError( "Attempt to use inactive (finished) session as a context manager" ) return self def __exit__(self, type, value, traceback): # pylint: disable=redefined-builtin """Exit the context manager context. Finishes the existing session. """ self.finish()
[docs]class TemporaryMapsetSession: """Session in another mapset in the same location By default, it assumes that the mapset exists and raises ValueError otherwise. Use *create* to create a new mapset and add *overwrite* to delete an existing one of the same name (with all the data in it) before the new one is created. To use an existing mapset if it exist and create it if it doesn't exist, use *ensure*. Note that *ensure* will not create a new mapset if the current is invalid. Invalid mapset may mean corrupt data, so it is not clear what to do. Using create with overwrite will work on an invalid mapset because the existing mapset is always deleted with overwrite enabled. Standard use of the object is to use it as a context manager, i.e., create it using the ``with`` statement. Then use its *env* property to pass the environment variables for the session to subprocesses: >>> with MapsetSession(name, ensure=True) as session: ... run_command("r.surf.fractal", output="surface", env=session.env) The name argument is positional-only. .. versionadded:: 8.4 """ def __init__(self, *, location=None, env=None): """Starts the session and creates the mapset if requested""" if location: # Simple resolution of location name versus path to location. # Assumes anything which is not a directory (existing files, # non-existing paths) are names. Existing directory with the corresponding # name works only if GISDBASE is the current directory. # Resolving mapsets handled in jupyter.Session.switch_mapset and # resolve_mapset_path functions. self._location_path = Path(location) if not self._location_path.is_dir(): gis_env = gs.gisenv(env=env) self._location_path = Path(gis_env["GISDBASE"]) / location else: gis_env = gs.gisenv(env=env) self._location_path = Path(gis_env["GISDBASE"]) / gis_env["LOCATION_NAME"] self._name = None self._path = None self._session_file = None self._active = False self._env = None self._start(env=env) @property def active(self): """True if session is active (i.e., not finished)""" return self._active @property def env(self): """Mapping object with environment variables This is suitable for subprocess which should run this mapset. """ # This could be a copy to be read-only, but # that may be too much overhead with env=session.env usage. return self._env @property def name(self): """Mapset name""" return self._name @property def mapset_path(self): """MapsetPath""" return self._path def _start(self, env): """Start the session and create the mapset if requested""" self._path = create_temporary_mapset(self._location_path) self._name = self._path.mapset self._session_file, self._env = gs.create_environment( self._location_path.parent, self._location_path.name, self._name, env=env, ) self._active = True
[docs] def finish(self): """Finish the session. If not used as a context manager, call explicitly to clean and close the mapset and finish the session. No GRASS modules can be called afterwards with the environment obtained from this object. """ if not self.active: raise ValueError("Attempt to finish an already finished session") self._active = False os.remove(self._session_file) shutil.rmtree(self._path.path, ignore_errors=True)
def __enter__(self): """Enter the context manager context. Notably, the session is activated in its *__init__* function. :returns: reference to the object (self) """ if not self.active: raise ValueError( "Attempt to use inactive (finished) session as a context manager" ) return self def __exit__(self, type, value, traceback): # pylint: disable=redefined-builtin """Exit the context manager context. Finishes the existing session. """ self.finish()
def _test(): """Quick tests of mapset session usage. The file should run outside of an existing session, but the grass package needs to be on path. """ with gs.setup.init("~/grassdata/nc_spm_08_grass7"): with TemporaryMapsetSession() as session: gs.run_command("g.region", res=10, rows=100, cols=200, env=session.env) gs.run_command( "r.surf.random", output="uniform_random", min=1, max=10, env=session.env ) print( gs.parse_command( "r.univar", map="uniform_random", flags="g", env=session.env )["max"] ) with MapsetSession("user1", ensure=True) as session: gs.run_command("g.region", raster="elevation", env=session.env) print( gs.parse_command( "r.univar", map="elevation", flags="g", env=session.env )["range"] ) gs.run_command("g.mapsets", flags="l") if __name__ == "__main__": _test()