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

1"""Path manipulation utilities for NexusLIMS.""" 

2 

3import logging 

4from pathlib import Path 

5from typing import List, Union 

6 

7from nexusLIMS.config import settings 

8 

9_logger = logging.getLogger(__name__) 

10 

11 

12def is_subpath(path: Path, of_paths: Union[Path, List[Path]]): 

13 """ 

14 Return if this path is a subpath of other paths. 

15 

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. 

19 

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`` 

30 

31 Returns 

32 ------- 

33 result : bool 

34 Whether or not path is a subpath of one of the directories in of_paths 

35 

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] 

45 

46 return any(subpath in path.parents for subpath in of_paths) 

47 

48 

49def join_instrument_filestore_path(filestore_path: str) -> Path: 

50 """ 

51 Safely join NX_INSTRUMENT_DATA_PATH with an instrument's filestore_path. 

52 

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. 

56 

57 Parameters 

58 ---------- 

59 filestore_path 

60 The instrument's filestore_path (may contain leading '/') 

61 

62 Returns 

63 ------- 

64 pathlib.Path 

65 A resolved Path object: NX_INSTRUMENT_DATA_PATH / filestore_path 

66 

67 Examples 

68 -------- 

69 >>> join_instrument_filestore_path("./Titan_STEM") 

70 PosixPath('/mnt/data/Titan_STEM') 

71 

72 >>> join_instrument_filestore_path("/Titan_STEM") # Leading slash stripped 

73 PosixPath('/mnt/data/Titan_STEM') 

74 

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("/") 

81 

82 return Path(settings.NX_INSTRUMENT_DATA_PATH) / normalized_path 

83 

84 

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. 

88 

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. 

91 

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``) 

100 

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)) 

108 

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)