Coverage for /usr/local/lib/python3.10/site-packages/opal_common-0.0.0-py3.10.egg/opal_common/paths.py: 30%

51 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-10-10 11:54 +0000

1from pathlib import Path 

2from typing import List, Set, Union 

3 

4from opal_common.utils import sorted_list_from_set 

5 

6 

7class PathUtils: 

8 @staticmethod 

9 def intermediate_directories(paths: List[Path]) -> List[Path]: 

10 """Returns the set of all parent directories for a list of paths. 

11 

12 i.e: calculate all partial paths that are directories. 

13 """ 

14 directories = set() 

15 for path in paths: 

16 directories.update(path.parents) 

17 return sorted_list_from_set(directories) 

18 

19 @staticmethod 

20 def is_child_of_directories(path: Path, directories: Set[Path]) -> bool: 

21 """Whether the input path is a child of one of the input 

22 directories.""" 

23 return bool(directories & set(path.parents)) 

24 

25 @staticmethod 

26 def filter_children_paths_of_directories( 

27 paths: List[Path], directories: Set[Path] 

28 ) -> List[Path]: 

29 """Returns only paths in :paths that are children of one of the paths 

30 in :directories.""" 

31 return [ 

32 path 

33 for path in paths 

34 if PathUtils.is_child_of_directories(path, directories) 

35 ] 

36 

37 @staticmethod 

38 def non_intersecting_directories(paths: List[Path]) -> Set[Path]: 

39 """Gets a list of paths (directories), and returns a set of directories 

40 that are non-intersecting, meaning no directory in the set is a parent 

41 of another directory in the set (i.e: parent directories "swallow" 

42 their subdirectories).""" 

43 output_paths = set() 

44 for candidate in paths: 

45 if set(candidate.parents) & output_paths: 

46 # the next candidate is covered by a parent which is already in output -> SKIP 

47 # or the next candidate is already in the list 

48 continue 

49 for out_path in list(output_paths): 

50 # the next candidate can displace a child from the output 

51 if candidate in list(out_path.parents): 

52 output_paths.remove(out_path) 

53 output_paths.add(candidate) 

54 return output_paths 

55 

56 @staticmethod 

57 def sort_paths_according_to_explicit_sorting( 

58 unsorted_paths: List[Path], explicit_sorting: List[Path] 

59 ) -> List[Path]: 

60 """The way this sorting works, is assuming that explicit_sorting does 

61 NOT necessarily contains all the paths found in the original list. 

62 

63 We must ensure that all items in unsorted_paths must also exist 

64 in the output list. 

65 """ 

66 unsorted = unsorted_paths.copy() 

67 

68 sorted_paths: List[Path] = [] 

69 for path in explicit_sorting: 

70 try: 

71 # we look for Path objects and not str for normalization of the path 

72 found_path: Path = unsorted.pop(unsorted.index(path)) 

73 sorted_paths.append(found_path) 

74 except ValueError: 

75 continue # skip, not found in the original list 

76 

77 # add the remainder to the end of the sorted list 

78 sorted_paths.extend(unsorted) 

79 

80 return sorted_paths 

81 

82 @staticmethod 

83 def glob_style_match_path_to_list(path: str, match_paths: List[str]): 

84 """ 

85 Check if given path matches any of the match_paths either via glob style matching or by being nested under - when the match path ends with "/**" 

86 return the match path if there's a match, and None otherwise 

87 """ 

88 # check if any of our ignore paths match the given path 

89 for match_path in match_paths: 

90 # if the path is the root "/", then it matches any path 

91 if match_path == "/" or match_path == "/**": 

92 return match_path 

93 # if the path is indicated as a parent via "/**" at the end 

94 if match_path.endswith("/**"): 

95 # check if the path is under the parent 

96 if (path + "/").startswith((match_path[:-3] + "/")): 

97 return match_path 

98 # otherwise check for simple (non-recursive glob matching) 

99 else: 

100 path_object = Path(path) 

101 if path_object.match(match_path): 

102 return match_path 

103 # if no match - this path shouldn't be ignored 

104 return None