python-ulid 2.7.0__py3-none-any.whl → 3.0.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.
- {python_ulid-2.7.0.dist-info → python_ulid-3.0.0.dist-info}/METADATA +1 -1
- python_ulid-3.0.0.dist-info/RECORD +10 -0
- {python_ulid-2.7.0.dist-info → python_ulid-3.0.0.dist-info}/WHEEL +1 -1
- ulid/__init__.py +51 -20
- ulid/__main__.py +22 -18
- ulid/base32.py +51 -59
- ulid/constants.py +5 -0
- python_ulid-2.7.0.dist-info/RECORD +0 -10
- {python_ulid-2.7.0.dist-info → python_ulid-3.0.0.dist-info}/entry_points.txt +0 -0
- {python_ulid-2.7.0.dist-info → python_ulid-3.0.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: python-ulid
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.0.0
|
|
4
4
|
Summary: Universally unique lexicographically sortable identifier
|
|
5
5
|
Project-URL: Homepage, https://github.com/mdomke/python-ulid
|
|
6
6
|
Project-URL: Documentation, https://python-ulid.readthedocs.io
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
ulid/__init__.py,sha256=yoBtXpTz-t8xnfOHtM70-hYIeYKk75ya7t6B6uWILhg,10971
|
|
2
|
+
ulid/__main__.py,sha256=TUo3dkWkJLfzRJyd55nfqGqvv1NcG7fBpsZHIi_p4rw,5421
|
|
3
|
+
ulid/base32.py,sha256=iUE5BGPW5Dul-Eu3ITvcymk8acNkIFAVTlrL12YIPKI,5961
|
|
4
|
+
ulid/constants.py,sha256=tJByGuryoTT_xfECvpMNPNwRvZMp2K_ZlZ7tOLeaXJs,335
|
|
5
|
+
ulid/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
+
python_ulid-3.0.0.dist-info/METADATA,sha256=82s8fmElXLy5Um_bjqhtZfiXd54YF0TDI2iOlKliLMs,5782
|
|
7
|
+
python_ulid-3.0.0.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
|
|
8
|
+
python_ulid-3.0.0.dist-info/entry_points.txt,sha256=9214ghzfSgS8a-TjjlWDF7bI7KSaeewOCBKn5uz7bdU,50
|
|
9
|
+
python_ulid-3.0.0.dist-info/licenses/LICENSE,sha256=DiDPNhW5yUHIqaKowvX0c1c9xWiI0xW4ji8R9N2NJbw,1056
|
|
10
|
+
python_ulid-3.0.0.dist-info/RECORD,,
|
ulid/__init__.py
CHANGED
|
@@ -4,10 +4,10 @@ import functools
|
|
|
4
4
|
import os
|
|
5
5
|
import time
|
|
6
6
|
import uuid
|
|
7
|
-
from collections.abc import Callable
|
|
8
7
|
from datetime import datetime
|
|
9
8
|
from datetime import timezone
|
|
10
9
|
from typing import Any
|
|
10
|
+
from typing import cast
|
|
11
11
|
from typing import Generic
|
|
12
12
|
from typing import TYPE_CHECKING
|
|
13
13
|
from typing import TypeVar
|
|
@@ -17,6 +17,8 @@ from ulid import constants
|
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
if TYPE_CHECKING: # pragma: no cover
|
|
20
|
+
from collections.abc import Callable
|
|
21
|
+
|
|
20
22
|
from pydantic import GetCoreSchemaHandler
|
|
21
23
|
from pydantic import ValidatorFunctionWrapHandler
|
|
22
24
|
from pydantic_core import CoreSchema
|
|
@@ -29,7 +31,6 @@ except ImportError: # pragma: no cover
|
|
|
29
31
|
|
|
30
32
|
__version__ = version("python-ulid")
|
|
31
33
|
|
|
32
|
-
|
|
33
34
|
T = TypeVar("T", bound=type)
|
|
34
35
|
R = TypeVar("R")
|
|
35
36
|
|
|
@@ -44,7 +45,7 @@ class validate_type(Generic[T]): # noqa: N801
|
|
|
44
45
|
if not isinstance(value, self.types):
|
|
45
46
|
message = "Value has to be of type "
|
|
46
47
|
message += " or ".join([t.__name__ for t in self.types])
|
|
47
|
-
raise
|
|
48
|
+
raise TypeError(message)
|
|
48
49
|
return func(cls, value)
|
|
49
50
|
|
|
50
51
|
return wrapped
|
|
@@ -102,7 +103,7 @@ class ULID:
|
|
|
102
103
|
|
|
103
104
|
@classmethod
|
|
104
105
|
@validate_type(int, float)
|
|
105
|
-
def from_timestamp(cls: type[U], value:
|
|
106
|
+
def from_timestamp(cls: type[U], value: float) -> U:
|
|
106
107
|
"""Create a new :class:`ULID`-object from a timestamp. The timestamp can be either a
|
|
107
108
|
`float` representing the time in seconds (as it would be returned by :func:`time.time()`)
|
|
108
109
|
or an `int` in milliseconds.
|
|
@@ -157,13 +158,45 @@ class ULID:
|
|
|
157
158
|
"""Create a new :class:`ULID`-object from an `int`."""
|
|
158
159
|
return cls(int.to_bytes(value, constants.BYTES_LEN, "big"))
|
|
159
160
|
|
|
161
|
+
@classmethod
|
|
162
|
+
def parse(cls: type[U], value: Any) -> U:
|
|
163
|
+
"""Create a new :class:`ULID`-object from a given value.
|
|
164
|
+
|
|
165
|
+
.. note:: This method should only be used when the caller is trying to parse a ULID from
|
|
166
|
+
a value when they're unsure what format/primitive type it will be given in.
|
|
167
|
+
"""
|
|
168
|
+
if isinstance(value, ULID):
|
|
169
|
+
return cast(U, value)
|
|
170
|
+
if isinstance(value, uuid.UUID):
|
|
171
|
+
return cls.from_uuid(value)
|
|
172
|
+
if isinstance(value, str):
|
|
173
|
+
len_value = len(value)
|
|
174
|
+
if len_value == constants.UUID_REPR_LEN:
|
|
175
|
+
return cls.from_uuid(uuid.UUID(value))
|
|
176
|
+
if len_value == constants.HEX_REPR_LEN:
|
|
177
|
+
return cls.from_hex(value)
|
|
178
|
+
if len_value == constants.REPR_LEN:
|
|
179
|
+
return cls.from_str(value)
|
|
180
|
+
raise ValueError(f"Cannot parse ULID from string of length {len_value}")
|
|
181
|
+
if isinstance(value, int):
|
|
182
|
+
if len(str(value)) == constants.INT_REPR_LEN:
|
|
183
|
+
return cls.from_int(value)
|
|
184
|
+
return cls.from_timestamp(value)
|
|
185
|
+
if isinstance(value, float):
|
|
186
|
+
return cls.from_timestamp(value)
|
|
187
|
+
if isinstance(value, datetime):
|
|
188
|
+
return cls.from_datetime(value)
|
|
189
|
+
if isinstance(value, bytes):
|
|
190
|
+
return cls.from_bytes(value)
|
|
191
|
+
raise TypeError(f"Cannot parse ULID from type {type(value)}")
|
|
192
|
+
|
|
160
193
|
@property
|
|
161
194
|
def milliseconds(self) -> int:
|
|
162
195
|
"""The timestamp part as epoch time in milliseconds.
|
|
163
196
|
|
|
164
197
|
Examples:
|
|
165
198
|
|
|
166
|
-
>>> ulid.
|
|
199
|
+
>>> ulid.milliseconds
|
|
167
200
|
1588257207560
|
|
168
201
|
"""
|
|
169
202
|
return int.from_bytes(self.bytes[: constants.TIMESTAMP_LEN], byteorder="big")
|
|
@@ -233,22 +266,22 @@ class ULID:
|
|
|
233
266
|
def __lt__(self, other: Any) -> bool:
|
|
234
267
|
if isinstance(other, ULID):
|
|
235
268
|
return self.bytes < other.bytes
|
|
236
|
-
|
|
269
|
+
if isinstance(other, int):
|
|
237
270
|
return int(self) < other
|
|
238
|
-
|
|
271
|
+
if isinstance(other, bytes):
|
|
239
272
|
return self.bytes < other
|
|
240
|
-
|
|
273
|
+
if isinstance(other, str):
|
|
241
274
|
return str(self) < other
|
|
242
275
|
return NotImplemented
|
|
243
276
|
|
|
244
|
-
def __eq__(self, other:
|
|
277
|
+
def __eq__(self, other: object) -> bool:
|
|
245
278
|
if isinstance(other, ULID):
|
|
246
279
|
return self.bytes == other.bytes
|
|
247
|
-
|
|
280
|
+
if isinstance(other, int):
|
|
248
281
|
return int(self) == other
|
|
249
|
-
|
|
282
|
+
if isinstance(other, bytes):
|
|
250
283
|
return self.bytes == other
|
|
251
|
-
|
|
284
|
+
if isinstance(other, str):
|
|
252
285
|
return str(self) == other
|
|
253
286
|
return NotImplemented
|
|
254
287
|
|
|
@@ -261,14 +294,12 @@ class ULID:
|
|
|
261
294
|
|
|
262
295
|
return core_schema.no_info_wrap_validator_function(
|
|
263
296
|
cls._pydantic_validate,
|
|
264
|
-
core_schema.union_schema(
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
]
|
|
271
|
-
),
|
|
297
|
+
core_schema.union_schema([
|
|
298
|
+
core_schema.is_instance_schema(ULID),
|
|
299
|
+
core_schema.no_info_plain_validator_function(ULID),
|
|
300
|
+
core_schema.str_schema(pattern=r"[A-Z0-9]{26}", min_length=26, max_length=26),
|
|
301
|
+
core_schema.bytes_schema(min_length=16, max_length=16),
|
|
302
|
+
]),
|
|
272
303
|
serialization=core_schema.to_string_ser_schema(
|
|
273
304
|
when_used="json-unless-none",
|
|
274
305
|
),
|
ulid/__main__.py
CHANGED
|
@@ -4,17 +4,21 @@ import argparse
|
|
|
4
4
|
import shutil
|
|
5
5
|
import sys
|
|
6
6
|
import textwrap
|
|
7
|
-
from collections.abc import Callable
|
|
8
|
-
from collections.abc import Sequence
|
|
9
7
|
from datetime import datetime
|
|
10
8
|
from functools import partial
|
|
11
9
|
from typing import Any
|
|
10
|
+
from typing import TYPE_CHECKING
|
|
12
11
|
from uuid import UUID
|
|
13
12
|
|
|
14
13
|
import ulid
|
|
15
14
|
from ulid import ULID
|
|
16
15
|
|
|
17
16
|
|
|
17
|
+
if TYPE_CHECKING: # pragma: no cover
|
|
18
|
+
from collections.abc import Callable
|
|
19
|
+
from collections.abc import Sequence
|
|
20
|
+
|
|
21
|
+
|
|
18
22
|
def make_parser(prog: str | None = None) -> argparse.ArgumentParser:
|
|
19
23
|
parser = argparse.ArgumentParser(
|
|
20
24
|
prog=prog,
|
|
@@ -135,30 +139,30 @@ def show(args: argparse.Namespace) -> str:
|
|
|
135
139
|
ulid: ULID = ULID.from_str(from_value_or_stdin(args.ulid))
|
|
136
140
|
if args.uuid:
|
|
137
141
|
return str(ulid.to_uuid())
|
|
138
|
-
|
|
142
|
+
if args.uuid4:
|
|
139
143
|
return str(ulid.to_uuid4())
|
|
140
|
-
|
|
144
|
+
if args.hex:
|
|
141
145
|
return ulid.hex
|
|
142
|
-
|
|
146
|
+
if args.int:
|
|
143
147
|
return str(int(ulid))
|
|
144
|
-
|
|
148
|
+
if args.timestamp:
|
|
145
149
|
return str(ulid.timestamp)
|
|
146
|
-
|
|
150
|
+
if args.datetime:
|
|
147
151
|
return ulid.datetime.isoformat()
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
).strip()
|
|
152
|
+
return textwrap.dedent(
|
|
153
|
+
f"""
|
|
154
|
+
ULID: {ulid!s}
|
|
155
|
+
Hex: {ulid.hex}
|
|
156
|
+
Int: {int(ulid)}
|
|
157
|
+
Timestamp: {ulid.timestamp}
|
|
158
|
+
Datetime: {ulid.datetime.isoformat()}
|
|
159
|
+
"""
|
|
160
|
+
).strip()
|
|
158
161
|
|
|
159
162
|
|
|
160
163
|
def entrypoint() -> None: # pragma: no cover
|
|
161
|
-
|
|
164
|
+
if (value := main(sys.argv[1:])) is not None:
|
|
165
|
+
print(value) # noqa: T201
|
|
162
166
|
|
|
163
167
|
|
|
164
168
|
if __name__ == "__main__": # pragma: no cover
|
ulid/base32.py
CHANGED
|
@@ -153,46 +153,42 @@ def encode_timestamp(binary: bytes) -> str:
|
|
|
153
153
|
if len(binary) != constants.TIMESTAMP_LEN:
|
|
154
154
|
raise ValueError("Timestamp value has to be exactly 6 bytes long.")
|
|
155
155
|
lut = ENCODE
|
|
156
|
-
return "".join(
|
|
157
|
-
[
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
]
|
|
169
|
-
)
|
|
156
|
+
return "".join([
|
|
157
|
+
lut[(binary[0] & 224) >> 5],
|
|
158
|
+
lut[(binary[0] & 31)],
|
|
159
|
+
lut[(binary[1] & 248) >> 3],
|
|
160
|
+
lut[((binary[1] & 7) << 2) | ((binary[2] & 192) >> 6)],
|
|
161
|
+
lut[((binary[2] & 62) >> 1)],
|
|
162
|
+
lut[((binary[2] & 1) << 4) | ((binary[3] & 240) >> 4)],
|
|
163
|
+
lut[((binary[3] & 15) << 1) | ((binary[4] & 128) >> 7)],
|
|
164
|
+
lut[(binary[4] & 124) >> 2],
|
|
165
|
+
lut[((binary[4] & 3) << 3) | ((binary[5] & 224) >> 5)],
|
|
166
|
+
lut[(binary[5] & 31)],
|
|
167
|
+
])
|
|
170
168
|
|
|
171
169
|
|
|
172
170
|
def encode_randomness(binary: bytes) -> str:
|
|
173
171
|
if len(binary) != constants.RANDOMNESS_LEN:
|
|
174
172
|
raise ValueError("Randomness value has to be exactly 10 bytes long.")
|
|
175
173
|
lut = ENCODE
|
|
176
|
-
return "".join(
|
|
177
|
-
[
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
]
|
|
195
|
-
)
|
|
174
|
+
return "".join([
|
|
175
|
+
lut[(binary[0] & 248) >> 3],
|
|
176
|
+
lut[((binary[0] & 7) << 2) | ((binary[1] & 192) >> 6)],
|
|
177
|
+
lut[(binary[1] & 62) >> 1],
|
|
178
|
+
lut[((binary[1] & 1) << 4) | ((binary[2] & 240) >> 4)],
|
|
179
|
+
lut[((binary[2] & 15) << 1) | ((binary[3] & 128) >> 7)],
|
|
180
|
+
lut[(binary[3] & 124) >> 2],
|
|
181
|
+
lut[((binary[3] & 3) << 3) | ((binary[4] & 224) >> 5)],
|
|
182
|
+
lut[(binary[4] & 31)],
|
|
183
|
+
lut[(binary[5] & 248) >> 3],
|
|
184
|
+
lut[((binary[5] & 7) << 2) | ((binary[6] & 192) >> 6)],
|
|
185
|
+
lut[(binary[6] & 62) >> 1],
|
|
186
|
+
lut[((binary[6] & 1) << 4) | ((binary[7] & 240) >> 4)],
|
|
187
|
+
lut[((binary[7] & 15) << 1) | ((binary[8] & 128) >> 7)],
|
|
188
|
+
lut[(binary[8] & 124) >> 2],
|
|
189
|
+
lut[((binary[8] & 3) << 3) | ((binary[9] & 224) >> 5)],
|
|
190
|
+
lut[(binary[9] & 31)],
|
|
191
|
+
])
|
|
196
192
|
|
|
197
193
|
|
|
198
194
|
def decode(encoded: str) -> bytes:
|
|
@@ -211,18 +207,16 @@ def decode_timestamp(encoded: str) -> bytes:
|
|
|
211
207
|
lut = DECODE
|
|
212
208
|
values: bytes = bytes(encoded, "ascii")
|
|
213
209
|
# https://github.com/ulid/spec?tab=readme-ov-file#overflow-errors-when-parsing-base32-strings
|
|
214
|
-
if lut[values[0]] > 7:
|
|
210
|
+
if lut[values[0]] > 7: # noqa: PLR2004
|
|
215
211
|
raise ValueError(f"Timestamp value {encoded} is too large and will overflow 128-bits.")
|
|
216
|
-
return bytes(
|
|
217
|
-
[
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
]
|
|
225
|
-
)
|
|
212
|
+
return bytes([
|
|
213
|
+
((lut[values[0]] << 5) | lut[values[1]]) & 0xFF,
|
|
214
|
+
((lut[values[2]] << 3) | (lut[values[3]] >> 2)) & 0xFF,
|
|
215
|
+
((lut[values[3]] << 6) | (lut[values[4]] << 1) | (lut[values[5]] >> 4)) & 0xFF,
|
|
216
|
+
((lut[values[5]] << 4) | (lut[values[6]] >> 1)) & 0xFF,
|
|
217
|
+
((lut[values[6]] << 7) | (lut[values[7]] << 2) | (lut[values[8]] >> 3)) & 0xFF,
|
|
218
|
+
((lut[values[8]] << 5) | (lut[values[9]])) & 0xFF,
|
|
219
|
+
])
|
|
226
220
|
|
|
227
221
|
|
|
228
222
|
def decode_randomness(encoded: str) -> bytes:
|
|
@@ -230,17 +224,15 @@ def decode_randomness(encoded: str) -> bytes:
|
|
|
230
224
|
raise ValueError("ULID randomness has to be exactly 16 characters long.")
|
|
231
225
|
lut = DECODE
|
|
232
226
|
values = bytes(encoded, "ascii")
|
|
233
|
-
return bytes(
|
|
234
|
-
[
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
]
|
|
246
|
-
)
|
|
227
|
+
return bytes([
|
|
228
|
+
((lut[values[0]] << 3) | (lut[values[1]] >> 2)) & 0xFF,
|
|
229
|
+
((lut[values[1]] << 6) | (lut[values[2]] << 1) | (lut[values[3]] >> 4)) & 0xFF,
|
|
230
|
+
((lut[values[3]] << 4) | (lut[values[4]] >> 1)) & 0xFF,
|
|
231
|
+
((lut[values[4]] << 7) | (lut[values[5]] << 2) | (lut[values[6]] >> 3)) & 0xFF,
|
|
232
|
+
((lut[values[6]] << 5) | (lut[values[7]])) & 0xFF,
|
|
233
|
+
((lut[values[8]] << 3) | (lut[values[9]] >> 2)) & 0xFF,
|
|
234
|
+
((lut[values[9]] << 6) | (lut[values[10]] << 1) | (lut[values[11]] >> 4)) & 0xFF,
|
|
235
|
+
((lut[values[11]] << 4) | (lut[values[12]] >> 1)) & 0xFF,
|
|
236
|
+
((lut[values[12]] << 7) | (lut[values[13]] << 2) | (lut[values[14]] >> 3)) & 0xFF,
|
|
237
|
+
((lut[values[14]] << 5) | (lut[values[15]])) & 0xFF,
|
|
238
|
+
])
|
ulid/constants.py
CHANGED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
ulid/__init__.py,sha256=tPyxks3cQzUiqkRdLgoDuunqiz090RiFkUfCMGxw7fU,9588
|
|
2
|
-
ulid/__main__.py,sha256=Cmg0NEz3GUZ2WhsZVpY3-Kq-JOkxJFaLsRNgj3Aosyw,5341
|
|
3
|
-
ulid/base32.py,sha256=cMj7Ye9qRdWn3n3hwaEXGMi2OkqmleAAxE20y7qmBqI,6184
|
|
4
|
-
ulid/constants.py,sha256=N_nw4W2ciXO8C1IeuO3YK3j4_x9QW-0KyvrSQUuM-G4,241
|
|
5
|
-
ulid/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
-
python_ulid-2.7.0.dist-info/METADATA,sha256=zJASFWDrkYVNYfnjjA70QGCC_kyzfyufEog6Ig4emH8,5782
|
|
7
|
-
python_ulid-2.7.0.dist-info/WHEEL,sha256=zEMcRr9Kr03x1ozGwg5v9NQBKn3kndp6LSoSlVg-jhU,87
|
|
8
|
-
python_ulid-2.7.0.dist-info/entry_points.txt,sha256=9214ghzfSgS8a-TjjlWDF7bI7KSaeewOCBKn5uz7bdU,50
|
|
9
|
-
python_ulid-2.7.0.dist-info/licenses/LICENSE,sha256=DiDPNhW5yUHIqaKowvX0c1c9xWiI0xW4ji8R9N2NJbw,1056
|
|
10
|
-
python_ulid-2.7.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|