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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: python-ulid
3
- Version: 2.7.0
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,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.24.2
2
+ Generator: hatchling 1.25.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
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 ValueError(message)
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: int | float) -> U:
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.timestamp
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
- elif isinstance(other, int):
269
+ if isinstance(other, int):
237
270
  return int(self) < other
238
- elif isinstance(other, bytes):
271
+ if isinstance(other, bytes):
239
272
  return self.bytes < other
240
- elif isinstance(other, str):
273
+ if isinstance(other, str):
241
274
  return str(self) < other
242
275
  return NotImplemented
243
276
 
244
- def __eq__(self, other: Any) -> bool:
277
+ def __eq__(self, other: object) -> bool:
245
278
  if isinstance(other, ULID):
246
279
  return self.bytes == other.bytes
247
- elif isinstance(other, int):
280
+ if isinstance(other, int):
248
281
  return int(self) == other
249
- elif isinstance(other, bytes):
282
+ if isinstance(other, bytes):
250
283
  return self.bytes == other
251
- elif isinstance(other, str):
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
- core_schema.is_instance_schema(ULID),
267
- core_schema.no_info_plain_validator_function(ULID),
268
- core_schema.str_schema(pattern=r"[A-Z0-9]{26}", min_length=26, max_length=26),
269
- core_schema.bytes_schema(min_length=16, max_length=16),
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
- elif args.uuid4:
142
+ if args.uuid4:
139
143
  return str(ulid.to_uuid4())
140
- elif args.hex:
144
+ if args.hex:
141
145
  return ulid.hex
142
- elif args.int:
146
+ if args.int:
143
147
  return str(int(ulid))
144
- elif args.timestamp:
148
+ if args.timestamp:
145
149
  return str(ulid.timestamp)
146
- elif args.datetime:
150
+ if args.datetime:
147
151
  return ulid.datetime.isoformat()
148
- else:
149
- return textwrap.dedent(
150
- f"""
151
- ULID: {ulid!s}
152
- Hex: {ulid.hex}
153
- Int: {int(ulid)}
154
- Timestamp: {ulid.timestamp}
155
- Datetime: {ulid.datetime.isoformat()}
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
- print(main(sys.argv[1:]))
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
- lut[(binary[0] & 224) >> 5],
159
- lut[(binary[0] & 31)],
160
- lut[(binary[1] & 248) >> 3],
161
- lut[((binary[1] & 7) << 2) | ((binary[2] & 192) >> 6)],
162
- lut[((binary[2] & 62) >> 1)],
163
- lut[((binary[2] & 1) << 4) | ((binary[3] & 240) >> 4)],
164
- lut[((binary[3] & 15) << 1) | ((binary[4] & 128) >> 7)],
165
- lut[(binary[4] & 124) >> 2],
166
- lut[((binary[4] & 3) << 3) | ((binary[5] & 224) >> 5)],
167
- lut[(binary[5] & 31)],
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
- lut[(binary[0] & 248) >> 3],
179
- lut[((binary[0] & 7) << 2) | ((binary[1] & 192) >> 6)],
180
- lut[(binary[1] & 62) >> 1],
181
- lut[((binary[1] & 1) << 4) | ((binary[2] & 240) >> 4)],
182
- lut[((binary[2] & 15) << 1) | ((binary[3] & 128) >> 7)],
183
- lut[(binary[3] & 124) >> 2],
184
- lut[((binary[3] & 3) << 3) | ((binary[4] & 224) >> 5)],
185
- lut[(binary[4] & 31)],
186
- lut[(binary[5] & 248) >> 3],
187
- lut[((binary[5] & 7) << 2) | ((binary[6] & 192) >> 6)],
188
- lut[(binary[6] & 62) >> 1],
189
- lut[((binary[6] & 1) << 4) | ((binary[7] & 240) >> 4)],
190
- lut[((binary[7] & 15) << 1) | ((binary[8] & 128) >> 7)],
191
- lut[(binary[8] & 124) >> 2],
192
- lut[((binary[8] & 3) << 3) | ((binary[9] & 224) >> 5)],
193
- lut[(binary[9] & 31)],
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
- ((lut[values[0]] << 5) | lut[values[1]]) & 0xFF,
219
- ((lut[values[2]] << 3) | (lut[values[3]] >> 2)) & 0xFF,
220
- ((lut[values[3]] << 6) | (lut[values[4]] << 1) | (lut[values[5]] >> 4)) & 0xFF,
221
- ((lut[values[5]] << 4) | (lut[values[6]] >> 1)) & 0xFF,
222
- ((lut[values[6]] << 7) | (lut[values[7]] << 2) | (lut[values[8]] >> 3)) & 0xFF,
223
- ((lut[values[8]] << 5) | (lut[values[9]])) & 0xFF,
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
- ((lut[values[0]] << 3) | (lut[values[1]] >> 2)) & 0xFF,
236
- ((lut[values[1]] << 6) | (lut[values[2]] << 1) | (lut[values[3]] >> 4)) & 0xFF,
237
- ((lut[values[3]] << 4) | (lut[values[4]] >> 1)) & 0xFF,
238
- ((lut[values[4]] << 7) | (lut[values[5]] << 2) | (lut[values[6]] >> 3)) & 0xFF,
239
- ((lut[values[6]] << 5) | (lut[values[7]])) & 0xFF,
240
- ((lut[values[8]] << 3) | (lut[values[9]] >> 2)) & 0xFF,
241
- ((lut[values[9]] << 6) | (lut[values[10]] << 1) | (lut[values[11]] >> 4)) & 0xFF,
242
- ((lut[values[11]] << 4) | (lut[values[12]] >> 1)) & 0xFF,
243
- ((lut[values[12]] << 7) | (lut[values[13]] << 2) | (lut[values[14]] >> 3)) & 0xFF,
244
- ((lut[values[14]] << 5) | (lut[values[15]])) & 0xFF,
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
@@ -8,3 +8,8 @@ BYTES_LEN = TIMESTAMP_LEN + RANDOMNESS_LEN
8
8
  TIMESTAMP_REPR_LEN = 10
9
9
  RANDOMNESS_REPR_LEN = 16
10
10
  REPR_LEN = TIMESTAMP_REPR_LEN + RANDOMNESS_REPR_LEN
11
+
12
+ INT_REPR_LEN = 37
13
+
14
+ HEX_REPR_LEN = 32
15
+ UUID_REPR_LEN = 36 # UUID with dash-separated segments
@@ -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,,