cuid2 2.0.4__tar.gz → 2.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.
cuid2-2.1.0/PKG-INFO ADDED
@@ -0,0 +1,140 @@
1
+ Metadata-Version: 2.4
2
+ Name: cuid2
3
+ Version: 2.1.0
4
+ Summary: Next generation GUIDs. Collision-resistant ids optimized for horizontal scaling and performance.
5
+ Keywords: crypt,security,uuid,guid,cuid,cryptography
6
+ Author: Will Gordon
7
+ Author-email: Will Gordon <dev@gordoncode.dev>
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Classifier: Development Status :: 5 - Production/Stable
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Programming Language :: Python :: 3.14
19
+ Classifier: Topic :: Security :: Cryptography
20
+ Classifier: Typing :: Typed
21
+ Requires-Python: >=3.10
22
+ Project-URL: repository, https://github.com/gordon-code/cuid2/
23
+ Description-Content-Type: text/markdown
24
+
25
+ # cuid2
26
+
27
+ [![Python](https://img.shields.io/badge/python-3.10+-blue.svg)](https://pypi.org/project/cuid2/)
28
+ [![PyPi](https://img.shields.io/pypi/v/cuid2.svg)](https://pypi.org/project/cuid2/)
29
+ [![PyPi](https://img.shields.io/pypi/dm/cuid2.svg)](https://pypi.org/project/cuid2/)
30
+
31
+ CUID2 for Python 3. Next generation GUIDs. Collision-resistant ids optimized for
32
+ horizontal scaling and performance.
33
+
34
+ A port of the [CUID2 reference implementation](https://github.com/paralleldrive/cuid2)
35
+ by [Parallel Drive](https://github.com/paralleldrive) to Python 3.
36
+
37
+ > :memo: Note: Originally taken from https://github.com/overflowdigital before it
38
+ > was unpublished. Thank you to @joshuathompsonlindley for your original contribution!
39
+
40
+ ## What is CUID2?
41
+
42
+ * Secure: It's not possible to guess the next ID.
43
+ * Collision resistant: It's extremely unlikely to generate the same ID twice.
44
+ * Horizontally scalable: Generate IDs on multiple machines without coordination.
45
+ * Offline-compatible: Generate IDs without a network connection.
46
+ * URL and name-friendly: No special characters.
47
+
48
+ ## Why?
49
+
50
+ For more information on the theory and usage of CUID2, see the
51
+ [following](https://github.com/paralleldrive/cuid2#why).
52
+
53
+ ## Improvements Over CUID
54
+
55
+ For more information on the improvements of CUID2 over CUID, see the
56
+ [following](https://github.com/paralleldrive/cuid2#improvements-over-cuid).
57
+
58
+
59
+ ## When to Use CUID2
60
+
61
+ * **Zero Timestamp or Creation-Rate Leakage**: Time-ordered formats (`uuid.uuid7()` in Python 3.14, PostgreSQL 18 `uuidv7()`, ULID) embed a 48-bit millisecond timestamp in their prefix, allowing anyone with a public ID to read exact creation times and infer business volume or request velocity ([RFC 9562 §8](https://www.rfc-editor.org/rfc/rfc9562.html#section-8)). CUID2 hashes nanosecond time, CSPRNG salt, a monotonic counter, and a host fingerprint through **SHA3-512**, revealing nothing about when or where an ID was generated.
62
+ * **HTML, CSS, and Lexical Safety (`[a-z][0-9a-z]+`)**: Every CUID2 is guaranteed to start with a lowercase ASCII letter (`[a-z]`) and contain only `[0-9a-z]`. By contrast, time-ordered **UUIDv7** (`019...`) starts with `0` until the year 2527 (and `0–9` until year 7541), **ULID** always starts with a timestamp digit (`0–7`), **UUIDv4** starts with a numeric digit (`0–9`) 62.5% of the time (`10/16` hex values), and **NanoID** starts with `0–9`, `_`, or `-` 18.75% of the time (`12/64` alphabet chars). CUID2 IDs are always valid unescaped HTML `id` attributes and CSS selectors, and copy cleanly with a single double-click.
63
+ * **Collision Resistance & Entropy Math**:
64
+ * **123.6-Bit Default Keyspace (`length=24`)**: 1 leading lowercase letter ($26$) followed by 23 base36 characters ($36^{23}$) yields $26 \times 36^{23} \approx \mathbf{1.61 \times 10^{37}}$ possible IDs ($\approx \mathbf{123.6\text{ bits}}$ of entropy)—slightly more than UUIDv4's 122 random bits in a 33% shorter string (24 vs. 36 chars). Reaching a 50% birthday-bound collision probability requires $\sqrt{2 \ln(2) \times 26 \times 36^{23}} \approx \mathbf{4.7 \times 10^{18}}$ IDs (generating **149 million IDs/sec continuously for 1,000 years**).
65
+ * **Up to 165.0 Bits (`BIG_LENGTH = 32`)**: At `length=32`, the keyspace expands to $26 \times 36^{31} \approx \mathbf{4.54 \times 10^{49}}$ IDs ($\approx \mathbf{165.0\text{ bits}}$; 50% collision bound at $\approx 7.9 \times 10^{24}$ IDs).
66
+ * **Histogram De-Biasing**: Because $2^{512} \approx 1.34 \times 10^{154}$ is not an exact power of 36, the most-significant base36 digit of a 512-bit SHA3-512 digest is biased toward low digits; CUID2 drops that leading hash character (`[1:]`) and prepends an independent uniform `[a-z]` letter so every character position has a uniform distribution.
67
+ * **Pre-Fork & Container Snapshot Resilience**: Combining `os.getpid()`, `socket.gethostname()`, environment variable keys, `time.time_ns()`, a randomly initialized monotonic counter (`INITIAL_COUNT_MAX = 476,782,367`, giving $\approx 25,700$ concurrent hosts before a 50% chance of two hosts even starting at the same counter value), and `secrets.SystemRandom` (`os.urandom`) provides defense-in-depth against PRNG or counter state collisions across forked worker processes (Gunicorn, uWSGI, Celery) and cloned VM/container snapshots.
68
+ * **Polyglot ORM Compatibility (Prisma & Drizzle)**: Generates 24-character base36 primary keys compatible with TypeScript/Node.js services using Prisma (`@default(cuid(2))` / `@onCreate(cuid(2))`) or Drizzle (`@paralleldrive/cuid2`), and replaces the deprecated CUIDv1 (`cuid`) format.
69
+
70
+ | Property | CUID2 (`cuid2`) | UUIDv7 (`uuid.uuid7`) | UUIDv4 (`uuid.uuid4`) | ULID (`python-ulid`) | NanoID (`nanoid`) |
71
+ | :--- | :--- | :--- | :--- | :--- | :--- |
72
+ | **Default Length** | **24 chars** (`2..32`) | 36 chars | 36 chars | 26 chars | 21 chars |
73
+ | **Alphabet** | `[a-z0-9]` (base36) | `[0-9a-f-]` (hex) | `[0-9a-f-]` (hex) | Crockford Base32 | `[A-Za-z0-9_-]` (url64) |
74
+ | **Starts with Letter?** | **Yes (100% `[a-z]`)** | **No** (`0` until yr 2527) | Not guaranteed (37.5% `a–f`) | **No** (always `0–7`) | Not guaranteed (81.25%) |
75
+ | **Double-Click Copyable?** | **Yes** | No (hyphens) | No (hyphens) | **Yes** | No (`-` breaks word) |
76
+ | **Entropy / Random Bits** | **~123.6 bits** (`165.0` at 32) | 74 bits / ms | 122 bits | 80 bits / ms | 126 bits |
77
+ | **50% Collision Bound** | **~4.7 × 10¹⁸** (`~7.9 × 10²⁴` at 32) | ~1.6 × 10¹¹ / ms | ~2.7 × 10¹⁸ | ~1.3 × 10¹² / ms | ~1.1 × 10¹⁹ |
78
+ | **Hides Creation Timestamp?** | **Yes (SHA3-512)** | No (leaks 48-bit ms) | **Yes** | No (leaks 48-bit ms) | **Yes** |
79
+ | **Fork / Snapshot Safe?** | **Yes (PID + host + CSPRNG)** | Counter can collide | OS CSPRNG only | Counter can collide | OS CSPRNG only |
80
+ | **B-Tree Index Locality** | Low (uniform hash) | **High (time-ordered)** | Low (uniform random) | **High (time-ordered)** | Low (uniform random) |
81
+
82
+ > **When to choose UUIDv7 instead:** Because CUID2 hashes its inputs via SHA3-512, IDs are uniformly distributed across the key space. For write-heavy relational tables where B-tree insert locality and 16-byte binary `UUID` storage matter more than hiding creation timestamps, prefer Python 3.14's `uuid.uuid7()` or PostgreSQL 18's `uuidv7()`.
83
+
84
+ ## Install
85
+ ```
86
+ pip install cuid2
87
+ ```
88
+
89
+ ## Usage
90
+
91
+ For most use cases—including Django, SQLAlchemy, and Pydantic model defaults—use the top-level `create_id` and `is_cuid` functions directly:
92
+ ```python
93
+ from cuid2 import create_id, is_cuid
94
+
95
+ my_cuid: str = create_id()
96
+ short_cuid: str = create_id(length=10)
97
+
98
+ print(is_cuid(my_cuid)) # True
99
+ ```
100
+
101
+ > **Tip for Django & ORM defaults:** Pass `default=create_id` directly on model fields (e.g., `models.CharField(max_length=24, default=create_id)`). Because `create_id` is a top-level function backed by a lazy singleton generator, Django `makemigrations` can serialize it directly and `Cuid()` initialization is deferred until the first ID is generated in each worker process.
102
+
103
+ You can also generate CUIDs directly in the terminal (optionally specifying a custom length):
104
+ ```bash
105
+ $ cuid2
106
+ $ cuid2 10
107
+ ```
108
+
109
+ If a framework, dataclass, or Django migration requires a zero-argument callable with a custom length, use `functools.partial` with `create_id`:
110
+ ```python
111
+ from functools import partial
112
+ from typing import Callable
113
+ from cuid2 import create_id
114
+
115
+ short_cuid_generator: Callable[[], str] = partial(create_id, length=10)
116
+
117
+
118
+ def main():
119
+ short_cuid: str = short_cuid_generator()
120
+ ```
121
+
122
+ > **Note:** `cuid_wrapper()` is deprecated as of `v2.1.0` and will be removed in `v3.0.0`. Use `create_id` directly for default 24-character IDs or `functools.partial(create_id, length=...)` for pre-configured custom lengths.
123
+
124
+ Finally, for more explicit control of the CUID generator, you can instantiate the class directly:
125
+ ```python
126
+ from cuid2 import Cuid
127
+
128
+ CUID_GENERATOR: Cuid = Cuid(length=10)
129
+
130
+
131
+ def main():
132
+ my_cuid: str = CUID_GENERATOR.generate()
133
+ next_cuid: str = CUID_GENERATOR.generate()
134
+
135
+ # Validate CUID
136
+ print(Cuid.is_cuid(my_cuid)) # True
137
+ ```
138
+
139
+ `is_cuid` (and `Cuid.is_cuid`) also accepts optional `min_length` and `max_length` arguments to tighten the allowed length.
140
+ The bounds are clamped to 2 and 98 characters, so they can narrow but never widen what is considered valid.
cuid2-2.1.0/README.md ADDED
@@ -0,0 +1,116 @@
1
+ # cuid2
2
+
3
+ [![Python](https://img.shields.io/badge/python-3.10+-blue.svg)](https://pypi.org/project/cuid2/)
4
+ [![PyPi](https://img.shields.io/pypi/v/cuid2.svg)](https://pypi.org/project/cuid2/)
5
+ [![PyPi](https://img.shields.io/pypi/dm/cuid2.svg)](https://pypi.org/project/cuid2/)
6
+
7
+ CUID2 for Python 3. Next generation GUIDs. Collision-resistant ids optimized for
8
+ horizontal scaling and performance.
9
+
10
+ A port of the [CUID2 reference implementation](https://github.com/paralleldrive/cuid2)
11
+ by [Parallel Drive](https://github.com/paralleldrive) to Python 3.
12
+
13
+ > :memo: Note: Originally taken from https://github.com/overflowdigital before it
14
+ > was unpublished. Thank you to @joshuathompsonlindley for your original contribution!
15
+
16
+ ## What is CUID2?
17
+
18
+ * Secure: It's not possible to guess the next ID.
19
+ * Collision resistant: It's extremely unlikely to generate the same ID twice.
20
+ * Horizontally scalable: Generate IDs on multiple machines without coordination.
21
+ * Offline-compatible: Generate IDs without a network connection.
22
+ * URL and name-friendly: No special characters.
23
+
24
+ ## Why?
25
+
26
+ For more information on the theory and usage of CUID2, see the
27
+ [following](https://github.com/paralleldrive/cuid2#why).
28
+
29
+ ## Improvements Over CUID
30
+
31
+ For more information on the improvements of CUID2 over CUID, see the
32
+ [following](https://github.com/paralleldrive/cuid2#improvements-over-cuid).
33
+
34
+
35
+ ## When to Use CUID2
36
+
37
+ * **Zero Timestamp or Creation-Rate Leakage**: Time-ordered formats (`uuid.uuid7()` in Python 3.14, PostgreSQL 18 `uuidv7()`, ULID) embed a 48-bit millisecond timestamp in their prefix, allowing anyone with a public ID to read exact creation times and infer business volume or request velocity ([RFC 9562 §8](https://www.rfc-editor.org/rfc/rfc9562.html#section-8)). CUID2 hashes nanosecond time, CSPRNG salt, a monotonic counter, and a host fingerprint through **SHA3-512**, revealing nothing about when or where an ID was generated.
38
+ * **HTML, CSS, and Lexical Safety (`[a-z][0-9a-z]+`)**: Every CUID2 is guaranteed to start with a lowercase ASCII letter (`[a-z]`) and contain only `[0-9a-z]`. By contrast, time-ordered **UUIDv7** (`019...`) starts with `0` until the year 2527 (and `0–9` until year 7541), **ULID** always starts with a timestamp digit (`0–7`), **UUIDv4** starts with a numeric digit (`0–9`) 62.5% of the time (`10/16` hex values), and **NanoID** starts with `0–9`, `_`, or `-` 18.75% of the time (`12/64` alphabet chars). CUID2 IDs are always valid unescaped HTML `id` attributes and CSS selectors, and copy cleanly with a single double-click.
39
+ * **Collision Resistance & Entropy Math**:
40
+ * **123.6-Bit Default Keyspace (`length=24`)**: 1 leading lowercase letter ($26$) followed by 23 base36 characters ($36^{23}$) yields $26 \times 36^{23} \approx \mathbf{1.61 \times 10^{37}}$ possible IDs ($\approx \mathbf{123.6\text{ bits}}$ of entropy)—slightly more than UUIDv4's 122 random bits in a 33% shorter string (24 vs. 36 chars). Reaching a 50% birthday-bound collision probability requires $\sqrt{2 \ln(2) \times 26 \times 36^{23}} \approx \mathbf{4.7 \times 10^{18}}$ IDs (generating **149 million IDs/sec continuously for 1,000 years**).
41
+ * **Up to 165.0 Bits (`BIG_LENGTH = 32`)**: At `length=32`, the keyspace expands to $26 \times 36^{31} \approx \mathbf{4.54 \times 10^{49}}$ IDs ($\approx \mathbf{165.0\text{ bits}}$; 50% collision bound at $\approx 7.9 \times 10^{24}$ IDs).
42
+ * **Histogram De-Biasing**: Because $2^{512} \approx 1.34 \times 10^{154}$ is not an exact power of 36, the most-significant base36 digit of a 512-bit SHA3-512 digest is biased toward low digits; CUID2 drops that leading hash character (`[1:]`) and prepends an independent uniform `[a-z]` letter so every character position has a uniform distribution.
43
+ * **Pre-Fork & Container Snapshot Resilience**: Combining `os.getpid()`, `socket.gethostname()`, environment variable keys, `time.time_ns()`, a randomly initialized monotonic counter (`INITIAL_COUNT_MAX = 476,782,367`, giving $\approx 25,700$ concurrent hosts before a 50% chance of two hosts even starting at the same counter value), and `secrets.SystemRandom` (`os.urandom`) provides defense-in-depth against PRNG or counter state collisions across forked worker processes (Gunicorn, uWSGI, Celery) and cloned VM/container snapshots.
44
+ * **Polyglot ORM Compatibility (Prisma & Drizzle)**: Generates 24-character base36 primary keys compatible with TypeScript/Node.js services using Prisma (`@default(cuid(2))` / `@onCreate(cuid(2))`) or Drizzle (`@paralleldrive/cuid2`), and replaces the deprecated CUIDv1 (`cuid`) format.
45
+
46
+ | Property | CUID2 (`cuid2`) | UUIDv7 (`uuid.uuid7`) | UUIDv4 (`uuid.uuid4`) | ULID (`python-ulid`) | NanoID (`nanoid`) |
47
+ | :--- | :--- | :--- | :--- | :--- | :--- |
48
+ | **Default Length** | **24 chars** (`2..32`) | 36 chars | 36 chars | 26 chars | 21 chars |
49
+ | **Alphabet** | `[a-z0-9]` (base36) | `[0-9a-f-]` (hex) | `[0-9a-f-]` (hex) | Crockford Base32 | `[A-Za-z0-9_-]` (url64) |
50
+ | **Starts with Letter?** | **Yes (100% `[a-z]`)** | **No** (`0` until yr 2527) | Not guaranteed (37.5% `a–f`) | **No** (always `0–7`) | Not guaranteed (81.25%) |
51
+ | **Double-Click Copyable?** | **Yes** | No (hyphens) | No (hyphens) | **Yes** | No (`-` breaks word) |
52
+ | **Entropy / Random Bits** | **~123.6 bits** (`165.0` at 32) | 74 bits / ms | 122 bits | 80 bits / ms | 126 bits |
53
+ | **50% Collision Bound** | **~4.7 × 10¹⁸** (`~7.9 × 10²⁴` at 32) | ~1.6 × 10¹¹ / ms | ~2.7 × 10¹⁸ | ~1.3 × 10¹² / ms | ~1.1 × 10¹⁹ |
54
+ | **Hides Creation Timestamp?** | **Yes (SHA3-512)** | No (leaks 48-bit ms) | **Yes** | No (leaks 48-bit ms) | **Yes** |
55
+ | **Fork / Snapshot Safe?** | **Yes (PID + host + CSPRNG)** | Counter can collide | OS CSPRNG only | Counter can collide | OS CSPRNG only |
56
+ | **B-Tree Index Locality** | Low (uniform hash) | **High (time-ordered)** | Low (uniform random) | **High (time-ordered)** | Low (uniform random) |
57
+
58
+ > **When to choose UUIDv7 instead:** Because CUID2 hashes its inputs via SHA3-512, IDs are uniformly distributed across the key space. For write-heavy relational tables where B-tree insert locality and 16-byte binary `UUID` storage matter more than hiding creation timestamps, prefer Python 3.14's `uuid.uuid7()` or PostgreSQL 18's `uuidv7()`.
59
+
60
+ ## Install
61
+ ```
62
+ pip install cuid2
63
+ ```
64
+
65
+ ## Usage
66
+
67
+ For most use cases—including Django, SQLAlchemy, and Pydantic model defaults—use the top-level `create_id` and `is_cuid` functions directly:
68
+ ```python
69
+ from cuid2 import create_id, is_cuid
70
+
71
+ my_cuid: str = create_id()
72
+ short_cuid: str = create_id(length=10)
73
+
74
+ print(is_cuid(my_cuid)) # True
75
+ ```
76
+
77
+ > **Tip for Django & ORM defaults:** Pass `default=create_id` directly on model fields (e.g., `models.CharField(max_length=24, default=create_id)`). Because `create_id` is a top-level function backed by a lazy singleton generator, Django `makemigrations` can serialize it directly and `Cuid()` initialization is deferred until the first ID is generated in each worker process.
78
+
79
+ You can also generate CUIDs directly in the terminal (optionally specifying a custom length):
80
+ ```bash
81
+ $ cuid2
82
+ $ cuid2 10
83
+ ```
84
+
85
+ If a framework, dataclass, or Django migration requires a zero-argument callable with a custom length, use `functools.partial` with `create_id`:
86
+ ```python
87
+ from functools import partial
88
+ from typing import Callable
89
+ from cuid2 import create_id
90
+
91
+ short_cuid_generator: Callable[[], str] = partial(create_id, length=10)
92
+
93
+
94
+ def main():
95
+ short_cuid: str = short_cuid_generator()
96
+ ```
97
+
98
+ > **Note:** `cuid_wrapper()` is deprecated as of `v2.1.0` and will be removed in `v3.0.0`. Use `create_id` directly for default 24-character IDs or `functools.partial(create_id, length=...)` for pre-configured custom lengths.
99
+
100
+ Finally, for more explicit control of the CUID generator, you can instantiate the class directly:
101
+ ```python
102
+ from cuid2 import Cuid
103
+
104
+ CUID_GENERATOR: Cuid = Cuid(length=10)
105
+
106
+
107
+ def main():
108
+ my_cuid: str = CUID_GENERATOR.generate()
109
+ next_cuid: str = CUID_GENERATOR.generate()
110
+
111
+ # Validate CUID
112
+ print(Cuid.is_cuid(my_cuid)) # True
113
+ ```
114
+
115
+ `is_cuid` (and `Cuid.is_cuid`) also accepts optional `min_length` and `max_length` arguments to tighten the allowed length.
116
+ The bounds are clamped to 2 and 98 characters, so they can narrow but never widen what is considered valid.
@@ -4,7 +4,7 @@ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "cuid2"
7
- version = "2.0.4"
7
+ version = "2.1.0"
8
8
  description = "Next generation GUIDs. Collision-resistant ids optimized for horizontal scaling and performance."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -4,7 +4,7 @@ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "cuid2"
7
- version = "2.0.4"
7
+ version = "2.1.0"
8
8
  authors = [
9
9
  {name = "Will Gordon", email = "dev@gordoncode.dev"},
10
10
  ]
@@ -7,7 +7,9 @@ from .generator import (
7
7
  MAXIMUM_LENGTH,
8
8
  MIN_LENGTH,
9
9
  Cuid,
10
+ create_id,
10
11
  cuid_wrapper,
12
+ is_cuid,
11
13
  )
12
14
 
13
15
  __all__ = [
@@ -17,5 +19,7 @@ __all__ = [
17
19
  "MAXIMUM_LENGTH",
18
20
  "MIN_LENGTH",
19
21
  "Cuid",
22
+ "create_id",
20
23
  "cuid_wrapper",
24
+ "is_cuid",
21
25
  ]
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  from argparse import ArgumentParser
4
4
  from typing import TYPE_CHECKING, Optional
5
5
 
6
- from cuid2.generator import DEFAULT_LENGTH, cuid_wrapper
6
+ from cuid2.generator import DEFAULT_LENGTH, create_id
7
7
 
8
8
  if TYPE_CHECKING:
9
9
  from collections.abc import Sequence
@@ -21,4 +21,4 @@ def main(args: Optional[Sequence[str]] = None) -> None:
21
21
  )
22
22
  cli_arguments = arguments_parser.parse_args(args)
23
23
 
24
- print(cuid_wrapper(cli_arguments.length)()) # noqa: T201 (print statement)
24
+ print(create_id(cli_arguments.length)) # noqa: T201 (print statement)
@@ -146,14 +146,14 @@ class Cuid: # pylint: disable=too-few-public-methods
146
146
  return first_letter + utils.create_hash(hash_input)[1:resolved_length]
147
147
 
148
148
  @staticmethod
149
- def is_cuid(cuid_str: str, min_length: int = 2, max_length: int = MAXIMUM_LENGTH) -> bool:
149
+ def is_cuid(cuid_str: str, min_length: int = MIN_LENGTH, max_length: int = MAXIMUM_LENGTH) -> bool:
150
150
  """Validates whether a given string is a valid CUID2 identifier.
151
151
 
152
152
  Parameters
153
153
  ----------
154
154
  cuid_str : str
155
155
  The string to validate as a CUID2 identifier.
156
- min_length : int, default=2
156
+ min_length : int, default=MIN_LENGTH (2)
157
157
  The minimum allowed length of the identifier. Values below 2 have no effect, since a valid
158
158
  identifier always has a leading letter followed by at least one more character.
159
159
  max_length : int, default=MAXIMUM_LENGTH (98)
@@ -175,8 +175,49 @@ class Cuid: # pylint: disable=too-few-public-methods
175
175
  )
176
176
 
177
177
 
178
+ _DEFAULT_GENERATOR: Optional[Cuid] = None
179
+
180
+
181
+ def create_id(length: Optional[int] = None) -> str:
182
+ """Generate a CUID2 string using the module-level lazy default generator.
183
+
184
+ Parameters
185
+ ----------
186
+ length : int, optional
187
+ The optional length of the generated CUID2 string. Defaults to `DEFAULT_LENGTH` (24) when omitted.
188
+ A length value less than 1 or greater than `MAXIMUM_LENGTH` (98 characters) will raise a ValueError.
189
+ Lengths outside `MIN_LENGTH`..`BIG_LENGTH` (2..32) emit a DeprecationWarning.
190
+
191
+ Returns
192
+ -------
193
+ str
194
+ A collision-resistant, base36-encoded CUID2 identifier starting with a lowercase ASCII letter.
195
+
196
+ Raises
197
+ ------
198
+ ValueError
199
+ If the length parameter is a boolean, less than 1, or greater than `MAXIMUM_LENGTH` (98 characters).
200
+
201
+ Warns
202
+ -----
203
+ DeprecationWarning
204
+ If the length parameter is outside `MIN_LENGTH`..`BIG_LENGTH` (2..32 characters).
205
+ """
206
+ global _DEFAULT_GENERATOR # noqa: PLW0603 # pylint: disable=global-statement
207
+ if _DEFAULT_GENERATOR is None:
208
+ _DEFAULT_GENERATOR = Cuid()
209
+ return _DEFAULT_GENERATOR.generate(length=length)
210
+
211
+
212
+ is_cuid = Cuid.is_cuid
213
+
214
+
178
215
  def cuid_wrapper(length: int = DEFAULT_LENGTH) -> Callable[[], str]:
179
- """Wrap a single Cuid class instance and return a callable that generates a CUID string.
216
+ """Wrap `create_id` and return a zero-argument callable that generates a CUID string.
217
+
218
+ .. deprecated:: 2.1.0
219
+ `cuid_wrapper` is deprecated and will be removed in v3.0.0.
220
+ Use `create_id` or `functools.partial(create_id, length=...)` instead.
180
221
 
181
222
  Parameters
182
223
  ----------
@@ -199,11 +240,18 @@ def cuid_wrapper(length: int = DEFAULT_LENGTH) -> Callable[[], str]:
199
240
  Warns
200
241
  -----
201
242
  DeprecationWarning
202
- If the length parameter is outside `MIN_LENGTH`..`BIG_LENGTH` (2..32 characters).
243
+ Always emitted because `cuid_wrapper` is deprecated and will be removed in v3.0.0,
244
+ and also emitted if the length parameter is outside `MIN_LENGTH`..`BIG_LENGTH` (2..32 characters).
203
245
  """
204
- cuid_generator: Cuid = Cuid(length=length)
246
+ _validate_length(length)
247
+ warnings.warn(
248
+ "cuid_wrapper() is deprecated and will be removed in v3.0.0. "
249
+ "Use create_id() or functools.partial(create_id, length=...) instead.",
250
+ DeprecationWarning,
251
+ stacklevel=2,
252
+ )
205
253
 
206
254
  def cuid() -> str:
207
- return cuid_generator.generate()
255
+ return create_id(length=length)
208
256
 
209
257
  return cuid
cuid2-2.0.4/PKG-INFO DELETED
@@ -1,102 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: cuid2
3
- Version: 2.0.4
4
- Summary: Next generation GUIDs. Collision-resistant ids optimized for horizontal scaling and performance.
5
- Keywords: crypt,security,uuid,guid,cuid,cryptography
6
- Author: Will Gordon
7
- Author-email: Will Gordon <dev@gordoncode.dev>
8
- License-Expression: MIT
9
- License-File: LICENSE
10
- Classifier: Development Status :: 5 - Production/Stable
11
- Classifier: Intended Audience :: Developers
12
- Classifier: Operating System :: OS Independent
13
- Classifier: Programming Language :: Python :: 3
14
- Classifier: Programming Language :: Python :: 3.10
15
- Classifier: Programming Language :: Python :: 3.11
16
- Classifier: Programming Language :: Python :: 3.12
17
- Classifier: Programming Language :: Python :: 3.13
18
- Classifier: Programming Language :: Python :: 3.14
19
- Classifier: Topic :: Security :: Cryptography
20
- Classifier: Typing :: Typed
21
- Requires-Python: >=3.10
22
- Project-URL: repository, https://github.com/gordon-code/cuid2/
23
- Description-Content-Type: text/markdown
24
-
25
- # cuid2
26
-
27
- [![Python](https://img.shields.io/badge/python-3.10+-blue.svg)](https://pypi.org/project/cuid2/)
28
- [![PyPi](https://img.shields.io/pypi/v/cuid2.svg)](https://pypi.org/project/cuid2/)
29
- [![PyPi](https://img.shields.io/pypi/dm/cuid2.svg)](https://pypi.org/project/cuid2/)
30
-
31
- CUID2 for Python 3. Next generation GUIDs. Collision-resistant ids optimized for
32
- horizontal scaling and performance.
33
-
34
- A port of the [CUID2 reference implementation](https://github.com/paralleldrive/cuid2)
35
- by [Parallel Drive](https://github.com/paralleldrive) to Python 3.
36
-
37
- > :memo: Note: Originally taken from https://github.com/overflowdigital before it
38
- > was unpublished. Thank you to @joshuathompsonlindley for your original contribution!
39
-
40
- ## What is CUID2?
41
-
42
- * Secure: It's not possible to guess the next ID.
43
- * Collision resistant: It's extremely unlikely to generate the same ID twice.
44
- * Horizontally scalable: Generate IDs on multiple machines without coordination.
45
- * Offline-compatible: Generate IDs without a network connection.
46
- * URL and name-friendly: No special characters.
47
-
48
- ## Why?
49
-
50
- For more information on the theory and usage of CUID2, see the
51
- [following](https://github.com/paralleldrive/cuid2#why).
52
-
53
- ## Improvements Over CUID
54
-
55
- For more information on the improvements of CUID2 over CUID, see the
56
- [following](https://github.com/paralleldrive/cuid2#improvements-over-cuid).
57
-
58
-
59
- ## Install
60
- ```
61
- pip install cuid2
62
- ```
63
-
64
- ## Usage
65
-
66
- You can generate CUIDs directly in the terminal with the following (optionally specifying a custom length):
67
- ```bash
68
- $ cuid2
69
- $ cuid2 10
70
- ```
71
-
72
- Or you can rely on a CUID wrapper if you only need default or custom-length generation:
73
- ```python
74
- from typing import Callable
75
- from cuid2 import cuid_wrapper
76
-
77
- cuid_generator: Callable[[], str] = cuid_wrapper()
78
- short_cuid_generator: Callable[[], str] = cuid_wrapper(length=10)
79
-
80
-
81
- def main():
82
- my_cuid: str = cuid_generator()
83
- short_cuid: str = short_cuid_generator()
84
- ```
85
-
86
- Finally, for more explicit control of the CUID generator, you can instantiate the class directly:
87
- ```python
88
- from cuid2 import Cuid
89
-
90
- CUID_GENERATOR: Cuid = Cuid(length=10)
91
-
92
-
93
- def main():
94
- my_cuid: str = CUID_GENERATOR.generate()
95
- next_cuid: str = CUID_GENERATOR.generate()
96
-
97
- # Validate CUID
98
- print(Cuid.is_cuid(my_cuid)) # True
99
- ```
100
-
101
- `Cuid.is_cuid` also accepts optional `min_length` and `max_length` arguments to tighten the allowed length.
102
- The bounds are clamped to 2 and 98 characters, so they can narrow but never widen what is considered valid.
cuid2-2.0.4/README.md DELETED
@@ -1,78 +0,0 @@
1
- # cuid2
2
-
3
- [![Python](https://img.shields.io/badge/python-3.10+-blue.svg)](https://pypi.org/project/cuid2/)
4
- [![PyPi](https://img.shields.io/pypi/v/cuid2.svg)](https://pypi.org/project/cuid2/)
5
- [![PyPi](https://img.shields.io/pypi/dm/cuid2.svg)](https://pypi.org/project/cuid2/)
6
-
7
- CUID2 for Python 3. Next generation GUIDs. Collision-resistant ids optimized for
8
- horizontal scaling and performance.
9
-
10
- A port of the [CUID2 reference implementation](https://github.com/paralleldrive/cuid2)
11
- by [Parallel Drive](https://github.com/paralleldrive) to Python 3.
12
-
13
- > :memo: Note: Originally taken from https://github.com/overflowdigital before it
14
- > was unpublished. Thank you to @joshuathompsonlindley for your original contribution!
15
-
16
- ## What is CUID2?
17
-
18
- * Secure: It's not possible to guess the next ID.
19
- * Collision resistant: It's extremely unlikely to generate the same ID twice.
20
- * Horizontally scalable: Generate IDs on multiple machines without coordination.
21
- * Offline-compatible: Generate IDs without a network connection.
22
- * URL and name-friendly: No special characters.
23
-
24
- ## Why?
25
-
26
- For more information on the theory and usage of CUID2, see the
27
- [following](https://github.com/paralleldrive/cuid2#why).
28
-
29
- ## Improvements Over CUID
30
-
31
- For more information on the improvements of CUID2 over CUID, see the
32
- [following](https://github.com/paralleldrive/cuid2#improvements-over-cuid).
33
-
34
-
35
- ## Install
36
- ```
37
- pip install cuid2
38
- ```
39
-
40
- ## Usage
41
-
42
- You can generate CUIDs directly in the terminal with the following (optionally specifying a custom length):
43
- ```bash
44
- $ cuid2
45
- $ cuid2 10
46
- ```
47
-
48
- Or you can rely on a CUID wrapper if you only need default or custom-length generation:
49
- ```python
50
- from typing import Callable
51
- from cuid2 import cuid_wrapper
52
-
53
- cuid_generator: Callable[[], str] = cuid_wrapper()
54
- short_cuid_generator: Callable[[], str] = cuid_wrapper(length=10)
55
-
56
-
57
- def main():
58
- my_cuid: str = cuid_generator()
59
- short_cuid: str = short_cuid_generator()
60
- ```
61
-
62
- Finally, for more explicit control of the CUID generator, you can instantiate the class directly:
63
- ```python
64
- from cuid2 import Cuid
65
-
66
- CUID_GENERATOR: Cuid = Cuid(length=10)
67
-
68
-
69
- def main():
70
- my_cuid: str = CUID_GENERATOR.generate()
71
- next_cuid: str = CUID_GENERATOR.generate()
72
-
73
- # Validate CUID
74
- print(Cuid.is_cuid(my_cuid)) # True
75
- ```
76
-
77
- `Cuid.is_cuid` also accepts optional `min_length` and `max_length` arguments to tighten the allowed length.
78
- The bounds are clamped to 2 and 98 characters, so they can narrow but never widen what is considered valid.
File without changes
File without changes
File without changes