Skip to content

Commit

Permalink
Implement ULID.parse() (#29)
Browse files Browse the repository at this point in the history
Co-authored-by: perro <hi@perrotuerto.blog>
  • Loading branch information
perrotuerto and perro authored Oct 11, 2024
1 parent 0804c91 commit ca8857c
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 3 deletions.
19 changes: 17 additions & 2 deletions tests/test_ulid.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,17 @@ def test_idempotency() -> None:
assert ULID.from_uuid(ulid.to_uuid()) == ulid
assert ULID.from_int(int(ulid)) == ulid
assert ULID.from_hex(ulid.hex) == ulid
assert ULID.parse(ulid) == ulid
assert ULID.parse(ulid.to_uuid()) == ulid
assert ULID.parse(str(ulid.to_uuid())) == ulid
assert ULID.parse(ulid.to_uuid().hex) == ulid
assert ULID.parse(str(ulid)) == ulid
assert ULID.parse(ulid.hex) == ulid
assert ULID.parse(ulid.to_uuid().int) == ulid
assert ULID.parse(ulid.milliseconds).milliseconds == ulid.milliseconds
assert ULID.parse(ulid.timestamp).timestamp == ulid.timestamp
assert ULID.parse(ulid.datetime).datetime == ulid.datetime
assert ULID.parse(ulid.bytes) == ulid


def test_to_uuid4() -> None:
Expand Down Expand Up @@ -160,10 +171,12 @@ def test_ulid_from_timestamp() -> None:
(ULID.from_str, "Z" * 26), # invalid timestamp
(ULID.from_int, "not-an-int"), # invalid type
(ULID.from_uuid, "not-a-uuid"), # invalid type
(ULID.parse, "not-a-uuid"), # invalid length
(ULID.parse, []), # invalid type
],
)
def test_ulid_invalid_input(constructor: Callable[[Params], ULID], value: Params) -> None:
with pytest.raises(ValueError): # noqa: PT011
with pytest.raises((ValueError, TypeError)):
constructor(value)


Expand Down Expand Up @@ -241,4 +254,6 @@ class Model(BaseModel):
} in model_json_schema["properties"]["ulid"]["anyOf"]
assert {
"type": "null",
} in model_json_schema["properties"]["ulid"]["anyOf"]
} in model_json_schema["properties"][
"ulid"
]["anyOf"]
34 changes: 33 additions & 1 deletion ulid/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@

__version__ = version("python-ulid")


T = TypeVar("T", bound=type)
R = TypeVar("R")

Expand Down Expand Up @@ -157,6 +156,39 @@ def from_int(cls: type[U], value: int) -> U:
"""Create a new :class:`ULID`-object from an `int`."""
return cls(int.to_bytes(value, constants.BYTES_LEN, "big"))

@classmethod
def parse(cls: type[U], value: Any) -> U:
"""Create a new :class:`ULID`-object from a given value.
.. note:: This method should only be used when the caller is trying to parse a ULID from
a value when they're unsure what format/primitive type it will be given in.
"""
if isinstance(value, ULID):
return value
if isinstance(value, uuid.UUID):
return cls.from_uuid(value)
if isinstance(value, str):
len_value = len(value)
if len_value in [36, 32]:
return cls.from_uuid(uuid.UUID(value))
if len_value == 26:
return cls.from_str(value)
if len_value == 16:
return cls.from_hex(value)
raise ValueError(f"Cannot parse ULID from string of length {len_value}")
if isinstance(value, int):
if len(str(value)) == 13:
return cls.from_timestamp(value)
else:
return cls.from_int(value)
if isinstance(value, float):
return cls.from_timestamp(value)
if isinstance(value, datetime):
return cls.from_datetime(value)
if isinstance(value, bytes):
return cls.from_bytes(value)
raise TypeError(f"Cannot parse ULID from type {type(value)}")

@property
def milliseconds(self) -> int:
"""The timestamp part as epoch time in milliseconds.
Expand Down

0 comments on commit ca8857c

Please sign in to comment.