the1conf 1.0.0__py3-none-any.whl → 1.2.3__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/config_var.py CHANGED
@@ -1,213 +1,30 @@
1
1
  from __future__ import annotations
2
2
 
3
- import copy
4
- from collections.abc import Sequence, Callable
5
- from dataclasses import dataclass
6
- from enum import Enum, auto
3
+ from collections.abc import Callable, Sequence
7
4
  from pathlib import Path
8
5
  from typing import (
6
+ TYPE_CHECKING,
9
7
  Any,
10
8
  Generic,
11
9
  Optional,
12
- TypeAlias,
13
10
  TypeVar,
14
11
  Union,
12
+ cast,
13
+ dataclass_transform,
15
14
  get_args,
16
15
  get_origin,
17
16
  get_type_hints,
18
- dataclass_transform,
19
- TYPE_CHECKING,
20
17
  )
21
-
18
+ import inspect
19
+ from .utils import Undefined, _undef, PathType, is_sequence
20
+ from .flags import Flags
22
21
 
23
22
  if TYPE_CHECKING:
24
23
  from .app_config import AppConfig
25
24
 
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
25
  _T = TypeVar("_T")
43
26
 
44
27
 
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
28
  @dataclass_transform()
212
29
  class ConfigVarDef(Generic[_T]):
