the1conf 1.3.0__py3-none-any.whl → 1.3.1__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/app_config.py CHANGED
@@ -389,19 +389,31 @@ class AppConfig(AttrDict, AutoProlog, metaclass=AppConfigMeta):
389
389
  self, vardef_to_serialize: Iterable[ConfigVarDef], for_file: bool = False
390
390
  ) -> dict[str, Any]:
391
391
  """
392
- Serialize the given variable definitions to a dictionary.
393
- We know that values came from strings and that they are either alone or in a list.
394
- Thus we can just use str() to serialize them.
392
+ Serialize the given variable definitions to a dictionary **that have a value**.
393
+
394
+ for_file is used to specify if we serialize for storing in a file or for display
395
+
396
+ if for file is True we serialize only variables that have no_conffile_search set to False and used
397
+ the first key in file_key as the key in the dict, otherwise the key is the variable name. In this case we don't
398
+ serialize variables whose type_info is None and their real type is not string because we would not be able to
399
+ read them back from the file and get the same value.
400
+
401
+ We use str() to serialize configuration variables .
395
402
  To detect list we need to look at the split_to_list attribute of the ConfigVarDef and call str() on
396
403
  each element of the list, then join them with the separator used to create the list.
397
- If for_file is True we serialize only variables that have no_conffile_search set to False and
398
- used file_key as the key in the dict, otherwise the key is the variable name.
399
404
  """
400
405
  # we are going to take advantage of the AttrDict to accept dotted keys in order to create nested dicts.
401
406
  dumper = AttrDict()
402
407
  for var in vardef_to_serialize:
403
- if for_file and not var.flags.no_conffile_search:
404
- key = var.file_key
408
+ if not self.has_value(var.name):
409
+ # we skip variables that don't have a value, surely their scope hasn't been resolved yet.
410
+ continue
411
+ stringifiable = True
412
+ if var._type_info is None and not isinstance(self[var.name], str):
413
+ # we don't know how to put this varialbe in a file so that we can read it afterward and get back the same value.
414
+ stringifiable = False
415
+ if for_file and var.file_key is not None and stringifiable:
416
+ key = var.file_key[0]
405
417
  elif not for_file:
406
418
  key = var.name
407
419
  else:
the1conf/click_option.py CHANGED
@@ -3,6 +3,7 @@ from __future__ import annotations
3
3
  from typing import Any, Callable
4
4
 
5
5
  import click
6
+ from click.types import BoolParamType
6
7
 
7
8
  from .app_config import ConfigVarDef
8
9
  from .utils import Undefined, _undef
@@ -17,6 +18,15 @@ class StringOrUndefined(click.ParamType):
17
18
  return click.STRING.convert(value, param, ctx)
18
19
 
19
20
 
21
+ class BoolOrUndefined(BoolParamType):
22
+ name = "boolean"
23
+
24
+ def convert(self, value: Any, param: Any, ctx: Any) -> Any:
25
+ if value is _undef:
26
+ return value
27
+ return super().convert(value, param, ctx)
28
+
29
+
20
30
  def click_option(config_var: Any, **kwargs: Any) -> Callable[[Any], Any]:
21
31
  """Wrapper for click.option with the metadata from ConfigVarDef."""
22
32
  if not isinstance(config_var, ConfigVarDef):
@@ -33,15 +43,24 @@ def click_option(config_var: Any, **kwargs: Any) -> Callable[[Any], Any]:
33
43
  else:
34
44
  keys = config_var.value_key
35
45
 
46
+ is_bool = config_var._type_info is bool
47
+
36
48
  for key in keys:
37
49
  if len(key) == 1:
38
50
  param_decls.append(f"-{key}")
39
51
  else:
40
- param_decls.append(f"--{key.replace('_', '-').lower()}")
52
+ param_name_str = key.replace("_", "-").lower()
53
+ if is_bool:
54
+ param_decls.append(f"--{param_name_str}/--no-{param_name_str}")
55
+ else:
56
+ param_decls.append(f"--{param_name_str}")
41
57
 
42
58
  # Used as destination in the dict returned by click
43
59
  param_name = keys[0]
44
60
 
61
+ if is_bool:
62
+ kwargs["is_flag"] = True
63
+
45
64
  # 2. Documentation
46
65
  if "help" not in kwargs and config_var.help:
47
66
  kwargs["help"] = config_var.help
