Coverage for python/univention/config_registry/misc.py: 100%

49 statements  

« prev     ^ index     » next       coverage.py v7.6.0, created at 2026-09-11 14:11 +0000

1"""Univention Configuration Registry helper functions.""" 

2# main configuration registry classes 

3# 

4# SPDX-FileCopyrightText: 2004-2026 Univention GmbH 

5# SPDX-License-Identifier: AGPL-3.0-only 

6 

7 

8import os 

9import re 

10import string # pylint: disable-msg=W0402 

11import sys 

12from shlex import quote as escape_value 

13from typing import IO 

14 

15 

16__all__ = [ 

17 'INVALID_KEY_CHARS', 

18 'directory_files', 

19 'escape_value', 

20 'key_shell_escape', 

21 'replace_dict', 

22 'replace_umlaut', 

23 'validate_key', 

24] 

25 

26 

27def replace_dict(line: str, dictionary: dict[str, str]) -> str: 

28 """ 

29 Map any character from line to its value from dictionary. 

30 

31 >>> replace_dict('kernel', {'e': 'E', 'k': '', 'n': 'pp'}) 

32 'ErppEl' 

33 """ 

34 return ''.join(dictionary.get(_, _) for _ in line) 

35 

36 

37def replace_umlaut(line: str) -> str: 

38 """ 

39 Replace german umlauts. 

40 

41 >>> replace_umlaut(u'überschrieben') == u'ueberschrieben' 

42 True 

43 """ 

44 return replace_dict(line, UMLAUTS) # pylint: disable-msg=E1101 

45 

46 

47UMLAUTS = { # type: ignore # pylint: disable-msg=W0612 

48 'Ä': 'Ae', 

49 'ä': 'ae', 

50 'Ö': 'Oe', 

51 'ö': 'oe', 

52 'Ü': 'Ue', 

53 'ü': 'ue', 

54 'ß': 'ss', 

55} 

56 

57 

58def asciify(text: str) -> str: 

59 """ 

60 Replace any non-ASCII characters. 

61 

62 :param text: Input text. 

63 :returns: Replaced text. 

64 """ 

65 return text.encode('ascii', 'replace').decode("ascii") 

66 

67 

68def key_shell_escape(line: str) -> str: 

69 """ 

70 Escape variable name by substituting shell invalid characters by '_'. 

71 

72 :param line: UCR variable name. 

73 :returns: substitued variable name 

74 """ 

75 if not line: 

76 raise ValueError('got empty line') 

77 new_line = [] 

78 if line[0] in string.digits: 

79 new_line.append('_') 

80 for letter in line: 

81 if letter in VALID_CHARS: # pylint: disable-msg=E1101 

82 new_line.append(letter) 

83 else: 

84 new_line.append('_') 

85 

86 return ''.join(new_line) 

87 

88 

89VALID_CHARS = ( # type: ignore # pylint: disable-msg=W0612 

90 string.ascii_letters + string.digits + '_') 

91 

92 

93def validate_key(key: str, out: IO = sys.stderr) -> bool: 

94 """ 

95 Check if key consists of only shell valid characters. 

96 

97 :param key: UCR variable name to check. 

98 :param out: Output stream where error message is printed to. 

99 :returns: `True` if the name is valid, `False` otherwise. 

100 """ 

101 old = key 

102 key = replace_umlaut(key) 

103 

104 if old != key: 

105 print('Umlauts in config variable key are not recommended. Please consider renaming "%s" to %s.' % (old, key), file=out) 

106 # return False # Bug #53742 

107 

108 if len(key) > 0: 

109 if ': ' in key: 

110 print('Please fix invalid ": " in config variable key "%s".' % (key,), file=out) 

111 return False 

112 match = INVALID_KEY_CHARS.search(key) 

113 

114 if not match: 

115 return True 

116 print('Please fix invalid character "%s" in config variable key "%s".' % (match.group(), key), file=out) 

117 return False 

118 

119 

120INVALID_KEY_CHARS = re.compile('[][\r\n!"#$%&\'()+,;<=>?\\\\`{}§]') 

121 

122 

123def directory_files(directory: str) -> list[str]: 

124 """ 

125 Return a list of all files below the given directory. 

126 

127 :param directory: Base directory path. 

128 :returns: List of absolute file names. 

129 """ 

130 result = [] 

131 for dirpath, _dirnames, filenames in os.walk(directory): 

132 for filename in filenames: 

133 filename = os.path.join(dirpath, filename) 

134 if os.path.isfile(filename): 

135 result.append(filename) 

136 return result