213
30
  """
@@ -217,15 +34,15 @@ class ConfigVarDef(Generic[_T]):
217
34
 
218
35
  Configuration are used indirectly by defining attributes of an AppConfig subclass with the configvar() function that creates ConfigVarDef objects:
219
36
  class MyAppConfig(AppConfig):
220
- my_var: int = configvar(Default=1)
37
+ my_var: int = configvar(default=1)
221
38
 
222
39
  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
40
  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
41
  or a configuration file, or an environment variables or a computation defined by a python Callable.
225
42
 
226
43
  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
44
+ - name : the name of the variable, inferred from the attribute name.
45
+ - type_info : 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
46
  builtin types.
230
47
  Here how to get the list of python builtin types:
231
48
  print([t.__name__ for t in __builtins__.__dict__.values() if isinstance(t, type)])
@@ -236,198 +53,242 @@ class ConfigVarDef(Generic[_T]):
236
53
 
237
54
  The directives that can be specified in configvar() are:
238
55
 
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
56
+ - help [optional]: a help string that describes the variable.
57
+ - default [optional]: the default value, can be a value or an Eval Form (see below explanation on Eval forms) whose
241
58
  returned value will be the value of the variable if no other value has been found.
242
59
  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
60
+ - env_name [optional ]: the name of the environment variable that can contain the value. By default search for name
61
+ - file_key [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
62
+ nested namespaces (see AppConfig documentation). By default search for name.
63
+ - value_key [optional ]: the key to look for in the "Values" dict. by default search for name. None value are ignored.
64
+ 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 '_'
65
+ - 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
66
+ of the variable before possible casting (like explain in type_info).
67
+ - scopes [optional]: a list of names indicating in which scope the variable is valid. If one of the scope names passed at variable resolution to
68
+ AppConfig.resolve_vars() matches one of the scope names of the variable or if the variable has no scope directive then the variable is
252
69
  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.
70
+ - split_to_list [optional ]: Indicates that the value found must be split in order to produce a list of string. The value
71
+
72
+ 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 type_info.
255
73
  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
74
+ - can_be_relative_to [optional]: a directory path or the name of another key that resolves in a directory path.
75
+ If type_info is a Path and the retrieved value is not an empty string nor None nor an absolute path then
258
76
  transform the value to an absolute path with the given directory as parent directory.
259
77
  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.
78
+ - make_dirs [optional ]: a PathType that indicates the type of Path for which directories are to be created if they do not exist yet.
261
79
  if PathType is:
262
80
  - Dir : create the hierarchy of directory necessary to contains this directory as well as the directory itself
263
81
  - File: create the parent hierarchy of directory necessary to contains this file
264
82
  See details below on how Path are processed.
83
+ - auto_prolog [optional]: mark the variable for automatic prolog processing - which means that the variable will be resolved at an early stage, before the configuration file
84
+ is resolved and before all other variables. If it has also the allow_override flag True then it can be processed again with all other variables in the regular resolution.
85
+
265
86
 
266
87
  - Flag directives:
267
88
  These directives can also be defined globally in the AppConfig object or when calling resolve_vars().Flags defined in a ConfigVarDef override
268
89
  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
90
+ - no_dir_processing [optional ]: don't run specific Path processing for this variable.
91
+ - no_env_search [optional ]: boolean, False by default. If true the value of the variable is not searched in the Environment.
92
+ - no_value_search[optional ]: boolean, False by default. If true the value of the variable is not searched in the values dict.
93
+ - no_conffile_search [optional ]: boolean, False by default. If true the value of the variable is not searched in the configuration file.
94
+ - click_key_conversion [optional ]: boolean, False by default. If true the value of the variable is converted using Click key conversion.
95
+ - allow_override [optional ]: boolean, False by default. If true the variable value can be overridden by another context.
96
+ - mandatory [optional ]: boolean, True by default. If true an exception is thrown if the variable value is not found. Otherwise the variable is not set which means that accessing it
97
+ would raise an Exception.
98
+ - no_search [optional] : boolean, False by default. It true the value of the variable is not searched in the Environment, the values or the configuration
99
+ file. It's equivaleut to set the directives no_env_search, no_value_search, no_conffile_search to True. In this case the value of the variable should be defined by
100
+ the default directive.
101
+
102
+ For backward compatibilty the special value __NO_SEARCH__ for env_name, config_key, file_key and value_key are still supported but it's recommended to use
103
+ the no_env_search, no_value_search, no_conffile_search or no_search directives instead.
104
+
105
+ By default throw an exception if no value are found except if mandatory is False
284
106
 
285
107
  Eval Forms:
286
108
  -----------
287
109
 
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.
110
+ 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
111
+ transform directive.
290
112
 
291
113
  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.
114
+ in the case of a transform directive.
293
115
 
294
116
  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
117
  knowing that variables are evaluated in the order they are defined.
296
118
 
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.
119
+ In the case of a default directive the third parameter of the callable will be None.
120
+ In the case of a transform directive the third parameter of the callable may be None if no value have been found and
121
+ the 'mandatory' global flag is set to False, otherwise it will be the value found for the variable before transformation.
300
122
 
301
- The returned value from the evaluation of the callable will be used to set the variable value before casting to TypeInfo.
123
+ The returned value from the evaluation of the callable will be used to set the variable value before casting to type_info.
302
124
  Note that the eventual type casting will be done after the Eval call.
303
125
  It's up to the caller to deal with the returned value.
304
126
 
305
127
  Note that the libraries used in Eval Forms must be imported in the module that defines the form.
306
128
 
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
129
+ !!Warning!!: default and transform directives should be set or return a value that must be 'castable' to the type defined by type_info.
130
+ 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
131
  Form that returns a string not the class instance.
310
132
 
311
- Path and Path list TypeInfo variables:
133
+ Path and Path list type_info variables:
312
134
  --------------------------------------
313
135
 
314
- If a variable has a TypeInfo that is a Path or a list of Path there a dedicated processing that applies on the
136
+ If a variable has a type_info that is a Path or a list of Path there a dedicated processing that applies on the
315
137
  value found for them.
316
138
  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
139
+ - the type_info is a Path or a list of Path
318
140
  - a value for the variable was found
319
- - the directive NoDirProcessing is False
141
+ - the directive no_dir_processing is False
320
142
 
321
143
  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
144
+ - can_be_relative_to: if this directive has a value that can be a path-like directory or the name of another key already defined
323
145
  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.
146
+ - then make a Path with the can_be_relative_to directory as parent and the value found as its child.
147
+ - make_dirs: if this directive is True , then create the necessary directories of the path if they don't exist yet.
326
148
  - Call the 'expanduser()' and 'resolve()' method on the resulting Path.
327
149
 
328
150
  List:
329
151
  -----
330
152
 
331
- All values found are strings that should be casted to the type specified in TypeInfo inferred from the type hint of the variable.
153
+ All values found are strings that should be casted to the type specified in type_info inferred from the type hint of the variable.
332
154
  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.
155
+ The value is considered as a list when split_to_list is not False and a value was found for the variable.
334
156
  In this case the temporaray string value found for the variable is split with the string 'split()' method to generate
335
157
  a list of string.
336
- Then all items of the list of strings are casted to the specified TypeInfo.
158
+ Then all items of the list of strings are casted to the specified type_info.
337
159
 
338
- Contexts:
160
+ scopes:
339
161
  --------
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.
162
+ Every configuration variable can specify a list of scope(s) in which it is valid.
163
+ When resolving variables it is also possible to specify for which scope(s) the resolution must be done.
342
164
 
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.
165
+ When one or several scopes are specify for the resolution then only the variable valid for these scopes will be considered for resolution
166
+ as well as the VarDef without Scope directive. Variable defined without Scope directive are common to all scope, the one with a Scope directive
167
+ are specific to their scopes.
346
168
 
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.
169
+ This allows to define variables that are specific to some scope and to resolve only these variables when needed.
170
+ When the flage allow_override is False and several resolutions are done, then variables are evaluated only once even if they are valid in several
171
+ of the requested scope(s), only the first valid scope is used.
350
172
 
351
173
  None value:
352
174
  ----------
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.
175
+ All the VarDef valid for the involved scopes will be resolved and the one for which no value have been found will be set to None if the
176
+ flag 'mandatory' is False.
355
177
 
356
- If the flag 'no_exception' is false and one varDef for the context has no value then an exception is raised.
178
+ If the flag 'mandatory' is true and one varDef for the scope has no value then an exception is raised.
357
179
 
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
180
+ Whatever is the value of the flag 'mandatory' it is possible to set a value to None either with the 'default' directive or with
359
181
  an Eval Form or a None value in one of the search location.
360
182
 
183
+ AutoProlog variables:
184
+ --------------------
185
+ Variables defined with the auto_prolog directive set to True are considered as AutoProlog variables. These variables are resolved at an early stage
186
+ before all other variables and before the configuration file is processed.
187
+ This allows to define variables that are necessary to locate the configuration file when its path depends on other variables. In this case these variables
188
+ must be defined as AutoProlog variables.
189
+ The default variables defined in the AutoProlog class are all AutoProlog variables and therefore are made available to be used in variable substitution for all other
190
+ variables and for locating the configuration file.
191
+
361
192
  Note on implementation:
362
193
  -----------------------
363
194
  ConfigVarDef is a descriptor class that defines the __get__ and __set__ methods.
364
195
  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.
196
+ The __set_name__ method is used to set the name and type_info directives based on the attribute name and type hint.
366
197
  """
