absfuyu 3.1.1__py3-none-any.whl → 3.3.3__py3-none-any.whl

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 (61) hide show
  1. absfuyu/__init__.py +3 -10
  2. absfuyu/__main__.py +5 -250
  3. absfuyu/cli/__init__.py +51 -0
  4. absfuyu/cli/color.py +24 -0
  5. absfuyu/cli/config_group.py +56 -0
  6. absfuyu/cli/do_group.py +76 -0
  7. absfuyu/cli/game_group.py +109 -0
  8. absfuyu/config/__init__.py +117 -100
  9. absfuyu/config/config.json +0 -7
  10. absfuyu/core.py +5 -66
  11. absfuyu/everything.py +7 -9
  12. absfuyu/extensions/beautiful.py +30 -23
  13. absfuyu/extensions/dev/__init__.py +11 -8
  14. absfuyu/extensions/dev/password_hash.py +4 -2
  15. absfuyu/extensions/dev/passwordlib.py +7 -5
  16. absfuyu/extensions/dev/project_starter.py +4 -2
  17. absfuyu/extensions/dev/shutdownizer.py +148 -0
  18. absfuyu/extensions/extra/__init__.py +1 -2
  19. absfuyu/extensions/extra/data_analysis.py +182 -107
  20. absfuyu/fun/WGS.py +50 -26
  21. absfuyu/fun/__init__.py +6 -7
  22. absfuyu/fun/tarot.py +1 -1
  23. absfuyu/game/__init__.py +75 -81
  24. absfuyu/game/game_stat.py +36 -0
  25. absfuyu/game/sudoku.py +41 -48
  26. absfuyu/game/tictactoe.py +303 -548
  27. absfuyu/game/wordle.py +56 -47
  28. absfuyu/general/__init__.py +17 -7
  29. absfuyu/general/content.py +16 -15
  30. absfuyu/general/data_extension.py +282 -90
  31. absfuyu/general/generator.py +67 -67
  32. absfuyu/general/human.py +74 -78
  33. absfuyu/logger.py +94 -68
  34. absfuyu/pkg_data/__init__.py +29 -25
  35. absfuyu/py.typed +0 -0
  36. absfuyu/sort.py +61 -47
  37. absfuyu/tools/__init__.py +0 -1
  38. absfuyu/tools/converter.py +80 -62
  39. absfuyu/tools/keygen.py +62 -67
  40. absfuyu/tools/obfuscator.py +57 -53
  41. absfuyu/tools/stats.py +24 -24
  42. absfuyu/tools/web.py +10 -9
  43. absfuyu/util/__init__.py +71 -33
  44. absfuyu/util/api.py +53 -43
  45. absfuyu/util/json_method.py +25 -27
  46. absfuyu/util/lunar.py +20 -24
  47. absfuyu/util/path.py +362 -241
  48. absfuyu/util/performance.py +217 -135
  49. absfuyu/util/pkl.py +8 -8
  50. absfuyu/util/zipped.py +17 -19
  51. absfuyu/version.py +160 -147
  52. absfuyu-3.3.3.dist-info/METADATA +124 -0
  53. absfuyu-3.3.3.dist-info/RECORD +59 -0
  54. {absfuyu-3.1.1.dist-info → absfuyu-3.3.3.dist-info}/WHEEL +1 -2
  55. {absfuyu-3.1.1.dist-info → absfuyu-3.3.3.dist-info}/entry_points.txt +1 -0
  56. {absfuyu-3.1.1.dist-info → absfuyu-3.3.3.dist-info/licenses}/LICENSE +1 -1
  57. absfuyu/extensions/dev/pkglib.py +0 -98
  58. absfuyu/game/tictactoe2.py +0 -318
  59. absfuyu-3.1.1.dist-info/METADATA +0 -215
  60. absfuyu-3.1.1.dist-info/RECORD +0 -55
  61. absfuyu-3.1.1.dist-info/top_level.txt +0 -1
@@ -3,22 +3,21 @@ Absfuyu: Data extension
3
3
  -----------------------
4
4
  Extension for data type such as ``list``, ``str``, ``dict``, ...
5
5
 
