diskinfo 4.2.0__tar.gz → 4.2.1__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.
- {diskinfo-4.2.0/src/diskinfo.egg-info → diskinfo-4.2.1}/PKG-INFO +1 -1
- {diskinfo-4.2.0 → diskinfo-4.2.1}/pyproject.toml +1 -1
- {diskinfo-4.2.0 → diskinfo-4.2.1}/src/diskinfo/disk.py +6 -6
- {diskinfo-4.2.0 → diskinfo-4.2.1}/src/diskinfo/filesystem.py +4 -4
- {diskinfo-4.2.0 → diskinfo-4.2.1}/src/diskinfo/partition.py +4 -4
- {diskinfo-4.2.0 → diskinfo-4.2.1}/src/diskinfo/utils.py +34 -8
- {diskinfo-4.2.0 → diskinfo-4.2.1/src/diskinfo.egg-info}/PKG-INFO +1 -1
- {diskinfo-4.2.0 → diskinfo-4.2.1}/test/test_disk.py +12 -0
- {diskinfo-4.2.0 → diskinfo-4.2.1}/test/test_filesystem.py +25 -0
- {diskinfo-4.2.0 → diskinfo-4.2.1}/test/test_partition.py +18 -3
- {diskinfo-4.2.0 → diskinfo-4.2.1}/test/test_utils.py +27 -6
- {diskinfo-4.2.0 → diskinfo-4.2.1}/LICENSE +0 -0
- {diskinfo-4.2.0 → diskinfo-4.2.1}/README.md +0 -0
- {diskinfo-4.2.0 → diskinfo-4.2.1}/setup.cfg +0 -0
- {diskinfo-4.2.0 → diskinfo-4.2.1}/src/diskinfo/__init__.py +0 -0
- {diskinfo-4.2.0 → diskinfo-4.2.1}/src/diskinfo/demo.py +0 -0
- {diskinfo-4.2.0 → diskinfo-4.2.1}/src/diskinfo/diskinfo.py +0 -0
- {diskinfo-4.2.0 → diskinfo-4.2.1}/src/diskinfo/disksmart.py +0 -0
- {diskinfo-4.2.0 → diskinfo-4.2.1}/src/diskinfo/disktype.py +0 -0
- {diskinfo-4.2.0 → diskinfo-4.2.1}/src/diskinfo.egg-info/SOURCES.txt +0 -0
- {diskinfo-4.2.0 → diskinfo-4.2.1}/src/diskinfo.egg-info/dependency_links.txt +0 -0
- {diskinfo-4.2.0 → diskinfo-4.2.1}/src/diskinfo.egg-info/requires.txt +0 -0
- {diskinfo-4.2.0 → diskinfo-4.2.1}/src/diskinfo.egg-info/top_level.txt +0 -0
- {diskinfo-4.2.0 → diskinfo-4.2.1}/test/test_diskinfo.py +0 -0
- {diskinfo-4.2.0 → diskinfo-4.2.1}/test/test_disksmart.py +0 -0
|
@@ -7,7 +7,7 @@ import re
|
|
|
7
7
|
from typing import List, Tuple, Union
|
|
8
8
|
import pySMART
|
|
9
9
|
from pyudev import Context, Device, Devices, DeviceNotFoundAtPathError
|
|
10
|
-
from diskinfo.utils import _read_file, size_in_hrf, _pyudev_getenc
|
|
10
|
+
from diskinfo.utils import _read_file, size_in_hrf, _pyudev_getenc, _pyudev_getstr
|
|
11
11
|
from diskinfo.disktype import DiskType
|
|
12
12
|
from diskinfo.filesystem import FileSystem
|
|
13
13
|
from diskinfo.partition import Partition
|
|
@@ -186,11 +186,11 @@ class Disk:
|
|
|
186
186
|
raise RuntimeError(f"Disk type cannot be determined: disk={pyudev_device.sys_name}, queue/rotational={rotational}.")
|
|
187
187
|
|
|
188
188
|
# Read further disk attributes.
|
|
189
|
-
self.__serial_number = pyudev_device
|
|
190
|
-
self.__firmware = pyudev_device
|
|
191
|
-
self.__wwn = pyudev_device
|
|
192
|
-
self.__part_table_type = pyudev_device
|
|
193
|
-
self.__part_table_uuid = pyudev_device
|
|
189
|
+
self.__serial_number = _pyudev_getstr(pyudev_device, "ID_SERIAL_SHORT")
|
|
190
|
+
self.__firmware = _pyudev_getstr(pyudev_device, "ID_REVISION")
|
|
191
|
+
self.__wwn = _pyudev_getstr(pyudev_device, "ID_WWN")
|
|
192
|
+
self.__part_table_type = _pyudev_getstr(pyudev_device, "ID_PART_TABLE_TYPE")
|
|
193
|
+
self.__part_table_uuid = _pyudev_getstr(pyudev_device, "ID_PART_TABLE_UUID")
|
|
194
194
|
self.__model = _pyudev_getenc(pyudev_device, "ID_MODEL")
|
|
195
195
|
|
|
196
196
|
# Read `/dev/disk/by-byid/` and `/dev/disk/by-path/` path elements.
|
|
@@ -6,7 +6,7 @@ import os
|
|
|
6
6
|
import re
|
|
7
7
|
from typing import Tuple
|
|
8
8
|
from pyudev import Device
|
|
9
|
-
from diskinfo.utils import size_in_hrf, _pyudev_getenc
|
|
9
|
+
from diskinfo.utils import size_in_hrf, _pyudev_getenc, _pyudev_getstr
|
|
10
10
|
|
|
11
11
|
# Path to the kernel mount table.
|
|
12
12
|
_PROC_MOUNTS = "/proc/mounts"
|
|
@@ -60,9 +60,9 @@ class FileSystem:
|
|
|
60
60
|
# Read filesystem attributes from udev properties.
|
|
61
61
|
self.__fs_label = _pyudev_getenc(_device, "ID_FS_LABEL")
|
|
62
62
|
self.__fs_uuid = _pyudev_getenc(_device, "ID_FS_UUID")
|
|
63
|
-
self.__fs_type = _device
|
|
64
|
-
self.__fs_version = _device
|
|
65
|
-
self.__fs_usage = _device
|
|
63
|
+
self.__fs_type = _pyudev_getstr(_device, "ID_FS_TYPE")
|
|
64
|
+
self.__fs_version = _pyudev_getstr(_device, "ID_FS_VERSION")
|
|
65
|
+
self.__fs_usage = _pyudev_getstr(_device, "ID_FS_USAGE")
|
|
66
66
|
self.__fs_mounting_point = ""
|
|
67
67
|
self.__fs_size = 0
|
|
68
68
|
self.__fs_free_size = 0
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
#
|
|
5
5
|
from typing import List, Tuple
|
|
6
6
|
from pyudev import Device
|
|
7
|
-
from diskinfo.utils import size_in_hrf, _pyudev_getint, _pyudev_getesc
|
|
7
|
+
from diskinfo.utils import size_in_hrf, _pyudev_getint, _pyudev_getesc, _pyudev_getstr
|
|
8
8
|
from diskinfo.filesystem import FileSystem
|
|
9
9
|
|
|
10
10
|
|
|
@@ -95,10 +95,10 @@ class Partition:
|
|
|
95
95
|
continue
|
|
96
96
|
|
|
97
97
|
# Save further partition attributes.
|
|
98
|
-
self.__part_scheme = _device
|
|
98
|
+
self.__part_scheme = _pyudev_getstr(_device, "ID_PART_ENTRY_SCHEME")
|
|
99
99
|
self.__part_label = _pyudev_getesc(_device, "ID_PART_ENTRY_NAME")
|
|
100
|
-
self.__part_uuid = _device
|
|
101
|
-
self.__part_type = _device
|
|
100
|
+
self.__part_uuid = _pyudev_getstr(_device, "ID_PART_ENTRY_UUID")
|
|
101
|
+
self.__part_type = _pyudev_getstr(_device, "ID_PART_ENTRY_TYPE")
|
|
102
102
|
self.__part_number = _pyudev_getint(_device, "ID_PART_ENTRY_NUMBER", 0)
|
|
103
103
|
self.__part_offset = _pyudev_getint(_device, "ID_PART_ENTRY_OFFSET", -1)
|
|
104
104
|
self.__part_size = _pyudev_getint(_device, "ID_PART_ENTRY_SIZE", 0)
|
|
@@ -242,17 +242,42 @@ def _pyudev_getint(device: Device, key: str, default_value: int = None) -> Union
|
|
|
242
242
|
return default_value
|
|
243
243
|
|
|
244
244
|
|
|
245
|
-
def
|
|
245
|
+
def _pyudev_getstr(device: Device, key: str) -> str:
|
|
246
|
+
"""Returns a string attribute value of a pyudev.Device() class. The function returns an empty string if the
|
|
247
|
+
attribute does not exist.
|
|
248
|
+
|
|
249
|
+
Args:
|
|
250
|
+
device (pyudev.Device): pyudev.Device() class
|
|
251
|
+
key (str): attribute key
|
|
252
|
+
|
|
253
|
+
Returns:
|
|
254
|
+
str: attribute value or empty string
|
|
255
|
+
|
|
256
|
+
Example:
|
|
257
|
+
An example about the use of the function::
|
|
258
|
+
|
|
259
|
+
>>> from diskinfo import *
|
|
260
|
+
>>> from pyudev import *
|
|
261
|
+
>>> c = Context()
|
|
262
|
+
>>> d = Devices.from_name(c, "block", "sda1")
|
|
263
|
+
>>> print(_pyudev_getstr(d, "ID_FS_TYPE"))
|
|
264
|
+
ext4
|
|
265
|
+
|
|
266
|
+
"""
|
|
267
|
+
return device.get(key) or ""
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def _pyudev_getesc(dev: Device, key: str) -> str:
|
|
246
271
|
"""Returns a device attribute value where udev stores the escaped value in the property itself, so there is
|
|
247
272
|
no `_ENC` key pair (e.g. `ID_PART_ENTRY_NAME`). The escape sequences of the value will be decoded and the
|
|
248
|
-
result will be stripped.
|
|
273
|
+
result will be stripped. The function returns an empty string if the attribute does not exist.
|
|
249
274
|
|
|
250
275
|
Args:
|
|
251
276
|
device (pyudev.Device): pyudev.Device() class
|
|
252
277
|
key (str): attribute key
|
|
253
278
|
|
|
254
279
|
Returns:
|
|
255
|
-
|
|
280
|
+
str: attribute value or empty string
|
|
256
281
|
|
|
257
282
|
Example:
|
|
258
283
|
An example about the use of the function::
|
|
@@ -268,21 +293,22 @@ def _pyudev_getesc(dev: Device, key: str) -> Union[str, None]:
|
|
|
268
293
|
value = dev.get(key)
|
|
269
294
|
if value:
|
|
270
295
|
return _unescape_udev_value(value).strip()
|
|
271
|
-
return
|
|
296
|
+
return ""
|
|
272
297
|
|
|
273
298
|
|
|
274
|
-
def _pyudev_getenc(dev: Device, key: str) ->
|
|
299
|
+
def _pyudev_getenc(dev: Device, key: str) -> str:
|
|
275
300
|
"""Returns one of the device attribute key pairs. There are udev attributes key pairs (e.g. `ID_MODEL` and
|
|
276
301
|
`ID_MODEL_ENC`), where this function reads and decodes the `_ENC` version of the key if it exists, otherwise
|
|
277
302
|
the simple key will be read. The `_ENC` version is always preferred, because the simple key contains the
|
|
278
|
-
sanitized value of udev (e.g. the space characters are replaced there).
|
|
303
|
+
sanitized value of udev (e.g. the space characters are replaced there). The function returns an empty string
|
|
304
|
+
if none of the attributes exists.
|
|
279
305
|
|
|
280
306
|
Args:
|
|
281
307
|
device (pyudev.Device): pyudev.Device() class
|
|
282
308
|
key (str): attribute key
|
|
283
309
|
|
|
284
310
|
Returns:
|
|
285
|
-
|
|
311
|
+
str: attribute value or empty string
|
|
286
312
|
|
|
287
313
|
Example:
|
|
288
314
|
An example about the use of the function::
|
|
@@ -302,7 +328,7 @@ def _pyudev_getenc(dev: Device, key: str) -> Union[str, None]:
|
|
|
302
328
|
if value:
|
|
303
329
|
return _unescape_udev_value(value).strip()
|
|
304
330
|
# Read the simple key.
|
|
305
|
-
return dev.get(key)
|
|
331
|
+
return dev.get(key) or ""
|
|
306
332
|
|
|
307
333
|
|
|
308
334
|
# End
|
|
@@ -1053,6 +1053,18 @@ def test_disk_blank_no_filesystem():
|
|
|
1053
1053
|
assert d.get_filesystem() is None
|
|
1054
1054
|
|
|
1055
1055
|
|
|
1056
|
+
def test_disk_missing_properties_return_empty_strings():
|
|
1057
|
+
"""Disk returns empty strings (not None) for all missing udev string properties."""
|
|
1058
|
+
dev = _make_device(name="sdc", dev_id="8:32", size=2097152, phys_bs=512, log_bs=512, rotational="1",
|
|
1059
|
+
serial=None, firmware=None, wwn=None, model=None, part_table_type=None, part_table_uuid=None)
|
|
1060
|
+
with pyudev_patched(dev):
|
|
1061
|
+
d = Disk(dev.sys_name)
|
|
1062
|
+
|
|
1063
|
+
values = [d.get_serial_number(), d.get_firmware(), d.get_wwn(), d.get_model(), d.get_partition_table_type(),
|
|
1064
|
+
d.get_partition_table_uuid()]
|
|
1065
|
+
assert values == [""] * 6
|
|
1066
|
+
|
|
1067
|
+
|
|
1056
1068
|
def test_disk_with_partitions_no_raw_filesystem(ssd_device):
|
|
1057
1069
|
"""Disk does not detect a raw filesystem when partitions exist."""
|
|
1058
1070
|
part = _make_partition_device(
|
|
@@ -83,6 +83,31 @@ def test_filesystem_stores_all_attributes():
|
|
|
83
83
|
assert fs.get_fs_mounting_point() == "/mnt/data"
|
|
84
84
|
|
|
85
85
|
|
|
86
|
+
@pytest.mark.parametrize("attribute", ["fs_label", "fs_uuid", "fs_type", "fs_version", "fs_usage"])
|
|
87
|
+
def test_filesystem_missing_attribute_returns_empty_string(attribute):
|
|
88
|
+
"""FileSystem returns an empty string (not None) when a udev property is missing."""
|
|
89
|
+
dev = _make_fs_device(**{attribute: None})
|
|
90
|
+
with patch("builtins.open", _proc_mounts()):
|
|
91
|
+
fs = FileSystem(dev)
|
|
92
|
+
|
|
93
|
+
value = getattr(fs, f"get_{attribute}")()
|
|
94
|
+
assert value == ""
|
|
95
|
+
assert isinstance(value, str)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def test_filesystem_without_any_property_returns_empty_strings():
|
|
99
|
+
"""FileSystem of a device without file system properties (e.g. a reserved partition) returns empty values."""
|
|
100
|
+
dev = _make_fs_device(fs_label=None, fs_uuid=None, fs_type=None, fs_version=None, fs_usage=None)
|
|
101
|
+
with patch("builtins.open", _proc_mounts()):
|
|
102
|
+
fs = FileSystem(dev)
|
|
103
|
+
|
|
104
|
+
values = [fs.get_fs_label(), fs.get_fs_uuid(), fs.get_fs_type(), fs.get_fs_version(), fs.get_fs_usage(),
|
|
105
|
+
fs.get_fs_mounting_point()]
|
|
106
|
+
assert values == [""] * 6
|
|
107
|
+
assert fs.get_fs_size() == 0
|
|
108
|
+
assert fs.get_fs_free_size() == 0
|
|
109
|
+
|
|
110
|
+
|
|
86
111
|
# ── FileSystem.__init__ – /proc/mounts parsing ──────────────────────────
|
|
87
112
|
|
|
88
113
|
|
|
@@ -397,15 +397,30 @@ def test_partition_decodes_part_label(escaped, expected):
|
|
|
397
397
|
assert p.get_part_label() == expected
|
|
398
398
|
|
|
399
399
|
|
|
400
|
-
def
|
|
401
|
-
"""Partition
|
|
400
|
+
def test_partition_missing_part_label_returns_empty_string():
|
|
401
|
+
"""Partition returns an empty string for a missing partition label instead of raising an exception."""
|
|
402
402
|
dev = _make_part_device(part_label=None)
|
|
403
403
|
m = _proc_mounts(("/dev/sda1", "/boot/efi"))
|
|
404
404
|
with (patch("builtins.open", m),
|
|
405
405
|
patch("os.statvfs", return_value=_statvfs_result(f_bavail=512000))):
|
|
406
406
|
p = Partition(dev)
|
|
407
407
|
|
|
408
|
-
assert p.get_part_label()
|
|
408
|
+
assert p.get_part_label() == ""
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
def test_partition_missing_properties_return_empty_strings():
|
|
412
|
+
"""Partition and its FileSystem return empty strings (not None) for all missing udev string properties."""
|
|
413
|
+
dev = _make_part_device(scheme=None, part_label=None, part_uuid=None, part_type=None, fs_label=None,
|
|
414
|
+
fs_uuid=None, fs_type=None, fs_version=None, fs_usage=None)
|
|
415
|
+
with patch("builtins.open", _proc_mounts()):
|
|
416
|
+
p = Partition(dev)
|
|
417
|
+
|
|
418
|
+
fs = p.get_filesystem()
|
|
419
|
+
values = [p.get_part_scheme(), p.get_part_label(), p.get_part_uuid(), p.get_part_type(), fs.get_fs_label(),
|
|
420
|
+
fs.get_fs_uuid(), fs.get_fs_type(), fs.get_fs_version(), fs.get_fs_usage()]
|
|
421
|
+
assert values == [""] * 9
|
|
422
|
+
# A typical caller pattern must not fail on a partition without a label.
|
|
423
|
+
assert fs.get_fs_label().startswith("hassos") is False
|
|
409
424
|
|
|
410
425
|
|
|
411
426
|
# End
|
|
@@ -14,6 +14,7 @@ from diskinfo.utils import (
|
|
|
14
14
|
_pyudev_getenc,
|
|
15
15
|
_unescape_udev_value,
|
|
16
16
|
_pyudev_getesc,
|
|
17
|
+
_pyudev_getstr,
|
|
17
18
|
)
|
|
18
19
|
|
|
19
20
|
|
|
@@ -246,6 +247,26 @@ def test_pyudev_getint_non_integer_no_default_returns_none():
|
|
|
246
247
|
assert _pyudev_getint(dev, "key") is None
|
|
247
248
|
|
|
248
249
|
|
|
250
|
+
# ── _pyudev_getstr ────────────────────────────────────────────────────────────
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def test_pyudev_getstr_returns_value():
|
|
254
|
+
"""_pyudev_getstr() returns the value of an existing key."""
|
|
255
|
+
dev = MagicMock()
|
|
256
|
+
dev.get.side_effect = {"ID_FS_TYPE": "ext4"}.get
|
|
257
|
+
assert _pyudev_getstr(dev, "ID_FS_TYPE") == "ext4"
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
@pytest.mark.parametrize("value", [None, ""], ids=["missing", "empty"])
|
|
261
|
+
def test_pyudev_getstr_missing_or_empty_returns_empty_string(value):
|
|
262
|
+
"""_pyudev_getstr() returns an empty string if the key does not exist or its value is empty."""
|
|
263
|
+
dev = MagicMock()
|
|
264
|
+
dev.get.return_value = value
|
|
265
|
+
result = _pyudev_getstr(dev, "ID_FS_TYPE")
|
|
266
|
+
assert result == ""
|
|
267
|
+
assert isinstance(result, str)
|
|
268
|
+
|
|
269
|
+
|
|
249
270
|
# ── _pyudev_getenc ────────────────────────────────────────────────────────────
|
|
250
271
|
|
|
251
272
|
|
|
@@ -290,11 +311,11 @@ def test_pyudev_getenc_decodes_non_ascii_without_space():
|
|
|
290
311
|
assert _pyudev_getenc(dev, "ID_FS_LABEL") == "CLÉUSB"
|
|
291
312
|
|
|
292
313
|
|
|
293
|
-
def
|
|
294
|
-
"""_pyudev_getenc() returns
|
|
314
|
+
def test_pyudev_getenc_returns_empty_string_when_both_keys_absent():
|
|
315
|
+
"""_pyudev_getenc() returns an empty string when neither _ENC nor plain key exists."""
|
|
295
316
|
dev = MagicMock()
|
|
296
317
|
dev.get.return_value = None
|
|
297
|
-
assert _pyudev_getenc(dev, "ID_MODEL")
|
|
318
|
+
assert _pyudev_getenc(dev, "ID_MODEL") == ""
|
|
298
319
|
|
|
299
320
|
|
|
300
321
|
def test_pyudev_getenc_strips_whitespace_after_decode():
|
|
@@ -354,11 +375,11 @@ def test_pyudev_getesc_strips_whitespace_after_decode():
|
|
|
354
375
|
assert _pyudev_getesc(dev, "ID_PART_ENTRY_NAME") == "Boot"
|
|
355
376
|
|
|
356
377
|
|
|
357
|
-
def
|
|
358
|
-
"""_pyudev_getesc() returns
|
|
378
|
+
def test_pyudev_getesc_missing_key_returns_empty_string():
|
|
379
|
+
"""_pyudev_getesc() returns an empty string if the key does not exist."""
|
|
359
380
|
dev = MagicMock()
|
|
360
381
|
dev.get.return_value = None
|
|
361
|
-
assert _pyudev_getesc(dev, "ID_PART_ENTRY_NAME")
|
|
382
|
+
assert _pyudev_getesc(dev, "ID_PART_ENTRY_NAME") == ""
|
|
362
383
|
|
|
363
384
|
|
|
364
385
|
def test_pyudev_getesc_empty_value_is_kept():
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|