lid-cli 0.2__tar.gz → 0.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
lid_cli-0.4/PKG-INFO ADDED
@@ -0,0 +1,54 @@
1
+ Metadata-Version: 2.4
2
+ Name: lid-cli
3
+ Version: 0.4
4
+ Summary: gio-based volume mounting and unmounting tool.
5
+ Author: Christian Heinze
6
+ License-Expression: MIT
7
+ License-File: LICENSES/MIT.txt
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Environment :: Console
10
+ Classifier: Intended Audience :: End Users/Desktop
11
+ Classifier: Operating System :: POSIX :: Linux
12
+ Classifier: Programming Language :: Python :: 3 :: Only
13
+ Classifier: Programming Language :: Python :: 3.14
14
+ Classifier: Topic :: Utilities
15
+ Classifier: Typing :: Typed
16
+ Requires-Dist: msgspec>=0.21
17
+ Requires-Dist: rich>=14.3
18
+ Requires-Dist: typer>=0.24
19
+ Requires-Python: >=3.14
20
+ Project-URL: Repository, https://codeberg.org/christianheinze/lid-cli
21
+ Description-Content-Type: text/markdown
22
+
23
+ # `gio`-based volume handling tool `lid`
24
+
25
+ ## (Un)Install
26
+
27
+ To install, run
28
+
29
+ ```bash
30
+ uv tool install lid-cli
31
+ ```
32
+
33
+ Then run
34
+
35
+ ```bash
36
+ yd --install-completion
37
+ ```
38
+
39
+ to install auto-completion in your shell.
40
+
41
+ In `bash`, the completion code is stored in `~/.bash_completions/lid.sh` and that file is sourced from `~/.bashrc`.
42
+ Remove both and call `uv tool uninstall lid-cli` to remove this tool.
43
+
44
+ ## (Un)Mounting
45
+
46
+ Currently, `lid` allows to (un)mount volumes using `gio`.
47
+
48
+ To find available volumes, run `lid ls` (add the `--mounted` option to restrict to mounted volumes).
49
+
50
+ Use `lid mount NAME` to mount a volume wherein name is the name displayed by `lid ls`.
51
+
52
+ Use `lid umount NAME` to unmount. `lid` will only unmount what it has itself mounted.
53
+ This allows to sandwich another action between a `lid mount` and a `lid umount` call to have the volume mounted during the action and retain the initial state afterwards
54
+ (if the volume was mounted before calling `lid mount` it is still mounted after `lid umount`).
lid_cli-0.4/README.md ADDED
@@ -0,0 +1,32 @@
1
+ # `gio`-based volume handling tool `lid`
2
+
3
+ ## (Un)Install
4
+
5
+ To install, run
6
+
7
+ ```bash
8
+ uv tool install lid-cli
9
+ ```
10
+
11
+ Then run
12
+
13
+ ```bash
14
+ yd --install-completion
15
+ ```
16
+
17
+ to install auto-completion in your shell.
18
+
19
+ In `bash`, the completion code is stored in `~/.bash_completions/lid.sh` and that file is sourced from `~/.bashrc`.
20
+ Remove both and call `uv tool uninstall lid-cli` to remove this tool.
21
+
22
+ ## (Un)Mounting
23
+
24
+ Currently, `lid` allows to (un)mount volumes using `gio`.
25
+
26
+ To find available volumes, run `lid ls` (add the `--mounted` option to restrict to mounted volumes).
27
+
28
+ Use `lid mount NAME` to mount a volume wherein name is the name displayed by `lid ls`.
29
+
30
+ Use `lid umount NAME` to unmount. `lid` will only unmount what it has itself mounted.
31
+ This allows to sandwich another action between a `lid mount` and a `lid umount` call to have the volume mounted during the action and retain the initial state afterwards
32
+ (if the volume was mounted before calling `lid mount` it is still mounted after `lid umount`).
@@ -4,8 +4,8 @@ requires = [ "uv-build>=0.10,<0.11" ]
4
4
 
5
5
  [project]
6
6
  name = "lid-cli"
7
- version = "0.2"
8
- description = "Project Lid Cli."
7
+ version = "0.4"
8
+ description = "gio-based volume mounting and unmounting tool."
9
9
  readme = "README.md"