6
- Version: 1.12.3
7
- Date updated: 23/01/2024 (dd/mm/yyyy)
6
+ Version: 1.14.3
7
+ Date updated: 05/04/2024 (dd/mm/yyyy)
8
8
 
9
9
  Features:
10
10
  ---------
11
- - Text
12
- - ListExt
13
11
  - DictExt
14
12
  - IntNumber
13
+ - ListExt
14
+ - Text
15
15
 
16
16
  Usage:
17
17
  ------
18
18
  >>> from absfuyu.general.data_extension import DictExt, IntNumber, ListExt, Text
19
19
  """
20
20
 
21
-
22
21
  # Module level
23
22
  ###########################################################################
24
23
  __all__ = [
@@ -34,21 +33,42 @@ __all__ = [
34
33
  "DictBoolTrue",
35
34
  "DictBoolFalse",
36
35
  # "DictNoDunder",
36
+ # Dict format
37
+ "TextAnalyzeDictFormat",
38
+ # Support
39
+ "DictAnalyzeResult",
37
40
  ]
38
41
 
39
42
 
40
43
  # Library
41
44
  ###########################################################################
42
- from collections import Counter
43
- from itertools import accumulate, chain, groupby
44
45
  import math
45
46
  import operator
46
47
  import random
47
- from typing import Any, Callable, Dict, List, Optional, Tuple, Union
48
- from sys import version_info as python_version
49
-
50
- from absfuyu.general.generator import Generator, Charset
51
- from absfuyu.logger import logger, _compress_list_for_print
48
+ from collections import Counter
49
+ from itertools import accumulate, chain, groupby
50
+ from sys import version_info as _python_version
51
+ from typing import (
52
+ Any,
53
+ Callable,
54
+ Dict,
55
+ List,
56
+ NamedTuple,
57
+ Optional,
58
+ Tuple,
59
+ TypedDict,
60
+ Union,
61
+ )
62
+
63
+ if _python_version.minor >= 11:
64
+ from typing import NotRequired
65
+ else:
66
+ from typing_extensions import NotRequired # type: ignore
67
+
68
+ from deprecated.sphinx import versionadded, versionchanged
69
+
70
+ from absfuyu.general.generator import Charset, Generator
71
+ from absfuyu.logger import _compress_list_for_print, logger
52
72
  from absfuyu.util import set_min, set_min_max
53
73
 
54
74
 
@@ -68,7 +88,7 @@ def _dict_bool(dict_object: dict, option: bool) -> Optional[dict]:
68
88
  return None
69
89
 
70
90
 
71
- # Sub class
91
+ # MARK: Sub class
72
92
  ###########################################################################
73
93
  class Pow:
74
94
  """Number power by a number"""
@@ -93,7 +113,7 @@ class Pow:
93
113
 
94
114
  :rtype: list[int | float]
95
115
  """
96
- return [self.number] * self.power_by
116
+ return [self.number] * int(self.power_by)
97
117
 
98
118
  def calculate(self) -> float:
99
119
  """
@@ -148,14 +168,59 @@ class DictBoolFalse(Dict[Any, bool]):
148
168
  # return out.__repr__()
149
169
 
150
170
 
171
+ class TextAnalyzeDictFormat(TypedDict):
172
+ """
173
+ Dict format for ``Text.analyze()`` method
174
+
175
+ Parameters
176
+ ----------
177
+ digit : int
178
+ Number of digit characters
179
+
180
+ uppercase : int
181
+ Number of uppercase characters
182
+
183
+ lowercase : int
184
+ Number of lowercase characters
185
+
186
+ other : int
187
+ Number of other printable characters
188
+
189
+ is_pangram : NotRequired[bool]
190
+ Is a pangram (Not required)
191
+
192
+ is_palindrome : NotRequired[bool]
193
+ Is a palindrome (Not required)
194
+ """
195
+
196
+ digit: int
197
+ uppercase: int
198
+ lowercase: int
199
+ other: int
200
+ is_pangram: NotRequired[bool]
201
+ is_palindrome: NotRequired[bool]
202
+
203
+
204
+ class DictAnalyzeResult(NamedTuple):
205
+ """
206
+ Result for ``DictExt.analyze()``
207
+ """
208
+
209
+ max_value: Union[int, float]
210
+ min_value: Union[int, float]
211
+ max_list: list
212
+ min_list: list
213
+
214
+
151
215
  # Class
152
216
  ###########################################################################
217
+ # MARK: Text
153
218
  class Text(str):
154
219
  """
