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

40 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2025-05-06 09:34 +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 

10 

11 

12def setup_logger(name: str, 

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

14 level=logging.DEBUG): 

15 """ 

16 Setup an class or module specific logger instance 

17 to ensure readable output for users. 

18 

19 :param str name: 

20 The name of the logger instance 

21 :param str,Path working_directory: 

22 The path where to store the logfile. 

23 If None is given, logs are not stored. 

24 :param str level: 

25 The logging level, default is DEBUG 

26 

27 .. versionadded:: 0.1.7 

28 """ 

29 logger = logging.getLogger(name=name) 

30 # Set log-level 

31 logger.setLevel(level=level) 

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

33 if logger.handlers: 

34 return logger 

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

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

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

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

39 console = logging.StreamHandler() 

40 console.setFormatter(fmt=formatter) 

41 logger.addHandler(hdlr=console) 

42 if working_directory is not None: 

43 os.makedirs(working_directory, exist_ok=True) 

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

45 file_handler.setFormatter(fmt=formatter) 

46 logger.addHandler(hdlr=file_handler) 

47 return logger 

48 

49 

50def get_names(all_names: list, patterns: Union[str, List[str]]) -> List[str]: 

51 """ 

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

53 

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

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

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

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

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

59 otherwise used as a literal match. 

60 2. Any pattern that matches no names in `all_names` raises a `KeyError`. 

61 

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

63 

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

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

66 to match against `all_names`. 

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

68 in original order. 

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

70 """ 

71 if isinstance(patterns, str): 

72 patterns = [patterns] 

73 

74 matched = set() 

75 unmatched = [] 

76 for pat in patterns: 

77 if '*' in pat: 

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

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

80 if hits: 

81 matched.update(hits) 

82 else: 

83 unmatched.append(pat) 

84 else: 

85 if pat in all_names: 

86 matched.add(pat) 

87 else: 

88 unmatched.append(pat) 

89 

90 if unmatched: 

91 raise KeyError( 

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

93 + ", ".join(unmatched) 

94 ) 

95 # preserve original order 

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

97 return names