rockbox-ffi 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,5 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.egg-info/
4
+ dist/
5
+ build/
@@ -0,0 +1,93 @@
1
+ Metadata-Version: 2.4
2
+ Name: rockbox-ffi
3
+ Version: 0.1.0
4
+ Summary: Python bindings for the Rockbox DSP / metadata / playback engine
5
+ License: GPL-2.0-or-later
6
+ Requires-Python: >=3.9
7
+ Requires-Dist: cffi>=1.16
8
+ Description-Content-Type: text/markdown
9
+
10
+ # rockbox-ffi (Python)
11
+
12
+ [![PyPI](https://img.shields.io/pypi/v/rockbox-ffi?logo=pypi&logoColor=white)](https://pypi.org/project/rockbox-ffi/)
13
+ ![Python](https://img.shields.io/badge/python-3.9%2B-3776AB?logo=python&logoColor=white)
14
+ ![FFI](https://img.shields.io/badge/FFI-cffi-306998)
15
+ ![uv](https://img.shields.io/badge/packaging-uv-DE5FE9?logo=uv&logoColor=white)
16
+ ![License](https://img.shields.io/badge/license-GPL--2.0--or--later-blue)
17
+
18
+ Python bindings for the Rockbox **DSP**, **metadata**, and **playback**
19
+ engine, via `cffi` (ABI mode) over the prebuilt `librockbox_ffi` shared
20
+ library.
21
+
22
+ ## Setup
23
+
24
+ Build the shared library once (from the repo root):
25
+
26
+ ```sh
27
+ cargo build --release -p rockbox-ffi
28
+ ```
29
+
30
+ Then install the Python package with [uv](https://docs.astral.sh/uv/):
31
+
32
+ ```sh
33
+ cd bindings/python
34
+ uv venv
35
+ uv pip install -e .
36
+ uv run python examples/smoke.py
37
+ ```
38
+
39
+ The library is located automatically by walking up to
40
+ `target/release/librockbox_ffi.{dylib,so}`. Override with the
41
+ `ROCKBOX_FFI_LIB` environment variable.
42
+
43
+ ## Usage
44
+
45
+ ```python
46
+ import rockbox_ffi as rb
47
+ from rockbox_ffi import Dsp, Player, metadata
48
+ from rockbox_ffi.enums import DspReplayGainMode, ReplayGainMode, CrossfadeMode
49
+
50
+ # --- metadata ---------------------------------------------------------
51
+ meta = metadata.read("song.flac")
52
+ print(meta["artist"], "—", meta["title"], meta["duration_ms"], "ms")
53
+ print(metadata.probe("track.opus")) # -> "Opus"
54
+
55
+ # --- DSP (interleaved stereo int16) -----------------------------------
56
+ with Dsp(44100) as dsp:
57
+ dsp.eq_enable(True)
58
+ dsp.set_eq_band(0, cutoff_hz=60, q=0.7, gain_db=3.0)
59
+ dsp.set_replaygain(DspReplayGainMode.TRACK, noclip=True, preamp_db=0.0)
60
+ dsp.set_replaygain_gains(track_gain_db=-6.02) # halves amplitude
61
+ processed = dsp.process(samples) # array('h')
62
+
63
+ # --- playback (needs an output device) --------------------------------
64
+ with Player(volume=0.8) as player:
65
+ player.set_replaygain(ReplayGainMode.TRACK, preamp_db=0.0, prevent_clipping=True)
66
+ player.set_crossfade(CrossfadeMode.ALWAYS)
67
+ player.set_queue(["a.flac", "b.mp3", "c.opus"])
68
+ player.play()
69
+ print(player.status()) # {'state': 'playing', 'index': 0, ...}
70
+ ```
71
+
72
+ ## API
73
+
74
+ | Module | Contents |
75
+ | ---------------------- | --------------------------------------------------------------------- |
76
+ | `rockbox_ffi.metadata` | `read(path) -> dict`, `probe(filename) -> str \| None` |
77
+ | `rockbox_ffi.Dsp` | EQ / tone / surround / compressor / ReplayGain, `process(samples)` |
78
+ | `rockbox_ffi.Player` | queue + transport + crossfade + ReplayGain, `status() -> dict` |
79
+ | `rockbox_ffi.enums` | `DspReplayGainMode`, `ReplayGainMode`, `CrossfadeMode`, `MixMode`, … |
80
+
81
+ ### Two ReplayGain encodings
82
+
83
+ The DSP and player use *different* mode integers (a quirk of the C ABI):
84
+
85
+ - `Dsp.set_replaygain` → `DspReplayGainMode` (`TRACK=0, ALBUM=1, SHUFFLE=2, OFF=3`)
86
+ - `Player.set_replaygain` → `ReplayGainMode` (`OFF=0, TRACK=1, ALBUM=2`)
87
+
88
+ Use the named enums and you won't have to remember which is which.
89
+
90
+ ## Memory
91
+
92
+ All heap allocations crossing the FFI boundary (JSON strings, sample
93
+ buffers) are freed inside the wrappers — you never call a `*_free` yourself.
@@ -0,0 +1,84 @@
1
+ # rockbox-ffi (Python)
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/rockbox-ffi?logo=pypi&logoColor=white)](https://pypi.org/project/rockbox-ffi/)
4
+ ![Python](https://img.shields.io/badge/python-3.9%2B-3776AB?logo=python&logoColor=white)
5
+ ![FFI](https://img.shields.io/badge/FFI-cffi-306998)
6
+ ![uv](https://img.shields.io/badge/packaging-uv-DE5FE9?logo=uv&logoColor=white)
7
+ ![License](https://img.shields.io/badge/license-GPL--2.0--or--later-blue)
8
+
9
+ Python bindings for the Rockbox **DSP**, **metadata**, and **playback**
10
+ engine, via `cffi` (ABI mode) over the prebuilt `librockbox_ffi` shared
11
+ library.
12
+
13
+ ## Setup
14
+
15
+ Build the shared library once (from the repo root):
16
+
17
+ ```sh
18
+ cargo build --release -p rockbox-ffi
19
+ ```
20
+
21
+ Then install the Python package with [uv](https://docs.astral.sh/uv/):
22
+
23
+ ```sh
24
+ cd bindings/python
25
+ uv venv
26
+ uv pip install -e .
27
+ uv run python examples/smoke.py
28
+ ```
29
+
30
+ The library is located automatically by walking up to
31
+ `target/release/librockbox_ffi.{dylib,so}`. Override with the
32
+ `ROCKBOX_FFI_LIB` environment variable.
33
+
34
+ ## Usage
35
+
36
+ ```python
37
+ import rockbox_ffi as rb
38
+ from rockbox_ffi import Dsp, Player, metadata
39
+ from rockbox_ffi.enums import DspReplayGainMode, ReplayGainMode, CrossfadeMode
40
+
41
+ # --- metadata ---------------------------------------------------------
42
+ meta = metadata.read("song.flac")
43
+ print(meta["artist"], "—", meta["title"], meta["duration_ms"], "ms")
44
+ print(metadata.probe("track.opus")) # -> "Opus"
45
+
46
+ # --- DSP (interleaved stereo int16) -----------------------------------
47
+ with Dsp(44100) as dsp:
48
+ dsp.eq_enable(True)
49
+ dsp.set_eq_band(0, cutoff_hz=60, q=0.7, gain_db=3.0)
50
+ dsp.set_replaygain(DspReplayGainMode.TRACK, noclip=True, preamp_db=0.0)
51
+ dsp.set_replaygain_gains(track_gain_db=-6.02) # halves amplitude
52
+ processed = dsp.process(samples) # array('h')
53
+
54
+ # --- playback (needs an output device) --------------------------------
55
+ with Player(volume=0.8) as player:
56
+ player.set_replaygain(ReplayGainMode.TRACK, preamp_db=0.0, prevent_clipping=True)
57
+ player.set_crossfade(CrossfadeMode.ALWAYS)
58
+ player.set_queue(["a.flac", "b.mp3", "c.opus"])
59
+ player.play()
60
+ print(player.status()) # {'state': 'playing', 'index': 0, ...}
61
+ ```
62
+
63
+ ## API
64
+
65
+ | Module | Contents |
66
+ | ---------------------- | --------------------------------------------------------------------- |
67
+ | `rockbox_ffi.metadata` | `read(path) -> dict`, `probe(filename) -> str \| None` |
68
+ | `rockbox_ffi.Dsp` | EQ / tone / surround / compressor / ReplayGain, `process(samples)` |
69
+ | `rockbox_ffi.Player` | queue + transport + crossfade + ReplayGain, `status() -> dict` |
70
+ | `rockbox_ffi.enums` | `DspReplayGainMode`, `ReplayGainMode`, `CrossfadeMode`, `MixMode`, … |
71
+
72
+ ### Two ReplayGain encodings
73
+
74
+ The DSP and player use *different* mode integers (a quirk of the C ABI):
75
+
76
+ - `Dsp.set_replaygain` → `DspReplayGainMode` (`TRACK=0, ALBUM=1, SHUFFLE=2, OFF=3`)
77
+ - `Player.set_replaygain` → `ReplayGainMode` (`OFF=0, TRACK=1, ALBUM=2`)
78
+
79
+ Use the named enums and you won't have to remember which is which.
80
+
81
+ ## Memory
82
+
83
+ All heap allocations crossing the FFI boundary (JSON strings, sample
84
+ buffers) are freed inside the wrappers — you never call a `*_free` yourself.
@@ -0,0 +1,60 @@
1
+ """End-to-end smoke test for the Python bindings.
2
+
3
+ Run: `uv run python examples/smoke.py`
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ from pathlib import Path
9
+
10
+ import rockbox_ffi as rb
11
+ from rockbox_ffi import Dsp, Player, metadata
12
+ from rockbox_ffi.enums import DspReplayGainMode, ReplayGainMode
13
+
14
+ REPO = Path(__file__).resolve().parents[3]
15
+ FIXTURE = REPO / "crates" / "rocksky" / "fixtures" / "08 - Internet Money - Speak(Explicit).m4a"
16
+
17
+
18
+ def main() -> None:
19
+ print(f"ABI version: {rb.abi_version()}")
20
+
21
+ # -- metadata ---------------------------------------------------------
22
+ meta = metadata.read(str(FIXTURE))
23
+ print(f"codec={meta['codec']!r} title={meta['title']!r} "
24
+ f"artist={meta['artist']!r} duration_ms={meta['duration_ms']} "
25
+ f"sample_rate={meta['sample_rate']}")
26
+ assert meta["codec"], "codec label should not be empty"
27
+
28
+ probe = metadata.probe("song.flac")
29
+ print(f"probe('song.flac') = {probe!r}")
30
+ assert probe, "flac probe should return a label"
31
+
32
+ # -- DSP: -6.0206 dB track gain should HALVE amplitude ----------------
33
+ rate = 44100
34
+ with Dsp(rate) as dsp:
35
+ dsp.set_replaygain(DspReplayGainMode.TRACK, noclip=False, preamp_db=0.0)
36
+ dsp.set_replaygain_gains(track_gain_db=-6.0206) # x0.5
37
+
38
+ sine = rb.sine_stereo(1000.0, 1.0, rate, amplitude=16000)
39
+ out = dsp.process(sine)
40
+ peak = max(abs(s) for s in out)
41
+ print(f"DSP peak after -6.02 dB track gain: {peak} (expected ~8000)")
42
+ assert 7600 <= peak <= 8400, f"peak {peak} out of range"
43
+
44
+ # -- Player (construct only, no audible playback) ---------------------
45
+ with Player(volume=0.0) as player:
46
+ rate = player.sample_rate()
47
+ print(f"Player sample_rate={rate}")
48
+ assert rate > 0
49
+ player.set_volume(0.0)
50
+ player.set_queue([str(FIXTURE)])
51
+ st = player.status()
52
+ print(f"Player status: state={st['state']!r} queue_len={st['queue_len']}")
53
+ assert st["state"] == "stopped"
54
+ assert st["queue_len"] == 1
55
+
56
+ print("\nALL CHECKS PASSED ✔")
57
+
58
+
59
+ if __name__ == "__main__":
60
+ main()
@@ -0,0 +1,15 @@
1
+ [project]
2
+ name = "rockbox-ffi"
3
+ version = "0.1.0"
4
+ description = "Python bindings for the Rockbox DSP / metadata / playback engine"
5
+ readme = "README.md"
6
+ requires-python = ">=3.9"
7
+ license = { text = "GPL-2.0-or-later" }
8
+ dependencies = ["cffi>=1.16"]
9
+
10
+ [build-system]
11
+ requires = ["hatchling"]
12
+ build-backend = "hatchling.build"
13
+
14
+ [tool.hatch.build.targets.wheel]
15
+ packages = ["src/rockbox_ffi"]
@@ -0,0 +1,35 @@
1
+ """Python bindings for the Rockbox DSP / metadata / playback engine.
2
+
3
+ Thin ``cffi`` wrappers over the ``librockbox_ffi`` C ABI. See the submodules
4
+ :mod:`rockbox_ffi.metadata`, :mod:`rockbox_ffi.dsp`, :mod:`rockbox_ffi.player`.
5
+ """
6
+
7
+ from . import metadata
8
+ from ._ffi import lib
9
+ from .dsp import Dsp, sine_stereo
10
+ from .enums import (
11
+ ChannelConfig,
12
+ CrossfadeMode,
13
+ DspReplayGainMode,
14
+ MixMode,
15
+ ReplayGainMode,
16
+ )
17
+ from .player import Player
18
+
19
+ __all__ = [
20
+ "metadata",
21
+ "Dsp",
22
+ "Player",
23
+ "sine_stereo",
24
+ "ChannelConfig",
25
+ "CrossfadeMode",
26
+ "DspReplayGainMode",
27
+ "MixMode",
28
+ "ReplayGainMode",
29
+ "abi_version",
30
+ ]
31
+
32
+
33
+ def abi_version() -> int:
34
+ """ABI major version of the loaded library (bumped on breaking changes)."""
35
+ return int(lib.rb_ffi_abi_version())
@@ -0,0 +1,135 @@
1
+ """cffi (ABI mode) loader for librockbox_ffi.
2
+
3
+ Declares exactly the functions we call and ``dlopen``s the prebuilt shared
4
+ library — no C is compiled here. The library is located via the
5
+ ``ROCKBOX_FFI_LIB`` env var, then by walking up to the repo's
6
+ ``target/release`` directory.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import os
12
+ from pathlib import Path
13
+
14
+ from cffi import FFI
15
+
16
+ ffi = FFI()
17
+
18
+ # Mirrors include/rockbox_ffi.h — keep in sync with the C ABI.
19
+ ffi.cdef(
20
+ """
21
+ typedef struct RbDsp RbDsp;
22
+ typedef struct RbPlayer RbPlayer;
23
+
24
+ uint32_t rb_ffi_abi_version(void);
25
+
26
+ void rb_string_free(char *p);
27
+ void rb_buffer_free(int16_t *p, size_t len);
28
+
29
+ RbDsp *rb_dsp_new(uint32_t sample_rate);
30
+ void rb_dsp_free(RbDsp *p);
31
+ void rb_dsp_set_input_frequency(RbDsp *p, uint32_t hz);
32
+ void rb_dsp_flush(RbDsp *p);
33
+ void rb_dsp_eq_enable(RbDsp *p, bool enable);
34
+ void rb_dsp_set_tone(RbDsp *p, int32_t bass_db, int32_t treble_db);
35
+ void rb_dsp_set_tone_cutoffs(RbDsp *p, int32_t bass_hz, int32_t treble_hz);
36
+ void rb_dsp_set_surround(RbDsp *p, int32_t delay_ms, int32_t balance,
37
+ int32_t fx1, int32_t fx2);
38
+ void rb_dsp_set_channel_config(RbDsp *p, int32_t mode);
39
+ void rb_dsp_set_stereo_width(RbDsp *p, int32_t percent);
40
+ void rb_dsp_set_compressor(RbDsp *p, int32_t threshold, int32_t makeup_gain,
41
+ int32_t ratio, int32_t knee, int32_t release_time,
42
+ int32_t attack_time);
43
+ void rb_dsp_set_replaygain(RbDsp *p, int32_t mode, bool noclip,
44
+ float preamp_db);
45
+ void rb_dsp_set_replaygain_gains(RbDsp *p, float track_gain_db,
46
+ float album_gain_db, float track_peak,
47
+ float album_peak);
48
+ void rb_dsp_set_replaygain_gains_raw(RbDsp *p, int64_t track_gain,
49
+ int64_t album_gain, int64_t track_peak,
50
+ int64_t album_peak);
51
+ void rb_dsp_set_eq_band(RbDsp *p, size_t band, int32_t cutoff_hz, float q,
52
+ float gain_db);
53
+ void rb_dsp_set_eq_precut(RbDsp *p, float db);
54
+ int16_t *rb_dsp_process(RbDsp *p, const int16_t *input, size_t in_len,
55
+ size_t *out_len);
56
+
57
+ char *rb_meta_read_json(const char *path);
58
+ char *rb_meta_probe(const char *filename);
59
+
60
+ RbPlayer *rb_player_new(void);
61
+ RbPlayer *rb_player_new_with_config(uint32_t sample_rate, float buffer_seconds,
62
+ float volume, int32_t rg_mode,
63
+ float rg_preamp_db, bool rg_prevent_clipping,
64
+ int32_t xfade_mode, uint32_t fo_delay_ms,
65
+ uint32_t fo_dur_ms, uint32_t fi_delay_ms,
66
+ uint32_t fi_dur_ms, int32_t mix_mode);
67
+ void rb_player_free(RbPlayer *p);
68
+ void rb_player_set_queue_json(RbPlayer *p, const char *json);
69
+ void rb_player_enqueue(RbPlayer *p, const char *path);
70
+ void rb_player_play(RbPlayer *p);
71
+ void rb_player_pause(RbPlayer *p);
72
+ void rb_player_toggle(RbPlayer *p);
73
+ void rb_player_stop(RbPlayer *p);
74
+ void rb_player_next(RbPlayer *p);
75
+ void rb_player_previous(RbPlayer *p);
76
+ void rb_player_skip_to(RbPlayer *p, size_t index);
77
+ void rb_player_seek_ms(RbPlayer *p, uint64_t ms);
78
+ void rb_player_set_volume(RbPlayer *p, float vol);
79
+ void rb_player_set_crossfade(RbPlayer *p, int32_t mode, uint32_t fo_delay_ms,
80
+ uint32_t fo_dur_ms, uint32_t fi_delay_ms,
81
+ uint32_t fi_dur_ms, int32_t mix_mode);
82
+ void rb_player_set_replaygain(RbPlayer *p, int32_t mode, float preamp_db,
83
+ bool prevent_clipping);
84
+ float rb_player_volume(RbPlayer *p);
85
+ uint32_t rb_player_sample_rate(RbPlayer *p);
86
+ char *rb_player_status_json(RbPlayer *p);
87
+ """
88
+ )
89
+
90
+
91
+ def _candidate_paths() -> list[Path]:
92
+ names = ["librockbox_ffi.dylib", "librockbox_ffi.so", "rockbox_ffi.dll"]
93
+ paths: list[Path] = []
94
+
95
+ env = os.environ.get("ROCKBOX_FFI_LIB")
96
+ if env:
97
+ paths.append(Path(env))
98
+
99
+ # Walk up from this file looking for target/release (repo checkout).
100
+ here = Path(__file__).resolve()
101
+ for parent in here.parents:
102
+ rel = parent / "target" / "release"
103
+ if rel.is_dir():
104
+ for name in names:
105
+ paths.append(rel / name)
106
+ return paths
107
+
108
+
109
+ def _load():
110
+ tried: list[str] = []
111
+ for path in _candidate_paths():
112
+ tried.append(str(path))
113
+ if path.exists():
114
+ return ffi.dlopen(str(path))
115
+ raise OSError(
116
+ "could not locate librockbox_ffi shared library. Set ROCKBOX_FFI_LIB "
117
+ "or run `cargo build --release -p rockbox-ffi`. Tried:\n "
118
+ + "\n ".join(tried)
119
+ )
120
+
121
+
122
+ lib = _load()
123
+
124
+
125
+ def take_string(ptr) -> str | None:
126
+ """Copy a heap C string returned by the ABI into a str, then free it.
127
+
128
+ Returns ``None`` for a NULL pointer (the ABI's error/absent signal).
129
+ """
130
+ if ptr == ffi.NULL:
131
+ return None
132
+ try:
133
+ return ffi.string(ptr).decode("utf-8", "replace")
134
+ finally:
135
+ lib.rb_string_free(ptr)
@@ -0,0 +1,159 @@
1
+ """The DSP pipeline: EQ, tone, surround, compressor, ReplayGain, resampler."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import array
6
+ import math
7
+ from typing import Iterable, Optional
8
+
9
+ from ._ffi import ffi, lib
10
+
11
+ _NAN = float("nan")
12
+
13
+
14
+ def _opt(v: Optional[float]) -> float:
15
+ """None -> NaN, the ABI's 'tag absent' sentinel."""
16
+ return _NAN if v is None else float(v)
17
+
18
+
19
+ class Dsp:
20
+ """Interleaved-S16LE-stereo DSP instance.
21
+
22
+ The underlying ``dsp_config`` is a process-wide singleton, so only one
23
+ :class:`Dsp` may exist at a time and it must be used from one thread.
24
+ Use as a context manager, or call :meth:`close` when done.
25
+ """
26
+
27
+ def __init__(self, sample_rate: int):
28
+ self._p = lib.rb_dsp_new(int(sample_rate))
29
+ if self._p == ffi.NULL:
30
+ raise RuntimeError("rb_dsp_new returned NULL")
31
+
32
+ # -- lifecycle --------------------------------------------------------
33
+ def close(self) -> None:
34
+ if getattr(self, "_p", ffi.NULL) != ffi.NULL:
35
+ lib.rb_dsp_free(self._p)
36
+ self._p = ffi.NULL
37
+
38
+ def __del__(self):
39
+ self.close()
40
+
41
+ def __enter__(self) -> "Dsp":
42
+ return self
43
+
44
+ def __exit__(self, *exc) -> None:
45
+ self.close()
46
+
47
+ # -- configuration ----------------------------------------------------
48
+ def set_input_frequency(self, hz: int) -> None:
49
+ lib.rb_dsp_set_input_frequency(self._p, int(hz))
50
+
51
+ def flush(self) -> None:
52
+ lib.rb_dsp_flush(self._p)
53
+
54
+ def eq_enable(self, enable: bool) -> None:
55
+ lib.rb_dsp_eq_enable(self._p, bool(enable))
56
+
57
+ def set_eq_band(self, band: int, cutoff_hz: int, q: float, gain_db: float) -> None:
58
+ """Configure one EQ band (0..=9). Band 0 low shelf, 9 high shelf."""
59
+ lib.rb_dsp_set_eq_band(self._p, int(band), int(cutoff_hz), float(q), float(gain_db))
60
+
61
+ def set_eq_precut(self, db: float) -> None:
62
+ lib.rb_dsp_set_eq_precut(self._p, float(db))
63
+
64
+ def set_tone(self, bass_db: int, treble_db: int) -> None:
65
+ lib.rb_dsp_set_tone(self._p, int(bass_db), int(treble_db))
66
+
67
+ def set_tone_cutoffs(self, bass_hz: int, treble_hz: int) -> None:
68
+ lib.rb_dsp_set_tone_cutoffs(self._p, int(bass_hz), int(treble_hz))
69
+
70
+ def set_surround(self, delay_ms: int, balance: int, fx1: int, fx2: int) -> None:
71
+ lib.rb_dsp_set_surround(self._p, int(delay_ms), int(balance), int(fx1), int(fx2))
72
+
73
+ def set_channel_config(self, mode: int) -> None:
74
+ lib.rb_dsp_set_channel_config(self._p, int(mode))
75
+
76
+ def set_stereo_width(self, percent: int) -> None:
77
+ lib.rb_dsp_set_stereo_width(self._p, int(percent))
78
+
79
+ def set_compressor(
80
+ self,
81
+ threshold: int,
82
+ makeup_gain: int,
83
+ ratio: int,
84
+ knee: int,
85
+ release_time: int,
86
+ attack_time: int,
87
+ ) -> None:
88
+ lib.rb_dsp_set_compressor(
89
+ self._p, int(threshold), int(makeup_gain), int(ratio),
90
+ int(knee), int(release_time), int(attack_time),
91
+ )
92
+
93
+ def set_replaygain(self, mode: int, noclip: bool, preamp_db: float) -> None:
94
+ """mode: see :class:`rockbox_ffi.enums.DspReplayGainMode`
95
+ (TRACK=0, ALBUM=1, SHUFFLE=2, OFF=3)."""
96
+ lib.rb_dsp_set_replaygain(self._p, int(mode), bool(noclip), float(preamp_db))
97
+
98
+ def set_replaygain_gains(
99
+ self,
100
+ track_gain_db: Optional[float] = None,
101
+ album_gain_db: Optional[float] = None,
102
+ track_peak: Optional[float] = None,
103
+ album_peak: Optional[float] = None,
104
+ ) -> None:
105
+ """Per-track gains in plain dB / peaks as linear amplitude
106
+ (1.0 = full scale). None for any absent tag."""
107
+ lib.rb_dsp_set_replaygain_gains(
108
+ self._p, _opt(track_gain_db), _opt(album_gain_db),
109
+ _opt(track_peak), _opt(album_peak),
110
+ )
111
+
112
+ def set_replaygain_gains_raw(
113
+ self, track_gain: int, album_gain: int, track_peak: int, album_peak: int
114
+ ) -> None:
115
+ """Native Q7.24 linear factors (the ``raw_*`` fields from
116
+ :func:`rockbox_ffi.metadata.read`), 0 = not tagged."""
117
+ lib.rb_dsp_set_replaygain_gains_raw(
118
+ self._p, int(track_gain), int(album_gain), int(track_peak), int(album_peak)
119
+ )
120
+
121
+ # -- processing -------------------------------------------------------
122
+ def process(self, samples: Iterable[int]) -> array.array:
123
+ """Run interleaved stereo S16 ``samples`` through the pipeline.
124
+
125
+ Accepts any int16 sequence (list, ``array('h')``, bytes-like via
126
+ ``array``). Returns a new ``array('h')`` of processed samples
127
+ (length may differ from the input — the resampler buffers).
128
+ """
129
+ buf = samples if isinstance(samples, array.array) and samples.typecode == "h" \
130
+ else array.array("h", samples)
131
+ n = len(buf)
132
+ if n % 2 != 0:
133
+ raise ValueError("input must be interleaved stereo (even length)")
134
+
135
+ cin = ffi.cast("const int16_t *", ffi.from_buffer(buf))
136
+ out_len = ffi.new("size_t *")
137
+ out_ptr = lib.rb_dsp_process(self._p, cin, n, out_len)
138
+
139
+ produced = int(out_len[0])
140
+ if out_ptr == ffi.NULL or produced == 0:
141
+ return array.array("h")
142
+ try:
143
+ result = array.array("h")
144
+ result.frombytes(ffi.buffer(out_ptr, produced * 2)[:])
145
+ return result
146
+ finally:
147
+ lib.rb_buffer_free(out_ptr, produced)
148
+
149
+
150
+ def sine_stereo(freq_hz: float, seconds: float, rate: int, amplitude: int = 16000) -> array.array:
151
+ """Helper: generate ``seconds`` of a sine as interleaved stereo int16."""
152
+ n = int(seconds * rate)
153
+ buf = array.array("h")
154
+ for i in range(n):
155
+ s = int(math.sin(i * 2.0 * math.pi * freq_hz / rate) * amplitude)
156
+ s = max(-32768, min(32767, s))
157
+ buf.append(s)
158
+ buf.append(s)
159
+ return buf
@@ -0,0 +1,53 @@
1
+ """Enum constants for the Rockbox FFI.
2
+
3
+ Note the two *different* ReplayGain encodings in the C ABI:
4
+
5
+ * The **DSP-level** call ``rb_dsp_set_replaygain`` (exposed via
6
+ :meth:`rockbox_ffi.dsp.Dsp.set_replaygain`) uses the Rockbox-native values:
7
+ ``TRACK=0, ALBUM=1, SHUFFLE=2, OFF=3`` — see :class:`DspReplayGainMode`.
8
+ * The **player-level** call ``rb_player_set_replaygain`` uses a simpler
9
+ encoding: ``OFF=0, TRACK=1, ALBUM=2`` — see :class:`ReplayGainMode`.
10
+ """
11
+
12
+ from enum import IntEnum
13
+
14
+
15
+ class DspReplayGainMode(IntEnum):
16
+ """Values for :meth:`rockbox_ffi.dsp.Dsp.set_replaygain` (native Rockbox)."""
17
+
18
+ TRACK = 0
19
+ ALBUM = 1
20
+ SHUFFLE = 2
21
+ OFF = 3
22
+
23
+
24
+ class ReplayGainMode(IntEnum):
25
+ """Values for :meth:`rockbox_ffi.player.Player.set_replaygain`."""
26
+
27
+ OFF = 0
28
+ TRACK = 1
29
+ ALBUM = 2
30
+
31
+
32
+ class CrossfadeMode(IntEnum):
33
+ OFF = 0
34
+ AUTO_SKIP = 1
35
+ MANUAL_SKIP = 2
36
+ SHUFFLE = 3
37
+ SHUFFLE_OR_MANUAL = 4
38
+ ALWAYS = 5
39
+
40
+
41
+ class MixMode(IntEnum):
42
+ CROSSFADE = 0
43
+ MIX = 1
44
+
45
+
46
+ class ChannelConfig(IntEnum):
47
+ STEREO = 0
48
+ MONO = 1
49
+ CUSTOM = 2
50
+ MONO_LEFT = 3
51
+ MONO_RIGHT = 4
52
+ KARAOKE = 5
53
+ SWAP = 6
@@ -0,0 +1,29 @@
1
+ """Audio file metadata parsing."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from typing import Optional
7
+
8
+ from ._ffi import ffi, lib, take_string
9
+
10
+
11
+ def read(path: str) -> dict:
12
+ """Parse the metadata of the audio file at ``path``.
13
+
14
+ Returns a dict with tag strings, ``duration_ms``, ``bitrate``,
15
+ ``sample_rate``, a nested ``replaygain`` dict (including the raw Q7.24
16
+ ``raw_*`` fields), and optional ``album_art`` / ``cuesheet`` locations.
17
+
18
+ Raises ``FileNotFoundError`` / ``ValueError`` on failure.
19
+ """
20
+ s = take_string(lib.rb_meta_read_json(path.encode("utf-8")))
21
+ if s is None:
22
+ raise ValueError(f"could not read metadata from {path!r}")
23
+ return json.loads(s)
24
+
25
+
26
+ def probe(filename: str) -> Optional[str]:
27
+ """Guess the codec label (e.g. ``"FLAC"``) from a filename's extension
28
+ without opening the file. Returns ``None`` for an unknown extension."""
29
+ return take_string(lib.rb_meta_probe(filename.encode("utf-8")))
@@ -0,0 +1,134 @@
1
+ """Queue-based player with native ReplayGain and Rockbox crossfade.
2
+
3
+ A :class:`Player` owns a live audio output device and a background engine
4
+ thread — construct it only where an output device exists.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ from typing import List, Optional
11
+
12
+ from ._ffi import ffi, lib, take_string
13
+ from .enums import CrossfadeMode, MixMode, ReplayGainMode
14
+
15
+
16
+ class Player:
17
+ def __init__(
18
+ self,
19
+ *,
20
+ sample_rate: int = 0,
21
+ buffer_seconds: float = 4.0,
22
+ volume: float = 1.0,
23
+ replaygain_mode: int = ReplayGainMode.OFF,
24
+ replaygain_preamp_db: float = 0.0,
25
+ replaygain_prevent_clipping: bool = True,
26
+ crossfade_mode: int = CrossfadeMode.OFF,
27
+ fade_out_delay_ms: int = 0,
28
+ fade_out_duration_ms: int = 2000,
29
+ fade_in_delay_ms: int = 0,
30
+ fade_in_duration_ms: int = 2000,
31
+ mix_mode: int = MixMode.CROSSFADE,
32
+ _default: bool = False,
33
+ ):
34
+ """Create a player. With no args, uses the device default sample rate
35
+ and Rockbox default settings. ``sample_rate=0`` means device default."""
36
+ if _default:
37
+ self._p = lib.rb_player_new()
38
+ else:
39
+ self._p = lib.rb_player_new_with_config(
40
+ int(sample_rate), float(buffer_seconds), float(volume),
41
+ int(replaygain_mode), float(replaygain_preamp_db),
42
+ bool(replaygain_prevent_clipping), int(crossfade_mode),
43
+ int(fade_out_delay_ms), int(fade_out_duration_ms),
44
+ int(fade_in_delay_ms), int(fade_in_duration_ms), int(mix_mode),
45
+ )
46
+ if self._p == ffi.NULL:
47
+ raise RuntimeError("failed to create Player (no output device?)")
48
+
49
+ @classmethod
50
+ def default(cls) -> "Player":
51
+ """Player on the default device with default settings."""
52
+ return cls(_default=True)
53
+
54
+ # -- lifecycle --------------------------------------------------------
55
+ def close(self) -> None:
56
+ if getattr(self, "_p", ffi.NULL) != ffi.NULL:
57
+ lib.rb_player_free(self._p)
58
+ self._p = ffi.NULL
59
+
60
+ def __del__(self):
61
+ self.close()
62
+
63
+ def __enter__(self) -> "Player":
64
+ return self
65
+
66
+ def __exit__(self, *exc) -> None:
67
+ self.close()
68
+
69
+ # -- queue ------------------------------------------------------------
70
+ def set_queue(self, paths: List[str]) -> None:
71
+ lib.rb_player_set_queue_json(self._p, json.dumps(list(paths)).encode("utf-8"))
72
+
73
+ def enqueue(self, path: str) -> None:
74
+ lib.rb_player_enqueue(self._p, path.encode("utf-8"))
75
+
76
+ # -- transport --------------------------------------------------------
77
+ def play(self) -> None:
78
+ lib.rb_player_play(self._p)
79
+
80
+ def pause(self) -> None:
81
+ lib.rb_player_pause(self._p)
82
+
83
+ def toggle(self) -> None:
84
+ lib.rb_player_toggle(self._p)
85
+
86
+ def stop(self) -> None:
87
+ lib.rb_player_stop(self._p)
88
+
89
+ def next(self) -> None:
90
+ lib.rb_player_next(self._p)
91
+
92
+ def previous(self) -> None:
93
+ lib.rb_player_previous(self._p)
94
+
95
+ def skip_to(self, index: int) -> None:
96
+ lib.rb_player_skip_to(self._p, int(index))
97
+
98
+ def seek_ms(self, ms: int) -> None:
99
+ lib.rb_player_seek_ms(self._p, int(ms))
100
+
101
+ # -- settings ---------------------------------------------------------
102
+ def set_volume(self, vol: float) -> None:
103
+ lib.rb_player_set_volume(self._p, float(vol))
104
+
105
+ def volume(self) -> float:
106
+ return float(lib.rb_player_volume(self._p))
107
+
108
+ def sample_rate(self) -> int:
109
+ return int(lib.rb_player_sample_rate(self._p))
110
+
111
+ def set_crossfade(
112
+ self,
113
+ mode: int,
114
+ fade_out_delay_ms: int = 0,
115
+ fade_out_duration_ms: int = 2000,
116
+ fade_in_delay_ms: int = 0,
117
+ fade_in_duration_ms: int = 2000,
118
+ mix_mode: int = MixMode.CROSSFADE,
119
+ ) -> None:
120
+ lib.rb_player_set_crossfade(
121
+ self._p, int(mode), int(fade_out_delay_ms), int(fade_out_duration_ms),
122
+ int(fade_in_delay_ms), int(fade_in_duration_ms), int(mix_mode),
123
+ )
124
+
125
+ def set_replaygain(self, mode: int, preamp_db: float, prevent_clipping: bool) -> None:
126
+ """mode: :class:`rockbox_ffi.enums.ReplayGainMode` (OFF=0, TRACK=1, ALBUM=2)."""
127
+ lib.rb_player_set_replaygain(self._p, int(mode), float(preamp_db), bool(prevent_clipping))
128
+
129
+ # -- status -----------------------------------------------------------
130
+ def status(self) -> dict:
131
+ s = take_string(lib.rb_player_status_json(self._p))
132
+ if s is None:
133
+ raise RuntimeError("rb_player_status_json returned NULL")
134
+ return json.loads(s)
@@ -0,0 +1,253 @@
1
+ version = 1
2
+ revision = 3
3
+ requires-python = ">=3.9"
4
+ resolution-markers = [
5
+ "python_full_version >= '3.10'",
6
+ "python_full_version < '3.10'",
7
+ ]
8
+
9
+ [[package]]
10
+ name = "cffi"
11
+ version = "2.0.0"
12
+ source = { registry = "https://pypi.org/simple" }
13
+ resolution-markers = [
14
+ "python_full_version < '3.10'",
15
+ ]
16
+ dependencies = [
17
+ { name = "pycparser", version = "2.23", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' and implementation_name != 'PyPy'" },
18
+ ]
19
+ sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" }
20
+ wheels = [
21
+ { url = "https://files.pythonhosted.org/packages/93/d7/516d984057745a6cd96575eea814fe1edd6646ee6efd552fb7b0921dec83/cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44", size = 184283, upload-time = "2025-09-08T23:22:08.01Z" },
22
+ { url = "https://files.pythonhosted.org/packages/9e/84/ad6a0b408daa859246f57c03efd28e5dd1b33c21737c2db84cae8c237aa5/cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49", size = 180504, upload-time = "2025-09-08T23:22:10.637Z" },
23
+ { url = "https://files.pythonhosted.org/packages/50/bd/b1a6362b80628111e6653c961f987faa55262b4002fcec42308cad1db680/cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c", size = 208811, upload-time = "2025-09-08T23:22:12.267Z" },
24
+ { url = "https://files.pythonhosted.org/packages/4f/27/6933a8b2562d7bd1fb595074cf99cc81fc3789f6a6c05cdabb46284a3188/cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb", size = 216402, upload-time = "2025-09-08T23:22:13.455Z" },
25
+ { url = "https://files.pythonhosted.org/packages/05/eb/b86f2a2645b62adcfff53b0dd97e8dfafb5c8aa864bd0d9a2c2049a0d551/cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0", size = 203217, upload-time = "2025-09-08T23:22:14.596Z" },
26
+ { url = "https://files.pythonhosted.org/packages/9f/e0/6cbe77a53acf5acc7c08cc186c9928864bd7c005f9efd0d126884858a5fe/cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4", size = 203079, upload-time = "2025-09-08T23:22:15.769Z" },
27
+ { url = "https://files.pythonhosted.org/packages/98/29/9b366e70e243eb3d14a5cb488dfd3a0b6b2f1fb001a203f653b93ccfac88/cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453", size = 216475, upload-time = "2025-09-08T23:22:17.427Z" },
28
+ { url = "https://files.pythonhosted.org/packages/21/7a/13b24e70d2f90a322f2900c5d8e1f14fa7e2a6b3332b7309ba7b2ba51a5a/cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495", size = 218829, upload-time = "2025-09-08T23:22:19.069Z" },
29
+ { url = "https://files.pythonhosted.org/packages/60/99/c9dc110974c59cc981b1f5b66e1d8af8af764e00f0293266824d9c4254bc/cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5", size = 211211, upload-time = "2025-09-08T23:22:20.588Z" },
30
+ { url = "https://files.pythonhosted.org/packages/49/72/ff2d12dbf21aca1b32a40ed792ee6b40f6dc3a9cf1644bd7ef6e95e0ac5e/cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb", size = 218036, upload-time = "2025-09-08T23:22:22.143Z" },
31
+ { url = "https://files.pythonhosted.org/packages/e2/cc/027d7fb82e58c48ea717149b03bcadcbdc293553edb283af792bd4bcbb3f/cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a", size = 172184, upload-time = "2025-09-08T23:22:23.328Z" },
32
+ { url = "https://files.pythonhosted.org/packages/33/fa/072dd15ae27fbb4e06b437eb6e944e75b068deb09e2a2826039e49ee2045/cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739", size = 182790, upload-time = "2025-09-08T23:22:24.752Z" },
33
+ { url = "https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe", size = 184344, upload-time = "2025-09-08T23:22:26.456Z" },
34
+ { url = "https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c", size = 180560, upload-time = "2025-09-08T23:22:28.197Z" },
35
+ { url = "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", size = 209613, upload-time = "2025-09-08T23:22:29.475Z" },
36
+ { url = "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", size = 216476, upload-time = "2025-09-08T23:22:31.063Z" },
37
+ { url = "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", size = 203374, upload-time = "2025-09-08T23:22:32.507Z" },
38
+ { url = "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", size = 202597, upload-time = "2025-09-08T23:22:34.132Z" },
39
+ { url = "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", size = 215574, upload-time = "2025-09-08T23:22:35.443Z" },
40
+ { url = "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", size = 218971, upload-time = "2025-09-08T23:22:36.805Z" },
41
+ { url = "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", size = 211972, upload-time = "2025-09-08T23:22:38.436Z" },
42
+ { url = "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", size = 217078, upload-time = "2025-09-08T23:22:39.776Z" },
43
+ { url = "https://files.pythonhosted.org/packages/2b/c0/015b25184413d7ab0a410775fdb4a50fca20f5589b5dab1dbbfa3baad8ce/cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5", size = 172076, upload-time = "2025-09-08T23:22:40.95Z" },
44
+ { url = "https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5", size = 182820, upload-time = "2025-09-08T23:22:42.463Z" },
45
+ { url = "https://files.pythonhosted.org/packages/95/5c/1b493356429f9aecfd56bc171285a4c4ac8697f76e9bbbbb105e537853a1/cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d", size = 177635, upload-time = "2025-09-08T23:22:43.623Z" },
46
+ { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" },
47
+ { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" },
48
+ { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" },
49
+ { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" },
50
+ { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" },
51
+ { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" },
52
+ { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" },
53
+ { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" },
54
+ { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" },
55
+ { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" },
56
+ { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" },
57
+ { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" },
58
+ { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" },
59
+ { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" },
60
+ { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" },
61
+ { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" },
62
+ { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" },
63
+ { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" },
64
+ { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" },
65
+ { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" },
66
+ { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" },
67
+ { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" },
68
+ { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" },
69
+ { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" },
70
+ { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" },
71
+ { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" },
72
+ { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" },
73
+ { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" },
74
+ { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" },
75
+ { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" },
76
+ { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" },
77
+ { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" },
78
+ { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" },
79
+ { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" },
80
+ { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" },
81
+ { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" },
82
+ { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" },
83
+ { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" },
84
+ { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" },
85
+ { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" },
86
+ { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" },
87
+ { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" },
88
+ { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" },
89
+ { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" },
90
+ { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" },
91
+ { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" },
92
+ { url = "https://files.pythonhosted.org/packages/c0/cc/08ed5a43f2996a16b462f64a7055c6e962803534924b9b2f1371d8c00b7b/cffi-2.0.0-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:fe562eb1a64e67dd297ccc4f5addea2501664954f2692b69a76449ec7913ecbf", size = 184288, upload-time = "2025-09-08T23:23:48.404Z" },
93
+ { url = "https://files.pythonhosted.org/packages/3d/de/38d9726324e127f727b4ecc376bc85e505bfe61ef130eaf3f290c6847dd4/cffi-2.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:de8dad4425a6ca6e4e5e297b27b5c824ecc7581910bf9aee86cb6835e6812aa7", size = 180509, upload-time = "2025-09-08T23:23:49.73Z" },
94
+ { url = "https://files.pythonhosted.org/packages/9b/13/c92e36358fbcc39cf0962e83223c9522154ee8630e1df7c0b3a39a8124e2/cffi-2.0.0-cp39-cp39-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:4647afc2f90d1ddd33441e5b0e85b16b12ddec4fca55f0d9671fef036ecca27c", size = 208813, upload-time = "2025-09-08T23:23:51.263Z" },
95
+ { url = "https://files.pythonhosted.org/packages/15/12/a7a79bd0df4c3bff744b2d7e52cc1b68d5e7e427b384252c42366dc1ecbc/cffi-2.0.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3f4d46d8b35698056ec29bca21546e1551a205058ae1a181d871e278b0b28165", size = 216498, upload-time = "2025-09-08T23:23:52.494Z" },
96
+ { url = "https://files.pythonhosted.org/packages/a3/ad/5c51c1c7600bdd7ed9a24a203ec255dccdd0ebf4527f7b922a0bde2fb6ed/cffi-2.0.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:e6e73b9e02893c764e7e8d5bb5ce277f1a009cd5243f8228f75f842bf937c534", size = 203243, upload-time = "2025-09-08T23:23:53.836Z" },
97
+ { url = "https://files.pythonhosted.org/packages/32/f2/81b63e288295928739d715d00952c8c6034cb6c6a516b17d37e0c8be5600/cffi-2.0.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:cb527a79772e5ef98fb1d700678fe031e353e765d1ca2d409c92263c6d43e09f", size = 203158, upload-time = "2025-09-08T23:23:55.169Z" },
98
+ { url = "https://files.pythonhosted.org/packages/1f/74/cc4096ce66f5939042ae094e2e96f53426a979864aa1f96a621ad128be27/cffi-2.0.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:61d028e90346df14fedc3d1e5441df818d095f3b87d286825dfcbd6459b7ef63", size = 216548, upload-time = "2025-09-08T23:23:56.506Z" },
99
+ { url = "https://files.pythonhosted.org/packages/e8/be/f6424d1dc46b1091ffcc8964fa7c0ab0cd36839dd2761b49c90481a6ba1b/cffi-2.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0f6084a0ea23d05d20c3edcda20c3d006f9b6f3fefeac38f59262e10cef47ee2", size = 218897, upload-time = "2025-09-08T23:23:57.825Z" },
100
+ { url = "https://files.pythonhosted.org/packages/f7/e0/dda537c2309817edf60109e39265f24f24aa7f050767e22c98c53fe7f48b/cffi-2.0.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1cd13c99ce269b3ed80b417dcd591415d3372bcac067009b6e0f59c7d4015e65", size = 211249, upload-time = "2025-09-08T23:23:59.139Z" },
101
+ { url = "https://files.pythonhosted.org/packages/2b/e7/7c769804eb75e4c4b35e658dba01de1640a351a9653c3d49ca89d16ccc91/cffi-2.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:89472c9762729b5ae1ad974b777416bfda4ac5642423fa93bd57a09204712322", size = 218041, upload-time = "2025-09-08T23:24:00.496Z" },
102
+ { url = "https://files.pythonhosted.org/packages/aa/d9/6218d78f920dcd7507fc16a766b5ef8f3b913cc7aa938e7fc80b9978d089/cffi-2.0.0-cp39-cp39-win32.whl", hash = "sha256:2081580ebb843f759b9f617314a24ed5738c51d2aee65d31e02f6f7a2b97707a", size = 172138, upload-time = "2025-09-08T23:24:01.7Z" },
103
+ { url = "https://files.pythonhosted.org/packages/54/8f/a1e836f82d8e32a97e6b29cc8f641779181ac7363734f12df27db803ebda/cffi-2.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:b882b3df248017dba09d6b16defe9b5c407fe32fc7c65a9c69798e6175601be9", size = 182794, upload-time = "2025-09-08T23:24:02.943Z" },
104
+ ]
105
+
106
+ [[package]]
107
+ name = "cffi"
108
+ version = "2.1.0"
109
+ source = { registry = "https://pypi.org/simple" }
110
+ resolution-markers = [
111
+ "python_full_version >= '3.10'",
112
+ ]
113
+ dependencies = [
114
+ { name = "pycparser", version = "3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' and implementation_name != 'PyPy'" },
115
+ ]
116
+ sdist = { url = "https://files.pythonhosted.org/packages/57/5f/ff100cae70ebe9d8df1c01a00e510e45d9adb5c1fdda84791b199141de97/cffi-2.1.0.tar.gz", hash = "sha256:efc1cdd798b1aaf39b4610bba7aad28c9bea9b910f25c784ccf9ec1fa719d1f9", size = 531036, upload-time = "2026-07-06T21:34:30.382Z" }
117
+ wheels = [
118
+ { url = "https://files.pythonhosted.org/packages/c0/e9/6d7724983b3d5a0908dbf74f64038ade77c18646ff6636ec7894fd392ce1/cffi-2.1.0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:b65f590ef2a44640f9a05dbb548a429b4ade77913ce683ac8b1480777658a6c0", size = 183837, upload-time = "2026-07-06T21:32:09.655Z" },
119
+ { url = "https://files.pythonhosted.org/packages/69/aa/24580a278de21fd7322635556334d9b535f1cbc00b0a3919447cdf464c65/cffi-2.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:164bff1657b2a74f0b6d54e11c9b375bc97b931f2ca9c43fcf875838da1570dd", size = 184226, upload-time = "2026-07-06T21:32:11.196Z" },
120
+ { url = "https://files.pythonhosted.org/packages/88/a9/02cae418ec4beb282ace11958d9d4737793439d561fadc7e6d56f2e2b354/cffi-2.1.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:c941bb58d5a6e1c3892d86e42927ed6c180302f07e6d395d08c416e594b98b46", size = 211107, upload-time = "2026-07-06T21:32:12.328Z" },
121
+ { url = "https://files.pythonhosted.org/packages/3b/30/c806937ed5e4c2c7ac30d9d6b76b5dc57ff8b75d83800d9bb11a8253cf2a/cffi-2.1.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a016194dbe13d14ee9556e734b772d8d67b947092b268d757fd4290e3ba2dfc2", size = 218733, upload-time = "2026-07-06T21:32:13.67Z" },
122
+ { url = "https://files.pythonhosted.org/packages/f9/cf/398272b8bbfd58aa314fda5a7f1cdbb26d1d78ae324a11211521315dd1f0/cffi-2.1.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:03e9810d18c646077e501f661b682fbf5dee4676048527ca3cffe66faa9960dd", size = 205543, upload-time = "2026-07-06T21:32:15.148Z" },
123
+ { url = "https://files.pythonhosted.org/packages/45/ca/f91641185cdd90c36d317a9dc7f85e88ef8682d8b300977baff5e23c35d8/cffi-2.1.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:19c54ac121cad98450b4896fa9a43ee0180d57bc4bc911a33db6cab1efab6cd3", size = 205460, upload-time = "2026-07-06T21:32:16.479Z" },
124
+ { url = "https://files.pythonhosted.org/packages/38/66/04781a77b411f0bb5b234d62c1814754ab75ebe455ccff1b08e8d7aae98f/cffi-2.1.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d433a51f1870e43a13b6732f92aaf540ff77c2015097c78556f75a2d6c030e0", size = 218760, upload-time = "2026-07-06T21:32:17.98Z" },
125
+ { url = "https://files.pythonhosted.org/packages/d0/9a/bb1d5ed9c3fcae158e9f6391bf309c95d98c2ac37ed56573228471d0af5e/cffi-2.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3d7f118b5adbfdfead90c25822690b02bc8074fba949bb7858bec4ebd55adb43", size = 221230, upload-time = "2026-07-06T21:32:19.407Z" },
126
+ { url = "https://files.pythonhosted.org/packages/41/aa/3c1409cdd26094efacd1c36c66e0a6eb9d4296e4fd4f9901b8b2042f4323/cffi-2.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c5f5df567f6eb216de69be06ce55c8b714090fae02b18a3b40da8163b8c5fa9c", size = 213524, upload-time = "2026-07-06T21:32:20.828Z" },
127
+ { url = "https://files.pythonhosted.org/packages/fa/75/74dfb7c3fc6ebbd408038476bd4c1d7e925c62614e7b9c534ecc34218288/cffi-2.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:11b3fb55f4f8ad92274ed26705f65d8f91457de71f5380061eb6d125a768fecd", size = 220341, upload-time = "2026-07-06T21:32:21.9Z" },
128
+ { url = "https://files.pythonhosted.org/packages/70/b6/9003c33a3e7d2c1306f5962e646457dcfe5a8cd8fce6bbe02d7af25db783/cffi-2.1.0-cp310-cp310-win32.whl", hash = "sha256:9d72af0cf10a76a600a9690078fe31c63b9588c8e86bf9fd353f713c84b5db0f", size = 174578, upload-time = "2026-07-06T21:32:23.073Z" },
129
+ { url = "https://files.pythonhosted.org/packages/8a/26/710688310447531c7a22f857c7f79d9855ec18b03e04494ced723fb37e2f/cffi-2.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:fb62edb5bb52cca65fab91a63afa7561607120d26090a7e8fda6fb9f064726da", size = 185071, upload-time = "2026-07-06T21:32:24.671Z" },
130
+ { url = "https://files.pythonhosted.org/packages/d3/67/85c89a59ba36a671e79638f44d466749f08179266a57e4f2ffdf92174072/cffi-2.1.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:02cb7ff33ded4f1532476731f89ede53e2e488a8e6205515a82144246ffa7dcc", size = 183845, upload-time = "2026-07-06T21:32:26.32Z" },
131
+ { url = "https://files.pythonhosted.org/packages/ea/dd/e3b0baa2d3d6a857ac72b7efbf18e32e487c9cdafcc13049ad765495b15e/cffi-2.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f5bce581e6b8c235e566a14768a943b172ada3ed73537bb0c0be1edee312d4e7", size = 184186, upload-time = "2026-07-06T21:32:28.025Z" },
132
+ { url = "https://files.pythonhosted.org/packages/65/68/9f3ef890cf3c6ab97bd531c5677f67613d302165d16f8142b2811782a614/cffi-2.1.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:30b65779d598c370374fefabf138d456fd6f3216bfa7bedfab1ba82025b0cd93", size = 211892, upload-time = "2026-07-06T21:32:29.565Z" },
133
+ { url = "https://files.pythonhosted.org/packages/22/d7/1a74539db16d8bfd839ff1515948948efbb162e574650fd3d846896eea95/cffi-2.1.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:88023dfe18799507b73f1dbb0d14326a17465de1bc9c9c7655c22845e9ddc3a2", size = 218793, upload-time = "2026-07-06T21:32:30.951Z" },
134
+ { url = "https://files.pythonhosted.org/packages/ec/d1/9a5b7169499e8e8d8e636de70b97ac7c9447104d2ff1a2cd94790cea5162/cffi-2.1.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:0a96b74cda968eebbad56d973efe5098974f0a9fb323865bf99ea1fd24e3e64c", size = 205737, upload-time = "2026-07-06T21:32:32.216Z" },
135
+ { url = "https://files.pythonhosted.org/packages/ba/b0/e131a9c41f10607926278453d9596163594fe1c4ebc46efe3b5e5b34eb84/cffi-2.1.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:a5781494d4d400a3f47f8f1da94b324f6e6b440a53387774002890a2a2f4b50f", size = 204909, upload-time = "2026-07-06T21:32:33.655Z" },
136
+ { url = "https://files.pythonhosted.org/packages/fb/d2/4398416cd699b35167947c6e22aca52c47e69ad5695073c9f1f2c52e04aa/cffi-2.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:aa7a1b53a2a4452ada2d1b5dade9960b2522f1e61293a811a077439e39029565", size = 217883, upload-time = "2026-07-06T21:32:35.173Z" },
137
+ { url = "https://files.pythonhosted.org/packages/a2/a5/d4fe77b589e5e82d43ebc809bf2e6474afe8e48e32ea050b9357645b6471/cffi-2.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9d8272c0e483b024e1b9ad029821470ed8ec65631dbd90217469da0e7cd89f1c", size = 221251, upload-time = "2026-07-06T21:32:36.527Z" },
138
+ { url = "https://files.pythonhosted.org/packages/22/f0/a2fc43084c0433caf7f461bccc013e28f848d04ee1c5ed7fce71423cf4d9/cffi-2.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7762faa47e8ff7eb80bd261d9a7d8eea2d8baa69de5e95b70c1f338bbe712f02", size = 214250, upload-time = "2026-07-06T21:32:37.852Z" },
139
+ { url = "https://files.pythonhosted.org/packages/04/8c/b925975448cf20634a9fbd5efceb807219db452653648d2897c0989cab2d/cffi-2.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:89095c1968b4ba8285840e131bf2891b09ae137fe2146905acae0354fbce1b5e", size = 219441, upload-time = "2026-07-06T21:32:39.146Z" },
140
+ { url = "https://files.pythonhosted.org/packages/eb/da/5c4918a2d61d86fa927d716cb3d8e4626ef8dc8f605a599d32f33897f59a/cffi-2.1.0-cp311-cp311-win32.whl", hash = "sha256:64c753a0f87a256020004f37a1c8c02c480e725f910f0b2a0f3f07debd1b2479", size = 174496, upload-time = "2026-07-06T21:32:40.467Z" },
141
+ { url = "https://files.pythonhosted.org/packages/f9/c8/6c2de1d55cf35ef8b92885d5ef280790f0fb9634d87ea1cc315176aecd61/cffi-2.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:4f26194e3d95e06501b942642855aed4f953d55e95d7d01b7c4483db3ecff458", size = 185113, upload-time = "2026-07-06T21:32:41.761Z" },
142
+ { url = "https://files.pythonhosted.org/packages/9e/4e/e8d7cb5783f1841a3c8fb3a7735838d7484d08ec08c9f984b14cac1ac0e9/cffi-2.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:35aaea0c7ee0e58a5cd8c2fd1a48fdf7ece0d2699b7ecdda08194e9ce5dd9b3d", size = 179927, upload-time = "2026-07-06T21:32:42.961Z" },
143
+ { url = "https://files.pythonhosted.org/packages/1e/85/990925db5df586ec90beb97529c853497e7f85ba0234830447faf41c3057/cffi-2.1.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:df2b82571a1b30f58a87bf4e5a9e78d2b1eff6c6ce8fd3aa3757221f93f0863f", size = 184829, upload-time = "2026-07-06T21:32:44.324Z" },
144
+ { url = "https://files.pythonhosted.org/packages/4b/92/e7bb136ad6b5352603732cf907ef862ca103f20f2031c1735a46300c20c9/cffi-2.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:78474632761faa0fb96f30b1c928c84ebcf68713cbb80d15bab09dfe61640fde", size = 184728, upload-time = "2026-07-06T21:32:45.683Z" },
145
+ { url = "https://files.pythonhosted.org/packages/c3/c0/d1ec30ffb370f748f2fb54425972bfef9871e0132e82fb589c46b6676049/cffi-2.1.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:5972433ad71a9e46516584ef60a0fda12d9dc459938d1539c3ddecf9bdc1368d", size = 214815, upload-time = "2026-07-06T21:32:48.557Z" },
146
+ { url = "https://files.pythonhosted.org/packages/1b/dc/5620cf930688be01f2d673804291de757a934c90b946dbdc3d84130c2ea4/cffi-2.1.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b6422532152adf4e59b110cb2808cee7a033800952f5c036b4af047ee43199e7", size = 222429, upload-time = "2026-07-06T21:32:49.848Z" },
147
+ { url = "https://files.pythonhosted.org/packages/4b/a4/77b53abbf7a1e0beb9637edbef2a94d15f9c822f591e85d439ffd91519a6/cffi-2.1.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:46b1c8db8f6122420f32d02fffb924c2fe9bc772d228c7c711748fff56aabb2b", size = 210315, upload-time = "2026-07-06T21:32:51.221Z" },
148
+ { url = "https://files.pythonhosted.org/packages/58/0c/f528df19cc94b675087324d4760d9e6d5bfae97d6217aa4fac43de4f5fcc/cffi-2.1.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9fafc5aa2e2a39aaf7f8cc0c1f044a9b07fca12e558dca53a3cc5c654ad67a7", size = 208859, upload-time = "2026-07-06T21:32:52.512Z" },
149
+ { url = "https://files.pythonhosted.org/packages/62/f2/c9522a81c32132799a1972c39f5c5f8b4c8b9f00488a23feaa6c06f07741/cffi-2.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1e9f50d192a3e525b15a75ab5114e442d83d657b7ec29182a991bc9a88fd3a66", size = 221844, upload-time = "2026-07-06T21:32:53.704Z" },
150
+ { url = "https://files.pythonhosted.org/packages/6e/28/bd53988b9833e8f8ad539d26f4c07a6b3f6bcb1e9e02e7ca038250b3428d/cffi-2.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:98fff996e983a36d3aa2eca83af40c5821202e7e6f32d13ae94e3d2286f10cfe", size = 225287, upload-time = "2026-07-06T21:32:54.907Z" },
151
+ { url = "https://files.pythonhosted.org/packages/79/99/0d0fd37f055224085f42bbb2c022d002e17dde4a97972822327b07d84101/cffi-2.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:379de10ce1ba048b1448599d1b37b24caee16309d1ac98d3982fc997f768700b", size = 223681, upload-time = "2026-07-06T21:32:56.329Z" },
152
+ { url = "https://files.pythonhosted.org/packages/b0/80/c138990aa2a70b1a269f6e06348729836d733d6f970867943f61d367f8cc/cffi-2.1.0-cp312-cp312-win32.whl", hash = "sha256:9b8f0f26ca4e7513c534d351eca551947d053fac438f2a04ac96d882909b0d3a", size = 175269, upload-time = "2026-07-06T21:32:57.777Z" },
153
+ { url = "https://files.pythonhosted.org/packages/a8/eb/f636456ff21a83fc13c032b58cc5dde061691546ac79efa284b2989b7982/cffi-2.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:c97f080ea627e2863524c5af3836e2270b5f5dfff1f104392b959f8df0c5d384", size = 185881, upload-time = "2026-07-06T21:32:59.253Z" },
154
+ { url = "https://files.pythonhosted.org/packages/dd/2c/400ea43e721727dca8a65c4521390e9196757caba4a45643acb2b63271b8/cffi-2.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:6d194185eabd279f1c05ebe3504265ddfc5ad2b58d0714f7db9f01da592e9eb6", size = 180088, upload-time = "2026-07-06T21:33:02.278Z" },
155
+ { url = "https://files.pythonhosted.org/packages/96/88/a996879e2eeccb815f6e3a5967b12a308257412acec882039d386bd2aa7b/cffi-2.1.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:10537b1df4967ca26d21e5072d7d54188354483b91dc75058968d3f0cf13fbda", size = 194331, upload-time = "2026-07-06T21:33:03.697Z" },
156
+ { url = "https://files.pythonhosted.org/packages/58/85/7ae00d5c8dd6266f4e944c3db630f3c5c9a98b61d469c714d848b1d8138a/cffi-2.1.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:a95b05f9baf29b91171b3a8bd2020b028835243e7b0ff6bb23e2a3c228518b1b", size = 196966, upload-time = "2026-07-06T21:33:05.353Z" },
157
+ { url = "https://files.pythonhosted.org/packages/8c/e9/45c3a76ad8d43ad9261f4c95436da61128d3ca545d72b9612c0ab5be0b1c/cffi-2.1.0-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:15faec4adfff450819f3aee0e2e02c812de6edb88203aa58807955db2003472a", size = 184795, upload-time = "2026-07-06T21:33:06.699Z" },
158
+ { url = "https://files.pythonhosted.org/packages/84/4c/82f132cb4418ee6d953d982b19191e87e2a6372c8a4ce36e50b69d6ade4a/cffi-2.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:716ff8ec22f20b4d988b12884086bcef0fc99737043e503f7a3935a6be99b1ea", size = 184746, upload-time = "2026-07-06T21:33:08.071Z" },
159
+ { url = "https://files.pythonhosted.org/packages/a0/1c/4ed5a0e5bdca6cbc275556de3328dd1b76fd0c11cc13c88fe66d1d8715f2/cffi-2.1.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:63960549e4f8dc41e31accb97b975abaecfc44c03e396c093a6436763c2ea7db", size = 214747, upload-time = "2026-07-06T21:33:09.671Z" },
160
+ { url = "https://files.pythonhosted.org/packages/3a/a6/e879bb68cc23a2bc9ba8f4b7d8019f0c2694bad2ab6c4a3701d429439f58/cffi-2.1.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ff067a8d8d880e7809e4ac88eb009bb848870115317b306666502ccad30b147f", size = 222392, upload-time = "2026-07-06T21:33:10.896Z" },
161
+ { url = "https://files.pythonhosted.org/packages/88/f6/01890cfd63c08f8eb96a8319b0443690197d240a8bd6346048cf7bde9190/cffi-2.1.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:3b926723c13eba9f81d2ef3820d63aeceec3b2d4639906047bf675cb8a7a500d", size = 210285, upload-time = "2026-07-06T21:33:12.251Z" },
162
+ { url = "https://files.pythonhosted.org/packages/a6/cf/2b684132056f438567b61e19d690dd31cd0921ace051e0a458be6074369e/cffi-2.1.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:47ff3a8bfd8cb9da1af7524b965127095055654c177fcfc7578debcb015eecd0", size = 208801, upload-time = "2026-07-06T21:33:13.617Z" },
163
+ { url = "https://files.pythonhosted.org/packages/6f/08/f2e7d62c460faae0926f2d6e423694aa409ced3bc1fe2927a0a6e5f05416/cffi-2.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:799416bae98336e400981ff6e532d67d5c709cfb30afb79865a1315f94b0e224", size = 221808, upload-time = "2026-07-06T21:33:15.466Z" },
164
+ { url = "https://files.pythonhosted.org/packages/38/37/04f54b8e63a02f3d908332c9effbf8c366167c6f733ed8a3d4f79b7e2a1e/cffi-2.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:961be50688f7fba2fa65f63712d3b9b341a22311f5253460ce933f52f0de1c8c", size = 225241, upload-time = "2026-07-06T21:33:16.869Z" },
165
+ { url = "https://files.pythonhosted.org/packages/a9/d6/c72eecca433cd3e681c65ed313ab4835d9d4a379704d0f628a6a05f51c2e/cffi-2.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bf5c6cf48238b0eb4c086978c492ad1cbc22373fc5b2d7353b3a598ce6db887a", size = 223588, upload-time = "2026-07-06T21:33:18.239Z" },
166
+ { url = "https://files.pythonhosted.org/packages/c6/4b/e706f67279140f92939da3475ad610df18bfd52d50f14953a8e5fede71d5/cffi-2.1.0-cp313-cp313-win32.whl", hash = "sha256:db3eb7d46527159a878ec3460e9d40615bc25ba337d477db681aea6e4f05c5d2", size = 175248, upload-time = "2026-07-06T21:33:19.799Z" },
167
+ { url = "https://files.pythonhosted.org/packages/5a/47/59eb7975cb0e4ef0afa764ea945b29a5bb4537a9f771cb7d6c8a5dd74c95/cffi-2.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:8e74a6135550c4748af665b1b1118b6aab33b1fc6a16f9aff630af107c3b4512", size = 185717, upload-time = "2026-07-06T21:33:21.47Z" },
168
+ { url = "https://files.pythonhosted.org/packages/5a/af/34fee85c48f8d94efc8597bc09470c9dd274c145f1c12e0fbc6ab6d38d74/cffi-2.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:2282cd5e38aa8accd03e99d1256af8411c84cdbee6a89d841b563fdbd1f3e50f", size = 180114, upload-time = "2026-07-06T21:33:22.515Z" },
169
+ { url = "https://files.pythonhosted.org/packages/d8/f0/81478e482afa03f6d18dc8f2afb5edc45b3080853b634b5ed91961be0998/cffi-2.1.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:d2117334c3af3bdcb9a88522b844a2bdb5efdc4f71c6c822df55486ae1c3347a", size = 194142, upload-time = "2026-07-06T21:33:23.657Z" },
170
+ { url = "https://files.pythonhosted.org/packages/7d/95/8de304305cd9204974b0ca051b86d307cafca13aa575a0ef1b44d92c0d8c/cffi-2.1.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:702c436735fbe99d59ada02a1f65cfc0d31c0ee8b7290912f8fbc5cd1e4b16c3", size = 196819, upload-time = "2026-07-06T21:33:25.007Z" },
171
+ { url = "https://files.pythonhosted.org/packages/20/71/7c8372d30e42415602ed9f268f7cfd66f1b855fed881ecd168bcb45dbc0b/cffi-2.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:1ff3456eab0d889592d1936d6125bbfbc7ae4d3354a700f8bd80450a66445d4d", size = 184965, upload-time = "2026-07-06T21:33:26.605Z" },
172
+ { url = "https://files.pythonhosted.org/packages/d6/5c/584e626835f0375c928176c04137c96927165cb8733cdb3150ec04e5ee5e/cffi-2.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c4165821e131d6d4ca444347c2b694e2311bcfa3fe5a861cc72968f28867beac", size = 184952, upload-time = "2026-07-06T21:33:27.823Z" },
173
+ { url = "https://files.pythonhosted.org/packages/2e/d2/065fcae1c73979fac8e054462478d0ff8a29c40cdc2ed7ea5676a061df53/cffi-2.1.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:276f20fffd7b396e12516ba8edf9509210ac248cbbc5acbc39cd512f9f59ebe6", size = 222353, upload-time = "2026-07-06T21:33:29.178Z" },
174
+ { url = "https://files.pythonhosted.org/packages/ed/a5/e8bbb1ce5b3ac2f53ad6a10bde44318a5a8d99d4f4a000d44a6e39aeb3e4/cffi-2.1.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:7d5980a3433d4b71a5e120f9dd551403d7824e31e2e67124fe2769c404c06913", size = 210051, upload-time = "2026-07-06T21:33:30.534Z" },
175
+ { url = "https://files.pythonhosted.org/packages/28/ed/c127d3ac36e899c965e3361357c3befacd6578c03f40125183e41c3b219e/cffi-2.1.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:6ca4919c6e4f89aa99c42510b42cf54596892c00b3f9077f6bdd1505e24b9c8d", size = 208630, upload-time = "2026-07-06T21:33:31.753Z" },
176
+ { url = "https://files.pythonhosted.org/packages/cc/d7/97d3136f81db489ec8d1d67748c110d6c994268fd7528014aa9f2b085e4e/cffi-2.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d53d10f7da99ae46f7373b9150393e9c5eab9b224909982b43832668de4779f5", size = 221593, upload-time = "2026-07-06T21:33:33.044Z" },
177
+ { url = "https://files.pythonhosted.org/packages/d3/27/93195977168ee63aed233a1a0993a2178798654d1f4bddcdd321d6fd3b21/cffi-2.1.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c351efb95e832a853a29361675f33a7ce53de1a109cd73fd47af0712213aa4ce", size = 225146, upload-time = "2026-07-06T21:33:34.224Z" },
178
+ { url = "https://files.pythonhosted.org/packages/b3/c1/6dbd291ee2ae5a50a034aa057207081f545923bbf15dad4511e985aafff5/cffi-2.1.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:dbf7c7a88e2bac086f06d14577332760bdeecc42bdec8ac4077f6260557d9326", size = 223240, upload-time = "2026-07-06T21:33:35.57Z" },
179
+ { url = "https://files.pythonhosted.org/packages/0f/6f/ade5ce9863a57992a6ea3d0d10d7e29b8749fc127204b3d493d667b2815f/cffi-2.1.0-cp314-cp314-win32.whl", hash = "sha256:1854b724d00f6654c742097d5387569021be12d3a0f770eae1df8f8acfcc6acd", size = 177723, upload-time = "2026-07-06T21:33:51.626Z" },
180
+ { url = "https://files.pythonhosted.org/packages/41/de/92b9eeed4ae4a21d6fd9b2a2c8505cbed573299902ea73981cc13f7ff62c/cffi-2.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:1b96bfe2c4bd825681b7d311ad6d9b7280a091f43e8f63da5729638083cd3bfb", size = 187937, upload-time = "2026-07-06T21:33:53.403Z" },
181
+ { url = "https://files.pythonhosted.org/packages/2e/1a/cc6ae6c2913a03aab8898eee57963cf1035b8df5872ed8b9115fcc7e2be8/cffi-2.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:7d28dff1db6764108bc30788d85d61c876beff416d9a49cb9dd7c5a9f34f5804", size = 183001, upload-time = "2026-07-06T21:33:54.74Z" },
182
+ { url = "https://files.pythonhosted.org/packages/14/f0/134c00ce0779ec86dea2aa1aac69339c2741a8045072676763512363a2ea/cffi-2.1.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7ea6b3e2c4250ff1de21c630fe72d0f63eb95c2c32ffbf64a358cf4a8836d714", size = 188538, upload-time = "2026-07-06T21:33:36.792Z" },
183
+ { url = "https://files.pythonhosted.org/packages/50/d8/3b86aba791cb610d24e8a3e1b2cd529e71fa15096b04e4d4e360049d4a4c/cffi-2.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6af371f3767faeffc6ac1ef57cdfd25844403e9d3f476c5537caee499de96376", size = 188230, upload-time = "2026-07-06T21:33:38.011Z" },
184
+ { url = "https://files.pythonhosted.org/packages/14/d0/117dcd9209255ad8571fbc8c92ef32593a1d294dcec91ddc4e4db50606f2/cffi-2.1.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:eb4e8997a49aa2c08a3e43c9045d224448b8941d88e7ac163c7d383e560cbf98", size = 223899, upload-time = "2026-07-06T21:33:39.514Z" },
185
+ { url = "https://files.pythonhosted.org/packages/b6/3d/f20f8b886b254e3ad10e15cd4186d3aed49f3e6a35ab37aab9f8f25f7c03/cffi-2.1.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:bf01d8c84cbea96b944c73b22182e6c7c432b3475632b8111dbfdc95ddad6e13", size = 211652, upload-time = "2026-07-06T21:33:40.851Z" },
186
+ { url = "https://files.pythonhosted.org/packages/28/3b/fad54de07260b93ddeef4b96d0131d57ea900675df1d410ae1deee52d7a6/cffi-2.1.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:33eb1ad83ebe8f313e0df035c406227d55a79456704a863fad9842136af5ad7d", size = 210755, upload-time = "2026-07-06T21:33:42.183Z" },
187
+ { url = "https://files.pythonhosted.org/packages/cc/82/3d5c705acb7abbba9bbd7d79b8e62e0f25b6120eb7ae6ac49f1b721722fe/cffi-2.1.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ac0f1a2d0cfa7eea3f2aaf006ab6e70e8feeb16b75d65b7e5939982ca2f11056", size = 223933, upload-time = "2026-07-06T21:33:43.603Z" },
188
+ { url = "https://files.pythonhosted.org/packages/6c/d0/47e338384ab6b1004241002fa616301020cea4fc95f283506565d252f276/cffi-2.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c16914df9fb7f500e440e6875fa23ff5e0b31db01fa9c06af98d59a91f0dc2e4", size = 226749, upload-time = "2026-07-06T21:33:45.046Z" },
189
+ { url = "https://files.pythonhosted.org/packages/70/25/65bd5b58ea4bfdfc15cde02cb5365f89ef8ab8b2adfb8fe5c4bd4233382f/cffi-2.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5ecbd0499275d57506d397eebe1981cee87b47fcd9ef5c22cab7ed7644a39a94", size = 225703, upload-time = "2026-07-06T21:33:46.374Z" },
190
+ { url = "https://files.pythonhosted.org/packages/dc/78/aa01ac599a8a4322533d45a1f9bc93b338276d2d59dabbe7c6d92a775c81/cffi-2.1.0-cp314-cp314t-win32.whl", hash = "sha256:7d034dcffa09e9a46c93fa3a3be402096cb5354ac6e41ab8e5cc9cd8b642ad76", size = 182857, upload-time = "2026-07-06T21:33:47.696Z" },
191
+ { url = "https://files.pythonhosted.org/packages/b9/26/d00496b22de4d4228f32dde94ad996f350c8aad676d63bcca0743c8dea4d/cffi-2.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:0582a58f3051372229ca8e7f5f589f9e5632678208d8636fea3676711fdf7fe5", size = 194065, upload-time = "2026-07-06T21:33:48.953Z" },
192
+ { url = "https://files.pythonhosted.org/packages/d5/dd/0c7dbf815a579ff005008a2d815a55d6bb047c349eef536d9dc53d3f0a8d/cffi-2.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:510aeeeac94811b138077451da1fb18b308a5feab47dd2b603af55804155e1c8", size = 186404, upload-time = "2026-07-06T21:33:50.309Z" },
193
+ { url = "https://files.pythonhosted.org/packages/55/c7/8c8c50cb11c6750051daf12164098a9a6f027ac4356967fd4d800a07f242/cffi-2.1.0-cp315-cp315-ios_13_0_arm64_iphoneos.whl", hash = "sha256:2e9dabb9abcb7ad15938c7196ad5c1718a4e6d33cc79b4c0209bdb64c4a54a5c", size = 194121, upload-time = "2026-07-06T21:33:56.109Z" },
194
+ { url = "https://files.pythonhosted.org/packages/99/e2/67680bf19a6b60d2bb7ff83baefa2a4c3d2d7dc0f3277034b802e1fc504c/cffi-2.1.0-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:37f525a7e7e50c017fdebe58b787be310ad59357ae43a053943a6e1a6c526001", size = 196820, upload-time = "2026-07-06T21:33:57.288Z" },
195
+ { url = "https://files.pythonhosted.org/packages/ed/da/4bbe583a3b3a5c8c60892124fe17f3fa3656523faf0d3484eae90f091853/cffi-2.1.0-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:95f2954c2c9473d892eca6e0409f3568b37ab62a8eedb122461f73cc273476e3", size = 184936, upload-time = "2026-07-06T21:33:58.765Z" },
196
+ { url = "https://files.pythonhosted.org/packages/e5/4b/1f4c36ab273980d7aa75bb126ea4f8971f24a96108acad3a0a084028c57b/cffi-2.1.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:cdf2448aab5f661c9315308ec8b93f4e8a1a67a3c733f8631067a2b67d5913dc", size = 185045, upload-time = "2026-07-06T21:34:00.085Z" },
197
+ { url = "https://files.pythonhosted.org/packages/ef/c3/ad299dc38f3583f8d916b299f028af418a9ec98bc695fcbebeae7420691c/cffi-2.1.0-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:90bec57cf82089383bd06a605b3eb8daebf7e5a668520beaf6e327a83a947699", size = 222342, upload-time = "2026-07-06T21:34:01.814Z" },
198
+ { url = "https://files.pythonhosted.org/packages/eb/d8/df4543cc087245044ed02ef3ad8e0a26619d0075ac7a77a12dc81177851b/cffi-2.1.0-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6274dcb2d15cef48daa73ed1be5a40d501d74dccd0cd6db364776d12cb6ba022", size = 210073, upload-time = "2026-07-06T21:34:03.255Z" },
199
+ { url = "https://files.pythonhosted.org/packages/2c/0e/fac738d73728c6cea2a88a2883dca54892496cbba88a1dc1f2909cb8a6f5/cffi-2.1.0-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:2b71d409cccee78310ab5dec549aed052aaea483346e282c7b02362596e01bb0", size = 208551, upload-time = "2026-07-06T21:34:04.433Z" },
200
+ { url = "https://files.pythonhosted.org/packages/e6/3f/0b04a700dd64f465c93020253a793a82c9b4dff9961f48facd0df945d9b8/cffi-2.1.0-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7d3538f9c0e50670f4deb93dbb696576e60590369cae2faf7de681e597a8a1f1", size = 221649, upload-time = "2026-07-06T21:34:06.157Z" },
201
+ { url = "https://files.pythonhosted.org/packages/5d/7c/b7379a5704c79eda57ce075869ba70a0368d1c850f803b3c0d078d39dcaf/cffi-2.1.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:8f9ec95b8a043d3dfbc74d9abc6f7baf524dd27a8dc160b0a32ff9cdab650c28", size = 225203, upload-time = "2026-07-06T21:34:07.489Z" },
202
+ { url = "https://files.pythonhosted.org/packages/5a/02/d5e6c43ea85c41bda2a184a3418f195fe7cf602967a8d2b94e085b83deef/cffi-2.1.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:af5e2915d41fe6c961694d7bfdc8562942638200f3ce2765dfb8b745cf997629", size = 223263, upload-time = "2026-07-06T21:34:08.712Z" },
203
+ { url = "https://files.pythonhosted.org/packages/2c/d8/772b8259bf75749adffb1c546828978381fb516f60cf701f6c83daf60c85/cffi-2.1.0-cp315-cp315-win32.whl", hash = "sha256:0a42c688d19fca6e095a53c6a6e2295a5b050a8b289f109adab02a9e61a25de6", size = 177696, upload-time = "2026-07-06T21:34:26.355Z" },
204
+ { url = "https://files.pythonhosted.org/packages/2f/dd/afa2191fc6d57fedd26e5844a2fe2fcc0bbfa00961bbaa5a41e4921e7cca/cffi-2.1.0-cp315-cp315-win_amd64.whl", hash = "sha256:bccbbb5ee76a61f9d99b5bf3846a51d7fca4b6a732fe46f89295610edaf41853", size = 187914, upload-time = "2026-07-06T21:34:27.58Z" },
205
+ { url = "https://files.pythonhosted.org/packages/05/ef/6cd4f8c671517162379dc79cfae5aea9106bc38abb89628d5c16adf6a838/cffi-2.1.0-cp315-cp315-win_arm64.whl", hash = "sha256:8d35c139744adb3e727cd51b1a18324bbe44b8bd41bf8322bca4d41289f48eda", size = 183004, upload-time = "2026-07-06T21:34:28.905Z" },
206
+ { url = "https://files.pythonhosted.org/packages/11/b6/12fc55092817a5faa26fb8c40c7f9d662e11a46ee248c137aafc42517d92/cffi-2.1.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:f9912624a0c0b834b7520d7769b3644453aabc0a7e1c839da7359f050750e9bc", size = 188378, upload-time = "2026-07-06T21:34:09.926Z" },
207
+ { url = "https://files.pythonhosted.org/packages/8d/2e/cdac88979f295fde5daa69622c7d2111e56e7ceb94f211357fbe452339e4/cffi-2.1.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:df92f2aba50eb4d96718b68ef76f2e57a57b54f2fa62333496d16c6d585a85ca", size = 188319, upload-time = "2026-07-06T21:34:11.101Z" },
208
+ { url = "https://files.pythonhosted.org/packages/e0/27/1d0b408497e41a74795af122d7b603c418c5fed0171450f899afd04e594f/cffi-2.1.0-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0520e1f4c35f44e209cbbb421b67eec42e6a157f59444dfb6058874ff3610e5d", size = 223904, upload-time = "2026-07-06T21:34:12.606Z" },
209
+ { url = "https://files.pythonhosted.org/packages/8b/31/e115c985105dd7ffb32444505f18ceb874bb42d992af05d5dced7ecf1980/cffi-2.1.0-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:3681e031db29958a7502f5c0c9d6bbc4c36cb20f7b104086fa642d1799631ff8", size = 211554, upload-time = "2026-07-06T21:34:13.987Z" },
210
+ { url = "https://files.pythonhosted.org/packages/5a/67/9e6e09409336d9e515c58367e7cfcf4f89df06ad25252675595a58eb59d5/cffi-2.1.0-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:762f99479dcb369f60ab9017ad4ab97a36a1dd7c1ee5a3b15db0f4b8659120cd", size = 210795, upload-time = "2026-07-06T21:34:15.972Z" },
211
+ { url = "https://files.pythonhosted.org/packages/19/e5/d3cc82a4a0be7902af279c04181ad038449c096734464a5ae1de3e1401bd/cffi-2.1.0-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0611e7ebf90573a535ebdc33ae9da222d037853983e13359f580fab781ca017f", size = 223843, upload-time = "2026-07-06T21:34:17.509Z" },
212
+ { url = "https://files.pythonhosted.org/packages/b9/65/b434abc97ce7cecc2c640fde160507c0ecc7e21544b483ba3325d2e2ea17/cffi-2.1.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:86cf8755a791f72c85dc287128cc62d4f24d392e3f1e15837245623f4a33cccc", size = 226773, upload-time = "2026-07-06T21:34:19.05Z" },
213
+ { url = "https://files.pythonhosted.org/packages/b5/9f/d4dc66ca651eb1145a133314cda721abf13cfac3d28c4a0402263ae6ad75/cffi-2.1.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:ba00f661f8ba35d075c937174e27c2c421cec3942fd2e0ea3e66996757c0fdd9", size = 225719, upload-time = "2026-07-06T21:34:20.576Z" },
214
+ { url = "https://files.pythonhosted.org/packages/68/5a/e536c528bc8057496c360c0978559a2dc45653f89dd6151078aa7d8fca1a/cffi-2.1.0-cp315-cp315t-win32.whl", hash = "sha256:cb96698e3c7413d906ce83f8ffd245ec1bd94707541f299d0ce4d6b0193e982b", size = 182760, upload-time = "2026-07-06T21:34:22.059Z" },
215
+ { url = "https://files.pythonhosted.org/packages/d3/0b/0ffe8b82d3875bced5fa1e7986a7a46b748262a40ab7f60b475eb9fb1bb3/cffi-2.1.0-cp315-cp315t-win_amd64.whl", hash = "sha256:f146d154428a2523f9cc7936c02353c2459b8f6cf07d3cd1ee1c0a611109c5d5", size = 193769, upload-time = "2026-07-06T21:34:23.589Z" },
216
+ { url = "https://files.pythonhosted.org/packages/a0/17/1073b53b68c9b5ca6914adf5f8bf55aacc2d3be102418c90700160ea8605/cffi-2.1.0-cp315-cp315t-win_arm64.whl", hash = "sha256:cbb7640ce37159548d2147b5b8c241f962143d4c71231431820783f4dc78f210", size = 186405, upload-time = "2026-07-06T21:34:24.857Z" },
217
+ ]
218
+
219
+ [[package]]
220
+ name = "pycparser"
221
+ version = "2.23"
222
+ source = { registry = "https://pypi.org/simple" }
223
+ resolution-markers = [
224
+ "python_full_version < '3.10'",
225
+ ]
226
+ sdist = { url = "https://files.pythonhosted.org/packages/fe/cf/d2d3b9f5699fb1e4615c8e32ff220203e43b248e1dfcc6736ad9057731ca/pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2", size = 173734, upload-time = "2025-09-09T13:23:47.91Z" }
227
+ wheels = [
228
+ { url = "https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934", size = 118140, upload-time = "2025-09-09T13:23:46.651Z" },
229
+ ]
230
+
231
+ [[package]]
232
+ name = "pycparser"
233
+ version = "3.0"
234
+ source = { registry = "https://pypi.org/simple" }
235
+ resolution-markers = [
236
+ "python_full_version >= '3.10'",
237
+ ]
238
+ sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" }
239
+ wheels = [
240
+ { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" },
241
+ ]
242
+
243
+ [[package]]
244
+ name = "rockbox-ffi"
245
+ version = "0.1.0"
246
+ source = { editable = "." }
247
+ dependencies = [
248
+ { name = "cffi", version = "2.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
249
+ { name = "cffi", version = "2.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
250
+ ]
251
+
252
+ [package.metadata]
253
+ requires-dist = [{ name = "cffi", specifier = ">=1.16" }]