absfuyu 5.11.0__tar.gz → 5.12.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.

Potentially problematic release.


This version of absfuyu might be problematic. Click here for more details.

Files changed (130) hide show
  1. {absfuyu-5.11.0 → absfuyu-5.12.0}/PKG-INFO +1 -1
  2. {absfuyu-5.11.0 → absfuyu-5.12.0}/pyproject.toml +14 -15
  3. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/__init__.py +1 -1
  4. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/__main__.py +2 -2
  5. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/cli/__init__.py +2 -2
  6. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/cli/color.py +2 -2
  7. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/cli/config_group.py +2 -2
  8. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/cli/do_group.py +2 -2
  9. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/cli/game_group.py +2 -2
  10. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/cli/tool_group.py +2 -2
  11. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/config/__init__.py +2 -2
  12. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/core/__init__.py +2 -2
  13. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/core/baseclass.py +2 -2
  14. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/core/baseclass2.py +2 -2
  15. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/core/decorator.py +2 -2
  16. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/core/docstring.py +2 -2
  17. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/core/dummy_cli.py +3 -3
  18. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/core/dummy_func.py +2 -2
  19. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/dxt/__init__.py +2 -2
  20. absfuyu-5.12.0/src/absfuyu/dxt/dictext.py +387 -0
  21. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/dxt/dxt_support.py +2 -2
  22. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/dxt/intext.py +2 -2
  23. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/dxt/listext.py +320 -7
  24. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/dxt/strext.py +2 -2
  25. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/extra/__init__.py +2 -2
  26. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/extra/beautiful.py +2 -2
  27. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/extra/da/__init__.py +2 -2
  28. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/extra/da/dadf.py +73 -3
  29. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/extra/da/dadf_base.py +2 -2
  30. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/extra/da/df_func.py +2 -2
  31. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/extra/da/mplt.py +2 -2
  32. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/extra/data_analysis.py +2 -2
  33. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/extra/pdf.py +2 -2
  34. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/extra/rclone.py +2 -2
  35. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/extra/xml.py +2 -2
  36. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/fun/__init__.py +2 -2
  37. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/fun/rubik.py +2 -2
  38. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/fun/tarot.py +2 -2
  39. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/game/__init__.py +2 -2
  40. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/game/game_stat.py +2 -2
  41. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/game/schulte.py +2 -2
  42. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/game/sudoku.py +2 -2
  43. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/game/tictactoe.py +2 -2
  44. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/game/wordle.py +2 -2
  45. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/general/__init__.py +2 -2
  46. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/general/content.py +2 -2
  47. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/general/human.py +2 -2
  48. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/general/shape.py +2 -2
  49. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/general/tax.py +22 -2
  50. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/logger.py +2 -2
  51. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/pkg_data/__init__.py +2 -2
  52. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/pkg_data/deprecated.py +2 -2
  53. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/pkg_data/logo.py +2 -2
  54. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/sort.py +2 -2
  55. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/tools/__init__.py +2 -2
  56. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/tools/checksum.py +2 -2
  57. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/tools/converter.py +2 -2
  58. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/tools/generator.py +2 -2
  59. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/tools/inspector.py +2 -2
  60. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/tools/keygen.py +2 -2
  61. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/tools/obfuscator.py +2 -2
  62. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/tools/passwordlib.py +2 -2
  63. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/tools/shutdownizer.py +2 -2
  64. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/tools/sw.py +2 -2
  65. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/tools/web.py +2 -2
  66. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/typings.py +5 -2
  67. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/util/__init__.py +2 -2
  68. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/util/api.py +2 -2
  69. absfuyu-5.12.0/src/absfuyu/util/cli.py +118 -0
  70. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/util/gui.py +2 -2
  71. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/util/json_method.py +2 -2
  72. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/util/lunar.py +2 -2
  73. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/util/path.py +2 -2
  74. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/util/performance.py +2 -2
  75. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/util/shorten_number.py +2 -2
  76. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/util/text_table.py +2 -2
  77. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/util/zipped.py +2 -2
  78. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/version.py +2 -2
  79. {absfuyu-5.11.0 → absfuyu-5.12.0}/tests/conftest.py +2 -2
  80. {absfuyu-5.11.0 → absfuyu-5.12.0}/tests/test_api.py +2 -2
  81. {absfuyu-5.11.0 → absfuyu-5.12.0}/tests/test_beautiful.py +2 -2
  82. {absfuyu-5.11.0 → absfuyu-5.12.0}/tests/test_config.py +2 -2
  83. {absfuyu-5.11.0 → absfuyu-5.12.0}/tests/test_converter.py +2 -2
  84. {absfuyu-5.11.0 → absfuyu-5.12.0}/tests/test_core.py +2 -2
  85. {absfuyu-5.11.0 → absfuyu-5.12.0}/tests/test_data_analysis.py +2 -2
  86. {absfuyu-5.11.0 → absfuyu-5.12.0}/tests/test_dictext.py +2 -2
  87. {absfuyu-5.11.0 → absfuyu-5.12.0}/tests/test_everything.py +2 -2
  88. {absfuyu-5.11.0 → absfuyu-5.12.0}/tests/test_extra.py +2 -2
  89. {absfuyu-5.11.0 → absfuyu-5.12.0}/tests/test_fun.py +2 -2
  90. {absfuyu-5.11.0 → absfuyu-5.12.0}/tests/test_game.py +2 -2
  91. {absfuyu-5.11.0 → absfuyu-5.12.0}/tests/test_generator.py +2 -2
  92. {absfuyu-5.11.0 → absfuyu-5.12.0}/tests/test_inspector.py +2 -2
  93. {absfuyu-5.11.0 → absfuyu-5.12.0}/tests/test_intext.py +2 -2
  94. {absfuyu-5.11.0 → absfuyu-5.12.0}/tests/test_listext.py +2 -2
  95. {absfuyu-5.11.0 → absfuyu-5.12.0}/tests/test_logger.py +2 -2
  96. {absfuyu-5.11.0 → absfuyu-5.12.0}/tests/test_obfuscator.py +2 -2
  97. {absfuyu-5.11.0 → absfuyu-5.12.0}/tests/test_passwordlib.py +2 -2
  98. {absfuyu-5.11.0 → absfuyu-5.12.0}/tests/test_performance.py +2 -2
  99. {absfuyu-5.11.0 → absfuyu-5.12.0}/tests/test_pkg_data.py +2 -2
  100. {absfuyu-5.11.0 → absfuyu-5.12.0}/tests/test_shape.py +2 -2
  101. {absfuyu-5.11.0 → absfuyu-5.12.0}/tests/test_shorten_number.py +2 -2
  102. {absfuyu-5.11.0 → absfuyu-5.12.0}/tests/test_strext.py +2 -2
  103. {absfuyu-5.11.0 → absfuyu-5.12.0}/tests/test_text_table.py +2 -2
  104. {absfuyu-5.11.0 → absfuyu-5.12.0}/tests/test_tools.py +2 -2
  105. {absfuyu-5.11.0 → absfuyu-5.12.0}/tests/test_util.py +2 -2
  106. {absfuyu-5.11.0 → absfuyu-5.12.0}/tests/test_version.py +2 -2
  107. absfuyu-5.11.0/src/absfuyu/dxt/dictext.py +0 -207
  108. {absfuyu-5.11.0 → absfuyu-5.12.0}/.gitignore +0 -0
  109. {absfuyu-5.11.0 → absfuyu-5.12.0}/LICENSE +0 -0
  110. {absfuyu-5.11.0 → absfuyu-5.12.0}/README.md +0 -0
  111. {absfuyu-5.11.0 → absfuyu-5.12.0}/dev_requirements.txt +0 -0
  112. {absfuyu-5.11.0 → absfuyu-5.12.0}/docs/Makefile +0 -0
  113. {absfuyu-5.11.0 → absfuyu-5.12.0}/docs/_template/versioning.html +0 -0
  114. {absfuyu-5.11.0 → absfuyu-5.12.0}/docs/conf.py +0 -0
  115. {absfuyu-5.11.0 → absfuyu-5.12.0}/docs/index.rst +0 -0
  116. {absfuyu-5.11.0 → absfuyu-5.12.0}/docs/info.md +0 -0
  117. {absfuyu-5.11.0 → absfuyu-5.12.0}/docs/make.bat +0 -0
  118. {absfuyu-5.11.0 → absfuyu-5.12.0}/docs/modules.rst +0 -0
  119. {absfuyu-5.11.0 → absfuyu-5.12.0}/images/Logo Full.png +0 -0
  120. {absfuyu-5.11.0 → absfuyu-5.12.0}/images/Logo Square.png +0 -0
  121. {absfuyu-5.11.0 → absfuyu-5.12.0}/images/Logo White.png +0 -0
  122. {absfuyu-5.11.0 → absfuyu-5.12.0}/images/repository-image-crop.png +0 -0
  123. {absfuyu-5.11.0 → absfuyu-5.12.0}/images/repository-image-white.png +0 -0
  124. {absfuyu-5.11.0 → absfuyu-5.12.0}/images/repository-image.png +0 -0
  125. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/config/config.json +0 -0
  126. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/pkg_data/chemistry.pkl +0 -0
  127. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/pkg_data/passwordlib_lzma.pkl +0 -0
  128. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/pkg_data/tarot.pkl +0 -0
  129. {absfuyu-5.11.0 → absfuyu-5.12.0}/src/absfuyu/py.typed +0 -0
  130. {absfuyu-5.11.0 → absfuyu-5.12.0}/tests/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: absfuyu
