python-ulid 2.2.0__tar.gz → 2.3.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.3.0}/.github/workflows/lint-and-test.yml +19 -6
- {python_ulid-2.2.0 → python_ulid-2.3.0}/.github/workflows/publish.yml +1 -1
- {python_ulid-2.2.0 → python_ulid-2.3.0}/.gitignore +1 -1
- python_ulid-2.3.0/.pre-commit-config.yaml +10 -0
- {python_ulid-2.2.0 → python_ulid-2.3.0}/.readthedocs.yml +3 -3
- {python_ulid-2.2.0 → python_ulid-2.3.0}/CHANGELOG.rst +16 -0
- {python_ulid-2.2.0 → python_ulid-2.3.0}/PKG-INFO +62 -6
- {python_ulid-2.2.0 → python_ulid-2.3.0}/README.rst +57 -4
- {python_ulid-2.2.0 → python_ulid-2.3.0}/docs/Makefile +2 -2
- python_ulid-2.3.0/docs/requirements.txt +2 -0
- python_ulid-2.3.0/docs/source/changelog.rst +1 -0
- {python_ulid-2.2.0/docs → python_ulid-2.3.0/docs/source}/conf.py +5 -11
- {python_ulid-2.2.0/docs → python_ulid-2.3.0/docs/source}/index.rst +8 -4
- {python_ulid-2.2.0 → python_ulid-2.3.0}/hatch.toml +13 -9
- {python_ulid-2.2.0 → python_ulid-2.3.0}/pyproject.toml +11 -10
- python_ulid-2.3.0/tests/test_cli.py +77 -0
- {python_ulid-2.2.0 → python_ulid-2.3.0}/tests/test_ulid.py +23 -1
- {python_ulid-2.2.0 → python_ulid-2.3.0}/ulid/__init__.py +55 -9
- {python_ulid-2.2.0 → python_ulid-2.3.0}/ulid/__main__.py +26 -27
- {python_ulid-2.2.0 → python_ulid-2.3.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.3.0}/LICENSE +0 -0
- {python_ulid-2.2.0/docs → python_ulid-2.3.0/docs/source}/api.rst +0 -0
- {python_ulid-2.2.0 → python_ulid-2.3.0}/logo.png +0 -0
- {python_ulid-2.2.0 → python_ulid-2.3.0}/tests/__init__.py +0 -0
- {python_ulid-2.2.0 → python_ulid-2.3.0}/tests/test_base32.py +0 -0
- {python_ulid-2.2.0 → python_ulid-2.3.0}/ulid/base32.py +0 -0
- {python_ulid-2.2.0 → python_ulid-2.3.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,21 @@ Changelog
|
|
|
5
5
|
|
|
6
6
|
Versions follow `Semantic Versioning <http://www.semver.org>`_
|
|
7
7
|
|
|
8
|
+
`2.3.0`_ - 2023-09-21
|
|
9
|
+
---------------------
|
|
10
|
+
|
|
11
|
+
Added
|
|
12
|
+
~~~~~
|
|
13
|
+
* :class:`.ULID` objects can now be converted to bytes with ``bytes(ulid)``.
|
|
14
|
+
* The Pydantic v2 protocol is now supported, so that the :class:`.ULID` class can be directly used
|
|
15
|
+
as type annotations in `Pydantic models <https://docs.pydantic.dev/latest/concepts/models/#basic-model-usage>`_
|
|
16
|
+
|
|
17
|
+
Changed
|
|
18
|
+
~~~~~~~
|
|
19
|
+
* The type annotations have been adapted, so that the classmethod constructors properly reflect the
|
|
20
|
+
type for :class:`.ULID` subclasses. Thanks to `@johnpaulett <https://github.com/johnpaulett>`_
|
|
21
|
+
|
|
22
|
+
|
|
8
23
|
`2.2.0`_ - 2023-09-21
|
|
9
24
|
---------------------
|
|
10
25
|
|
|
@@ -128,6 +143,7 @@ Changed
|
|
|
128
143
|
* The package now has no external dependencies.
|
|
129
144
|
* The test-coverage has been raised to 100%.
|
|
130
145
|
|
|
146
|
+
.. _2.3.0: https://github.com/mdomke/python-ulid/compare/2.2.0...2.3.0
|
|
131
147
|
.. _2.2.0: https://github.com/mdomke/python-ulid/compare/2.1.0...2.2.0
|
|
132
148
|
.. _2.1.0: https://github.com/mdomke/python-ulid/compare/2.0.0...2.1.0
|
|
133
149
|
.. _2.0.0: https://github.com/mdomke/python-ulid/compare/1.1.0...2.0.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: python-ulid
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.0
|
|
4
4
|
Summary: Universally unique lexicographically sortable identifier
|
|
5
5
|
Author-email: Martin Domke <mail@martindomke.net>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -15,8 +15,11 @@ Classifier: Programming Language :: Python
|
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.9
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.10
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
19
|
Classifier: Topic :: Software Development :: Libraries
|
|
19
20
|
Requires-Python: >=3.9
|
|
21
|
+
Provides-Extra: pydantic
|
|
22
|
+
Requires-Dist: pydantic>=2.0; extra == 'pydantic'
|
|
20
23
|
Description-Content-Type: text/x-rst
|
|
21
24
|
|
|
22
25
|
|
|
@@ -57,6 +60,12 @@ Use ``pip`` to install the library
|
|
|
57
60
|
|
|
58
61
|
$ pip install python-ulid
|
|
59
62
|
|
|
63
|
+
to include Pydantic support install the optional dependency like so
|
|
64
|
+
|
|
65
|
+
.. code-block:: bash
|
|
66
|
+
|
|
67
|
+
$ pip install python-ulid[pydantic]
|
|
68
|
+
|
|
60
69
|
.. installation-end
|
|
61
70
|
|
|
62
71
|
.. usage-begin
|
|
@@ -82,8 +91,8 @@ or use one of the named constructors
|
|
|
82
91
|
>>> ULID.from_datetime(datetime.datetime.now())
|
|
83
92
|
ULID(01E75J2XBK390V2XRH44EHC10X)
|
|
84
93
|
|
|
85
|
-
There are several options for encoding the ``ULID`` object
|
|
86
|
-
|
|
94
|
+
There are several options for encoding the ``ULID`` object
|
|
95
|
+
(e.g. string, hex, int, bytes, UUID):
|
|
87
96
|
|
|
88
97
|
.. code-block:: pycon
|
|
89
98
|
|
|
@@ -91,15 +100,47 @@ as well as to access the timestamp attribute in different formats:
|
|
|
91
100
|
'01BTGNYV6HRNK8K8VKZASZCFPE'
|
|
92
101
|
>>> ulid.hex
|
|
93
102
|
'015ea15f6cd1c56689a373fab3f63ece'
|
|
103
|
+
>>> int(ulid)
|
|
104
|
+
1820576928786795198723644692628913870
|
|
105
|
+
>>> bytes(ulid)
|
|
106
|
+
b'\x01^\xa1_l\xd1\xc5f\x89\xa3s\xfa\xb3\xf6>\xce'
|
|
107
|
+
>>> ulid.to_uuid()
|
|
108
|
+
UUID('015ea15f-6cd1-c566-89a3-73fab3f63ece')
|
|
109
|
+
|
|
110
|
+
It is also possible to directly access the timestamp component of a ``ULID``,
|
|
111
|
+
either in UNIX epoch or as ``datetime.datetime``
|
|
112
|
+
|
|
113
|
+
.. code-block:: pycon
|
|
114
|
+
|
|
94
115
|
>>> ulid.timestamp
|
|
95
116
|
1505945939.153
|
|
96
117
|
>>> ulid.datetime
|
|
97
118
|
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
119
|
|
|
101
120
|
.. usage-end
|
|
102
121
|
|
|
122
|
+
.. pydantic-begin
|
|
123
|
+
|
|
124
|
+
Pydantic integration
|
|
125
|
+
---------------------
|
|
126
|
+
|
|
127
|
+
The ``ULID`` class can be directly used for the popular data validation library
|
|
128
|
+
`Pydantic <https://docs.pydantic.dev/latest/>`_ like so
|
|
129
|
+
|
|
130
|
+
.. code-block:: python
|
|
131
|
+
|
|
132
|
+
from pydantic import BaseModel
|
|
133
|
+
from ulid import ULID
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class Model(BaseModel):
|
|
137
|
+
ulid: ULID
|
|
138
|
+
|
|
139
|
+
model = Model(ulid="DX89370400440532013000") # OK
|
|
140
|
+
model = Model(ulid="not-a-ulid") # Raises ValidationError
|
|
141
|
+
|
|
142
|
+
.. pydantic-end
|
|
143
|
+
|
|
103
144
|
.. cli-begin
|
|
104
145
|
|
|
105
146
|
Command line interface
|
|
@@ -124,6 +165,14 @@ and convert ULIDs, e.g.
|
|
|
124
165
|
Timestamp: 1695219822.248
|
|
125
166
|
Datetime: 2023-09-20 14:23:42.248000+00:00
|
|
126
167
|
|
|
168
|
+
There are several flags to select specific output formats for the ``show`` command, e.g.
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
.. code-block:: bash
|
|
172
|
+
|
|
173
|
+
$ ulid show --datetime 01HASFKBN8SKZTSVVS03K5AMMS
|
|
174
|
+
2023-09-20 14:23:42.248000+00:00
|
|
175
|
+
|
|
127
176
|
The special character ``-`` allows to read values from ``stdin`` so that they can be piped. E.g.
|
|
128
177
|
|
|
129
178
|
.. code-block:: bash
|
|
@@ -134,6 +183,9 @@ The special character ``-`` allows to read values from ``stdin`` so that they ca
|
|
|
134
183
|
$ date --iso-8601 | python -m ulid build --from-datetime -
|
|
135
184
|
01HAT9PVR02T3S13XB48S7GEHE
|
|
136
185
|
|
|
186
|
+
For a full overview of flags for the ``build`` and ``show`` commands use the ``--help`` option
|
|
187
|
+
(e.g. ``ulid show --help``).
|
|
188
|
+
|
|
137
189
|
.. cli-end
|
|
138
190
|
|
|
139
191
|
Other implementations
|
|
@@ -142,3 +194,7 @@ Other implementations
|
|
|
142
194
|
* `ahawker/ulid <https://github.com/ahawker/ulid>`_
|
|
143
195
|
* `valohai/ulid2 <https://github.com/valohai/ulid2>`_
|
|
144
196
|
* `mdipierro/ulid <https://github.com/mdipierro/ulid>`_
|
|
197
|
+
* `oklog/ulid <https://github.com/oklog/ulid>`_
|
|
198
|
+
* `ulid/javascript <https://github.com/ulid/javascript>`_
|
|
199
|
+
* `RobThree/NUlid <https://github.com/RobThree/NUlid>`_
|
|
200
|
+
* `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,15 @@ 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.optional-dependencies]
|
|
30
|
+
pydantic = [
|
|
31
|
+
"pydantic>=2.0"
|
|
32
|
+
]
|
|
33
|
+
|
|
28
34
|
[project.scripts]
|
|
29
35
|
ulid = "ulid.__main__:entrypoint"
|
|
30
36
|
|
|
@@ -42,6 +48,8 @@ line-length = 100
|
|
|
42
48
|
[tool.ruff]
|
|
43
49
|
target-version = "py39"
|
|
44
50
|
line-length = 100
|
|
51
|
+
|
|
52
|
+
[tool.ruff.lint]
|
|
45
53
|
select = ["A", "B", "C", "C4", "E", "F", "I", "N", "PT", "Q", "RUF", "S", "SIM", "T10", "UP", "W", "YTT"]
|
|
46
54
|
fixable = ["RUF100", "I001"]
|
|
47
55
|
ignore = [
|
|
@@ -50,21 +58,14 @@ ignore = [
|
|
|
50
58
|
"A003", # Allow shadowing bultins on classes
|
|
51
59
|
]
|
|
52
60
|
|
|
53
|
-
[tool.ruff.mccabe]
|
|
61
|
+
[tool.ruff.lint.mccabe]
|
|
54
62
|
max-complexity = 15
|
|
55
63
|
|
|
56
|
-
[tool.ruff.isort]
|
|
64
|
+
[tool.ruff.lint.isort]
|
|
57
65
|
force-single-line = true
|
|
58
66
|
lines-after-imports = 2
|
|
59
67
|
order-by-type = false
|
|
60
68
|
|
|
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
69
|
[tool.coverage.run]
|
|
69
70
|
branch = true
|
|
70
71
|
parallel = true
|
|
@@ -78,4 +79,4 @@ content-type = "text/x-rst"
|
|
|
78
79
|
|
|
79
80
|
[[tool.hatch.metadata.hooks.fancy-pypi-readme.fragments]]
|
|
80
81
|
path = "README.rst"
|
|
81
|
-
start-after = ".. teaser-begin"
|
|
82
|
+
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
|
|
@@ -8,6 +8,8 @@ from typing import Union
|
|
|
8
8
|
|
|
9
9
|
import pytest
|
|
10
10
|
from freezegun import freeze_time
|
|
11
|
+
from pydantic import BaseModel
|
|
12
|
+
from pydantic import ValidationError
|
|
11
13
|
|
|
12
14
|
from ulid import base32
|
|
13
15
|
from ulid import constants
|
|
@@ -115,7 +117,8 @@ def test_hash() -> None:
|
|
|
115
117
|
@freeze_time()
|
|
116
118
|
def test_ulid_from_time() -> None:
|
|
117
119
|
ulid1 = ULID.from_timestamp(time.time())
|
|
118
|
-
ulid2 = ULID.
|
|
120
|
+
ulid2 = ULID.from_timestamp(time.time_ns() // 1000000)
|
|
121
|
+
ulid3 = ULID.from_datetime(utcnow())
|
|
119
122
|
|
|
120
123
|
now = utcnow()
|
|
121
124
|
t = time.time()
|
|
@@ -126,6 +129,9 @@ def test_ulid_from_time() -> None:
|
|
|
126
129
|
assert ulid2.timestamp == pytest.approx(t)
|
|
127
130
|
datetimes_almost_equal(ulid2.datetime, now)
|
|
128
131
|
|
|
132
|
+
assert ulid2.timestamp == pytest.approx(t)
|
|
133
|
+
datetimes_almost_equal(ulid3.datetime, now)
|
|
134
|
+
|
|
129
135
|
|
|
130
136
|
@freeze_time()
|
|
131
137
|
def test_ulid_from_timestamp() -> None:
|
|
@@ -155,3 +161,19 @@ Params = Union[bytes, str, int, float]
|
|
|
155
161
|
def test_ulid_invalid_input(constructor: Callable[[Params], ULID], value: Params) -> None:
|
|
156
162
|
with pytest.raises(ValueError): # noqa: PT011
|
|
157
163
|
constructor(value)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def test_pydantic_protocol() -> None:
|
|
167
|
+
ulid = ULID()
|
|
168
|
+
|
|
169
|
+
class Model(BaseModel):
|
|
170
|
+
ulid: ULID
|
|
171
|
+
|
|
172
|
+
for value in [ulid, str(ulid), int(ulid), bytes(ulid)]:
|
|
173
|
+
model = Model(ulid=value)
|
|
174
|
+
assert isinstance(model.ulid, ULID)
|
|
175
|
+
assert model.ulid == ulid
|
|
176
|
+
|
|
177
|
+
for value in [b"not-enough", "not-enough"]:
|
|
178
|
+
with pytest.raises(ValidationError):
|
|
179
|
+
Model(ulid=value)
|
|
@@ -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,34 @@ 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
|
+
)
|
|
265
|
+
|
|
266
|
+
@classmethod
|
|
267
|
+
def _pydantic_validate(cls, value: Any, handler: ValidatorFunctionWrapHandler) -> Any:
|
|
268
|
+
from pydantic_core import PydanticCustomError
|
|
269
|
+
|
|
270
|
+
try:
|
|
271
|
+
if isinstance(value, int):
|
|
272
|
+
ulid = cls.from_int(value)
|
|
273
|
+
elif isinstance(value, str):
|
|
274
|
+
ulid = cls.from_str(value)
|
|
275
|
+
elif isinstance(value, ULID):
|
|
276
|
+
ulid = value
|
|
277
|
+
else:
|
|
278
|
+
ulid = cls.from_bytes(value)
|
|
279
|
+
except ValueError as err:
|
|
280
|
+
raise PydanticCustomError("ulid_format", "Unrecognized format") from err
|
|
281
|
+
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
|