config2py 0.1.36__py3-none-any.whl → 0.1.38__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
@@ -13,6 +13,7 @@ from config2py.tools import (
13
13
  )
14
14
  from config2py.base import get_config, user_gettable, sources_chainmap
15
15
  from config2py.util import (
16
+ envvar, # os.environ, but with dict display override to hide secrets
16
17
  ask_user_for_input,
17
18
  get_app_data_folder,
18
19
  get_configs_folder_for_app,
config2py/base.py CHANGED
@@ -94,7 +94,7 @@ GetConfigEgress = Callable[[KT, VT], VT]
94
94
 
95
95
  def is_not_none_nor_empty(x):
96
96
  if isinstance(x, str):
97
- return x != ''
97
+ return x != ""
98
98
  else:
99
99
  return x is not None
100
100
 
@@ -211,7 +211,7 @@ def get_config(
211
211
  value = chain_map.get(key, not_found)
212
212
  if value is not_found:
213
213
  if default is no_default:
214
- raise ConfigNotFound(f'Could not find config for key: {key}')
214
+ raise ConfigNotFound(f"Could not find config for key: {key}")
215
215
  else:
216
216
  value = default
217
217
  if egress is not None:
@@ -299,11 +299,11 @@ class FuncBasedGettableContainer:
299
299
  v = self.getter(k)
300
300
  except self.config_not_found_exceptions as e:
301
301
  raise KeyError(
302
- f'There was an exception when computing key: {k} with the function '
303
- f'{self.getter}. The exception was: {e}'
302
+ f"There was an exception when computing key: {k} with the function "
303
+ f"{self.getter}. The exception was: {e}"
304
304
  )
305
305
  if not self.val_is_valid(v):
306
- raise KeyError(f'Value for key {k} is not valid: {v}')
306
+ raise KeyError(f"Value for key {k} is not valid: {v}")
307
307
  return v
308
308
 
309
309
  # TODO: Is this used to indicate that the getter couldn't find a key.
@@ -317,10 +317,10 @@ class FuncBasedGettableContainer:
317
317
 
318
318
  def __iter__(self):
319
319
  raise TypeError(
320
- f'Iteration is NOT ACTUALLY implemented for {type(self).__name__}. '
321
- 'The reason we still stuck it in the class is because python will '
322
- 'automatically try to use __getitem__ on 0, 1, ... to iterate over the '
323
- 'object, and we want to avoid that, since this would result in an obscure '
320
+ f"Iteration is NOT ACTUALLY implemented for {type(self).__name__}. "
321
+ "The reason we still stuck it in the class is because python will "
322
+ "automatically try to use __getitem__ on 0, 1, ... to iterate over the "
323
+ "object, and we want to avoid that, since this would result in an obscure "
324
324
  "error message saying that the getter function can't be called on 0"
325
325
  )
326
326
 
@@ -342,7 +342,7 @@ def gettable_containers(
342
342
  )
343
343
  else:
344
344
  raise AssertionError(
345
- f'Source must be a Gettable or a Callable, not {type(src)}'
345
+ f"Source must be a Gettable or a Callable, not {type(src)}"
346
346
  )
347
347
 
348
348
 
@@ -362,7 +362,7 @@ SaveTo = Optional[Union[MutableMapping, KTSaver]]
362
362
 
363
363
  def is_not_empty(val) -> bool:
364
364
  if isinstance(val, str):
365
- return val != ''
365
+ return val != ""
366
366
  else:
367
367
  return val is not None
368
368
 
@@ -370,7 +370,7 @@ def is_not_empty(val) -> bool:
370
370
  def ask_user_for_key(
371
371
  key=None,
372
372
  *,
373
- prompt_template='Enter a value for {}: ',
373
+ prompt_template="Enter a value for {}: ",
374
374
  save_to: SaveTo = None,
375
375
  save_condition=is_not_empty,
376
376
  user_asker=ask_user_for_input,
@@ -389,7 +389,7 @@ def ask_user_for_key(
389
389
  if isinstance(egress, Callable):
390
390
  val = egress(key, val)
391
391
  if save_to is not None and save_condition(val):
392
- if hasattr(save_to, '__setitem__'):
392
+ if hasattr(save_to, "__setitem__"):
393
393
  save_to_func = save_to.__setitem__
394
394
  save_to_func(key, val)
395
395
  return val
@@ -398,7 +398,7 @@ def ask_user_for_key(
398
398
  def user_gettable(
399
399
  save_to: SaveTo = None,
400
400
  *,
401
- prompt_template='Enter a value for {}: ',
401
+ prompt_template="Enter a value for {}: ",
402
402
  egress: Optional[Callable] = None,
403
403
  user_asker=ask_user_for_input,
404
404
  val_is_valid: Callable[[VT], bool] = is_not_empty,
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Data Object Layer for configparser standard lib.
3
3
  """
4
+
4
5
  from configparser import ConfigParser
5
6
  from configparser import BasicInterpolation, ExtendedInterpolation
6
7
  from functools import wraps
@@ -10,7 +11,7 @@ from dol import Store
10
11
 
11
12
  # from py2store.signatures import Sig
12
13
 
13
- _test_config_str = '''[Simple Values]
14
+ _test_config_str = """[Simple Values]
14
15
  key=value
15
16
  spaces in keys=allowed
16
17
  spaces in values=allowed as well
@@ -51,7 +52,7 @@ empty string value here =
51
52
  deeper than the first line
52
53
  of a value
53
54
  # Did I mention we can indent comments, too?
54
- '''
55
+ """
55
56
 
56
57
 
57
58
  def persist_after_operation(method_func):
@@ -87,7 +88,7 @@ def super_and_persist(super_cls, method_name):
87
88
 
88
89
 
89
90
  ConfigParserStore = Store.wrap(ConfigParser)
90
- ConfigParserStore.__name__ = 'ConfigParserStore'
91
+ ConfigParserStore.__name__ = "ConfigParserStore"
91
92
 
92
93
 
93
94
  # TODO: ConfigParser is already a mapping, but pros/cons of subclassing?
@@ -199,7 +200,8 @@ class ConfigStore(ConfigParserStore):
199
200
  >>> ConfigReader(ini_filepath).to_dict()
200
201
  {'DEFAULT': {}, 'nothing': {'like': 'that', 'ever': 'happened'}}
201
202
 
202
- """
203
+ """
204
+
203
205
  space_around_delimiters = True
204
206
  BasicInterpolation = BasicInterpolation
205
207
  ExtendedInterpolation = ExtendedInterpolation
@@ -224,24 +226,24 @@ class ConfigStore(ConfigParserStore):
224
226
  self._within_context_manager = False
225
227
 
226
228
  if isinstance(source, str):
227
- if '\n' in source:
229
+ if "\n" in source:
228
230
  self.read_string(source)
229
- source_kind = 'string'
231
+ source_kind = "string"
230
232
  else:
231
233
  self.read(source)
232
- source_kind = 'filepath'
234
+ source_kind = "filepath"
233
235
  elif isinstance(source, bytes):
234
236
  self.read_string(source.decode())
235
- source_kind = 'bytes'
237
+ source_kind = "bytes"
236
238
  elif isinstance(source, dict):
237
239
  self.read_dict(source)
238
- source_kind = 'dict'
239
- elif hasattr(source, 'read'):
240
+ source_kind = "dict"
241
+ elif hasattr(source, "read"):
240
242
  self.read_file(source)
241
- source_kind = 'stream'
243
+ source_kind = "stream"
242
244
  else:
243
245
  self.read(source)
244
- source_kind = 'unknown'
246
+ source_kind = "unknown"
245
247
  self.source = source
246
248
  self.source_kind = source_kind
247
249
  self.target_kind = target_kind or source_kind
@@ -264,28 +266,28 @@ class ConfigStore(ConfigParserStore):
264
266
  Persists means to call
265
267
  """
266
268
  if not self._within_context_manager:
267
- if self.target_kind == 'filepath':
268
- with open(self.source, 'w') as fp:
269
+ if self.target_kind == "filepath":
270
+ with open(self.source, "w") as fp:
269
271
  return self.write(fp, self.space_around_delimiters)
270
272
  else:
271
- if self.target_kind == 'stream':
273
+ if self.target_kind == "stream":
272
274
  target = self.source
273
275
  return self.write(target, self.space_around_delimiters)
274
- elif self.target_kind in {'string', 'bytes'}:
276
+ elif self.target_kind in {"string", "bytes"}:
275
277
  string_target = StringIO()
276
278
  self.write(string_target, self.space_around_delimiters)
277
279
  string_target.seek(0)
278
280
  string_data = string_target.read()
279
- if self.target_kind == 'string':
281
+ if self.target_kind == "string":
280
282
  return string_data
281
- elif self.target_kind == 'bytes':
283
+ elif self.target_kind == "bytes":
282
284
  return string_data.encode()
283
285
  else:
284
- raise ValueError(f'Unknown target_kind: {self.target_kind}')
285
- elif self.target_kind == 'dict':
286
+ raise ValueError(f"Unknown target_kind: {self.target_kind}")
287
+ elif self.target_kind == "dict":
286
288
  return self.to_dict()
287
289
  else:
288
- raise ValueError(f'Unknown target_kind: {self.target_kind}')
290
+ raise ValueError(f"Unknown target_kind: {self.target_kind}")
289
291
 
290
292
  def __enter__(self):
291
293
  self._within_context_manager = True
@@ -360,13 +362,13 @@ class ConfigReader(ConfigStore):
360
362
  """
361
363
 
362
364
  def persist(self):
363
- raise NotImplementedError('persist disabled for ConfigReader')
365
+ raise NotImplementedError("persist disabled for ConfigReader")
364
366
 
365
367
  def __setitem__(self, k, v):
366
- raise NotImplementedError('__setitem__ disabled for ConfigReader')
368
+ raise NotImplementedError("__setitem__ disabled for ConfigReader")
367
369
 
368
370
  def __delitem__(self, k):
369
- raise NotImplementedError('__delitem__ disabled for ConfigReader')
371
+ raise NotImplementedError("__delitem__ disabled for ConfigReader")
370
372
 
371
373
 
372
374
  # TODO: Need to wrap SectionProxy to make this work, since the obj and data here are
@@ -404,11 +406,11 @@ def postprocess_ini_section_items(items: Union[Mapping, Iterable]) -> Generator:
404
406
  {'name': 'aspyre', 'keywords': ['documentation', 'packaging', 'publishing']}
405
407
 
406
408
  """
407
- splitter_re = re.compile('[\n\r\t]+')
409
+ splitter_re = re.compile("[\n\r\t]+")
408
410
  if isinstance(items, Mapping):
409
411
  items = items.items()
410
412
  for k, v in items:
411
- if v.startswith('\n'):
413
+ if v.startswith("\n"):
412
414
  v = splitter_re.split(v[1:])
413
415
  v = [vv.strip() for vv in v if vv.strip()]
414
416
  yield k, v
@@ -433,5 +435,5 @@ def preprocess_ini_section_items(items: Union[Mapping, Iterable]) -> Generator:
433
435
  items = items.items()
434
436
  for k, v in items:
435
437
  if isinstance(v, list):
436
- v = '\n\t' + '\n\t'.join(v)
438
+ v = "\n\t" + "\n\t".join(v)
437
439
  yield k, v
@@ -12,21 +12,21 @@ from config2py.tests.utils_for_testing import user_input_patch
12
12
 
13
13
  @pytest.fixture
14
14
  def mock_config_store_factory():
15
- with patch('config2py.tools.get_configs_local_store') as mock_factory:
15
+ with patch("config2py.tools.get_configs_local_store") as mock_factory:
16
16
  yield mock_factory
17
17
 
18
18
 
19
19
  def test_simple_config_getter(mock_config_store_factory):
20
- key = '_CONFIG2PY_SAFE_TO_DELETE_VAR_'
20
+ key = "_CONFIG2PY_SAFE_TO_DELETE_VAR_"
21
21
 
22
22
  # Set up mock config store
23
- mock_config_store = {key: 'from store'}
23
+ mock_config_store = {key: "from store"}
24
24
  mock_config_store_factory.return_value = mock_config_store
25
25
 
26
26
  # Test getting config from environment variable
27
- os.environ[key] = 'from env var'
27
+ os.environ[key] = "from env var"
28
28
  config_getter = simple_config_getter(first_look_in_env_vars=True)
29
- assert config_getter(key) == 'from env var'
29
+ assert config_getter(key) == "from env var"
30
30
 
31
31
  # Test getting config from central config store
32
32
  del os.environ[key] # delete env var to test central config store
@@ -34,9 +34,9 @@ def test_simple_config_getter(mock_config_store_factory):
34
34
  # assert config_getter(key) == "from store"
35
35
 
36
36
  # Test getting config with ask_user_if_key_not_found=True
37
- with patch('builtins.input', return_value='from user'):
37
+ with patch("builtins.input", return_value="from user"):
38
38
  config_getter = simple_config_getter(ask_user_if_key_not_found=True)
39
- assert config_getter('new_key') == 'from user'
39
+ assert config_getter("new_key") == "from user"
40
40
 
41
41
  # TODO: Make this test work
42
42
  # Test that config_getter.configs is set correctly
@@ -57,7 +57,7 @@ def test_simple_config_getter_with_user_input(monkeypatch):
57
57
  my_get_config = simple_config_getter(
58
58
  local_config_dir, ask_user_if_key_not_found=True
59
59
  )
60
- config_name = 'SOME_CONFIG_NAME'
60
+ config_name = "SOME_CONFIG_NAME"
61
61
 
62
62
  # make sure config_name not in environment or local_config_dir
63
63
  assert config_name not in os.environ
@@ -67,41 +67,41 @@ def test_simple_config_getter_with_user_input(monkeypatch):
67
67
  # should try to get it from the user
68
68
 
69
69
  # Use monkeypatch to replace the input function with the mock_input function
70
- user_inputs('') # user enters nothing
71
- val = my_get_config(config_name, default='default_value')
70
+ user_inputs("") # user enters nothing
71
+ val = my_get_config(config_name, default="default_value")
72
72
 
73
73
  # This the user didn't enter anything, the default value should be returned:
74
- assert val == 'default_value'
74
+ assert val == "default_value"
75
75
 
76
76
  # Still no config_name in the local_config_dir
77
77
  assert config_name not in os.listdir(local_config_dir)
78
78
 
79
- user_inputs('user_value') # user enters user_value
80
- val = my_get_config(config_name, default='default_value')
79
+ user_inputs("user_value") # user enters user_value
80
+ val = my_get_config(config_name, default="default_value")
81
81
  # Now the user entered a value, so that value should be returned:
82
- assert val == 'user_value'
82
+ assert val == "user_value"
83
83
 
84
84
  # And now there's a config_name in the local_config_dir
85
85
  assert config_name in os.listdir(local_config_dir)
86
86
 
87
87
 
88
88
  def test_source_config_params():
89
- @source_config_params('a', 'b')
89
+ @source_config_params("a", "b")
90
90
  def foo(a, b, c):
91
91
  return a, b, c
92
92
 
93
- config = {'a': 1, 'b': 2, 'c': 3}
94
- _v = foo(a='a', b='b', c=3, _config_getter=config.get)
93
+ config = {"a": 1, "b": 2, "c": 3}
94
+ _v = foo(a="a", b="b", c=3, _config_getter=config.get)
95
95
  assert _v == (1, 2, 3)
96
96
 
97
- @source_config_params('a', 'b')
97
+ @source_config_params("a", "b")
98
98
  def bar(a, b, c, **kw):
99
- assert 'kw' not in kw, f'kw should be unpacked into **kw. Got: {kw=}'
99
+ assert "kw" not in kw, f"kw should be unpacked into **kw. Got: {kw=}"
100
100
  return a + b + c + sum(kw.values())
101
101
 
102
- _v = bar(a='a', b='b', c=3, d=4, e=5, _config_getter=config.get)
102
+ _v = bar(a="a", b="b", c=3, d=4, e=5, _config_getter=config.get)
103
103
  assert _v == 15
104
104
 
105
105
 
106
- if __name__ == '__main__':
107
- pytest.main(['-v', __file__])
106
+ if __name__ == "__main__":
107
+ pytest.main(["-v", __file__])
@@ -2,4 +2,4 @@ from functools import partial
2
2
 
3
3
 
4
4
  def user_input_patch(monkeypatch, user_input_string: str):
5
- monkeypatch.setattr('builtins.input', lambda _: user_input_string)
5
+ monkeypatch.setattr("builtins.input", lambda _: user_input_string)
config2py/tools.py CHANGED
@@ -8,6 +8,7 @@ import os
8
8
  from i2 import Sig
9
9
 
10
10
  from config2py.util import (
11
+ envvar,
11
12
  get_configs_folder_for_app,
12
13
  DFLT_CONFIG_FOLDER,
13
14
  is_repl,
@@ -44,7 +45,7 @@ def get_configs_local_store(
44
45
  # TODO: Not tested
45
46
  # TODO: Make this open-closed plug-in via routing argument
46
47
  _, extension = os.path.splitext(config_src)
47
- if extension in {'.ini', '.cfg'}:
48
+ if extension in {".ini", ".cfg"}:
48
49
  from config2py.s_configparser import ConfigStore
49
50
 
50
51
  return ConfigStore(config_src)
@@ -54,8 +55,8 @@ def get_configs_local_store(
54
55
  return TextFiles(path)
55
56
  else:
56
57
  raise ValueError(
57
- f'config_src must be a directory, ini or cfg file, or app name. '
58
- f'Was: {config_src}'
58
+ f"config_src must be a directory, ini or cfg file, or app name. "
59
+ f"Was: {config_src}"
59
60
  )
60
61
 
61
62
 
@@ -89,7 +90,7 @@ def simple_config_getter(
89
90
  central_configs = config_store_factory(configs_src)
90
91
  sources = []
91
92
  if first_look_in_env_vars:
92
- sources.append(os.environ)
93
+ sources.append(envvar)
93
94
  sources.append(central_configs)
94
95
  if ask_user_if_key_not_found is None:
95
96
  # if the user didn't ask for anythin explicit (True or False), then
@@ -126,13 +127,13 @@ configs = local_configs # TODO: backwards compatibility alias
126
127
 
127
128
  # --------------------------------------------------------------------
128
129
 
129
- export_line_p = re.compile('export .+')
130
+ export_line_p = re.compile("export .+")
130
131
  export_p = re.compile(r'(\w+)\s?\=\s?"(.+)"')
131
132
 
132
133
  _extract_name_and_value_from_export_line = Pipe(
133
- lambda x: x[len('export ') :],
134
+ lambda x: x[len("export ") :],
134
135
  lambda x: export_p.match(x),
135
- lambda x: x.groups() if x else '',
136
+ lambda x: x.groups() if x else "",
136
137
  )
137
138
 
138
139
 
@@ -155,7 +156,7 @@ def extract_exports(exports: str) -> dict:
155
156
  then this simple parser can be useful.
156
157
 
157
158
  """
158
- if '\n' not in exports and Path(resolve_path(exports)).is_file():
159
+ if "\n" not in exports and Path(resolve_path(exports)).is_file():
159
160
  exports = Path(resolve_path(exports)).read_text()
160
161
  return dict(
161
162
  filter(
@@ -211,7 +212,7 @@ def source_config_params(*config_params):
211
212
 
212
213
  sig = Sig(func)
213
214
 
214
- @sig.add_params(['_config_getter'])
215
+ @sig.add_params(["_config_getter"])
215
216
  def wrapped_func(*args, _config_getter, **kwargs):
216
217
  def source(k, v):
217
218
  if k == sig.var_keyword_name:
config2py/util.py CHANGED
@@ -3,6 +3,7 @@
3
3
  import re
4
4
  import os
5
5
  import ast
6
+ from collections import ChainMap
6
7
  from pathlib import Path
7
8
  from typing import Optional, Union, Any, Callable, Set, Iterable
8
9
  import getpass
@@ -14,11 +15,11 @@ from i2 import mk_sentinel # TODO: Only i2 dependency. Consider replacing.
14
15
  # def mk_sentinel(name): # TODO: Only i2 dependency. Here's replacement, but not picklable
15
16
  # return type(name, (), {'__repr__': lambda self: name})()
16
17
 
17
- DFLT_APP_NAME = 'config2py'
18
+ DFLT_APP_NAME = "config2py"
18
19
  DFLT_MASKING_INPUT = False
19
20
 
20
- not_found = mk_sentinel('not_found')
21
- no_default = mk_sentinel('no_default')
21
+ not_found = mk_sentinel("not_found")
22
+ no_default = mk_sentinel("no_default")
22
23
 
23
24
 
24
25
  def always_true(x: Any) -> bool:
@@ -36,10 +37,34 @@ def is_not_empty(x: Any) -> bool:
36
37
  return bool(x)
37
38
 
38
39
 
40
+ # Note: Why subclassing ChainMap works, but subclassing dict doesn't.
41
+ # The `EnvironmentVariables` class inherits from `collections.ChainMap` and wraps
42
+ # `os.environ`, providing a dynamic view of the environment variables without exposing
43
+ # sensitive data. Unlike a regular `dict` copy, which creates a static snapshot,
44
+ # `ChainMap` maintains a live reference to `os.environ`, so any changes to the
45
+ # environment—whether through `os.environ['KEY'] = 'value'` or external updates—are
46
+ # immediately reflected in `EnvironmentVariables`. Overriding `__repr__` ensures that
47
+ # printing the object (e.g., in a REPL or log) hides the actual contents,
48
+ # preserving confidentiality while retaining full read/write functionality.
49
+ class EnvironmentVariables(ChainMap):
50
+ """
51
+ Class to wrap environment variables without revealing sensitive information.
52
+ """
53
+
54
+ def __init__(self):
55
+ super().__init__(os.environ)
56
+
57
+ def __repr__(self):
58
+ return "EnvironmentVariables"
59
+
60
+
61
+ envvar = EnvironmentVariables()
62
+
63
+
39
64
  # TODO: Make this into an open-closed mini-framework
40
65
  def ask_user_for_input(
41
66
  prompt: str,
42
- default: str = '',
67
+ default: str = "",
43
68
  *,
44
69
  mask_input=DFLT_MASKING_INPUT,
45
70
  masking_toggle_str: str = None,
@@ -59,16 +84,16 @@ def ask_user_for_input(
59
84
  :return: The user's response (or the default value if the user entered nothing)
60
85
  """
61
86
  _original_prompt = prompt
62
- if prompt[-1] != ' ': # pragma: no cover
63
- prompt = prompt + ' '
87
+ if prompt[-1] != " ": # pragma: no cover
88
+ prompt = prompt + " "
64
89
  if masking_toggle_str is not None:
65
90
  prompt = (
66
- f'{prompt}\n'
91
+ f"{prompt}\n"
67
92
  f" (Input masking is {'ENABLED' if mask_input else 'DISABLED'}. "
68
93
  f"Enter '{masking_toggle_str}' (without quotes) to toggle input masking)\n"
69
94
  )
70
- if default not in {''}:
71
- prompt = prompt + f' [{default}]: '
95
+ if default not in {""}:
96
+ prompt = prompt + f" [{default}]: "
72
97
  if mask_input:
73
98
  _prompt_func = getpass.getpass
74
99
  else:
@@ -151,8 +176,8 @@ def extract_variable_declarations(
151
176
  expand = None
152
177
 
153
178
  env_vars = {}
154
- pattern = re.compile(r'^export\s+([A-Za-z0-9_]+)=(.*)$')
155
- lines = string.split('\n')
179
+ pattern = re.compile(r"^export\s+([A-Za-z0-9_]+)=(.*)$")
180
+ lines = string.split("\n")
156
181
  for line in lines:
157
182
  line = line.strip()
158
183
  match = pattern.match(line)
@@ -161,7 +186,7 @@ def extract_variable_declarations(
161
186
  value = match.group(2).strip('"')
162
187
  if expand is not None:
163
188
  for key, val in expand.items():
164
- value = value.replace(f'${key}', val)
189
+ value = value.replace(f"${key}", val)
165
190
  env_vars[name] = value
166
191
  expand = dict(expand, **env_vars)
167
192
  else:
@@ -171,17 +196,17 @@ def extract_variable_declarations(
171
196
 
172
197
  def _system_default_for_app_data_folder():
173
198
  """Get the system default for the app data folder."""
174
- if os.name == 'nt':
199
+ if os.name == "nt":
175
200
  # Windows
176
- app_data_folder = os.getenv('APPDATA')
201
+ app_data_folder = os.getenv("APPDATA")
177
202
  else:
178
203
  # macOS and Linux/Unix
179
- app_data_folder = os.path.expanduser('~/.config')
204
+ app_data_folder = os.path.expanduser("~/.config")
180
205
  return app_data_folder
181
206
 
182
207
 
183
208
  DFLT_APP_DATA_FOLDER = os.getenv(
184
- 'CONFIG2PY_APP_DATA_FOLDER', _system_default_for_app_data_folder()
209
+ "CONFIG2PY_APP_DATA_FOLDER", _system_default_for_app_data_folder()
185
210
  )
186
211
 
187
212
 
@@ -220,7 +245,7 @@ def create_directories(dirpath, max_dirs_to_make=None):
220
245
  >>> shutil.rmtree(temp_dir) # Cleanup
221
246
  """
222
247
  if max_dirs_to_make is not None and max_dirs_to_make < 0:
223
- raise ValueError('max_dirs_to_make must be non-negative or None')
248
+ raise ValueError("max_dirs_to_make must be non-negative or None")
224
249
 
225
250
  if os.path.exists(dirpath):
226
251
  return True
@@ -294,7 +319,7 @@ def _default_folder_setup(directory_path: str) -> None:
294
319
  # Add a hidden file to annotate the directory as one managed by config2py.
295
320
  # This helps distinguish it from directories created by other programs
296
321
  # (this can be useful to avoid conflicts).
297
- (Path(directory_path) / '.config2py').write_text('Created by config2py.')
322
+ (Path(directory_path) / ".config2py").write_text("Created by config2py.")
298
323
 
299
324
 
300
325
  def get_app_data_folder(
@@ -347,7 +372,7 @@ def get_app_data_folder(
347
372
  return app_data_path
348
373
 
349
374
 
350
- DFLT_CONFIGS_NAME = 'configs'
375
+ DFLT_CONFIGS_NAME = "configs"
351
376
 
352
377
 
353
378
  # TODO: is "get" the right word, since it makes the folder too?
@@ -387,11 +412,11 @@ import sys
387
412
 
388
413
 
389
414
  def _get_ipython_in_globals():
390
- return 'get_ipython' in globals()
415
+ return "get_ipython" in globals()
391
416
 
392
417
 
393
418
  def _main_does_not_have_file_attribute():
394
- return not hasattr(sys.modules['__main__'], '__file__')
419
+ return not hasattr(sys.modules["__main__"], "__file__")
395
420
 
396
421
 
397
422
  _repl_conditions = {_get_ipython_in_globals, _main_does_not_have_file_attribute}
@@ -1,16 +1,15 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: config2py
3
- Version: 0.1.36
3
+ Version: 0.1.38
4
4
  Summary: Simplified reading and writing configurations from various sources and formats
5
5
  Home-page: https://github.com/i2mint/config2py
6
- Author: OtoSense
7
6
  License: apache-2.0
8
7
  Platform: any
9
8
  Description-Content-Type: text/markdown
10
9
  License-File: LICENSE
11
10
  Requires-Dist: dol
12
11
  Requires-Dist: i2
13
- Requires-Dist: importlib-resources ; python_version < "3.9"
12
+ Requires-Dist: importlib-resources; python_version < "3.9"
14
13
 
15
14
  # config2py
16
15
 
@@ -0,0 +1,15 @@
1
+ config2py/__init__.py,sha256=GZ8lkm8TOKrV-zVd_Tc_8XVXDzFePECacjhOSGe2xcs,844
2
+ config2py/base.py,sha256=eQpRQjZYT-z6GhBemepaPUEyVVP8e_l04dghYeBBJdI,15880
3
+ config2py/errors.py,sha256=QdwGsoJhv6LHDHp-_yyz4oUg1Fgu4S-S7O2nuA0a5cw,203
4
+ config2py/s_configparser.py,sha256=-Sl2-J-QOLUiahwhCTiPsmjs4cKc79JuTbQ9gQcOiGY,15871
5
+ config2py/tools.py,sha256=goDuHHXKJzdFgmHzDnLBGMZEhp0kKU-aK47c1-MpJT8,9199
6
+ config2py/util.py,sha256=bchsJjaMnsfnplCJsg7i0Osyzuha3N-hExuc6gmS-7I,17188
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=sdiBNTavuzxW2AsqBRTO9U21iWig5DEyV38r6lmaZak,3728
10
+ config2py/tests/utils_for_testing.py,sha256=RcMiVtKK39rc8BsgIXQH3RCkd8qKo2o2MT7Rt0dJF2E,162
11
+ config2py-0.1.38.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
12
+ config2py-0.1.38.dist-info/METADATA,sha256=cj4bH2tshyDKLOGqttbOXrNWV7zB_AjNr-1-dEYtfVg,14541
13
+ config2py-0.1.38.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
14
+ config2py-0.1.38.dist-info/top_level.txt,sha256=DFnlOIKMIGWQRROr3voJFhWFViHaWgTTeWZjC5YC9QQ,10
15
+ config2py-0.1.38.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.43.0)
2
+ Generator: bdist_wheel (0.45.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,15 +0,0 @@
1
- config2py/__init__.py,sha256=ru-bUQk5EuOLzHmNAcElSN8P2saSUEb5KFRkfTkzsUo,770
2
- config2py/base.py,sha256=fDKClGpgNecWFL9a7Y_FpqwuRxAAD_XCuByW68Yq5KE,15880
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=W2YQm-PerKRN8prsxVfcBCChx75pZ9H8UqWH8pGxECE,9191
6
- config2py/util.py,sha256=xVxfOduKsv1BdNIFVVdPMmA0wxBYyipCFYpPD-r6A6Y,16136
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=km9RNh-gDtSJNjYiLQdkWcRi9IB5MwpijD3U2XCyi1Y,3728
10
- config2py/tests/utils_for_testing.py,sha256=Vz6EDY27uy_RZCSceZ7jqXkp_CXe52KAZSXcYKivazM,162
11
- config2py-0.1.36.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
12
- config2py-0.1.36.dist-info/METADATA,sha256=VGU8KFs1Xt3w8HL2cp-895nDZhbmXxgpTjBMhc_2lR0,14559
13
- config2py-0.1.36.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
14
- config2py-0.1.36.dist-info/top_level.txt,sha256=DFnlOIKMIGWQRROr3voJFhWFViHaWgTTeWZjC5YC9QQ,10
15
- config2py-0.1.36.dist-info/RECORD,,