config2py 0.1.27__py3-none-any.whl → 0.1.29__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.
config2py/__init__.py CHANGED
@@ -18,4 +18,5 @@ from config2py.util import (
18
18
  get_configs_folder_for_app,
19
19
  is_repl,
20
20
  parse_assignments_from_py_source,
21
+ process_path,
21
22
  )
config2py/base.py CHANGED
@@ -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)
config2py/tools.py CHANGED
@@ -37,6 +37,7 @@ def get_configs_local_store(
37
37
  from config2py.s_configparser import ConfigStore
38
38
 
39
39
  return ConfigStore(config_src)
40
+
40
41
  elif os.path.sep not in config_src: # it's just a string
41
42
  # TODO: is "get" the right word, since it makes the folder too
42
43
  path = get_configs_folder_for_app(config_src, configs_name=configs_name)
config2py/util.py CHANGED
@@ -4,7 +4,7 @@ import re
4
4
  import os
5
5
  import ast
6
6
  from pathlib import Path
7
- from typing import Optional, Union, Any, Callable, Set
7
+ from typing import Optional, Union, Any, Callable, Set, Iterable
8
8
  import getpass
9
9
 
10
10
  DFLT_APP_NAME = 'config2py'
@@ -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
- response = getpass.getpass(prompt)
63
+ _prompt_func = getpass.getpass
59
64
  else:
60
- response = input(prompt)
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,
@@ -168,19 +176,19 @@ DFLT_APP_DATA_FOLDER = os.getenv(
168
176
 
169
177
 
170
178
  def process_path(
171
- path: str,
172
- *,
179
+ *path: Iterable[str],
173
180
  ensure_dir_exists=False,
174
181
  assert_exists=False,
175
182
  ensure_endswith_slash=False,
176
183
  ensure_does_not_end_with_slash=False,
177
184
  expanduser=True,
185
+ rootdir: str = '',
178
186
  ) -> str:
179
187
  """
180
188
  Process a path string, ensuring it exists, and optionally expanding user.
181
189
 
182
190
  Args:
183
- path (str): The path to process.
191
+ path (Iterable[str]): The path to process. Can be multiple components of a path.
184
192
  ensure_dir_exists (bool): Whether to ensure the path exists.
185
193
  assert_exists (bool): Whether to assert that the path exists.
186
194
  ensure_endswith_slash (bool): Whether to ensure the path ends with a slash.
@@ -190,11 +198,20 @@ def process_path(
190
198
  Returns:
191
199
  str: The processed path.
192
200
 
201
+ >>> process_path('a', 'b', 'c')
202
+ 'a/b/c'
203
+ >>> from functools import partial
204
+ >>> process_path('a', 'b', 'c', rootdir='/root/dir/', ensure_endswith_slash=True)
205
+ '/root/dir/a/b/c/'
206
+
193
207
  """
208
+ path = os.path.join(*path)
194
209
  if ensure_endswith_slash and ensure_does_not_end_with_slash:
195
210
  raise ValueError(
196
211
  'Cannot ensure both ends with slash and does not end with slash.'
197
212
  )
213
+ if rootdir:
214
+ path = os.path.join(rootdir, path)
198
215
  if expanduser:
199
216
  path = os.path.expanduser(path)
200
217
  if ensure_endswith_slash:
@@ -283,8 +300,8 @@ def get_app_data_folder(
283
300
  >>> get_app_data_folder() # doctest: +ELLIPSIS
284
301
  '.../.config/config2py'
285
302
 
286
- You can specify a different app name though.
287
- 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
288
305
  directory.
289
306
 
290
307
  >>> path = get_app_data_folder('my_app', ensure_exists=True) # doctest: +SKIP
@@ -303,7 +320,7 @@ def get_app_data_folder(
303
320
  get_app_data_rootdir(ensure_exists=ensure_exists), app_name
304
321
  )
305
322
  app_data_folder_did_not_exist = not os.path.isdir(app_data_path)
306
- app_data_folder = process_path(app_data_path, ensure_dir_exists=True)
323
+ process_path(app_data_path, ensure_dir_exists=True)
307
324
 
308
325
  if app_data_folder_did_not_exist:
309
326
  setup_callback(app_data_path)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: config2py
3
- Version: 0.1.27
3
+ Version: 0.1.29
4
4
  Summary: Simplified reading and writing configurations from various sources and formats
5
5
  Home-page: https://github.com/i2mint/config2py
6
6
  Author: OtoSense
@@ -0,0 +1,15 @@
1
+ config2py/__init__.py,sha256=ru-bUQk5EuOLzHmNAcElSN8P2saSUEb5KFRkfTkzsUo,770
2
+ config2py/base.py,sha256=saq1YVbeRg8jBoXcmc0y-maNJ5vHHeWwu5tc_EbOetE,15895
3
+ config2py/errors.py,sha256=QdwGsoJhv6LHDHp-_yyz4oUg1Fgu4S-S7O2nuA0a5cw,203
4
+ config2py/s_configparser.py,sha256=XhxFz6-PG4-QsecJfbjLFdBWHcPU6dwgqwkTZyY_y3E,15873
5
+ config2py/tools.py,sha256=k-PcyvcWFaO_vQn9gbkHf41CDh_sEtZYAWGRno7xsxo,8425
6
+ config2py/util.py,sha256=XW0kIu8MulBx9WbnEBJnByZ-wv08vqFJRtm2mzkRwok,15729
7
+ config2py/scrap/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ config2py/tests/__init__.py,sha256=sk-yGJQOZES2z70M4xmZB57tsxSktX_84ybDuV8Cz5Q,297
9
+ config2py/tests/test_tools.py,sha256=T0rBy8s6wHgQXnnr7Z1xkF1so3XkdGVASerEQ27ByxE,1950
10
+ config2py/tests/util.py,sha256=vO1VIepbH6vY2e-VHP7HX6jnVzzIDyFsp6md_uBnIXw,1351
11
+ config2py-0.1.29.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
12
+ config2py-0.1.29.dist-info/METADATA,sha256=-Rdx2t-wJ1rxNGDzXlNuBz4FsWyhOyR-a4oNLtPe6s4,14559
13
+ config2py-0.1.29.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
14
+ config2py-0.1.29.dist-info/top_level.txt,sha256=DFnlOIKMIGWQRROr3voJFhWFViHaWgTTeWZjC5YC9QQ,10
15
+ config2py-0.1.29.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.42.0)
2
+ Generator: bdist_wheel (0.43.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,15 +0,0 @@
1
- config2py/__init__.py,sha256=pIRwstYdRf794ew7o8zgb057dV-RSMi7uQYiod2IhHU,752
2
- config2py/base.py,sha256=hqXR0KeCsss24X7O_rxklWVBw-Gea1uUY7K-0ByNZvo,15244
3
- config2py/errors.py,sha256=QdwGsoJhv6LHDHp-_yyz4oUg1Fgu4S-S7O2nuA0a5cw,203
4
- config2py/s_configparser.py,sha256=XhxFz6-PG4-QsecJfbjLFdBWHcPU6dwgqwkTZyY_y3E,15873
5
- config2py/tools.py,sha256=G31ed8MrCPIMFNUuXeIkSQVWnfRgdK94d7Dmtg1vrDY,8424
6
- config2py/util.py,sha256=3nUgRmzJg7sXnutB1rR8BRHhRmJ9t_7xYCGWvwr1HOM,15237
7
- config2py/scrap/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- config2py/tests/__init__.py,sha256=sk-yGJQOZES2z70M4xmZB57tsxSktX_84ybDuV8Cz5Q,297
9
- config2py/tests/test_tools.py,sha256=T0rBy8s6wHgQXnnr7Z1xkF1so3XkdGVASerEQ27ByxE,1950
10
- config2py/tests/util.py,sha256=vO1VIepbH6vY2e-VHP7HX6jnVzzIDyFsp6md_uBnIXw,1351
11
- config2py-0.1.27.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
12
- config2py-0.1.27.dist-info/METADATA,sha256=HGBgqpSEVM4Afh_RhF1e4uvFVNK5Zg29Mna_sn--kO8,14559
13
- config2py-0.1.27.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
14
- config2py-0.1.27.dist-info/top_level.txt,sha256=DFnlOIKMIGWQRROr3voJFhWFViHaWgTTeWZjC5YC9QQ,10
15
- config2py-0.1.27.dist-info/RECORD,,