potato-util 0.1.0__tar.gz → 0.2.0__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 (40) hide show
  1. {potato_util-0.1.0/src/potato_util.egg-info → potato_util-0.2.0}/PKG-INFO +3 -1
  2. {potato_util-0.1.0 → potato_util-0.2.0}/requirements.txt +2 -0
  3. potato_util-0.2.0/src/potato_util/__version__.py +1 -0
  4. {potato_util-0.1.0 → potato_util-0.2.0}/src/potato_util/io/_async.py +208 -0
  5. {potato_util-0.1.0 → potato_util-0.2.0}/src/potato_util/io/_sync.py +201 -0
  6. {potato_util-0.1.0 → potato_util-0.2.0/src/potato_util.egg-info}/PKG-INFO +3 -1
  7. {potato_util-0.1.0 → potato_util-0.2.0}/src/potato_util.egg-info/requires.txt +4 -0
  8. potato_util-0.1.0/src/potato_util/__version__.py +0 -1
  9. {potato_util-0.1.0 → potato_util-0.2.0}/.python-version +0 -0
  10. {potato_util-0.1.0 → potato_util-0.2.0}/LICENSE.txt +0 -0
  11. {potato_util-0.1.0 → potato_util-0.2.0}/README.md +0 -0
  12. {potato_util-0.1.0 → potato_util-0.2.0}/pyproject.toml +0 -0
  13. {potato_util-0.1.0 → potato_util-0.2.0}/requirements/requirements.async.txt +0 -0
  14. {potato_util-0.1.0 → potato_util-0.2.0}/requirements/requirements.build.txt +0 -0
  15. {potato_util-0.1.0 → potato_util-0.2.0}/requirements/requirements.dev.txt +0 -0
  16. {potato_util-0.1.0 → potato_util-0.2.0}/requirements/requirements.docs.txt +0 -0
  17. {potato_util-0.1.0 → potato_util-0.2.0}/requirements/requirements.fastapi.txt +0 -0
  18. {potato_util-0.1.0 → potato_util-0.2.0}/requirements/requirements.test.txt +0 -0
  19. {potato_util-0.1.0 → potato_util-0.2.0}/setup.cfg +0 -0
  20. {potato_util-0.1.0 → potato_util-0.2.0}/setup.py +0 -0
  21. {potato_util-0.1.0 → potato_util-0.2.0}/src/potato_util/__init__.py +0 -0
  22. {potato_util-0.1.0 → potato_util-0.2.0}/src/potato_util/_base.py +0 -0
  23. {potato_util-0.1.0 → potato_util-0.2.0}/src/potato_util/constants/__init__.py +0 -0
  24. {potato_util-0.1.0 → potato_util-0.2.0}/src/potato_util/constants/_base.py +0 -0
  25. {potato_util-0.1.0 → potato_util-0.2.0}/src/potato_util/constants/_enum.py +0 -0
  26. {potato_util-0.1.0 → potato_util-0.2.0}/src/potato_util/constants/_regex.py +0 -0
  27. {potato_util-0.1.0 → potato_util-0.2.0}/src/potato_util/dt.py +0 -0
  28. {potato_util-0.1.0 → potato_util-0.2.0}/src/potato_util/generator.py +0 -0
  29. {potato_util-0.1.0 → potato_util-0.2.0}/src/potato_util/http/__init__.py +0 -0
  30. {potato_util-0.1.0 → potato_util-0.2.0}/src/potato_util/http/_async.py +0 -0
  31. {potato_util-0.1.0 → potato_util-0.2.0}/src/potato_util/http/_base.py +0 -0
  32. {potato_util-0.1.0 → potato_util-0.2.0}/src/potato_util/http/_sync.py +0 -0
  33. {potato_util-0.1.0 → potato_util-0.2.0}/src/potato_util/http/fastapi.py +0 -0
  34. {potato_util-0.1.0 → potato_util-0.2.0}/src/potato_util/io/__init__.py +0 -0
  35. {potato_util-0.1.0 → potato_util-0.2.0}/src/potato_util/sanitizer.py +0 -0
  36. {potato_util-0.1.0 → potato_util-0.2.0}/src/potato_util/secure.py +0 -0
  37. {potato_util-0.1.0 → potato_util-0.2.0}/src/potato_util/validator.py +0 -0
  38. {potato_util-0.1.0 → potato_util-0.2.0}/src/potato_util.egg-info/SOURCES.txt +0 -0
  39. {potato_util-0.1.0 → potato_util-0.2.0}/src/potato_util.egg-info/dependency_links.txt +0 -0
  40. {potato_util-0.1.0 → potato_util-0.2.0}/src/potato_util.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: potato_util
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: 'potato_util' is collection of simple useful utils package for python.
5
5
  Author-email: Batkhuu Byambajav <batkhuu10@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/bybatkhuu/module-python-utils