3
- Version: 5.11.0
3
+ Version: 5.12.0
4
4
  Summary: A small collection of code
5
5
  Project-URL: Homepage, https://github.com/AbsoluteWinter/absfuyu-public
6
6
  Project-URL: Documentation, https://absolutewinter.github.io/absfuyu-docs/
@@ -57,26 +57,25 @@ Repository = "https://github.com/AbsoluteWinter/absfuyu-public"
57
57
  Issues = "https://github.com/AbsoluteWinter/absfuyu-public/issues"
58
58
 
59
59
  [project.optional-dependencies]
60
- full = [
61
- "numpy",
62
- "pandas",
63
- "rich",
64
- "tqdm",
65
- "unidecode",
66
- "openpyxl",
67
- "xlsxwriter",
68
- "spire-pdf",
69
- "xmltodict",
70
- "rclone-crypt",
71
- # "jinja2"
72
- ]
73
60
  docs = ["numpy", "pandas", "rich", "spire-pdf"]
61
+ dadf = ["numpy", "pandas", "openpyxl", "xlsxwriter"]
74
62
  beautiful = ["rich"]
75
- extra = ["numpy", "pandas", "openpyxl", "xlsxwriter", "spire-pdf", "xmltodict"]
76
63
  pdf = ["spire-pdf"]
