python-ulid 2.5.0__tar.gz → 2.7.0__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.
- {python_ulid-2.5.0 → python_ulid-2.7.0}/CHANGELOG.rst +17 -0
- {python_ulid-2.5.0 → python_ulid-2.7.0}/PKG-INFO +1 -1
- {python_ulid-2.5.0 → python_ulid-2.7.0}/tests/test_base32.py +1 -0
- {python_ulid-2.5.0 → python_ulid-2.7.0}/tests/test_ulid.py +42 -9
- {python_ulid-2.5.0 → python_ulid-2.7.0}/ulid/__init__.py +6 -0
- {python_ulid-2.5.0 → python_ulid-2.7.0}/ulid/base32.py +5 -0
- {python_ulid-2.5.0 → python_ulid-2.7.0}/.github/workflows/lint-and-test.yml +0 -0
- {python_ulid-2.5.0 → python_ulid-2.7.0}/.github/workflows/publish.yml +0 -0
- {python_ulid-2.5.0 → python_ulid-2.7.0}/.gitignore +0 -0
- {python_ulid-2.5.0 → python_ulid-2.7.0}/.pre-commit-config.yaml +0 -0
- {python_ulid-2.5.0 → python_ulid-2.7.0}/.readthedocs.yml +0 -0
- {python_ulid-2.5.0 → python_ulid-2.7.0}/LICENSE +0 -0
- {python_ulid-2.5.0 → python_ulid-2.7.0}/README.rst +0 -0
- {python_ulid-2.5.0 → python_ulid-2.7.0}/docs/Makefile +0 -0
- {python_ulid-2.5.0 → python_ulid-2.7.0}/docs/requirements.txt +0 -0
- {python_ulid-2.5.0 → python_ulid-2.7.0}/docs/source/api.rst +0 -0
- {python_ulid-2.5.0 → python_ulid-2.7.0}/docs/source/changelog.rst +0 -0
- {python_ulid-2.5.0 → python_ulid-2.7.0}/docs/source/conf.py +0 -0
- {python_ulid-2.5.0 → python_ulid-2.7.0}/docs/source/index.rst +0 -0
- {python_ulid-2.5.0 → python_ulid-2.7.0}/hatch.toml +0 -0
- {python_ulid-2.5.0 → python_ulid-2.7.0}/logo.png +0 -0
- {python_ulid-2.5.0 → python_ulid-2.7.0}/pyproject.toml +0 -0
- {python_ulid-2.5.0 → python_ulid-2.7.0}/tests/__init__.py +0 -0
- {python_ulid-2.5.0 → python_ulid-2.7.0}/tests/test_cli.py +0 -0
- {python_ulid-2.5.0 → python_ulid-2.7.0}/ulid/__main__.py +0 -0
- {python_ulid-2.5.0 → python_ulid-2.7.0}/ulid/constants.py +0 -0
- {python_ulid-2.5.0 → python_ulid-2.7.0}/ulid/py.typed +0 -0
|
@@ -5,6 +5,21 @@ Changelog
|
|
|
5
5
|
|
|
6
6
|
Versions follow `Semantic Versioning <http://www.semver.org>`_
|
|
7
7
|
|
|
8
|
+
`2.7.0`_ - 2024-06-17
|
|
9
|
+
---------------------
|
|
10
|
+
Changed
|
|
11
|
+
~~~~~~~
|
|
12
|
+
* Ensure that the validation of ULID's timestamp component aligns more closely with
|
|
13
|
+
the ULID specification.
|
|
14
|
+
|
|
15
|
+
`2.6.0`_ - 2024-05-26
|
|
16
|
+
---------------------
|
|
17
|
+
Changed
|
|
18
|
+
~~~~~~~
|
|
19
|
+
* Use stricter validation when a :class:`.ULID` value from user input. When using
|
|
20
|
+
:meth:`.ULID.from_str` we will check if the characters match the base32 alphabet. In general,
|
|
21
|
+
it is ensured that the timestamp part of the ULID is not out of range.
|
|
22
|
+
|
|
8
23
|
`2.5.0`_ - 2024-04-26
|
|
9
24
|
---------------------
|
|
10
25
|
|
|
@@ -159,6 +174,8 @@ Changed
|
|
|
159
174
|
* The package now has no external dependencies.
|
|
160
175
|
* The test-coverage has been raised to 100%.
|
|
161
176
|
|
|
177
|
+
.. _2.7.0: https://github.com/mdomke/python-ulid/compare/2.6.0...2.7.0
|
|
178
|
+
.. _2.6.0: https://github.com/mdomke/python-ulid/compare/2.5.0...2.6.0
|
|
162
179
|
.. _2.5.0: https://github.com/mdomke/python-ulid/compare/2.4.0...2.5.0
|
|
163
180
|
.. _2.4.0: https://github.com/mdomke/python-ulid/compare/2.3.0...2.4.0
|
|
164
181
|
.. _2.3.0: https://github.com/mdomke/python-ulid/compare/2.2.0...2.3.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: python-ulid
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.7.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
|
|
@@ -21,6 +21,7 @@ from ulid import constants
|
|
|
21
21
|
(base32.decode, "A" * (constants.REPR_LEN + 1)),
|
|
22
22
|
(base32.decode_timestamp, "A" * (constants.TIMESTAMP_REPR_LEN - 1)),
|
|
23
23
|
(base32.decode_timestamp, "A" * (constants.TIMESTAMP_REPR_LEN + 1)),
|
|
24
|
+
(base32.decode_timestamp, "Z" * constants.TIMESTAMP_REPR_LEN),
|
|
24
25
|
(base32.decode_randomness, "A" * (constants.RANDOMNESS_REPR_LEN - 1)),
|
|
25
26
|
(base32.decode_randomness, "A" * (constants.RANDOMNESS_REPR_LEN + 1)),
|
|
26
27
|
],
|
|
@@ -149,15 +149,17 @@ Params = Union[bytes, str, int, float]
|
|
|
149
149
|
@pytest.mark.parametrize(
|
|
150
150
|
("constructor", "value"),
|
|
151
151
|
[
|
|
152
|
-
(ULID, b"sdf"),
|
|
153
|
-
(ULID.from_timestamp, b"not-a-timestamp"),
|
|
154
|
-
(ULID.from_datetime, time.time()),
|
|
155
|
-
(ULID.from_bytes, b"not-enough"),
|
|
156
|
-
(ULID.from_bytes, 123),
|
|
157
|
-
(ULID.from_str, "not-enough"),
|
|
158
|
-
(ULID.from_str, 123),
|
|
159
|
-
(ULID.
|
|
160
|
-
(ULID.
|
|
152
|
+
(ULID, b"sdf"), # invalid length
|
|
153
|
+
(ULID.from_timestamp, b"not-a-timestamp"), # invalid type
|
|
154
|
+
(ULID.from_datetime, time.time()), # invalid type
|
|
155
|
+
(ULID.from_bytes, b"not-enough"), # invalid length
|
|
156
|
+
(ULID.from_bytes, 123), # invalid type
|
|
157
|
+
(ULID.from_str, "not-enough"), # invalid length
|
|
158
|
+
(ULID.from_str, 123), # inavlid type
|
|
159
|
+
(ULID.from_str, "notavalidulidnotavalidulid"), # invalid alphabet
|
|
160
|
+
(ULID.from_str, "Z" * 26), # invalid timestamp
|
|
161
|
+
(ULID.from_int, "not-an-int"), # invalid type
|
|
162
|
+
(ULID.from_uuid, "not-a-uuid"), # invalid type
|
|
161
163
|
],
|
|
162
164
|
)
|
|
163
165
|
def test_ulid_invalid_input(constructor: Callable[[Params], ULID], value: Params) -> None:
|
|
@@ -165,6 +167,37 @@ def test_ulid_invalid_input(constructor: Callable[[Params], ULID], value: Params
|
|
|
165
167
|
constructor(value)
|
|
166
168
|
|
|
167
169
|
|
|
170
|
+
@pytest.mark.parametrize(
|
|
171
|
+
("constructor", "value"),
|
|
172
|
+
[
|
|
173
|
+
(ULID, b"\x00" * 16),
|
|
174
|
+
(ULID.from_timestamp, 0),
|
|
175
|
+
(ULID.from_bytes, b"\x00" * 16),
|
|
176
|
+
(ULID.from_str, "0" * 26),
|
|
177
|
+
(ULID.from_hex, "0" * 32),
|
|
178
|
+
(ULID.from_uuid, uuid.UUID("0" * 32)),
|
|
179
|
+
],
|
|
180
|
+
)
|
|
181
|
+
def test_ulid_min_input(constructor: Callable[[Params], ULID], value: Params) -> None:
|
|
182
|
+
constructor(value)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
@pytest.mark.parametrize(
|
|
186
|
+
("constructor", "value"),
|
|
187
|
+
[
|
|
188
|
+
(ULID, b"\xff" * 16),
|
|
189
|
+
(ULID.from_timestamp, 281474976710655),
|
|
190
|
+
(ULID.from_datetime, datetime.max.replace(tzinfo=timezone.utc)),
|
|
191
|
+
(ULID.from_bytes, b"\xff" * 16),
|
|
192
|
+
(ULID.from_str, "7" + "Z" * 25),
|
|
193
|
+
(ULID.from_hex, "f" * 32),
|
|
194
|
+
(ULID.from_uuid, uuid.UUID("f" * 32)),
|
|
195
|
+
],
|
|
196
|
+
)
|
|
197
|
+
def test_ulid_max_input(constructor: Callable[[Params], ULID], value: Params) -> None:
|
|
198
|
+
constructor(value)
|
|
199
|
+
|
|
200
|
+
|
|
168
201
|
def test_pydantic_protocol() -> None:
|
|
169
202
|
ulid = ULID()
|
|
170
203
|
|
|
@@ -71,6 +71,12 @@ class ULID:
|
|
|
71
71
|
>>> ulid = ULID()
|
|
72
72
|
>>> str(ulid)
|
|
73
73
|
'01E75PVKXA3GFABX1M1J9NZZNF'
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
value (bytes, None): A sequence of 16 bytes representing an encoded ULID.
|
|
77
|
+
|
|
78
|
+
Raises:
|
|
79
|
+
ValueError: If the provided value is not a valid encoded ULID.
|
|
74
80
|
"""
|
|
75
81
|
|
|
76
82
|
def __init__(self, value: bytes | None = None) -> None:
|
|
@@ -198,6 +198,8 @@ def encode_randomness(binary: bytes) -> str:
|
|
|
198
198
|
def decode(encoded: str) -> bytes:
|
|
199
199
|
if len(encoded) != constants.REPR_LEN:
|
|
200
200
|
raise ValueError("Encoded ULID has to be exactly 26 characters long.")
|
|
201
|
+
if any((c not in ENCODE) for c in encoded):
|
|
202
|
+
raise ValueError(f"Encoded ULID can only consist of letters in {ENCODE}.")
|
|
201
203
|
return decode_timestamp(encoded[: constants.TIMESTAMP_REPR_LEN]) + decode_randomness(
|
|
202
204
|
encoded[constants.TIMESTAMP_REPR_LEN :]
|
|
203
205
|
)
|
|
@@ -208,6 +210,9 @@ def decode_timestamp(encoded: str) -> bytes:
|
|
|
208
210
|
raise ValueError("ULID timestamp has to be exactly 10 characters long.")
|
|
209
211
|
lut = DECODE
|
|
210
212
|
values: bytes = bytes(encoded, "ascii")
|
|
213
|
+
# https://github.com/ulid/spec?tab=readme-ov-file#overflow-errors-when-parsing-base32-strings
|
|
214
|
+
if lut[values[0]] > 7:
|
|
215
|
+
raise ValueError(f"Timestamp value {encoded} is too large and will overflow 128-bits.")
|
|
211
216
|
return bytes(
|
|
212
217
|
[
|
|
213
218
|
((lut[values[0]] << 5) | lut[values[1]]) & 0xFF,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|