danielutils 0.9.72__tar.gz → 0.9.73__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 (117) hide show
  1. {danielutils-0.9.72 → danielutils-0.9.73}/PKG-INFO +1 -2
  2. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/__init__.py +2 -1
  3. danielutils-0.9.73/danielutils/classes/frange.py +226 -0
  4. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/classes/repl.py +8 -5
  5. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/classes/typed_builtins/factory.py +2 -2
  6. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/colors.py +6 -6
  7. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/data_structures/Comparer.py +1 -1
  8. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/data_structures/Stack.py +18 -7
  9. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/data_structures/__init__.py +1 -1
  10. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/data_structures/default_dict.py +4 -5
  11. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/decorators/__init__.py +2 -1
  12. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/decorators/attach.py +0 -1
  13. danielutils-0.9.73/danielutils/decorators/decorate_conditionally.py +32 -0
  14. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/decorators/delay_call.py +0 -1
  15. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/decorators/deprecate.py +0 -1
  16. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/decorators/limit_recursion.py +0 -1
  17. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/decorators/memo.py +2 -3
  18. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/decorators/overload.py +31 -19
  19. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/decorators/partially_implemented.py +0 -1
  20. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/decorators/processify.py +3 -3
  21. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/decorators/threadify.py +0 -1
  22. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/decorators/timeout.py +1 -2
  23. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/decorators/validate.py +0 -1
  24. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/files_and_folders.py +7 -0
  25. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/generators/join_generators.py +2 -1
  26. danielutils-0.9.73/danielutils/multi_x.py +26 -0
  27. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/my_tqdm.py +17 -5
  28. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/reflection/__init__.py +1 -1
  29. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/reflection/class_reflection.py +18 -3
  30. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/reflection/file_reflection.py +4 -3
  31. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/reflection/function_reflections.py +51 -65
  32. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/reflection/system_reflections.py +0 -1
  33. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/system/windows/utils/filetime.py +2 -1
  34. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/threads/worker.py +2 -4
  35. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/threads/worker_pool.py +7 -3
  36. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/university/__init__.py +1 -0
  37. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils.egg-info/PKG-INFO +1 -2
  38. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils.egg-info/SOURCES.txt +0 -2
  39. {danielutils-0.9.72 → danielutils-0.9.73}/pyproject.toml +2 -2
  40. danielutils-0.9.72/danielutils/classes/frange.py +0 -125
  41. danielutils-0.9.72/danielutils/decorators/decorate_conditionally.py +0 -33
  42. danielutils-0.9.72/danielutils/multi_x.py +0 -16
  43. danielutils-0.9.72/danielutils/reflection/get_prev_frame.py +0 -30
  44. danielutils-0.9.72/danielutils.egg-info/requires.txt +0 -1
  45. {danielutils-0.9.72 → danielutils-0.9.73}/LICENSE +0 -0
  46. {danielutils-0.9.72 → danielutils-0.9.73}/README.md +0 -0
  47. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/aliases.py +0 -0
  48. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/classes/Convenience.py +0 -0
  49. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/classes/Counter.py +0 -0
  50. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/classes/Tree.py +0 -0
  51. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/classes/__init__.py +0 -0
  52. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/classes/sorted_builtins/__init__.py +0 -0
  53. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/classes/sorted_builtins/sset.py +0 -0
  54. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/classes/typed_builtins/__init__.py +0 -0
  55. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/classes/typed_builtins/tdict.py +0 -0
  56. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/classes/typed_builtins/tlist.py +0 -0
  57. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/classes/typed_builtins/tset.py +0 -0
  58. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/classes/typed_builtins/ttuple.py +0 -0
  59. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/conversions/__init__.py +0 -0
  60. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/conversions/main_conversions.py +0 -0
  61. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/conversions/specialized_conversions/__init__.py +0 -0
  62. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/conversions/specialized_conversions/to_hex.py +0 -0
  63. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/conversions/specialized_conversions/to_int.py +0 -0
  64. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/d_print.py +0 -0
  65. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/data_structures/functions.py +0 -0
  66. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/date.py +0 -0
  67. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/date_time.py +0 -0
  68. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/decorators/atomic.py +0 -0
  69. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/decorators/chain_decorators.py +0 -0
  70. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/decorators/property.py +0 -0
  71. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/exceptions.py +0 -0
  72. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/functions/__init__.py +0 -0
  73. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/functions/areoneof.py +0 -0
  74. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/functions/check_foreach.py +0 -0
  75. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/functions/isoftype.py +0 -0
  76. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/functions/isoneof.py +0 -0
  77. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/functions/powerset.py +0 -0
  78. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/functions/types_subseteq.py +0 -0
  79. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/generators/__init__.py +0 -0
  80. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/generators/conditional_generator.py +0 -0
  81. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/generators/generator_from_stream.py +0 -0
  82. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/internet.py +0 -0
  83. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/loops.py +0 -0
  84. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/math/__init__.py +0 -0
  85. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/math/constants.py +0 -0
  86. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/math/functions.py +0 -0
  87. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/math/math_print.py +0 -0
  88. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/math/math_symbols.py +0 -0
  89. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/metaclasses/Interface.py +0 -0
  90. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/metaclasses/__init__.py +0 -0
  91. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/metaclasses/atomic_class_meta.py +0 -0
  92. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/metaclasses/implicit_data_deleter_meta.py +0 -0
  93. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/metaclasses/instance_cache_meta.py +0 -0
  94. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/metaclasses/overload_meta.py +0 -0
  95. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/path.py +0 -0
  96. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/py.typed +0 -0
  97. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/reflection/get_traceback.py +0 -0
  98. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/reflection/module_reflections.py +0 -0
  99. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/relations.py +0 -0
  100. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/signals.py +0 -0
  101. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/snippets/__init__.py +0 -0
  102. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/snippets/try_get.py +0 -0
  103. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/system/__init__.py +0 -0
  104. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/system/independent.py +0 -0
  105. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/system/windows/__init__.py +0 -0
  106. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/system/windows/utils/__init__.py +0 -0
  107. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/system/windows/win32_ctime.py +0 -0
  108. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/system/windows/windows.py +0 -0
  109. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/text.py +0 -0
  110. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/threads/__init__.py +0 -0
  111. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/time.py +0 -0
  112. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/university/databases/__init__.py +0 -0
  113. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils/university/databases/all.py +0 -0
  114. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils.egg-info/dependency_links.txt +0 -0
  115. {danielutils-0.9.72 → danielutils-0.9.73}/danielutils.egg-info/top_level.txt +0 -0
  116. {danielutils-0.9.72 → danielutils-0.9.73}/setup.cfg +0 -0
  117. {danielutils-0.9.72 → danielutils-0.9.73}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: danielutils
