the1conf 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- the1conf/__init__.py +4 -0
- the1conf/app_config.py +643 -0
- the1conf/attr_dict.py +155 -0
- the1conf/click_option.py +35 -0
- the1conf/config_var.py +626 -0
- the1conf/py.typed +0 -0
- the1conf-1.0.0.dist-info/METADATA +516 -0
- the1conf-1.0.0.dist-info/RECORD +10 -0
- the1conf-1.0.0.dist-info/WHEEL +4 -0
- the1conf-1.0.0.dist-info/licenses/LICENSE +21 -0
the1conf/config_var.py
ADDED
|
@@ -0,0 +1,626 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import copy
|
|
4
|
+
from collections.abc import Sequence, Callable
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from enum import Enum, auto
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import (
|
|
9
|
+
Any,
|
|
10
|
+
Generic,
|
|
11
|
+
Optional,
|
|
12
|
+
TypeAlias,
|
|
13
|
+
TypeVar,
|
|
14
|
+
Union,
|
|
15
|
+
get_args,
|
|
16
|
+
get_origin,
|
|
17
|
+
get_type_hints,
|
|
18
|
+
dataclass_transform,
|
|
19
|
+
TYPE_CHECKING,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
if TYPE_CHECKING:
|
|
24
|
+
from .app_config import AppConfig
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class _UndefinedSentinel:
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
Undefined: TypeAlias = _UndefinedSentinel
|
|
32
|
+
_undef: Undefined = _UndefinedSentinel()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class PathType(Enum):
|
|
36
|
+
Dir = auto()
|
|
37
|
+
File = auto()
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
__NO_SEARCH__ = "__NOSEARCH__"
|
|
41
|
+
|
|
42
|
+
_T = TypeVar("_T")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class Flags:
|
|
46
|
+
"""
|
|
47
|
+
Flags that control how to search for variable values in AppConfig.
|
|
48
|
+
These flags can be set globally in the AppConfig object or locally when calling resolve_vars() or
|
|
49
|
+
per config variables.
|
|
50
|
+
|
|
51
|
+
Not None flag values override other values with the following precedence:
|
|
52
|
+
ConfigVariableDef > resolve_vars() parameters > AppConfig global flags.
|
|
53
|
+
Which means that a not None value in a ConfVarDef directive will overrride a global flag value defined in AppConfig. And also that a None value in a ConfVarDef directive
|
|
54
|
+
will not override the value set in resolve_vars() parameters or an AppConfig global flags.
|
|
55
|
+
|
|
56
|
+
Attributes:
|
|
57
|
+
no_env_search (bool|None): don't search values in the environment.
|
|
58
|
+
no_value_search (bool|None): don't search values in the values dict.
|
|
59
|
+
no_conffile_search (bool|None): don't search values in the configuration file.
|
|
60
|
+
no_search (bool|None): don't search values in any location.
|
|
61
|
+
click_key_conversion (bool|None): use the variable name converted to lower case and with dashes converted to underscores as
|
|
62
|
+
the key to search in values.
|
|
63
|
+
allow_override (bool|None): allow overriding variable in the configuration when calling resolve_vars.
|
|
64
|
+
no_exception (bool|None): don't raise exception when no value is found for a variable.
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
_no_env_search: bool | None
|
|
68
|
+
_no_value_search: bool | None
|
|
69
|
+
_no_conffile_search: bool | None
|
|
70
|
+
_no_search: bool | None
|
|
71
|
+
_click_key_conversion: bool | None
|
|
72
|
+
_allow_override: bool | None
|
|
73
|
+
_no_exception: bool | None
|
|
74
|
+
|
|
75
|
+
def __init__(
|
|
76
|
+
self,
|
|
77
|
+
*,
|
|
78
|
+
no_env_search: Optional[bool] = None,
|
|
79
|
+
no_value_search: Optional[bool] = None,
|
|
80
|
+
no_conffile_search: Optional[bool] = None,
|
|
81
|
+
no_search: Optional[bool] = None,
|
|
82
|
+
click_key_conversion: Optional[bool] = None,
|
|
83
|
+
allow_override: Optional[bool] = None,
|
|
84
|
+
no_exception: Optional[bool] = None,
|
|
85
|
+
) -> None:
|
|
86
|
+
self._no_env_search = no_env_search
|
|
87
|
+
self._no_value_search = no_value_search
|
|
88
|
+
self._no_conffile_search = no_conffile_search
|
|
89
|
+
self._no_search = no_search
|
|
90
|
+
if self._no_search:
|
|
91
|
+
self._no_env_search = True
|
|
92
|
+
self._no_value_search = True
|
|
93
|
+
self._no_conffile_search = True
|
|
94
|
+
self._click_key_conversion = click_key_conversion
|
|
95
|
+
self._allow_override = allow_override
|
|
96
|
+
self._no_exception = no_exception
|
|
97
|
+
|
|
98
|
+
def merge(self, *, inplace: bool = False, other: Flags) -> Flags | None:
|
|
99
|
+
"""
|
|
100
|
+
Merge the current flags with the given flags.
|
|
101
|
+
If inplace is True then modify the current object, otherwise return a new Flags object.
|
|
102
|
+
|
|
103
|
+
Flags given as parameters have precedence over the current object values if they are not None.
|
|
104
|
+
"""
|
|
105
|
+
cur_no_search: Optional[bool]
|
|
106
|
+
cur_no_env_search: Optional[bool]
|
|
107
|
+
cur_no_value_search: Optional[bool]
|
|
108
|
+
cur_no_conffile_search: Optional[bool]
|
|
109
|
+
cur_click_key_conversion: Optional[bool]
|
|
110
|
+
cur_allow_override: Optional[bool]
|
|
111
|
+
cur_no_exception: Optional[bool]
|
|
112
|
+
|
|
113
|
+
cur_no_search = (
|
|
114
|
+
other._no_search if other._no_search is not None else self._no_search
|
|
115
|
+
)
|
|
116
|
+
if cur_no_search:
|
|
117
|
+
cur_no_env_search = True
|
|
118
|
+
cur_no_value_search = True
|
|
119
|
+
cur_no_conffile_search = True
|
|
120
|
+
else:
|
|
121
|
+
cur_no_env_search = (
|
|
122
|
+
other._no_env_search
|
|
123
|
+
if other._no_env_search is not None
|
|
124
|
+
else self._no_env_search
|
|
125
|
+
)
|
|
126
|
+
cur_no_value_search = (
|
|
127
|
+
other._no_value_search
|
|
128
|
+
if other._no_value_search is not None
|
|
129
|
+
else self._no_value_search
|
|
130
|
+
)
|
|
131
|
+
cur_no_conffile_search = (
|
|
132
|
+
other._no_conffile_search
|
|
133
|
+
if other._no_conffile_search is not None
|
|
134
|
+
else self._no_conffile_search
|
|
135
|
+
)
|
|
136
|
+
cur_click_key_conversion = (
|
|
137
|
+
other._click_key_conversion
|
|
138
|
+
if other._click_key_conversion is not None
|
|
139
|
+
else self._click_key_conversion
|
|
140
|
+
)
|
|
141
|
+
cur_allow_override = (
|
|
142
|
+
other._allow_override
|
|
143
|
+
if other._allow_override is not None
|
|
144
|
+
else self._allow_override
|
|
145
|
+
)
|
|
146
|
+
cur_no_exception = (
|
|
147
|
+
other._no_exception if other._no_exception is not None else self._no_exception
|
|
148
|
+
)
|
|
149
|
+
if not inplace:
|
|
150
|
+
return Flags(
|
|
151
|
+
no_env_search=cur_no_env_search,
|
|
152
|
+
no_value_search=cur_no_value_search,
|
|
153
|
+
no_conffile_search=cur_no_conffile_search,
|
|
154
|
+
no_search=cur_no_search,
|
|
155
|
+
click_key_conversion=cur_click_key_conversion,
|
|
156
|
+
allow_override=cur_allow_override,
|
|
157
|
+
no_exception=cur_no_exception,
|
|
158
|
+
)
|
|
159
|
+
else:
|
|
160
|
+
self._no_env_search = cur_no_env_search
|
|
161
|
+
self._no_value_search = cur_no_value_search
|
|
162
|
+
self._no_conffile_search = cur_no_conffile_search
|
|
163
|
+
self._no_search = cur_no_search
|
|
164
|
+
self._click_key_conversion = cur_click_key_conversion
|
|
165
|
+
self._allow_override = cur_allow_override
|
|
166
|
+
self._no_exception = cur_no_exception
|
|
167
|
+
return None
|
|
168
|
+
|
|
169
|
+
@property
|
|
170
|
+
def no_env_search(self) -> bool:
|
|
171
|
+
return (
|
|
172
|
+
False
|
|
173
|
+
if self._no_env_search is None or self._no_env_search == __NO_SEARCH__
|
|
174
|
+
else self._no_env_search
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
@property
|
|
178
|
+
def no_value_search(self) -> bool:
|
|
179
|
+
return (
|
|
180
|
+
False
|
|
181
|
+
if self._no_value_search is None or self._no_value_search == __NO_SEARCH__
|
|
182
|
+
else self._no_value_search
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
@property
|
|
186
|
+
def no_conffile_search(self) -> bool:
|
|
187
|
+
return (
|
|
188
|
+
False
|
|
189
|
+
if self._no_conffile_search is None
|
|
190
|
+
or self._no_conffile_search == __NO_SEARCH__
|
|
191
|
+
else self._no_conffile_search
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
@property
|
|
195
|
+
def no_search(self) -> bool:
|
|
196
|
+
return False if self._no_search is None else self._no_search
|
|
197
|
+
|
|
198
|
+
@property
|
|
199
|
+
def click_key_conversion(self) -> bool:
|
|
200
|
+
return False if self._click_key_conversion is None else self._click_key_conversion
|
|
201
|
+
|
|
202
|
+
@property
|
|
203
|
+
def allow_override(self) -> bool:
|
|
204
|
+
return False if self._allow_override is None else self._allow_override
|
|
205
|
+
|
|
206
|
+
@property
|
|
207
|
+
def no_exception(self) -> bool:
|
|
208
|
+
return False if self._no_exception is None else self._no_exception
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
@dataclass_transform()
|
|
212
|
+
class ConfigVarDef(Generic[_T]):
|
|
213
|
+
"""
|
|
214
|
+
Definition of an application configuration variable.
|
|
215
|
+
|
|
216
|
+
Attribute of this class are called configuration directives that are used to specify the configuration of the application with the AppConfig class.
|
|
217
|
+
|
|
218
|
+
Configuration are used indirectly by defining attributes of an AppConfig subclass with the configvar() function that creates ConfigVarDef objects:
|
|
219
|
+
class MyAppConfig(AppConfig):
|
|
220
|
+
my_var: int = configvar(Default=1)
|
|
221
|
+
|
|
222
|
+
The method resolve_vars() of the AppConfig class uses these configuration variable definitions to look for the values of the variables as explained in its documentation.
|
|
223
|
+
Each configuration variable definition contains several directives to define how to look for the values of the variables in a dictionnarary that can be the CLI parameters,
|
|
224
|
+
or a configuration file, or an environment variables or a computation defined by a python Callable.
|
|
225
|
+
|
|
226
|
+
Two directive are not specified in the constructor of ConfigVarDef because they are inferred from the attribute name and type hint:
|
|
227
|
+
- Name : the name of the variable, inferred from the attribute name.
|
|
228
|
+
- TypeInfo : the type to which to cast the variable value, inferred from the attribute type hint. Must be a python builtin type or a class or a list of
|
|
229
|
+
builtin types.
|
|
230
|
+
Here how to get the list of python builtin types:
|
|
231
|
+
print([t.__name__ for t in __builtins__.__dict__.values() if isinstance(t, type)])
|
|
232
|
+
Lists are handle with a specific processing detail below in the paragraph 'Lists'.
|
|
233
|
+
Complex casting can be handle with a transfom Eval Form or with a class that implements the complex type.
|
|
234
|
+
Optional value are supported like Optional[int] or Union[str, None] to indicate that the variable can be None.
|
|
235
|
+
When the type hint is missing the variable is considered to be of type str.
|
|
236
|
+
|
|
237
|
+
The directives that can be specified in configvar() are:
|
|
238
|
+
|
|
239
|
+
- Help [optional]: a help string that describes the variable.
|
|
240
|
+
- Default [optional]: the default value, can be a value or an Eval Form (see below explanation on Eval forms) whose
|
|
241
|
+
returned value will be the value of the variable if no other value has been found.
|
|
242
|
+
See paragraph about None below for more information.
|
|
243
|
+
- EnvName [optional ]: the name of the environment variable that can contain the value. By default search for Name
|
|
244
|
+
- FileKey [optional ]: the key to look for in the configuration file, key can be in the form a1.a2.a3 (attributes separated by dots) to indicate
|
|
245
|
+
nested namespaces (see AppConfig documentation). By default search for Name.
|
|
246
|
+
- ValueKey [optional ]: the key to look for in the "Values" dict. by default search for Name. None value are ignored.
|
|
247
|
+
If the constructor parameter click_key_conversion flag (see below for flags) is true then the default value is the Name in lowercase and with '-' replaced by '_'
|
|
248
|
+
- Transform : an Eval Form (see below explanation on Eval forms) that transform the value found for the variable and produce another value that will be the value
|
|
249
|
+
of the variable before possible casting (like explain in TypeInfo).
|
|
250
|
+
- Contexts [optional ]: a list of names indicating in which context the variable is valid. If one of the context names passed at variable resolution to
|
|
251
|
+
AppConfig.resolve_vars() matches on e of the context names of the variable or if the variable has no Contexts directive then the variable is
|
|
252
|
+
considered for resolution.
|
|
253
|
+
- SplitToLists [optional ]: Indicates that the value found must be split in order to produce a list of string. The value
|
|
254
|
+
of the directive is the list separator or True if the separator is a comma. Value in the string list will be casted to the type specified in TypeInfo.
|
|
255
|
+
See the paragraph below about Lists that explain how list handled.
|
|
256
|
+
- CanBeRelativeTo [optional]: a directory path or the name of another key that resolves in a directory path.
|
|
257
|
+
If TypeInfo is a Path and the retrieved value is not an empty string nor None nor an absolute path then
|
|
258
|
+
transform the value to an absolute path with the given directory as parent directory.
|
|
259
|
+
See details below on how Path are processed.
|
|
260
|
+
- MakeDirs [optional ]: a PathType that indicates the type of Path for which directories are to be created if they do not exist yet.
|
|
261
|
+
if PathType is:
|
|
262
|
+
- Dir : create the hierarchy of directory necessary to contains this directory as well as the directory itself
|
|
263
|
+
- File: create the parent hierarchy of directory necessary to contains this file
|
|
264
|
+
See details below on how Path are processed.
|
|
265
|
+
|
|
266
|
+
- Flag directives:
|
|
267
|
+
These directives can also be defined globally in the AppConfig object or when calling resolve_vars().Flags defined in a ConfigVarDef override
|
|
268
|
+
the other values if they are defined.
|
|
269
|
+
- NoDirProcessings [optional ]: don't run specific Path processing for this variable.
|
|
270
|
+
- NoEnvSearch [optional ]: boolean, False by default. If true the value of the variable is not searched in the Environment.
|
|
271
|
+
- NoValueSearch[optional ]: boolean, False by default. If true the value of the variable is not searched in the values dict.
|
|
272
|
+
- NoConffileSearch [optional ]: boolean, False by default. If true the value of the variable is not searched in the configuration file.
|
|
273
|
+
- ClickKeyConversion [optional ]: boolean, False by default. If true the value of the variable is converted using Click key conversion.
|
|
274
|
+
- AllowOverride [optional ]: boolean, False by default. If true the variable value can be overridden by another context.
|
|
275
|
+
- NoException [optional ]: boolean, False by default. If true no exception is thrown if the variable value is not found.
|
|
276
|
+
- NoSearch [optional] : boolean, False by default. It true the value of the variable is not searched in the Environment, the values or the configuration
|
|
277
|
+
file. It's equivaleut to set the directives NoEnvSearch, NoValueSearch, NoConffileSearch to True. In this case the value of the variable should be defined by
|
|
278
|
+
the Default directive.
|
|
279
|
+
|
|
280
|
+
For backward compatibilty the special value __NO_SEARCH__ for EnvName, ConfigKey, FileKey and ValueKey are still supported but it's recommended to use
|
|
281
|
+
the NoEnvSearch, NoValueSearch, NoConffileSearch or NoSearch directives instead.
|
|
282
|
+
|
|
283
|
+
By default throw an exception if no value are found except if no_exception is True
|
|
284
|
+
|
|
285
|
+
Eval Forms:
|
|
286
|
+
-----------
|
|
287
|
+
|
|
288
|
+
an eval form is a callable that is evaluated to compute a value for a variable. Eval forms can be used in the Default directive or the
|
|
289
|
+
Transform directive.
|
|
290
|
+
|
|
291
|
+
The callable receives three parameters: the variable name, this configuration object and the current value found for the variable if one has been found
|
|
292
|
+
in the case of a Transform directive.
|
|
293
|
+
|
|
294
|
+
The expression can use value already defined in this application configuration object by using the first parameter of the callable which is the AppConfig object itself,
|
|
295
|
+
knowing that variables are evaluated in the order they are defined.
|
|
296
|
+
|
|
297
|
+
In the case of a Default directive the third parameter of the callable will be None.
|
|
298
|
+
In the case of a Transform directive the third parameter of the callable may be None if no value have been found and
|
|
299
|
+
the 'no_exception' global flag is set to True, otherwise it will be the value found for the variable before transformation.
|
|
300
|
+
|
|
301
|
+
The returned value from the evaluation of the callable will be used to set the variable value before casting to TypeInfo.
|
|
302
|
+
Note that the eventual type casting will be done after the Eval call.
|
|
303
|
+
It's up to the caller to deal with the returned value.
|
|
304
|
+
|
|
305
|
+
Note that the libraries used in Eval Forms must be imported in the module that defines the form.
|
|
306
|
+
|
|
307
|
+
!!Warning!!: Default and Transform directives should be set or return a value that must be 'castable' to the type defined by TypeInfo.
|
|
308
|
+
For example if a variable is declared as a class that accept a string in its constructor then the Default directive should be a string or an Eval
|
|
309
|
+
Form that returns a string not the class instance.
|
|
310
|
+
|
|
311
|
+
Path and Path list TypeInfo variables:
|
|
312
|
+
--------------------------------------
|
|
313
|
+
|
|
314
|
+
If a variable has a TypeInfo that is a Path or a list of Path there a dedicated processing that applies on the
|
|
315
|
+
value found for them.
|
|
316
|
+
This processing takes place at the end of the processing of normal variable and occurs when:
|
|
317
|
+
- the TypeInfo is a Path or a list of Path
|
|
318
|
+
- a value for the variable was found
|
|
319
|
+
- the directive NoDirProcessing is False
|
|
320
|
+
|
|
321
|
+
The specific processing is the following on the Path or on each of the Path in the list of Path:
|
|
322
|
+
- CanBeRelativeTo: if this directive has a value that can be a path-like directory or the name of another key already defined
|
|
323
|
+
that resolves in a path-like directory and the value found is not an absolute Path:
|
|
324
|
+
- then make a Path with the CanBeRelativeTo directory as parent and the value found as its child.
|
|
325
|
+
- MakeDirs: if this directive is True , then create the necessary directories of the path if they don't exist yet.
|
|
326
|
+
- Call the 'expanduser()' and 'resolve()' method on the resulting Path.
|
|
327
|
+
|
|
328
|
+
List:
|
|
329
|
+
-----
|
|
330
|
+
|
|
331
|
+
All values found are strings that should be casted to the type specified in TypeInfo inferred from the type hint of the variable.
|
|
332
|
+
Only list of single type are supported like list[int], list[str], list[MyClass], etc.
|
|
333
|
+
The value is considered as a list when SplitToList is not False and a value was found for the variable.
|
|
334
|
+
In this case the temporaray string value found for the variable is split with the string 'split()' method to generate
|
|
335
|
+
a list of string.
|
|
336
|
+
Then all items of the list of strings are casted to the specified TypeInfo.
|
|
337
|
+
|
|
338
|
+
Contexts:
|
|
339
|
+
--------
|
|
340
|
+
Every configuration variable can specify a list of context(s) in which it is valid.
|
|
341
|
+
When resolving variables it is also possible to specify for which context(s) the resolution must be done.
|
|
342
|
+
|
|
343
|
+
When one or several contexts are specify for the resolution then only the variable valid for these contexts will be considered for resolution
|
|
344
|
+
as well as the VarDef without Context directive. Variable defined without Context directive are common to all context, the one with a Context directive
|
|
345
|
+
are specific to their contexts.
|
|
346
|
+
|
|
347
|
+
This allows to define variables that are specific to some context and to resolve only these variables when needed.
|
|
348
|
+
When the flage AllowOverride is False and several resolutions are done, then variables are evaluated only once even if they are valid in several
|
|
349
|
+
of the requested context(s), only the first valid context is used.
|
|
350
|
+
|
|
351
|
+
None value:
|
|
352
|
+
----------
|
|
353
|
+
All the VarDef valid for the involved contexts will be resolved and the one for which no value have been found will be set to None if the
|
|
354
|
+
flag 'no_exception' is True.
|
|
355
|
+
|
|
356
|
+
If the flag 'no_exception' is false and one varDef for the context has no value then an exception is raised.
|
|
357
|
+
|
|
358
|
+
Whatever is the value of the flag 'no_exception' it is possible to set a value to None either with the 'Default' directive or with
|
|
359
|
+
an Eval Form or a None value in one of the search location.
|
|
360
|
+
|
|
361
|
+
Note on implementation:
|
|
362
|
+
-----------------------
|
|
363
|
+
ConfigVarDef is a descriptor class that defines the __get__ and __set__ methods.
|
|
364
|
+
This allows to define configuration variables as class attributes on AppConfig subclasses.
|
|
365
|
+
The __set_name__ method is used to set the Name and TypeInfo directives based on the attribute name and type hint.
|
|
366
|
+
"""
|
|
367
|
+
|
|
368
|
+
_name: Optional[str] = None
|
|
369
|
+
_help: str = ""
|
|
370
|
+
_default: Any | Callable[[str, Any, Any], Any] | Undefined = _undef
|
|
371
|
+
_env_name: Optional[str] = None
|
|
372
|
+
_file_key: Optional[str] = None
|
|
373
|
+
_value_key: Optional[str] = None
|
|
374
|
+
_type_info: Any = None
|
|
375
|
+
_transform: Optional[Callable[[str, Any, Any], Any]] = None
|
|
376
|
+
_contexts: Optional[Sequence[str]] = None
|
|
377
|
+
_split_to_list: Optional[bool | str | None] = None
|
|
378
|
+
_can_be_relative_to: Optional[Path | str] = None
|
|
379
|
+
_make_dirs: Optional[PathType] = None
|
|
380
|
+
_no_dir_processing: Optional[bool] = None
|
|
381
|
+
|
|
382
|
+
def __init__(
|
|
383
|
+
self,
|
|
384
|
+
*,
|
|
385
|
+
Help: str = "",
|
|
386
|
+
Default: Any | Callable[[str, Any, Any], Any] | Undefined = _undef,
|
|
387
|
+
EnvName: Optional[str] = None,
|
|
388
|
+
FileKey: Optional[str] = None,
|
|
389
|
+
ValueKey: Optional[str] = None,
|
|
390
|
+
Transform: Optional[Callable[[str, Any, Any], Any]] = None,
|
|
391
|
+
CanBeRelativeTo: Optional[Path | str] = None,
|
|
392
|
+
Contexts: Optional[Sequence[str]] = None,
|
|
393
|
+
MakeDirs: Optional[PathType] = None,
|
|
394
|
+
SplitToList: Optional[bool | str] = None,
|
|
395
|
+
NoDirProcessing: Optional[bool] = None,
|
|
396
|
+
NoSearch: Optional[bool] = None,
|
|
397
|
+
NoEnvSearch: Optional[bool] = None,
|
|
398
|
+
NoValueSearch: Optional[bool] = None,
|
|
399
|
+
NoConffileSearch: Optional[bool] = None,
|
|
400
|
+
ClickKeyConversion: Optional[bool] = None,
|
|
401
|
+
AllowOverride: Optional[bool] = None,
|
|
402
|
+
NoException: Optional[bool] = None,
|
|
403
|
+
) -> None:
|
|
404
|
+
"""Initialize the configuration variable definition.
|
|
405
|
+
Should not be used directly but be used through the function configvar() to define a configuration variable as a class attribute
|
|
406
|
+
on an AppConfig subclass like this:
|
|
407
|
+
class MyCfg(AppConfig):
|
|
408
|
+
my_var: int = configvar(Default=1)
|
|
409
|
+
|
|
410
|
+
"""
|
|
411
|
+
self._help = Help
|
|
412
|
+
self.__doc__ = Help
|
|
413
|
+
self._default = Default
|
|
414
|
+
self._env_name = EnvName
|
|
415
|
+
self._file_key = FileKey
|
|
416
|
+
self._value_key = ValueKey
|
|
417
|
+
self._no_search = NoSearch
|
|
418
|
+
self._transform = Transform
|
|
419
|
+
self._can_be_relative_to = CanBeRelativeTo
|
|
420
|
+
self._contexts = Contexts
|
|
421
|
+
self._make_dirs = MakeDirs
|
|
422
|
+
self._split_to_list = SplitToList
|
|
423
|
+
self._no_dir_processing = NoDirProcessing
|
|
424
|
+
self.flags = Flags(
|
|
425
|
+
no_env_search=NoEnvSearch,
|
|
426
|
+
no_value_search=NoValueSearch,
|
|
427
|
+
no_conffile_search=NoConffileSearch,
|
|
428
|
+
click_key_conversion=ClickKeyConversion,
|
|
429
|
+
allow_override=AllowOverride,
|
|
430
|
+
no_exception=NoException,
|
|
431
|
+
)
|
|
432
|
+
|
|
433
|
+
def __set_name__(self, owner: type, name: str) -> None:
|
|
434
|
+
"""This method is a special python method called when a descriptor is assigned to a class attribute.
|
|
435
|
+
ConfigVarDef is a descriptor class because it defines the __get__ and __set__ methods.
|
|
436
|
+
|
|
437
|
+
The goal of this method is to:
|
|
438
|
+
- set the Name directive of this ConfigVarDef based on the attribute name.
|
|
439
|
+
- infer the type of the attribute and set the TypeInfo directive with it.
|
|
440
|
+
|
|
441
|
+
Args:
|
|
442
|
+
owner (type): the owning class of the attribute that is being assigned the descriptor
|
|
443
|
+
name (str): the attribute name
|
|
444
|
+
"""
|
|
445
|
+
|
|
446
|
+
# set Name directive
|
|
447
|
+
self._name = name
|
|
448
|
+
|
|
449
|
+
# get all type hints of the owner class
|
|
450
|
+
try:
|
|
451
|
+
hints = get_type_hints(owner, include_extras=True)
|
|
452
|
+
except Exception:
|
|
453
|
+
hints = getattr(owner, "__annotations__", {})
|
|
454
|
+
|
|
455
|
+
# test if there is a type hint for this attribute, if not we will consider it to be a string.
|
|
456
|
+
if name in hints:
|
|
457
|
+
annotated_type = hints[name]
|
|
458
|
+
self._type_info = annotated_type
|
|
459
|
+
|
|
460
|
+
if self._split_to_list is None:
|
|
461
|
+
origin = get_origin(annotated_type)
|
|
462
|
+
args = get_args(annotated_type)
|
|
463
|
+
|
|
464
|
+
# Check for basic list
|
|
465
|
+
if origin is list or annotated_type is list:
|
|
466
|
+
self._split_to_list = True
|
|
467
|
+
# Check for Optional[list] / Union[list, ...]
|
|
468
|
+
elif origin is Union:
|
|
469
|
+
for arg in args:
|
|
470
|
+
arg_origin = get_origin(arg)
|
|
471
|
+
if arg_origin is list or arg is list:
|
|
472
|
+
self._split_to_list = True
|
|
473
|
+
break
|
|
474
|
+
else:
|
|
475
|
+
# no type hint, consider it as a string
|
|
476
|
+
self._type_info = str
|
|
477
|
+
|
|
478
|
+
def __get__(
|
|
479
|
+
self, instance: Optional[AppConfig], owner: Optional[type] = None
|
|
480
|
+
) -> ConfigVarDef[_T] | _T | None:
|
|
481
|
+
"""
|
|
482
|
+
Descriptor method that gets the value of the configuration variable from the AppConfig instance.
|
|
483
|
+
|
|
484
|
+
The value is retrieved from the instance's __dict__ using the variable's Name because we want to
|
|
485
|
+
handle the case where we have dotted names for nested attributes.
|
|
486
|
+
"""
|
|
487
|
+
if instance is None:
|
|
488
|
+
return self
|
|
489
|
+
name = self.Name
|
|
490
|
+
if name in instance.__dict__:
|
|
491
|
+
return instance[name] # type: ignore
|
|
492
|
+
return None
|
|
493
|
+
|
|
494
|
+
def __set__(self, instance: AppConfig, value: Any) -> None:
|
|
495
|
+
"""
|
|
496
|
+
Descriptor method that gets the value of the configuration variable from the AppConfig instance.
|
|
497
|
+
The value is set in the instance's __dict__ using the variable's Name because we want to
|
|
498
|
+
handle the case where we have dotted names for nested attributes.
|
|
499
|
+
"""
|
|
500
|
+
instance._set_value(self.Name, value)
|
|
501
|
+
|
|
502
|
+
@property
|
|
503
|
+
def Name(self) -> str:
|
|
504
|
+
if self._name is None:
|
|
505
|
+
raise RuntimeError(
|
|
506
|
+
"ConfigVarDef has no Name. Declare it on an AppConfig subclass "
|
|
507
|
+
)
|
|
508
|
+
return self._name
|
|
509
|
+
|
|
510
|
+
@property
|
|
511
|
+
def Help(self) -> str:
|
|
512
|
+
return self._help
|
|
513
|
+
|
|
514
|
+
@property
|
|
515
|
+
def Contexts(self) -> Sequence[str] | None:
|
|
516
|
+
return self._contexts
|
|
517
|
+
|
|
518
|
+
@property
|
|
519
|
+
def ValueKey(self) -> Optional[str]:
|
|
520
|
+
"""Note: we can't compute the final value here because it depends on the click_key_conversion
|
|
521
|
+
flag that can be set globally or in resolve_vars."""
|
|
522
|
+
if self.flags.no_value_search:
|
|
523
|
+
return None
|
|
524
|
+
else:
|
|
525
|
+
return self.Name if self._value_key is None else self._value_key
|
|
526
|
+
|
|
527
|
+
@property
|
|
528
|
+
def EnvName(self) -> Optional[str]:
|
|
529
|
+
if self.flags.no_env_search:
|
|
530
|
+
return None
|
|
531
|
+
else:
|
|
532
|
+
return self._env_name if self._env_name is not None else self.Name
|
|
533
|
+
|
|
534
|
+
@property
|
|
535
|
+
def FileKey(self) -> Optional[str]:
|
|
536
|
+
if self.flags.no_conffile_search:
|
|
537
|
+
return None
|
|
538
|
+
else:
|
|
539
|
+
return self._file_key if self._file_key is not None else self.Name
|
|
540
|
+
|
|
541
|
+
@property
|
|
542
|
+
def Default(self) -> Any | Callable[[str, Any, Any], Any] | Undefined:
|
|
543
|
+
return self._default
|
|
544
|
+
|
|
545
|
+
@property
|
|
546
|
+
def SplitToList(self) -> bool | str:
|
|
547
|
+
return False if self._split_to_list is None else self._split_to_list
|
|
548
|
+
|
|
549
|
+
@property
|
|
550
|
+
def Transform(self) -> Optional[Callable[[str, Any, Any], Any]]:
|
|
551
|
+
return self._transform
|
|
552
|
+
|
|
553
|
+
@property
|
|
554
|
+
def TypeInfo(self) -> Any:
|
|
555
|
+
return self._type_info
|
|
556
|
+
|
|
557
|
+
@property
|
|
558
|
+
def NoDirProcessing(self) -> bool:
|
|
559
|
+
return False if self._no_dir_processing is None else self._no_dir_processing
|
|
560
|
+
|
|
561
|
+
@property
|
|
562
|
+
def CanBeRelativeTo(self) -> Optional[Path | str]:
|
|
563
|
+
return self._can_be_relative_to
|
|
564
|
+
|
|
565
|
+
@property
|
|
566
|
+
def MakeDirs(self) -> Optional[PathType]:
|
|
567
|
+
return self._make_dirs
|
|
568
|
+
|
|
569
|
+
|
|
570
|
+
def configvar(
|
|
571
|
+
*,
|
|
572
|
+
Help: str = "",
|
|
573
|
+
Default: Any | Callable[[str, Any, Any], Any] | Undefined = _undef,
|
|
574
|
+
EnvName: Optional[str] = None,
|
|
575
|
+
FileKey: Optional[str] = None,
|
|
576
|
+
ValueKey: Optional[str] = None,
|
|
577
|
+
Transform: Optional[Callable[[str, Any, Any], Any]] = None,
|
|
578
|
+
CanBeRelativeTo: Optional[Path | str] = None,
|
|
579
|
+
Contexts: Optional[Sequence[str]] = None,
|
|
580
|
+
MakeDirs: Optional[PathType] = None,
|
|
581
|
+
SplitToList: Optional[bool | str] = None,
|
|
582
|
+
NoDirProcessing: Optional[bool] = None,
|
|
583
|
+
NoSearch: Optional[bool] = None,
|
|
584
|
+
NoEnvSearch: Optional[bool] = None,
|
|
585
|
+
NoValueSearch: Optional[bool] = None,
|
|
586
|
+
NoConffileSearch: Optional[bool] = None,
|
|
587
|
+
ClickKeyConversion: Optional[bool] = None,
|
|
588
|
+
AllowOverride: Optional[bool] = None,
|
|
589
|
+
NoException: Optional[bool] = None,
|
|
590
|
+
) -> Any:
|
|
591
|
+
"""Create a declarative configuration variable definition.
|
|
592
|
+
|
|
593
|
+
Use it as a class attribute on an AppConfig subclass:
|
|
594
|
+
|
|
595
|
+
class MyCfg(AppConfig):
|
|
596
|
+
my_var: int = configvar(Default=1)
|
|
597
|
+
|
|
598
|
+
The variable name defaults to the attribute name and its type is inferred from the type hint of the attribute declaration.
|
|
599
|
+
|
|
600
|
+
This function allows to assign a ConfigVarDef objects to an attribute definitions of any type by telling the type checkers
|
|
601
|
+
that the type of the assignation is Any instead of ConfigVarDef.
|
|
602
|
+
Indeed ConfigVarDef is a generic class and my_var: int = ConfigVarDef(...) or my_var: ConfigVarDef[int] = ConfigVarDef(...)
|
|
603
|
+
would not be accepted by type checkers. configvar() declares returning Any which deactivates type checking.
|
|
604
|
+
|
|
605
|
+
"""
|
|
606
|
+
|
|
607
|
+
return ConfigVarDef(
|
|
608
|
+
Help=Help,
|
|
609
|
+
Default=Default,
|
|
610
|
+
EnvName=EnvName,
|
|
611
|
+
FileKey=FileKey,
|
|
612
|
+
ValueKey=ValueKey,
|
|
613
|
+
Transform=Transform,
|
|
614
|
+
CanBeRelativeTo=CanBeRelativeTo,
|
|
615
|
+
Contexts=Contexts,
|
|
616
|
+
MakeDirs=MakeDirs,
|
|
617
|
+
SplitToList=SplitToList,
|
|
618
|
+
NoDirProcessing=NoDirProcessing,
|
|
619
|
+
NoSearch=NoSearch,
|
|
620
|
+
NoEnvSearch=NoEnvSearch,
|
|
621
|
+
NoValueSearch=NoValueSearch,
|
|
622
|
+
NoConffileSearch=NoConffileSearch,
|
|
623
|
+
ClickKeyConversion=ClickKeyConversion,
|
|
624
|
+
AllowOverride=AllowOverride,
|
|
625
|
+
NoException=NoException,
|
|
626
|
+
)
|
the1conf/py.typed
ADDED
|
File without changes
|