367
198
 
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
199
+ _auto_prolog: Optional[bool] = None
378
200
  _can_be_relative_to: Optional[Path | str] = None
201
+ _scopes: Optional[Sequence[str]] = None
202
+ _default: Any | Callable[[str, Any, Any], Any] | Undefined = _undef
203
+ _env_name: Optional[Sequence[str]] = None
204
+ _file_key: Optional[Sequence[str]] = None
205
+ _help: str = ""
379
206
  _make_dirs: Optional[PathType] = None
207
+ _mandatory: Optional[bool] = True
380
208
  _no_dir_processing: Optional[bool] = None
209
+ _name: Optional[str] = None
210
+ _split_to_list: Optional[bool | str] = None
211
+ _transform: Optional[Union[Callable[[str, Any, Any], Any], str]] = None
212
+ _type_info: Any = None
213
+ _value_key: Optional[Sequence[str]] = None
381
214
 
382
215
  def __init__(
383
216
  self,
384
217
  *,
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,
218
+ allow_override: Optional[bool] = None,
219
+ auto_prolog: Optional[bool] = None,
220
+ can_be_relative_to: Optional[Path | str] = None,
221
+ click_key_conversion: Optional[bool] = None,
222
+ scopes: Optional[Sequence[str]] = None,
223
+ default: Any | Callable[[str, Any, Any], Any] | Undefined = _undef,
224
+ env_name: Optional[str | Sequence[str]] = None,
225
+ file_key: Optional[str | Sequence[str]] = None,
226
+ help: str = "",
227
+ make_dirs: Optional[PathType] = None,
228
+ mandatory: Optional[bool] = None,
229
+ no_conffile_search: Optional[bool] = None,
230
+ no_dir_processing: Optional[bool] = None,
231
+ no_env_search: Optional[bool] = None,
232
+ no_search: Optional[bool] = None,
233
+ no_value_search: Optional[bool] = None,
234
+ split_to_list: Optional[bool | str] = None,
235
+ transform: Optional[Union[Callable[[str, Any, Any], Any], str]] = None,
236
+ type_info: Any = None,
237
+ value_key: Optional[str | Sequence[str]] = None,
403
238
  ) -> None:
