mac-speech-analyzer 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mac_speech_analyzer-0.1.0/.github/workflows/release.yml +33 -0
- mac_speech_analyzer-0.1.0/.python-version +1 -0
- mac_speech_analyzer-0.1.0/CMakeLists.txt +19 -0
- mac_speech_analyzer-0.1.0/LICENSE +22 -0
- mac_speech_analyzer-0.1.0/PKG-INFO +82 -0
- mac_speech_analyzer-0.1.0/Package.swift +19 -0
- mac_speech_analyzer-0.1.0/README.md +72 -0
- mac_speech_analyzer-0.1.0/Speech.entitlements +11 -0
- mac_speech_analyzer-0.1.0/pyproject.toml +35 -0
- mac_speech_analyzer-0.1.0/src/macspeechanalyzer/__init__.py +4 -0
- mac_speech_analyzer-0.1.0/src/macspeechanalyzer/main.py +116 -0
- mac_speech_analyzer-0.1.0/swift/bridge.swift +87 -0
- mac_speech_analyzer-0.1.0/swift/core.swift +95 -0
- mac_speech_analyzer-0.1.0/uv.lock +185 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
name: "Publish release to PyPI"
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
# Publish on any tag starting with a `v`, e.g., v0.1.0
|
|
7
|
+
- v*
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
build:
|
|
11
|
+
runs-on: macos-26
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
- uses: astral-sh/setup-uv@v5
|
|
15
|
+
- uses: maxim-lobanov/setup-xcode@v1
|
|
16
|
+
with:
|
|
17
|
+
xcode-version: latest-stable
|
|
18
|
+
- run: uv build
|
|
19
|
+
- uses: actions/upload-artifact@v4
|
|
20
|
+
with:
|
|
21
|
+
name: dist
|
|
22
|
+
path: dist/
|
|
23
|
+
publish:
|
|
24
|
+
needs: build
|
|
25
|
+
runs-on: ubuntu-latest
|
|
26
|
+
environment: pypi
|
|
27
|
+
permissions:
|
|
28
|
+
id-token: write
|
|
29
|
+
steps:
|
|
30
|
+
- uses: actions/download-artifact@v4
|
|
31
|
+
with: { name: dist, path: dist/ }
|
|
32
|
+
- uses: astral-sh/setup-uv@v7
|
|
33
|
+
- run: uv publish
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.14
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
cmake_minimum_required(VERSION 3.26)
|
|
2
|
+
project(macspeechanalyzer LANGUAGES Swift)
|
|
3
|
+
|
|
4
|
+
add_library(macspeechanalyzer_core SHARED
|
|
5
|
+
swift/core.swift
|
|
6
|
+
swift/bridge.swift)
|
|
7
|
+
|
|
8
|
+
# Editable/inplace: no install step runs, so aim the linker output
|
|
9
|
+
# directly at the package. $<0:> keeps multi-config generators happy.
|
|
10
|
+
if(SKBUILD_STATE STREQUAL "editable")
|
|
11
|
+
set_target_properties(macspeechanalyzer_core PROPERTIES
|
|
12
|
+
LIBRARY_OUTPUT_DIRECTORY
|
|
13
|
+
"${CMAKE_CURRENT_SOURCE_DIR}/src/macspeechanalyzer/$<0:>")
|
|
14
|
+
endif()
|
|
15
|
+
|
|
16
|
+
# Wheel builds: install step runs, destination is relative to the wheel root
|
|
17
|
+
install(TARGETS macspeechanalyzer_core
|
|
18
|
+
LIBRARY DESTINATION macspeechanalyzer
|
|
19
|
+
RUNTIME DESTINATION macspeechanalyzer)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 paraversal
|
|
4
|
+
Copyright (c) 2025 Argmax, Inc.
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mac-speech-analyzer
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: On-device speech transcription for macOS via Apple's SpeechAnalyzer
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Classifier: Operating System :: MacOS
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Requires-Python: >=3.10
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
|
|
11
|
+
# mac-speech-analyzer
|
|
12
|
+
|
|
13
|
+
🐍 Python package to run the macOS 26 SpeechAnalyzer API from Python to transcribe audio files.
|
|
14
|
+
|
|
15
|
+
The SpeechAnalyzer API generally provides [faster and better results]((https://get-inscribe.com/blog/apple-speech-api-benchmark.html)) than other locally running transcription engines of comparable size.
|
|
16
|
+
|
|
17
|
+
## Features
|
|
18
|
+
|
|
19
|
+
- **Online** or **Offline** transcription
|
|
20
|
+
- Automatic download of new languages
|
|
21
|
+
|
|
22
|
+
## Requirements
|
|
23
|
+
|
|
24
|
+
- macOS >= 26.0
|
|
25
|
+
- XCode command-line tools
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
mac-speech-analyzer is currently not tracked on pypi yet.
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
uv add git+https://github.com/paraversal/mac-speech-analyzer
|
|
33
|
+
# or
|
|
34
|
+
pip install git+https://github.com/paraversal/mac-speech-analyzer
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
### Basic usage
|
|
40
|
+
|
|
41
|
+
```py
|
|
42
|
+
from macspeechanalyzer import transcribe
|
|
43
|
+
|
|
44
|
+
text = transcribe("/path/to/file", locale = "en_US")
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Technical details
|
|
48
|
+
|
|
49
|
+
This package is made up of two parts: the Python wrapper provides the API, but the actual functionality is achieved by embedding a Swift project which exposes functions to C. With this kind of three language interplay, a clean separation of concerns is really important. Therefore, the implementation is divided into the following four layers:
|
|
50
|
+
|
|
51
|
+
- Pure Python
|
|
52
|
+
- Python → C compat
|
|
53
|
+
- Swift → C compat
|
|
54
|
+
- Pure Swift
|
|
55
|
+
|
|
56
|
+
The pure Swift and pure Python layers only deal with their respective languages & types, while the compat layers do all the ugly work of converting between native and C-compatible types
|
|
57
|
+
|
|
58
|
+
## Roadmap
|
|
59
|
+
|
|
60
|
+
- ✅ Basic one-shot transcription
|
|
61
|
+
- Class-based interface
|
|
62
|
+
- Context manager implementation
|
|
63
|
+
- Transcription of live audio stream (microphone, ...)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
## Development
|
|
67
|
+
|
|
68
|
+
```zsh
|
|
69
|
+
uv pip install -e .
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
to one-time-install the package as editable, from then on:
|
|
73
|
+
|
|
74
|
+
```zsh
|
|
75
|
+
ninja
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
to rebuild the package
|
|
79
|
+
|
|
80
|
+
# License
|
|
81
|
+
|
|
82
|
+
mac-speech-analyzer is released under the MIT License. See [LICENSE](./LICENSE) for more details.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import PackageDescription
|
|
2
|
+
|
|
3
|
+
let package = Package(
|
|
4
|
+
name: "mac-speech-analyzer",
|
|
5
|
+
platforms: [ .macOS(.v26) ],
|
|
6
|
+
targets: [
|
|
7
|
+
.executableTarget(
|
|
8
|
+
name: "mac-speech-analyzer",
|
|
9
|
+
linkerSettings: [
|
|
10
|
+
.unsafeFlags([
|
|
11
|
+
"-Xlinker","-sectcreate",
|
|
12
|
+
"-Xlinker","__TEXT",
|
|
13
|
+
"-Xlinker","__info_plist",
|
|
14
|
+
"-Xlinker","Info.plist"
|
|
15
|
+
])
|
|
16
|
+
]
|
|
17
|
+
)
|
|
18
|
+
]
|
|
19
|
+
)
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# mac-speech-analyzer
|
|
2
|
+
|
|
3
|
+
🐍 Python package to run the macOS 26 SpeechAnalyzer API from Python to transcribe audio files.
|
|
4
|
+
|
|
5
|
+
The SpeechAnalyzer API generally provides [faster and better results]((https://get-inscribe.com/blog/apple-speech-api-benchmark.html)) than other locally running transcription engines of comparable size.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Online** or **Offline** transcription
|
|
10
|
+
- Automatic download of new languages
|
|
11
|
+
|
|
12
|
+
## Requirements
|
|
13
|
+
|
|
14
|
+
- macOS >= 26.0
|
|
15
|
+
- XCode command-line tools
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
mac-speech-analyzer is currently not tracked on pypi yet.
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
uv add git+https://github.com/paraversal/mac-speech-analyzer
|
|
23
|
+
# or
|
|
24
|
+
pip install git+https://github.com/paraversal/mac-speech-analyzer
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
### Basic usage
|
|
30
|
+
|
|
31
|
+
```py
|
|
32
|
+
from macspeechanalyzer import transcribe
|
|
33
|
+
|
|
34
|
+
text = transcribe("/path/to/file", locale = "en_US")
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Technical details
|
|
38
|
+
|
|
39
|
+
This package is made up of two parts: the Python wrapper provides the API, but the actual functionality is achieved by embedding a Swift project which exposes functions to C. With this kind of three language interplay, a clean separation of concerns is really important. Therefore, the implementation is divided into the following four layers:
|
|
40
|
+
|
|
41
|
+
- Pure Python
|
|
42
|
+
- Python → C compat
|
|
43
|
+
- Swift → C compat
|
|
44
|
+
- Pure Swift
|
|
45
|
+
|
|
46
|
+
The pure Swift and pure Python layers only deal with their respective languages & types, while the compat layers do all the ugly work of converting between native and C-compatible types
|
|
47
|
+
|
|
48
|
+
## Roadmap
|
|
49
|
+
|
|
50
|
+
- ✅ Basic one-shot transcription
|
|
51
|
+
- Class-based interface
|
|
52
|
+
- Context manager implementation
|
|
53
|
+
- Transcription of live audio stream (microphone, ...)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
## Development
|
|
57
|
+
|
|
58
|
+
```zsh
|
|
59
|
+
uv pip install -e .
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
to one-time-install the package as editable, from then on:
|
|
63
|
+
|
|
64
|
+
```zsh
|
|
65
|
+
ninja
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
to rebuild the package
|
|
69
|
+
|
|
70
|
+
# License
|
|
71
|
+
|
|
72
|
+
mac-speech-analyzer is released under the MIT License. See [LICENSE](./LICENSE) for more details.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
|
3
|
+
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
4
|
+
<plist version="1.0">
|
|
5
|
+
<dict>
|
|
6
|
+
<key>com.apple.developer.speech-recognition</key>
|
|
7
|
+
<true/>
|
|
8
|
+
<key>com.apple.security.cs.disable-library-validation</key>
|
|
9
|
+
<true/>
|
|
10
|
+
</dict>
|
|
11
|
+
</plist>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["scikit-build-core>=0.10"]
|
|
3
|
+
build-backend = "scikit_build_core.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "mac-speech-analyzer"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "On-device speech transcription for macOS via Apple's SpeechAnalyzer"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
classifiers = [
|
|
13
|
+
"Operating System :: MacOS",
|
|
14
|
+
"Programming Language :: Python :: 3"
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
[tool.scikit-build]
|
|
18
|
+
wheel.packages = ["src/macspeechanalyzer"]
|
|
19
|
+
wheel.py-api = "py3"
|
|
20
|
+
minimum-version = "build-system.requires"
|
|
21
|
+
build-dir = "build/{wheel_tag}"
|
|
22
|
+
|
|
23
|
+
[tool.scikit-build.cmake]
|
|
24
|
+
version = ">=3.26"
|
|
25
|
+
|
|
26
|
+
[tool.scikit-build.editable]
|
|
27
|
+
mode = "inplace"
|
|
28
|
+
rebuild = true
|
|
29
|
+
|
|
30
|
+
[dependency-groups]
|
|
31
|
+
dev = [
|
|
32
|
+
"cmake>=4.4.0",
|
|
33
|
+
"ninja>=1.13.0",
|
|
34
|
+
"scikit-build-core>=1.0.3",
|
|
35
|
+
]
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import ctypes
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import logging
|
|
4
|
+
from typing import Callable
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
log = logging.getLogger(__name__)
|
|
8
|
+
|
|
9
|
+
LOG_CB = ctypes.CFUNCTYPE(None, ctypes.c_int, ctypes.c_char_p)
|
|
10
|
+
SEGMENT_CB = ctypes.CFUNCTYPE(None, ctypes.c_char_p)
|
|
11
|
+
|
|
12
|
+
@LOG_CB
|
|
13
|
+
def _log_cb(level, msg):
|
|
14
|
+
# for now, hacky: newline and flush stdout before/after each log print, otherwise logs may be bolted straight to a segment
|
|
15
|
+
print("", flush=True)
|
|
16
|
+
log.log(level or logging.INFO, msg.decode("utf-8", "replace") if msg else "")
|
|
17
|
+
|
|
18
|
+
@SEGMENT_CB
|
|
19
|
+
def _live_cb(msg):
|
|
20
|
+
print(msg.decode("utf-8", "replace") if msg else "", end="", flush=True)
|
|
21
|
+
|
|
22
|
+
_lib = ctypes.CDLL(str(Path(__file__).parent / f"libmacspeechanalyzer_core.dylib"))
|
|
23
|
+
|
|
24
|
+
# int32 msa_transcribe(const char* url, const char* locale, char** outText, bool* emitLiveFragments)
|
|
25
|
+
_lib.msa_transcribe.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.POINTER(ctypes.c_void_p), ctypes.c_bool]
|
|
26
|
+
_lib.msa_transcribe.restype = ctypes.c_int32
|
|
27
|
+
|
|
28
|
+
# void msa_free_string(char* ptr)
|
|
29
|
+
_lib.msa_free_string.argtypes = [ctypes.c_void_p]
|
|
30
|
+
_lib.msa_free_string.restype = None
|
|
31
|
+
|
|
32
|
+
# char* msa_last_error() — strdup'ed message, caller frees
|
|
33
|
+
_lib.msa_last_error.argtypes = []
|
|
34
|
+
_lib.msa_last_error.restype = ctypes.c_void_p
|
|
35
|
+
|
|
36
|
+
_lib.msa_set_log_callback.argtypes = [LOG_CB]
|
|
37
|
+
_lib.msa_set_log_callback.restype = None
|
|
38
|
+
_lib.msa_set_log_callback(_log_cb)
|
|
39
|
+
|
|
40
|
+
_lib.msa_set_live_callback.argtypes = [SEGMENT_CB]
|
|
41
|
+
_lib.msa_set_live_callback.restype = None
|
|
42
|
+
|
|
43
|
+
class SpeechAnalyzerError(Exception):
|
|
44
|
+
pass
|
|
45
|
+
|
|
46
|
+
def _take_string(ptr: ctypes.c_void_p) -> str:
|
|
47
|
+
"""Copy a Swift-allocated C string into a Python str, then free it."""
|
|
48
|
+
try:
|
|
49
|
+
return ctypes.string_at(ptr).decode("utf-8")
|
|
50
|
+
finally:
|
|
51
|
+
_lib.msa_free_string(ptr)
|
|
52
|
+
|
|
53
|
+
def _raise_last_error(status: int) -> None:
|
|
54
|
+
ptr = _lib.msa_last_error()
|
|
55
|
+
msg = _take_string(ctypes.c_void_p(ptr)) if ptr else f"native error (status {status})"
|
|
56
|
+
raise SpeechAnalyzerError(msg)
|
|
57
|
+
|
|
58
|
+
def transcribe(path: str | Path, locale: str | None = None, on_segment: Callable[[str], None] | None = None) -> str:
|
|
59
|
+
"""Transcribe an audio file on-device.
|
|
60
|
+
|
|
61
|
+
Runs synchronously and returns the full transcript once the file has
|
|
62
|
+
been fully analyzed. If ``on_segment`` is provided, it is also invoked
|
|
63
|
+
once per finalized segment as transcription progresses, allowing the
|
|
64
|
+
caller to observe or display partial results without waiting for the
|
|
65
|
+
final return value.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
path: Filesystem path to the audio file to transcribe. Any format
|
|
69
|
+
readable by ``AVAudioFile`` is accepted (e.g. wav, m4a, mp3,
|
|
70
|
+
caf, aiff).
|
|
71
|
+
locale: BCP 47 / Apple locale identifier (e.g. ``"en_US"``,
|
|
72
|
+
``"de_DE"``, ``"ja_JP"``) selecting the transcription language.
|
|
73
|
+
If ``None``, the system's current locale is used when supported,
|
|
74
|
+
otherwise English. If the requested locale's model is not yet
|
|
75
|
+
installed on the device, it will be downloaded on first use.
|
|
76
|
+
on_segment: Optional callback invoked from a background thread once
|
|
77
|
+
per finalized transcript segment, in order, with the segment
|
|
78
|
+
text as its single argument. Segments are non-overlapping and
|
|
79
|
+
never revised, so appending them reconstructs the full
|
|
80
|
+
transcript. Pass ``print`` to echo segments to stdout as they
|
|
81
|
+
arrive.
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
The complete transcript as a single string.
|
|
85
|
+
|
|
86
|
+
Raises:
|
|
87
|
+
FileNotFoundError: If ``url`` does not point to a readable file.
|
|
88
|
+
RuntimeError: If the host is running a macOS version older than
|
|
89
|
+
26.0, or if the native transcription pipeline fails.
|
|
90
|
+
"""
|
|
91
|
+
emit_live_segments = False
|
|
92
|
+
if on_segment is print:
|
|
93
|
+
_lib.msa_set_live_callback(_live_cb)
|
|
94
|
+
emit_live_segments = True
|
|
95
|
+
elif on_segment is not None:
|
|
96
|
+
def _trampoline(raw: bytes | None) -> None:
|
|
97
|
+
if raw is not None:
|
|
98
|
+
on_segment(raw.decode("utf-8", "replace"))
|
|
99
|
+
cb = SEGMENT_CB(_trampoline)
|
|
100
|
+
_lib.msa_set_live_callback(cb)
|
|
101
|
+
emit_live_segments = True
|
|
102
|
+
|
|
103
|
+
path = os.fspath(path)
|
|
104
|
+
|
|
105
|
+
out = ctypes.c_void_p()
|
|
106
|
+
status = _lib.msa_transcribe(
|
|
107
|
+
path.encode("utf-8"),
|
|
108
|
+
locale.encode("utf-8") if locale is not None else None,
|
|
109
|
+
ctypes.byref(out), # &out -> char**
|
|
110
|
+
emit_live_segments
|
|
111
|
+
)
|
|
112
|
+
if status != 0:
|
|
113
|
+
_raise_last_error(status)
|
|
114
|
+
if not out.value:
|
|
115
|
+
raise SpeechAnalyzerError("transcribe returned success but no text")
|
|
116
|
+
return _take_string(out)
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
// Async compat
|
|
4
|
+
final class ResultBox<T>: @unchecked Sendable {
|
|
5
|
+
var value: Result<T, Error>?
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
func runBlocking<T: Sendable>(_ body: @Sendable @escaping () async throws -> T) throws -> T {
|
|
9
|
+
let box = ResultBox<T>()
|
|
10
|
+
let semaphore = DispatchSemaphore(value: 0)
|
|
11
|
+
Task.detached {
|
|
12
|
+
do { box.value = .success(try await body()) }
|
|
13
|
+
catch { box.value = .failure(error) }
|
|
14
|
+
semaphore.signal()
|
|
15
|
+
}
|
|
16
|
+
semaphore.wait()
|
|
17
|
+
return try box.value!.get()
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
private let errorLock = NSLock()
|
|
21
|
+
private var _lastError: String?
|
|
22
|
+
|
|
23
|
+
func setLastError(_ message: String) {
|
|
24
|
+
errorLock.lock(); defer { errorLock.unlock() }
|
|
25
|
+
_lastError = message
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@_cdecl("msa_last_error")
|
|
29
|
+
public func msaLastError() -> UnsafeMutablePointer<CChar>? {
|
|
30
|
+
errorLock.lock(); defer { errorLock.unlock() }
|
|
31
|
+
guard let err = _lastError else { return nil }
|
|
32
|
+
return strdup(err) // Python copies + msa_free_string's it
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@_cdecl("msa_transcribe")
|
|
36
|
+
public func msaTranscribe(
|
|
37
|
+
_ url: UnsafePointer<CChar>?,
|
|
38
|
+
_ locale: UnsafePointer<CChar>?,
|
|
39
|
+
_ outText: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?,
|
|
40
|
+
_ emitLiveSegments: CBool) -> Int32 {
|
|
41
|
+
let localeStr = locale.map { String(cString: $0) } // nil stays nil
|
|
42
|
+
let urlStr = url.map { String(cString: $0) }!
|
|
43
|
+
do {
|
|
44
|
+
let text = try runBlocking { try await transcribe(url: urlStr, locale: localeStr, emitLiveSegments: emitLiveSegments) }
|
|
45
|
+
outText?.pointee = strdup(text)
|
|
46
|
+
return 0
|
|
47
|
+
} catch {
|
|
48
|
+
setLastError("\(error)")
|
|
49
|
+
return 1
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
@_cdecl("msa_free_string")
|
|
54
|
+
public func msaFreeString(_ ptr: UnsafeMutablePointer<CChar>?) {
|
|
55
|
+
free(ptr)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
public typealias MSALogCallback = @convention(c) (Int32, UnsafePointer<CChar>?) -> Void
|
|
59
|
+
private var logSink: MSALogCallback?
|
|
60
|
+
|
|
61
|
+
@_cdecl("msa_set_log_callback")
|
|
62
|
+
public func msa_set_log_callback(_ cb: MSALogCallback?) {
|
|
63
|
+
logSink = cb
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// use this everywhere instead of print()
|
|
67
|
+
func msaLog(_ level: Int32, _ message: String) {
|
|
68
|
+
if let sink = logSink {
|
|
69
|
+
message.withCString { sink(level, $0) }
|
|
70
|
+
} else {
|
|
71
|
+
print(message) // fallback when nobody registered
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
public typealias MSALiveTranscription = @convention(c) (UnsafePointer<CChar>?) -> Void
|
|
76
|
+
private var liveTranscriptionSink: MSALiveTranscription?
|
|
77
|
+
|
|
78
|
+
@_cdecl("msa_set_live_callback")
|
|
79
|
+
public func msa_set_live_transcription_callback(_ cb: MSALiveTranscription?) {
|
|
80
|
+
liveTranscriptionSink = cb
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
func msaLiveTranscriptionSendFragment(_ message: String) {
|
|
84
|
+
if let sink = liveTranscriptionSink {
|
|
85
|
+
message.withCString { sink($0) }
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import AVFAudio
|
|
3
|
+
import Speech
|
|
4
|
+
import Darwin
|
|
5
|
+
|
|
6
|
+
let INFO = Int32(20)
|
|
7
|
+
|
|
8
|
+
enum MSAError: Error {
|
|
9
|
+
case osRequirementError(String)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
func checkMacosVersion () throws {
|
|
13
|
+
guard #available(macOS 26.0, *) else {
|
|
14
|
+
throw MSAError.osRequirementError("SpeechAnalyzer is only available on macos >= 26.0.")
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
func transcribe(url: String, locale: String?, emitLiveSegments: Bool) async throws -> String {
|
|
19
|
+
try checkMacosVersion()
|
|
20
|
+
let locale = await getLocale(customLocale: locale)
|
|
21
|
+
|
|
22
|
+
let transcriber = SpeechTranscriber(
|
|
23
|
+
locale: locale,
|
|
24
|
+
preset: .transcription
|
|
25
|
+
)
|
|
26
|
+
do {
|
|
27
|
+
try await downloadLocale(locale, transcriber)
|
|
28
|
+
} catch {
|
|
29
|
+
msaLog(INFO, "so this failed...")
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let analyzer = SpeechAnalyzer(modules: [transcriber])
|
|
33
|
+
let inputURL = URL(fileURLWithPath: url)
|
|
34
|
+
let audioFile = try AVAudioFile(forReading: inputURL)
|
|
35
|
+
|
|
36
|
+
var transcript = ""
|
|
37
|
+
let consumer = Task {
|
|
38
|
+
for try await result in transcriber.results where result.isFinal {
|
|
39
|
+
let piece = String(result.text.characters)
|
|
40
|
+
if emitLiveSegments {
|
|
41
|
+
msaLiveTranscriptionSendFragment(piece)
|
|
42
|
+
}
|
|
43
|
+
transcript += piece
|
|
44
|
+
}
|
|
45
|
+
return transcript
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if let last = try await analyzer.analyzeSequence(from: audioFile) {
|
|
49
|
+
try await analyzer.finalizeAndFinish(through: last)
|
|
50
|
+
} else {
|
|
51
|
+
await analyzer.cancelAndFinishNow()
|
|
52
|
+
}
|
|
53
|
+
try await consumer.value
|
|
54
|
+
|
|
55
|
+
msaLog(INFO, "Successfully transcribed text.")
|
|
56
|
+
return transcript
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/// Obtains a valid locale (e.g., en_US).
|
|
60
|
+
/// Only a subset of all available locales are supported by the SpeechAnalyzer API.
|
|
61
|
+
/// 1. If a custom locale was passed, there is an attempt to parse it.
|
|
62
|
+
/// 2. If there was no custom locale passed or the parsing failed, we try to use the system locale.
|
|
63
|
+
/// 3. If this also fails, we use the `en` locale
|
|
64
|
+
func getLocale(customLocale: String?) async -> Locale {
|
|
65
|
+
var locale: Locale
|
|
66
|
+
// if a custom locale was passed; try to parse it
|
|
67
|
+
if let setCustomLocaleString = customLocale {
|
|
68
|
+
let setCustomLocale = Locale.init(identifier: setCustomLocaleString)
|
|
69
|
+
if let supportedCustomLocale = await SpeechTranscriber.supportedLocale(equivalentTo: setCustomLocale) {
|
|
70
|
+
return supportedCustomLocale
|
|
71
|
+
} else {
|
|
72
|
+
// we should probably throw an exception here
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// if not, try to use current system locale
|
|
77
|
+
if let currentLocale = await SpeechTranscriber.supportedLocale(equivalentTo: Locale.current) {
|
|
78
|
+
locale = currentLocale
|
|
79
|
+
} else {
|
|
80
|
+
// if even this doesn't work, we simply use English
|
|
81
|
+
locale = Locale.init(languageCode: .english, script: .latin, languageRegion: .unknown)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return locale
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/// Checks the availability of the selected locale, and downloads it if necessary.
|
|
88
|
+
func downloadLocale(_ locale: Locale, _ transcriber: SpeechTranscriber) async throws {
|
|
89
|
+
if !(await SpeechTranscriber.installedLocales).contains(locale) {
|
|
90
|
+
msaLog(INFO, "Downloading locale \(locale)")
|
|
91
|
+
if let request = try await AssetInventory.assetInstallationRequest(supporting: [transcriber]) {
|
|
92
|
+
try await request.downloadAndInstall()
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
version = 1
|
|
2
|
+
revision = 3
|
|
3
|
+
requires-python = ">=3.10"
|
|
4
|
+
|
|
5
|
+
[[package]]
|
|
6
|
+
name = "cmake"
|
|
7
|
+
version = "4.4.0"
|
|
8
|
+
source = { registry = "https://pypi.org/simple" }
|
|
9
|
+
sdist = { url = "https://files.pythonhosted.org/packages/ca/4c/fec299c7e437299ac354b7907bdcedfb85860afce76471ed0fcc6879166d/cmake-4.4.0.tar.gz", hash = "sha256:933eb5977ecc5226e47d702d1c412b69f77ca00f59a4660c80b436cf7d5472de", size = 34915, upload-time = "2026-07-10T20:50:32.879Z" }
|
|
10
|
+
wheels = [
|
|
11
|
+
{ url = "https://files.pythonhosted.org/packages/50/21/9e97957b80e548f44277753f369bfcb9a997333380d5adb9f4dcf8cc6790/cmake-4.4.0-py3-none-macosx_10_10_universal2.whl", hash = "sha256:3068d219f21dde2c757fc65bf6fa1f11e892ddba1a00f2bc87276529d9c95f9a", size = 54381909, upload-time = "2026-07-10T20:48:51.167Z" },
|
|
12
|
+
{ url = "https://files.pythonhosted.org/packages/a3/fc/83b3472c3c40759e29970f583a6602fb584ae0123b573de2ff44a0a52712/cmake-4.4.0-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7ce803007cbebd3440eb0c52cec6b28a172b5cd7737ceaf613bc566880c1d0a4", size = 30277284, upload-time = "2026-07-10T20:48:57.604Z" },
|
|
13
|
+
{ url = "https://files.pythonhosted.org/packages/3d/36/0c83d4682758f6e1cbc56f0aaac5b392a3c93d81518d99418aa0f2e1d805/cmake-4.4.0-py3-none-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:41021341a394406fa56d54dabe2eaa32ac7f74fde076720397872bdc3f96a19d", size = 31451960, upload-time = "2026-07-10T20:49:02.31Z" },
|
|
14
|
+
{ url = "https://files.pythonhosted.org/packages/22/fc/33224d3f8310555e26934a119a681b76e6324601e5617b88515b2aecddd5/cmake-4.4.0-py3-none-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:825ce04c2873b4d1e6185d7cf8330d38658b00d72e56a375445241d14fe21ff7", size = 31208506, upload-time = "2026-07-10T20:49:07.954Z" },
|
|
15
|
+
{ url = "https://files.pythonhosted.org/packages/fe/86/640651cd15993ce76f54bf953cc39c1f0114f5d22960301b208887c0b8d5/cmake-4.4.0-py3-none-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:4329555c90091d10c4651272c36c8fe91b7af8c25655529c8276c33461d02957", size = 29087073, upload-time = "2026-07-10T20:49:12.731Z" },
|
|
16
|
+
{ url = "https://files.pythonhosted.org/packages/f8/8d/6f62747b52bf1b48f05f1bf187ea95bfbc6338d21692223f67db2576b944/cmake-4.4.0-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:51bcb2e65c0d5c1af4a749ba40a321b7436420c7359724ab46719004f3cd2149", size = 30215347, upload-time = "2026-07-10T20:49:18.265Z" },
|
|
17
|
+
{ url = "https://files.pythonhosted.org/packages/f1/5a/370b663877b0f58083fdcf1c5490604d0d4bf7750d63169e1c49c7035a5e/cmake-4.4.0-py3-none-manylinux_2_31_armv7l.whl", hash = "sha256:a70cafba0e4f7a741b8ef41fc8a58da0963512a0f505a38256ed0a2c70ef07eb", size = 27309126, upload-time = "2026-07-10T20:49:23.161Z" },
|
|
18
|
+
{ url = "https://files.pythonhosted.org/packages/2f/63/83bf7eacccf338d939098f5f6944eed3b27b7da2c56442eb7640c2468f7d/cmake-4.4.0-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:a550aaae137eaed453eb4493ceafa135f70da0b6660cd3ef05adbf9a2e9da629", size = 27419954, upload-time = "2026-07-10T20:49:28.13Z" },
|
|
19
|
+
{ url = "https://files.pythonhosted.org/packages/5e/f6/4a9158c97cbbc73a99102228914453f71a429cc05e54193bdb94306d6679/cmake-4.4.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:43d07406792836dc27d3d17f0ef927aa1f07f9cabf19674bf3daf64998a1a4c1", size = 39458539, upload-time = "2026-07-10T20:49:34.228Z" },
|
|
20
|
+
{ url = "https://files.pythonhosted.org/packages/29/8c/86cf5eb866dabc1e19c64ea0e2445504b32633a609d54ae78537a9374d11/cmake-4.4.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:efaca203845fdb611e2709fc88e39034d9596139e7ef8c87875ff8b63728576e", size = 36091712, upload-time = "2026-07-10T20:49:40.29Z" },
|
|
21
|
+
{ url = "https://files.pythonhosted.org/packages/1e/37/b23658343f7c00ced44813dd1dd272840c733601fb8c05d99e8e7b1f6246/cmake-4.4.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:2df0e65b29fd68067ace39a5405ce334a6f3a738ae59f9ca50f79bee178d1ae0", size = 42227957, upload-time = "2026-07-10T20:49:46.901Z" },
|
|
22
|
+
{ url = "https://files.pythonhosted.org/packages/f2/b4/f197a34feaee62b5ffd0091cce8bb89b46420ae322fd0ba503829b08f1a0/cmake-4.4.0-py3-none-musllinux_1_2_ppc64le.whl", hash = "sha256:3e9260372bbdc68f4b575548ca92ba02c459ed6acfc1903fae49d8945afe529b", size = 41381016, upload-time = "2026-07-10T20:49:53.213Z" },
|
|
23
|
+
{ url = "https://files.pythonhosted.org/packages/97/d9/6deaf3453ce1c60d72cdfea8b3fe05cf367578f5610ed96d5548e3019970/cmake-4.4.0-py3-none-musllinux_1_2_riscv64.whl", hash = "sha256:fc30f2cffc29951ba996db0f73a5f6cb4e4ef96be983199a10c2eeb25c4f151e", size = 36306195, upload-time = "2026-07-10T20:49:59.07Z" },
|
|
24
|
+
{ url = "https://files.pythonhosted.org/packages/8b/0f/2c397d6712fac6ed892d2614ed08947b78714aeca53ccb6c58b62730304c/cmake-4.4.0-py3-none-musllinux_1_2_s390x.whl", hash = "sha256:66271dacfa14a1a6abd845b65ae76dc14491716c3fdaa499745e6d16f41712c8", size = 38652910, upload-time = "2026-07-10T20:50:05.141Z" },
|
|
25
|
+
{ url = "https://files.pythonhosted.org/packages/f1/85/24cb4bf15eecfb7bf8092bccbe6263c4ff1cd22ce88077fb5131f5279d16/cmake-4.4.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:842de4296c7fb651799e6e3c1f8dc2355d3539067507f5c8012d1dc4625657b2", size = 39428686, upload-time = "2026-07-10T20:50:11.299Z" },
|
|
26
|
+
{ url = "https://files.pythonhosted.org/packages/ca/51/f2057dcfdd2d87e98a7ddd4347111165612780a9da872195f538d947e869/cmake-4.4.0-py3-none-win32.whl", hash = "sha256:8e245108da252fc67410b549f025d578d7689f99366e00d8cea1f6a1c49fc1a9", size = 38759093, upload-time = "2026-07-10T20:50:17.491Z" },
|
|
27
|
+
{ url = "https://files.pythonhosted.org/packages/6d/cb/7118b720745d76a6dca9ddf3c096fb90c71a2841b8ca493218202fc9d40a/cmake-4.4.0-py3-none-win_amd64.whl", hash = "sha256:13209a4d89d554c0b5623ed1158024b045e468bbb912c3f491de5e957b940858", size = 42309353, upload-time = "2026-07-10T20:50:23.85Z" },
|
|
28
|
+
{ url = "https://files.pythonhosted.org/packages/04/8f/2818561f47151e0ffdfe44356c160038b5ae4e9025c0a3e61e36cd685489/cmake-4.4.0-py3-none-win_arm64.whl", hash = "sha256:7d481da3868e1b5831db34573c061daa8534a584baaffac93b1348b92e493f20", size = 40473618, upload-time = "2026-07-10T20:50:30.352Z" },
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[[package]]
|
|
32
|
+
name = "exceptiongroup"
|
|
33
|
+
version = "1.3.1"
|
|
34
|
+
source = { registry = "https://pypi.org/simple" }
|
|
35
|
+
dependencies = [
|
|
36
|
+
{ name = "typing-extensions" },
|
|
37
|
+
]
|
|
38
|
+
sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" }
|
|
39
|
+
wheels = [
|
|
40
|
+
{ url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" },
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
[[package]]
|
|
44
|
+
name = "mac-speech-analyzer"
|
|
45
|
+
version = "0.1.0"
|
|
46
|
+
source = { editable = "." }
|
|
47
|
+
|
|
48
|
+
[package.dev-dependencies]
|
|
49
|
+
dev = [
|
|
50
|
+
{ name = "cmake" },
|
|
51
|
+
{ name = "ninja" },
|
|
52
|
+
{ name = "scikit-build-core" },
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
[package.metadata]
|
|
56
|
+
|
|
57
|
+
[package.metadata.requires-dev]
|
|
58
|
+
dev = [
|
|
59
|
+
{ name = "cmake", specifier = ">=4.4.0" },
|
|
60
|
+
{ name = "ninja", specifier = ">=1.13.0" },
|
|
61
|
+
{ name = "scikit-build-core", specifier = ">=1.0.3" },
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
[[package]]
|
|
65
|
+
name = "ninja"
|
|
66
|
+
version = "1.13.0"
|
|
67
|
+
source = { registry = "https://pypi.org/simple" }
|
|
68
|
+
sdist = { url = "https://files.pythonhosted.org/packages/43/73/79a0b22fc731989c708068427579e840a6cf4e937fe7ae5c5d0b7356ac22/ninja-1.13.0.tar.gz", hash = "sha256:4a40ce995ded54d9dc24f8ea37ff3bf62ad192b547f6c7126e7e25045e76f978", size = 242558, upload-time = "2025-08-11T15:10:19.421Z" }
|
|
69
|
+
wheels = [
|
|
70
|
+
{ url = "https://files.pythonhosted.org/packages/3c/74/d02409ed2aa865e051b7edda22ad416a39d81a84980f544f8de717cab133/ninja-1.13.0-py3-none-macosx_10_9_universal2.whl", hash = "sha256:fa2a8bfc62e31b08f83127d1613d10821775a0eb334197154c4d6067b7068ff1", size = 310125, upload-time = "2025-08-11T15:09:50.971Z" },
|
|
71
|
+
{ url = "https://files.pythonhosted.org/packages/8e/de/6e1cd6b84b412ac1ef327b76f0641aeb5dcc01e9d3f9eee0286d0c34fd93/ninja-1.13.0-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3d00c692fb717fd511abeb44b8c5d00340c36938c12d6538ba989fe764e79630", size = 177467, upload-time = "2025-08-11T15:09:52.767Z" },
|
|
72
|
+
{ url = "https://files.pythonhosted.org/packages/c8/83/49320fb6e58ae3c079381e333575fdbcf1cca3506ee160a2dcce775046fa/ninja-1.13.0-py3-none-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:be7f478ff9f96a128b599a964fc60a6a87b9fa332ee1bd44fa243ac88d50291c", size = 187834, upload-time = "2025-08-11T15:09:54.115Z" },
|
|
73
|
+
{ url = "https://files.pythonhosted.org/packages/56/c7/ba22748fb59f7f896b609cd3e568d28a0a367a6d953c24c461fe04fc4433/ninja-1.13.0-py3-none-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:60056592cf495e9a6a4bea3cd178903056ecb0943e4de45a2ea825edb6dc8d3e", size = 202736, upload-time = "2025-08-11T15:09:55.745Z" },
|
|
74
|
+
{ url = "https://files.pythonhosted.org/packages/79/22/d1de07632b78ac8e6b785f41fa9aad7a978ec8c0a1bf15772def36d77aac/ninja-1.13.0-py3-none-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:1c97223cdda0417f414bf864cfb73b72d8777e57ebb279c5f6de368de0062988", size = 179034, upload-time = "2025-08-11T15:09:57.394Z" },
|
|
75
|
+
{ url = "https://files.pythonhosted.org/packages/ed/de/0e6edf44d6a04dabd0318a519125ed0415ce437ad5a1ec9b9be03d9048cf/ninja-1.13.0-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fb46acf6b93b8dd0322adc3a4945452a4e774b75b91293bafcc7b7f8e6517dfa", size = 180716, upload-time = "2025-08-11T15:09:58.696Z" },
|
|
76
|
+
{ url = "https://files.pythonhosted.org/packages/54/28/938b562f9057aaa4d6bfbeaa05e81899a47aebb3ba6751e36c027a7f5ff7/ninja-1.13.0-py3-none-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4be9c1b082d244b1ad7ef41eb8ab088aae8c109a9f3f0b3e56a252d3e00f42c1", size = 146843, upload-time = "2025-08-11T15:10:00.046Z" },
|
|
77
|
+
{ url = "https://files.pythonhosted.org/packages/2a/fb/d06a3838de4f8ab866e44ee52a797b5491df823901c54943b2adb0389fbb/ninja-1.13.0-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:6739d3352073341ad284246f81339a384eec091d9851a886dfa5b00a6d48b3e2", size = 154402, upload-time = "2025-08-11T15:10:01.657Z" },
|
|
78
|
+
{ url = "https://files.pythonhosted.org/packages/31/bf/0d7808af695ceddc763cf251b84a9892cd7f51622dc8b4c89d5012779f06/ninja-1.13.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:11be2d22027bde06f14c343f01d31446747dbb51e72d00decca2eb99be911e2f", size = 552388, upload-time = "2025-08-11T15:10:03.349Z" },
|
|
79
|
+
{ url = "https://files.pythonhosted.org/packages/9d/70/c99d0c2c809f992752453cce312848abb3b1607e56d4cd1b6cded317351a/ninja-1.13.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:aa45b4037b313c2f698bc13306239b8b93b4680eb47e287773156ac9e9304714", size = 472501, upload-time = "2025-08-11T15:10:04.735Z" },
|
|
80
|
+
{ url = "https://files.pythonhosted.org/packages/9f/43/c217b1153f0e499652f5e0766da8523ce3480f0a951039c7af115e224d55/ninja-1.13.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:5f8e1e8a1a30835eeb51db05cf5a67151ad37542f5a4af2a438e9490915e5b72", size = 638280, upload-time = "2025-08-11T15:10:06.512Z" },
|
|
81
|
+
{ url = "https://files.pythonhosted.org/packages/8c/45/9151bba2c8d0ae2b6260f71696330590de5850e5574b7b5694dce6023e20/ninja-1.13.0-py3-none-musllinux_1_2_ppc64le.whl", hash = "sha256:3d7d7779d12cb20c6d054c61b702139fd23a7a964ec8f2c823f1ab1b084150db", size = 642420, upload-time = "2025-08-11T15:10:08.35Z" },
|
|
82
|
+
{ url = "https://files.pythonhosted.org/packages/3c/fb/95752eb635bb8ad27d101d71bef15bc63049de23f299e312878fc21cb2da/ninja-1.13.0-py3-none-musllinux_1_2_riscv64.whl", hash = "sha256:d741a5e6754e0bda767e3274a0f0deeef4807f1fec6c0d7921a0244018926ae5", size = 585106, upload-time = "2025-08-11T15:10:09.818Z" },
|
|
83
|
+
{ url = "https://files.pythonhosted.org/packages/c1/31/aa56a1a286703800c0cbe39fb4e82811c277772dc8cd084f442dd8e2938a/ninja-1.13.0-py3-none-musllinux_1_2_s390x.whl", hash = "sha256:e8bad11f8a00b64137e9b315b137d8bb6cbf3086fbdc43bf1f90fd33324d2e96", size = 707138, upload-time = "2025-08-11T15:10:11.366Z" },
|
|
84
|
+
{ url = "https://files.pythonhosted.org/packages/34/6f/5f5a54a1041af945130abdb2b8529cbef0cdcbbf9bcf3f4195378319d29a/ninja-1.13.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b4f2a072db3c0f944c32793e91532d8948d20d9ab83da9c0c7c15b5768072200", size = 581758, upload-time = "2025-08-11T15:10:13.295Z" },
|
|
85
|
+
{ url = "https://files.pythonhosted.org/packages/95/97/51359c77527d45943fe7a94d00a3843b81162e6c4244b3579fe8fc54cb9c/ninja-1.13.0-py3-none-win32.whl", hash = "sha256:8cfbb80b4a53456ae8a39f90ae3d7a2129f45ea164f43fadfa15dc38c4aef1c9", size = 267201, upload-time = "2025-08-11T15:10:15.158Z" },
|
|
86
|
+
{ url = "https://files.pythonhosted.org/packages/29/45/c0adfbfb0b5895aa18cec400c535b4f7ff3e52536e0403602fc1a23f7de9/ninja-1.13.0-py3-none-win_amd64.whl", hash = "sha256:fb8ee8719f8af47fed145cced4a85f0755dd55d45b2bddaf7431fa89803c5f3e", size = 309975, upload-time = "2025-08-11T15:10:16.697Z" },
|
|
87
|
+
{ url = "https://files.pythonhosted.org/packages/df/93/a7b983643d1253bb223234b5b226e69de6cda02b76cdca7770f684b795f5/ninja-1.13.0-py3-none-win_arm64.whl", hash = "sha256:3c0b40b1f0bba764644385319028650087b4c1b18cdfa6f45cb39a3669b81aa9", size = 290806, upload-time = "2025-08-11T15:10:18.018Z" },
|
|
88
|
+
]
|
|
89
|
+
|
|
90
|
+
[[package]]
|
|
91
|
+
name = "packaging"
|
|
92
|
+
version = "26.2"
|
|
93
|
+
source = { registry = "https://pypi.org/simple" }
|
|
94
|
+
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" }
|
|
95
|
+
wheels = [
|
|
96
|
+
{ 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" },
|
|
97
|
+
]
|
|
98
|
+
|
|
99
|
+
[[package]]
|
|
100
|
+
name = "pathspec"
|
|
101
|
+
version = "1.1.1"
|
|
102
|
+
source = { registry = "https://pypi.org/simple" }
|
|
103
|
+
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" }
|
|
104
|
+
wheels = [
|
|
105
|
+
{ 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" },
|
|
106
|
+
]
|
|
107
|
+
|
|
108
|
+
[[package]]
|
|
109
|
+
name = "scikit-build-core"
|
|
110
|
+
version = "1.0.3"
|
|
111
|
+
source = { registry = "https://pypi.org/simple" }
|
|
112
|
+
dependencies = [
|
|
113
|
+
{ name = "exceptiongroup", marker = "python_full_version < '3.11'" },
|
|
114
|
+
{ name = "packaging" },
|
|
115
|
+
{ name = "pathspec" },
|
|
116
|
+
{ name = "tomli", marker = "python_full_version < '3.11'" },
|
|
117
|
+
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
|
|
118
|
+
]
|
|
119
|
+
sdist = { url = "https://files.pythonhosted.org/packages/8d/7c/0f69b0c7150ce4bcee78c199fe3b7e03a6e01578451bd5d5d1a58beb32f9/scikit_build_core-1.0.3.tar.gz", hash = "sha256:a4d7a05978ee37975c37743510c8991e2debce7ef83afb0a07c0c576fd4f16e8", size = 477539, upload-time = "2026-07-12T05:08:30.298Z" }
|
|
120
|
+
wheels = [
|
|
121
|
+
{ url = "https://files.pythonhosted.org/packages/b7/b0/7ad8fa9aebb0835b2e20afebf1483cbe175d1612fe01e85042ce9ced8755/scikit_build_core-1.0.3-py3-none-any.whl", hash = "sha256:ae95427b7d3c14a6cf8bbfd4d901f6138ab64c99e20cbe8ea7d75cd26093f085", size = 278294, upload-time = "2026-07-12T05:08:28.725Z" },
|
|
122
|
+
]
|
|
123
|
+
|
|
124
|
+
[[package]]
|
|
125
|
+
name = "tomli"
|
|
126
|
+
version = "2.4.1"
|
|
127
|
+
source = { registry = "https://pypi.org/simple" }
|
|
128
|
+
sdist = { url = "https://files.pythonhosted.org/packages/22/de/48c59722572767841493b26183a0d1cc411d54fd759c5607c4590b6563a6/tomli-2.4.1.tar.gz", hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f", size = 17543, upload-time = "2026-03-25T20:22:03.828Z" }
|
|
129
|
+
wheels = [
|
|
130
|
+
{ url = "https://files.pythonhosted.org/packages/f4/11/db3d5885d8528263d8adc260bb2d28ebf1270b96e98f0e0268d32b8d9900/tomli-2.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30", size = 154704, upload-time = "2026-03-25T20:21:10.473Z" },
|
|
131
|
+
{ url = "https://files.pythonhosted.org/packages/6d/f7/675db52c7e46064a9aa928885a9b20f4124ecb9bc2e1ce74c9106648d202/tomli-2.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a", size = 149454, upload-time = "2026-03-25T20:21:12.036Z" },
|
|
132
|
+
{ url = "https://files.pythonhosted.org/packages/61/71/81c50943cf953efa35bce7646caab3cf457a7d8c030b27cfb40d7235f9ee/tomli-2.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076", size = 237561, upload-time = "2026-03-25T20:21:13.098Z" },
|
|
133
|
+
{ url = "https://files.pythonhosted.org/packages/48/c1/f41d9cb618acccca7df82aaf682f9b49013c9397212cb9f53219e3abac37/tomli-2.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9", size = 243824, upload-time = "2026-03-25T20:21:14.569Z" },
|
|
134
|
+
{ url = "https://files.pythonhosted.org/packages/22/e4/5a816ecdd1f8ca51fb756ef684b90f2780afc52fc67f987e3c61d800a46d/tomli-2.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c", size = 242227, upload-time = "2026-03-25T20:21:15.712Z" },
|
|
135
|
+
{ url = "https://files.pythonhosted.org/packages/6b/49/2b2a0ef529aa6eec245d25f0c703e020a73955ad7edf73e7f54ddc608aa5/tomli-2.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc", size = 247859, upload-time = "2026-03-25T20:21:17.001Z" },
|
|
136
|
+
{ url = "https://files.pythonhosted.org/packages/83/bd/6c1a630eaca337e1e78c5903104f831bda934c426f9231429396ce3c3467/tomli-2.4.1-cp311-cp311-win32.whl", hash = "sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049", size = 97204, upload-time = "2026-03-25T20:21:18.079Z" },
|
|
137
|
+
{ url = "https://files.pythonhosted.org/packages/42/59/71461df1a885647e10b6bb7802d0b8e66480c61f3f43079e0dcd315b3954/tomli-2.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e", size = 108084, upload-time = "2026-03-25T20:21:18.978Z" },
|
|
138
|
+
{ url = "https://files.pythonhosted.org/packages/b8/83/dceca96142499c069475b790e7913b1044c1a4337e700751f48ed723f883/tomli-2.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece", size = 95285, upload-time = "2026-03-25T20:21:20.309Z" },
|
|
139
|
+
{ url = "https://files.pythonhosted.org/packages/c1/ba/42f134a3fe2b370f555f44b1d72feebb94debcab01676bf918d0cb70e9aa/tomli-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a", size = 155924, upload-time = "2026-03-25T20:21:21.626Z" },
|
|
140
|
+
{ url = "https://files.pythonhosted.org/packages/dc/c7/62d7a17c26487ade21c5422b646110f2162f1fcc95980ef7f63e73c68f14/tomli-2.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085", size = 150018, upload-time = "2026-03-25T20:21:23.002Z" },
|
|
141
|
+
{ url = "https://files.pythonhosted.org/packages/5c/05/79d13d7c15f13bdef410bdd49a6485b1c37d28968314eabee452c22a7fda/tomli-2.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9", size = 244948, upload-time = "2026-03-25T20:21:24.04Z" },
|
|
142
|
+
{ url = "https://files.pythonhosted.org/packages/10/90/d62ce007a1c80d0b2c93e02cab211224756240884751b94ca72df8a875ca/tomli-2.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5", size = 253341, upload-time = "2026-03-25T20:21:25.177Z" },
|
|
143
|
+
{ url = "https://files.pythonhosted.org/packages/1a/7e/caf6496d60152ad4ed09282c1885cca4eea150bfd007da84aea07bcc0a3e/tomli-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585", size = 248159, upload-time = "2026-03-25T20:21:26.364Z" },
|
|
144
|
+
{ url = "https://files.pythonhosted.org/packages/99/e7/c6f69c3120de34bbd882c6fba7975f3d7a746e9218e56ab46a1bc4b42552/tomli-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1", size = 253290, upload-time = "2026-03-25T20:21:27.46Z" },
|
|
145
|
+
{ url = "https://files.pythonhosted.org/packages/d6/2f/4a3c322f22c5c66c4b836ec58211641a4067364f5dcdd7b974b4c5da300c/tomli-2.4.1-cp312-cp312-win32.whl", hash = "sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917", size = 98141, upload-time = "2026-03-25T20:21:28.492Z" },
|
|
146
|
+
{ url = "https://files.pythonhosted.org/packages/24/22/4daacd05391b92c55759d55eaee21e1dfaea86ce5c571f10083360adf534/tomli-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9", size = 108847, upload-time = "2026-03-25T20:21:29.386Z" },
|
|
147
|
+
{ url = "https://files.pythonhosted.org/packages/68/fd/70e768887666ddd9e9f5d85129e84910f2db2796f9096aa02b721a53098d/tomli-2.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257", size = 95088, upload-time = "2026-03-25T20:21:30.677Z" },
|
|
148
|
+
{ url = "https://files.pythonhosted.org/packages/07/06/b823a7e818c756d9a7123ba2cda7d07bc2dd32835648d1a7b7b7a05d848d/tomli-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54", size = 155866, upload-time = "2026-03-25T20:21:31.65Z" },
|
|
149
|
+
{ url = "https://files.pythonhosted.org/packages/14/6f/12645cf7f08e1a20c7eb8c297c6f11d31c1b50f316a7e7e1e1de6e2e7b7e/tomli-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a", size = 149887, upload-time = "2026-03-25T20:21:33.028Z" },
|
|
150
|
+
{ url = "https://files.pythonhosted.org/packages/5c/e0/90637574e5e7212c09099c67ad349b04ec4d6020324539297b634a0192b0/tomli-2.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897", size = 243704, upload-time = "2026-03-25T20:21:34.51Z" },
|
|
151
|
+
{ url = "https://files.pythonhosted.org/packages/10/8f/d3ddb16c5a4befdf31a23307f72828686ab2096f068eaf56631e136c1fdd/tomli-2.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f", size = 251628, upload-time = "2026-03-25T20:21:36.012Z" },
|
|
152
|
+
{ url = "https://files.pythonhosted.org/packages/e3/f1/dbeeb9116715abee2485bf0a12d07a8f31af94d71608c171c45f64c0469d/tomli-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d", size = 247180, upload-time = "2026-03-25T20:21:37.136Z" },
|
|
153
|
+
{ url = "https://files.pythonhosted.org/packages/d3/74/16336ffd19ed4da28a70959f92f506233bd7cfc2332b20bdb01591e8b1d1/tomli-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5", size = 251674, upload-time = "2026-03-25T20:21:38.298Z" },
|
|
154
|
+
{ url = "https://files.pythonhosted.org/packages/16/f9/229fa3434c590ddf6c0aa9af64d3af4b752540686cace29e6281e3458469/tomli-2.4.1-cp313-cp313-win32.whl", hash = "sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd", size = 97976, upload-time = "2026-03-25T20:21:39.316Z" },
|
|
155
|
+
{ url = "https://files.pythonhosted.org/packages/6a/1e/71dfd96bcc1c775420cb8befe7a9d35f2e5b1309798f009dca17b7708c1e/tomli-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36", size = 108755, upload-time = "2026-03-25T20:21:40.248Z" },
|
|
156
|
+
{ url = "https://files.pythonhosted.org/packages/83/7a/d34f422a021d62420b78f5c538e5b102f62bea616d1d75a13f0a88acb04a/tomli-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd", size = 95265, upload-time = "2026-03-25T20:21:41.219Z" },
|
|
157
|
+
{ url = "https://files.pythonhosted.org/packages/3c/fb/9a5c8d27dbab540869f7c1f8eb0abb3244189ce780ba9cd73f3770662072/tomli-2.4.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf", size = 155726, upload-time = "2026-03-25T20:21:42.23Z" },
|
|
158
|
+
{ url = "https://files.pythonhosted.org/packages/62/05/d2f816630cc771ad836af54f5001f47a6f611d2d39535364f148b6a92d6b/tomli-2.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac", size = 149859, upload-time = "2026-03-25T20:21:43.386Z" },
|
|
159
|
+
{ url = "https://files.pythonhosted.org/packages/ce/48/66341bdb858ad9bd0ceab5a86f90eddab127cf8b046418009f2125630ecb/tomli-2.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662", size = 244713, upload-time = "2026-03-25T20:21:44.474Z" },
|
|
160
|
+
{ url = "https://files.pythonhosted.org/packages/df/6d/c5fad00d82b3c7a3ab6189bd4b10e60466f22cfe8a08a9394185c8a8111c/tomli-2.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853", size = 252084, upload-time = "2026-03-25T20:21:45.62Z" },
|
|
161
|
+
{ url = "https://files.pythonhosted.org/packages/00/71/3a69e86f3eafe8c7a59d008d245888051005bd657760e96d5fbfb0b740c2/tomli-2.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15", size = 247973, upload-time = "2026-03-25T20:21:46.937Z" },
|
|
162
|
+
{ url = "https://files.pythonhosted.org/packages/67/50/361e986652847fec4bd5e4a0208752fbe64689c603c7ae5ea7cb16b1c0ca/tomli-2.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba", size = 256223, upload-time = "2026-03-25T20:21:48.467Z" },
|
|
163
|
+
{ url = "https://files.pythonhosted.org/packages/8c/9a/b4173689a9203472e5467217e0154b00e260621caa227b6fa01feab16998/tomli-2.4.1-cp314-cp314-win32.whl", hash = "sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6", size = 98973, upload-time = "2026-03-25T20:21:49.526Z" },
|
|
164
|
+
{ url = "https://files.pythonhosted.org/packages/14/58/640ac93bf230cd27d002462c9af0d837779f8773bc03dee06b5835208214/tomli-2.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7", size = 109082, upload-time = "2026-03-25T20:21:50.506Z" },
|
|
165
|
+
{ url = "https://files.pythonhosted.org/packages/d5/2f/702d5e05b227401c1068f0d386d79a589bb12bf64c3d2c72ce0631e3bc49/tomli-2.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232", size = 96490, upload-time = "2026-03-25T20:21:51.474Z" },
|
|
166
|
+
{ url = "https://files.pythonhosted.org/packages/45/4b/b877b05c8ba62927d9865dd980e34a755de541eb65fffba52b4cc495d4d2/tomli-2.4.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4", size = 164263, upload-time = "2026-03-25T20:21:52.543Z" },
|
|
167
|
+
{ url = "https://files.pythonhosted.org/packages/24/79/6ab420d37a270b89f7195dec5448f79400d9e9c1826df982f3f8e97b24fd/tomli-2.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c", size = 160736, upload-time = "2026-03-25T20:21:53.674Z" },
|
|
168
|
+
{ url = "https://files.pythonhosted.org/packages/02/e0/3630057d8eb170310785723ed5adcdfb7d50cb7e6455f85ba8a3deed642b/tomli-2.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d", size = 270717, upload-time = "2026-03-25T20:21:55.129Z" },
|
|
169
|
+
{ url = "https://files.pythonhosted.org/packages/7a/b4/1613716072e544d1a7891f548d8f9ec6ce2faf42ca65acae01d76ea06bb0/tomli-2.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41", size = 278461, upload-time = "2026-03-25T20:21:56.228Z" },
|
|
170
|
+
{ url = "https://files.pythonhosted.org/packages/05/38/30f541baf6a3f6df77b3df16b01ba319221389e2da59427e221ef417ac0c/tomli-2.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c", size = 274855, upload-time = "2026-03-25T20:21:57.653Z" },
|
|
171
|
+
{ url = "https://files.pythonhosted.org/packages/77/a3/ec9dd4fd2c38e98de34223b995a3b34813e6bdadf86c75314c928350ed14/tomli-2.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f", size = 283144, upload-time = "2026-03-25T20:21:59.089Z" },
|
|
172
|
+
{ url = "https://files.pythonhosted.org/packages/ef/be/605a6261cac79fba2ec0c9827e986e00323a1945700969b8ee0b30d85453/tomli-2.4.1-cp314-cp314t-win32.whl", hash = "sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8", size = 108683, upload-time = "2026-03-25T20:22:00.214Z" },
|
|
173
|
+
{ url = "https://files.pythonhosted.org/packages/12/64/da524626d3b9cc40c168a13da8335fe1c51be12c0a63685cc6db7308daae/tomli-2.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26", size = 121196, upload-time = "2026-03-25T20:22:01.169Z" },
|
|
174
|
+
{ url = "https://files.pythonhosted.org/packages/5a/cd/e80b62269fc78fc36c9af5a6b89c835baa8af28ff5ad28c7028d60860320/tomli-2.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396", size = 100393, upload-time = "2026-03-25T20:22:02.137Z" },
|
|
175
|
+
{ url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583, upload-time = "2026-03-25T20:22:03.012Z" },
|
|
176
|
+
]
|
|
177
|
+
|
|
178
|
+
[[package]]
|
|
179
|
+
name = "typing-extensions"
|
|
180
|
+
version = "4.16.0"
|
|
181
|
+
source = { registry = "https://pypi.org/simple" }
|
|
182
|
+
sdist = { url = "https://files.pythonhosted.org/packages/f6/cc/6253133b5bb138fc3306cebfbda2c520f545d36b5be2c7255cc528bb45d6/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5", size = 113555, upload-time = "2026-07-02T08:40:05.92Z" }
|
|
183
|
+
wheels = [
|
|
184
|
+
{ url = "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571, upload-time = "2026-07-02T08:40:04.659Z" },
|
|
185
|
+
]
|