##// END OF EJS Templates
chore(sync-up): synced libs/settings maker from vcsserver
super-admin -
r5337:363e5238 default
parent child Browse files
Show More
@@ -1,166 +1,184 b''
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2023 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
5 # (only), as published by the Free Software Foundation.
5 # (only), as published by the Free Software Foundation.
6 #
6 #
7 # This program is distributed in the hope that it will be useful,
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
10 # GNU General Public License for more details.
11 #
11 #
12 # You should have received a copy of the GNU Affero General Public License
12 # You should have received a copy of the GNU Affero General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 #
14 #
15 # This program is dual-licensed. If you wish to learn more about the
15 # This program is dual-licensed. If you wish to learn more about the
16 # RhodeCode Enterprise Edition, including its added features, Support services,
16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18
19 import os
19 import os
20 import textwrap
20 import textwrap
21 import string
21 import string
22 import functools
22 import functools
23 import logging
23 import logging
24 import tempfile
24 import tempfile
25 import logging.config
25 import logging.config
26
26 from rhodecode.lib.type_utils import str2bool, aslist
27 from rhodecode.lib.type_utils import str2bool, aslist
27
28
28 log = logging.getLogger(__name__)
29 log = logging.getLogger(__name__)
29
30
30
31
31 # skip keys, that are set here, so we don't double process those
32 # skip keys, that are set here, so we don't double process those
32 set_keys = {
33 set_keys = {
33 '__file__': ''
34 '__file__': ''
34 }
35 }
35
36
36
37
37 class SettingsMaker(object):
38 class SettingsMaker:
38
39
39 def __init__(self, app_settings):
40 def __init__(self, app_settings):
40 self.settings = app_settings
41 self.settings = app_settings
41
42
42 @classmethod
43 @classmethod
43 def _bool_func(cls, input_val):
44 def _bool_func(cls, input_val):
45 if isinstance(input_val, bytes):
46 # decode to str
47 input_val = input_val.decode('utf8')
44 return str2bool(input_val)
48 return str2bool(input_val)
45
49
46 @classmethod
50 @classmethod
47 def _int_func(cls, input_val):
51 def _int_func(cls, input_val):
48 return int(input_val)
52 return int(input_val)
49
53
50 @classmethod
54 @classmethod
51 def _float_func(cls, input_val):
55 def _float_func(cls, input_val):
52 return float(input_val)
56 return float(input_val)
53
57
54 @classmethod
58 @classmethod
55 def _list_func(cls, input_val, sep=','):
59 def _list_func(cls, input_val, sep=','):
56 return aslist(input_val, sep=sep)
60 return aslist(input_val, sep=sep)
57
61
58 @classmethod
62 @classmethod
59 def _string_func(cls, input_val, lower=True):
63 def _string_func(cls, input_val, lower=True):
60 if lower:
64 if lower:
61 input_val = input_val.lower()
65 input_val = input_val.lower()
62 return input_val
66 return input_val
63
67
64 @classmethod
68 @classmethod
69 def _string_no_quote_func(cls, input_val, lower=True):
70 """
71 Special case string function that detects if value is set to empty quote string
72 e.g.
73
74 core.binar_dir = ""
75 """
76
77 input_val = cls._string_func(input_val, lower=lower)
78 if input_val in ['""', "''"]:
79 return ''
80
81 @classmethod
65 def _dir_func(cls, input_val, ensure_dir=False, mode=0o755):
82 def _dir_func(cls, input_val, ensure_dir=False, mode=0o755):
66
83
67 # ensure we have our dir created
84 # ensure we have our dir created
68 if not os.path.isdir(input_val) and ensure_dir:
85 if not os.path.isdir(input_val) and ensure_dir:
69 os.makedirs(input_val, mode=mode)
86 os.makedirs(input_val, mode=mode, exist_ok=True)
70
87
71 if not os.path.isdir(input_val):
88 if not os.path.isdir(input_val):
72 raise Exception(f'Dir at {input_val} does not exist')
89 raise Exception(f'Dir at {input_val} does not exist')
73 return input_val
90 return input_val
74
91
75 @classmethod
92 @classmethod
76 def _file_path_func(cls, input_val, ensure_dir=False, mode=0o755):
93 def _file_path_func(cls, input_val, ensure_dir=False, mode=0o755):
77 dirname = os.path.dirname(input_val)
94 dirname = os.path.dirname(input_val)
78 cls._dir_func(dirname, ensure_dir=ensure_dir)
95 cls._dir_func(dirname, ensure_dir=ensure_dir)
79 return input_val
96 return input_val
80
97
81 @classmethod
98 @classmethod
82 def _key_transformator(cls, key):
99 def _key_transformator(cls, key):
83 return "{}_{}".format('RC'.upper(), key.upper().replace('.', '_').replace('-', '_'))
100 return "{}_{}".format('RC'.upper(), key.upper().replace('.', '_').replace('-', '_'))
84
101
85 def maybe_env_key(self, key):
102 def maybe_env_key(self, key):
86 # now maybe we have this KEY in env, search and use the value with higher priority.
103 # now maybe we have this KEY in env, search and use the value with higher priority.
87 transformed_key = self._key_transformator(key)
104 transformed_key = self._key_transformator(key)
88 envvar_value = os.environ.get(transformed_key)
105 envvar_value = os.environ.get(transformed_key)
89 if envvar_value:
106 if envvar_value:
90 log.debug('using `%s` key instead of `%s` key for config', transformed_key, key)
107 log.debug('using `%s` key instead of `%s` key for config', transformed_key, key)
91
108
92 return envvar_value
109 return envvar_value
93
110
94 def env_expand(self):
111 def env_expand(self):
95 replaced = {}
112 replaced = {}
96 for k, v in self.settings.items():
113 for k, v in self.settings.items():
97 if k not in set_keys:
114 if k not in set_keys:
98 envvar_value = self.maybe_env_key(k)
115 envvar_value = self.maybe_env_key(k)
99 if envvar_value:
116 if envvar_value:
100 replaced[k] = envvar_value
117 replaced[k] = envvar_value
101 set_keys[k] = envvar_value
118 set_keys[k] = envvar_value
102
119
103 # replace ALL keys updated
120 # replace ALL keys updated
104 self.settings.update(replaced)
121 self.settings.update(replaced)
105
122
106 def enable_logging(self, logging_conf=None, level='INFO', formatter='generic'):
123 def enable_logging(self, logging_conf=None, level='INFO', formatter='generic'):
107 """
124 """
108 Helper to enable debug on running instance
125 Helper to enable debug on running instance
109 :return:
126 :return:
110 """
127 """
111
128
112 if not str2bool(self.settings.get('logging.autoconfigure')):
129 if not str2bool(self.settings.get('logging.autoconfigure')):
113 log.info('logging configuration based on main .ini file')
130 log.info('logging configuration based on main .ini file')
114 return
131 return
115
132
116 if logging_conf is None:
133 if logging_conf is None:
117 logging_conf = self.settings.get('logging.logging_conf_file') or ''
134 logging_conf = self.settings.get('logging.logging_conf_file') or ''
118
135
119 if not os.path.isfile(logging_conf):
136 if not os.path.isfile(logging_conf):
120 log.error('Unable to setup logging based on %s, '
137 log.error('Unable to setup logging based on %s, '
121 'file does not exist.... specify path using logging.logging_conf_file= config setting. ', logging_conf)
138 'file does not exist.... specify path using logging.logging_conf_file= config setting. ', logging_conf)
122 return
139 return
123
140
124 with open(logging_conf, 'rt') as f:
141 with open(logging_conf, 'rt') as f:
125 ini_template = textwrap.dedent(f.read())
142 ini_template = textwrap.dedent(f.read())
126 ini_template = string.Template(ini_template).safe_substitute(
143 ini_template = string.Template(ini_template).safe_substitute(
127 RC_LOGGING_LEVEL=os.environ.get('RC_LOGGING_LEVEL', '') or level,
144 RC_LOGGING_LEVEL=os.environ.get('RC_LOGGING_LEVEL', '') or level,
128 RC_LOGGING_FORMATTER=os.environ.get('RC_LOGGING_FORMATTER', '') or formatter
145 RC_LOGGING_FORMATTER=os.environ.get('RC_LOGGING_FORMATTER', '') or formatter
129 )
146 )
130
147
131 with tempfile.NamedTemporaryFile(prefix='rc_logging_', suffix='.ini', delete=False) as f:
148 with tempfile.NamedTemporaryFile(prefix='rc_logging_', suffix='.ini', delete=False) as f:
132 log.info('Saved Temporary LOGGING config at %s', f.name)
149 log.info('Saved Temporary LOGGING config at %s', f.name)
133 f.write(ini_template)
150 f.write(ini_template)
134
151
135 logging.config.fileConfig(f.name)
152 logging.config.fileConfig(f.name)
136 os.remove(f.name)
153 os.remove(f.name)
137
154
138 def make_setting(self, key, default, lower=False, default_when_empty=False, parser=None):
155 def make_setting(self, key, default, lower=False, default_when_empty=False, parser=None):
139 input_val = self.settings.get(key, default)
156 input_val = self.settings.get(key, default)
140
157
141 if default_when_empty and not input_val:
158 if default_when_empty and not input_val:
142 # use default value when value is set in the config but it is empty
159 # use default value when value is set in the config but it is empty
143 input_val = default
160 input_val = default
144
161
145 parser_func = {
162 parser_func = {
146 'bool': self._bool_func,
163 'bool': self._bool_func,
147 'int': self._int_func,
164 'int': self._int_func,
148 'float': self._float_func,
165 'float': self._float_func,
149 'list': self._list_func,
166 'list': self._list_func,
150 'list:newline': functools.partial(self._list_func, sep='/n'),
167 'list:newline': functools.partial(self._list_func, sep='/n'),
151 'list:spacesep': functools.partial(self._list_func, sep=' '),
168 'list:spacesep': functools.partial(self._list_func, sep=' '),
152 'string': functools.partial(self._string_func, lower=lower),
169 'string': functools.partial(self._string_func, lower=lower),
170 'string:noquote': functools.partial(self._string_no_quote_func, lower=lower),
153 'dir': self._dir_func,
171 'dir': self._dir_func,
154 'dir:ensured': functools.partial(self._dir_func, ensure_dir=True),
172 'dir:ensured': functools.partial(self._dir_func, ensure_dir=True),
155 'file': self._file_path_func,
173 'file': self._file_path_func,
156 'file:ensured': functools.partial(self._file_path_func, ensure_dir=True),
174 'file:ensured': functools.partial(self._file_path_func, ensure_dir=True),
157 None: lambda i: i
175 None: lambda i: i
158 }[parser]
176 }[parser]
159
177
160 envvar_value = self.maybe_env_key(key)
178 envvar_value = self.maybe_env_key(key)
161 if envvar_value:
179 if envvar_value:
162 input_val = envvar_value
180 input_val = envvar_value
163 set_keys[key] = input_val
181 set_keys[key] = input_val
164
182
165 self.settings[key] = parser_func(input_val)
183 self.settings[key] = parser_func(input_val)
166 return self.settings[key]
184 return self.settings[key]
@@ -1,169 +1,183 b''
1 # Copyright (C) 2011-2023 RhodeCode GmbH
1 # Copyright (C) 2011-2023 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
5 # (only), as published by the Free Software Foundation.
5 # (only), as published by the Free Software Foundation.
6 #
6 #
7 # This program is distributed in the hope that it will be useful,
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
10 # GNU General Public License for more details.
11 #
11 #
12 # You should have received a copy of the GNU Affero General Public License
12 # You should have received a copy of the GNU Affero General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 #
14 #
15 # This program is dual-licensed. If you wish to learn more about the
15 # This program is dual-licensed. If you wish to learn more about the
16 # RhodeCode Enterprise Edition, including its added features, Support services,
16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18
19 import typing
19 import typing
20 import base64
20 import base64
21 import logging
21 import logging
22 from unidecode import unidecode
22 from unidecode import unidecode
23
23
24 import rhodecode
24 import rhodecode
25 from rhodecode.lib.type_utils import aslist
25 from rhodecode.lib.type_utils import aslist
26
26
27
27
28 log = logging.getLogger(__name__)
28 log = logging.getLogger(__name__)
29
29
30
30
31 def safe_int(val, default=None) -> int:
31 def safe_int(val, default=None) -> int:
32 """
32 """
33 Returns int() of val if val is not convertable to int use default
33 Returns int() of val if val is not convertable to int use default
34 instead
34 instead
35
35
36 :param val:
36 :param val:
37 :param default:
37 :param default:
38 """
38 """
39
39
40 try:
40 try:
41 val = int(val)
41 val = int(val)
42 except (ValueError, TypeError):
42 except (ValueError, TypeError):
43 val = default
43 val = default
44
44
45 return val
45 return val
46
46
47
47
48 def safe_float(val, default=None) -> float:
48 def safe_float(val, default=None) -> float:
49 """
49 """
50 Returns float() of val if val is not convertable to float use default
50 Returns float() of val if val is not convertable to float use default
51 instead
51 instead
52
52
53 :param val:
53 :param val:
54 :param default:
54 :param default:
55 """
55 """
56
56
57 try:
57 try:
58 val = float(val)
58 val = float(val)
59 except (ValueError, TypeError):
59 except (ValueError, TypeError):
60 val = default
60 val = default
61
61
62 return val
62 return val
63
63
64
64
65 def base64_to_str(text: str | bytes) -> str:
65 def base64_to_str(text: str | bytes) -> str:
66 return safe_str(base64.encodebytes(safe_bytes(text))).strip()
66 return safe_str(base64.encodebytes(safe_bytes(text))).strip()
67
67
68
68
69 def get_default_encodings() -> list[str]:
69 def get_default_encodings() -> list[str]:
70 return aslist(rhodecode.CONFIG.get('default_encoding', 'utf8'), sep=',')
70 return aslist(rhodecode.CONFIG.get('default_encoding', 'utf8'), sep=',')
71
71
72
72
73 DEFAULT_ENCODINGS = get_default_encodings()
73 DEFAULT_ENCODINGS = get_default_encodings()
74
74
75
75
76 def safe_str(str_, to_encoding=None) -> str:
76 def safe_str(str_, to_encoding=None) -> str:
77 """
77 """
78 safe str function. Does few trick to turn unicode_ into string
78 safe str function. Does few trick to turn unicode_ into string
79
79
80 :param str_: str to encode
80 :param str_: str to encode
81 :param to_encoding: encode to this type UTF8 default
81 :param to_encoding: encode to this type UTF8 default
82 """
82 """
83 if isinstance(str_, str):
83 if isinstance(str_, str):
84 return str_
84 return str_
85
85
86 # if it's bytes cast to str
86 # if it's bytes cast to str
87 if not isinstance(str_, bytes):
87 if not isinstance(str_, bytes):
88 return str(str_)
88 return str(str_)
89
89
90 to_encoding = to_encoding or DEFAULT_ENCODINGS
90 to_encoding = to_encoding or DEFAULT_ENCODINGS
91 if not isinstance(to_encoding, (list, tuple)):
91 if not isinstance(to_encoding, (list, tuple)):
92 to_encoding = [to_encoding]
92 to_encoding = [to_encoding]
93
93
94 for enc in to_encoding:
94 for enc in to_encoding:
95 try:
95 try:
96 return str(str_, enc)
96 return str(str_, enc)
97 except UnicodeDecodeError:
97 except UnicodeDecodeError:
98 pass
98 pass
99
99
100 return str(str_, to_encoding[0], 'replace')
100 return str(str_, to_encoding[0], 'replace')
101
101
102
102
103 def safe_bytes(str_, from_encoding=None) -> bytes:
103 def safe_bytes(str_, from_encoding=None) -> bytes:
104 """
104 """
105 safe bytes function. Does few trick to turn str_ into bytes string:
105 safe bytes function. Does few trick to turn str_ into bytes string:
106
106
107 :param str_: string to decode
107 :param str_: string to decode
108 :param from_encoding: encode from this type UTF8 default
108 :param from_encoding: encode from this type UTF8 default
109 """
109 """
110 if isinstance(str_, bytes):
110 if isinstance(str_, bytes):
111 return str_
111 return str_
112
112
113 if not isinstance(str_, str):
113 if not isinstance(str_, str):
114 raise ValueError(f'safe_bytes cannot convert other types than str: got: {type(str_)}')
114 raise ValueError(f'safe_bytes cannot convert other types than str: got: {type(str_)}')
115
115
116 from_encoding = from_encoding or get_default_encodings()
116 from_encoding = from_encoding or get_default_encodings()
117 if not isinstance(from_encoding, (list, tuple)):
117 if not isinstance(from_encoding, (list, tuple)):
118 from_encoding = [from_encoding]
118 from_encoding = [from_encoding]
119
119
120 for enc in from_encoding:
120 for enc in from_encoding:
121 try:
121 try:
122 return str_.encode(enc)
122 return str_.encode(enc)
123 except UnicodeDecodeError:
123 except UnicodeDecodeError:
124 pass
124 pass
125
125
126 return str_.encode(from_encoding[0], 'replace')
126 return str_.encode(from_encoding[0], 'replace')
127
127
128
128
129 def ascii_bytes(str_, allow_bytes=False) -> bytes:
129 def ascii_bytes(str_, allow_bytes=False) -> bytes:
130 """
130 """
131 Simple conversion from str to bytes, with assumption that str_ is pure ASCII.
131 Simple conversion from str to bytes, with assumption that str_ is pure ASCII.
132 Fails with UnicodeError on invalid input.
132 Fails with UnicodeError on invalid input.
133 This should be used where encoding and "safe" ambiguity should be avoided.
133 This should be used where encoding and "safe" ambiguity should be avoided.
134 Where strings already have been encoded in other ways but still are unicode
134 Where strings already have been encoded in other ways but still are unicode
135 string - for example to hex, base64, json, urlencoding, or are known to be
135 string - for example to hex, base64, json, urlencoding, or are known to be
136 identifiers.
136 identifiers.
137 """
137 """
138 if allow_bytes and isinstance(str_, bytes):
138 if allow_bytes and isinstance(str_, bytes):
139 return str_
139 return str_
140
140
141 if not isinstance(str_, str):
141 if not isinstance(str_, str):
142 raise ValueError(f'ascii_bytes cannot convert other types than str: got: {type(str_)}')
142 raise ValueError(f'ascii_bytes cannot convert other types than str: got: {type(str_)}')
143 return str_.encode('ascii')
143 return str_.encode('ascii')
144
144
145
145
146 def ascii_str(str_) -> str:
146 def ascii_str(str_) -> str:
147 """
147 """
148 Simple conversion from bytes to str, with assumption that str_ is pure ASCII.
148 Simple conversion from bytes to str, with assumption that str_ is pure ASCII.
149 Fails with UnicodeError on invalid input.
149 Fails with UnicodeError on invalid input.
150 This should be used where encoding and "safe" ambiguity should be avoided.
150 This should be used where encoding and "safe" ambiguity should be avoided.
151 Where strings are encoded but also in other ways are known to be ASCII, and
151 Where strings are encoded but also in other ways are known to be ASCII, and
152 where a unicode string is wanted without caring about encoding. For example
152 where a unicode string is wanted without caring about encoding. For example
153 to hex, base64, urlencoding, or are known to be identifiers.
153 to hex, base64, urlencoding, or are known to be identifiers.
154 """
154 """
155
155
156 if not isinstance(str_, bytes):
156 if not isinstance(str_, bytes):
157 raise ValueError(f'ascii_str cannot convert other types than bytes: got: {type(str_)}')
157 raise ValueError(f'ascii_str cannot convert other types than bytes: got: {type(str_)}')
158 return str_.decode('ascii')
158 return str_.decode('ascii')
159
159
160
160
161 def convert_special_chars(str_) -> str:
161 def convert_special_chars(str_) -> str:
162 """
162 """
163 trie to replace non-ascii letters to their ascii representation eg::
163 trie to replace non-ascii letters to their ascii representation eg::
164
164
165 `żołw` converts into `zolw`
165 `żołw` converts into `zolw`
166 """
166 """
167 value = safe_str(str_)
167 value = safe_str(str_)
168 converted_value = unidecode(value)
168 converted_value = unidecode(value)
169 return converted_value
169 return converted_value
170
171
172 def splitnewlines(text: bytes):
173 """
174 like splitlines, but only split on newlines.
175 """
176
177 lines = [_l + b'\n' for _l in text.split(b'\n')]
178 if lines:
179 if lines[-1] == b'\n':
180 lines.pop()
181 else:
182 lines[-1] = lines[-1][:-1]
183 return lines
General Comments 0
You need to be logged in to leave comments. Login now