404
239
  """Initialize the configuration variable definition.
405
240
  Should not be used directly but be used through the function configvar() to define a configuration variable as a class attribute
406
241
  on an AppConfig subclass like this:
407
242
  class MyCfg(AppConfig):
408
- my_var: int = configvar(Default=1)
243
+ my_var: int = configvar(default=1)
409
244
 
410
245
  """
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
246
+ self.__doc__ = help
247
+ self._auto_prolog = auto_prolog
248
+ self._can_be_relative_to = can_be_relative_to
249
+ self._scopes = scopes
250
+ self._default = default
251
+ self._env_name = (
252
+ None
253
+ if env_name is None
254
+ else (
255
+ env_name
256
+ if is_sequence(env_name)
257
+ else [env_name] if isinstance(env_name, str) else None
258
+ )
259
+ )
260
+ self._file_key = (
261
+ None
262
+ if file_key is None
263
+ else (
264
+ file_key
265
+ if is_sequence(file_key)
266
+ else [file_key] if isinstance(file_key, str) else None
267
+ )
268
+ )
269
+ self._help = help
270
+ self._make_dirs = make_dirs
271
+ self._mandatory = mandatory
272
+ self._no_dir_processing = no_dir_processing
273
+ self._no_search = no_search
274
+ self._split_to_list = split_to_list
275
+ self._transform = transform
276
+ self._type_info = type_info
277
+ self._value_key = (
278
+ None
279
+ if value_key is None
280
+ else (
281
+ value_key
282
+ if is_sequence(value_key)
283
+ else [value_key] if isinstance(value_key, str) else None
284
+ )
285
+ )
424
286
  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,
287
+ no_env_search=no_env_search,
288
+ no_value_search=no_value_search,
289
+ no_conffile_search=no_conffile_search,
290
+ click_key_conversion=click_key_conversion,
291
+ allow_override=allow_override,
431
292
  )
432
293
 
433
294
  def __set_name__(self, owner: type, name: str) -> None:
@@ -435,34 +296,82 @@ class ConfigVarDef(Generic[_T]):
435
296
  ConfigVarDef is a descriptor class because it defines the __get__ and __set__ methods.
436
297
 
437
298
  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.
299
+ - set the name directive of this ConfigVarDef based on the attribute name.
300
+ - infer the type of the attribute and set the type_info directive with it.
440
301
 
441
302
  Args:
442
303
  owner (type): the owning class of the attribute that is being assigned the descriptor
443
304
  name (str): the attribute name
444
305
  """
445
306
 
446
- # set Name directive
307
+ # set name directive
447
308
  self._name = name
448
309
 
449
- # get all type hints of the owner class
310
+ # try to find a type:
311
+ # if we have a type hints and a type_info directive we take the type_info.
312
+ # This allow to define a type for the type checker that allow override with another type if the user wants to.
313
+ # (it is usefull for exec_stage)
314
+ # but still have a correct run time type , this is correct as long as the user use duck typing types
315
+ # that are compatible.
450
316
  try:
451
317
  hints = get_type_hints(owner, include_extras=True)
452
318
  except Exception:
453
319
  hints = getattr(owner, "__annotations__", {})
454
320
 
455
- # test if there is a type hint for this attribute, if not we will consider it to be a string.
321
+ # test if there is a type hint for this attribute
322
+ annotated_type: dict[str, Any] | Any | dict[Any, Any] = None
456
323
  if name in hints:
457
324
  annotated_type = hints[name]
325
+ if isinstance(annotated_type, str):
326
+ # Why can it be a string? In Python, type hints are strings if:
327
+ # from __future__ import annotations is used (postponed evaluation of annotations).
328
+ # The user explicitly used quotes for a forward reference (e.g., var: "MyClass" where MyClass is defined later).
329
+ for frame_info in inspect.stack():
330
+ # try to resolve the type with an eval to which we provide the globals and locals for its resolution.
331
+ # But it can happen that the type is defined in a local scope, so we need to go through all frames
332
+ # using the stack.
333
+ try:
334
+ resolved_type = eval(
335
+ annotated_type,
336
+ frame_info.frame.f_globals,
337
+ frame_info.frame.f_locals,
338
+ )
339
+ if resolved_type is not None:
340
+ annotated_type = resolved_type
341
+ break
342
+ except Exception:
343
+ continue
344
+ else:
345
+ raise TypeError(
346
+ f"Could not resolve type hint '{annotated_type}' for configuration variable '{name}'. "
347
+ "Please check that the type is imported and available."
348
+ )
349
+
350
+ type_info_directive: type | None = None
351
+ if self._type_info is not None:
352
+ type_info_directive = self._type_info
353
+
354
+ if annotated_type is not None and type_info_directive is not None:
355
+ # both type hint and type_info directive are defined, use type_info directive
356
+ self._type_info = type_info_directive
357
+ elif annotated_type is not None:
358
+ # only type hint is defined, use it
458
359
  self._type_info = annotated_type
360
+ elif type_info_directive is not None:
361
+ # only type_info directive is defined, use it
362
+ self._type_info = type_info_directive
363
+ else:
364
+ # no type hint nor type_info directive, default to str
365
+ self._type_info = str
459
366
 
460
- if self._split_to_list is None:
461
- origin = get_origin(annotated_type)
462
- args = get_args(annotated_type)
367
+ # We still need to handle split_to_list if not set and the type selected is a list
368
+ if self._split_to_list is None:
369
+ try:
370
+ origin = get_origin(self._type_info)
371
+ args = get_args(self._type_info)
463
372
 
464
373
  # Check for basic list
465
- if origin is list or annotated_type is list:
374
+ if origin is list or self._type_info is list:
466
375
  self._split_to_list = True
467
376
  # Check for Optional[list] / Union[list, ...]
468
377
  elif origin is Union:
@@ -471,9 +380,8 @@ class ConfigVarDef(Generic[_T]):
471
380
  if arg_origin is list or arg is list:
472
381
  self._split_to_list = True
473
382
  break
474
- else:
475
- # no type hint, consider it as a string
476
- self._type_info = str
383
+ except Exception:
384
+ pass
477
385
 
478
386
  def __get__(
479
387
  self, instance: Optional[AppConfig], owner: Optional[type] = None
@@ -481,119 +389,125 @@ class ConfigVarDef(Generic[_T]):
481
389
  """
