v440 0.2.0__3-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.
v440/__init__.py ADDED
@@ -0,0 +1 @@
1
+ from v440._core import *
v440/_core/Local.py ADDED
@@ -0,0 +1,118 @@
1
+ from __future__ import annotations
2
+
3
+ import types
4
+ import typing
5
+
6
+ import scaevola
7
+
8
+ from . import utils
9
+
10
+
11
+ @utils.compclass(list)
12
+ class Local(scaevola.Scaevola):
13
+ def __add__(self, other, /):
14
+ return self._new(self._data + self._todata(other))
15
+
16
+ @classmethod
17
+ def __class_getitem__(cls, key, /):
18
+ return types.GenericAlias(cls, key)
19
+
20
+ def __eq__(self, other):
21
+ if type(self) != type(other):
22
+ return False
23
+ return self._data == other._data
24
+
25
+ def __getitem__(self, key, /):
26
+ if type(key) is not slice:
27
+ return self._data[key]
28
+ return self._new(self._data[key])
29
+
30
+ def __hash__(self):
31
+ raise TypeError("unhashable type: %r" % type(self).__name__)
32
+
33
+ def __iadd__(self, other, /):
34
+ self._data += self._todata(other)
35
+
36
+ def __imul__(self, other, /):
37
+ self._data *= other
38
+
39
+ def __init__(self, data=[]):
40
+ self.data = data
41
+
42
+ def __le__(self, other):
43
+ other = type(self)(other)
44
+ return self._cmpkey() <= other._cmpkey()
45
+
46
+ def __lt__(self, other):
47
+ other = type(self)(other)
48
+ return self._cmpkey() < other._cmpkey()
49
+
50
+ def __mul__(self, other, /):
51
+ return self._new(self._data * other)
52
+
53
+ def __repr__(self) -> str:
54
+ return "%s(%r)" % (type(self).__name__, str(self))
55
+
56
+ def __reversed__(self, /):
57
+ ans = self[:]
58
+ ans._data.reverse()
59
+ return ans
60
+
61
+ def __rmul__(self, other, /):
62
+ return self.__mul__(other)
63
+
64
+ def __setitem__(self, key, value, /):
65
+ if type(key) is not slice:
66
+ self._data[key] = utils.segment(value)
67
+ else:
68
+ self._data[key] = self._todata(value._data)
69
+
70
+ def __str__(self) -> str:
71
+ return ".".join(str(x) for x in self)
72
+
73
+ def _cmpkey(self):
74
+ return [self._sortkey(x) for x in self]
75
+
76
+ @classmethod
77
+ def _new(cls, data, /):
78
+ ans = object.__new__(cls)
79
+ ans._data = data
80
+ return ans
81
+
82
+ @staticmethod
83
+ def _sortkey(value):
84
+ return type(value) is int, value
85
+
86
+ @staticmethod
87
+ def _todata(data, /):
88
+ return utils.todata(data, "+")
89
+
90
+ def append(self, elem, /):
91
+ self._data.append(utils.segment(elem))
92
+
93
+ def copy(self):
94
+ return self._new(self.data)
95
+
96
+ @property
97
+ def data(self, /):
98
+ return list(self._data)
99
+
100
+ @data.setter
101
+ @utils.setterdeco
102
+ def data(self, data, /):
103
+ self._data = self._todata(data)
104
+
105
+ @data.deleter
106
+ def data(self):
107
+ self._data = []
108
+
109
+ def extend(self, data, /):
110
+ self.__iadd__(data)
111
+
112
+ def insert(self, index, elem, /):
113
+ self._data.insert(index, utils.segment(elem))
114
+
115
+ def sort(self, *, key=None, reverse=False):
116
+ if key is None:
117
+ key = self._sortkey
118
+ self._data.sort(key=key, reverse=reverse)
v440/_core/Pre.py ADDED
@@ -0,0 +1,138 @@
1
+ from __future__ import annotations
2
+
3
+ import typing
4
+
5
+ import scaevola
6
+
7
+ from . import utils
8
+
9
+
10
+ class Pre:
11
+ _PHASEDICT = dict(
12
+ alpha="a",
13
+ a="a",
14
+ beta="b",
15
+ b="b",
16
+ preview="rc",
17
+ pre="rc",
18
+ c="rc",
19
+ rc="rc",
20
+ )
21
+
22
+ def __bool__(self):
23
+ return bool(self.phase)
24
+
25
+ def __eq__(self, other):
26
+ if type(other) is not type(self):
27
+ return False
28
+ return self.data == other.data
29
+
30
+ def __hash__(self) -> int:
31
+ return hash((self.phase, self.subphase))
32
+
33
+ @typing.overload
34
+ def __init__(self, data=None) -> None: ...
35
+ @typing.overload
36
+ def __init__(self, phase, subphase=0): ...
37
+
38
+ def __init__(self, *args, **kwargs):
39
+ self._init(*args, **kwargs)(*args, **kwargs)
40
+
41
+ def __repr__(self) -> str:
42
+ return "%s(%r)" % (type(self).__name__, str(self))
43
+
44
+ def __str__(self) -> str:
45
+ if self:
46
+ return self.phase + str(self.subphase)
47
+ else:
48
+ return ""
49
+
50
+ def _init(self, *args, **kwargs):
51
+ if "data" in kwargs.keys():
52
+ return self._init_data
53
+ if kwargs:
54
+ return self._init_items
55
+ if len(args) > 1:
56
+ return self._init_items
57
+ return self._init_data
58
+
59
+ def _init_data(self, data=None):
60
+ self.data = data
61
+
62
+ def _init_items(self, phase, subphase=0):
63
+ self.phase = phase
64
+ self.subphase = subphase
65
+
66
+ def copy(self):
67
+ return type(self)(self)
68
+
69
+ @property
70
+ def data(self):
71
+ if not self.phase:
72
+ return None
73
+ return self.phase, self.subphase
74
+
75
+ @data.setter
76
+ @utils.setterdeco
77
+ def data(self, value, /):
78
+ value = utils.todata(value, ".")
79
+ if len(value) == 0:
80
+ del self.data
81
+ return
82
+ if len(value) > 1:
83
+ self.phase, self.subphase = value
84
+ return
85
+ value = value[0]
86
+ if type(value) is int:
87
+ raise TypeError
88
+ for long, short in type(self)._PHASEDICT.items():
89
+ p, value = utils.lsplit(value, long, "")
90
+ if p == "":
91
+ continue
92
+ self.phase = short
93
+ self.subphase = value[len(long) :]
94
+ return
95
+ raise ValueError
96
+
97
+ @data.deleter
98
+ def data(self):
99
+ del self.phase
100
+
101
+ @property
102
+ def phase(self):
103
+ return self._phase
104
+
105
+ @phase.setter
106
+ @utils.setterdeco
107
+ def phase(self, value):
108
+ if not value:
109
+ del self.phase
110
+ return
111
+ value = utils.literal(value)
112
+ value = type(self)._PHASEDICT[value]
113
+ self._phase = value
114
+
115
+ @phase.deleter
116
+ def phase(self):
117
+ self._phase = None
118
+ self._subphase = 0
119
+
120
+ @property
121
+ def subphase(self):
122
+ return self._subphase
123
+
124
+ @subphase.setter
125
+ @utils.setterdeco
126
+ def subphase(self, value):
127
+ value = utils.numeral(value)
128
+ if value == 0:
129
+ del self.subphase
130
+ return
131
+ if self.phase:
132
+ self._subphase = value
133
+ return
134
+ raise utils.VersionError("no subphase allowed without a phase")
135
+
136
+ @subphase.deleter
137
+ def subphase(self):
138
+ self._subphase = 0
v440/_core/Release.py ADDED
@@ -0,0 +1,251 @@
1
+ from __future__ import annotations
2
+
3
+ import types
4
+ import typing
5
+
6
+ import scaevola
7
+
8
+ from . import utils
9
+
10
+
11
+ @utils.compclass(list)
12
+ class Release(scaevola.Scaevola):
13
+ def __add__(self, other):
14
+ return type(self)(self._data + list(other))
15
+
16
+ @classmethod
17
+ def __class_getitem__(cls, key, /):
18
+ return types.GenericAlias(cls, key)
19
+
20
+ def __delitem__(self, index, /) -> None:
21
+ del self._data[index]
22
+ self._rstrip()
23
+
24
+ def __eq__(self, other):
25
+ if type(self) != type(other):
26
+ return False
27
+ return self._data == other._data
28
+
29
+ def __format__(self, cutoff=None):
30
+ return self.format(cutoff=cutoff)
31
+
32
+ def __getitem__(self, key):
33
+ if type(key) is slice:
34
+ return self._getitem_slice(key)
35
+ else:
36
+ return self._getitem_index(key)
37
+
38
+ def __hash__(self):
39
+ raise TypeError("unhashable type: %r" % type(self).__name__)
40
+
41
+ def __iadd__(self, other, /):
42
+ self._data += self._todata(other)
43
+ self._rstrip()
44
+
45
+ def __init__(self, data=[], /):
46
+ self.data = data
47
+
48
+ def __le__(self, other):
49
+ other = type(self)(other)
50
+ return self._data <= other._data
51
+
52
+ def __lt__(self, other):
53
+ other = type(self)(other)
54
+ return self._data < other._data
55
+
56
+ def __mul__(self, other):
57
+ return self._new(self._data * other)
58
+
59
+ def __repr__(self) -> str:
60
+ return "%s(%r)" % (type(self).__name__, str(self))
61
+
62
+ def __reversed__(self):
63
+ return type(self)(reversed(self._data))
64
+
65
+ def __rmul__(self, other):
66
+ return self.__mul__(other)
67
+
68
+ def __setitem__(self, key, value):
69
+ if type(key) is slice:
70
+ self._setitem_slice(key, value)
71
+ else:
72
+ self._setitem_index(key, value)
73
+
74
+ def __str__(self) -> str:
75
+ return ".".join(str(x) for x in self)
76
+
77
+ def _getitem_index(self, key):
78
+ key = utils.toindex(key)
79
+ if len(self) <= key:
80
+ return 0
81
+ return self._data[key]
82
+
83
+ def _getitem_slice(self, key):
84
+ ans = self._range(key)
85
+ ans = [self._getitem_index(i) for i in ans]
86
+ return ans
87
+
88
+ @classmethod
89
+ def _new(cls, data, /):
90
+ ans = object.__new__(cls)
91
+ ans._data = data
92
+ return ans
93
+
94
+ def _range(self, key):
95
+ start = key.start
96
+ stop = key.stop
97
+ step = key.step
98
+ if step is None:
99
+ step = 1
100
+ else:
101
+ step = utils.toindex(step)
102
+ if step == 0:
103
+ raise ValueError
104
+ fwd = step > 0
105
+ if start is None:
106
+ start = 0 if fwd else len(self) - 1
107
+ else:
108
+ start = utils.toindex(start)
109
+ if stop is None:
110
+ stop = len(self) if fwd else -1
111
+ else:
112
+ stop = utils.toindex(stop)
113
+ if start < 0:
114
+ start += len(self)
115
+ if start < 0:
116
+ start = 0 if fwd else -1
117
+ if stop < 0:
118
+ stop += len(self)
119
+ if stop < 0:
120
+ stop = 0 if fwd else -1
121
+ return range(start, stop, step)
122
+
123
+ def _rstrip(self):
124
+ if not self._data:
125
+ return
126
+ while self._data[-1] == 0:
127
+ self._data.pop(0)
128
+
129
+ def _setitem_index(self, key, value):
130
+ key = utils.toindex(key)
131
+ value = utils.numeral(value)
132
+ if len(self) > key:
133
+ self._data[key] = value
134
+ self._rstrip()
135
+ return
136
+ if value == 0:
137
+ return
138
+ self._data.extend([0] * (key - len(self)))
139
+ self._data.append(value)
140
+
141
+ def _setitem_slice(self, key, value):
142
+ key = list(self._range(key))
143
+ value = self._todata(value)
144
+ if len(key) != len(value):
145
+ e = "attempt to assign sequence of size %s to extended slice of size %s"
146
+ e %= (len(value), len(key))
147
+ raise ValueError(e)
148
+ for k, v in zip(key, value):
149
+ self._setitem_index(k, v)
150
+
151
+ @staticmethod
152
+ def _todata(value):
153
+ return [utils.numeral(x) for x in utils.tolist(value, "v")]
154
+
155
+ def append(self, element):
156
+ element = utils.numeral(element)
157
+ if element:
158
+ self._data.append(element)
159
+
160
+ def bump(self, index=-1, amount=1):
161
+ x = self._getitem_index(index) + amount
162
+ self._setitem_index(index, x)
163
+ if index != -1:
164
+ self._data = self._data[: index + 1]
165
+ self._rstrip()
166
+
167
+ def copy(self):
168
+ return self.__mul__(1)
169
+
170
+ @property
171
+ def data(self):
172
+ return list(self._data)
173
+
174
+ @data.setter
175
+ @utils.setterdeco
176
+ def data(self, v):
177
+ del self.data
178
+ self.extend(v)
179
+
180
+ @data.deleter
181
+ def data(self):
182
+ self._data = []
183
+
184
+ def extend(self, data, /):
185
+ self._data.extend(self._todata(data))
186
+ self._rstrip()
187
+
188
+ def format(self, cutoff=None):
189
+ ans = self[:cutoff]
190
+ ans = [str(x) for x in ans]
191
+ ans = ".".join(ans)
192
+ return ans
193
+
194
+ def insert(self, index, elem, /):
195
+ self[index:index] = [elem]
196
+
197
+ @property
198
+ def major(self) -> int:
199
+ return self[0]
200
+
201
+ @major.setter
202
+ @utils.setterdeco
203
+ def major(self, value: typing.Any):
204
+ self[0] = value
205
+
206
+ @major.deleter
207
+ def major(self):
208
+ del self[0]
209
+
210
+ @property
211
+ def micro(self) -> int:
212
+ return self[2]
213
+
214
+ @micro.setter
215
+ @utils.setterdeco
216
+ def micro(self, value: typing.Any):
217
+ self[2] = value
218
+
219
+ @micro.deleter
220
+ def micro(self):
221
+ del self[2]
222
+
223
+ @property
224
+ def minor(self) -> int:
225
+ return self[1]
226
+
227
+ @minor.setter
228
+ @utils.setterdeco
229
+ def minor(self, value: typing.Any):
230
+ self[1] = value
231
+
232
+ @minor.deleter
233
+ def minor(self):
234
+ del self[1]
235
+
236
+ def pop(self, index=-1, /):
237
+ ans = self._data.pop(index)
238
+ self._rstrip()
239
+ return ans
240
+
241
+ def remove(self, elem, /) -> None:
242
+ self._data.remove(elem)
243
+ self._rstrip()
244
+
245
+ def reverse(self):
246
+ self._data.reverse()
247
+ self._rstrip()
248
+
249
+ def sort(self, *, key=None, reverse=False):
250
+ self._data.sort(key=key, reverse=reverse)
251
+ self._rstrip()
v440/_core/Version.py ADDED
@@ -0,0 +1,264 @@
1
+ from __future__ import annotations
2
+
3
+ import typing
4
+
5
+ import packaging.version
6
+ import scaevola
7
+
8
+ from v440._core.Local import Local
9
+ from v440._core.Pre import Pre
10
+ from v440._core.Release import Release
11
+
12
+ from . import utils
13
+
14
+
15
+ class Version(scaevola.Scaevola):
16
+ def __bool__(self):
17
+ return self != type(self)()
18
+
19
+ def __eq__(self, other) -> bool:
20
+ other = type(self)(other)
21
+ return self._cmpkey() == other._cmpkey()
22
+
23
+ def __hash__(self) -> int:
24
+ return hash(self._cmpkey())
25
+
26
+ def __init__(self, data="0", /, **kwargs) -> None:
27
+ self.data = data
28
+ self.update(**kwargs)
29
+
30
+ def __le__(self, other) -> bool:
31
+ other = type(self)(other)
32
+ return self._cmpkey() <= other._cmpkey()
33
+
34
+ def __lt__(self, other) -> bool:
35
+ other = type(self)(other)
36
+ return self._cmpkey() < other._cmpkey()
37
+
38
+ def __repr__(self) -> str:
39
+ return "%s(%r)" % (type(self).__name__, str(self))
40
+
41
+ def __str__(self) -> str:
42
+ return self.data
43
+
44
+ def _aux(self) -> str:
45
+ ans = str(self.pre)
46
+ if self.post is not None:
47
+ ans += ".post%s" % self.post
48
+ if self.dev is not None:
49
+ ans += ".dev%s" % self.dev
50
+ return ans
51
+
52
+ def _cmpkey(self) -> tuple:
53
+ if self.pre:
54
+ pre = self.pre.data
55
+ elif self.post is None and self.dev is not None:
56
+ pre = "", -1
57
+ else:
58
+ pre = "z", float("inf")
59
+ post = -1 if self.post is None else self.post
60
+ dev = float("inf") if self.dev is None else self.dev
61
+ return self.epoch, self.release, pre, post, dev, self.local
62
+
63
+ @property
64
+ def base(self) -> str:
65
+ if self.epoch:
66
+ return "%s!%s" % (self.epoch, self.release)
67
+ else:
68
+ return str(self.release)
69
+
70
+ @base.setter
71
+ @utils.setterdeco
72
+ def base(self, v):
73
+ v = str(v)
74
+ if "!" in v:
75
+ self.epoch, self.release = v.split("!")
76
+ else:
77
+ self.epoch, self.release = 0, v
78
+
79
+ @base.deleter
80
+ def base(self):
81
+ del self.epoch
82
+ del self.release
83
+
84
+ def clear(self):
85
+ del self.public
86
+ del self.local
87
+
88
+ def copy(self):
89
+ return type(self)(self)
90
+
91
+ @property
92
+ def data(self):
93
+ return self.format()
94
+
95
+ @data.setter
96
+ @utils.setterdeco
97
+ def data(self, x):
98
+ x = str(x)
99
+ if "+" in x:
100
+ self.public, self.local = x.split("+")
101
+ else:
102
+ self.public, self.local = x, None
103
+
104
+ @data.deleter
105
+ def data(self):
106
+ del self.public
107
+ del self.local
108
+
109
+ @property
110
+ def dev(self):
111
+ return self._dev
112
+
113
+ @dev.setter
114
+ @utils.setterdeco
115
+ def dev(self, v):
116
+ v = utils.tolist(v, ".")
117
+ if len(v) > 2:
118
+ raise ValueError
119
+ if len(v) == 0:
120
+ del self.dev
121
+ return
122
+ if len(v) == 1:
123
+ v = v[0]
124
+ v = utils.lsplit(v, "dev", "")
125
+ if v[0] != "dev":
126
+ raise ValueError
127
+ v = utils.numeral(v[1])
128
+ self._dev = v
129
+
130
+ @dev.deleter
131
+ def dev(self):
132
+ self._dev = None
133
+
134
+ @property
135
+ def epoch(self):
136
+ return self._epoch
137
+
138
+ @epoch.setter
139
+ @utils.setterdeco
140
+ def epoch(self, v):
141
+ v = str(v)
142
+ v = utils.lsplit(v, "v", "")[1]
143
+ if v.endswith("!"):
144
+ v = v[:-1]
145
+ v = utils.numeral(v)
146
+ return v
147
+
148
+ @epoch.deleter
149
+ def epoch(self):
150
+ self._epoch = 0
151
+
152
+ def format(self, cutoff=None) -> str:
153
+ ans = ""
154
+ if self.epoch:
155
+ ans += "%s!" % self.epoch
156
+ ans += self.release.format(cutoff=cutoff)
157
+ ans += self._aux()
158
+ if self.local:
159
+ ans += "+%s" % self.local
160
+ return ans
161
+
162
+ def is_prerelease(self) -> bool:
163
+ return self.dev is not None or self.pre is not None
164
+
165
+ def is_postrelease(self) -> bool:
166
+ return self.post is not None
167
+
168
+ def is_devrelease(self) -> bool:
169
+ return self.dev is not None
170
+
171
+ @property
172
+ def local(self) -> str:
173
+ return self._local
174
+
175
+ @local.setter
176
+ @utils.setterdeco
177
+ def local(self, v):
178
+ self._local = Local(v)
179
+
180
+ @local.deleter
181
+ def local(self):
182
+ self._local = Local()
183
+
184
+ def packaging(self):
185
+ return packaging.version.Version(self.data)
186
+
187
+ @property
188
+ def post(self):
189
+ return self._post
190
+
191
+ @post.setter
192
+ @utils.setterdeco
193
+ def post(self, v):
194
+ v = utils.tolist(v, ".")
195
+ if len(v) > 2:
196
+ raise ValueError
197
+ if len(v) == 0:
198
+ del self.post
199
+ return
200
+ prefices = "post", "rev", "r", ""
201
+ if len(v) == 1:
202
+ v = utils.lsplit(v[0], *prefices)
203
+ elif v[0] not in prefices:
204
+ raise ValueError
205
+ v = utils.numeral(v[1])
206
+ self._post = v
207
+
208
+ @post.deleter
209
+ def post(self):
210
+ self._post = None
211
+
212
+ @property
213
+ def pre(self):
214
+ return self._pre
215
+
216
+ @pre.setter
217
+ @utils.setterdeco
218
+ def pre(self, data, /):
219
+ self._pre = Pre(data)
220
+
221
+ @pre.deleter
222
+ def pre(self):
223
+ self._pre = Pre()
224
+
225
+ @property
226
+ def public(self) -> str:
227
+ return self.base + self._aux()
228
+
229
+ @public.setter
230
+ @utils.setterdeco
231
+ def public(self, v):
232
+ v = str(v).lower().strip()
233
+ d = utils.Pattern.PUBLIC.regex.search(v).groupdict()
234
+ names = "epoch release pre post dev".split()
235
+ for n in names:
236
+ setattr(self, n, d[n])
237
+
238
+ @public.deleter
239
+ def public(self):
240
+ self.public = "0"
241
+
242
+ @property
243
+ def release(self) -> Release:
244
+ return self._release
245
+
246
+ @release.setter
247
+ @utils.setterdeco
248
+ def release(self, v):
249
+ self._release = Release(v)
250
+
251
+ @release.deleter
252
+ def release(self):
253
+ self._release = Release()
254
+
255
+ def update(self, **kwargs):
256
+ for k, v in kwargs.items():
257
+ attr = getattr(type(self), k)
258
+ if isinstance(attr, property):
259
+ setattr(self, k, v)
260
+ continue
261
+ e = "%r is not a property"
262
+ e %= k
263
+ e = AttributeError(e)
264
+ raise e
v440/_core/__init__.py ADDED
@@ -0,0 +1,2 @@
1
+ from v440._core.utils import VersionError
2
+ from v440._core.Version import Version
v440/_core/utils.py ADDED
@@ -0,0 +1,196 @@
1
+ from __future__ import annotations
2
+
3
+ import enum
4
+ import functools
5
+ import inspect
6
+ import re
7
+ import string
8
+ import typing
9
+
10
+ SEGCHARS = string.ascii_lowercase + string.digits
11
+
12
+
13
+ class EMPTY: ...
14
+
15
+
16
+ def compclass(innerclass):
17
+ return functools.partial(compclass_core, innerclass)
18
+
19
+
20
+ def compclass_core(innerclass, outerclass):
21
+ for n in dir(innerclass):
22
+ if not (isnunder(n) or ismagic(n)):
23
+ continue
24
+ i = getattr(innerclass, n)
25
+ if type(i) is type:
26
+ continue
27
+ if not callable(i):
28
+ continue
29
+ o = getattr(outerclass, n, EMPTY)
30
+ o = compclass_outerfunc(n, o)
31
+ if isnunder(n):
32
+ o = functools.wraps(i)(o)
33
+ setattr(outerclass, n, o)
34
+ return outerclass
35
+
36
+
37
+ def compclass_outerfunc(n, gotten, /):
38
+ if gotten is not EMPTY:
39
+ return gotten
40
+
41
+ def o(self, *args, **kwargs):
42
+ f = getattr(self._data, n)
43
+ y = f(*args, **kwargs)
44
+ return y
45
+
46
+ return o
47
+
48
+
49
+ def ismagic(name):
50
+ name = str(name)
51
+ if len(name) < 5:
52
+ return False
53
+ if not name.startswith("__"):
54
+ return False
55
+ if not name.endswith("__"):
56
+ return False
57
+ if name.startswith("___"):
58
+ return False
59
+ if name.endswith("___"):
60
+ return False
61
+ return True
62
+
63
+
64
+ def isnunder(name):
65
+ name = str(name)
66
+ return not name.startswith("_")
67
+
68
+
69
+ def literal(value, /):
70
+ e = "%r is not a valid literal segment"
71
+ e = VersionError(e % value)
72
+ try:
73
+ x = segment(value)
74
+ except:
75
+ raise e
76
+ if type(x) is str:
77
+ return x
78
+ raise e
79
+
80
+
81
+ def lsplit(value: str, *prefices):
82
+ for p in prefices:
83
+ if value.startswith(p):
84
+ return p, value[len(p) :]
85
+ raise ValueError
86
+
87
+
88
+ def numeral(value, /):
89
+ e = "%r is not a valid numeral segment"
90
+ e = VersionError(e % value)
91
+ try:
92
+ x = segment(value)
93
+ except:
94
+ raise e
95
+ if type(x) is int:
96
+ return x
97
+ if x == "":
98
+ return 0
99
+ raise e
100
+
101
+
102
+ def segment(value, /):
103
+ e = "%r is not a valid segment"
104
+ e = VersionError(e % value)
105
+ try:
106
+ x = str(value).lower().strip()
107
+ except:
108
+ raise e
109
+ if x.strip(SEGCHARS):
110
+ raise e
111
+ try:
112
+ return int(x)
113
+ except:
114
+ return x
115
+
116
+
117
+ def setterdeco(old, /):
118
+ @functools.wraps(old)
119
+ def new(self, value, /):
120
+ if value is None:
121
+ delattr(self, old.__name__)
122
+ return
123
+ try:
124
+ old(self, value)
125
+ except VersionError:
126
+ raise
127
+ except:
128
+ e = "%r is an invalid value for %r"
129
+ e %= (value, old.__name__)
130
+ raise VersionError(e)
131
+
132
+ return new
133
+
134
+
135
+ def todata(value, *args):
136
+ try:
137
+ return tolist(value, *args)
138
+ except VersionError:
139
+ raise
140
+ except:
141
+ e = "%r is an invalid value for %r"
142
+ e %= (value, "data")
143
+ raise VersionError(e)
144
+
145
+
146
+ def toindex(value, /):
147
+ ans = value.__index__()
148
+ if type(ans) is not int:
149
+ raise TypeError("__index__ returned non-int (type %s)" % type(ans).__name__)
150
+ return ans
151
+
152
+
153
+ def tolist(value, prefix):
154
+ if value is None:
155
+ return
156
+ if not issubclass(type(value), str) and hasattr(value, "__iter__"):
157
+ return list(value)
158
+ value = str(value).lower()
159
+ value = value.replace("-", ".")
160
+ value = value.replace("_", ".")
161
+ value = lsplit(value, prefix, "")[1]
162
+ if value == "":
163
+ return []
164
+ value = value.split(".")
165
+ return value
166
+
167
+
168
+ class Pattern(enum.StrEnum):
169
+ EPOCH = r"(?:(?P<epoch>[0-9]+)!)?"
170
+ RELEASE = r"(?P<release>[0-9]+(?:\.[0-9]+)*)"
171
+ PRE = r"""
172
+ (?P<pre>
173
+ [-_\.]?
174
+ (?P<pre_l>alpha|a|beta|b|preview|pre|c|rc)
175
+ [-_\.]?
176
+ (?P<pre_n>[0-9]+)?
177
+ )?"""
178
+ POST = r"""
179
+ (?P<post>
180
+ (?:-(?:[0-9]+))
181
+ |
182
+ (?: [-_\.]? (?:post|rev|r) [-_\.]? (?:[0-9]+)? )
183
+ )?"""
184
+ DEV = r"""(?P<dev> [-_\.]? dev [-_\.]? (?:[0-9]+)? )?"""
185
+ LOCAL = r"""(?:\+(?P<local>[a-z0-9]+(?:[-_\.][a-z0-9]+)*))?"""
186
+ PUBLIC = f"v? {EPOCH} {RELEASE} {PRE} {POST} {DEV}"
187
+
188
+ @functools.cached_property
189
+ def regex(self):
190
+ p = self.value
191
+ p = r"^" + p + r"$"
192
+ ans = re.compile(p, re.VERBOSE)
193
+ return ans
194
+
195
+
196
+ class VersionError(ValueError): ...
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2024 Johannes
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,74 @@
1
+ Metadata-Version: 2.1
2
+ Name: v440
3
+ Version: 0.2.0
4
+ Summary: mutable version objects
5
+ Author-email: Johannes <johannes-programming@mailfence.com>
6
+ License: The MIT License (MIT)
7
+
8
+ Copyright (c) 2024 Johannes
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+ Project-URL: Download, https://pypi.org/project/v440/#files
28
+ Project-URL: Source, https://github.com/johannes-programming/v440
29
+ Classifier: License :: OSI Approved :: MIT License
30
+ Classifier: Programming Language :: Python
31
+ Classifier: Programming Language :: Python :: 3
32
+ Classifier: Programming Language :: Python :: 3 :: Only
33
+ Requires-Python: >=3.12
34
+ Description-Content-Type: text/x-rst
35
+ License-File: LICENSE.txt
36
+ Requires-Dist: packaging >=23.2
37
+ Requires-Dist: scaevola >=1.0.5
38
+
39
+ ====
40
+ v440
41
+ ====
42
+
43
+ Overview
44
+ --------
45
+
46
+ mutable version objects in accordance with PEP440
47
+
48
+ Installation
49
+ ------------
50
+
51
+ To install v440, you can use `pip`. Open your terminal and run:
52
+
53
+ .. code-block:: bash
54
+
55
+ pip install v440
56
+
57
+ License
58
+ -------
59
+
60
+ This project is licensed under the MIT License.
61
+
62
+ Links
63
+ -----
64
+
65
+ * `Download <https://pypi.org/project/v440/#files>`_
66
+ * `Source <https://github.com/johannes-programming/v440>`_
67
+
68
+ Credits
69
+ -------
70
+
71
+ - Author: Johannes
72
+ - Email: johannes-programming@mailfence.com
73
+
74
+ Thank you for using v440!
@@ -0,0 +1,12 @@
1
+ v440/__init__.py,sha256=MQrVNWvZoxumw3SE5XBnGWje_gQ7V_DvfDLv29UX0SE,25
2
+ v440/_core/Local.py,sha256=P_r3ZYlN_Ifn3Qp15wYa24y0bPvgksVHgrwwx4lyuY8,2820
3
+ v440/_core/Pre.py,sha256=yOA2bst5PdFwjcJhBHHC_a9XwdmvOnlwJhauXEYS0CI,3195
4
+ v440/_core/Release.py,sha256=-tEpa1uTQqK9vAt2WdOMKapvH0kT50_LSXUYOGjaPJo,6106
5
+ v440/_core/Version.py,sha256=BcnxNQvoA5CGg-3uBhg6BjcfHbU94_tIy6CSqAK_ETU,6114
6
+ v440/_core/__init__.py,sha256=uYjdaudy0Sc4ys1eoI8c7-WvW5394p5UNw2BI_thrRA,81
7
+ v440/_core/utils.py,sha256=q2sgR4nKSEuwjVgIk5kVdVBkoZ-zmGOEynGdspFwIpQ,4369
8
+ v440-0.2.0.dist-info/LICENSE.txt,sha256=QwqYepQwTN5xG57cngKt6xxYD9z9hw9d3lcI8zSLiNg,1074
9
+ v440-0.2.0.dist-info/METADATA,sha256=E0zZRIJP5VRvmTqKiCIJJ_9k_qW3lhxtpFR5NBULG94,2382
10
+ v440-0.2.0.dist-info/WHEEL,sha256=cVxcB9AmuTcXqmwrtPhNK88dr7IR_b6qagTj0UvIEbY,91
11
+ v440-0.2.0.dist-info/top_level.txt,sha256=0KhvBmvlzcE4QlmK9pkVo1pXR2q-YDKG5Bj5yIXmWTE,5
12
+ v440-0.2.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (74.1.2)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ v440