@@ -21,6 +21,8 @@ Classifier: Programming Language :: Python :: 3.13
21
21
  Requires-Python: <4.0,>=3.10
22
22
  Description-Content-Type: text/markdown
23
23
  License-File: LICENSE.txt
24
+ Requires-Dist: toml>=0.10.2; python_version < "3.11"
25
+ Requires-Dist: PyYAML<7.0.0,>=6.0.2
24
26
  Requires-Dist: python-dotenv<2.0.0,>=1.0.1
25
27
  Requires-Dist: pydantic[email,timezone]<3.0.0,>=2.0.3
26
28
  Requires-Dist: pydantic-settings<3.0.0,>=2.2.1
@@ -1,3 +1,5 @@
1
+ toml>=0.10.2; python_version < "3.11"
2
+ PyYAML>=6.0.2,<7.0.0
1
3
  python-dotenv>=1.0.1,<2.0.0
2
4
  pydantic[email,timezone]>=2.0.3,<3.0.0
3
5
  pydantic-settings>=2.2.1,<3.0.0
@@ -0,0 +1 @@
1
+ __version__ = "0.2.0"
@@ -1,7 +1,21 @@
1
+ import sys
2
+ import json
1
3
  import errno
2
4
  import hashlib
3
5
  import logging
6
+ import configparser
7
+ from pathlib import Path
8
+ from typing import Any
4
9
 
10
+ _binary_toml = False
11
+ if sys.version_info >= (3, 11):
12
+ import tomllib # type: ignore
13
+
14
+ _binary_toml = True
15
+ else:
16
+ import toml as tomllib # type: ignore
17
+
18
+ import yaml
5
19
  import aioshutil
6
20
  import aiofiles.os
7
21
  from pydantic import validate_call