@@ -49,7 +68,10 @@ def click_option(config_var: Any, **kwargs: Any) -> Callable[[Any], Any]:
49
68
  # 3. Strict constraint: Always strings
50
69
  # We overwrite any passed type to ensure that resolve_vars receives a string.
51
70
  # We use a custom type to handle the `_undef` default value without converting it to string.
52
- kwargs["type"] = StringOrUndefined()
71
+ if not is_bool:
72
+ kwargs["type"] = StringOrUndefined()
73
+ else:
74
+ kwargs["type"] = BoolOrUndefined()
53
75
 
54
76
  # We set default to `_undef` to distinguish between "option not provided" (_undef)
55
77
  # and "option provided with no value" (which shouldn't happen with string) or None.
@@ -69,4 +91,4 @@ def click_option(config_var: Any, **kwargs: Any) -> Callable[[Any], Any]:
69
91
  # Note: We do NOT pass 'envvar' nor 'default'
70
92
 
71
93
  # We force the destination name to match the key expected by the1conf
72
- return click.option(*param_decls, param_name, **kwargs)
94
+ return click.option(*param_decls, **kwargs)
the1conf/config_var.py CHANGED
@@ -209,7 +209,7 @@ class ConfigVarDef(Generic[_T]):
209
209
  _name: Optional[str] = None
210
210
  _split_to_list: Optional[bool | str] = None
211
211
  _transform: Optional[Union[Callable[[str, Any, Any], Any], str]] = None
212
- _type_info: Any = None
212
+ _type_info: Any = _undef
213
213
  _value_key: Optional[Sequence[str]] = None
214
214
 
215
215
  def __init__(
@@ -233,7 +233,7 @@ class ConfigVarDef(Generic[_T]):
233
233
  no_value_search: Optional[bool] = None,
234
234
  split_to_list: Optional[bool | str] = None,
235
235
  transform: Optional[Union[Callable[[str, Any, Any], Any], str]] = None,
236
- type_info: Any = None,
236
+ type_info: Any = _undef,
237
237
  value_key: Optional[str | Sequence[str]] = None,
238
238
  ) -> None:
239
239
  """Initialize the configuration variable definition.
@@ -289,6 +289,7 @@ class ConfigVarDef(Generic[_T]):
289
289
  no_conffile_search=no_conffile_search,
290
290
  click_key_conversion=click_key_conversion,
291
291
  allow_override=allow_override,
292
+ no_search=no_search,
292
293
  )
293
294
 
294
295
  def __set_name__(self, owner: type, name: str) -> None:
@@ -347,17 +348,15 @@ class ConfigVarDef(Generic[_T]):
347
348
  "Please check that the type is imported and available."
348
349
  )
349
350
 
350
- type_info_directive: type | None = None
351
- if self._type_info is not None:
352
- type_info_directive = self._type_info
351
+ type_info_directive = self._type_info
353
352
 
354
- if annotated_type is not None and type_info_directive is not None:
353
+ if annotated_type is not None and type_info_directive != _undef:
355
354
  # both type hint and type_info directive are defined, use type_info directive
356
355
  self._type_info = type_info_directive
357
356
  elif annotated_type is not None:
358
357
  # only type hint is defined, use it
359
358
  self._type_info = annotated_type
360
- elif type_info_directive is not None:
359
+ elif type_info_directive != _undef:
361
360
  # only type_info directive is defined, use it
362
361
  self._type_info = type_info_directive
363
362
  else:
@@ -428,14 +427,14 @@ class ConfigVarDef(Generic[_T]):
428
427
  if self.flags.no_search or self.flags.no_env_search:
429
428
  return None
430
429
  else:
431
- return self._env_name or self.name
430
+ return self._env_name or [self.name]
432
431
 
433
432
  @property
434
- def file_key(self) -> Optional[str | Sequence[str]]:
433
+ def file_key(self) -> Optional[Sequence[str]]:
435
434
  if self.flags.no_search or self.flags.no_conffile_search:
436
435
  return None
437
436
  else:
438
- return self._file_key or self.name
437
+ return self._file_key or [self.name]
439
438
 
440
439
  @property
441
440
  def help(self) -> str:
@@ -470,13 +469,13 @@ class ConfigVarDef(Generic[_T]):
470
469
  return self._transform
471
470
 
472
471
  @property
473
- def value_key(self) -> Optional[str | Sequence[str]]:
472
+ def value_key(self) -> Optional[Sequence[str]]:
474
473
  """Note: we can't compute the final value here because it depends on the click_key_conversion
