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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: config2py
3
- Version: 0.1.38
3
+ Version: 0.1.40
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
@@ -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, Iterable
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
- # Note: First possible i2 dependency -- vendoring for now
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
- On Windows, this is typically %APPDATA%.
281
- On macOS, this is typically ~/.config.
282
- On Linux, this is typically ~/.config.
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
- See https://github.com/i2mint/i2mint/issues/1.
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
- >>> get_app_data_rootdir() # doctest: +SKIP
290
- '/Users/.../.config'
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
- >>> get_app_data_rootdir(ensure_exists=True) # doctest: +SKIP
295
- '/Users/.../.config'
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
- Note: The default app data folder is the system default for the current operating
298
- system. If you want to override this, you can do so by setting the
299
- CONFIG2PY_APP_DATA_FOLDER environment variable to the path of the folder you want
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
- return process_path(DFLT_APP_DATA_FOLDER, ensure_dir_exists=True)
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
- - app_name (str): Name of the app for which the data directory is needed.
336
- - setup_callback (Callable[[str], None]): A callback function to initialize the directory.
337
- Default is _default_folder_setup.
338
- - ensure_exists (bool): Whether to ensure the directory exists.
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
- - str: Path to the app data directory.
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 though.
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
- >>> path = get_app_data_folder('my_app', ensure_exists=True) # doctest: +SKIP
353
- >>> path # doctest: +SKIP
354
- '/Users/.../.config/my_app'
355
- >>> os.path.exists(path) # doctest: +SKIP
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
- You can also specify a path relative to the app data root directory
358
- (on linux/mac systems, this is typically ~/.config)
438
+ >>> get_app_data_folder('another/app/subfolder', folder_kind='data') # doctest: +SKIP
439
+ '/Users/.../.local/share/another/app/subfolder'
359
440
 
360
- >>> get_app_data_folder('another/app/and/subfolder') # doctest: +SKIP
361
- '/Users/.../.config/another/app/and/subfolder'
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
- get_app_data_rootdir(ensure_exists=ensure_exists), app_name
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)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: config2py
3
- Version: 0.1.38
3
+ Version: 0.1.40
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
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = config2py
3
- version = 0.1.38
3
+ version = 0.1.40
4
4
  url = https://github.com/i2mint/config2py
5
5
  platforms = any
6
6
  description_file = README.md
File without changes
File without changes
File without changes
File without changes