Coverage for ebcpy/utils/__init__.py: 100%

52 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2026-05-29 13:01 +0000

1""" 

2Package containing utility functions used in different packages. 

3Contains a statistics analyzer and a visualizer. 

4""" 

5import logging 

6import os 

7import re 

8from pathlib import Path 

9from typing import Union, List 

10import warnings 

11 

12 

13def setup_logger(name: str, 

14 working_directory: Union[Path, str] = None, 

15 level=logging.DEBUG): 

16 """ 

17 Setup an class or module specific logger instance 

18 to ensure readable output for users. 

19 

20 :param str name: 

21 The name of the logger instance 

22 :param str,Path working_directory: 

23 The path where to store the logfile. 

24 If None is given, logs are not stored. 

25 :param str level: 

26 The logging level, default is DEBUG 

27 

28 .. versionadded:: 0.1.7 

29 """ 

30 logger = logging.getLogger(name=name) 

31 # Set log-level 

32 logger.setLevel(level=level) 

33 # Check if logger was already instantiated. If so, return already. 

34 if logger.handlers: 

35 return logger 

36 # Add handlers if not set already by logging.basicConfig and if path is specified 

37 formatter = logging.Formatter(fmt='%(asctime)s %(levelname)s %(name)s: %(message)s', 

38 datefmt='%d.%m.%Y-%H:%M:%S') 

39 if not logging.getLogger().hasHandlers(): 

40 console = logging.StreamHandler() 

41 console.setFormatter(fmt=formatter) 

42 logger.addHandler(hdlr=console) 

43 if working_directory is not None: 

44 os.makedirs(working_directory, exist_ok=True) 

45 file_handler = logging.FileHandler(filename=working_directory.joinpath(f"{name}.log")) 

46 file_handler.setFormatter(fmt=formatter) 

47 logger.addHandler(hdlr=file_handler) 

48 return logger 

49 

50 

51def get_names( 

52 all_names: list, 

53 patterns: Union[str, List[str]], 

54 exclude: Union[str, List[str]] = None 

55) -> List[str]: 

56 """ 

57 Filter a list of candidate names by literal values or glob-style patterns, 

58 optionally excluding names that match exclusion patterns. 

59 

60 This function returns all names from ``all_names`` that match the provided 

61 ``patterns`` and do not match any of the ``exclude`` patterns. 

62 Patterns may be a single string or a list of strings, and may 

63 contain the wildcard ``*`` to match any sequence of characters. Literal names 

64 without ``*`` must match exactly. 

65 

66 The returned list preserves the order of ``all_names``. 

67 

68 :param list all_names: 

69 List of available names to filter. 

70 :param str,list[str] patterns: 

71 A pattern or list of patterns (with optional ``*`` wildcards) 

72 to match against ``all_names``. 

73 :param str,list[str] exclude: 

74 A pattern or list of patterns to exclude from the results. 

75 Names matching any exclusion pattern are removed after the 

76 inclusion step. Default is None (no exclusion). 

77 :return: A list of names from ``all_names`` that match any of the given 

78 patterns and none of the exclusion patterns, in original order. 

79 :rtype: list[str] 

80 :raises warning: If any inclusion pattern does not match at least one name. 

81 

82 Example: 

83 

84 >>> names = ["wall.layer[1].T", "wall.layer[2].T", "wall.layer[1].Q_flow"] 

85 >>> get_names(names, "wall.layer[*].T") 

86 ['wall.layer[1].T', 'wall.layer[2].T'] 

87 >>> get_names(names, "wall.layer[*].*", exclude="*Q_flow") 

88 ['wall.layer[1].T', 'wall.layer[2].T'] 

89 """ 

90 if isinstance(patterns, str): 

91 patterns = [patterns] 

92 

93 matched = set() 

94 unmatched = [] 

95 for pat in patterns: 

96 if '*' in pat: 

97 regex = '^' + re.escape(pat).replace(r'\*', '.*') + '$' 

98 hits = [k for k in all_names if re.match(regex, k)] 

99 if hits: 

100 matched.update(hits) 

101 else: 

102 unmatched.append(pat) 

103 else: 

104 if pat in all_names: 

105 matched.add(pat) 

106 else: 

107 unmatched.append(pat) 

108 

109 if unmatched: 

110 warnings.warn( 

111 "The following variable names/patterns are not in the given .mat file: " 

112 + ", ".join(unmatched) 

113 ) 

114 

115 # Apply exclusion patterns 

116 if exclude is not None: 

117 if isinstance(exclude, str): 

118 exclude = [exclude] 

119 excluded = set() 

120 for pat in exclude: 

121 if '*' in pat: 

122 regex = '^' + re.escape(pat).replace(r'\*', '.*') + '$' 

123 excluded.update(k for k in matched if re.match(regex, k)) 

124 else: 

125 if pat in matched: 

126 excluded.add(pat) 

127 matched -= excluded 

128 

129 # Preserve original order 

130 names = [var for var in all_names if var in matched] 

131 return names