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

41 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2025-07-09 10:42 +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(all_names: list, patterns: Union[str, List[str]]) -> List[str]: 

52 """ 

53 Filter a list of candidate names by literal values or glob-style patterns. 

54 

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

56 `patterns`. Patterns may be a single string or a list of strings, and may 

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

58 without `*` must match exactly. The matching is performed in two steps: 

59 1. Each pattern is translated to a regular expression if it contains `*`, 

60 otherwise used as a literal match. 

61 2. Any pattern that matches no names in `all_names` raises a warning. 

62 

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

64 

65 :param all_names: List of available names to filter. 

66 :param patterns: A pattern or list of patterns (with optional `*` wildcards) 

67 to match against `all_names`. 

68 :return: A list of names from `all_names` that match any of the given patterns, 

69 in original order. 

70 :raises KeyError: If any pattern does not match at least one name. 

71 """ 

72 if isinstance(patterns, str): 

73 patterns = [patterns] 

74 

75 matched = set() 

76 unmatched = [] 

77 for pat in patterns: 

78 if '*' in pat: 

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

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

81 if hits: 

82 matched.update(hits) 

83 else: 

84 unmatched.append(pat) 

85 else: 

86 if pat in all_names: 

87 matched.add(pat) 

88 else: 

89 unmatched.append(pat) 

90 

91 if unmatched: 

92 warnings.warn( 

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

94 + ", ".join(unmatched) 

95 ) 

96 # preserve original order 

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

98 return names