From 052b052d3f4be33b7f89dbdafe624ef11eb1a760 Mon Sep 17 00:00:00 2001 From: Beckett Frey Date: Tue, 28 Apr 2026 15:18:49 -0500 Subject: [PATCH 01/11] First draft for completion criteria; Interspeech 2026 --- QA_CHECKLIST.md | 75 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 QA_CHECKLIST.md diff --git a/QA_CHECKLIST.md b/QA_CHECKLIST.md new file mode 100644 index 0000000..0e2f84d --- /dev/null +++ b/QA_CHECKLIST.md @@ -0,0 +1,75 @@ +# VoxKit — Interspeech 2026 Release Readiness Checklist + +Defines completeness criteria for VoxKit prior to Interspeech 2026, where Nina and Michael will present using the app. + +--- + +## 1. Dataset Registration + +- [ ] Empty dataset fails with a descriptive popup +- [ ] Valid dataset registers successfully +- [ ] Valid dataset with optional hand alignments registers and ingests alignments +- [ ] Cached dataset is properly ingested +- [ ] `transcribed` flag is verified at registration: + - [ ] Marked transcribed but isn't → fails with descriptive popup + - [ ] Not marked transcribed but is → fails with descriptive popup +- [ ] DeID label works for a valid dataset +- [ ] Invalid folder structures are caught at registration time +- [ ] Folder with valid structure but no audio files (determined by extension) fails registration + +## 2. Dataset Bundle Lifecycle + +Registration ingests a dataset into a VoxKit-compatible bundle. One user registers; others can import the bundle and skip validation/scanning/copying. + +- [ ] Import a registered dataset bundle (skips validation/scanning/copy) +- [ ] Export a dataset bundle +- [ ] Delete a dataset + +## 3. Dataset Analysis + +Analysis captures metadata via a class instantiated at registration time. + +- [ ] Analyze a dataset using 2+ methods +- [ ] Metadata is captured correctly per method + +## 4. Model Registration + +Engines in scope: **W2TG** and **MFA**. Test 2 models per engine. + +- [ ] Register a model (×2 per engine) +- [ ] Import a model (×2 per engine) +- [ ] Export a model (×2 per engine) +- [ ] Delete a model (×2 per engine) + +## 5. Transcription Pipeline + +- [ ] Transcribe audio with each of the 2 engine options +- [ ] Custom settings: notable combinations either function correctly or give clear error feedback +- [ ] Run transcription on every registered dataset from §1 +- [ ] Run transcription on every imported variant from §2 +- [ ] `transcribed = false` → transcription succeeds +- [ ] `transcribed = true` → transcription fails (does not overwrite existing transcript files) + +## 6. Aligner Training + +- [ ] Train a new aligner using hand alignments, per engine +- [ ] Validate different settings combinations (function correctly or give clear error feedback) + +## 7. Alignment Generation + +- [ ] Generate alignments for each dataset variant from import +- [ ] Generate alignments using the newly trained models (edge-case coverage) + +## 8. Alignment Comparison Plots + +- [ ] Generate plots comparing alignment sets of the same dataset +- [ ] All comparison-plot functionality exercised + +## 9. Praat-Style Alignment Viewer + +- [ ] View alignments in Praat-style visualization + +## 10. PLLR Score Plotting + +- [ ] Plot PLLR scores +- [ ] Settings combinations function correctly or provide clear error feedback From 92693b2477f5e204b5772b5cadfaf916bc14cb59 Mon Sep 17 00:00:00 2001 From: Beckett <83560790+BeckettFrey@users.noreply.github.com> Date: Thu, 7 May 2026 13:54:40 -0500 Subject: [PATCH 02/11] QA windows pass (#131) * Adapt logic to pass tests on windows and macos * Configure stdout/stderr to use UTF-8 before any output in main.py * Skip lab file validation when registering an untranscribed dataset * Skip dot-underscore macOS resource fork files during transcription * Resolve conda executable path across platforms for MFA service calls * Pin separate git dependencies to local branches * Switch to editable for local deps * Fix cache default based on dataset cached * Pin forked speechbrain to patch problems * Add lock file updates * Fix purple alternating table rows by overriding system palette alternate-background-color * Separate tasks for build by os, add windows-build * Block terminal from opening during subprocesses in built exe * Add windows voxkit icon for exe * Add transcription verification w.r.t. expectation * Store textgrids in local storage for all datasets and fix w2tg using wrong audio root for non-cached * Add 2 additional dataset analysis methods * Fix and expand dataset validation tests * Fix model import path rewrite on Windows * Fix training using wrong audio path for non-cached datasets * Fix aggregation function resolution for compute_pllr * Remove local task file * Move some dependencies to remote --- .pre-commit-config.yaml | 2 +- QA_CHECKLIST.md | 75 -- assets/voxkit.ico | Bin 0 -> 75553 bytes main.py | 12 + pyproject.toml | 13 +- src/voxkit/analyzers/__init__.py | 23 +- src/voxkit/analyzers/audio_format_profile.py | 84 ++ .../analyzers/clip_duration_statistics.py | 197 +++++ src/voxkit/config/app_config.py | 2 +- src/voxkit/config/pipeline_config.py | 2 +- src/voxkit/engines/faster_whisper_engine.py | 2 +- src/voxkit/engines/mfa_engine.py | 2 +- src/voxkit/engines/w2tg_engine.py | 7 +- .../gui/frameworks/settings_modal/generic.py | 2 +- .../gui/pages/datasets/datasets_page.py | 1 + src/voxkit/gui/pages/pipeline/pllr_stacker.py | 40 +- .../gui/pages/pipeline/training_stacker.py | 15 +- src/voxkit/gui/styles/__init__.py | 1 + src/voxkit/gui/workers/datasets_thread.py | 2 +- src/voxkit/services/mfa.py | 68 +- src/voxkit/storage/alignments.py | 22 +- src/voxkit/storage/datasets.py | 73 +- src/voxkit/storage/models.py | 23 +- src/voxkit/storage/utils.py | 21 +- tasks.py | 18 +- tests/storage/test_datasets.py | 40 +- tests/storage/test_models.py | 45 ++ uv.lock | 715 +++++++++--------- 28 files changed, 970 insertions(+), 537 deletions(-) delete mode 100644 QA_CHECKLIST.md create mode 100644 assets/voxkit.ico create mode 100644 src/voxkit/analyzers/audio_format_profile.py create mode 100644 src/voxkit/analyzers/clip_duration_statistics.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3da7c96..8af580e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,7 +3,7 @@ repos: hooks: - id: shredguard-check name: shredguard check - entry: shredguard check + entry: uv run --only-group dev shredguard check language: system types: [text] diff --git a/QA_CHECKLIST.md b/QA_CHECKLIST.md deleted file mode 100644 index 0e2f84d..0000000 --- a/QA_CHECKLIST.md +++ /dev/null @@ -1,75 +0,0 @@ -# VoxKit — Interspeech 2026 Release Readiness Checklist - -Defines completeness criteria for VoxKit prior to Interspeech 2026, where Nina and Michael will present using the app. - ---- - -## 1. Dataset Registration - -- [ ] Empty dataset fails with a descriptive popup -- [ ] Valid dataset registers successfully -- [ ] Valid dataset with optional hand alignments registers and ingests alignments -- [ ] Cached dataset is properly ingested -- [ ] `transcribed` flag is verified at registration: - - [ ] Marked transcribed but isn't → fails with descriptive popup - - [ ] Not marked transcribed but is → fails with descriptive popup -- [ ] DeID label works for a valid dataset -- [ ] Invalid folder structures are caught at registration time -- [ ] Folder with valid structure but no audio files (determined by extension) fails registration - -## 2. Dataset Bundle Lifecycle - -Registration ingests a dataset into a VoxKit-compatible bundle. One user registers; others can import the bundle and skip validation/scanning/copying. - -- [ ] Import a registered dataset bundle (skips validation/scanning/copy) -- [ ] Export a dataset bundle -- [ ] Delete a dataset - -## 3. Dataset Analysis - -Analysis captures metadata via a class instantiated at registration time. - -- [ ] Analyze a dataset using 2+ methods -- [ ] Metadata is captured correctly per method - -## 4. Model Registration - -Engines in scope: **W2TG** and **MFA**. Test 2 models per engine. - -- [ ] Register a model (×2 per engine) -- [ ] Import a model (×2 per engine) -- [ ] Export a model (×2 per engine) -- [ ] Delete a model (×2 per engine) - -## 5. Transcription Pipeline - -- [ ] Transcribe audio with each of the 2 engine options -- [ ] Custom settings: notable combinations either function correctly or give clear error feedback -- [ ] Run transcription on every registered dataset from §1 -- [ ] Run transcription on every imported variant from §2 -- [ ] `transcribed = false` → transcription succeeds -- [ ] `transcribed = true` → transcription fails (does not overwrite existing transcript files) - -## 6. Aligner Training - -- [ ] Train a new aligner using hand alignments, per engine -- [ ] Validate different settings combinations (function correctly or give clear error feedback) - -## 7. Alignment Generation - -- [ ] Generate alignments for each dataset variant from import -- [ ] Generate alignments using the newly trained models (edge-case coverage) - -## 8. Alignment Comparison Plots - -- [ ] Generate plots comparing alignment sets of the same dataset -- [ ] All comparison-plot functionality exercised - -## 9. Praat-Style Alignment Viewer - -- [ ] View alignments in Praat-style visualization - -## 10. PLLR Score Plotting - -- [ ] Plot PLLR scores -- [ ] Settings combinations function correctly or provide clear error feedback diff --git a/assets/voxkit.ico b/assets/voxkit.ico new file mode 100644 index 0000000000000000000000000000000000000000..274be3038df9d90b356d18edad174ac5c20336a4 GIT binary patch literal 75553 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≻nIt=|FCHPfteynxL5$Od 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%0A65u;+PpztJhHiSxG>WqA=yUAwmcLHG3;I4o87A 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 literal 0 HcmV?d00001 diff --git a/main.py b/main.py index fa00987..e90e82f 100644 --- a/main.py +++ b/main.py @@ -4,6 +4,18 @@ import os import multiprocessing +# Windows: configure console and stdout/stderr for UTF-8 before any output. +# Without this, rich's legacy renderer falls back to cp1252 and chokes on +# Unicode characters (e.g., circled letters in pipeline config YAML). +if sys.platform == 'win32': + try: + import ctypes + ctypes.windll.kernel32.SetConsoleOutputCP(65001) + sys.stdout.reconfigure(encoding='utf-8', errors='replace') + sys.stderr.reconfigure(encoding='utf-8', errors='replace') + except Exception: + pass + # Apply patches for frozen (PyInstaller) environment BEFORE other imports if getattr(sys, 'frozen', False): import _frozen_patch diff --git a/pyproject.toml b/pyproject.toml index d65a0eb..7a924e4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,8 +16,8 @@ dependencies = [ "pyqt6>=6.9.1", "torch==2.8.0", "torchaudio==2.8.0", - "pypllrcomputer @ git+https://github.com/BrainBehaviorAnalyticsLab/PyPLLRComputer.git@48a0c934e75f73235ba5a002538b8588fd6697e7", - "wav2textgrid @ git+https://github.com/pkadambi/Wav2TextGrid.git@3862476185a7558d481f1ae1c9cbca2e48c825a0", + "pypllrcomputer", + "wav2textgrid", "datasets>=4.3.0", "accelerate>=1.11.0", "keyring>=25.6.0", @@ -28,7 +28,8 @@ dependencies = [ "pyyaml>=6.0.0", "rich>=14.2.0", "faster-whisper>=1.1.0", - "alignment-comparison-plots==0.1.1", + "alignment-comparison-plots", + "speechbrain>=1.0.3", ] [dependency-groups] @@ -58,6 +59,12 @@ Homepage = "https://github.com/BrainBehaviorAnalyticsLab/voxkit-desktop" Repository = "https://github.com/BrainBehaviorAnalyticsLab/voxkit-desktop" Issues = "https://github.com/BrainBehaviorAnalyticsLab/voxkit-desktop/issues" +[tool.uv.sources] +pypllrcomputer = { git = "https://github.com/pkadambi/PyPhonemePronunciationScorer", branch = "voxkit-windows-variant" } +wav2textgrid = { git = "https://github.com/pkadambi/Wav2TextGrid", branch = "voxkit-windows-variant" } +alignment-comparison-plots = { git = "https://github.com/WISCLab/alignment-comparison-plots", branch = "voxkit-windows-variant" } +speechbrain = { git = "https://github.com/BeckettFrey/speechbrain.git", branch = "fix/windows-lazy-import-inspect-path" } + [tool.ruff] line-length = 100 target-version = "py310" diff --git a/src/voxkit/analyzers/__init__.py b/src/voxkit/analyzers/__init__.py index 4f29df5..43bcd01 100644 --- a/src/voxkit/analyzers/__init__.py +++ b/src/voxkit/analyzers/__init__.py @@ -14,6 +14,14 @@ Extracts speaker count and audio file counts per speaker directory. Includes a bar chart visualization for quick dataset overview. +**ClipDurationStatisticsAnalyzer** + Reads audio metadata to compute total, average, min, and max clip duration + per speaker. Includes a bar chart visualization of total duration. + +**AudioFormatProfileAnalyzer** + Reads audio metadata to surface dominant sample rate, channel count, and + flags files that deviate from the speaker's dominant format. + Output Structure ---------------- Analyzer output is stored alongside dataset metadata:: @@ -35,7 +43,9 @@ from typing import List +from .audio_format_profile import AudioFormatProfileAnalyzer from .base import DatasetAnalyzer +from .clip_duration_statistics import ClipDurationStatisticsAnalyzer from .default_analyzer import DefaultAnalyzer @@ -69,9 +79,18 @@ def get_analyzer(self, analyzer_id: str) -> DatasetAnalyzer: raise ValueError(f"No analyzer with id: {analyzer_id}") -default_analyzer_instance = DefaultAnalyzer() +_default = DefaultAnalyzer() +_duration = ClipDurationStatisticsAnalyzer() +_format = AudioFormatProfileAnalyzer() + # Singleton instance for unified export/interface -ManageAnalyzers = AnalyzerManager({default_analyzer_instance.name: default_analyzer_instance}) +ManageAnalyzers = AnalyzerManager( + { + _default.name: _default, + _duration.name: _duration, + _format.name: _format, + } +) __all__ = [ "ManageAnalyzers", diff --git a/src/voxkit/analyzers/audio_format_profile.py b/src/voxkit/analyzers/audio_format_profile.py new file mode 100644 index 0000000..4844d81 --- /dev/null +++ b/src/voxkit/analyzers/audio_format_profile.py @@ -0,0 +1,84 @@ +"""Audio Format Profile Analyzer. + +Reads audio metadata (no decode) to surface sample rate and channel count +per speaker. Flags speakers with inconsistent formats across their files. + +Output Columns +-------------- +- **speaker_id**: Name of the speaker subdirectory +- **file_count**: Number of audio files scanned +- **dominant_sample_rate_hz**: Most common sample rate across the speaker's files +- **dominant_channels**: Most common channel count (1 = mono, 2 = stereo) +- **inconsistent_files**: Number of files that differ from the dominant sample rate +""" + +import logging +import os +from collections import Counter +from pathlib import Path +from typing import Any, Dict, List + +from .base import DatasetAnalyzer + +logger = logging.getLogger(__name__) + + +class AudioFormatProfileAnalyzer(DatasetAnalyzer): + """Per-speaker audio format profile: sample rate, channels, and consistency.""" + + @property + def name(self) -> str: + return "Audio Format Profile" + + @property + def description(self) -> str: + return "Sample rate, channel count, and format consistency per speaker" + + def analyze(self, dataset_path: str) -> List[Dict[str, Any]]: + import torchaudio + + results = [] + audio_extensions = {".wav", ".flac", ".mp3", ".ogg", ".m4a"} + + try: + for entry in os.scandir(dataset_path): + if not entry.is_dir(): + continue + + sample_rates: List[int] = [] + channels: List[int] = [] + for f in os.scandir(entry.path): + if not f.is_file() or Path(f.name).suffix.lower() not in audio_extensions: + continue + try: + info = torchaudio.info(f.path) + if info.sample_rate > 0: + sample_rates.append(info.sample_rate) + channels.append(info.num_channels) + else: + waveform, sr = torchaudio.load(f.path) + sample_rates.append(sr) + channels.append(waveform.shape[0]) + except Exception as e: + logger.warning("Skipping %s: %s", f.path, e) + + if not sample_rates: + continue + + dominant_sr = Counter(sample_rates).most_common(1)[0][0] + dominant_ch = Counter(channels).most_common(1)[0][0] + inconsistent = sum(1 for sr in sample_rates if sr != dominant_sr) + + results.append( + { + "speaker_id": entry.name, + "file_count": len(sample_rates), + "dominant_sample_rate_hz": dominant_sr, + "dominant_channels": dominant_ch, + "inconsistent_files": inconsistent, + } + ) + except Exception as e: + print(f"Error analyzing dataset: {e}") + + return results diff --git a/src/voxkit/analyzers/clip_duration_statistics.py b/src/voxkit/analyzers/clip_duration_statistics.py new file mode 100644 index 0000000..5f8d259 --- /dev/null +++ b/src/voxkit/analyzers/clip_duration_statistics.py @@ -0,0 +1,197 @@ +"""Clip Duration Statistics Analyzer. + +Reads audio metadata (no decode) to compute per-speaker clip duration stats. + +Output Columns +-------------- +- **speaker_id**: Name of the speaker subdirectory +- **file_count**: Number of audio files scanned +- **total_duration_s**: Total audio duration in seconds +- **avg_duration_s**: Average clip duration in seconds +- **min_duration_s**: Shortest clip in seconds +- **max_duration_s**: Longest clip in seconds +""" + +import logging +import os +from pathlib import Path +from typing import Any, Dict, List + +from .base import DatasetAnalyzer + +logger = logging.getLogger(__name__) + + +class ClipDurationStatisticsAnalyzer(DatasetAnalyzer): + """Per-speaker clip duration statistics derived from audio file metadata.""" + + @property + def name(self) -> str: + return "Clip Duration Statistics" + + @property + def description(self) -> str: + return "Total, average, min, and max clip duration per speaker" + + def analyze(self, dataset_path: str) -> List[Dict[str, Any]]: + import torchaudio + + results = [] + audio_extensions = {".wav", ".flac", ".mp3", ".ogg", ".m4a"} + + try: + for entry in os.scandir(dataset_path): + if not entry.is_dir(): + continue + + durations: List[float] = [] + for f in os.scandir(entry.path): + if not f.is_file() or Path(f.name).suffix.lower() not in audio_extensions: + continue + try: + info = torchaudio.info(f.path) + if info.num_frames > 0: + durations.append(info.num_frames / info.sample_rate) + else: + # num_frames is unreliable for some formats (MP3, M4A, OGG) + waveform, sr = torchaudio.load(f.path) + durations.append(waveform.shape[1] / sr) + except Exception as e: + logger.warning("Skipping %s: %s", f.path, e) + + if durations: + total = sum(durations) + results.append( + { + "speaker_id": entry.name, + "file_count": len(durations), + "total_duration_s": round(total, 2), + "avg_duration_s": round(total / len(durations), 2), + "min_duration_s": round(min(durations), 2), + "max_duration_s": round(max(durations), 2), + } + ) + except Exception as e: + print(f"Error analyzing dataset: {e}") + + return results + + def visualize(self, data: List[Dict[str, Any]]): + from PyQt6.QtCore import QRectF, Qt + from PyQt6.QtGui import QColor, QFont, QPainter + from PyQt6.QtWidgets import QLabel, QScrollArea, QVBoxLayout, QWidget + + entries = [] + for row in data: + speaker = str(row.get("speaker_id", "")) + try: + total = float(row.get("total_duration_s", 0)) + avg = float(row.get("avg_duration_s", 0)) + file_count = int(row.get("file_count", 0)) + except (TypeError, ValueError): + total, avg, file_count = 0.0, 0.0, 0 + entries.append((speaker, total, avg, file_count)) + entries.sort(key=lambda x: x[1], reverse=True) + + if not entries: + empty = QLabel("No data to visualize.") + empty.setAlignment(Qt.AlignmentFlag.AlignCenter) + return empty + + BAR_HEIGHT = 28 + BAR_SPACING = 6 + LABEL_WIDTH = 140 + PADDING = 16 + max_total = max(e[1] for e in entries) or 1 + VALUE_WIDTH = 120 + total_h = PADDING + len(entries) * (BAR_HEIGHT + BAR_SPACING) + PADDING + + class _Canvas(QWidget): + def __init__(self, parent=None): + super().__init__(parent) + self.setMinimumHeight(max(total_h, 100)) + self.setMinimumWidth(400) + + def paintEvent(self, event): + painter = QPainter(self) + painter.setRenderHint(QPainter.RenderHint.Antialiasing) + bar_area = self.width() - LABEL_WIDTH - PADDING * 2 - VALUE_WIDTH + y = PADDING + + for speaker, total, avg, file_count in entries: + painter.setPen(QColor("#2c3e50")) + label_font = QFont() + label_font.setPointSize(10) + painter.setFont(label_font) + label_rect = QRectF(PADDING, y, LABEL_WIDTH, BAR_HEIGHT) + fm = painter.fontMetrics() + elided = fm.elidedText( + speaker, Qt.TextElideMode.ElideRight, int(LABEL_WIDTH - 8) + ) + painter.drawText( + label_rect, + Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter, + elided, + ) + + bar_x = PADDING + LABEL_WIDTH + 10 + bar_w = max((total / max_total) * bar_area, 2) + bar_rect = QRectF(bar_x, y + 3, bar_w, BAR_HEIGHT - 6) + + color = QColor("#27ae60") + h, s, lightness, a = color.getHsl() + assert h is not None and s is not None and lightness is not None + ratio = total / max_total + new_l = int(lightness + (220 - lightness) * (1 - ratio)) + color.setHsl(h, s, min(new_l, 240), a) + + painter.setBrush(color) + painter.setPen(Qt.PenStyle.NoPen) + painter.drawRoundedRect(bar_rect, 4, 4) + + val_font = QFont() + val_font.setPointSize(9) + val_font.setBold(True) + painter.setFont(val_font) + painter.setPen(QColor("#2c3e50")) + minutes, seconds = divmod(total, 60) + label = f"{int(minutes)}m {seconds:.0f}s ({file_count} files)" + val_rect = QRectF(bar_x + bar_w + 6, y, VALUE_WIDTH, BAR_HEIGHT) + painter.drawText( + val_rect, + Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter, + label, + ) + + y += BAR_HEIGHT + BAR_SPACING + + painter.end() + + container = QWidget() + layout = QVBoxLayout(container) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(8) + + canvas = _Canvas() + scroll = QScrollArea() + scroll.setWidget(canvas) + scroll.setWidgetResizable(True) + scroll.setStyleSheet("QScrollArea { border: none; background: white; }") + layout.addWidget(scroll) + + grand_total = sum(e[1] for e in entries) + total_files = sum(e[3] for e in entries) + n_speakers = len(entries) + overall_avg = grand_total / n_speakers if n_speakers else 0 + gt_min, gt_sec = divmod(grand_total, 60) + stats = QLabel( + f"{n_speakers} speakers | " + f"{total_files} files | " + f"Total: {int(gt_min)}m {gt_sec:.0f}s | " + f"Avg per speaker: {overall_avg:.1f}s" + ) + stats.setStyleSheet("color: #7f8c8d; font-size: 12px; font-style: italic; padding: 8px;") + stats.setAlignment(Qt.AlignmentFlag.AlignCenter) + layout.addWidget(stats) + + return container diff --git a/src/voxkit/config/app_config.py b/src/voxkit/config/app_config.py index 300169f..83d6328 100644 --- a/src/voxkit/config/app_config.py +++ b/src/voxkit/config/app_config.py @@ -151,7 +151,7 @@ def from_yaml(cls, config_path: Path) -> "AppConfig": if not config_path.exists(): raise FileNotFoundError(f"App config file not found: {config_path}") - with open(config_path, "r") as f: + with open(config_path, "r", encoding="utf-8") as f: data = yaml.safe_load(f) return cls( diff --git a/src/voxkit/config/pipeline_config.py b/src/voxkit/config/pipeline_config.py index 12e3f4f..760bfc6 100644 --- a/src/voxkit/config/pipeline_config.py +++ b/src/voxkit/config/pipeline_config.py @@ -119,7 +119,7 @@ def from_yaml(cls, config_path: Path) -> "PipelineConfig": if not config_path.exists(): raise FileNotFoundError(f"Pipeline config file not found: {config_path}") - with open(config_path, "r") as f: + with open(config_path, "r", encoding="utf-8") as f: data = yaml.safe_load(f) # Parse pipeline steps diff --git a/src/voxkit/engines/faster_whisper_engine.py b/src/voxkit/engines/faster_whisper_engine.py index b67281a..f4e0005 100644 --- a/src/voxkit/engines/faster_whisper_engine.py +++ b/src/voxkit/engines/faster_whisper_engine.py @@ -134,7 +134,7 @@ def transcribe(self, dataset_id: str) -> None: if not speaker_dir.is_dir() or speaker_dir.name.startswith("."): continue - wav_files = sorted(speaker_dir.glob("*.wav")) + wav_files = sorted(f for f in speaker_dir.glob("*.wav") if not f.name.startswith(".")) for wav_path in wav_files: lab_path = wav_path.with_suffix(".lab") diff --git a/src/voxkit/engines/mfa_engine.py b/src/voxkit/engines/mfa_engine.py index 3035779..61c3438 100644 --- a/src/voxkit/engines/mfa_engine.py +++ b/src/voxkit/engines/mfa_engine.py @@ -204,7 +204,7 @@ def train_aligner( for key in serializable: if isinstance(serializable[key], Path): serializable[key] = str(serializable[key]) - with open(model_metadata_path, "w") as f: + with open(model_metadata_path, "w", encoding="utf-8") as f: import json json.dump(serializable, f, indent=4) diff --git a/src/voxkit/engines/w2tg_engine.py b/src/voxkit/engines/w2tg_engine.py index 8134edb..51d2d24 100644 --- a/src/voxkit/engines/w2tg_engine.py +++ b/src/voxkit/engines/w2tg_engine.py @@ -144,6 +144,8 @@ def align(self, dataset_id: str, model_id: str) -> None: assert not isinstance(msg, str) alignment_meta = msg dataset_meta = datasets.get_dataset_metadata(dataset_id) + if dataset_meta is None: + raise ValueError(f"Dataset '{dataset_id}' not found.") model_meta = models.get_model_metadata(self.id, model_id) print(f"Aligning with settings: {settings}") @@ -153,7 +155,10 @@ def align(self, dataset_id: str, model_id: str) -> None: model_path = model_meta["model_path"] print(f"Using model path: {model_path}") - audio_root = datasets._get_dataset_root(dataset_id) + if dataset_meta["cached"]: + audio_root = datasets._get_dataset_root(dataset_id) + else: + audio_root = Path(dataset_meta["original_path"]) try: align_dirs( diff --git a/src/voxkit/gui/frameworks/settings_modal/generic.py b/src/voxkit/gui/frameworks/settings_modal/generic.py index d8a65bc..f65b662 100644 --- a/src/voxkit/gui/frameworks/settings_modal/generic.py +++ b/src/voxkit/gui/frameworks/settings_modal/generic.py @@ -157,7 +157,7 @@ def _load_saved_values(self): print("Saved values json doesn't exist yet.") return try: - with open(self.store_values_path, "r") as f: + with open(self.store_values_path, "r", encoding="utf-8") as f: saved_values = json.load(f) for name, value in saved_values.items(): print(f"Loading saved value for {name}: {value}") diff --git a/src/voxkit/gui/pages/datasets/datasets_page.py b/src/voxkit/gui/pages/datasets/datasets_page.py index a45c841..2957fdd 100644 --- a/src/voxkit/gui/pages/datasets/datasets_page.py +++ b/src/voxkit/gui/pages/datasets/datasets_page.py @@ -306,6 +306,7 @@ def _create_list_section(self): QTableWidget { gridline-color: #ecf0f1; background-color: white; + alternate-background-color: #f5f8fa; border: 1px solid #bdc3c7; border-radius: 4px; } diff --git a/src/voxkit/gui/pages/pipeline/pllr_stacker.py b/src/voxkit/gui/pages/pipeline/pllr_stacker.py index 27d2642..65c0839 100644 --- a/src/voxkit/gui/pages/pipeline/pllr_stacker.py +++ b/src/voxkit/gui/pages/pipeline/pllr_stacker.py @@ -80,8 +80,11 @@ default_value="aggregate_by_phoneme_occurrence", options=[ "aggregate_by_phoneme_occurrence", - "aggregate_by_phoneme_average", - "aggregate_by_phoneme_median", + "aggregate_by_unique_phonemes_in_utterance", + "aggregate_by_utterance", + "aggregate_by_phoneme_per_speaker", + "aggregate_by_type_per_speaker", + "aggregate_by_speaker", ], tooltip="Method to aggregate framewise probabilities into phonewise scores.", ), @@ -358,7 +361,10 @@ def on_extract_pllr(self): ) return - textgrid_path = alignment_data["tg_path"] + "/cache" + is_cached = alignment_data["local"] == "True" or alignment_data["local"] is True + textgrid_path = ( + alignment_data["tg_path"] + "/cache" if is_cached else alignment_data["tg_path"] + ) print(f"[DEBUG] TextGrid path from alignment: {textgrid_path}") print("[DEBUG] Checking if TextGrid path exists...") @@ -486,7 +492,7 @@ def extract_pllr_logic(self, textgrid_path, wavlab_path, output_path): print(f"[LOGIC] Settings file found at: {path_to_pllr_settings}") from json import load as json_load - with open(path_to_pllr_settings, "r") as f: + with open(path_to_pllr_settings, "r", encoding="utf-8") as f: pllr_settings = json_load(f) print(f"[LOGIC] Settings loaded: {pllr_settings}") else: @@ -505,6 +511,28 @@ def extract_pllr_logic(self, textgrid_path, wavlab_path, output_path): print(" likelihood_dct=None") try: + from pypllrcomputer.pypllr_compute import ( + aggregate_by_phoneme_occurrence, + aggregate_by_phoneme_per_speaker, + aggregate_by_speaker, + aggregate_by_type_per_speaker, + aggregate_by_unique_phonemes_in_utterance, + aggregate_by_utterance, + ) + + _agg_fns = { + "aggregate_by_phoneme_occurrence": aggregate_by_phoneme_occurrence, + "aggregate_by_unique_phonemes_in_utterance": ( + aggregate_by_unique_phonemes_in_utterance + ), + "aggregate_by_utterance": aggregate_by_utterance, + "aggregate_by_phoneme_per_speaker": aggregate_by_phoneme_per_speaker, + "aggregate_by_type_per_speaker": aggregate_by_type_per_speaker, + "aggregate_by_speaker": aggregate_by_speaker, + } + agg_key = pllr_settings.get("aggregation_function", "aggregate_by_phoneme_occurrence") + agg_fn = _agg_fns.get(agg_key, aggregate_by_phoneme_occurrence) + compute_pllr( tg_files_path=textgrid_path, wav_files_path=wavlab_path, @@ -513,9 +541,7 @@ def extract_pllr_logic(self, textgrid_path, wavlab_path, output_path): framewise_proba_df=framewise_path, recompute_probas=pllr_settings.get("recompute_probas", True), likelihood_dct=pllr_settings.get("likelihood_dct", None), - # aggregation_function=pllr_settings.get( - # "aggregation_function", "aggregate_by_phoneme_occurrence" - # ), + aggregation_function=agg_fn, ) print("[LOGIC] compute_pllr() completed successfully") return "PLLR extracted successfully" diff --git a/src/voxkit/gui/pages/pipeline/training_stacker.py b/src/voxkit/gui/pages/pipeline/training_stacker.py index de2bb30..a4817d9 100644 --- a/src/voxkit/gui/pages/pipeline/training_stacker.py +++ b/src/voxkit/gui/pages/pipeline/training_stacker.py @@ -136,8 +136,19 @@ def on_train_model(self): return # Get dataset path - audio_path = datasets._get_dataset_root(selected_dataset_id) - if not audio_path: + dataset_metadata = datasets.get_dataset_metadata(selected_dataset_id) + if not dataset_metadata: + QMessageBox.warning( + self, "Invalid Dataset", f"Could not find path for dataset '{selected_dataset_id}'." + ) + return + + if bool(dataset_metadata["cached"]): + audio_path = datasets._get_dataset_root(selected_dataset_id) + else: + audio_path = Path(dataset_metadata["original_path"]) + + if not audio_path or not Path(audio_path).exists(): QMessageBox.warning( self, "Invalid Dataset", f"Could not find path for dataset '{selected_dataset_id}'." ) diff --git a/src/voxkit/gui/styles/__init__.py b/src/voxkit/gui/styles/__init__.py index b0abc8c..4db9929 100644 --- a/src/voxkit/gui/styles/__init__.py +++ b/src/voxkit/gui/styles/__init__.py @@ -665,6 +665,7 @@ class Containers: QTableWidget { gridline-color: #ecf0f1; background-color: white; + alternate-background-color: #f5f8fa; border: 1px solid #bdc3c7; border-radius: 4px; } diff --git a/src/voxkit/gui/workers/datasets_thread.py b/src/voxkit/gui/workers/datasets_thread.py index ee269af..81e9ca6 100644 --- a/src/voxkit/gui/workers/datasets_thread.py +++ b/src/voxkit/gui/workers/datasets_thread.py @@ -52,7 +52,7 @@ def run(self): self.progress.emit("Validating dataset structure...") # First validate the dataset - valid, msg = datasets.validate_dataset(self.dataset_path) + valid, msg = datasets.validate_dataset(self.dataset_path, transcribed=self.transcribed) if not valid: self.finished.emit( diff --git a/src/voxkit/services/mfa.py b/src/voxkit/services/mfa.py index ad74af0..dde029d 100755 --- a/src/voxkit/services/mfa.py +++ b/src/voxkit/services/mfa.py @@ -1,4 +1,51 @@ +import os +import shutil import subprocess +import sys +from pathlib import Path + + +def _no_window() -> dict: + """Return creationflags to suppress console windows on Windows.""" + if sys.platform == "win32": + return {"creationflags": subprocess.CREATE_NO_WINDOW} + return {} + + +def _find_conda() -> str: + """Return the conda executable path, checking common install locations on Windows.""" + # Fast path: conda is already on PATH + if shutil.which("conda"): + return "conda" + + if sys.platform == "win32": + home = Path.home() + candidates = [ + home / "miniconda3" / "Scripts" / "conda.exe", + home / "anaconda3" / "Scripts" / "conda.exe", + home / "miniforge3" / "Scripts" / "conda.exe", + home / "mambaforge" / "Scripts" / "conda.exe", + home / "Miniconda3" / "Scripts" / "conda.exe", + home / "Anaconda3" / "Scripts" / "conda.exe", + Path("C:/ProgramData/miniconda3/Scripts/conda.exe"), + Path("C:/ProgramData/anaconda3/Scripts/conda.exe"), + Path("C:/tools/miniconda3/Scripts/conda.exe"), + ] + # Also check CONDA_PREFIX env var (set when a conda env is active) + conda_prefix = os.environ.get("CONDA_PREFIX") or os.environ.get("CONDA_EXE", "") + if conda_prefix: + candidates.insert(0, Path(conda_prefix).parent / "conda.exe") + candidates.insert(0, Path(conda_prefix)) + + for path in candidates: + if path.exists(): + return str(path) + + raise FileNotFoundError( + "conda not found. Install Miniconda from https://docs.conda.io/en/latest/miniconda.html " + "and create the aligner environment with: " + "conda create -n aligner -c conda-forge montreal-forced-aligner" + ) def ensure_dictionary_downloaded(dictionary_name: str = "english_us_arpa") -> None: @@ -10,8 +57,9 @@ def ensure_dictionary_downloaded(dictionary_name: str = "english_us_arpa") -> No Raises: AssertionError: If dictionary download fails and dictionary is not available. """ + conda = _find_conda() download_cmd = [ - "conda", + conda, "run", "-n", "aligner", @@ -23,14 +71,14 @@ def ensure_dictionary_downloaded(dictionary_name: str = "english_us_arpa") -> No ] print(f"[mfa] Ensuring dictionary '{dictionary_name}' is downloaded...") - result = subprocess.run(download_cmd, capture_output=True, text=True) + result = subprocess.run(download_cmd, capture_output=True, text=True, **_no_window()) # Check if dictionary is available (either just downloaded or already present) # MFA returns success if already downloaded, or downloads successfully if result.returncode != 0: # Try to list dictionaries to check if it's already available list_cmd = [ - "conda", + conda, "run", "-n", "aligner", @@ -39,7 +87,7 @@ def ensure_dictionary_downloaded(dictionary_name: str = "english_us_arpa") -> No "list", "dictionary", ] - list_result = subprocess.run(list_cmd, capture_output=True, text=True) + list_result = subprocess.run(list_cmd, capture_output=True, text=True, **_no_window()) assert dictionary_name in list_result.stdout, ( f"Dictionary '{dictionary_name}' is not available. " f"Download failed with: {result.stderr}" @@ -69,8 +117,9 @@ def run_mfa_align( # Ensure dictionary is downloaded ensure_dictionary_downloaded(dictionary_name) + conda = _find_conda() cmd = [ - "conda", + conda, "run", "-n", "aligner", @@ -89,7 +138,7 @@ def run_mfa_align( try: print(f"[mfa.run_mfa_align] Running MFA align with command: {' '.join(cmd)}") - subprocess.run(cmd, check=True) + subprocess.run(cmd, check=True, **_no_window()) print("[mfa.run_mfa_align] MFA alignment completed successfully.") except subprocess.CalledProcessError as e: print(f"[mfa.run_mfa_align] MFA alignment failed with error: {e}") @@ -120,8 +169,9 @@ def run_mfa_adapt( # Ensure dictionary is downloaded ensure_dictionary_downloaded(dictionary_name) + conda = _find_conda() cmd = [ - "conda", + conda, "run", "-n", "aligner", @@ -138,7 +188,7 @@ def run_mfa_adapt( try: print(f"[mfa.run_mfa_adapt] Running MFA adapt with command: {' '.join(cmd)}") - subprocess.run(cmd, check=True, capture_output=True, text=True) + subprocess.run(cmd, check=True, capture_output=True, text=True, **_no_window()) print("[mfa.run_mfa_adapt] MFA adaptation completed successfully.") except subprocess.CalledProcessError as e: print(f"[mfa.run_mfa_adapt] MFA adaptation failed with error: {e}") @@ -158,7 +208,7 @@ def download_acoustic_model(release_path, output_file): cmd = ["curl", "-L", "-o", output_file, url] try: print(f"[mfa.download_acoustic_model] Downloading model from {url} to {output_file}") - subprocess.run(cmd, check=True) + subprocess.run(cmd, check=True, **_no_window()) print("[mfa.download_acoustic_model] Model downloaded successfully.") except subprocess.CalledProcessError as e: print(f"[mfa.download_acoustic_model] Model download failed with error: {e}") diff --git a/src/voxkit/storage/alignments.py b/src/voxkit/storage/alignments.py index 6ca093e..618ca68 100644 --- a/src/voxkit/storage/alignments.py +++ b/src/voxkit/storage/alignments.py @@ -163,15 +163,9 @@ def create_alignment( alignment_root.mkdir(parents=False, exist_ok=False) try: - tg_path = None local = dataset_metadata["cached"] - if bool(local) is False: - tg_path = Path(dataset_metadata["original_path"]) / "textgrids" - tg_path.mkdir(parents=False, exist_ok=True) - - else: - tg_path = alignment_root / "textgrids" - tg_path.mkdir(parents=False, exist_ok=True) + tg_path = alignment_root / "textgrids" + tg_path.mkdir(parents=False, exist_ok=True) metadata = AlignmentMetadata( id=now, @@ -186,7 +180,7 @@ def create_alignment( # Fetch model metadata metadata_path = alignment_root / "voxkit_alignment.json" - with open(metadata_path, "w") as f: + with open(metadata_path, "w", encoding="utf-8") as f: json.dump(metadata, f, indent=4) return True, metadata @@ -322,7 +316,7 @@ def create_hand_alignment( ) metadata_path = alignment_root / "voxkit_alignment.json" - with open(metadata_path, "w") as f: + with open(metadata_path, "w", encoding="utf-8") as f: json.dump(metadata, f, indent=4) return True, metadata @@ -357,7 +351,7 @@ def get_alignment_metadata(dataset_id: str, alignment_id: str) -> AlignmentMetad metadata_path = alignment_root / "voxkit_alignment.json" try: - with open(metadata_path, "r") as f: + with open(metadata_path, "r", encoding="utf-8") as f: metadata: AlignmentMetadata = json.load(f) # Normalize status to lowercase for consistency if "status" in metadata: @@ -399,7 +393,7 @@ def update_alignment(dataset_id: str, alignment_id: str, updates: dict) -> Tuple metadata_path = alignment_root / "voxkit_alignment.json" try: - with open(metadata_path, "r") as f: + with open(metadata_path, "r", encoding="utf-8") as f: metadata = json.load(f) # Update fields @@ -410,7 +404,7 @@ def update_alignment(dataset_id: str, alignment_id: str, updates: dict) -> Tuple value = value.lower() metadata[key] = value - with open(metadata_path, "w") as f: + with open(metadata_path, "w", encoding="utf-8") as f: json.dump(metadata, f, indent=4) return True, "Alignment metadata updated successfully." @@ -447,7 +441,7 @@ def list_alignments(dataset_id: str) -> List[AlignmentMetadata]: metadata_path = dir / "voxkit_alignment.json" if metadata_path.exists(): try: - with open(metadata_path, "r") as f: + with open(metadata_path, "r", encoding="utf-8") as f: metadata = json.load(f) # Normalize status to lowercase for consistency if "status" in metadata: diff --git a/src/voxkit/storage/datasets.py b/src/voxkit/storage/datasets.py index 1e9d2bc..6180cda 100644 --- a/src/voxkit/storage/datasets.py +++ b/src/voxkit/storage/datasets.py @@ -114,7 +114,7 @@ def _get_dataset_metadata(dataset_root: Path) -> DatasetMetadata | None: metadata_path = dataset_root / "voxkit_dataset.json" if not metadata_path.exists(): return None - with open(metadata_path, "r") as f: + with open(metadata_path, "r", encoding="utf-8") as f: result: DatasetMetadata = json.load(f) return result except Exception: @@ -163,7 +163,7 @@ def create_dataset( - If analysis_data is provided, saves to {analysis_method}_summary.csv """ # Validate dataset structure - valid, msg = validate_dataset(Path(original_path)) + valid, msg = validate_dataset(Path(original_path), transcribed=transcribed) if not valid: return False, msg @@ -193,7 +193,7 @@ def create_dataset( alignments_dir = dataset_dir / ALIGNMENTS_ROOT alignments_dir.mkdir(parents=False, exist_ok=False) metadata_path = dataset_dir / "voxkit_dataset.json" - with open(metadata_path, "w") as f: + with open(metadata_path, "w", encoding="utf-8") as f: json.dump(metadata, f, indent=2) # Cache the dataset if requested @@ -297,7 +297,7 @@ def list_datasets_metadata() -> List[DatasetMetadata]: if entry.is_dir(): metadata_path = os.path.join(entry.path, "voxkit_dataset.json") if os.path.exists(metadata_path): - with open(metadata_path, "r") as f: + with open(metadata_path, "r", encoding="utf-8") as f: metadata = json.load(f) datasets.append(metadata) return datasets @@ -340,7 +340,7 @@ def update_dataset_metadata( # Save the updated metadata metadata_path = _get_datasets_root() / dataset_id / "voxkit_dataset.json" - with open(metadata_path, "w") as f: + with open(metadata_path, "w", encoding="utf-8") as f: json.dump(metadata, f, indent=2) return True, "Dataset metadata updated successfully" @@ -452,7 +452,7 @@ def _rewrite_imported_alignments(new_dataset_path: Path) -> None: if not metadata_file.exists(): continue try: - with open(metadata_file, "r") as f: + with open(metadata_file, "r", encoding="utf-8") as f: alignment_metadata = json.load(f) except (OSError, json.JSONDecodeError) as e: print(f"Skipping alignment metadata rewrite for '{metadata_file}': {e}") @@ -463,7 +463,7 @@ def _rewrite_imported_alignments(new_dataset_path: Path) -> None: alignment_metadata["tg_path"] = str(alignment_dir / "textgrids") try: - with open(metadata_file, "w") as f: + with open(metadata_file, "w", encoding="utf-8") as f: json.dump(alignment_metadata, f, indent=4) except OSError as e: print(f"Failed to rewrite alignment metadata '{metadata_file}': {e}") @@ -498,17 +498,18 @@ def import_dataset(dataset_path: Path) -> Tuple[bool, str]: return False, f"Dataset path '{dataset_path}' does not exist." if not dataset_path.is_dir(): return False, f"Dataset path '{dataset_path}' is not a directory." - valid, valid_msg = validate_dataset(dataset_path / "cache") + dataset_metadata_typed = _get_dataset_metadata(dataset_path) + if dataset_metadata_typed is None: + return False, "Dataset metadata file not found in the provided dataset path." + + transcribed_flag: bool = bool(dataset_metadata_typed.get("transcribed", True)) + valid, valid_msg = validate_dataset(dataset_path / "cache", transcribed=transcribed_flag) now = generate_unique_id() print(now) dataset_dest = _get_datasets_root() / now try: - dataset_metadata_typed = _get_dataset_metadata(dataset_path) - if dataset_metadata_typed is None: - return False, "Dataset metadata file not found in the provided dataset path." - # Change metadata accordingly dataset_metadata: dict[str, Any] = dict(dataset_metadata_typed) # Make a copy to modify dataset_metadata["id"] = now @@ -536,7 +537,7 @@ def import_dataset(dataset_path: Path) -> Tuple[bool, str]: shutil.copytree(dataset_path, dataset_dest, dirs_exist_ok=True) - with open(metadata_path, "w") as f: + with open(metadata_path, "w", encoding="utf-8") as f: json.dump(dataset_metadata, f, indent=2) _rewrite_imported_alignments(dataset_dest) @@ -551,7 +552,7 @@ def import_dataset(dataset_path: Path) -> Tuple[bool, str]: return False, f"Failed to import dataset: {str(e)}" -def validate_dataset(dataset_path: Path) -> Tuple[bool, str]: +def validate_dataset(dataset_path: Path, transcribed: bool = True) -> Tuple[bool, str]: """Validate if a dataset follows the expected organization pattern. Validation checks: @@ -560,16 +561,14 @@ def validate_dataset(dataset_path: Path) -> Tuple[bool, str]: - Contains speaker subdirectories (not files at root level) - Each speaker directory is not empty - Each speaker directory contains audio files (.wav, .flac, .mp3, .ogg, .m4a) - - Each speaker directory contains label files (.lab, .txt) - - Number of audio files matches number of label files per speaker - - Each audio file has a matching label file with the same stem name + - If transcribed=True: each audio file has a matching .lab file in the same directory Expected structure: dataset_path/ ├── speaker_001/ │ ├── audio_001.wav - │ ├── audio_001.lab + │ ├── audio_001.lab (only required when transcribed=True) │ ├── audio_002.wav │ └── audio_002.lab └── speaker_002/ @@ -578,6 +577,7 @@ def validate_dataset(dataset_path: Path) -> Tuple[bool, str]: Args: dataset_path: Path to dataset root directory + transcribed: Whether to require a .lab file for every audio file Returns: Tuple of (True, validation_message) if valid or (False, error_description) if invalid @@ -617,29 +617,28 @@ def validate_dataset(dataset_path: Path) -> Tuple[bool, str]: for f in os.listdir(speaker_path) if f.endswith((".wav", ".flac", ".mp3", ".ogg", ".m4a")) ] - label_files = [f for f in os.listdir(speaker_path) if f.endswith((".lab", ".txt"))] if not audio_files: return False, f"No audio files found in speaker directory '{speaker_path}'." - if not label_files: - return False, f"No label files found in speaker directory '{speaker_path}'." - - if len(audio_files) != len(label_files): - return ( - False, - f"Mismatch between number of audio and label files in speaker " - f"directory '{speaker_path}'.", - ) + lab_files = [f for f in os.listdir(speaker_path) if f.endswith(".lab")] - audio_stems = {Path(f).stem for f in audio_files} - label_stems = {Path(f).stem for f in label_files} - unmatched = audio_stems.symmetric_difference(label_stems) - if unmatched: - return ( - False, - f"Unpaired audio/label files in speaker directory '{speaker_path}': " - f"{', '.join(sorted(unmatched))}.", - ) + if transcribed: + audio_stems = {Path(f).stem for f in audio_files} + lab_stems = {Path(f).stem for f in lab_files} + missing = audio_stems - lab_stems + if missing: + return ( + False, + f"Missing .lab files for audio files in speaker directory " + f"'{speaker_path}': {', '.join(sorted(missing))}.", + ) + else: + if lab_files: + return ( + False, + f"Dataset is marked as not transcribed but .lab files were found in " + f"'{speaker_path}'. Set 'Transcribed' to true or remove the .lab files.", + ) return True, "Dataset is valid." diff --git a/src/voxkit/storage/models.py b/src/voxkit/storage/models.py index 52e5f7b..b197e94 100644 --- a/src/voxkit/storage/models.py +++ b/src/voxkit/storage/models.py @@ -179,7 +179,7 @@ def create_model( json_metadata = {k: str(v) if isinstance(v, Path) else v for k, v in model_metadata.items()} # Create metadata file and write metadata - with open(metadata_path, "w") as f: + with open(metadata_path, "w", encoding="utf-8") as f: json.dump(json_metadata, f, indent=4) return True, model_metadata @@ -218,7 +218,7 @@ def update_model_metadata(engine_id: str, model_id: str, updates: dict) -> Tuple metadata_path = Path(model_root) / "voxkit_model.json" try: - with open(metadata_path, "r") as f: + with open(metadata_path, "r", encoding="utf-8") as f: metadata = json.load(f) # Update fields @@ -226,7 +226,7 @@ def update_model_metadata(engine_id: str, model_id: str, updates: dict) -> Tuple if key in metadata: metadata[key] = str(value) - with open(metadata_path, "w") as f: + with open(metadata_path, "w", encoding="utf-8") as f: json.dump(metadata, f, indent=4) return True, "Model metadata updated successfully." @@ -266,7 +266,7 @@ def list_models(engine_id: str) -> list[ModelMetadata]: metadata_path = dir / "voxkit_model.json" if metadata_path.exists(): try: - with open(metadata_path, "r") as f: + with open(metadata_path, "r", encoding="utf-8") as f: metadata = json.load(f) models_found.append(metadata) except json.JSONDecodeError as e: @@ -302,7 +302,7 @@ def get_model_metadata(engine_id: str, model_id: str) -> ModelMetadata: metadata_path = Path(model_root) / "voxkit_model.json" if not metadata_path.exists(): raise FileNotFoundError(f"Metadata file not found for model '{model_id}'") - with open(metadata_path, "r") as f: + with open(metadata_path, "r", encoding="utf-8") as f: metadata: ModelMetadata = json.load(f) return metadata @@ -445,7 +445,7 @@ def import_models(engine_id, new_models_root: Path) -> Tuple[bool, str]: metadata = None # Read json metadata - with open(metadata_path, "r") as f: + with open(metadata_path, "r", encoding="utf-8") as f: metadata = json.load(f) if metadata is None: @@ -460,15 +460,12 @@ def import_models(engine_id, new_models_root: Path) -> Tuple[bool, str]: if engine_id != metadata["engine_id"]: return False, f"{source_model_path.name} (engine_id mismatch)" - parts = metadata["model_path"].split(MODELS_ROOT) - if len(parts) != 2: + entrypoint_name = Path(metadata["model_path"]).name + if not entrypoint_name: return False, f"{source_model_path.name} (invalid model_path in metadata)" - # Retain the model path don't assume it's the same for all models - path_after_root = parts[1] - dest_model_entrypoint = ( - engine_models_root / MODELS_ROOT / model_id / path_after_root.split("/")[-1] + engine_models_root / MODELS_ROOT / model_id / entrypoint_name ) new_metadata = ModelMetadata( name=metadata["name"], @@ -494,7 +491,7 @@ def import_models(engine_id, new_models_root: Path) -> Tuple[bool, str]: # Overwrite metadata file with new IDs and paths new_metadata_path = dest_path / "voxkit_model.json" - with open(new_metadata_path, "w") as f: + with open(new_metadata_path, "w", encoding="utf-8") as f: json.dump(json_metadata, f, indent=4) except Exception as e: diff --git a/src/voxkit/storage/utils.py b/src/voxkit/storage/utils.py index 7cb4588..3f25184 100644 --- a/src/voxkit/storage/utils.py +++ b/src/voxkit/storage/utils.py @@ -19,13 +19,17 @@ """ import json -from datetime import datetime +import threading +from datetime import datetime, timedelta from functools import lru_cache from pathlib import Path from typing import Any from .config import STORAGE_ROOT +_id_lock = threading.Lock() +_last_id_dt: datetime | None = None + @lru_cache(maxsize=1) def get_storage_root() -> Path: @@ -52,10 +56,17 @@ def generate_unique_id(prefix: str | None = None) -> str: Returns: Unique identifier string in format: [prefix_]YYYYMMDD_HHMMSS_ffffff """ - now = datetime.now().strftime("%Y%m%d_%H%M%S_%f") + global _last_id_dt + # Windows datetime.now() has ~15ms resolution; enforce monotonic microsecond precision. + with _id_lock: + now = datetime.now() + if _last_id_dt is not None and now <= _last_id_dt: + now = _last_id_dt + timedelta(microseconds=1) + _last_id_dt = now + now_str = now.strftime("%Y%m%d_%H%M%S_%f") if prefix: - return f"{prefix}_{now}" - return now + return f"{prefix}_{now_str}" + return now_str def readable_from_unique_id(date_str: str) -> str: @@ -110,5 +121,5 @@ def save_json(file_path: Path, data: dict[str, Any]) -> None: data: Dictionary to serialize as JSON """ file_path.parent.mkdir(parents=True, exist_ok=True) - with open(file_path, "w") as f: + with open(file_path, "w", encoding="utf-8") as f: json.dump(data, f, indent=4) diff --git a/tasks.py b/tasks.py index f132e3c..50d4993 100644 --- a/tasks.py +++ b/tasks.py @@ -88,16 +88,26 @@ def clean(c): _log("Cleanup completed", GREEN) -@task(pre=[clean]) -def build(c): - """Build standalone executable for current platform.""" - _log("Building VoxKit...") +@task(pre=[clean], name="macos-build") +def macos_build(c): + """Build standalone executable for macOS.""" + _log("Building VoxKit for macOS...") c.run( "uv run --group installation python scripts/build.py build " "--entry main.py --name VoxKit --icon ./assets/voxkit.icns --windowed" ) +@task(pre=[clean], name="windows-build") +def windows_build(c): + """Build standalone executable for Windows.""" + _log("Building VoxKit for Windows...") + c.run( + "uv run --group installation python scripts/build.py build " + "--entry main.py --name VoxKit --icon ./assets/voxkit.ico --windowed" + ) + + @task def format(c): """Format code with Ruff.""" diff --git a/tests/storage/test_datasets.py b/tests/storage/test_datasets.py index ddc85a3..e188f81 100644 --- a/tests/storage/test_datasets.py +++ b/tests/storage/test_datasets.py @@ -30,6 +30,15 @@ def generate_fake_datasets(): wav_file.touch() lab_file.touch() + # Create a valid untranscribed dataset (wav only, no .lab files) + dataset_path = mock_get_storage_root() / "fake_datasets" / "valid_untranscribed" + dataset_path.mkdir(parents=True, exist_ok=True) + for participant in participant_names: + wavlab_path = dataset_path / participant + wavlab_path.mkdir(parents=True, exist_ok=True) + for i in range(5): + (wavlab_path / f"sample_{i}.wav").touch() + # Create an invalid dataset path dataset_path = mock_get_storage_root() / "fake_datasets" / "invalid" dataset_path.mkdir(parents=True, exist_ok=True) @@ -60,6 +69,7 @@ def manage_test_environment(): valid_dataset_path = mock_get_storage_root() / "fake_datasets" / "valid" +valid_untranscribed_dataset_path = mock_get_storage_root() / "fake_datasets" / "valid_untranscribed" invalid_dataset_path = mock_get_storage_root() / "fake_datasets" / "invalid" empty_dataset_path = mock_get_storage_root() / "fake_datasets" / "empty" @@ -111,7 +121,7 @@ def test_create_dataset_success_with_cache(self, monkeypatch): success, message = create_dataset( name="test_dataset_cached", description="A test dataset with caching", - original_path=valid_dataset_path, + original_path=valid_untranscribed_dataset_path, cached=True, anonymize=True, transcribed=False, @@ -144,7 +154,7 @@ def test_create_dataset_invalid_path(self, monkeypatch): assert success is False assert isinstance(message, str) - assert "No label files found" in message + assert "Missing .lab files" in message assert invalid_dataset_path.exists() is True class TestListDatasets: @@ -166,7 +176,7 @@ def test_list_datasets_metadata(self, monkeypatch): create_dataset( name="dataset_two", description="Second test dataset", - original_path=valid_dataset_path, + original_path=valid_untranscribed_dataset_path, cached=True, anonymize=True, transcribed=False, @@ -596,7 +606,7 @@ def test_update_dataset_metadata_success(self, monkeypatch): success, message = create_dataset( name="dataset_update_test", description="Original description", - original_path=valid_dataset_path, + original_path=valid_untranscribed_dataset_path, cached=False, anonymize=False, transcribed=False, @@ -863,7 +873,7 @@ def test_validate_dataset_missing_label_files(self, monkeypatch): is_valid, msg = validate_dataset(invalid_dataset_path) assert is_valid is False - assert "No label files" in msg + assert "Missing .lab files" in msg def test_validate_dataset_mismatched_counts(self, monkeypatch): from voxkit.storage.datasets import validate_dataset @@ -880,7 +890,8 @@ def test_validate_dataset_mismatched_counts(self, monkeypatch): is_valid, msg = validate_dataset(mismatched_path) assert is_valid is False - assert "Mismatch" in msg + assert "Missing .lab files" in msg + assert "sample_2" in msg def test_validate_dataset_unpaired_stems(self, monkeypatch): from voxkit.storage.datasets import validate_dataset @@ -896,4 +907,19 @@ def test_validate_dataset_unpaired_stems(self, monkeypatch): is_valid, msg = validate_dataset(unpaired_path) assert is_valid is False - assert "Unpaired" in msg + assert "Missing .lab files" in msg + assert "recording_A" in msg + + def test_validate_dataset_not_transcribed_but_has_lab_files(self, monkeypatch): + from voxkit.storage.datasets import validate_dataset + + lab_present_path = mock_get_storage_root() / "fake_datasets" / "lab_but_not_transcribed" + speaker_path = lab_present_path / "speaker_1" + speaker_path.mkdir(parents=True, exist_ok=True) + (speaker_path / "sample_1.wav").touch() + (speaker_path / "sample_1.lab").touch() + + is_valid, msg = validate_dataset(lab_present_path, transcribed=False) + + assert is_valid is False + assert "not transcribed" in msg diff --git a/tests/storage/test_models.py b/tests/storage/test_models.py index 8cb7846..95f325b 100644 --- a/tests/storage/test_models.py +++ b/tests/storage/test_models.py @@ -670,6 +670,51 @@ def test_import_models_success(self, monkeypatch): assert success is True assert "imported successfully" in msg + def test_import_models_paths_rewritten(self, monkeypatch): + import json + + from voxkit.storage import models + from voxkit.storage.config import MODELS_ROOT + from voxkit.storage.models import import_models, list_models + + monkeypatch.setattr(models, "get_storage_root", mock_get_storage_root) + + engine_id = ENGINE_IDS[0] + import_source = mock_get_storage_root() / "import_source_paths" + model_dir = import_source / "model_to_import" + model_dir.mkdir(parents=True, exist_ok=True) + (model_dir / "entrypoint.model").mkdir(parents=True, exist_ok=True) + + old_base = "/old/machine/path" + metadata = { + "name": "path_check_model", + "engine_id": engine_id, + "model_path": f"{old_base}/{engine_id}/{MODELS_ROOT}/old_id/entrypoint.model", + "data_path": f"{old_base}/data", + "eval_path": f"{old_base}/eval", + "train_path": f"{old_base}/train", + "download_date": "January 01, 2024 at 12:00:00 PM", + "id": "old_id", + } + with open(model_dir / "voxkit_model.json", "w") as f: + json.dump(metadata, f, indent=4) + + success, _ = import_models(engine_id=engine_id, new_models_root=import_source) + assert success is True + + imported = list_models(engine_id) + assert len(imported) == 1 + imported_meta = imported[0] + + storage_root = str(mock_get_storage_root()) + assert old_base not in imported_meta["model_path"] + assert old_base not in imported_meta["data_path"] + assert old_base not in imported_meta["eval_path"] + assert old_base not in imported_meta["train_path"] + assert storage_root in imported_meta["model_path"] + assert "old_id" not in imported_meta["id"] + assert imported_meta["name"] == "path_check_model" + def test_import_models_missing_metadata(self, monkeypatch): from voxkit.storage import models from voxkit.storage.models import import_models diff --git a/uv.lock b/uv.lock index 993ae89..5456aa0 100644 --- a/uv.lock +++ b/uv.lock @@ -2,21 +2,10 @@ version = 1 revision = 3 requires-python = ">=3.11" resolution-markers = [ - "python_full_version >= '3.15' and sys_platform == 'win32'", - "python_full_version >= '3.15' and sys_platform == 'emscripten'", - "python_full_version >= '3.15' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.14.*' and sys_platform == 'win32'", - "python_full_version == '3.14.*' and sys_platform == 'emscripten'", - "python_full_version == '3.14.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.13.*' and sys_platform == 'win32'", - "python_full_version == '3.13.*' and sys_platform == 'emscripten'", - "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.12.*' and sys_platform == 'win32'", - "python_full_version == '3.12.*' and sys_platform == 'emscripten'", - "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version < '3.12' and sys_platform == 'win32'", - "python_full_version < '3.12' and sys_platform == 'emscripten'", - "python_full_version < '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.15'", + "python_full_version >= '3.13' and python_full_version < '3.15'", + "python_full_version == '3.12.*'", + "python_full_version < '3.12'", ] [[package]] @@ -163,17 +152,13 @@ wheels = [ [[package]] name = "alignment-comparison-plots" -version = "0.1.1" -source = { registry = "https://pypi.org/simple" } +version = "0.1.2.dev2+gb44799d30" +source = { git = "https://github.com/WISCLab/alignment-comparison-plots?branch=voxkit-windows-variant#b44799d3089b7631e3a87604f046966a2090582b" } dependencies = [ { name = "matplotlib" }, { name = "praat-textgrids" }, { name = "pyqt6" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b9/4c/3b8e6c262abf4d7b78b44c04d30ac058a3fe8f60685e8ebdbbd538b5f08d/alignment_comparison_plots-0.1.1.tar.gz", hash = "sha256:d5aa66044fa7d9e2871f8aa14205a60d32918766cd81248098318b69e542ca40", size = 536340, upload-time = "2026-03-11T06:16:19.583Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/99/97654e96039cb9bbae5e79dd55b9f4595f6e2f20d6935a2cd00dfc0b7b62/alignment_comparison_plots-0.1.1-py3-none-any.whl", hash = "sha256:6a5bde06fa72a0be04ff8e0011fa612c12c43455bcbf1303343543db39f134ec", size = 12211, upload-time = "2026-03-11T06:16:21.124Z" }, -] [[package]] name = "altgraph" @@ -815,7 +800,7 @@ wheels = [ [[package]] name = "datasets" -version = "4.8.4" +version = "4.8.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "dill" }, @@ -833,9 +818,9 @@ dependencies = [ { name = "tqdm" }, { name = "xxhash" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/22/22/73e46ac7a8c25e7ef0b3bd6f10da3465021d90219a32eb0b4d2afea4c56e/datasets-4.8.4.tar.gz", hash = "sha256:a1429ed853275ce7943a01c6d2e25475b4501eb758934362106a280470df3a52", size = 604382, upload-time = "2026-03-23T14:21:17.987Z" } +sdist = { url = "https://files.pythonhosted.org/packages/66/34/14cd8e76f907f7d4dca2334cfeec9f81d30fd15c25a015f99aaea694eaed/datasets-4.8.5.tar.gz", hash = "sha256:0f0c1c3d56ffff2c93b2f4c63c95bac94f3d7e8621aea2a2a576275233bba772", size = 605649, upload-time = "2026-04-27T15:43:57.384Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b0/e5/247d094108e42ac26363ab8dc57f168840cf7c05774b40ffeb0d78868fcc/datasets-4.8.4-py3-none-any.whl", hash = "sha256:cdc8bee4698e549d78bf1fed6aea2eebc760b22b084f07e6fc020c6577a6ce6d", size = 526991, upload-time = "2026-03-23T14:21:15.89Z" }, + { url = "https://files.pythonhosted.org/packages/65/99/00f3196036501b53032c4b1ab8337a0b978dee832ed276dae3815df4e8b5/datasets-4.8.5-py3-none-any.whl", hash = "sha256:5079900781719c0e063a8efdd2cd95a31ad0c63209178669cd23cf1b926149ff", size = 528973, upload-time = "2026-04-27T15:43:53.702Z" }, ] [[package]] @@ -1187,7 +1172,7 @@ wheels = [ [[package]] name = "huggingface-hub" -version = "1.12.0" +version = "1.13.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "filelock" }, @@ -1200,9 +1185,9 @@ dependencies = [ { name = "typer" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/56/52/1b54cb569509c725a32c1315261ac9fd0e6b91bbbf74d86fca10d3376164/huggingface_hub-1.12.0.tar.gz", hash = "sha256:7c3fe85e24b652334e5d456d7a812cd9a071e75630fac4365d9165ab5e4a34b6", size = 763091, upload-time = "2026-04-24T13:32:08.674Z" } +sdist = { url = "https://files.pythonhosted.org/packages/89/ff/ec7ed2eb43bd7ce8bb2233d109cc235c3e807ffe5e469dc09db261fac05e/huggingface_hub-1.13.0.tar.gz", hash = "sha256:f6df2dac5abe82ce2fe05873d10d5ff47bc677d616a2f521f4ee26db9415d9d0", size = 781788, upload-time = "2026-04-30T11:57:33.858Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/2b/ef03ddb96bd1123503c2bd6932001020292deea649e9bf4caa2cb65a85bf/huggingface_hub-1.12.0-py3-none-any.whl", hash = "sha256:d74939969585ee35748bd66de09baf84099d461bda7287cd9043bfb99b0e424d", size = 646806, upload-time = "2026-04-24T13:32:06.717Z" }, + { url = "https://files.pythonhosted.org/packages/93/db/4b1cdae9460ae1f3ca020cd767f013430ce23eb1d9c890ae3a0609b38d26/huggingface_hub-1.13.0-py3-none-any.whl", hash = "sha256:e942cb50d6a08dd5306688b1ac05bda157fd2fcc88b63dae405f7bd0d3234005", size = 660643, upload-time = "2026-04-30T11:57:31.802Z" }, ] [[package]] @@ -1717,7 +1702,7 @@ wheels = [ [[package]] name = "matplotlib" -version = "3.10.9" +version = "3.10.6" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "contourpy" }, @@ -1730,53 +1715,53 @@ dependencies = [ { name = "pyparsing" }, { name = "python-dateutil" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/63/1b/4be5be87d43d327a0cf4de1a56e86f7f84c89312452406cf122efe2839e6/matplotlib-3.10.9.tar.gz", hash = "sha256:fd66508e8c6877d98e586654b608a0456db8d7e8a546eb1e2600efd957302358", size = 34811233, upload-time = "2026-04-24T00:14:13.539Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4c/8c/290f021104741fea63769c31494f5324c0cd249bf536a65a4350767b1f22/matplotlib-3.10.9-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:68cfdcede415f7c8f5577b03303dd94526cdb6d11036cecdc205e08733b2d2bb", size = 8306860, upload-time = "2026-04-24T00:12:01.207Z" }, - { url = "https://files.pythonhosted.org/packages/51/18/325cd32ece1120d1da51cc4e4294c6580190699490183fc2fe8cb6d61ec5/matplotlib-3.10.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfca0129678bd56379db26c52b5d77ed7de314c047492fbdc763aa7501710cfb", size = 8199254, upload-time = "2026-04-24T00:12:04.239Z" }, - { url = "https://files.pythonhosted.org/packages/79/db/e28c1b83e3680740aa78925f5fb2ae4d16207207419ad75ea9fe604f8676/matplotlib-3.10.9-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8e436d155fa8a3399dc62683f8f5d0e2e50d25d0144a73edd73f82eec8f4abfb", size = 8777092, upload-time = "2026-04-24T00:12:06.793Z" }, - { url = "https://files.pythonhosted.org/packages/55/fa/3ce7adfe9ba101748f465211660d9c6374c876b671bdb8c2bb6d347e8b94/matplotlib-3.10.9-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:56fc0bd271b00025c6edfdc7c2dcd247372c8e1544971d62e1dc7c17367e8bf9", size = 9595691, upload-time = "2026-04-24T00:12:09.706Z" }, - { url = "https://files.pythonhosted.org/packages/36/c4/6960a76686ed668f2c60f84e9799ba4c0d56abdb36b1577b60c1d061d1ec/matplotlib-3.10.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a5a6104ed666402ba5106d7f36e0e0cdca4e8d7fa4d39708ca88019e2835a2eb", size = 9659771, upload-time = "2026-04-24T00:12:12.766Z" }, - { url = "https://files.pythonhosted.org/packages/7e/0d/271aace3342157c64700c9ff4c59c7b392f3dbab393692e8db6fbe7ab96c/matplotlib-3.10.9-cp311-cp311-win_amd64.whl", hash = "sha256:d730e984eddf56974c3e72b6129c7ca462ac38dc624338f4b0b23eb23ecba00f", size = 8205112, upload-time = "2026-04-24T00:12:15.773Z" }, - { url = "https://files.pythonhosted.org/packages/e2/ee/cb57ad4754f3e7b9174ce6ce66d9205fb827067e48a9f58ac09d7e7d6b77/matplotlib-3.10.9-cp311-cp311-win_arm64.whl", hash = "sha256:51bf0ddbdc598e060d46c16b5590708f81a1624cefbaaf62f6a81bf9285b8c80", size = 8132310, upload-time = "2026-04-24T00:12:18.645Z" }, - { url = "https://files.pythonhosted.org/packages/35/c6/5581e26c72233ebb2a2a6fed2d24fb7c66b4700120b813f51b0555acf0b6/matplotlib-3.10.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f0c3c28d9fbcc1fe7a03be236d73430cf6409c41fb2383a7ac52fe932b072cb1", size = 8319908, upload-time = "2026-04-24T00:12:21.323Z" }, - { url = "https://files.pythonhosted.org/packages/b7/18/4880dd762e40cd360c1bf06e890c5a97b997e91cb324602b1a19950ad5ce/matplotlib-3.10.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41cb28c2bd769aa3e98322c6ab09854cbcc52ab69d2759d681bba3e327b2b320", size = 8216016, upload-time = "2026-04-24T00:12:23.4Z" }, - { url = "https://files.pythonhosted.org/packages/32/91/d024616abdba99e83120e07a20658976f6a343646710760c4a51df126029/matplotlib-3.10.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ae20801130378b82d647ff5047c07316295b68dc054ca6b3c13519d0ea624285", size = 8789336, upload-time = "2026-04-24T00:12:26.096Z" }, - { url = "https://files.pythonhosted.org/packages/5c/04/030a2f61ef2158f5e4c259487a92ac877732499fb33d871585d89e03c42d/matplotlib-3.10.9-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6c63ebcd8b4b169eb2f5c200552ae6b8be8999a005b6b507ed76fb8d7d674fe2", size = 9604602, upload-time = "2026-04-24T00:12:29.052Z" }, - { url = "https://files.pythonhosted.org/packages/fc/c2/541e4d09d87bb6b5830fc28b4c887a9a8cf4e1c6cee698a8c05552ae2003/matplotlib-3.10.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d75d11c949914165976c621b2324f9ef162af7ebf4b057ddf95dd1dba7e5edcf", size = 9670966, upload-time = "2026-04-24T00:12:32.131Z" }, - { url = "https://files.pythonhosted.org/packages/04/a1/4571fc46e7702de8d0c2dc54ad1b2f8e29328dea3ee90831181f7353d93c/matplotlib-3.10.9-cp312-cp312-win_amd64.whl", hash = "sha256:d091f9d758b34aaaaa6331d13574bf01891d903b3dec59bfff458ef7551de5d6", size = 8217462, upload-time = "2026-04-24T00:12:35.226Z" }, - { url = "https://files.pythonhosted.org/packages/4b/d0/2269edb12aa30c13c8bcc9382892e39943ce1d28aab4ec296e0381798e81/matplotlib-3.10.9-cp312-cp312-win_arm64.whl", hash = "sha256:10cc5ce06d10231c36f40e875f3c7e8050362a4ee8f0ee5d29a6b3277d57bb42", size = 8136688, upload-time = "2026-04-24T00:12:37.442Z" }, - { url = "https://files.pythonhosted.org/packages/aa/d3/8d4f6afbecb49fc04e060a57c0fce39ea51cc163a6bd87303ccd698e4fa6/matplotlib-3.10.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b580440f1ff81a0e34122051a3dfabb7e4b7f9e380629929bde0eff9af72165f", size = 8320331, upload-time = "2026-04-24T00:12:39.688Z" }, - { url = "https://files.pythonhosted.org/packages/63/d9/9e14bc7564bf92d5ffa801ae5fac819ce74b925dfb55e3ebde61a3bbad3e/matplotlib-3.10.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b1b745c489cd1a77a0dc1120a05dc87af9798faebc913601feb8c73d89bf2d1e", size = 8216461, upload-time = "2026-04-24T00:12:42.494Z" }, - { url = "https://files.pythonhosted.org/packages/8a/17/4402d0d14ccf1dfc70932600b68097fbbf9c898a4871d2cbbe79c7801a32/matplotlib-3.10.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8f3bcac1ca5ed000a6f4337d47ba67dfddf37ed6a46c15fd7f014997f7bf865f", size = 8790091, upload-time = "2026-04-24T00:12:44.789Z" }, - { url = "https://files.pythonhosted.org/packages/3e/0b/322aeec06dd9b91411f92028b37d447342770a24392aa4813e317064dad5/matplotlib-3.10.9-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a8d66a55def891c33147ba3ba9bfcabf0b526a43764c818acbb4525e5ed0838", size = 9605027, upload-time = "2026-04-24T00:12:47.583Z" }, - { url = "https://files.pythonhosted.org/packages/74/88/5f13482f55e7b00bcfc09838b093c2456e1379978d2a146844aae05350ad/matplotlib-3.10.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d843374407c4017a6403b59c6c81606773d136f3259d5b6da3131bc814542cc2", size = 9671269, upload-time = "2026-04-24T00:12:50.878Z" }, - { url = "https://files.pythonhosted.org/packages/c5/e0/0840fd2f93da988ec660b8ad1984abe9f25d2aed22a5e394ff1c68c88307/matplotlib-3.10.9-cp313-cp313-win_amd64.whl", hash = "sha256:f4399f64b3e94cd500195490972ae1ee81170df1636fa15364d157d5bdd7b921", size = 8217588, upload-time = "2026-04-24T00:12:53.784Z" }, - { url = "https://files.pythonhosted.org/packages/47/b9/d706d06dd605c49b9f83a2aed8c13e3e5db70697d7a80b7e3d7915de6b17/matplotlib-3.10.9-cp313-cp313-win_arm64.whl", hash = "sha256:ba7b3b8ef09eab7df0e86e9ae086faa433efbfbdb46afcb3aa16aabf779469a8", size = 8136913, upload-time = "2026-04-24T00:12:56.501Z" }, - { url = "https://files.pythonhosted.org/packages/9b/45/6e32d96978264c8ca8c4b1010adb955a1a49cfaf314e212bbc8908f04a61/matplotlib-3.10.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:09218df8a93712bd6ea133e83a153c755448cf7868316c531cffcc43f69d1cc9", size = 8368019, upload-time = "2026-04-24T00:12:58.896Z" }, - { url = "https://files.pythonhosted.org/packages/86/0a/c8e3d3bba245f0f7fc424937f8ff7ef77291a36af3edb97ccd78aa93d84f/matplotlib-3.10.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:82368699727bfb7b0182e1aa13082e3c08e092fa1a25d3e1fd92405bff96f6d4", size = 8264645, upload-time = "2026-04-24T00:13:01.406Z" }, - { url = "https://files.pythonhosted.org/packages/3d/aa/5bf5a14fe4fed73a4209a155606f8096ff797aad89c6c35179026571133e/matplotlib-3.10.9-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3225f4e1edcb8c86c884ddf79ebe20ecd0a67d30188f279897554ccd8fded4dc", size = 8802194, upload-time = "2026-04-24T00:13:03.702Z" }, - { url = "https://files.pythonhosted.org/packages/dd/5e/b4be852d6bba6fd15893fadf91ff26ae49cb91aac789e95dde9d342e664f/matplotlib-3.10.9-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:de2445a0c6690d21b7eb6ce071cebad6d40a2e9bdf10d039074a96ba19797b99", size = 9622684, upload-time = "2026-04-24T00:13:06.647Z" }, - { url = "https://files.pythonhosted.org/packages/4c/3d/ed428c971139112ef730f62770654d609467346d09d4b62617e1afd68a5a/matplotlib-3.10.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:b2b9516251cb89ff618d757daec0e2ed1bf21248013844a853d87ef85ab3081d", size = 9680790, upload-time = "2026-04-24T00:13:10.009Z" }, - { url = "https://files.pythonhosted.org/packages/e7/09/052e884aaf2b985c63cb79f715f1d5b6a3eaa7de78f6a52b9dbc077d5b53/matplotlib-3.10.9-cp313-cp313t-win_amd64.whl", hash = "sha256:e9fae004b941b23ff2edcf1567a857ed77bafc8086ffa258190462328434faf8", size = 8287571, upload-time = "2026-04-24T00:13:13.087Z" }, - { url = "https://files.pythonhosted.org/packages/f4/38/ae27288e788c35a4250491422f3db7750366fc8c97d6f36fbdecfc1f5518/matplotlib-3.10.9-cp313-cp313t-win_arm64.whl", hash = "sha256:6b63d9c7c769b88ab81e10dc86e4e0607cf56817b9f9e6cf24b2a5f1693b8e38", size = 8188292, upload-time = "2026-04-24T00:13:15.546Z" }, - { url = "https://files.pythonhosted.org/packages/d6/e6/3bd8afd04949f02eabc1c17115ea5255e19cacd4d06fc5abdde4eeb0052c/matplotlib-3.10.9-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:172db52c9e683f5d12eaf57f0f54834190e12581fe1cc2a19595a8f5acb4e77d", size = 8321276, upload-time = "2026-04-24T00:13:18.318Z" }, - { url = "https://files.pythonhosted.org/packages/41/86/86231232fff41c9f8e4a1a7d7a597d349a02527109c3af7d618366122139/matplotlib-3.10.9-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:97e35e8d39ccc85859095e01a53847432ba9a53ddf7986f7a54a11b73d0e143f", size = 8218218, upload-time = "2026-04-24T00:13:20.974Z" }, - { url = "https://files.pythonhosted.org/packages/85/8f/becc9722cafc64f5d2eb0b7c1bf5f585271c618a45dbd8fabeb021f898b6/matplotlib-3.10.9-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aba1615dabe83188e19d4f75a253c6a08423e04c1425e64039f800050a69de6b", size = 9608145, upload-time = "2026-04-24T00:13:23.228Z" }, - { url = "https://files.pythonhosted.org/packages/32/5d/f7e914f7d9325abff4057cee62c0fa70263683189f774473cbfb534cd13b/matplotlib-3.10.9-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34cf8167e023ad956c15f36302911d5406bd99a9862c1a8499ea6f7c0e015dc2", size = 9885085, upload-time = "2026-04-24T00:13:25.849Z" }, - { url = "https://files.pythonhosted.org/packages/a5/fd/fa69f2221534e80cc5772ac2b7d222011a2acafc2ec7216d5dd174c864ae/matplotlib-3.10.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:59476c6d29d612b8e9bb6ce8c5b631be6ba8f9e3a2421f22a02b192c7dd28716", size = 9672358, upload-time = "2026-04-24T00:13:28.906Z" }, - { url = "https://files.pythonhosted.org/packages/ab/1a/5a4f747a8b271cbb024946d2dd3c913ab5032ba430626f8c3528ada96b4b/matplotlib-3.10.9-cp314-cp314-win_amd64.whl", hash = "sha256:336b9acc64d309063126edcdaca00db9373af3c476bb94388fe9c5a53ad13e6f", size = 8349970, upload-time = "2026-04-24T00:13:31.904Z" }, - { url = "https://files.pythonhosted.org/packages/64/dc/95d60ecaefe30680a154b52ea96ab4b0dab547f1fd6aa12f5fb655e89cae/matplotlib-3.10.9-cp314-cp314-win_arm64.whl", hash = "sha256:2dc9477819ffd78ad12a20df1d9d6a6bd4fec6aaa9072681465fddca052f1456", size = 8272785, upload-time = "2026-04-24T00:13:34.511Z" }, - { url = "https://files.pythonhosted.org/packages/70/a0/005d68bc8b8418300ce6591f18586910a8526806e2ab663933d9f20a41e9/matplotlib-3.10.9-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:da4e09638420548f31c354032a6250e473c68e5a4e96899b4844cf39ddea23fe", size = 8367999, upload-time = "2026-04-24T00:13:36.962Z" }, - { url = "https://files.pythonhosted.org/packages/22/05/1236cc9290be70b2498af20ca348add76e3fffe7f67b477db5133a84f3ea/matplotlib-3.10.9-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:345f6f68ecc8da0ca56fad2ea08fde1a115eda530079eca185d50a7bc3e146c6", size = 8264543, upload-time = "2026-04-24T00:13:39.851Z" }, - { url = "https://files.pythonhosted.org/packages/cd/c2/071f5a5ff6c5bd63aaaf2f45c811d9bf2ced94bde188d9e1a519e21d0cba/matplotlib-3.10.9-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4edcfbd8565339aa62f1cd4012f7180926fdbe71850f7b0d3c379c175cd6b66c", size = 9622800, upload-time = "2026-04-24T00:13:42.296Z" }, - { url = "https://files.pythonhosted.org/packages/95/57/da7d1f10a85624b9e7db68e069dd94e58dc41dbf9463c5921632ecbe3661/matplotlib-3.10.9-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6be157fe17fc37cb95ac1d7374cf717ce9259616edec911a78d9d26dae8522d4", size = 9888561, upload-time = "2026-04-24T00:13:45.026Z" }, - { url = "https://files.pythonhosted.org/packages/67/b2/ef8d6bb59b0edb6c16c968b70f548aa13b54348972def5aa6ac85df67145/matplotlib-3.10.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:4e42042d54db34fda4e95a7bd3e5789c2a995d2dad3eb8850232ee534092fbbf", size = 9680884, upload-time = "2026-04-24T00:13:48.066Z" }, - { url = "https://files.pythonhosted.org/packages/61/1c/d21bfeb9931881ebe96bcfcff27c7ae4b160ae0ec291a714c42641a56d75/matplotlib-3.10.9-cp314-cp314t-win_amd64.whl", hash = "sha256:c27df8b3848f32a83d1767566595e43cfaa4460380974da06f4279a7ec143c39", size = 8432333, upload-time = "2026-04-24T00:13:51.008Z" }, - { url = "https://files.pythonhosted.org/packages/78/23/92493c3e6e1b635ccfff146f7b99e674808787915420373ac399283764c2/matplotlib-3.10.9-cp314-cp314t-win_arm64.whl", hash = "sha256:a49f1eadc84ca85fd72fa4e89e70e61bf86452df6f971af04b12c60761a0772c", size = 8324785, upload-time = "2026-04-24T00:13:53.633Z" }, - { url = "https://files.pythonhosted.org/packages/63/e2/9f66ca6a651a52abfe0d4964ce01439ed34f3f1e119de10ff3a07f403043/matplotlib-3.10.9-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:42fb814efabe95c06c1994d8ab5a8385f43a249e23badd3ba931d4308e5bca20", size = 8304420, upload-time = "2026-04-24T00:14:04.57Z" }, - { url = "https://files.pythonhosted.org/packages/e8/e8/467c03568218792906aa87b5e7bb379b605e056ed0c74fe00c051786d925/matplotlib-3.10.9-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:f76e640a5268850bfda54b5131b1b1941cc685e42c5fa98ed9f2d64038308cba", size = 8197981, upload-time = "2026-04-24T00:14:07.233Z" }, - { url = "https://files.pythonhosted.org/packages/6f/87/afead29192170917537934c6aff4b008c805fff7b1ccea0c79120d96beda/matplotlib-3.10.9-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3fc0364dfbe1d07f6d15c5ebd0c5bf89e126916e5a8667dd4a7a6e84c36653d4", size = 8774002, upload-time = "2026-04-24T00:14:09.816Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/a0/59/c3e6453a9676ffba145309a73c462bb407f4400de7de3f2b41af70720a3c/matplotlib-3.10.6.tar.gz", hash = "sha256:ec01b645840dd1996df21ee37f208cd8ba57644779fa20464010638013d3203c", size = 34804264, upload-time = "2025-08-30T00:14:25.137Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/80/d6/5d3665aa44c49005aaacaa68ddea6fcb27345961cd538a98bb0177934ede/matplotlib-3.10.6-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:905b60d1cb0ee604ce65b297b61cf8be9f4e6cfecf95a3fe1c388b5266bc8f4f", size = 8257527, upload-time = "2025-08-30T00:12:45.31Z" }, + { url = "https://files.pythonhosted.org/packages/8c/af/30ddefe19ca67eebd70047dabf50f899eaff6f3c5e6a1a7edaecaf63f794/matplotlib-3.10.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7bac38d816637343e53d7185d0c66677ff30ffb131044a81898b5792c956ba76", size = 8119583, upload-time = "2025-08-30T00:12:47.236Z" }, + { url = "https://files.pythonhosted.org/packages/d3/29/4a8650a3dcae97fa4f375d46efcb25920d67b512186f8a6788b896062a81/matplotlib-3.10.6-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:942a8de2b5bfff1de31d95722f702e2966b8a7e31f4e68f7cd963c7cd8861cf6", size = 8692682, upload-time = "2025-08-30T00:12:48.781Z" }, + { url = "https://files.pythonhosted.org/packages/aa/d3/b793b9cb061cfd5d42ff0f69d1822f8d5dbc94e004618e48a97a8373179a/matplotlib-3.10.6-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a3276c85370bc0dfca051ec65c5817d1e0f8f5ce1b7787528ec8ed2d524bbc2f", size = 9521065, upload-time = "2025-08-30T00:12:50.602Z" }, + { url = "https://files.pythonhosted.org/packages/f7/c5/53de5629f223c1c66668d46ac2621961970d21916a4bc3862b174eb2a88f/matplotlib-3.10.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9df5851b219225731f564e4b9e7f2ac1e13c9e6481f941b5631a0f8e2d9387ce", size = 9576888, upload-time = "2025-08-30T00:12:52.92Z" }, + { url = "https://files.pythonhosted.org/packages/fc/8e/0a18d6d7d2d0a2e66585032a760d13662e5250c784d53ad50434e9560991/matplotlib-3.10.6-cp311-cp311-win_amd64.whl", hash = "sha256:abb5d9478625dd9c9eb51a06d39aae71eda749ae9b3138afb23eb38824026c7e", size = 8115158, upload-time = "2025-08-30T00:12:54.863Z" }, + { url = "https://files.pythonhosted.org/packages/07/b3/1a5107bb66c261e23b9338070702597a2d374e5aa7004b7adfc754fbed02/matplotlib-3.10.6-cp311-cp311-win_arm64.whl", hash = "sha256:886f989ccfae63659183173bb3fced7fd65e9eb793c3cc21c273add368536951", size = 7992444, upload-time = "2025-08-30T00:12:57.067Z" }, + { url = "https://files.pythonhosted.org/packages/ea/1a/7042f7430055d567cc3257ac409fcf608599ab27459457f13772c2d9778b/matplotlib-3.10.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:31ca662df6a80bd426f871105fdd69db7543e28e73a9f2afe80de7e531eb2347", size = 8272404, upload-time = "2025-08-30T00:12:59.112Z" }, + { url = "https://files.pythonhosted.org/packages/a9/5d/1d5f33f5b43f4f9e69e6a5fe1fb9090936ae7bc8e2ff6158e7a76542633b/matplotlib-3.10.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1678bb61d897bb4ac4757b5ecfb02bfb3fddf7f808000fb81e09c510712fda75", size = 8128262, upload-time = "2025-08-30T00:13:01.141Z" }, + { url = "https://files.pythonhosted.org/packages/67/c3/135fdbbbf84e0979712df58e5e22b4f257b3f5e52a3c4aacf1b8abec0d09/matplotlib-3.10.6-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:56cd2d20842f58c03d2d6e6c1f1cf5548ad6f66b91e1e48f814e4fb5abd1cb95", size = 8697008, upload-time = "2025-08-30T00:13:03.24Z" }, + { url = "https://files.pythonhosted.org/packages/9c/be/c443ea428fb2488a3ea7608714b1bd85a82738c45da21b447dc49e2f8e5d/matplotlib-3.10.6-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:662df55604a2f9a45435566d6e2660e41efe83cd94f4288dfbf1e6d1eae4b0bb", size = 9530166, upload-time = "2025-08-30T00:13:05.951Z" }, + { url = "https://files.pythonhosted.org/packages/a9/35/48441422b044d74034aea2a3e0d1a49023f12150ebc58f16600132b9bbaf/matplotlib-3.10.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:08f141d55148cd1fc870c3387d70ca4df16dee10e909b3b038782bd4bda6ea07", size = 9593105, upload-time = "2025-08-30T00:13:08.356Z" }, + { url = "https://files.pythonhosted.org/packages/45/c3/994ef20eb4154ab84cc08d033834555319e4af970165e6c8894050af0b3c/matplotlib-3.10.6-cp312-cp312-win_amd64.whl", hash = "sha256:590f5925c2d650b5c9d813c5b3b5fc53f2929c3f8ef463e4ecfa7e052044fb2b", size = 8122784, upload-time = "2025-08-30T00:13:10.367Z" }, + { url = "https://files.pythonhosted.org/packages/57/b8/5c85d9ae0e40f04e71bedb053aada5d6bab1f9b5399a0937afb5d6b02d98/matplotlib-3.10.6-cp312-cp312-win_arm64.whl", hash = "sha256:f44c8d264a71609c79a78d50349e724f5d5fc3684ead7c2a473665ee63d868aa", size = 7992823, upload-time = "2025-08-30T00:13:12.24Z" }, + { url = "https://files.pythonhosted.org/packages/a0/db/18380e788bb837e724358287b08e223b32bc8dccb3b0c12fa8ca20bc7f3b/matplotlib-3.10.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:819e409653c1106c8deaf62e6de6b8611449c2cd9939acb0d7d4e57a3d95cc7a", size = 8273231, upload-time = "2025-08-30T00:13:13.881Z" }, + { url = "https://files.pythonhosted.org/packages/d3/0f/38dd49445b297e0d4f12a322c30779df0d43cb5873c7847df8a82e82ec67/matplotlib-3.10.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:59c8ac8382fefb9cb71308dde16a7c487432f5255d8f1fd32473523abecfecdf", size = 8128730, upload-time = "2025-08-30T00:13:15.556Z" }, + { url = "https://files.pythonhosted.org/packages/e5/b8/9eea6630198cb303d131d95d285a024b3b8645b1763a2916fddb44ca8760/matplotlib-3.10.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:84e82d9e0fd70c70bc55739defbd8055c54300750cbacf4740c9673a24d6933a", size = 8698539, upload-time = "2025-08-30T00:13:17.297Z" }, + { url = "https://files.pythonhosted.org/packages/71/34/44c7b1f075e1ea398f88aeabcc2907c01b9cc99e2afd560c1d49845a1227/matplotlib-3.10.6-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:25f7a3eb42d6c1c56e89eacd495661fc815ffc08d9da750bca766771c0fd9110", size = 9529702, upload-time = "2025-08-30T00:13:19.248Z" }, + { url = "https://files.pythonhosted.org/packages/b5/7f/e5c2dc9950c7facaf8b461858d1b92c09dd0cf174fe14e21953b3dda06f7/matplotlib-3.10.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f9c862d91ec0b7842920a4cfdaaec29662195301914ea54c33e01f1a28d014b2", size = 9593742, upload-time = "2025-08-30T00:13:21.181Z" }, + { url = "https://files.pythonhosted.org/packages/ff/1d/70c28528794f6410ee2856cd729fa1f1756498b8d3126443b0a94e1a8695/matplotlib-3.10.6-cp313-cp313-win_amd64.whl", hash = "sha256:1b53bd6337eba483e2e7d29c5ab10eee644bc3a2491ec67cc55f7b44583ffb18", size = 8122753, upload-time = "2025-08-30T00:13:23.44Z" }, + { url = "https://files.pythonhosted.org/packages/e8/74/0e1670501fc7d02d981564caf7c4df42974464625935424ca9654040077c/matplotlib-3.10.6-cp313-cp313-win_arm64.whl", hash = "sha256:cbd5eb50b7058b2892ce45c2f4e92557f395c9991f5c886d1bb74a1582e70fd6", size = 7992973, upload-time = "2025-08-30T00:13:26.632Z" }, + { url = "https://files.pythonhosted.org/packages/b1/4e/60780e631d73b6b02bd7239f89c451a72970e5e7ec34f621eda55cd9a445/matplotlib-3.10.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:acc86dd6e0e695c095001a7fccff158c49e45e0758fdf5dcdbb0103318b59c9f", size = 8316869, upload-time = "2025-08-30T00:13:28.262Z" }, + { url = "https://files.pythonhosted.org/packages/f8/15/baa662374a579413210fc2115d40c503b7360a08e9cc254aa0d97d34b0c1/matplotlib-3.10.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e228cd2ffb8f88b7d0b29e37f68ca9aaf83e33821f24a5ccc4f082dd8396bc27", size = 8178240, upload-time = "2025-08-30T00:13:30.007Z" }, + { url = "https://files.pythonhosted.org/packages/c6/3f/3c38e78d2aafdb8829fcd0857d25aaf9e7dd2dfcf7ec742765b585774931/matplotlib-3.10.6-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:658bc91894adeab669cf4bb4a186d049948262987e80f0857216387d7435d833", size = 8711719, upload-time = "2025-08-30T00:13:31.72Z" }, + { url = "https://files.pythonhosted.org/packages/96/4b/2ec2bbf8cefaa53207cc56118d1fa8a0f9b80642713ea9390235d331ede4/matplotlib-3.10.6-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8913b7474f6dd83ac444c9459c91f7f0f2859e839f41d642691b104e0af056aa", size = 9541422, upload-time = "2025-08-30T00:13:33.611Z" }, + { url = "https://files.pythonhosted.org/packages/83/7d/40255e89b3ef11c7871020563b2dd85f6cb1b4eff17c0f62b6eb14c8fa80/matplotlib-3.10.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:091cea22e059b89f6d7d1a18e2c33a7376c26eee60e401d92a4d6726c4e12706", size = 9594068, upload-time = "2025-08-30T00:13:35.833Z" }, + { url = "https://files.pythonhosted.org/packages/f0/a9/0213748d69dc842537a113493e1c27daf9f96bd7cc316f933dc8ec4de985/matplotlib-3.10.6-cp313-cp313t-win_amd64.whl", hash = "sha256:491e25e02a23d7207629d942c666924a6b61e007a48177fdd231a0097b7f507e", size = 8200100, upload-time = "2025-08-30T00:13:37.668Z" }, + { url = "https://files.pythonhosted.org/packages/be/15/79f9988066ce40b8a6f1759a934ea0cde8dc4adc2262255ee1bc98de6ad0/matplotlib-3.10.6-cp313-cp313t-win_arm64.whl", hash = "sha256:3d80d60d4e54cda462e2cd9a086d85cd9f20943ead92f575ce86885a43a565d5", size = 8042142, upload-time = "2025-08-30T00:13:39.426Z" }, + { url = "https://files.pythonhosted.org/packages/7c/58/e7b6d292beae6fb4283ca6fb7fa47d7c944a68062d6238c07b497dd35493/matplotlib-3.10.6-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:70aaf890ce1d0efd482df969b28a5b30ea0b891224bb315810a3940f67182899", size = 8273802, upload-time = "2025-08-30T00:13:41.006Z" }, + { url = "https://files.pythonhosted.org/packages/9f/f6/7882d05aba16a8cdd594fb9a03a9d3cca751dbb6816adf7b102945522ee9/matplotlib-3.10.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1565aae810ab79cb72e402b22facfa6501365e73ebab70a0fdfb98488d2c3c0c", size = 8131365, upload-time = "2025-08-30T00:13:42.664Z" }, + { url = "https://files.pythonhosted.org/packages/94/bf/ff32f6ed76e78514e98775a53715eca4804b12bdcf35902cdd1cf759d324/matplotlib-3.10.6-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3b23315a01981689aa4e1a179dbf6ef9fbd17143c3eea77548c2ecfb0499438", size = 9533961, upload-time = "2025-08-30T00:13:44.372Z" }, + { url = "https://files.pythonhosted.org/packages/fe/c3/6bf88c2fc2da7708a2ff8d2eeb5d68943130f50e636d5d3dcf9d4252e971/matplotlib-3.10.6-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:30fdd37edf41a4e6785f9b37969de57aea770696cb637d9946eb37470c94a453", size = 9804262, upload-time = "2025-08-30T00:13:46.614Z" }, + { url = "https://files.pythonhosted.org/packages/0f/7a/e05e6d9446d2d577b459427ad060cd2de5742d0e435db3191fea4fcc7e8b/matplotlib-3.10.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:bc31e693da1c08012c764b053e702c1855378e04102238e6a5ee6a7117c53a47", size = 9595508, upload-time = "2025-08-30T00:13:48.731Z" }, + { url = "https://files.pythonhosted.org/packages/39/fb/af09c463ced80b801629fd73b96f726c9f6124c3603aa2e480a061d6705b/matplotlib-3.10.6-cp314-cp314-win_amd64.whl", hash = "sha256:05be9bdaa8b242bc6ff96330d18c52f1fc59c6fb3a4dd411d953d67e7e1baf98", size = 8252742, upload-time = "2025-08-30T00:13:50.539Z" }, + { url = "https://files.pythonhosted.org/packages/b1/f9/b682f6db9396d9ab8f050c0a3bfbb5f14fb0f6518f08507c04cc02f8f229/matplotlib-3.10.6-cp314-cp314-win_arm64.whl", hash = "sha256:f56a0d1ab05d34c628592435781d185cd99630bdfd76822cd686fb5a0aecd43a", size = 8124237, upload-time = "2025-08-30T00:13:54.3Z" }, + { url = "https://files.pythonhosted.org/packages/b5/d2/b69b4a0923a3c05ab90527c60fdec899ee21ca23ede7f0fb818e6620d6f2/matplotlib-3.10.6-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:94f0b4cacb23763b64b5dace50d5b7bfe98710fed5f0cef5c08135a03399d98b", size = 8316956, upload-time = "2025-08-30T00:13:55.932Z" }, + { url = "https://files.pythonhosted.org/packages/28/e9/dc427b6f16457ffaeecb2fc4abf91e5adb8827861b869c7a7a6d1836fa73/matplotlib-3.10.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:cc332891306b9fb39462673d8225d1b824c89783fee82840a709f96714f17a5c", size = 8178260, upload-time = "2025-08-30T00:14:00.942Z" }, + { url = "https://files.pythonhosted.org/packages/c4/89/1fbd5ad611802c34d1c7ad04607e64a1350b7fb9c567c4ec2c19e066ed35/matplotlib-3.10.6-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee1d607b3fb1590deb04b69f02ea1d53ed0b0bf75b2b1a5745f269afcbd3cdd3", size = 9541422, upload-time = "2025-08-30T00:14:02.664Z" }, + { url = "https://files.pythonhosted.org/packages/b0/3b/65fec8716025b22c1d72d5a82ea079934c76a547696eaa55be6866bc89b1/matplotlib-3.10.6-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:376a624a218116461696b27b2bbf7a8945053e6d799f6502fc03226d077807bf", size = 9803678, upload-time = "2025-08-30T00:14:04.741Z" }, + { url = "https://files.pythonhosted.org/packages/c7/b0/40fb2b3a1ab9381bb39a952e8390357c8be3bdadcf6d5055d9c31e1b35ae/matplotlib-3.10.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:83847b47f6524c34b4f2d3ce726bb0541c48c8e7692729865c3df75bfa0f495a", size = 9594077, upload-time = "2025-08-30T00:14:07.012Z" }, + { url = "https://files.pythonhosted.org/packages/76/34/c4b71b69edf5b06e635eee1ed10bfc73cf8df058b66e63e30e6a55e231d5/matplotlib-3.10.6-cp314-cp314t-win_amd64.whl", hash = "sha256:c7e0518e0d223683532a07f4b512e2e0729b62674f1b3a1a69869f98e6b1c7e3", size = 8342822, upload-time = "2025-08-30T00:14:09.041Z" }, + { url = "https://files.pythonhosted.org/packages/e8/62/aeabeef1a842b6226a30d49dd13e8a7a1e81e9ec98212c0b5169f0a12d83/matplotlib-3.10.6-cp314-cp314t-win_arm64.whl", hash = "sha256:4dd83e029f5b4801eeb87c64efd80e732452781c16a9cf7415b7b63ec8f374d7", size = 8172588, upload-time = "2025-08-30T00:14:11.166Z" }, + { url = "https://files.pythonhosted.org/packages/12/bb/02c35a51484aae5f49bd29f091286e7af5f3f677a9736c58a92b3c78baeb/matplotlib-3.10.6-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:f2d684c3204fa62421bbf770ddfebc6b50130f9cad65531eeba19236d73bb488", size = 8252296, upload-time = "2025-08-30T00:14:19.49Z" }, + { url = "https://files.pythonhosted.org/packages/7d/85/41701e3092005aee9a2445f5ee3904d9dbd4a7df7a45905ffef29b7ef098/matplotlib-3.10.6-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:6f4a69196e663a41d12a728fab8751177215357906436804217d6d9cf0d4d6cf", size = 8116749, upload-time = "2025-08-30T00:14:21.344Z" }, + { url = "https://files.pythonhosted.org/packages/16/53/8d8fa0ea32a8c8239e04d022f6c059ee5e1b77517769feccd50f1df43d6d/matplotlib-3.10.6-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d6ca6ef03dfd269f4ead566ec6f3fb9becf8dab146fb999022ed85ee9f6b3eb", size = 8693933, upload-time = "2025-08-30T00:14:22.942Z" }, ] [[package]] @@ -2327,7 +2312,7 @@ wheels = [ [[package]] name = "onnxruntime" -version = "1.25.0" +version = "1.25.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "flatbuffers" }, @@ -2336,108 +2321,102 @@ dependencies = [ { name = "protobuf" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/66/67/edb2cb6c4a38ebf539c3529c5449d68b8031f3006d8ee36574a6d45559b2/onnxruntime-1.25.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:a71baa8e0e2f3417106e3a8b2183fd5741875b998041f1a2422a1d0240f302cb", size = 17727193, upload-time = "2026-04-22T17:20:31.582Z" }, - { url = "https://files.pythonhosted.org/packages/ce/87/21324e795f504ee5abd9f2a6c0ab87c8b88a5efbe683373199cb26b7e9b6/onnxruntime-1.25.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:15f220ed2ac4a549c97a31e57f21311add9f13d381f13ed1a52be5b25275038c", size = 15830173, upload-time = "2026-04-22T17:19:27.796Z" }, - { url = "https://files.pythonhosted.org/packages/2c/04/dac532fe3c2babf052209788f10c3458af0016cd72847f3f2c2f645a48d6/onnxruntime-1.25.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a349feb15476092372eb045a340cd88e53f7965ce5b489ebf5fea20c0bd49add", size = 17978264, upload-time = "2026-04-22T17:19:50.952Z" }, - { url = "https://files.pythonhosted.org/packages/7b/6b/6309ae7c8b27ffb7b5c888517c64ff21e1dcc67a9cfcbf3b4d014f4522e8/onnxruntime-1.25.0-cp311-cp311-win_amd64.whl", hash = "sha256:d32eff37efac78c676f1a3b3102863de5e55fbabc18348ec2c1439c18f3e90fa", size = 12866299, upload-time = "2026-04-22T17:20:19.939Z" }, - { url = "https://files.pythonhosted.org/packages/54/44/35fe3af177b584e9ade0f1ef934c4c04016a9c967bfa646f82ae5913eaa1/onnxruntime-1.25.0-cp311-cp311-win_arm64.whl", hash = "sha256:fcb074b3c62ffa315e222ad246e46125b046b9b531c719852deda7c89b72ebb7", size = 12592117, upload-time = "2026-04-22T17:20:08.513Z" }, - { url = "https://files.pythonhosted.org/packages/7a/69/f98c6bda4c34ac382b70c36033a989ceffd1caf5afba47bd2ef26535850f/onnxruntime-1.25.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:8ecd3362de3fb496fb3e2d055a95d5acab611cf759a27609c6d99704c9d8f184", size = 17742518, upload-time = "2026-04-22T17:20:34.444Z" }, - { url = "https://files.pythonhosted.org/packages/5a/c6/19c5bfbc60396791e975652f982bcff9ff4b27947c8e2bf0064ac5d5727b/onnxruntime-1.25.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c99238d20bfa80ac68c7b03c2c936d389189ae40997f78a30d151570d7e18bf", size = 15841110, upload-time = "2026-04-22T17:19:31.284Z" }, - { url = "https://files.pythonhosted.org/packages/a9/1b/d681878f227513917d8620e4ea504af5eb3313fc01f8aea7b19a976c65db/onnxruntime-1.25.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:be93baa694ef8e5831fcb7b542da21f502b122918b5b9612d9f02972e043ee01", size = 17996146, upload-time = "2026-04-22T17:19:53.792Z" }, - { url = "https://files.pythonhosted.org/packages/55/fe/ec98e416bd75063dea1e493661c7c939e18660ee41d6a63d7221e5657f48/onnxruntime-1.25.0-cp312-cp312-win_amd64.whl", hash = "sha256:9596040c1f7d247bbfab5d4db1e7651c790235e48e460c7d445ec81687d5a182", size = 12872370, upload-time = "2026-04-22T17:20:22.856Z" }, - { url = "https://files.pythonhosted.org/packages/f7/86/9a1ac7c8a8eba7967935d4c109fc956d8f9ba61cba61d9368315bb27d0bc/onnxruntime-1.25.0-cp312-cp312-win_arm64.whl", hash = "sha256:463aed7f5e4a3ca5a476db7e9bba9164fa26921ef34c37e59b28c4c61e55f266", size = 12600072, upload-time = "2026-04-22T17:20:11.523Z" }, - { url = "https://files.pythonhosted.org/packages/c1/5f/3b916a303f43e9c7eed3a705ea69f6867233c161dede30f4df21538c6693/onnxruntime-1.25.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:1b3d76cf770afba76859f270679c9ad0b017b9357eb5892e91926943e05ca82c", size = 17743247, upload-time = "2026-04-22T17:19:45.206Z" }, - { url = "https://files.pythonhosted.org/packages/d5/b3/9e45ba86ed39ab688578f21dd39ed4b575726205596891870a1a8b4d5ca9/onnxruntime-1.25.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cddb565dfd630550a8817b3d5493ffcfa0fec273b545b2816f2fce53384e1151", size = 15841442, upload-time = "2026-04-22T17:19:34.209Z" }, - { url = "https://files.pythonhosted.org/packages/d2/c4/810809e3b411fd66958bdd7285a63acf948988ab4189e1fd860a2f999db3/onnxruntime-1.25.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ade74e651e28b39e6bfd6f576cb9b8a4edfa0916234145154dc891bd55331c22", size = 17993660, upload-time = "2026-04-22T17:19:56.719Z" }, - { url = "https://files.pythonhosted.org/packages/42/3d/b736cda9c71b3df022ca6bbcb991d14b7723c068dbebe826af9102e79777/onnxruntime-1.25.0-cp313-cp313-win_amd64.whl", hash = "sha256:9196c32c039c37ce8362cbee0aa3a704679be5f2b6fb3e849fea927c98fe1e5b", size = 12871906, upload-time = "2026-04-22T17:20:25.705Z" }, - { url = "https://files.pythonhosted.org/packages/0d/1f/d7bb87cdbb839b356133e9f9e3851fc0c3130dd1c360640c9ce948e3e083/onnxruntime-1.25.0-cp313-cp313-win_arm64.whl", hash = "sha256:b3e52dc2208dec6f61ef118dff04610927e9a18d99e019a828799b23cc9cdea4", size = 12599753, upload-time = "2026-04-22T17:20:14.661Z" }, - { url = "https://files.pythonhosted.org/packages/04/3c/edb0d825a65beed40a3de8a51521d49d433aa767f8d00e633cd2602024c0/onnxruntime-1.25.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:de8548d8fe8fd58ca841178051d535d6f378efae14a4b4eb336617d80540fb41", size = 15852628, upload-time = "2026-04-22T17:19:36.886Z" }, - { url = "https://files.pythonhosted.org/packages/55/51/7a660b4d087f27b273ff725f744880e7664f64a9331bfb1eae91ed2a9f0a/onnxruntime-1.25.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4edec672d09e34b9e83ad09c44454ce97627388f32858b1d59fe01d091ff54b5", size = 17997241, upload-time = "2026-04-22T17:19:59.653Z" }, - { url = "https://files.pythonhosted.org/packages/78/be/5254acb849f414c8fb2643fe21f2c2ef8089fab18569f24775ccb8ee182d/onnxruntime-1.25.0-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:38f27febd2ff034a600a8bdbea34b1f7c961a2dab6bcb5351e70548fea456161", size = 17744932, upload-time = "2026-04-22T17:19:48.097Z" }, - { url = "https://files.pythonhosted.org/packages/49/98/c2593aaa392e278a41bec35a00298aa5f22bb382483ad02ca451a556b2a2/onnxruntime-1.25.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e0ae389ed1647f11c1b501ba1cef1e2c7453002f626136ace214c9c46153ee4", size = 15842603, upload-time = "2026-04-22T17:19:39.879Z" }, - { url = "https://files.pythonhosted.org/packages/08/b6/07e924b8a47adc9ce2f92a7ef71a6fb709981b1ebd08179f61cbce6ff9b3/onnxruntime-1.25.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7ca32d38173c0f58699ca9dc9e867de74d2c2ab7d1c2d969f862ee8633370b77", size = 17994808, upload-time = "2026-04-22T17:20:02.462Z" }, - { url = "https://files.pythonhosted.org/packages/56/31/e0147d87acfd06992a9bf45ffc070fd3ab49ff9a1f12de9fb403f2fc0b97/onnxruntime-1.25.0-cp314-cp314-win_amd64.whl", hash = "sha256:a2829e29621db7a4bcd457e6d0f3e4f541fb274c7127e7d2e1a5b46c70572672", size = 13183697, upload-time = "2026-04-22T17:20:28.658Z" }, - { url = "https://files.pythonhosted.org/packages/18/29/b1d5b91d04ae80768ed8e38639ab2fcc92750a67fddc30ad6b700f244113/onnxruntime-1.25.0-cp314-cp314-win_arm64.whl", hash = "sha256:2bed9b35568b3ecf8ab34dc832d37216e47947e86508a0fd6b75e4c19d7ba907", size = 12933438, upload-time = "2026-04-22T17:20:17.223Z" }, - { url = "https://files.pythonhosted.org/packages/56/f4/cfd47f88da545ea57c1f2a4b5886d455ec64f53b723b1a448fc44ed757e9/onnxruntime-1.25.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:00548a16e8f0d52cb1c67ef50177e5e2be848ccffc6db60010ee37faaccbbb6f", size = 15853591, upload-time = "2026-04-22T17:19:42.325Z" }, - { url = "https://files.pythonhosted.org/packages/89/de/8b406be6ea4f2c254f9bc850cbe8038064c7768a94cdf7785420b3652ea7/onnxruntime-1.25.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a071a0740388e0ffad081c583761f37837b113bde3d03dc70790ed6cf4f4de0b", size = 17996166, upload-time = "2026-04-22T17:20:05.873Z" }, + { url = "https://files.pythonhosted.org/packages/c1/00/dccf702195572df51a40784fc939304595a0ae3577537d3b5be79273151a/onnxruntime-1.25.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:5cf58ec7601120bb4370f0b868f794d3e3626db7b1b1dba366c27874b224e9de", size = 17762805, upload-time = "2026-04-27T22:00:45.336Z" }, + { url = "https://files.pythonhosted.org/packages/64/2a/54a784e321093459ed18b8430ebb043af9049838a8b2c485fa7d41dca181/onnxruntime-1.25.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fa7d4daa78a18b8f3b410e31e82dab8580363c85cac644179a853f2748618e89", size = 15866935, upload-time = "2026-04-27T21:59:33.842Z" }, + { url = "https://files.pythonhosted.org/packages/b4/18/e3966c6035789a0b5b494ca0a9a5f331d57b5c15ae7795b9fffae2e277c5/onnxruntime-1.25.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:79162f873cdfa38cfc8d53d59a8dc7a71a14074df3d565b2f8ce24289545ddc0", size = 18007288, upload-time = "2026-04-27T22:00:02.34Z" }, + { url = "https://files.pythonhosted.org/packages/19/c1/a08f7ce3959af4ea7017210233a129c4c3260d08f728fdf6c0d4b743ce2d/onnxruntime-1.25.1-cp311-cp311-win_amd64.whl", hash = "sha256:451b9494056f7f96b1be76a32745ccc4582bd61b2a0e1bc52de3708446151d5d", size = 12899002, upload-time = "2026-04-27T22:00:33.956Z" }, + { url = "https://files.pythonhosted.org/packages/d3/f4/95de11cdc1b50686454c041273c9f84e67c4d1bc3ee40e36fa3dafe74c0a/onnxruntime-1.25.1-cp311-cp311-win_arm64.whl", hash = "sha256:7e608f8950076da02c0aeceec2dd790d201eeb31dd73acb04ec989b2bf6199dc", size = 12625773, upload-time = "2026-04-27T22:00:22.483Z" }, + { url = "https://files.pythonhosted.org/packages/c0/52/8b2a10e8dedf5d486332bc2b3bca0b1ed8049c0b9e4a5cced95413aadfdd/onnxruntime-1.25.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:66e52f7a30d1f780a34aa84d68a0a04d382d9f5b141884ecbf45b7566b9fbde9", size = 17770987, upload-time = "2026-04-27T22:00:47.985Z" }, + { url = "https://files.pythonhosted.org/packages/3f/87/a424d2867477c42ef8c60172709281120797f7b0f1fd33cc36b24329c825/onnxruntime-1.25.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a5f41779f044d1ff75593df5c10a4d311bc82563687796d5218e2685b8f9da25", size = 15871829, upload-time = "2026-04-27T21:59:39.088Z" }, + { url = "https://files.pythonhosted.org/packages/d4/55/7819e64c515f17c86005447ede8122b974ca851255a94125e2119376f0f8/onnxruntime-1.25.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:905409e9eb2ef87f8226e073f56e71faf731c3e480ebd34952cf953730e4a4ff", size = 18024586, upload-time = "2026-04-27T22:00:05.359Z" }, + { url = "https://files.pythonhosted.org/packages/89/36/b4f3eb5e95c66389aafd490950b5255e87c9333742cf90516eb50898e1dc/onnxruntime-1.25.1-cp312-cp312-win_amd64.whl", hash = "sha256:d4097b75b77486bb45835a8ed25b9a67976040ec6c258aeabae6aadfbdd1201c", size = 12905112, upload-time = "2026-04-27T22:00:36.478Z" }, + { url = "https://files.pythonhosted.org/packages/38/fa/e5c43397632a399f542663ed3e3e37763ee203ba845b10b266cd2ede8925/onnxruntime-1.25.1-cp312-cp312-win_arm64.whl", hash = "sha256:b6c7aa5cae606d5c90a392679fac074b60f80025a2e83e1e90fdf882bd2a97f0", size = 12634433, upload-time = "2026-04-27T22:00:25.918Z" }, + { url = "https://files.pythonhosted.org/packages/d2/ee/db3ac55ef770347a926ac0f1317df0ab42c8bc604350833b30c7356bf936/onnxruntime-1.25.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e9d9b3b1694196bc3c5bc66f760a237a5e27d7688aaa2e2c9c0f66abd0486699", size = 17770761, upload-time = "2026-04-27T21:59:54.853Z" }, + { url = "https://files.pythonhosted.org/packages/dc/9a/33225481a94a59906fce44e27ab12fc3bddd2aaecdc6160bd73341ca1aba/onnxruntime-1.25.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:311d29b943e46a55ca72ca1ea48d7815c993122bfc359f68215fddeb9583fff4", size = 15871542, upload-time = "2026-04-27T21:59:41.881Z" }, + { url = "https://files.pythonhosted.org/packages/8b/09/f20aac60f6fcf840543be54d4e9252cfeb7e8c2bb6d22477aaeb180e763e/onnxruntime-1.25.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:98016a038b31160db23208706139fa3b99cd60bc1c5ffdade77aafd6a37a92ad", size = 18036960, upload-time = "2026-04-27T22:00:10.739Z" }, + { url = "https://files.pythonhosted.org/packages/50/83/47964ac7e2f7e2f9e83c69ec466642c6835466252cc2ef0561eafeb56b66/onnxruntime-1.25.1-cp313-cp313-win_amd64.whl", hash = "sha256:08717d6eee2820807ba60b1b17032af207bd7aaca5b6c4abaee71f83feae877b", size = 12904886, upload-time = "2026-04-27T22:00:39.878Z" }, + { url = "https://files.pythonhosted.org/packages/d4/6c/a6c5aea47dc95fca7728f8a5af67c184ec9e7d4e7882125c7062e4bba8dd/onnxruntime-1.25.1-cp313-cp313-win_arm64.whl", hash = "sha256:84f8963d70e00167bae273ab7e80e9795bfc5eb94f6b23236a99c5c11af00844", size = 12634117, upload-time = "2026-04-27T22:00:29.15Z" }, + { url = "https://files.pythonhosted.org/packages/a8/8a/3b65e7911eec86c125e3d6f43d690a6f68671500543c0390ecd6eb59b771/onnxruntime-1.25.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:03e800b3a4b48d9f3a2d23aacc4fa95486a3b406b14e51d1a9b8b6981d9adf9c", size = 15882935, upload-time = "2026-04-27T21:59:44.912Z" }, + { url = "https://files.pythonhosted.org/packages/3c/bb/410a760694f8ae7bbfc5fa81ccbeb7da241e6d520ee02a333a439cf462a2/onnxruntime-1.25.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd83ef5c10cfc051a1cb465db692d57b996a1bc75a2a97b161398e29cdbc47ff", size = 18021727, upload-time = "2026-04-27T22:00:13.846Z" }, + { url = "https://files.pythonhosted.org/packages/fb/aa/04530bd38e31e26970fa1212346d76cf81705dc16a8ee5e6f4fb24634c11/onnxruntime-1.25.1-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:395eb662c437fa2407f44266e4778b75bff261b17c2a6fef042421f9069f871d", size = 17773721, upload-time = "2026-04-27T21:59:59.24Z" }, + { url = "https://files.pythonhosted.org/packages/ef/7f/ec79ab5cece6a688c944a7fa214a8511d548b9d5142a15d1a3d730b705f1/onnxruntime-1.25.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9ae85395f41b291ae3e61780ec5092640181d369ef6c268aa8141c478b509e69", size = 15875954, upload-time = "2026-04-27T21:59:49.394Z" }, + { url = "https://files.pythonhosted.org/packages/67/fe/20428215d822099ea2c1e3cf35c295cf1a58f467bf18b6c607597a39c18a/onnxruntime-1.25.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:828e1b12710fbedb6dfab5e7bae6f11563617cddf3c2e7e8d84c64de566a4a3a", size = 18038703, upload-time = "2026-04-27T22:00:16.199Z" }, + { url = "https://files.pythonhosted.org/packages/5a/b1/b15db965e6a68bc47ca7eb584de4e6b3d2d2f484d46cc57f715b596f6528/onnxruntime-1.25.1-cp314-cp314-win_amd64.whl", hash = "sha256:2affc9d2fd9ab013b9c9637464e649a0cca870d57ae18bfef74180eee65c3369", size = 13218513, upload-time = "2026-04-27T22:00:42.506Z" }, + { url = "https://files.pythonhosted.org/packages/5a/f9/25cd2d1b29cdc8140eee4afbb6fb930b69125526632b1d579bc747975306/onnxruntime-1.25.1-cp314-cp314-win_arm64.whl", hash = "sha256:3387d75d1a815b4b2495b4e47a05ef1b3bcb64a817ddc68587e0bfcb9702bcf6", size = 12969835, upload-time = "2026-04-27T22:00:31.504Z" }, + { url = "https://files.pythonhosted.org/packages/8d/0e/6c507d1e65b2421fb44e241cbba577c7276792279485024fb1752b43f5c5/onnxruntime-1.25.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:06280b06604660595037f783c6d24bc70cbe5c6093975f194cd1482e77d450de", size = 15883298, upload-time = "2026-04-27T21:59:51.991Z" }, + { url = "https://files.pythonhosted.org/packages/df/4e/1c9df57496409dc86b320bd38f29ad7a34b7115e4f35b8fca44a827568a7/onnxruntime-1.25.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7e79fd5ce7db10ebcc24e020e2ed0159476e69e2326b9b7828e5aadcf6184212", size = 18021249, upload-time = "2026-04-27T22:00:18.954Z" }, ] [[package]] name = "packaging" -version = "26.1" +version = "26.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/df/de/0d2b39fb4af88a0258f3bac87dfcbb48e73fbdea4a2ed0e2213f9a4c2f9a/packaging-26.1.tar.gz", hash = "sha256:f042152b681c4bfac5cae2742a55e103d27ab2ec0f3d88037136b6bfe7c9c5de", size = 215519, upload-time = "2026-04-14T21:12:49.362Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/c2/920ef838e2f0028c8262f16101ec09ebd5969864e5a64c4c05fad0617c56/packaging-26.1-py3-none-any.whl", hash = "sha256:5d9c0669c6285e491e0ced2eee587eaf67b670d94a19e94e3984a481aba6802f", size = 95831, upload-time = "2026-04-14T21:12:47.56Z" }, + { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, ] [[package]] name = "pandas" -version = "3.0.2" +version = "2.3.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, { name = "python-dateutil" }, - { name = "tzdata", marker = "sys_platform == 'emscripten' or sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/da/99/b342345300f13440fe9fe385c3c481e2d9a595ee3bab4d3219247ac94e9a/pandas-3.0.2.tar.gz", hash = "sha256:f4753e73e34c8d83221ba58f232433fca2748be8b18dbca02d242ed153945043", size = 4645855, upload-time = "2026-03-31T06:48:30.816Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/97/35/6411db530c618e0e0005187e35aa02ce60ae4c4c4d206964a2f978217c27/pandas-3.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a727a73cbdba2f7458dc82449e2315899d5140b449015d822f515749a46cbbe0", size = 10326926, upload-time = "2026-03-31T06:46:08.29Z" }, - { url = "https://files.pythonhosted.org/packages/c4/d3/b7da1d5d7dbdc5ef52ed7debd2b484313b832982266905315dad5a0bf0b1/pandas-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dbbd4aa20ca51e63b53bbde6a0fa4254b1aaabb74d2f542df7a7959feb1d760c", size = 9926987, upload-time = "2026-03-31T06:46:11.724Z" }, - { url = "https://files.pythonhosted.org/packages/52/77/9b1c2d6070b5dbe239a7bc889e21bfa58720793fb902d1e070695d87c6d0/pandas-3.0.2-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:339dda302bd8369dedeae979cb750e484d549b563c3f54f3922cb8ff4978c5eb", size = 10757067, upload-time = "2026-03-31T06:46:14.903Z" }, - { url = "https://files.pythonhosted.org/packages/20/17/ec40d981705654853726e7ac9aea9ddbb4a5d9cf54d8472222f4f3de06c2/pandas-3.0.2-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:61c2fd96d72b983a9891b2598f286befd4ad262161a609c92dc1652544b46b76", size = 11258787, upload-time = "2026-03-31T06:46:17.683Z" }, - { url = "https://files.pythonhosted.org/packages/90/e3/3f1126d43d3702ca8773871a81c9f15122a1f412342cc56284ffda5b1f70/pandas-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c934008c733b8bbea273ea308b73b3156f0181e5b72960790b09c18a2794fe1e", size = 11771616, upload-time = "2026-03-31T06:46:20.532Z" }, - { url = "https://files.pythonhosted.org/packages/2e/cf/0f4e268e1f5062e44a6bda9f925806721cd4c95c2b808a4c82ebe914f96b/pandas-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:60a80bb4feacbef5e1447a3f82c33209c8b7e07f28d805cfd1fb951e5cb443aa", size = 12337623, upload-time = "2026-03-31T06:46:23.754Z" }, - { url = "https://files.pythonhosted.org/packages/44/a0/97a6339859d4acb2536efb24feb6708e82f7d33b2ed7e036f2983fcced82/pandas-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:ed72cb3f45190874eb579c64fa92d9df74e98fd63e2be7f62bce5ace0ade61df", size = 9897372, upload-time = "2026-03-31T06:46:26.703Z" }, - { url = "https://files.pythonhosted.org/packages/8f/eb/781516b808a99ddf288143cec46b342b3016c3414d137da1fdc3290d8860/pandas-3.0.2-cp311-cp311-win_arm64.whl", hash = "sha256:f12b1a9e332c01e09510586f8ca9b108fd631fd656af82e452d7315ef6df5f9f", size = 9154922, upload-time = "2026-03-31T06:46:30.284Z" }, - { url = "https://files.pythonhosted.org/packages/f3/b0/c20bd4d6d3f736e6bd6b55794e9cd0a617b858eaad27c8f410ea05d953b7/pandas-3.0.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:232a70ebb568c0c4d2db4584f338c1577d81e3af63292208d615907b698a0f18", size = 10347921, upload-time = "2026-03-31T06:46:33.36Z" }, - { url = "https://files.pythonhosted.org/packages/35/d0/4831af68ce30cc2d03c697bea8450e3225a835ef497d0d70f31b8cdde965/pandas-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:970762605cff1ca0d3f71ed4f3a769ea8f85fc8e6348f6e110b8fea7e6eb5a14", size = 9888127, upload-time = "2026-03-31T06:46:36.253Z" }, - { url = "https://files.pythonhosted.org/packages/61/a9/16ea9346e1fc4a96e2896242d9bc674764fb9049b0044c0132502f7a771e/pandas-3.0.2-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aff4e6f4d722e0652707d7bcb190c445fe58428500c6d16005b02401764b1b3d", size = 10399577, upload-time = "2026-03-31T06:46:39.224Z" }, - { url = "https://files.pythonhosted.org/packages/c4/a8/3a61a721472959ab0ce865ef05d10b0d6bfe27ce8801c99f33d4fa996e65/pandas-3.0.2-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ef8b27695c3d3dc78403c9a7d5e59a62d5464a7e1123b4e0042763f7104dc74f", size = 10880030, upload-time = "2026-03-31T06:46:42.412Z" }, - { url = "https://files.pythonhosted.org/packages/da/65/7225c0ea4d6ce9cb2160a7fb7f39804871049f016e74782e5dade4d14109/pandas-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f8d68083e49e16b84734eb1a4dcae4259a75c90fb6e2251ab9a00b61120c06ab", size = 11409468, upload-time = "2026-03-31T06:46:45.2Z" }, - { url = "https://files.pythonhosted.org/packages/fa/5b/46e7c76032639f2132359b5cf4c785dd8cf9aea5ea64699eac752f02b9db/pandas-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:32cc41f310ebd4a296d93515fcac312216adfedb1894e879303987b8f1e2b97d", size = 11936381, upload-time = "2026-03-31T06:46:48.293Z" }, - { url = "https://files.pythonhosted.org/packages/7b/8b/721a9cff6fa6a91b162eb51019c6243b82b3226c71bb6c8ef4a9bd65cbc6/pandas-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:a4785e1d6547d8427c5208b748ae2efb64659a21bd82bf440d4262d02bfa02a4", size = 9744993, upload-time = "2026-03-31T06:46:51.488Z" }, - { url = "https://files.pythonhosted.org/packages/d5/18/7f0bd34ae27b28159aa80f2a6799f47fda34f7fb938a76e20c7b7fe3b200/pandas-3.0.2-cp312-cp312-win_arm64.whl", hash = "sha256:08504503f7101300107ecdc8df73658e4347586db5cfdadabc1592e9d7e7a0fd", size = 9056118, upload-time = "2026-03-31T06:46:54.548Z" }, - { url = "https://files.pythonhosted.org/packages/bf/ca/3e639a1ea6fcd0617ca4e8ca45f62a74de33a56ae6cd552735470b22c8d3/pandas-3.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b5918ba197c951dec132b0c5929a00c0bf05d5942f590d3c10a807f6e15a57d3", size = 10321105, upload-time = "2026-03-31T06:46:57.327Z" }, - { url = "https://files.pythonhosted.org/packages/0b/77/dbc82ff2fb0e63c6564356682bf201edff0ba16c98630d21a1fb312a8182/pandas-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d606a041c89c0a474a4702d532ab7e73a14fe35c8d427b972a625c8e46373668", size = 9864088, upload-time = "2026-03-31T06:46:59.935Z" }, - { url = "https://files.pythonhosted.org/packages/5c/2b/341f1b04bbca2e17e13cd3f08c215b70ef2c60c5356ef1e8c6857449edc7/pandas-3.0.2-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:710246ba0616e86891b58ab95f2495143bb2bc83ab6b06747c74216f583a6ac9", size = 10369066, upload-time = "2026-03-31T06:47:02.792Z" }, - { url = "https://files.pythonhosted.org/packages/12/c5/cbb1ffefb20a93d3f0e1fdcda699fb84976210d411b008f97f48bf6ce27e/pandas-3.0.2-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5d3cfe227c725b1f3dff4278b43d8c784656a42a9325b63af6b1492a8232209e", size = 10876780, upload-time = "2026-03-31T06:47:06.205Z" }, - { url = "https://files.pythonhosted.org/packages/98/fe/2249ae5e0a69bd0ddf17353d0a5d26611d70970111f5b3600cdc8be883e7/pandas-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c3b723df9087a9a9a840e263ebd9f88b64a12075d1bf2ea401a5a42f254f084d", size = 11375181, upload-time = "2026-03-31T06:47:09.383Z" }, - { url = "https://files.pythonhosted.org/packages/de/64/77a38b09e70b6464883b8d7584ab543e748e42c1b5d337a2ee088e0df741/pandas-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a3096110bf9eac0070b7208465f2740e2d8a670d5cb6530b5bb884eca495fd39", size = 11928899, upload-time = "2026-03-31T06:47:12.686Z" }, - { url = "https://files.pythonhosted.org/packages/5e/52/42855bf626868413f761addd574acc6195880ae247a5346477a4361c3acb/pandas-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:07a10f5c36512eead51bc578eb3354ad17578b22c013d89a796ab5eee90cd991", size = 9746574, upload-time = "2026-03-31T06:47:15.64Z" }, - { url = "https://files.pythonhosted.org/packages/88/39/21304ae06a25e8bf9fc820d69b29b2c495b2ae580d1e143146c309941760/pandas-3.0.2-cp313-cp313-win_arm64.whl", hash = "sha256:5fdbfa05931071aba28b408e59226186b01eb5e92bea2ab78b65863ca3228d84", size = 9047156, upload-time = "2026-03-31T06:47:18.595Z" }, - { url = "https://files.pythonhosted.org/packages/72/20/7defa8b27d4f330a903bb68eea33be07d839c5ea6bdda54174efcec0e1d2/pandas-3.0.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:dbc20dea3b9e27d0e66d74c42b2d0c1bed9c2ffe92adea33633e3bedeb5ac235", size = 10756238, upload-time = "2026-03-31T06:47:22.012Z" }, - { url = "https://files.pythonhosted.org/packages/e9/95/49433c14862c636afc0e9b2db83ff16b3ad92959364e52b2955e44c8e94c/pandas-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b75c347eff42497452116ce05ef461822d97ce5b9ff8df6edacb8076092c855d", size = 10408520, upload-time = "2026-03-31T06:47:25.197Z" }, - { url = "https://files.pythonhosted.org/packages/3b/f8/462ad2b5881d6b8ec8e5f7ed2ea1893faa02290d13870a1600fe72ad8efc/pandas-3.0.2-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1478075142e83a5571782ad007fb201ed074bdeac7ebcc8890c71442e96adf7", size = 10324154, upload-time = "2026-03-31T06:47:28.097Z" }, - { url = "https://files.pythonhosted.org/packages/0a/65/d1e69b649cbcddda23ad6e4c40ef935340f6f652a006e5cbc3555ac8adb3/pandas-3.0.2-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5880314e69e763d4c8b27937090de570f1fb8d027059a7ada3f7f8e98bdcb677", size = 10714449, upload-time = "2026-03-31T06:47:30.85Z" }, - { url = "https://files.pythonhosted.org/packages/47/a4/85b59bc65b8190ea3689882db6cdf32a5003c0ccd5a586c30fdcc3ffc4fc/pandas-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b5329e26898896f06035241a626d7c335daa479b9bbc82be7c2742d048e41172", size = 11338475, upload-time = "2026-03-31T06:47:34.026Z" }, - { url = "https://files.pythonhosted.org/packages/1e/c4/bc6966c6e38e5d9478b935272d124d80a589511ed1612a5d21d36f664c68/pandas-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:81526c4afd31971f8b62671442a4b2b51e0aa9acc3819c9f0f12a28b6fcf85f1", size = 11786568, upload-time = "2026-03-31T06:47:36.941Z" }, - { url = "https://files.pythonhosted.org/packages/e8/74/09298ca9740beed1d3504e073d67e128aa07e5ca5ca2824b0c674c0b8676/pandas-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:7cadd7e9a44ec13b621aec60f9150e744cfc7a3dd32924a7e2f45edff31823b0", size = 10488652, upload-time = "2026-03-31T06:47:40.612Z" }, - { url = "https://files.pythonhosted.org/packages/bb/40/c6ea527147c73b24fc15c891c3fcffe9c019793119c5742b8784a062c7db/pandas-3.0.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:db0dbfd2a6cdf3770aa60464d50333d8f3d9165b2f2671bcc299b72de5a6677b", size = 10326084, upload-time = "2026-03-31T06:47:43.834Z" }, - { url = "https://files.pythonhosted.org/packages/95/25/bdb9326c3b5455f8d4d3549fce7abcf967259de146fe2cf7a82368141948/pandas-3.0.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0555c5882688a39317179ab4a0ed41d3ebc8812ab14c69364bbee8fb7a3f6288", size = 9914146, upload-time = "2026-03-31T06:47:46.67Z" }, - { url = "https://files.pythonhosted.org/packages/8d/77/3a227ff3337aa376c60d288e1d61c5d097131d0ac71f954d90a8f369e422/pandas-3.0.2-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:01f31a546acd5574ef77fe199bc90b55527c225c20ccda6601cf6b0fd5ed597c", size = 10444081, upload-time = "2026-03-31T06:47:49.681Z" }, - { url = "https://files.pythonhosted.org/packages/15/88/3cdd54fa279341afa10acf8d2b503556b1375245dccc9315659f795dd2e9/pandas-3.0.2-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:deeca1b5a931fdf0c2212c8a659ade6d3b1edc21f0914ce71ef24456ca7a6535", size = 10897535, upload-time = "2026-03-31T06:47:53.033Z" }, - { url = "https://files.pythonhosted.org/packages/06/9d/98cc7a7624f7932e40f434299260e2917b090a579d75937cb8a57b9d2de3/pandas-3.0.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0f48afd9bb13300ffb5a3316973324c787054ba6665cda0da3fbd67f451995db", size = 11446992, upload-time = "2026-03-31T06:47:56.193Z" }, - { url = "https://files.pythonhosted.org/packages/9a/cd/19ff605cc3760e80602e6826ddef2824d8e7050ed80f2e11c4b079741dc3/pandas-3.0.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6c4d8458b97a35717b62469a4ea0e85abd5ed8687277f5ccfc67f8a5126f8c53", size = 11968257, upload-time = "2026-03-31T06:47:59.137Z" }, - { url = "https://files.pythonhosted.org/packages/db/60/aba6a38de456e7341285102bede27514795c1eaa353bc0e7638b6b785356/pandas-3.0.2-cp314-cp314-win_amd64.whl", hash = "sha256:b35d14bb5d8285d9494fe93815a9e9307c0876e10f1e8e89ac5b88f728ec8dcf", size = 9865893, upload-time = "2026-03-31T06:48:02.038Z" }, - { url = "https://files.pythonhosted.org/packages/08/71/e5ec979dd2e8a093dacb8864598c0ff59a0cee0bbcdc0bfec16a51684d4f/pandas-3.0.2-cp314-cp314-win_arm64.whl", hash = "sha256:63d141b56ef686f7f0d714cfb8de4e320475b86bf4b620aa0b7da89af8cbdbbb", size = 9188644, upload-time = "2026-03-31T06:48:05.045Z" }, - { url = "https://files.pythonhosted.org/packages/f1/6c/7b45d85db19cae1eb524f2418ceaa9d85965dcf7b764ed151386b7c540f0/pandas-3.0.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:140f0cffb1fa2524e874dde5b477d9defe10780d8e9e220d259b2c0874c89d9d", size = 10776246, upload-time = "2026-03-31T06:48:07.789Z" }, - { url = "https://files.pythonhosted.org/packages/a8/3e/7b00648b086c106e81766f25322b48aa8dfa95b55e621dbdf2fdd413a117/pandas-3.0.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ae37e833ff4fed0ba352f6bdd8b73ba3ab3256a85e54edfd1ab51ae40cca0af8", size = 10424801, upload-time = "2026-03-31T06:48:10.897Z" }, - { url = "https://files.pythonhosted.org/packages/da/6e/558dd09a71b53b4008e7fc8a98ec6d447e9bfb63cdaeea10e5eb9b2dabe8/pandas-3.0.2-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4d888a5c678a419a5bb41a2a93818e8ed9fd3172246555c0b37b7cc27027effd", size = 10345643, upload-time = "2026-03-31T06:48:13.7Z" }, - { url = "https://files.pythonhosted.org/packages/be/e3/921c93b4d9a280409451dc8d07b062b503bbec0531d2627e73a756e99a82/pandas-3.0.2-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b444dc64c079e84df91baa8bf613d58405645461cabca929d9178f2cd392398d", size = 10743641, upload-time = "2026-03-31T06:48:16.659Z" }, - { url = "https://files.pythonhosted.org/packages/56/ca/fd17286f24fa3b4d067965d8d5d7e14fe557dd4f979a0b068ac0deaf8228/pandas-3.0.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4544c7a54920de8eeacaa1466a6b7268ecfbc9bc64ab4dbb89c6bbe94d5e0660", size = 11361993, upload-time = "2026-03-31T06:48:19.475Z" }, - { url = "https://files.pythonhosted.org/packages/e4/a5/2f6ed612056819de445a433ca1f2821ac3dab7f150d569a59e9cc105de1d/pandas-3.0.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:734be7551687c00fbd760dc0522ed974f82ad230d4a10f54bf51b80d44a08702", size = 11815274, upload-time = "2026-03-31T06:48:22.695Z" }, - { url = "https://files.pythonhosted.org/packages/00/2f/b622683e99ec3ce00b0854bac9e80868592c5b051733f2cf3a868e5fea26/pandas-3.0.2-cp314-cp314t-win_amd64.whl", hash = "sha256:57a07209bebcbcf768d2d13c9b78b852f9a15978dac41b9e6421a81ad4cdd276", size = 10888530, upload-time = "2026-03-31T06:48:25.806Z" }, - { url = "https://files.pythonhosted.org/packages/cb/2b/f8434233fab2bd66a02ec014febe4e5adced20e2693e0e90a07d118ed30e/pandas-3.0.2-cp314-cp314t-win_arm64.whl", hash = "sha256:5371b72c2d4d415d08765f32d689217a43227484e81b2305b52076e328f6f482", size = 9455341, upload-time = "2026-03-31T06:48:28.418Z" }, + { name = "pytz" }, + { name = "tzdata" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223, upload-time = "2025-09-29T23:34:51.853Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/fa/7ac648108144a095b4fb6aa3de1954689f7af60a14cf25583f4960ecb878/pandas-2.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:602b8615ebcc4a0c1751e71840428ddebeb142ec02c786e8ad6b1ce3c8dec523", size = 11578790, upload-time = "2025-09-29T23:18:30.065Z" }, + { url = "https://files.pythonhosted.org/packages/9b/35/74442388c6cf008882d4d4bdfc4109be87e9b8b7ccd097ad1e7f006e2e95/pandas-2.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8fe25fc7b623b0ef6b5009149627e34d2a4657e880948ec3c840e9402e5c1b45", size = 10833831, upload-time = "2025-09-29T23:38:56.071Z" }, + { url = "https://files.pythonhosted.org/packages/fe/e4/de154cbfeee13383ad58d23017da99390b91d73f8c11856f2095e813201b/pandas-2.3.3-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b468d3dad6ff947df92dcb32ede5b7bd41a9b3cceef0a30ed925f6d01fb8fa66", size = 12199267, upload-time = "2025-09-29T23:18:41.627Z" }, + { url = "https://files.pythonhosted.org/packages/bf/c9/63f8d545568d9ab91476b1818b4741f521646cbdd151c6efebf40d6de6f7/pandas-2.3.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b98560e98cb334799c0b07ca7967ac361a47326e9b4e5a7dfb5ab2b1c9d35a1b", size = 12789281, upload-time = "2025-09-29T23:18:56.834Z" }, + { url = "https://files.pythonhosted.org/packages/f2/00/a5ac8c7a0e67fd1a6059e40aa08fa1c52cc00709077d2300e210c3ce0322/pandas-2.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37b5848ba49824e5c30bedb9c830ab9b7751fd049bc7914533e01c65f79791", size = 13240453, upload-time = "2025-09-29T23:19:09.247Z" }, + { url = "https://files.pythonhosted.org/packages/27/4d/5c23a5bc7bd209231618dd9e606ce076272c9bc4f12023a70e03a86b4067/pandas-2.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:db4301b2d1f926ae677a751eb2bd0e8c5f5319c9cb3f88b0becbbb0b07b34151", size = 13890361, upload-time = "2025-09-29T23:19:25.342Z" }, + { url = "https://files.pythonhosted.org/packages/8e/59/712db1d7040520de7a4965df15b774348980e6df45c129b8c64d0dbe74ef/pandas-2.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:f086f6fe114e19d92014a1966f43a3e62285109afe874f067f5abbdcbb10e59c", size = 11348702, upload-time = "2025-09-29T23:19:38.296Z" }, + { url = "https://files.pythonhosted.org/packages/9c/fb/231d89e8637c808b997d172b18e9d4a4bc7bf31296196c260526055d1ea0/pandas-2.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d21f6d74eb1725c2efaa71a2bfc661a0689579b58e9c0ca58a739ff0b002b53", size = 11597846, upload-time = "2025-09-29T23:19:48.856Z" }, + { url = "https://files.pythonhosted.org/packages/5c/bd/bf8064d9cfa214294356c2d6702b716d3cf3bb24be59287a6a21e24cae6b/pandas-2.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3fd2f887589c7aa868e02632612ba39acb0b8948faf5cc58f0850e165bd46f35", size = 10729618, upload-time = "2025-09-29T23:39:08.659Z" }, + { url = "https://files.pythonhosted.org/packages/57/56/cf2dbe1a3f5271370669475ead12ce77c61726ffd19a35546e31aa8edf4e/pandas-2.3.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecaf1e12bdc03c86ad4a7ea848d66c685cb6851d807a26aa245ca3d2017a1908", size = 11737212, upload-time = "2025-09-29T23:19:59.765Z" }, + { url = "https://files.pythonhosted.org/packages/e5/63/cd7d615331b328e287d8233ba9fdf191a9c2d11b6af0c7a59cfcec23de68/pandas-2.3.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b3d11d2fda7eb164ef27ffc14b4fcab16a80e1ce67e9f57e19ec0afaf715ba89", size = 12362693, upload-time = "2025-09-29T23:20:14.098Z" }, + { url = "https://files.pythonhosted.org/packages/a6/de/8b1895b107277d52f2b42d3a6806e69cfef0d5cf1d0ba343470b9d8e0a04/pandas-2.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a68e15f780eddf2b07d242e17a04aa187a7ee12b40b930bfdd78070556550e98", size = 12771002, upload-time = "2025-09-29T23:20:26.76Z" }, + { url = "https://files.pythonhosted.org/packages/87/21/84072af3187a677c5893b170ba2c8fbe450a6ff911234916da889b698220/pandas-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:371a4ab48e950033bcf52b6527eccb564f52dc826c02afd9a1bc0ab731bba084", size = 13450971, upload-time = "2025-09-29T23:20:41.344Z" }, + { url = "https://files.pythonhosted.org/packages/86/41/585a168330ff063014880a80d744219dbf1dd7a1c706e75ab3425a987384/pandas-2.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:a16dcec078a01eeef8ee61bf64074b4e524a2a3f4b3be9326420cabe59c4778b", size = 10992722, upload-time = "2025-09-29T23:20:54.139Z" }, + { url = "https://files.pythonhosted.org/packages/cd/4b/18b035ee18f97c1040d94debd8f2e737000ad70ccc8f5513f4eefad75f4b/pandas-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:56851a737e3470de7fa88e6131f41281ed440d29a9268dcbf0002da5ac366713", size = 11544671, upload-time = "2025-09-29T23:21:05.024Z" }, + { url = "https://files.pythonhosted.org/packages/31/94/72fac03573102779920099bcac1c3b05975c2cb5f01eac609faf34bed1ca/pandas-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdcd9d1167f4885211e401b3036c0c8d9e274eee67ea8d0758a256d60704cfe8", size = 10680807, upload-time = "2025-09-29T23:21:15.979Z" }, + { url = "https://files.pythonhosted.org/packages/16/87/9472cf4a487d848476865321de18cc8c920b8cab98453ab79dbbc98db63a/pandas-2.3.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e32e7cc9af0f1cc15548288a51a3b681cc2a219faa838e995f7dc53dbab1062d", size = 11709872, upload-time = "2025-09-29T23:21:27.165Z" }, + { url = "https://files.pythonhosted.org/packages/15/07/284f757f63f8a8d69ed4472bfd85122bd086e637bf4ed09de572d575a693/pandas-2.3.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:318d77e0e42a628c04dc56bcef4b40de67918f7041c2b061af1da41dcff670ac", size = 12306371, upload-time = "2025-09-29T23:21:40.532Z" }, + { url = "https://files.pythonhosted.org/packages/33/81/a3afc88fca4aa925804a27d2676d22dcd2031c2ebe08aabd0ae55b9ff282/pandas-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e0a175408804d566144e170d0476b15d78458795bb18f1304fb94160cabf40c", size = 12765333, upload-time = "2025-09-29T23:21:55.77Z" }, + { url = "https://files.pythonhosted.org/packages/8d/0f/b4d4ae743a83742f1153464cf1a8ecfafc3ac59722a0b5c8602310cb7158/pandas-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2d9ab0fc11822b5eece72ec9587e172f63cff87c00b062f6e37448ced4493", size = 13418120, upload-time = "2025-09-29T23:22:10.109Z" }, + { url = "https://files.pythonhosted.org/packages/4f/c7/e54682c96a895d0c808453269e0b5928a07a127a15704fedb643e9b0a4c8/pandas-2.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:f8bfc0e12dc78f777f323f55c58649591b2cd0c43534e8355c51d3fede5f4dee", size = 10993991, upload-time = "2025-09-29T23:25:04.889Z" }, + { url = "https://files.pythonhosted.org/packages/f9/ca/3f8d4f49740799189e1395812f3bf23b5e8fc7c190827d55a610da72ce55/pandas-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:75ea25f9529fdec2d2e93a42c523962261e567d250b0013b16210e1d40d7c2e5", size = 12048227, upload-time = "2025-09-29T23:22:24.343Z" }, + { url = "https://files.pythonhosted.org/packages/0e/5a/f43efec3e8c0cc92c4663ccad372dbdff72b60bdb56b2749f04aa1d07d7e/pandas-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74ecdf1d301e812db96a465a525952f4dde225fdb6d8e5a521d47e1f42041e21", size = 11411056, upload-time = "2025-09-29T23:22:37.762Z" }, + { url = "https://files.pythonhosted.org/packages/46/b1/85331edfc591208c9d1a63a06baa67b21d332e63b7a591a5ba42a10bb507/pandas-2.3.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6435cb949cb34ec11cc9860246ccb2fdc9ecd742c12d3304989017d53f039a78", size = 11645189, upload-time = "2025-09-29T23:22:51.688Z" }, + { url = "https://files.pythonhosted.org/packages/44/23/78d645adc35d94d1ac4f2a3c4112ab6f5b8999f4898b8cdf01252f8df4a9/pandas-2.3.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:900f47d8f20860de523a1ac881c4c36d65efcb2eb850e6948140fa781736e110", size = 12121912, upload-time = "2025-09-29T23:23:05.042Z" }, + { url = "https://files.pythonhosted.org/packages/53/da/d10013df5e6aaef6b425aa0c32e1fc1f3e431e4bcabd420517dceadce354/pandas-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a45c765238e2ed7d7c608fc5bc4a6f88b642f2f01e70c0c23d2224dd21829d86", size = 12712160, upload-time = "2025-09-29T23:23:28.57Z" }, + { url = "https://files.pythonhosted.org/packages/bd/17/e756653095a083d8a37cbd816cb87148debcfcd920129b25f99dd8d04271/pandas-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c4fc4c21971a1a9f4bdb4c73978c7f7256caa3e62b323f70d6cb80db583350bc", size = 13199233, upload-time = "2025-09-29T23:24:24.876Z" }, + { url = "https://files.pythonhosted.org/packages/04/fd/74903979833db8390b73b3a8a7d30d146d710bd32703724dd9083950386f/pandas-2.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:ee15f284898e7b246df8087fc82b87b01686f98ee67d85a17b7ab44143a3a9a0", size = 11540635, upload-time = "2025-09-29T23:25:52.486Z" }, + { url = "https://files.pythonhosted.org/packages/21/00/266d6b357ad5e6d3ad55093a7e8efc7dd245f5a842b584db9f30b0f0a287/pandas-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1611aedd912e1ff81ff41c745822980c49ce4a7907537be8692c8dbc31924593", size = 10759079, upload-time = "2025-09-29T23:26:33.204Z" }, + { url = "https://files.pythonhosted.org/packages/ca/05/d01ef80a7a3a12b2f8bbf16daba1e17c98a2f039cbc8e2f77a2c5a63d382/pandas-2.3.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d2cefc361461662ac48810cb14365a365ce864afe85ef1f447ff5a1e99ea81c", size = 11814049, upload-time = "2025-09-29T23:27:15.384Z" }, + { url = "https://files.pythonhosted.org/packages/15/b2/0e62f78c0c5ba7e3d2c5945a82456f4fac76c480940f805e0b97fcbc2f65/pandas-2.3.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ee67acbbf05014ea6c763beb097e03cd629961c8a632075eeb34247120abcb4b", size = 12332638, upload-time = "2025-09-29T23:27:51.625Z" }, + { url = "https://files.pythonhosted.org/packages/c5/33/dd70400631b62b9b29c3c93d2feee1d0964dc2bae2e5ad7a6c73a7f25325/pandas-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c46467899aaa4da076d5abc11084634e2d197e9460643dd455ac3db5856b24d6", size = 12886834, upload-time = "2025-09-29T23:28:21.289Z" }, + { url = "https://files.pythonhosted.org/packages/d3/18/b5d48f55821228d0d2692b34fd5034bb185e854bdb592e9c640f6290e012/pandas-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6253c72c6a1d990a410bc7de641d34053364ef8bcd3126f7e7450125887dffe3", size = 13409925, upload-time = "2025-09-29T23:28:58.261Z" }, + { url = "https://files.pythonhosted.org/packages/a6/3d/124ac75fcd0ecc09b8fdccb0246ef65e35b012030defb0e0eba2cbbbe948/pandas-2.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:1b07204a219b3b7350abaae088f451860223a52cfb8a6c53358e7948735158e5", size = 11109071, upload-time = "2025-09-29T23:32:27.484Z" }, + { url = "https://files.pythonhosted.org/packages/89/9c/0e21c895c38a157e0faa1fb64587a9226d6dd46452cac4532d80c3c4a244/pandas-2.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2462b1a365b6109d275250baaae7b760fd25c726aaca0054649286bcfbb3e8ec", size = 12048504, upload-time = "2025-09-29T23:29:31.47Z" }, + { url = "https://files.pythonhosted.org/packages/d7/82/b69a1c95df796858777b68fbe6a81d37443a33319761d7c652ce77797475/pandas-2.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0242fe9a49aa8b4d78a4fa03acb397a58833ef6199e9aa40a95f027bb3a1b6e7", size = 11410702, upload-time = "2025-09-29T23:29:54.591Z" }, + { url = "https://files.pythonhosted.org/packages/f9/88/702bde3ba0a94b8c73a0181e05144b10f13f29ebfc2150c3a79062a8195d/pandas-2.3.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a21d830e78df0a515db2b3d2f5570610f5e6bd2e27749770e8bb7b524b89b450", size = 11634535, upload-time = "2025-09-29T23:30:21.003Z" }, + { url = "https://files.pythonhosted.org/packages/a4/1e/1bac1a839d12e6a82ec6cb40cda2edde64a2013a66963293696bbf31fbbb/pandas-2.3.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e3ebdb170b5ef78f19bfb71b0dc5dc58775032361fa188e814959b74d726dd5", size = 12121582, upload-time = "2025-09-29T23:30:43.391Z" }, + { url = "https://files.pythonhosted.org/packages/44/91/483de934193e12a3b1d6ae7c8645d083ff88dec75f46e827562f1e4b4da6/pandas-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d051c0e065b94b7a3cea50eb1ec32e912cd96dba41647eb24104b6c6c14c5788", size = 12699963, upload-time = "2025-09-29T23:31:10.009Z" }, + { url = "https://files.pythonhosted.org/packages/70/44/5191d2e4026f86a2a109053e194d3ba7a31a2d10a9c2348368c63ed4e85a/pandas-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3869faf4bd07b3b66a9f462417d0ca3a9df29a9f6abd5d0d0dbab15dac7abe87", size = 13202175, upload-time = "2025-09-29T23:31:59.173Z" }, ] [[package]] name = "pathspec" -version = "1.1.0" +version = "1.1.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2e/17/9c3094b822982b9f1ea666d8580ce59000f61f87c1663556fb72031ad9ec/pathspec-1.1.0.tar.gz", hash = "sha256:f5d7c555da02fd8dde3e4a2354b6aba817a89112fa8f333f7917a2a4834dd080", size = 133918, upload-time = "2026-04-23T01:46:22.298Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/82/42f767fc1c1143d6fd36efb827202a2d997a375e160a71eb2888a925aac1/pathspec-1.1.1.tar.gz", hash = "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a", size = 135180, upload-time = "2026-04-27T01:46:08.907Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/c9/8eed0486f074e9f1ca7f8ce5ad663e65f12fdab344028d658fa1b03d35e0/pathspec-1.1.0-py3-none-any.whl", hash = "sha256:574b128f7456bd899045ccd142dd446af7e6cfd0072d63ad73fbc55fbb4aaa42", size = 56264, upload-time = "2026-04-23T01:46:20.606Z" }, + { url = "https://files.pythonhosted.org/packages/f1/d9/7fb5aa316bc299258e68c73ba3bddbc499654a07f151cba08f6153988714/pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189", size = 57328, upload-time = "2026-04-27T01:46:07.06Z" }, ] [[package]] @@ -2594,14 +2573,14 @@ wheels = [ [[package]] name = "praatio" -version = "6.2.2" +version = "6.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/de/fa/be0bab2b2da04427204f84cd5e54bd3364937456adf03d98f0f2cfb8f402/praatio-6.2.2.tar.gz", hash = "sha256:ed8fdee6121bdae142817879ed9a50cc8334e72c03c110162e21ed8160180d2a", size = 2176936, upload-time = "2026-01-16T09:21:52.888Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3d/44/99cf2f7e4333cc10af2efd4a27658a7a18e397f5a1d181762dad02a361c4/praatio-6.2.0.tar.gz", hash = "sha256:7d2a7f8135a3e0691743ada0af84308b64e637f07038cea77d814b8aa2fa2e40", size = 85100, upload-time = "2023-12-10T09:24:26.833Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/76/12/59f3928dc6583d652798758b513ab6062e4dddbbf624623d0ec02e7bbb23/praatio-6.2.2-py3-none-any.whl", hash = "sha256:d097b0e6991715559d68537c6af1baba86a0e0f9ad077e6d078e3d21c510743f", size = 82261, upload-time = "2026-01-16T09:21:50.124Z" }, + { url = "https://files.pythonhosted.org/packages/c4/8e/eb83f93f0e46c5bfd20a3040eef7ccece8ac0703b06a500fb17a57627a2e/praatio-6.2.0-py3-none-any.whl", hash = "sha256:6541018791a3f0b087a8168d1746a165937c3fff1f94c7a6883b3f470e0cf405", size = 80029, upload-time = "2023-12-10T09:24:24.375Z" }, ] [[package]] @@ -3015,6 +2994,7 @@ dependencies = [ { name = "pytest" }, { name = "pyyaml" }, { name = "rich" }, + { name = "speechbrain" }, { name = "torch" }, { name = "torchaudio" }, { name = "wav2textgrid" }, @@ -3044,21 +3024,22 @@ installation = [ [package.metadata] requires-dist = [ { name = "accelerate", specifier = ">=1.11.0" }, - { name = "alignment-comparison-plots", specifier = "==0.1.1" }, + { name = "alignment-comparison-plots", git = "https://github.com/WISCLab/alignment-comparison-plots?branch=voxkit-windows-variant" }, { name = "datasets", specifier = ">=4.3.0" }, { name = "faster-whisper", specifier = ">=1.1.0" }, { name = "keyring", specifier = ">=25.6.0" }, { name = "numba", specifier = ">=0.59.0" }, { name = "pre-commit", specifier = ">=4.3.0" }, { name = "pydantic", specifier = ">=2.12.3" }, - { name = "pypllrcomputer", git = "https://github.com/BrainBehaviorAnalyticsLab/PyPLLRComputer.git?rev=48a0c934e75f73235ba5a002538b8588fd6697e7" }, + { name = "pypllrcomputer", git = "https://github.com/pkadambi/PyPhonemePronunciationScorer?branch=voxkit-windows-variant" }, { name = "pyqt6", specifier = ">=6.9.1" }, { name = "pytest", specifier = ">=8.4.2" }, { name = "pyyaml", specifier = ">=6.0.0" }, { name = "rich", specifier = ">=14.2.0" }, + { name = "speechbrain", git = "https://github.com/BeckettFrey/speechbrain.git?branch=fix%2Fwindows-lazy-import-inspect-path" }, { name = "torch", specifier = "==2.8.0" }, { name = "torchaudio", specifier = "==2.8.0" }, - { name = "wav2textgrid", git = "https://github.com/pkadambi/Wav2TextGrid.git?rev=3862476185a7558d481f1ae1c9cbca2e48c825a0" }, + { name = "wav2textgrid", git = "https://github.com/pkadambi/Wav2TextGrid?branch=voxkit-windows-variant" }, ] [package.metadata.requires-dev] @@ -3080,19 +3061,17 @@ installation = [{ name = "pyinstaller", specifier = ">=5.10.1" }] [[package]] name = "pypllrcomputer" -version = "0.1.0" -source = { git = "https://github.com/BrainBehaviorAnalyticsLab/PyPLLRComputer.git?rev=48a0c934e75f73235ba5a002538b8588fd6697e7#48a0c934e75f73235ba5a002538b8588fd6697e7" } +version = "1.0.0" +source = { git = "https://github.com/pkadambi/PyPhonemePronunciationScorer?branch=voxkit-windows-variant#31165d3d5def6ee07b69b477da7f28329f0ad52e" } dependencies = [ { name = "g2p-en" }, { name = "librosa" }, { name = "matplotlib" }, { name = "pandas" }, { name = "praatio" }, - { name = "pytest" }, { name = "scipy" }, { name = "seaborn" }, { name = "soundfile" }, - { name = "tdqm" }, { name = "torch" }, { name = "transformers" }, ] @@ -3237,6 +3216,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d8/db/795879cc3ddfe338599bddea6388cc5100b088db0a4caf6e6c1af1c27e04/python_discovery-1.2.2-py3-none-any.whl", hash = "sha256:e1ae95d9af875e78f15e19aed0c6137ab1bb49c200f21f5061786490c9585c7a", size = 31894, upload-time = "2026-04-07T17:28:48.09Z" }, ] +[[package]] +name = "pytz" +version = "2026.1.post1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/56/db/b8721d71d945e6a8ac63c0fc900b2067181dbb50805958d4d4661cf7d277/pytz-2026.1.post1.tar.gz", hash = "sha256:3378dde6a0c3d26719182142c56e60c7f9af7e968076f31aae569d72a0358ee1", size = 321088, upload-time = "2026-03-03T07:47:50.683Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/99/781fe0c827be2742bcc775efefccb3b048a3a9c6ce9aec0cbf4a101677e5/pytz-2026.1.post1-py2.py3-none-any.whl", hash = "sha256:f2fd16142fda348286a75e1a524be810bb05d444e5a081f37f7affc635035f7a", size = 510489, upload-time = "2026-03-03T07:47:49.167Z" }, +] + [[package]] name = "pywin32-ctypes" version = "0.2.3" @@ -3592,73 +3580,73 @@ wheels = [ [[package]] name = "scipy" -version = "1.17.1" +version = "1.16.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7a/97/5a3609c4f8d58b039179648e62dd220f89864f56f7357f5d4f45c29eb2cc/scipy-1.17.1.tar.gz", hash = "sha256:95d8e012d8cb8816c226aef832200b1d45109ed4464303e997c5b13122b297c0", size = 30573822, upload-time = "2026-02-23T00:26:24.851Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/75/b4ce781849931fef6fd529afa6b63711d5a733065722d0c3e2724af9e40a/scipy-1.17.1-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:1f95b894f13729334fb990162e911c9e5dc1ab390c58aa6cbecb389c5b5e28ec", size = 31613675, upload-time = "2026-02-23T00:16:00.13Z" }, - { url = "https://files.pythonhosted.org/packages/f7/58/bccc2861b305abdd1b8663d6130c0b3d7cc22e8d86663edbc8401bfd40d4/scipy-1.17.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:e18f12c6b0bc5a592ed23d3f7b891f68fd7f8241d69b7883769eb5d5dfb52696", size = 28162057, upload-time = "2026-02-23T00:16:09.456Z" }, - { url = "https://files.pythonhosted.org/packages/6d/ee/18146b7757ed4976276b9c9819108adbc73c5aad636e5353e20746b73069/scipy-1.17.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:a3472cfbca0a54177d0faa68f697d8ba4c80bbdc19908c3465556d9f7efce9ee", size = 20334032, upload-time = "2026-02-23T00:16:17.358Z" }, - { url = "https://files.pythonhosted.org/packages/ec/e6/cef1cf3557f0c54954198554a10016b6a03b2ec9e22a4e1df734936bd99c/scipy-1.17.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:766e0dc5a616d026a3a1cffa379af959671729083882f50307e18175797b3dfd", size = 22709533, upload-time = "2026-02-23T00:16:25.791Z" }, - { url = "https://files.pythonhosted.org/packages/4d/60/8804678875fc59362b0fb759ab3ecce1f09c10a735680318ac30da8cd76b/scipy-1.17.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:744b2bf3640d907b79f3fd7874efe432d1cf171ee721243e350f55234b4cec4c", size = 33062057, upload-time = "2026-02-23T00:16:36.931Z" }, - { url = "https://files.pythonhosted.org/packages/09/7d/af933f0f6e0767995b4e2d705a0665e454d1c19402aa7e895de3951ebb04/scipy-1.17.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43af8d1f3bea642559019edfe64e9b11192a8978efbd1539d7bc2aaa23d92de4", size = 35349300, upload-time = "2026-02-23T00:16:49.108Z" }, - { url = "https://files.pythonhosted.org/packages/b4/3d/7ccbbdcbb54c8fdc20d3b6930137c782a163fa626f0aef920349873421ba/scipy-1.17.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cd96a1898c0a47be4520327e01f874acfd61fb48a9420f8aa9f6483412ffa444", size = 35127333, upload-time = "2026-02-23T00:17:01.293Z" }, - { url = "https://files.pythonhosted.org/packages/e8/19/f926cb11c42b15ba08e3a71e376d816ac08614f769b4f47e06c3580c836a/scipy-1.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4eb6c25dd62ee8d5edf68a8e1c171dd71c292fdae95d8aeb3dd7d7de4c364082", size = 37741314, upload-time = "2026-02-23T00:17:12.576Z" }, - { url = "https://files.pythonhosted.org/packages/95/da/0d1df507cf574b3f224ccc3d45244c9a1d732c81dcb26b1e8a766ae271a8/scipy-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:d30e57c72013c2a4fe441c2fcb8e77b14e152ad48b5464858e07e2ad9fbfceff", size = 36607512, upload-time = "2026-02-23T00:17:23.424Z" }, - { url = "https://files.pythonhosted.org/packages/68/7f/bdd79ceaad24b671543ffe0ef61ed8e659440eb683b66f033454dcee90eb/scipy-1.17.1-cp311-cp311-win_arm64.whl", hash = "sha256:9ecb4efb1cd6e8c4afea0daa91a87fbddbce1b99d2895d151596716c0b2e859d", size = 24599248, upload-time = "2026-02-23T00:17:34.561Z" }, - { url = "https://files.pythonhosted.org/packages/35/48/b992b488d6f299dbe3f11a20b24d3dda3d46f1a635ede1c46b5b17a7b163/scipy-1.17.1-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:35c3a56d2ef83efc372eaec584314bd0ef2e2f0d2adb21c55e6ad5b344c0dcb8", size = 31610954, upload-time = "2026-02-23T00:17:49.855Z" }, - { url = "https://files.pythonhosted.org/packages/b2/02/cf107b01494c19dc100f1d0b7ac3cc08666e96ba2d64db7626066cee895e/scipy-1.17.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:fcb310ddb270a06114bb64bbe53c94926b943f5b7f0842194d585c65eb4edd76", size = 28172662, upload-time = "2026-02-23T00:18:01.64Z" }, - { url = "https://files.pythonhosted.org/packages/cf/a9/599c28631bad314d219cf9ffd40e985b24d603fc8a2f4ccc5ae8419a535b/scipy-1.17.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:cc90d2e9c7e5c7f1a482c9875007c095c3194b1cfedca3c2f3291cdc2bc7c086", size = 20344366, upload-time = "2026-02-23T00:18:12.015Z" }, - { url = "https://files.pythonhosted.org/packages/35/f5/906eda513271c8deb5af284e5ef0206d17a96239af79f9fa0aebfe0e36b4/scipy-1.17.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:c80be5ede8f3f8eded4eff73cc99a25c388ce98e555b17d31da05287015ffa5b", size = 22704017, upload-time = "2026-02-23T00:18:21.502Z" }, - { url = "https://files.pythonhosted.org/packages/da/34/16f10e3042d2f1d6b66e0428308ab52224b6a23049cb2f5c1756f713815f/scipy-1.17.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e19ebea31758fac5893a2ac360fedd00116cbb7628e650842a6691ba7ca28a21", size = 32927842, upload-time = "2026-02-23T00:18:35.367Z" }, - { url = "https://files.pythonhosted.org/packages/01/8e/1e35281b8ab6d5d72ebe9911edcdffa3f36b04ed9d51dec6dd140396e220/scipy-1.17.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:02ae3b274fde71c5e92ac4d54bc06c42d80e399fec704383dcd99b301df37458", size = 35235890, upload-time = "2026-02-23T00:18:49.188Z" }, - { url = "https://files.pythonhosted.org/packages/c5/5c/9d7f4c88bea6e0d5a4f1bc0506a53a00e9fcb198de372bfe4d3652cef482/scipy-1.17.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8a604bae87c6195d8b1045eddece0514d041604b14f2727bbc2b3020172045eb", size = 35003557, upload-time = "2026-02-23T00:18:54.74Z" }, - { url = "https://files.pythonhosted.org/packages/65/94/7698add8f276dbab7a9de9fb6b0e02fc13ee61d51c7c3f85ac28b65e1239/scipy-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f590cd684941912d10becc07325a3eeb77886fe981415660d9265c4c418d0bea", size = 37625856, upload-time = "2026-02-23T00:19:00.307Z" }, - { url = "https://files.pythonhosted.org/packages/a2/84/dc08d77fbf3d87d3ee27f6a0c6dcce1de5829a64f2eae85a0ecc1f0daa73/scipy-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:41b71f4a3a4cab9d366cd9065b288efc4d4f3c0b37a91a8e0947fb5bd7f31d87", size = 36549682, upload-time = "2026-02-23T00:19:07.67Z" }, - { url = "https://files.pythonhosted.org/packages/bc/98/fe9ae9ffb3b54b62559f52dedaebe204b408db8109a8c66fdd04869e6424/scipy-1.17.1-cp312-cp312-win_arm64.whl", hash = "sha256:f4115102802df98b2b0db3cce5cb9b92572633a1197c77b7553e5203f284a5b3", size = 24547340, upload-time = "2026-02-23T00:19:12.024Z" }, - { url = "https://files.pythonhosted.org/packages/76/27/07ee1b57b65e92645f219b37148a7e7928b82e2b5dbeccecb4dff7c64f0b/scipy-1.17.1-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:5e3c5c011904115f88a39308379c17f91546f77c1667cea98739fe0fccea804c", size = 31590199, upload-time = "2026-02-23T00:19:17.192Z" }, - { url = "https://files.pythonhosted.org/packages/ec/ae/db19f8ab842e9b724bf5dbb7db29302a91f1e55bc4d04b1025d6d605a2c5/scipy-1.17.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:6fac755ca3d2c3edcb22f479fceaa241704111414831ddd3bc6056e18516892f", size = 28154001, upload-time = "2026-02-23T00:19:22.241Z" }, - { url = "https://files.pythonhosted.org/packages/5b/58/3ce96251560107b381cbd6e8413c483bbb1228a6b919fa8652b0d4090e7f/scipy-1.17.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:7ff200bf9d24f2e4d5dc6ee8c3ac64d739d3a89e2326ba68aaf6c4a2b838fd7d", size = 20325719, upload-time = "2026-02-23T00:19:26.329Z" }, - { url = "https://files.pythonhosted.org/packages/b2/83/15087d945e0e4d48ce2377498abf5ad171ae013232ae31d06f336e64c999/scipy-1.17.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:4b400bdc6f79fa02a4d86640310dde87a21fba0c979efff5248908c6f15fad1b", size = 22683595, upload-time = "2026-02-23T00:19:30.304Z" }, - { url = "https://files.pythonhosted.org/packages/b4/e0/e58fbde4a1a594c8be8114eb4aac1a55bcd6587047efc18a61eb1f5c0d30/scipy-1.17.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2b64ca7d4aee0102a97f3ba22124052b4bd2152522355073580bf4845e2550b6", size = 32896429, upload-time = "2026-02-23T00:19:35.536Z" }, - { url = "https://files.pythonhosted.org/packages/f5/5f/f17563f28ff03c7b6799c50d01d5d856a1d55f2676f537ca8d28c7f627cd/scipy-1.17.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:581b2264fc0aa555f3f435a5944da7504ea3a065d7029ad60e7c3d1ae09c5464", size = 35203952, upload-time = "2026-02-23T00:19:42.259Z" }, - { url = "https://files.pythonhosted.org/packages/8d/a5/9afd17de24f657fdfe4df9a3f1ea049b39aef7c06000c13db1530d81ccca/scipy-1.17.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:beeda3d4ae615106d7094f7e7cef6218392e4465cc95d25f900bebabfded0950", size = 34979063, upload-time = "2026-02-23T00:19:47.547Z" }, - { url = "https://files.pythonhosted.org/packages/8b/13/88b1d2384b424bf7c924f2038c1c409f8d88bb2a8d49d097861dd64a57b2/scipy-1.17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6609bc224e9568f65064cfa72edc0f24ee6655b47575954ec6339534b2798369", size = 37598449, upload-time = "2026-02-23T00:19:53.238Z" }, - { url = "https://files.pythonhosted.org/packages/35/e5/d6d0e51fc888f692a35134336866341c08655d92614f492c6860dc45bb2c/scipy-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:37425bc9175607b0268f493d79a292c39f9d001a357bebb6b88fdfaff13f6448", size = 36510943, upload-time = "2026-02-23T00:20:50.89Z" }, - { url = "https://files.pythonhosted.org/packages/2a/fd/3be73c564e2a01e690e19cc618811540ba5354c67c8680dce3281123fb79/scipy-1.17.1-cp313-cp313-win_arm64.whl", hash = "sha256:5cf36e801231b6a2059bf354720274b7558746f3b1a4efb43fcf557ccd484a87", size = 24545621, upload-time = "2026-02-23T00:20:55.871Z" }, - { url = "https://files.pythonhosted.org/packages/6f/6b/17787db8b8114933a66f9dcc479a8272e4b4da75fe03b0c282f7b0ade8cd/scipy-1.17.1-cp313-cp313t-macosx_10_14_x86_64.whl", hash = "sha256:d59c30000a16d8edc7e64152e30220bfbd724c9bbb08368c054e24c651314f0a", size = 31936708, upload-time = "2026-02-23T00:19:58.694Z" }, - { url = "https://files.pythonhosted.org/packages/38/2e/524405c2b6392765ab1e2b722a41d5da33dc5c7b7278184a8ad29b6cb206/scipy-1.17.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:010f4333c96c9bb1a4516269e33cb5917b08ef2166d5556ca2fd9f082a9e6ea0", size = 28570135, upload-time = "2026-02-23T00:20:03.934Z" }, - { url = "https://files.pythonhosted.org/packages/fd/c3/5bd7199f4ea8556c0c8e39f04ccb014ac37d1468e6cfa6a95c6b3562b76e/scipy-1.17.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:2ceb2d3e01c5f1d83c4189737a42d9cb2fc38a6eeed225e7515eef71ad301dce", size = 20741977, upload-time = "2026-02-23T00:20:07.935Z" }, - { url = "https://files.pythonhosted.org/packages/d9/b8/8ccd9b766ad14c78386599708eb745f6b44f08400a5fd0ade7cf89b6fc93/scipy-1.17.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:844e165636711ef41f80b4103ed234181646b98a53c8f05da12ca5ca289134f6", size = 23029601, upload-time = "2026-02-23T00:20:12.161Z" }, - { url = "https://files.pythonhosted.org/packages/6d/a0/3cb6f4d2fb3e17428ad2880333cac878909ad1a89f678527b5328b93c1d4/scipy-1.17.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:158dd96d2207e21c966063e1635b1063cd7787b627b6f07305315dd73d9c679e", size = 33019667, upload-time = "2026-02-23T00:20:17.208Z" }, - { url = "https://files.pythonhosted.org/packages/f3/c3/2d834a5ac7bf3a0c806ad1508efc02dda3c8c61472a56132d7894c312dea/scipy-1.17.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:74cbb80d93260fe2ffa334efa24cb8f2f0f622a9b9febf8b483c0b865bfb3475", size = 35264159, upload-time = "2026-02-23T00:20:23.087Z" }, - { url = "https://files.pythonhosted.org/packages/4d/77/d3ed4becfdbd217c52062fafe35a72388d1bd82c2d0ba5ca19d6fcc93e11/scipy-1.17.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:dbc12c9f3d185f5c737d801da555fb74b3dcfa1a50b66a1a93e09190f41fab50", size = 35102771, upload-time = "2026-02-23T00:20:28.636Z" }, - { url = "https://files.pythonhosted.org/packages/bd/12/d19da97efde68ca1ee5538bb261d5d2c062f0c055575128f11a2730e3ac1/scipy-1.17.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:94055a11dfebe37c656e70317e1996dc197e1a15bbcc351bcdd4610e128fe1ca", size = 37665910, upload-time = "2026-02-23T00:20:34.743Z" }, - { url = "https://files.pythonhosted.org/packages/06/1c/1172a88d507a4baaf72c5a09bb6c018fe2ae0ab622e5830b703a46cc9e44/scipy-1.17.1-cp313-cp313t-win_amd64.whl", hash = "sha256:e30bdeaa5deed6bc27b4cc490823cd0347d7dae09119b8803ae576ea0ce52e4c", size = 36562980, upload-time = "2026-02-23T00:20:40.575Z" }, - { url = "https://files.pythonhosted.org/packages/70/b0/eb757336e5a76dfa7911f63252e3b7d1de00935d7705cf772db5b45ec238/scipy-1.17.1-cp313-cp313t-win_arm64.whl", hash = "sha256:a720477885a9d2411f94a93d16f9d89bad0f28ca23c3f8daa521e2dcc3f44d49", size = 24856543, upload-time = "2026-02-23T00:20:45.313Z" }, - { url = "https://files.pythonhosted.org/packages/cf/83/333afb452af6f0fd70414dc04f898647ee1423979ce02efa75c3b0f2c28e/scipy-1.17.1-cp314-cp314-macosx_10_14_x86_64.whl", hash = "sha256:a48a72c77a310327f6a3a920092fa2b8fd03d7deaa60f093038f22d98e096717", size = 31584510, upload-time = "2026-02-23T00:21:01.015Z" }, - { url = "https://files.pythonhosted.org/packages/ed/a6/d05a85fd51daeb2e4ea71d102f15b34fedca8e931af02594193ae4fd25f7/scipy-1.17.1-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:45abad819184f07240d8a696117a7aacd39787af9e0b719d00285549ed19a1e9", size = 28170131, upload-time = "2026-02-23T00:21:05.888Z" }, - { url = "https://files.pythonhosted.org/packages/db/7b/8624a203326675d7746a254083a187398090a179335b2e4a20e2ddc46e83/scipy-1.17.1-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:3fd1fcdab3ea951b610dc4cef356d416d5802991e7e32b5254828d342f7b7e0b", size = 20342032, upload-time = "2026-02-23T00:21:09.904Z" }, - { url = "https://files.pythonhosted.org/packages/c9/35/2c342897c00775d688d8ff3987aced3426858fd89d5a0e26e020b660b301/scipy-1.17.1-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:7bdf2da170b67fdf10bca777614b1c7d96ae3ca5794fd9587dce41eb2966e866", size = 22678766, upload-time = "2026-02-23T00:21:14.313Z" }, - { url = "https://files.pythonhosted.org/packages/ef/f2/7cdb8eb308a1a6ae1e19f945913c82c23c0c442a462a46480ce487fdc0ac/scipy-1.17.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:adb2642e060a6549c343603a3851ba76ef0b74cc8c079a9a58121c7ec9fe2350", size = 32957007, upload-time = "2026-02-23T00:21:19.663Z" }, - { url = "https://files.pythonhosted.org/packages/0b/2e/7eea398450457ecb54e18e9d10110993fa65561c4f3add5e8eccd2b9cd41/scipy-1.17.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eee2cfda04c00a857206a4330f0c5e3e56535494e30ca445eb19ec624ae75118", size = 35221333, upload-time = "2026-02-23T00:21:25.278Z" }, - { url = "https://files.pythonhosted.org/packages/d9/77/5b8509d03b77f093a0d52e606d3c4f79e8b06d1d38c441dacb1e26cacf46/scipy-1.17.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d2650c1fb97e184d12d8ba010493ee7b322864f7d3d00d3f9bb97d9c21de4068", size = 35042066, upload-time = "2026-02-23T00:21:31.358Z" }, - { url = "https://files.pythonhosted.org/packages/f9/df/18f80fb99df40b4070328d5ae5c596f2f00fffb50167e31439e932f29e7d/scipy-1.17.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:08b900519463543aa604a06bec02461558a6e1cef8fdbb8098f77a48a83c8118", size = 37612763, upload-time = "2026-02-23T00:21:37.247Z" }, - { url = "https://files.pythonhosted.org/packages/4b/39/f0e8ea762a764a9dc52aa7dabcfad51a354819de1f0d4652b6a1122424d6/scipy-1.17.1-cp314-cp314-win_amd64.whl", hash = "sha256:3877ac408e14da24a6196de0ddcace62092bfc12a83823e92e49e40747e52c19", size = 37290984, upload-time = "2026-02-23T00:22:35.023Z" }, - { url = "https://files.pythonhosted.org/packages/7c/56/fe201e3b0f93d1a8bcf75d3379affd228a63d7e2d80ab45467a74b494947/scipy-1.17.1-cp314-cp314-win_arm64.whl", hash = "sha256:f8885db0bc2bffa59d5c1b72fad7a6a92d3e80e7257f967dd81abb553a90d293", size = 25192877, upload-time = "2026-02-23T00:22:39.798Z" }, - { url = "https://files.pythonhosted.org/packages/96/ad/f8c414e121f82e02d76f310f16db9899c4fcde36710329502a6b2a3c0392/scipy-1.17.1-cp314-cp314t-macosx_10_14_x86_64.whl", hash = "sha256:1cc682cea2ae55524432f3cdff9e9a3be743d52a7443d0cba9017c23c87ae2f6", size = 31949750, upload-time = "2026-02-23T00:21:42.289Z" }, - { url = "https://files.pythonhosted.org/packages/7c/b0/c741e8865d61b67c81e255f4f0a832846c064e426636cd7de84e74d209be/scipy-1.17.1-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:2040ad4d1795a0ae89bfc7e8429677f365d45aa9fd5e4587cf1ea737f927b4a1", size = 28585858, upload-time = "2026-02-23T00:21:47.706Z" }, - { url = "https://files.pythonhosted.org/packages/ed/1b/3985219c6177866628fa7c2595bfd23f193ceebbe472c98a08824b9466ff/scipy-1.17.1-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:131f5aaea57602008f9822e2115029b55d4b5f7c070287699fe45c661d051e39", size = 20757723, upload-time = "2026-02-23T00:21:52.039Z" }, - { url = "https://files.pythonhosted.org/packages/c0/19/2a04aa25050d656d6f7b9e7b685cc83d6957fb101665bfd9369ca6534563/scipy-1.17.1-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:9cdc1a2fcfd5c52cfb3045feb399f7b3ce822abdde3a193a6b9a60b3cb5854ca", size = 23043098, upload-time = "2026-02-23T00:21:56.185Z" }, - { url = "https://files.pythonhosted.org/packages/86/f1/3383beb9b5d0dbddd030335bf8a8b32d4317185efe495374f134d8be6cce/scipy-1.17.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e3dcd57ab780c741fde8dc68619de988b966db759a3c3152e8e9142c26295ad", size = 33030397, upload-time = "2026-02-23T00:22:01.404Z" }, - { url = "https://files.pythonhosted.org/packages/41/68/8f21e8a65a5a03f25a79165ec9d2b28c00e66dc80546cf5eb803aeeff35b/scipy-1.17.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a9956e4d4f4a301ebf6cde39850333a6b6110799d470dbbb1e25326ac447f52a", size = 35281163, upload-time = "2026-02-23T00:22:07.024Z" }, - { url = "https://files.pythonhosted.org/packages/84/8d/c8a5e19479554007a5632ed7529e665c315ae7492b4f946b0deb39870e39/scipy-1.17.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:a4328d245944d09fd639771de275701ccadf5f781ba0ff092ad141e017eccda4", size = 35116291, upload-time = "2026-02-23T00:22:12.585Z" }, - { url = "https://files.pythonhosted.org/packages/52/52/e57eceff0e342a1f50e274264ed47497b59e6a4e3118808ee58ddda7b74a/scipy-1.17.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a77cbd07b940d326d39a1d1b37817e2ee4d79cb30e7338f3d0cddffae70fcaa2", size = 37682317, upload-time = "2026-02-23T00:22:18.513Z" }, - { url = "https://files.pythonhosted.org/packages/11/2f/b29eafe4a3fbc3d6de9662b36e028d5f039e72d345e05c250e121a230dd4/scipy-1.17.1-cp314-cp314t-win_amd64.whl", hash = "sha256:eb092099205ef62cd1782b006658db09e2fed75bffcae7cc0d44052d8aa0f484", size = 37345327, upload-time = "2026-02-23T00:22:24.442Z" }, - { url = "https://files.pythonhosted.org/packages/07/39/338d9219c4e87f3e708f18857ecd24d22a0c3094752393319553096b98af/scipy-1.17.1-cp314-cp314t-win_arm64.whl", hash = "sha256:200e1050faffacc162be6a486a984a0497866ec54149a01270adc8a59b7c7d21", size = 25489165, upload-time = "2026-02-23T00:22:29.563Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/4c/3b/546a6f0bfe791bbb7f8d591613454d15097e53f906308ec6f7c1ce588e8e/scipy-1.16.2.tar.gz", hash = "sha256:af029b153d243a80afb6eabe40b0a07f8e35c9adc269c019f364ad747f826a6b", size = 30580599, upload-time = "2025-09-11T17:48:08.271Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/ef/37ed4b213d64b48422df92560af7300e10fe30b5d665dd79932baebee0c6/scipy-1.16.2-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:6ab88ea43a57da1af33292ebd04b417e8e2eaf9d5aa05700be8d6e1b6501cd92", size = 36619956, upload-time = "2025-09-11T17:39:20.5Z" }, + { url = "https://files.pythonhosted.org/packages/85/ab/5c2eba89b9416961a982346a4d6a647d78c91ec96ab94ed522b3b6baf444/scipy-1.16.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:c95e96c7305c96ede73a7389f46ccd6c659c4da5ef1b2789466baeaed3622b6e", size = 28931117, upload-time = "2025-09-11T17:39:29.06Z" }, + { url = "https://files.pythonhosted.org/packages/80/d1/eed51ab64d227fe60229a2d57fb60ca5898cfa50ba27d4f573e9e5f0b430/scipy-1.16.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:87eb178db04ece7c698220d523c170125dbffebb7af0345e66c3554f6f60c173", size = 20921997, upload-time = "2025-09-11T17:39:34.892Z" }, + { url = "https://files.pythonhosted.org/packages/be/7c/33ea3e23bbadde96726edba6bf9111fb1969d14d9d477ffa202c67bec9da/scipy-1.16.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:4e409eac067dcee96a57fbcf424c13f428037827ec7ee3cb671ff525ca4fc34d", size = 23523374, upload-time = "2025-09-11T17:39:40.846Z" }, + { url = "https://files.pythonhosted.org/packages/96/0b/7399dc96e1e3f9a05e258c98d716196a34f528eef2ec55aad651ed136d03/scipy-1.16.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e574be127bb760f0dad24ff6e217c80213d153058372362ccb9555a10fc5e8d2", size = 33583702, upload-time = "2025-09-11T17:39:49.011Z" }, + { url = "https://files.pythonhosted.org/packages/1a/bc/a5c75095089b96ea72c1bd37a4497c24b581ec73db4ef58ebee142ad2d14/scipy-1.16.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f5db5ba6188d698ba7abab982ad6973265b74bb40a1efe1821b58c87f73892b9", size = 35883427, upload-time = "2025-09-11T17:39:57.406Z" }, + { url = "https://files.pythonhosted.org/packages/ab/66/e25705ca3d2b87b97fe0a278a24b7f477b4023a926847935a1a71488a6a6/scipy-1.16.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ec6e74c4e884104ae006d34110677bfe0098203a3fec2f3faf349f4cb05165e3", size = 36212940, upload-time = "2025-09-11T17:40:06.013Z" }, + { url = "https://files.pythonhosted.org/packages/d6/fd/0bb911585e12f3abdd603d721d83fc1c7492835e1401a0e6d498d7822b4b/scipy-1.16.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:912f46667d2d3834bc3d57361f854226475f695eb08c08a904aadb1c936b6a88", size = 38865092, upload-time = "2025-09-11T17:40:15.143Z" }, + { url = "https://files.pythonhosted.org/packages/d6/73/c449a7d56ba6e6f874183759f8483cde21f900a8be117d67ffbb670c2958/scipy-1.16.2-cp311-cp311-win_amd64.whl", hash = "sha256:91e9e8a37befa5a69e9cacbe0bcb79ae5afb4a0b130fd6db6ee6cc0d491695fa", size = 38687626, upload-time = "2025-09-11T17:40:24.041Z" }, + { url = "https://files.pythonhosted.org/packages/68/72/02f37316adf95307f5d9e579023c6899f89ff3a051fa079dbd6faafc48e5/scipy-1.16.2-cp311-cp311-win_arm64.whl", hash = "sha256:f3bf75a6dcecab62afde4d1f973f1692be013110cad5338007927db8da73249c", size = 25503506, upload-time = "2025-09-11T17:40:30.703Z" }, + { url = "https://files.pythonhosted.org/packages/b7/8d/6396e00db1282279a4ddd507c5f5e11f606812b608ee58517ce8abbf883f/scipy-1.16.2-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:89d6c100fa5c48472047632e06f0876b3c4931aac1f4291afc81a3644316bb0d", size = 36646259, upload-time = "2025-09-11T17:40:39.329Z" }, + { url = "https://files.pythonhosted.org/packages/3b/93/ea9edd7e193fceb8eef149804491890bde73fb169c896b61aa3e2d1e4e77/scipy-1.16.2-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:ca748936cd579d3f01928b30a17dc474550b01272d8046e3e1ee593f23620371", size = 28888976, upload-time = "2025-09-11T17:40:46.82Z" }, + { url = "https://files.pythonhosted.org/packages/91/4d/281fddc3d80fd738ba86fd3aed9202331180b01e2c78eaae0642f22f7e83/scipy-1.16.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:fac4f8ce2ddb40e2e3d0f7ec36d2a1e7f92559a2471e59aec37bd8d9de01fec0", size = 20879905, upload-time = "2025-09-11T17:40:52.545Z" }, + { url = "https://files.pythonhosted.org/packages/69/40/b33b74c84606fd301b2915f0062e45733c6ff5708d121dd0deaa8871e2d0/scipy-1.16.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:033570f1dcefd79547a88e18bccacff025c8c647a330381064f561d43b821232", size = 23553066, upload-time = "2025-09-11T17:40:59.014Z" }, + { url = "https://files.pythonhosted.org/packages/55/a7/22c739e2f21a42cc8f16bc76b47cff4ed54fbe0962832c589591c2abec34/scipy-1.16.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ea3421209bf00c8a5ef2227de496601087d8f638a2363ee09af059bd70976dc1", size = 33336407, upload-time = "2025-09-11T17:41:06.796Z" }, + { url = "https://files.pythonhosted.org/packages/53/11/a0160990b82999b45874dc60c0c183d3a3a969a563fffc476d5a9995c407/scipy-1.16.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f66bd07ba6f84cd4a380b41d1bf3c59ea488b590a2ff96744845163309ee8e2f", size = 35673281, upload-time = "2025-09-11T17:41:15.055Z" }, + { url = "https://files.pythonhosted.org/packages/96/53/7ef48a4cfcf243c3d0f1643f5887c81f29fdf76911c4e49331828e19fc0a/scipy-1.16.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5e9feab931bd2aea4a23388c962df6468af3d808ddf2d40f94a81c5dc38f32ef", size = 36004222, upload-time = "2025-09-11T17:41:23.868Z" }, + { url = "https://files.pythonhosted.org/packages/49/7f/71a69e0afd460049d41c65c630c919c537815277dfea214031005f474d78/scipy-1.16.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:03dfc75e52f72cf23ec2ced468645321407faad8f0fe7b1f5b49264adbc29cb1", size = 38664586, upload-time = "2025-09-11T17:41:31.021Z" }, + { url = "https://files.pythonhosted.org/packages/34/95/20e02ca66fb495a95fba0642fd48e0c390d0ece9b9b14c6e931a60a12dea/scipy-1.16.2-cp312-cp312-win_amd64.whl", hash = "sha256:0ce54e07bbb394b417457409a64fd015be623f36e330ac49306433ffe04bc97e", size = 38550641, upload-time = "2025-09-11T17:41:36.61Z" }, + { url = "https://files.pythonhosted.org/packages/92/ad/13646b9beb0a95528ca46d52b7babafbe115017814a611f2065ee4e61d20/scipy-1.16.2-cp312-cp312-win_arm64.whl", hash = "sha256:2a8ffaa4ac0df81a0b94577b18ee079f13fecdb924df3328fc44a7dc5ac46851", size = 25456070, upload-time = "2025-09-11T17:41:41.3Z" }, + { url = "https://files.pythonhosted.org/packages/c1/27/c5b52f1ee81727a9fc457f5ac1e9bf3d6eab311805ea615c83c27ba06400/scipy-1.16.2-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:84f7bf944b43e20b8a894f5fe593976926744f6c185bacfcbdfbb62736b5cc70", size = 36604856, upload-time = "2025-09-11T17:41:47.695Z" }, + { url = "https://files.pythonhosted.org/packages/32/a9/15c20d08e950b540184caa8ced675ba1128accb0e09c653780ba023a4110/scipy-1.16.2-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:5c39026d12edc826a1ef2ad35ad1e6d7f087f934bb868fc43fa3049c8b8508f9", size = 28864626, upload-time = "2025-09-11T17:41:52.642Z" }, + { url = "https://files.pythonhosted.org/packages/4c/fc/ea36098df653cca26062a627c1a94b0de659e97127c8491e18713ca0e3b9/scipy-1.16.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e52729ffd45b68777c5319560014d6fd251294200625d9d70fd8626516fc49f5", size = 20855689, upload-time = "2025-09-11T17:41:57.886Z" }, + { url = "https://files.pythonhosted.org/packages/dc/6f/d0b53be55727f3e6d7c72687ec18ea6d0047cf95f1f77488b99a2bafaee1/scipy-1.16.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:024dd4a118cccec09ca3209b7e8e614931a6ffb804b2a601839499cb88bdf925", size = 23512151, upload-time = "2025-09-11T17:42:02.303Z" }, + { url = "https://files.pythonhosted.org/packages/11/85/bf7dab56e5c4b1d3d8eef92ca8ede788418ad38a7dc3ff50262f00808760/scipy-1.16.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7a5dc7ee9c33019973a470556081b0fd3c9f4c44019191039f9769183141a4d9", size = 33329824, upload-time = "2025-09-11T17:42:07.549Z" }, + { url = "https://files.pythonhosted.org/packages/da/6a/1a927b14ddc7714111ea51f4e568203b2bb6ed59bdd036d62127c1a360c8/scipy-1.16.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c2275ff105e508942f99d4e3bc56b6ef5e4b3c0af970386ca56b777608ce95b7", size = 35681881, upload-time = "2025-09-11T17:42:13.255Z" }, + { url = "https://files.pythonhosted.org/packages/c1/5f/331148ea5780b4fcc7007a4a6a6ee0a0c1507a796365cc642d4d226e1c3a/scipy-1.16.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:af80196eaa84f033e48444d2e0786ec47d328ba00c71e4299b602235ffef9acb", size = 36006219, upload-time = "2025-09-11T17:42:18.765Z" }, + { url = "https://files.pythonhosted.org/packages/46/3a/e991aa9d2aec723b4a8dcfbfc8365edec5d5e5f9f133888067f1cbb7dfc1/scipy-1.16.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9fb1eb735fe3d6ed1f89918224e3385fbf6f9e23757cacc35f9c78d3b712dd6e", size = 38682147, upload-time = "2025-09-11T17:42:25.177Z" }, + { url = "https://files.pythonhosted.org/packages/a1/57/0f38e396ad19e41b4c5db66130167eef8ee620a49bc7d0512e3bb67e0cab/scipy-1.16.2-cp313-cp313-win_amd64.whl", hash = "sha256:fda714cf45ba43c9d3bae8f2585c777f64e3f89a2e073b668b32ede412d8f52c", size = 38520766, upload-time = "2025-09-11T17:43:25.342Z" }, + { url = "https://files.pythonhosted.org/packages/1b/a5/85d3e867b6822d331e26c862a91375bb7746a0b458db5effa093d34cdb89/scipy-1.16.2-cp313-cp313-win_arm64.whl", hash = "sha256:2f5350da923ccfd0b00e07c3e5cfb316c1c0d6c1d864c07a72d092e9f20db104", size = 25451169, upload-time = "2025-09-11T17:43:30.198Z" }, + { url = "https://files.pythonhosted.org/packages/09/d9/60679189bcebda55992d1a45498de6d080dcaf21ce0c8f24f888117e0c2d/scipy-1.16.2-cp313-cp313t-macosx_10_14_x86_64.whl", hash = "sha256:53d8d2ee29b925344c13bda64ab51785f016b1b9617849dac10897f0701b20c1", size = 37012682, upload-time = "2025-09-11T17:42:30.677Z" }, + { url = "https://files.pythonhosted.org/packages/83/be/a99d13ee4d3b7887a96f8c71361b9659ba4ef34da0338f14891e102a127f/scipy-1.16.2-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:9e05e33657efb4c6a9d23bd8300101536abd99c85cca82da0bffff8d8764d08a", size = 29389926, upload-time = "2025-09-11T17:42:35.845Z" }, + { url = "https://files.pythonhosted.org/packages/bf/0a/130164a4881cec6ca8c00faf3b57926f28ed429cd6001a673f83c7c2a579/scipy-1.16.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:7fe65b36036357003b3ef9d37547abeefaa353b237e989c21027b8ed62b12d4f", size = 21381152, upload-time = "2025-09-11T17:42:40.07Z" }, + { url = "https://files.pythonhosted.org/packages/47/a6/503ffb0310ae77fba874e10cddfc4a1280bdcca1d13c3751b8c3c2996cf8/scipy-1.16.2-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:6406d2ac6d40b861cccf57f49592f9779071655e9f75cd4f977fa0bdd09cb2e4", size = 23914410, upload-time = "2025-09-11T17:42:44.313Z" }, + { url = "https://files.pythonhosted.org/packages/fa/c7/1147774bcea50d00c02600aadaa919facbd8537997a62496270133536ed6/scipy-1.16.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ff4dc42bd321991fbf611c23fc35912d690f731c9914bf3af8f417e64aca0f21", size = 33481880, upload-time = "2025-09-11T17:42:49.325Z" }, + { url = "https://files.pythonhosted.org/packages/6a/74/99d5415e4c3e46b2586f30cdbecb95e101c7192628a484a40dd0d163811a/scipy-1.16.2-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:654324826654d4d9133e10675325708fb954bc84dae6e9ad0a52e75c6b1a01d7", size = 35791425, upload-time = "2025-09-11T17:42:54.711Z" }, + { url = "https://files.pythonhosted.org/packages/1b/ee/a6559de7c1cc710e938c0355d9d4fbcd732dac4d0d131959d1f3b63eb29c/scipy-1.16.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:63870a84cd15c44e65220eaed2dac0e8f8b26bbb991456a033c1d9abfe8a94f8", size = 36178622, upload-time = "2025-09-11T17:43:00.375Z" }, + { url = "https://files.pythonhosted.org/packages/4e/7b/f127a5795d5ba8ece4e0dce7d4a9fb7cb9e4f4757137757d7a69ab7d4f1a/scipy-1.16.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:fa01f0f6a3050fa6a9771a95d5faccc8e2f5a92b4a2e5440a0fa7264a2398472", size = 38783985, upload-time = "2025-09-11T17:43:06.661Z" }, + { url = "https://files.pythonhosted.org/packages/3e/9f/bc81c1d1e033951eb5912cd3750cc005943afa3e65a725d2443a3b3c4347/scipy-1.16.2-cp313-cp313t-win_amd64.whl", hash = "sha256:116296e89fba96f76353a8579820c2512f6e55835d3fad7780fece04367de351", size = 38631367, upload-time = "2025-09-11T17:43:14.44Z" }, + { url = "https://files.pythonhosted.org/packages/d6/5e/2cc7555fd81d01814271412a1d59a289d25f8b63208a0a16c21069d55d3e/scipy-1.16.2-cp313-cp313t-win_arm64.whl", hash = "sha256:98e22834650be81d42982360382b43b17f7ba95e0e6993e2a4f5b9ad9283a94d", size = 25787992, upload-time = "2025-09-11T17:43:19.745Z" }, + { url = "https://files.pythonhosted.org/packages/8b/ac/ad8951250516db71619f0bd3b2eb2448db04b720a003dd98619b78b692c0/scipy-1.16.2-cp314-cp314-macosx_10_14_x86_64.whl", hash = "sha256:567e77755019bb7461513c87f02bb73fb65b11f049aaaa8ca17cfaa5a5c45d77", size = 36595109, upload-time = "2025-09-11T17:43:35.713Z" }, + { url = "https://files.pythonhosted.org/packages/ff/f6/5779049ed119c5b503b0f3dc6d6f3f68eefc3a9190d4ad4c276f854f051b/scipy-1.16.2-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:17d9bb346194e8967296621208fcdfd39b55498ef7d2f376884d5ac47cec1a70", size = 28859110, upload-time = "2025-09-11T17:43:40.814Z" }, + { url = "https://files.pythonhosted.org/packages/82/09/9986e410ae38bf0a0c737ff8189ac81a93b8e42349aac009891c054403d7/scipy-1.16.2-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:0a17541827a9b78b777d33b623a6dcfe2ef4a25806204d08ead0768f4e529a88", size = 20850110, upload-time = "2025-09-11T17:43:44.981Z" }, + { url = "https://files.pythonhosted.org/packages/0d/ad/485cdef2d9215e2a7df6d61b81d2ac073dfacf6ae24b9ae87274c4e936ae/scipy-1.16.2-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:d7d4c6ba016ffc0f9568d012f5f1eb77ddd99412aea121e6fa8b4c3b7cbad91f", size = 23497014, upload-time = "2025-09-11T17:43:49.074Z" }, + { url = "https://files.pythonhosted.org/packages/a7/74/f6a852e5d581122b8f0f831f1d1e32fb8987776ed3658e95c377d308ed86/scipy-1.16.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9702c4c023227785c779cba2e1d6f7635dbb5b2e0936cdd3a4ecb98d78fd41eb", size = 33401155, upload-time = "2025-09-11T17:43:54.661Z" }, + { url = "https://files.pythonhosted.org/packages/d9/f5/61d243bbc7c6e5e4e13dde9887e84a5cbe9e0f75fd09843044af1590844e/scipy-1.16.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d1cdf0ac28948d225decdefcc45ad7dd91716c29ab56ef32f8e0d50657dffcc7", size = 35691174, upload-time = "2025-09-11T17:44:00.101Z" }, + { url = "https://files.pythonhosted.org/packages/03/99/59933956331f8cc57e406cdb7a483906c74706b156998f322913e789c7e1/scipy-1.16.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:70327d6aa572a17c2941cdfb20673f82e536e91850a2e4cb0c5b858b690e1548", size = 36070752, upload-time = "2025-09-11T17:44:05.619Z" }, + { url = "https://files.pythonhosted.org/packages/c6/7d/00f825cfb47ee19ef74ecf01244b43e95eae74e7e0ff796026ea7cd98456/scipy-1.16.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5221c0b2a4b58aa7c4ed0387d360fd90ee9086d383bb34d9f2789fafddc8a936", size = 38701010, upload-time = "2025-09-11T17:44:11.322Z" }, + { url = "https://files.pythonhosted.org/packages/e4/9f/b62587029980378304ba5a8563d376c96f40b1e133daacee76efdcae32de/scipy-1.16.2-cp314-cp314-win_amd64.whl", hash = "sha256:f5a85d7b2b708025af08f060a496dd261055b617d776fc05a1a1cc69e09fe9ff", size = 39360061, upload-time = "2025-09-11T17:45:09.814Z" }, + { url = "https://files.pythonhosted.org/packages/82/04/7a2f1609921352c7fbee0815811b5050582f67f19983096c4769867ca45f/scipy-1.16.2-cp314-cp314-win_arm64.whl", hash = "sha256:2cc73a33305b4b24556957d5857d6253ce1e2dcd67fa0ff46d87d1670b3e1e1d", size = 26126914, upload-time = "2025-09-11T17:45:14.73Z" }, + { url = "https://files.pythonhosted.org/packages/51/b9/60929ce350c16b221928725d2d1d7f86cf96b8bc07415547057d1196dc92/scipy-1.16.2-cp314-cp314t-macosx_10_14_x86_64.whl", hash = "sha256:9ea2a3fed83065d77367775d689401a703d0f697420719ee10c0780bcab594d8", size = 37013193, upload-time = "2025-09-11T17:44:16.757Z" }, + { url = "https://files.pythonhosted.org/packages/2a/41/ed80e67782d4bc5fc85a966bc356c601afddd175856ba7c7bb6d9490607e/scipy-1.16.2-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:7280d926f11ca945c3ef92ba960fa924e1465f8d07ce3a9923080363390624c4", size = 29390172, upload-time = "2025-09-11T17:44:21.783Z" }, + { url = "https://files.pythonhosted.org/packages/c4/a3/2f673ace4090452696ccded5f5f8efffb353b8f3628f823a110e0170b605/scipy-1.16.2-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:8afae1756f6a1fe04636407ef7dbece33d826a5d462b74f3d0eb82deabefd831", size = 21381326, upload-time = "2025-09-11T17:44:25.982Z" }, + { url = "https://files.pythonhosted.org/packages/42/bf/59df61c5d51395066c35836b78136accf506197617c8662e60ea209881e1/scipy-1.16.2-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:5c66511f29aa8d233388e7416a3f20d5cae7a2744d5cee2ecd38c081f4e861b3", size = 23915036, upload-time = "2025-09-11T17:44:30.527Z" }, + { url = "https://files.pythonhosted.org/packages/91/c3/edc7b300dc16847ad3672f1a6f3f7c5d13522b21b84b81c265f4f2760d4a/scipy-1.16.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:efe6305aeaa0e96b0ccca5ff647a43737d9a092064a3894e46c414db84bc54ac", size = 33484341, upload-time = "2025-09-11T17:44:35.981Z" }, + { url = "https://files.pythonhosted.org/packages/26/c7/24d1524e72f06ff141e8d04b833c20db3021020563272ccb1b83860082a9/scipy-1.16.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7f3a337d9ae06a1e8d655ee9d8ecb835ea5ddcdcbd8d23012afa055ab014f374", size = 35790840, upload-time = "2025-09-11T17:44:41.76Z" }, + { url = "https://files.pythonhosted.org/packages/aa/b7/5aaad984eeedd56858dc33d75efa59e8ce798d918e1033ef62d2708f2c3d/scipy-1.16.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bab3605795d269067d8ce78a910220262711b753de8913d3deeaedb5dded3bb6", size = 36174716, upload-time = "2025-09-11T17:44:47.316Z" }, + { url = "https://files.pythonhosted.org/packages/fd/c2/e276a237acb09824822b0ada11b028ed4067fdc367a946730979feacb870/scipy-1.16.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b0348d8ddb55be2a844c518cd8cc8deeeb8aeba707cf834db5758fc89b476a2c", size = 38790088, upload-time = "2025-09-11T17:44:53.011Z" }, + { url = "https://files.pythonhosted.org/packages/c6/b4/5c18a766e8353015439f3780f5fc473f36f9762edc1a2e45da3ff5a31b21/scipy-1.16.2-cp314-cp314t-win_amd64.whl", hash = "sha256:26284797e38b8a75e14ea6631d29bda11e76ceaa6ddb6fdebbfe4c4d90faf2f9", size = 39457455, upload-time = "2025-09-11T17:44:58.899Z" }, + { url = "https://files.pythonhosted.org/packages/97/30/2f9a5243008f76dfc5dee9a53dfb939d9b31e16ce4bd4f2e628bfc5d89d2/scipy-1.16.2-cp314-cp314t-win_arm64.whl", hash = "sha256:d2a4472c231328d4de38d5f1f68fdd6d28a615138f842580a8a321b5845cf779", size = 26448374, upload-time = "2025-09-11T17:45:03.45Z" }, ] [[package]] @@ -3832,7 +3820,7 @@ wheels = [ [[package]] name = "speechbrain" version = "1.1.0" -source = { git = "https://github.com/speechbrain/speechbrain.git?rev=develop#9c826ef3937bac885c8a6ca86c6889a30f256fb5" } +source = { git = "https://github.com/BeckettFrey/speechbrain.git?branch=fix%2Fwindows-lazy-import-inspect-path#a89d8ffd2354bdb47c22010676eaf79ac7498b0c" } dependencies = [ { name = "huggingface-hub" }, { name = "hyperpyyaml" }, @@ -3894,15 +3882,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" }, ] -[[package]] -name = "tdqm" -version = "0.0.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "tqdm" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5a/38/58c9e22b95e98666fe29f35e217ab6126a03798dadf3f4f8f3e5f898510b/tdqm-0.0.1.tar.gz", hash = "sha256:f050004a76b1d22f70b78209b48781353c82440215b6ba7d22b1f499b05a0101", size = 1388, upload-time = "2020-08-19T20:16:22.191Z" } - [[package]] name = "threadpoolctl" version = "3.6.0" @@ -4067,14 +4046,14 @@ wheels = [ [[package]] name = "tqdm" -version = "4.67.3" +version = "4.67.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/09/a9/6ba95a270c6f1fbcd8dac228323f2777d886cb206987444e4bce66338dd4/tqdm-4.67.3.tar.gz", hash = "sha256:7d825f03f89244ef73f1d4ce193cb1774a8179fd96f31d7e1dcde62092b960bb", size = 169598, upload-time = "2026-02-03T17:35:53.048Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737, upload-time = "2024-11-24T20:12:22.481Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/16/e1/3079a9ff9b8e11b846c6ac5c8b5bfb7ff225eee721825310c91b3b50304f/tqdm-4.67.3-py3-none-any.whl", hash = "sha256:ee1e4c0e59148062281c49d80b25b67771a127c85fc9676d3be5f243206826bf", size = 78374, upload-time = "2026-02-03T17:35:50.982Z" }, + { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540, upload-time = "2024-11-24T20:12:19.698Z" }, ] [[package]] @@ -4125,7 +4104,7 @@ wheels = [ [[package]] name = "typer" -version = "0.24.2" +version = "0.25.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-doc" }, @@ -4133,9 +4112,9 @@ dependencies = [ { name = "rich" }, { name = "shellingham" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/83/b8/9ebb531b6c2d377af08ac6746a5df3425b21853a5d2260876919b58a2a4a/typer-0.24.2.tar.gz", hash = "sha256:ec070dcfca1408e85ee203c6365001e818c3b7fffe686fd07ff2d68095ca0480", size = 119849, upload-time = "2026-04-22T17:45:34.413Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7b/27/ede8cec7596e0041ba7e7b80b47d132562f56ff454313a16f6084e555c9f/typer-0.25.0.tar.gz", hash = "sha256:123eaf9f19bb40fd268310e12a542c0c6b4fab9c98d9d23342a01ff95e3ce930", size = 120150, upload-time = "2026-04-26T08:46:14.767Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/39/d1/9484b497e0a0410b901c12b8251c3e746e1e863f7d28419ffe06f7892fda/typer-0.24.2-py3-none-any.whl", hash = "sha256:b618bc3d721f9a8d30f3e05565be26416d06e9bcc29d49bc491dc26aba674fa8", size = 55977, upload-time = "2026-04-22T17:45:33.055Z" }, + { url = "https://files.pythonhosted.org/packages/9a/72/193d4e586ec5a4db834a36bbeb47641a62f951f114ffd0fe5b1b46e8d56f/typer-0.25.0-py3-none-any.whl", hash = "sha256:ac01b48823d3db9a83c9e164338057eadbb1c9957a2a6b4eeb486669c560b5dc", size = 55993, upload-time = "2026-04-26T08:46:15.889Z" }, ] [[package]] @@ -4188,7 +4167,7 @@ wheels = [ [[package]] name = "virtualenv" -version = "21.2.4" +version = "21.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "distlib" }, @@ -4196,15 +4175,15 @@ dependencies = [ { name = "platformdirs" }, { name = "python-discovery" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0c/98/3a7e644e19cb26133488caff231be390579860bbbb3da35913c49a1d0a46/virtualenv-21.2.4.tar.gz", hash = "sha256:b294ef68192638004d72524ce7ef303e9d0cf5a44c95ce2e54a7500a6381cada", size = 5850742, upload-time = "2026-04-14T22:15:31.438Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3f/8b/6331f7a7fe70131c301106ec1e7cf23e2501bf7d4ca3636805801ca191bb/virtualenv-21.3.0.tar.gz", hash = "sha256:733750db978ec95c2d8eb4feadaa57091002bce404cb39ba69899cf7bd28944e", size = 7614069, upload-time = "2026-04-27T17:05:58.927Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/27/8d/edd0bd910ff803c308ee9a6b7778621af0d10252219ad9f19ef4d4982a61/virtualenv-21.2.4-py3-none-any.whl", hash = "sha256:29d21e941795206138d0f22f4e45ff7050e5da6c6472299fb7103318763861ac", size = 5831232, upload-time = "2026-04-14T22:15:29.342Z" }, + { url = "https://files.pythonhosted.org/packages/4b/eb/03bfb1299d4c4510329e470f13f9a4ce793df7fcb5a2fd3510f911066f61/virtualenv-21.3.0-py3-none-any.whl", hash = "sha256:4d28ee41f6d9ec8f1f00cd472b9ffbcedda1b3d3b9a575b5c94a2d004fd51bd7", size = 7594690, upload-time = "2026-04-27T17:05:55.468Z" }, ] [[package]] name = "wav2textgrid" version = "0.1.2" -source = { git = "https://github.com/pkadambi/Wav2TextGrid.git?rev=3862476185a7558d481f1ae1c9cbca2e48c825a0#3862476185a7558d481f1ae1c9cbca2e48c825a0" } +source = { git = "https://github.com/pkadambi/Wav2TextGrid?branch=voxkit-windows-variant#57f50b79c8f7c68cc0f24c1df197f888aa9db3f2" } dependencies = [ { name = "argparse" }, { name = "g2p-en" }, @@ -4214,7 +4193,6 @@ dependencies = [ { name = "numpy" }, { name = "pandas" }, { name = "praatio" }, - { name = "speechbrain" }, { name = "torch" }, { name = "torchaudio" }, { name = "tqdm" }, @@ -4223,105 +4201,140 @@ dependencies = [ [[package]] name = "xxhash" -version = "3.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/02/84/30869e01909fb37a6cc7e18688ee8bf1e42d57e7e0777636bd47524c43c7/xxhash-3.6.0.tar.gz", hash = "sha256:f0162a78b13a0d7617b2845b90c763339d1f1d82bb04a4b07f4ab535cc5e05d6", size = 85160, upload-time = "2025-10-02T14:37:08.097Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/17/d4/cc2f0400e9154df4b9964249da78ebd72f318e35ccc425e9f403c392f22a/xxhash-3.6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b47bbd8cf2d72797f3c2772eaaac0ded3d3af26481a26d7d7d41dc2d3c46b04a", size = 32844, upload-time = "2025-10-02T14:34:14.037Z" }, - { url = "https://files.pythonhosted.org/packages/5e/ec/1cc11cd13e26ea8bc3cb4af4eaadd8d46d5014aebb67be3f71fb0b68802a/xxhash-3.6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2b6821e94346f96db75abaa6e255706fb06ebd530899ed76d32cd99f20dc52fa", size = 30809, upload-time = "2025-10-02T14:34:15.484Z" }, - { url = "https://files.pythonhosted.org/packages/04/5f/19fe357ea348d98ca22f456f75a30ac0916b51c753e1f8b2e0e6fb884cce/xxhash-3.6.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d0a9751f71a1a65ce3584e9cae4467651c7e70c9d31017fa57574583a4540248", size = 194665, upload-time = "2025-10-02T14:34:16.541Z" }, - { url = "https://files.pythonhosted.org/packages/90/3b/d1f1a8f5442a5fd8beedae110c5af7604dc37349a8e16519c13c19a9a2de/xxhash-3.6.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b29ee68625ab37b04c0b40c3fafdf24d2f75ccd778333cfb698f65f6c463f62", size = 213550, upload-time = "2025-10-02T14:34:17.878Z" }, - { url = "https://files.pythonhosted.org/packages/c4/ef/3a9b05eb527457d5db13a135a2ae1a26c80fecd624d20f3e8dcc4cb170f3/xxhash-3.6.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6812c25fe0d6c36a46ccb002f40f27ac903bf18af9f6dd8f9669cb4d176ab18f", size = 212384, upload-time = "2025-10-02T14:34:19.182Z" }, - { url = "https://files.pythonhosted.org/packages/0f/18/ccc194ee698c6c623acbf0f8c2969811a8a4b6185af5e824cd27b9e4fd3e/xxhash-3.6.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4ccbff013972390b51a18ef1255ef5ac125c92dc9143b2d1909f59abc765540e", size = 445749, upload-time = "2025-10-02T14:34:20.659Z" }, - { url = "https://files.pythonhosted.org/packages/a5/86/cf2c0321dc3940a7aa73076f4fd677a0fb3e405cb297ead7d864fd90847e/xxhash-3.6.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:297b7fbf86c82c550e12e8fb71968b3f033d27b874276ba3624ea868c11165a8", size = 193880, upload-time = "2025-10-02T14:34:22.431Z" }, - { url = "https://files.pythonhosted.org/packages/82/fb/96213c8560e6f948a1ecc9a7613f8032b19ee45f747f4fca4eb31bb6d6ed/xxhash-3.6.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dea26ae1eb293db089798d3973a5fc928a18fdd97cc8801226fae705b02b14b0", size = 210912, upload-time = "2025-10-02T14:34:23.937Z" }, - { url = "https://files.pythonhosted.org/packages/40/aa/4395e669b0606a096d6788f40dbdf2b819d6773aa290c19e6e83cbfc312f/xxhash-3.6.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7a0b169aafb98f4284f73635a8e93f0735f9cbde17bd5ec332480484241aaa77", size = 198654, upload-time = "2025-10-02T14:34:25.644Z" }, - { url = "https://files.pythonhosted.org/packages/67/74/b044fcd6b3d89e9b1b665924d85d3f400636c23590226feb1eb09e1176ce/xxhash-3.6.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:08d45aef063a4531b785cd72de4887766d01dc8f362a515693df349fdb825e0c", size = 210867, upload-time = "2025-10-02T14:34:27.203Z" }, - { url = "https://files.pythonhosted.org/packages/bc/fd/3ce73bf753b08cb19daee1eb14aa0d7fe331f8da9c02dd95316ddfe5275e/xxhash-3.6.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:929142361a48ee07f09121fe9e96a84950e8d4df3bb298ca5d88061969f34d7b", size = 414012, upload-time = "2025-10-02T14:34:28.409Z" }, - { url = "https://files.pythonhosted.org/packages/ba/b3/5a4241309217c5c876f156b10778f3ab3af7ba7e3259e6d5f5c7d0129eb2/xxhash-3.6.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:51312c768403d8540487dbbfb557454cfc55589bbde6424456951f7fcd4facb3", size = 191409, upload-time = "2025-10-02T14:34:29.696Z" }, - { url = "https://files.pythonhosted.org/packages/c0/01/99bfbc15fb9abb9a72b088c1d95219fc4782b7d01fc835bd5744d66dd0b8/xxhash-3.6.0-cp311-cp311-win32.whl", hash = "sha256:d1927a69feddc24c987b337ce81ac15c4720955b667fe9b588e02254b80446fd", size = 30574, upload-time = "2025-10-02T14:34:31.028Z" }, - { url = "https://files.pythonhosted.org/packages/65/79/9d24d7f53819fe301b231044ea362ce64e86c74f6e8c8e51320de248b3e5/xxhash-3.6.0-cp311-cp311-win_amd64.whl", hash = "sha256:26734cdc2d4ffe449b41d186bbeac416f704a482ed835d375a5c0cb02bc63fef", size = 31481, upload-time = "2025-10-02T14:34:32.062Z" }, - { url = "https://files.pythonhosted.org/packages/30/4e/15cd0e3e8772071344eab2961ce83f6e485111fed8beb491a3f1ce100270/xxhash-3.6.0-cp311-cp311-win_arm64.whl", hash = "sha256:d72f67ef8bf36e05f5b6c65e8524f265bd61071471cd4cf1d36743ebeeeb06b7", size = 27861, upload-time = "2025-10-02T14:34:33.555Z" }, - { url = "https://files.pythonhosted.org/packages/9a/07/d9412f3d7d462347e4511181dea65e47e0d0e16e26fbee2ea86a2aefb657/xxhash-3.6.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:01362c4331775398e7bb34e3ab403bc9ee9f7c497bc7dee6272114055277dd3c", size = 32744, upload-time = "2025-10-02T14:34:34.622Z" }, - { url = "https://files.pythonhosted.org/packages/79/35/0429ee11d035fc33abe32dca1b2b69e8c18d236547b9a9b72c1929189b9a/xxhash-3.6.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b7b2df81a23f8cb99656378e72501b2cb41b1827c0f5a86f87d6b06b69f9f204", size = 30816, upload-time = "2025-10-02T14:34:36.043Z" }, - { url = "https://files.pythonhosted.org/packages/b7/f2/57eb99aa0f7d98624c0932c5b9a170e1806406cdbcdb510546634a1359e0/xxhash-3.6.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:dc94790144e66b14f67b10ac8ed75b39ca47536bf8800eb7c24b50271ea0c490", size = 194035, upload-time = "2025-10-02T14:34:37.354Z" }, - { url = "https://files.pythonhosted.org/packages/4c/ed/6224ba353690d73af7a3f1c7cdb1fc1b002e38f783cb991ae338e1eb3d79/xxhash-3.6.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:93f107c673bccf0d592cdba077dedaf52fe7f42dcd7676eba1f6d6f0c3efffd2", size = 212914, upload-time = "2025-10-02T14:34:38.6Z" }, - { url = "https://files.pythonhosted.org/packages/38/86/fb6b6130d8dd6b8942cc17ab4d90e223653a89aa32ad2776f8af7064ed13/xxhash-3.6.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2aa5ee3444c25b69813663c9f8067dcfaa2e126dc55e8dddf40f4d1c25d7effa", size = 212163, upload-time = "2025-10-02T14:34:39.872Z" }, - { url = "https://files.pythonhosted.org/packages/ee/dc/e84875682b0593e884ad73b2d40767b5790d417bde603cceb6878901d647/xxhash-3.6.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f7f99123f0e1194fa59cc69ad46dbae2e07becec5df50a0509a808f90a0f03f0", size = 445411, upload-time = "2025-10-02T14:34:41.569Z" }, - { url = "https://files.pythonhosted.org/packages/11/4f/426f91b96701ec2f37bb2b8cec664eff4f658a11f3fa9d94f0a887ea6d2b/xxhash-3.6.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:49e03e6fe2cac4a1bc64952dd250cf0dbc5ef4ebb7b8d96bce82e2de163c82a2", size = 193883, upload-time = "2025-10-02T14:34:43.249Z" }, - { url = "https://files.pythonhosted.org/packages/53/5a/ddbb83eee8e28b778eacfc5a85c969673e4023cdeedcfcef61f36731610b/xxhash-3.6.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bd17fede52a17a4f9a7bc4472a5867cb0b160deeb431795c0e4abe158bc784e9", size = 210392, upload-time = "2025-10-02T14:34:45.042Z" }, - { url = "https://files.pythonhosted.org/packages/1e/c2/ff69efd07c8c074ccdf0a4f36fcdd3d27363665bcdf4ba399abebe643465/xxhash-3.6.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:6fb5f5476bef678f69db04f2bd1efbed3030d2aba305b0fc1773645f187d6a4e", size = 197898, upload-time = "2025-10-02T14:34:46.302Z" }, - { url = "https://files.pythonhosted.org/packages/58/ca/faa05ac19b3b622c7c9317ac3e23954187516298a091eb02c976d0d3dd45/xxhash-3.6.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:843b52f6d88071f87eba1631b684fcb4b2068cd2180a0224122fe4ef011a9374", size = 210655, upload-time = "2025-10-02T14:34:47.571Z" }, - { url = "https://files.pythonhosted.org/packages/d4/7a/06aa7482345480cc0cb597f5c875b11a82c3953f534394f620b0be2f700c/xxhash-3.6.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:7d14a6cfaf03b1b6f5f9790f76880601ccc7896aff7ab9cd8978a939c1eb7e0d", size = 414001, upload-time = "2025-10-02T14:34:49.273Z" }, - { url = "https://files.pythonhosted.org/packages/23/07/63ffb386cd47029aa2916b3d2f454e6cc5b9f5c5ada3790377d5430084e7/xxhash-3.6.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:418daf3db71e1413cfe211c2f9a528456936645c17f46b5204705581a45390ae", size = 191431, upload-time = "2025-10-02T14:34:50.798Z" }, - { url = "https://files.pythonhosted.org/packages/0f/93/14fde614cadb4ddf5e7cebf8918b7e8fac5ae7861c1875964f17e678205c/xxhash-3.6.0-cp312-cp312-win32.whl", hash = "sha256:50fc255f39428a27299c20e280d6193d8b63b8ef8028995323bf834a026b4fbb", size = 30617, upload-time = "2025-10-02T14:34:51.954Z" }, - { url = "https://files.pythonhosted.org/packages/13/5d/0d125536cbe7565a83d06e43783389ecae0c0f2ed037b48ede185de477c0/xxhash-3.6.0-cp312-cp312-win_amd64.whl", hash = "sha256:c0f2ab8c715630565ab8991b536ecded9416d615538be8ecddce43ccf26cbc7c", size = 31534, upload-time = "2025-10-02T14:34:53.276Z" }, - { url = "https://files.pythonhosted.org/packages/54/85/6ec269b0952ec7e36ba019125982cf11d91256a778c7c3f98a4c5043d283/xxhash-3.6.0-cp312-cp312-win_arm64.whl", hash = "sha256:eae5c13f3bc455a3bbb68bdc513912dc7356de7e2280363ea235f71f54064829", size = 27876, upload-time = "2025-10-02T14:34:54.371Z" }, - { url = "https://files.pythonhosted.org/packages/33/76/35d05267ac82f53ae9b0e554da7c5e281ee61f3cad44c743f0fcd354f211/xxhash-3.6.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:599e64ba7f67472481ceb6ee80fa3bd828fd61ba59fb11475572cc5ee52b89ec", size = 32738, upload-time = "2025-10-02T14:34:55.839Z" }, - { url = "https://files.pythonhosted.org/packages/31/a8/3fbce1cd96534a95e35d5120637bf29b0d7f5d8fa2f6374e31b4156dd419/xxhash-3.6.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7d8b8aaa30fca4f16f0c84a5c8d7ddee0e25250ec2796c973775373257dde8f1", size = 30821, upload-time = "2025-10-02T14:34:57.219Z" }, - { url = "https://files.pythonhosted.org/packages/0c/ea/d387530ca7ecfa183cb358027f1833297c6ac6098223fd14f9782cd0015c/xxhash-3.6.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d597acf8506d6e7101a4a44a5e428977a51c0fadbbfd3c39650cca9253f6e5a6", size = 194127, upload-time = "2025-10-02T14:34:59.21Z" }, - { url = "https://files.pythonhosted.org/packages/ba/0c/71435dcb99874b09a43b8d7c54071e600a7481e42b3e3ce1eb5226a5711a/xxhash-3.6.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:858dc935963a33bc33490128edc1c12b0c14d9c7ebaa4e387a7869ecc4f3e263", size = 212975, upload-time = "2025-10-02T14:35:00.816Z" }, - { url = "https://files.pythonhosted.org/packages/84/7a/c2b3d071e4bb4a90b7057228a99b10d51744878f4a8a6dd643c8bd897620/xxhash-3.6.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ba284920194615cb8edf73bf52236ce2e1664ccd4a38fdb543506413529cc546", size = 212241, upload-time = "2025-10-02T14:35:02.207Z" }, - { url = "https://files.pythonhosted.org/packages/81/5f/640b6eac0128e215f177df99eadcd0f1b7c42c274ab6a394a05059694c5a/xxhash-3.6.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4b54219177f6c6674d5378bd862c6aedf64725f70dd29c472eaae154df1a2e89", size = 445471, upload-time = "2025-10-02T14:35:03.61Z" }, - { url = "https://files.pythonhosted.org/packages/5e/1e/3c3d3ef071b051cc3abbe3721ffb8365033a172613c04af2da89d5548a87/xxhash-3.6.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:42c36dd7dbad2f5238950c377fcbf6811b1cdb1c444fab447960030cea60504d", size = 193936, upload-time = "2025-10-02T14:35:05.013Z" }, - { url = "https://files.pythonhosted.org/packages/2c/bd/4a5f68381939219abfe1c22a9e3a5854a4f6f6f3c4983a87d255f21f2e5d/xxhash-3.6.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f22927652cba98c44639ffdc7aaf35828dccf679b10b31c4ad72a5b530a18eb7", size = 210440, upload-time = "2025-10-02T14:35:06.239Z" }, - { url = "https://files.pythonhosted.org/packages/eb/37/b80fe3d5cfb9faff01a02121a0f4d565eb7237e9e5fc66e73017e74dcd36/xxhash-3.6.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b45fad44d9c5c119e9c6fbf2e1c656a46dc68e280275007bbfd3d572b21426db", size = 197990, upload-time = "2025-10-02T14:35:07.735Z" }, - { url = "https://files.pythonhosted.org/packages/d7/fd/2c0a00c97b9e18f72e1f240ad4e8f8a90fd9d408289ba9c7c495ed7dc05c/xxhash-3.6.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:6f2580ffab1a8b68ef2b901cde7e55fa8da5e4be0977c68f78fc80f3c143de42", size = 210689, upload-time = "2025-10-02T14:35:09.438Z" }, - { url = "https://files.pythonhosted.org/packages/93/86/5dd8076a926b9a95db3206aba20d89a7fc14dd5aac16e5c4de4b56033140/xxhash-3.6.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:40c391dd3cd041ebc3ffe6f2c862f402e306eb571422e0aa918d8070ba31da11", size = 414068, upload-time = "2025-10-02T14:35:11.162Z" }, - { url = "https://files.pythonhosted.org/packages/af/3c/0bb129170ee8f3650f08e993baee550a09593462a5cddd8e44d0011102b1/xxhash-3.6.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f205badabde7aafd1a31e8ca2a3e5a763107a71c397c4481d6a804eb5063d8bd", size = 191495, upload-time = "2025-10-02T14:35:12.971Z" }, - { url = "https://files.pythonhosted.org/packages/e9/3a/6797e0114c21d1725e2577508e24006fd7ff1d8c0c502d3b52e45c1771d8/xxhash-3.6.0-cp313-cp313-win32.whl", hash = "sha256:2577b276e060b73b73a53042ea5bd5203d3e6347ce0d09f98500f418a9fcf799", size = 30620, upload-time = "2025-10-02T14:35:14.129Z" }, - { url = "https://files.pythonhosted.org/packages/86/15/9bc32671e9a38b413a76d24722a2bf8784a132c043063a8f5152d390b0f9/xxhash-3.6.0-cp313-cp313-win_amd64.whl", hash = "sha256:757320d45d2fbcce8f30c42a6b2f47862967aea7bf458b9625b4bbe7ee390392", size = 31542, upload-time = "2025-10-02T14:35:15.21Z" }, - { url = "https://files.pythonhosted.org/packages/39/c5/cc01e4f6188656e56112d6a8e0dfe298a16934b8c47a247236549a3f7695/xxhash-3.6.0-cp313-cp313-win_arm64.whl", hash = "sha256:457b8f85dec5825eed7b69c11ae86834a018b8e3df5e77783c999663da2f96d6", size = 27880, upload-time = "2025-10-02T14:35:16.315Z" }, - { url = "https://files.pythonhosted.org/packages/f3/30/25e5321c8732759e930c555176d37e24ab84365482d257c3b16362235212/xxhash-3.6.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a42e633d75cdad6d625434e3468126c73f13f7584545a9cf34e883aa1710e702", size = 32956, upload-time = "2025-10-02T14:35:17.413Z" }, - { url = "https://files.pythonhosted.org/packages/9f/3c/0573299560d7d9f8ab1838f1efc021a280b5ae5ae2e849034ef3dee18810/xxhash-3.6.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:568a6d743219e717b07b4e03b0a828ce593833e498c3b64752e0f5df6bfe84db", size = 31072, upload-time = "2025-10-02T14:35:18.844Z" }, - { url = "https://files.pythonhosted.org/packages/7a/1c/52d83a06e417cd9d4137722693424885cc9878249beb3a7c829e74bf7ce9/xxhash-3.6.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:bec91b562d8012dae276af8025a55811b875baace6af510412a5e58e3121bc54", size = 196409, upload-time = "2025-10-02T14:35:20.31Z" }, - { url = "https://files.pythonhosted.org/packages/e3/8e/c6d158d12a79bbd0b878f8355432075fc82759e356ab5a111463422a239b/xxhash-3.6.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:78e7f2f4c521c30ad5e786fdd6bae89d47a32672a80195467b5de0480aa97b1f", size = 215736, upload-time = "2025-10-02T14:35:21.616Z" }, - { url = "https://files.pythonhosted.org/packages/bc/68/c4c80614716345d55071a396cf03d06e34b5f4917a467faf43083c995155/xxhash-3.6.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3ed0df1b11a79856df5ffcab572cbd6b9627034c1c748c5566fa79df9048a7c5", size = 214833, upload-time = "2025-10-02T14:35:23.32Z" }, - { url = "https://files.pythonhosted.org/packages/7e/e9/ae27c8ffec8b953efa84c7c4a6c6802c263d587b9fc0d6e7cea64e08c3af/xxhash-3.6.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0e4edbfc7d420925b0dd5e792478ed393d6e75ff8fc219a6546fb446b6a417b1", size = 448348, upload-time = "2025-10-02T14:35:25.111Z" }, - { url = "https://files.pythonhosted.org/packages/d7/6b/33e21afb1b5b3f46b74b6bd1913639066af218d704cc0941404ca717fc57/xxhash-3.6.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fba27a198363a7ef87f8c0f6b171ec36b674fe9053742c58dd7e3201c1ab30ee", size = 196070, upload-time = "2025-10-02T14:35:26.586Z" }, - { url = "https://files.pythonhosted.org/packages/96/b6/fcabd337bc5fa624e7203aa0fa7d0c49eed22f72e93229431752bddc83d9/xxhash-3.6.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:794fe9145fe60191c6532fa95063765529770edcdd67b3d537793e8004cabbfd", size = 212907, upload-time = "2025-10-02T14:35:28.087Z" }, - { url = "https://files.pythonhosted.org/packages/4b/d3/9ee6160e644d660fcf176c5825e61411c7f62648728f69c79ba237250143/xxhash-3.6.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:6105ef7e62b5ac73a837778efc331a591d8442f8ef5c7e102376506cb4ae2729", size = 200839, upload-time = "2025-10-02T14:35:29.857Z" }, - { url = "https://files.pythonhosted.org/packages/0d/98/e8de5baa5109394baf5118f5e72ab21a86387c4f89b0e77ef3e2f6b0327b/xxhash-3.6.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:f01375c0e55395b814a679b3eea205db7919ac2af213f4a6682e01220e5fe292", size = 213304, upload-time = "2025-10-02T14:35:31.222Z" }, - { url = "https://files.pythonhosted.org/packages/7b/1d/71056535dec5c3177eeb53e38e3d367dd1d16e024e63b1cee208d572a033/xxhash-3.6.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:d706dca2d24d834a4661619dcacf51a75c16d65985718d6a7d73c1eeeb903ddf", size = 416930, upload-time = "2025-10-02T14:35:32.517Z" }, - { url = "https://files.pythonhosted.org/packages/dc/6c/5cbde9de2cd967c322e651c65c543700b19e7ae3e0aae8ece3469bf9683d/xxhash-3.6.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5f059d9faeacd49c0215d66f4056e1326c80503f51a1532ca336a385edadd033", size = 193787, upload-time = "2025-10-02T14:35:33.827Z" }, - { url = "https://files.pythonhosted.org/packages/19/fa/0172e350361d61febcea941b0cc541d6e6c8d65d153e85f850a7b256ff8a/xxhash-3.6.0-cp313-cp313t-win32.whl", hash = "sha256:1244460adc3a9be84731d72b8e80625788e5815b68da3da8b83f78115a40a7ec", size = 30916, upload-time = "2025-10-02T14:35:35.107Z" }, - { url = "https://files.pythonhosted.org/packages/ad/e6/e8cf858a2b19d6d45820f072eff1bea413910592ff17157cabc5f1227a16/xxhash-3.6.0-cp313-cp313t-win_amd64.whl", hash = "sha256:b1e420ef35c503869c4064f4a2f2b08ad6431ab7b229a05cce39d74268bca6b8", size = 31799, upload-time = "2025-10-02T14:35:36.165Z" }, - { url = "https://files.pythonhosted.org/packages/56/15/064b197e855bfb7b343210e82490ae672f8bc7cdf3ddb02e92f64304ee8a/xxhash-3.6.0-cp313-cp313t-win_arm64.whl", hash = "sha256:ec44b73a4220623235f67a996c862049f375df3b1052d9899f40a6382c32d746", size = 28044, upload-time = "2025-10-02T14:35:37.195Z" }, - { url = "https://files.pythonhosted.org/packages/7e/5e/0138bc4484ea9b897864d59fce9be9086030825bc778b76cb5a33a906d37/xxhash-3.6.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:a40a3d35b204b7cc7643cbcf8c9976d818cb47befcfac8bbefec8038ac363f3e", size = 32754, upload-time = "2025-10-02T14:35:38.245Z" }, - { url = "https://files.pythonhosted.org/packages/18/d7/5dac2eb2ec75fd771957a13e5dda560efb2176d5203f39502a5fc571f899/xxhash-3.6.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a54844be970d3fc22630b32d515e79a90d0a3ddb2644d8d7402e3c4c8da61405", size = 30846, upload-time = "2025-10-02T14:35:39.6Z" }, - { url = "https://files.pythonhosted.org/packages/fe/71/8bc5be2bb00deb5682e92e8da955ebe5fa982da13a69da5a40a4c8db12fb/xxhash-3.6.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:016e9190af8f0a4e3741343777710e3d5717427f175adfdc3e72508f59e2a7f3", size = 194343, upload-time = "2025-10-02T14:35:40.69Z" }, - { url = "https://files.pythonhosted.org/packages/e7/3b/52badfb2aecec2c377ddf1ae75f55db3ba2d321c5e164f14461c90837ef3/xxhash-3.6.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4f6f72232f849eb9d0141e2ebe2677ece15adfd0fa599bc058aad83c714bb2c6", size = 213074, upload-time = "2025-10-02T14:35:42.29Z" }, - { url = "https://files.pythonhosted.org/packages/a2/2b/ae46b4e9b92e537fa30d03dbc19cdae57ed407e9c26d163895e968e3de85/xxhash-3.6.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:63275a8aba7865e44b1813d2177e0f5ea7eadad3dd063a21f7cf9afdc7054063", size = 212388, upload-time = "2025-10-02T14:35:43.929Z" }, - { url = "https://files.pythonhosted.org/packages/f5/80/49f88d3afc724b4ac7fbd664c8452d6db51b49915be48c6982659e0e7942/xxhash-3.6.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cd01fa2aa00d8b017c97eb46b9a794fbdca53fc14f845f5a328c71254b0abb7", size = 445614, upload-time = "2025-10-02T14:35:45.216Z" }, - { url = "https://files.pythonhosted.org/packages/ed/ba/603ce3961e339413543d8cd44f21f2c80e2a7c5cfe692a7b1f2cccf58f3c/xxhash-3.6.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0226aa89035b62b6a86d3c68df4d7c1f47a342b8683da2b60cedcddb46c4d95b", size = 194024, upload-time = "2025-10-02T14:35:46.959Z" }, - { url = "https://files.pythonhosted.org/packages/78/d1/8e225ff7113bf81545cfdcd79eef124a7b7064a0bba53605ff39590b95c2/xxhash-3.6.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c6e193e9f56e4ca4923c61238cdaced324f0feac782544eb4c6d55ad5cc99ddd", size = 210541, upload-time = "2025-10-02T14:35:48.301Z" }, - { url = "https://files.pythonhosted.org/packages/6f/58/0f89d149f0bad89def1a8dd38feb50ccdeb643d9797ec84707091d4cb494/xxhash-3.6.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:9176dcaddf4ca963d4deb93866d739a343c01c969231dbe21680e13a5d1a5bf0", size = 198305, upload-time = "2025-10-02T14:35:49.584Z" }, - { url = "https://files.pythonhosted.org/packages/11/38/5eab81580703c4df93feb5f32ff8fa7fe1e2c51c1f183ee4e48d4bb9d3d7/xxhash-3.6.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:c1ce4009c97a752e682b897aa99aef84191077a9433eb237774689f14f8ec152", size = 210848, upload-time = "2025-10-02T14:35:50.877Z" }, - { url = "https://files.pythonhosted.org/packages/5e/6b/953dc4b05c3ce678abca756416e4c130d2382f877a9c30a20d08ee6a77c0/xxhash-3.6.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:8cb2f4f679b01513b7adbb9b1b2f0f9cdc31b70007eaf9d59d0878809f385b11", size = 414142, upload-time = "2025-10-02T14:35:52.15Z" }, - { url = "https://files.pythonhosted.org/packages/08/a9/238ec0d4e81a10eb5026d4a6972677cbc898ba6c8b9dbaec12ae001b1b35/xxhash-3.6.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:653a91d7c2ab54a92c19ccf43508b6a555440b9be1bc8be553376778be7f20b5", size = 191547, upload-time = "2025-10-02T14:35:53.547Z" }, - { url = "https://files.pythonhosted.org/packages/f1/ee/3cf8589e06c2164ac77c3bf0aa127012801128f1feebf2a079272da5737c/xxhash-3.6.0-cp314-cp314-win32.whl", hash = "sha256:a756fe893389483ee8c394d06b5ab765d96e68fbbfe6fde7aa17e11f5720559f", size = 31214, upload-time = "2025-10-02T14:35:54.746Z" }, - { url = "https://files.pythonhosted.org/packages/02/5d/a19552fbc6ad4cb54ff953c3908bbc095f4a921bc569433d791f755186f1/xxhash-3.6.0-cp314-cp314-win_amd64.whl", hash = "sha256:39be8e4e142550ef69629c9cd71b88c90e9a5db703fecbcf265546d9536ca4ad", size = 32290, upload-time = "2025-10-02T14:35:55.791Z" }, - { url = "https://files.pythonhosted.org/packages/b1/11/dafa0643bc30442c887b55baf8e73353a344ee89c1901b5a5c54a6c17d39/xxhash-3.6.0-cp314-cp314-win_arm64.whl", hash = "sha256:25915e6000338999236f1eb68a02a32c3275ac338628a7eaa5a269c401995679", size = 28795, upload-time = "2025-10-02T14:35:57.162Z" }, - { url = "https://files.pythonhosted.org/packages/2c/db/0e99732ed7f64182aef4a6fb145e1a295558deec2a746265dcdec12d191e/xxhash-3.6.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c5294f596a9017ca5a3e3f8884c00b91ab2ad2933cf288f4923c3fd4346cf3d4", size = 32955, upload-time = "2025-10-02T14:35:58.267Z" }, - { url = "https://files.pythonhosted.org/packages/55/f4/2a7c3c68e564a099becfa44bb3d398810cc0ff6749b0d3cb8ccb93f23c14/xxhash-3.6.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1cf9dcc4ab9cff01dfbba78544297a3a01dafd60f3bde4e2bfd016cf7e4ddc67", size = 31072, upload-time = "2025-10-02T14:35:59.382Z" }, - { url = "https://files.pythonhosted.org/packages/c6/d9/72a29cddc7250e8a5819dad5d466facb5dc4c802ce120645630149127e73/xxhash-3.6.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:01262da8798422d0685f7cef03b2bd3f4f46511b02830861df548d7def4402ad", size = 196579, upload-time = "2025-10-02T14:36:00.838Z" }, - { url = "https://files.pythonhosted.org/packages/63/93/b21590e1e381040e2ca305a884d89e1c345b347404f7780f07f2cdd47ef4/xxhash-3.6.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:51a73fb7cb3a3ead9f7a8b583ffd9b8038e277cdb8cb87cf890e88b3456afa0b", size = 215854, upload-time = "2025-10-02T14:36:02.207Z" }, - { url = "https://files.pythonhosted.org/packages/ce/b8/edab8a7d4fa14e924b29be877d54155dcbd8b80be85ea00d2be3413a9ed4/xxhash-3.6.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b9c6df83594f7df8f7f708ce5ebeacfc69f72c9fbaaababf6cf4758eaada0c9b", size = 214965, upload-time = "2025-10-02T14:36:03.507Z" }, - { url = "https://files.pythonhosted.org/packages/27/67/dfa980ac7f0d509d54ea0d5a486d2bb4b80c3f1bb22b66e6a05d3efaf6c0/xxhash-3.6.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:627f0af069b0ea56f312fd5189001c24578868643203bca1abbc2c52d3a6f3ca", size = 448484, upload-time = "2025-10-02T14:36:04.828Z" }, - { url = "https://files.pythonhosted.org/packages/8c/63/8ffc2cc97e811c0ca5d00ab36604b3ea6f4254f20b7bc658ca825ce6c954/xxhash-3.6.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aa912c62f842dfd013c5f21a642c9c10cd9f4c4e943e0af83618b4a404d9091a", size = 196162, upload-time = "2025-10-02T14:36:06.182Z" }, - { url = "https://files.pythonhosted.org/packages/4b/77/07f0e7a3edd11a6097e990f6e5b815b6592459cb16dae990d967693e6ea9/xxhash-3.6.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:b465afd7909db30168ab62afe40b2fcf79eedc0b89a6c0ab3123515dc0df8b99", size = 213007, upload-time = "2025-10-02T14:36:07.733Z" }, - { url = "https://files.pythonhosted.org/packages/ae/d8/bc5fa0d152837117eb0bef6f83f956c509332ce133c91c63ce07ee7c4873/xxhash-3.6.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:a881851cf38b0a70e7c4d3ce81fc7afd86fbc2a024f4cfb2a97cf49ce04b75d3", size = 200956, upload-time = "2025-10-02T14:36:09.106Z" }, - { url = "https://files.pythonhosted.org/packages/26/a5/d749334130de9411783873e9b98ecc46688dad5db64ca6e04b02acc8b473/xxhash-3.6.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:9b3222c686a919a0f3253cfc12bb118b8b103506612253b5baeaac10d8027cf6", size = 213401, upload-time = "2025-10-02T14:36:10.585Z" }, - { url = "https://files.pythonhosted.org/packages/89/72/abed959c956a4bfc72b58c0384bb7940663c678127538634d896b1195c10/xxhash-3.6.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:c5aa639bc113e9286137cec8fadc20e9cd732b2cc385c0b7fa673b84fc1f2a93", size = 417083, upload-time = "2025-10-02T14:36:12.276Z" }, - { url = "https://files.pythonhosted.org/packages/0c/b3/62fd2b586283b7d7d665fb98e266decadf31f058f1cf6c478741f68af0cb/xxhash-3.6.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5c1343d49ac102799905e115aee590183c3921d475356cb24b4de29a4bc56518", size = 193913, upload-time = "2025-10-02T14:36:14.025Z" }, - { url = "https://files.pythonhosted.org/packages/9a/9a/c19c42c5b3f5a4aad748a6d5b4f23df3bed7ee5445accc65a0fb3ff03953/xxhash-3.6.0-cp314-cp314t-win32.whl", hash = "sha256:5851f033c3030dd95c086b4a36a2683c2ff4a799b23af60977188b057e467119", size = 31586, upload-time = "2025-10-02T14:36:15.603Z" }, - { url = "https://files.pythonhosted.org/packages/03/d6/4cc450345be9924fd5dc8c590ceda1db5b43a0a889587b0ae81a95511360/xxhash-3.6.0-cp314-cp314t-win_amd64.whl", hash = "sha256:0444e7967dac37569052d2409b00a8860c2135cff05502df4da80267d384849f", size = 32526, upload-time = "2025-10-02T14:36:16.708Z" }, - { url = "https://files.pythonhosted.org/packages/0f/c9/7243eb3f9eaabd1a88a5a5acadf06df2d83b100c62684b7425c6a11bcaa8/xxhash-3.6.0-cp314-cp314t-win_arm64.whl", hash = "sha256:bb79b1e63f6fd84ec778a4b1916dfe0a7c3fdb986c06addd5db3a0d413819d95", size = 28898, upload-time = "2025-10-02T14:36:17.843Z" }, - { url = "https://files.pythonhosted.org/packages/93/1e/8aec23647a34a249f62e2398c42955acd9b4c6ed5cf08cbea94dc46f78d2/xxhash-3.6.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0f7b7e2ec26c1666ad5fc9dbfa426a6a3367ceaf79db5dd76264659d509d73b0", size = 30662, upload-time = "2025-10-02T14:37:01.743Z" }, - { url = "https://files.pythonhosted.org/packages/b8/0b/b14510b38ba91caf43006209db846a696ceea6a847a0c9ba0a5b1adc53d6/xxhash-3.6.0-pp311-pypy311_pp73-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5dc1e14d14fa0f5789ec29a7062004b5933964bb9b02aae6622b8f530dc40296", size = 41056, upload-time = "2025-10-02T14:37:02.879Z" }, - { url = "https://files.pythonhosted.org/packages/50/55/15a7b8a56590e66ccd374bbfa3f9ffc45b810886c8c3b614e3f90bd2367c/xxhash-3.6.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:881b47fc47e051b37d94d13e7455131054b56749b91b508b0907eb07900d1c13", size = 36251, upload-time = "2025-10-02T14:37:04.44Z" }, - { url = "https://files.pythonhosted.org/packages/62/b2/5ac99a041a29e58e95f907876b04f7067a0242cb85b5f39e726153981503/xxhash-3.6.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c6dc31591899f5e5666f04cc2e529e69b4072827085c1ef15294d91a004bc1bd", size = 32481, upload-time = "2025-10-02T14:37:05.869Z" }, - { url = "https://files.pythonhosted.org/packages/7b/d9/8d95e906764a386a3d3b596f3c68bb63687dfca806373509f51ce8eea81f/xxhash-3.6.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:15e0dac10eb9309508bfc41f7f9deaa7755c69e35af835db9cb10751adebc35d", size = 31565, upload-time = "2025-10-02T14:37:06.966Z" }, +version = "3.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/24/2f/e183a1b407002f5af81822bee18b61cdb94b8670208ef34734d8d2b8ebe9/xxhash-3.7.0.tar.gz", hash = "sha256:6cc4eefbb542a5d6ffd6d70ea9c502957c925e800f998c5630ecc809d6702bae", size = 82022, upload-time = "2026-04-25T11:10:32.553Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/f4/7bd35089ff1f8e2c96baa2dce05775a122aacd2e3830a73165e27a4d0848/xxhash-3.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fdc7d06929ae28dda98297a18eef7b0fd38991a3b405d8d7b55c9ef24c296958", size = 33423, upload-time = "2026-04-25T11:05:47.628Z" }, + { url = "https://files.pythonhosted.org/packages/a3/26/4e00c88a6a2c8a759cfb77d2a9a405f901e8aa66e60ef1fd0aeb35edda48/xxhash-3.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea6daa712f4e094a30830cf01e9b47d03b24d05cc9dab8609f0d9a9db8454712", size = 30857, upload-time = "2026-04-25T11:05:49.189Z" }, + { url = "https://files.pythonhosted.org/packages/82/2f/eeb942c17a5a761a8f01cb9180a0b76bfb62a2c39e6f46b1f9001899027a/xxhash-3.7.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:9e6c0d843f1daf85ea23aeb053579135552bde575b7b98af20bfc667b6e4548d", size = 194702, upload-time = "2026-04-25T11:05:50.457Z" }, + { url = "https://files.pythonhosted.org/packages/0e/fd/96f132c08b1e5951c68691d3b9ec351ec2edc028f6a01fcd294f46b9d9f0/xxhash-3.7.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:363c139bf15e1ac5f136b981d3c077eb551299b1effede7f12faa010b8590a60", size = 213613, upload-time = "2026-04-25T11:05:52.571Z" }, + { url = "https://files.pythonhosted.org/packages/82/89/d4e92b796c5ed052d29ed324dbfc1dc1188e0c4bf64bebbf0f8fc20698df/xxhash-3.7.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a778b25874cb0f862eaab5986bff4ca49ffb0def7c0a34c237b948b3c6c775b2", size = 236726, upload-time = "2026-04-25T11:05:54.395Z" }, + { url = "https://files.pythonhosted.org/packages/40/f1/81fc4361921dc6e557a9c60cb3712f36d244d06eeeb71cd2f4252ac42678/xxhash-3.7.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3e1860f1e43d40e9d904cf22d93e587ea42e010ebce4160877e46bcab4bc232a", size = 212443, upload-time = "2026-04-25T11:05:56.334Z" }, + { url = "https://files.pythonhosted.org/packages/6a/d0/afeddd4cff50a332f50d4b8a2e8857673153ab0564ef472fcdeb0b5430df/xxhash-3.7.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9122ad6f867c4a0f5e655f5c3bdf89103852009dbb442a3d23e688b9e699e800", size = 445793, upload-time = "2026-04-25T11:05:58.953Z" }, + { url = "https://files.pythonhosted.org/packages/f7/d0/3c91e4e6a05ca4d7df8e39ec3a75b713609258ec84705ab34be6430826a1/xxhash-3.7.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d7d9110d0c3fb02679972837a033251fd186c529aa62f19c132fc909c74052b8", size = 193937, upload-time = "2026-04-25T11:06:00.546Z" }, + { url = "https://files.pythonhosted.org/packages/4e/3a/a6b0772d9801dd4bea4ca4fd34734d6e9b51a711c8a611a24a79de26a878/xxhash-3.7.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:347a93f2b4ce67ce61959665e32a7447c380f8347e55e100daa23766baacf0e5", size = 285188, upload-time = "2026-04-25T11:06:01.96Z" }, + { url = "https://files.pythonhosted.org/packages/6c/f8/cf8e31fd7282230fe7367cd501a2e75b4b67b222bfc7eacccfc20d2652cb/xxhash-3.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:acbb48679ddf3852c45280c10ff10d52ca2cd1da2e552fb81db1ff786c75d0e4", size = 210966, upload-time = "2026-04-25T11:06:03.453Z" }, + { url = "https://files.pythonhosted.org/packages/cc/f0/fd36cc4a81bf52ee5633275daae2b93dd958aace67fd4f5d466ec83b5f35/xxhash-3.7.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:fe14c356f8b23ad811dc026077a6d4abccdaa7bce5ca98579605550657b6fcfb", size = 241994, upload-time = "2026-04-25T11:06:05.264Z" }, + { url = "https://files.pythonhosted.org/packages/08/e1/67f5d9c9369be42eaf99ba02c01bf14c5ecd67087b02567960bfcee43b63/xxhash-3.7.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f420ad3d41e38194353a498bbc9561fd5a9973a27b536ce46d8583479cf44335", size = 198707, upload-time = "2026-04-25T11:06:07.044Z" }, + { url = "https://files.pythonhosted.org/packages/50/17/a4c865ca22d2da6b1bc7d739bf88cab209533cf52ba06ca9da27c3039bee/xxhash-3.7.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:693d02c6dc7d1aa0a45921d54cd8c1ff629e09dfdc2238471507af1f7a1c6f04", size = 210917, upload-time = "2026-04-25T11:06:08.853Z" }, + { url = "https://files.pythonhosted.org/packages/49/8b/453b35810d697abac3c96bde3528bece685869227da274eb80a4a4d4a119/xxhash-3.7.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:14bf7a54e43825ec131ee7fe3c60e142e7c2c1e676ad0f93fc893432d15414af", size = 275772, upload-time = "2026-04-25T11:06:10.645Z" }, + { url = "https://files.pythonhosted.org/packages/b5/ad/4eed7eab07fd3ee6678f416190f0413d097ab5d7c1278906bf1e9549d789/xxhash-3.7.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:ae3a39a4d96bdb6f8d154fd7f490c4ad06f0532fcd2bb656052a9a7762cf5d31", size = 414068, upload-time = "2026-04-25T11:06:12.511Z" }, + { url = "https://files.pythonhosted.org/packages/d3/4e/fd6f8a680ba248fdb83054fa71a8bfa3891225200de1708b888ef2c49829/xxhash-3.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1cc07c639e3a77ef1d32987464d3e408565b8a3be57b545d3542b191054d9923", size = 191459, upload-time = "2026-04-25T11:06:14.07Z" }, + { url = "https://files.pythonhosted.org/packages/50/7c/8cb34b3bed4f44ca6827a534d50833f9bc6c006e83b0eb410ac9fa0793bd/xxhash-3.7.0-cp311-cp311-win32.whl", hash = "sha256:3281ba1d1e60ee7a382a7b958513ba03c2c0d5fcbd9a6f7517c0a81251a23422", size = 30628, upload-time = "2026-04-25T11:06:15.802Z" }, + { url = "https://files.pythonhosted.org/packages/0b/47/a49767bd7b40782bedae9ff0721bfe1d7e4dd9dc1585dea684e57ba67c20/xxhash-3.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:a7f25baec4c5d851d40718d6fae52285b31683093d4ff5207e63ab306ccf14a5", size = 31461, upload-time = "2026-04-25T11:06:17.104Z" }, + { url = "https://files.pythonhosted.org/packages/7c/c6/3957bfacfb706bd687be246dfa8dd60f8df97c44186d229f7fd6e26c4b7e/xxhash-3.7.0-cp311-cp311-win_arm64.whl", hash = "sha256:4c2454448ce847c72635827bb75c15c5a3434b03ee1afd28cb6dc6fb2597d830", size = 27746, upload-time = "2026-04-25T11:06:18.716Z" }, + { url = "https://files.pythonhosted.org/packages/f2/8a/51a14cdef4728c6c2337db8a7d8704422cc65676d9199d77215464c880af/xxhash-3.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:082c87bfdd2b9f457606c7a4a53457f4c4b48b0cdc48de0277f4349d79bb3d7a", size = 33357, upload-time = "2026-04-25T11:06:20.44Z" }, + { url = "https://files.pythonhosted.org/packages/b9/1b/0c2c933809421ffd9bf42b59315552c143c755db5d9a816b2f1ae273e884/xxhash-3.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5e7ce913b61f35b0c1c839a49ac9c8e75dd8d860150688aed353b0ce1bf409d8", size = 30869, upload-time = "2026-04-25T11:06:21.989Z" }, + { url = "https://files.pythonhosted.org/packages/03/a8/89d5fdd6ee12d70ba99451de46dd0e8010167468dcd913ec855653f4dd50/xxhash-3.7.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3beb1de3b1e9694fcdd853e570ee64c631c7062435d2f8c69c1adf809bc086f0", size = 194100, upload-time = "2026-04-25T11:06:23.586Z" }, + { url = "https://files.pythonhosted.org/packages/87/ee/2f9f2ed993e77206d1e66991290a1ebe22e843351ca3ebec8e49e01ba186/xxhash-3.7.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3e7b689c3bce16699efcf736066f5c6cc4472c3840fe4b22bd8279daf4abdac", size = 212977, upload-time = "2026-04-25T11:06:25.019Z" }, + { url = "https://files.pythonhosted.org/packages/de/60/5a91644615a9e9d4e42c2e9925f1908e3a24e4e691d9de7340d565bea024/xxhash-3.7.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a6545e6b409e3d5cbafc850fb84c55a1ca26ed15a6b11e3bf07a0e0cd84517c8", size = 236373, upload-time = "2026-04-25T11:06:26.482Z" }, + { url = "https://files.pythonhosted.org/packages/22/c0/f3a9384eaaed9d14d4d062a5d953aa0da489bfe9747877aa994caa87cd0b/xxhash-3.7.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:31ab1461c77a11461d703c88eb949e132a1c6515933cf675d97ec680f4bd18de", size = 212229, upload-time = "2026-04-25T11:06:28.065Z" }, + { url = "https://files.pythonhosted.org/packages/2e/67/02f07a9fd79726804190f2172c4894c3ed9a4ebccaca05653c84beb58025/xxhash-3.7.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7c4d596b7676f811172687ec567cbafb9e4dea2f9be1bbb4f622410cb7f40f40", size = 445462, upload-time = "2026-04-25T11:06:30.048Z" }, + { url = "https://files.pythonhosted.org/packages/40/37/558f5a90c0672fc9b4402dc25d87ac5b7406616e8969430c9ca4e52ee74d/xxhash-3.7.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13805f0461cba0a857924e70ff91ae6d52d2598f79a884e788db80532614a4a1", size = 193932, upload-time = "2026-04-25T11:06:31.857Z" }, + { url = "https://files.pythonhosted.org/packages/d5/90/aaa09cd58661d32044dbbad7df55bbe22a623032b810e7ed3b8c569a2a6f/xxhash-3.7.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1d398f372496152f1c6933a33566373f8d1b37b98b8c9d608fa6edc0976f23b2", size = 284807, upload-time = "2026-04-25T11:06:33.697Z" }, + { url = "https://files.pythonhosted.org/packages/d6/f3/53df3719ab127a02c174f0c1c74924fcd110866e89c966bc7909cfa8fa84/xxhash-3.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d610aa62cdb7d4d497740741772a24a794903bf3e79eaa51d2e800082abe11e5", size = 210445, upload-time = "2026-04-25T11:06:35.488Z" }, + { url = "https://files.pythonhosted.org/packages/72/33/d219975c0e8b6fa2eb9ccd486fe47e21bf1847985b878dd2fbc3126e0d5c/xxhash-3.7.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:073c23900a9fbf3d26616c17c830db28af9803677cd5b33aea3224d824111514", size = 241273, upload-time = "2026-04-25T11:06:37.24Z" }, + { url = "https://files.pythonhosted.org/packages/3e/50/49b1afe610eb3964cedcb90a4d4c3d46a261ee8669cbd4f060652619ae3c/xxhash-3.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:418a463c3e6a590c0cdc890f8be19adb44a8c8acd175ca5b2a6de77e61d0b386", size = 197950, upload-time = "2026-04-25T11:06:39.148Z" }, + { url = "https://files.pythonhosted.org/packages/c6/75/5f42a1a4c78717d906a4b6a140c6dbf837ab1f547a54d23c4e2903310936/xxhash-3.7.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:03f8ff4474ee61c845758ce00711d7087a770d77efb36f7e74a6e867301000b8", size = 210709, upload-time = "2026-04-25T11:06:40.958Z" }, + { url = "https://files.pythonhosted.org/packages/8a/85/237e446c25abced71e9c53d269f2cef5bab8a82b3f88a12e00c5368e7368/xxhash-3.7.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:44fba4a5f1d179b7ddc7b3dc40f56f9209046421679b57025d4d8821b376fd8d", size = 275345, upload-time = "2026-04-25T11:06:42.525Z" }, + { url = "https://files.pythonhosted.org/packages/62/34/c2c26c0a6a9cc739bc2a5f0ae03ba8b87deb12b8bce35f7ac495e790dc6d/xxhash-3.7.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31e3516a0f829d06ded4a2c0f3c7c5561993256bfa1c493975fb9dc7bfa828a1", size = 414056, upload-time = "2026-04-25T11:06:44.343Z" }, + { url = "https://files.pythonhosted.org/packages/a0/aa/5c58e9bc8071b8afd8dcf297ff362f723c4892168faba149f19904132bf4/xxhash-3.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b59ee2ac81de57771a09ecad09191e840a1d2fae1ef684208320591055768f83", size = 191485, upload-time = "2026-04-25T11:06:46.262Z" }, + { url = "https://files.pythonhosted.org/packages/d4/69/a929cf9d1e2e65a48b818cdce72cb6b69eab2e6877f21436d0a1942aff43/xxhash-3.7.0-cp312-cp312-win32.whl", hash = "sha256:74bbd92f8c7fcc397ba0a11bfdc106bc72ad7f11e3a60277753f87e7532b4d81", size = 30671, upload-time = "2026-04-25T11:06:48.039Z" }, + { url = "https://files.pythonhosted.org/packages/b9/1b/104b41a8947f4e1d4a66ce1e628eea752f37d1890bfd7453559ca7a3d950/xxhash-3.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:7bd7bc82dd4f185f28f35193c2e968ef46131628e3cac62f639dadf321cba4d1", size = 31514, upload-time = "2026-04-25T11:06:49.279Z" }, + { url = "https://files.pythonhosted.org/packages/98/a0/1fd0ea1f1b886d9e7c73f0397571e22333a7d79e31da6d7127c2a4a71d75/xxhash-3.7.0-cp312-cp312-win_arm64.whl", hash = "sha256:7d7148180ec99ba36585b42c8c5de25e9b40191613bc4be68909b4d25a77a852", size = 27761, upload-time = "2026-04-25T11:06:50.448Z" }, + { url = "https://files.pythonhosted.org/packages/c1/ca/d5174b4c36d10f64d4ca7050563138c5a599efb01a765858ddefc9c1202a/xxhash-3.7.0-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:4b6d6b33f141158692bd4eafbb96edbc5aa0dabdb593a962db01a91983d4f8fa", size = 36813, upload-time = "2026-04-25T11:06:51.73Z" }, + { url = "https://files.pythonhosted.org/packages/41/d0/abc6c9d347ba1f1e1e1d98125d0881a0452c7f9a76a9dd03a7b5d2197f23/xxhash-3.7.0-cp313-cp313-android_21_x86_64.whl", hash = "sha256:845d347df254d6c619f616afa921331bada8614b8d373d58725c663ba97c3605", size = 35121, upload-time = "2026-04-25T11:06:53.048Z" }, + { url = "https://files.pythonhosted.org/packages/bf/11/4cc834eb3d79f2f2b3a6ef7324195208bcdfbdcf7534d2b17267aa5f3a8f/xxhash-3.7.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:fddbbb69a6fff4f421e7a0d1fa28f894b20112e9e3fab306af451e2dfd0e459b", size = 29624, upload-time = "2026-04-25T11:06:54.311Z" }, + { url = "https://files.pythonhosted.org/packages/23/83/e97d3e7b635fe73a1dfb1e91f805324dd6d930bb42041cbf18f183bc0b6d/xxhash-3.7.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:54876a4e45101cec2bf8f31a973cda073a23e2e108538dad224ba07f85f22487", size = 30638, upload-time = "2026-04-25T11:06:55.864Z" }, + { url = "https://files.pythonhosted.org/packages/f4/40/d84951d80c35db1f4c40a29a64a8520eea5d56e764c603906b4fe763580f/xxhash-3.7.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:0c72fe9c7e3d6dfd7f1e21e224a877917fa09c465694ba4e06464b9511b65544", size = 33323, upload-time = "2026-04-25T11:06:57.336Z" }, + { url = "https://files.pythonhosted.org/packages/89/cc/c7dc6558d97e9ab023f663d69ab28b340ed9bf4d2d94f2c259cf896bb354/xxhash-3.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a6d73a830b17ef49bc04e00182bd839164c1b3c59c127cd7c54fcb10c7ed8ee8", size = 33362, upload-time = "2026-04-25T11:06:58.656Z" }, + { url = "https://files.pythonhosted.org/packages/2a/6e/46b84017b1301d54091430353d4ad5901654a3e0871649877a416f7f1644/xxhash-3.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:91c3b07cf3362086d8f126c6aecd8e5e9396ad8b2f2219ea7e49a8250c318acd", size = 30874, upload-time = "2026-04-25T11:06:59.834Z" }, + { url = "https://files.pythonhosted.org/packages/df/5e/8f9158e3ab906ad3fec51e09b5ea0093e769f12207bfa42a368ca204e7ab/xxhash-3.7.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:50e879ebbac351c81565ca108db766d7832f5b8b6a5b14b8c0151f7190028e3d", size = 194185, upload-time = "2026-04-25T11:07:01.658Z" }, + { url = "https://files.pythonhosted.org/packages/f3/29/a804ded9f5d3d3758292678d23e7528b08fda7b7e750688d08b052322475/xxhash-3.7.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:921c14e93817842dd0dd9f372890a0f0c72e534650b6ab13c5be5cd0db11d47e", size = 213033, upload-time = "2026-04-25T11:07:03.606Z" }, + { url = "https://files.pythonhosted.org/packages/8b/91/1ce5a7d2fdc975267320e2c78fc1cecfe7ab735ccbcf6993ec5dd541cb2c/xxhash-3.7.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e64a7c9d7dfca3e0fafcbc5e455519090706a3e36e95d655cec3e04e79f95aaa", size = 236140, upload-time = "2026-04-25T11:07:05.396Z" }, + { url = "https://files.pythonhosted.org/packages/34/04/fd595a4fd8617b05fa27bd9b684ecb4985bfed27917848eea85d54036d06/xxhash-3.7.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2220af08163baf5fa36c2b8af079dc2cbe6e66ae061385267f9472362dfd53c6", size = 212291, upload-time = "2026-04-25T11:07:06.966Z" }, + { url = "https://files.pythonhosted.org/packages/03/fb/f1a379cbc372ae5b9f4ab36154c48a849ca6ebe3ac477067a57865bf3bc6/xxhash-3.7.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f14bb8b22a4a91325813e3d553b8963c10cf8c756cff65ee50c194431296c655", size = 445532, upload-time = "2026-04-25T11:07:08.525Z" }, + { url = "https://files.pythonhosted.org/packages/65/59/172424b79f8cfd4b6d8a122b2193e6b8ad4b11f7159bb3b6f9b3191329bb/xxhash-3.7.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:496736f86a9bedaf64b0dc70e3539d0766df01c71ea22032698e88f3f04a1ce9", size = 193990, upload-time = "2026-04-25T11:07:10.315Z" }, + { url = "https://files.pythonhosted.org/packages/b9/19/aeac22161d953f139f07ba5586cb4a17c5b7b6dff985122803bb12933500/xxhash-3.7.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0ff71596bd79816975b3de7130ab1ff4541410285a3c084584eeb1c8239996fd", size = 284876, upload-time = "2026-04-25T11:07:12.15Z" }, + { url = "https://files.pythonhosted.org/packages/77/d5/4fd0b59e7a02242953da05ff679fbb961b0a4368eac97a217e11dae110c1/xxhash-3.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1ad86695c19b1d46fe106925db3c7a37f16be37669dcf58dcc70a9dd6e324676", size = 210495, upload-time = "2026-04-25T11:07:13.952Z" }, + { url = "https://files.pythonhosted.org/packages/aa/fb/976a3165c728c7faf74aa1b5ab3cf6a85e6d731612894741840524c7d28c/xxhash-3.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:970f9f8c50961d639cbd0d988c96f80ddf66006de93641719282c4fe7a87c5e6", size = 241331, upload-time = "2026-04-25T11:07:15.557Z" }, + { url = "https://files.pythonhosted.org/packages/4a/2c/6763d5901d53ac9e6ba296e5717ae599025c9d268396e8faa8b4b0a8e0ac/xxhash-3.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5886ad85e9e347911783760a1d16cb6b393e8f9e3b52c982568226cb56927bdc", size = 198037, upload-time = "2026-04-25T11:07:17.563Z" }, + { url = "https://files.pythonhosted.org/packages/61/2b/876e722d533833f5f9a83473e6ba993e48745701096944e77bbecf29b2c3/xxhash-3.7.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:6e934bbae1e0ec74e27d5f0d7f37ef547ce5ff9f0a7e63fb39e559fc99526734", size = 210744, upload-time = "2026-04-25T11:07:19.055Z" }, + { url = "https://files.pythonhosted.org/packages/21/e6/d7e7baef7ce24166b4668d3c48557bb35a23b92ecadcac7e7718d099ab69/xxhash-3.7.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:3b6b3d28228af044ebcded71c4a3dd86e1dbd7e2f4645bf40f7b5da65bb5fb5a", size = 275406, upload-time = "2026-04-25T11:07:20.908Z" }, + { url = "https://files.pythonhosted.org/packages/92/fe/198b3763b2e01ca908f2154969a2352ec99bda892b574a11a9a151c5ede4/xxhash-3.7.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:6be4d70d9ab76c9f324ead9c01af6ff52c324745ea0c3731682a0cf99720f1fe", size = 414125, upload-time = "2026-04-25T11:07:23.037Z" }, + { url = "https://files.pythonhosted.org/packages/3a/6d/019a11affd5a5499137cacca53808659964785439855b5aa40dfd3412916/xxhash-3.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:151d7520838d4465461a0b7f4ae488b3b00de16183dd3214c1a6b14bf89d7fb6", size = 191555, upload-time = "2026-04-25T11:07:24.991Z" }, + { url = "https://files.pythonhosted.org/packages/76/21/b96d58568df2d01533244c3e0e5cbdd0c8b2b25c4bec4d72f19259a292d7/xxhash-3.7.0-cp313-cp313-win32.whl", hash = "sha256:d798c1e291bffb8e37b5bbe0dda77fc767cd19e89cadaf66e6ed5d0ff88c9fe6", size = 30668, upload-time = "2026-04-25T11:07:26.665Z" }, + { url = "https://files.pythonhosted.org/packages/99/57/d849a8d3afa1f8f4bc6a831cd89f49f9706fbbad94d2975d6140a171988c/xxhash-3.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:875811ba23c543b1a1c3143c926e43996eb27ebb8f52d3500744aa608c275aed", size = 31524, upload-time = "2026-04-25T11:07:27.92Z" }, + { url = "https://files.pythonhosted.org/packages/81/52/bacc753e92dee78b058af8dcef0a50815f5f860986c664a92d75f965b6a5/xxhash-3.7.0-cp313-cp313-win_arm64.whl", hash = "sha256:54a675cb300dda83d71daae2a599389d22db8021a0f8db0dd659e14626eb3ecc", size = 27768, upload-time = "2026-04-25T11:07:29.113Z" }, + { url = "https://files.pythonhosted.org/packages/1c/47/ddbd683b7fc7e592c1a8d9d65f73ce9ab513f082b3967eee2baf549b8fc6/xxhash-3.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a3b19a42111c4057c1547a4a1396a53961dca576a0f6b82bfa88a2d1561764b2", size = 33576, upload-time = "2026-04-25T11:07:30.469Z" }, + { url = "https://files.pythonhosted.org/packages/07/f2/36d3310161db7f72efb4562aadde0ed429f1d0531782dd6345b12d2da527/xxhash-3.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8f4608a06e4d61b7a3425665a46d00e0579122e1a2fae97a0c52953a3aad9aa3", size = 31123, upload-time = "2026-04-25T11:07:31.989Z" }, + { url = "https://files.pythonhosted.org/packages/0d/3f/75937a5c69556ed213021e43cbedd84c8e0279d0d74e7d41a255d84ba4b1/xxhash-3.7.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ad37c7792479e49cf96c1ab25517d7003fe0d93687a772ba19a097d235bbe41e", size = 196491, upload-time = "2026-04-25T11:07:33.358Z" }, + { url = "https://files.pythonhosted.org/packages/22/29/f10d7ff8c7a733d4403a43b9de18c8fabc005f98cec054644f04418659ee/xxhash-3.7.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc026e3b89d98e30a8288c95cb696e77d150b3f0fb7a51f73dcd49ee6b5577fa", size = 215793, upload-time = "2026-04-25T11:07:34.919Z" }, + { url = "https://files.pythonhosted.org/packages/8b/fd/778f60aa295f58907938f030a8b514611f391405614a525cccd2ffc00eb5/xxhash-3.7.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c9b31ab1f28b078a6a1ac1a54eb35e7d5390deddd56870d0be3a0a733d1c321c", size = 237993, upload-time = "2026-04-25T11:07:36.638Z" }, + { url = "https://files.pythonhosted.org/packages/70/f5/736db5de387b4a540e37a05b84b40dc58a1ce974bfd2b4e5754ce29b68c3/xxhash-3.7.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3bb5fd680c038fd5229e44e9c493782f90df9bef632fd0499d442374688ff70b", size = 214887, upload-time = "2026-04-25T11:07:38.564Z" }, + { url = "https://files.pythonhosted.org/packages/4d/aa/09a095f22fdb9a27fbb716841fbff52119721f9ca4261952d07a912f7839/xxhash-3.7.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:030c0fd688fce3569fbb49a2feefd4110cbb0b650186fb4610759ecfac677548", size = 448407, upload-time = "2026-04-25T11:07:40.552Z" }, + { url = "https://files.pythonhosted.org/packages/74/8a/b745efeeca9e34a91c26fdc97ad8514c43d5a81ac78565cba80a1353870a/xxhash-3.7.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5b1bde10324f4c31812ae0d0502e92d916ae8917cad7209353f122b8b8f610c3", size = 196119, upload-time = "2026-04-25T11:07:42.101Z" }, + { url = "https://files.pythonhosted.org/packages/8a/5c/0cfceb024af90c191f665c7933b1f318ee234f4797858383bebd1881d52f/xxhash-3.7.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:503722d52a615f2604f5e7611de7d43878df010dc0053094ef91cb9a9ac3d987", size = 286751, upload-time = "2026-04-25T11:07:43.568Z" }, + { url = "https://files.pythonhosted.org/packages/0b/0a/0793e405dc3cf8f4ebe2c1acec1e4e4608cd9e7e50ea691dabbc2a95ccbb/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c72500a3b6d6c30ebfc135035bcace9eb5884f2dc220804efcaaba43e9f611dd", size = 212961, upload-time = "2026-04-25T11:07:45.388Z" }, + { url = "https://files.pythonhosted.org/packages/0c/7e/721118ffc63bfff94aa565bcf2555a820f9f4bdb0f001e0d609bdfad70de/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:43475925a766d01ca8cd9a857fd87f3d50406983c8506a4c07c4df12adcc867f", size = 243703, upload-time = "2026-04-25T11:07:47.053Z" }, + { url = "https://files.pythonhosted.org/packages/6e/18/16f6267160488b8276fd3d449d425712512add292ba545c1b6946bfdb7dd/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8d09dfd2ab135b985daf868b594315ebe11ad86cd9fea46e6c69f19b28f7d25a", size = 200894, upload-time = "2026-04-25T11:07:48.657Z" }, + { url = "https://files.pythonhosted.org/packages/2d/94/80ba841287fd97e3e9cac1d228788c8ef623746f570404961eec748ecb5c/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c50269d0055ac1faecfd559886d2cbe4b730de236585aba0e873f9d9dadbe585", size = 213357, upload-time = "2026-04-25T11:07:50.257Z" }, + { url = "https://files.pythonhosted.org/packages/a1/7e/106d4067130c59f1e18a55ffadcd876d8c68534883a1e02685b29d3d8153/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:1910df4756a5ab58cfad8744fc2d0f23926e3efcc346ee76e87b974abab922f4", size = 277600, upload-time = "2026-04-25T11:07:51.745Z" }, + { url = "https://files.pythonhosted.org/packages/c5/86/a081dd30da71d720b2612a792bfd55e45fa9a07ac76a0507f60487473c25/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:d006faf3b491957efcb433489be3c149efe4787b7063d5cddb8ddaefdc60e0c1", size = 416980, upload-time = "2026-04-25T11:07:53.504Z" }, + { url = "https://files.pythonhosted.org/packages/35/29/1a95221a029a3c1293773869e1ab47b07cbbdd82444a42809e8c60156626/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:abb65b4e947e958f7b3b0d71db3ce447d1bc5f37f5eab871ce7223bda8768a04", size = 193840, upload-time = "2026-04-25T11:07:55.103Z" }, + { url = "https://files.pythonhosted.org/packages/c5/e0/db909dd0823285de2286f67e10ee4d81e96ad35d7d8e964ecb07fccd8af9/xxhash-3.7.0-cp313-cp313t-win32.whl", hash = "sha256:178959906cb1716a1ce08e0d69c82886c70a15a6f2790fc084fdd146ca30cd49", size = 30966, upload-time = "2026-04-25T11:07:56.524Z" }, + { url = "https://files.pythonhosted.org/packages/7b/ff/d705b15b22f21ee106adce239cb65d35067a158c630b240270f09b17c2e6/xxhash-3.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2524a1e20d4c231d13b50f7cf39e44265b055669a64a7a4b9a2a44faa03f19b6", size = 31784, upload-time = "2026-04-25T11:07:57.758Z" }, + { url = "https://files.pythonhosted.org/packages/a2/1f/b2cf83c3638fd0588e0b17f22e5a9400bdfb1a3e3755324ac0aee2250b88/xxhash-3.7.0-cp313-cp313t-win_arm64.whl", hash = "sha256:37d994d0ffe81ef087bb330d392caa809bb5853c77e22ea3f71db024a0543dba", size = 27932, upload-time = "2026-04-25T11:07:59.109Z" }, + { url = "https://files.pythonhosted.org/packages/0e/cc/431db584f6fbb9312e40a173af027644e5580d39df1f73603cbb9dca4d6b/xxhash-3.7.0-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:8c5fcfd806c335bfa2adf1cd0b3110a44fc7b6995c3a648c27489bae85801465", size = 36644, upload-time = "2026-04-25T11:08:00.658Z" }, + { url = "https://files.pythonhosted.org/packages/bc/01/255ec513e0a705d1f9a61413e78dfce4e3235203f0ed525a24c2b4b56345/xxhash-3.7.0-cp314-cp314-android_24_x86_64.whl", hash = "sha256:506a0b488f190f0a06769575e30caf71615c898ed93ab18b0dbcb6dec5c3713c", size = 35003, upload-time = "2026-04-25T11:08:02.338Z" }, + { url = "https://files.pythonhosted.org/packages/68/70/c55fc33c93445b44d8fc5a17b41ed99e3cebe92bcf8396809e63fc9a1165/xxhash-3.7.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:ec68dbba21532c0173a9872298e65c89749f7c9d21538c3a78b5bb6105871568", size = 29655, upload-time = "2026-04-25T11:08:03.701Z" }, + { url = "https://files.pythonhosted.org/packages/c2/72/ff8de73df000d74467d12a59ce6d6e2b2a368b978d41ab7b1fba5ed442be/xxhash-3.7.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:fa77e7ec1450d415d20129961814787c9abd9a07f98872f070b1fe96c5084611", size = 30664, upload-time = "2026-04-25T11:08:05.011Z" }, + { url = "https://files.pythonhosted.org/packages/b6/91/08416d9bd9bc3bf39d831abe8a5631ac2db5141dfd6fe81c3fe59a1f9264/xxhash-3.7.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:fe32736295ea38e43e7d9424053c8c47c9f64fecfc7c895fb3da9b30b131c9ee", size = 33317, upload-time = "2026-04-25T11:08:06.413Z" }, + { url = "https://files.pythonhosted.org/packages/0e/3b/86b1caa4dee10a99f4bf9521e623359341c5e50d05158fa10c275b2bd079/xxhash-3.7.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:ab9dd2c83c4bbd63e422181a76f13502d049d3ddcac9a1bdc29196263d692bb8", size = 33457, upload-time = "2026-04-25T11:08:08.099Z" }, + { url = "https://files.pythonhosted.org/packages/ed/38/98ea14ad1517e1461292a65906951458d520689782bfbae111050145bdba/xxhash-3.7.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3afec3a336a2286601a437cb07562ab0227685e6fbb9ec17e8c18457ff348ecf", size = 30894, upload-time = "2026-04-25T11:08:09.429Z" }, + { url = "https://files.pythonhosted.org/packages/61/a2/074654d0b893606541199993c7db70067d9fc63b748e0d60020a52a1bd36/xxhash-3.7.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:565df64437a9390f84465dcca33e7377114c7ede8d05cd2cf20081f831ea788e", size = 194409, upload-time = "2026-04-25T11:08:10.91Z" }, + { url = "https://files.pythonhosted.org/packages/e2/26/6d2a1afc468189f77ca28c32e1c83e1b9da1178231e05641dbc1b350e332/xxhash-3.7.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:12eca820a5d558633d423bf8bb78ce72a55394823f64089247f788a7e0ae691e", size = 213135, upload-time = "2026-04-25T11:08:12.575Z" }, + { url = "https://files.pythonhosted.org/packages/8e/0e/d8aecf95e09c42547453137be74d2f7b8b14e08f5177fa2fab6144a19061/xxhash-3.7.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f262b8f7599516567e070abf607b9af649052b2c4bd6f9be02b0cb41b7024805", size = 236379, upload-time = "2026-04-25T11:08:14.206Z" }, + { url = "https://files.pythonhosted.org/packages/f2/74/8140e8210536b3dd0cc816c4faaeb5ba6e63e8125ab25af4bcddd6a037b3/xxhash-3.7.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1598916cb197681e03e601901e4ab96a9a963de398c59d0964f8a6f44a2b361", size = 212447, upload-time = "2026-04-25T11:08:15.79Z" }, + { url = "https://files.pythonhosted.org/packages/a0/d2/462001d2903b4bee5a5689598a0a55e5e7cd1ac7f4247a5545cff10d3ebb/xxhash-3.7.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:322b2f0622230f526aeb1738149948a7ae357a9e2ceb1383c6fd1fdaecdafa16", size = 445660, upload-time = "2026-04-25T11:08:17.441Z" }, + { url = "https://files.pythonhosted.org/packages/23/09/2bd1ed7f8689b20e51727952cac8329d50c694dc32b2eba06ba5bc742b37/xxhash-3.7.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24cc22070880cc57b830a65cde4e65fa884c6d9b28ae4803b5ee05911e7bafba", size = 194076, upload-time = "2026-04-25T11:08:19.134Z" }, + { url = "https://files.pythonhosted.org/packages/c9/6e/692302cd0a5f4ac4e6289f37fa888dc2e1e07750b68fe3e4bfe939b8cea3/xxhash-3.7.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb5a888a968b2434abf9ecda357b5d43f10d7b5a6da6fdbbe036208473aff0e2", size = 284990, upload-time = "2026-04-25T11:08:20.618Z" }, + { url = "https://files.pythonhosted.org/packages/05/d9/e54b159b3d9df7999d2a7c676ce7b323d1b5588a64f8f51ed8172567bd87/xxhash-3.7.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a999771ff97bec27d18341be4f3a36b163bb1ac41ec17bef6d2dabd84acd33c7", size = 210590, upload-time = "2026-04-25T11:08:22.24Z" }, + { url = "https://files.pythonhosted.org/packages/50/93/0e0df1a3a196ced4ca71de76d65ead25d8e87bbfb87b64306ea47a40c00d/xxhash-3.7.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:ed4a6efe2dee1655adb73e7ad40c6aa955a6892422b1e3b95de6a34de56e3cbb", size = 241442, upload-time = "2026-04-25T11:08:23.844Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a9/d917a7a814e90b218f8a0d37967105eea91bf752c3303683c99a1f7bfc1f/xxhash-3.7.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:9fd17f14ac0faa12126c2f9ca774a8cf342957265ec3c8669c144e5e6cdb478c", size = 198356, upload-time = "2026-04-25T11:08:25.99Z" }, + { url = "https://files.pythonhosted.org/packages/89/5e/f2ba1877c39469abbefc72991d6ebdcbd4c0880db01ae8cb1f553b0c537d/xxhash-3.7.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:05fd1254268c59b5cb2a029dfc204275e9fc52de2913f1e53aa8d01442c96b4d", size = 210898, upload-time = "2026-04-25T11:08:27.608Z" }, + { url = "https://files.pythonhosted.org/packages/90/c6/be56b58e73de531f39a10de1355bb77ceb663900dc4bf2d6d3002a9c3f9e/xxhash-3.7.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:a2eae53197c6276d5b317f75a1be226bbf440c20b58bf525f36b5d0e1f657ca6", size = 275519, upload-time = "2026-04-25T11:08:29.301Z" }, + { url = "https://files.pythonhosted.org/packages/92/e2/17ddc85d5765b9c709f192009ed8f5a1fc876f4eb35bba7c307b5b1169f9/xxhash-3.7.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:bfe6f92e3522dcbe8c4281efd74fa7542a336cb00b0e3272c4ec0edabeaeaf67", size = 414191, upload-time = "2026-04-25T11:08:31.16Z" }, + { url = "https://files.pythonhosted.org/packages/9c/42/85f5b79f4bf1ec7ba052491164adfd4f4e9519f5dc7246de4fbd64a1bd56/xxhash-3.7.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7ab9a49c410d8c6c786ab99e79c529938d894c01433130353dd0fe999111077a", size = 191604, upload-time = "2026-04-25T11:08:32.862Z" }, + { url = "https://files.pythonhosted.org/packages/b8/d0/6127b623aa4cca18d8b7743592b048d689fd6c6e37ff26a22cddf6cd9d7f/xxhash-3.7.0-cp314-cp314-win32.whl", hash = "sha256:040ea63668f9185b92bc74942df09c7e65703deed71431333678fc6e739a9955", size = 31271, upload-time = "2026-04-25T11:08:34.651Z" }, + { url = "https://files.pythonhosted.org/packages/64/4f/44fc4788568004c43921701cbc127f48218a1eede2c9aea231115323564d/xxhash-3.7.0-cp314-cp314-win_amd64.whl", hash = "sha256:2a61e2a3fb23c892496d587b470dee7fa1b58b248a187719c65ea8e94ec13257", size = 32284, upload-time = "2026-04-25T11:08:35.987Z" }, + { url = "https://files.pythonhosted.org/packages/6d/77/18bb895eb60a49453d16e17d67990e5caff557c78eafc90ad4e2eabf4570/xxhash-3.7.0-cp314-cp314-win_arm64.whl", hash = "sha256:c7741c7524961d8c0cb4d4c21b28957ff731a3fd5b5cd8b856dc80a40e9e5acc", size = 28701, upload-time = "2026-04-25T11:08:37.767Z" }, + { url = "https://files.pythonhosted.org/packages/45/a0/46f72244570c550fbbb7db1ef554183dd5ebe9136385f30e032b781ae8f6/xxhash-3.7.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:fc84bf7aa7592f31ec63a3e7b11d624f468a3f19f5238cec7282a42e838ab1d7", size = 33646, upload-time = "2026-04-25T11:08:39.109Z" }, + { url = "https://files.pythonhosted.org/packages/4a/3a/453846a7eceea11e75def361eed01ec6a0205b9822c19927ed364ccae7cc/xxhash-3.7.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9f1563fdc8abfc389748e6932c7e4e99c89a53e4ec37d4563c24fc06f5e5644b", size = 31125, upload-time = "2026-04-25T11:08:40.467Z" }, + { url = "https://files.pythonhosted.org/packages/bd/3e/49434aba738885d512f9e486db1bdd19db28dfa40372b56da26ef7a4e738/xxhash-3.7.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:2d415f18becf6f153046ab6adc97da77e3643a0ee205dae61c4012604113a020", size = 196633, upload-time = "2026-04-25T11:08:41.943Z" }, + { url = "https://files.pythonhosted.org/packages/a4/e9/006cb6127baeb9f8abe6d15e62faa01349f09b34e2bfd65175b2422d026b/xxhash-3.7.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bb16aa13ed175bc9be5c2491ba031b85a9b51c4ed90e0b3d4ebe63cf3fb54f8e", size = 215899, upload-time = "2026-04-25T11:08:43.645Z" }, + { url = "https://files.pythonhosted.org/packages/27/e4/cc57d72e66df0ae29b914335f1c6dcf61e8f3746ddf0ae3c471aa4f15e00/xxhash-3.7.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f9fd595f1e5941b3d7863e4774e4b30caa6731fc34b9277da032295aa5656ee5", size = 238116, upload-time = "2026-04-25T11:08:45.698Z" }, + { url = "https://files.pythonhosted.org/packages/af/78/3531d4a3fd8a0038cc6be1f265a69c1b3587f557a10b677dd736de2202c1/xxhash-3.7.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1295325c5a98d552333fa53dc2b026b0ef0ec9c8e73ca3a952990b4c7d65d459", size = 215012, upload-time = "2026-04-25T11:08:47.355Z" }, + { url = "https://files.pythonhosted.org/packages/b4/f6/259fb1eaaec921f59b17203b0daee69829761226d3b980d5191d7723dd83/xxhash-3.7.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3573a651d146912da9daa9e29e5fbc45994420daaa9ef1e2fa5823e1dc485513", size = 448534, upload-time = "2026-04-25T11:08:49.149Z" }, + { url = "https://files.pythonhosted.org/packages/7b/16/a66d0eaf6a7e68532c07714361ddc904c663ec940f3b028c1ae4a21a7b9d/xxhash-3.7.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5ec1e080a3d02d94ea9335bfab0e3374b877e25411422c18f51a943fa4b46381", size = 196217, upload-time = "2026-04-25T11:08:50.805Z" }, + { url = "https://files.pythonhosted.org/packages/8d/ef/d2efc7fc51756dc52509109d1a25cefc859d74bc4b19a167b12dbd8c2786/xxhash-3.7.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:84415265192072d8638a3afc3c1bc5995e310570cd9acb54dc46d3939e364fe0", size = 286906, upload-time = "2026-04-25T11:08:52.418Z" }, + { url = "https://files.pythonhosted.org/packages/fc/67/25decd1d4a4018582ec4db2a868a2b7e40640f4adb20dfeb19ac923aa825/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d4dea659b57443989ef32f4295104fd6912c73d0bf26d1d148bb88a9f159b02", size = 213057, upload-time = "2026-04-25T11:08:54.105Z" }, + { url = "https://files.pythonhosted.org/packages/0d/5d/17651eb29d06786cdc40c60ae3d27d645aa5d61d2eca6237a7ba0b94789b/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:05ece0fe4d9c9c2728912d1981ae1566cfc83a011571b24732cbf76e1fb70dca", size = 243886, upload-time = "2026-04-25T11:08:56.109Z" }, + { url = "https://files.pythonhosted.org/packages/8a/d4/174d9cf7502243d586e6a9ae842b1ae23026620995114f85f1380e588bc9/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:fd880353cf1ffaf321bc18dd663e111976dbd0d3bbd8a66d58d2b470dfa7f396", size = 201015, upload-time = "2026-04-25T11:08:57.777Z" }, + { url = "https://files.pythonhosted.org/packages/91/8c/2254e2d06c3ac5e6fe22eaf3da791b87ea823ae9f2c17b4af66755c5752d/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:4e15cc9e2817f6481160f930c62842b3ff419e20e13072bcbab12230943092bc", size = 213457, upload-time = "2026-04-25T11:08:59.826Z" }, + { url = "https://files.pythonhosted.org/packages/79/a2/e3daa762545921173e3360f3b4ff7fc63c2d27359f7230ec1a7a74e117f6/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:90b9d1a8bd37d768ffc92a1f651ec69afc532a96fa1ac2ea7abbed5d630b3237", size = 277738, upload-time = "2026-04-25T11:09:01.423Z" }, + { url = "https://files.pythonhosted.org/packages/e1/4c/e186da2c46b87f5204640e008d42730bf3c1ee9f0efb71ae1ebcdfeac681/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:157c49475b34ecea8809e51123d9769a534e139d1247942f7a4bc67710bb2533", size = 417127, upload-time = "2026-04-25T11:09:03.592Z" }, + { url = "https://files.pythonhosted.org/packages/17/28/3798e15007a3712d0da3d3fe70f8e11916569858b5cc371053bc26270832/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5a6ddec83325685e729ca119d1f5c518ec39294212ecd770e60693cdc5f7eb79", size = 193962, upload-time = "2026-04-25T11:09:06.228Z" }, + { url = "https://files.pythonhosted.org/packages/ad/95/a26baa93b5241fd7630998816a4ec47a5a0bad193b3f8fc8f3593e1a4a67/xxhash-3.7.0-cp314-cp314t-win32.whl", hash = "sha256:a04a6cab47e2166435aaf5b9e5ee41d1532cc8300efdef87f2a4d0acb7db19ed", size = 31643, upload-time = "2026-04-25T11:09:08.153Z" }, + { url = "https://files.pythonhosted.org/packages/44/36/5454f13c447e395f9b06a3e91274c59f503d31fad84e1836efe3bdb71f6a/xxhash-3.7.0-cp314-cp314t-win_amd64.whl", hash = "sha256:8653dd7c2eda020545bb2c71c7f7039b53fe7434d0fc1a0a9deb79ab3f1a4fc1", size = 32522, upload-time = "2026-04-25T11:09:09.534Z" }, + { url = "https://files.pythonhosted.org/packages/74/35/698e7e3ff38e22992ea24870a511d8762474fb6783627a2910ff22a185c2/xxhash-3.7.0-cp314-cp314t-win_arm64.whl", hash = "sha256:468f0fc114faaa4b36699f8e328bbc3bb11dc418ba94ac52c26dd736d4b6c637", size = 28807, upload-time = "2026-04-25T11:09:11.234Z" }, + { url = "https://files.pythonhosted.org/packages/54/c1/e57ac7317b1f58a92bab692da6d497e2a7ce44735b224e296347a7ecc754/xxhash-3.7.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ad3aa71e12ee634f22b39a0ff439357583706e50765f17f05550f92dbf128a23", size = 31232, upload-time = "2026-04-25T11:10:21.51Z" }, + { url = "https://files.pythonhosted.org/packages/4f/4e/075559bd712bc62e84915ea46bbee859f935d285659082c129bdbff679dd/xxhash-3.7.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:5de686e73690cdaf72b96d4fa083c230ec9020bcc2627ce6316138e2cf2fe2d1", size = 28553, upload-time = "2026-04-25T11:10:23.1Z" }, + { url = "https://files.pythonhosted.org/packages/92/ca/a9c78cb384d4b033b0c58196bd5c8509873cabe76389e195127b0302a741/xxhash-3.7.0-pp311-pypy311_pp73-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7fbec49f5341bbdea0c471f7d1e2fb41ae8925af9b6f28025c28defd8eb94274", size = 41109, upload-time = "2026-04-25T11:10:25.022Z" }, + { url = "https://files.pythonhosted.org/packages/bd/b1/dfe2629f7c77eb2fa234c72ff537cdd64939763df704e256446ed364a16d/xxhash-3.7.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48b542c347c2089f43dc5a6db31d2a6f3cdb04ee33505ec6e9f653834dbb0bde", size = 36307, upload-time = "2026-04-25T11:10:26.949Z" }, + { url = "https://files.pythonhosted.org/packages/e7/f7/5a484afce0f48dd8083208b42e4911f290a82c7b52458ef2927e4d421a45/xxhash-3.7.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a169a036bed0995e090d1493b283cc2cc8a6f5046821086b843abefff80643bc", size = 32534, upload-time = "2026-04-25T11:10:29.01Z" }, + { url = "https://files.pythonhosted.org/packages/0f/5f/4acfcd490db9780cf36c58534d828003c564cde5350220a1c783c4d10776/xxhash-3.7.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:ec101643395d7f21405b640f728f6f627e6986557027d740f2f9b220955edafe", size = 31552, upload-time = "2026-04-25T11:10:30.727Z" }, ] [[package]] From 4b7d3d3b6ef0659883736f2d9d451163d8ca4850 Mon Sep 17 00:00:00 2001 From: beckett Date: Thu, 7 May 2026 14:43:48 -0500 Subject: [PATCH 03/11] Use logger.exception in W2TG engine error paths --- src/voxkit/engines/w2tg_engine.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/voxkit/engines/w2tg_engine.py b/src/voxkit/engines/w2tg_engine.py index 51d2d24..8a5b4d2 100644 --- a/src/voxkit/engines/w2tg_engine.py +++ b/src/voxkit/engines/w2tg_engine.py @@ -21,6 +21,7 @@ - See https://huggingface.co/pkadambi/Wav2TextGrid """ +import logging from pathlib import Path from voxkit.gui.frameworks.settings_modal import ( @@ -34,6 +35,8 @@ from .base import AlignmentEngine +logger = logging.getLogger(__name__) + TrainerConfiguration: SettingsConfig = SettingsConfig( title="Wav2TextGrid Trainer Settings", dimensions=(400, 300), @@ -176,7 +179,7 @@ def align(self, dataset_id: str, model_id: str) -> None: ) except Exception as e: - print(f"Alignment failed: {e}") + logger.exception("W2TG alignment failed") alignments.update_alignment( dataset_id=dataset_id, alignment_id=alignment_meta["id"], @@ -241,7 +244,7 @@ def train_aligner( download_nltk=True, ) except Exception as e: - print(f"Training failed: {e}") + logger.exception("W2TG training failed") # Clean up model entry on failure if new_model_actual_id is not None: models.delete_model(engine_id=self.id, model_id=new_model_actual_id) From bdf4fc197090d2c977ed0b0071129ed0b864d133 Mon Sep 17 00:00:00 2001 From: beckett Date: Thu, 7 May 2026 14:44:11 -0500 Subject: [PATCH 04/11] Harden main.py for windowed PyInstaller builds on Windows --- main.py | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/main.py b/main.py index e90e82f..d2c30e8 100644 --- a/main.py +++ b/main.py @@ -1,8 +1,25 @@ import sys +import multiprocessing +import os + +# CRITICAL: Must run before any heavy imports (voxkit, torch, PyQt6, etc.). +# When PyTorch DataLoader workers spawn on Windows, each child re-launches the +# frozen exe and re-imports this module. freeze_support() short-circuits the +# child before it re-runs main() and tries to open another GUI window. +if __name__ == "__main__": + multiprocessing.freeze_support() + +# In a --windowed PyInstaller build there is no console, so sys.stdout and +# sys.stderr are None. Libraries like tqdm (used by transformers' Trainer) +# crash with AttributeError: 'NoneType' object has no attribute 'write' when +# they try to print progress. Redirect to devnull so writes silently no-op. +if sys.stdout is None: + sys.stdout = open(os.devnull, "w", encoding="utf-8") +if sys.stderr is None: + sys.stderr = open(os.devnull, "w", encoding="utf-8") + import faulthandler import logging -import os -import multiprocessing # Windows: configure console and stdout/stderr for UTF-8 before any output. # Without this, rich's legacy renderer falls back to cp1252 and chokes on @@ -29,10 +46,6 @@ logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(name)s: %(message)s") log = logging.getLogger("voxkit.main") -# CRITICAL: Must be at the top for frozen apps using multiprocessing -if __name__ == "__main__": - multiprocessing.freeze_support() - # Enable detailed crash reports faulthandler.enable() From 983ae4fd8748eb21c740ca20b43c9818952f05f8 Mon Sep 17 00:00:00 2001 From: beckett Date: Thu, 7 May 2026 14:47:39 -0500 Subject: [PATCH 05/11] Add Inno Setup script for Windows installer --- .gitignore | 1 + installer/windows/VoxKit.iss | 42 ++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 installer/windows/VoxKit.iss diff --git a/.gitignore b/.gitignore index 0a9e218..54b6ff1 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,7 @@ ENV/ build/ dist/ downloads/ +installer/windows/output/ .eggs/ .eggs/** *.egg-info/ diff --git a/installer/windows/VoxKit.iss b/installer/windows/VoxKit.iss new file mode 100644 index 0000000..0170b76 --- /dev/null +++ b/installer/windows/VoxKit.iss @@ -0,0 +1,42 @@ +#define AppName "VoxKit" +#define AppVersion "0.4.1" +#define AppPublisher "Brain Behavior Analytics Lab" +#define AppURL "https://github.com/BrainBehaviorAnalyticsLab/voxkit-desktop" +#define AppExeName "VoxKit.exe" + +[Setup] +AppId={{A3F2C1D4-8B7E-4F6A-9D2E-1C5B3A7E8F90} +AppName={#AppName} +AppVersion={#AppVersion} +AppPublisher={#AppPublisher} +AppPublisherURL={#AppURL} +AppSupportURL={#AppURL} +AppUpdatesURL={#AppURL} +DefaultDirName={autopf}\{#AppName} +DefaultGroupName={#AppName} +DisableProgramGroupPage=yes +OutputDir=output +OutputBaseFilename=VoxKit-setup-windowsOS +SetupIconFile=..\..\assets\voxkit.ico +Compression=lzma +SolidCompression=yes +WizardStyle=modern +PrivilegesRequired=lowest +ArchitecturesAllowed=x64compatible +ArchitecturesInstallIn64BitMode=x64compatible + +[Languages] +Name: "english"; MessagesFile: "compiler:Default.isl" + +[Tasks] +Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}" + +[Files] +Source: "..\..\dist\{#AppExeName}"; DestDir: "{app}"; Flags: ignoreversion + +[Icons] +Name: "{autoprograms}\{#AppName}"; Filename: "{app}\{#AppExeName}"; IconFilename: "{app}\{#AppExeName}" +Name: "{autodesktop}\{#AppName}"; Filename: "{app}\{#AppExeName}"; IconFilename: "{app}\{#AppExeName}"; Tasks: desktopicon + +[Run] +Filename: "{app}\{#AppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(AppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent From 86c16a78e560779d135dda3dced3558a430086a9 Mon Sep 17 00:00:00 2001 From: BeckettFrey Date: Fri, 8 May 2026 13:53:54 -0500 Subject: [PATCH 06/11] Add linux-build invoke task and AppImage packaging --- installer/linux/VoxKit.desktop | 8 ++++ pyproject.toml | 4 +- tasks.py | 82 ++++++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 installer/linux/VoxKit.desktop diff --git a/installer/linux/VoxKit.desktop b/installer/linux/VoxKit.desktop new file mode 100644 index 0000000..d8c8be2 --- /dev/null +++ b/installer/linux/VoxKit.desktop @@ -0,0 +1,8 @@ +[Desktop Entry] +Name=VoxKit +Exec=VoxKit +Icon=voxkit +Type=Application +Categories=Science;Audio;Education; +Comment=AI/ML Speech Pathology Analysis +Terminal=false diff --git a/pyproject.toml b/pyproject.toml index 7a924e4..b5c13ff 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -127,7 +127,9 @@ exclude = [ "build.py", "example_startup_script.py", "test_imports.py", - "tests/" + "tests/", + "build/", + "dist/", ] diff --git a/tasks.py b/tasks.py index 50d4993..b16ec3b 100644 --- a/tasks.py +++ b/tasks.py @@ -1,8 +1,10 @@ """Invoke task definitions for VoxKit. Run with `invoke ` or `invoke --list`.""" import shutil +import stat import subprocess import sys +import urllib.request from pathlib import Path from invoke import task @@ -98,6 +100,86 @@ def macos_build(c): ) +@task(pre=[clean], name="linux-build") +def linux_build(c): + """Build AppImage for Linux.""" + tools_dir = ROOT / "tools" + tools_dir.mkdir(exist_ok=True) + + # Step 1: PyInstaller one-dir build + _log("Building VoxKit for Linux (one-dir)...") + c.run( + "uv run --group installation python scripts/build.py build " + "--entry main.py --name VoxKit --icon ./assets/voxkit.png --windowed --no-onefile" + ) + + dist_dir = ROOT / "dist" / "VoxKit" + if not dist_dir.exists(): + _log(f"PyInstaller output not found at {dist_dir}", RED) + return + + # Step 2: Bundle libxcb-cursor — Qt's xcb platform plugin loads it via dlopen + # at runtime so PyInstaller's ldd scan misses it. We copy it next to libqxcb.so + # so LD_LIBRARY_PATH (set by PyInstaller's bootloader) picks it up. + _log("Bundling libxcb-cursor...") + xcb_cursor_candidates = [ + Path("/usr/lib/x86_64-linux-gnu/libxcb-cursor.so.0"), + Path("/usr/lib/libxcb-cursor.so.0"), + ] + xcb_cursor_src = next((p for p in xcb_cursor_candidates if p.exists()), None) + if xcb_cursor_src is None: + result = subprocess.run( # noqa: S603 + ["find", "/usr", "-name", "libxcb-cursor.so.0"], # noqa: S607 + capture_output=True, + text=True, + check=False, + ) + found = result.stdout.strip().splitlines() + xcb_cursor_src = Path(found[0]) if found else None + + if xcb_cursor_src is None: + _log("libxcb-cursor.so.0 not found — run: sudo apt-get install libxcb-cursor0", RED) + return + + internal_dir = dist_dir / "_internal" + dest_dir = internal_dir if internal_dir.is_dir() else dist_dir + shutil.copy2(xcb_cursor_src, dest_dir / "libxcb-cursor.so.0") + _log(f"Bundled {xcb_cursor_src} -> {dest_dir / 'libxcb-cursor.so.0'}", GREEN) + + # Step 3: Set up AppDir + _log("Setting up AppDir...") + appdir = ROOT / "build" / "AppDir" + _rmtree(appdir) + appdir.mkdir(parents=True) + + shutil.copytree(dist_dir, appdir / "VoxKit") + + apprun = appdir / "AppRun" + apprun.write_text( + "#!/bin/bash\n" + 'APPDIR="$(dirname "$(readlink -f "$0")")"\n' + 'exec "$APPDIR/VoxKit/VoxKit" "$@"\n' + ) + apprun.chmod(apprun.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH) + + shutil.copy2(ROOT / "installer" / "linux" / "VoxKit.desktop", appdir / "VoxKit.desktop") + shutil.copy2(ROOT / "assets" / "voxkit.png", appdir / "voxkit.png") + + # Step 4: Download appimagetool if needed + appimagetool = tools_dir / "appimagetool-x86_64.AppImage" + if not appimagetool.exists(): + _log("Downloading appimagetool...") + url = "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage" + urllib.request.urlretrieve(url, appimagetool) # noqa: S310 + appimagetool.chmod(appimagetool.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH) + + # Step 5: Package AppImage + _log("Packaging AppImage...") + output = ROOT / "dist" / "VoxKit-x86_64.AppImage" + c.run(f"ARCH=x86_64 {appimagetool} {appdir} {output}") + _log(f"AppImage created: {output}", GREEN) + + @task(pre=[clean], name="windows-build") def windows_build(c): """Build standalone executable for Windows.""" From 03e7e1fe3a55f7bc96866f11c4d6b1ec708d0919 Mon Sep 17 00:00:00 2001 From: BeckettFrey Date: Fri, 8 May 2026 13:54:31 -0500 Subject: [PATCH 07/11] Propagate MFA errors and surface stderr in failure messages --- src/voxkit/engines/mfa_engine.py | 1 + src/voxkit/services/mfa.py | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/voxkit/engines/mfa_engine.py b/src/voxkit/engines/mfa_engine.py index 61c3438..7cfde55 100644 --- a/src/voxkit/engines/mfa_engine.py +++ b/src/voxkit/engines/mfa_engine.py @@ -159,6 +159,7 @@ def align(self, dataset_id: str, model_id: str) -> None: alignment_id=msg["id"], updates={"status": "failed"}, ) + raise def train_aligner( self, audio_root: Path, textgrid_root: Path, base_model_id: str | None, new_model_id: str diff --git a/src/voxkit/services/mfa.py b/src/voxkit/services/mfa.py index dde029d..c8a225c 100755 --- a/src/voxkit/services/mfa.py +++ b/src/voxkit/services/mfa.py @@ -138,11 +138,12 @@ def run_mfa_align( try: print(f"[mfa.run_mfa_align] Running MFA align with command: {' '.join(cmd)}") - subprocess.run(cmd, check=True, **_no_window()) + subprocess.run(cmd, check=True, capture_output=True, text=True, **_no_window()) print("[mfa.run_mfa_align] MFA alignment completed successfully.") except subprocess.CalledProcessError as e: - print(f"[mfa.run_mfa_align] MFA alignment failed with error: {e}") - raise + stderr_msg = e.stderr.strip() if e.stderr else "(no output captured)" + print(f"[mfa.run_mfa_align] MFA alignment failed:\n{stderr_msg}") + raise RuntimeError(f"MFA alignment failed (exit {e.returncode}):\n{stderr_msg}") from e def run_mfa_adapt( From 8ad2c0187166ade8ec81857c46f60c798e0dee77 Mon Sep 17 00:00:00 2001 From: BeckettFrey Date: Fri, 8 May 2026 13:57:01 -0500 Subject: [PATCH 08/11] Ignore tools/ directory --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 54b6ff1..468de47 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ build/ dist/ downloads/ installer/windows/output/ +tools/ .eggs/ .eggs/** *.egg-info/ From 98b824aa8bf3a7b895383054e21a9a3083230c25 Mon Sep 17 00:00:00 2001 From: beckett Date: Fri, 8 May 2026 15:25:12 -0500 Subject: [PATCH 09/11] Auto-start MFA Postgres server on Windows to avoid SQLite race MFA 3.3.x's SQLite backend hits a multiprocessing race in collect_alignments on Windows where the word_interval_temp staging table disappears between worker connections, aborting alignment. Run `mfa server init` + `mfa server start` (both idempotent) before align and adapt so the bundled Postgres backend is used instead. Gated to win32 so macOS and Linux behavior is unchanged. --- src/voxkit/services/mfa.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/voxkit/services/mfa.py b/src/voxkit/services/mfa.py index c8a225c..bf373ed 100755 --- a/src/voxkit/services/mfa.py +++ b/src/voxkit/services/mfa.py @@ -97,6 +97,39 @@ def ensure_dictionary_downloaded(dictionary_name: str = "english_us_arpa") -> No print(f"[mfa] Dictionary '{dictionary_name}' is ready.") +def _ensure_mfa_server_running() -> None: + """Start MFA's bundled Postgres server on Windows. No-op elsewhere. + + Why: MFA 3.3.x's SQLite backend has a multiprocessing race in + `collect_alignments` on Windows where the `word_interval_temp` staging + table goes missing between worker connections, aborting the run with + "no such table: word_interval_temp". The Postgres backend (also bundled + in the aligner conda env) does not have this race. macOS and Linux are + not affected by the SQLite race in practice, so this is gated to win32 + to avoid changing their behavior. + """ + if sys.platform != "win32": + return + + conda = _find_conda() + # Both calls are idempotent: `init` errors if the server dir already exists, + # `start` errors if it's already running. Either error state is the goal, + # so we ignore returncodes and only guard against true failures (timeout, + # missing conda). If the server genuinely can't come up, alignment will + # fall back to SQLite and surface its own error via the existing path. + for sub in (("server", "init"), ("server", "start")): + try: + subprocess.run( + [conda, "run", "-n", "aligner", "mfa", *sub], + capture_output=True, + text=True, + timeout=60, + **_no_window(), + ) + except (subprocess.TimeoutExpired, FileNotFoundError): + pass + + def run_mfa_align( corpus_dir, model_path, output_dir, dictionary_name="english_us_arpa", eval_dir=None ) -> None: @@ -116,6 +149,7 @@ def run_mfa_align( """ # Ensure dictionary is downloaded ensure_dictionary_downloaded(dictionary_name) + _ensure_mfa_server_running() conda = _find_conda() cmd = [ @@ -169,6 +203,7 @@ def run_mfa_adapt( """ # Ensure dictionary is downloaded ensure_dictionary_downloaded(dictionary_name) + _ensure_mfa_server_running() conda = _find_conda() cmd = [ From 99d08be6d2e65aa6d0a9c69848621fcdb844b032 Mon Sep 17 00:00:00 2001 From: Beckett Frey Date: Fri, 8 May 2026 16:08:36 -0500 Subject: [PATCH 10/11] Use astral-sh/setup-uv action in CI workflows Old install script added $HOME/.cargo/bin to PATH, but modern uv installs to $HOME/.local/bin, breaking Windows and Ubuntu jobs. --- .github/workflows/tests-macos.yml | 5 +---- .github/workflows/tests-ubuntu.yml | 5 +---- .github/workflows/tests-windows.yml | 5 +---- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/.github/workflows/tests-macos.yml b/.github/workflows/tests-macos.yml index d53ce2b..28f8cab 100644 --- a/.github/workflows/tests-macos.yml +++ b/.github/workflows/tests-macos.yml @@ -25,10 +25,7 @@ jobs: python-version: '3.11' - name: Install uv - run: | - curl -LsSf https://astral.sh/uv/install.sh | sh - echo "$HOME/.cargo/bin" >> $GITHUB_PATH - shell: bash + uses: astral-sh/setup-uv@v3 - name: Configure Git for private repos run: | diff --git a/.github/workflows/tests-ubuntu.yml b/.github/workflows/tests-ubuntu.yml index 0401d15..ced65b9 100644 --- a/.github/workflows/tests-ubuntu.yml +++ b/.github/workflows/tests-ubuntu.yml @@ -25,10 +25,7 @@ jobs: python-version: '3.11' - name: Install uv - run: | - curl -LsSf https://astral.sh/uv/install.sh | sh - echo "$HOME/.cargo/bin" >> $GITHUB_PATH - shell: bash + uses: astral-sh/setup-uv@v3 - name: Configure Git for private repos run: | diff --git a/.github/workflows/tests-windows.yml b/.github/workflows/tests-windows.yml index 1eb5895..13d4bf8 100644 --- a/.github/workflows/tests-windows.yml +++ b/.github/workflows/tests-windows.yml @@ -25,10 +25,7 @@ jobs: python-version: '3.11' - name: Install uv - run: | - curl -LsSf https://astral.sh/uv/install.sh | sh - echo "$HOME/.cargo/bin" >> $GITHUB_PATH - shell: bash + uses: astral-sh/setup-uv@v3 - name: Configure Git for private repos run: | From 9254b90be1d74a8283362de5493ef21b0a574f67 Mon Sep 17 00:00:00 2001 From: Beckett Frey Date: Fri, 8 May 2026 16:12:05 -0500 Subject: [PATCH 11/11] Install Qt system deps and run tests under xvfb on Ubuntu CI pytest-qt failed with 'libEGL.so.1: cannot open shared object file' on ubuntu-latest because Qt's runtime libraries aren't preinstalled and there's no display server available. --- .github/workflows/tests-ubuntu.yml | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests-ubuntu.yml b/.github/workflows/tests-ubuntu.yml index ced65b9..3e933f0 100644 --- a/.github/workflows/tests-ubuntu.yml +++ b/.github/workflows/tests-ubuntu.yml @@ -24,6 +24,29 @@ jobs: with: python-version: '3.11' + - name: Install Qt system dependencies + run: | + sudo apt-get update + sudo apt-get install -y \ + libegl1 \ + libgl1 \ + libglib2.0-0 \ + libdbus-1-3 \ + libxkbcommon0 \ + libxcb-icccm4 \ + libxcb-image0 \ + libxcb-keysyms1 \ + libxcb-randr0 \ + libxcb-render-util0 \ + libxcb-shape0 \ + libxcb-sync1 \ + libxcb-xfixes0 \ + libxcb-xinerama0 \ + libxcb-cursor0 \ + libfontconfig1 \ + libxkbcommon-x11-0 \ + xvfb + - name: Install uv uses: astral-sh/setup-uv@v3 @@ -37,7 +60,7 @@ jobs: - name: Run tests run: | - uv run invoke run-tests + xvfb-run -a uv run invoke run-tests - name: Run linting run: |