Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions lib/init/grass.py
Original file line number Diff line number Diff line change
Expand Up @@ -2176,12 +2176,20 @@ def main() -> None:
GISBASE, \
CONFIG_PROJSHARE

runtime_paths = RuntimePaths()
runtime_paths = RuntimePaths(set_env_variables=True)
CMD_NAME = runtime_paths.grass_exe_name
GRASS_VERSION = runtime_paths.version
GRASS_VERSION_MAJOR = runtime_paths.version_major
GRASS_VERSION_GIT = runtime_paths.grass_version_git
GISBASE = get_install_path(runtime_paths.gisbase)
gisbase = runtime_paths.gisbase
if not os.path.isdir(gisbase):
gisbase = get_install_path(gisbase)
# Set the main prefix again.
# See also grass.script.setup.setup_runtime_env.
runtime_paths = RuntimePaths(set_env_variables=True, prefix=gisbase)
# Do not trust the value it came up with, and use the one which we determined.
os.environ["GISBASE"] = gisbase
GISBASE = gisbase
CONFIG_PROJSHARE = runtime_paths.config_projshare

grass_config_dir = create_grass_config_dir()
Expand Down
91 changes: 65 additions & 26 deletions python/grass/app/runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import subprocess
import sys

from . import resource_paths as res_paths
from . import resource_paths

# Get the system name
WINDOWS = sys.platform.startswith("win")
Expand All @@ -28,59 +28,101 @@
class RuntimePaths:
"""Get runtime paths to resources and basic GRASS build properties

The resource paths are also set as environmental variables.
The resource paths are accessible as attributes (e.g., `.gisbase`, `.etc_dir`)
and can optionally be exported to environment variables.

Example:

>>> paths = RuntimePaths(set_env_variables=True)
>>> paths.etc_dir
'/usr/lib/grass/etc'
>>> os.environ["GRASS_ETCDIR"]
'/usr/lib/grass/etc'
"""

def __init__(self, env=None):
# Mapping of attribute names to environment variable name except the prefix.
_env_vars = {
"gisbase": "GISBASE",
}

def __init__(self, *, env=None, set_env_variables=False, prefix=None):
if env is None:
env = os.environ
self.env = env
if prefix:
self._custom_prefix = os.path.normpath(prefix)
self._custom_prefix = self._custom_prefix.removesuffix(
resource_paths.GISBASE
)
else:
self._custom_prefix = None
if set_env_variables:
self.set_env_variables()

def set_env_variables(self):
"""Populate all GRASS-related environment variables."""
self.env["GRASS_PREFIX"] = self.prefix
for env_var in self._env_vars.values():
self.env[env_var] = self.__get_dir(env_var, use_env_values=False)

@property
def version(self):
return res_paths.GRASS_VERSION
return resource_paths.GRASS_VERSION

@property
def version_major(self):
return res_paths.GRASS_VERSION_MAJOR
return resource_paths.GRASS_VERSION_MAJOR

@property
def version_minor(self):
return res_paths.GRASS_VERSION_MINOR
return resource_paths.GRASS_VERSION_MINOR

@property
def ld_library_path_var(self):
return res_paths.LD_LIBRARY_PATH_VAR
return resource_paths.LD_LIBRARY_PATH_VAR

@property
def grass_exe_name(self):
return res_paths.GRASS_EXE_NAME
return resource_paths.GRASS_EXE_NAME

@property
def grass_version_git(self):
return res_paths.GRASS_VERSION_GIT
return resource_paths.GRASS_VERSION_GIT

@property
def gisbase(self):
return self.__get_dir("GISBASE")
def config_projshare(self):
return self.env.get("GRASS_PROJSHARE", resource_paths.CONFIG_PROJSHARE)

@property
def prefix(self):
return self.__get_dir("GRASS_PREFIX")

@property
def config_projshare(self):
return self.env.get("GRASS_PROJSHARE", res_paths.CONFIG_PROJSHARE)

def __get_dir(self, env_var):
if self._custom_prefix:
return self._custom_prefix
return os.path.normpath(resource_paths.GRASS_PREFIX)

def __getattr__(self, name):
"""Access paths by attributes."""
if name in self._env_vars:
env_var = self._env_vars[name]
return self.__get_dir(env_var)
msg = f"{type(self).__name__!r} has no attribute {name!r}"
raise AttributeError(msg)

def __dir__(self):
"""List both static and dynamic attributes."""
base_dir = set(super().__dir__())
dynamic_dir = set(self._env_vars.keys())
return sorted(base_dir | dynamic_dir)

def __get_dir(self, env_var, *, use_env_values=True):
"""Get the directory stored in the environmental variable 'env_var'

If the environmental variable not yet set, it is retrived and
If the environmental variable not yet set, it is retrieved and
set from resource_paths."""
if env_var in self.env and len(self.env[env_var]) > 0:
if use_env_values and env_var in self.env and self.env[env_var]:
return os.path.normpath(self.env[env_var])
path = getattr(res_paths, env_var)
return os.path.normpath(os.path.join(res_paths.GRASS_PREFIX, path))
# Default to path from the installation
path = getattr(resource_paths, env_var)
return os.path.normpath(os.path.join(self.prefix, path))


def get_grass_config_dir(*, env):
Expand Down Expand Up @@ -184,16 +226,13 @@ def set_executable_paths(install_path, grass_config_dir, env):

def set_paths(install_path, grass_config_dir):
"""Set variables with executable paths, library paths, and other paths"""
# Set main prefix.
# See also grass.script.setup.setup_runtime_env.
os.environ["GISBASE"] = install_path
set_executable_paths(
install_path=install_path, grass_config_dir=grass_config_dir, env=os.environ
)
# Set LD_LIBRARY_PATH (etc) to find GRASS shared libraries.
# This works for subprocesses, but won't affect the current process.
set_dynamic_library_path(
variable_name=res_paths.LD_LIBRARY_PATH_VAR,
variable_name=resource_paths.LD_LIBRARY_PATH_VAR,
install_path=install_path,
env=os.environ,
)
Expand Down
41 changes: 41 additions & 0 deletions python/grass/app/tests/grass_app_resource_paths_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import os

import pytest

from grass.app import resource_paths


# This is just a sample, not trying to test all.
@pytest.mark.parametrize(
"name",
[
"GRASS_VERSION",
"GRASS_VERSION_MAJOR",
"LD_LIBRARY_PATH_VAR",
"GRASS_VERSION_GIT",
],
)
def test_non_path_values_substituted(name):
value = getattr(resource_paths, name)
assert not (value.startswith("@") and value.endswith("@"))


# This is just a sample, not trying to test all.
@pytest.mark.parametrize("name", ["GRASS_PREFIX", "GISBASE"])
def test_path_values_substituted(name):
value = getattr(resource_paths, name)
assert not (value.startswith("@") and value.endswith("@"))


# GISBASE may be empty after build because everything goes to prefix.
@pytest.mark.parametrize("name", ["GRASS_PREFIX"])
def test_value_not_empty(name):
value = getattr(resource_paths, name)
assert value


@pytest.mark.parametrize("name", ["GRASS_PREFIX"])
def test_value_is_directory(name):
value = getattr(resource_paths, name)
assert os.path.exists(value)
assert os.path.isdir(value)
83 changes: 0 additions & 83 deletions python/grass/app/tests/grass_app_runtime.py

This file was deleted.

Loading
Loading