10
10
  license = "MIT"
11
11
  license-files = [ "LICENSES/MIT.txt" ]
@@ -1,7 +1,7 @@
1
1
  # SPDX-FileCopyrightText: Christian Heinze
2
2
  #
3
3
  # SPDX-License-Identifier: MIT
4
- """lid-cli."""
4
+ """lid CLI tool."""
5
5
 
6
6
  from __future__ import annotations
7
7
 
@@ -11,6 +11,7 @@ import pathlib as pl
11
11
  from typing import Annotated
12
12
 
13
13
  import rich.console
14
+ import rich.table
14
15
  import rich.theme
15
16
  import typer
16
17
 
@@ -22,7 +23,7 @@ env: lid.types.Environment
22
23
  console: rich.console.Console
23
24
 
24
25
  _HELP = """
25
- Resource activation and communication helper.
26
+ `gio`-based volume (un)mounter.
26
27
  """
27
28
 
28
29
  app = typer.Typer(help=_HELP, no_args_is_help=True, rich_markup_mode="markdown")
@@ -90,11 +91,14 @@ def _complete_mount_kw(incomplete: str) -> list[str]:
90
91
  import asyncio
91
92
 
92
93
  env = lid.io.capture_environment()
93
- return asyncio.run(
94
- lid.gio.find_device_names(
95
- incomplete, mounted_only=False, env=env, runner=lid.subprocess.run
94
+ try:
95
+ return asyncio.run(
96
+ lid.gio.find_volume_names(
97
+ incomplete, mounted_only=False, env=env, runner=lid.subprocess.run
98
+ )
96
99
  )
97
- )
100
+ except lid.types.GioError:
101
+ return []
98
102
 
99
103
 
100
104
  def _require_nonempty(param: typer.CallbackParam, v: str | None) -> str | None:
@@ -103,12 +107,13 @@ def _require_nonempty(param: typer.CallbackParam, v: str | None) -> str | None:
103
107
  raise typer.BadParameter(f"parameter `{param.name}` must not be an empty string")
104
108
 
105
109
 
106
- @app.command()
110
+ @app.command(help="Mount volume.")
107
111
  def mount(
108
112
  kw: Annotated[
109
113
  str,
110
114
  typer.Argument(
111
- help="Device keyword.",
115
+ help="Volume name as displayed by `lid ls` (partial names"
116
+ " are sufficient as long as they uniquely identify a single volume name).",
112
117
  autocompletion=_complete_mount_kw,
113
118
  callback=_require_nonempty,
114
119
  ),
@@ -124,8 +129,8 @@ def mount(
124
129
  mt = runner.run(lid.gio.mount(kw, env=env, runner=lid.subprocess.run))
125
130
  except lid.types.GioNotFoundError as err:
126
131
  log.exception("Failed to mount for keyword `%s`.", kw)
127
- raise typer.BadParameter(f"Device not determined: {err}.") from None
128
- except lid.types.GioMountError as err:
132
+ raise typer.BadParameter(f"Volume not determined: {err}.") from None
133
+ except lid.types.GioError as err:
129
134
  log.exception("Failed to mount for keyword `%s`.", kw)
130
135
  console.print(f"Mount failure: {err}.", style="error")
131
136
  raise typer.Exit(2) from None
@@ -138,11 +143,13 @@ def mount(
138
143
  env.runtime_dir.joinpath(str(uuid.uuid4())).write_bytes(mt_info)
139
144
  except OSError, PermissionError:
140
145
  log.exception("Failed to create mount marker file.")
146
+ log.debug("Attempting unmount.")
141
147
  try:
142
148
  runner.run(
143
149
  lid.gio.umount(mount=mt, env=env, runner=lid.subprocess.run)
144
150
  )
145
- except lid.types.GioMountError as err:
151
+ except lid.types.GioError as err:
152
+ log.exception("Unmounting failed.")
146
153
  console.print(
147
154
  f"Failed to create marker file, then failed to umount: {err}.",
148
155
  style="error",
@@ -154,26 +161,80 @@ def mount(
154
161
  return 0
155
162
 
156
163
 
164
+ @app.command("ls", help="List available volumes.")
165
+ def list_volumes(
166
+ mounted: Annotated[
167
+ bool,
168
+ typer.Option(
169
+ "--mounted",
170
+ help="Only list mounted volumes.",
171
+ ),
172
+ ] = False,
173
+ ) -> int:
174
+ import asyncio
175
+
176
+ try:
177
+ names = asyncio.run(
178
+ lid.gio.find_volume_names(
179
+ "", mounted_only=mounted, env=env, runner=lid.subprocess.run
180
+ )
181
+ )
182
+ except lid.types.GioError as err:
183
+ log.exception("Failed to list volumes.")
184
+ console.print(f"Volume discovery failure: {err}.", style="error")
185
+ raise typer.Exit(2) from None
186
+
187
+ if not names:
188
+ console.print("No volumes found.", style="warning")
189
+ return 0
190
+
191
+ table = rich.table.Table(title="Volumes")
192
+ table.add_column("Name", overflow="fold")
193
+ for name in names:
194
+ table.add_row(name)
195
+ console.print(table)
196
+ return 0
197
+
198
+
157
199
  def _complete_umount_kw(incomplete: str) -> list[str]:
158
200
  import asyncio
159
201
 
160
202
  env = lid.io.capture_environment()
161
- return asyncio.run(
162
- lid.gio.find_device_names(
163
- incomplete, mounted_only=True, env=env, runner=lid.subprocess.run
203
+ try:
204
+ return asyncio.run(
205
+ lid.gio.find_volume_names(
206
+ incomplete, mounted_only=True, env=env, runner=lid.subprocess.run
207
+ )
164
208
  )
165
- )
209
+ except lid.types.GioError:
210
+ return []
166
211
 
167
212
 
168
213
  async def _umount(kw: str, /, env: lid.types.Environment) -> int:
169
214
  import asyncio
170
215
 
171
- names, markers = await asyncio.gather(
172
- lid.gio.find_device_names(
173
- kw, mounted_only=True, env=env, runner=lid.subprocess.run
174
- ),
175
- asyncio.to_thread(lid.io.read_mount_markers, env),
176
- )
216
+ async with asyncio.TaskGroup() as tg:
217
+ names_task = tg.create_task(
218
+ lid.gio.find_volume_names(
219
+ kw, mounted_only=True, env=env, runner=lid.subprocess.run
220
+ )
221
+ )
222
+ markers_task = tg.create_task(
223
+ asyncio.to_thread(lid.io.read_mount_markers, env),
224
+ )
225
+ try:
226
+ names, markers = await names_task, await markers_task
227
+ except* (OSError, PermissionError) as err:
228
+ log.exception(
229
+ "Failed to access mount marker files in `%s`.", env.runtime_dir
230
+ )
231
+ console.print("Failed to access marker files.", style="error")
232
+ raise typer.Exit(2) from err
233
+ except* lid.types.GioError as err:
234
+ log.exception("Failed to gather volume information for keyword `%s`.", kw)
235
+ console.print(f"Unmount failure: {err}.", style="error")
236
+ raise typer.Exit(2) from err
237
+
177
238
  match [(p, m) for n, (p, m) in it.product(names, markers) if n == m.name]:
178
239
  case []:
179
240
  log.info("Found no matching marker file. Exiting.")
@@ -181,10 +242,7 @@ async def _umount(kw: str, /, env: lid.types.Environment) -> int:
181
242
  case [[pl.Path() as f, lid.types.Mount() as mt]]:
182
243
  try:
183
244
  await lid.gio.umount(mount=mt, env=env, runner=lid.subprocess.run)
184
- except lid.types.GioNotFoundError as err:
185
- log.exception("Failed to mount for keyword `%s` (match: `%s`.", kw, mt)
186
- raise typer.BadParameter(f"Device not determined: {err}.") from None
187
- except lid.types.GioMountError as err:
245
+ except lid.types.GioError as err:
188
246
  log.exception(
189
247
  "Failed to unmount for keyword `%s` (match: `%s`).", kw, mt
190
248
  )
@@ -218,7 +276,8 @@ def umount(
218
276
  kw: Annotated[
219
277
  str,
220
278
  typer.Argument(
221
- help="Device keyword.",
279
+ help="Volume name as displayed by `lid ls` (partial names"
280
+ " are sufficient as long as they uniquely identify a single volume name).",
222
281
  autocompletion=_complete_umount_kw,
223
282
  callback=_require_nonempty,
224
283
  ),
@@ -38,54 +38,54 @@ if TYPE_CHECKING:
38
38
  ) -> None: ...
39
39
 
40
40
 
41
- __all__ = ["find_device_names", "mount", "umount"]
41
+ __all__ = ["find_volume_names", "mount", "umount"]
42
42
 
43
43
  log = logging.getLogger(__name__)
44
44
 
45
45
 
46
46
  # Return type dictated by use a completion suggester.
47
- async def find_device_names(
47
+ async def find_volume_names(
48
48
  kw: str, /, mounted_only: bool, env: types.Environment, runner: AsyncRunner
49
49
  ) -> list[str]:
50
- device_info = await _query_matching_device_info(
50
+ volume_info = await _query_matching_volume_info(
51
51
  kw, include_details=False, env=env, runner=runner
52
52
  )
53
53
  if not mounted_only:
54
- return [d["__name__"] for d in device_info]
55
- return [d["__name__"] for d in device_info if any(k.startswith("Mount") for k in d)]
54
+ return [d["__name__"] for d in volume_info]
55
+ return [d["__name__"] for d in volume_info if any(k.startswith("Mount") for k in d)]
56
56
 
57
57
 
58
58
  async def mount(kw: str, /, env: types.Environment, runner: AsyncRunner) -> types.Mount:
59
- """Mount device.
59
+ """Mount volume.
60
60
 
61
61
  Parameters
62
62
  ----------
63
63
  kw
64
- (Partial) name of device as given in the top line of the gio output section.
64
+ (Partial) name of volume as given in the top line of the gio output section.
65
65
  """
66
- device_info = await _query_matching_device_info(
66
+ volume_info = await _query_matching_volume_info(
67
67
  kw, include_details=True, env=env, runner=runner
68
68
  )
69
- device = _extract_device(kw, device_info=device_info)
69
+ volume = _extract_volume(kw, volume_info=volume_info)
70
70
 
71
- name, mount_id, is_mounted = device.name, device.id_, device.is_mounted
71
+ name, mount_id, is_mounted = volume.name, volume.id_, volume.is_mounted
72
72
  if is_mounted:
73
- log.info("Device `%s` already mounted.", name)
73
+ log.info("Volume `%s` already mounted.", name)
74
74
  else:
75
- log.info("Mounting device `%s`.", name)
75
+ log.info("Mounting volume `%s`.", name)
76
76
  try:
77
77
  await runner(env.gio_bin, "mount", "--device", mount_id)
78
78
  except types.SubprocessError as err:
79
- log.exception("Failed to mount device ID `%s`.", mount_id)
80
- raise types.GioMountError(
81
- f"failed to mount device with ID`{mount_id}`"
82
- ) from err
79
+ log.exception("Failed to mount volume ID `%s`.", mount_id)
80
+ raise types.GioError(f"failed to mount volume with ID`{mount_id}`") from err
83
81
 
84
82
  try:
85
- mp = await _find_mountpoint(mount_id, env=env, runner=runner)
86
- except types.GioMountError as err:
83
+ mp = await _find_mountpoint(
84
+ volume.data["activation_root"], env=env, runner=runner
85
+ )
86
+ except types.GioError as err:
87
87
  log.exception("Found no mount point for `%s`.", name)
88
- raise types.GioMountError(f"found no mount point for `{name}`") from err
88
+ raise types.GioError(f"found no mount point for `{name}`") from err
89
89
 
90
90
  return types.Mount(name=name, mountpoint=mp, triggered=mount_id is not None)
91
91
 
@@ -98,23 +98,25 @@ async def umount(
98
98
  env: types.Environment,
99
99
  runner: AsyncRunner,
100
100
  ) -> None:
101
- """Unmount device."""
101
+ """Unmount volume."""
102
102
  if kw is not None:
103
- device_info = await _query_matching_device_info(
103
+ volume_info = await _query_matching_volume_info(
104
104
  kw, include_details=True, env=env, runner=runner
105
105
  )
106
- device = _extract_device(kw, device_info=device_info)
106
+ volume = _extract_volume(kw, volume_info=volume_info)
107
107
 
108
- if not device.is_mounted:
109
- log.info("Device `%s` not mounted.")
108
+ if not volume.is_mounted:
109
+ log.info("Volume `%s` not mounted.")
110
110
  return
111
- name, mount_id = device.name, device.id_
111
+ name = volume.name
112
112
 
113
113
  try:
114
- mountpoint = await _find_mountpoint(mount_id, env=env, runner=runner)
115
- except types.GioMountError as err:
114
+ mountpoint = await _find_mountpoint(
115
+ volume.data["activation_root"], env=env, runner=runner
116
+ )
117
+ except types.GioError as err:
116
118
  log.exception("Found no mount point for `%s`.", name)
117
- raise types.GioMountError(f"found no mount point for `{name}`") from err
119
+ raise types.GioError(f"found no mount point for `{name}`") from err
118
120
  else:
119
121
  if TYPE_CHECKING:
120
122
  mount = cast("types.Mount", mount)
@@ -123,23 +125,26 @@ async def umount(
123
125
  try:
124
126
  await runner(env.gio_bin, "mount", "--unmount", mountpoint)
125
127
  except types.SubprocessError as err:
126
- raise types.GioMountError(f"failed to unmount `{mountpoint}`") from err
128
+ raise types.GioError(f"failed to unmount `{mountpoint}`") from err
127
129
 
128
130
 
129
131
  @dataclass(slots=True, kw_only=True)
130
- class _Device:
132
+ class _Volume:
131
133
  name: str
132
134
  id_: str
135
+
133
136
  is_mounted: bool
134
137
  data: dict[str, Any]
135
138
 
136
139
 
137
- async def _query_matching_device_info(
140
+ async def _query_matching_volume_info(
138
141
  kw: str, /, include_details: bool, env: types.Environment, runner: AsyncRunner
139
142
  ) -> Sequence[dict[str, Any]]:
140
- if not kw:
141
- raise ValueError("empty keyword cannot be used for device identification")
143
+ """Query volume information.
142
144
 
145
+ Only volumes with names containing `kw` (interpreted as a regular expression)
146
+ are returned.
147
+ """
143
148
  if include_details:
144
149
  gio_args = ("mount", "--list", "--detail")
145
150
  else:
@@ -147,30 +152,28 @@ async def _query_matching_device_info(
147
152
  try:
148
153
  gio_out = await runner(env.gio_bin, *gio_args)
149
154
  except types.SubprocessError as err:
150
- raise types.GioMountError(
151
- "failed to collect gio output for device discovery"
155
+ raise types.GioError(
156
+ "failed to collect gio output for volume discovery"
152
157
  ) from err
153
158
  try:
154
159
  info = _parse_output(gio_out)
155
- except types.GioParseError as err:
156
- raise types.GioMountError(
157
- "failed to parse gio output for device discovery"
158
- ) from err
160
+ except types.GioError as err:
161
+ raise types.GioError("failed to parse gio output for volume discovery") from err
159
162
 
160
163
  pattern = re.compile(kw, re.IGNORECASE)
161
164
  return [
162
165
  v
163
166
  for k, v in info.items()
164
- if k.startswith(("Volume", "Drive"))
167
+ if k.startswith("Volume")
165
168
  and isinstance(v, dict)
166
169
  and pattern.search(v.get("__name__", "")) is not None
167
170
  ]
168
171
 
169
172
 
170
- def _extract_device(kw: str, /, device_info: Sequence[dict[str, Any]]) -> _Device:
171
- match device_info:
173
+ def _extract_volume(kw: str, /, volume_info: Sequence[dict[str, Any]]) -> _Volume:
174
+ match volume_info:
172
175
  case []:
173
- raise types.GioNotFoundError(f"found no device matching for `{kw}`")
176
+ raise types.GioNotFoundError(f"found no volume matching for `{kw}`")
174
177
  case [{"__name__": name} as v]:
175
178
  log.debug("Matched `%s` to full name `%s`.", kw, name)
176
179
 
@@ -183,35 +186,33 @@ def _extract_device(kw: str, /, device_info: Sequence[dict[str, Any]]) -> _Devic
183
186
  case _:
184
187
  log.error("Failed to find mount ID in `%s`.", v)
185
188
  raise types.GioNotFoundError(f"failed to find ID for `{name}`")
186
- return _Device(name=name, id_=mount_id, is_mounted=is_mounted, data=v)
189
+ return _Volume(name=name, id_=mount_id, is_mounted=is_mounted, data=v)
187
190
  case [f, s, *ms]:
188
191
  fn, sn, lms = f["__name__"], s["__name__"], len(ms)
189
192
  raise types.GioNotFoundError(
190
193
  f"found multiple matches for `{kw}`: `{fn}`, `{sn}`, and {lms} more."
191
194
  )
192
195
  case _:
193
- raise types.GioNotFoundError("unsupported gio output structure")
196
+ raise types.GioError("unsupported gio output structure")
194
197
 
195
198
 
196
199
  async def _find_mountpoint(
197
200
  id_: str, /, env: types.Environment, runner: AsyncRunner
198
201
  ) -> str:
199
202
  try:
200
- # Requires device to be mounted.
203
+ # Requires volume to be mounted.
201
204
  # Need to the US locale as the output key are adapted to that setting.
202
205
  gio_out = await runner(
203
206
  env.gio_bin, "info", "-a", "local", id_, env_mods={"LANG": "en_US-UTF-8"}
204
207
  )
205
208
  except types.SubprocessError as err:
206
- raise types.GioMountError(
209
+ raise types.GioError(
207
210
  "failed to collect gio output for mountpoint discovery"
208
211
  ) from err
209
212
  try:
210
213
  gio_out = _parse_output(gio_out)
211
- except types.GioParseError as err:
212
- raise types.GioMountError(
213
- "failed to parse gio output for path discovery"
214
- ) from err
214
+ except types.GioError as err:
215
+ raise types.GioError("failed to parse gio output for path discovery") from err
215
216
 
216
217
  return gio_out["local path"]
217
218
 
@@ -233,7 +234,7 @@ def _parse_output(text: str, /, header_name: str = "__name__") -> dict[str, Any]
233
234
  stack.pop()
234
235
  current = stack[-1]
235
236
  if line.indent < current.indent:
236
- raise types.GioParseError(
237
+ raise types.GioError(
237
238
  f"indent {line.indent} does not match any previous level"
238
239
  )
239
240
 
@@ -293,7 +294,7 @@ def _parse_line(
293
294
  log.debug("Extracted content: `%s` (indent: %s).", content, indent)
294
295
 
295
296
  if (spec_match := _SPEC.fullmatch(content)) is None:
296
- raise types.GioParseError("line not matching expected pattern: `%s`", content)
297
+ raise types.GioError("line not matching expected pattern: `%s`", content)
297
298
 
298
299
  key, value = spec_match.group("key"), spec_match.group("val") or None
299
300
  log.debug("Found key/value: `%s`=`%s`.", key, value)
@@ -7,6 +7,7 @@ from __future__ import annotations
7
7
 
8
8
  import asyncio
9
9
  import contextvars
10
+ import logging
10
11
  import os
11
12
 
12
13
  from lid import types
@@ -18,6 +19,8 @@ if TYPE_CHECKING:
18
19
 
19
20
  __all__ = ["raise_on_stderr", "run"]
20
21
 
22
+ log = logging.getLogger()
23
+
21
24
  RUNNING = contextvars.ContextVar[str]("_RUNNING")
22
25
 
23
26
 
@@ -46,7 +49,8 @@ async def run(
46
49
  stderr=asyncio.subprocess.PIPE,
47
50
  **proc_kwargs,
48
51
  )
49
- token = RUNNING.set(f"`{bin_} {' '.join(args)}`")
52
+ token = RUNNING.set(running := f"`{bin_} {' '.join(args)}`")
53
+ log.debug("Running %s.", running)
50
54
  try:
51
55
  stdout, stderr = await proc.communicate()
52
56
  except asyncio.CancelledError:
@@ -55,7 +59,7 @@ async def run(
55
59
  raise
56
60
 
57
61
  if proc.returncode != 0:
58
- running = RUNNING.get()
62
+ log.error("Running %s failed (error code %s).", running, proc.returncode)
59
63
  RUNNING.reset(token)
60
64
  raise types.SubprocessError(
61
65
  f"{running} exited with return code {proc.returncode}:"
@@ -63,6 +67,8 @@ async def run(
63
67
  )
64
68
  try:
65
69
  if stderr:
70
+ log.warning("Found STDERR output: `%s`.", stderr)
71
+
66
72
  handle_stderr(stderr)
67
73
  stdout = stdout.decode("utf-8")
68
74
  finally:
@@ -26,16 +26,16 @@ class EnvCaptureError(LidError):
26
26
  """Exception raised when capturing the environment fails."""
27
27
 
28
28
 
29
- class GioParseError(LidError):
30
- """Raised when gio CLI output cannot be parsed."""
29
+ class GioError(LidError):
30
+ """Exception raised when interaction with `gio` failes.
31
31
 
32
-
33
- class GioMountError(LidError):
34
- """Exception raised when (un)mounting device fails."""
32
+ This includes all issues not related to user input, e.g., unsupported formats
33
+ int the `gio` output.
34
+ """
35
35
 
36
36
 
37
- class GioNotFoundError(GioMountError):
38
- """Exception raised when (un)mounting device fails due to missing device."""
37
+ class GioNotFoundError(LidError):
38
+ """Exception raised when no matching volume can be found."""
39
39
 
40
40
 
41
41
  @dataclass(slots=True)
@@ -55,11 +55,11 @@ class Mount(msgspec.Struct, forbid_unknown_fields=True, kw_only=True):
55
55
  Parameters
56
56
  ----------
57
57
  name
58
- Device ID as displayed by `gio mount --list`.
58
+ Volume ID as displayed by `gio mount --list`.
59
59
  mountpoint
60
- Mountpoint (local directory logically linked to device file system).
60
+ Mountpoint (local directory logically linked to volume file system).
61
61
  triggered
62
- True if the attempt actually mounted the device.
62
+ True if the attempt actually mounted the volume.
63
63
  """
64
64
 
65
65
  name: str
lid_cli-0.2/PKG-INFO DELETED
@@ -1,31 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: lid-cli
3
- Version: 0.2
4
- Summary: Project Lid Cli.
5
- Author: Christian Heinze
6
- License-Expression: MIT
7
- License-File: LICENSES/MIT.txt
8
- Classifier: Development Status :: 3 - Alpha
9
- Classifier: Environment :: Console
10
- Classifier: Intended Audience :: End Users/Desktop
11
- Classifier: Operating System :: POSIX :: Linux
12
- Classifier: Programming Language :: Python :: 3 :: Only
13
- Classifier: Programming Language :: Python :: 3.14
14
- Classifier: Topic :: Utilities
15
- Classifier: Typing :: Typed
16
- Requires-Dist: msgspec>=0.21
17
- Requires-Dist: rich>=14.3
18
- Requires-Dist: typer>=0.24
19
- Requires-Python: >=3.14
20
- Project-URL: Repository, https://codeberg.org/christianheinze/lid-cli
21
- Description-Content-Type: text/markdown
22
-
23
- # lid-cli
24
-
25
- To find device, run `gio mount --list --detail` and select ID in the first line.
26
-
27
- ## TODO
28
-
29
- - More error handling and testing.
30
- - Build CLI with mount and umount commands.
31
- - Possibly write target to disk in a small file for umount to read from.
lid_cli-0.2/README.md DELETED
@@ -1,9 +0,0 @@
1
- # lid-cli
2
-
3
- To find device, run `gio mount --list --detail` and select ID in the first line.
4
-
5
- ## TODO
6
-
7
- - More error handling and testing.
8
- - Build CLI with mount and umount commands.
9
- - Possibly write target to disk in a small file for umount to read from.
File without changes
File without changes
File without changes
File without changes