config2py 0.1.39__tar.gz → 0.1.41__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.39 → config2py-0.1.41}/PKG-INFO +2 -2
- {config2py-0.1.39 → config2py-0.1.41}/README.md +1 -1
- {config2py-0.1.39 → config2py-0.1.41}/config2py/__init__.py +1 -1
- {config2py-0.1.39 → config2py-0.1.41}/config2py/util.py +143 -68
- {config2py-0.1.39 → config2py-0.1.41}/config2py.egg-info/PKG-INFO +2 -2
- {config2py-0.1.39 → config2py-0.1.41}/setup.cfg +1 -1
- {config2py-0.1.39 → config2py-0.1.41}/LICENSE +0 -0
- {config2py-0.1.39 → config2py-0.1.41}/config2py/base.py +0 -0
- {config2py-0.1.39 → config2py-0.1.41}/config2py/errors.py +0 -0
- {config2py-0.1.39 → config2py-0.1.41}/config2py/s_configparser.py +0 -0
- {config2py-0.1.39 → config2py-0.1.41}/config2py/scrap/__init__.py +0 -0
- {config2py-0.1.39 → config2py-0.1.41}/config2py/tests/__init__.py +0 -0
- {config2py-0.1.39 → config2py-0.1.41}/config2py/tests/test_tools.py +0 -0
- {config2py-0.1.39 → config2py-0.1.41}/config2py/tests/utils_for_testing.py +0 -0
- {config2py-0.1.39 → config2py-0.1.41}/config2py/tools.py +0 -0
- {config2py-0.1.39 → config2py-0.1.41}/config2py.egg-info/SOURCES.txt +0 -0
- {config2py-0.1.39 → config2py-0.1.41}/config2py.egg-info/dependency_links.txt +0 -0
- {config2py-0.1.39 → config2py-0.1.41}/config2py.egg-info/not-zip-safe +0 -0
- {config2py-0.1.39 → config2py-0.1.41}/config2py.egg-info/requires.txt +0 -0
- {config2py-0.1.39 → config2py-0.1.41}/config2py.egg-info/top_level.txt +0 -0
- {config2py-0.1.39 → config2py-0.1.41}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: config2py
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.41
|
|
4
4
|
Summary: Simplified reading and writing configurations from various sources and formats
|
|
5
5
|
Home-page: https://github.com/i2mint/config2py
|
|
6
6
|
License: apache-2.0
|
|
@@ -212,7 +212,7 @@ user again.
|
|
|
212
212
|
* `get_config`: Get a config value from a list of sources. See more below.
|
|
213
213
|
* `user_gettable`: Create a ``GettableContainer`` that asks the user for a value, optionally saving it.
|
|
214
214
|
* `ask_user_for_input`: Ask the user for input, optionally masking, validating and transforming the input.
|
|
215
|
-
* `
|
|
215
|
+
* `get_app_config_folder`: Returns the full path of a directory suitable for storing application-specific data for a given app name.
|
|
216
216
|
* `get_configs_local_store`: Get a local store (mapping interface of local files) of configs for a given app or package name
|
|
217
217
|
* `configs`: A default store instance for configs, defaulting to a local store under a default configuration local directory.
|
|
218
218
|
|
|
@@ -202,7 +202,7 @@ user again.
|
|
|
202
202
|
* `get_config`: Get a config value from a list of sources. See more below.
|
|
203
203
|
* `user_gettable`: Create a ``GettableContainer`` that asks the user for a value, optionally saving it.
|
|
204
204
|
* `ask_user_for_input`: Ask the user for input, optionally masking, validating and transforming the input.
|
|
205
|
-
* `
|
|
205
|
+
* `get_app_config_folder`: Returns the full path of a directory suitable for storing application-specific data for a given app name.
|
|
206
206
|
* `get_configs_local_store`: Get a local store (mapping interface of local files) of configs for a given app or package name
|
|
207
207
|
* `configs`: A default store instance for configs, defaulting to a local store under a default configuration local directory.
|
|
208
208
|
|
|
@@ -15,7 +15,7 @@ from config2py.base import get_config, user_gettable, sources_chainmap
|
|
|
15
15
|
from config2py.util import (
|
|
16
16
|
envvar, # os.environ, but with dict display override to hide secrets
|
|
17
17
|
ask_user_for_input,
|
|
18
|
-
|
|
18
|
+
get_app_config_folder,
|
|
19
19
|
get_configs_folder_for_app,
|
|
20
20
|
is_repl,
|
|
21
21
|
parse_assignments_from_py_source,
|
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
import re
|
|
4
4
|
import os
|
|
5
5
|
import ast
|
|
6
|
-
from collections import ChainMap
|
|
6
|
+
from collections import ChainMap, namedtuple
|
|
7
7
|
from pathlib import Path
|
|
8
|
-
from typing import Optional, Union, Any, Callable, Set, Literal,
|
|
8
|
+
from typing import Optional, Union, Any, Callable, Set, Literal, get_args
|
|
9
|
+
from types import SimpleNamespace
|
|
9
10
|
import getpass
|
|
10
11
|
|
|
11
12
|
from dol import process_path
|
|
@@ -18,8 +19,6 @@ from i2 import mk_sentinel # TODO: Only i2 dependency. Consider replacing.
|
|
|
18
19
|
DFLT_APP_NAME = "config2py"
|
|
19
20
|
DFLT_MASKING_INPUT = False
|
|
20
21
|
|
|
21
|
-
AppFolderKind = Literal["data", "config", "cache"]
|
|
22
|
-
|
|
23
22
|
not_found = mk_sentinel("not_found")
|
|
24
23
|
no_default = mk_sentinel("no_default")
|
|
25
24
|
|
|
@@ -196,22 +195,6 @@ def extract_variable_declarations(
|
|
|
196
195
|
return env_vars
|
|
197
196
|
|
|
198
197
|
|
|
199
|
-
def _system_default_for_app_data_folder():
|
|
200
|
-
"""Get the system default for the app data folder."""
|
|
201
|
-
if os.name == "nt":
|
|
202
|
-
# Windows
|
|
203
|
-
app_data_folder = os.getenv("APPDATA")
|
|
204
|
-
else:
|
|
205
|
-
# macOS and Linux/Unix
|
|
206
|
-
app_data_folder = os.path.expanduser("~/.config")
|
|
207
|
-
return app_data_folder
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
DFLT_APP_DATA_FOLDER = os.getenv(
|
|
211
|
-
"CONFIG2PY_APP_DATA_FOLDER", _system_default_for_app_data_folder()
|
|
212
|
-
)
|
|
213
|
-
|
|
214
|
-
|
|
215
198
|
def create_directories(dirpath, max_dirs_to_make=None):
|
|
216
199
|
"""
|
|
217
200
|
Create directories up to a specified limit.
|
|
@@ -274,36 +257,119 @@ def create_directories(dirpath, max_dirs_to_make=None):
|
|
|
274
257
|
return True
|
|
275
258
|
|
|
276
259
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
260
|
+
FolderSpec = namedtuple("FolderSpec", ["env_var", "default_path"])
|
|
261
|
+
|
|
262
|
+
if os.name == "nt":
|
|
263
|
+
APP_FOLDER_STANDARDS = dict(
|
|
264
|
+
config=FolderSpec("APPDATA", os.getenv("APPDATA", "")),
|
|
265
|
+
data=FolderSpec("LOCALAPPDATA", os.getenv("LOCALAPPDATA", "")),
|
|
266
|
+
cache=FolderSpec(
|
|
267
|
+
"LOCALAPPDATA", os.path.join(os.getenv("LOCALAPPDATA", ""), "Temp")
|
|
268
|
+
),
|
|
269
|
+
state=FolderSpec("LOCALAPPDATA", os.getenv("LOCALAPPDATA", "")),
|
|
270
|
+
runtime=FolderSpec("TEMP", os.getenv("TEMP", "")),
|
|
271
|
+
)
|
|
272
|
+
else:
|
|
273
|
+
APP_FOLDER_STANDARDS = dict(
|
|
274
|
+
config=FolderSpec("XDG_CONFIG_HOME", "~/.config"),
|
|
275
|
+
data=FolderSpec("XDG_DATA_HOME", "~/.local/share"),
|
|
276
|
+
cache=FolderSpec("XDG_CACHE_HOME", "~/.cache"),
|
|
277
|
+
state=FolderSpec("XDG_STATE_HOME", "~/.local/state"),
|
|
278
|
+
runtime=FolderSpec("XDG_RUNTIME_DIR", "/tmp"),
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
AppFolderKind = Literal["config", "data", "cache", "state", "runtime"]
|
|
283
|
+
|
|
284
|
+
# Verify AppFolderKind matches _APP_FOLDER_STANDARDS_DICT keys
|
|
285
|
+
# Note: This is due to the fact that static type checkers can't verify
|
|
286
|
+
# that the keys of _APP_FOLDER_STANDARDS_DICT match the Literal values.
|
|
287
|
+
# This breaks SSOT, but here we at least validate alignment at runtime.
|
|
288
|
+
_literal_kinds = get_args(AppFolderKind)
|
|
289
|
+
assert set(_literal_kinds) == set(APP_FOLDER_STANDARDS.keys()), (
|
|
290
|
+
f"AppFolderKind Literal {_literal_kinds} doesn't match "
|
|
291
|
+
f"APP_FOLDER_STANDARDS keys {tuple(APP_FOLDER_STANDARDS.keys())}"
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
config2py_env_var = SimpleNamespace(
|
|
295
|
+
**{k: f"CONFIG2PY_{k.upper()}_DIR" for k in APP_FOLDER_STANDARDS}
|
|
296
|
+
)
|
|
281
297
|
|
|
282
|
-
On Windows, this is typically %APPDATA%.
|
|
283
|
-
On macOS, this is typically ~/.config.
|
|
284
|
-
On Linux, this is typically ~/.config.
|
|
285
298
|
|
|
286
|
-
|
|
287
|
-
|
|
299
|
+
DFLT_APP_FOLDER_KIND: AppFolderKind = "config" # type: ignore (for <3.11)
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def system_default_for_app_data_folder(
|
|
303
|
+
folder_kind: AppFolderKind = DFLT_APP_FOLDER_KIND, # type: ignore (for <3.11)
|
|
304
|
+
) -> str:
|
|
305
|
+
"""Get the system default for the app data folder."""
|
|
306
|
+
# Platform-specific specs: (env_var, default_path)
|
|
288
307
|
|
|
289
|
-
|
|
308
|
+
# Same logic for both platforms: check env var, then use default
|
|
309
|
+
env_var, default = APP_FOLDER_STANDARDS[folder_kind]
|
|
310
|
+
return os.path.expanduser(os.getenv(env_var, default))
|
|
290
311
|
|
|
291
|
-
>>> get_app_rootdir() # doctest: +SKIP
|
|
292
|
-
'/Users/.../.config'
|
|
293
312
|
|
|
294
|
-
|
|
295
|
-
|
|
313
|
+
DFLT_CONFIG_FOLDER = system_default_for_app_data_folder("config")
|
|
314
|
+
DFLT_DATA_FOLDER = system_default_for_app_data_folder("data")
|
|
315
|
+
DFLT_CACHE_FOLDER = system_default_for_app_data_folder("cache")
|
|
316
|
+
DFLT_STATE_FOLDER = system_default_for_app_data_folder("state")
|
|
317
|
+
DFLT_RUNTIME_FOLDER = system_default_for_app_data_folder("runtime")
|
|
296
318
|
|
|
297
|
-
>>> get_app_rootdir(ensure_exists=True) # doctest: +SKIP
|
|
298
|
-
'/Users/.../.config'
|
|
299
319
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
320
|
+
def get_app_rootdir(
|
|
321
|
+
folder_kind: AppFolderKind = DFLT_APP_FOLDER_KIND, # type: ignore (for <3.11)
|
|
322
|
+
*,
|
|
323
|
+
ensure_exists: bool = True,
|
|
324
|
+
) -> str:
|
|
325
|
+
"""
|
|
326
|
+
Returns the root directory for a specific folder kind.
|
|
327
|
+
|
|
328
|
+
The folder kind determines which standard directory is returned:
|
|
329
|
+
- 'config': Configuration files (XDG_CONFIG_HOME, default ~/.config)
|
|
330
|
+
- 'data': Application data (XDG_DATA_HOME, default ~/.local/share)
|
|
331
|
+
- 'cache': Temporary/cache files (XDG_CACHE_HOME, default ~/.cache)
|
|
332
|
+
- 'state': State data/logs (XDG_STATE_HOME, default ~/.local/state)
|
|
333
|
+
- 'runtime': Runtime files (XDG_RUNTIME_DIR, default /tmp)
|
|
334
|
+
|
|
335
|
+
On Windows:
|
|
336
|
+
- 'config': %APPDATA%
|
|
337
|
+
- 'data': %LOCALAPPDATA%
|
|
338
|
+
- 'cache': %LOCALAPPDATA%\\Temp
|
|
339
|
+
- 'state': %LOCALAPPDATA%
|
|
340
|
+
- 'runtime': %TEMP%
|
|
341
|
+
|
|
342
|
+
Args:
|
|
343
|
+
folder_kind: Type of folder ('config', 'data', 'cache', 'state', or 'runtime')
|
|
344
|
+
ensure_exists: Whether to create the directory if it doesn't exist
|
|
304
345
|
|
|
346
|
+
Returns:
|
|
347
|
+
str: The full path of the app root folder for the specified kind.
|
|
348
|
+
|
|
349
|
+
Note: The default root folder follows XDG Base Directory standards on Unix/Linux/macOS.
|
|
350
|
+
You can override this by setting environment variables:
|
|
351
|
+
- CONFIG2PY_CONFIG_FOLDER, CONFIG2PY_DATA_FOLDER, CONFIG2PY_CACHE_FOLDER, etc.
|
|
352
|
+
(highest priority, overrides everything)
|
|
353
|
+
- XDG_CONFIG_HOME, XDG_DATA_HOME, XDG_CACHE_HOME, etc.
|
|
354
|
+
(standard XDG override)
|
|
355
|
+
- If neither is set, uses platform defaults
|
|
356
|
+
|
|
357
|
+
Examples:
|
|
358
|
+
>>> get_app_rootdir('config') # doctest: +SKIP
|
|
359
|
+
'/Users/.../.config'
|
|
360
|
+
>>> get_app_rootdir('data') # doctest: +SKIP
|
|
361
|
+
'/Users/.../.local/share'
|
|
362
|
+
>>> get_app_rootdir('cache') # doctest: +SKIP
|
|
363
|
+
'/Users/.../.cache'
|
|
305
364
|
"""
|
|
306
|
-
|
|
365
|
+
folderpath = os.getenv(
|
|
366
|
+
getattr(config2py_env_var, folder_kind), # Check config2py custom env var first
|
|
367
|
+
system_default_for_app_data_folder(
|
|
368
|
+
folder_kind
|
|
369
|
+
), # ... if not, use system default
|
|
370
|
+
)
|
|
371
|
+
|
|
372
|
+
return process_path(folderpath, ensure_dir_exists=ensure_exists)
|
|
307
373
|
|
|
308
374
|
|
|
309
375
|
# renaming get_app_data_rootdir to get_app_rootdir
|
|
@@ -329,10 +395,7 @@ def _default_folder_setup(directory_path: str) -> None:
|
|
|
329
395
|
(Path(directory_path) / ".config2py").write_text("Created by config2py.")
|
|
330
396
|
|
|
331
397
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
def get_app_data_folder(
|
|
398
|
+
def get_app_config_folder(
|
|
336
399
|
app_name: str = DFLT_APP_NAME,
|
|
337
400
|
*,
|
|
338
401
|
setup_callback: Callable[[str], None] = _default_folder_setup,
|
|
@@ -340,41 +403,53 @@ def get_app_data_folder(
|
|
|
340
403
|
folder_kind: AppFolderKind = DFLT_APP_FOLDER_KIND,
|
|
341
404
|
) -> str:
|
|
342
405
|
"""
|
|
343
|
-
Retrieve or create the app data directory specific to the given app name.
|
|
406
|
+
Retrieve or create the app data directory specific to the given app name and folder kind.
|
|
407
|
+
|
|
408
|
+
The folder kind determines where the app's files are stored:
|
|
409
|
+
- 'config': For configuration/settings files
|
|
410
|
+
- 'data': For essential user data, databases, sessions
|
|
411
|
+
- 'cache': For temporary/regeneratable data
|
|
344
412
|
|
|
345
413
|
Args:
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
414
|
+
app_name: Name of the app for which the data directory is needed.
|
|
415
|
+
setup_callback: A callback function to initialize the directory.
|
|
416
|
+
Default is _default_folder_setup.
|
|
417
|
+
ensure_exists: Whether to ensure the directory exists.
|
|
418
|
+
folder_kind: Type of folder ('config', 'data', or 'cache').
|
|
419
|
+
Default is 'config' for backward compatibility.
|
|
350
420
|
|
|
351
421
|
Returns:
|
|
352
|
-
|
|
422
|
+
str: Path to the app data directory.
|
|
353
423
|
|
|
354
|
-
By default, the app will be "config2py":
|
|
424
|
+
By default, the app will be "config2py" and folder_kind will be "config":
|
|
355
425
|
|
|
356
|
-
>>>
|
|
426
|
+
>>> get_app_config_folder() # doctest: +ELLIPSIS
|
|
357
427
|
'.../.config/config2py'
|
|
358
428
|
|
|
359
|
-
You can specify a different app name
|
|
360
|
-
And if you want, you can also specify a callback function to initialize the
|
|
361
|
-
directory.
|
|
429
|
+
You can specify a different app name and folder kind:
|
|
362
430
|
|
|
363
|
-
>>>
|
|
364
|
-
|
|
365
|
-
'
|
|
366
|
-
|
|
431
|
+
>>> get_app_config_folder('my_app', folder_kind='data') # doctest: +SKIP
|
|
432
|
+
'/Users/.../.local/share/my_app'
|
|
433
|
+
>>> get_app_config_folder('my_app', folder_kind='cache') # doctest: +SKIP
|
|
434
|
+
'/Users/.../.cache/my_app'
|
|
435
|
+
|
|
436
|
+
You can also specify a path relative to the app root directory:
|
|
367
437
|
|
|
368
|
-
|
|
369
|
-
|
|
438
|
+
>>> get_app_config_folder('another/app/subfolder', folder_kind='data') # doctest: +SKIP
|
|
439
|
+
'/Users/.../.local/share/another/app/subfolder'
|
|
370
440
|
|
|
371
|
-
|
|
372
|
-
|
|
441
|
+
If ensure_exists is True, the directory will be created and initialized
|
|
442
|
+
with the setup_callback:
|
|
373
443
|
|
|
444
|
+
>>> path = get_app_config_folder('my_app', ensure_exists=True) # doctest: +SKIP
|
|
445
|
+
>>> os.path.exists(path) # doctest: +SKIP
|
|
446
|
+
True
|
|
374
447
|
"""
|
|
375
|
-
app_data_path = os.path.join(
|
|
448
|
+
app_data_path = os.path.join(
|
|
449
|
+
get_app_rootdir(folder_kind, ensure_exists=ensure_exists), app_name
|
|
450
|
+
)
|
|
376
451
|
app_data_folder_did_not_exist = not os.path.isdir(app_data_path)
|
|
377
|
-
process_path(app_data_path, ensure_dir_exists=True)
|
|
452
|
+
# process_path(app_data_path, ensure_dir_exists=True)
|
|
378
453
|
|
|
379
454
|
if app_data_folder_did_not_exist:
|
|
380
455
|
setup_callback(app_data_path)
|
|
@@ -403,13 +478,13 @@ def get_configs_folder_for_app(
|
|
|
403
478
|
- config_dir_setup_callback (Callable[[str], None]): A callback function to initialize the configs directory.
|
|
404
479
|
Default is _default_folder_setup.
|
|
405
480
|
"""
|
|
406
|
-
app_dir =
|
|
481
|
+
app_dir = get_app_config_folder(app_name, setup_callback=app_dir_setup_callback)
|
|
407
482
|
configs_dir = os.path.join(app_dir, configs_name)
|
|
408
483
|
config_dir_setup_callback(configs_dir)
|
|
409
484
|
return configs_dir
|
|
410
485
|
|
|
411
486
|
|
|
412
|
-
get_app_data_directory =
|
|
487
|
+
get_app_data_directory = get_app_config_folder # backwards compatibility alias
|
|
413
488
|
get_configs_directory_for_app = (
|
|
414
489
|
get_configs_folder_for_app # backwards compatibility alias
|
|
415
490
|
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: config2py
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.41
|
|
4
4
|
Summary: Simplified reading and writing configurations from various sources and formats
|
|
5
5
|
Home-page: https://github.com/i2mint/config2py
|
|
6
6
|
License: apache-2.0
|
|
@@ -212,7 +212,7 @@ user again.
|
|
|
212
212
|
* `get_config`: Get a config value from a list of sources. See more below.
|
|
213
213
|
* `user_gettable`: Create a ``GettableContainer`` that asks the user for a value, optionally saving it.
|
|
214
214
|
* `ask_user_for_input`: Ask the user for input, optionally masking, validating and transforming the input.
|
|
215
|
-
* `
|
|
215
|
+
* `get_app_config_folder`: Returns the full path of a directory suitable for storing application-specific data for a given app name.
|
|
216
216
|
* `get_configs_local_store`: Get a local store (mapping interface of local files) of configs for a given app or package name
|
|
217
217
|
* `configs`: A default store instance for configs, defaulting to a local store under a default configuration local directory.
|
|
218
218
|
|
|
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
|