config2py 0.1.38__tar.gz → 0.1.40__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.38 → config2py-0.1.40}/PKG-INFO +1 -1
- {config2py-0.1.38 → config2py-0.1.40}/config2py/util.py +142 -58
- {config2py-0.1.38 → config2py-0.1.40}/config2py.egg-info/PKG-INFO +1 -1
- {config2py-0.1.38 → config2py-0.1.40}/setup.cfg +1 -1
- {config2py-0.1.38 → config2py-0.1.40}/LICENSE +0 -0
- {config2py-0.1.38 → config2py-0.1.40}/README.md +0 -0
- {config2py-0.1.38 → config2py-0.1.40}/config2py/__init__.py +0 -0
- {config2py-0.1.38 → config2py-0.1.40}/config2py/base.py +0 -0
- {config2py-0.1.38 → config2py-0.1.40}/config2py/errors.py +0 -0
- {config2py-0.1.38 → config2py-0.1.40}/config2py/s_configparser.py +0 -0
- {config2py-0.1.38 → config2py-0.1.40}/config2py/scrap/__init__.py +0 -0
- {config2py-0.1.38 → config2py-0.1.40}/config2py/tests/__init__.py +0 -0
- {config2py-0.1.38 → config2py-0.1.40}/config2py/tests/test_tools.py +0 -0
- {config2py-0.1.38 → config2py-0.1.40}/config2py/tests/utils_for_testing.py +0 -0
- {config2py-0.1.38 → config2py-0.1.40}/config2py/tools.py +0 -0
- {config2py-0.1.38 → config2py-0.1.40}/config2py.egg-info/SOURCES.txt +0 -0
- {config2py-0.1.38 → config2py-0.1.40}/config2py.egg-info/dependency_links.txt +0 -0
- {config2py-0.1.38 → config2py-0.1.40}/config2py.egg-info/not-zip-safe +0 -0
- {config2py-0.1.38 → config2py-0.1.40}/config2py.egg-info/requires.txt +0 -0
- {config2py-0.1.38 → config2py-0.1.40}/config2py.egg-info/top_level.txt +0 -0
- {config2py-0.1.38 → config2py-0.1.40}/setup.py +0 -0
|
@@ -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,
|
|
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
|
|
@@ -194,22 +195,6 @@ def extract_variable_declarations(
|
|
|
194
195
|
return env_vars
|
|
195
196
|
|
|
196
197
|
|
|
197
|
-
def _system_default_for_app_data_folder():
|
|
198
|
-
"""Get the system default for the app data folder."""
|
|
199
|
-
if os.name == "nt":
|
|
200
|
-
# Windows
|
|
201
|
-
app_data_folder = os.getenv("APPDATA")
|
|
202
|
-
else:
|
|
203
|
-
# macOS and Linux/Unix
|
|
204
|
-
app_data_folder = os.path.expanduser("~/.config")
|
|
205
|
-
return app_data_folder
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
DFLT_APP_DATA_FOLDER = os.getenv(
|
|
209
|
-
"CONFIG2PY_APP_DATA_FOLDER", _system_default_for_app_data_folder()
|
|
210
|
-
)
|
|
211
|
-
|
|
212
|
-
|
|
213
198
|
def create_directories(dirpath, max_dirs_to_make=None):
|
|
214
199
|
"""
|
|
215
200
|
Create directories up to a specified limit.
|
|
@@ -272,35 +257,123 @@ def create_directories(dirpath, max_dirs_to_make=None):
|
|
|
272
257
|
return True
|
|
273
258
|
|
|
274
259
|
|
|
275
|
-
|
|
276
|
-
def get_app_data_rootdir(*, ensure_exists=False) -> str:
|
|
277
|
-
"""
|
|
278
|
-
Returns the full path of a directory suitable for storing application-specific data.
|
|
260
|
+
FolderSpec = namedtuple("FolderSpec", ["env_var", "default_path"])
|
|
279
261
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
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
|
+
)
|
|
283
280
|
|
|
284
|
-
Returns:
|
|
285
|
-
str: The full path of the app data folder.
|
|
286
281
|
|
|
287
|
-
|
|
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
|
+
)
|
|
297
|
+
|
|
298
|
+
|
|
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)
|
|
307
|
+
|
|
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))
|
|
311
|
+
|
|
288
312
|
|
|
289
|
-
|
|
290
|
-
|
|
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")
|
|
291
318
|
|
|
292
|
-
If ``ensure_exists`` is ``True``, the folder will be created if it doesn't exist.
|
|
293
319
|
|
|
294
|
-
|
|
295
|
-
|
|
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%
|
|
296
341
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
to use.
|
|
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
|
|
301
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'
|
|
302
364
|
"""
|
|
303
|
-
|
|
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)
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
# renaming get_app_data_rootdir to get_app_rootdir
|
|
376
|
+
_legacy_app_data_rootdir = get_app_rootdir # backwards compatibility alias
|
|
304
377
|
|
|
305
378
|
|
|
306
379
|
def _default_folder_setup(directory_path: str) -> None:
|
|
@@ -327,45 +400,56 @@ def get_app_data_folder(
|
|
|
327
400
|
*,
|
|
328
401
|
setup_callback: Callable[[str], None] = _default_folder_setup,
|
|
329
402
|
ensure_exists: bool = False,
|
|
403
|
+
folder_kind: AppFolderKind = DFLT_APP_FOLDER_KIND,
|
|
330
404
|
) -> str:
|
|
331
405
|
"""
|
|
332
|
-
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
|
|
333
412
|
|
|
334
413
|
Args:
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
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.
|
|
339
420
|
|
|
340
421
|
Returns:
|
|
341
|
-
|
|
422
|
+
str: Path to the app data directory.
|
|
342
423
|
|
|
343
|
-
By default, the app will be "config2py":
|
|
424
|
+
By default, the app will be "config2py" and folder_kind will be "config":
|
|
344
425
|
|
|
345
426
|
>>> get_app_data_folder() # doctest: +ELLIPSIS
|
|
346
427
|
'.../.config/config2py'
|
|
347
428
|
|
|
348
|
-
You can specify a different app name
|
|
349
|
-
And if you want, you can also specify a callback function to initialize the
|
|
350
|
-
directory.
|
|
429
|
+
You can specify a different app name and folder kind:
|
|
351
430
|
|
|
352
|
-
>>>
|
|
353
|
-
|
|
354
|
-
'
|
|
355
|
-
|
|
431
|
+
>>> get_app_data_folder('my_app', folder_kind='data') # doctest: +SKIP
|
|
432
|
+
'/Users/.../.local/share/my_app'
|
|
433
|
+
>>> get_app_data_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:
|
|
356
437
|
|
|
357
|
-
|
|
358
|
-
|
|
438
|
+
>>> get_app_data_folder('another/app/subfolder', folder_kind='data') # doctest: +SKIP
|
|
439
|
+
'/Users/.../.local/share/another/app/subfolder'
|
|
359
440
|
|
|
360
|
-
|
|
361
|
-
|
|
441
|
+
If ensure_exists is True, the directory will be created and initialized
|
|
442
|
+
with the setup_callback:
|
|
362
443
|
|
|
444
|
+
>>> path = get_app_data_folder('my_app', ensure_exists=True) # doctest: +SKIP
|
|
445
|
+
>>> os.path.exists(path) # doctest: +SKIP
|
|
446
|
+
True
|
|
363
447
|
"""
|
|
364
448
|
app_data_path = os.path.join(
|
|
365
|
-
|
|
449
|
+
get_app_rootdir(folder_kind, ensure_exists=ensure_exists), app_name
|
|
366
450
|
)
|
|
367
451
|
app_data_folder_did_not_exist = not os.path.isdir(app_data_path)
|
|
368
|
-
process_path(app_data_path, ensure_dir_exists=True)
|
|
452
|
+
# process_path(app_data_path, ensure_dir_exists=True)
|
|
369
453
|
|
|
370
454
|
if app_data_folder_did_not_exist:
|
|
371
455
|
setup_callback(app_data_path)
|
|
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
|
|
File without changes
|