pydantic-snowflake 0.1.1__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.
@@ -0,0 +1,216 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[codz]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py.cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ # .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ # Pipfile.lock
96
+
97
+ # UV
98
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ # uv.lock
102
+
103
+ # poetry
104
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
105
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
106
+ # commonly ignored for libraries.
107
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
108
+ # poetry.lock
109
+ # poetry.toml
110
+
111
+ # pdm
112
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
113
+ # pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
114
+ # https://pdm-project.org/en/latest/usage/project/#working-with-version-control
115
+ # pdm.lock
116
+ # pdm.toml
117
+ .pdm-python
118
+ .pdm-build/
119
+
120
+ # pixi
121
+ # Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
122
+ # pixi.lock
123
+ # Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
124
+ # in the .venv directory. It is recommended not to include this directory in version control.
125
+ .pixi
126
+
127
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
128
+ __pypackages__/
129
+
130
+ # Celery stuff
131
+ celerybeat-schedule
132
+ celerybeat.pid
133
+
134
+ # Redis
135
+ *.rdb
136
+ *.aof
137
+ *.pid
138
+
139
+ # RabbitMQ
140
+ mnesia/
141
+ rabbitmq/
142
+ rabbitmq-data/
143
+
144
+ # ActiveMQ
145
+ activemq-data/
146
+
147
+ # SageMath parsed files
148
+ *.sage.py
149
+
150
+ # Environments
151
+ .env
152
+ .envrc
153
+ .venv
154
+ env/
155
+ venv/
156
+ ENV/
157
+ env.bak/
158
+ venv.bak/
159
+
160
+ # Spyder project settings
161
+ .spyderproject
162
+ .spyproject
163
+
164
+ # Rope project settings
165
+ .ropeproject
166
+
167
+ # mkdocs documentation
168
+ /site
169
+
170
+ # mypy
171
+ .mypy_cache/
172
+ .dmypy.json
173
+ dmypy.json
174
+
175
+ # Pyre type checker
176
+ .pyre/
177
+
178
+ # pytype static type analyzer
179
+ .pytype/
180
+
181
+ # Cython debug symbols
182
+ cython_debug/
183
+
184
+ # PyCharm
185
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
186
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
187
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
188
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
189
+ # .idea/
190
+
191
+ # Abstra
192
+ # Abstra is an AI-powered process automation framework.
193
+ # Ignore directories containing user credentials, local state, and settings.
194
+ # Learn more at https://abstra.io/docs
195
+ .abstra/
196
+
197
+ # Visual Studio Code
198
+ # Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
199
+ # that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
200
+ # and can be added to the global gitignore or merged into this file. However, if you prefer,
201
+ # you could uncomment the following to ignore the entire vscode folder
202
+ # .vscode/
203
+
204
+ # Ruff stuff:
205
+ .ruff_cache/
206
+
207
+ # PyPI configuration file
208
+ .pypirc
209
+
210
+ # Marimo
211
+ marimo/_static/
212
+ marimo/_lsp/
213
+ __marimo__/
214
+
215
+ # Streamlit
216
+ .streamlit/secrets.toml
@@ -0,0 +1,14 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+
7
+ ## [0.1.1] - 2026-01-15
8
+ ### Changed
9
+ - Updated README.md.
10
+
11
+ ## [0.1.0] - 2026-01-15
12
+
13
+ ### Added
14
+ - Project initialization
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025-2026, Chih-Hao Chuang
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,108 @@
1
+ Metadata-Version: 2.4
2
+ Name: pydantic-snowflake
3
+ Version: 0.1.1
4
+ Summary: A Pydantic custom type for handling Snowflake IDs with ease.
5
+ Project-URL: Homepage, https://github.com/zhihao1021/pydantic_snowflake
6
+ Project-URL: GitHub, https://github.com/zhihao1021/pydantic_snowflake
7
+ Project-URL: Bug, https://github.com/zhihao1021/pydantic_snowflake/issues
8
+ Project-URL: Changelog, https://github.com/zhihao1021/pydantic_snowflake/blob/main/CHANGELOG.md
9
+ Author-email: Chih-Hao Chuang <contact@chih-hao.xyz>
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: custom type,data validation,pydantic,snowflake
13
+ Classifier: Development Status :: 5 - Production/Stable
14
+ Classifier: Framework :: Pydantic
15
+ Classifier: Framework :: Pydantic :: 2
16
+ Classifier: Intended Audience :: Developers
17
+ Classifier: License :: OSI Approved :: MIT License
18
+ Classifier: Operating System :: OS Independent
19
+ Classifier: Programming Language :: Python
20
+ Classifier: Programming Language :: Python :: 3
21
+ Classifier: Programming Language :: Python :: 3 :: Only
22
+ Classifier: Programming Language :: Python :: 3.7
23
+ Classifier: Programming Language :: Python :: 3.8
24
+ Classifier: Programming Language :: Python :: 3.9
25
+ Classifier: Programming Language :: Python :: 3.10
26
+ Classifier: Programming Language :: Python :: 3.11
27
+ Classifier: Programming Language :: Python :: 3.12
28
+ Classifier: Programming Language :: Python :: 3.13
29
+ Classifier: Programming Language :: Python :: 3.14
30
+ Classifier: Programming Language :: Python :: Implementation :: CPython
31
+ Classifier: Programming Language :: Python :: Implementation :: PyPy
32
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
33
+ Classifier: Typing :: Typed
34
+ Requires-Python: >=3.7
35
+ Requires-Dist: pydantic>=2.0.0
36
+ Description-Content-Type: text/markdown
37
+
38
+ # Pydantic Snowflake ID
39
+
40
+ A Pydantic custom type for validating and serializing Snowflake IDs.
41
+
42
+ ## Features
43
+ - Custom Pydantic type `SnowflakeId` for Snowflake ID validation.
44
+ - `SnowflakeGenerator` for generating unique Snowflake IDs.
45
+ - Supports custom epochs, worker IDs, and process IDs.
46
+ - Supports serialization and deserialization of pydantic models with Snowflake IDs.
47
+
48
+ ## Installation
49
+ You can install the package via pip:
50
+
51
+ ```bash
52
+ pip install -U pydantic-snowflakes
53
+ ```
54
+
55
+ ## Usage
56
+ ```python
57
+ from pydantic import BaseModel
58
+ from pydantic_snowflake import SnowflakeId, SnowflakeGenerator
59
+
60
+ from datetime import datetime
61
+
62
+ custom_epoch = datetime(2020, 1, 1)
63
+
64
+ class User(BaseModel):
65
+ id: SnowflakeId
66
+ name: str
67
+
68
+ # Generating a Snowflake ID
69
+ generator = SnowflakeGenerator(
70
+ epoch=custom_epoch,
71
+ worker_id=0,
72
+ process_id=0
73
+ )
74
+
75
+ new_id = generator.next()
76
+ user = User(id=new_id, name="Alice")
77
+ print(user)
78
+
79
+ # Generating multiple Snowflake IDs
80
+ for _id in [generator.next() for _ in range(5)]:
81
+ user = User(id=_id, name="Bob")
82
+ print(user)
83
+ ```
84
+
85
+ ## License
86
+ ```
87
+ MIT License
88
+
89
+ Copyright (c) 2025-2026, Chih-Hao Chuang
90
+
91
+ Permission is hereby granted, free of charge, to any person obtaining a copy
92
+ of this software and associated documentation files (the "Software"), to deal
93
+ in the Software without restriction, including without limitation the rights
94
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
95
+ copies of the Software, and to permit persons to whom the Software is
96
+ furnished to do so, subject to the following conditions:
97
+
98
+ The above copyright notice and this permission notice shall be included in all
99
+ copies or substantial portions of the Software.
100
+
101
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
102
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
103
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
104
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
105
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
106
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
107
+ SOFTWARE.
108
+ ```
@@ -0,0 +1,71 @@
1
+ # Pydantic Snowflake ID
2
+
3
+ A Pydantic custom type for validating and serializing Snowflake IDs.
4
+
5
+ ## Features
6
+ - Custom Pydantic type `SnowflakeId` for Snowflake ID validation.
7
+ - `SnowflakeGenerator` for generating unique Snowflake IDs.
8
+ - Supports custom epochs, worker IDs, and process IDs.
9
+ - Supports serialization and deserialization of pydantic models with Snowflake IDs.
10
+
11
+ ## Installation
12
+ You can install the package via pip:
13
+
14
+ ```bash
15
+ pip install -U pydantic-snowflakes
16
+ ```
17
+
18
+ ## Usage
19
+ ```python
20
+ from pydantic import BaseModel
21
+ from pydantic_snowflake import SnowflakeId, SnowflakeGenerator
22
+
23
+ from datetime import datetime
24
+
25
+ custom_epoch = datetime(2020, 1, 1)
26
+
27
+ class User(BaseModel):
28
+ id: SnowflakeId
29
+ name: str
30
+
31
+ # Generating a Snowflake ID
32
+ generator = SnowflakeGenerator(
33
+ epoch=custom_epoch,
34
+ worker_id=0,
35
+ process_id=0
36
+ )
37
+
38
+ new_id = generator.next()
39
+ user = User(id=new_id, name="Alice")
40
+ print(user)
41
+
42
+ # Generating multiple Snowflake IDs
43
+ for _id in [generator.next() for _ in range(5)]:
44
+ user = User(id=_id, name="Bob")
45
+ print(user)
46
+ ```
47
+
48
+ ## License
49
+ ```
50
+ MIT License
51
+
52
+ Copyright (c) 2025-2026, Chih-Hao Chuang
53
+
54
+ Permission is hereby granted, free of charge, to any person obtaining a copy
55
+ of this software and associated documentation files (the "Software"), to deal
56
+ in the Software without restriction, including without limitation the rights
57
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
58
+ copies of the Software, and to permit persons to whom the Software is
59
+ furnished to do so, subject to the following conditions:
60
+
61
+ The above copyright notice and this permission notice shall be included in all
62
+ copies or substantial portions of the Software.
63
+
64
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
65
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
66
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
67
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
68
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
69
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
70
+ SOFTWARE.
71
+ ```
@@ -0,0 +1,7 @@
1
+ from .snowflake import SnowflakeId
2
+ from .generator import SnowflakeGenerator
3
+
4
+ __all__ = [
5
+ "SnowflakeId",
6
+ "SnowflakeGenerator",
7
+ ]
@@ -0,0 +1,32 @@
1
+ from pydantic import GetCoreSchemaHandler, GetJsonSchemaHandler
2
+ from pydantic.json_schema import JsonSchemaValue
3
+ from pydantic_core import CoreSchema, core_schema
4
+
5
+ from datetime import datetime, timedelta
6
+ from typing import Any, Union
7
+
8
+ try:
9
+ from datetime import UTC # type: ignore
10
+ except ImportError:
11
+ from datetime import timezone
12
+ UTC = timezone.utc
13
+
14
+ DEFAULT_EPOCH_TS = datetime(
15
+ year=2015,
16
+ month=1,
17
+ day=1,
18
+ tzinfo=UTC
19
+ )
20
+
21
+ TS_LEN = 42
22
+ WORK_ID_LEN = 5
23
+ PROC_ID_LEN = 5
24
+ SEQ_LEN = 12
25
+
26
+ TS_OFFSET = 22
27
+ WORK_OFFSET = 17
28
+ PROC_OFFSET = 12
29
+
30
+ WORK_MASK = (1 << WORK_ID_LEN) - 1
31
+ PROC_MASK = (1 << PROC_ID_LEN) - 1
32
+ SEQ_MASK = (1 << SEQ_LEN) - 1
@@ -0,0 +1,102 @@
1
+ from datetime import datetime, timezone
2
+
3
+ try:
4
+ from datetime import UTC # type: ignore
5
+ except ImportError:
6
+ from datetime import timezone
7
+ UTC = timezone.utc
8
+
9
+ from .const import (
10
+ DEFAULT_EPOCH_TS,
11
+ TS_OFFSET,
12
+ WORK_OFFSET,
13
+ WORK_MASK,
14
+ PROC_OFFSET,
15
+ PROC_MASK,
16
+ SEQ_MASK,
17
+ )
18
+ from .snowflake import SnowflakeId
19
+
20
+ STARTUP_DATETIME = datetime.now(UTC)
21
+
22
+
23
+ class SnowflakeGenerator():
24
+ """
25
+ A Snowflake ID generator class.
26
+ """
27
+ _last_timestamp: int
28
+ _epoch: datetime
29
+ _worker: int
30
+ _process: int
31
+ _sequence: int
32
+
33
+ def __init__(
34
+ self,
35
+ epoch: datetime = DEFAULT_EPOCH_TS,
36
+ worker_id: int = 0,
37
+ process_id: int = 0,
38
+ ):
39
+ """
40
+ :param epoch: The epoch datetime for timestamp calculation.
41
+ :type epoch: datetime
42
+ :param worker_id: The worker ID.
43
+ :type worker_id: int
44
+ :param process_id: The process ID.
45
+ :type process_id: int
46
+ """
47
+ if worker_id < 0 or worker_id > WORK_MASK:
48
+ raise ValueError(f"worker_id must be between 0 and {WORK_MASK}")
49
+ if process_id < 0 or process_id > PROC_MASK:
50
+ raise ValueError(f"process_id must be between 0 and {PROC_MASK}")
51
+
52
+ self._last_timestamp = 0
53
+ self._epoch = epoch.astimezone(UTC)
54
+ self._worker = worker_id
55
+ self._process = process_id
56
+ self._sequence = 0
57
+
58
+ now_datetime = datetime.now(UTC)
59
+ if now_datetime < STARTUP_DATETIME:
60
+ raise ValueError("System clock moved backwards.")
61
+
62
+ if (now_datetime - self._epoch).total_seconds() < 0:
63
+ raise ValueError("Epoch is set in the future.")
64
+
65
+ while (now_datetime - STARTUP_DATETIME).total_seconds() < 0.001:
66
+ now_datetime = datetime.now(UTC)
67
+
68
+ def __iter__(self):
69
+ return self
70
+
71
+ def __next__(self) -> SnowflakeId:
72
+ delta = (datetime.now(UTC) - self._epoch)
73
+ current = int(delta.total_seconds() * 1000)
74
+
75
+ if current == self._last_timestamp:
76
+ if self._sequence < SEQ_MASK:
77
+ self._sequence += 1
78
+ else:
79
+ while current != self._last_timestamp:
80
+ delta = (datetime.now(UTC) - self._epoch)
81
+ current = int(delta.total_seconds() * 1000)
82
+ self._sequence = 0
83
+ else:
84
+ self._sequence = 0
85
+
86
+ self._last_timestamp = current
87
+
88
+ value = (current << TS_OFFSET)
89
+ value |= self._worker << WORK_OFFSET
90
+ value |= self._process << PROC_OFFSET
91
+ value |= self._sequence
92
+
93
+ return SnowflakeId(value=value, epoch=self._epoch)
94
+
95
+ def next(self) -> SnowflakeId:
96
+ """
97
+ Generate the next Snowflake ID.
98
+
99
+ :return: The next Snowflake ID.
100
+ :rtype: SnowflakeId
101
+ """
102
+ return self.__next__()
@@ -0,0 +1,175 @@
1
+ from pydantic import GetCoreSchemaHandler, GetJsonSchemaHandler
2
+ from pydantic.json_schema import JsonSchemaValue
3
+ from pydantic_core import CoreSchema, core_schema
4
+
5
+ from typing import Any, Union
6
+ from datetime import datetime as sys_datetime, timezone, timedelta
7
+
8
+ try:
9
+ from datetime import UTC # type: ignore
10
+ except ImportError:
11
+ from datetime import timezone
12
+ UTC = timezone.utc
13
+
14
+ from .const import (
15
+ DEFAULT_EPOCH_TS,
16
+ TS_OFFSET,
17
+ WORK_OFFSET,
18
+ WORK_MASK,
19
+ PROC_OFFSET,
20
+ PROC_MASK,
21
+ SEQ_MASK,
22
+ )
23
+
24
+
25
+ class SnowflakeId():
26
+ """
27
+ A Snowflake ID representation class for Pydantic models.
28
+
29
+ :var value: Snowflake ID's integer value
30
+ :vartype value: int
31
+ :var epoch: Epoch datetime used for timestamp calculation
32
+ :vartype epoch: datetime
33
+ """
34
+ value: int
35
+ epoch: sys_datetime
36
+
37
+ def __init__(self, value: Union[int, str], epoch: sys_datetime = DEFAULT_EPOCH_TS) -> None:
38
+ """
39
+ :param value: The Snowflake ID value.
40
+ :type value: Union[int, str]
41
+ :param epoch: The epoch datetime for timestamp calculation.
42
+ :type epoch: datetime
43
+ """
44
+ self.epoch = epoch.astimezone(UTC)
45
+
46
+ if isinstance(value, int):
47
+ self.value = value
48
+ return
49
+
50
+ try:
51
+ self.value = int(value)
52
+ except ValueError as e:
53
+ raise ValueError(f"Invalid Snowflake ID string: {value}") from e
54
+
55
+ def __int__(self) -> int:
56
+ return self.value
57
+
58
+ def __str__(self) -> str:
59
+ return str(self.value)
60
+
61
+ def __repr__(self) -> str:
62
+ return f"<SnowflakeId value={self.value}>"
63
+
64
+ def __eq__(self, obj: object) -> bool:
65
+ if isinstance(obj, self.__class__):
66
+ return self.value == obj.value
67
+
68
+ if isinstance(obj, int):
69
+ return self.value == obj
70
+
71
+ try:
72
+ other_value = int(obj) # type: ignore
73
+ return self.value == other_value
74
+ except:
75
+ return False
76
+
77
+ def __hash__(self) -> int:
78
+ return self.value
79
+
80
+ @property
81
+ def datetime(self) -> sys_datetime:
82
+ """
83
+ :return: The datetime representation of the Snowflake ID.
84
+ :rtype: datetime
85
+ """
86
+ mill_seconds = (self.value >> TS_OFFSET) % 1000
87
+ delta_seconds = (self.value >> TS_OFFSET) // 1000
88
+
89
+ return self.epoch + timedelta(
90
+ seconds=delta_seconds,
91
+ milliseconds=mill_seconds
92
+ )
93
+
94
+ @property
95
+ def timestamp(self) -> float:
96
+ """
97
+ :return: The timestamp representation of the Snowflake ID.
98
+ :rtype: float
99
+ """
100
+ dt = self.datetime
101
+ return dt.timestamp()
102
+
103
+ @property
104
+ def worker_id(self) -> int:
105
+ """
106
+ :return: The worker ID extracted from the Snowflake ID.
107
+ :rtype: int
108
+ """
109
+ return (self.value >> WORK_OFFSET) & WORK_MASK
110
+
111
+ @property
112
+ def process_id(self) -> int:
113
+ """
114
+ :return: The process ID extracted from the Snowflake ID.
115
+ :rtype: int
116
+ """
117
+ return (self.value >> PROC_OFFSET) & PROC_MASK
118
+
119
+ @property
120
+ def sequence(self) -> int:
121
+ """
122
+ :return: The sequence number extracted from the Snowflake ID.
123
+ :rtype: int
124
+ """
125
+ return self.value & SEQ_MASK
126
+
127
+ @classmethod
128
+ def __get_pydantic_json_schema__(
129
+ cls, core_schema: CoreSchema, handler: GetJsonSchemaHandler
130
+ ) -> JsonSchemaValue:
131
+ json_schema = handler(core_schema)
132
+ json_schema = handler.resolve_ref_schema(json_schema)
133
+ json_schema["type"] = "string"
134
+ json_schema["examples"] = ["6209533852516352"]
135
+ json_schema["title"] = "SnowflakeId"
136
+ return json_schema
137
+
138
+ @classmethod
139
+ def __get_pydantic_core_schema__(
140
+ cls, source_type: Any, handler: GetCoreSchemaHandler
141
+ ) -> CoreSchema:
142
+ def from_string(value: Any) -> SnowflakeId:
143
+ if isinstance(value, SnowflakeId):
144
+ return value
145
+ try:
146
+ return SnowflakeId(value=value)
147
+ except ValueError:
148
+ raise ValueError(f"Invalid value for SnowflakeId: {value}")
149
+
150
+ def to_string(value: SnowflakeId) -> str:
151
+ return str(value.value)
152
+
153
+ return core_schema.json_or_python_schema(
154
+ json_schema=core_schema.chain_schema([
155
+ core_schema.str_schema(),
156
+ core_schema.no_info_before_validator_function(
157
+ from_string,
158
+ core_schema.any_schema()
159
+ )
160
+ ]),
161
+ python_schema=core_schema.union_schema([
162
+ core_schema.is_instance_schema(cls),
163
+ core_schema.int_schema(),
164
+ core_schema.str_schema(),
165
+ core_schema.no_info_before_validator_function(
166
+ from_string,
167
+ core_schema.any_schema()
168
+ )
169
+ ]),
170
+ serialization=core_schema.plain_serializer_function_ser_schema(
171
+ to_string,
172
+ return_schema=core_schema.str_schema(),
173
+ when_used="json"
174
+ )
175
+ )
@@ -0,0 +1,46 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "pydantic-snowflake"
7
+ description = "A Pydantic custom type for handling Snowflake IDs with ease."
8
+ license = "MIT"
9
+ license-files = ["LICENSE"]
10
+ authors = [{ name = "Chih-Hao Chuang", email = "contact@chih-hao.xyz" }]
11
+ version = "0.1.1"
12
+ readme = "README.md"
13
+ keywords = ["pydantic", "snowflake", "custom type", "data validation"]
14
+ classifiers = [
15
+ "Development Status :: 5 - Production/Stable",
16
+ "Framework :: Pydantic",
17
+ "Framework :: Pydantic :: 2",
18
+ "Intended Audience :: Developers",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Operating System :: OS Independent",
21
+ "Programming Language :: Python",
22
+ "Programming Language :: Python :: 3",
23
+ "Programming Language :: Python :: 3 :: Only",
24
+ "Programming Language :: Python :: 3.7",
25
+ "Programming Language :: Python :: 3.8",
26
+ "Programming Language :: Python :: 3.9",
27
+ "Programming Language :: Python :: 3.10",
28
+ "Programming Language :: Python :: 3.11",
29
+ "Programming Language :: Python :: 3.12",
30
+ "Programming Language :: Python :: 3.13",
31
+ "Programming Language :: Python :: 3.14",
32
+ "Programming Language :: Python :: Implementation :: CPython",
33
+ "Programming Language :: Python :: Implementation :: PyPy",
34
+ "Topic :: Software Development :: Libraries :: Python Modules",
35
+ "Typing :: Typed"
36
+ ]
37
+ requires-python = ">=3.7"
38
+ dependencies = [
39
+ "pydantic>=2.0.0"
40
+ ]
41
+
42
+ [project.urls]
43
+ Homepage = "https://github.com/zhihao1021/pydantic_snowflake"
44
+ GitHub = "https://github.com/zhihao1021/pydantic_snowflake"
45
+ Bug = "https://github.com/zhihao1021/pydantic_snowflake/issues"
46
+ Changelog = "https://github.com/zhihao1021/pydantic_snowflake/blob/main/CHANGELOG.md"