pyuuid7 0.1.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.
@@ -0,0 +1,146 @@
1
+ name: Build and Publish to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - 'v*'
7
+ workflow_dispatch:
8
+ inputs:
9
+ publish:
10
+ description: 'Publish to PyPI'
11
+ required: true
12
+ default: false
13
+ type: boolean
14
+
15
+ env:
16
+ REGISTRY: ghcr.io
17
+
18
+ jobs:
19
+ # =======================================================
20
+ # JOB 1 — Build Wheels (Linux)
21
+ # =======================================================
22
+ build-linux:
23
+ runs-on: self-hosted
24
+
25
+ steps:
26
+ - name: Checkout repository
27
+ uses: actions/checkout@v4
28
+ with:
29
+ path: .
30
+
31
+ - name: Build wheels with manylinux
32
+ run: |
33
+ echo "🏗️ Building Linux wheels..."
34
+
35
+ docker run --rm \
36
+ --user "$(id -u):$(id -g)" \
37
+ --volumes-from "$(hostname)" \
38
+ -w "${{ github.workspace }}" \
39
+ ghcr.io/pyo3/maturin:latest \
40
+ build --release --out dist \
41
+ --interpreter python3.9 python3.10 python3.11 python3.12
42
+
43
+ echo "📦 Built wheels:"
44
+ ls -la dist/
45
+
46
+ echo "✅ Linux wheels build complete"
47
+
48
+ - name: Upload wheels
49
+ uses: actions/upload-artifact@v4
50
+ with:
51
+ name: wheels-linux
52
+ path: dist/*.whl
53
+
54
+ # =======================================================
55
+ # JOB 2 — Build Source Distribution
56
+ # =======================================================
57
+ build-sdist:
58
+ runs-on: self-hosted
59
+
60
+ steps:
61
+ - name: Checkout repository
62
+ uses: actions/checkout@v4
63
+ with:
64
+ path: .
65
+
66
+ - name: Build sdist
67
+ run: |
68
+ echo "📦 Building source distribution..."
69
+
70
+ docker run --rm \
71
+ --user "$(id -u):$(id -g)" \
72
+ --volumes-from "$(hostname)" \
73
+ -w "${{ github.workspace }}" \
74
+ ghcr.io/pyo3/maturin:latest \
75
+ sdist --out dist
76
+
77
+ echo "📦 Built sdist:"
78
+ ls -la dist/
79
+
80
+ echo "✅ Source distribution build complete"
81
+
82
+ - name: Upload sdist
83
+ uses: actions/upload-artifact@v4
84
+ with:
85
+ name: sdist
86
+ path: dist/*.tar.gz
87
+
88
+ # =======================================================
89
+ # JOB 3 — Publish to PyPI
90
+ # =======================================================
91
+ publish:
92
+ needs: [build-linux, build-sdist]
93
+ if: startsWith(github.ref, 'refs/tags/v') || github.event.inputs.publish == 'true'
94
+ runs-on: self-hosted
95
+
96
+ steps:
97
+ - name: Download all artifacts
98
+ uses: actions/download-artifact@v4
99
+ with:
100
+ path: dist
101
+ merge-multiple: true
102
+
103
+ - name: Show artifacts
104
+ run: |
105
+ echo "📦 Artifacts to publish:"
106
+ ls -la dist/
107
+
108
+ - name: Publish to PyPI
109
+ run: |
110
+ echo "🚀 Publishing to PyPI..."
111
+
112
+ docker run --rm \
113
+ --user "$(id -u):$(id -g)" \
114
+ --volumes-from "$(hostname)" \
115
+ -w "${{ github.workspace }}" \
116
+ -e MATURIN_PYPI_TOKEN="${{ secrets.PYPI_API_TOKEN }}" \
117
+ ghcr.io/pyo3/maturin:latest \
118
+ upload --skip-existing dist/*
119
+
120
+ echo "✅ Published to PyPI"
121
+
122
+ # =======================================================
123
+ # JOB 4 — Summary
124
+ # =======================================================
125
+ summary:
126
+ needs: [build-linux, build-sdist, publish]
127
+ if: always()
128
+ runs-on: ubuntu-latest
129
+
130
+ steps:
131
+ - name: Workflow Summary
132
+ run: |
133
+ echo "📋 Workflow Summary"
134
+ echo "=================="
135
+ echo "Build Linux: ${{ needs.build-linux.result }}"
136
+ echo "Build Sdist: ${{ needs.build-sdist.result }}"
137
+ echo "Publish: ${{ needs.publish.result || 'skipped' }}"
138
+ echo ""
139
+
140
+ if [ "${{ needs.publish.result }}" = "success" ]; then
141
+ echo "✅ Package published to PyPI successfully"
142
+ elif [ "${{ needs.publish.result }}" = "skipped" ]; then
143
+ echo "⏭️ Publish was skipped (not a tag push)"
144
+ else
145
+ echo "❌ Workflow failed"
146
+ fi
@@ -0,0 +1,19 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ .eggs/
6
+ dist/
7
+ build/
8
+ *.whl
9
+
10
+ # Rust
11
+ target/
12
+
13
+ # Virtual environment
14
+ .venv/
15
+
16
+ # IDE
17
+ .idea/
18
+ .vscode/
19
+ *.swp
@@ -0,0 +1,12 @@
1
+ repos:
2
+ - repo: https://github.com/pre-commit/pre-commit-hooks
3
+ rev: v6.0.0
4
+ hooks:
5
+ - id: end-of-file-fixer
6
+ - id: trailing-whitespace
7
+
8
+ - repo: https://github.com/doublify/pre-commit-rust
9
+ rev: v1.0
10
+ hooks:
11
+ - id: fmt
12
+ args: ["--manifest-path", "Cargo.toml", "--"]
@@ -0,0 +1,293 @@
1
+ # This file is automatically @generated by Cargo.
2
+ # It is not intended for manual editing.
3
+ version = 3
4
+
5
+ [[package]]
6
+ name = "autocfg"
7
+ version = "1.5.0"
8
+ source = "registry+https://github.com/rust-lang/crates.io-index"
9
+ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
10
+
11
+ [[package]]
12
+ name = "bumpalo"
13
+ version = "3.19.0"
14
+ source = "registry+https://github.com/rust-lang/crates.io-index"
15
+ checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
16
+
17
+ [[package]]
18
+ name = "cfg-if"
19
+ version = "1.0.4"
20
+ source = "registry+https://github.com/rust-lang/crates.io-index"
21
+ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
22
+
23
+ [[package]]
24
+ name = "getrandom"
25
+ version = "0.3.4"
26
+ source = "registry+https://github.com/rust-lang/crates.io-index"
27
+ checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
28
+ dependencies = [
29
+ "cfg-if",
30
+ "libc",
31
+ "r-efi",
32
+ "wasip2",
33
+ ]
34
+
35
+ [[package]]
36
+ name = "heck"
37
+ version = "0.5.0"
38
+ source = "registry+https://github.com/rust-lang/crates.io-index"
39
+ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
40
+
41
+ [[package]]
42
+ name = "indoc"
43
+ version = "2.0.7"
44
+ source = "registry+https://github.com/rust-lang/crates.io-index"
45
+ checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706"
46
+ dependencies = [
47
+ "rustversion",
48
+ ]
49
+
50
+ [[package]]
51
+ name = "js-sys"
52
+ version = "0.3.83"
53
+ source = "registry+https://github.com/rust-lang/crates.io-index"
54
+ checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8"
55
+ dependencies = [
56
+ "once_cell",
57
+ "wasm-bindgen",
58
+ ]
59
+
60
+ [[package]]
61
+ name = "libc"
62
+ version = "0.2.178"
63
+ source = "registry+https://github.com/rust-lang/crates.io-index"
64
+ checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091"
65
+
66
+ [[package]]
67
+ name = "memoffset"
68
+ version = "0.9.1"
69
+ source = "registry+https://github.com/rust-lang/crates.io-index"
70
+ checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
71
+ dependencies = [
72
+ "autocfg",
73
+ ]
74
+
75
+ [[package]]
76
+ name = "once_cell"
77
+ version = "1.21.3"
78
+ source = "registry+https://github.com/rust-lang/crates.io-index"
79
+ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
80
+
81
+ [[package]]
82
+ name = "portable-atomic"
83
+ version = "1.11.1"
84
+ source = "registry+https://github.com/rust-lang/crates.io-index"
85
+ checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483"
86
+
87
+ [[package]]
88
+ name = "proc-macro2"
89
+ version = "1.0.103"
90
+ source = "registry+https://github.com/rust-lang/crates.io-index"
91
+ checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
92
+ dependencies = [
93
+ "unicode-ident",
94
+ ]
95
+
96
+ [[package]]
97
+ name = "pyo3"
98
+ version = "0.23.5"
99
+ source = "registry+https://github.com/rust-lang/crates.io-index"
100
+ checksum = "7778bffd85cf38175ac1f545509665d0b9b92a198ca7941f131f85f7a4f9a872"
101
+ dependencies = [
102
+ "cfg-if",
103
+ "indoc",
104
+ "libc",
105
+ "memoffset",
106
+ "once_cell",
107
+ "portable-atomic",
108
+ "pyo3-build-config",
109
+ "pyo3-ffi",
110
+ "pyo3-macros",
111
+ "unindent",
112
+ ]
113
+
114
+ [[package]]
115
+ name = "pyo3-build-config"
116
+ version = "0.23.5"
117
+ source = "registry+https://github.com/rust-lang/crates.io-index"
118
+ checksum = "94f6cbe86ef3bf18998d9df6e0f3fc1050a8c5efa409bf712e661a4366e010fb"
119
+ dependencies = [
120
+ "once_cell",
121
+ "target-lexicon",
122
+ ]
123
+
124
+ [[package]]
125
+ name = "pyo3-ffi"
126
+ version = "0.23.5"
127
+ source = "registry+https://github.com/rust-lang/crates.io-index"
128
+ checksum = "e9f1b4c431c0bb1c8fb0a338709859eed0d030ff6daa34368d3b152a63dfdd8d"
129
+ dependencies = [
130
+ "libc",
131
+ "pyo3-build-config",
132
+ ]
133
+
134
+ [[package]]
135
+ name = "pyo3-macros"
136
+ version = "0.23.5"
137
+ source = "registry+https://github.com/rust-lang/crates.io-index"
138
+ checksum = "fbc2201328f63c4710f68abdf653c89d8dbc2858b88c5d88b0ff38a75288a9da"
139
+ dependencies = [
140
+ "proc-macro2",
141
+ "pyo3-macros-backend",
142
+ "quote",
143
+ "syn",
144
+ ]
145
+
146
+ [[package]]
147
+ name = "pyo3-macros-backend"
148
+ version = "0.23.5"
149
+ source = "registry+https://github.com/rust-lang/crates.io-index"
150
+ checksum = "fca6726ad0f3da9c9de093d6f116a93c1a38e417ed73bf138472cf4064f72028"
151
+ dependencies = [
152
+ "heck",
153
+ "proc-macro2",
154
+ "pyo3-build-config",
155
+ "quote",
156
+ "syn",
157
+ ]
158
+
159
+ [[package]]
160
+ name = "pyuuid7"
161
+ version = "0.1.0"
162
+ dependencies = [
163
+ "pyo3",
164
+ "uuid",
165
+ ]
166
+
167
+ [[package]]
168
+ name = "quote"
169
+ version = "1.0.42"
170
+ source = "registry+https://github.com/rust-lang/crates.io-index"
171
+ checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
172
+ dependencies = [
173
+ "proc-macro2",
174
+ ]
175
+
176
+ [[package]]
177
+ name = "r-efi"
178
+ version = "5.3.0"
179
+ source = "registry+https://github.com/rust-lang/crates.io-index"
180
+ checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
181
+
182
+ [[package]]
183
+ name = "rustversion"
184
+ version = "1.0.22"
185
+ source = "registry+https://github.com/rust-lang/crates.io-index"
186
+ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
187
+
188
+ [[package]]
189
+ name = "sha1_smol"
190
+ version = "1.0.1"
191
+ source = "registry+https://github.com/rust-lang/crates.io-index"
192
+ checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d"
193
+
194
+ [[package]]
195
+ name = "syn"
196
+ version = "2.0.111"
197
+ source = "registry+https://github.com/rust-lang/crates.io-index"
198
+ checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87"
199
+ dependencies = [
200
+ "proc-macro2",
201
+ "quote",
202
+ "unicode-ident",
203
+ ]
204
+
205
+ [[package]]
206
+ name = "target-lexicon"
207
+ version = "0.12.16"
208
+ source = "registry+https://github.com/rust-lang/crates.io-index"
209
+ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
210
+
211
+ [[package]]
212
+ name = "unicode-ident"
213
+ version = "1.0.22"
214
+ source = "registry+https://github.com/rust-lang/crates.io-index"
215
+ checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
216
+
217
+ [[package]]
218
+ name = "unindent"
219
+ version = "0.2.4"
220
+ source = "registry+https://github.com/rust-lang/crates.io-index"
221
+ checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3"
222
+
223
+ [[package]]
224
+ name = "uuid"
225
+ version = "1.19.0"
226
+ source = "registry+https://github.com/rust-lang/crates.io-index"
227
+ checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a"
228
+ dependencies = [
229
+ "getrandom",
230
+ "js-sys",
231
+ "sha1_smol",
232
+ "wasm-bindgen",
233
+ ]
234
+
235
+ [[package]]
236
+ name = "wasip2"
237
+ version = "1.0.1+wasi-0.2.4"
238
+ source = "registry+https://github.com/rust-lang/crates.io-index"
239
+ checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
240
+ dependencies = [
241
+ "wit-bindgen",
242
+ ]
243
+
244
+ [[package]]
245
+ name = "wasm-bindgen"
246
+ version = "0.2.106"
247
+ source = "registry+https://github.com/rust-lang/crates.io-index"
248
+ checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd"
249
+ dependencies = [
250
+ "cfg-if",
251
+ "once_cell",
252
+ "rustversion",
253
+ "wasm-bindgen-macro",
254
+ "wasm-bindgen-shared",
255
+ ]
256
+
257
+ [[package]]
258
+ name = "wasm-bindgen-macro"
259
+ version = "0.2.106"
260
+ source = "registry+https://github.com/rust-lang/crates.io-index"
261
+ checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3"
262
+ dependencies = [
263
+ "quote",
264
+ "wasm-bindgen-macro-support",
265
+ ]
266
+
267
+ [[package]]
268
+ name = "wasm-bindgen-macro-support"
269
+ version = "0.2.106"
270
+ source = "registry+https://github.com/rust-lang/crates.io-index"
271
+ checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40"
272
+ dependencies = [
273
+ "bumpalo",
274
+ "proc-macro2",
275
+ "quote",
276
+ "syn",
277
+ "wasm-bindgen-shared",
278
+ ]
279
+
280
+ [[package]]
281
+ name = "wasm-bindgen-shared"
282
+ version = "0.2.106"
283
+ source = "registry+https://github.com/rust-lang/crates.io-index"
284
+ checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4"
285
+ dependencies = [
286
+ "unicode-ident",
287
+ ]
288
+
289
+ [[package]]
290
+ name = "wit-bindgen"
291
+ version = "0.46.0"
292
+ source = "registry+https://github.com/rust-lang/crates.io-index"
293
+ checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
@@ -0,0 +1,16 @@
1
+ [package]
2
+ name = "pyuuid7"
3
+ version = "0.1.0"
4
+ edition = "2021"
5
+ license = "MIT"
6
+ description = "Fast UUID v4, v5, v7 generation in Rust for Python"
7
+ repository = "https://github.com/tarcisio/pyuuid7"
8
+ readme = "README.md"
9
+
10
+ [lib]
11
+ name = "pyuuid7"
12
+ crate-type = ["cdylib"]
13
+
14
+ [dependencies]
15
+ pyo3 = { version = "0.23", features = ["extension-module"] }
16
+ uuid = { version = "1.11", features = ["v4", "v5", "v7"] }
pyuuid7-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024
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.
pyuuid7-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,75 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyuuid7
3
+ Version: 0.1.0
4
+ Classifier: Development Status :: 4 - Beta
5
+ Classifier: Intended Audience :: Developers
6
+ Classifier: License :: OSI Approved :: MIT License
7
+ Classifier: Programming Language :: Rust
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.9
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: Implementation :: CPython
14
+ Classifier: Topic :: Software Development :: Libraries
15
+ License-File: LICENSE
16
+ Summary: Fast UUID v4, v5, v7 generation in Rust
17
+ Keywords: uuid,uuid7,uuid5,uuid4,rust
18
+ License: MIT
19
+ Requires-Python: >=3.9
20
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
21
+ Project-URL: Repository, https://github.com/tarcisio/pyuuid7
22
+
23
+ # PyUUID7
24
+
25
+ Fast UUID v4, v5, and v7 generation in Rust for Python.
26
+
27
+ ## Installation
28
+
29
+ ```bash
30
+ pip install pyuuid7
31
+ ```
32
+
33
+ ## Usage
34
+
35
+ ```python
36
+ import pyuuid7
37
+
38
+ # Generate UUIDs
39
+ uuid_v4 = pyuuid7.uuid4() # Random UUID
40
+ uuid_v7 = pyuuid7.uuid7() # Time-sortable UUID
41
+ uuid_v5 = pyuuid7.uuid5( # Name-based UUID
42
+ "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
43
+ "example.com"
44
+ )
45
+
46
+ # Validation and parsing
47
+ pyuuid7.is_valid("a1b2c3d4-...") # True/False
48
+ pyuuid7.get_version("a1b2c3d4-...") # 4, 5, 7, etc.
49
+ pyuuid7.parse("A1B2C3D4-...") # Lowercase canonical format
50
+ ```
51
+
52
+ ## UUID Versions
53
+
54
+ | Version | Description |
55
+ |---------|-------------|
56
+ | v4 | Random UUID (122 random bits) |
57
+ | v5 | Name-based UUID using SHA-1 |
58
+ | v7 | Time-sortable UUID (Unix timestamp + random) |
59
+
60
+ ## Development
61
+
62
+ ```bash
63
+ # Setup environment
64
+ mise install
65
+ mise run setup
66
+
67
+ # Build and test
68
+ mise run dev
69
+ mise run test
70
+ ```
71
+
72
+ ## License
73
+
74
+ MIT
75
+
@@ -0,0 +1,52 @@
1
+ # PyUUID7
2
+
3
+ Fast UUID v4, v5, and v7 generation in Rust for Python.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install pyuuid7
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```python
14
+ import pyuuid7
15
+
16
+ # Generate UUIDs
17
+ uuid_v4 = pyuuid7.uuid4() # Random UUID
18
+ uuid_v7 = pyuuid7.uuid7() # Time-sortable UUID
19
+ uuid_v5 = pyuuid7.uuid5( # Name-based UUID
20
+ "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
21
+ "example.com"
22
+ )
23
+
24
+ # Validation and parsing
25
+ pyuuid7.is_valid("a1b2c3d4-...") # True/False
26
+ pyuuid7.get_version("a1b2c3d4-...") # 4, 5, 7, etc.
27
+ pyuuid7.parse("A1B2C3D4-...") # Lowercase canonical format
28
+ ```
29
+
30
+ ## UUID Versions
31
+
32
+ | Version | Description |
33
+ |---------|-------------|
34
+ | v4 | Random UUID (122 random bits) |
35
+ | v5 | Name-based UUID using SHA-1 |
36
+ | v7 | Time-sortable UUID (Unix timestamp + random) |
37
+
38
+ ## Development
39
+
40
+ ```bash
41
+ # Setup environment
42
+ mise install
43
+ mise run setup
44
+
45
+ # Build and test
46
+ mise run dev
47
+ mise run test
48
+ ```
49
+
50
+ ## License
51
+
52
+ MIT
@@ -0,0 +1,26 @@
1
+ [tools]
2
+ rust = "stable"
3
+ python = "3.12"
4
+
5
+ [env]
6
+ VIRTUAL_ENV = "{{env.PWD}}/.venv"
7
+ PATH = "{{env.VIRTUAL_ENV}}/bin:{{env.PATH}}"
8
+
9
+ [tasks.setup]
10
+ run = """
11
+ python -m venv .venv
12
+ .venv/bin/pip install -U pip maturin pre-commit pytest
13
+ pre-commit install
14
+ """
15
+
16
+ [tasks.dev]
17
+ run = "maturin develop"
18
+
19
+ [tasks.build]
20
+ run = "maturin build --release"
21
+
22
+ [tasks.test]
23
+ run = "pytest tests/ -v"
24
+
25
+ [tasks.publish]
26
+ run = "maturin publish"
@@ -0,0 +1,31 @@
1
+ [build-system]
2
+ requires = ["maturin>=1.7,<2.0"]
3
+ build-backend = "maturin"
4
+
5
+ [project]
6
+ name = "pyuuid7"
7
+ version = "0.1.0"
8
+ description = "Fast UUID v4, v5, v7 generation in Rust"
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = { text = "MIT" }
12
+ keywords = ["uuid", "uuid7", "uuid5", "uuid4", "rust"]
13
+ classifiers = [
14
+ "Development Status :: 4 - Beta",
15
+ "Intended Audience :: Developers",
16
+ "License :: OSI Approved :: MIT License",
17
+ "Programming Language :: Rust",
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3.9",
20
+ "Programming Language :: Python :: 3.10",
21
+ "Programming Language :: Python :: 3.11",
22
+ "Programming Language :: Python :: 3.12",
23
+ "Programming Language :: Python :: Implementation :: CPython",
24
+ "Topic :: Software Development :: Libraries",
25
+ ]
26
+
27
+ [project.urls]
28
+ Repository = "https://github.com/tarcisio/pyuuid7"
29
+
30
+ [tool.maturin]
31
+ features = ["pyo3/extension-module"]
@@ -0,0 +1,171 @@
1
+ //! PyUUID7 - Fast UUID generation in Rust for Python
2
+ //!
3
+ //! Provides UUID v4 (random), v5 (name-based), and v7 (time-sortable) generation,
4
+ //! along with validation and version identification.
5
+
6
+ use pyo3::prelude::*;
7
+ use uuid::Uuid;
8
+
9
+ /// Generate a random UUID v4.
10
+ ///
11
+ /// Returns:
12
+ /// str: A new random UUID v4 string.
13
+ ///
14
+ /// Example:
15
+ /// >>> import pyuuid7
16
+ /// >>> pyuuid7.uuid4()
17
+ /// 'a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d'
18
+ #[pyfunction]
19
+ fn uuid4() -> String {
20
+ Uuid::new_v4().to_string()
21
+ }
22
+
23
+ /// Generate a name-based UUID v5 using SHA-1 hashing.
24
+ ///
25
+ /// Args:
26
+ /// namespace: A valid UUID string representing the namespace.
27
+ /// name: The name to hash within the namespace.
28
+ ///
29
+ /// Returns:
30
+ /// str: A deterministic UUID v5 string, or None if namespace is invalid.
31
+ ///
32
+ /// Example:
33
+ /// >>> import pyuuid7
34
+ /// >>> pyuuid7.uuid5("6ba7b810-9dad-11d1-80b4-00c04fd430c8", "example.com")
35
+ /// '2ed6657d-e927-568b-95e1-2665a8aea6a2'
36
+ #[pyfunction]
37
+ fn uuid5(namespace: &str, name: &str) -> Option<String> {
38
+ Uuid::parse_str(namespace)
39
+ .ok()
40
+ .map(|ns| Uuid::new_v5(&ns, name.as_bytes()).to_string())
41
+ }
42
+
43
+ /// Generate a time-sortable UUID v7.
44
+ ///
45
+ /// UUID v7 combines a Unix timestamp with random data, making it
46
+ /// suitable for database primary keys where ordering matters.
47
+ ///
48
+ /// Returns:
49
+ /// str: A new time-sortable UUID v7 string.
50
+ ///
51
+ /// Example:
52
+ /// >>> import pyuuid7
53
+ /// >>> pyuuid7.uuid7()
54
+ /// '019389a1-2b3c-7d4e-8f5a-6b7c8d9e0f1a'
55
+ #[pyfunction]
56
+ fn uuid7() -> String {
57
+ Uuid::now_v7().to_string()
58
+ }
59
+
60
+ /// Validate if a string is a valid UUID.
61
+ ///
62
+ /// Args:
63
+ /// uuid_str: The string to validate.
64
+ ///
65
+ /// Returns:
66
+ /// bool: True if the string is a valid UUID, False otherwise.
67
+ ///
68
+ /// Example:
69
+ /// >>> import pyuuid7
70
+ /// >>> pyuuid7.is_valid("a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d")
71
+ /// True
72
+ /// >>> pyuuid7.is_valid("invalid")
73
+ /// False
74
+ #[pyfunction]
75
+ fn is_valid(uuid_str: &str) -> bool {
76
+ Uuid::parse_str(uuid_str).is_ok()
77
+ }
78
+
79
+ /// Get the version number of a UUID.
80
+ ///
81
+ /// Args:
82
+ /// uuid_str: A valid UUID string.
83
+ ///
84
+ /// Returns:
85
+ /// int | None: The UUID version (1-8) or None if invalid.
86
+ ///
87
+ /// Example:
88
+ /// >>> import pyuuid7
89
+ /// >>> pyuuid7.get_version("a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d")
90
+ /// 4
91
+ #[pyfunction]
92
+ fn get_version(uuid_str: &str) -> Option<u8> {
93
+ Uuid::parse_str(uuid_str)
94
+ .ok()
95
+ .map(|u| u.get_version_num() as u8)
96
+ }
97
+
98
+ /// Parse a UUID string and return it in canonical lowercase format.
99
+ ///
100
+ /// Args:
101
+ /// uuid_str: A UUID string (can be uppercase or have different formatting).
102
+ ///
103
+ /// Returns:
104
+ /// str | None: The canonical UUID string or None if invalid.
105
+ ///
106
+ /// Example:
107
+ /// >>> import pyuuid7
108
+ /// >>> pyuuid7.parse("A1B2C3D4-E5F6-4A7B-8C9D-0E1F2A3B4C5D")
109
+ /// 'a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d'
110
+ #[pyfunction]
111
+ fn parse(uuid_str: &str) -> Option<String> {
112
+ Uuid::parse_str(uuid_str).ok().map(|u| u.to_string())
113
+ }
114
+
115
+ /// PyUUID7 Python module.
116
+ #[pymodule]
117
+ fn pyuuid7(m: &Bound<'_, PyModule>) -> PyResult<()> {
118
+ m.add_function(wrap_pyfunction!(uuid4, m)?)?;
119
+ m.add_function(wrap_pyfunction!(uuid5, m)?)?;
120
+ m.add_function(wrap_pyfunction!(uuid7, m)?)?;
121
+ m.add_function(wrap_pyfunction!(is_valid, m)?)?;
122
+ m.add_function(wrap_pyfunction!(get_version, m)?)?;
123
+ m.add_function(wrap_pyfunction!(parse, m)?)?;
124
+ Ok(())
125
+ }
126
+
127
+ #[cfg(test)]
128
+ mod tests {
129
+ use super::*;
130
+
131
+ #[test]
132
+ fn test_uuid4_format() {
133
+ let id = uuid4();
134
+ assert!(is_valid(&id));
135
+ assert_eq!(get_version(&id), Some(4));
136
+ }
137
+
138
+ #[test]
139
+ fn test_uuid5_deterministic() {
140
+ let ns = "6ba7b810-9dad-11d1-80b4-00c04fd430c8";
141
+ let id1 = uuid5(ns, "test");
142
+ let id2 = uuid5(ns, "test");
143
+ assert_eq!(id1, id2);
144
+ assert_eq!(get_version(&id1.unwrap()), Some(5));
145
+ }
146
+
147
+ #[test]
148
+ fn test_uuid7_format() {
149
+ let id = uuid7();
150
+ assert!(is_valid(&id));
151
+ assert_eq!(get_version(&id), Some(7));
152
+ }
153
+
154
+ #[test]
155
+ fn test_is_valid() {
156
+ assert!(is_valid("a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"));
157
+ assert!(!is_valid("invalid"));
158
+ assert!(!is_valid(""));
159
+ }
160
+
161
+ #[test]
162
+ fn test_parse() {
163
+ let upper = "A1B2C3D4-E5F6-4A7B-8C9D-0E1F2A3B4C5D";
164
+ let lower = parse(upper);
165
+ assert_eq!(
166
+ lower,
167
+ Some("a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d".to_string())
168
+ );
169
+ assert_eq!(parse("invalid"), None);
170
+ }
171
+ }
@@ -0,0 +1,103 @@
1
+ """Tests for pyuuid7 library."""
2
+
3
+ import re
4
+
5
+ import pyuuid7
6
+
7
+ UUID_REGEX = re.compile(
8
+ r"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
9
+ )
10
+
11
+
12
+ class TestUUID4:
13
+ """Tests for UUID v4 generation."""
14
+
15
+ def test_generates_valid_uuid(self):
16
+ result = pyuuid7.uuid4()
17
+ assert UUID_REGEX.match(result)
18
+
19
+ def test_version_is_4(self):
20
+ result = pyuuid7.uuid4()
21
+ assert pyuuid7.get_version(result) == 4
22
+
23
+ def test_generates_unique_values(self):
24
+ uuids = [pyuuid7.uuid4() for _ in range(100)]
25
+ assert len(set(uuids)) == 100
26
+
27
+
28
+ class TestUUID5:
29
+ """Tests for UUID v5 generation."""
30
+
31
+ DNS_NAMESPACE = "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
32
+
33
+ def test_generates_valid_uuid(self):
34
+ result = pyuuid7.uuid5(self.DNS_NAMESPACE, "example.com")
35
+ assert result is not None
36
+ assert UUID_REGEX.match(result)
37
+
38
+ def test_version_is_5(self):
39
+ result = pyuuid7.uuid5(self.DNS_NAMESPACE, "test")
40
+ assert pyuuid7.get_version(result) == 5
41
+
42
+ def test_is_deterministic(self):
43
+ result1 = pyuuid7.uuid5(self.DNS_NAMESPACE, "test")
44
+ result2 = pyuuid7.uuid5(self.DNS_NAMESPACE, "test")
45
+ assert result1 == result2
46
+
47
+ def test_different_names_produce_different_uuids(self):
48
+ result1 = pyuuid7.uuid5(self.DNS_NAMESPACE, "name1")
49
+ result2 = pyuuid7.uuid5(self.DNS_NAMESPACE, "name2")
50
+ assert result1 != result2
51
+
52
+ def test_invalid_namespace_returns_none(self):
53
+ result = pyuuid7.uuid5("invalid", "test")
54
+ assert result is None
55
+
56
+
57
+ class TestUUID7:
58
+ """Tests for UUID v7 generation."""
59
+
60
+ def test_generates_valid_uuid(self):
61
+ result = pyuuid7.uuid7()
62
+ assert UUID_REGEX.match(result)
63
+
64
+ def test_version_is_7(self):
65
+ result = pyuuid7.uuid7()
66
+ assert pyuuid7.get_version(result) == 7
67
+
68
+ def test_generates_unique_values(self):
69
+ uuids = [pyuuid7.uuid7() for _ in range(100)]
70
+ assert len(set(uuids)) == 100
71
+
72
+ def test_is_time_sortable(self):
73
+ uuids = [pyuuid7.uuid7() for _ in range(10)]
74
+ assert uuids == sorted(uuids)
75
+
76
+
77
+ class TestValidation:
78
+ """Tests for UUID validation functions."""
79
+
80
+ def test_is_valid_with_valid_uuid(self):
81
+ assert pyuuid7.is_valid("a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d")
82
+
83
+ def test_is_valid_with_invalid_uuid(self):
84
+ assert not pyuuid7.is_valid("invalid")
85
+ assert not pyuuid7.is_valid("")
86
+ assert not pyuuid7.is_valid("12345")
87
+
88
+ def test_get_version_returns_correct_version(self):
89
+ v4 = pyuuid7.uuid4()
90
+ v7 = pyuuid7.uuid7()
91
+ assert pyuuid7.get_version(v4) == 4
92
+ assert pyuuid7.get_version(v7) == 7
93
+
94
+ def test_get_version_invalid_returns_none(self):
95
+ assert pyuuid7.get_version("invalid") is None
96
+
97
+ def test_parse_normalizes_case(self):
98
+ upper = "A1B2C3D4-E5F6-4A7B-8C9D-0E1F2A3B4C5D"
99
+ result = pyuuid7.parse(upper)
100
+ assert result == "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"
101
+
102
+ def test_parse_invalid_returns_none(self):
103
+ assert pyuuid7.parse("invalid") is None