python-ulid 3.1.0__tar.gz → 3.2.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.
- python_ulid-3.2.1/.envrc +1 -0
- python_ulid-3.2.1/.github/workflows/lint-and-test.yml +101 -0
- python_ulid-3.2.1/.github/workflows/publish.yml +23 -0
- {python_ulid-3.1.0 → python_ulid-3.2.1}/.gitignore +1 -1
- python_ulid-3.2.1/.pre-commit-config.yaml +18 -0
- python_ulid-3.2.1/AGENTS.md +77 -0
- {python_ulid-3.1.0 → python_ulid-3.2.1}/CHANGELOG.rst +24 -0
- python_ulid-3.2.1/CONTRIBUTING.md +146 -0
- {python_ulid-3.1.0 → python_ulid-3.2.1}/PKG-INFO +25 -3
- {python_ulid-3.1.0 → python_ulid-3.2.1}/README.rst +21 -0
- {python_ulid-3.1.0 → python_ulid-3.2.1}/docs/source/index.rst +4 -0
- {python_ulid-3.1.0 → python_ulid-3.2.1}/pyproject.toml +55 -8
- {python_ulid-3.1.0 → python_ulid-3.2.1}/tests/test_base32.py +1 -1
- {python_ulid-3.1.0 → python_ulid-3.2.1}/tests/test_cli.py +6 -2
- {python_ulid-3.1.0 → python_ulid-3.2.1}/tests/test_ulid.py +130 -5
- {python_ulid-3.1.0 → python_ulid-3.2.1}/ulid/__init__.py +93 -9
- {python_ulid-3.1.0 → python_ulid-3.2.1}/ulid/__main__.py +3 -0
- python_ulid-3.2.1/uv.lock +1038 -0
- python_ulid-3.1.0/.envrc +0 -10
- python_ulid-3.1.0/.github/workflows/lint-and-test.yml +0 -113
- python_ulid-3.1.0/.github/workflows/publish.yml +0 -29
- python_ulid-3.1.0/.pre-commit-config.yaml +0 -10
- python_ulid-3.1.0/devbox.json +0 -11
- python_ulid-3.1.0/devbox.lock +0 -178
- python_ulid-3.1.0/hatch.toml +0 -40
- python_ulid-3.1.0/uv.lock +0 -1359
- {python_ulid-3.1.0 → python_ulid-3.2.1}/.readthedocs.yml +0 -0
- {python_ulid-3.1.0 → python_ulid-3.2.1}/.ruff_defaults.toml +0 -0
- {python_ulid-3.1.0 → python_ulid-3.2.1}/LICENSE +0 -0
- {python_ulid-3.1.0 → python_ulid-3.2.1}/docs/Makefile +0 -0
- {python_ulid-3.1.0 → python_ulid-3.2.1}/docs/requirements.txt +0 -0
- {python_ulid-3.1.0 → python_ulid-3.2.1}/docs/source/api.rst +0 -0
- {python_ulid-3.1.0 → python_ulid-3.2.1}/docs/source/changelog.rst +0 -0
- {python_ulid-3.1.0 → python_ulid-3.2.1}/docs/source/conf.py +0 -0
- {python_ulid-3.1.0 → python_ulid-3.2.1}/logo.png +0 -0
- {python_ulid-3.1.0 → python_ulid-3.2.1}/tests/__init__.py +0 -0
- {python_ulid-3.1.0 → python_ulid-3.2.1}/ulid/base32.py +0 -0
- {python_ulid-3.1.0 → python_ulid-3.2.1}/ulid/constants.py +0 -0
- {python_ulid-3.1.0 → python_ulid-3.2.1}/ulid/py.typed +0 -0
python_ulid-3.2.1/.envrc
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
use flox
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
name: lint-and-test
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
lint-docs:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v7
|
|
14
|
+
- name: Install uv
|
|
15
|
+
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
|
|
16
|
+
with:
|
|
17
|
+
python-version: "3.14"
|
|
18
|
+
- name: Lint documentation
|
|
19
|
+
run: uv run doc8 docs/source
|
|
20
|
+
|
|
21
|
+
lint-code:
|
|
22
|
+
runs-on: ubuntu-latest
|
|
23
|
+
name: Lint code
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@v7
|
|
26
|
+
- name: Install uv
|
|
27
|
+
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
|
|
28
|
+
with:
|
|
29
|
+
python-version: "3.14"
|
|
30
|
+
- name: Lint style
|
|
31
|
+
run: uv run ruff check
|
|
32
|
+
|
|
33
|
+
lint-typing:
|
|
34
|
+
runs-on: ubuntu-latest
|
|
35
|
+
name: Lint type annotations
|
|
36
|
+
steps:
|
|
37
|
+
- uses: actions/checkout@v7
|
|
38
|
+
- name: Install uv
|
|
39
|
+
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
|
|
40
|
+
with:
|
|
41
|
+
python-version: "3.14"
|
|
42
|
+
- name: Lint typing
|
|
43
|
+
run: uv run poe check-types
|
|
44
|
+
|
|
45
|
+
test:
|
|
46
|
+
runs-on: ubuntu-latest
|
|
47
|
+
name: Run tests
|
|
48
|
+
strategy:
|
|
49
|
+
matrix:
|
|
50
|
+
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
|
|
51
|
+
steps:
|
|
52
|
+
- uses: actions/checkout@v7
|
|
53
|
+
- name: Install uv
|
|
54
|
+
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
|
|
55
|
+
with:
|
|
56
|
+
python-version: ${{ matrix.python-version }}
|
|
57
|
+
|
|
58
|
+
- name: Test
|
|
59
|
+
run: uv run poe test
|
|
60
|
+
|
|
61
|
+
- name: Upload coverage data
|
|
62
|
+
uses: actions/upload-artifact@v7
|
|
63
|
+
with:
|
|
64
|
+
include-hidden-files: true
|
|
65
|
+
name: coverage-${{ matrix.python-version }}
|
|
66
|
+
path: .coverage*
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
coverage:
|
|
70
|
+
runs-on: ubuntu-latest
|
|
71
|
+
name: Report coverage
|
|
72
|
+
needs:
|
|
73
|
+
- test
|
|
74
|
+
steps:
|
|
75
|
+
- uses: actions/checkout@v7
|
|
76
|
+
- name: Install uv
|
|
77
|
+
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
|
|
78
|
+
|
|
79
|
+
- name: Download coverage data
|
|
80
|
+
uses: actions/download-artifact@v8
|
|
81
|
+
with:
|
|
82
|
+
pattern: coverage-*
|
|
83
|
+
merge-multiple: true
|
|
84
|
+
|
|
85
|
+
- name: Combine coverage data
|
|
86
|
+
run: uv run poe cov-combine
|
|
87
|
+
|
|
88
|
+
- name: Export coverage reports
|
|
89
|
+
run: uv run poe cov-xml
|
|
90
|
+
|
|
91
|
+
- uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
|
|
92
|
+
with:
|
|
93
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
package:
|
|
97
|
+
name: Build & verify package
|
|
98
|
+
runs-on: ubuntu-latest
|
|
99
|
+
steps:
|
|
100
|
+
- uses: actions/checkout@v7
|
|
101
|
+
- uses: hynek/build-and-inspect-python-package@v2
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
name: publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ["*"]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
deploy:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v7
|
|
13
|
+
- name: Install uv
|
|
14
|
+
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
|
|
15
|
+
with:
|
|
16
|
+
python-version: "3.14"
|
|
17
|
+
- name: Build
|
|
18
|
+
run: uv build
|
|
19
|
+
- name: Publish
|
|
20
|
+
env:
|
|
21
|
+
UV_PUBLISH_USERNAME: ${{ secrets.PYPI_USERNAME }}
|
|
22
|
+
UV_PUBLISH_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
|
|
23
|
+
run: uv publish
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
3
|
+
rev: v0.15.4
|
|
4
|
+
hooks:
|
|
5
|
+
- id: ruff-check
|
|
6
|
+
- id: ruff-format
|
|
7
|
+
- repo: https://github.com/pycqa/doc8
|
|
8
|
+
rev: v2.0.0
|
|
9
|
+
hooks:
|
|
10
|
+
- id: doc8
|
|
11
|
+
- repo: local
|
|
12
|
+
hooks:
|
|
13
|
+
- id: pyrefly-check
|
|
14
|
+
name: pyrefly
|
|
15
|
+
entry: uv run pyrefly check
|
|
16
|
+
language: system
|
|
17
|
+
types: [python]
|
|
18
|
+
pass_filenames: false
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Guidelines for AI Coding Agents
|
|
2
|
+
|
|
3
|
+
Welcome! If you are an AI assistant, coding agent, or language model (e.g., Antigravity, Claude Code, Cursor, Copilot) helping to develop `python-ulid`, please adhere to the guidelines in this document.
|
|
4
|
+
|
|
5
|
+
These instructions ensure consistency, prevent common development errors, and keep code quality aligned with the project's standards.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 🏗️ Project Stack & Tooling
|
|
10
|
+
|
|
11
|
+
We use modern, fast, and strict tooling for Python development. Always use the specified commands below.
|
|
12
|
+
|
|
13
|
+
- **Dependency Manager**: We use **[uv](https://github.com/astral-sh/uv)**.
|
|
14
|
+
- Do *not* use raw `pip`, `poetry`, or `pdm`.
|
|
15
|
+
- To install dependencies, run: `uv sync`.
|
|
16
|
+
- Always prefix commands with `uv run` to execute them in the correct environment (e.g., `uv run pytest`).
|
|
17
|
+
- **Task Runner**: We use **[Poe the Poet](https://github.com/nat-n/poethepoet)**.
|
|
18
|
+
- Development tasks are defined in `pyproject.toml` under `[tool.poe.tasks]`.
|
|
19
|
+
- Run checks with `uv run poe check`.
|
|
20
|
+
- **Linting & Formatting**: We use **[Ruff](https://github.com/astral-sh/ruff)**.
|
|
21
|
+
- Standard line-length limit is **100 characters**.
|
|
22
|
+
- Auto-format code using `uv run poe fmt`.
|
|
23
|
+
- **Static Typing**: We use **[pyrefly](https://github.com/mdomke/pyrefly)** for strict static analysis.
|
|
24
|
+
- Run type checking using `uv run poe check-types`.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 🎯 Coding Standards for Agents
|
|
29
|
+
|
|
30
|
+
### 1. Mandatory Type Annotations
|
|
31
|
+
We require full, strict type annotations across the entire codebase.
|
|
32
|
+
- **Rules**:
|
|
33
|
+
- Always annotate all function parameters and return values.
|
|
34
|
+
- Avoid `Any` where possible; use specific types, unions, or generics.
|
|
35
|
+
- Run type checking to verify compliance: `uv run poe check-types`.
|
|
36
|
+
|
|
37
|
+
### 2. Ruff Compliance
|
|
38
|
+
- **Formatting**: Run formatting before finalizing any file modifications.
|
|
39
|
+
- Use `uv run poe fmt` to auto-format.
|
|
40
|
+
- Code style rules are detailed in `pyproject.toml` and `.ruff_defaults.toml`.
|
|
41
|
+
- **Imports**: We use single-line imports and specific import order (`isort` rules). Let Ruff handle this automatically.
|
|
42
|
+
|
|
43
|
+
### 3. Production-Ready Code Only
|
|
44
|
+
- Never generate code with comments like `# TODO: implement this`, `# placeholder`, or partial code blocks.
|
|
45
|
+
- Implement the full logic requested, ensuring error handling and correct edge cases are covered.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## 🛠️ Verification Workflow
|
|
50
|
+
|
|
51
|
+
Before completing any task, you **MUST** run the verification commands to ensure no regressions or style issues are introduced.
|
|
52
|
+
|
|
53
|
+
1. **Format Code**:
|
|
54
|
+
```bash
|
|
55
|
+
uv run poe fmt
|
|
56
|
+
```
|
|
57
|
+
2. **Run Lints, Types & Style Checks**:
|
|
58
|
+
```bash
|
|
59
|
+
uv run poe check
|
|
60
|
+
```
|
|
61
|
+
3. **Run Test Suite**:
|
|
62
|
+
```bash
|
|
63
|
+
uv run poe test
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Make sure all checks pass without errors.
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## 📂 Codebase Navigation
|
|
71
|
+
|
|
72
|
+
- **`/ulid`**: Contains the source code of the `python-ulid` package.
|
|
73
|
+
- **`/tests`**: Contains all unit and integration tests. Write corresponding test cases here for any new logic.
|
|
74
|
+
- **`/docs`**: Contains Sphinx-based documentation.
|
|
75
|
+
- **`pyproject.toml`**: The single source of truth for dependencies, tools configuration, and Poe tasks.
|
|
76
|
+
|
|
77
|
+
Refer to the primary developer documentation and [CONTRIBUTING.md](file:///Users/martin.domke/Source/private/ulid/CONTRIBUTING.md) for more details.
|
|
@@ -5,6 +5,29 @@ Changelog
|
|
|
5
5
|
|
|
6
6
|
Versions follow `Semantic Versioning <http://www.semver.org>`_
|
|
7
7
|
|
|
8
|
+
`3.2.0`_ - 2026-07-17
|
|
9
|
+
---------------------
|
|
10
|
+
Added
|
|
11
|
+
~~~~~
|
|
12
|
+
* Added transparent bidirectional UUIDv7 conversion methods (``ULID.to_uuid7`` and
|
|
13
|
+
``ULID.from_uuidv7``) `@pirate <https://github.com/pirate>`_.
|
|
14
|
+
* Added a ``--uuid7`` option to the CLI's ``show`` command to convert and output a ULID as an
|
|
15
|
+
RFC 4122 compliant UUIDv7.
|
|
16
|
+
|
|
17
|
+
Changed
|
|
18
|
+
~~~~~~~
|
|
19
|
+
* Dropped support for Python 3.9 and added support for Python 3.14
|
|
20
|
+
`@Avihais12344 <https://github.com/Avihais12344>`_.
|
|
21
|
+
* Depend on ``typing-extensions`` only when running on Python < 3.11 `@musicinmybrain <https://github.com/musicinmybrain>`_.
|
|
22
|
+
* Migrated package and development workflow from Hatch to ``uv`` and ``poethepoet``.
|
|
23
|
+
* Configured Flox for bootstrapping the development environment.
|
|
24
|
+
|
|
25
|
+
Fixed
|
|
26
|
+
~~~~~
|
|
27
|
+
* Fixed a clock-skew bug where non-monotonic ULIDs could be generated when crossing a millisecond boundary between timestamp and randomness sampling `@gaoflow <https://github.com/gaoflow>`_.
|
|
28
|
+
* Fixed warning from ``uv`` during synchronization `@jaseemabid <https://github.com/jaseemabid>`_.
|
|
29
|
+
* Resolved various Ruff linter warnings and Pyrefly type checker issues.
|
|
30
|
+
|
|
8
31
|
`3.1.0`_ - 2025-08-18
|
|
9
32
|
---------------------
|
|
10
33
|
Changed
|
|
@@ -197,6 +220,7 @@ Changed
|
|
|
197
220
|
* The package now has no external dependencies.
|
|
198
221
|
* The test-coverage has been raised to 100%.
|
|
199
222
|
|
|
223
|
+
.. _3.2.0: https://github.com/mdomke/python-ulid/compare/3.1.0...3.2.0
|
|
200
224
|
.. _3.1.0: https://github.com/mdomke/python-ulid/compare/3.0.0...3.1.0
|
|
201
225
|
.. _3.0.0: https://github.com/mdomke/python-ulid/compare/2.7.0...3.0.0
|
|
202
226
|
.. _2.7.0: https://github.com/mdomke/python-ulid/compare/2.6.0...2.7.0
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# Contributing to python-ulid
|
|
2
|
+
|
|
3
|
+
Thank you for your interest in contributing to `python-ulid`! We appreciate all kinds of contributions: fixing bugs, improving documentation, submitting feature requests, or writing code.
|
|
4
|
+
|
|
5
|
+
This document guides you through setting up your local environment and details our development workflow and coding standards.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 🚀 Getting Started
|
|
10
|
+
|
|
11
|
+
We support two ways to set up your local development environment: **Flox** (recommended, zero-configuration setup) or a **Manual** setup.
|
|
12
|
+
|
|
13
|
+
### Option 1: Using Flox (Recommended)
|
|
14
|
+
|
|
15
|
+
If you have [Flox](https://flox.dev/) installed, setting up your environment is completely automated. Flox will automatically handle installing `uv`, Python, project dependencies, and pre-commit hooks inside a reproducible environment.
|
|
16
|
+
|
|
17
|
+
1. **Fork and clone** the repository:
|
|
18
|
+
```bash
|
|
19
|
+
git clone https://github.com/<your-username>/python-ulid.git
|
|
20
|
+
cd python-ulid
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
2. **Activate the Flox environment**:
|
|
24
|
+
```bash
|
|
25
|
+
flox activate
|
|
26
|
+
```
|
|
27
|
+
That's it! Flox automatically sets up everything for you and activates your python virtual environment.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
### Option 2: Manual Setup
|
|
32
|
+
|
|
33
|
+
If Flox is not an option, you can set up your environment manually. We use [uv](https://github.com/astral-sh/uv) for fast, robust package and dependency management.
|
|
34
|
+
|
|
35
|
+
1. **Prerequisites**:
|
|
36
|
+
- **Python**: `>= 3.10`
|
|
37
|
+
- **uv**: Install via `curl -LsSf https://astral.sh/uv/install.sh | sh` (or your preferred package manager)
|
|
38
|
+
|
|
39
|
+
2. **Fork and clone** the repository:
|
|
40
|
+
```bash
|
|
41
|
+
git clone https://github.com/<your-username>/python-ulid.git
|
|
42
|
+
cd python-ulid
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
3. **Sync the dependencies**:
|
|
46
|
+
This will automatically create a `.venv` virtual environment and install all packages in the developer and documentation groups:
|
|
47
|
+
```bash
|
|
48
|
+
uv sync
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
4. **Install pre-commit hooks**:
|
|
52
|
+
We use `prek` (a fast, Rust-based alternative to pre-commit) to manage our Git hooks, ensuring code style, formatting, and static typing checks run automatically before each commit.
|
|
53
|
+
```bash
|
|
54
|
+
uv run prek install
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## 🛠️ Development Tasks & Commands
|
|
60
|
+
|
|
61
|
+
We use [Poe the Poet](https://github.com/nat-n/poethepoet) as our task runner. Depending on how you set up your development environment, you can invoke the tasks directly or via `uv run`:
|
|
62
|
+
|
|
63
|
+
- **With Flox (Recommended)**: Since the virtual environment is automatically activated in your shell, you can run all commands directly:
|
|
64
|
+
```bash
|
|
65
|
+
poe test
|
|
66
|
+
```
|
|
67
|
+
- **With Manual Setup**: If you did not use Flox, prefix all tasks with `uv run`:
|
|
68
|
+
```bash
|
|
69
|
+
uv run poe test
|
|
70
|
+
```
|
|
71
|
+
(You can also manually activate the virtual environment to get the same shorthands as above).
|
|
72
|
+
|
|
73
|
+
### Common Development Tasks
|
|
74
|
+
|
|
75
|
+
| Task | Command (Flox) | Command (Manual) | Description |
|
|
76
|
+
| :--- | :--- | :--- | :--- |
|
|
77
|
+
| **All Checks** | `poe check` | `uv run poe check` | Run linter, formatter checks, type checking, and docs check. |
|
|
78
|
+
| **Formatting** | `poe fmt` | `uv run poe fmt` | Auto-format Python source files using `ruff format`. |
|
|
79
|
+
| **Lint** | `poe check-code` | `uv run poe check-code` | Check source files for lint and code style issues using `ruff`. |
|
|
80
|
+
| **Format Check** | `poe check-fmt` | `uv run poe check-fmt` | Dry-run format check without modifying files. |
|
|
81
|
+
| **Type Check** | `poe check-types` | `uv run poe check-types` | Run strict static analysis and type verification with `pyrefly`. |
|
|
82
|
+
| **Docs Lint** | `poe check-docs` | `uv run poe check-docs` | Lint documentation style using `doc8`. |
|
|
83
|
+
| **Run Tests** | `poe test` | `uv run poe test` | Run pytest with code coverage tracking. |
|
|
84
|
+
| **Build Docs** | `poe docs` | `uv run poe docs` | Build the HTML documentation locally using Sphinx. |
|
|
85
|
+
|
|
86
|
+
> [!TIP]
|
|
87
|
+
> Before pushing any changes or opening a Pull Request, always make sure to run the complete checks suite with **`poe check`** (or `uv run poe check`) and run the test suite with **`poe test`** (or `uv run poe test`).
|
|
88
|
+
|
|
89
|
+
### Manual Hook Execution with prek
|
|
90
|
+
|
|
91
|
+
If you want to run all checks against your staged or unstaged files manually:
|
|
92
|
+
|
|
93
|
+
- **With Flox**:
|
|
94
|
+
```bash
|
|
95
|
+
prek run --all-files
|
|
96
|
+
```
|
|
97
|
+
- **With Manual Setup**:
|
|
98
|
+
```bash
|
|
99
|
+
uv run prek run --all-files
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## 🎨 Coding Standards
|
|
105
|
+
|
|
106
|
+
To maintain a clean, readable, and highly maintainable codebase, we enforce the following development standards:
|
|
107
|
+
|
|
108
|
+
### 1. Type Annotations
|
|
109
|
+
- **Strictly Required**: We enforce type annotations across the entire codebase. This includes all function signatures, arguments, and return types.
|
|
110
|
+
- **Static Analysis**: Our configuration has `disallow_untyped_defs = true` set for Mypy and uses strict `pyrefly` checks. Untyped or partially-typed code will fail the lint pipelines.
|
|
111
|
+
- **Example**:
|
|
112
|
+
```python
|
|
113
|
+
def generate_ulid(timestamp: float | None = None) -> ULID:
|
|
114
|
+
...
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### 2. Ruff Formatting & Linting
|
|
118
|
+
- **Formatting**: We use [Ruff](https://github.com/astral-sh/ruff) for automatic code formatting (`ruff format`).
|
|
119
|
+
- **Code Style**: Ruff also handles our lint checks, including import sorting (`isort`), code complexity limits (`mccabe`), and code style conventions.
|
|
120
|
+
- **Configuration**:
|
|
121
|
+
- Line length limit: **100 characters**.
|
|
122
|
+
- Imports are forced to single lines and ordered.
|
|
123
|
+
- Standard Ruff defaults apply (see `.ruff_defaults.toml`).
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## 📬 Submitting a Pull Request
|
|
128
|
+
|
|
129
|
+
1. **Create a branch** for your feature or bugfix:
|
|
130
|
+
```bash
|
|
131
|
+
git checkout -b feature/my-cool-improvement
|
|
132
|
+
```
|
|
133
|
+
2. **Make your changes**, ensuring you write comprehensive unit tests.
|
|
134
|
+
3. **Format and verify** your code (e.g. using `poe` directly if using Flox, or prefixed with `uv run` if manual):
|
|
135
|
+
```bash
|
|
136
|
+
poe fmt
|
|
137
|
+
poe check
|
|
138
|
+
poe test
|
|
139
|
+
```
|
|
140
|
+
4. **Commit your changes**:
|
|
141
|
+
Our pre-commit hooks will automatically trigger to verify your work.
|
|
142
|
+
5. **Push and open a PR** on GitHub! Please provide a descriptive title and detailed explanation of your changes in the PR description.
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
Thank you for helping make `python-ulid` better! 🌟
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-ulid
|
|
3
|
-
Version: 3.1
|
|
3
|
+
Version: 3.2.1
|
|
4
4
|
Summary: Universally unique lexicographically sortable identifier
|
|
5
5
|
Project-URL: Homepage, https://github.com/mdomke/python-ulid
|
|
6
6
|
Project-URL: Documentation, https://python-ulid.readthedocs.io
|
|
@@ -18,13 +18,14 @@ Classifier: License :: OSI Approved :: MIT License
|
|
|
18
18
|
Classifier: Operating System :: MacOS :: MacOS X
|
|
19
19
|
Classifier: Operating System :: POSIX :: Linux
|
|
20
20
|
Classifier: Programming Language :: Python
|
|
21
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
22
21
|
Classifier: Programming Language :: Python :: 3.10
|
|
23
22
|
Classifier: Programming Language :: Python :: 3.11
|
|
24
23
|
Classifier: Programming Language :: Python :: 3.12
|
|
25
24
|
Classifier: Programming Language :: Python :: 3.13
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
26
26
|
Classifier: Topic :: Software Development :: Libraries
|
|
27
|
-
Requires-Python: >=3.
|
|
27
|
+
Requires-Python: >=3.10
|
|
28
|
+
Requires-Dist: typing-extensions; python_version < '3.11'
|
|
28
29
|
Provides-Extra: pydantic
|
|
29
30
|
Requires-Dist: pydantic>=2.0; extra == 'pydantic'
|
|
30
31
|
Description-Content-Type: text/x-rst
|
|
@@ -40,6 +41,7 @@ A ``ULID`` is a *universally unique lexicographically sortable identifier*. It i
|
|
|
40
41
|
* Uses Crockford's base32 for better efficiency and readability (5 bits per character)
|
|
41
42
|
* Case insensitive
|
|
42
43
|
* No special characters (URL safe)
|
|
44
|
+
* Monotonic sort order (correctly detects and handles the same millisecond)
|
|
43
45
|
|
|
44
46
|
In general the structure of a ULID is as follows:
|
|
45
47
|
|
|
@@ -148,6 +150,20 @@ The ``ULID`` class can be directly used for the popular data validation library
|
|
|
148
150
|
|
|
149
151
|
.. pydantic-end
|
|
150
152
|
|
|
153
|
+
.. monotonic-begin
|
|
154
|
+
|
|
155
|
+
Monotonic Support
|
|
156
|
+
-----------------
|
|
157
|
+
|
|
158
|
+
This library by default supports the implementation for monotonic sort order suggested by the
|
|
159
|
+
official ULID specification.
|
|
160
|
+
|
|
161
|
+
This means that ULID values generated in the same millisecond will have linear increasing randomness
|
|
162
|
+
values. If :math:`r_1` and :math:`r_2` are the randomness values of two ULIDs with the same
|
|
163
|
+
timestamp, then :math:`r_2 = r_1 + 1`.
|
|
164
|
+
|
|
165
|
+
.. monotonic-end
|
|
166
|
+
|
|
151
167
|
.. cli-begin
|
|
152
168
|
|
|
153
169
|
Command line interface
|
|
@@ -205,3 +221,9 @@ Other implementations
|
|
|
205
221
|
* `ulid/javascript <https://github.com/ulid/javascript>`_
|
|
206
222
|
* `RobThree/NUlid <https://github.com/RobThree/NUlid>`_
|
|
207
223
|
* `imdario/go-ulid <https://github.com/imdario/go-ulid>`_
|
|
224
|
+
|
|
225
|
+
Contributions
|
|
226
|
+
-------------
|
|
227
|
+
|
|
228
|
+
Contributions are welcome! Feel free to create pull-requests for issues or feature requests.
|
|
229
|
+
It might be worth creating an issue upfront to discuss the matter.
|
|
@@ -42,6 +42,7 @@ A ``ULID`` is a *universally unique lexicographically sortable identifier*. It i
|
|
|
42
42
|
* Uses Crockford's base32 for better efficiency and readability (5 bits per character)
|
|
43
43
|
* Case insensitive
|
|
44
44
|
* No special characters (URL safe)
|
|
45
|
+
* Monotonic sort order (correctly detects and handles the same millisecond)
|
|
45
46
|
|
|
46
47
|
In general the structure of a ULID is as follows:
|
|
47
48
|
|
|
@@ -150,6 +151,20 @@ The ``ULID`` class can be directly used for the popular data validation library
|
|
|
150
151
|
|
|
151
152
|
.. pydantic-end
|
|
152
153
|
|
|
154
|
+
.. monotonic-begin
|
|
155
|
+
|
|
156
|
+
Monotonic Support
|
|
157
|
+
-----------------
|
|
158
|
+
|
|
159
|
+
This library by default supports the implementation for monotonic sort order suggested by the
|
|
160
|
+
official ULID specification.
|
|
161
|
+
|
|
162
|
+
This means that ULID values generated in the same millisecond will have linear increasing randomness
|
|
163
|
+
values. If :math:`r_1` and :math:`r_2` are the randomness values of two ULIDs with the same
|
|
164
|
+
timestamp, then :math:`r_2 = r_1 + 1`.
|
|
165
|
+
|
|
166
|
+
.. monotonic-end
|
|
167
|
+
|
|
153
168
|
.. cli-begin
|
|
154
169
|
|
|
155
170
|
Command line interface
|
|
@@ -207,3 +222,9 @@ Other implementations
|
|
|
207
222
|
* `ulid/javascript <https://github.com/ulid/javascript>`_
|
|
208
223
|
* `RobThree/NUlid <https://github.com/RobThree/NUlid>`_
|
|
209
224
|
* `imdario/go-ulid <https://github.com/imdario/go-ulid>`_
|
|
225
|
+
|
|
226
|
+
Contributions
|
|
227
|
+
-------------
|
|
228
|
+
|
|
229
|
+
Contributions are welcome! Feel free to create pull-requests for issues or feature requests.
|
|
230
|
+
It might be worth creating an issue upfront to discuss the matter.
|
|
@@ -20,6 +20,10 @@ Release v\ |release| (:ref:`What's new <changelog>`)
|
|
|
20
20
|
:start-after: pydantic-begin
|
|
21
21
|
:end-before: pydantic-end
|
|
22
22
|
|
|
23
|
+
.. include:: ../../README.rst
|
|
24
|
+
:start-after: monotonic-begin
|
|
25
|
+
:end-before: monotonic-end
|
|
26
|
+
|
|
23
27
|
.. include:: ../../README.rst
|
|
24
28
|
:start-after: cli-begin
|
|
25
29
|
:end-before: cli-end
|
|
@@ -7,7 +7,7 @@ name = "python-ulid"
|
|
|
7
7
|
description = "Universally unique lexicographically sortable identifier"
|
|
8
8
|
dynamic = ["version", "readme"]
|
|
9
9
|
license = "MIT"
|
|
10
|
-
requires-python = ">=3.
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
11
|
authors = [
|
|
12
12
|
{ name = "Martin Domke", email = "mail@martindomke.net" },
|
|
13
13
|
]
|
|
@@ -19,13 +19,16 @@ classifiers = [
|
|
|
19
19
|
"Operating System :: POSIX :: Linux",
|
|
20
20
|
"Operating System :: MacOS :: MacOS X",
|
|
21
21
|
"Programming Language :: Python",
|
|
22
|
-
"Programming Language :: Python :: 3.9",
|
|
23
22
|
"Programming Language :: Python :: 3.10",
|
|
24
23
|
"Programming Language :: Python :: 3.11",
|
|
25
24
|
"Programming Language :: Python :: 3.12",
|
|
26
25
|
"Programming Language :: Python :: 3.13",
|
|
26
|
+
"Programming Language :: Python :: 3.14",
|
|
27
27
|
"Topic :: Software Development :: Libraries",
|
|
28
28
|
]
|
|
29
|
+
dependencies = [
|
|
30
|
+
"typing-extensions; python_version < '3.11'",
|
|
31
|
+
]
|
|
29
32
|
|
|
30
33
|
[project.urls]
|
|
31
34
|
Homepage = "https://github.com/mdomke/python-ulid"
|
|
@@ -50,10 +53,14 @@ source = "vcs"
|
|
|
50
53
|
packages = [
|
|
51
54
|
"ulid",
|
|
52
55
|
]
|
|
56
|
+
[tool.hatch.build.targets.sdist]
|
|
57
|
+
exclude = [
|
|
58
|
+
".flox",
|
|
59
|
+
]
|
|
53
60
|
|
|
54
61
|
[tool.ruff]
|
|
55
62
|
extend = ".ruff_defaults.toml"
|
|
56
|
-
target-version = "
|
|
63
|
+
target-version = "py310"
|
|
57
64
|
line-length = 100
|
|
58
65
|
|
|
59
66
|
[tool.ruff.format]
|
|
@@ -97,6 +104,38 @@ plugins = [
|
|
|
97
104
|
"pydantic.mypy",
|
|
98
105
|
]
|
|
99
106
|
|
|
107
|
+
[tool.uv]
|
|
108
|
+
default-groups = ["dev", "doc"]
|
|
109
|
+
|
|
110
|
+
[tool.pyrefly]
|
|
111
|
+
preset = "strict"
|
|
112
|
+
project-excludes = ["docs"]
|
|
113
|
+
|
|
114
|
+
[tool.poe.tasks]
|
|
115
|
+
test = "uv run --extra pydantic coverage run -m pytest"
|
|
116
|
+
check-code = "ruff check"
|
|
117
|
+
check-fmt = "ruff format --check"
|
|
118
|
+
check-types = "uv run --extra pydantic pyrefly check"
|
|
119
|
+
check-docs = "doc8 docs/source"
|
|
120
|
+
fmt = "ruff format"
|
|
121
|
+
build = "uv build"
|
|
122
|
+
cov-combine = "coverage combine"
|
|
123
|
+
cov-xml = "coverage xml"
|
|
124
|
+
|
|
125
|
+
[tool.poe.tasks.docs]
|
|
126
|
+
help = "Build the HTML documentation"
|
|
127
|
+
cmd = "uv run --with-requirements requirements.txt make html"
|
|
128
|
+
cwd = "docs"
|
|
129
|
+
|
|
130
|
+
[tool.poe.tasks.check]
|
|
131
|
+
help = "Run all checks (code, format, types, docs)"
|
|
132
|
+
sequence = [
|
|
133
|
+
{ ref = "check-code" },
|
|
134
|
+
{ ref = "check-fmt" },
|
|
135
|
+
{ ref = "check-types" },
|
|
136
|
+
{ ref = "check-docs" },
|
|
137
|
+
]
|
|
138
|
+
|
|
100
139
|
[tool.coverage.run]
|
|
101
140
|
branch = true
|
|
102
141
|
parallel = true
|
|
@@ -105,11 +144,19 @@ source = ["ulid"]
|
|
|
105
144
|
[tool.doc8]
|
|
106
145
|
max-line-length = 100
|
|
107
146
|
|
|
108
|
-
[
|
|
109
|
-
dev
|
|
110
|
-
"
|
|
111
|
-
"freezegun
|
|
112
|
-
"
|
|
147
|
+
[dependency-groups]
|
|
148
|
+
dev = [
|
|
149
|
+
"coverage==7.15.*",
|
|
150
|
+
"freezegun==1.5.*",
|
|
151
|
+
"poethepoet==0.48.*",
|
|
152
|
+
"prek==0.4.*",
|
|
153
|
+
"pyrefly==1.1.*",
|
|
154
|
+
"pytest==9.1.*",
|
|
155
|
+
"ruff==0.15.*",
|
|
156
|
+
]
|
|
157
|
+
doc = [
|
|
158
|
+
"doc8>=2.0.0",
|
|
159
|
+
"sphinx==8.1.*",
|
|
113
160
|
]
|
|
114
161
|
|
|
115
162
|
[tool.hatch.metadata.hooks.fancy-pypi-readme]
|
|
@@ -26,6 +26,6 @@ from ulid import constants
|
|
|
26
26
|
(base32.decode_randomness, "A" * (constants.RANDOMNESS_REPR_LEN + 1)),
|
|
27
27
|
],
|
|
28
28
|
)
|
|
29
|
-
def test_invalid_input(func: Callable, value: Any) -> None:
|
|
29
|
+
def test_invalid_input(func: Callable[[bytes], str], value: Any) -> None:
|
|
30
30
|
with pytest.raises(ValueError): # noqa: PT011
|
|
31
31
|
func(value)
|
|
@@ -4,7 +4,9 @@ import ulid.__main__ as cli
|
|
|
4
4
|
from ulid import ULID
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
@pytest.mark.parametrize(
|
|
7
|
+
@pytest.mark.parametrize(
|
|
8
|
+
"option", ["", "uuid", "uuid4", "uuid7", "hex", "int", "timestamp", "datetime"]
|
|
9
|
+
)
|
|
8
10
|
def test_parse_show(option: str):
|
|
9
11
|
ulid = ULID()
|
|
10
12
|
argv = ["show", f"--{option}", str(ulid)]
|
|
@@ -13,6 +15,8 @@ def test_parse_show(option: str):
|
|
|
13
15
|
assert output == str(ulid.to_uuid())
|
|
14
16
|
elif option == "uuid4":
|
|
15
17
|
assert output == str(ulid.to_uuid4())
|
|
18
|
+
elif option == "uuid7":
|
|
19
|
+
assert output == str(ulid.to_uuid7(compliant=True))
|
|
16
20
|
elif option == "hex":
|
|
17
21
|
assert output == ulid.hex
|
|
18
22
|
elif option == "int":
|
|
@@ -43,7 +47,7 @@ def test_parse_show(option: str):
|
|
|
43
47
|
)
|
|
44
48
|
def test_build(option: str):
|
|
45
49
|
ulid = ULID()
|
|
46
|
-
value: str
|
|
50
|
+
value: str = ""
|
|
47
51
|
includes_randomness = True
|
|
48
52
|
includes_timestamp = True
|
|
49
53
|
if option.endswith("uuid"):
|