155
220
  ``str`` extension
156
221
  """
157
222
 
158
- def divide(self, string_split_size: int = 60) -> list:
223
+ def divide(self, string_split_size: int = 60) -> List[str]:
159
224
  """
160
225
  Divide long string into smaller size
161
226
 
@@ -167,7 +232,7 @@ class Text(str):
167
232
 
168
233
  Returns
169
234
  -------
170
- list
235
+ list[str]
171
236
  A list in which each item is a smaller
172
237
  string with the size of ``string_split_size``
173
238
  (need to be concaternate later)
@@ -179,7 +244,7 @@ class Text(str):
179
244
  >>> test.divide(string_split_size=20)
180
245
  ['This is an extremely', ' long line of text!']
181
246
  """
182
- temp = self
247
+ temp = str(self)
183
248
  output = []
184
249
  while len(temp) != 0:
185
250
  output.append(temp[:string_split_size])
@@ -243,7 +308,6 @@ class Text(str):
243
308
  output = []
244
309
 
245
310
  # split variable
246
- splt_var_len = split_var_len
247
311
  splt_len = len(temp)
248
312
 
249
313
  if custom_var_name is None:
@@ -283,13 +347,20 @@ class Text(str):
283
347
 
284
348
  return output
285
349
 
286
- def analyze(self) -> dict:
350
+ @versionchanged(version="3.3.0", reason="Update functionality")
351
+ def analyze(self, full: bool = False) -> TextAnalyzeDictFormat:
287
352
  """
288
353
  String analyze (count number of type of character)
289
354
 
355
+ Parameters
356
+ ----------
357
+ full : bool
358
+ Full analyze when ``True``
359
+ (Default: ``False``)
360
+
290
361
  Returns
291
362
  -------
292
- dict
363
+ dict | TextAnalyzeDictFormat
293
364
  A dictionary contains number of digit character,
294
365
  uppercase character, lowercase character, and
295
366
  special character
@@ -304,7 +375,12 @@ class Text(str):
304
375
 
305
376
  temp = self
306
377
 
307
- detail = {"digit": 0, "uppercase": 0, "lowercase": 0, "other": 0}
378
+ detail: TextAnalyzeDictFormat = {
379
+ "digit": 0,
380
+ "uppercase": 0,
381
+ "lowercase": 0,
382
+ "other": 0,
383
+ }
308
384
 
309
385
  for x in temp:
310
386
  if ord(x) in range(48, 58): # num
@@ -316,6 +392,10 @@ class Text(str):
316
392
  else:
317
393
  detail["other"] += 1
318
394
 
395
+ if full:
396
+ detail["is_palindrome"] = self.is_palindrome()
397
+ detail["is_pangram"] = self.is_pangram()
398
+
319
399
  return detail
320
400
 
321
401
  def reverse(self) -> "Text":
@@ -334,7 +414,7 @@ class Text(str):
334
414
  >>> test.reverse()
335
415
  '!dlroW ,olleH'
336
416
  """
337
- return __class__(self[::-1])
417
+ return self.__class__(self[::-1])
338
418
 
339
419
  def is_pangram(self) -> bool:
340
420
  """
@@ -403,7 +483,7 @@ class Text(str):
403
483
 
404
484
  for i in range(str_len):
405
485
  if i % 2 == 0:
406
- temp.append(f"\\x")
486
+ temp.append("\\x")
407
487
  temp.append(hex_str[i])
408
488
  return "".join(temp)
409
489
  else:
@@ -431,7 +511,7 @@ class Text(str):
431
511
  >>> test.random_capslock()
432
512
  'tHis iS An ExtREmELY loNg liNE oF tExT!'
