Skip to content

Commit 26dd1f1

Browse files
authored
grass.app: Move lock to mapset subcommand (#6437)
The lock and unlock subcommands are operating on a mapset and are low-level (they specifically just lock and unlock and don't do anything with any session). So, this is moving them under a new top-level subcommand mapset (from #6462). With the lock subcommands being accessible outside of GRASS through CLI without a Python API or a GRASS tools (with or without #6442), other applications which can call subprocesses (such as QGIS) can use GRASS locking when accessing GRASS mapsets. This is using the new structure in the cli.py file with parser definitions having their own functions (minimizing name conflicts between parses and keeping the main function short). Additionally, the interface and execution code are placed together because they would be changed at the same time. Tests now test locking the the default mapset and a separate mapset in a smoke test fashion (more in-depth tests exist in the Python API). Example: grass mapset lock /tmp/project_1/work_2
1 parent 6a0fc7d commit 26dd1f1

File tree

2 files changed

+58
-34
lines changed

2 files changed

+58
-34
lines changed

python/grass/app/cli.py

Lines changed: 41 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,37 @@ def add_mapset_subparser(subparsers):
100100
subparser.add_argument("path", help="path to the new mapset")
101101
subparser.set_defaults(func=subcommand_mapset_create)
102102

103+
subparser = mapset_subparsers.add_parser("lock", help="lock a mapset")
104+
subparser.add_argument("mapset_path", type=str)
105+
subparser.add_argument(
106+
"--process-id",
107+
metavar="PID",
108+
type=int,
109+
default=1,
110+
help=_(
111+
"process ID of the process locking the mapset (a mapset can be "
112+
"automatically unlocked if there is no process with this PID)"
113+
),
114+
)
115+
subparser.add_argument(
116+
"--timeout",
117+
metavar="TIMEOUT",
118+
type=float,
119+
default=30,
120+
help=_("mapset locking timeout in seconds"),
121+
)
122+
subparser.add_argument(
123+
"-f",
124+
"--force-remove-lock",
125+
action="store_true",
126+
help=_("remove lock if present"),
127+
)
128+
subparser.set_defaults(func=subcommand_lock_mapset)
129+
130+
subparser = mapset_subparsers.add_parser("unlock", help="unlock a mapset")
131+
subparser.add_argument("mapset_path", type=str)
132+
subparser.set_defaults(func=subcommand_unlock_mapset)
133+
103134

104135
def subcommand_mapset_create(args) -> int:
105136
try:
@@ -110,7 +141,7 @@ def subcommand_mapset_create(args) -> int:
110141
return 0
111142

112143

113-
def subcommand_lock_mapset(args):
144+
def subcommand_lock_mapset(args) -> int:
114145
gs.setup.setup_runtime_env()
115146
try:
116147
lock_mapset(
@@ -122,10 +153,17 @@ def subcommand_lock_mapset(args):
122153
)
123154
except MapsetLockingException as e:
124155
print(str(e), file=sys.stderr)
156+
return 1
157+
return 0
125158

126159

127-
def subcommand_unlock_mapset(args):
128-
unlock_mapset(args.mapset_path)
160+
def subcommand_unlock_mapset(args) -> int:
161+
try:
162+
unlock_mapset(args.mapset_path)
163+
except OSError as error:
164+
print(str(error), file=sys.stderr)
165+
return 1
166+
return 0
129167

130168

131169
def call_g_manual(**kwargs):
@@ -222,37 +260,6 @@ def main(args=None, program=None):
222260
add_project_subparser(subparsers)
223261
add_mapset_subparser(subparsers)
224262

225-
subparser = subparsers.add_parser("lock", help="lock a mapset")
226-
subparser.add_argument("mapset_path", type=str)
227-
subparser.add_argument(
228-
"--process-id",
229-
metavar="PID",
230-
type=int,
231-
default=1,
232-
help=_(
233-
"process ID of the process locking the mapset (a mapset can be "
234-
"automatically unlocked if there is no process with this PID)"
235-
),
236-
)
237-
subparser.add_argument(
238-
"--timeout",
239-
metavar="TIMEOUT",
240-
type=float,
241-
default=30,
242-
help=_("mapset locking timeout in seconds"),
243-
)
244-
subparser.add_argument(
245-
"-f",
246-
"--force-remove-lock",
247-
action="store_true",
248-
help=_("remove lock if present"),
249-
)
250-
subparser.set_defaults(func=subcommand_lock_mapset)
251-
252-
subparser = subparsers.add_parser("unlock", help="unlock a mapset")
253-
subparser.add_argument("mapset_path", type=str)
254-
subparser.set_defaults(func=subcommand_unlock_mapset)
255-
256263
subparser = subparsers.add_parser(
257264
"help", help="show HTML documentation for a tool or topic"
258265
)

python/grass/app/tests/grass_app_cli_test.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,23 @@ def test_subcommand_run_with_crs_as_pack_subprocess(pack_raster_file4x5_rows, ca
129129
assert json.loads(result.stdout)["srid"] == "EPSG:3358"
130130

131131

132+
def test_create_lock_unlock(tmp_path):
133+
"""Check that we can create, lock, unlock (smoke test)"""
134+
project = tmp_path / "test_1"
135+
assert main(["project", "create", str(project)]) == 0
136+
assert main(["mapset", "lock", str(project / "PERMANENT")]) == 0
137+
assert main(["mapset", "unlock", str(project / "PERMANENT")]) == 0
138+
139+
140+
def test_create_mapset_lock_unlock(tmp_path):
141+
"""Check that we can create, lock, unlock (smoke test)"""
142+
project = tmp_path / "test_1"
143+
assert main(["project", "create", str(project)]) == 0
144+
assert main(["mapset", "create", str(project / "data_1")]) == 0
145+
assert main(["mapset", "lock", str(project / "data_1")]) == 0
146+
assert main(["mapset", "unlock", str(project / "data_1")]) == 0
147+
148+
132149
def test_create_mapset(tmp_path):
133150
"""Check that we can create mapset and we can set its computational region"""
134151
project = tmp_path / "test_1"

0 commit comments

Comments
 (0)