config2py 0.1.43__tar.gz → 0.1.44__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.
Files changed (23) hide show
  1. {config2py-0.1.43 → config2py-0.1.44}/PKG-INFO +1 -1
  2. {config2py-0.1.43 → config2py-0.1.44}/config2py/sync_store.py +61 -7
  3. {config2py-0.1.43 → config2py-0.1.44}/config2py.egg-info/PKG-INFO +1 -1
  4. {config2py-0.1.43 → config2py-0.1.44}/setup.cfg +1 -1
  5. {config2py-0.1.43 → config2py-0.1.44}/LICENSE +0 -0
  6. {config2py-0.1.43 → config2py-0.1.44}/README.md +0 -0
  7. {config2py-0.1.43 → config2py-0.1.44}/config2py/__init__.py +0 -0
  8. {config2py-0.1.43 → config2py-0.1.44}/config2py/base.py +0 -0
  9. {config2py-0.1.43 → config2py-0.1.44}/config2py/errors.py +0 -0
  10. {config2py-0.1.43 → config2py-0.1.44}/config2py/s_configparser.py +0 -0
  11. {config2py-0.1.43 → config2py-0.1.44}/config2py/scrap/__init__.py +0 -0
  12. {config2py-0.1.43 → config2py-0.1.44}/config2py/tests/__init__.py +0 -0
  13. {config2py-0.1.43 → config2py-0.1.44}/config2py/tests/test_sync_store.py +0 -0
  14. {config2py-0.1.43 → config2py-0.1.44}/config2py/tests/test_tools.py +0 -0
  15. {config2py-0.1.43 → config2py-0.1.44}/config2py/tests/utils_for_testing.py +0 -0
  16. {config2py-0.1.43 → config2py-0.1.44}/config2py/tools.py +0 -0
  17. {config2py-0.1.43 → config2py-0.1.44}/config2py/util.py +0 -0
  18. {config2py-0.1.43 → config2py-0.1.44}/config2py.egg-info/SOURCES.txt +0 -0
  19. {config2py-0.1.43 → config2py-0.1.44}/config2py.egg-info/dependency_links.txt +0 -0
  20. {config2py-0.1.43 → config2py-0.1.44}/config2py.egg-info/not-zip-safe +0 -0
  21. {config2py-0.1.43 → config2py-0.1.44}/config2py.egg-info/requires.txt +0 -0
  22. {config2py-0.1.43 → config2py-0.1.44}/config2py.egg-info/top_level.txt +0 -0
  23. {config2py-0.1.43 → config2py-0.1.44}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: config2py
3
- Version: 0.1.43
3
+ Version: 0.1.44
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
@@ -266,21 +266,38 @@ class FileStore(SyncStore):
266
266
  dumper: Optional custom dumper (auto-detected from extension if not provided)
267
267
  mode: File read mode ('r' for text, 'rb' for binary)
268
268
  dump_kwargs: Additional kwargs for dumper
269
+ create_file_content: Optional factory callable that returns initial dict content
270
+ for missing files. If None, FileNotFoundError is raised for missing files.
271
+ create_key_path_content: Optional factory callable that returns initial content
272
+ for missing key_path. If None, KeyError is raised for missing key paths.
269
273
 
270
274
  Example:
271
275
  >>> import tempfile
276
+ >>> import os
277
+ >>>
278
+ >>> # Basic usage with existing file
272
279
  >>> with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
273
280
  ... _ = f.write('{"section": {"key": "value"}}')
274
281
  ... temp_file = f.name
275
282
  >>>
276
- >>> # Work with nested section
277
283
  >>> section = FileStore(temp_file, key_path='section')
278
284
  >>> section['key']
279
285
  'value'
280
286
  >>> section['new'] = 'data'
281
- >>>
282
- >>> import os
283
287
  >>> os.unlink(temp_file)
288
+ >>>
289
+ >>> # Auto-create missing file and key_path
290
+ >>> with tempfile.TemporaryDirectory() as tmpdir:
291
+ ... new_file = os.path.join(tmpdir, 'config.json')
292
+ ... store = FileStore(
293
+ ... new_file,
294
+ ... key_path='servers',
295
+ ... create_file_content=lambda: {},
296
+ ... create_key_path_content=lambda: {}
297
+ ... )
298
+ ... store['myserver'] = {'command': 'python'}
299
+ ... 'myserver' in store
300
+ True
284
301
  """
285
302
 
286
303
  def __init__(
@@ -292,11 +309,15 @@ class FileStore(SyncStore):
292
309
  dumper: Optional[Callable[[dict], str]] = None,
293
310
  mode: str = "r",
294
311
  dump_kwargs: Optional[dict] = None,
312
+ create_file_content: Optional[Callable[[], dict]] = None,
313
+ create_key_path_content: Optional[Callable[[], Any]] = None,
295
314
  ):
296
315
  self.filepath = Path(filepath).expanduser()
297
316
  self.key_path = _normalize_key_path(key_path)
298
317
  self.mode = mode
299
318
  self.dump_kwargs = dump_kwargs or {}
319
+ self.create_file_content = create_file_content
320
+ self.create_key_path_content = create_key_path_content
300
321
 
301
322
  # Auto-detect format if not provided
302
323
  if loader is None or dumper is None:
@@ -318,10 +339,43 @@ class FileStore(SyncStore):
318
339
 
319
340
  def _load_from_file(self) -> dict:
320
341
  """Read and parse file, returning the section specified by key_path."""
321
- with open(self.filepath, self.mode) as f:
322
- content = f.read()
323
- data = self._file_loader(content)
324
- return _get_nested(data, self.key_path)
342
+ # Handle missing file
343
+ if not self.filepath.exists():
344
+ if self.create_file_content is None:
345
+ raise FileNotFoundError(f"File not found: {self.filepath}")
346
+
347
+ # Create file with initial content
348
+ initial_data = self.create_file_content()
349
+ self.filepath.parent.mkdir(parents=True, exist_ok=True)
350
+ content = self._file_dumper(initial_data, **self.dump_kwargs)
351
+ write_mode = "w" if "b" not in self.mode else "wb"
352
+ with open(self.filepath, write_mode) as f:
353
+ f.write(content)
354
+ data = initial_data
355
+ else:
356
+ # Load existing file
357
+ with open(self.filepath, self.mode) as f:
358
+ content = f.read()
359
+ data = self._file_loader(content)
360
+
361
+ # Handle missing key_path
362
+ try:
363
+ return _get_nested(data, self.key_path)
364
+ except (KeyError, TypeError):
365
+ if self.create_key_path_content is None:
366
+ raise KeyError(f"Key path not found: {self.key_path}")
367
+
368
+ # Create key_path with initial content
369
+ initial_content = self.create_key_path_content()
370
+ full_data = _set_nested(data, self.key_path, initial_content)
371
+
372
+ # Write back to file
373
+ content = self._file_dumper(full_data, **self.dump_kwargs)
374
+ write_mode = "w" if "b" not in self.mode else "wb"
375
+ with open(self.filepath, write_mode) as f:
376
+ f.write(content)
377
+
378
+ return initial_content
325
379
 
326
380
  def _dump_to_file(self, section_data: dict) -> None:
327
381
  """Write data to file, updating only the section specified by key_path."""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: config2py
3
- Version: 0.1.43
3
+ Version: 0.1.44
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.43
3
+ version = 0.1.44
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
File without changes