433
513
  """
434
- probability = set_min_max(probability)
514
+ probability = int(set_min_max(probability))
435
515
  text = self.lower()
436
516
 
437
517
  temp = []
@@ -440,7 +520,7 @@ class Text(str):
440
520
  x = x.upper()
441
521
  temp.append(x)
442
522
  logger.debug(temp)
443
- return __class__("".join(temp))
523
+ return self.__class__("".join(temp))
444
524
 
445
525
  def reverse_capslock(self) -> "Text":
446
526
  """
@@ -464,12 +544,12 @@ class Text(str):
464
544
  temp[i] = x.lower()
465
545
  else:
466
546
  temp[i] = x.upper()
467
- return __class__("".join(temp))
547
+ return self.__class__("".join(temp))
468
548
 
469
549
  def to_list(self) -> List[str]:
470
550
  """
471
551
  Convert into list
472
-
552
+
473
553
  Returns
474
554
  -------
475
555
  list[str]
@@ -484,7 +564,111 @@ class Text(str):
484
564
  """
485
565
  return list(self)
486
566
 
567
+ @versionadded(version="3.3.0")
568
+ def to_listext(self) -> "ListExt":
569
+ """
570
+ Convert into ``ListExt``
571
+
572
+ Returns
573
+ -------
574
+ ListExt[str]
575
+ List of string
576
+
577
+
578
+ Example:
579
+ --------
580
+ >>> test = Text("test")
581
+ >>> test.to_listext()
582
+ ['t', 'e', 's', 't']
583
+ """
584
+ return ListExt(self)
585
+
586
+ @versionadded(version="3.3.0")
587
+ def count_pattern(self, pattern: str, ignore_capslock: bool = False) -> int:
588
+ """
589
+ Returns how many times ``pattern`` appears in text
590
+ (Inspired by hackerrank exercise)
487
591
 
592
+ Parameters
593
+ ----------
594
+ pattern : str
595
+ Pattern to count
596
+
597
+ ignore_capslock : bool
598
+ Ignore the pattern uppercase or lowercase
599
+ (Default: ``False`` - Exact match)
600
+
601
+ Returns
602
+ -------
603
+ int
604
+ How many times pattern appeared
605
+
606
+
607
+ Example:
608
+ --------
609
+ >>> Text("test").count_pattern("t")
610
+ 2
611
+ """
612
+ if len(pattern) > len(self):
613
+ raise ValueError(f"len(pattern) must not larger than {len(self)}")
614
+
615
+ temp = str(self)
616
+ if ignore_capslock:
617
+ pattern = pattern.lower()
618
+ temp = temp.lower()
619
+
620
+ out = [
621
+ 1
622
+ for i in range(len(temp) - len(pattern) + 1)
623
+ if temp[i : i + len(pattern)] == pattern
624
+ ]
625
+ return sum(out)
626
+
627
+ @versionadded(version="3.3.0")
628
+ def hapax(self, strict: bool = False) -> List[str]:
629
+ """
630
+ A hapax legomenon (often abbreviated to hapax)
631
+ is a word which occurs only once in either
632
+ the written record of a language, the works of
633
+ an author, or in a single text.
634
+
635
+ This function returns a list of hapaxes (if any)
636
+ (Lettercase is ignored)
637
+
638
+ Parameters
639
+ ----------
640
+ strict : bool
641
+ Remove all special characters before checking for hapax
642
+ (Default: ``False``)
643
+
644
+ Returns
645
+ -------
646
+ list[str]
647
+ A list of hapaxes
648
+
649
+
650
+ Example:
651
+ --------
652
+ >>> test = Text("A a. a, b c c= C| d d")
653
+ >>> test.hapax()
654
+ ['a', 'a.', 'a,', 'b', 'c', 'c=', 'c|']
655
+
656
+ >>> test.hapax(strict=True)
657
+ ['b']
658
+ """
659
+ word_list: List[str] = self.lower().split()
660
+ if strict:
661
+ remove_characters: List[str] = list(r"\"'.,:;|()[]{}\/!@#$%^&*-_=+?<>`~")
662
+ temp = str(self)
663
+ for x in remove_characters:
664
+ temp = temp.replace(x, "")
665
+ word_list = temp.lower().split()
666
+
667
+ hapaxes = filter(lambda x: word_list.count(x) == 1, word_list)
668
+ return list(hapaxes)
669
+
670
+
671
+ # MARK: IntNumber
488
672
  class IntNumber(int):
