pUUID 1.0.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.
- puuid-1.0.0/PKG-INFO +109 -0
- puuid-1.0.0/README.md +87 -0
- puuid-1.0.0/pyproject.toml +76 -0
- puuid-1.0.0/src/puuid/__init__.py +33 -0
- puuid-1.0.0/src/puuid/base.py +616 -0
- puuid-1.0.0/src/puuid/py.typed +0 -0
- puuid-1.0.0/src/puuid/sqlalchemy.py +55 -0
puuid-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pUUID
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Prefixed UUIDs for Python with Pydantic & SQLAlchemy support.
|
|
5
|
+
Author: Jendrik Potyka, Fabian Preiss
|
|
6
|
+
Author-email: Jendrik Potyka <jpotyka@digon.io>, Fabian Preiss <fpreiss@digon.io>
|
|
7
|
+
License-Expression: LGPL-3.0-only
|
|
8
|
+
Classifier: Development Status :: 4 - Beta
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
12
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
13
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
14
|
+
Classifier: Topic :: Utilities
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Typing :: Typed
|
|
17
|
+
Requires-Dist: pydantic>=2.12.5
|
|
18
|
+
Maintainer: Jendrik A. Potyka, Fabian A. Preiss
|
|
19
|
+
Maintainer-email: Jendrik A. Potyka <jpotyka@digon.io>, Fabian A. Preiss <fpreiss@digon.io>
|
|
20
|
+
Requires-Python: >=3.14
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
|
|
23
|
+

