Coverage for nexusLIMS/utils/paths.py: 100%
18 statements
« prev ^ index » next coverage.py v7.11.3, created at 2026-03-24 05:23 +0000
« prev ^ index » next coverage.py v7.11.3, created at 2026-03-24 05:23 +0000
1"""Path manipulation utilities for NexusLIMS."""
3import logging
4from pathlib import Path
5from typing import List, Union
7from nexusLIMS.config import settings
9_logger = logging.getLogger(__name__)
12def is_subpath(path: Path, of_paths: Union[Path, List[Path]]):
13 """
14 Return if this path is a subpath of other paths.
16 Helper function to determine if a given path is a "subpath" of a set of
17 paths. Useful to help determine which instrument a given file comes from,
18 given the instruments ``filestore_path`` and the path of the file to test.
20 Parameters
21 ----------
22 path
23 The path of the file (or directory) to test. This will usually be the
24 absolute path to a file on the local filesystem (to be compared using
25 the host-specific ``nx_instrument_data_path``.
26 of_paths
27 The "higher-level" path to test against (or list thereof). In typical
28 use, this will be a path joined of an instruments ``filestore_path``
29 with the root-level ``nx_instrument_data_path``
31 Returns
32 -------
33 result : bool
34 Whether or not path is a subpath of one of the directories in of_paths
36 Examples
37 --------
38 >>> is_subpath(Path('/path/to/file.dm3'),
39 ... settings.NX_INSTRUMENT_DATA_PATH /
40 ... titan.filestore_path))
41 True
42 """
43 if isinstance(of_paths, Path):
44 of_paths = [of_paths]
46 return any(subpath in path.parents for subpath in of_paths)
49def join_instrument_filestore_path(filestore_path: str) -> Path:
50 """
51 Safely join NX_INSTRUMENT_DATA_PATH with an instrument's filestore_path.
53 This helper handles filestore_path values with leading slashes gracefully.
54 If filestore_path starts with '/', the leading slash is stripped before joining
55 to ensure the path remains relative to NX_INSTRUMENT_DATA_PATH.
57 Parameters
58 ----------
59 filestore_path
60 The instrument's filestore_path (may contain leading '/')
62 Returns
63 -------
64 pathlib.Path
65 A resolved Path object: NX_INSTRUMENT_DATA_PATH / filestore_path
67 Examples
68 --------
69 >>> join_instrument_filestore_path("./Titan_STEM")
70 PosixPath('/mnt/data/Titan_STEM')
72 >>> join_instrument_filestore_path("/Titan_STEM") # Leading slash stripped
73 PosixPath('/mnt/data/Titan_STEM')
75 >>> join_instrument_filestore_path("Titan_STEM")
76 PosixPath('/mnt/data/Titan_STEM')
77 """
78 # Strip leading slash to ensure relative path behavior
79 # pathlib treats absolute paths specially - they override the base path
80 normalized_path = filestore_path.lstrip("/")
82 return Path(settings.NX_INSTRUMENT_DATA_PATH) / normalized_path
85def replace_instrument_data_path(path: Path, suffix: str) -> Path:
86 """
87 Given an "NX_INSTRUMENT_DATA_PATH" path, generate equivalent"NX_DATA_PATH" path.
89 If the given path is not a subpath of "NX_INSTRUMENT_DATA_PATH", a warning will
90 be logged and the suffix will just be added at the end.
92 Parameters
93 ----------
94 path
95 The input path, which is expected to be a subpath of the
96 NX_INSTRUMENT_DATA_PATH directory
97 suffix
98 Any added suffix to add to the path (useful for appending with a new extension,
99 such as ``.json``)
101 Returns
102 -------
103 pathlib.Path
104 A resolved pathlib.Path object pointing to the new path
105 """
106 instr_data_path = Path(str(settings.NX_INSTRUMENT_DATA_PATH))
107 nexuslims_path = Path(str(settings.NX_DATA_PATH))
109 if instr_data_path not in path.parents:
110 _logger.warning(
111 "%s is not a sub-path of %s", path, str(settings.NX_INSTRUMENT_DATA_PATH)
112 )
113 return Path(str(path).replace(str(instr_data_path), str(nexuslims_path)) + suffix)