489
673
  """
490
674
  ``int`` extension
@@ -640,7 +824,7 @@ class IntNumber(int):
640
824
  # a perfect number have a form of (2**(n-1))*((2**n)-1)
641
825
  perfect_number.append((2**(x-1))*((2**x)-1))
642
826
  """
643
- number = self
827
+ number = int(self)
644
828
 
645
829
  perfect_number = [
646
830
  6,
@@ -653,15 +837,15 @@ class IntNumber(int):
653
837
  2_305_843_008_139_952_128,
654
838
  ]
655
839
 
656
- if int(number) in perfect_number:
840
+ if number in perfect_number:
657
841
  return True
658
842
 
659
- elif int(number) < perfect_number[-1]:
843
+ elif number < perfect_number[-1]:
660
844
  return False
661
845
 
662
846
  else:
663
847
  # Faster way to check
664
- perfect_number_index = [
848
+ perfect_number_index: List[int] = [
665
849
  61,
666
850
  89,
667
851
  107,
@@ -709,9 +893,9 @@ class IntNumber(int):
709
893
  for x in perfect_number_index:
710
894
  # a perfect number have a form of (2**(n-1))*((2**n)-1)
711
895
  perfect_number = (2 ** (x - 1)) * ((2**x) - 1)
712
- if number < perfect_number:
896
+ if number < perfect_number: # type: ignore
713
897
  return False
714
- elif number == perfect_number:
898
+ elif number == perfect_number: # type: ignore
715
899
  return True
716
900
 
717
901
  # Manual way when above method not working
@@ -721,7 +905,7 @@ class IntNumber(int):
721
905
  i = 2
722
906
  while i * i <= number:
723
907
  if number % i == 0:
724
- s += +i + number / i
908
+ s += +i + number / i # type: ignore
725
909
  i += 1
726
910
  # s == number -> perfect
727
911
  return True if s == number and number != 1 else False
@@ -745,8 +929,9 @@ class IntNumber(int):
745
929
  """
746
930
  try:
747
931
  check = sum([int(x) ** len(str(self)) for x in str(self)])
748
- return self == check
749
- except:
932
+ res = int(self) == check
933
+ return res # type: ignore
934
+ except Exception:
750
935
  return False
751
936
 
752
937
  def reverse(self) -> "IntNumber":
@@ -765,10 +950,10 @@ class IntNumber(int):
765
950
  >>> test.reverse()
766
951
  201
767
952
  """
768
- number = self
953
+ number = int(self)
769
954
  if number <= 1:
770
955
  number *= -1
771
- return __class__(str(number)[::-1])
956
+ return self.__class__(str(number)[::-1])
772
957
 
773
958
  def is_palindromic(self) -> bool:
774
959
  """
@@ -797,6 +982,7 @@ class IntNumber(int):
797
982
  return self.is_palindromic() and self.is_prime()
798
983
 
799
984
  # calculation stuff
985
+ @versionchanged(version="3.3.0", reason="Fix bug")
800
986
  def lcm(self, with_number: int) -> "IntNumber":
801
987
  """
802
988
  Least common multiple of ``self`` and ``with_number``
@@ -818,8 +1004,12 @@ class IntNumber(int):
818
1004
  >>> test.lcm(5)
819
1005
  510
820
1006
  """
821
- return __class__((self * with_number) // math.gcd(self, with_number))
1007
+ try:
1008
+ return self.__class__(math.lcm(self, with_number))
1009
+ except AttributeError: # Python < 3.9
1010
+ return self.__class__((self * with_number) // math.gcd(self, with_number))
822
1011
 
1012
+ @versionchanged(version="3.3.0", reason="Fix bug")
823
1013
  def gcd(self, with_number: int) -> "IntNumber":
824
1014
  """
825
1015
  Greatest common divisor of ``self`` and ``with_number``
@@ -841,10 +1031,7 @@ class IntNumber(int):
841
1031
  >>> test.gcd(8)
842
1032
  8
843
1033
  """
844
- if python_version.micro < 9:
845
- return __class__(math.gcd(self, with_number))
846
- else:
847
- return __class__(math.lcm(self, with_number))
1034
+ return self.__class__(math.gcd(self, with_number))
848
1035
 
849
1036
  def add_to_one_digit(self, master_number: bool = False) -> "IntNumber":
850
1037
  """
@@ -873,7 +1060,7 @@ class IntNumber(int):
873
1060
  >>> test.add_to_one_digit(master_number=True)
874
1061
  11
875
1062
  """
876
- number = self
1063
+ number = int(self)
877
1064
  if number < 0:
878
1065
  number *= -1
879
1066
  logger.debug(f"Current number: {number}")
@@ -883,7 +1070,7 @@ class IntNumber(int):
883
1070
  if number == 22 or number == 11:
884
1071
  break # Master number
885
1072
  logger.debug(f"Sum after loop: {number}")
886
- return __class__(number)
1073
+ return self.__class__(number)
887
1074
 
888
1075
  def divisible_list(self, short_form: bool = True) -> List[int]:
889
1076
  """
@@ -1024,9 +1211,10 @@ class IntNumber(int):
1024
1211
  characteristic = DictBoolTrue(characteristic)
1025
1212
 
1026
1213
  output["characteristic"] = characteristic
1027
- return output
1214
+ return output # type: ignore
1028
1215
 
1029
1216
 
1217
+ # MARK: ListExt
1030
1218
  class ListExt(list):
1031
1219
  """
1032
1220
  ``list`` extension
@@ -1065,7 +1253,9 @@ class ListExt(list):
1065
1253
  list
1066
1254
  Filtered list
1067
1255
  """
1068
- number_of_items = set_min_max(number_of_items, min_value=0, max_value=len(self))
1256
+ number_of_items = int(
1257
+ set_min_max(number_of_items, min_value=0, max_value=len(self))
1258
+ )
1069
1259
  return self[:number_of_items]
1070
1260
 
1071
1261
  def tail(self, number_of_items: int = 5) -> list:
@@ -1083,7 +1273,9 @@ class ListExt(list):
1083
1273
  list
1084
1274
  Filtered list
1085
1275
  """
1086
- number_of_items = set_min_max(number_of_items, min_value=0, max_value=len(self))
1276
+ number_of_items = int(
1277
+ set_min_max(number_of_items, min_value=0, max_value=len(self))
1278
+ )
1087
1279
  return self[::-1][:number_of_items][::-1]
1088
1280
 
1089
1281
  def sorts(self, reverse: bool = False) -> "ListExt":
@@ -1110,7 +1302,7 @@ class ListExt(list):
1110
1302
  [1, 9, 'aaa', 'abc', 1.4, 3.5]
1111
1303
  """
1112
1304
  lst = self.copy()
1113
- type_weights = {}
1305
+ type_weights: dict = {}
1114
1306
  for x in lst:
1115
1307
  if type(x) not in type_weights:
1116
1308
  type_weights[type(x)] = len(type_weights)
@@ -1121,14 +1313,14 @@ class ListExt(list):
1121
1313
  )
1122
1314
 
1123
1315
  logger.debug(output)
1124
- return __class__(output)
1316
+ return self.__class__(output)
1125
1317
 
1126
1318
  def freq(
1127
1319
  self,
1128
1320
  sort: bool = False,
1129
1321
  num_of_first_char: Optional[int] = None,
1130
1322
  appear_increment: bool = False,
1131
- ):
1323
+ ) -> Union[dict, List[int]]:
1132
1324
  """
1133
1325
  Find frequency of each item in list
1134
1326
 
@@ -1140,7 +1332,7 @@ class ListExt(list):
1140
1332
 
1141
1333
  num_of_first_char : int | None
1142
1334
  | Number of first character taken into account to sort
1143
- | (Default: None)
1335
+ | (Default: ``None``)
1144
1336
  | (num_of_first_char = ``1``: first character in each item)
1145
1337
 
1146
1338
  appear_increment : bool
@@ -1152,7 +1344,7 @@ class ListExt(list):
1152
1344
  dict
1153
1345
  A dict that show frequency
1154
1346
 
1155
- list
1347
+ list[int]
1156
1348
  Incremental index list
1157
1349
 
1158
1350
 
@@ -1168,14 +1360,14 @@ class ListExt(list):
1168
1360
  """
