manage.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. """
  2. Managing existing objects in a GRASS GIS Spatial Database
  3. (C) 2020 by the GRASS Development Team
  4. This program is free software under the GNU General Public
  5. License (>=v2). Read the file COPYING that comes with GRASS
  6. for details.
  7. .. sectionauthor:: Vaclav Petras <wenzeslaus gmail com>
  8. """
  9. import os
  10. import shutil
  11. import sys
  12. from pathlib import Path
  13. import grass.grassdb.config
  14. def delete_mapset(database, location, mapset):
  15. """Deletes a specified mapset"""
  16. if mapset == "PERMANENT":
  17. raise ValueError(
  18. _("Mapset PERMANENT cannot be deleted (a whole location can be)")
  19. )
  20. shutil.rmtree(os.path.join(database, location, mapset))
  21. def delete_location(database, location):
  22. """Deletes a specified location"""
  23. shutil.rmtree(os.path.join(database, location))
  24. def delete_grassdb(database):
  25. """Deletes a specified GRASS database"""
  26. shutil.rmtree(database)
  27. def rename_mapset(database, location, old_name, new_name):
  28. """Rename mapset from *old_name* to *new_name*"""
  29. if old_name == "PERMANENT":
  30. raise ValueError(_("Mapset PERMANENT cannot be renamed"))
  31. location_path = os.path.join(database, location)
  32. os.rename(
  33. os.path.join(location_path, old_name), os.path.join(location_path, new_name)
  34. )
  35. def rename_location(database, old_name, new_name):
  36. """Rename location from *old_name* to *new_name*"""
  37. os.rename(os.path.join(database, old_name), os.path.join(database, new_name))
  38. class MapsetPath:
  39. """This is a representation of a path to mapset.
  40. Individual components are accessible through read-only properties
  41. and objects have an os.PathLike interface.
  42. Paths are currently stored as is (not resolved, not expanded),
  43. but that may change in the future.
  44. """
  45. def __init__(self, path, directory, location, mapset):
  46. # Path as an attribute. Inheriting from Path would be something to consider
  47. # here, however the Path inheritance is somewhat complex at this point.
  48. self._path = Path(path)
  49. self._directory = str(directory)
  50. self._location = location
  51. self._mapset = mapset
  52. def __repr__(self):
  53. return (
  54. f"{self.__class__.__name__}("
  55. f"{self._path!r}, "
  56. f"{self._directory!r}, {self._location!r}, {self._mapset!r})"
  57. )
  58. def __str__(self):
  59. return str(self._path)
  60. def __fspath__(self):
  61. return os.fspath(self._path)
  62. @property
  63. def path(self):
  64. """Full path to the mapset as a pathlib.Path object"""
  65. return self._path
  66. @property
  67. def directory(self):
  68. """Location name"""
  69. return self._directory
  70. @property
  71. def location(self):
  72. """Location name"""
  73. return self._location
  74. @property
  75. def mapset(self):
  76. """Mapset name"""
  77. return self._mapset
  78. def split_mapset_path(mapset_path):
  79. """Split mapset path to three parts - grassdb, location, mapset"""
  80. mapset_path = Path(mapset_path)
  81. if len(mapset_path.parts) < 3:
  82. ValueError(
  83. _("Mapset path '{}' needs at least three components").format(mapset_path)
  84. )
  85. mapset = mapset_path.name
  86. location_path = mapset_path.parent
  87. location = location_path.name
  88. grassdb = location_path.parent
  89. return os.fspath(grassdb), location, mapset
  90. def resolve_mapset_path(path, location=None, mapset=None):
  91. """Resolve full path to mapset from given combination of parameters.
  92. Full or relative path to mapset can be provided as *path*. If the *path*
  93. points to a valid location instead of a valid mapset, the mapset defaults
  94. to PERMANENT.
  95. Alternatively, location and mapset can be provided separately. In that case,
  96. location and mapset are added to the path. If location is provided and mapset
  97. is not, mapset defaults to PERMANENT.
  98. Home represented by ``~`` (tilde) and relative paths are resolved
  99. and the result contains absolute paths.
  100. The function does not enforce the existence of the directory or that it
  101. is a mapset. It only manipulates the paths except for internal checks
  102. which help to determine the result in some cases. On Windows, if the path
  103. does not exist and ``..`` is present in the path, it will be not be resolved
  104. due to path resolution limitation in the Python pathlib package.
  105. Returns a MapsetPath object.
  106. """
  107. # We reduce the top-level imports because this is initialization code.
  108. # pylint: disable=import-outside-toplevel
  109. path = Path(path).expanduser()
  110. if not sys.platform.startswith("win") or path.exists():
  111. # The resolve function works just fine on Windows when the path exists
  112. # and everywhere even if it does not.
  113. # This also resolves symlinks which may or may not be desired.
  114. path = path.resolve()
  115. else:
  116. # On Windows when the path does not exist, resolve does not work.
  117. # This does not resolve `..` which is not desired.
  118. path = Path.cwd() / path
  119. default_mapset = grass.grassdb.config.permanent_mapset
  120. if location and mapset:
  121. directory = str(path)
  122. path = path / location / mapset
  123. elif location:
  124. mapset = default_mapset
  125. directory = str(path)
  126. path = path / location / mapset
  127. elif mapset:
  128. # mapset, but not location
  129. raise ValueError(
  130. _(
  131. "Provide only path, or path and location, "
  132. "or path, location, and mapset, but not mapset without location"
  133. )
  134. )
  135. else:
  136. from grass.grassdb.checks import is_mapset_valid
  137. if not is_mapset_valid(path) and is_mapset_valid(path / default_mapset):
  138. path = path / default_mapset
  139. parts = path.parts
  140. if len(parts) < 3:
  141. raise ValueError(
  142. _(
  143. "Parameter path needs to be 'path/to/location/mapset' "
  144. "or location and mapset need to be set"
  145. )
  146. )
  147. directory, location, mapset = split_mapset_path(path)
  148. return MapsetPath(path=path, directory=directory, location=location, mapset=mapset)