typemore 0.1.0__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.
- typemore/_PackageTools.py +12 -0
- typemore/__init__.py +36 -0
- typemore/py.typed +0 -0
- typemore/types/__init__.py +0 -0
- typemore/types/auto.py +119 -0
- typemore/types/chainNode.py +293 -0
- typemore/types/debug.py +239 -0
- typemore/types/displayProxy.py +264 -0
- typemore/types/infiniteIterator.py +52 -0
- typemore/types/interval.py +358 -0
- typemore/types/lenLimit.py +105 -0
- typemore/types/memLimit.py +167 -0
- typemore/types/point2D.py +131 -0
- typemore/types/point3D.py +133 -0
- typemore/types/pointAnyD.py +201 -0
- typemore/types/range.py +271 -0
- typemore/types/readOnlyBox.py +66 -0
- typemore/types/strictFloat.py +164 -0
- typemore/types/timeLimit.py +66 -0
- typemore-0.1.0.dist-info/METADATA +44 -0
- typemore-0.1.0.dist-info/RECORD +23 -0
- typemore-0.1.0.dist-info/WHEEL +4 -0
- typemore-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
3
|
+
def private(func):
|
|
4
|
+
def wrapper(*args, **kwargs):
|
|
5
|
+
caller_qualname = sys._getframe(1).f_code.co_qualname
|
|
6
|
+
method_qualname = func.__qualname__
|
|
7
|
+
parent = method_qualname[:method_qualname.rfind('.')]
|
|
8
|
+
|
|
9
|
+
if not caller_qualname.startswith(parent):
|
|
10
|
+
raise RuntimeError(f"{method_qualname} is private")
|
|
11
|
+
return func(*args, **kwargs)
|
|
12
|
+
return wrapper
|
typemore/__init__.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from .types.range import Range
|
|
2
|
+
from .types.point2D import Point2D
|
|
3
|
+
from .types.point3D import Point3D
|
|
4
|
+
from .types.pointAnyD import PointAnyD
|
|
5
|
+
from .types.auto import Auto
|
|
6
|
+
from .types.debug import Debug
|
|
7
|
+
from .types.strictFloat import StrictFloat
|
|
8
|
+
from .types.interval import Interval
|
|
9
|
+
from .types.chainNode import ChainNode
|
|
10
|
+
from .types.infiniteIterator import InfiniteIterator
|
|
11
|
+
from .types.readOnlyBox import ReadOnlyBox
|
|
12
|
+
from .types.displayProxy import DisplayProxy
|
|
13
|
+
from .types.lenLimit import LenLimit, LenLimitError
|
|
14
|
+
from .types.timeLimit import TimeLimit, TimeLimitError
|
|
15
|
+
from .types.memLimit import MemLimit, MemLimitError
|
|
16
|
+
|
|
17
|
+
__version__ = "0.1.0"
|
|
18
|
+
__author__ = "maoshen"
|
|
19
|
+
__all__ = ["Range",
|
|
20
|
+
"Point2D",
|
|
21
|
+
"Point3D",
|
|
22
|
+
"PointAnyD",
|
|
23
|
+
"Auto",
|
|
24
|
+
"Debug",
|
|
25
|
+
"StrictFloat",
|
|
26
|
+
"Interval",
|
|
27
|
+
"ChainNode",
|
|
28
|
+
"InfiniteIterator",
|
|
29
|
+
"ReadOnlyBox",
|
|
30
|
+
"DisplayProxy",
|
|
31
|
+
"LenLimit",
|
|
32
|
+
"LenLimitError",
|
|
33
|
+
"TimeLimit",
|
|
34
|
+
"TimeLimitError",
|
|
35
|
+
"MemLimit",
|
|
36
|
+
"MemLimitError"]
|
typemore/py.typed
ADDED
|
File without changes
|
|
File without changes
|
typemore/types/auto.py
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
class Auto:
|
|
2
|
+
"""
|
|
3
|
+
This class is compatible with most commonly used type-conversion functions.
|
|
4
|
+
It prevents exceptions for conversion operations that would normally raise them.
|
|
5
|
+
"""
|
|
6
|
+
_cache = {}
|
|
7
|
+
_hash = None
|
|
8
|
+
|
|
9
|
+
def __new__(cls, auto):
|
|
10
|
+
base = type(auto)
|
|
11
|
+
if base not in cls._cache:
|
|
12
|
+
if base in (bool, type(None)):
|
|
13
|
+
bases = (cls,)
|
|
14
|
+
else:
|
|
15
|
+
bases = (cls, base)
|
|
16
|
+
cls._cache[base] = type(
|
|
17
|
+
f"{cls.__name__}[{base.__name__}]",
|
|
18
|
+
bases,
|
|
19
|
+
{}
|
|
20
|
+
)
|
|
21
|
+
new_cls = cls._cache[base]
|
|
22
|
+
|
|
23
|
+
if auto is None or base is bool:
|
|
24
|
+
instance = super().__new__(new_cls)
|
|
25
|
+
elif issubclass(base, (int, float, complex, str, tuple, frozenset, bytes)):
|
|
26
|
+
instance = base.__new__(new_cls, auto)
|
|
27
|
+
elif issubclass(base, (list, dict, set, bytearray)):
|
|
28
|
+
instance = base.__new__(new_cls)
|
|
29
|
+
base.__init__(instance, auto)
|
|
30
|
+
else:
|
|
31
|
+
instance = base.__new__(new_cls)
|
|
32
|
+
return instance
|
|
33
|
+
|
|
34
|
+
def __init__(self, auto):
|
|
35
|
+
self.auto = self
|
|
36
|
+
self._hash = None
|
|
37
|
+
try:
|
|
38
|
+
hash(auto)
|
|
39
|
+
self._hash = True
|
|
40
|
+
except TypeError:
|
|
41
|
+
self._hash = False
|
|
42
|
+
|
|
43
|
+
def __repr__(self):
|
|
44
|
+
return f"{self.__class__.__name__}:{super().__repr__()}"
|
|
45
|
+
|
|
46
|
+
def __getattr__(self, name):
|
|
47
|
+
raise AttributeError(name)
|
|
48
|
+
|
|
49
|
+
def __dir__(self):
|
|
50
|
+
return list(set(dir(self.__class__) + dir(self.__class__.__mro__[1]) + ['auto']))
|
|
51
|
+
|
|
52
|
+
def __int__(self):
|
|
53
|
+
if not self._hash:
|
|
54
|
+
return len(self)
|
|
55
|
+
if isinstance(self, bool):
|
|
56
|
+
return 1 if bool.__bool__(self) else 0
|
|
57
|
+
if isinstance(self, int):
|
|
58
|
+
return int.__int__(self)
|
|
59
|
+
if isinstance(self, float):
|
|
60
|
+
return int(float.__int__(self))
|
|
61
|
+
if isinstance(self, complex):
|
|
62
|
+
return int(self.real)
|
|
63
|
+
if isinstance(self, str):
|
|
64
|
+
try:
|
|
65
|
+
return int(str.__str__(self))
|
|
66
|
+
except ValueError:
|
|
67
|
+
return 1 if str.__len__(self) else 0
|
|
68
|
+
return 0
|
|
69
|
+
|
|
70
|
+
def __float__(self) -> float:
|
|
71
|
+
return float(self.__int__())
|
|
72
|
+
|
|
73
|
+
def __complex__(self) -> complex:
|
|
74
|
+
if self._hash:
|
|
75
|
+
if isinstance(self.auto, complex):
|
|
76
|
+
return complex(self.real, self.imag)
|
|
77
|
+
elif isinstance(self.auto, int | float):
|
|
78
|
+
return complex(self.auto, 0j)
|
|
79
|
+
elif isinstance(self.auto, bool):
|
|
80
|
+
if self.auto:
|
|
81
|
+
real = 1
|
|
82
|
+
else:
|
|
83
|
+
real = 0
|
|
84
|
+
return complex(real, 0j)
|
|
85
|
+
elif isinstance(self.auto, str):
|
|
86
|
+
try:
|
|
87
|
+
return complex(self.auto)
|
|
88
|
+
except ValueError:
|
|
89
|
+
if self.auto.strip() == '':
|
|
90
|
+
return 0
|
|
91
|
+
else:
|
|
92
|
+
return 1
|
|
93
|
+
return complex(0)
|
|
94
|
+
else:
|
|
95
|
+
return complex(self.__int__(), 0j)
|
|
96
|
+
|
|
97
|
+
def __index__(self) -> int:
|
|
98
|
+
if self._hash:
|
|
99
|
+
if isinstance(self.auto, int | float | bool):
|
|
100
|
+
return int(self.auto)
|
|
101
|
+
elif isinstance(self.auto, str):
|
|
102
|
+
try:
|
|
103
|
+
return int(self.auto)
|
|
104
|
+
except ValueError:
|
|
105
|
+
if self.auto.strip() == '':
|
|
106
|
+
return 0
|
|
107
|
+
else:
|
|
108
|
+
return 1
|
|
109
|
+
elif isinstance(self.auto, complex):
|
|
110
|
+
return int(self.auto.real)
|
|
111
|
+
return 0
|
|
112
|
+
else:
|
|
113
|
+
return self.__int__()
|
|
114
|
+
|
|
115
|
+
def __iter__(self):
|
|
116
|
+
auto = self.auto
|
|
117
|
+
if isinstance(auto, (int, float, complex, bool, bytes)):
|
|
118
|
+
return iter([auto])
|
|
119
|
+
return iter(auto)
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
class PointerNone:
|
|
4
|
+
pass
|
|
5
|
+
|
|
6
|
+
point_none = PointerNone()
|
|
7
|
+
|
|
8
|
+
class ChainNode:
|
|
9
|
+
"""
|
|
10
|
+
A singly linked list node whose class also inherits from type(value).
|
|
11
|
+
|
|
12
|
+
Each instance stores a value and a pointer to the next node. The class
|
|
13
|
+
of an instance is generated dynamically based on the type of value, so
|
|
14
|
+
a node can also use the methods and operators of its underlying type.
|
|
15
|
+
For example, ChainNode(5) inherits from int, and ChainNode("a") inherits
|
|
16
|
+
from str.
|
|
17
|
+
|
|
18
|
+
The end of a chain is marked by point_none, a sentinel instance of
|
|
19
|
+
PointerNone. Use "pointer is point_none" to test for the end.
|
|
20
|
+
"""
|
|
21
|
+
_cache = {}
|
|
22
|
+
|
|
23
|
+
def __new__(cls, value, pointer = point_none):
|
|
24
|
+
base = type(value)
|
|
25
|
+
if base not in cls._cache:
|
|
26
|
+
if base in (bool, type(None)):
|
|
27
|
+
bases = (cls,)
|
|
28
|
+
else:
|
|
29
|
+
bases = (cls, base)
|
|
30
|
+
cls._cache[base] = type(
|
|
31
|
+
f"{cls.__name__}[{base.__name__}]",
|
|
32
|
+
bases,
|
|
33
|
+
{}
|
|
34
|
+
)
|
|
35
|
+
new_cls = cls._cache[base]
|
|
36
|
+
if value is None or base is bool:
|
|
37
|
+
instance = super().__new__(new_cls)
|
|
38
|
+
return instance
|
|
39
|
+
elif issubclass(base, (int, float, str, tuple, frozenset, bytes)):
|
|
40
|
+
instance= base.__new__(new_cls, value)
|
|
41
|
+
return instance
|
|
42
|
+
else:
|
|
43
|
+
instance = base.__new__(new_cls)
|
|
44
|
+
return instance
|
|
45
|
+
|
|
46
|
+
def __init__(self, value, pointer = point_none):
|
|
47
|
+
self.value = value
|
|
48
|
+
self.pointer = pointer
|
|
49
|
+
|
|
50
|
+
def __repr__(self) -> str:
|
|
51
|
+
return f"Pointer({self.value}, {self.pointer if self.pointer is not point_none else ''})"
|
|
52
|
+
|
|
53
|
+
def __str__(self) -> str:
|
|
54
|
+
return f"({self.value}, {self.pointer if self.pointer is not point_none else ''})"
|
|
55
|
+
|
|
56
|
+
def getvalue_number(self, number: int):
|
|
57
|
+
"""
|
|
58
|
+
Return the value of the node at index number, where 0 is self.
|
|
59
|
+
|
|
60
|
+
Raises ValueError if number is not a non-negative int, and IndexError
|
|
61
|
+
if the chain ends before reaching that index.
|
|
62
|
+
"""
|
|
63
|
+
if not isinstance(number, int) or number < 0:
|
|
64
|
+
raise ValueError("index out of range")
|
|
65
|
+
node = self
|
|
66
|
+
for _ in range(number):
|
|
67
|
+
if not isinstance(node.pointer, ChainNode):
|
|
68
|
+
raise IndexError("index out of range")
|
|
69
|
+
node = node.pointer
|
|
70
|
+
return node.value
|
|
71
|
+
|
|
72
|
+
def getpointer_number(self, number: int):
|
|
73
|
+
"""
|
|
74
|
+
Return the node at index number, where 0 is self.
|
|
75
|
+
|
|
76
|
+
Raises ValueError if number is not a non-negative int, and IndexError
|
|
77
|
+
if the chain ends before reaching that index.
|
|
78
|
+
"""
|
|
79
|
+
if not isinstance(number, int) or number < 0:
|
|
80
|
+
raise ValueError("index out of range")
|
|
81
|
+
node = self
|
|
82
|
+
for _ in range(number):
|
|
83
|
+
if not isinstance(node.pointer, ChainNode):
|
|
84
|
+
raise IndexError("index out of range")
|
|
85
|
+
node = node.pointer
|
|
86
|
+
return node
|
|
87
|
+
|
|
88
|
+
def getpointer_value(self, value):
|
|
89
|
+
"""
|
|
90
|
+
Return the pointer of the first node whose value equals value.
|
|
91
|
+
|
|
92
|
+
Raises ValueError if no such node exists.
|
|
93
|
+
"""
|
|
94
|
+
node = self._find_node(value)
|
|
95
|
+
if node is None:
|
|
96
|
+
raise ValueError("value not found in chain")
|
|
97
|
+
return node.pointer
|
|
98
|
+
|
|
99
|
+
def setpointer_number(self, number: int, pointer) -> None:
|
|
100
|
+
"""
|
|
101
|
+
Set the pointer of the node at index number, where 0 is self.
|
|
102
|
+
|
|
103
|
+
Raises ValueError if number is not a non-negative int, and IndexError
|
|
104
|
+
if the chain ends before reaching that index.
|
|
105
|
+
"""
|
|
106
|
+
if not isinstance(number, int) or number < 0:
|
|
107
|
+
raise ValueError("index out of range")
|
|
108
|
+
node = self
|
|
109
|
+
for _ in range(number):
|
|
110
|
+
if not isinstance(node, ChainNode):
|
|
111
|
+
raise IndexError("index out of range")
|
|
112
|
+
node = node.pointer
|
|
113
|
+
if not isinstance(node, ChainNode):
|
|
114
|
+
raise IndexError("index out of range")
|
|
115
|
+
node.pointer = pointer
|
|
116
|
+
|
|
117
|
+
def setpointer_value(self, value, pointer) -> None:
|
|
118
|
+
"""
|
|
119
|
+
Set the pointer of the first node whose value equals value.
|
|
120
|
+
|
|
121
|
+
Raises ValueError if no such node exists.
|
|
122
|
+
"""
|
|
123
|
+
node = self._find_node(value)
|
|
124
|
+
if node is None:
|
|
125
|
+
raise ValueError("value not found in chain")
|
|
126
|
+
node.pointer = pointer
|
|
127
|
+
|
|
128
|
+
def setvalue_number(self, number: int, value) -> None:
|
|
129
|
+
"""
|
|
130
|
+
Set the value attribute of the node at index number, where 0 is self.
|
|
131
|
+
|
|
132
|
+
For immutable subclasses such as ChainNode[int], the instance's own
|
|
133
|
+
numeric value does not change; only the value attribute is updated.
|
|
134
|
+
Raises ValueError if number is not a non-negative int, and IndexError
|
|
135
|
+
if the chain ends before reaching that index.
|
|
136
|
+
"""
|
|
137
|
+
if not isinstance(number, int) or number < 0:
|
|
138
|
+
raise ValueError("index out of range")
|
|
139
|
+
node = self
|
|
140
|
+
for _ in range(number):
|
|
141
|
+
if not isinstance(node, ChainNode):
|
|
142
|
+
raise IndexError("index out of range")
|
|
143
|
+
node = node.pointer
|
|
144
|
+
if not isinstance(node, ChainNode):
|
|
145
|
+
raise IndexError("index out of range")
|
|
146
|
+
node.value = value
|
|
147
|
+
|
|
148
|
+
def delete_number(self, number: int):
|
|
149
|
+
"""
|
|
150
|
+
Delete the node at index number.
|
|
151
|
+
|
|
152
|
+
If number is 0, return the next node as the new head. Otherwise, return
|
|
153
|
+
self. Raises ValueError if number is not a non-negative int, and
|
|
154
|
+
IndexError if the chain ends before reaching that index.
|
|
155
|
+
"""
|
|
156
|
+
if not isinstance(number, int) or number < 0:
|
|
157
|
+
raise ValueError("index out of range")
|
|
158
|
+
|
|
159
|
+
if number == 0:
|
|
160
|
+
if not isinstance(self.pointer, ChainNode):
|
|
161
|
+
raise IndexError("index out of range")
|
|
162
|
+
return self.pointer
|
|
163
|
+
|
|
164
|
+
prev = self
|
|
165
|
+
for _ in range(number - 1):
|
|
166
|
+
if not isinstance(prev.pointer, ChainNode):
|
|
167
|
+
raise IndexError("index out of range")
|
|
168
|
+
prev = prev.pointer
|
|
169
|
+
|
|
170
|
+
target = prev.pointer
|
|
171
|
+
if not isinstance(target, ChainNode):
|
|
172
|
+
raise IndexError("index out of range")
|
|
173
|
+
prev.pointer = target.pointer
|
|
174
|
+
return self
|
|
175
|
+
|
|
176
|
+
def delete_value(self, value):
|
|
177
|
+
"""
|
|
178
|
+
Delete the first node whose value equals value.
|
|
179
|
+
|
|
180
|
+
Return the new head. When the deleted node is the head, the successor
|
|
181
|
+
becomes the new head. Raises ValueError if no such node exists.
|
|
182
|
+
"""
|
|
183
|
+
if self.value == value:
|
|
184
|
+
if not isinstance(self.pointer, ChainNode):
|
|
185
|
+
raise ValueError("value not found in chain")
|
|
186
|
+
return self.pointer
|
|
187
|
+
|
|
188
|
+
prev = self
|
|
189
|
+
cur = self.pointer
|
|
190
|
+
while isinstance(cur, ChainNode):
|
|
191
|
+
if cur.value == value:
|
|
192
|
+
prev.pointer = cur.pointer
|
|
193
|
+
return self
|
|
194
|
+
prev = cur
|
|
195
|
+
cur = cur.pointer
|
|
196
|
+
raise ValueError("value not found in chain")
|
|
197
|
+
|
|
198
|
+
def node_search(self, value) -> int:
|
|
199
|
+
"""
|
|
200
|
+
Return the index of the first node whose value equals value.
|
|
201
|
+
|
|
202
|
+
Index 0 is self. Raises ValueError if no such node exists.
|
|
203
|
+
"""
|
|
204
|
+
if self.value == value:
|
|
205
|
+
return 0
|
|
206
|
+
pointer_save = self
|
|
207
|
+
pointer_now = self.pointer
|
|
208
|
+
i = 1
|
|
209
|
+
while True:
|
|
210
|
+
if not isinstance(pointer_now, ChainNode):
|
|
211
|
+
if pointer_now == value and pointer_now == pointer_save.value:
|
|
212
|
+
return i
|
|
213
|
+
else:
|
|
214
|
+
raise ValueError(f"{value} not found in chain")
|
|
215
|
+
if pointer_now.value == value:
|
|
216
|
+
return i
|
|
217
|
+
pointer_now = pointer_now.pointer
|
|
218
|
+
pointer_save = pointer_save.pointer
|
|
219
|
+
i += 1
|
|
220
|
+
|
|
221
|
+
def _find_node(self, value):
|
|
222
|
+
"""Return the first node whose value equals value, or None."""
|
|
223
|
+
node = self
|
|
224
|
+
while isinstance(node, ChainNode):
|
|
225
|
+
if node.value == value:
|
|
226
|
+
return node
|
|
227
|
+
node = node.pointer
|
|
228
|
+
return None
|
|
229
|
+
|
|
230
|
+
def node_len(self, value=point_none, pointer=point_none) -> int:
|
|
231
|
+
"""
|
|
232
|
+
Return the number of nodes starting after a given position.
|
|
233
|
+
|
|
234
|
+
With no argument, count nodes after self. With pointer=xxx, count
|
|
235
|
+
starting at xxx (inclusive). With value=xxx, count starting after the
|
|
236
|
+
node holding xxx. The two keyword arguments are mutually exclusive.
|
|
237
|
+
"""
|
|
238
|
+
if pointer is not point_none and value is not point_none:
|
|
239
|
+
raise ValueError("pointer and value are mutually exclusive")
|
|
240
|
+
|
|
241
|
+
if value is not point_none:
|
|
242
|
+
node = self._find_node(value)
|
|
243
|
+
if node is None:
|
|
244
|
+
raise ValueError(f"value {value} not found in chain")
|
|
245
|
+
start = node.pointer
|
|
246
|
+
elif pointer is not point_none:
|
|
247
|
+
start = pointer
|
|
248
|
+
else:
|
|
249
|
+
start = self.pointer
|
|
250
|
+
|
|
251
|
+
i = 0
|
|
252
|
+
while isinstance(start, ChainNode):
|
|
253
|
+
start = start.pointer
|
|
254
|
+
i += 1
|
|
255
|
+
return i
|
|
256
|
+
|
|
257
|
+
def deep_len(self, value=point_none, pointer=point_none) -> int:
|
|
258
|
+
"""
|
|
259
|
+
Return the depth of a node relative to self.
|
|
260
|
+
|
|
261
|
+
Positive if the node comes after self, 0 if it is self, negative if it
|
|
262
|
+
comes before self. The two keyword arguments are mutually exclusive.
|
|
263
|
+
Raises ValueError if the node is not on the same chain.
|
|
264
|
+
"""
|
|
265
|
+
if pointer is not point_none and value is not point_none:
|
|
266
|
+
raise ValueError("pointer and value are mutually exclusive")
|
|
267
|
+
|
|
268
|
+
if value is not point_none:
|
|
269
|
+
node = self._find_node(value)
|
|
270
|
+
if node is None:
|
|
271
|
+
raise ValueError(f"value {value} not found in chain")
|
|
272
|
+
elif pointer is not point_none:
|
|
273
|
+
node = pointer
|
|
274
|
+
else:
|
|
275
|
+
raise ValueError("either pointer or value is required")
|
|
276
|
+
|
|
277
|
+
cur = self
|
|
278
|
+
depth = 0
|
|
279
|
+
while isinstance(cur, ChainNode):
|
|
280
|
+
if cur is node:
|
|
281
|
+
return depth
|
|
282
|
+
cur = cur.pointer
|
|
283
|
+
depth += 1
|
|
284
|
+
|
|
285
|
+
cur = node
|
|
286
|
+
depth = 0
|
|
287
|
+
while isinstance(cur, ChainNode):
|
|
288
|
+
if cur is self:
|
|
289
|
+
return -depth
|
|
290
|
+
cur = cur.pointer
|
|
291
|
+
depth += 1
|
|
292
|
+
|
|
293
|
+
raise ValueError("the two nodes are not in the same chain")
|