1169
1361
 
1170
1362
  if sort:
1171
- data = self.sorts()
1363
+ data = self.sorts().copy()
1172
1364
  else:
1173
1365
  data = self.copy()
1174
1366
 
1175
1367
  if num_of_first_char is None:
1176
1368
  temp = Counter(data)
1177
1369
  else:
1178
- max_char = min([len(str(x)) for x in data])
1370
+ max_char: int = min([len(str(x)) for x in data])
1179
1371
  logger.debug(f"Max character: {max_char}")
1180
1372
  if num_of_first_char not in range(1, max_char):
1181
1373
  logger.debug(f"Not in {range(1, max_char)}. Using default value...")
@@ -1186,12 +1378,12 @@ class ListExt(list):
1186
1378
 
1187
1379
  try:
1188
1380
  times_appear = dict(sorted(temp.items()))
1189
- except:
1190
- times_appear = dict(__class__(temp.items()).sorts())
1381
+ except Exception:
1382
+ times_appear = dict(self.__class__(temp.items()).sorts())
1191
1383
  logger.debug(times_appear)
1192
1384
 
1193
1385
  if appear_increment:
1194
- times_appear_increment = list(
1386
+ times_appear_increment: List[int] = list(
1195
1387
  accumulate(times_appear.values(), operator.add)
1196
1388
  )
1197
1389
  logger.debug(times_appear_increment)
@@ -1329,7 +1521,7 @@ class ListExt(list):
1329
1521
  ['1', '2', '3']
1330
1522
  """
1331
1523
  # return __class__(func(x) for x in self)
1332
- return __class__(map(func, self))
1524
+ return self.__class__(map(func, self))
1333
1525
 
1334
1526
  def unique(self) -> "ListExt":
1335
1527
  """
@@ -1347,7 +1539,7 @@ class ListExt(list):
1347
1539
  >>> test.unique()
1348
1540
  [1, 2, 3]
1349
1541
  """
1350
- return __class__(set(self))
1542
+ return self.__class__(set(self))
1351
1543
 
1352
1544
  def group_by_unique(self) -> "ListExt":
1353
1545
  """
@@ -1371,7 +1563,7 @@ class ListExt(list):
1371
1563
 
1372
1564
  # New
1373
1565
  temp = groupby(self.sorts())
1374
- return __class__([list(g) for _, g in temp])
1566
+ return self.__class__([list(g) for _, g in temp])
1375
1567
 
1376
1568
  @staticmethod
1377
1569
  def _group_by_unique(iterable: list) -> List[list]:
@@ -1414,7 +1606,7 @@ class ListExt(list):
1414
1606
  iter = self.copy()
1415
1607
 
1416
1608
  # Init loop
1417
- for _ in range(set_min(max_loop, min_value=3)):
1609
+ for _ in range(int(set_min(max_loop, min_value=3))):
1418
1610
  temp: Dict[Any, list] = {}
1419
1611
  # Make dict{key: all `item` that contains `key`}
1420
1612
  for item in iter:
@@ -1450,7 +1642,7 @@ class ListExt(list):
1450
1642
  """
1451
1643
  try:
1452
1644
  return ListExt(chain(*self))
1453
- except:
1645
+ except Exception:
1454
1646
  temp = list(map(lambda x: x if isinstance(x, list) else [x], self))
1455
1647
  return ListExt(chain(*temp))
1456
1648
 
@@ -1477,7 +1669,7 @@ class ListExt(list):
1477
1669
  >>> test.numbering()
1478
1670
  [(0, 9), (1, 9), (2, 9)]
1479
1671
  """
1480
- start = set_min(start, min_value=0)
1672
+ start = int(set_min(start, min_value=0))
1481
1673
  return ListExt(enumerate(self, start=start))
1482
1674
 
1483
1675
  @staticmethod
@@ -1485,16 +1677,21 @@ class ListExt(list):
1485
1677
  """
1486
1678
  Static method for ``numbering``
1487
1679
  """
1488
- start = set_min(start, min_value=0)
1680
+ start = int(set_min(start, min_value=0))
1489
1681
  return list(enumerate(iterable, start=start))
1490
1682
 
1491
1683
 
1684
+ # MARK: DictExt
1492
1685
  class DictExt(dict):
1493
1686
  """
1494
1687
  ``dict`` extension
1495
1688
  """
1496
1689
 
1497
- def analyze(self) -> dict:
1690
+ @versionchanged(
1691
+ version="3.3.0",
1692
+ reason="Change function output; Before: <dict>, Now: DictAnalyzeResult",
1693
+ )
1694
+ def analyze(self) -> DictAnalyzeResult:
1498
1695
  """
1499
1696
  Analyze all the key values (``int``, ``float``)
1500
1697
  in ``dict`` then return highest/lowest index
@@ -1515,18 +1712,13 @@ class DictExt(dict):
1515
1712
  >>> "mno": 1
1516
1713
  >>> })
1517
1714
  >>> test.analyze()
1518
- {
1519
- 'max_value': 9,
1520
- 'min_value': 1,
1521
- 'max': [('abc', 9), ('def', 9)],
1522
- 'min': [('jkl', 1), ('mno', 1)]
1523
- }
1715
+ DictAnalyzeResult(max_value=9, min_value=1, max_list=[('abc', 9), ('def', 9)], min_list=[('jkl', 1), ('mno', 1)])
1524
1716
  """
1525
1717
  try:
1526
- dct = self.copy()
1718
+ dct: dict = self.copy()
1527
1719
 
1528
- max_val = max(list(dct.values()))
1529
- min_val = min(list(dct.values()))
1720
+ max_val: Union[int, float] = max(list(dct.values()))
1721
+ min_val: Union[int, float] = min(list(dct.values()))
1530
1722
  max_list = []
1531
1723
  min_list = []
1532
1724
 
@@ -1536,20 +1728,21 @@ class DictExt(dict):
1536
1728
  if v == min_val:
1537
1729
  min_list.append((k, v))
1538
1730
 
1539
- output = {
1540
- "max_value": max_val,
1541
- "min_value": min_val,
1542
- "max": max_list,
1543
- "min": min_list,
1544
- }
1731
+ # output = {
1732
+ # "max_value": max_val,
1733
+ # "min_value": min_val,
1734
+ # "max": max_list,
1735
+ # "min": min_list,
1736
+ # }
1545
1737
 
1546
- logger.debug(output)
1547
- return output
1738
+ # logger.debug(output)
1739
+ # return output
1740
+ return DictAnalyzeResult(max_val, min_val, max_list, min_list)
1548
1741
 
1549
- except:
1742
+ except Exception:
1550
1743
  err_msg = "Value must be int or float"
1551
1744
  logger.error(err_msg)
1552
- raise ValueError(err_msg)
1745
+ raise ValueError(err_msg) # noqa: B904
1553
1746
 
1554
1747
  def swap_items(self) -> "DictExt":
1555
1748
  """
@@ -1567,7 +1760,7 @@ class DictExt(dict):
1567
1760
  >>> test.swap_items()
1568
1761
  {9: 'abc'}
1569
1762
  """
1570
- return __class__(zip(self.values(), self.keys()))
1763
+ return self.__class__(zip(self.values(), self.keys()))
1571
1764
 
1572
1765
  def apply(self, func: Callable, apply_to_value: bool = True) -> "DictExt":
1573
1766
  """
@@ -1598,13 +1791,12 @@ class DictExt(dict):
1598
1791
  k = self.keys()
1599
1792
  v = map(func, self.values())
1600
1793
  else:
1601
- k = map(func, self.keys())
1602
- v = self.values()
1603
- return __class__(zip(k, v))
1794
+ k = map(func, self.keys()) # type: ignore
1795
+ v = self.values() # type: ignore
1796
+ return self.__class__(zip(k, v))
1604
1797
 
1605
1798
 
1606
1799
  # Run
1607
1800
  ###########################################################################
1608
1801
  if __name__ == "__main__":
1609
1802
  logger.setLevel(10)
1610
- # from rich import print