3
- Version: 0.9.72
3
+ Version: 0.9.73
4
4
  Summary: A python utils library for things I find useful
5
5
  Author-email: danielnachumdev <danielnachumdev@gmail.com>
6
6
  License: MIT License
@@ -34,7 +34,6 @@ Classifier: Operating System :: Microsoft :: Windows
34
34
  Requires-Python: >=3.8.0
35
35
  Description-Content-Type: text/markdown
36
36
  License-File: LICENSE
37
- Requires-Dist: tqdm
38
37
 
39
38
 
40
39
  [![Python package](https://github.com/danielnachumdev/danielutils/actions/workflows/python-package.yml/badge.svg)](https://github.com/danielnachumdev/danielutils/actions/workflows/python-package.yml)
@@ -12,7 +12,8 @@ from .signals import *
12
12
  from .aliases import *
13
13
  from .exceptions import PrintCatchOne
14
14
  from .snippets import *
15
-
15
+ from .abstractions import *
16
+ from .imports import *
16
17
  # =================================================================
17
18
  # ========================= ORDER MATTERS =========================
18
19
  # =================================================================
@@ -0,0 +1,226 @@
1
+ import math
2
+ import decimal
3
+ from typing import Callable, Optional, Iterator, Sequence, overload, Union
4
+
5
+
6
+ class frange(Sequence[float]):
7
+ """This class is the same like the builtin range but with float values
8
+ """
9
+
10
+ @staticmethod
11
+ def from_range(r: range) -> 'frange':
12
+ """will "downcast" `range` to `frange` correctly"""
13
+ return frange(r.start, r.stop, r.step)
14
+
15
+ def __init__(self, start: float, stop: Optional[float] = None,
16
+ step: float = 1, round_method: Callable[[float], float] = lambda f: round(f, 3)):
17
+ if stop is None:
18
+ stop = start
19
+ start = 0
20
+ self.start = start
21
+ self.stop = stop
22
+ self.step = step
23
+ self.method = round_method
24
+ self._is_finite = len(self) != float("inf")
25
+
26
+ @property
27
+ def is_finite(self) -> bool:
28
+ """
29
+ Returns `True` if the range is finite
30
+ inverse if `is_infinite`
31
+ Returns:
32
+ bool
33
+ """
34
+ return self._is_finite
35
+
36
+ @property
37
+ def is_infinite(self) -> bool:
38
+ """
39
+ Returns `True` if the range is infinite
40
+ inverse if `is_finite`
41
+ Returns:
42
+ bool
43
+ """
44
+ return not self.is_finite
45
+
46
+ @overload
47
+ def __getitem__(self, index: int) -> float:
48
+ ...
49
+
50
+ @overload
51
+ def __getitem__(self, index: slice) -> 'frange':
52
+ ...
53
+
54
+ def __getitem__(self, index: Union[float, slice]) -> Union[float, 'frange']:
55
+ if isinstance(index, slice):
56
+ index = slice(
57
+ index.start if index.start is not None else 0,
58
+ index.stop if index.stop is not None else len(self),
59
+ index.step if index.step is not None else 1,
60
+ )
61
+ if index.step > 0:
62
+ step = self.step * index.step
63
+ start = self.start + step * index.start
64
+ stop = self.start + step * index.stop
65
+ return frange(start, stop, step)
66
+ s = slice(index.start, index.stop, abs(index.step))
67
+ return reversed(self[s])
68
+ if index < 0:
69
+ raise ValueError(f"At {self.__class__.__qualname__}.__getitem__ 'index' must be a positive integer")
70
+ return self.start + self.step * index
71
+
72
+ def __reversed__(self) -> 'frange':
73
+ return frange(self.stop - 1, self.start - 1, -self.step)
74
+
75
+ def __eq__(self, other):
76
+ if not isinstance(other, frange):
77
+ raise NotImplementedError
78
+ return self.start == other.start and self.stop == other.stop and self.step == other.step
79
+
80
+ def __iter__(self) -> Iterator[float]:
81
+ if self.stop < self.start:
82
+ return
83
+ if self.start > self.stop:
84
+ return
85
+ if abs(self.stop - self.start) < abs(self.step):
86
+ return
87
+ if self.stop > 0 and self.step < 0:
88
+ return
89
+ if self.stop < 0 and self.step > 0:
90
+ return
91
+
92
+ cur = self.start
93
+ while cur < self.stop:
94
+ yield self.method(cur)
95
+ cur += self.step
96
+
97
+ def __len__(self) -> int:
98
+ if self.stop in {float("inf"), -float("inf")}:
99
+ return float("inf")
100
+ return int((self.stop - self.start) // self.step)
101
+
102
+ def __str__(self) -> str:
103
+ return repr(self)
104
+
105
+ def __repr__(self):
106
+ return f"{self.__class__.__name__}({self.start}, {self.stop}, {self.step})"
107
+
108
+ @staticmethod
109
+ def _is_int(n: Union[int, float]) -> bool:
110
+ if isinstance(n, int):
111
+ return True
112
+
113
+ return n.is_integer()
114
+
115
+ def __contains__(self, item):
116
+ if item < self.start:
117
+ return False
118
+ if item >= self.stop:
119
+ return False
120
+
121
+ if frange._is_int(self.step):
122
+ if not frange._is_int(item):
123
+ return False
124
+
125
+ return item / self.step - item // self.step == 0
126
+
127
+ def normalize(self) -> 'frange':
128
+ """
129
+ will normalize the `frange` object
130
+ Returns:
131
+ frange
132
+ """
133
+ return frange(self.start / self.step, self.stop / self.step, 1)
134
+
135
+ # def _normalize_with(self, other: 'frange') -> 'frange':
136
+ # return frange(self.start / (self.step * other.step), self.stop / (self.step * other.step), 1)
137
+
138
+ @staticmethod
139
+ def _lcm_float(a: float, b: float) -> float:
140
+ prec = min(5, max(decimal.getcontext().prec, 10))
141
+ a = round(a, prec)
142
+ b = round(b, prec)
143
+ return math.lcm(int(a * 10 ** prec), int(b * 10 ** prec)) / 10 ** prec
144
+
145
+ @staticmethod
146
+ def _find_min_step(s1: float, s2: float) -> float:
147
+ """
148
+ returns the minimum LCM for two step values
149
+ Args:
150
+ s1 (float): first step value:
151
+ s2 (float): second step value:
152
+
153
+ Returns:
154
+ float: minimum LCM
155
+ """
156
+ M = max(s1, s2)
157
+ m = min(s1, s2)
158
+ if float.is_integer(M / m):
159
+ return M
160
+ return frange._lcm_float(s1, s2)
161
+
162
+ def intersect(self, other: 'frange') -> 'frange':
163
+ if not isinstance(other, frange):
164
+ raise ValueError("frange.intercept only accepts frange objects")
165
+ a, b = self.normalize(), other.normalize()
166
+ start1, stop1 = a.start, a.stop
167
+ start2, stop2 = b.start, b.stop
168
+ remainder1, remainder2 = start1 - int(start1), start2 - int(start2)
169
+ start = max(self.start, other.start)
170
+ stop = min(self.stop, other.stop)
171
+ if remainder1 == remainder2:
172
+ min_step = self._find_min_step(self.step, other.step)
173
+ if stop1 == float("inf") or stop2 == float("inf"):
174
+ return frange(start, float("inf"), min_step)
175
+ return frange(start, stop, min_step)
176
+ # find k; start1 + remainder1*k == start2 +remainder2*k
177
+ k = (start1 - start2) / (remainder2 - remainder1)
178
+ if k <= 0:
179
+ return frange(0)
180
+ if stop1 == float("inf") or stop2 == float("inf"):
181
+ raise NotImplementedError("this part is not implemented yet. one has inf")
182
+ raise NotImplementedError("this part is not implemented yet")
183
+
184
+
185
+ class frange_iterator(Iterator[float]):
186
+ def __init__(self, obj: frange):
187
+ self.r = obj
188
+
189
+ def __next__(self):
190
+ if self.r.stop < self.r.start:
191
+ return
192
+ if self.r.start > self.r.stop:
193
+ return
194
+ if abs(self.r.stop - self.r.start) < abs(self.r.step):
195
+ return
196
+ if self.r.stop > 0 and self.r.step < 0:
197
+ return
198
+ if self.r.stop < 0 and self.r.step > 0:
199
+ return
200
+
201
+ cur = self.r.start
202
+ while cur < self.r.stop:
203
+ yield self.r.method(cur)
204
+ cur += self.r.step
205
+
206
+ def __iter__(self):
207
+ return self
208
+
209
+
210
+ class brange(frange):
211
+ """like frange but with tqdm
212
+ """
213
+
214
+ def __iter__(self):
215
+ itr = super().__iter__()
216
+ try:
217
+ from my_tqdm import tqdm # type:ignore # pylint: disable=import-error
218
+ return iter(tqdm(itr, desc=f"{self}", total=len(self)))
219
+ except:
220
+ return itr
221
+
222
+
223
+ __all__ = [
224
+ "frange",
225
+ "brange"
226
+ ]
@@ -1,6 +1,8 @@
1
+ import copy
1
2
  from typing import Any, Callable, Union, Tuple as t_tuple, List as t_list, Dict as t_dict
2
3
  import re
3
- from ..reflection import get_python_version
4
+ from ..reflection import get_python_version # pylint :disable=relative-beyond-top-level
5
+
4
6
  if get_python_version() >= (3, 9):
5
7
  from builtins import tuple as t_tuple, list as t_list, dict as t_dict # type:ignore
6
8
 
@@ -39,9 +41,10 @@ class REPL:
39
41
  """a class to easily create a shell application and get functionality for free
40
42
  """
41
43
 
44
+ # pylint: disable=dangerous-default-value
42
45
  def __init__(self, routes: t_list[Command], *, prompt_symbol: str = ">>> ", exit_keywords: set = {"exit", "quit"}):
43
46
  self.prompt_symbol = prompt_symbol
44
- self.exit_keywords = exit_keywords
47
+ self.exit_keywords = copy.copy(exit_keywords)
45
48
  self.routes: t_dict[str, Command] = {
46
49
  com.command.name: com for com in routes}
47
50
 
@@ -58,7 +61,7 @@ class REPL:
58
61
 
59
62
  if prompt == "help":
60
63
  print("Available commands:")
61
- for com in list(self.routes.keys())+list(self.exit_keywords):
64
+ for com in list(self.routes.keys()) + list(self.exit_keywords):
62
65
  print(f"\t{com}")
63
66
  continue
64
67
 
@@ -70,9 +73,9 @@ class REPL:
70
73
  except TypeError as e:
71
74
  msg = str(e)
72
75
  if re.match(r".*missing.*required.*argument.*", msg):
73
- print(f"'{command}' "+msg[msg.find("missing"):])
76
+ print(f"'{command}' " + msg[msg.find("missing"):])
74
77
  elif re.match(r".*takes.*arguments but.*given", msg):
75
- print(f"'{command}' "+msg[msg.find("takes"):])
78
+ print(f"'{command}' " + msg[msg.find("takes"):])
76
79
  else:
77
80
  raise e
78
81
 
@@ -3,6 +3,7 @@ from abc import abstractmethod
3
3
  from typing import Any, Iterable, List as t_list, Set as t_set, Dict as t_dict, Tuple as t_tuple
4
4
  from ...functions import types_subseteq, isoftype
5
5
  from ...reflection import get_caller_name, get_python_version
6
+
6
7
  if get_python_version() >= (3, 9):
7
8
  from builtins import list as t_list, set as t_set, dict as t_dict, tuple as t_tuple # type:ignore
8
9
  # needed for python 3.8
@@ -39,7 +40,6 @@ def create_typed_class(name: str, fallback_class: type = object) -> type:
39
40
 
40
41
  def __instancecheck__(self, instance: Any) -> bool:
41
42
  if isinstance(instance, cls):
42
-
43
43
  return types_subseteq(
44
44
  instance.get_params(), # type:ignore
45
45
  self.get_params()
@@ -51,7 +51,7 @@ def create_typed_class(name: str, fallback_class: type = object) -> type:
51
51
  )
52
52
 
53
53
  def __init__(self, item) -> None:
54
- if not get_caller_name(0) == "__class_getitem__":
54
+ if not get_caller_name(1) == "__class_getitem__":
55
55
  raise ValueError(
56
56
  f"Can't instantiate {self.__class__.__name__} without a supplied type")
57
57
  fallback_class.__init__(self) # type: ignore
@@ -1,7 +1,6 @@
1
1
  from typing import Optional, IO
2
2
  from .decorators import validate
3
3
 
4
-
5
4
  RESET = "\033[0m"
6
5
 
7
6
 
@@ -16,15 +15,16 @@ class ColoredText:
16
15
  white,
17
16
  black
18
17
  """
19
- @staticmethod
18
+
19
+ @staticmethod # type:ignore
20
20
  @validate
21
21
  def from_rgb(red: int, green: int, blue: int, text: str) -> str:
22
22
  """Applies an RGB color to the given text.
23
23
 
24
24
  Args:
25
- r (int): The red component of the color.
26
- g (int): The green component of the color.
27
- b (int): The blue component of the color.
25
+ red (int): The red component of the color.
26
+ green (int): The green component of the color.
27
+ blue (int): The blue component of the color.
28
28
  text (str): The text to apply the color to.
29
29
 
30
30
  Returns:
@@ -178,7 +178,7 @@ def error(*args, sep: str = " ", end: str = "\n"):
178
178
 
179
179
 
180
180
  def info(*args, sep: str = " ", end: str = "\n"):
181
- """print an error message
181
+ """print an info message
182
182
 
183
183
  Args:
184
184
  sep (str, optional): print separator. Defaults to " ".
@@ -1,5 +1,5 @@
1
1
  """Comparer class"""
2
- from typing import Callable, Any, Union, Generic, TypeVar
2
+ from typing import Callable, Union, Generic, TypeVar
3
3
  from .functions import default_weight_function
4
4
 
5
5
  U = TypeVar("U")
@@ -1,4 +1,4 @@
1
- from typing import Optional, Generator, Any, cast, TypeVar, Generic
1
+ from typing import Optional, Generator, TypeVar, Generic
2
2
  from .graph import Node
3
3
 
4
4
  T = TypeVar('T')
@@ -12,7 +12,7 @@ class Stack(Generic[T]):
12
12
  self.head: Optional[Node[T]] = None
13
13
  self.size = 0
14
14
 
15
- def push(self, value: Node[T]):
15
+ def push(self, value: T):
16
16
  """push an item to the stack
17
17
 
18
18
  Args:
@@ -25,23 +25,33 @@ class Stack(Generic[T]):
25
25
  self.head = new_head
26
26
  self.size += 1
27
27
 
28
- def pop(self) -> Node[T]:
28
+ def pop(self) -> T:
29
29
  """pop an item from the stack
30
30
 
31
31
  Returns:
32
32
  Any: poped item
33
33
  """
34
34
  if not self.is_empty():
35
- self.head = cast(Node, self.head)
36
- res = self.head.data
35
+ res = self.head.data # type:ignore
37
36
  self.size -= 1
38
- self.head = self.head.next
37
+ self.head = self.head.next # type:ignore
39
38
  return res
39
+ raise RuntimeError("Can't pop from an empty stack")
40
+
41
+ def peek(self) -> Optional[T]:
42
+ """
43
+ Returns the top element of the stack
44
+ Returns:
45
+ Optional[T]
46
+ """
47
+ if self.is_empty():
48
+ return None
49
+ return self.head.data
40
50
 
41
51
  def __len__(self) -> int:
42
52
  return self.size
43
53
 
44
- def __iter__(self) -> Generator[Node[T], None, None]:
54
+ def __iter__(self) -> Generator[T, None, None]:
45
55
  while self:
46
56
  yield self.pop()
47
57
 
@@ -58,6 +68,7 @@ class Stack(Generic[T]):
58
68
  while curr is not None:
59
69
  if curr.data == value:
60
70
  return True
71
+ curr = curr.next
61
72
  return False
62
73
 
63
74
  def __str__(self) -> str:
@@ -4,4 +4,4 @@ from .comparer import *
4
4
  from .graph import *
5
5
  from .stack import *
6
6
  from .default_dict import *
7
- from .trees import *
7
+ from .trees import *
@@ -1,9 +1,8 @@
1
- """
2
- My implementation to collections.defaultdict
3
- """
4
-
5
-
6
1
  class DefaultDict(dict):
2
+ """
3
+ My implementation to `collections.defaultdict`
4
+ """
5
+
7
6
  def __init__(self, cls):
8
7
  self._cls = cls
9
8
 
@@ -12,4 +12,5 @@ from .threadify import *
12
12
  from .delay_call import *
13
13
  from .deprecate import *
14
14
  from .processify import *
15
- from .singleton import *
15
+ from .singleton import *
16
+ from .total_ordering import *
@@ -1,6 +1,5 @@
1
1
  import functools
2
2
  from typing import Callable, Optional, TypeVar
3
- import platform
4
3
  from .validate import validate
5
4
  from ..reflection import get_python_version
6
5
  if get_python_version() < (3, 9):
@@ -0,0 +1,32 @@
1
+ import functools
2
+ from typing import Callable, Optional, Union
3
+ from .validate import validate
4
+
5
+
6
+ @validate(strict=False)
7
+ def decorate_conditionally(decorator: Callable, predicate: Union[bool, Callable[[], bool]], *,
8
+ decorator_args: Optional[list] = None, decorator_kwargs: Optional[dict] = None):
9
+ """will decorate a function iff the predicate is True or returns True
10
+
11
+ Args:
12
+ decorator (Callable): the decorator to use
13
+ predicate (bool | Callable[[], bool]): the predicate
14
+ """
15
+
16
+ def deco(func):
17
+ if (predicate() if callable(predicate) else predicate):
18
+ nonlocal decorator_args, decorator_kwargs, decorator
19
+ if decorator_args is None:
20
+ decorator_args = []
21
+ if decorator_kwargs is None:
22
+ decorator_kwargs = {}
23
+ decorator = functools.wraps(func)(decorator)
24
+ return decorator(*decorator_args, **decorator_kwargs)(func)
25
+ return func
26
+
27
+ return deco
28
+
29
+
30
+ __all__ = [
31
+ "decorate_conditionally"
32
+ ]
@@ -1,5 +1,4 @@
1
1
  from typing import Callable, TypeVar, Union
2
- import platform
3
2
  import time
4
3
  import functools
5
4
  from .decorate_conditionally import decorate_conditionally
@@ -1,4 +1,3 @@
1
- import platform
2
1
  from typing import Callable, TypeVar
3
2
  from ..colors import warning, ColoredText
4
3
 
@@ -1,7 +1,6 @@
1
1
  import functools
2
2
  import re
3
3
  import traceback
4
- import platform
5
4
  from typing import Any, Callable, TypeVar
6
5
  from .validate import validate
7
6
  from ..colors import warning
@@ -1,6 +1,6 @@
1
- from typing import Callable, Any, TypeVar, Dict as t_dict
2
1
  import functools
3
- import platform
2
+ from typing import Callable, Any, TypeVar, Dict as t_dict
3
+ from copy import deepcopy
4
4
  from .validate import validate
5
5
 
6
6
  from ..reflection import get_python_version
@@ -9,7 +9,6 @@ if get_python_version() < (3, 9):
9
9
  else:
10
10
  from builtins import dict as t_dict
11
11
  from typing import ParamSpec # type:ignore # pylint: disable=ungrouped-imports
12
- from copy import deepcopy
13
12
  T = TypeVar("T")
14
13
  P = ParamSpec("P")
15
14
  FuncT = Callable[P, T] # type:ignore
@@ -1,12 +1,12 @@
1
1
  from typing import Callable, cast, Any, TypeVar, Dict as t_dict, List as t_list
2
2
  import inspect
3
- import platform
4
3
  import functools
5
4
  from ..reflection import is_function_annotated_properly
6
5
  from ..functions import isoftype, isoneof, isoneof_strict
7
6
  from ..exceptions import OverloadDuplication, OverloadNotFound
8
7
  from .deprecate import deprecate
9
8
  from ..reflection import get_python_version
9
+
10
10
  if get_python_version() < (3, 9):
11
11
  from typing_extensions import ParamSpec
12
12
  else:
@@ -79,7 +79,7 @@ def explicit_global_overload(*types) -> Callable:
79
79
 
80
80
  __overload_dict[name][types] = func
81
81
 
82
- @ functools.wraps(func)
82
+ @functools.wraps(func)
83
83
  def wrapper(*args, **kwargs) -> Any:
84
84
  default_func = None
85
85
  # select correct overload
@@ -112,6 +112,7 @@ def explicit_global_overload(*types) -> Callable:
112
112
  f"function {func.__module__}.{func.__qualname__} is not overloaded with {[type(v) for v in args]}")
113
113
 
114
114
  return wrapper
115
+
115
116
  return deco
116
117
 
117
118
 
@@ -160,31 +161,42 @@ class overload:
160
161
  return self
161
162
 
162
163
  def __call__(self, *args, **kwargs):
163
- num_args = len(args)+len(kwargs.keys())
164
+ num_args = len(args) + len(kwargs.keys())
164
165
  if num_args not in self._functions:
165
166
  raise AttributeError(
166
167
  f"No overload with {num_args} argument found for {self._moudle}.{self._qualname}")
167
- selected_func = None
168
- if len(self._functions[num_args]) == 1:
169
- selected_func = self._functions[num_args][0]
170
- else:
171
- for func in self._functions[num_args]:
172
- signature = inspect.signature(func)
173
- for i, tup in enumerate(signature.parameters.items()):
174
- param_name, param_type = tup
175
- if param_name in overload.__SKIP_SET:
176
- continue
177
168
 
178
- if not isoftype(args[i], param_type.annotation):
179
- break
169
+ if num_args == 0:
170
+ return self._functions[num_args][0](*args, **kwargs)
171
+
172
+ max_score = 0
173
+ winner = self._functions[num_args][0]
174
+ EXACT_MATCH = 1 / num_args
175
+ SUBCLASS = 1 / num_args
176
+ for func in self._functions[num_args]:
177
+ score = 0
178
+ signature = inspect.signature(func)
179
+ for i, tup in enumerate(signature.parameters.items()):
180
+ param_name, param_type = tup
181
+ if param_name in overload.__SKIP_SET:
182
+ continue
183
+
184
+ if type(args[i]) == param_type.annotation: # pylint :disable=unidiomatic-typecheck
185
+ score += EXACT_MATCH
186
+
187
+ elif isoftype(args[i], param_type.annotation):
188
+ score += SUBCLASS
180
189
  else:
181
- # reaching here means current function matches perfectly the annotation
182
- selected_func = func
183
190
  break
191
+
184
192
  else:
185
- raise AttributeError("No overload found")
193
+ # reaching here means current function matches perfectly the annotation
194
+ if score > max_score:
195
+ max_score = score
196
+ winner = func
197
+ # raise AttributeError("No overload found")
186
198
 
187
- return selected_func(*args, **kwargs)
199
+ return winner(*args, **kwargs)
188
200
 
189
201
 
190
202
  __all__ = [
@@ -1,6 +1,5 @@
1
1
  from typing import Callable, Any, TypeVar
2
2
  import functools
3
- import platform
4
3
  from .validate import validate
5
4
  from ..colors import warning
6
5
 
@@ -2,7 +2,7 @@ import inspect
2
2
  import functools
3
3
  import multiprocessing
4
4
 
5
- from ..reflection import get_n_caller_func, get_current_frame, get_n_prev_frame
5
+ from ..reflection import get_prev_frame
6
6
  from ..multi_x import process_id
7
7
  import pickle
8
8
 
@@ -24,7 +24,7 @@ def processify(func):
24
24
  def wrapper(*args, **kwargs):
25
25
  main_pid = kwargs.get("__main_pid", process_id())
26
26
  if process_id() == main_pid:
27
- frame = get_n_prev_frame(2)
27
+ frame = get_prev_frame(2)
28
28
  dct = {k: v for k, v in frame.f_globals.items() if type(v) != type(inspect)}
29
29
  p = multiprocessing.Process(target=_run_func, args=(main_pid, dct, func.__name__, args, kwargs))
30
30
  p.start()
@@ -41,7 +41,7 @@ def _run_func(main_pid: int, dct: dict, func_name: str, args, kwargs) -> None:
41
41
  return dct[func_name](*args, __main_pid=main_pid, **kwargs)
42
42
 
43
43
  def debug_info(include_builtins: bool = False) -> dict:
44
- f = get_n_prev_frame(2)
44
+ f = get_prev_frame(2)
45
45
  g = {k: v for k, v in f.f_globals.items() if k != "__builtins__"} if not include_builtins else dict(f.f_globals)
46
46
  return {
47
47
  "file": f.f_code.co_filename,
@@ -1,5 +1,4 @@
1
1
  from typing import Callable, TypeVar
2
- import platform
3
2
  import functools
4
3
  import threading
5
4