475
474
  flag that can be set globally or in resolve_vars."""
476
475
  if self.flags.no_search or self.flags.no_value_search:
477
476
  return None
478
477
  else:
479
- return self._value_key or self.name
478
+ return self._value_key or [self.name]
480
479
 
481
480
 
482
481
  def configvar(
@@ -499,7 +498,7 @@ def configvar(
499
498
  no_value_search: Optional[bool] = None,
500
499
  split_to_list: Optional[bool | str] = None,
501
500
  transform: Optional[Union[Callable[[str, Any, Any], Any], str]] = None,
502
- type_info: Any = None,
501
+ type_info: Any = _undef,
503
502
  value_key: Optional[str | Sequence[str]] = None,
504
503
  ) -> Any:
505
504
  """Create a declarative configuration variable definition.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: the1conf
3
- Version: 1.3.0
3
+ Version: 1.3.1
4
4
  Summary: All in one configuration management tool for your python applications.
5
5
  License-Expression: MIT
6
6
  License-File: LICENSE
@@ -1,16 +1,16 @@
1
1
  the1conf/__init__.py,sha256=TdB7eB_sfFkr1tQJhNQuPj5GidtkHLWyykYCrpQvljw,328
2
- the1conf/app_config.py,sha256=2yQXS-yQ8Z1TduR3V3IcXeHaE9IqpGvVH_s2SA926rU,23022
2
+ the1conf/app_config.py,sha256=E2eg8D55Z_SQncb833P3KBIdQuQzhnhA2V-ZV1Rm5l0,23738
3
3
  the1conf/app_config_meta.py,sha256=qRS1t4yu-gUgg2UpUrlPs1mZiwTsvg9TGS8ihq00U_k,9321
4
4
  the1conf/attr_dict.py,sha256=p5ihUUz9dXSVbkwWVC5hMTX2EjZ5G3gPxdP_Qs-xzJU,5692
5
5
  the1conf/auto_prolog.py,sha256=bWmE8soWCUwPXuAgy7Bha5DiZY94jMpGMdiqvRwTHfc,6075
6
- the1conf/click_option.py,sha256=UYMa4LdxfmehTf88kX9-2Y77_NbD6D023xF8Po5FqVY,2527
7
- the1conf/config_var.py,sha256=M79uMHiN_GHdbGrcEiHQHOaSPyVVeBpNqXe0vv0s4sM,27308
6
+ the1conf/click_option.py,sha256=42ZLOoybugPPRtCcWqHNIVSwNG8SGEk-mKSktYCkbrY,3124
7
+ the1conf/config_var.py,sha256=4GjA2oV9ZmYZb9SDDsWzhqLJekVZKSgC2SJ5Cv1EeQk,27245
8
8
  the1conf/flags.py,sha256=I2K2_RrftGZcYQavmdftH8cdaM1Cd67QT3P_xpVGxHY,5785
9
9
  the1conf/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
10
  the1conf/solver.py,sha256=9SoYCUwAZ67nhMNULWA0G-5mlR2roh3F5cjai4SOnHU,14686
11
11
  the1conf/utils.py,sha256=fdnsYVM2dzqX5wKh_8vJSUxT293Yametsm64yNduC3I,3385
12
12
  the1conf/var_substitution.py,sha256=mBo7ooZw6aq6g0E0OtxOCKUhNIFM5vQCC4HzecOzFck,1960
13
- the1conf-1.3.0.dist-info/METADATA,sha256=F6EwJU5IpMvO7vbLShsjEPaamUb1cVTmQ3NlUPztGQA,35633
14
- the1conf-1.3.0.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
15
- the1conf-1.3.0.dist-info/licenses/LICENSE,sha256=uQf4G5VB4kzrWVzT_gA_vqn9bUQzaRlL4RkeFI17BeI,1069
16
- the1conf-1.3.0.dist-info/RECORD,,
13
+ the1conf-1.3.1.dist-info/METADATA,sha256=8qNUKgZqOeA4F4APaRubfQP-5nk_9XPR9YtvECTuYnk,35633
14
+ the1conf-1.3.1.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
15
+ the1conf-1.3.1.dist-info/licenses/LICENSE,sha256=uQf4G5VB4kzrWVzT_gA_vqn9bUQzaRlL4RkeFI17BeI,1069
16
+ the1conf-1.3.1.dist-info/RECORD,,