# -*- coding: utf-8 -*-
#
# Univention Configuration Registry
# dictionary class for localized keys
#
# Copyright 2007-2022 Univention GmbH
#
# https://www.univention.de/
#
# All rights reserved.
#
# The source code of this program is made available
# under the terms of the GNU Affero General Public License version 3
# (GNU AGPL V3) as published by the Free Software Foundation.
#
# Binary versions of this program provided by Univention to you as
# well as other copyrighted, protected or trademarked materials like
# Logos, graphics, fonts, specific documentations and configurations,
# cryptographic keys etc. are subject to a license agreement between
# you and Univention and not subject to the GNU AGPL V3.
#
# In the case you use this program under the terms of the GNU AGPL V3,
# the program is provided in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public
# License with the Debian GNU/Linux or Univention distribution in file
# /usr/share/common-licenses/AGPL-3; if not, see
# <https://www.gnu.org/licenses/>.
import re
import six
from six.moves import configparser
try:
from typing import overload, Dict, Optional, TypeVar, Union # noqa: F401
_VT = TypeVar('_VT')
except ImportError:
def overload(f):
pass
# default locale
_locale = 'de'
MYPY = False
if MYPY: # pragma: no cover
__LVD = Dict[str, str]
else:
__LVD = dict
[docs]class LocalizedValue(__LVD):
"""Localized description entry."""
def __init__(self, *args, **kwargs):
tmp = dict(*args, **kwargs)
self.__default = tmp.pop('__default', '')
dict.__init__(self, tmp)
def __repr__(self):
return '%s(%s, __default=%r)' % (
self.__class__.__name__,
dict.__repr__(self),
self.__default,
)
[docs] def get(self, locale=None): # type: ignore
# type: (str) -> str
if not locale:
locale = _locale
if locale in self:
return self[locale]
return self.__default
[docs] def set(self, value, locale=None):
# type: (str, str) -> None
self[locale or _locale] = value
[docs] def set_default(self, default):
# type: (str) -> None
self.__default = default
[docs] def get_default(self):
# type: () -> str
return self.__default
if MYPY: # pragma: no cover
__LD = Dict[str, str]
else:
__LD = dict
[docs]class LocalizedDictionary(__LD):
"""Localized descriptions."""
_LOCALE_REGEX = re.compile(r'(?P<key>[a-zA-Z]*)\[(?P<lang>[a-z]*)\]$')
def __init__(self):
# type: () -> None
dict.__init__(self)
def __setitem__(self, key, value):
# type: (str, str) -> None
key = key.lower()
matches = LocalizedDictionary._LOCALE_REGEX.match(key)
# localized value?
if matches:
key, lang = matches.groups()
val = self.setdefault(key, LocalizedValue()) # type: ignore
if matches:
val.set(value, lang) # type: ignore
else:
val.set_default(value) # type: ignore
def __getitem__(self, key):
# type: (str) -> str
key = key.lower()
lang = None # type: Optional[str]
matches = LocalizedDictionary._LOCALE_REGEX.match(key)
# localized value?
if matches:
key, lang = matches.groups()
return dict.__getitem__(self, key).get(lang) # type: ignore
@overload
def get(self, key): # pragma: no cover
# type: (str) -> Optional[str]
pass
@overload
def get(self, key, default): # noqa: F811 # pragma: no cover
# type: (str, _VT) -> Union[str, _VT]
pass
[docs] def get(self, key, default=None): # noqa: F811
# type: (str, _VT) -> Union[str, _VT]
try:
value = self.__getitem__(key) or default
return value # type: ignore
except KeyError:
return default # type: ignore
def __contains__(self, key): # type: ignore
# type: (str) -> bool
key = key.lower()
matches = LocalizedDictionary._LOCALE_REGEX.match(key)
if matches:
key = matches.group(1)
return dict.__contains__(self, key)
has_key = __contains__ # type: ignore
def __normalize_key(self, key):
# type: (str) -> Dict[str, str]
if key not in self:
return {}
temp = {}
variable = dict.__getitem__(self, key) # type: LocalizedValue # type: ignore
for locale, value in variable.items():
temp['%s[%s]' % (key, locale)] = value
if variable.get_default(): # type: ignore
temp[key] = variable.get_default()
return temp
[docs] def normalize(self, key=None): # noqa: F811
# type: (str) -> Dict[str, str]
if key:
return self.__normalize_key(key)
temp = {} # type: Dict[str, str]
for key2 in self.keys():
temp.update(self.__normalize_key(key2))
return temp
[docs] def get_dict(self, key):
# type: (str) -> Dict[str, str]
if key not in self:
return {}
return dict.__getitem__(self, key) # type: ignore
def __eq__(self, other):
if not isinstance(other, dict):
return False
me = self.normalize()
you = other.normalize()
return dict.__eq__(me, you)
def __ne__(self, other):
return not self.__eq__(other)
# my config parser
[docs]class UnicodeConfig(configparser.ConfigParser):
def __init__(self):
if six.PY3:
configparser.ConfigParser.__init__(self, strict=False, interpolation=None)
else:
configparser.ConfigParser.__init__(self)
[docs] def read(self, filename, encoding='UTF-8'):
kwargs = {}
if six.PY3:
kwargs['encoding'] = encoding
return configparser.ConfigParser.read(self, filename, **kwargs)
[docs] def write(self, fp):
"""Write an .ini-format representation of the configuration state."""
if self._defaults:
fp.write("[%s]\n" % configparser.DEFAULTSECT)
for (key, value) in self._defaults.items():
fp.write("%s = %s\n" % (key, str(value).replace('\n', '\n\t')))
fp.write("\n")
for section in self._sections:
fp.write("[%s]\n" % section)
for (key, value) in self._sections[section].items():
if key != "__name__":
fp.write("%s = %s\n" % (key, value.replace('\n', '\n\t')))
fp.write("\n")
[docs]def set_language(lang):
global _locale
_locale = lang