+73
.github/workflows/ci.yml
+73
.github/workflows/ci.yml
···
1
+
name: CI
2
+
3
+
on:
4
+
pull_request:
5
+
paths:
6
+
- "src/**"
7
+
- "tests/**"
8
+
- "pyproject.toml"
9
+
- "uv.lock"
10
+
- ".pre-commit-config.yaml"
11
+
- ".github/workflows/ci.yml"
12
+
push:
13
+
branches: [main]
14
+
paths:
15
+
- "src/**"
16
+
- "tests/**"
17
+
- "pyproject.toml"
18
+
- "uv.lock"
19
+
- ".pre-commit-config.yaml"
20
+
- ".github/workflows/ci.yml"
21
+
22
+
permissions:
23
+
contents: read
24
+
25
+
jobs:
26
+
pre-commit:
27
+
timeout-minutes: 5
28
+
runs-on: ubuntu-latest
29
+
30
+
steps:
31
+
- uses: actions/checkout@v5
32
+
33
+
- name: install uv
34
+
uses: astral-sh/setup-uv@v7
35
+
with:
36
+
enable-cache: true
37
+
cache-dependency-glob: "uv.lock"
38
+
39
+
- name: install dependencies
40
+
run: uv sync
41
+
42
+
- name: check lockfile is up to date
43
+
run: |
44
+
if ! uv lock --check; then
45
+
echo "lockfile is out of date!"
46
+
echo "to update the lockfile, run 'uv lock'."
47
+
exit 1
48
+
fi
49
+
echo "lockfile is up to date"
50
+
51
+
- name: run prek
52
+
run: uv run prek run --all-files
53
+
env:
54
+
SKIP: no-commit-to-branch
55
+
56
+
test:
57
+
timeout-minutes: 5
58
+
runs-on: ubuntu-latest
59
+
60
+
steps:
61
+
- uses: actions/checkout@v5
62
+
63
+
- name: install uv
64
+
uses: astral-sh/setup-uv@v7
65
+
with:
66
+
enable-cache: true
67
+
cache-dependency-glob: "uv.lock"
68
+
69
+
- name: install dependencies
70
+
run: uv sync
71
+
72
+
- name: run tests
73
+
run: uv run pytest -v
+26
.github/workflows/publish.yml
+26
.github/workflows/publish.yml
···
1
+
name: Publish pmgfal to PyPI
2
+
on:
3
+
release:
4
+
types: [published]
5
+
workflow_dispatch:
6
+
7
+
jobs:
8
+
pypi-publish:
9
+
name: Upload to PyPI
10
+
runs-on: ubuntu-latest
11
+
permissions:
12
+
id-token: write
13
+
steps:
14
+
- name: Checkout
15
+
uses: actions/checkout@v5
16
+
with:
17
+
fetch-depth: 0
18
+
19
+
- name: "Install uv"
20
+
uses: astral-sh/setup-uv@v7
21
+
22
+
- name: Build
23
+
run: uv build
24
+
25
+
- name: Publish to PyPI
26
+
run: uv publish -v dist/*
+34
.gitignore
+34
.gitignore
···
1
+
__pycache__/
2
+
*.py[cod]
3
+
*$py.class
4
+
*.so
5
+
.Python
6
+
build/
7
+
develop-eggs/
8
+
dist/
9
+
downloads/
10
+
eggs/
11
+
.eggs/
12
+
lib/
13
+
lib64/
14
+
parts/
15
+
sdist/
16
+
var/
17
+
wheels/
18
+
*.egg-info/
19
+
.installed.cfg
20
+
*.egg
21
+
.venv/
22
+
venv/
23
+
ENV/
24
+
.env
25
+
*.env
26
+
.pytest_cache/
27
+
.coverage
28
+
htmlcov/
29
+
.mypy_cache/
30
+
.ruff_cache/
31
+
.ty_cache/
32
+
33
+
# rust
34
+
target/
+30
.pre-commit-config.yaml
+30
.pre-commit-config.yaml
···
1
+
fail_fast: false
2
+
3
+
repos:
4
+
- repo: https://github.com/abravalheri/validate-pyproject
5
+
rev: v0.24.1
6
+
hooks:
7
+
- id: validate-pyproject
8
+
9
+
- repo: https://github.com/astral-sh/ruff-pre-commit
10
+
rev: v0.12.1
11
+
hooks:
12
+
- id: ruff-check
13
+
args: [--fix, --exit-non-zero-on-fix]
14
+
- id: ruff-format
15
+
16
+
- repo: local
17
+
hooks:
18
+
- id: type-check
19
+
name: type check
20
+
entry: uv run ty check
21
+
language: system
22
+
types: [python]
23
+
pass_filenames: false
24
+
25
+
- repo: https://github.com/pre-commit/pre-commit-hooks
26
+
rev: v6.0.0
27
+
hooks:
28
+
- id: no-commit-to-branch
29
+
name: prevent commits to main
30
+
args: [--branch, main]
+635
Cargo.lock
+635
Cargo.lock
···
1
+
# This file is automatically @generated by Cargo.
2
+
# It is not intended for manual editing.
3
+
version = 4
4
+
5
+
[[package]]
6
+
name = "android_system_properties"
7
+
version = "0.1.5"
8
+
source = "registry+https://github.com/rust-lang/crates.io-index"
9
+
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
10
+
dependencies = [
11
+
"libc",
12
+
]
13
+
14
+
[[package]]
15
+
name = "atrium-lex"
16
+
version = "0.1.0"
17
+
source = "git+https://github.com/atrium-rs/atrium?branch=main#6d59a772bc933743bee1dc877e02b692a9711b37"
18
+
dependencies = [
19
+
"serde",
20
+
"serde_repr",
21
+
"serde_with",
22
+
]
23
+
24
+
[[package]]
25
+
name = "autocfg"
26
+
version = "1.5.0"
27
+
source = "registry+https://github.com/rust-lang/crates.io-index"
28
+
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
29
+
30
+
[[package]]
31
+
name = "base64"
32
+
version = "0.13.1"
33
+
source = "registry+https://github.com/rust-lang/crates.io-index"
34
+
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
35
+
36
+
[[package]]
37
+
name = "bumpalo"
38
+
version = "3.19.0"
39
+
source = "registry+https://github.com/rust-lang/crates.io-index"
40
+
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
41
+
42
+
[[package]]
43
+
name = "cc"
44
+
version = "1.2.49"
45
+
source = "registry+https://github.com/rust-lang/crates.io-index"
46
+
checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215"
47
+
dependencies = [
48
+
"find-msvc-tools",
49
+
"shlex",
50
+
]
51
+
52
+
[[package]]
53
+
name = "cfg-if"
54
+
version = "1.0.4"
55
+
source = "registry+https://github.com/rust-lang/crates.io-index"
56
+
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
57
+
58
+
[[package]]
59
+
name = "chrono"
60
+
version = "0.4.42"
61
+
source = "registry+https://github.com/rust-lang/crates.io-index"
62
+
checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2"
63
+
dependencies = [
64
+
"iana-time-zone",
65
+
"num-traits",
66
+
"serde",
67
+
"windows-link",
68
+
]
69
+
70
+
[[package]]
71
+
name = "core-foundation-sys"
72
+
version = "0.8.7"
73
+
source = "registry+https://github.com/rust-lang/crates.io-index"
74
+
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
75
+
76
+
[[package]]
77
+
name = "darling"
78
+
version = "0.20.11"
79
+
source = "registry+https://github.com/rust-lang/crates.io-index"
80
+
checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee"
81
+
dependencies = [
82
+
"darling_core",
83
+
"darling_macro",
84
+
]
85
+
86
+
[[package]]
87
+
name = "darling_core"
88
+
version = "0.20.11"
89
+
source = "registry+https://github.com/rust-lang/crates.io-index"
90
+
checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e"
91
+
dependencies = [
92
+
"fnv",
93
+
"ident_case",
94
+
"proc-macro2",
95
+
"quote",
96
+
"strsim",
97
+
"syn",
98
+
]
99
+
100
+
[[package]]
101
+
name = "darling_macro"
102
+
version = "0.20.11"
103
+
source = "registry+https://github.com/rust-lang/crates.io-index"
104
+
checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
105
+
dependencies = [
106
+
"darling_core",
107
+
"quote",
108
+
"syn",
109
+
]
110
+
111
+
[[package]]
112
+
name = "deranged"
113
+
version = "0.5.5"
114
+
source = "registry+https://github.com/rust-lang/crates.io-index"
115
+
checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587"
116
+
dependencies = [
117
+
"powerfmt",
118
+
"serde_core",
119
+
]
120
+
121
+
[[package]]
122
+
name = "find-msvc-tools"
123
+
version = "0.1.5"
124
+
source = "registry+https://github.com/rust-lang/crates.io-index"
125
+
checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844"
126
+
127
+
[[package]]
128
+
name = "fnv"
129
+
version = "1.0.7"
130
+
source = "registry+https://github.com/rust-lang/crates.io-index"
131
+
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
132
+
133
+
[[package]]
134
+
name = "hashbrown"
135
+
version = "0.12.3"
136
+
source = "registry+https://github.com/rust-lang/crates.io-index"
137
+
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
138
+
139
+
[[package]]
140
+
name = "heck"
141
+
version = "0.5.0"
142
+
source = "registry+https://github.com/rust-lang/crates.io-index"
143
+
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
144
+
145
+
[[package]]
146
+
name = "hex"
147
+
version = "0.4.3"
148
+
source = "registry+https://github.com/rust-lang/crates.io-index"
149
+
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
150
+
151
+
[[package]]
152
+
name = "iana-time-zone"
153
+
version = "0.1.64"
154
+
source = "registry+https://github.com/rust-lang/crates.io-index"
155
+
checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb"
156
+
dependencies = [
157
+
"android_system_properties",
158
+
"core-foundation-sys",
159
+
"iana-time-zone-haiku",
160
+
"js-sys",
161
+
"log",
162
+
"wasm-bindgen",
163
+
"windows-core",
164
+
]
165
+
166
+
[[package]]
167
+
name = "iana-time-zone-haiku"
168
+
version = "0.1.2"
169
+
source = "registry+https://github.com/rust-lang/crates.io-index"
170
+
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
171
+
dependencies = [
172
+
"cc",
173
+
]
174
+
175
+
[[package]]
176
+
name = "ident_case"
177
+
version = "1.0.1"
178
+
source = "registry+https://github.com/rust-lang/crates.io-index"
179
+
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
180
+
181
+
[[package]]
182
+
name = "indexmap"
183
+
version = "1.9.3"
184
+
source = "registry+https://github.com/rust-lang/crates.io-index"
185
+
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
186
+
dependencies = [
187
+
"autocfg",
188
+
"hashbrown",
189
+
"serde",
190
+
]
191
+
192
+
[[package]]
193
+
name = "indoc"
194
+
version = "2.0.7"
195
+
source = "registry+https://github.com/rust-lang/crates.io-index"
196
+
checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706"
197
+
dependencies = [
198
+
"rustversion",
199
+
]
200
+
201
+
[[package]]
202
+
name = "itoa"
203
+
version = "1.0.15"
204
+
source = "registry+https://github.com/rust-lang/crates.io-index"
205
+
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
206
+
207
+
[[package]]
208
+
name = "js-sys"
209
+
version = "0.3.83"
210
+
source = "registry+https://github.com/rust-lang/crates.io-index"
211
+
checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8"
212
+
dependencies = [
213
+
"once_cell",
214
+
"wasm-bindgen",
215
+
]
216
+
217
+
[[package]]
218
+
name = "libc"
219
+
version = "0.2.178"
220
+
source = "registry+https://github.com/rust-lang/crates.io-index"
221
+
checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091"
222
+
223
+
[[package]]
224
+
name = "log"
225
+
version = "0.4.29"
226
+
source = "registry+https://github.com/rust-lang/crates.io-index"
227
+
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
228
+
229
+
[[package]]
230
+
name = "memchr"
231
+
version = "2.7.6"
232
+
source = "registry+https://github.com/rust-lang/crates.io-index"
233
+
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
234
+
235
+
[[package]]
236
+
name = "memoffset"
237
+
version = "0.9.1"
238
+
source = "registry+https://github.com/rust-lang/crates.io-index"
239
+
checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
240
+
dependencies = [
241
+
"autocfg",
242
+
]
243
+
244
+
[[package]]
245
+
name = "num-conv"
246
+
version = "0.1.0"
247
+
source = "registry+https://github.com/rust-lang/crates.io-index"
248
+
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
249
+
250
+
[[package]]
251
+
name = "num-traits"
252
+
version = "0.2.19"
253
+
source = "registry+https://github.com/rust-lang/crates.io-index"
254
+
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
255
+
dependencies = [
256
+
"autocfg",
257
+
]
258
+
259
+
[[package]]
260
+
name = "once_cell"
261
+
version = "1.21.3"
262
+
source = "registry+https://github.com/rust-lang/crates.io-index"
263
+
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
264
+
265
+
[[package]]
266
+
name = "pmgfal"
267
+
version = "0.1.0"
268
+
dependencies = [
269
+
"atrium-lex",
270
+
"heck",
271
+
"pyo3",
272
+
"serde",
273
+
"serde_json",
274
+
]
275
+
276
+
[[package]]
277
+
name = "portable-atomic"
278
+
version = "1.11.1"
279
+
source = "registry+https://github.com/rust-lang/crates.io-index"
280
+
checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483"
281
+
282
+
[[package]]
283
+
name = "powerfmt"
284
+
version = "0.2.0"
285
+
source = "registry+https://github.com/rust-lang/crates.io-index"
286
+
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
287
+
288
+
[[package]]
289
+
name = "proc-macro2"
290
+
version = "1.0.103"
291
+
source = "registry+https://github.com/rust-lang/crates.io-index"
292
+
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
293
+
dependencies = [
294
+
"unicode-ident",
295
+
]
296
+
297
+
[[package]]
298
+
name = "pyo3"
299
+
version = "0.26.0"
300
+
source = "registry+https://github.com/rust-lang/crates.io-index"
301
+
checksum = "7ba0117f4212101ee6544044dae45abe1083d30ce7b29c4b5cbdfa2354e07383"
302
+
dependencies = [
303
+
"indoc",
304
+
"libc",
305
+
"memoffset",
306
+
"once_cell",
307
+
"portable-atomic",
308
+
"pyo3-build-config",
309
+
"pyo3-ffi",
310
+
"pyo3-macros",
311
+
"unindent",
312
+
]
313
+
314
+
[[package]]
315
+
name = "pyo3-build-config"
316
+
version = "0.26.0"
317
+
source = "registry+https://github.com/rust-lang/crates.io-index"
318
+
checksum = "4fc6ddaf24947d12a9aa31ac65431fb1b851b8f4365426e182901eabfb87df5f"
319
+
dependencies = [
320
+
"target-lexicon",
321
+
]
322
+
323
+
[[package]]
324
+
name = "pyo3-ffi"
325
+
version = "0.26.0"
326
+
source = "registry+https://github.com/rust-lang/crates.io-index"
327
+
checksum = "025474d3928738efb38ac36d4744a74a400c901c7596199e20e45d98eb194105"
328
+
dependencies = [
329
+
"libc",
330
+
"pyo3-build-config",
331
+
]
332
+
333
+
[[package]]
334
+
name = "pyo3-macros"
335
+
version = "0.26.0"
336
+
source = "registry+https://github.com/rust-lang/crates.io-index"
337
+
checksum = "2e64eb489f22fe1c95911b77c44cc41e7c19f3082fc81cce90f657cdc42ffded"
338
+
dependencies = [
339
+
"proc-macro2",
340
+
"pyo3-macros-backend",
341
+
"quote",
342
+
"syn",
343
+
]
344
+
345
+
[[package]]
346
+
name = "pyo3-macros-backend"
347
+
version = "0.26.0"
348
+
source = "registry+https://github.com/rust-lang/crates.io-index"
349
+
checksum = "100246c0ecf400b475341b8455a9213344569af29a3c841d29270e53102e0fcf"
350
+
dependencies = [
351
+
"heck",
352
+
"proc-macro2",
353
+
"pyo3-build-config",
354
+
"quote",
355
+
"syn",
356
+
]
357
+
358
+
[[package]]
359
+
name = "quote"
360
+
version = "1.0.42"
361
+
source = "registry+https://github.com/rust-lang/crates.io-index"
362
+
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
363
+
dependencies = [
364
+
"proc-macro2",
365
+
]
366
+
367
+
[[package]]
368
+
name = "rustversion"
369
+
version = "1.0.22"
370
+
source = "registry+https://github.com/rust-lang/crates.io-index"
371
+
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
372
+
373
+
[[package]]
374
+
name = "ryu"
375
+
version = "1.0.20"
376
+
source = "registry+https://github.com/rust-lang/crates.io-index"
377
+
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
378
+
379
+
[[package]]
380
+
name = "serde"
381
+
version = "1.0.228"
382
+
source = "registry+https://github.com/rust-lang/crates.io-index"
383
+
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
384
+
dependencies = [
385
+
"serde_core",
386
+
"serde_derive",
387
+
]
388
+
389
+
[[package]]
390
+
name = "serde_core"
391
+
version = "1.0.228"
392
+
source = "registry+https://github.com/rust-lang/crates.io-index"
393
+
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
394
+
dependencies = [
395
+
"serde_derive",
396
+
]
397
+
398
+
[[package]]
399
+
name = "serde_derive"
400
+
version = "1.0.228"
401
+
source = "registry+https://github.com/rust-lang/crates.io-index"
402
+
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
403
+
dependencies = [
404
+
"proc-macro2",
405
+
"quote",
406
+
"syn",
407
+
]
408
+
409
+
[[package]]
410
+
name = "serde_json"
411
+
version = "1.0.145"
412
+
source = "registry+https://github.com/rust-lang/crates.io-index"
413
+
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
414
+
dependencies = [
415
+
"itoa",
416
+
"memchr",
417
+
"ryu",
418
+
"serde",
419
+
"serde_core",
420
+
]
421
+
422
+
[[package]]
423
+
name = "serde_repr"
424
+
version = "0.1.20"
425
+
source = "registry+https://github.com/rust-lang/crates.io-index"
426
+
checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c"
427
+
dependencies = [
428
+
"proc-macro2",
429
+
"quote",
430
+
"syn",
431
+
]
432
+
433
+
[[package]]
434
+
name = "serde_with"
435
+
version = "2.3.3"
436
+
source = "registry+https://github.com/rust-lang/crates.io-index"
437
+
checksum = "07ff71d2c147a7b57362cead5e22f772cd52f6ab31cfcd9edcd7f6aeb2a0afbe"
438
+
dependencies = [
439
+
"base64",
440
+
"chrono",
441
+
"hex",
442
+
"indexmap",
443
+
"serde",
444
+
"serde_json",
445
+
"serde_with_macros",
446
+
"time",
447
+
]
448
+
449
+
[[package]]
450
+
name = "serde_with_macros"
451
+
version = "2.3.3"
452
+
source = "registry+https://github.com/rust-lang/crates.io-index"
453
+
checksum = "881b6f881b17d13214e5d494c939ebab463d01264ce1811e9d4ac3a882e7695f"
454
+
dependencies = [
455
+
"darling",
456
+
"proc-macro2",
457
+
"quote",
458
+
"syn",
459
+
]
460
+
461
+
[[package]]
462
+
name = "shlex"
463
+
version = "1.3.0"
464
+
source = "registry+https://github.com/rust-lang/crates.io-index"
465
+
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
466
+
467
+
[[package]]
468
+
name = "strsim"
469
+
version = "0.11.1"
470
+
source = "registry+https://github.com/rust-lang/crates.io-index"
471
+
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
472
+
473
+
[[package]]
474
+
name = "syn"
475
+
version = "2.0.111"
476
+
source = "registry+https://github.com/rust-lang/crates.io-index"
477
+
checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87"
478
+
dependencies = [
479
+
"proc-macro2",
480
+
"quote",
481
+
"unicode-ident",
482
+
]
483
+
484
+
[[package]]
485
+
name = "target-lexicon"
486
+
version = "0.13.3"
487
+
source = "registry+https://github.com/rust-lang/crates.io-index"
488
+
checksum = "df7f62577c25e07834649fc3b39fafdc597c0a3527dc1c60129201ccfcbaa50c"
489
+
490
+
[[package]]
491
+
name = "time"
492
+
version = "0.3.44"
493
+
source = "registry+https://github.com/rust-lang/crates.io-index"
494
+
checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d"
495
+
dependencies = [
496
+
"deranged",
497
+
"itoa",
498
+
"num-conv",
499
+
"powerfmt",
500
+
"serde",
501
+
"time-core",
502
+
"time-macros",
503
+
]
504
+
505
+
[[package]]
506
+
name = "time-core"
507
+
version = "0.1.6"
508
+
source = "registry+https://github.com/rust-lang/crates.io-index"
509
+
checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b"
510
+
511
+
[[package]]
512
+
name = "time-macros"
513
+
version = "0.2.24"
514
+
source = "registry+https://github.com/rust-lang/crates.io-index"
515
+
checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3"
516
+
dependencies = [
517
+
"num-conv",
518
+
"time-core",
519
+
]
520
+
521
+
[[package]]
522
+
name = "unicode-ident"
523
+
version = "1.0.22"
524
+
source = "registry+https://github.com/rust-lang/crates.io-index"
525
+
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
526
+
527
+
[[package]]
528
+
name = "unindent"
529
+
version = "0.2.4"
530
+
source = "registry+https://github.com/rust-lang/crates.io-index"
531
+
checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3"
532
+
533
+
[[package]]
534
+
name = "wasm-bindgen"
535
+
version = "0.2.106"
536
+
source = "registry+https://github.com/rust-lang/crates.io-index"
537
+
checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd"
538
+
dependencies = [
539
+
"cfg-if",
540
+
"once_cell",
541
+
"rustversion",
542
+
"wasm-bindgen-macro",
543
+
"wasm-bindgen-shared",
544
+
]
545
+
546
+
[[package]]
547
+
name = "wasm-bindgen-macro"
548
+
version = "0.2.106"
549
+
source = "registry+https://github.com/rust-lang/crates.io-index"
550
+
checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3"
551
+
dependencies = [
552
+
"quote",
553
+
"wasm-bindgen-macro-support",
554
+
]
555
+
556
+
[[package]]
557
+
name = "wasm-bindgen-macro-support"
558
+
version = "0.2.106"
559
+
source = "registry+https://github.com/rust-lang/crates.io-index"
560
+
checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40"
561
+
dependencies = [
562
+
"bumpalo",
563
+
"proc-macro2",
564
+
"quote",
565
+
"syn",
566
+
"wasm-bindgen-shared",
567
+
]
568
+
569
+
[[package]]
570
+
name = "wasm-bindgen-shared"
571
+
version = "0.2.106"
572
+
source = "registry+https://github.com/rust-lang/crates.io-index"
573
+
checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4"
574
+
dependencies = [
575
+
"unicode-ident",
576
+
]
577
+
578
+
[[package]]
579
+
name = "windows-core"
580
+
version = "0.62.2"
581
+
source = "registry+https://github.com/rust-lang/crates.io-index"
582
+
checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
583
+
dependencies = [
584
+
"windows-implement",
585
+
"windows-interface",
586
+
"windows-link",
587
+
"windows-result",
588
+
"windows-strings",
589
+
]
590
+
591
+
[[package]]
592
+
name = "windows-implement"
593
+
version = "0.60.2"
594
+
source = "registry+https://github.com/rust-lang/crates.io-index"
595
+
checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
596
+
dependencies = [
597
+
"proc-macro2",
598
+
"quote",
599
+
"syn",
600
+
]
601
+
602
+
[[package]]
603
+
name = "windows-interface"
604
+
version = "0.59.3"
605
+
source = "registry+https://github.com/rust-lang/crates.io-index"
606
+
checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
607
+
dependencies = [
608
+
"proc-macro2",
609
+
"quote",
610
+
"syn",
611
+
]
612
+
613
+
[[package]]
614
+
name = "windows-link"
615
+
version = "0.2.1"
616
+
source = "registry+https://github.com/rust-lang/crates.io-index"
617
+
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
618
+
619
+
[[package]]
620
+
name = "windows-result"
621
+
version = "0.4.1"
622
+
source = "registry+https://github.com/rust-lang/crates.io-index"
623
+
checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
624
+
dependencies = [
625
+
"windows-link",
626
+
]
627
+
628
+
[[package]]
629
+
name = "windows-strings"
630
+
version = "0.5.1"
631
+
source = "registry+https://github.com/rust-lang/crates.io-index"
632
+
checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
633
+
dependencies = [
634
+
"windows-link",
635
+
]
+22
Cargo.toml
+22
Cargo.toml
···
1
+
[package]
2
+
name = "pmgfal"
3
+
version = "0.1.0"
4
+
edition = "2021"
5
+
license = "MIT"
6
+
description = "pydantic model generator for atproto lexicons"
7
+
8
+
[lib]
9
+
name = "pmgfal"
10
+
crate-type = ["cdylib", "rlib"]
11
+
12
+
[dependencies]
13
+
pyo3 = { version = "0.26", features = ["extension-module"] }
14
+
atrium-lex = { git = "https://github.com/atrium-rs/atrium", branch = "main" }
15
+
serde = { version = "1.0", features = ["derive"] }
16
+
serde_json = "1.0"
17
+
heck = "0.5"
18
+
19
+
[profile.release]
20
+
lto = true
21
+
codegen-units = 1
22
+
strip = true
+21
LICENSE
+21
LICENSE
···
1
+
MIT License
2
+
3
+
Copyright (c) 2024 zzstoatzz
4
+
5
+
Permission is hereby granted, free of charge, to any person obtaining a copy
6
+
of this software and associated documentation files (the "Software"), to deal
7
+
in the Software without restriction, including without limitation the rights
8
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+
copies of the Software, and to permit persons to whom the Software is
10
+
furnished to do so, subject to the following conditions:
11
+
12
+
The above copyright notice and this permission notice shall be included in all
13
+
copies or substantial portions of the Software.
14
+
15
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+
SOFTWARE.
+49
README.md
+49
README.md
···
1
+
# pmgfal
2
+
3
+
pydantic model generator for atproto lexicons
4
+
5
+
rust-powered lexicon parsing and python code generation.
6
+
7
+
## install
8
+
9
+
```bash
10
+
uv add pmgfal
11
+
```
12
+
13
+
## usage
14
+
15
+
```bash
16
+
# auto-detect ./lexicons or current dir
17
+
uvx pmgfal
18
+
19
+
# explicit paths
20
+
uvx pmgfal ./lexicons -o ./src/models
21
+
22
+
# filter by namespace
23
+
uvx pmgfal -p fm.plyr
24
+
```
25
+
26
+
## output
27
+
28
+
```python
29
+
# auto-generated by pmgfal - do not edit
30
+
31
+
from __future__ import annotations
32
+
33
+
from pydantic import BaseModel, Field
34
+
35
+
36
+
class FmPlyrTrack(BaseModel):
37
+
"""fm.plyr.track record"""
38
+
39
+
uri: str
40
+
title: str
41
+
artist: str
42
+
duration_ms: int | None = Field(default=None, alias="durationMs")
43
+
```
44
+
45
+
## how it works
46
+
47
+
1. parses lexicon json using [atrium-lex](https://github.com/atrium-rs/atrium) (rust)
48
+
2. generates pydantic v2 models
49
+
3. outputs standalone python - no atproto sdk dependency
+16
justfile
+16
justfile
+62
pyproject.toml
+62
pyproject.toml
···
1
+
[project]
2
+
name = "pmgfal"
3
+
dynamic = ["version"]
4
+
description = "pydantic model generator for atproto lexicons"
5
+
readme = "README.md"
6
+
authors = [{ name = "zzstoatzz", email = "thrast36@gmail.com" }]
7
+
requires-python = ">=3.10"
8
+
license = "MIT"
9
+
10
+
keywords = ["atproto", "bluesky", "lexicon", "pydantic", "codegen"]
11
+
12
+
classifiers = [
13
+
"Development Status :: 3 - Alpha",
14
+
"Intended Audience :: Developers",
15
+
"License :: OSI Approved :: MIT License",
16
+
"Programming Language :: Python :: 3",
17
+
"Programming Language :: Python :: 3.10",
18
+
"Programming Language :: Python :: 3.11",
19
+
"Programming Language :: Python :: 3.12",
20
+
"Programming Language :: Python :: 3.13",
21
+
"Programming Language :: Python :: 3.14",
22
+
"Programming Language :: Rust",
23
+
"Topic :: Software Development :: Code Generators",
24
+
"Typing :: Typed",
25
+
]
26
+
27
+
dependencies = []
28
+
29
+
[project.scripts]
30
+
pmgfal = "pmgfal:main"
31
+
32
+
[dependency-groups]
33
+
dev = [
34
+
"maturin>=1.8.0",
35
+
"pytest>=8.3.0",
36
+
"pytest-sugar",
37
+
"ruff>=0.12.0",
38
+
"pydantic>=2.0.0",
39
+
]
40
+
41
+
[build-system]
42
+
requires = ["maturin>=1.8.0"]
43
+
build-backend = "maturin"
44
+
45
+
[tool.maturin]
46
+
python-source = "python"
47
+
module-name = "pmgfal._pmgfal"
48
+
bindings = "pyo3"
49
+
features = ["pyo3/extension-module"]
50
+
51
+
[tool.pytest.ini_options]
52
+
pythonpath = ["."]
53
+
testpaths = ["tests"]
54
+
55
+
[tool.ruff.lint]
56
+
fixable = ["ALL"]
57
+
ignore = ["COM812", "PLR0913", "SIM102", "SIM108"]
58
+
extend-select = ["B", "C4", "I", "PIE", "RUF", "SIM", "UP"]
59
+
60
+
[tool.ruff.lint.per-file-ignores]
61
+
"__init__.py" = ["F401", "I001"]
62
+
"tests/**/*.py" = ["S101"]
+78
python/pmgfal/__init__.py
+78
python/pmgfal/__init__.py
···
1
+
"""pydantic model generator for atproto lexicons."""
2
+
3
+
from __future__ import annotations
4
+
5
+
import argparse
6
+
import sys
7
+
from pathlib import Path
8
+
9
+
from pmgfal._pmgfal import __version__, generate
10
+
11
+
__all__ = ["__version__", "generate", "main"]
12
+
13
+
14
+
def main(args: list[str] | None = None) -> int:
15
+
"""cli entry point."""
16
+
parser = argparse.ArgumentParser(
17
+
prog="pmgfal",
18
+
description="pydantic model generator for atproto lexicons",
19
+
)
20
+
parser.add_argument(
21
+
"lexicon_dir",
22
+
nargs="?",
23
+
type=Path,
24
+
help="directory containing lexicon json files (default: ./lexicons or .)",
25
+
)
26
+
parser.add_argument(
27
+
"-o",
28
+
"--output",
29
+
type=Path,
30
+
default=Path("./generated"),
31
+
help="output directory (default: ./generated)",
32
+
)
33
+
parser.add_argument(
34
+
"-p",
35
+
"--prefix",
36
+
type=str,
37
+
default=None,
38
+
help="namespace prefix filter (e.g. 'fm.plyr')",
39
+
)
40
+
parser.add_argument(
41
+
"-V",
42
+
"--version",
43
+
action="version",
44
+
version=f"%(prog)s {__version__}",
45
+
)
46
+
47
+
parsed = parser.parse_args(args)
48
+
49
+
# auto-detect lexicon directory
50
+
if parsed.lexicon_dir is None:
51
+
if Path("./lexicons").is_dir():
52
+
lexicon_dir = Path("./lexicons")
53
+
else:
54
+
lexicon_dir = Path(".")
55
+
else:
56
+
lexicon_dir = parsed.lexicon_dir
57
+
58
+
if not lexicon_dir.is_dir():
59
+
print(f"error: not a directory: {lexicon_dir}", file=sys.stderr)
60
+
return 1
61
+
62
+
try:
63
+
files = generate(
64
+
str(lexicon_dir),
65
+
str(parsed.output),
66
+
parsed.prefix,
67
+
)
68
+
print(f"generated {len(files)} file(s):")
69
+
for f in files:
70
+
print(f" {f}")
71
+
return 0
72
+
except Exception as e:
73
+
print(f"error: {e}", file=sys.stderr)
74
+
return 1
75
+
76
+
77
+
if __name__ == "__main__":
78
+
sys.exit(main())
+19
python/pmgfal/_pmgfal.pyi
+19
python/pmgfal/_pmgfal.pyi
···
1
+
"""type stubs for rust bindings."""
2
+
3
+
__version__: str
4
+
5
+
def generate(
6
+
lexicon_dir: str,
7
+
output_dir: str,
8
+
namespace_prefix: str | None = None,
9
+
) -> list[str]:
10
+
"""generate pydantic models from lexicon files.
11
+
12
+
Args:
13
+
lexicon_dir: directory containing lexicon json files
14
+
output_dir: directory to write generated python files
15
+
namespace_prefix: optional filter for specific nsid prefix
16
+
17
+
Returns:
18
+
list of generated file paths
19
+
"""
python/pmgfal/py.typed
python/pmgfal/py.typed
This is a binary file and will not be displayed.
+241
src/lib.rs
+241
src/lib.rs
···
1
+
use std::fs;
2
+
use std::path::Path;
3
+
4
+
use atrium_lex::lexicon::{
5
+
LexObject, LexObjectProperty, LexRecord, LexUserType,
6
+
};
7
+
use atrium_lex::LexiconDoc;
8
+
use heck::{ToPascalCase, ToSnakeCase};
9
+
use pyo3::prelude::*;
10
+
11
+
const HEADER: &str = r#"# auto-generated by pmgfal - do not edit
12
+
13
+
from __future__ import annotations
14
+
15
+
from typing import Any
16
+
17
+
from pydantic import BaseModel, Field
18
+
"#;
19
+
20
+
/// convert lexicon type to python type annotation
21
+
fn type_to_python(prop: &LexObjectProperty) -> String {
22
+
match prop {
23
+
LexObjectProperty::Boolean(_) => "bool".to_string(),
24
+
LexObjectProperty::Integer(_) => "int".to_string(),
25
+
LexObjectProperty::String(_) => "str".to_string(),
26
+
LexObjectProperty::Bytes(_) => "bytes".to_string(),
27
+
LexObjectProperty::CidLink(_) => "str".to_string(),
28
+
LexObjectProperty::Blob(_) => "dict[str, Any]".to_string(),
29
+
LexObjectProperty::Array(arr) => {
30
+
let item_type = match &arr.items {
31
+
atrium_lex::lexicon::LexArrayItem::Boolean(_) => "bool",
32
+
atrium_lex::lexicon::LexArrayItem::Integer(_) => "int",
33
+
atrium_lex::lexicon::LexArrayItem::String(_) => "str",
34
+
atrium_lex::lexicon::LexArrayItem::Bytes(_) => "bytes",
35
+
atrium_lex::lexicon::LexArrayItem::CidLink(_) => "str",
36
+
atrium_lex::lexicon::LexArrayItem::Blob(_) => "dict[str, Any]",
37
+
atrium_lex::lexicon::LexArrayItem::Ref(_) => "dict[str, Any]",
38
+
atrium_lex::lexicon::LexArrayItem::Union(_) => "dict[str, Any]",
39
+
atrium_lex::lexicon::LexArrayItem::Unknown(_) => "Any",
40
+
};
41
+
format!("list[{}]", item_type)
42
+
}
43
+
LexObjectProperty::Ref(_) => "dict[str, Any]".to_string(),
44
+
LexObjectProperty::Union(_) => "dict[str, Any]".to_string(),
45
+
LexObjectProperty::Unknown(_) => "Any".to_string(),
46
+
}
47
+
}
48
+
49
+
/// generate class name from nsid and def name
50
+
fn to_class_name(nsid: &str, def_name: &str) -> String {
51
+
let mut parts: Vec<&str> = nsid.split('.').collect();
52
+
if def_name != "main" {
53
+
parts.push(def_name);
54
+
}
55
+
parts.iter().map(|p| p.to_pascal_case()).collect()
56
+
}
57
+
58
+
/// generate python field name
59
+
fn to_field_name(name: &str) -> String {
60
+
let snake = name.to_snake_case();
61
+
// handle python keywords
62
+
match snake.as_str() {
63
+
"type" | "class" | "import" | "from" | "global" | "lambda" => format!("{}_", snake),
64
+
_ => snake,
65
+
}
66
+
}
67
+
68
+
/// generate a pydantic model class from an object definition
69
+
fn generate_object_class(
70
+
class_name: &str,
71
+
obj: &LexObject,
72
+
description: Option<&str>,
73
+
) -> String {
74
+
let mut lines = vec![format!("class {}(BaseModel):", class_name)];
75
+
76
+
if let Some(desc) = description {
77
+
lines.push(format!(" \"\"\"{}\"\"\"", desc));
78
+
}
79
+
80
+
if obj.properties.is_empty() {
81
+
lines.push(" pass".to_string());
82
+
return lines.join("\n");
83
+
}
84
+
85
+
let required: std::collections::HashSet<_> = obj
86
+
.required
87
+
.as_ref()
88
+
.map(|r| r.iter().collect())
89
+
.unwrap_or_default();
90
+
91
+
for (name, prop) in &obj.properties {
92
+
let field_name = to_field_name(name);
93
+
let mut py_type = type_to_python(prop);
94
+
95
+
let is_required = required.contains(name);
96
+
if !is_required {
97
+
py_type = format!("{} | None", py_type);
98
+
}
99
+
100
+
let needs_alias = field_name != *name;
101
+
let needs_default = !is_required;
102
+
103
+
if needs_alias || needs_default {
104
+
let mut field_args = vec![];
105
+
if needs_default {
106
+
field_args.push("default=None".to_string());
107
+
}
108
+
if needs_alias {
109
+
field_args.push(format!("alias=\"{}\"", name));
110
+
}
111
+
lines.push(format!(
112
+
" {}: {} = Field({})",
113
+
field_name,
114
+
py_type,
115
+
field_args.join(", ")
116
+
));
117
+
} else {
118
+
lines.push(format!(" {}: {}", field_name, py_type));
119
+
}
120
+
}
121
+
122
+
lines.join("\n")
123
+
}
124
+
125
+
/// parse lexicon files from a directory
126
+
fn parse_lexicons(dir: &Path) -> Result<Vec<LexiconDoc>, String> {
127
+
let mut docs = vec![];
128
+
129
+
fn visit_dir(dir: &Path, docs: &mut Vec<LexiconDoc>) -> Result<(), String> {
130
+
if !dir.is_dir() {
131
+
return Err(format!("not a directory: {}", dir.display()));
132
+
}
133
+
134
+
for entry in fs::read_dir(dir).map_err(|e| e.to_string())? {
135
+
let entry = entry.map_err(|e| e.to_string())?;
136
+
let path = entry.path();
137
+
138
+
if path.is_dir() {
139
+
visit_dir(&path, docs)?;
140
+
} else if path.extension().map(|e| e == "json").unwrap_or(false) {
141
+
let content = fs::read_to_string(&path).map_err(|e| e.to_string())?;
142
+
match serde_json::from_str::<LexiconDoc>(&content) {
143
+
Ok(doc) => docs.push(doc),
144
+
Err(_) => continue, // skip non-lexicon json
145
+
}
146
+
}
147
+
}
148
+
Ok(())
149
+
}
150
+
151
+
visit_dir(dir, &mut docs)?;
152
+
docs.sort_by(|a, b| a.id.cmp(&b.id));
153
+
Ok(docs)
154
+
}
155
+
156
+
/// generate pydantic models from lexicon files
157
+
#[pyfunction]
158
+
#[pyo3(signature = (lexicon_dir, output_dir, namespace_prefix=None))]
159
+
fn generate(
160
+
lexicon_dir: &str,
161
+
output_dir: &str,
162
+
namespace_prefix: Option<&str>,
163
+
) -> PyResult<Vec<String>> {
164
+
let lexicon_path = Path::new(lexicon_dir);
165
+
let output_path = Path::new(output_dir);
166
+
167
+
let docs = parse_lexicons(lexicon_path)
168
+
.map_err(|e| PyErr::new::<pyo3::exceptions::PyValueError, _>(e))?;
169
+
170
+
let filtered: Vec<_> = docs
171
+
.iter()
172
+
.filter(|doc| {
173
+
namespace_prefix
174
+
.map(|p| doc.id.starts_with(p))
175
+
.unwrap_or(true)
176
+
})
177
+
.collect();
178
+
179
+
if filtered.is_empty() {
180
+
return Ok(vec![]);
181
+
}
182
+
183
+
fs::create_dir_all(output_path)
184
+
.map_err(|e| PyErr::new::<pyo3::exceptions::PyIOError, _>(e.to_string()))?;
185
+
186
+
let mut output = String::from(HEADER);
187
+
output.push('\n');
188
+
189
+
for doc in &filtered {
190
+
output.push_str(&format!("\n# {}\n", doc.id));
191
+
192
+
for (def_name, def) in &doc.defs {
193
+
let class_name = to_class_name(&doc.id, def_name);
194
+
195
+
match def {
196
+
LexUserType::Record(LexRecord { record, description, .. }) => {
197
+
let atrium_lex::lexicon::LexRecordRecord::Object(obj) = record;
198
+
let desc = description.as_deref().unwrap_or(&doc.id);
199
+
output.push_str(&generate_object_class(&class_name, obj, Some(desc)));
200
+
output.push_str("\n\n");
201
+
}
202
+
LexUserType::Object(obj) => {
203
+
output.push_str(&generate_object_class(
204
+
&class_name,
205
+
obj,
206
+
obj.description.as_deref(),
207
+
));
208
+
output.push_str("\n\n");
209
+
}
210
+
LexUserType::Token(_) => {
211
+
output.push_str(&format!(
212
+
"# token: {}\n{} = \"{}#{}\"\n\n",
213
+
class_name,
214
+
class_name.to_uppercase(),
215
+
doc.id,
216
+
def_name
217
+
));
218
+
}
219
+
_ => {}
220
+
}
221
+
}
222
+
}
223
+
224
+
let output_file = if let Some(prefix) = namespace_prefix {
225
+
output_path.join(format!("{}.py", prefix.replace('.', "_")))
226
+
} else {
227
+
output_path.join("models.py")
228
+
};
229
+
230
+
fs::write(&output_file, &output)
231
+
.map_err(|e| PyErr::new::<pyo3::exceptions::PyIOError, _>(e.to_string()))?;
232
+
233
+
Ok(vec![output_file.to_string_lossy().to_string()])
234
+
}
235
+
236
+
#[pymodule]
237
+
fn _pmgfal(m: &Bound<'_, PyModule>) -> PyResult<()> {
238
+
m.add_function(wrap_pyfunction!(generate, m)?)?;
239
+
m.add("__version__", env!("CARGO_PKG_VERSION"))?;
240
+
Ok(())
241
+
}
+1
tests/__init__.py
+1
tests/__init__.py
···
1
+
"""black-box tests for pmgfal."""
+161
tests/test_generate.py
+161
tests/test_generate.py
···
1
+
"""black-box tests for model generation."""
2
+
3
+
import json
4
+
import tempfile
5
+
from pathlib import Path
6
+
7
+
8
+
class TestGenerate:
9
+
"""test the generate function end-to-end."""
10
+
11
+
def test_generate_simple_record(self):
12
+
"""generate a pydantic model from a simple record lexicon."""
13
+
from pmgfal import generate
14
+
15
+
lexicon = {
16
+
"lexicon": 1,
17
+
"id": "fm.plyr.track",
18
+
"description": "a music track",
19
+
"defs": {
20
+
"main": {
21
+
"type": "record",
22
+
"description": "track record",
23
+
"record": {
24
+
"type": "object",
25
+
"properties": {
26
+
"uri": {"type": "string"},
27
+
"title": {"type": "string"},
28
+
"artist": {"type": "string"},
29
+
"durationMs": {"type": "integer"},
30
+
},
31
+
"required": ["uri", "title", "artist"],
32
+
},
33
+
}
34
+
},
35
+
}
36
+
37
+
with tempfile.TemporaryDirectory() as tmpdir:
38
+
lexicon_dir = Path(tmpdir) / "lexicons"
39
+
lexicon_dir.mkdir()
40
+
(lexicon_dir / "track.json").write_text(json.dumps(lexicon))
41
+
42
+
output_dir = Path(tmpdir) / "generated"
43
+
files = generate(str(lexicon_dir), str(output_dir))
44
+
45
+
assert len(files) == 1
46
+
content = Path(files[0]).read_text()
47
+
48
+
# verify header
49
+
assert "auto-generated by pmgfal" in content
50
+
51
+
# verify class exists
52
+
assert "class FmPlyrTrack(BaseModel):" in content
53
+
54
+
# verify required fields
55
+
assert "uri: str" in content
56
+
assert "title: str" in content
57
+
assert "artist: str" in content
58
+
59
+
# verify optional field with alias
60
+
assert "duration_ms" in content
61
+
assert "durationMs" in content
62
+
63
+
def test_generate_with_namespace_filter(self):
64
+
"""filter by namespace prefix."""
65
+
from pmgfal import generate
66
+
67
+
lexicons = [
68
+
{
69
+
"lexicon": 1,
70
+
"id": "fm.plyr.track",
71
+
"defs": {
72
+
"main": {
73
+
"type": "record",
74
+
"record": {"type": "object", "properties": {}},
75
+
}
76
+
},
77
+
},
78
+
{
79
+
"lexicon": 1,
80
+
"id": "com.other.thing",
81
+
"defs": {
82
+
"main": {
83
+
"type": "record",
84
+
"record": {"type": "object", "properties": {}},
85
+
}
86
+
},
87
+
},
88
+
]
89
+
90
+
with tempfile.TemporaryDirectory() as tmpdir:
91
+
lexicon_dir = Path(tmpdir) / "lexicons"
92
+
lexicon_dir.mkdir()
93
+
94
+
for lex in lexicons:
95
+
name = lex["id"].replace(".", "_")
96
+
(lexicon_dir / f"{name}.json").write_text(json.dumps(lex))
97
+
98
+
output_dir = Path(tmpdir) / "generated"
99
+
files = generate(str(lexicon_dir), str(output_dir), "fm.plyr")
100
+
101
+
assert len(files) == 1
102
+
content = Path(files[0]).read_text()
103
+
assert "FmPlyrTrack" in content
104
+
assert "ComOtherThing" not in content
105
+
106
+
def test_generate_empty_dir(self):
107
+
"""empty directory returns empty list."""
108
+
from pmgfal import generate
109
+
110
+
with tempfile.TemporaryDirectory() as tmpdir:
111
+
lexicon_dir = Path(tmpdir) / "lexicons"
112
+
lexicon_dir.mkdir()
113
+
output_dir = Path(tmpdir) / "generated"
114
+
115
+
files = generate(str(lexicon_dir), str(output_dir))
116
+
assert files == []
117
+
118
+
def test_generated_models_are_valid_pydantic(self):
119
+
"""generated models should be importable and work with pydantic."""
120
+
from pmgfal import generate
121
+
122
+
lexicon = {
123
+
"lexicon": 1,
124
+
"id": "test.example",
125
+
"defs": {
126
+
"main": {
127
+
"type": "record",
128
+
"record": {
129
+
"type": "object",
130
+
"properties": {
131
+
"name": {"type": "string"},
132
+
"count": {"type": "integer"},
133
+
},
134
+
"required": ["name"],
135
+
},
136
+
}
137
+
},
138
+
}
139
+
140
+
with tempfile.TemporaryDirectory() as tmpdir:
141
+
lexicon_dir = Path(tmpdir) / "lexicons"
142
+
lexicon_dir.mkdir()
143
+
(lexicon_dir / "example.json").write_text(json.dumps(lexicon))
144
+
145
+
output_dir = Path(tmpdir) / "generated"
146
+
files = generate(str(lexicon_dir), str(output_dir))
147
+
148
+
# exec the generated code
149
+
content = Path(files[0]).read_text()
150
+
namespace = {}
151
+
exec(content, namespace)
152
+
153
+
# verify class exists and works
154
+
TestExample = namespace["TestExample"]
155
+
instance = TestExample(name="test", count=42)
156
+
assert instance.name == "test"
157
+
assert instance.count == 42
158
+
159
+
# verify optional field default
160
+
instance2 = TestExample(name="test")
161
+
assert instance2.count is None
+386
uv.lock
+386
uv.lock
···
1
+
version = 1
2
+
revision = 3
3
+
requires-python = ">=3.10"
4
+
5
+
[[package]]
6
+
name = "annotated-types"
7
+
version = "0.7.0"
8
+
source = { registry = "https://pypi.org/simple" }
9
+
sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" }
10
+
wheels = [
11
+
{ url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" },
12
+
]
13
+
14
+
[[package]]
15
+
name = "colorama"
16
+
version = "0.4.6"
17
+
source = { registry = "https://pypi.org/simple" }
18
+
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
19
+
wheels = [
20
+
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
21
+
]
22
+
23
+
[[package]]
24
+
name = "exceptiongroup"
25
+
version = "1.3.1"
26
+
source = { registry = "https://pypi.org/simple" }
27
+
dependencies = [
28
+
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
29
+
]
30
+
sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" }
31
+
wheels = [
32
+
{ url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" },
33
+
]
34
+
35
+
[[package]]
36
+
name = "iniconfig"
37
+
version = "2.3.0"
38
+
source = { registry = "https://pypi.org/simple" }
39
+
sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" }
40
+
wheels = [
41
+
{ url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" },
42
+
]
43
+
44
+
[[package]]
45
+
name = "maturin"
46
+
version = "1.10.2"
47
+
source = { registry = "https://pypi.org/simple" }
48
+
dependencies = [
49
+
{ name = "tomli", marker = "python_full_version < '3.11'" },
50
+
]
51
+
sdist = { url = "https://files.pythonhosted.org/packages/02/44/c593afce7d418ae6016b955c978055232359ad28c707a9ac6643fc60512d/maturin-1.10.2.tar.gz", hash = "sha256:259292563da89850bf8f7d37aa4ddba22905214c1e180b1c8f55505dfd8c0e81", size = 217835, upload-time = "2025-11-19T11:53:17.348Z" }
52
+
wheels = [
53
+
{ url = "https://files.pythonhosted.org/packages/15/74/7f7e93019bb71aa072a7cdf951cbe4c9a8d5870dd86c66ec67002153487f/maturin-1.10.2-py3-none-linux_armv6l.whl", hash = "sha256:11c73815f21a755d2129c410e6cb19dbfacbc0155bfc46c706b69930c2eb794b", size = 8763201, upload-time = "2025-11-19T11:52:42.98Z" },
54
+
{ url = "https://files.pythonhosted.org/packages/4a/85/1d1b64dbb6518ee633bfde8787e251ae59428818fea7a6bdacb8008a09bd/maturin-1.10.2-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:7fbd997c5347649ee7987bd05a92bd5b8b07efa4ac3f8bcbf6196e07eb573d89", size = 17072583, upload-time = "2025-11-19T11:52:45.636Z" },
55
+
{ url = "https://files.pythonhosted.org/packages/7c/45/2418f0d6e1cbdf890205d1dc73ebea6778bb9ce80f92e866576c701ded72/maturin-1.10.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:e3ce9b2ad4fb9c341f450a6d32dc3edb409a2d582a81bc46ba55f6e3b6196b22", size = 8827021, upload-time = "2025-11-19T11:52:48.143Z" },
56
+
{ url = "https://files.pythonhosted.org/packages/7f/83/14c96ddc93b38745d8c3b85126f7d78a94f809a49dc9644bb22b0dc7b78c/maturin-1.10.2-py3-none-manylinux_2_12_i686.manylinux2010_i686.musllinux_1_1_i686.whl", hash = "sha256:f0d1b7b5f73c8d30a7e71cd2a2189a7f0126a3a3cd8b3d6843e7e1d4db50f759", size = 8751780, upload-time = "2025-11-19T11:52:51.613Z" },
57
+
{ url = "https://files.pythonhosted.org/packages/46/8d/753148c0d0472acd31a297f6d11c3263cd2668d38278ed29d523625f7290/maturin-1.10.2-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.musllinux_1_1_x86_64.whl", hash = "sha256:efcd496a3202ffe0d0489df1f83d08b91399782fb2dd545d5a1e7bf6fd81af39", size = 9241884, upload-time = "2025-11-19T11:52:53.946Z" },
58
+
{ url = "https://files.pythonhosted.org/packages/b9/f9/f5ca9fe8cad70cac6f3b6008598cc708f8a74dd619baced99784a6253f23/maturin-1.10.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:a41ec70d99e27c05377be90f8e3c3def2a7bae4d0d9d5ea874aaf2d1da625d5c", size = 8671736, upload-time = "2025-11-19T11:52:57.133Z" },
59
+
{ url = "https://files.pythonhosted.org/packages/0a/76/f59cbcfcabef0259c3971f8b5754c85276a272028d8363386b03ec4e9947/maturin-1.10.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:07a82864352feeaf2167247c8206937ef6c6ae9533025d416b7004ade0ea601d", size = 8633475, upload-time = "2025-11-19T11:53:00.389Z" },
60
+
{ url = "https://files.pythonhosted.org/packages/53/40/96cd959ad1dda6c12301860a74afece200a3209d84b393beedd5d7d915c0/maturin-1.10.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.musllinux_1_1_ppc64le.whl", hash = "sha256:04df81ee295dcda37828bd025a4ac688ea856e3946e4cb300a8f44a448de0069", size = 11177118, upload-time = "2025-11-19T11:53:03.014Z" },
61
+
{ url = "https://files.pythonhosted.org/packages/e5/b6/144f180f36314be183f5237011528f0e39fe5fd2e74e65c3b44a5795971e/maturin-1.10.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96e1d391e4c1fa87edf2a37e4d53d5f2e5f39dd880b9d8306ac9f8eb212d23f8", size = 9320218, upload-time = "2025-11-19T11:53:05.39Z" },
62
+
{ url = "https://files.pythonhosted.org/packages/eb/2d/2c483c1b3118e2e10fd8219d5291843f5f7c12284113251bf506144a3ac1/maturin-1.10.2-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:a217aa7c42aa332fb8e8377eb07314e1f02cf0fe036f614aca4575121952addd", size = 8985266, upload-time = "2025-11-19T11:53:07.618Z" },
63
+
{ url = "https://files.pythonhosted.org/packages/1d/98/1d0222521e112cd058b56e8d96c72cf9615f799e3b557adb4b16004f42aa/maturin-1.10.2-py3-none-win32.whl", hash = "sha256:da031771d9fb6ddb1d373638ec2556feee29e4507365cd5749a2d354bcadd818", size = 7667897, upload-time = "2025-11-19T11:53:10.14Z" },
64
+
{ url = "https://files.pythonhosted.org/packages/a0/ec/c6c973b1def0d04533620b439d5d7aebb257657ba66710885394514c8045/maturin-1.10.2-py3-none-win_amd64.whl", hash = "sha256:da777766fd584440dc9fecd30059a94f85e4983f58b09e438ae38ee4b494024c", size = 8908416, upload-time = "2025-11-19T11:53:12.862Z" },
65
+
{ url = "https://files.pythonhosted.org/packages/1b/01/7da60c9f7d5dc92dfa5e8888239fd0fb2613ee19e44e6db5c2ed5595fab3/maturin-1.10.2-py3-none-win_arm64.whl", hash = "sha256:a4c29a770ea2c76082e0afc6d4efd8ee94405588bfae00d10828f72e206c739b", size = 7506680, upload-time = "2025-11-19T11:53:15.403Z" },
66
+
]
67
+
68
+
[[package]]
69
+
name = "packaging"
70
+
version = "25.0"
71
+
source = { registry = "https://pypi.org/simple" }
72
+
sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" }
73
+
wheels = [
74
+
{ url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" },
75
+
]
76
+
77
+
[[package]]
78
+
name = "pluggy"
79
+
version = "1.6.0"
80
+
source = { registry = "https://pypi.org/simple" }
81
+
sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" }
82
+
wheels = [
83
+
{ url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
84
+
]
85
+
86
+
[[package]]
87
+
name = "pmgfal"
88
+
source = { editable = "." }
89
+
90
+
[package.dev-dependencies]
91
+
dev = [
92
+
{ name = "maturin" },
93
+
{ name = "pydantic" },
94
+
{ name = "pytest" },
95
+
{ name = "pytest-sugar" },
96
+
{ name = "ruff" },
97
+
]
98
+
99
+
[package.metadata]
100
+
101
+
[package.metadata.requires-dev]
102
+
dev = [
103
+
{ name = "maturin", specifier = ">=1.8.0" },
104
+
{ name = "pydantic", specifier = ">=2.0.0" },
105
+
{ name = "pytest", specifier = ">=8.3.0" },
106
+
{ name = "pytest-sugar" },
107
+
{ name = "ruff", specifier = ">=0.12.0" },
108
+
]
109
+
110
+
[[package]]
111
+
name = "pydantic"
112
+
version = "2.12.5"
113
+
source = { registry = "https://pypi.org/simple" }
114
+
dependencies = [
115
+
{ name = "annotated-types" },
116
+
{ name = "pydantic-core" },
117
+
{ name = "typing-extensions" },
118
+
{ name = "typing-inspection" },
119
+
]
120
+
sdist = { url = "https://files.pythonhosted.org/packages/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" }
121
+
wheels = [
122
+
{ url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" },
123
+
]
124
+
125
+
[[package]]
126
+
name = "pydantic-core"
127
+
version = "2.41.5"
128
+
source = { registry = "https://pypi.org/simple" }
129
+
dependencies = [
130
+
{ name = "typing-extensions" },
131
+
]
132
+
sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" }
133
+
wheels = [
134
+
{ url = "https://files.pythonhosted.org/packages/c6/90/32c9941e728d564b411d574d8ee0cf09b12ec978cb22b294995bae5549a5/pydantic_core-2.41.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146", size = 2107298, upload-time = "2025-11-04T13:39:04.116Z" },
135
+
{ url = "https://files.pythonhosted.org/packages/fb/a8/61c96a77fe28993d9a6fb0f4127e05430a267b235a124545d79fea46dd65/pydantic_core-2.41.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2", size = 1901475, upload-time = "2025-11-04T13:39:06.055Z" },
136
+
{ url = "https://files.pythonhosted.org/packages/5d/b6/338abf60225acc18cdc08b4faef592d0310923d19a87fba1faf05af5346e/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5921a4d3ca3aee735d9fd163808f5e8dd6c6972101e4adbda9a4667908849b97", size = 1918815, upload-time = "2025-11-04T13:39:10.41Z" },
137
+
{ url = "https://files.pythonhosted.org/packages/d1/1c/2ed0433e682983d8e8cba9c8d8ef274d4791ec6a6f24c58935b90e780e0a/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25c479382d26a2a41b7ebea1043564a937db462816ea07afa8a44c0866d52f9", size = 2065567, upload-time = "2025-11-04T13:39:12.244Z" },
138
+
{ url = "https://files.pythonhosted.org/packages/b3/24/cf84974ee7d6eae06b9e63289b7b8f6549d416b5c199ca2d7ce13bbcf619/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f547144f2966e1e16ae626d8ce72b4cfa0caedc7fa28052001c94fb2fcaa1c52", size = 2230442, upload-time = "2025-11-04T13:39:13.962Z" },
139
+
{ url = "https://files.pythonhosted.org/packages/fd/21/4e287865504b3edc0136c89c9c09431be326168b1eb7841911cbc877a995/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f52298fbd394f9ed112d56f3d11aabd0d5bd27beb3084cc3d8ad069483b8941", size = 2350956, upload-time = "2025-11-04T13:39:15.889Z" },
140
+
{ url = "https://files.pythonhosted.org/packages/a8/76/7727ef2ffa4b62fcab916686a68a0426b9b790139720e1934e8ba797e238/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:100baa204bb412b74fe285fb0f3a385256dad1d1879f0a5cb1499ed2e83d132a", size = 2068253, upload-time = "2025-11-04T13:39:17.403Z" },
141
+
{ url = "https://files.pythonhosted.org/packages/d5/8c/a4abfc79604bcb4c748e18975c44f94f756f08fb04218d5cb87eb0d3a63e/pydantic_core-2.41.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:05a2c8852530ad2812cb7914dc61a1125dc4e06252ee98e5638a12da6cc6fb6c", size = 2177050, upload-time = "2025-11-04T13:39:19.351Z" },
142
+
{ url = "https://files.pythonhosted.org/packages/67/b1/de2e9a9a79b480f9cb0b6e8b6ba4c50b18d4e89852426364c66aa82bb7b3/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:29452c56df2ed968d18d7e21f4ab0ac55e71dc59524872f6fc57dcf4a3249ed2", size = 2147178, upload-time = "2025-11-04T13:39:21Z" },
143
+
{ url = "https://files.pythonhosted.org/packages/16/c1/dfb33f837a47b20417500efaa0378adc6635b3c79e8369ff7a03c494b4ac/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:d5160812ea7a8a2ffbe233d8da666880cad0cbaf5d4de74ae15c313213d62556", size = 2341833, upload-time = "2025-11-04T13:39:22.606Z" },
144
+
{ url = "https://files.pythonhosted.org/packages/47/36/00f398642a0f4b815a9a558c4f1dca1b4020a7d49562807d7bc9ff279a6c/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:df3959765b553b9440adfd3c795617c352154e497a4eaf3752555cfb5da8fc49", size = 2321156, upload-time = "2025-11-04T13:39:25.843Z" },
145
+
{ url = "https://files.pythonhosted.org/packages/7e/70/cad3acd89fde2010807354d978725ae111ddf6d0ea46d1ea1775b5c1bd0c/pydantic_core-2.41.5-cp310-cp310-win32.whl", hash = "sha256:1f8d33a7f4d5a7889e60dc39856d76d09333d8a6ed0f5f1190635cbec70ec4ba", size = 1989378, upload-time = "2025-11-04T13:39:27.92Z" },
146
+
{ url = "https://files.pythonhosted.org/packages/76/92/d338652464c6c367e5608e4488201702cd1cbb0f33f7b6a85a60fe5f3720/pydantic_core-2.41.5-cp310-cp310-win_amd64.whl", hash = "sha256:62de39db01b8d593e45871af2af9e497295db8d73b085f6bfd0b18c83c70a8f9", size = 2013622, upload-time = "2025-11-04T13:39:29.848Z" },
147
+
{ url = "https://files.pythonhosted.org/packages/e8/72/74a989dd9f2084b3d9530b0915fdda64ac48831c30dbf7c72a41a5232db8/pydantic_core-2.41.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6", size = 2105873, upload-time = "2025-11-04T13:39:31.373Z" },
148
+
{ url = "https://files.pythonhosted.org/packages/12/44/37e403fd9455708b3b942949e1d7febc02167662bf1a7da5b78ee1ea2842/pydantic_core-2.41.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b", size = 1899826, upload-time = "2025-11-04T13:39:32.897Z" },
149
+
{ url = "https://files.pythonhosted.org/packages/33/7f/1d5cab3ccf44c1935a359d51a8a2a9e1a654b744b5e7f80d41b88d501eec/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a", size = 1917869, upload-time = "2025-11-04T13:39:34.469Z" },
150
+
{ url = "https://files.pythonhosted.org/packages/6e/6a/30d94a9674a7fe4f4744052ed6c5e083424510be1e93da5bc47569d11810/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8", size = 2063890, upload-time = "2025-11-04T13:39:36.053Z" },
151
+
{ url = "https://files.pythonhosted.org/packages/50/be/76e5d46203fcb2750e542f32e6c371ffa9b8ad17364cf94bb0818dbfb50c/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e", size = 2229740, upload-time = "2025-11-04T13:39:37.753Z" },
152
+
{ url = "https://files.pythonhosted.org/packages/d3/ee/fed784df0144793489f87db310a6bbf8118d7b630ed07aa180d6067e653a/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1", size = 2350021, upload-time = "2025-11-04T13:39:40.94Z" },
153
+
{ url = "https://files.pythonhosted.org/packages/c8/be/8fed28dd0a180dca19e72c233cbf58efa36df055e5b9d90d64fd1740b828/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b", size = 2066378, upload-time = "2025-11-04T13:39:42.523Z" },
154
+
{ url = "https://files.pythonhosted.org/packages/b0/3b/698cf8ae1d536a010e05121b4958b1257f0b5522085e335360e53a6b1c8b/pydantic_core-2.41.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b", size = 2175761, upload-time = "2025-11-04T13:39:44.553Z" },
155
+
{ url = "https://files.pythonhosted.org/packages/b8/ba/15d537423939553116dea94ce02f9c31be0fa9d0b806d427e0308ec17145/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284", size = 2146303, upload-time = "2025-11-04T13:39:46.238Z" },
156
+
{ url = "https://files.pythonhosted.org/packages/58/7f/0de669bf37d206723795f9c90c82966726a2ab06c336deba4735b55af431/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594", size = 2340355, upload-time = "2025-11-04T13:39:48.002Z" },
157
+
{ url = "https://files.pythonhosted.org/packages/e5/de/e7482c435b83d7e3c3ee5ee4451f6e8973cff0eb6007d2872ce6383f6398/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e", size = 2319875, upload-time = "2025-11-04T13:39:49.705Z" },
158
+
{ url = "https://files.pythonhosted.org/packages/fe/e6/8c9e81bb6dd7560e33b9053351c29f30c8194b72f2d6932888581f503482/pydantic_core-2.41.5-cp311-cp311-win32.whl", hash = "sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b", size = 1987549, upload-time = "2025-11-04T13:39:51.842Z" },
159
+
{ url = "https://files.pythonhosted.org/packages/11/66/f14d1d978ea94d1bc21fc98fcf570f9542fe55bfcc40269d4e1a21c19bf7/pydantic_core-2.41.5-cp311-cp311-win_amd64.whl", hash = "sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe", size = 2011305, upload-time = "2025-11-04T13:39:53.485Z" },
160
+
{ url = "https://files.pythonhosted.org/packages/56/d8/0e271434e8efd03186c5386671328154ee349ff0354d83c74f5caaf096ed/pydantic_core-2.41.5-cp311-cp311-win_arm64.whl", hash = "sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f", size = 1972902, upload-time = "2025-11-04T13:39:56.488Z" },
161
+
{ url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990, upload-time = "2025-11-04T13:39:58.079Z" },
162
+
{ url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003, upload-time = "2025-11-04T13:39:59.956Z" },
163
+
{ url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200, upload-time = "2025-11-04T13:40:02.241Z" },
164
+
{ url = "https://files.pythonhosted.org/packages/38/de/8c36b5198a29bdaade07b5985e80a233a5ac27137846f3bc2d3b40a47360/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75", size = 2052578, upload-time = "2025-11-04T13:40:04.401Z" },
165
+
{ url = "https://files.pythonhosted.org/packages/00/b5/0e8e4b5b081eac6cb3dbb7e60a65907549a1ce035a724368c330112adfdd/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05", size = 2208504, upload-time = "2025-11-04T13:40:06.072Z" },
166
+
{ url = "https://files.pythonhosted.org/packages/77/56/87a61aad59c7c5b9dc8caad5a41a5545cba3810c3e828708b3d7404f6cef/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc", size = 2335816, upload-time = "2025-11-04T13:40:07.835Z" },
167
+
{ url = "https://files.pythonhosted.org/packages/0d/76/941cc9f73529988688a665a5c0ecff1112b3d95ab48f81db5f7606f522d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c", size = 2075366, upload-time = "2025-11-04T13:40:09.804Z" },
168
+
{ url = "https://files.pythonhosted.org/packages/d3/43/ebef01f69baa07a482844faaa0a591bad1ef129253ffd0cdaa9d8a7f72d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5", size = 2171698, upload-time = "2025-11-04T13:40:12.004Z" },
169
+
{ url = "https://files.pythonhosted.org/packages/b1/87/41f3202e4193e3bacfc2c065fab7706ebe81af46a83d3e27605029c1f5a6/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c", size = 2132603, upload-time = "2025-11-04T13:40:13.868Z" },
170
+
{ url = "https://files.pythonhosted.org/packages/49/7d/4c00df99cb12070b6bccdef4a195255e6020a550d572768d92cc54dba91a/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294", size = 2329591, upload-time = "2025-11-04T13:40:15.672Z" },
171
+
{ url = "https://files.pythonhosted.org/packages/cc/6a/ebf4b1d65d458f3cda6a7335d141305dfa19bdc61140a884d165a8a1bbc7/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1", size = 2319068, upload-time = "2025-11-04T13:40:17.532Z" },
172
+
{ url = "https://files.pythonhosted.org/packages/49/3b/774f2b5cd4192d5ab75870ce4381fd89cf218af999515baf07e7206753f0/pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d", size = 1985908, upload-time = "2025-11-04T13:40:19.309Z" },
173
+
{ url = "https://files.pythonhosted.org/packages/86/45/00173a033c801cacf67c190fef088789394feaf88a98a7035b0e40d53dc9/pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815", size = 2020145, upload-time = "2025-11-04T13:40:21.548Z" },
174
+
{ url = "https://files.pythonhosted.org/packages/f9/22/91fbc821fa6d261b376a3f73809f907cec5ca6025642c463d3488aad22fb/pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3", size = 1976179, upload-time = "2025-11-04T13:40:23.393Z" },
175
+
{ url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" },
176
+
{ url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" },
177
+
{ url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" },
178
+
{ url = "https://files.pythonhosted.org/packages/9a/e3/6324802931ae1d123528988e0e86587c2072ac2e5394b4bc2bc34b61ff6e/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", size = 2063258, upload-time = "2025-11-04T13:40:33.544Z" },
179
+
{ url = "https://files.pythonhosted.org/packages/c9/d4/2230d7151d4957dd79c3044ea26346c148c98fbf0ee6ebd41056f2d62ab5/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", size = 2214917, upload-time = "2025-11-04T13:40:35.479Z" },
180
+
{ url = "https://files.pythonhosted.org/packages/e6/9f/eaac5df17a3672fef0081b6c1bb0b82b33ee89aa5cec0d7b05f52fd4a1fa/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", size = 2332186, upload-time = "2025-11-04T13:40:37.436Z" },
181
+
{ url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164, upload-time = "2025-11-04T13:40:40.289Z" },
182
+
{ url = "https://files.pythonhosted.org/packages/bf/e3/f6e262673c6140dd3305d144d032f7bd5f7497d3871c1428521f19f9efa2/pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", size = 2179146, upload-time = "2025-11-04T13:40:42.809Z" },
183
+
{ url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788, upload-time = "2025-11-04T13:40:44.752Z" },
184
+
{ url = "https://files.pythonhosted.org/packages/3a/8d/34318ef985c45196e004bc46c6eab2eda437e744c124ef0dbe1ff2c9d06b/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", size = 2340133, upload-time = "2025-11-04T13:40:46.66Z" },
185
+
{ url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852, upload-time = "2025-11-04T13:40:48.575Z" },
186
+
{ url = "https://files.pythonhosted.org/packages/1a/d9/c248c103856f807ef70c18a4f986693a46a8ffe1602e5d361485da502d20/pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", size = 1994679, upload-time = "2025-11-04T13:40:50.619Z" },
187
+
{ url = "https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", size = 2019766, upload-time = "2025-11-04T13:40:52.631Z" },
188
+
{ url = "https://files.pythonhosted.org/packages/73/7d/f2f9db34af103bea3e09735bb40b021788a5e834c81eedb541991badf8f5/pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", size = 1981005, upload-time = "2025-11-04T13:40:54.734Z" },
189
+
{ url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622, upload-time = "2025-11-04T13:40:56.68Z" },
190
+
{ url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725, upload-time = "2025-11-04T13:40:58.807Z" },
191
+
{ url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040, upload-time = "2025-11-04T13:41:00.853Z" },
192
+
{ url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691, upload-time = "2025-11-04T13:41:03.504Z" },
193
+
{ url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897, upload-time = "2025-11-04T13:41:05.804Z" },
194
+
{ url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302, upload-time = "2025-11-04T13:41:07.809Z" },
195
+
{ url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877, upload-time = "2025-11-04T13:41:09.827Z" },
196
+
{ url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680, upload-time = "2025-11-04T13:41:12.379Z" },
197
+
{ url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960, upload-time = "2025-11-04T13:41:14.627Z" },
198
+
{ url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102, upload-time = "2025-11-04T13:41:16.868Z" },
199
+
{ url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039, upload-time = "2025-11-04T13:41:18.934Z" },
200
+
{ url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126, upload-time = "2025-11-04T13:41:21.418Z" },
201
+
{ url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489, upload-time = "2025-11-04T13:41:24.076Z" },
202
+
{ url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288, upload-time = "2025-11-04T13:41:26.33Z" },
203
+
{ url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255, upload-time = "2025-11-04T13:41:28.569Z" },
204
+
{ url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760, upload-time = "2025-11-04T13:41:31.055Z" },
205
+
{ url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092, upload-time = "2025-11-04T13:41:33.21Z" },
206
+
{ url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385, upload-time = "2025-11-04T13:41:35.508Z" },
207
+
{ url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832, upload-time = "2025-11-04T13:41:37.732Z" },
208
+
{ url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585, upload-time = "2025-11-04T13:41:40Z" },
209
+
{ url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078, upload-time = "2025-11-04T13:41:42.323Z" },
210
+
{ url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914, upload-time = "2025-11-04T13:41:45.221Z" },
211
+
{ url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560, upload-time = "2025-11-04T13:41:47.474Z" },
212
+
{ url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244, upload-time = "2025-11-04T13:41:49.992Z" },
213
+
{ url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955, upload-time = "2025-11-04T13:41:54.079Z" },
214
+
{ url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" },
215
+
{ url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" },
216
+
{ url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" },
217
+
{ url = "https://files.pythonhosted.org/packages/11/72/90fda5ee3b97e51c494938a4a44c3a35a9c96c19bba12372fb9c634d6f57/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034", size = 2115441, upload-time = "2025-11-04T13:42:39.557Z" },
218
+
{ url = "https://files.pythonhosted.org/packages/1f/53/8942f884fa33f50794f119012dc6a1a02ac43a56407adaac20463df8e98f/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c", size = 1930291, upload-time = "2025-11-04T13:42:42.169Z" },
219
+
{ url = "https://files.pythonhosted.org/packages/79/c8/ecb9ed9cd942bce09fc888ee960b52654fbdbede4ba6c2d6e0d3b1d8b49c/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2", size = 1948632, upload-time = "2025-11-04T13:42:44.564Z" },
220
+
{ url = "https://files.pythonhosted.org/packages/2e/1b/687711069de7efa6af934e74f601e2a4307365e8fdc404703afc453eab26/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad", size = 2138905, upload-time = "2025-11-04T13:42:47.156Z" },
221
+
{ url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495, upload-time = "2025-11-04T13:42:49.689Z" },
222
+
{ url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388, upload-time = "2025-11-04T13:42:52.215Z" },
223
+
{ url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879, upload-time = "2025-11-04T13:42:56.483Z" },
224
+
{ url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" },
225
+
{ url = "https://files.pythonhosted.org/packages/e6/b0/1a2aa41e3b5a4ba11420aba2d091b2d17959c8d1519ece3627c371951e73/pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b5819cd790dbf0c5eb9f82c73c16b39a65dd6dd4d1439dcdea7816ec9adddab8", size = 2103351, upload-time = "2025-11-04T13:43:02.058Z" },
226
+
{ url = "https://files.pythonhosted.org/packages/a4/ee/31b1f0020baaf6d091c87900ae05c6aeae101fa4e188e1613c80e4f1ea31/pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5a4e67afbc95fa5c34cf27d9089bca7fcab4e51e57278d710320a70b956d1b9a", size = 1925363, upload-time = "2025-11-04T13:43:05.159Z" },
227
+
{ url = "https://files.pythonhosted.org/packages/e1/89/ab8e86208467e467a80deaca4e434adac37b10a9d134cd2f99b28a01e483/pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ece5c59f0ce7d001e017643d8d24da587ea1f74f6993467d85ae8a5ef9d4f42b", size = 2135615, upload-time = "2025-11-04T13:43:08.116Z" },
228
+
{ url = "https://files.pythonhosted.org/packages/99/0a/99a53d06dd0348b2008f2f30884b34719c323f16c3be4e6cc1203b74a91d/pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:16f80f7abe3351f8ea6858914ddc8c77e02578544a0ebc15b4c2e1a0e813b0b2", size = 2175369, upload-time = "2025-11-04T13:43:12.49Z" },
229
+
{ url = "https://files.pythonhosted.org/packages/6d/94/30ca3b73c6d485b9bb0bc66e611cff4a7138ff9736b7e66bcf0852151636/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:33cb885e759a705b426baada1fe68cbb0a2e68e34c5d0d0289a364cf01709093", size = 2144218, upload-time = "2025-11-04T13:43:15.431Z" },
230
+
{ url = "https://files.pythonhosted.org/packages/87/57/31b4f8e12680b739a91f472b5671294236b82586889ef764b5fbc6669238/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:c8d8b4eb992936023be7dee581270af5c6e0697a8559895f527f5b7105ecd36a", size = 2329951, upload-time = "2025-11-04T13:43:18.062Z" },
231
+
{ url = "https://files.pythonhosted.org/packages/7d/73/3c2c8edef77b8f7310e6fb012dbc4b8551386ed575b9eb6fb2506e28a7eb/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:242a206cd0318f95cd21bdacff3fcc3aab23e79bba5cac3db5a841c9ef9c6963", size = 2318428, upload-time = "2025-11-04T13:43:20.679Z" },
232
+
{ url = "https://files.pythonhosted.org/packages/2f/02/8559b1f26ee0d502c74f9cca5c0d2fd97e967e083e006bbbb4e97f3a043a/pydantic_core-2.41.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a", size = 2147009, upload-time = "2025-11-04T13:43:23.286Z" },
233
+
{ url = "https://files.pythonhosted.org/packages/5f/9b/1b3f0e9f9305839d7e84912f9e8bfbd191ed1b1ef48083609f0dabde978c/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26", size = 2101980, upload-time = "2025-11-04T13:43:25.97Z" },
234
+
{ url = "https://files.pythonhosted.org/packages/a4/ed/d71fefcb4263df0da6a85b5d8a7508360f2f2e9b3bf5814be9c8bccdccc1/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808", size = 1923865, upload-time = "2025-11-04T13:43:28.763Z" },
235
+
{ url = "https://files.pythonhosted.org/packages/ce/3a/626b38db460d675f873e4444b4bb030453bbe7b4ba55df821d026a0493c4/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc", size = 2134256, upload-time = "2025-11-04T13:43:31.71Z" },
236
+
{ url = "https://files.pythonhosted.org/packages/83/d9/8412d7f06f616bbc053d30cb4e5f76786af3221462ad5eee1f202021eb4e/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1", size = 2174762, upload-time = "2025-11-04T13:43:34.744Z" },
237
+
{ url = "https://files.pythonhosted.org/packages/55/4c/162d906b8e3ba3a99354e20faa1b49a85206c47de97a639510a0e673f5da/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84", size = 2143141, upload-time = "2025-11-04T13:43:37.701Z" },
238
+
{ url = "https://files.pythonhosted.org/packages/1f/f2/f11dd73284122713f5f89fc940f370d035fa8e1e078d446b3313955157fe/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770", size = 2330317, upload-time = "2025-11-04T13:43:40.406Z" },
239
+
{ url = "https://files.pythonhosted.org/packages/88/9d/b06ca6acfe4abb296110fb1273a4d848a0bfb2ff65f3ee92127b3244e16b/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f", size = 2316992, upload-time = "2025-11-04T13:43:43.602Z" },
240
+
{ url = "https://files.pythonhosted.org/packages/36/c7/cfc8e811f061c841d7990b0201912c3556bfeb99cdcb7ed24adc8d6f8704/pydantic_core-2.41.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51", size = 2145302, upload-time = "2025-11-04T13:43:46.64Z" },
241
+
]
242
+
243
+
[[package]]
244
+
name = "pygments"
245
+
version = "2.19.2"
246
+
source = { registry = "https://pypi.org/simple" }
247
+
sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" }
248
+
wheels = [
249
+
{ url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" },
250
+
]
251
+
252
+
[[package]]
253
+
name = "pytest"
254
+
version = "9.0.2"
255
+
source = { registry = "https://pypi.org/simple" }
256
+
dependencies = [
257
+
{ name = "colorama", marker = "sys_platform == 'win32'" },
258
+
{ name = "exceptiongroup", marker = "python_full_version < '3.11'" },
259
+
{ name = "iniconfig" },
260
+
{ name = "packaging" },
261
+
{ name = "pluggy" },
262
+
{ name = "pygments" },
263
+
{ name = "tomli", marker = "python_full_version < '3.11'" },
264
+
]
265
+
sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" }
266
+
wheels = [
267
+
{ url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" },
268
+
]
269
+
270
+
[[package]]
271
+
name = "pytest-sugar"
272
+
version = "1.1.1"
273
+
source = { registry = "https://pypi.org/simple" }
274
+
dependencies = [
275
+
{ name = "pytest" },
276
+
{ name = "termcolor" },
277
+
]
278
+
sdist = { url = "https://files.pythonhosted.org/packages/0b/4e/60fed105549297ba1a700e1ea7b828044842ea27d72c898990510b79b0e2/pytest-sugar-1.1.1.tar.gz", hash = "sha256:73b8b65163ebf10f9f671efab9eed3d56f20d2ca68bda83fa64740a92c08f65d", size = 16533, upload-time = "2025-08-23T12:19:35.737Z" }
279
+
wheels = [
280
+
{ url = "https://files.pythonhosted.org/packages/87/d5/81d38a91c1fdafb6711f053f5a9b92ff788013b19821257c2c38c1e132df/pytest_sugar-1.1.1-py3-none-any.whl", hash = "sha256:2f8319b907548d5b9d03a171515c1d43d2e38e32bd8182a1781eb20b43344cc8", size = 11440, upload-time = "2025-08-23T12:19:34.894Z" },
281
+
]
282
+
283
+
[[package]]
284
+
name = "ruff"
285
+
version = "0.14.8"
286
+
source = { registry = "https://pypi.org/simple" }
287
+
sdist = { url = "https://files.pythonhosted.org/packages/ed/d9/f7a0c4b3a2bf2556cd5d99b05372c29980249ef71e8e32669ba77428c82c/ruff-0.14.8.tar.gz", hash = "sha256:774ed0dd87d6ce925e3b8496feb3a00ac564bea52b9feb551ecd17e0a23d1eed", size = 5765385, upload-time = "2025-12-04T15:06:17.669Z" }
288
+
wheels = [
289
+
{ url = "https://files.pythonhosted.org/packages/48/b8/9537b52010134b1d2b72870cc3f92d5fb759394094741b09ceccae183fbe/ruff-0.14.8-py3-none-linux_armv6l.whl", hash = "sha256:ec071e9c82eca417f6111fd39f7043acb53cd3fde9b1f95bbed745962e345afb", size = 13441540, upload-time = "2025-12-04T15:06:14.896Z" },
290
+
{ url = "https://files.pythonhosted.org/packages/24/00/99031684efb025829713682012b6dd37279b1f695ed1b01725f85fd94b38/ruff-0.14.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:8cdb162a7159f4ca36ce980a18c43d8f036966e7f73f866ac8f493b75e0c27e9", size = 13669384, upload-time = "2025-12-04T15:06:51.809Z" },
291
+
{ url = "https://files.pythonhosted.org/packages/72/64/3eb5949169fc19c50c04f28ece2c189d3b6edd57e5b533649dae6ca484fe/ruff-0.14.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:2e2fcbefe91f9fad0916850edf0854530c15bd1926b6b779de47e9ab619ea38f", size = 12806917, upload-time = "2025-12-04T15:06:08.925Z" },
292
+
{ url = "https://files.pythonhosted.org/packages/c4/08/5250babb0b1b11910f470370ec0cbc67470231f7cdc033cee57d4976f941/ruff-0.14.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9d70721066a296f45786ec31916dc287b44040f553da21564de0ab4d45a869b", size = 13256112, upload-time = "2025-12-04T15:06:23.498Z" },
293
+
{ url = "https://files.pythonhosted.org/packages/78/4c/6c588e97a8e8c2d4b522c31a579e1df2b4d003eddfbe23d1f262b1a431ff/ruff-0.14.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2c87e09b3cd9d126fc67a9ecd3b5b1d3ded2b9c7fce3f16e315346b9d05cfb52", size = 13227559, upload-time = "2025-12-04T15:06:33.432Z" },
294
+
{ url = "https://files.pythonhosted.org/packages/23/ce/5f78cea13eda8eceac71b5f6fa6e9223df9b87bb2c1891c166d1f0dce9f1/ruff-0.14.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d62cb310c4fbcb9ee4ac023fe17f984ae1e12b8a4a02e3d21489f9a2a5f730c", size = 13896379, upload-time = "2025-12-04T15:06:02.687Z" },
295
+
{ url = "https://files.pythonhosted.org/packages/cf/79/13de4517c4dadce9218a20035b21212a4c180e009507731f0d3b3f5df85a/ruff-0.14.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:1af35c2d62633d4da0521178e8a2641c636d2a7153da0bac1b30cfd4ccd91344", size = 15372786, upload-time = "2025-12-04T15:06:29.828Z" },
296
+
{ url = "https://files.pythonhosted.org/packages/00/06/33df72b3bb42be8a1c3815fd4fae83fa2945fc725a25d87ba3e42d1cc108/ruff-0.14.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:25add4575ffecc53d60eed3f24b1e934493631b48ebbc6ebaf9d8517924aca4b", size = 14990029, upload-time = "2025-12-04T15:06:36.812Z" },
297
+
{ url = "https://files.pythonhosted.org/packages/64/61/0f34927bd90925880394de0e081ce1afab66d7b3525336f5771dcf0cb46c/ruff-0.14.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4c943d847b7f02f7db4201a0600ea7d244d8a404fbb639b439e987edcf2baf9a", size = 14407037, upload-time = "2025-12-04T15:06:39.979Z" },
298
+
{ url = "https://files.pythonhosted.org/packages/96/bc/058fe0aefc0fbf0d19614cb6d1a3e2c048f7dc77ca64957f33b12cfdc5ef/ruff-0.14.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb6e8bf7b4f627548daa1b69283dac5a296bfe9ce856703b03130732e20ddfe2", size = 14102390, upload-time = "2025-12-04T15:06:46.372Z" },
299
+
{ url = "https://files.pythonhosted.org/packages/af/a4/e4f77b02b804546f4c17e8b37a524c27012dd6ff05855d2243b49a7d3cb9/ruff-0.14.8-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:7aaf2974f378e6b01d1e257c6948207aec6a9b5ba53fab23d0182efb887a0e4a", size = 14230793, upload-time = "2025-12-04T15:06:20.497Z" },
300
+
{ url = "https://files.pythonhosted.org/packages/3f/52/bb8c02373f79552e8d087cedaffad76b8892033d2876c2498a2582f09dcf/ruff-0.14.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e5758ca513c43ad8a4ef13f0f081f80f08008f410790f3611a21a92421ab045b", size = 13160039, upload-time = "2025-12-04T15:06:49.06Z" },
301
+
{ url = "https://files.pythonhosted.org/packages/1f/ad/b69d6962e477842e25c0b11622548df746290cc6d76f9e0f4ed7456c2c31/ruff-0.14.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:f74f7ba163b6e85a8d81a590363bf71618847e5078d90827749bfda1d88c9cdf", size = 13205158, upload-time = "2025-12-04T15:06:54.574Z" },
302
+
{ url = "https://files.pythonhosted.org/packages/06/63/54f23da1315c0b3dfc1bc03fbc34e10378918a20c0b0f086418734e57e74/ruff-0.14.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:eed28f6fafcc9591994c42254f5a5c5ca40e69a30721d2ab18bb0bb3baac3ab6", size = 13469550, upload-time = "2025-12-04T15:05:59.209Z" },
303
+
{ url = "https://files.pythonhosted.org/packages/70/7d/a4d7b1961e4903bc37fffb7ddcfaa7beb250f67d97cfd1ee1d5cddb1ec90/ruff-0.14.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:21d48fa744c9d1cb8d71eb0a740c4dd02751a5de9db9a730a8ef75ca34cf138e", size = 14211332, upload-time = "2025-12-04T15:06:06.027Z" },
304
+
{ url = "https://files.pythonhosted.org/packages/5d/93/2a5063341fa17054e5c86582136e9895db773e3c2ffb770dde50a09f35f0/ruff-0.14.8-py3-none-win32.whl", hash = "sha256:15f04cb45c051159baebb0f0037f404f1dc2f15a927418f29730f411a79bc4e7", size = 13151890, upload-time = "2025-12-04T15:06:11.668Z" },
305
+
{ url = "https://files.pythonhosted.org/packages/02/1c/65c61a0859c0add13a3e1cbb6024b42de587456a43006ca2d4fd3d1618fe/ruff-0.14.8-py3-none-win_amd64.whl", hash = "sha256:9eeb0b24242b5bbff3011409a739929f497f3fb5fe3b5698aba5e77e8c833097", size = 14537826, upload-time = "2025-12-04T15:06:26.409Z" },
306
+
{ url = "https://files.pythonhosted.org/packages/6d/63/8b41cea3afd7f58eb64ac9251668ee0073789a3bc9ac6f816c8c6fef986d/ruff-0.14.8-py3-none-win_arm64.whl", hash = "sha256:965a582c93c63fe715fd3e3f8aa37c4b776777203d8e1d8aa3cc0c14424a4b99", size = 13634522, upload-time = "2025-12-04T15:06:43.212Z" },
307
+
]
308
+
309
+
[[package]]
310
+
name = "termcolor"
311
+
version = "3.2.0"
312
+
source = { registry = "https://pypi.org/simple" }
313
+
sdist = { url = "https://files.pythonhosted.org/packages/87/56/ab275c2b56a5e2342568838f0d5e3e66a32354adcc159b495e374cda43f5/termcolor-3.2.0.tar.gz", hash = "sha256:610e6456feec42c4bcd28934a8c87a06c3fa28b01561d46aa09a9881b8622c58", size = 14423, upload-time = "2025-10-25T19:11:42.586Z" }
314
+
wheels = [
315
+
{ url = "https://files.pythonhosted.org/packages/f9/d5/141f53d7c1eb2a80e6d3e9a390228c3222c27705cbe7f048d3623053f3ca/termcolor-3.2.0-py3-none-any.whl", hash = "sha256:a10343879eba4da819353c55cb8049b0933890c2ebf9ad5d3ecd2bb32ea96ea6", size = 7698, upload-time = "2025-10-25T19:11:41.536Z" },
316
+
]
317
+
318
+
[[package]]
319
+
name = "tomli"
320
+
version = "2.3.0"
321
+
source = { registry = "https://pypi.org/simple" }
322
+
sdist = { url = "https://files.pythonhosted.org/packages/52/ed/3f73f72945444548f33eba9a87fc7a6e969915e7b1acc8260b30e1f76a2f/tomli-2.3.0.tar.gz", hash = "sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549", size = 17392, upload-time = "2025-10-08T22:01:47.119Z" }
323
+
wheels = [
324
+
{ url = "https://files.pythonhosted.org/packages/b3/2e/299f62b401438d5fe1624119c723f5d877acc86a4c2492da405626665f12/tomli-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:88bd15eb972f3664f5ed4b57c1634a97153b4bac4479dcb6a495f41921eb7f45", size = 153236, upload-time = "2025-10-08T22:01:00.137Z" },
325
+
{ url = "https://files.pythonhosted.org/packages/86/7f/d8fffe6a7aefdb61bced88fcb5e280cfd71e08939da5894161bd71bea022/tomli-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:883b1c0d6398a6a9d29b508c331fa56adbcdff647f6ace4dfca0f50e90dfd0ba", size = 148084, upload-time = "2025-10-08T22:01:01.63Z" },
326
+
{ url = "https://files.pythonhosted.org/packages/47/5c/24935fb6a2ee63e86d80e4d3b58b222dafaf438c416752c8b58537c8b89a/tomli-2.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1381caf13ab9f300e30dd8feadb3de072aeb86f1d34a8569453ff32a7dea4bf", size = 234832, upload-time = "2025-10-08T22:01:02.543Z" },
327
+
{ url = "https://files.pythonhosted.org/packages/89/da/75dfd804fc11e6612846758a23f13271b76d577e299592b4371a4ca4cd09/tomli-2.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0e285d2649b78c0d9027570d4da3425bdb49830a6156121360b3f8511ea3441", size = 242052, upload-time = "2025-10-08T22:01:03.836Z" },
328
+
{ url = "https://files.pythonhosted.org/packages/70/8c/f48ac899f7b3ca7eb13af73bacbc93aec37f9c954df3c08ad96991c8c373/tomli-2.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a154a9ae14bfcf5d8917a59b51ffd5a3ac1fd149b71b47a3a104ca4edcfa845", size = 239555, upload-time = "2025-10-08T22:01:04.834Z" },
329
+
{ url = "https://files.pythonhosted.org/packages/ba/28/72f8afd73f1d0e7829bfc093f4cb98ce0a40ffc0cc997009ee1ed94ba705/tomli-2.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:74bf8464ff93e413514fefd2be591c3b0b23231a77f901db1eb30d6f712fc42c", size = 245128, upload-time = "2025-10-08T22:01:05.84Z" },
330
+
{ url = "https://files.pythonhosted.org/packages/b6/eb/a7679c8ac85208706d27436e8d421dfa39d4c914dcf5fa8083a9305f58d9/tomli-2.3.0-cp311-cp311-win32.whl", hash = "sha256:00b5f5d95bbfc7d12f91ad8c593a1659b6387b43f054104cda404be6bda62456", size = 96445, upload-time = "2025-10-08T22:01:06.896Z" },
331
+
{ url = "https://files.pythonhosted.org/packages/0a/fe/3d3420c4cb1ad9cb462fb52967080575f15898da97e21cb6f1361d505383/tomli-2.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:4dc4ce8483a5d429ab602f111a93a6ab1ed425eae3122032db7e9acf449451be", size = 107165, upload-time = "2025-10-08T22:01:08.107Z" },
332
+
{ url = "https://files.pythonhosted.org/packages/ff/b7/40f36368fcabc518bb11c8f06379a0fd631985046c038aca08c6d6a43c6e/tomli-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d7d86942e56ded512a594786a5ba0a5e521d02529b3826e7761a05138341a2ac", size = 154891, upload-time = "2025-10-08T22:01:09.082Z" },
333
+
{ url = "https://files.pythonhosted.org/packages/f9/3f/d9dd692199e3b3aab2e4e4dd948abd0f790d9ded8cd10cbaae276a898434/tomli-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:73ee0b47d4dad1c5e996e3cd33b8a76a50167ae5f96a2607cbe8cc773506ab22", size = 148796, upload-time = "2025-10-08T22:01:10.266Z" },
334
+
{ url = "https://files.pythonhosted.org/packages/60/83/59bff4996c2cf9f9387a0f5a3394629c7efa5ef16142076a23a90f1955fa/tomli-2.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:792262b94d5d0a466afb5bc63c7daa9d75520110971ee269152083270998316f", size = 242121, upload-time = "2025-10-08T22:01:11.332Z" },
335
+
{ url = "https://files.pythonhosted.org/packages/45/e5/7c5119ff39de8693d6baab6c0b6dcb556d192c165596e9fc231ea1052041/tomli-2.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f195fe57ecceac95a66a75ac24d9d5fbc98ef0962e09b2eddec5d39375aae52", size = 250070, upload-time = "2025-10-08T22:01:12.498Z" },
336
+
{ url = "https://files.pythonhosted.org/packages/45/12/ad5126d3a278f27e6701abde51d342aa78d06e27ce2bb596a01f7709a5a2/tomli-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e31d432427dcbf4d86958c184b9bfd1e96b5b71f8eb17e6d02531f434fd335b8", size = 245859, upload-time = "2025-10-08T22:01:13.551Z" },
337
+
{ url = "https://files.pythonhosted.org/packages/fb/a1/4d6865da6a71c603cfe6ad0e6556c73c76548557a8d658f9e3b142df245f/tomli-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b0882799624980785240ab732537fcfc372601015c00f7fc367c55308c186f6", size = 250296, upload-time = "2025-10-08T22:01:14.614Z" },
338
+
{ url = "https://files.pythonhosted.org/packages/a0/b7/a7a7042715d55c9ba6e8b196d65d2cb662578b4d8cd17d882d45322b0d78/tomli-2.3.0-cp312-cp312-win32.whl", hash = "sha256:ff72b71b5d10d22ecb084d345fc26f42b5143c5533db5e2eaba7d2d335358876", size = 97124, upload-time = "2025-10-08T22:01:15.629Z" },
339
+
{ url = "https://files.pythonhosted.org/packages/06/1e/f22f100db15a68b520664eb3328fb0ae4e90530887928558112c8d1f4515/tomli-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:1cb4ed918939151a03f33d4242ccd0aa5f11b3547d0cf30f7c74a408a5b99878", size = 107698, upload-time = "2025-10-08T22:01:16.51Z" },
340
+
{ url = "https://files.pythonhosted.org/packages/89/48/06ee6eabe4fdd9ecd48bf488f4ac783844fd777f547b8d1b61c11939974e/tomli-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5192f562738228945d7b13d4930baffda67b69425a7f0da96d360b0a3888136b", size = 154819, upload-time = "2025-10-08T22:01:17.964Z" },
341
+
{ url = "https://files.pythonhosted.org/packages/f1/01/88793757d54d8937015c75dcdfb673c65471945f6be98e6a0410fba167ed/tomli-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:be71c93a63d738597996be9528f4abe628d1adf5e6eb11607bc8fe1a510b5dae", size = 148766, upload-time = "2025-10-08T22:01:18.959Z" },
342
+
{ url = "https://files.pythonhosted.org/packages/42/17/5e2c956f0144b812e7e107f94f1cc54af734eb17b5191c0bbfb72de5e93e/tomli-2.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4665508bcbac83a31ff8ab08f424b665200c0e1e645d2bd9ab3d3e557b6185b", size = 240771, upload-time = "2025-10-08T22:01:20.106Z" },
343
+
{ url = "https://files.pythonhosted.org/packages/d5/f4/0fbd014909748706c01d16824eadb0307115f9562a15cbb012cd9b3512c5/tomli-2.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4021923f97266babc6ccab9f5068642a0095faa0a51a246a6a02fccbb3514eaf", size = 248586, upload-time = "2025-10-08T22:01:21.164Z" },
344
+
{ url = "https://files.pythonhosted.org/packages/30/77/fed85e114bde5e81ecf9bc5da0cc69f2914b38f4708c80ae67d0c10180c5/tomli-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4ea38c40145a357d513bffad0ed869f13c1773716cf71ccaa83b0fa0cc4e42f", size = 244792, upload-time = "2025-10-08T22:01:22.417Z" },
345
+
{ url = "https://files.pythonhosted.org/packages/55/92/afed3d497f7c186dc71e6ee6d4fcb0acfa5f7d0a1a2878f8beae379ae0cc/tomli-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ad805ea85eda330dbad64c7ea7a4556259665bdf9d2672f5dccc740eb9d3ca05", size = 248909, upload-time = "2025-10-08T22:01:23.859Z" },
346
+
{ url = "https://files.pythonhosted.org/packages/f8/84/ef50c51b5a9472e7265ce1ffc7f24cd4023d289e109f669bdb1553f6a7c2/tomli-2.3.0-cp313-cp313-win32.whl", hash = "sha256:97d5eec30149fd3294270e889b4234023f2c69747e555a27bd708828353ab606", size = 96946, upload-time = "2025-10-08T22:01:24.893Z" },
347
+
{ url = "https://files.pythonhosted.org/packages/b2/b7/718cd1da0884f281f95ccfa3a6cc572d30053cba64603f79d431d3c9b61b/tomli-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0c95ca56fbe89e065c6ead5b593ee64b84a26fca063b5d71a1122bf26e533999", size = 107705, upload-time = "2025-10-08T22:01:26.153Z" },
348
+
{ url = "https://files.pythonhosted.org/packages/19/94/aeafa14a52e16163008060506fcb6aa1949d13548d13752171a755c65611/tomli-2.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:cebc6fe843e0733ee827a282aca4999b596241195f43b4cc371d64fc6639da9e", size = 154244, upload-time = "2025-10-08T22:01:27.06Z" },
349
+
{ url = "https://files.pythonhosted.org/packages/db/e4/1e58409aa78eefa47ccd19779fc6f36787edbe7d4cd330eeeedb33a4515b/tomli-2.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4c2ef0244c75aba9355561272009d934953817c49f47d768070c3c94355c2aa3", size = 148637, upload-time = "2025-10-08T22:01:28.059Z" },
350
+
{ url = "https://files.pythonhosted.org/packages/26/b6/d1eccb62f665e44359226811064596dd6a366ea1f985839c566cd61525ae/tomli-2.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c22a8bf253bacc0cf11f35ad9808b6cb75ada2631c2d97c971122583b129afbc", size = 241925, upload-time = "2025-10-08T22:01:29.066Z" },
351
+
{ url = "https://files.pythonhosted.org/packages/70/91/7cdab9a03e6d3d2bb11beae108da5bdc1c34bdeb06e21163482544ddcc90/tomli-2.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0eea8cc5c5e9f89c9b90c4896a8deefc74f518db5927d0e0e8d4a80953d774d0", size = 249045, upload-time = "2025-10-08T22:01:31.98Z" },
352
+
{ url = "https://files.pythonhosted.org/packages/15/1b/8c26874ed1f6e4f1fcfeb868db8a794cbe9f227299402db58cfcc858766c/tomli-2.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b74a0e59ec5d15127acdabd75ea17726ac4c5178ae51b85bfe39c4f8a278e879", size = 245835, upload-time = "2025-10-08T22:01:32.989Z" },
353
+
{ url = "https://files.pythonhosted.org/packages/fd/42/8e3c6a9a4b1a1360c1a2a39f0b972cef2cc9ebd56025168c4137192a9321/tomli-2.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b5870b50c9db823c595983571d1296a6ff3e1b88f734a4c8f6fc6188397de005", size = 253109, upload-time = "2025-10-08T22:01:34.052Z" },
354
+
{ url = "https://files.pythonhosted.org/packages/22/0c/b4da635000a71b5f80130937eeac12e686eefb376b8dee113b4a582bba42/tomli-2.3.0-cp314-cp314-win32.whl", hash = "sha256:feb0dacc61170ed7ab602d3d972a58f14ee3ee60494292d384649a3dc38ef463", size = 97930, upload-time = "2025-10-08T22:01:35.082Z" },
355
+
{ url = "https://files.pythonhosted.org/packages/b9/74/cb1abc870a418ae99cd5c9547d6bce30701a954e0e721821df483ef7223c/tomli-2.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:b273fcbd7fc64dc3600c098e39136522650c49bca95df2d11cf3b626422392c8", size = 107964, upload-time = "2025-10-08T22:01:36.057Z" },
356
+
{ url = "https://files.pythonhosted.org/packages/54/78/5c46fff6432a712af9f792944f4fcd7067d8823157949f4e40c56b8b3c83/tomli-2.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:940d56ee0410fa17ee1f12b817b37a4d4e4dc4d27340863cc67236c74f582e77", size = 163065, upload-time = "2025-10-08T22:01:37.27Z" },
357
+
{ url = "https://files.pythonhosted.org/packages/39/67/f85d9bd23182f45eca8939cd2bc7050e1f90c41f4a2ecbbd5963a1d1c486/tomli-2.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f85209946d1fe94416debbb88d00eb92ce9cd5266775424ff81bc959e001acaf", size = 159088, upload-time = "2025-10-08T22:01:38.235Z" },
358
+
{ url = "https://files.pythonhosted.org/packages/26/5a/4b546a0405b9cc0659b399f12b6adb750757baf04250b148d3c5059fc4eb/tomli-2.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a56212bdcce682e56b0aaf79e869ba5d15a6163f88d5451cbde388d48b13f530", size = 268193, upload-time = "2025-10-08T22:01:39.712Z" },
359
+
{ url = "https://files.pythonhosted.org/packages/42/4f/2c12a72ae22cf7b59a7fe75b3465b7aba40ea9145d026ba41cb382075b0e/tomli-2.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c5f3ffd1e098dfc032d4d3af5c0ac64f6d286d98bc148698356847b80fa4de1b", size = 275488, upload-time = "2025-10-08T22:01:40.773Z" },
360
+
{ url = "https://files.pythonhosted.org/packages/92/04/a038d65dbe160c3aa5a624e93ad98111090f6804027d474ba9c37c8ae186/tomli-2.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e01decd096b1530d97d5d85cb4dff4af2d8347bd35686654a004f8dea20fc67", size = 272669, upload-time = "2025-10-08T22:01:41.824Z" },
361
+
{ url = "https://files.pythonhosted.org/packages/be/2f/8b7c60a9d1612a7cbc39ffcca4f21a73bf368a80fc25bccf8253e2563267/tomli-2.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8a35dd0e643bb2610f156cca8db95d213a90015c11fee76c946aa62b7ae7e02f", size = 279709, upload-time = "2025-10-08T22:01:43.177Z" },
362
+
{ url = "https://files.pythonhosted.org/packages/7e/46/cc36c679f09f27ded940281c38607716c86cf8ba4a518d524e349c8b4874/tomli-2.3.0-cp314-cp314t-win32.whl", hash = "sha256:a1f7f282fe248311650081faafa5f4732bdbfef5d45fe3f2e702fbc6f2d496e0", size = 107563, upload-time = "2025-10-08T22:01:44.233Z" },
363
+
{ url = "https://files.pythonhosted.org/packages/84/ff/426ca8683cf7b753614480484f6437f568fd2fda2edbdf57a2d3d8b27a0b/tomli-2.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:70a251f8d4ba2d9ac2542eecf008b3c8a9fc5c3f9f02c56a9d7952612be2fdba", size = 119756, upload-time = "2025-10-08T22:01:45.234Z" },
364
+
{ url = "https://files.pythonhosted.org/packages/77/b8/0135fadc89e73be292b473cb820b4f5a08197779206b33191e801feeae40/tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b", size = 14408, upload-time = "2025-10-08T22:01:46.04Z" },
365
+
]
366
+
367
+
[[package]]
368
+
name = "typing-extensions"
369
+
version = "4.15.0"
370
+
source = { registry = "https://pypi.org/simple" }
371
+
sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" }
372
+
wheels = [
373
+
{ url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" },
374
+
]
375
+
376
+
[[package]]
377
+
name = "typing-inspection"
378
+
version = "0.4.2"
379
+
source = { registry = "https://pypi.org/simple" }
380
+
dependencies = [
381
+
{ name = "typing-extensions" },
382
+
]
383
+
sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" }
384
+
wheels = [
385
+
{ url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" },
386
+
]