77
- dadf = ["numpy", "pandas", "openpyxl", "xlsxwriter"]
78
64
  xml = ["xmltodict"]
79
65
  rclone = ["rclone-crypt"]
66
+ extra = [
67
+ "absfuyu[dadf]", # numpy, pandas, openpyxl, xlsxwriter
68
+ "absfuyu[pdf]", # spire-pdf
69
+ "absfuyu[xml]", # xmltodict
70
+ ]
71
+ full = [
72
+ "absfuyu[beautiful]", # rich
73
+ "absfuyu[rclone]", # rclone-crypt
74
+ "absfuyu[extra]",
75
+ "tqdm",
76
+ "unidecode",
77
+ # "jinja2"
78
+ ]
80
79
 
81
80
 
82
81
  [project.scripts]
@@ -22,7 +22,7 @@ Using in cmd:
22
22
  __title__ = "absfuyu"
23
23
  __author__ = "AbsoluteWinter"
24
24
  __license__ = "MIT License"
25
- __version__ = "5.11.0"
25
+ __version__ = "5.12.0"
26
26
  __all__ = [
27
27
  "core",
28
28
  "config",
@@ -3,8 +3,8 @@ ABSFUYU
3
3
  -------
4
4
  COMMAND LINE INTERFACE
5
5
 
6
- Version: 5.11.0
7
- Date updated: 30/09/2025 (dd/mm/yyyy)
6
+ Version: 5.12.0
7
+ Date updated: 17/10/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Library
@@ -3,8 +3,8 @@ ABSFUYU
3
3
  -------
4
4
  COMMAND LINE INTERFACE
5
5
 
6
- Version: 5.11.0
7
- Date updated: 30/09/2025 (dd/mm/yyyy)
6
+ Version: 5.12.0
7
+ Date updated: 17/10/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  __all__ = ["cli"]
@@ -3,8 +3,8 @@ ABSFUYU CLI
3
3
  -----------
4
4
  Color
5
5
 
6
- Version: 5.11.0
7
- Date updated: 30/09/2025 (dd/mm/yyyy)
6
+ Version: 5.12.0
7
+ Date updated: 17/10/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module Package
@@ -3,8 +3,8 @@ ABSFUYU CLI
3
3
  -----------
4
4
  Config
5
5
 
6
- Version: 5.11.0
7
- Date updated: 30/09/2025 (dd/mm/yyyy)
6
+ Version: 5.12.0
7
+ Date updated: 17/10/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module Package
@@ -3,8 +3,8 @@ ABSFUYU CLI
3
3
  -----------
4
4
  Do
5
5
 
6
- Version: 5.11.0
7
- Date updated: 30/09/2025 (dd/mm/yyyy)
6
+ Version: 5.12.0
7
+ Date updated: 17/10/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module Package
@@ -3,8 +3,8 @@ ABSFUYU CLI
3
3
  -----------
4
4
  Game
5
5
 
6
- Version: 5.11.0
7
- Date updated: 30/09/2025 (dd/mm/yyyy)
6
+ Version: 5.12.0
7
+ Date updated: 17/10/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module Package
@@ -3,8 +3,8 @@ ABSFUYU CLI
3
3
  -----------
4
4
  Tool
5
5
 
6
- Version: 5.11.0
7
- Date updated: 30/09/2025 (dd/mm/yyyy)
6
+ Version: 5.12.0
7
+ Date updated: 17/10/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module Package
@@ -3,8 +3,8 @@ Absfuyu: Configuration
3
3
  ----------------------
4
4
  Package configuration module
5
5
 
6
- Version: 5.11.0
7
- Date updated: 30/09/2025 (dd/mm/yyyy)
6
+ Version: 5.12.0
7
+ Date updated: 17/10/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module level
@@ -3,8 +3,8 @@ Absfuyu: Core
3
3
  -------------
4
4
  Bases for other features
5
5
 
6
- Version: 5.11.0
7
- Date updated: 30/09/2025 (dd/mm/yyyy)
6
+ Version: 5.12.0
7
+ Date updated: 17/10/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module Package
@@ -3,8 +3,8 @@ Absfuyu: Core
3
3
  -------------
4
4
  Bases for other features
5
5
 
6
- Version: 5.11.0
7
- Date updated: 05/10/2025 (dd/mm/yyyy)
6
+ Version: 5.12.0
7
+ Date updated: 17/10/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module Package
@@ -3,8 +3,8 @@ Absfuyu: Core
3
3
  -------------
4
4
  Bases for other features (with library)
5
5
 
6
- Version: 5.11.0
7
- Date updated: 30/09/2025 (dd/mm/yyyy)
6
+ Version: 5.12.0
7
+ Date updated: 17/10/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module Package
@@ -3,8 +3,8 @@ Absfuyu: Core
3
3
  -------------
4
4
  Decorator
5
5
 
6
- Version: 5.11.0
7
- Date updated: 30/09/2025 (dd/mm/yyyy)
6
+ Version: 5.12.0
7
+ Date updated: 17/10/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module Package
@@ -3,8 +3,8 @@ Absfuyu: Core
3
3
  -------------
4
4
  Sphinx docstring decorator
5
5
 
6
- Version: 5.11.0
7
- Date updated: 30/09/2025 (dd/mm/yyyy)
6
+ Version: 5.12.0
7
+ Date updated: 17/10/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module Package
@@ -3,8 +3,8 @@ Absfuyu: Core
3
3
  -------------
4
4
  Dummy cli
5
5
 
6
- Version: 5.11.0
7
- Date updated: 30/09/2025 (dd/mm/yyyy)
6
+ Version: 5.12.0
7
+ Date updated: 17/10/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module Package
@@ -57,7 +57,7 @@ def get_parser(
57
57
 
58
58
 
59
59
  def cli() -> None:
60
- desc = "This is a dummy cli, please install <click> and <colorama> package to use this feature"
60
+ desc = "This is a dummy cli, install <click> and <colorama> package to use this feature"
61
61
  arg_parser = get_parser(
62
62
  name=__title__,
63
63
  description=desc,
@@ -3,8 +3,8 @@ Absfuyu: Core
3
3
  -------------
4
4
  Dummy functions when other libraries are unvailable
5
5
 
6
- Version: 5.11.0
7
- Date updated: 30/09/2025 (dd/mm/yyyy)
6
+ Version: 5.12.0
7
+ Date updated: 17/10/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module Package
@@ -3,8 +3,8 @@ Absfuyu: Data Extension
3
3
  -----------------------
4
4
  Extension for data type such as ``list``, ``str``, ``dict``, ...
5
5
 
6
- Version: 5.11.0
7
- Date updated: 30/09/2025 (dd/mm/yyyy)
6
+ Version: 5.12.0
7
+ Date updated: 17/10/2025 (dd/mm/yyyy)
8
8
 
9
9
  Features:
10
10
  ---------
@@ -0,0 +1,387 @@
1
+ """
2
+ Absfuyu: Data Extension
3
+ -----------------------
4
+ dict extension
5
+
6
+ Version: 5.12.0
7
+ Date updated: 17/10/2025 (dd/mm/yyyy)
8
+ """
9
+
10
+ # Module Package
11
+ # ---------------------------------------------------------------------------
12
+ __all__ = ["DictExt", "DictAnalyzeResult"]
13
+
14
+
15
+ # Library
16
+ # ---------------------------------------------------------------------------
17
+ import operator
18
+ from collections.abc import Callable
19
+ from typing import Any, Literal, NamedTuple, Self, TypeVar, overload
20
+
21
+ from absfuyu.core import GetClassMembersMixin, versionadded, versionchanged
22
+
23
+ # from absfuyu.typings import KT as _KT
24
+ # from absfuyu.typings import VT as _VT
25
+
26
+
27
+ # Type
28
+ # ---------------------------------------------------------------------------
29
+ KT = TypeVar("KT")
30
+ VT = TypeVar("VT")
31
+ VT2 = TypeVar("VT2")
32
+ R = TypeVar("R") # Return type - Can be anything
33
+
34
+
35
+ # Class
36
+ # ---------------------------------------------------------------------------
37
+ class DictAnalyzeResult(NamedTuple):
38
+ """
39
+ Result for ``DictExt.analyze()``
40
+ """
41
+
42
+ max_value: int | float
43
+ min_value: int | float
44
+ max_list: list
45
+ min_list: list
46
+
47
+
48
+ class DictExtLegacy(GetClassMembersMixin, dict[KT, VT]):
49
+ """
50
+ ``dict`` extension (with no generic type-hints)
51
+
52
+ >>> # For a list of new methods
53
+ >>> DictExt.show_all_methods()
54
+ """
55
+
56
+ @versionchanged("3.3.0", reason="Updated return type")
57
+ def analyze(self) -> DictAnalyzeResult:
58
+ """
59
+ Analyze all the key values (``int``, ``float``)
60
+ in ``dict`` then return highest/lowest index
61
+
62
+ Returns
63
+ -------
64
+ dict
65
+ Analyzed data
66
+
67
+
68
+ Example:
69
+ --------
70
+ >>> test = DictExt({"abc": 9, "def": 9, "ghi": 8, "jkl": 1, "mno": 1})
71
+ >>> test.analyze()
72
+ DictAnalyzeResult(max_value=9, min_value=1, max_list=[('abc', 9), ('def', 9)], min_list=[('jkl', 1), ('mno', 1)])
73
+ """
74
+ try:
75
+ dct: dict = self.copy()
76
+
77
+ max_val: int | float = max(list(dct.values()))
78
+ min_val: int | float = min(list(dct.values()))
79
+ max_list = []
80
+ min_list = []
81
+
82
+ for k, v in dct.items():
83
+ if v == max_val:
84
+ max_list.append((k, v))
85
+ if v == min_val:
86
+ min_list.append((k, v))
87
+
88
+ return DictAnalyzeResult(max_val, min_val, max_list, min_list)
89
+
90
+ except TypeError:
91
+ err_msg = "Value must be int or float"
92
+ # logger.error(err_msg)
93
+ raise ValueError(err_msg) # noqa: B904
94
+
95
+ def swap_items(self) -> Self:
96
+ """
97
+ Swap ``dict.keys()`` with ``dict.values()``
98
+
99
+ Returns
100
+ -------
101
+ DictExt
102
+ Swapped dict
103
+
104
+
105
+ Example:
106
+ --------
107
+ >>> test = DictExt({"abc": 9})
108
+ >>> test.swap_items()
109
+ {9: 'abc'}
110
+ """
111
+ # return self.__class__(zip(self.values(), self.keys()))
112
+ return self.__class__({v: k for k, v in self.items()}) # type: ignore
113
+
114
+ def apply(self, func: Callable[[Any], Any], apply_to_value: bool = True) -> Self:
115
+ """
116
+ Apply function to ``DictExt.keys()`` or ``DictExt.values()``
117
+
118
+ Parameters
119
+ ----------
120
+ func : Callable
121
+ Callable function
122
+
123
+ apply_to_value : bool
124
+ | ``True``: Apply ``func`` to ``DictExt.values()``
125
+ | ``False``: Apply ``func`` to ``DictExt.keys()``
126
+
127
+ Returns
128
+ -------
129
+ DictExt
130
+ DictExt
131
+
132
+
133
+ Example:
134
+ --------
135
+ >>> test = DictExt({"abc": 9})
136
+ >>> test.apply(str)
137
+ {'abc': '9'}
138
+ """
139
+ if apply_to_value:
140
+ new_dict = {k: func(v) for k, v in self.items()}
141
+ else:
142
+ new_dict = {func(k): v for k, v in self.items()}
143
+ return self.__class__(new_dict)
144
+
145
+ @versionchanged("5.0.0", reason="Updated to handle more types and operator")
146
+ @versionadded("3.4.0")
147
+ def aggregate(
148
+ self,
149
+ other_dict: dict[KT, VT],
150
+ default_value: Any = 0,
151
+ operator_func: Callable[[Any, Any], Any] = operator.add, # operator add
152
+ ) -> Self:
153
+ """
154
+ Aggregates the values of the current dictionary with another dictionary.
155
+
156
+ For each unique key, this method applies the specified operator to the values
157
+ from both dictionaries. If a key exists in only one dictionary, its value is used.
158
+ If an error occurs during aggregation (e.g., incompatible types), the values
159
+ from both dictionaries are returned as a list.
160
+
161
+ Parameters
162
+ ----------
163
+ other_dict : dict
164
+ The dictionary to aggregate with.
165
+
166
+ default_value : Any, optional
167
+ The value to use for missing keys, by default ``0``
168
+
169
+
170
+ operator_func : Callable[[Any, Any], Any], optional
171
+ A function that takes two arguments and returns a single value,
172
+ by default ``operator.add``
173
+
174
+ Returns
175
+ -------
176
+ Self
177
+ A new instance of the aggregated dictionary.
178
+
179
+
180
+ Example:
181
+ --------
182
+ >>> test = DictExt({"test": 5, "test2": 9})
183
+ >>> agg = {"test1": 10, "test2": 1}
184
+ >>> print(test.aggregate(agg))
185
+ {'test1': 10, 'test': 5, 'test2': 10}
186
+
187
+ >>> test = DictExt({"test": 5, "test2": 9})
188
+ >>> agg = {"test1": 10, "test2": "1"}
189
+ >>> print(test.aggregate(agg))
190
+ {'test1': 10, 'test': 5, 'test2': [9, '1']}
191
+ """
192
+ merged_output = {}
193
+
194
+ # Create a set of all unique keys from both dictionaries
195
+ all_keys = set(self) | set(other_dict)
196
+
197
+ for k in all_keys:
198
+ # Retrieve values with default fallback
199
+ value_self = self.get(k, default_value)
200
+ value_other = other_dict.get(k, default_value)
201
+
202
+ try:
203
+ # Attempt to apply the operator for existing keys
204
+ merged_output[k] = operator_func(value_self, value_other)
205
+ except TypeError:
206
+ # If a TypeError occurs (e.g., if values are not compatible), store values as a list
207
+ merged_output[k] = [value_self, value_other]
208
+
209
+ return self.__class__(merged_output)
210
+
211
+
212
+ # Type-hints are hard coded, no work around Self[KT, VT] yet
213
+ class DictExt(GetClassMembersMixin, dict[KT, VT]):
214
+ """
215
+ ``dict`` extension
216
+
217
+ >>> # For a list of new methods
218
+ >>> DictExt.show_all_methods()
219
+ """
220
+
221
+ # Analyze
222
+ @versionchanged("3.3.0", reason="Updated return type")
223
+ def analyze(self) -> DictAnalyzeResult:
224
+ """
225
+ Analyze all the key values (``int``, ``float``)
226
+ in ``dict`` then return highest/lowest index
227
+
228
+ Returns
229
+ -------
230
+ dict
231
+ Analyzed data
232
+
233
+
234
+ Example:
235
+ --------
236
+ >>> test = DictExt({"abc": 9, "def": 9, "ghi": 8, "jkl": 1, "mno": 1})
237
+ >>> test.analyze()
238
+ DictAnalyzeResult(max_value=9, min_value=1, max_list=[('abc', 9), ('def', 9)], min_list=[('jkl', 1), ('mno', 1)])
239
+ """
240
+ try:
241
+ dct: dict = self.copy()
242
+
243
+ max_val: int | float = max(list(dct.values()))
244
+ min_val: int | float = min(list(dct.values()))
245
+ max_list = []
246
+ min_list = []
247
+
248
+ for k, v in dct.items():
249
+ if v == max_val:
250
+ max_list.append((k, v))
251
+ if v == min_val:
252
+ min_list.append((k, v))
253
+
254
+ return DictAnalyzeResult(max_val, min_val, max_list, min_list)
255
+
256
+ except TypeError:
257
+ err_msg = "Value must be int or float"
258
+ # logger.error(err_msg)
259
+ raise ValueError(err_msg) # noqa: B904
260
+
261
+ # Swap
262
+ def swap_items(self) -> "DictExt[VT, KT]":
263
+ """
264
+ Swap ``dict.keys()`` with ``dict.values()``
265
+
266
+ Returns
267
+ -------
268
+ DictExt
269
+ Swapped dict
270
+
271
+
272
+ Example:
273
+ --------
274
+ >>> test = DictExt({"abc": 9})
275
+ >>> test.swap_items()
276
+ {9: 'abc'}
277
+ """
278
+ # return self.__class__(zip(self.values(), self.keys()))
279
+ out: dict[VT, KT] = {v: k for k, v in self.items()}
280
+ # return self.__class__({v: k for k, v in self.items()}) # type: ignore
281
+ return self.__class__(out) # type: ignore
282
+
283
+ # Apply
284
+ @overload
285
+ def apply(self, func: Callable[[VT], R]) -> "DictExt[KT, R]": ...
286
+
287
+ @overload
288
+ def apply(self, func: Callable[[KT], R], apply_to_value: Literal[False] = ...) -> "DictExt[R, VT]": ...
289
+
290
+ def apply(self, func: Callable[[KT | VT], R], apply_to_value: bool = True): # type: ignore
291
+ """
292
+ Apply function to ``DictExt.keys()`` or ``DictExt.values()``
293
+
294
+ Parameters
295
+ ----------
296
+ func : Callable
297
+ Callable function
298
+
299
+ apply_to_value : bool
300
+ | ``True``: Apply ``func`` to ``DictExt.values()``
301
+ | ``False``: Apply ``func`` to ``DictExt.keys()``
302
+
303
+ Returns
304
+ -------
305
+ DictExt
306
+ DictExt
307
+
308
+
309
+ Example:
310
+ --------
311
+ >>> test = DictExt({"abc": 9})
312
+ >>> test.apply(str)
313
+ {'abc': '9'}
314
+ """
315
+ if apply_to_value:
316
+ new_dict_v: dict[KT, R] = {k: func(v) for k, v in self.items()}
317
+ return self.__class__(new_dict_v) # type: ignore
318
+ else:
319
+ new_dict_k: dict[R, VT] = {func(k): v for k, v in self.items()}
320
+ return self.__class__(new_dict_k) # type: ignore
321
+
322
+ # Aggregate
323
+ @versionchanged("5.0.0", reason="Updated to handle more types and operator")
324
+ @versionadded("3.4.0")
325
+ def aggregate(
326
+ self,
327
+ other_dict: dict[KT, VT | VT2],
328
+ default_value: Any = 0,
329
+ operator_func: Callable[[Any, Any], R] = operator.add, # operator add
330
+ ):
331
+ """
332
+ Aggregates the values of the current dictionary with another dictionary.
333
+
334
+ For each unique key, this method applies the specified operator to the values
335
+ from both dictionaries. If a key exists in only one dictionary, its value is used.
336
+ If an error occurs during aggregation (e.g., incompatible types), the values
337
+ from both dictionaries are returned as a list.
338
+
339
+ Parameters
340
+ ----------
341
+ other_dict : dict
342
+ The dictionary to aggregate with.
343
+
344
+ default_value : Any, optional
345
+ The value to use for missing keys, by default ``0``
346
+
347
+
348
+ operator_func : Callable[[Any, Any], Any], optional
349
+ A function that takes two arguments and returns a single value,
350
+ by default ``operator.add``
351
+
352
+ Returns
353
+ -------
354
+ Self
355
+ A new instance of the aggregated dictionary.
356
+
357
+
358
+ Example:
359
+ --------
360
+ >>> test = DictExt({"test": 5, "test2": 9})
361
+ >>> agg = {"test1": 10, "test2": 1}
362
+ >>> print(test.aggregate(agg))
363
+ {'test1': 10, 'test': 5, 'test2': 10}
364
+
365
+ >>> test = DictExt({"test": 5, "test2": 9})
366
+ >>> agg = {"test1": 10, "test2": "1"}
367
+ >>> print(test.aggregate(agg))
368
+ {'test1': 10, 'test': 5, 'test2': [9, '1']}
369
+ """
370
+ merged_output: dict[KT, VT | R | list[VT | VT2]] = {}
371
+
372
+ # Create a set of all unique keys from both dictionaries
373
+ all_keys = set(self) | set(other_dict)
374
+
375
+ for k in all_keys:
376
+ # Retrieve values with default fallback
377
+ value_self = self.get(k, default_value)
378
+ value_other = other_dict.get(k, default_value)
379
+
380
+ try:
381
+ # Attempt to apply the operator for existing keys
382
+ merged_output[k] = operator_func(value_self, value_other)
383
+ except TypeError:
384
+ # If a TypeError occurs (e.g., if values are not compatible), store values as a list
385
+ merged_output[k] = [value_self, value_other] # type: ignore
386
+
387
+ return self.__class__(merged_output) # type: ignore
@@ -3,8 +3,8 @@ Absfuyu: Data Extension
3
3
  -----------------------
4
4
  Support classes
5
5
 
6
- Version: 5.11.0
7
- Date updated: 30/09/2025 (dd/mm/yyyy)
6
+ Version: 5.12.0
7
+ Date updated: 17/10/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module Package
@@ -3,8 +3,8 @@ Absfuyu: Data Extension
3
3
  -----------------------
4
4
  int extension
5
5
 
6
- Version: 5.11.0
7
- Date updated: 30/09/2025 (dd/mm/yyyy)
6
+ Version: 5.12.0
7
+ Date updated: 17/10/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module Package