python-ulid 2.2.0__tar.gz → 2.4.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.
- {python_ulid-2.2.0 → python_ulid-2.4.0}/.github/workflows/lint-and-test.yml +19 -6
- {python_ulid-2.2.0 → python_ulid-2.4.0}/.github/workflows/publish.yml +1 -1
- {python_ulid-2.2.0 → python_ulid-2.4.0}/.gitignore +1 -1
- python_ulid-2.4.0/.pre-commit-config.yaml +10 -0
- {python_ulid-2.2.0 → python_ulid-2.4.0}/.readthedocs.yml +3 -3
- {python_ulid-2.2.0 → python_ulid-2.4.0}/CHANGELOG.rst +25 -0
- {python_ulid-2.2.0 → python_ulid-2.4.0}/PKG-INFO +65 -6
- {python_ulid-2.2.0 → python_ulid-2.4.0}/README.rst +57 -4
- {python_ulid-2.2.0 → python_ulid-2.4.0}/docs/Makefile +2 -2
- python_ulid-2.4.0/docs/requirements.txt +2 -0
- python_ulid-2.4.0/docs/source/changelog.rst +1 -0
- {python_ulid-2.2.0/docs → python_ulid-2.4.0/docs/source}/conf.py +5 -11
- {python_ulid-2.2.0/docs → python_ulid-2.4.0/docs/source}/index.rst +8 -4
- {python_ulid-2.2.0 → python_ulid-2.4.0}/hatch.toml +13 -9
- {python_ulid-2.2.0 → python_ulid-2.4.0}/pyproject.toml +16 -10
- python_ulid-2.4.0/tests/test_cli.py +77 -0
- {python_ulid-2.2.0 → python_ulid-2.4.0}/tests/test_ulid.py +34 -1
- {python_ulid-2.2.0 → python_ulid-2.4.0}/ulid/__init__.py +58 -9
- {python_ulid-2.2.0 → python_ulid-2.4.0}/ulid/__main__.py +26 -27
- {python_ulid-2.2.0 → python_ulid-2.4.0}/ulid/constants.py +1 -0
- python_ulid-2.2.0/.pre-commit-config.yaml +0 -18
- python_ulid-2.2.0/docs/changelog.rst +0 -1
- python_ulid-2.2.0/docs/requirements.txt +0 -1
- {python_ulid-2.2.0 → python_ulid-2.4.0}/LICENSE +0 -0
- {python_ulid-2.2.0/docs → python_ulid-2.4.0/docs/source}/api.rst +0 -0
- {python_ulid-2.2.0 → python_ulid-2.4.0}/logo.png +0 -0
- {python_ulid-2.2.0 → python_ulid-2.4.0}/tests/__init__.py +0 -0
- {python_ulid-2.2.0 → python_ulid-2.4.0}/tests/test_base32.py +0 -0
- {python_ulid-2.2.0 → python_ulid-2.4.0}/ulid/base32.py +0 -0
- {python_ulid-2.2.0 → python_ulid-2.4.0}/ulid/py.typed +0 -0
|
@@ -9,12 +9,13 @@ on:
|
|
|
9
9
|
jobs:
|
|
10
10
|
lint-code:
|
|
11
11
|
runs-on: ubuntu-latest
|
|
12
|
+
name: Lint code
|
|
12
13
|
steps:
|
|
13
14
|
- uses: actions/checkout@v4
|
|
14
15
|
- name: Set up Python
|
|
15
|
-
uses: actions/setup-python@
|
|
16
|
+
uses: actions/setup-python@v5
|
|
16
17
|
with:
|
|
17
|
-
python-version: "3.
|
|
18
|
+
python-version: "3.12"
|
|
18
19
|
- name: Install dependencies
|
|
19
20
|
run: |
|
|
20
21
|
pip install hatch
|
|
@@ -25,12 +26,13 @@ jobs:
|
|
|
25
26
|
|
|
26
27
|
lint-typing:
|
|
27
28
|
runs-on: ubuntu-latest
|
|
29
|
+
name: Lint type annotations
|
|
28
30
|
steps:
|
|
29
31
|
- uses: actions/checkout@v4
|
|
30
32
|
- name: Set up Python
|
|
31
|
-
uses: actions/setup-python@
|
|
33
|
+
uses: actions/setup-python@v5
|
|
32
34
|
with:
|
|
33
|
-
python-version: "3.
|
|
35
|
+
python-version: "3.12"
|
|
34
36
|
- name: Install dependencies
|
|
35
37
|
run: |
|
|
36
38
|
pip install hatch
|
|
@@ -41,13 +43,14 @@ jobs:
|
|
|
41
43
|
|
|
42
44
|
test:
|
|
43
45
|
runs-on: ubuntu-latest
|
|
46
|
+
name: Run tests
|
|
44
47
|
strategy:
|
|
45
48
|
matrix:
|
|
46
|
-
python-version: ["3.9", "3.10", "3.11"]
|
|
49
|
+
python-version: ["3.9", "3.10", "3.11", "3.12"]
|
|
47
50
|
steps:
|
|
48
51
|
- uses: actions/checkout@v4
|
|
49
52
|
- name: Set up Python ${{ matrix.python-version }}
|
|
50
|
-
uses: actions/setup-python@
|
|
53
|
+
uses: actions/setup-python@v5
|
|
51
54
|
with:
|
|
52
55
|
python-version: ${{ matrix.python-version }}
|
|
53
56
|
- name: Install dependencies
|
|
@@ -57,3 +60,13 @@ jobs:
|
|
|
57
60
|
pip install hatch-fancy-pypi-readme
|
|
58
61
|
- name: Test
|
|
59
62
|
run: hatch run cov-test
|
|
63
|
+
- uses: codecov/codecov-action@v4
|
|
64
|
+
with:
|
|
65
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
66
|
+
|
|
67
|
+
package:
|
|
68
|
+
name: Build & verify package
|
|
69
|
+
runs-on: ubuntu-latest
|
|
70
|
+
steps:
|
|
71
|
+
- uses: actions/checkout@v4
|
|
72
|
+
- uses: hynek/build-and-inspect-python-package@v2
|
|
@@ -5,6 +5,29 @@ Changelog
|
|
|
5
5
|
|
|
6
6
|
Versions follow `Semantic Versioning <http://www.semver.org>`_
|
|
7
7
|
|
|
8
|
+
`2.4.0`_ - 2024-04-02
|
|
9
|
+
---------------------
|
|
10
|
+
|
|
11
|
+
Added
|
|
12
|
+
~~~~~
|
|
13
|
+
* :class:`.ULID` objects are now properly serialized when used as Pydantic types `@Avihais12344 <https://github.com/Avihais12344>`_.
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
`2.3.0`_ - 2024-03-21
|
|
17
|
+
---------------------
|
|
18
|
+
|
|
19
|
+
Added
|
|
20
|
+
~~~~~
|
|
21
|
+
* :class:`.ULID` objects can now be converted to bytes with ``bytes(ulid)``.
|
|
22
|
+
* The Pydantic v2 protocol is now supported, so that the :class:`.ULID` class can be directly used
|
|
23
|
+
as type annotations in `Pydantic models <https://docs.pydantic.dev/latest/concepts/models/#basic-model-usage>`_
|
|
24
|
+
|
|
25
|
+
Changed
|
|
26
|
+
~~~~~~~
|
|
27
|
+
* The type annotations have been adapted, so that the classmethod constructors properly reflect the
|
|
28
|
+
type for :class:`.ULID` subclasses. Thanks to `@johnpaulett <https://github.com/johnpaulett>`_
|
|
29
|
+
|
|
30
|
+
|
|
8
31
|
`2.2.0`_ - 2023-09-21
|
|
9
32
|
---------------------
|
|
10
33
|
|
|
@@ -128,6 +151,8 @@ Changed
|
|
|
128
151
|
* The package now has no external dependencies.
|
|
129
152
|
* The test-coverage has been raised to 100%.
|
|
130
153
|
|
|
154
|
+
.. _2.4.0: https://github.com/mdomke/python-ulid/compare/2.3.0...2.4.0
|
|
155
|
+
.. _2.3.0: https://github.com/mdomke/python-ulid/compare/2.2.0...2.3.0
|
|
131
156
|
.. _2.2.0: https://github.com/mdomke/python-ulid/compare/2.1.0...2.2.0
|
|
132
157
|
.. _2.1.0: https://github.com/mdomke/python-ulid/compare/2.0.0...2.1.0
|
|
133
158
|
.. _2.0.0: https://github.com/mdomke/python-ulid/compare/1.1.0...2.0.0
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: python-ulid
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.4.0
|
|
4
4
|
Summary: Universally unique lexicographically sortable identifier
|
|
5
|
+
Project-URL: Homepage, https://github.com/mdomke/python-ulid
|
|
6
|
+
Project-URL: Repository, https://github.com/mdomke/python-ulid
|
|
7
|
+
Project-URL: Documentation, https://python-ulid.readthedocs.io/
|
|
5
8
|
Author-email: Martin Domke <mail@martindomke.net>
|
|
6
9
|
License-Expression: MIT
|
|
7
10
|
License-File: LICENSE
|
|
@@ -15,8 +18,11 @@ Classifier: Programming Language :: Python
|
|
|
15
18
|
Classifier: Programming Language :: Python :: 3.9
|
|
16
19
|
Classifier: Programming Language :: Python :: 3.10
|
|
17
20
|
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
22
|
Classifier: Topic :: Software Development :: Libraries
|
|
19
23
|
Requires-Python: >=3.9
|
|
24
|
+
Provides-Extra: pydantic
|
|
25
|
+
Requires-Dist: pydantic>=2.0; extra == 'pydantic'
|
|
20
26
|
Description-Content-Type: text/x-rst
|
|
21
27
|
|
|
22
28
|
|
|
@@ -57,6 +63,12 @@ Use ``pip`` to install the library
|
|
|
57
63
|
|
|
58
64
|
$ pip install python-ulid
|
|
59
65
|
|
|
66
|
+
to include Pydantic support install the optional dependency like so
|
|
67
|
+
|
|
68
|
+
.. code-block:: bash
|
|
69
|
+
|
|
70
|
+
$ pip install python-ulid[pydantic]
|
|
71
|
+
|
|
60
72
|
.. installation-end
|
|
61
73
|
|
|
62
74
|
.. usage-begin
|
|
@@ -82,8 +94,8 @@ or use one of the named constructors
|
|
|
82
94
|
>>> ULID.from_datetime(datetime.datetime.now())
|
|
83
95
|
ULID(01E75J2XBK390V2XRH44EHC10X)
|
|
84
96
|
|
|
85
|
-
There are several options for encoding the ``ULID`` object
|
|
86
|
-
|
|
97
|
+
There are several options for encoding the ``ULID`` object
|
|
98
|
+
(e.g. string, hex, int, bytes, UUID):
|
|
87
99
|
|
|
88
100
|
.. code-block:: pycon
|
|
89
101
|
|
|
@@ -91,15 +103,47 @@ as well as to access the timestamp attribute in different formats:
|
|
|
91
103
|
'01BTGNYV6HRNK8K8VKZASZCFPE'
|
|
92
104
|
>>> ulid.hex
|
|
93
105
|
'015ea15f6cd1c56689a373fab3f63ece'
|
|
106
|
+
>>> int(ulid)
|
|
107
|
+
1820576928786795198723644692628913870
|
|
108
|
+
>>> bytes(ulid)
|
|
109
|
+
b'\x01^\xa1_l\xd1\xc5f\x89\xa3s\xfa\xb3\xf6>\xce'
|
|
110
|
+
>>> ulid.to_uuid()
|
|
111
|
+
UUID('015ea15f-6cd1-c566-89a3-73fab3f63ece')
|
|
112
|
+
|
|
113
|
+
It is also possible to directly access the timestamp component of a ``ULID``,
|
|
114
|
+
either in UNIX epoch or as ``datetime.datetime``
|
|
115
|
+
|
|
116
|
+
.. code-block:: pycon
|
|
117
|
+
|
|
94
118
|
>>> ulid.timestamp
|
|
95
119
|
1505945939.153
|
|
96
120
|
>>> ulid.datetime
|
|
97
121
|
datetime.datetime(2017, 9, 20, 22, 18, 59, 153000, tzinfo=datetime.timezone.utc)
|
|
98
|
-
>>> ulid.to_uuid()
|
|
99
|
-
UUID('015ea15f-6cd1-c566-89a3-73fab3f63ece')
|
|
100
122
|
|
|
101
123
|
.. usage-end
|
|
102
124
|
|
|
125
|
+
.. pydantic-begin
|
|
126
|
+
|
|
127
|
+
Pydantic integration
|
|
128
|
+
---------------------
|
|
129
|
+
|
|
130
|
+
The ``ULID`` class can be directly used for the popular data validation library
|
|
131
|
+
`Pydantic <https://docs.pydantic.dev/latest/>`_ like so
|
|
132
|
+
|
|
133
|
+
.. code-block:: python
|
|
134
|
+
|
|
135
|
+
from pydantic import BaseModel
|
|
136
|
+
from ulid import ULID
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
class Model(BaseModel):
|
|
140
|
+
ulid: ULID
|
|
141
|
+
|
|
142
|
+
model = Model(ulid="DX89370400440532013000") # OK
|
|
143
|
+
model = Model(ulid="not-a-ulid") # Raises ValidationError
|
|
144
|
+
|
|
145
|
+
.. pydantic-end
|
|
146
|
+
|
|
103
147
|
.. cli-begin
|
|
104
148
|
|
|
105
149
|
Command line interface
|
|
@@ -124,6 +168,14 @@ and convert ULIDs, e.g.
|
|
|
124
168
|
Timestamp: 1695219822.248
|
|
125
169
|
Datetime: 2023-09-20 14:23:42.248000+00:00
|
|
126
170
|
|
|
171
|
+
There are several flags to select specific output formats for the ``show`` command, e.g.
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
.. code-block:: bash
|
|
175
|
+
|
|
176
|
+
$ ulid show --datetime 01HASFKBN8SKZTSVVS03K5AMMS
|
|
177
|
+
2023-09-20 14:23:42.248000+00:00
|
|
178
|
+
|
|
127
179
|
The special character ``-`` allows to read values from ``stdin`` so that they can be piped. E.g.
|
|
128
180
|
|
|
129
181
|
.. code-block:: bash
|
|
@@ -134,6 +186,9 @@ The special character ``-`` allows to read values from ``stdin`` so that they ca
|
|
|
134
186
|
$ date --iso-8601 | python -m ulid build --from-datetime -
|
|
135
187
|
01HAT9PVR02T3S13XB48S7GEHE
|
|
136
188
|
|
|
189
|
+
For a full overview of flags for the ``build`` and ``show`` commands use the ``--help`` option
|
|
190
|
+
(e.g. ``ulid show --help``).
|
|
191
|
+
|
|
137
192
|
.. cli-end
|
|
138
193
|
|
|
139
194
|
Other implementations
|
|
@@ -142,3 +197,7 @@ Other implementations
|
|
|
142
197
|
* `ahawker/ulid <https://github.com/ahawker/ulid>`_
|
|
143
198
|
* `valohai/ulid2 <https://github.com/valohai/ulid2>`_
|
|
144
199
|
* `mdipierro/ulid <https://github.com/mdipierro/ulid>`_
|
|
200
|
+
* `oklog/ulid <https://github.com/oklog/ulid>`_
|
|
201
|
+
* `ulid/javascript <https://github.com/ulid/javascript>`_
|
|
202
|
+
* `RobThree/NUlid <https://github.com/RobThree/NUlid>`_
|
|
203
|
+
* `imdario/go-ulid <https://github.com/imdario/go-ulid>`_
|
|
@@ -69,6 +69,12 @@ Use ``pip`` to install the library
|
|
|
69
69
|
|
|
70
70
|
$ pip install python-ulid
|
|
71
71
|
|
|
72
|
+
to include Pydantic support install the optional dependency like so
|
|
73
|
+
|
|
74
|
+
.. code-block:: bash
|
|
75
|
+
|
|
76
|
+
$ pip install python-ulid[pydantic]
|
|
77
|
+
|
|
72
78
|
.. installation-end
|
|
73
79
|
|
|
74
80
|
.. usage-begin
|
|
@@ -94,8 +100,8 @@ or use one of the named constructors
|
|
|
94
100
|
>>> ULID.from_datetime(datetime.datetime.now())
|
|
95
101
|
ULID(01E75J2XBK390V2XRH44EHC10X)
|
|
96
102
|
|
|
97
|
-
There are several options for encoding the ``ULID`` object
|
|
98
|
-
|
|
103
|
+
There are several options for encoding the ``ULID`` object
|
|
104
|
+
(e.g. string, hex, int, bytes, UUID):
|
|
99
105
|
|
|
100
106
|
.. code-block:: pycon
|
|
101
107
|
|
|
@@ -103,15 +109,47 @@ as well as to access the timestamp attribute in different formats:
|
|
|
103
109
|
'01BTGNYV6HRNK8K8VKZASZCFPE'
|
|
104
110
|
>>> ulid.hex
|
|
105
111
|
'015ea15f6cd1c56689a373fab3f63ece'
|
|
112
|
+
>>> int(ulid)
|
|
113
|
+
1820576928786795198723644692628913870
|
|
114
|
+
>>> bytes(ulid)
|
|
115
|
+
b'\x01^\xa1_l\xd1\xc5f\x89\xa3s\xfa\xb3\xf6>\xce'
|
|
116
|
+
>>> ulid.to_uuid()
|
|
117
|
+
UUID('015ea15f-6cd1-c566-89a3-73fab3f63ece')
|
|
118
|
+
|
|
119
|
+
It is also possible to directly access the timestamp component of a ``ULID``,
|
|
120
|
+
either in UNIX epoch or as ``datetime.datetime``
|
|
121
|
+
|
|
122
|
+
.. code-block:: pycon
|
|
123
|
+
|
|
106
124
|
>>> ulid.timestamp
|
|
107
125
|
1505945939.153
|
|
108
126
|
>>> ulid.datetime
|
|
109
127
|
datetime.datetime(2017, 9, 20, 22, 18, 59, 153000, tzinfo=datetime.timezone.utc)
|
|
110
|
-
>>> ulid.to_uuid()
|
|
111
|
-
UUID('015ea15f-6cd1-c566-89a3-73fab3f63ece')
|
|
112
128
|
|
|
113
129
|
.. usage-end
|
|
114
130
|
|
|
131
|
+
.. pydantic-begin
|
|
132
|
+
|
|
133
|
+
Pydantic integration
|
|
134
|
+
---------------------
|
|
135
|
+
|
|
136
|
+
The ``ULID`` class can be directly used for the popular data validation library
|
|
137
|
+
`Pydantic <https://docs.pydantic.dev/latest/>`_ like so
|
|
138
|
+
|
|
139
|
+
.. code-block:: python
|
|
140
|
+
|
|
141
|
+
from pydantic import BaseModel
|
|
142
|
+
from ulid import ULID
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
class Model(BaseModel):
|
|
146
|
+
ulid: ULID
|
|
147
|
+
|
|
148
|
+
model = Model(ulid="DX89370400440532013000") # OK
|
|
149
|
+
model = Model(ulid="not-a-ulid") # Raises ValidationError
|
|
150
|
+
|
|
151
|
+
.. pydantic-end
|
|
152
|
+
|
|
115
153
|
.. cli-begin
|
|
116
154
|
|
|
117
155
|
Command line interface
|
|
@@ -136,6 +174,14 @@ and convert ULIDs, e.g.
|
|
|
136
174
|
Timestamp: 1695219822.248
|
|
137
175
|
Datetime: 2023-09-20 14:23:42.248000+00:00
|
|
138
176
|
|
|
177
|
+
There are several flags to select specific output formats for the ``show`` command, e.g.
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
.. code-block:: bash
|
|
181
|
+
|
|
182
|
+
$ ulid show --datetime 01HASFKBN8SKZTSVVS03K5AMMS
|
|
183
|
+
2023-09-20 14:23:42.248000+00:00
|
|
184
|
+
|
|
139
185
|
The special character ``-`` allows to read values from ``stdin`` so that they can be piped. E.g.
|
|
140
186
|
|
|
141
187
|
.. code-block:: bash
|
|
@@ -146,6 +192,9 @@ The special character ``-`` allows to read values from ``stdin`` so that they ca
|
|
|
146
192
|
$ date --iso-8601 | python -m ulid build --from-datetime -
|
|
147
193
|
01HAT9PVR02T3S13XB48S7GEHE
|
|
148
194
|
|
|
195
|
+
For a full overview of flags for the ``build`` and ``show`` commands use the ``--help`` option
|
|
196
|
+
(e.g. ``ulid show --help``).
|
|
197
|
+
|
|
149
198
|
.. cli-end
|
|
150
199
|
|
|
151
200
|
Other implementations
|
|
@@ -154,3 +203,7 @@ Other implementations
|
|
|
154
203
|
* `ahawker/ulid <https://github.com/ahawker/ulid>`_
|
|
155
204
|
* `valohai/ulid2 <https://github.com/valohai/ulid2>`_
|
|
156
205
|
* `mdipierro/ulid <https://github.com/mdipierro/ulid>`_
|
|
206
|
+
* `oklog/ulid <https://github.com/oklog/ulid>`_
|
|
207
|
+
* `ulid/javascript <https://github.com/ulid/javascript>`_
|
|
208
|
+
* `RobThree/NUlid <https://github.com/RobThree/NUlid>`_
|
|
209
|
+
* `imdario/go-ulid <https://github.com/imdario/go-ulid>`_
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.. include:: ../../CHANGELOG.rst
|
|
@@ -3,9 +3,9 @@ import sys
|
|
|
3
3
|
from datetime import datetime
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
sys.path.insert(0, os.path.abspath("
|
|
6
|
+
sys.path.insert(0, os.path.abspath("../.."))
|
|
7
7
|
|
|
8
|
-
import ulid
|
|
8
|
+
import ulid
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
copyright = f"{datetime.now().year}, Martin Domke"
|
|
@@ -24,6 +24,7 @@ extensions = [
|
|
|
24
24
|
"sphinx.ext.autodoc",
|
|
25
25
|
"sphinx.ext.napoleon",
|
|
26
26
|
"sphinx.ext.viewcode",
|
|
27
|
+
"sphinx_copybutton",
|
|
27
28
|
]
|
|
28
29
|
|
|
29
30
|
# Add any paths that contain templates here, relative to this directory.
|
|
@@ -32,7 +33,7 @@ templates_path = ["_templates"]
|
|
|
32
33
|
# List of patterns, relative to source directory, that match files and
|
|
33
34
|
# directories to ignore when looking for source files.
|
|
34
35
|
# This pattern also affects html_static_path and html_extra_path.
|
|
35
|
-
exclude_patterns = [
|
|
36
|
+
exclude_patterns = []
|
|
36
37
|
|
|
37
38
|
|
|
38
39
|
# -- Options for HTML output -------------------------------------------------
|
|
@@ -40,15 +41,8 @@ exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
|
|
|
40
41
|
# The theme to use for HTML and HTML Help pages. See the documentation for
|
|
41
42
|
# a list of builtin themes.
|
|
42
43
|
#
|
|
43
|
-
html_logo = "
|
|
44
|
+
html_logo = "../../logo.png"
|
|
44
45
|
html_theme = "furo"
|
|
45
|
-
html_theme_options = {
|
|
46
|
-
"github_user": "mdomke",
|
|
47
|
-
"github_repo": "python-ulid",
|
|
48
|
-
"description": "A library for working with ULIDs",
|
|
49
|
-
"sidebar_collapse": False,
|
|
50
|
-
"logo_text_align": "center",
|
|
51
|
-
}
|
|
52
46
|
|
|
53
47
|
html_title = "Python ULID docs"
|
|
54
48
|
|
|
@@ -4,19 +4,23 @@ Python ULID
|
|
|
4
4
|
Release v\ |release| (:ref:`What's new <changelog>`)
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
.. include::
|
|
7
|
+
.. include:: ../../README.rst
|
|
8
8
|
:start-after: teaser-begin
|
|
9
9
|
:end-before: teaser-end
|
|
10
10
|
|
|
11
|
-
.. include::
|
|
11
|
+
.. include:: ../../README.rst
|
|
12
12
|
:start-after: installation-begin
|
|
13
13
|
:end-before: installation-end
|
|
14
14
|
|
|
15
|
-
.. include::
|
|
15
|
+
.. include:: ../../README.rst
|
|
16
16
|
:start-after: usage-begin
|
|
17
17
|
:end-before: usage-end
|
|
18
18
|
|
|
19
|
-
.. include::
|
|
19
|
+
.. include:: ../../README.rst
|
|
20
|
+
:start-after: pydantic-begin
|
|
21
|
+
:end-before: pydantic-end
|
|
22
|
+
|
|
23
|
+
.. include:: ../../README.rst
|
|
20
24
|
:start-after: cli-begin
|
|
21
25
|
:end-before: cli-end
|
|
22
26
|
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
[envs.default]
|
|
2
2
|
dependencies = [
|
|
3
|
-
"freezegun==1.
|
|
3
|
+
"freezegun==1.4.*",
|
|
4
4
|
"pytest-cov==4.1.*",
|
|
5
|
-
"pytest==
|
|
5
|
+
"pytest==8.1.*",
|
|
6
|
+
]
|
|
7
|
+
features = [
|
|
8
|
+
"pydantic"
|
|
6
9
|
]
|
|
7
10
|
|
|
8
11
|
[envs.default.scripts]
|
|
@@ -11,18 +14,19 @@ cov-test = "pytest --cov {args:ulid} --cov-report=term-missing --cov-report=xml"
|
|
|
11
14
|
|
|
12
15
|
[envs.lint]
|
|
13
16
|
dependencies = [
|
|
14
|
-
"ruff==0.
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
+
"ruff==0.3.*",
|
|
18
|
+
"mypy==1.9.*",
|
|
19
|
+
"doc8==1.1.*",
|
|
17
20
|
]
|
|
18
21
|
|
|
19
22
|
[envs.lint.scripts]
|
|
20
23
|
typing = "mypy --install-types --non-interactive {args:ulid}"
|
|
21
24
|
style = [
|
|
22
|
-
"
|
|
23
|
-
"ruff {args:.}",
|
|
25
|
+
"ruff format --check --diff {args:.}",
|
|
26
|
+
"ruff check {args:.}",
|
|
24
27
|
]
|
|
25
28
|
fmt = [
|
|
26
|
-
"
|
|
27
|
-
"ruff --fix {args:.}",
|
|
29
|
+
"ruff format {args:.}",
|
|
30
|
+
"ruff check --fix {args:.}",
|
|
28
31
|
]
|
|
32
|
+
docs = "doc8 docs"
|
|
@@ -22,9 +22,20 @@ classifiers = [
|
|
|
22
22
|
"Programming Language :: Python :: 3.9",
|
|
23
23
|
"Programming Language :: Python :: 3.10",
|
|
24
24
|
"Programming Language :: Python :: 3.11",
|
|
25
|
+
"Programming Language :: Python :: 3.12",
|
|
25
26
|
"Topic :: Software Development :: Libraries",
|
|
26
27
|
]
|
|
27
28
|
|
|
29
|
+
[project.urls]
|
|
30
|
+
Homepage = "https://github.com/mdomke/python-ulid"
|
|
31
|
+
Repository = "https://github.com/mdomke/python-ulid"
|
|
32
|
+
Documentation = "https://python-ulid.readthedocs.io/"
|
|
33
|
+
|
|
34
|
+
[project.optional-dependencies]
|
|
35
|
+
pydantic = [
|
|
36
|
+
"pydantic>=2.0"
|
|
37
|
+
]
|
|
38
|
+
|
|
28
39
|
[project.scripts]
|
|
29
40
|
ulid = "ulid.__main__:entrypoint"
|
|
30
41
|
|
|
@@ -42,6 +53,8 @@ line-length = 100
|
|
|
42
53
|
[tool.ruff]
|
|
43
54
|
target-version = "py39"
|
|
44
55
|
line-length = 100
|
|
56
|
+
|
|
57
|
+
[tool.ruff.lint]
|
|
45
58
|
select = ["A", "B", "C", "C4", "E", "F", "I", "N", "PT", "Q", "RUF", "S", "SIM", "T10", "UP", "W", "YTT"]
|
|
46
59
|
fixable = ["RUF100", "I001"]
|
|
47
60
|
ignore = [
|
|
@@ -50,21 +63,14 @@ ignore = [
|
|
|
50
63
|
"A003", # Allow shadowing bultins on classes
|
|
51
64
|
]
|
|
52
65
|
|
|
53
|
-
[tool.ruff.mccabe]
|
|
66
|
+
[tool.ruff.lint.mccabe]
|
|
54
67
|
max-complexity = 15
|
|
55
68
|
|
|
56
|
-
[tool.ruff.isort]
|
|
69
|
+
[tool.ruff.lint.isort]
|
|
57
70
|
force-single-line = true
|
|
58
71
|
lines-after-imports = 2
|
|
59
72
|
order-by-type = false
|
|
60
73
|
|
|
61
|
-
[tool.isort]
|
|
62
|
-
profile = "black"
|
|
63
|
-
force_alphabetical_sort_within_sections = true
|
|
64
|
-
force_single_line = true
|
|
65
|
-
lines_after_imports = 2
|
|
66
|
-
line_length = 100
|
|
67
|
-
|
|
68
74
|
[tool.coverage.run]
|
|
69
75
|
branch = true
|
|
70
76
|
parallel = true
|
|
@@ -78,4 +84,4 @@ content-type = "text/x-rst"
|
|
|
78
84
|
|
|
79
85
|
[[tool.hatch.metadata.hooks.fancy-pypi-readme.fragments]]
|
|
80
86
|
path = "README.rst"
|
|
81
|
-
start-after = ".. teaser-begin"
|
|
87
|
+
start-after = ".. teaser-begin"
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
import ulid.__main__ as cli
|
|
4
|
+
from ulid import ULID
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@pytest.mark.parametrize("option", ["", "uuid", "uuid4", "hex", "int", "timestamp", "datetime"])
|
|
8
|
+
def test_parse_show(option: str):
|
|
9
|
+
ulid = ULID()
|
|
10
|
+
argv = ["show", f"--{option}", str(ulid)]
|
|
11
|
+
output = cli.main(argv)
|
|
12
|
+
if option == "uuid":
|
|
13
|
+
assert output == str(ulid.to_uuid())
|
|
14
|
+
elif option == "uuid4":
|
|
15
|
+
assert output == str(ulid.to_uuid4())
|
|
16
|
+
elif option == "hex":
|
|
17
|
+
assert output == ulid.hex
|
|
18
|
+
elif option == "int":
|
|
19
|
+
assert output == str(int(ulid))
|
|
20
|
+
elif option == "timestamp":
|
|
21
|
+
assert output == str(ulid.timestamp)
|
|
22
|
+
elif option == "datetime":
|
|
23
|
+
assert output == ulid.datetime.isoformat()
|
|
24
|
+
else:
|
|
25
|
+
assert str(ulid) in output
|
|
26
|
+
assert ulid.hex in output
|
|
27
|
+
assert str(int(ulid)) in output
|
|
28
|
+
assert str(ulid.timestamp) in output
|
|
29
|
+
assert str(ulid.datetime.isoformat()) in output
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@pytest.mark.parametrize(
|
|
33
|
+
"option",
|
|
34
|
+
[
|
|
35
|
+
"",
|
|
36
|
+
"from-uuid",
|
|
37
|
+
"from-str",
|
|
38
|
+
"from-hex",
|
|
39
|
+
"from-int",
|
|
40
|
+
"from-timestamp",
|
|
41
|
+
"from-datetime",
|
|
42
|
+
],
|
|
43
|
+
)
|
|
44
|
+
def test_build(option: str):
|
|
45
|
+
ulid = ULID()
|
|
46
|
+
value: str
|
|
47
|
+
includes_randomness = True
|
|
48
|
+
includes_timestamp = True
|
|
49
|
+
if option.endswith("uuid"):
|
|
50
|
+
value = str(ulid.to_uuid())
|
|
51
|
+
elif option.endswith("str"):
|
|
52
|
+
value = str(ulid)
|
|
53
|
+
elif option.endswith("hex"):
|
|
54
|
+
value = ulid.hex
|
|
55
|
+
elif option.endswith("int"):
|
|
56
|
+
value = str(int(ulid))
|
|
57
|
+
elif option.endswith("timestamp"):
|
|
58
|
+
value = str(ulid.timestamp)
|
|
59
|
+
includes_randomness = False
|
|
60
|
+
elif option.endswith("datetime"):
|
|
61
|
+
value = ulid.datetime.isoformat()
|
|
62
|
+
includes_randomness = False
|
|
63
|
+
else:
|
|
64
|
+
includes_timestamp = False
|
|
65
|
+
includes_randomness = False
|
|
66
|
+
|
|
67
|
+
argv = ["build"]
|
|
68
|
+
if option:
|
|
69
|
+
argv += [f"--{option}", value]
|
|
70
|
+
output = cli.main(argv)
|
|
71
|
+
|
|
72
|
+
if includes_randomness and includes_timestamp:
|
|
73
|
+
assert output == str(ulid)
|
|
74
|
+
|
|
75
|
+
ulid_out = ULID.from_str(output)
|
|
76
|
+
if includes_timestamp:
|
|
77
|
+
assert ulid_out.datetime == ulid.datetime
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import json
|
|
1
2
|
import time
|
|
2
3
|
import uuid
|
|
3
4
|
from collections.abc import Callable
|
|
@@ -8,6 +9,8 @@ from typing import Union
|
|
|
8
9
|
|
|
9
10
|
import pytest
|
|
10
11
|
from freezegun import freeze_time
|
|
12
|
+
from pydantic import BaseModel
|
|
13
|
+
from pydantic import ValidationError
|
|
11
14
|
|
|
12
15
|
from ulid import base32
|
|
13
16
|
from ulid import constants
|
|
@@ -115,7 +118,8 @@ def test_hash() -> None:
|
|
|
115
118
|
@freeze_time()
|
|
116
119
|
def test_ulid_from_time() -> None:
|
|
117
120
|
ulid1 = ULID.from_timestamp(time.time())
|
|
118
|
-
ulid2 = ULID.
|
|
121
|
+
ulid2 = ULID.from_timestamp(time.time_ns() // 1000000)
|
|
122
|
+
ulid3 = ULID.from_datetime(utcnow())
|
|
119
123
|
|
|
120
124
|
now = utcnow()
|
|
121
125
|
t = time.time()
|
|
@@ -126,6 +130,9 @@ def test_ulid_from_time() -> None:
|
|
|
126
130
|
assert ulid2.timestamp == pytest.approx(t)
|
|
127
131
|
datetimes_almost_equal(ulid2.datetime, now)
|
|
128
132
|
|
|
133
|
+
assert ulid2.timestamp == pytest.approx(t)
|
|
134
|
+
datetimes_almost_equal(ulid3.datetime, now)
|
|
135
|
+
|
|
129
136
|
|
|
130
137
|
@freeze_time()
|
|
131
138
|
def test_ulid_from_timestamp() -> None:
|
|
@@ -155,3 +162,29 @@ Params = Union[bytes, str, int, float]
|
|
|
155
162
|
def test_ulid_invalid_input(constructor: Callable[[Params], ULID], value: Params) -> None:
|
|
156
163
|
with pytest.raises(ValueError): # noqa: PT011
|
|
157
164
|
constructor(value)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def test_pydantic_protocol() -> None:
|
|
168
|
+
ulid = ULID()
|
|
169
|
+
|
|
170
|
+
class Model(BaseModel):
|
|
171
|
+
ulid: ULID
|
|
172
|
+
|
|
173
|
+
for value in [ulid, str(ulid), int(ulid), bytes(ulid)]:
|
|
174
|
+
model = Model(ulid=value)
|
|
175
|
+
assert isinstance(model.ulid, ULID)
|
|
176
|
+
assert model.ulid == ulid
|
|
177
|
+
|
|
178
|
+
for value in [b"not-enough", "not-enough"]:
|
|
179
|
+
with pytest.raises(ValidationError):
|
|
180
|
+
Model(ulid=value)
|
|
181
|
+
|
|
182
|
+
model_dict = model.model_dump()
|
|
183
|
+
ulid_from_dict = model_dict["ulid"]
|
|
184
|
+
assert ulid_from_dict == ulid
|
|
185
|
+
assert isinstance(ulid_from_dict, ULID)
|
|
186
|
+
assert Model(**model_dict) == model
|
|
187
|
+
|
|
188
|
+
model_json = model.model_dump_json()
|
|
189
|
+
assert isinstance(json.loads(model_json)["ulid"], str)
|
|
190
|
+
assert Model.model_validate_json(model_json) == model
|
|
@@ -9,15 +9,21 @@ from datetime import datetime
|
|
|
9
9
|
from datetime import timezone
|
|
10
10
|
from typing import Any
|
|
11
11
|
from typing import Generic
|
|
12
|
+
from typing import TYPE_CHECKING
|
|
12
13
|
from typing import TypeVar
|
|
13
14
|
|
|
14
15
|
from ulid import base32
|
|
15
16
|
from ulid import constants
|
|
16
17
|
|
|
17
18
|
|
|
19
|
+
if TYPE_CHECKING: # pragma: no cover
|
|
20
|
+
from pydantic import GetCoreSchemaHandler
|
|
21
|
+
from pydantic import ValidatorFunctionWrapHandler
|
|
22
|
+
from pydantic_core import CoreSchema
|
|
23
|
+
|
|
18
24
|
try:
|
|
19
25
|
from importlib.metadata import version
|
|
20
|
-
except ImportError:
|
|
26
|
+
except ImportError: # pragma: no cover
|
|
21
27
|
from importlib_metadata import version # type: ignore
|
|
22
28
|
|
|
23
29
|
|
|
@@ -44,6 +50,9 @@ class validate_type(Generic[T]): # noqa: N801
|
|
|
44
50
|
return wrapped
|
|
45
51
|
|
|
46
52
|
|
|
53
|
+
U = TypeVar("U", bound="ULID")
|
|
54
|
+
|
|
55
|
+
|
|
47
56
|
@functools.total_ordering
|
|
48
57
|
class ULID:
|
|
49
58
|
"""The :class:`ULID` object consists of a timestamp part of 48 bits and of 80 random bits.
|
|
@@ -67,11 +76,13 @@ class ULID:
|
|
|
67
76
|
def __init__(self, value: bytes | None = None) -> None:
|
|
68
77
|
if value is not None and len(value) != constants.BYTES_LEN:
|
|
69
78
|
raise ValueError("ULID has to be exactly 16 bytes long.")
|
|
70
|
-
self.bytes: bytes =
|
|
79
|
+
self.bytes: bytes = (
|
|
80
|
+
value or ULID.from_timestamp(time.time_ns() // constants.NANOSECS_IN_MILLISECS).bytes
|
|
81
|
+
)
|
|
71
82
|
|
|
72
83
|
@classmethod
|
|
73
84
|
@validate_type(datetime)
|
|
74
|
-
def from_datetime(cls, value: datetime) ->
|
|
85
|
+
def from_datetime(cls: type[U], value: datetime) -> U:
|
|
75
86
|
"""Create a new :class:`ULID`-object from a :class:`datetime`. The timestamp part of the
|
|
76
87
|
`ULID` will be set to the corresponding timestamp of the datetime.
|
|
77
88
|
|
|
@@ -85,7 +96,7 @@ class ULID:
|
|
|
85
96
|
|
|
86
97
|
@classmethod
|
|
87
98
|
@validate_type(int, float)
|
|
88
|
-
def from_timestamp(cls, value: int | float) ->
|
|
99
|
+
def from_timestamp(cls: type[U], value: int | float) -> U:
|
|
89
100
|
"""Create a new :class:`ULID`-object from a timestamp. The timestamp can be either a
|
|
90
101
|
`float` representing the time in seconds (as it would be returned by :func:`time.time()`)
|
|
91
102
|
or an `int` in milliseconds.
|
|
@@ -104,7 +115,7 @@ class ULID:
|
|
|
104
115
|
|
|
105
116
|
@classmethod
|
|
106
117
|
@validate_type(uuid.UUID)
|
|
107
|
-
def from_uuid(cls, value: uuid.UUID) ->
|
|
118
|
+
def from_uuid(cls: type[U], value: uuid.UUID) -> U:
|
|
108
119
|
"""Create a new :class:`ULID`-object from a :class:`uuid.UUID`. The timestamp part will be
|
|
109
120
|
random in that case.
|
|
110
121
|
|
|
@@ -118,25 +129,25 @@ class ULID:
|
|
|
118
129
|
|
|
119
130
|
@classmethod
|
|
120
131
|
@validate_type(bytes)
|
|
121
|
-
def from_bytes(cls, bytes_: bytes) ->
|
|
132
|
+
def from_bytes(cls: type[U], bytes_: bytes) -> U:
|
|
122
133
|
"""Create a new :class:`ULID`-object from sequence of 16 bytes."""
|
|
123
134
|
return cls(bytes_)
|
|
124
135
|
|
|
125
136
|
@classmethod
|
|
126
137
|
@validate_type(str)
|
|
127
|
-
def from_hex(cls, value: str) ->
|
|
138
|
+
def from_hex(cls: type[U], value: str) -> U:
|
|
128
139
|
"""Create a new :class:`ULID`-object from 32 character string of hex values."""
|
|
129
140
|
return cls.from_bytes(bytes.fromhex(value))
|
|
130
141
|
|
|
131
142
|
@classmethod
|
|
132
143
|
@validate_type(str)
|
|
133
|
-
def from_str(cls, string: str) ->
|
|
144
|
+
def from_str(cls: type[U], string: str) -> U:
|
|
134
145
|
"""Create a new :class:`ULID`-object from a 26 char long string representation."""
|
|
135
146
|
return cls(base32.decode(string))
|
|
136
147
|
|
|
137
148
|
@classmethod
|
|
138
149
|
@validate_type(int)
|
|
139
|
-
def from_int(cls, value: int) ->
|
|
150
|
+
def from_int(cls: type[U], value: int) -> U:
|
|
140
151
|
"""Create a new :class:`ULID`-object from an `int`."""
|
|
141
152
|
return cls(int.to_bytes(value, constants.BYTES_LEN, "big"))
|
|
142
153
|
|
|
@@ -209,6 +220,10 @@ class ULID:
|
|
|
209
220
|
"""Encode this object as an integer."""
|
|
210
221
|
return int.from_bytes(self.bytes, byteorder="big")
|
|
211
222
|
|
|
223
|
+
def __bytes__(self) -> bytes:
|
|
224
|
+
"""Encode this object as byte sequence."""
|
|
225
|
+
return self.bytes
|
|
226
|
+
|
|
212
227
|
def __lt__(self, other: Any) -> bool:
|
|
213
228
|
if isinstance(other, ULID):
|
|
214
229
|
return self.bytes < other.bytes
|
|
@@ -233,3 +248,37 @@ class ULID:
|
|
|
233
248
|
|
|
234
249
|
def __hash__(self) -> int:
|
|
235
250
|
return hash(self.bytes)
|
|
251
|
+
|
|
252
|
+
@classmethod
|
|
253
|
+
def __get_pydantic_core_schema__(cls, source: Any, handler: GetCoreSchemaHandler) -> CoreSchema:
|
|
254
|
+
from pydantic_core import core_schema
|
|
255
|
+
|
|
256
|
+
return core_schema.no_info_wrap_validator_function(
|
|
257
|
+
cls._pydantic_validate,
|
|
258
|
+
core_schema.union_schema(
|
|
259
|
+
[
|
|
260
|
+
core_schema.is_instance_schema(ULID),
|
|
261
|
+
core_schema.no_info_plain_validator_function(ULID),
|
|
262
|
+
]
|
|
263
|
+
),
|
|
264
|
+
serialization=core_schema.to_string_ser_schema(
|
|
265
|
+
when_used="json-unless-none",
|
|
266
|
+
),
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
@classmethod
|
|
270
|
+
def _pydantic_validate(cls, value: Any, handler: ValidatorFunctionWrapHandler) -> Any:
|
|
271
|
+
from pydantic_core import PydanticCustomError
|
|
272
|
+
|
|
273
|
+
try:
|
|
274
|
+
if isinstance(value, int):
|
|
275
|
+
ulid = cls.from_int(value)
|
|
276
|
+
elif isinstance(value, str):
|
|
277
|
+
ulid = cls.from_str(value)
|
|
278
|
+
elif isinstance(value, ULID):
|
|
279
|
+
ulid = value
|
|
280
|
+
else:
|
|
281
|
+
ulid = cls.from_bytes(value)
|
|
282
|
+
except ValueError as err:
|
|
283
|
+
raise PydanticCustomError("ulid_format", "Unrecognized format") from err
|
|
284
|
+
return handler(ulid)
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import argparse
|
|
2
4
|
import shutil
|
|
3
5
|
import sys
|
|
@@ -7,7 +9,6 @@ from collections.abc import Sequence
|
|
|
7
9
|
from datetime import datetime
|
|
8
10
|
from functools import partial
|
|
9
11
|
from typing import Any
|
|
10
|
-
from typing import Optional
|
|
11
12
|
from uuid import UUID
|
|
12
13
|
|
|
13
14
|
import ulid
|
|
@@ -92,12 +93,12 @@ def make_parser(prog: str | None = None) -> argparse.ArgumentParser:
|
|
|
92
93
|
return parser
|
|
93
94
|
|
|
94
95
|
|
|
95
|
-
def main(argv: Sequence[str], prog: str | None = None) ->
|
|
96
|
+
def main(argv: Sequence[str], prog: str | None = None) -> str:
|
|
96
97
|
args = make_parser(prog).parse_args(argv)
|
|
97
|
-
args.func(args)
|
|
98
|
+
return args.func(args)
|
|
98
99
|
|
|
99
100
|
|
|
100
|
-
def from_value_or_stdin(value: str, convert:
|
|
101
|
+
def from_value_or_stdin(value: str, convert: Callable[[str], Any] | None = None) -> Any:
|
|
101
102
|
value = sys.stdin.readline().strip() if value == "-" else value
|
|
102
103
|
if convert is not None:
|
|
103
104
|
return convert(value)
|
|
@@ -111,7 +112,7 @@ def parse_numeric(s: str) -> int | float:
|
|
|
111
112
|
return float(s)
|
|
112
113
|
|
|
113
114
|
|
|
114
|
-
def build(args: argparse.Namespace) ->
|
|
115
|
+
def build(args: argparse.Namespace) -> str:
|
|
115
116
|
ulid: ULID
|
|
116
117
|
if args.from_int is not None:
|
|
117
118
|
ulid = ULID.from_int(from_value_or_stdin(args.from_int, int))
|
|
@@ -127,40 +128,38 @@ def build(args: argparse.Namespace) -> None:
|
|
|
127
128
|
ulid = ULID.from_uuid(from_value_or_stdin(args.from_uuid, UUID))
|
|
128
129
|
else:
|
|
129
130
|
ulid = ULID()
|
|
130
|
-
|
|
131
|
+
return str(ulid)
|
|
131
132
|
|
|
132
133
|
|
|
133
|
-
def show(args: argparse.Namespace) ->
|
|
134
|
+
def show(args: argparse.Namespace) -> str:
|
|
134
135
|
ulid: ULID = ULID.from_str(from_value_or_stdin(args.ulid))
|
|
135
136
|
if args.uuid:
|
|
136
|
-
|
|
137
|
+
return str(ulid.to_uuid())
|
|
137
138
|
elif args.uuid4:
|
|
138
|
-
|
|
139
|
+
return str(ulid.to_uuid4())
|
|
139
140
|
elif args.hex:
|
|
140
|
-
|
|
141
|
+
return ulid.hex
|
|
141
142
|
elif args.int:
|
|
142
|
-
|
|
143
|
+
return str(int(ulid))
|
|
143
144
|
elif args.timestamp:
|
|
144
|
-
|
|
145
|
+
return str(ulid.timestamp)
|
|
145
146
|
elif args.datetime:
|
|
146
|
-
|
|
147
|
+
return ulid.datetime.isoformat()
|
|
147
148
|
else:
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
).strip()
|
|
158
|
-
)
|
|
149
|
+
return textwrap.dedent(
|
|
150
|
+
f"""
|
|
151
|
+
ULID: {ulid!s}
|
|
152
|
+
Hex: {ulid.hex}
|
|
153
|
+
Int: {int(ulid)}
|
|
154
|
+
Timestamp: {ulid.timestamp}
|
|
155
|
+
Datetime: {ulid.datetime.isoformat()}
|
|
156
|
+
"""
|
|
157
|
+
).strip()
|
|
159
158
|
|
|
160
159
|
|
|
161
|
-
def entrypoint() -> None:
|
|
162
|
-
main(sys.argv[1:])
|
|
160
|
+
def entrypoint() -> None: # pragma: no cover
|
|
161
|
+
print(main(sys.argv[1:]))
|
|
163
162
|
|
|
164
163
|
|
|
165
|
-
if __name__ == "__main__":
|
|
164
|
+
if __name__ == "__main__": # pragma: no cover
|
|
166
165
|
main(sys.argv[1:], "python -m ulid")
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
repos:
|
|
2
|
-
- repo: https://github.com/psf/black
|
|
3
|
-
rev: 23.9.1
|
|
4
|
-
hooks:
|
|
5
|
-
- id: black
|
|
6
|
-
- repo: https://github.com/timothycrosley/isort
|
|
7
|
-
rev: 5.12.0
|
|
8
|
-
hooks:
|
|
9
|
-
- id: isort
|
|
10
|
-
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
11
|
-
# Ruff version.
|
|
12
|
-
rev: v0.0.290
|
|
13
|
-
hooks:
|
|
14
|
-
- id: ruff
|
|
15
|
-
- repo: https://github.com/pycqa/doc8
|
|
16
|
-
rev: v1.1.1
|
|
17
|
-
hooks:
|
|
18
|
-
- id: doc8
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
.. include:: ../CHANGELOG.rst
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
furo
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|