Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/test_lint_scan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ jobs:
"$WHEEL_TEST_DIR/venv/bin/python" -I -c "import pathlib, cuid2; assert 'site-packages' in pathlib.Path(cuid2.__file__).parts, cuid2.__file__"
CLI_ID="$("$WHEEL_TEST_DIR/venv/bin/cuid2")"
test "${#CLI_ID}" -eq 24
MOD_ID="$("$WHEEL_TEST_DIR/venv/bin/python" -I -m cuid2)"
test "${#MOD_ID}" -eq 24
(
cd "$WHEEL_TEST_DIR"
COVERAGE_FILE="$WHEEL_TEST_DIR/.coverage" "$WHEEL_TEST_DIR/venv/bin/python" -I -m pytest -c "$GITHUB_WORKSPACE/pyproject.toml" --cov-config="$GITHUB_WORKSPACE/pyproject.toml" -o cache_dir="$WHEEL_TEST_DIR/.pytest_cache" "$GITHUB_WORKSPACE/local/tests"
Expand Down Expand Up @@ -139,6 +141,8 @@ jobs:
languages: python
- name: Perform Runtime Code Analysis
uses: github/codeql-action/analyze@6f2ac81f5cd81fe351a197ba0b00d91d03cd2aee # v4.39.0
with:
category: "/language:python"
env:
CODEQL_EXTRACTOR_PYTHON_ANALYSIS_VERSION: "3.13"
- name: Perform Secure Code Analysis (Secrets)
Expand Down
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,16 @@ This repository continues the `overflowdigital` `cuid2` package (`b02c8ca`); ver

- Python 3.15 support in CI and package classifiers.
- `Changelog` and `Issues` project URLs in the package metadata.
- CLI flags `-V`/`--version`, `-l`/`--length`, `--slug` (5-character short ID preset), `-n`/`-c`/`--count`, `-f`/`--fingerprint` (used verbatim as the fingerprint, as in `@paralleldrive/cuid2`), and `--check`/`--validate` (with `-` `stdin`, skipping blank lines, and `-q`/`--quiet` support), plus `python -m cuid2` module execution. A count below `1` or an empty fingerprint exits with code `2`.

### Changed

- Internal: strengthened `ruff`, `mypy`, and `pyright` static checking, enabled `pytest` 9 strict mode and 100% branch coverage enforcement, added `hypothesis` + `atheris` property-based fuzzing and bounded-memory statistical collision testing in CI, added `rumdl` markdown linting/formatting, `zizmor` and `actionlint` workflow linting, `prek.toml` pre-commit hooks, and OpenSSF Scorecard analysis, hardened the release pipeline with SLSA build provenance and PEP 740 PyPI attestations plus `CHANGELOG.md` release notes, extended `codespell` to repository documentation, and removed `pylint` and `tox`.
- `cuid_wrapper()` is no longer deprecated and now returns `functools.partial(create_id, length=length)`, making it picklable and directly usable as a Django migration `default=` callable (passing an extra positional argument now raises `TypeError: create_id() got multiple values for argument 'length'`, and passing `length=...` by keyword overrides the bound length).
- Internal: strengthened `ruff`, `mypy`, and `pyright` static checking, enabled `pytest` 9 strict mode and 100% branch coverage enforcement, added `hypothesis` + `atheris` property-based fuzzing and bounded-memory statistical collision testing in CI, added `rumdl` markdown linting/formatting, `zizmor` and `actionlint` workflow linting, `prek.toml` pre-commit hooks, and OpenSSF Scorecard analysis, set a fixed CodeQL analysis category shared by the CI and release workflows, hardened the release pipeline with SLSA build provenance and PEP 740 PyPI attestations plus `CHANGELOG.md` release notes, extended `codespell` to repository documentation, and removed `pylint` and `tox`.

### Fixed

- CLI out-of-range lengths (`<= 0` or `> 98`) now exit with code `2` and an `argparse` usage error on `stderr` instead of an uncaught `ValueError` traceback.

## [2.1.0] - 2026-10-25

Expand Down
19 changes: 12 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,22 +79,27 @@ print(is_cuid(my_cuid)) # True

> **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.

You can also generate CUIDs directly in the terminal (optionally specifying a custom length):
You can also generate and validate CUIDs directly in the terminal:

```bash
cuid2
cuid2 10
cuid2 # Generate a 24-character CUID2
cuid2 -l 10 # Generate a 10-character CUID2 (or positional: cuid2 10)
cuid2 --slug # Generate a 5-character short ID slug
cuid2 -n 5 # Generate 5 CUID2s in a batch
cuid2 -f server-1 # Use "server-1" verbatim as the fingerprint (IDs stay random)
cuid2 --check <id> # Validate an ID (prints "<id>: valid" or "<id>: invalid"; -q for exit code only)
cuid2 -n 5 | cuid2 --check - # Validate newline-delimited IDs from stdin (blank lines are skipped)
python -m cuid2 # Run via module execution
```

If a framework, dataclass, or Django migration requires a zero-argument callable with a custom length, use `functools.partial` with `create_id`:
If a framework, dataclass, or Django migration requires a zero-argument callable with a custom length, use `cuid_wrapper` (which returns `functools.partial(create_id, length=10)` directly, making it picklable for `multiprocessing` and serializable by Django `makemigrations`):

```python
from collections.abc import Callable
from functools import partial

from cuid2 import create_id
from cuid2 import cuid_wrapper

short_cuid_generator: Callable[[], str] = partial(create_id, length=10)
short_cuid_generator: Callable[[], str] = cuid_wrapper(length=10)


def main() -> None:
Expand Down
Loading
Loading