config2py 0.1.28__tar.gz → 0.1.30__tar.gz
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.
- {config2py-0.1.28 → config2py-0.1.30}/PKG-INFO +1 -1
- {config2py-0.1.28 → config2py-0.1.30}/config2py/base.py +23 -5
- {config2py-0.1.28 → config2py-0.1.30}/config2py/util.py +13 -5
- {config2py-0.1.28 → config2py-0.1.30}/config2py.egg-info/PKG-INFO +1 -1
- {config2py-0.1.28 → config2py-0.1.30}/setup.cfg +1 -1
- {config2py-0.1.28 → config2py-0.1.30}/LICENSE +0 -0
- {config2py-0.1.28 → config2py-0.1.30}/README.md +0 -0
- {config2py-0.1.28 → config2py-0.1.30}/config2py/__init__.py +0 -0
- {config2py-0.1.28 → config2py-0.1.30}/config2py/errors.py +0 -0
- {config2py-0.1.28 → config2py-0.1.30}/config2py/s_configparser.py +0 -0
- {config2py-0.1.28 → config2py-0.1.30}/config2py/scrap/__init__.py +0 -0
- {config2py-0.1.28 → config2py-0.1.30}/config2py/tests/__init__.py +0 -0
- {config2py-0.1.28 → config2py-0.1.30}/config2py/tests/test_tools.py +0 -0
- {config2py-0.1.28 → config2py-0.1.30}/config2py/tests/util.py +0 -0
- {config2py-0.1.28 → config2py-0.1.30}/config2py/tools.py +0 -0
- {config2py-0.1.28 → config2py-0.1.30}/config2py.egg-info/SOURCES.txt +0 -0
- {config2py-0.1.28 → config2py-0.1.30}/config2py.egg-info/dependency_links.txt +0 -0
- {config2py-0.1.28 → config2py-0.1.30}/config2py.egg-info/not-zip-safe +0 -0
- {config2py-0.1.28 → config2py-0.1.30}/config2py.egg-info/requires.txt +0 -0
- {config2py-0.1.28 → config2py-0.1.30}/config2py.egg-info/top_level.txt +0 -0
- {config2py-0.1.28 → config2py-0.1.30}/setup.py +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Base for getting configs from various sources and formats
|
|
3
3
|
"""
|
|
4
|
+
|
|
4
5
|
from collections import ChainMap
|
|
5
6
|
from typing import (
|
|
6
7
|
Callable,
|
|
@@ -317,6 +318,15 @@ class FuncBasedGettableContainer:
|
|
|
317
318
|
else:
|
|
318
319
|
return True
|
|
319
320
|
|
|
321
|
+
def __iter__(self):
|
|
322
|
+
raise TypeError(
|
|
323
|
+
f'Iteration is NOT ACTUALLY implemented for {type(self).__name__}. '
|
|
324
|
+
'The reason we still stuck it in the class is because python will '
|
|
325
|
+
'automatically try to use __getitem__ on 0, 1, ... to iterate over the '
|
|
326
|
+
'object, and we want to avoid that, since this would result in an obscure '
|
|
327
|
+
"error message saying that the getter function can't be called on 0"
|
|
328
|
+
)
|
|
329
|
+
|
|
320
330
|
|
|
321
331
|
def gettable_containers(
|
|
322
332
|
sources: Sources,
|
|
@@ -353,11 +363,19 @@ KTSaver = Callable[[KT, VT], Any]
|
|
|
353
363
|
SaveTo = Optional[Union[MutableMapping, KTSaver]]
|
|
354
364
|
|
|
355
365
|
|
|
366
|
+
def is_not_empty(val) -> bool:
|
|
367
|
+
if isinstance(val, str):
|
|
368
|
+
return val != ''
|
|
369
|
+
else:
|
|
370
|
+
return val is not None
|
|
371
|
+
|
|
372
|
+
|
|
356
373
|
def ask_user_for_key(
|
|
357
374
|
key=None,
|
|
358
375
|
*,
|
|
359
376
|
prompt_template='Enter a value for {}: ',
|
|
360
377
|
save_to: SaveTo = None,
|
|
378
|
+
save_condition=is_not_empty,
|
|
361
379
|
user_asker=ask_user_for_input,
|
|
362
380
|
egress: Optional[Callable] = None,
|
|
363
381
|
):
|
|
@@ -372,7 +390,7 @@ def ask_user_for_key(
|
|
|
372
390
|
val = user_asker(prompt_template.format(key))
|
|
373
391
|
if isinstance(egress, Callable):
|
|
374
392
|
val = egress(key, val)
|
|
375
|
-
if save_to is not None:
|
|
393
|
+
if save_to is not None and save_condition(val):
|
|
376
394
|
if hasattr(save_to, '__setitem__'):
|
|
377
395
|
save_to_func = save_to.__setitem__
|
|
378
396
|
save_to_func(key, val)
|
|
@@ -397,7 +415,7 @@ def user_gettable(
|
|
|
397
415
|
contain a placeholder for the key, e.g. ``"Enter a value for {}: "``.
|
|
398
416
|
:param egress: A function to apply to the user's response before returning it.
|
|
399
417
|
This can be used to validate the response, for example.
|
|
400
|
-
:param user_asker: A function that asks the user for input. It should take a
|
|
418
|
+
:param user_asker: A function that asks the user for input. It should take a
|
|
401
419
|
prompt string and return the user's response.
|
|
402
420
|
:param val_is_valid: A function that takes a value and returns a boolean. If it
|
|
403
421
|
returns ``False``, the user will be asked for a new value.
|
|
@@ -408,7 +426,7 @@ def user_gettable(
|
|
|
408
426
|
it.
|
|
409
427
|
|
|
410
428
|
Example:
|
|
411
|
-
|
|
429
|
+
|
|
412
430
|
>>> s = user_gettable()
|
|
413
431
|
>>> v = s['SOME_KEY'] # doctest: +SKIP
|
|
414
432
|
'SOME_VAL'
|
|
@@ -416,8 +434,8 @@ def user_gettable(
|
|
|
416
434
|
This will trigger a prompt for the user to enter the value of ``SOME_KEY``.
|
|
417
435
|
When they do (say they entered 'SOME_VAL') it will return that value.
|
|
418
436
|
|
|
419
|
-
And if you specify a save_to store (usually a persistent MutableMapping made with
|
|
420
|
-
the ``dol`` package) then it will save the value to that store for future use.
|
|
437
|
+
And if you specify a save_to store (usually a persistent MutableMapping made with
|
|
438
|
+
the ``dol`` package) then it will save the value to that store for future use.
|
|
421
439
|
|
|
422
440
|
>>> d = dict(some='store')
|
|
423
441
|
>>> s = user_gettable(save_to=d)
|
|
@@ -21,6 +21,11 @@ def identity(x: Any) -> Any:
|
|
|
21
21
|
return x
|
|
22
22
|
|
|
23
23
|
|
|
24
|
+
def is_not_empty(x: Any) -> bool:
|
|
25
|
+
"""Function that returns True if x is not empty."""
|
|
26
|
+
return bool(x)
|
|
27
|
+
|
|
28
|
+
|
|
24
29
|
# TODO: Make this into an open-closed mini-framework
|
|
25
30
|
def ask_user_for_input(
|
|
26
31
|
prompt: str,
|
|
@@ -55,9 +60,12 @@ def ask_user_for_input(
|
|
|
55
60
|
if default:
|
|
56
61
|
prompt = prompt + f' [{default}]: '
|
|
57
62
|
if mask_input:
|
|
58
|
-
|
|
63
|
+
_prompt_func = getpass.getpass
|
|
59
64
|
else:
|
|
60
|
-
|
|
65
|
+
_prompt_func = input
|
|
66
|
+
|
|
67
|
+
response = _prompt_func(prompt)
|
|
68
|
+
|
|
61
69
|
if masking_toggle_str is not None and response == masking_toggle_str:
|
|
62
70
|
return ask_user_for_input(
|
|
63
71
|
_original_prompt,
|
|
@@ -292,8 +300,8 @@ def get_app_data_folder(
|
|
|
292
300
|
>>> get_app_data_folder() # doctest: +ELLIPSIS
|
|
293
301
|
'.../.config/config2py'
|
|
294
302
|
|
|
295
|
-
You can specify a different app name though.
|
|
296
|
-
And if you want, you can also specify a callback function to initialize the
|
|
303
|
+
You can specify a different app name though.
|
|
304
|
+
And if you want, you can also specify a callback function to initialize the
|
|
297
305
|
directory.
|
|
298
306
|
|
|
299
307
|
>>> path = get_app_data_folder('my_app', ensure_exists=True) # doctest: +SKIP
|
|
@@ -395,7 +403,7 @@ def is_repl():
|
|
|
395
403
|
return False
|
|
396
404
|
|
|
397
405
|
|
|
398
|
-
is_repl.repl_conditions: Set[Callable] = _repl_conditions
|
|
406
|
+
is_repl.repl_conditions: Set[Callable] = _repl_conditions # type: ignore
|
|
399
407
|
|
|
400
408
|
|
|
401
409
|
def _value_node_is_instance_of(
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|