|
|
24
|
+
|
|
25
|
+
**pUUID** - Prefixed UUIDs for Python with **Pydantic** & **SQLAlchemy** support.
|
|
26
|
+
|
|
27
|
+
[](https://gitlab.com/DigonIO/puuid)
|
|
28
|
+
[](https://github.com/DigonIO/puuid)
|
|
29
|
+
[](https://spdx.org/licenses/LGPL-3.0-only.html)
|
|
30
|
+
[](https://gitlab.com/DigonIO/puuid/-/pipelines)
|
|
31
|
+
[](https://gitlab.com/DigonIO/puuid/-/pipelines)
|
|
32
|
+
[](https://github.com/psf/black)
|
|
33
|
+
[](https://pycqa.github.io/isort/)
|
|
34
|
+
|
|
35
|
+
[](https://pypi.org/project/puuid/)
|
|
36
|
+
[](https://pypi.org/project/puuid/)
|
|
37
|
+
[](https://pepy.tech/project/puuid)
|
|
38
|
+
[](https://pepy.tech/project/puuid)
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
If you find the **pUUID** library beneficial, please consider supporting the project by [starring it on GitHub](https://github.com/DigonIO/pUUID).
|
|
43
|
+
|
|
44
|
+
[](https://github.com/DigonIO/pUUID)
|
|
45
|
+
|
|
46
|
+
# pUUID - Prefixed UUIDs for Python
|
|
47
|
+
|
|
48
|
+
## Features
|
|
49
|
+
|
|
50
|
+
- **Human-Friendly UUIDs:** `user_019b956e...` instead of just `019b956e...`
|
|
51
|
+
- **All UUID versions from [RFC 9562](https://www.rfc-editor.org/rfc/rfc95629).**
|
|
52
|
+
- **Pydantic support.** [(Read more)](https://puuid.digon.io/quick_start/#pydantic-integration)
|
|
53
|
+
- **SQLAlchemy support.** [(Read more)](https://puuid.digon.io/quick_start/#sqlalchemy-integration)
|
|
54
|
+
- **Strong type guarantees!**
|
|
55
|
+
|
|
56
|
+
## Installation
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pip install pUUID
|
|
60
|
+
|
|
61
|
+
# For SQLAlchemy support:
|
|
62
|
+
pip install 'pUUID[sqlalchemy]'
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Usage
|
|
66
|
+
|
|
67
|
+
Define a domain-specific ID by inheriting from a versioned base:
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
from typing import Literal
|
|
71
|
+
from puuid import PUUIDv7
|
|
72
|
+
|
|
73
|
+
class UserUUID(PUUIDv7[Literal["user"]]):
|
|
74
|
+
_prefix = "user"
|
|
75
|
+
|
|
76
|
+
# Generation
|
|
77
|
+
uid = UserUUID()
|
|
78
|
+
print(uid) # user_019b956e-ed25-70db-9d0a-0f30fb9047c2
|
|
79
|
+
|
|
80
|
+
# Deserialization
|
|
81
|
+
uid = UserUUID.from_string("user_019b956e-ed25-70db-9d0a-0f30fb9047c2")
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Resources
|
|
85
|
+
|
|
86
|
+
- [Online documentation](https://puuid.digon.io)
|
|
87
|
+
- [API Reference](https://puuid.digon.io/api_ref)
|
|
88
|
+
- [Changelog](https://puuid.digon.io/changelog)
|
|
89
|
+
- [Coverage Report](https://puuid.digon.io/coverage)
|
|
90
|
+
- [How to contribute](https://gitlab.com/DigonIO/puuid/-/blob/main/CONTRIBUTING.md)
|
|
91
|
+
|
|
92
|
+
## Alternatives
|
|
93
|
+
|
|
94
|
+
If you only need lexicographically sortable IDs and want to build the **SQLAlchemy** support yourself, these two projects might be for you:
|
|
95
|
+
|
|
96
|
+
- [**TypeID**](https://github.com/akhundMurad/typeid-python) - **pUUID** supports all **UUID** versions because it uses Python’s standard `uuid` library for **UUID** generation, while **TypeID** uses a custom generator that comes with performance improvements but only supports **UUIDv7**. **TypeID** does not support **SQLAlchemy** out of the box.
|
|
97
|
+
- [**UPID**](https://github.com/carderne/upid) - **UPID** implements a modified version of the **ULID** standard, which was designed before **UUIDv7** was available. **UPID** does not support **SQLAlchemy** out of the box.
|
|
98
|
+
|
|
99
|
+
## Sponsor
|
|
100
|
+
|
|
101
|
+

|
|
102
|
+
|
|
103
|
+
Digon.IO provides dev & data end-to-end consulting for SMEs and software companies. [(Website)](https://digon.io) [(Technical Blog)](https://digon.io/en/blog)
|
|
104
|
+
|
|
105
|
+
*The sponsor logo is the property of Digon.IO GmbH. Standard trademark and copyright restrictions apply to any use outside this repository.*
|
|
106
|
+
|
|
107
|
+
## License
|
|
108
|
+
|
|
109
|
+
- **Library source code:** Licensed under [LGPLv3](https://spdx.org/licenses/LGPL-3.0-only.html).
|
puuid-1.0.0/README.md
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+

|
|
2
|
+
|
|
3
|
+
**pUUID** - Prefixed UUIDs for Python with **Pydantic** & **SQLAlchemy** support.
|
|
4
|
+
|
|
5
|
+
[](https://gitlab.com/DigonIO/puuid)
|
|
6
|
+
[](https://github.com/DigonIO/puuid)
|
|
7
|
+
[](https://spdx.org/licenses/LGPL-3.0-only.html)
|
|
8
|
+
[](https://gitlab.com/DigonIO/puuid/-/pipelines)
|
|
9
|
+
[](https://gitlab.com/DigonIO/puuid/-/pipelines)
|
|
10
|
+
[](https://github.com/psf/black)
|
|
11
|
+
[](https://pycqa.github.io/isort/)
|
|
12
|
+
|
|
13
|
+
[](https://pypi.org/project/puuid/)
|
|
14
|
+
[](https://pypi.org/project/puuid/)
|
|
15
|
+
[](https://pepy.tech/project/puuid)
|
|
16
|
+
[](https://pepy.tech/project/puuid)
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
If you find the **pUUID** library beneficial, please consider supporting the project by [starring it on GitHub](https://github.com/DigonIO/pUUID).
|
|
21
|
+
|
|
22
|
+
[](https://github.com/DigonIO/pUUID)
|
|
23
|
+
|
|
24
|
+
# pUUID - Prefixed UUIDs for Python
|
|
25
|
+
|
|
26
|
+
## Features
|
|
27
|
+
|
|
28
|
+
- **Human-Friendly UUIDs:** `user_019b956e...` instead of just `019b956e...`
|
|
29
|
+
- **All UUID versions from [RFC 9562](https://www.rfc-editor.org/rfc/rfc95629).**
|
|
30
|
+
- **Pydantic support.** [(Read more)](https://puuid.digon.io/quick_start/#pydantic-integration)
|
|
31
|
+
- **SQLAlchemy support.** [(Read more)](https://puuid.digon.io/quick_start/#sqlalchemy-integration)
|
|
32
|
+
- **Strong type guarantees!**
|
|
33
|
+
|
|
34
|
+
## Installation
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pip install pUUID
|
|
38
|
+
|
|
39
|
+
# For SQLAlchemy support:
|
|
40
|
+
pip install 'pUUID[sqlalchemy]'
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Usage
|
|
44
|
+
|
|
45
|
+
Define a domain-specific ID by inheriting from a versioned base:
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
from typing import Literal
|
|
49
|
+
from puuid import PUUIDv7
|
|
50
|
+
|
|
51
|
+
class UserUUID(PUUIDv7[Literal["user"]]):
|
|
52
|
+
_prefix = "user"
|
|
53
|
+
|
|
54
|
+
# Generation
|
|
55
|
+
uid = UserUUID()
|
|
56
|
+
print(uid) # user_019b956e-ed25-70db-9d0a-0f30fb9047c2
|
|
57
|
+
|
|
58
|
+
# Deserialization
|
|
59
|
+
uid = UserUUID.from_string("user_019b956e-ed25-70db-9d0a-0f30fb9047c2")
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Resources
|
|
63
|
+
|
|
64
|
+
- [Online documentation](https://puuid.digon.io)
|
|
65
|
+
- [API Reference](https://puuid.digon.io/api_ref)
|
|
66
|
+
- [Changelog](https://puuid.digon.io/changelog)
|
|
67
|
+
- [Coverage Report](https://puuid.digon.io/coverage)
|
|
68
|
+
- [How to contribute](https://gitlab.com/DigonIO/puuid/-/blob/main/CONTRIBUTING.md)
|
|
69
|
+
|
|
70
|
+
## Alternatives
|
|
71
|
+
|
|
72
|
+
If you only need lexicographically sortable IDs and want to build the **SQLAlchemy** support yourself, these two projects might be for you:
|
|
73
|
+
|
|
74
|
+
- [**TypeID**](https://github.com/akhundMurad/typeid-python) - **pUUID** supports all **UUID** versions because it uses Python’s standard `uuid` library for **UUID** generation, while **TypeID** uses a custom generator that comes with performance improvements but only supports **UUIDv7**. **TypeID** does not support **SQLAlchemy** out of the box.
|
|
75
|
+
- [**UPID**](https://github.com/carderne/upid) - **UPID** implements a modified version of the **ULID** standard, which was designed before **UUIDv7** was available. **UPID** does not support **SQLAlchemy** out of the box.
|
|
76
|
+
|
|
77
|
+
## Sponsor
|
|
78
|
+
|
|
79
|
+

|
|
80
|
+
|
|
81
|
+
Digon.IO provides dev & data end-to-end consulting for SMEs and software companies. [(Website)](https://digon.io) [(Technical Blog)](https://digon.io/en/blog)
|
|
82
|
+
|
|
83
|
+
*The sponsor logo is the property of Digon.IO GmbH. Standard trademark and copyright restrictions apply to any use outside this repository.*
|
|
84
|
+
|
|
85
|
+
## License
|
|
86
|
+
|
|
87
|
+
- **Library source code:** Licensed under [LGPLv3](https://spdx.org/licenses/LGPL-3.0-only.html).
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "pUUID"
|
|
3
|
+
version = "1.0.0"
|
|
4
|
+
license = "LGPL-3.0-only"
|
|
5
|
+
classifiers = [
|
|
6
|
+
"Development Status :: 4 - Beta",
|
|
7
|
+
"Intended Audience :: Developers",
|
|
8
|
+
"License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)",
|
|
9
|
+
"Programming Language :: Python :: 3.14",
|
|
10
|
+
"Programming Language :: Python :: Implementation :: CPython",
|
|
11
|
+
"Topic :: Software Development :: Libraries",
|
|
12
|
+
"Topic :: Utilities",
|
|
13
|
+
"Operating System :: OS Independent",
|
|
14
|
+
"Typing :: Typed",
|
|
15
|
+
]
|
|
16
|
+
description = "Prefixed UUIDs for Python with Pydantic & SQLAlchemy support."
|
|
17
|
+
readme = "README.md"
|
|
18
|
+
authors = [
|
|
19
|
+
{ name = "Jendrik Potyka", email = "jpotyka@digon.io" },
|
|
20
|
+
{ name = "Fabian Preiss", email = "fpreiss@digon.io" },
|
|
21
|
+
]
|
|
22
|
+
maintainers = [
|
|
23
|
+
{ name = "Jendrik A. Potyka", email = "jpotyka@digon.io" },
|
|
24
|
+
{ name = "Fabian A. Preiss", email = "fpreiss@digon.io" },
|
|
25
|
+
]
|
|
26
|
+
requires-python = ">=3.14"
|
|
27
|
+
dependencies = ["pydantic>=2.12.5"]
|
|
28
|
+
|
|
29
|
+
[build-system]
|
|
30
|
+
requires = ["uv_build>=0.9.13,<0.10.0"]
|
|
31
|
+
build-backend = "uv_build"
|
|
32
|
+
|
|
33
|
+
[dependency-groups]
|
|
34
|
+
dev = [
|
|
35
|
+
"basedpyright>=1.37.0",
|
|
36
|
+
"black>=25.12.0",
|
|
37
|
+
"coverage>=7.13.1",
|
|
38
|
+
"isort>=7.0.0",
|
|
39
|
+
"marimo>=0.18.4",
|
|
40
|
+
"mkdocs>=1.6.1",
|
|
41
|
+
"mkdocs-coverage>=2.0.0",
|
|
42
|
+
"mkdocs-material[imaging]>=9.7.1",
|
|
43
|
+
"mkdocstrings[python]>=1.0.0",
|
|
44
|
+
"pdbpp>=0.11.7",
|
|
45
|
+
"pytest>=9.0.2",
|
|
46
|
+
"pytest-cov>=7.0.0",
|
|
47
|
+
"pytest-markdown-docs>=0.9.0",
|
|
48
|
+
"ruff>=0.14.10",
|
|
49
|
+
"sqlalchemy[mypy]>=2.0.45",
|
|
50
|
+
]
|
|
51
|
+
sqlalchemy = ["sqlalchemy>=2.0.45"]
|
|
52
|
+
|
|
53
|
+
[tool.black]
|
|
54
|
+
target-version = ['py313'] # black is still not properly supporting py314
|
|
55
|
+
|
|
56
|
+
[tool.isort]
|
|
57
|
+
py_version = 314
|
|
58
|
+
profile = "black"
|
|
59
|
+
|
|
60
|
+
[tool.mypy]
|
|
61
|
+
python_version = "3.14"
|
|
62
|
+
strict = true
|
|
63
|
+
warn_return_any = true
|
|
64
|
+
warn_unused_configs = true
|
|
65
|
+
disallow_any_generics = true
|
|
66
|
+
disallow_subclassing_any = true
|
|
67
|
+
|
|
68
|
+
[tool.pyright]
|
|
69
|
+
typeCheckingMode = "strict"
|
|
70
|
+
|
|
71
|
+
[tool.coverage.run]
|
|
72
|
+
branch = true
|
|
73
|
+
source = ["src/puuid"]
|
|
74
|
+
|
|
75
|
+
[tool.coverage.report]
|
|
76
|
+
show_missing = true
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""
|
|
2
|
+
pUUID - Prefixed UUIDs for Python with Pydantic & SQLAlchemy support.
|
|
3
|
+
|
|
4
|
+
Author: Jendrik Potyka, Fabian Preiss
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
__version__ = "1.0.0"
|
|
8
|
+
__author__ = "Jendrik Potyka, Fabian Preiss"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
from puuid.base import (
|
|
12
|
+
PUUID,
|
|
13
|
+
PUUIDError,
|
|
14
|
+
PUUIDv1,
|
|
15
|
+
PUUIDv3,
|
|
16
|
+
PUUIDv4,
|
|
17
|
+
PUUIDv5,
|
|
18
|
+
PUUIDv6,
|
|
19
|
+
PUUIDv7,
|
|
20
|
+
PUUIDv8,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"PUUID",
|
|
25
|
+
"PUUIDv1",
|
|
26
|
+
"PUUIDv3",
|
|
27
|
+
"PUUIDv4",
|
|
28
|
+
"PUUIDv5",
|
|
29
|
+
"PUUIDv6",
|
|
30
|
+
"PUUIDv7",
|
|
31
|
+
"PUUIDv8",
|
|
32
|
+
"PUUIDError",
|
|
33
|
+
]
|
|
@@ -0,0 +1,616 @@
|
|
|
1
|
+
"""
|
|
2
|
+
pUUID Base Implementation.
|
|
3
|
+
|
|
4
|
+
Provides the abstract base class and version-specific implementations for Prefixed UUIDs.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from abc import ABC, abstractmethod
|
|
8
|
+
from typing import Self, final, overload, override
|
|
9
|
+
from uuid import UUID, uuid1, uuid3, uuid4, uuid5, uuid6, uuid7, uuid8
|
|
10
|
+
|
|
11
|
+
from pydantic import GetCoreSchemaHandler
|
|
12
|
+
from pydantic_core import core_schema
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@final
|
|
16
|
+
class ERR_MSG:
|
|
17
|
+
UUID_VERSION_MISMATCH = "Expected 'UUID' with version '{expected}', got '{actual}'"
|
|
18
|
+
FACTORY_UNSUPPORTED = "'PUUID.factory' is only supported for 'PUUIDv1', 'PUUIDv4', 'PUUIDv6', 'PUUIDv7' and 'PUUIDv8'!"
|
|
19
|
+
PREFIX_DESERIALIZATION_ERROR = "Unable to deserialize prefix '{prefix}', separator '_' or UUID for '{classname}' from '{serial_puuid}'!"
|
|
20
|
+
INVALID_TYPE_FOR_SERIAL_PUUID = "'{classname}' can not be created from invalid type '{type}' with value '{value}'!"
|
|
21
|
+
INVALID_PUUIDv1_ARGS = "Invalid 'PUUIDv1' arguments: Provide either 'node' and 'clock_seq' or a 'uuid'!"
|
|
22
|
+
INVALID_PUUIDv3_ARGS = "Invalid 'PUUIDv3' arguments: Provide either 'namespace' and 'name' or a 'uuid'!"
|
|
23
|
+
INVALID_PUUIDv5_ARGS = "Invalid 'PUUIDv5' arguments: Provide either 'namespace' and 'name' or a 'uuid'!"
|
|
24
|
+
INVALID_PUUIDv6_ARGS = "Invalid 'PUUIDv6' arguments: Provide either 'node' and 'clock_seq' or a 'uuid'!"
|
|
25
|
+
INVALID_PUUIDv8_ARGS = (
|
|
26
|
+
"Invalid 'PUUIDv8' arguments: Provide either 'a', 'b' and 'c' or 'uuid'!"
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class PUUIDError(Exception):
|
|
31
|
+
"""Base exception for pUUID related errors."""
|
|
32
|
+
|
|
33
|
+
message: str
|
|
34
|
+
|
|
35
|
+
def __init__(self, message: str = "") -> None:
|
|
36
|
+
super().__init__(message)
|
|
37
|
+
self.message = message
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
################################################################################
|
|
41
|
+
#### PUUID
|
|
42
|
+
################################################################################
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class PUUID[TPrefix: str](ABC):
|
|
46
|
+
"""Abstract Base Class for Prefixed UUIDs."""
|
|
47
|
+
|
|
48
|
+
_prefix: TPrefix
|
|
49
|
+
_serial: str
|
|
50
|
+
_uuid: UUID
|
|
51
|
+
|
|
52
|
+
@abstractmethod
|
|
53
|
+
def __init__(self, *, uuid: UUID) -> None: ...
|
|
54
|
+
|
|
55
|
+
@classmethod
|
|
56
|
+
def prefix(cls) -> TPrefix:
|
|
57
|
+
"""
|
|
58
|
+
Return the defined prefix for the class.
|
|
59
|
+
|
|
60
|
+
Returns
|
|
61
|
+
-------
|
|
62
|
+
TPrefix
|
|
63
|
+
The prefix string.
|
|
64
|
+
"""
|
|
65
|
+
return cls._prefix
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def uuid(self) -> UUID:
|
|
69
|
+
"""
|
|
70
|
+
Return the underlying UUID object.
|
|
71
|
+
|
|
72
|
+
Returns
|
|
73
|
+
-------
|
|
74
|
+
UUID
|
|
75
|
+
The native UUID instance.
|
|
76
|
+
"""
|
|
77
|
+
return self._uuid
|
|
78
|
+
|
|
79
|
+
def to_string(self) -> str:
|
|
80
|
+
"""
|
|
81
|
+
Return the string representation of the Prefixed UUID.
|
|
82
|
+
|
|
83
|
+
Returns
|
|
84
|
+
-------
|
|
85
|
+
str
|
|
86
|
+
The formatted string (e.g., `<prefix>_<uuid-hex-string>`).
|
|
87
|
+
"""
|
|
88
|
+
return self._serial
|
|
89
|
+
|
|
90
|
+
@classmethod
|
|
91
|
+
def factory(cls) -> Self:
|
|
92
|
+
"""
|
|
93
|
+
Create a new instance using default generation.
|
|
94
|
+
|
|
95
|
+
Supported by version variants that allow generation without arguments.
|
|
96
|
+
|
|
97
|
+
Returns
|
|
98
|
+
-------
|
|
99
|
+
Self
|
|
100
|
+
A new instance of the pUUID class.
|
|
101
|
+
|
|
102
|
+
Raises
|
|
103
|
+
------
|
|
104
|
+
PUUIDError
|
|
105
|
+
If the variant does not support parameterless generation.
|
|
106
|
+
"""
|
|
107
|
+
raise PUUIDError(ERR_MSG.FACTORY_UNSUPPORTED)
|
|
108
|
+
|
|
109
|
+
@classmethod
|
|
110
|
+
def from_string(cls, serial_puuid: str) -> Self:
|
|
111
|
+
"""
|
|
112
|
+
Create a pUUID instance from its string representation.
|
|
113
|
+
|
|
114
|
+
Parameters
|
|
115
|
+
----------
|
|
116
|
+
serial_puuid : str
|
|
117
|
+
The prefixed UUID string (e.g., `user_550e8400-e29b...`).
|
|
118
|
+
|
|
119
|
+
Returns
|
|
120
|
+
-------
|
|
121
|
+
Self
|
|
122
|
+
The deserialized pUUID instance.
|
|
123
|
+
|
|
124
|
+
Raises
|
|
125
|
+
------
|
|
126
|
+
PUUIDError
|
|
127
|
+
If the string is malformed or the prefix does not match.
|
|
128
|
+
"""
|
|
129
|
+
try:
|
|
130
|
+
if "_" not in serial_puuid:
|
|
131
|
+
raise ValueError("Missing separator")
|
|
132
|
+
|
|
133
|
+
prefix, serialized_uuid = serial_puuid.split("_", 1)
|
|
134
|
+
|
|
135
|
+
if prefix != cls._prefix:
|
|
136
|
+
raise ValueError("Prefix mismatch")
|
|
137
|
+
|
|
138
|
+
uuid = UUID(serialized_uuid)
|
|
139
|
+
return cls(uuid=uuid)
|
|
140
|
+
|
|
141
|
+
except ValueError as err:
|
|
142
|
+
raise PUUIDError(
|
|
143
|
+
ERR_MSG.PREFIX_DESERIALIZATION_ERROR.format(
|
|
144
|
+
prefix=cls._prefix,
|
|
145
|
+
classname=cls.__name__,
|
|
146
|
+
serial_puuid=serial_puuid,
|
|
147
|
+
)
|
|
148
|
+
) from err
|
|
149
|
+
|
|
150
|
+
@override
|
|
151
|
+
def __str__(self) -> str:
|
|
152
|
+
return self._serial
|
|
153
|
+
|
|
154
|
+
@override
|
|
155
|
+
def __eq__(self, other: object) -> bool:
|
|
156
|
+
if isinstance(other, PUUID):
|
|
157
|
+
return self._serial == other._serial
|
|
158
|
+
return False
|
|
159
|
+
|
|
160
|
+
@override
|
|
161
|
+
def __hash__(self) -> int:
|
|
162
|
+
return hash((self._prefix, self._uuid))
|
|
163
|
+
|
|
164
|
+
@classmethod
|
|
165
|
+
def __get_pydantic_core_schema__(
|
|
166
|
+
cls,
|
|
167
|
+
_source_type: object,
|
|
168
|
+
_handler: GetCoreSchemaHandler,
|
|
169
|
+
) -> core_schema.CoreSchema:
|
|
170
|
+
def validate(value: object) -> PUUID[TPrefix]:
|
|
171
|
+
|
|
172
|
+
if isinstance(value, cls):
|
|
173
|
+
return value
|
|
174
|
+
|
|
175
|
+
if isinstance(value, str):
|
|
176
|
+
try:
|
|
177
|
+
return cls.from_string(value)
|
|
178
|
+
except PUUIDError as err:
|
|
179
|
+
raise ValueError(str(err)) from err
|
|
180
|
+
|
|
181
|
+
raise ValueError(
|
|
182
|
+
ERR_MSG.INVALID_TYPE_FOR_SERIAL_PUUID.format(
|
|
183
|
+
classname=cls.__name__, type=type(value), value=value
|
|
184
|
+
)
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
def serialize(value: PUUID[TPrefix]) -> str:
|
|
188
|
+
return value.to_string()
|
|
189
|
+
|
|
190
|
+
return core_schema.no_info_plain_validator_function(
|
|
191
|
+
validate,
|
|
192
|
+
serialization=core_schema.plain_serializer_function_ser_schema(
|
|
193
|
+
serialize,
|
|
194
|
+
return_schema=core_schema.str_schema(),
|
|
195
|
+
),
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
################################################################################
|
|
200
|
+
#### PUUIDv1
|
|
201
|
+
################################################################################
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
class PUUIDv1[TPrefix: str](PUUID[TPrefix]):
|
|
205
|
+
"""Prefixed UUID Version 1 (MAC address and time)."""
|
|
206
|
+
|
|
207
|
+
_uuid: UUID
|
|
208
|
+
_serial: str
|
|
209
|
+
|
|
210
|
+
@overload
|
|
211
|
+
def __init__(
|
|
212
|
+
self, *, node: int | None = None, clock_seq: int | None = None
|
|
213
|
+
) -> None: ...
|
|
214
|
+
|
|
215
|
+
@overload
|
|
216
|
+
def __init__(self, *, uuid: UUID) -> None: ...
|
|
217
|
+
|
|
218
|
+
def __init__(
|
|
219
|
+
self,
|
|
220
|
+
*,
|
|
221
|
+
node: int | None = None,
|
|
222
|
+
clock_seq: int | None = None,
|
|
223
|
+
uuid: UUID | None = None,
|
|
224
|
+
) -> None:
|
|
225
|
+
"""
|
|
226
|
+
Initialize a PUUIDv1.
|
|
227
|
+
|
|
228
|
+
Parameters
|
|
229
|
+
----------
|
|
230
|
+
node : int | None, optional
|
|
231
|
+
Hardware address. If None, `uuid1` generates a random value.
|
|
232
|
+
clock_seq : int | None, optional
|
|
233
|
+
Clock sequence.
|
|
234
|
+
uuid : UUID | None, optional
|
|
235
|
+
Existing UUID v1 instance.
|
|
236
|
+
|
|
237
|
+
Raises
|
|
238
|
+
------
|
|
239
|
+
PUUIDError
|
|
240
|
+
If arguments are inconsistent or the UUID version is incorrect.
|
|
241
|
+
"""
|
|
242
|
+
match node, clock_seq, uuid:
|
|
243
|
+
case int() | None, int() | None, None:
|
|
244
|
+
self._uuid = uuid1(node, clock_seq)
|
|
245
|
+
case None, None, UUID(version=1):
|
|
246
|
+
self._uuid = uuid
|
|
247
|
+
case None, None, UUID(version=version):
|
|
248
|
+
raise PUUIDError(
|
|
249
|
+
ERR_MSG.UUID_VERSION_MISMATCH.format(expected=1, actual=version)
|
|
250
|
+
)
|
|
251
|
+
case _:
|
|
252
|
+
raise PUUIDError(ERR_MSG.INVALID_PUUIDv1_ARGS)
|
|
253
|
+
|
|
254
|
+
self._serial = f"{self._prefix}_{self._uuid}"
|
|
255
|
+
|
|
256
|
+
@override
|
|
257
|
+
@classmethod
|
|
258
|
+
def factory(cls) -> Self:
|
|
259
|
+
"""
|
|
260
|
+
Create a new PUUIDv1 instance using current time and MAC address.
|
|
261
|
+
|
|
262
|
+
Returns
|
|
263
|
+
-------
|
|
264
|
+
Self
|
|
265
|
+
A new pUUID v1 instance.
|
|
266
|
+
"""
|
|
267
|
+
return cls()
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
################################################################################
|
|
271
|
+
#### PUUIDv3
|
|
272
|
+
################################################################################
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
class PUUIDv3[TPrefix: str](PUUID[TPrefix]):
|
|
276
|
+
"""Prefixed UUID Version 3 (MD5 hash of namespace and name)."""
|
|
277
|
+
|
|
278
|
+
_uuid: UUID
|
|
279
|
+
_serial: str
|
|
280
|
+
|
|
281
|
+
@overload
|
|
282
|
+
def __init__(self, *, namespace: UUID, name: str | bytes) -> None: ...
|
|
283
|
+
|
|
284
|
+
@overload
|
|
285
|
+
def __init__(self, *, uuid: UUID) -> None: ...
|
|
286
|
+
|
|
287
|
+
def __init__(
|
|
288
|
+
self,
|
|
289
|
+
*,
|
|
290
|
+
namespace: UUID | None = None,
|
|
291
|
+
name: str | bytes | None = None,
|
|
292
|
+
uuid: UUID | None = None,
|
|
293
|
+
) -> None:
|
|
294
|
+
"""
|
|
295
|
+
Initialize a PUUIDv3.
|
|
296
|
+
|
|
297
|
+
Parameters
|
|
298
|
+
----------
|
|
299
|
+
namespace : UUID | None, optional
|
|
300
|
+
Namespace UUID.
|
|
301
|
+
name : str | bytes | None, optional
|
|
302
|
+
The name used for hashing.
|
|
303
|
+
uuid : UUID | None, optional
|
|
304
|
+
Existing UUID v3 instance.
|
|
305
|
+
|
|
306
|
+
Raises
|
|
307
|
+
------
|
|
308
|
+
PUUIDError
|
|
309
|
+
If arguments are inconsistent or the UUID version is incorrect.
|
|
310
|
+
"""
|
|
311
|
+
match namespace, name, uuid:
|
|
312
|
+
case UUID(), str() | bytes(), None:
|
|
313
|
+
self._uuid = uuid3(namespace, name)
|
|
314
|
+
case None, None, UUID(version=3):
|
|
315
|
+
self._uuid = uuid
|
|
316
|
+
case None, None, UUID(version=version):
|
|
317
|
+
raise PUUIDError(
|
|
318
|
+
ERR_MSG.UUID_VERSION_MISMATCH.format(expected=3, actual=version)
|
|
319
|
+
)
|
|
320
|
+
case _:
|
|
321
|
+
raise PUUIDError(ERR_MSG.INVALID_PUUIDv3_ARGS)
|
|
322
|
+
|
|
323
|
+
self._serial = f"{self._prefix}_{self._uuid}"
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
################################################################################
|
|
327
|
+
#### PUUIDv4
|
|
328
|
+
################################################################################
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
class PUUIDv4[TPrefix: str](PUUID[TPrefix]):
|
|
332
|
+
"""Prefixed UUID Version 4 (randomly generated)."""
|
|
333
|
+
|
|
334
|
+
_uuid: UUID
|
|
335
|
+
_serial: str
|
|
336
|
+
|
|
337
|
+
def __init__(self, uuid: UUID | None = None) -> None:
|
|
338
|
+
"""
|
|
339
|
+
Initialize a PUUIDv4.
|
|
340
|
+
|
|
341
|
+
Parameters
|
|
342
|
+
----------
|
|
343
|
+
uuid : UUID | None, optional
|
|
344
|
+
Existing UUID v4 instance. If None, a new random UUID is generated.
|
|
345
|
+
|
|
346
|
+
Raises
|
|
347
|
+
------
|
|
348
|
+
PUUIDError
|
|
349
|
+
If the provided UUID is not version 4.
|
|
350
|
+
"""
|
|
351
|
+
if uuid is not None and uuid.version != 4:
|
|
352
|
+
raise PUUIDError(
|
|
353
|
+
ERR_MSG.UUID_VERSION_MISMATCH.format(expected=4, actual=uuid.version)
|
|
354
|
+
)
|
|
355
|
+
self._uuid = uuid if uuid else uuid4()
|
|
356
|
+
self._serial = f"{self._prefix}_{self._uuid}"
|
|
357
|
+
|
|
358
|
+
@override
|
|
359
|
+
@classmethod
|
|
360
|
+
def factory(cls) -> Self:
|
|
361
|
+
"""
|
|
362
|
+
Create a new PUUIDv4 instance using random generation.
|
|
363
|
+
|
|
364
|
+
Returns
|
|
365
|
+
-------
|
|
366
|
+
Self
|
|
367
|
+
A new pUUID v4 instance.
|
|
368
|
+
"""
|
|
369
|
+
return cls()
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
################################################################################
|
|
373
|
+
#### PUUIDv5
|
|
374
|
+
################################################################################
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
class PUUIDv5[TPrefix: str](PUUID[TPrefix]):
|
|
378
|
+
"""Prefixed UUID Version 5 (SHA-1 hash of namespace and name)."""
|
|
379
|
+
|
|
380
|
+
_uuid: UUID
|
|
381
|
+
_serial: str
|
|
382
|
+
|
|
383
|
+
@overload
|
|
384
|
+
def __init__(self, *, namespace: UUID, name: str | bytes) -> None: ...
|
|
385
|
+
|
|
386
|
+
@overload
|
|
387
|
+
def __init__(self, *, uuid: UUID) -> None: ...
|
|
388
|
+
|
|
389
|
+
def __init__(
|
|
390
|
+
self,
|
|
391
|
+
*,
|
|
392
|
+
namespace: UUID | None = None,
|
|
393
|
+
name: str | bytes | None = None,
|
|
394
|
+
uuid: UUID | None = None,
|
|
395
|
+
) -> None:
|
|
396
|
+
"""
|
|
397
|
+
Initialize a PUUIDv5.
|
|
398
|
+
|
|
399
|
+
Parameters
|
|
400
|
+
----------
|
|
401
|
+
namespace : UUID | None, optional
|
|
402
|
+
Namespace UUID.
|
|
403
|
+
name : str | bytes | None, optional
|
|
404
|
+
The name used for hashing.
|
|
405
|
+
uuid : UUID | None, optional
|
|
406
|
+
Existing UUID v5 instance.
|
|
407
|
+
|
|
408
|
+
Raises
|
|
409
|
+
------
|
|
410
|
+
PUUIDError
|
|
411
|
+
If arguments are inconsistent or the UUID version is incorrect.
|
|
412
|
+
"""
|
|
413
|
+
match namespace, name, uuid:
|
|
414
|
+
case UUID(), str() | bytes(), None:
|
|
415
|
+
self._uuid = uuid5(namespace, name)
|
|
416
|
+
case None, None, UUID(version=5):
|
|
417
|
+
self._uuid = uuid
|
|
418
|
+
case None, None, UUID(version=version):
|
|
419
|
+
raise PUUIDError(
|
|
420
|
+
ERR_MSG.UUID_VERSION_MISMATCH.format(expected=5, actual=version)
|
|
421
|
+
)
|
|
422
|
+
case _:
|
|
423
|
+
raise PUUIDError(ERR_MSG.INVALID_PUUIDv5_ARGS)
|
|
424
|
+
|
|
425
|
+
self._serial = f"{self._prefix}_{self._uuid}"
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
################################################################################
|
|
429
|
+
#### PUUIDv6
|
|
430
|
+
################################################################################
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
class PUUIDv6[TPrefix: str](PUUID[TPrefix]):
|
|
434
|
+
"""Prefixed UUID Version 6 (reordered v1 for DB locality)."""
|
|
435
|
+
|
|
436
|
+
_uuid: UUID
|
|
437
|
+
_serial: str
|
|
438
|
+
|
|
439
|
+
@overload
|
|
440
|
+
def __init__(
|
|
441
|
+
self, *, node: int | None = None, clock_seq: int | None = None
|
|
442
|
+
) -> None: ...
|
|
443
|
+
|
|
444
|
+
@overload
|
|
445
|
+
def __init__(self, *, uuid: UUID) -> None: ...
|
|
446
|
+
|
|
447
|
+
def __init__(
|
|
448
|
+
self,
|
|
449
|
+
*,
|
|
450
|
+
node: int | None = None,
|
|
451
|
+
clock_seq: int | None = None,
|
|
452
|
+
uuid: UUID | None = None,
|
|
453
|
+
) -> None:
|
|
454
|
+
"""
|
|
455
|
+
Initialize a PUUIDv6.
|
|
456
|
+
|
|
457
|
+
Parameters
|
|
458
|
+
----------
|
|
459
|
+
node : int | None, optional
|
|
460
|
+
Hardware address.
|
|
461
|
+
clock_seq : int | None, optional
|
|
462
|
+
Clock sequence.
|
|
463
|
+
uuid : UUID | None, optional
|
|
464
|
+
Existing UUID v6 instance.
|
|
465
|
+
|
|
466
|
+
Raises
|
|
467
|
+
------
|
|
468
|
+
PUUIDError
|
|
469
|
+
If arguments are inconsistent or the UUID version is incorrect.
|
|
470
|
+
"""
|
|
471
|
+
match node, clock_seq, uuid:
|
|
472
|
+
case int() | None, int() | None, None:
|
|
473
|
+
self._uuid = uuid6(node, clock_seq)
|
|
474
|
+
case None, None, UUID(version=6):
|
|
475
|
+
self._uuid = uuid
|
|
476
|
+
case None, None, UUID(version=version):
|
|
477
|
+
raise PUUIDError(
|
|
478
|
+
ERR_MSG.UUID_VERSION_MISMATCH.format(expected=6, actual=version)
|
|
479
|
+
)
|
|
480
|
+
case _:
|
|
481
|
+
raise PUUIDError(ERR_MSG.INVALID_PUUIDv6_ARGS)
|
|
482
|
+
|
|
483
|
+
self._serial = f"{self._prefix}_{self._uuid}"
|
|
484
|
+
|
|
485
|
+
@override
|
|
486
|
+
@classmethod
|
|
487
|
+
def factory(cls) -> Self:
|
|
488
|
+
"""
|
|
489
|
+
Create a new PUUIDv6 instance using reordered time-based generation.
|
|
490
|
+
|
|
491
|
+
Returns
|
|
492
|
+
-------
|
|
493
|
+
Self
|
|
494
|
+
A new pUUID v6 instance optimized for DB locality.
|
|
495
|
+
"""
|
|
496
|
+
return cls()
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
################################################################################
|
|
500
|
+
#### PUUIDv7
|
|
501
|
+
################################################################################
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
class PUUIDv7[TPrefix: str](PUUID[TPrefix]):
|
|
505
|
+
"""Prefixed UUID Version 7 (time-ordered)."""
|
|
506
|
+
|
|
507
|
+
_uuid: UUID
|
|
508
|
+
_serial: str
|
|
509
|
+
|
|
510
|
+
def __init__(self, uuid: UUID | None = None) -> None:
|
|
511
|
+
"""
|
|
512
|
+
Initialize a PUUIDv7.
|
|
513
|
+
|
|
514
|
+
Parameters
|
|
515
|
+
----------
|
|
516
|
+
uuid : UUID | None, optional
|
|
517
|
+
Existing UUID v7 instance. If None, a new time-ordered UUID is generated.
|
|
518
|
+
|
|
519
|
+
Raises
|
|
520
|
+
------
|
|
521
|
+
PUUIDError
|
|
522
|
+
If the provided UUID is not version 7.
|
|
523
|
+
"""
|
|
524
|
+
if uuid is not None and uuid.version != 7:
|
|
525
|
+
raise PUUIDError(
|
|
526
|
+
ERR_MSG.UUID_VERSION_MISMATCH.format(expected=7, actual=uuid.version)
|
|
527
|
+
)
|
|
528
|
+
self._uuid = uuid if uuid else uuid7()
|
|
529
|
+
self._serial = f"{self._prefix}_{self._uuid}"
|
|
530
|
+
|
|
531
|
+
@override
|
|
532
|
+
@classmethod
|
|
533
|
+
def factory(cls) -> Self:
|
|
534
|
+
"""
|
|
535
|
+
Create a new PUUIDv7 instance using time-ordered generation.
|
|
536
|
+
|
|
537
|
+
Returns
|
|
538
|
+
-------
|
|
539
|
+
Self
|
|
540
|
+
A new pUUID v7 instance.
|
|
541
|
+
"""
|
|
542
|
+
return cls()
|
|
543
|
+
|
|
544
|
+
|
|
545
|
+
################################################################################
|
|
546
|
+
#### PUUIDv8
|
|
547
|
+
################################################################################
|
|
548
|
+
|
|
549
|
+
|
|
550
|
+
class PUUIDv8[TPrefix: str](PUUID[TPrefix]):
|
|
551
|
+
"""Prefixed UUID Version 8 (custom implementation)."""
|
|
552
|
+
|
|
553
|
+
_uuid: UUID
|
|
554
|
+
_serial: str
|
|
555
|
+
|
|
556
|
+
@overload
|
|
557
|
+
def __init__(
|
|
558
|
+
self, *, a: int | None = None, b: int | None = None, c: int | None = None
|
|
559
|
+
) -> None: ...
|
|
560
|
+
|
|
561
|
+
@overload
|
|
562
|
+
def __init__(self, *, uuid: UUID) -> None: ...
|
|
563
|
+
|
|
564
|
+
def __init__(
|
|
565
|
+
self,
|
|
566
|
+
*,
|
|
567
|
+
a: int | None = None,
|
|
568
|
+
b: int | None = None,
|
|
569
|
+
c: int | None = None,
|
|
570
|
+
uuid: UUID | None = None,
|
|
571
|
+
) -> None:
|
|
572
|
+
"""
|
|
573
|
+
Initialize a PUUIDv8.
|
|
574
|
+
|
|
575
|
+
Parameters
|
|
576
|
+
----------
|
|
577
|
+
a : int | None, optional
|
|
578
|
+
First custom 48-bit value.
|
|
579
|
+
b : int | None, optional
|
|
580
|
+
Second custom 12-bit value.
|
|
581
|
+
c : int | None, optional
|
|
582
|
+
Third custom 62-bit value.
|
|
583
|
+
uuid : UUID | None, optional
|
|
584
|
+
Existing UUID v8 instance.
|
|
585
|
+
|
|
586
|
+
Raises
|
|
587
|
+
------
|
|
588
|
+
PUUIDError
|
|
589
|
+
If arguments are inconsistent or the UUID version is incorrect.
|
|
590
|
+
"""
|
|
591
|
+
match a, b, c, uuid:
|
|
592
|
+
case int() | None, int() | None, int() | None, None:
|
|
593
|
+
self._uuid = uuid8(a, b, c)
|
|
594
|
+
case None, None, None, UUID(version=8):
|
|
595
|
+
self._uuid = uuid
|
|
596
|
+
case None, None, None, UUID(version=version):
|
|
597
|
+
raise PUUIDError(
|
|
598
|
+
ERR_MSG.UUID_VERSION_MISMATCH.format(expected=8, actual=version)
|
|
599
|
+
)
|
|
600
|
+
case _:
|
|
601
|
+
raise PUUIDError(ERR_MSG.INVALID_PUUIDv8_ARGS)
|
|
602
|
+
|
|
603
|
+
self._serial = f"{self._prefix}_{self._uuid}"
|
|
604
|
+
|
|
605
|
+
@override
|
|
606
|
+
@classmethod
|
|
607
|
+
def factory(cls) -> Self:
|
|
608
|
+
"""
|
|
609
|
+
Create a new PUUIDv8 instance using custom generation.
|
|
610
|
+
|
|
611
|
+
Returns
|
|
612
|
+
-------
|
|
613
|
+
Self
|
|
614
|
+
A new pUUID v8 instance.
|
|
615
|
+
"""
|
|
616
|
+
return cls()
|
|
File without changes
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
from typing import final, override
|
|
2
|
+
|
|
3
|
+
from sqlalchemy.engine.interfaces import Dialect
|
|
4
|
+
from sqlalchemy.types import String, TypeDecorator
|
|
5
|
+
|
|
6
|
+
from puuid.base import PUUID
|
|
7
|
+
|
|
8
|
+
_SEPARATOR_LENGTH = 1
|
|
9
|
+
_UUID_LENGTH = 36
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@final
|
|
13
|
+
class SqlPUUID(TypeDecorator[PUUID[str]]):
|
|
14
|
+
"""
|
|
15
|
+
SQLAlchemy type for storing Prefixed UUIDs.
|
|
16
|
+
|
|
17
|
+
Maps a `PUUID` instance to a `VARCHAR` column in the database and
|
|
18
|
+
reconstructs the specific `PUUID` subclass on retrieval.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
impl = String
|
|
22
|
+
cache_ok = True
|
|
23
|
+
|
|
24
|
+
puuid_cls: type[PUUID[str]]
|
|
25
|
+
|
|
26
|
+
def __init__(self, puuid_cls: type[PUUID[str]], prefix_length: int = 4) -> None:
|
|
27
|
+
"""
|
|
28
|
+
Initialize the SqlPUUID type.
|
|
29
|
+
|
|
30
|
+
Parameters
|
|
31
|
+
----------
|
|
32
|
+
puuid_cls : type[PUUID[str]]
|
|
33
|
+
The pUUID class (e.g., `UserUUID`) to associate with this column.
|
|
34
|
+
prefix_length : int, default 4
|
|
35
|
+
The length of the prefix string to calculate the column width.
|
|
36
|
+
"""
|
|
37
|
+
self.puuid_cls = puuid_cls
|
|
38
|
+
varchar_length = prefix_length + _SEPARATOR_LENGTH + _UUID_LENGTH
|
|
39
|
+
super().__init__(length=varchar_length)
|
|
40
|
+
|
|
41
|
+
@override
|
|
42
|
+
def process_bind_param(
|
|
43
|
+
self, value: PUUID[str] | None, dialect: Dialect
|
|
44
|
+
) -> str | None:
|
|
45
|
+
if value is None:
|
|
46
|
+
return None
|
|
47
|
+
return value.to_string()
|
|
48
|
+
|
|
49
|
+
@override
|
|
50
|
+
def process_result_value(
|
|
51
|
+
self, value: str | None, dialect: Dialect
|
|
52
|
+
) -> PUUID[str] | None:
|
|
53
|
+
if value is None:
|
|
54
|
+
return None
|
|
55
|
+
return self.puuid_cls.from_string(value)
|