@@ -276,6 +290,195 @@ async def async_get_file_checksum(
276
290
  return _file_checksum
277
291
 
278
292
 
293
+ @validate_call
294
+ async def async_read_yaml_file(file_path: str | Path) -> dict[str, Any]:
295
+ """Asynchronous read YAML file.
296
+
297
+ Args:
298
+ file_path (str | Path, required): YAML file path.
299
+
300
+ Raises:
301
+ FileNotFoundError: If YAML file is not found.
302
+ Exception : If failed to read YAML file.
303
+
304
+ Returns:
305
+ dict[str, Any]: YAML file data as dictionary.
306
+ """
307
+
308
+ _data: dict[str, Any] = {}
309
+
310
+ if isinstance(file_path, str):
311
+ file_path = Path(file_path)
312
+
313
+ if not await aiofiles.os.path.isfile(file_path):
314
+ raise FileNotFoundError(f"Not found '{file_path}' YAML file!")
315
+
316
+ try:
317
+ async with aiofiles.open(file_path, "r", encoding="utf-8") as _file:
318
+ _content = await _file.read()
319
+ _data = yaml.safe_load(_content) or {}
320
+ except Exception:
321
+ logger.error(f"Failed to read '{file_path}' YAML file!")
322
+ raise
323
+
324
+ return _data
325
+
326
+
327
+ @validate_call
328
+ async def async_read_json_file(file_path: str | Path) -> dict[str, Any]:
329
+ """Asynchronous read JSON file.
330
+
331
+ Args:
332
+ file_path (str | Path, required): JSON file path.
333
+
334
+ Raises:
335
+ FileNotFoundError: If JSON file is not found.
336
+ Exception : If failed to read JSON file.
337
+
338
+ Returns:
339
+ dict[str, Any]: JSON file data as dictionary.
340
+ """
341
+
342
+ _data: dict[str, Any] = {}
343
+
344
+ if isinstance(file_path, str):
345
+ file_path = Path(file_path)
346
+
347
+ if not await aiofiles.os.path.isfile(file_path):
348
+ raise FileNotFoundError(f"Not found '{file_path}' JSON file!")
349
+
350
+ try:
351
+ async with aiofiles.open(file_path, "r", encoding="utf-8") as _file:
352
+ _content = await _file.read()
353
+ _data = json.loads(_content) or {}
354
+ except Exception:
355
+ logger.error(f"Failed to read '{file_path}' JSON file!")
356
+ raise
357
+
358
+ return _data
359
+
360
+
361
+ @validate_call
362
+ async def async_read_toml_file(file_path: str | Path) -> dict[str, Any]:
363
+ """Asynchronous read TOML file.
364
+
365
+ Args:
366
+ file_path (str | Path, required): TOML file path.
367
+
368
+ Raises:
369
+ FileNotFoundError: If TOML file is not found.
370
+ Exception : If failed to read TOML file.
371
+
372
+ Returns:
373
+ dict[str, Any]: TOML file data as dictionary.
374
+ """
375
+
376
+ _data: dict[str, Any] = {}
377
+
378
+ if isinstance(file_path, str):
379
+ file_path = Path(file_path)
380
+
381
+ if not await aiofiles.os.path.isfile(file_path):
382
+ raise FileNotFoundError(f"Not found '{file_path}' TOML file!")
383
+
384
+ try:
385
+ _content: str = ""
386
+ if _binary_toml:
387
+ async with aiofiles.open(file_path, "rb") as _file:
388
+ _content = await _file.read() # type: ignore
389
+ _data = tomllib.load(_content) or {}
390
+ else:
391
+ async with aiofiles.open(file_path, "r", encoding="utf-8") as _file:
392
+ _content = await _file.read() # type: ignore
393
+ _data = tomllib.loads(_content) or {}
394
+
395
+ except Exception:
396
+ logger.error(f"Failed to read '{file_path}' TOML file!")
397
+ raise
398
+
399
+ return _data
400
+
401
+
402
+ @validate_call
403
+ async def async_read_ini_file(file_path: str | Path) -> dict[str, Any]:
404
+ """Asynchronous read INI config file.
405
+
406
+ Args:
407
+ file_path (str | Path, required): INI config file path.
408
+
409
+ Raises:
410
+ FileNotFoundError: If INI config file is not found.
411
+ Exception : If failed to read INI config file.
412
+
413
+ Returns:
414
+ dict[str, Any]: INI config file data as dictionary.
415
+ """
416
+
417
+ _config: dict[str, Any] = {}
418
+
419
+ if isinstance(file_path, str):
420
+ file_path = Path(file_path)
421
+
422
+ if not await aiofiles.os.path.isfile(file_path):
423
+ raise FileNotFoundError(f"Not found '{file_path}' INI config file!")
424
+
425
+ try:
426
+ _content: str = ""
427
+ async with aiofiles.open(file_path, "r", encoding="utf-8") as _file:
428
+ _content = await _file.read()
429
+
430
+ _config_parser = configparser.ConfigParser()
431
+ _config_parser.read_string(_content)
432
+ for _section in _config_parser.sections():
433
+ _config[_section] = dict(_config_parser.items(_section))
434
+
435
+ except Exception:
436
+ logger.error(f"Failed to read '{file_path}' INI config file!")
437
+ raise
438
+
439
+ return _config
440
+
441
+
442
+ @validate_call
443
+ async def async_read_config_file(config_path: str | Path) -> dict[str, Any]:
444
+ """Asynchronous read config file (YAML, JSON, TOML, INI).
445
+
446
+ Args:
447
+ config_path (str | Path, required): Config file path.
448
+
449
+ Raises:
450
+ FileNotFoundError: If config file is not found.
451
+ ValueError : If config file format is not supported.
452
+
453
+ Returns:
454
+ dict[str, Any]: Config file data as dictionary.
455
+ """
456
+
457
+ _config: dict[str, Any] = {}
458
+
459
+ if isinstance(config_path, str):
460
+ config_path = Path(config_path)
461
+
462
+ if not await aiofiles.os.path.isfile(config_path):
463
+ raise FileNotFoundError(f"Not found '{config_path}' config file!")
464
+
465
+ _suffix = config_path.suffix.lower()
466
+ if _suffix in (".yaml", ".yml"):
467
+ _config = await async_read_yaml_file(config_path)
468
+ elif _suffix == ".json":
469
+ _config = await async_read_json_file(config_path)
470
+ elif _suffix == ".toml":
471
+ _config = await async_read_toml_file(config_path)
472
+ elif _suffix in (".ini", ".cfg"):
473
+ _config = await async_read_ini_file(config_path)
474
+ else:
475
+ raise ValueError(
476
+ f"Unsupported config file format '{_suffix}' for '{config_path}'!"
477
+ )
478
+
479
+ return _config
480
+
481
+
279
482
  __all__ = [
280
483
  "async_create_dir",
281
484
  "async_remove_dir",
@@ -283,4 +486,9 @@ __all__ = [
283
486
  "async_remove_file",
284
487
  "async_remove_files",
285
488
  "async_get_file_checksum",
489
+ "async_read_yaml_file",
490
+ "async_read_json_file",
491
+ "async_read_toml_file",
492
+ "async_read_ini_file",
493
+ "async_read_config_file",
286
494
  ]
@@ -1,11 +1,28 @@
1
+ # noqa: E402
2
+
1
3
  import os
4
+ import sys
5
+ import json
2
6
  import errno
3
7
  import shutil
4
8
  import hashlib
5
9
  import logging
10
+ import configparser
11
+ from pathlib import Path
12
+ from typing import Any
13
+
14
+ _binary_toml = False
15
+ if sys.version_info >= (3, 11):
16
+ import tomllib # type: ignore
17
+
18
+ _binary_toml = True
19
+ else:
20
+ import toml as tomllib # type: ignore
6
21
 
22
+ import yaml
7
23
  from pydantic import validate_call
8
24
 
25
+
9
26
  from ..constants import WarnEnum, HashAlgoEnum, MAX_PATH_LENGTH
10
27
 
11
28
 
@@ -270,6 +287,185 @@ def get_file_checksum(
270
287
  return _file_checksum
271
288
 
272
289
 
290
+ @validate_call
291
+ def read_yaml_file(file_path: str | Path) -> dict[str, Any]:
292
+ """Read YAML file.
293
+
294
+ Args:
295
+ file_path (str | Path, required): YAML file path.
296
+
297
+ Raises:
298
+ FileNotFoundError: If YAML file is not found.
299
+ Exception : If failed to read YAML file.
300
+
301
+ Returns:
302
+ dict[str, Any]: YAML file data as dictionary.
303
+ """
304
+
305
+ _data: dict[str, Any] = {}
306
+
307
+ if isinstance(file_path, str):
308
+ file_path = Path(file_path)
309
+
310
+ if not os.path.isfile(file_path):
311
+ raise FileNotFoundError(f"Not found '{file_path}' YAML file!")
312
+
313
+ try:
314
+ with open(file_path, encoding="utf-8") as _file:
315
+ _data = yaml.safe_load(_file) or {}
316
+ except Exception:
317
+ logger.error(f"Failed to read '{file_path}' YAML file!")
318
+ raise
319
+
320
+ return _data
321
+
322
+
323
+ @validate_call
324
+ def read_json_file(file_path: str | Path) -> dict[str, Any]:
325
+ """Read JSON file.
326
+
327
+ Args:
328
+ file_path (str | Path, required): JSON file path.
329
+
330
+ Raises:
331
+ FileNotFoundError: If JSON file is not found.
332
+ Exception : If failed to read JSON file.
333
+
334
+ Returns:
335
+ dict[str, Any]: JSON file data as dictionary.
336
+ """
337
+
338
+ _data: dict[str, Any] = {}
339
+
340
+ if isinstance(file_path, str):
341
+ file_path = Path(file_path)
342
+
343
+ if not os.path.isfile(file_path):
344
+ raise FileNotFoundError(f"Not found '{file_path}' JSON file!")
345
+
346
+ try:
347
+ with open(file_path, encoding="utf-8") as _file:
348
+ _data = json.load(_file) or {}
349
+ except Exception:
350
+ logger.error(f"Failed to read '{file_path}' JSON file!")
351
+ raise
352
+
353
+ return _data
354
+
355
+
356
+ @validate_call
357
+ def read_toml_file(file_path: str | Path) -> dict[str, Any]:
358
+ """Read TOML file.
359
+
360
+ Args:
361
+ file_path (str | Path, required): TOML file path.
362
+
363
+ Raises:
364
+ FileNotFoundError: If TOML file is not found.
365
+ Exception : If failed to read TOML file.
366
+
367
+ Returns:
368
+ dict[str, Any]: TOML file data as dictionary.
369
+ """
370
+
371
+ _data: dict[str, Any] = {}
372
+
373
+ if isinstance(file_path, str):
374
+ file_path = Path(file_path)
375
+
376
+ if not os.path.isfile(file_path):
377
+ raise FileNotFoundError(f"Not found '{file_path}' TOML file!")
378
+
379
+ try:
380
+ if _binary_toml:
381
+ with open(file_path, "rb") as _file:
382
+ _data = tomllib.load(_file) or {} # type: ignore
383
+ else:
384
+ with open(file_path, encoding="utf-8") as _file:
385
+ _data = tomllib.load(_file) or {} # type: ignore
386
+ except Exception:
387
+ logger.error(f"Failed to read '{file_path}' TOML file!")
388
+ raise
389
+
390
+ return _data
391
+
392
+
393
+ @validate_call
394
+ def read_ini_file(file_path: str | Path) -> dict[str, Any]:
395
+ """Read INI config file.
396
+
397
+ Args:
398
+ file_path (str | Path, required): INI config file path.
399
+
400
+ Raises:
401
+ FileNotFoundError: If INI config file is not found.
402
+ Exception : If failed to read INI config file.
403
+
404
+ Returns:
405
+ dict[str, Any]: INI config file data as dictionary.
406
+ """
407
+
408
+ _config: dict[str, Any] = {}
409
+
410
+ if isinstance(file_path, str):
411
+ file_path = Path(file_path)
412
+
413
+ if not os.path.isfile(file_path):
414
+ raise FileNotFoundError(f"Not found '{file_path}' INI config file!")
415
+
416
+ try:
417
+ _config_parser = configparser.ConfigParser()
418
+ _config_parser.read(file_path)
419
+ for _section in _config_parser.sections():
420
+ _config[_section] = dict(_config_parser.items(_section))
421
+
422
+ except Exception:
423
+ logger.error(f"Failed to read '{file_path}' INI config file!")
424
+ raise
425
+
426
+ return _config
427
+
428
+
429
+ @validate_call
430
+ def read_config_file(config_path: str | Path) -> dict[str, Any]:
431
+ """Read config file (YAML, JSON, TOML, INI).
432
+
433
+ Args:
434
+ config_path (str | Path, required): Config file path.
435
+
436
+ Raises:
437
+ FileNotFoundError: If config file is not found.
438
+ ValueError : If config file format is not supported.
439
+
440
+ Returns:
441
+ dict[str, Any]: Config file data as dictionary.
442
+ """
443
+
444
+ _config: dict[str, Any] = {}
445
+
446
+ if isinstance(config_path, str):
447
+ config_path = Path(config_path)
448
+
449
+ if not os.path.isfile(config_path):
450
+ raise FileNotFoundError(f"Not found '{config_path}' config file!")
451
+
452
+ _suffix = config_path.suffix.lower()
453
+ if _suffix in (".yaml", ".yml"):
454
+ _config = read_yaml_file(file_path=config_path)
455
+ elif _suffix == ".json":
456
+ _config = read_json_file(file_path=config_path)
457
+ elif _suffix == ".toml":
458
+ _config = read_toml_file(file_path=config_path)
459
+ elif _suffix in (".ini", ".cfg"):
460
+ _config = read_ini_file(file_path=config_path)
461
+ else:
462
+ raise ValueError(
463
+ f"Unsupported config file format '{config_path.suffix}' for '{config_path}'!"
464
+ )
465
+
466
+ return _config
467
+
468
+
273
469
  __all__ = [
274
470
  "create_dir",
275
471
  "remove_dir",
@@ -277,4 +473,9 @@ __all__ = [
277
473
  "remove_file",
278
474
  "remove_files",
279
475
  "get_file_checksum",
476
+ "read_yaml_file",
477
+ "read_json_file",
478
+ "read_toml_file",
479
+ "read_ini_file",
480
+ "read_config_file",
280
481
  ]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: potato_util
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: 'potato_util' is collection of simple useful utils package for python.
5
5
  Author-email: Batkhuu Byambajav <batkhuu10@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/bybatkhuu/module-python-utils
@@ -21,6 +21,8 @@ Classifier: Programming Language :: Python :: 3.13
21
21
  Requires-Python: <4.0,>=3.10
22
22
  Description-Content-Type: text/markdown
23
23
  License-File: LICENSE.txt
24
+ Requires-Dist: toml>=0.10.2; python_version < "3.11"
25
+ Requires-Dist: PyYAML<7.0.0,>=6.0.2
24
26
  Requires-Dist: python-dotenv<2.0.0,>=1.0.1
25
27
  Requires-Dist: pydantic[email,timezone]<3.0.0,>=2.0.3
26
28
  Requires-Dist: pydantic-settings<3.0.0,>=2.2.1
@@ -1,7 +1,11 @@
1
+ PyYAML<7.0.0,>=6.0.2
1
2
  python-dotenv<2.0.0,>=1.0.1
2
3
  pydantic[email,timezone]<3.0.0,>=2.0.3
3
4
  pydantic-settings<3.0.0,>=2.2.1
4
5
 
6
+ [:python_version < "3.11"]
7
+ toml>=0.10.2
8
+
5
9
  [all]
6
10
  aiofiles<26.0.0,>=24.1.0
7
11
  aioshutil<2.0.0,>=1.5
@@ -1 +0,0 @@
1
- __version__ = "0.1.0"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes