Coverage for python/univention/info_tools.py: 99%
108 statements
« prev ^ index » next coverage.py v7.6.0, created at 2026-09-11 14:11 +0000
« prev ^ index » next coverage.py v7.6.0, created at 2026-09-11 14:11 +0000
1#
2# Univention Configuration Registry
3# dictionary class for localized keys
4#
5# SPDX-FileCopyrightText: 2007-2026 Univention GmbH
6# SPDX-License-Identifier: AGPL-3.0-only
8import configparser
9import re
10from typing import TypeVar, overload
13_VT = TypeVar('_VT')
15# default locale
16_locale = 'de'
17MYPY = False
20if MYPY: # pragma: no cover
21 __LVD = dict[str, str]
22else:
23 __LVD = dict
26class LocalizedValue(__LVD):
27 """Localized description entry."""
29 def __init__(self, *args, **kwargs):
30 tmp = dict(*args, **kwargs)
31 self.__default = tmp.pop('__default', '')
32 dict.__init__(self, tmp)
34 def __repr__(self):
35 return '%s(%s, __default=%r)' % (
36 self.__class__.__name__,
37 dict.__repr__(self),
38 self.__default,
39 )
41 def get(self, locale: str | None = None) -> str: # type: ignore
42 if not locale:
43 locale = _locale
44 if locale in self:
45 return self[locale]
46 return self.__default
48 def set(self, value: str, locale: str | None = None) -> None:
49 self[locale or _locale] = value
51 def set_default(self, default: str) -> None:
52 self.__default = default
54 def get_default(self) -> str:
55 return self.__default
58if MYPY: # pragma: no cover
59 __LD = dict[str, str]
60else:
61 __LD = dict
64class LocalizedDictionary(__LD): # noqa: PLW1641
65 """Localized descriptions."""
67 _LOCALE_REGEX = re.compile(r'(?P<key>[a-zA-Z]*)\[(?P<lang>[a-z]*)\]$')
69 def __init__(self) -> None:
70 dict.__init__(self)
72 def __setitem__(self, key: str, value: str) -> None:
73 key = key.lower()
74 matches = LocalizedDictionary._LOCALE_REGEX.match(key)
75 # localized value?
76 if matches:
77 key, lang = matches.groups()
79 val = self.setdefault(key, LocalizedValue()) # type: ignore
80 if matches:
81 val.set(value, lang) # type: ignore
82 else:
83 val.set_default(value) # type: ignore
85 def __getitem__(self, key: str) -> str:
86 key = key.lower()
87 lang: str | None = None
88 matches = LocalizedDictionary._LOCALE_REGEX.match(key)
89 # localized value?
90 if matches:
91 key, lang = matches.groups()
93 return dict.__getitem__(self, key).get(lang) # type: ignore
95 @overload
96 def get(self, key: str) -> str | None: # pragma: no cover
97 pass
99 @overload
100 def get(self, key: str, default: _VT) -> str | _VT: # pragma: no cover
101 pass
103 def get(self, key: str, default: _VT | None = None) -> str | _VT:
104 try:
105 value = self.__getitem__(key) or default
106 return value # type: ignore
107 except KeyError:
108 return default # type: ignore
110 def __contains__(self, key: str) -> bool: # type: ignore
111 key = key.lower()
112 matches = LocalizedDictionary._LOCALE_REGEX.match(key)
113 if matches:
114 key = matches.group(1)
115 return dict.__contains__(self, key) # type: ignore[operator]
116 has_key = __contains__ # type: ignore
118 def __normalize_key(self, key: str) -> dict[str, str]:
119 if key not in self:
120 return {}
122 temp = {}
123 variable: LocalizedValue = dict.__getitem__(self, key) # type: ignore
124 for locale, value in variable.items():
125 temp['%s[%s]' % (key, locale)] = value
127 if variable.get_default(): # type: ignore
128 temp[key] = variable.get_default()
130 return temp
132 def normalize(self, key: str | None = None) -> dict[str, str]:
133 if key:
134 return self.__normalize_key(key)
135 temp: dict[str, str] = {}
136 for key2 in self.keys():
137 temp.update(self.__normalize_key(key2))
138 return temp
140 def get_dict(self, key: str) -> dict[str, str]:
141 if key not in self:
142 return {}
143 return dict.__getitem__(self, key) # type: ignore
145 def __eq__(self, other):
146 if not isinstance(other, dict):
147 return False
148 me = self.normalize()
149 you = other.normalize()
150 return dict.__eq__(me, you)
152 def __ne__(self, other):
153 return not self.__eq__(other)
156# my config parser
157class UnicodeConfig(configparser.ConfigParser):
159 def __init__(self):
160 configparser.ConfigParser.__init__(self, strict=False, interpolation=None)
162 def read(self, filename, encoding='UTF-8'):
163 return configparser.ConfigParser.read(self, filename, encoding=encoding)
165 def write(self, fp):
166 """Write an .ini-format representation of the configuration state."""
167 if self._defaults:
168 fp.write("[%s]\n" % configparser.DEFAULTSECT)
169 for (key, value) in self._defaults.items():
170 fp.write("%s = %s\n" % (key, str(value).replace('\n', '\n\t')))
171 fp.write("\n")
172 for section in self._sections:
173 fp.write("[%s]\n" % section)
174 for (key, value) in self._sections[section].items():
175 if key != "__name__": 175 ↛ 174line 175 didn't jump to line 174 because the condition on line 175 was always true
176 fp.write("%s = %s\n" % (key, value.replace('\n', '\n\t')))
177 fp.write("\n")
180def set_language(lang):
181 global _locale
182 _locale = lang