482
390
  Descriptor method that gets the value of the configuration variable from the AppConfig instance.
483
391
 
484
- The value is retrieved from the instance's __dict__ using the variable's Name because we want to
392
+ The value is retrieved from the instance's __dict__ using the variable's name because we want to
485
393
  handle the case where we have dotted names for nested attributes.
486
394
  """
487
395
  if instance is None:
488
396
  return self
489
- name = self.Name
397
+ name = self.name
490
398
  if name in instance.__dict__:
491
- return instance[name] # type: ignore
399
+ return instance[name]
492
400
  return None
493
401
 
494
402
  def __set__(self, instance: AppConfig, value: Any) -> None:
495
403
  """
496
404
  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
405
+ The value is set in the instance's __dict__ using the variable's name because we want to
498
406
  handle the case where we have dotted names for nested attributes.
499
407
  """
500
- instance._set_value(self.Name, value)
408
+ instance._set_value(self.name, value)
501
409
 
502
410
  @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
411
+ def auto_prolog(self) -> Optional[bool]:
412
+ return self._auto_prolog
509
413
 
510
414
  @property
511
- def Help(self) -> str:
512
- return self._help
415
+ def can_be_relative_to(self) -> Optional[Path | str]:
416
+ return self._can_be_relative_to
513
417
 
514
418
  @property
515
- def Contexts(self) -> Sequence[str] | None:
516
- return self._contexts
419
+ def scopes(self) -> Sequence[str] | None:
420
+ return self._scopes
517
421
 
518
422
  @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
423
+ def default(self) -> Any | Callable[[str, Any, Any], Any] | Undefined:
424
+ return self._default
526
425
 
527
426
  @property
528
- def EnvName(self) -> Optional[str]:
529
- if self.flags.no_env_search:
427
+ def env_name(self) -> Optional[Sequence[str]]:
428
+ if self.flags.no_search or self.flags.no_env_search:
530
429
  return None
531
430
  else:
532
- return self._env_name if self._env_name is not None else self.Name
431
+ return self._env_name or self.name
533
432
 
534
433
  @property
535
- def FileKey(self) -> Optional[str]:
536
- if self.flags.no_conffile_search:
434
+ def file_key(self) -> Optional[str | Sequence[str]]:
435
+ if self.flags.no_search or self.flags.no_conffile_search:
537
436
  return None
538
437
  else:
539
- return self._file_key if self._file_key is not None else self.Name
438
+ return self._file_key or self.name
540
439
 
541
440
  @property
542
- def Default(self) -> Any | Callable[[str, Any, Any], Any] | Undefined:
543
- return self._default
441
+ def help(self) -> str:
442
+ return self._help
544
443
 
545
444
  @property
546
- def SplitToList(self) -> bool | str:
547
- return False if self._split_to_list is None else self._split_to_list
445
+ def make_dirs(self) -> Optional[PathType]:
446
+ return self._make_dirs
548
447
 
549
448
  @property
550
- def Transform(self) -> Optional[Callable[[str, Any, Any], Any]]:
551
- return self._transform
449
+ def mandatory(self) -> bool:
450
+ return True if self._mandatory is None else self._mandatory
451
+
452
+ @property
453
+ def no_dir_processing(self) -> bool:
454
+ return False if self._no_dir_processing is None else self._no_dir_processing
552
455
 
553
456
  @property
554
- def TypeInfo(self) -> Any:
555
- return self._type_info
457
+ def name(self) -> str:
458
+ if self._name is None:
459
+ raise RuntimeError(
460
+ "ConfigVarDef has no name. Declare it on an AppConfig subclass "
461
+ )
462
+ return self._name
556
463
 
557
464
  @property
558
- def NoDirProcessing(self) -> bool:
559
- return False if self._no_dir_processing is None else self._no_dir_processing
465
+ def split_to_list(self) -> bool | str:
466
+ return False if self._split_to_list is None else self._split_to_list
560
467
 
561
468
  @property
562
- def CanBeRelativeTo(self) -> Optional[Path | str]:
563
- return self._can_be_relative_to
469
+ def transform(self) -> Optional[Union[Callable[[str, Any, Any], Any], str]]:
470
+ return self._transform
564
471
 
565
472
  @property
566
- def MakeDirs(self) -> Optional[PathType]:
567
- return self._make_dirs
473
+ def value_key(self) -> Optional[str | Sequence[str]]:
474
+ """Note: we can't compute the final value here because it depends on the click_key_conversion
475
+ flag that can be set globally or in resolve_vars."""
476
+ if self.flags.no_search or self.flags.no_value_search:
477
+ return None
478
+ else:
479
+ return self._value_key or self.name
568
480
 
569
481
 
570
482
  def configvar(
571
483
  *,
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,
484
+ allow_override: Optional[bool] = None,
485
+ auto_prolog: Optional[bool] = None,
486
+ can_be_relative_to: Optional[Path | str] = None,
487
+ click_key_conversion: Optional[bool] = None,
488
+ scopes: Optional[Sequence[str]] = None,
489
+ default: Any | Callable[[str, Any, Any], Any] | Undefined = _undef,
490
+ env_name: Optional[str | Sequence[str]] = None,
491
+ file_key: Optional[str | Sequence[str]] = None,
492
+ help: str = "",
493
+ make_dirs: Optional[PathType] = None,
494
+ mandatory: Optional[bool] = None,
495
+ no_conffile_search: Optional[bool] = None,
496
+ no_dir_processing: Optional[bool] = None,
497
+ no_env_search: Optional[bool] = None,
498
+ no_search: Optional[bool] = None,
499
+ no_value_search: Optional[bool] = None,
500
+ split_to_list: Optional[bool | str] = None,
501
+ transform: Optional[Union[Callable[[str, Any, Any], Any], str]] = None,
502
+ type_info: Any = None,
503
+ value_key: Optional[str | Sequence[str]] = None,
590
504
  ) -> Any:
591
505
  """Create a declarative configuration variable definition.
592
506
 
593
507
  Use it as a class attribute on an AppConfig subclass:
594
508
 
595
509
  class MyCfg(AppConfig):
596
- my_var: int = configvar(Default=1)
510
+ my_var: int = configvar(default=1)
597
511
 
598
512
  The variable name defaults to the attribute name and its type is inferred from the type hint of the attribute declaration.
599
513
 
@@ -605,22 +519,24 @@ def configvar(
605
519
  """
606
520
 
607
521
  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,
522
+ allow_override=allow_override,
523
+ auto_prolog=auto_prolog,
524
+ can_be_relative_to=can_be_relative_to,
525
+ click_key_conversion=click_key_conversion,
526
+ scopes=scopes,
527
+ default=default,
528
+ env_name=env_name,
529
+ file_key=file_key,
530
+ help=help,
531
+ make_dirs=make_dirs,
532
+ mandatory=mandatory,
533
+ no_conffile_search=no_conffile_search,
534
+ no_dir_processing=no_dir_processing,
535
+ no_env_search=no_env_search,
536
+ no_search=no_search,
537
+ no_value_search=no_value_search,
538
+ split_to_list=split_to_list,
539
+ transform=transform,
540
+ type_info=type_info,
541
+ value_key=value_key,
626
542
  )