ezmsg-xdf 0.1__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.
- ezmsg_xdf-0.1/.github/workflows/python-publish-ezmsg-xdf.yml +26 -0
- ezmsg_xdf-0.1/.github/workflows/python-tests.yml +41 -0
- ezmsg_xdf-0.1/.gitignore +12 -0
- ezmsg_xdf-0.1/.python-version +1 -0
- ezmsg_xdf-0.1/LICENSE +21 -0
- ezmsg_xdf-0.1/PKG-INFO +18 -0
- ezmsg_xdf-0.1/README.md +3 -0
- ezmsg_xdf-0.1/docs/scripts_nbs/example_xdf_system.py +27 -0
- ezmsg_xdf-0.1/pyproject.toml +39 -0
- ezmsg_xdf-0.1/src/ezmsg/xdf/__init__.py +0 -0
- ezmsg_xdf-0.1/src/ezmsg/xdf/__version__.py +16 -0
- ezmsg_xdf-0.1/src/ezmsg/xdf/iter.py +327 -0
- ezmsg_xdf-0.1/src/ezmsg/xdf/source.py +109 -0
- ezmsg_xdf-0.1/tests/test_iter.py +3 -0
- ezmsg_xdf-0.1/uv.lock +324 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
name: Upload Python Package - ezmsg-xdf
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
build:
|
|
10
|
+
name: build and upload release to PyPI
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
environment: "release"
|
|
13
|
+
permissions:
|
|
14
|
+
id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Install uv
|
|
20
|
+
uses: astral-sh/setup-uv@v2
|
|
21
|
+
|
|
22
|
+
- name: Build Package
|
|
23
|
+
run: uv build
|
|
24
|
+
|
|
25
|
+
- name: Publish package distributions to PyPI
|
|
26
|
+
run: uv publish
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
name: Test package
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
# push:
|
|
5
|
+
# branches: [main]
|
|
6
|
+
# pull_request:
|
|
7
|
+
# branches: [main]
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
build:
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
15
|
+
os:
|
|
16
|
+
- "ubuntu-latest"
|
|
17
|
+
- "windows-latest"
|
|
18
|
+
- "macos-latest"
|
|
19
|
+
runs-on: ${{matrix.os}}
|
|
20
|
+
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v4
|
|
23
|
+
|
|
24
|
+
- name: Install uv
|
|
25
|
+
uses: astral-sh/setup-uv@v2
|
|
26
|
+
with:
|
|
27
|
+
enable-cache: true
|
|
28
|
+
cache-dependency-glob: "uv.lock"
|
|
29
|
+
|
|
30
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
31
|
+
run: uv python install ${{ matrix.python-version }}
|
|
32
|
+
|
|
33
|
+
- name: Install the project
|
|
34
|
+
run: uv sync --all-extras --dev
|
|
35
|
+
|
|
36
|
+
- name: Lint
|
|
37
|
+
run:
|
|
38
|
+
uv tool run ruff check --output-format=github src
|
|
39
|
+
|
|
40
|
+
- name: Run tests
|
|
41
|
+
run: uv run pytest tests
|
ezmsg_xdf-0.1/.gitignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.10
|
ezmsg_xdf-0.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 ezmsg-org
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
ezmsg_xdf-0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: ezmsg-xdf
|
|
3
|
+
Version: 0.1
|
|
4
|
+
Summary: Namespace package for ezmsg to load iterate data from xdf files
|
|
5
|
+
Author-email: Chadwick Boulay <chadwick.boulay@gmail.com>
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Requires-Python: >=3.9
|
|
8
|
+
Requires-Dist: ezmsg-lsl>=0.5.0
|
|
9
|
+
Requires-Dist: ezmsg>=3.5.0
|
|
10
|
+
Requires-Dist: numpy>=2.0.2
|
|
11
|
+
Requires-Dist: pyxdf>=1.16.8
|
|
12
|
+
Provides-Extra: test
|
|
13
|
+
Requires-Dist: pytest>=8.3.3; extra == 'test'
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
# ezmsg.xdf
|
|
17
|
+
|
|
18
|
+
ezmsg namespace package for working with XDF files.
|
ezmsg_xdf-0.1/README.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
import ezmsg.core as ez
|
|
4
|
+
from ezmsg.util.messages.key import FilterOnKey
|
|
5
|
+
from ezmsg.util.debuglog import DebugLog
|
|
6
|
+
import typer
|
|
7
|
+
|
|
8
|
+
from ezmsg.xdf.source import XDFMultiIteratorUnit
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def main(file_path: Path):
|
|
12
|
+
comps = {
|
|
13
|
+
"SOURCE": XDFMultiIteratorUnit(
|
|
14
|
+
file_path, rezero=True, select=None, self_terminating=True
|
|
15
|
+
),
|
|
16
|
+
"SELECT": FilterOnKey("BrainVision RDA"),
|
|
17
|
+
"SINK": DebugLog(),
|
|
18
|
+
}
|
|
19
|
+
conns = (
|
|
20
|
+
(comps["SOURCE"].OUTPUT_SIGNAL, comps["SELECT"].INPUT_SIGNAL),
|
|
21
|
+
(comps["SELECT"].OUTPUT_SIGNAL, comps["SINK"].INPUT),
|
|
22
|
+
)
|
|
23
|
+
ez.run(components=comps, connections=conns)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
if __name__ == "__main__":
|
|
27
|
+
typer.run(main)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "ezmsg-xdf"
|
|
3
|
+
description = "Namespace package for ezmsg to load iterate data from xdf files"
|
|
4
|
+
readme = "README.md"
|
|
5
|
+
authors = [
|
|
6
|
+
{ name = "Chadwick Boulay", email = "chadwick.boulay@gmail.com" }
|
|
7
|
+
]
|
|
8
|
+
requires-python = ">=3.9"
|
|
9
|
+
dynamic = ["version"]
|
|
10
|
+
dependencies = [
|
|
11
|
+
"ezmsg-lsl>=0.5.0",
|
|
12
|
+
"ezmsg>=3.5.0",
|
|
13
|
+
"numpy>=2.0.2",
|
|
14
|
+
"pyxdf>=1.16.8",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
[project.optional-dependencies]
|
|
18
|
+
test = [
|
|
19
|
+
"pytest>=8.3.3",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
[build-system]
|
|
23
|
+
requires = ["hatchling", "hatch-vcs"]
|
|
24
|
+
build-backend = "hatchling.build"
|
|
25
|
+
|
|
26
|
+
[tool.hatch.version]
|
|
27
|
+
source = "vcs"
|
|
28
|
+
|
|
29
|
+
[tool.hatch.build.hooks.vcs]
|
|
30
|
+
version-file = "src/ezmsg/xdf/__version__.py"
|
|
31
|
+
|
|
32
|
+
[tool.hatch.build.targets.wheel]
|
|
33
|
+
packages = ["src/ezmsg"]
|
|
34
|
+
|
|
35
|
+
[tool.uv]
|
|
36
|
+
dev-dependencies = [
|
|
37
|
+
"ruff>=0.6.8",
|
|
38
|
+
"typer>=0.12.5",
|
|
39
|
+
]
|
|
File without changes
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# file generated by setuptools_scm
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
TYPE_CHECKING = False
|
|
4
|
+
if TYPE_CHECKING:
|
|
5
|
+
from typing import Tuple, Union
|
|
6
|
+
VERSION_TUPLE = Tuple[Union[int, str], ...]
|
|
7
|
+
else:
|
|
8
|
+
VERSION_TUPLE = object
|
|
9
|
+
|
|
10
|
+
version: str
|
|
11
|
+
__version__: str
|
|
12
|
+
__version_tuple__: VERSION_TUPLE
|
|
13
|
+
version_tuple: VERSION_TUPLE
|
|
14
|
+
|
|
15
|
+
__version__ = version = '0.1'
|
|
16
|
+
__version_tuple__ = version_tuple = (0, 1)
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
from dataclasses import replace
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import queue
|
|
4
|
+
import typing
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
import numpy.typing as npt
|
|
8
|
+
import pyxdf
|
|
9
|
+
from ezmsg.lsl.util import AxisArray
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class XDFIterator:
|
|
13
|
+
def __init__(
|
|
14
|
+
self,
|
|
15
|
+
filepath: typing.Union[Path, str],
|
|
16
|
+
select: typing.Optional[
|
|
17
|
+
set[str]
|
|
18
|
+
] = None, # If set, then the iterator yields only AxisArray of selected stream(s).
|
|
19
|
+
# If None (default), then the iterator yields dicts with keys for each stream
|
|
20
|
+
chunk_dur: float = 1.0, # Attempt to chunk data into chunks of this duration.
|
|
21
|
+
start_time: typing.Optional[float] = None,
|
|
22
|
+
stop_time: typing.Optional[float] = None,
|
|
23
|
+
rezero: bool = True,
|
|
24
|
+
):
|
|
25
|
+
"""
|
|
26
|
+
An Iterator that yields chunks from an XDF.
|
|
27
|
+
A typical offline analysis might load the entire file into memory, then perform a processing step on the entire
|
|
28
|
+
recording duration, and the next step on the entire result of the first step, and so on. This might require a
|
|
29
|
+
tremendous amount of memory and, if one is not careful about memory layout, can be incredibly slow. An
|
|
30
|
+
alternative procedure is to load the file into memory a chunk at a time (see Note1), then pass that chunk
|
|
31
|
+
through the entire processing pipeline, then proceed onto the next chunk (See Note2). We create an Iterator to
|
|
32
|
+
provide our chunks.
|
|
33
|
+
> Note1: I have not written a true lazy-loader for XDF because it has not yet been necessary as the files are
|
|
34
|
+
all small. Thus, I use pyxdf.load_xdf which loads the entire raw data into memory. The processing is still
|
|
35
|
+
done chunk-by-chunk.
|
|
36
|
+
> Note2: It should be possible to start on chunk[ix+1] while chunk[ix] is still going through the pipeline.
|
|
37
|
+
Indeed, this is (optionally) how it works online. However, the overhead of setting this up for offline
|
|
38
|
+
analysis is not worth the gain, at least not at this stage.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
filepath: The path to the file to load and iterate over.
|
|
42
|
+
select: (Optional) A set of stream names to select. If None, then all streams are selected.
|
|
43
|
+
chunk_dur: The duration of each chunk in seconds.
|
|
44
|
+
start_time: Start playback at this time. If rezero is True then this is relative to the file start time.
|
|
45
|
+
If rezero is False then this is relative to the original timestamps.
|
|
46
|
+
stop_time: Truncate the playback to stop at this time. If rezero is True then this is relative to the file
|
|
47
|
+
start time. If rezero is False then this is relative to the original timestamps.
|
|
48
|
+
rezero: The absolute value of timestamps in an XDF file are useful for synchronization WITHIN file, but they
|
|
49
|
+
are absolutely meaningless outside the exact XDF file like in an ezmsg application. Thus, by default we
|
|
50
|
+
rezero the timestamps to start at t=0.0 for simplicity. However, there may be rare circumstances where
|
|
51
|
+
one wants to compare the timestamps produced by ezmsg to timestamps produced by another XDF analysis
|
|
52
|
+
tool that does not rezero. In that case, set rezero=False.
|
|
53
|
+
"""
|
|
54
|
+
if isinstance(filepath, str):
|
|
55
|
+
filepath = Path(filepath).expanduser()
|
|
56
|
+
self._filepath = filepath
|
|
57
|
+
self._select = select
|
|
58
|
+
self._chunk_dur = chunk_dur
|
|
59
|
+
self._rezero = rezero
|
|
60
|
+
self._n_chunks = 0
|
|
61
|
+
self._t0 = 0.0
|
|
62
|
+
self._chunk_ix = 0
|
|
63
|
+
self._last_time = 0.0
|
|
64
|
+
self._metadata = {}
|
|
65
|
+
self._prev_file_read_s: float = (
|
|
66
|
+
0 # File read header in seconds for previous iteration
|
|
67
|
+
)
|
|
68
|
+
self._time_range: typing.Tuple[
|
|
69
|
+
typing.Optional[float], typing.Optional[float]
|
|
70
|
+
] = (start_time, stop_time)
|
|
71
|
+
self._scan_file()
|
|
72
|
+
|
|
73
|
+
def _scan_file(self):
|
|
74
|
+
# Note: For larger datafiles we wouldn't want to load the entire thing into memory with load_xdf.
|
|
75
|
+
# Instead, get a file handle, then
|
|
76
|
+
# - Scan the file for chunk boundaries and timestamps
|
|
77
|
+
# - Maintain a list of chunk boundaries
|
|
78
|
+
# - Perform timestamp corrections (maintain corrected ts in memory or use func to correct during next pass?)
|
|
79
|
+
# - Iterator operates on original chunk-boundaries, but using corrected timestamps.
|
|
80
|
+
# However, we would need a custom file parser for that. For now, we load the relatively small
|
|
81
|
+
# file into memory simply with pyxdf.load_xdf then iterate over the items in memory
|
|
82
|
+
# at a user-defined chunk boundary (`chunk_dur`).
|
|
83
|
+
# Load xdf
|
|
84
|
+
self._streams, fileheader = pyxdf.load_xdf(
|
|
85
|
+
self._filepath,
|
|
86
|
+
select_streams=None
|
|
87
|
+
if (self._select is None or self._rezero)
|
|
88
|
+
else [{"name": _} for _ in self._select],
|
|
89
|
+
)
|
|
90
|
+
self._metadata = {}
|
|
91
|
+
self._file_read_s = 0
|
|
92
|
+
self._prev_file_read_s = 0
|
|
93
|
+
xdf_t0 = np.inf
|
|
94
|
+
xdf_tmax = 0
|
|
95
|
+
for strm in self._streams:
|
|
96
|
+
# Convert empty data to an array for easier slicing
|
|
97
|
+
if type(strm["time_series"]) is list:
|
|
98
|
+
strm["time_series"] = np.array(strm["time_series"])
|
|
99
|
+
|
|
100
|
+
# Get more digestable metadata
|
|
101
|
+
info = strm["info"]
|
|
102
|
+
new_meta = {
|
|
103
|
+
"name": info["name"][0],
|
|
104
|
+
"type": info["type"][0],
|
|
105
|
+
"channel_count": int(info["channel_count"][0]),
|
|
106
|
+
"nominal_srate": float(info["nominal_srate"][0]),
|
|
107
|
+
}
|
|
108
|
+
self._metadata[new_meta["name"]] = new_meta
|
|
109
|
+
|
|
110
|
+
# Update time range limits
|
|
111
|
+
tvec = strm["time_stamps"]
|
|
112
|
+
if len(tvec) > 0:
|
|
113
|
+
xdf_t0 = min(xdf_t0, tvec[0])
|
|
114
|
+
xdf_tmax = max(xdf_tmax, tvec[-1])
|
|
115
|
+
|
|
116
|
+
# Permanently modify streams' time stamps
|
|
117
|
+
if self._rezero:
|
|
118
|
+
for strm in self._streams:
|
|
119
|
+
strm["time_stamps"] = strm["time_stamps"] - xdf_t0
|
|
120
|
+
xdf_tmax -= xdf_t0
|
|
121
|
+
xdf_t0 = 0
|
|
122
|
+
|
|
123
|
+
# Adjust for provided time bounds
|
|
124
|
+
for strm in self._streams:
|
|
125
|
+
tvec = strm["time_stamps"]
|
|
126
|
+
if len(tvec) > 0:
|
|
127
|
+
b_keep = np.ones(len(tvec), dtype=bool)
|
|
128
|
+
if self._time_range[0] is not None:
|
|
129
|
+
b_keep = np.logical_and(b_keep, tvec >= self._time_range[0])
|
|
130
|
+
if self._time_range[1] is not None:
|
|
131
|
+
b_keep = np.logical_and(b_keep, tvec <= self._time_range[1])
|
|
132
|
+
if np.any(~b_keep):
|
|
133
|
+
strm["time_stamps"] = tvec[b_keep]
|
|
134
|
+
strm["timeseries"] = strm["timeseries"][b_keep]
|
|
135
|
+
|
|
136
|
+
# Recalculate tmax
|
|
137
|
+
xdf_dur = 0
|
|
138
|
+
for strm in self._streams:
|
|
139
|
+
tvec = strm["time_stamps"]
|
|
140
|
+
srate = float(strm["info"]["nominal_srate"][0])
|
|
141
|
+
adj = (1 / srate if srate > 0 else 0) - xdf_t0
|
|
142
|
+
if len(tvec) > 0:
|
|
143
|
+
xdf_dur = max(xdf_dur, tvec[-1] + adj)
|
|
144
|
+
|
|
145
|
+
# Chunking
|
|
146
|
+
self._n_chunks = int(np.ceil(xdf_dur / self._chunk_dur))
|
|
147
|
+
self._t0 = xdf_t0
|
|
148
|
+
|
|
149
|
+
# Drop streams that were not selected. (Could not drop earlier due to timestamp rezero)
|
|
150
|
+
if self._rezero and self._select is not None:
|
|
151
|
+
stream_names = [_["info"]["name"][0] for _ in self._streams]
|
|
152
|
+
self._streams = [self._streams[stream_names.index(_)] for _ in self._select]
|
|
153
|
+
self._metadata = {k: self._metadata[k] for k in self._select}
|
|
154
|
+
|
|
155
|
+
print(
|
|
156
|
+
f"Imported {len(self._streams)} streams from {self._filepath} "
|
|
157
|
+
f"spanning {xdf_dur:.2f} s beginning at t={xdf_t0:.2f}."
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
@property
|
|
161
|
+
def stream_meta(self) -> typing.Union[list[dict], dict]:
|
|
162
|
+
return self._metadata
|
|
163
|
+
|
|
164
|
+
@property
|
|
165
|
+
def n_chunks(self) -> int:
|
|
166
|
+
return self._n_chunks
|
|
167
|
+
|
|
168
|
+
def __iter__(self):
|
|
169
|
+
self._chunk_ix = 0
|
|
170
|
+
return self
|
|
171
|
+
|
|
172
|
+
def __next__(self) -> dict[str, tuple[npt.NDArray, npt.NDArray]]:
|
|
173
|
+
if self._chunk_ix >= self.n_chunks:
|
|
174
|
+
raise StopIteration
|
|
175
|
+
else:
|
|
176
|
+
out_dict = {}
|
|
177
|
+
t_start, t_stop = (
|
|
178
|
+
self._chunk_ix * self._chunk_dur + self._t0,
|
|
179
|
+
(self._chunk_ix + 1) * self._chunk_dur + self._t0,
|
|
180
|
+
)
|
|
181
|
+
for strm in self._streams:
|
|
182
|
+
b_chunk = np.logical_and(
|
|
183
|
+
strm["time_stamps"] >= t_start, strm["time_stamps"] < t_stop
|
|
184
|
+
)
|
|
185
|
+
out_tvec = strm["time_stamps"][b_chunk]
|
|
186
|
+
out_data = strm["time_series"][b_chunk]
|
|
187
|
+
out_dict[strm["info"]["name"][0]] = (out_data, out_tvec)
|
|
188
|
+
if len(out_tvec) > 0:
|
|
189
|
+
self._last_time = max(self._last_time, out_tvec[-1])
|
|
190
|
+
self._chunk_ix += 1
|
|
191
|
+
return out_dict
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def labels_from_strm(strm: dict) -> list[str]:
|
|
195
|
+
desc = strm["info"]["desc"][0]
|
|
196
|
+
if desc is not None and "channels" in desc:
|
|
197
|
+
labels = [_["label"][0] for _ in desc["channels"][0]["channel"]]
|
|
198
|
+
else:
|
|
199
|
+
n_ch = int(strm["info"]["channel_count"][0])
|
|
200
|
+
labels = [str(_ + 1) for _ in range(n_ch)]
|
|
201
|
+
return labels
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
class XDFAxisArrayIterator(XDFIterator):
|
|
205
|
+
def __init__(self, *args, select: str, **kwargs):
|
|
206
|
+
"""
|
|
207
|
+
This Iterator loads only a single stream and yields a single :obj:`AxisArray` object per chunk.
|
|
208
|
+
|
|
209
|
+
Args:
|
|
210
|
+
*args:
|
|
211
|
+
select: Unlike :obj:`XDFIterator`, this must be a single string, the name of the stream to select.
|
|
212
|
+
**kwargs:
|
|
213
|
+
"""
|
|
214
|
+
kwargs["select"] = set((select,))
|
|
215
|
+
super().__init__(*args, **kwargs)
|
|
216
|
+
_sel = [_ for _ in self._select][0]
|
|
217
|
+
fs = self._metadata[_sel]["nominal_srate"] or 1.0
|
|
218
|
+
labels = labels_from_strm(self._streams[0])
|
|
219
|
+
|
|
220
|
+
self._template = AxisArray(
|
|
221
|
+
data=np.zeros(
|
|
222
|
+
(0, len(labels)), dtype=self._streams[0]["time_series"].dtype
|
|
223
|
+
),
|
|
224
|
+
dims=["time", "ch"],
|
|
225
|
+
axes={
|
|
226
|
+
"time": AxisArray.Axis.TimeAxis(fs=fs, offset=0.0),
|
|
227
|
+
"ch": AxisArray.Axis.SpaceAxis(labels=labels),
|
|
228
|
+
},
|
|
229
|
+
key=self._streams[0]["info"]["name"][0],
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
def __next__(self) -> AxisArray:
|
|
233
|
+
result: typing.Optional[AxisArray] = None
|
|
234
|
+
chunk_dict = super().__next__()
|
|
235
|
+
# Should only be 1 in self._select. If there are more then we overwrite with the last.
|
|
236
|
+
for strm_name in self._select:
|
|
237
|
+
if strm_name in chunk_dict:
|
|
238
|
+
data, tvec = chunk_dict[strm_name]
|
|
239
|
+
result = replace(
|
|
240
|
+
self._template,
|
|
241
|
+
data=data,
|
|
242
|
+
axes={
|
|
243
|
+
**self._template.axes,
|
|
244
|
+
"time": replace(
|
|
245
|
+
self._template.axes["time"],
|
|
246
|
+
offset=tvec[0] if len(tvec) else self._last_time,
|
|
247
|
+
),
|
|
248
|
+
},
|
|
249
|
+
)
|
|
250
|
+
return result
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
class XDFMultiAxArrIterator(XDFIterator):
|
|
254
|
+
def __init__(self, *args, force_single_sample: set = set(), **kwargs):
|
|
255
|
+
"""
|
|
256
|
+
This Iterator loads multiple streams and yields a :obj:`AxisArray` object per iteration,
|
|
257
|
+
but the stream source might different between chunks.
|
|
258
|
+
|
|
259
|
+
Args:
|
|
260
|
+
*args:
|
|
261
|
+
force_single_sample: Use this to identify irregular-rate streams that might conceivably have more than one
|
|
262
|
+
event within the defined chunk_dur, for which :obj:`AxisArray` cannot represent timestamps properly.
|
|
263
|
+
**kwargs:
|
|
264
|
+
"""
|
|
265
|
+
super().__init__(*args, **kwargs)
|
|
266
|
+
self._force_single_sample = force_single_sample
|
|
267
|
+
stream_names = [_["info"]["name"][0] for _ in self._streams]
|
|
268
|
+
|
|
269
|
+
# Create template messages for each stream
|
|
270
|
+
self._templates = {}
|
|
271
|
+
for stream_name, stream_meta in self._metadata.items():
|
|
272
|
+
stream = self._streams[stream_names.index(stream_name)]
|
|
273
|
+
labels = labels_from_strm(stream)
|
|
274
|
+
fs = stream_meta["nominal_srate"]
|
|
275
|
+
self._templates[stream_name] = AxisArray(
|
|
276
|
+
data=np.zeros(
|
|
277
|
+
(0, stream_meta["channel_count"]), dtype=stream["time_series"].dtype
|
|
278
|
+
),
|
|
279
|
+
dims=["time", "ch"],
|
|
280
|
+
axes={
|
|
281
|
+
"time": AxisArray.Axis.TimeAxis(fs=fs or 1.0, offset=0.0),
|
|
282
|
+
"ch": AxisArray.Axis.SpaceAxis(labels=labels),
|
|
283
|
+
},
|
|
284
|
+
key=stream_name,
|
|
285
|
+
)
|
|
286
|
+
self._pubqueue: queue.SimpleQueue[AxisArray] = queue.SimpleQueue()
|
|
287
|
+
|
|
288
|
+
def __next__(self) -> typing.Optional[AxisArray]:
|
|
289
|
+
if self._pubqueue.empty():
|
|
290
|
+
chunk_dict = super().__next__()
|
|
291
|
+
for k, template in self._templates.items():
|
|
292
|
+
if k in chunk_dict and len(chunk_dict[k][1]) > 0:
|
|
293
|
+
data, tvec = chunk_dict[k]
|
|
294
|
+
if k in self._force_single_sample:
|
|
295
|
+
for ix, _t in enumerate(tvec):
|
|
296
|
+
self._pubqueue.put_nowait(
|
|
297
|
+
replace(
|
|
298
|
+
template,
|
|
299
|
+
data=data[ix : ix + 1],
|
|
300
|
+
axes={
|
|
301
|
+
**template.axes,
|
|
302
|
+
"time": replace(
|
|
303
|
+
template.axes["time"], offset=_t
|
|
304
|
+
),
|
|
305
|
+
},
|
|
306
|
+
)
|
|
307
|
+
)
|
|
308
|
+
else:
|
|
309
|
+
self._pubqueue.put_nowait(
|
|
310
|
+
replace(
|
|
311
|
+
template,
|
|
312
|
+
data=data,
|
|
313
|
+
axes={
|
|
314
|
+
**template.axes,
|
|
315
|
+
"time": replace(
|
|
316
|
+
template.axes["time"],
|
|
317
|
+
offset=tvec[0]
|
|
318
|
+
if len(tvec)
|
|
319
|
+
else self._last_time,
|
|
320
|
+
),
|
|
321
|
+
},
|
|
322
|
+
)
|
|
323
|
+
)
|
|
324
|
+
try:
|
|
325
|
+
return self._pubqueue.get_nowait()
|
|
326
|
+
except queue.Empty:
|
|
327
|
+
return None
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import os
|
|
3
|
+
import typing
|
|
4
|
+
from dataclasses import field
|
|
5
|
+
|
|
6
|
+
import ezmsg.core as ez
|
|
7
|
+
from ezmsg.util.generator import GenState
|
|
8
|
+
from ezmsg.util.messages.axisarray import AxisArray
|
|
9
|
+
|
|
10
|
+
from .iter import XDFAxisArrayIterator, XDFMultiAxArrIterator
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class XDFIteratorSettings(ez.Settings):
|
|
14
|
+
filepath: typing.Union[os.PathLike, str]
|
|
15
|
+
select: str
|
|
16
|
+
chunk_dur: float = 1.0
|
|
17
|
+
start_time: typing.Optional[float] = None
|
|
18
|
+
stop_time: typing.Optional[float] = None
|
|
19
|
+
rezero: bool = True
|
|
20
|
+
self_terminating: bool = False
|
|
21
|
+
"""
|
|
22
|
+
If True, the unit will raise a :obj:`ez.NormalTermination` exception when the file is exhausted.
|
|
23
|
+
Note, however, that this will terminate the pipeline even if the data published by this unit are still in transit,
|
|
24
|
+
which will lead to the pipeline output being truncated before it has finished processing the stream.
|
|
25
|
+
`self_terminating` should only be used when it is not important that the pipeline finish processing data, such
|
|
26
|
+
as during prototyping and testing.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class XDFIteratorUnit(ez.Unit):
|
|
31
|
+
STATE = GenState
|
|
32
|
+
SETTINGS = XDFIteratorSettings
|
|
33
|
+
|
|
34
|
+
OUTPUT_SIGNAL = ez.OutputStream(AxisArray)
|
|
35
|
+
OUTPUT_TERM = ez.OutputStream(typing.Any)
|
|
36
|
+
|
|
37
|
+
def initialize(self) -> None:
|
|
38
|
+
self.construct_generator()
|
|
39
|
+
|
|
40
|
+
def construct_generator(self):
|
|
41
|
+
self.STATE.gen = XDFAxisArrayIterator(
|
|
42
|
+
filepath=self.SETTINGS.filepath,
|
|
43
|
+
select=self.SETTINGS.select,
|
|
44
|
+
chunk_dur=self.SETTINGS.chunk_dur,
|
|
45
|
+
start_time=self.SETTINGS.start_time,
|
|
46
|
+
stop_time=self.SETTINGS.stop_time,
|
|
47
|
+
rezero=self.SETTINGS.rezero,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
@ez.publisher(OUTPUT_SIGNAL)
|
|
51
|
+
async def pub_chunk(self) -> typing.AsyncGenerator:
|
|
52
|
+
try:
|
|
53
|
+
while True:
|
|
54
|
+
msg = next(self.STATE.gen)
|
|
55
|
+
if msg.data.size > 0:
|
|
56
|
+
yield self.OUTPUT_SIGNAL, msg
|
|
57
|
+
else:
|
|
58
|
+
await asyncio.sleep(0)
|
|
59
|
+
except StopIteration:
|
|
60
|
+
ez.logger.debug(
|
|
61
|
+
f"File ({self.SETTINGS.filepath} :: {self.SETTINGS.select}) exhausted."
|
|
62
|
+
)
|
|
63
|
+
if self.SETTINGS.self_terminating:
|
|
64
|
+
raise ez.NormalTermination
|
|
65
|
+
yield self.OUTPUT_TERM, True
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class XDFMultiIteratorUnitSettings(XDFIteratorSettings):
|
|
69
|
+
select: typing.Optional[set[str]] = None # Override with a default
|
|
70
|
+
force_single_sample: set = field(default_factory=set)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class XDFMultiIteratorUnit(ez.Unit):
|
|
74
|
+
STATE = GenState
|
|
75
|
+
SETTINGS = XDFMultiIteratorUnitSettings
|
|
76
|
+
|
|
77
|
+
OUTPUT_SIGNAL = ez.OutputStream(AxisArray)
|
|
78
|
+
OUTPUT_TERM = ez.OutputStream(typing.Any)
|
|
79
|
+
|
|
80
|
+
def initialize(self) -> None:
|
|
81
|
+
self.construct_generator()
|
|
82
|
+
|
|
83
|
+
def construct_generator(self):
|
|
84
|
+
self.STATE.gen = XDFMultiAxArrIterator(
|
|
85
|
+
filepath=self.SETTINGS.filepath,
|
|
86
|
+
select=self.SETTINGS.select,
|
|
87
|
+
chunk_dur=self.SETTINGS.chunk_dur,
|
|
88
|
+
start_time=self.SETTINGS.start_time,
|
|
89
|
+
stop_time=self.SETTINGS.stop_time,
|
|
90
|
+
rezero=self.SETTINGS.rezero,
|
|
91
|
+
force_single_sample=self.SETTINGS.force_single_sample,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
@ez.publisher(OUTPUT_SIGNAL)
|
|
95
|
+
async def pub_multi(self) -> typing.AsyncGenerator:
|
|
96
|
+
try:
|
|
97
|
+
while True:
|
|
98
|
+
msg = next(self.STATE.gen)
|
|
99
|
+
if msg is not None:
|
|
100
|
+
yield self.OUTPUT_SIGNAL, msg
|
|
101
|
+
else:
|
|
102
|
+
await asyncio.sleep(0)
|
|
103
|
+
except StopIteration:
|
|
104
|
+
ez.logger.debug(
|
|
105
|
+
f"File ({self.SETTINGS.filepath} :: {self.SETTINGS.select}) exhausted."
|
|
106
|
+
)
|
|
107
|
+
if self.SETTINGS.self_terminating:
|
|
108
|
+
raise ez.NormalTermination
|
|
109
|
+
yield self.OUTPUT_TERM, True
|
ezmsg_xdf-0.1/uv.lock
ADDED
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
version = 1
|
|
2
|
+
requires-python = ">=3.9"
|
|
3
|
+
|
|
4
|
+
[[package]]
|
|
5
|
+
name = "click"
|
|
6
|
+
version = "8.1.7"
|
|
7
|
+
source = { registry = "https://pypi.org/simple" }
|
|
8
|
+
dependencies = [
|
|
9
|
+
{ name = "colorama", marker = "platform_system == 'Windows'" },
|
|
10
|
+
]
|
|
11
|
+
sdist = { url = "https://files.pythonhosted.org/packages/96/d3/f04c7bfcf5c1862a2a5b845c6b2b360488cf47af55dfa79c98f6a6bf98b5/click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de", size = 336121 }
|
|
12
|
+
wheels = [
|
|
13
|
+
{ url = "https://files.pythonhosted.org/packages/00/2e/d53fa4befbf2cfa713304affc7ca780ce4fc1fd8710527771b58311a3229/click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", size = 97941 },
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
[[package]]
|
|
17
|
+
name = "colorama"
|
|
18
|
+
version = "0.4.6"
|
|
19
|
+
source = { registry = "https://pypi.org/simple" }
|
|
20
|
+
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 }
|
|
21
|
+
wheels = [
|
|
22
|
+
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 },
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[[package]]
|
|
26
|
+
name = "exceptiongroup"
|
|
27
|
+
version = "1.2.2"
|
|
28
|
+
source = { registry = "https://pypi.org/simple" }
|
|
29
|
+
sdist = { url = "https://files.pythonhosted.org/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc", size = 28883 }
|
|
30
|
+
wheels = [
|
|
31
|
+
{ url = "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453 },
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[[package]]
|
|
35
|
+
name = "ezmsg"
|
|
36
|
+
version = "3.5.0"
|
|
37
|
+
source = { registry = "https://pypi.org/simple" }
|
|
38
|
+
dependencies = [
|
|
39
|
+
{ name = "typing-extensions" },
|
|
40
|
+
]
|
|
41
|
+
sdist = { url = "https://files.pythonhosted.org/packages/48/a7/7e228053869f41c9e71d7fca1111fffdb3716ec8307de1aca1b57c845d9d/ezmsg-3.5.0.tar.gz", hash = "sha256:1b08972f1231299b7148d383519374113e5b44e9eaf7bf287baa60be40aa21cd", size = 136358 }
|
|
42
|
+
wheels = [
|
|
43
|
+
{ url = "https://files.pythonhosted.org/packages/92/53/42cfc9f0c5f1dfb4715b5e5dfe6abc279c4e83814488d1d5ecd361ec527b/ezmsg-3.5.0-py3-none-any.whl", hash = "sha256:f37864d46ecf5f75de5d8a89253f8c19563ecd5fe8beeddf2b334ef000319ff9", size = 67621 },
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
[[package]]
|
|
47
|
+
name = "ezmsg-lsl"
|
|
48
|
+
version = "0.5.0"
|
|
49
|
+
source = { registry = "https://pypi.org/simple" }
|
|
50
|
+
dependencies = [
|
|
51
|
+
{ name = "ezmsg" },
|
|
52
|
+
{ name = "numpy" },
|
|
53
|
+
{ name = "pylsl" },
|
|
54
|
+
]
|
|
55
|
+
sdist = { url = "https://files.pythonhosted.org/packages/17/cc/f24edf159ebc7a4a48893f0ebc95992625bda37ebb6d8d7856535433ee07/ezmsg_lsl-0.5.0.tar.gz", hash = "sha256:fb8b2eec12675be286d99be51fe5bd8e14e2b6e2e5eb10ef71c23cb8fc9c0c17", size = 31062 }
|
|
56
|
+
wheels = [
|
|
57
|
+
{ url = "https://files.pythonhosted.org/packages/56/37/c187421926d83e00ba00e9bda23f13ae9ed40367269ebc768ce06eccaba6/ezmsg_lsl-0.5.0-py3-none-any.whl", hash = "sha256:c3d404b20bcf5d01699a8ce4122f4576e96f454116139d492041a4b2aea743d0", size = 8629 },
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
[[package]]
|
|
61
|
+
name = "ezmsg-xdf"
|
|
62
|
+
version = "0.1.dev4+g0c5d38f.d20241104"
|
|
63
|
+
source = { editable = "." }
|
|
64
|
+
dependencies = [
|
|
65
|
+
{ name = "ezmsg" },
|
|
66
|
+
{ name = "ezmsg-lsl" },
|
|
67
|
+
{ name = "numpy" },
|
|
68
|
+
{ name = "pyxdf" },
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
[package.optional-dependencies]
|
|
72
|
+
test = [
|
|
73
|
+
{ name = "pytest" },
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
[package.dev-dependencies]
|
|
77
|
+
dev = [
|
|
78
|
+
{ name = "ruff" },
|
|
79
|
+
{ name = "typer" },
|
|
80
|
+
]
|
|
81
|
+
|
|
82
|
+
[package.metadata]
|
|
83
|
+
requires-dist = [
|
|
84
|
+
{ name = "ezmsg", specifier = ">=3.5.0" },
|
|
85
|
+
{ name = "ezmsg-lsl", specifier = ">=0.5.0" },
|
|
86
|
+
{ name = "numpy", specifier = ">=2.0.2" },
|
|
87
|
+
{ name = "pytest", marker = "extra == 'test'", specifier = ">=8.3.3" },
|
|
88
|
+
{ name = "pyxdf", specifier = ">=1.16.8" },
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
[package.metadata.requires-dev]
|
|
92
|
+
dev = [
|
|
93
|
+
{ name = "ruff", specifier = ">=0.6.8" },
|
|
94
|
+
{ name = "typer", specifier = ">=0.12.5" },
|
|
95
|
+
]
|
|
96
|
+
|
|
97
|
+
[[package]]
|
|
98
|
+
name = "iniconfig"
|
|
99
|
+
version = "2.0.0"
|
|
100
|
+
source = { registry = "https://pypi.org/simple" }
|
|
101
|
+
sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 }
|
|
102
|
+
wheels = [
|
|
103
|
+
{ url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 },
|
|
104
|
+
]
|
|
105
|
+
|
|
106
|
+
[[package]]
|
|
107
|
+
name = "markdown-it-py"
|
|
108
|
+
version = "3.0.0"
|
|
109
|
+
source = { registry = "https://pypi.org/simple" }
|
|
110
|
+
dependencies = [
|
|
111
|
+
{ name = "mdurl" },
|
|
112
|
+
]
|
|
113
|
+
sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 }
|
|
114
|
+
wheels = [
|
|
115
|
+
{ url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 },
|
|
116
|
+
]
|
|
117
|
+
|
|
118
|
+
[[package]]
|
|
119
|
+
name = "mdurl"
|
|
120
|
+
version = "0.1.2"
|
|
121
|
+
source = { registry = "https://pypi.org/simple" }
|
|
122
|
+
sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 }
|
|
123
|
+
wheels = [
|
|
124
|
+
{ url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 },
|
|
125
|
+
]
|
|
126
|
+
|
|
127
|
+
[[package]]
|
|
128
|
+
name = "numpy"
|
|
129
|
+
version = "2.0.2"
|
|
130
|
+
source = { registry = "https://pypi.org/simple" }
|
|
131
|
+
sdist = { url = "https://files.pythonhosted.org/packages/a9/75/10dd1f8116a8b796cb2c737b674e02d02e80454bda953fa7e65d8c12b016/numpy-2.0.2.tar.gz", hash = "sha256:883c987dee1880e2a864ab0dc9892292582510604156762362d9326444636e78", size = 18902015 }
|
|
132
|
+
wheels = [
|
|
133
|
+
{ url = "https://files.pythonhosted.org/packages/21/91/3495b3237510f79f5d81f2508f9f13fea78ebfdf07538fc7444badda173d/numpy-2.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:51129a29dbe56f9ca83438b706e2e69a39892b5eda6cedcb6b0c9fdc9b0d3ece", size = 21165245 },
|
|
134
|
+
{ url = "https://files.pythonhosted.org/packages/05/33/26178c7d437a87082d11019292dce6d3fe6f0e9026b7b2309cbf3e489b1d/numpy-2.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f15975dfec0cf2239224d80e32c3170b1d168335eaedee69da84fbe9f1f9cd04", size = 13738540 },
|
|
135
|
+
{ url = "https://files.pythonhosted.org/packages/ec/31/cc46e13bf07644efc7a4bf68df2df5fb2a1a88d0cd0da9ddc84dc0033e51/numpy-2.0.2-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:8c5713284ce4e282544c68d1c3b2c7161d38c256d2eefc93c1d683cf47683e66", size = 5300623 },
|
|
136
|
+
{ url = "https://files.pythonhosted.org/packages/6e/16/7bfcebf27bb4f9d7ec67332ffebee4d1bf085c84246552d52dbb548600e7/numpy-2.0.2-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:becfae3ddd30736fe1889a37f1f580e245ba79a5855bff5f2a29cb3ccc22dd7b", size = 6901774 },
|
|
137
|
+
{ url = "https://files.pythonhosted.org/packages/f9/a3/561c531c0e8bf082c5bef509d00d56f82e0ea7e1e3e3a7fc8fa78742a6e5/numpy-2.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2da5960c3cf0df7eafefd806d4e612c5e19358de82cb3c343631188991566ccd", size = 13907081 },
|
|
138
|
+
{ url = "https://files.pythonhosted.org/packages/fa/66/f7177ab331876200ac7563a580140643d1179c8b4b6a6b0fc9838de2a9b8/numpy-2.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:496f71341824ed9f3d2fd36cf3ac57ae2e0165c143b55c3a035ee219413f3318", size = 19523451 },
|
|
139
|
+
{ url = "https://files.pythonhosted.org/packages/25/7f/0b209498009ad6453e4efc2c65bcdf0ae08a182b2b7877d7ab38a92dc542/numpy-2.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a61ec659f68ae254e4d237816e33171497e978140353c0c2038d46e63282d0c8", size = 19927572 },
|
|
140
|
+
{ url = "https://files.pythonhosted.org/packages/3e/df/2619393b1e1b565cd2d4c4403bdd979621e2c4dea1f8532754b2598ed63b/numpy-2.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d731a1c6116ba289c1e9ee714b08a8ff882944d4ad631fd411106a30f083c326", size = 14400722 },
|
|
141
|
+
{ url = "https://files.pythonhosted.org/packages/22/ad/77e921b9f256d5da36424ffb711ae79ca3f451ff8489eeca544d0701d74a/numpy-2.0.2-cp310-cp310-win32.whl", hash = "sha256:984d96121c9f9616cd33fbd0618b7f08e0cfc9600a7ee1d6fd9b239186d19d97", size = 6472170 },
|
|
142
|
+
{ url = "https://files.pythonhosted.org/packages/10/05/3442317535028bc29cf0c0dd4c191a4481e8376e9f0db6bcf29703cadae6/numpy-2.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:c7b0be4ef08607dd04da4092faee0b86607f111d5ae68036f16cc787e250a131", size = 15905558 },
|
|
143
|
+
{ url = "https://files.pythonhosted.org/packages/8b/cf/034500fb83041aa0286e0fb16e7c76e5c8b67c0711bb6e9e9737a717d5fe/numpy-2.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:49ca4decb342d66018b01932139c0961a8f9ddc7589611158cb3c27cbcf76448", size = 21169137 },
|
|
144
|
+
{ url = "https://files.pythonhosted.org/packages/4a/d9/32de45561811a4b87fbdee23b5797394e3d1504b4a7cf40c10199848893e/numpy-2.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:11a76c372d1d37437857280aa142086476136a8c0f373b2e648ab2c8f18fb195", size = 13703552 },
|
|
145
|
+
{ url = "https://files.pythonhosted.org/packages/c1/ca/2f384720020c7b244d22508cb7ab23d95f179fcfff33c31a6eeba8d6c512/numpy-2.0.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:807ec44583fd708a21d4a11d94aedf2f4f3c3719035c76a2bbe1fe8e217bdc57", size = 5298957 },
|
|
146
|
+
{ url = "https://files.pythonhosted.org/packages/0e/78/a3e4f9fb6aa4e6fdca0c5428e8ba039408514388cf62d89651aade838269/numpy-2.0.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8cafab480740e22f8d833acefed5cc87ce276f4ece12fdaa2e8903db2f82897a", size = 6905573 },
|
|
147
|
+
{ url = "https://files.pythonhosted.org/packages/a0/72/cfc3a1beb2caf4efc9d0b38a15fe34025230da27e1c08cc2eb9bfb1c7231/numpy-2.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a15f476a45e6e5a3a79d8a14e62161d27ad897381fecfa4a09ed5322f2085669", size = 13914330 },
|
|
148
|
+
{ url = "https://files.pythonhosted.org/packages/ba/a8/c17acf65a931ce551fee11b72e8de63bf7e8a6f0e21add4c937c83563538/numpy-2.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13e689d772146140a252c3a28501da66dfecd77490b498b168b501835041f951", size = 19534895 },
|
|
149
|
+
{ url = "https://files.pythonhosted.org/packages/ba/86/8767f3d54f6ae0165749f84648da9dcc8cd78ab65d415494962c86fac80f/numpy-2.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9ea91dfb7c3d1c56a0e55657c0afb38cf1eeae4544c208dc465c3c9f3a7c09f9", size = 19937253 },
|
|
150
|
+
{ url = "https://files.pythonhosted.org/packages/df/87/f76450e6e1c14e5bb1eae6836478b1028e096fd02e85c1c37674606ab752/numpy-2.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c1c9307701fec8f3f7a1e6711f9089c06e6284b3afbbcd259f7791282d660a15", size = 14414074 },
|
|
151
|
+
{ url = "https://files.pythonhosted.org/packages/5c/ca/0f0f328e1e59f73754f06e1adfb909de43726d4f24c6a3f8805f34f2b0fa/numpy-2.0.2-cp311-cp311-win32.whl", hash = "sha256:a392a68bd329eafac5817e5aefeb39038c48b671afd242710b451e76090e81f4", size = 6470640 },
|
|
152
|
+
{ url = "https://files.pythonhosted.org/packages/eb/57/3a3f14d3a759dcf9bf6e9eda905794726b758819df4663f217d658a58695/numpy-2.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:286cd40ce2b7d652a6f22efdfc6d1edf879440e53e76a75955bc0c826c7e64dc", size = 15910230 },
|
|
153
|
+
{ url = "https://files.pythonhosted.org/packages/45/40/2e117be60ec50d98fa08c2f8c48e09b3edea93cfcabd5a9ff6925d54b1c2/numpy-2.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:df55d490dea7934f330006d0f81e8551ba6010a5bf035a249ef61a94f21c500b", size = 20895803 },
|
|
154
|
+
{ url = "https://files.pythonhosted.org/packages/46/92/1b8b8dee833f53cef3e0a3f69b2374467789e0bb7399689582314df02651/numpy-2.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8df823f570d9adf0978347d1f926b2a867d5608f434a7cff7f7908c6570dcf5e", size = 13471835 },
|
|
155
|
+
{ url = "https://files.pythonhosted.org/packages/7f/19/e2793bde475f1edaea6945be141aef6c8b4c669b90c90a300a8954d08f0a/numpy-2.0.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:9a92ae5c14811e390f3767053ff54eaee3bf84576d99a2456391401323f4ec2c", size = 5038499 },
|
|
156
|
+
{ url = "https://files.pythonhosted.org/packages/e3/ff/ddf6dac2ff0dd50a7327bcdba45cb0264d0e96bb44d33324853f781a8f3c/numpy-2.0.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:a842d573724391493a97a62ebbb8e731f8a5dcc5d285dfc99141ca15a3302d0c", size = 6633497 },
|
|
157
|
+
{ url = "https://files.pythonhosted.org/packages/72/21/67f36eac8e2d2cd652a2e69595a54128297cdcb1ff3931cfc87838874bd4/numpy-2.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05e238064fc0610c840d1cf6a13bf63d7e391717d247f1bf0318172e759e692", size = 13621158 },
|
|
158
|
+
{ url = "https://files.pythonhosted.org/packages/39/68/e9f1126d757653496dbc096cb429014347a36b228f5a991dae2c6b6cfd40/numpy-2.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0123ffdaa88fa4ab64835dcbde75dcdf89c453c922f18dced6e27c90d1d0ec5a", size = 19236173 },
|
|
159
|
+
{ url = "https://files.pythonhosted.org/packages/d1/e9/1f5333281e4ebf483ba1c888b1d61ba7e78d7e910fdd8e6499667041cc35/numpy-2.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:96a55f64139912d61de9137f11bf39a55ec8faec288c75a54f93dfd39f7eb40c", size = 19634174 },
|
|
160
|
+
{ url = "https://files.pythonhosted.org/packages/71/af/a469674070c8d8408384e3012e064299f7a2de540738a8e414dcfd639996/numpy-2.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ec9852fb39354b5a45a80bdab5ac02dd02b15f44b3804e9f00c556bf24b4bded", size = 14099701 },
|
|
161
|
+
{ url = "https://files.pythonhosted.org/packages/d0/3d/08ea9f239d0e0e939b6ca52ad403c84a2bce1bde301a8eb4888c1c1543f1/numpy-2.0.2-cp312-cp312-win32.whl", hash = "sha256:671bec6496f83202ed2d3c8fdc486a8fc86942f2e69ff0e986140339a63bcbe5", size = 6174313 },
|
|
162
|
+
{ url = "https://files.pythonhosted.org/packages/b2/b5/4ac39baebf1fdb2e72585c8352c56d063b6126be9fc95bd2bb5ef5770c20/numpy-2.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:cfd41e13fdc257aa5778496b8caa5e856dc4896d4ccf01841daee1d96465467a", size = 15606179 },
|
|
163
|
+
{ url = "https://files.pythonhosted.org/packages/43/c1/41c8f6df3162b0c6ffd4437d729115704bd43363de0090c7f913cfbc2d89/numpy-2.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9059e10581ce4093f735ed23f3b9d283b9d517ff46009ddd485f1747eb22653c", size = 21169942 },
|
|
164
|
+
{ url = "https://files.pythonhosted.org/packages/39/bc/fd298f308dcd232b56a4031fd6ddf11c43f9917fbc937e53762f7b5a3bb1/numpy-2.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:423e89b23490805d2a5a96fe40ec507407b8ee786d66f7328be214f9679df6dd", size = 13711512 },
|
|
165
|
+
{ url = "https://files.pythonhosted.org/packages/96/ff/06d1aa3eeb1c614eda245c1ba4fb88c483bee6520d361641331872ac4b82/numpy-2.0.2-cp39-cp39-macosx_14_0_arm64.whl", hash = "sha256:2b2955fa6f11907cf7a70dab0d0755159bca87755e831e47932367fc8f2f2d0b", size = 5306976 },
|
|
166
|
+
{ url = "https://files.pythonhosted.org/packages/2d/98/121996dcfb10a6087a05e54453e28e58694a7db62c5a5a29cee14c6e047b/numpy-2.0.2-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:97032a27bd9d8988b9a97a8c4d2c9f2c15a81f61e2f21404d7e8ef00cb5be729", size = 6906494 },
|
|
167
|
+
{ url = "https://files.pythonhosted.org/packages/15/31/9dffc70da6b9bbf7968f6551967fc21156207366272c2a40b4ed6008dc9b/numpy-2.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e795a8be3ddbac43274f18588329c72939870a16cae810c2b73461c40718ab1", size = 13912596 },
|
|
168
|
+
{ url = "https://files.pythonhosted.org/packages/b9/14/78635daab4b07c0930c919d451b8bf8c164774e6a3413aed04a6d95758ce/numpy-2.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26b258c385842546006213344c50655ff1555a9338e2e5e02a0756dc3e803dd", size = 19526099 },
|
|
169
|
+
{ url = "https://files.pythonhosted.org/packages/26/4c/0eeca4614003077f68bfe7aac8b7496f04221865b3a5e7cb230c9d055afd/numpy-2.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fec9451a7789926bcf7c2b8d187292c9f93ea30284802a0ab3f5be8ab36865d", size = 19932823 },
|
|
170
|
+
{ url = "https://files.pythonhosted.org/packages/f1/46/ea25b98b13dccaebddf1a803f8c748680d972e00507cd9bc6dcdb5aa2ac1/numpy-2.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9189427407d88ff25ecf8f12469d4d39d35bee1db5d39fc5c168c6f088a6956d", size = 14404424 },
|
|
171
|
+
{ url = "https://files.pythonhosted.org/packages/c8/a6/177dd88d95ecf07e722d21008b1b40e681a929eb9e329684d449c36586b2/numpy-2.0.2-cp39-cp39-win32.whl", hash = "sha256:905d16e0c60200656500c95b6b8dca5d109e23cb24abc701d41c02d74c6b3afa", size = 6476809 },
|
|
172
|
+
{ url = "https://files.pythonhosted.org/packages/ea/2b/7fc9f4e7ae5b507c1a3a21f0f15ed03e794c1242ea8a242ac158beb56034/numpy-2.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:a3f4ab0caa7f053f6797fcd4e1e25caee367db3112ef2b6ef82d749530768c73", size = 15911314 },
|
|
173
|
+
{ url = "https://files.pythonhosted.org/packages/8f/3b/df5a870ac6a3be3a86856ce195ef42eec7ae50d2a202be1f5a4b3b340e14/numpy-2.0.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7f0a0c6f12e07fa94133c8a67404322845220c06a9e80e85999afe727f7438b8", size = 21025288 },
|
|
174
|
+
{ url = "https://files.pythonhosted.org/packages/2c/97/51af92f18d6f6f2d9ad8b482a99fb74e142d71372da5d834b3a2747a446e/numpy-2.0.2-pp39-pypy39_pp73-macosx_14_0_x86_64.whl", hash = "sha256:312950fdd060354350ed123c0e25a71327d3711584beaef30cdaa93320c392d4", size = 6762793 },
|
|
175
|
+
{ url = "https://files.pythonhosted.org/packages/12/46/de1fbd0c1b5ccaa7f9a005b66761533e2f6a3e560096682683a223631fe9/numpy-2.0.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26df23238872200f63518dd2aa984cfca675d82469535dc7162dc2ee52d9dd5c", size = 19334885 },
|
|
176
|
+
{ url = "https://files.pythonhosted.org/packages/cc/dc/d330a6faefd92b446ec0f0dfea4c3207bb1fef3c4771d19cf4543efd2c78/numpy-2.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a46288ec55ebbd58947d31d72be2c63cbf839f0a63b49cb755022310792a3385", size = 15828784 },
|
|
177
|
+
]
|
|
178
|
+
|
|
179
|
+
[[package]]
|
|
180
|
+
name = "packaging"
|
|
181
|
+
version = "24.1"
|
|
182
|
+
source = { registry = "https://pypi.org/simple" }
|
|
183
|
+
sdist = { url = "https://files.pythonhosted.org/packages/51/65/50db4dda066951078f0a96cf12f4b9ada6e4b811516bf0262c0f4f7064d4/packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002", size = 148788 }
|
|
184
|
+
wheels = [
|
|
185
|
+
{ url = "https://files.pythonhosted.org/packages/08/aa/cc0199a5f0ad350994d660967a8efb233fe0416e4639146c089643407ce6/packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124", size = 53985 },
|
|
186
|
+
]
|
|
187
|
+
|
|
188
|
+
[[package]]
|
|
189
|
+
name = "pluggy"
|
|
190
|
+
version = "1.5.0"
|
|
191
|
+
source = { registry = "https://pypi.org/simple" }
|
|
192
|
+
sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 }
|
|
193
|
+
wheels = [
|
|
194
|
+
{ url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 },
|
|
195
|
+
]
|
|
196
|
+
|
|
197
|
+
[[package]]
|
|
198
|
+
name = "pygments"
|
|
199
|
+
version = "2.18.0"
|
|
200
|
+
source = { registry = "https://pypi.org/simple" }
|
|
201
|
+
sdist = { url = "https://files.pythonhosted.org/packages/8e/62/8336eff65bcbc8e4cb5d05b55faf041285951b6e80f33e2bff2024788f31/pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199", size = 4891905 }
|
|
202
|
+
wheels = [
|
|
203
|
+
{ url = "https://files.pythonhosted.org/packages/f7/3f/01c8b82017c199075f8f788d0d906b9ffbbc5a47dc9918a945e13d5a2bda/pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a", size = 1205513 },
|
|
204
|
+
]
|
|
205
|
+
|
|
206
|
+
[[package]]
|
|
207
|
+
name = "pylsl"
|
|
208
|
+
version = "1.16.2"
|
|
209
|
+
source = { registry = "https://pypi.org/simple" }
|
|
210
|
+
wheels = [
|
|
211
|
+
{ url = "https://files.pythonhosted.org/packages/12/85/6fa5adfebde09524836fb8dbc1d9a35f990e2048708a426dc31d0603983b/pylsl-1.16.2-py2.py3-none-any.whl", hash = "sha256:c7ebf9e0d96fe6c18bd4966dc734060900fabadd3cf30f652b256db732add5d7", size = 36338 },
|
|
212
|
+
{ url = "https://files.pythonhosted.org/packages/67/59/17f0b884a4ce873360bd3f694ae66ba72da410d250c006afa35452648f6e/pylsl-1.16.2-py2.py3-none-win32.whl", hash = "sha256:6138b0856e8653bbad6bfd043a62315fffbd6e21528ee905d1f989dbb7299322", size = 283457 },
|
|
213
|
+
{ url = "https://files.pythonhosted.org/packages/c5/cc/1f0e240ffca3c993b9e817ace77b37d5f60e7a17c09788ab58b1b91d82ed/pylsl-1.16.2-py2.py3-none-win_amd64.whl", hash = "sha256:827c7b75952a83aadbd6df8dd04adff6853a75c43c17b355370153cdb7c77d9f", size = 353577 },
|
|
214
|
+
]
|
|
215
|
+
|
|
216
|
+
[[package]]
|
|
217
|
+
name = "pytest"
|
|
218
|
+
version = "8.3.3"
|
|
219
|
+
source = { registry = "https://pypi.org/simple" }
|
|
220
|
+
dependencies = [
|
|
221
|
+
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
|
222
|
+
{ name = "exceptiongroup", marker = "python_full_version < '3.11'" },
|
|
223
|
+
{ name = "iniconfig" },
|
|
224
|
+
{ name = "packaging" },
|
|
225
|
+
{ name = "pluggy" },
|
|
226
|
+
{ name = "tomli", marker = "python_full_version < '3.11'" },
|
|
227
|
+
]
|
|
228
|
+
sdist = { url = "https://files.pythonhosted.org/packages/8b/6c/62bbd536103af674e227c41a8f3dcd022d591f6eed5facb5a0f31ee33bbc/pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181", size = 1442487 }
|
|
229
|
+
wheels = [
|
|
230
|
+
{ url = "https://files.pythonhosted.org/packages/6b/77/7440a06a8ead44c7757a64362dd22df5760f9b12dc5f11b6188cd2fc27a0/pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2", size = 342341 },
|
|
231
|
+
]
|
|
232
|
+
|
|
233
|
+
[[package]]
|
|
234
|
+
name = "pyxdf"
|
|
235
|
+
version = "1.16.8"
|
|
236
|
+
source = { registry = "https://pypi.org/simple" }
|
|
237
|
+
dependencies = [
|
|
238
|
+
{ name = "numpy" },
|
|
239
|
+
]
|
|
240
|
+
sdist = { url = "https://files.pythonhosted.org/packages/fe/4a/b9cf5def63e63ed2faa08533ff8c5e38443ddc101b82ddfce584b1d071e5/pyxdf-1.16.8.tar.gz", hash = "sha256:9c75db4e42b46bd8dbd69ff7ab190d7a461f55e28e2ff8279d5136d57f7849f6", size = 21714 }
|
|
241
|
+
wheels = [
|
|
242
|
+
{ url = "https://files.pythonhosted.org/packages/c3/22/4aa399b40a10a77e2594b8bf3063443a92b25d4c8bcc00303a49405c3188/pyxdf-1.16.8-py2.py3-none-any.whl", hash = "sha256:8745e4b41aae974d7327cd35ebeeed84cc010991200078ef3dead20e1e79bf2d", size = 17108 },
|
|
243
|
+
]
|
|
244
|
+
|
|
245
|
+
[[package]]
|
|
246
|
+
name = "rich"
|
|
247
|
+
version = "13.9.4"
|
|
248
|
+
source = { registry = "https://pypi.org/simple" }
|
|
249
|
+
dependencies = [
|
|
250
|
+
{ name = "markdown-it-py" },
|
|
251
|
+
{ name = "pygments" },
|
|
252
|
+
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
|
|
253
|
+
]
|
|
254
|
+
sdist = { url = "https://files.pythonhosted.org/packages/ab/3a/0316b28d0761c6734d6bc14e770d85506c986c85ffb239e688eeaab2c2bc/rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", size = 223149 }
|
|
255
|
+
wheels = [
|
|
256
|
+
{ url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424 },
|
|
257
|
+
]
|
|
258
|
+
|
|
259
|
+
[[package]]
|
|
260
|
+
name = "ruff"
|
|
261
|
+
version = "0.7.2"
|
|
262
|
+
source = { registry = "https://pypi.org/simple" }
|
|
263
|
+
sdist = { url = "https://files.pythonhosted.org/packages/95/51/231bb3790e5b0b9fd4131f9a231d73d061b3667522e3f406fd9b63334d0e/ruff-0.7.2.tar.gz", hash = "sha256:2b14e77293380e475b4e3a7a368e14549288ed2931fce259a6f99978669e844f", size = 3210036 }
|
|
264
|
+
wheels = [
|
|
265
|
+
{ url = "https://files.pythonhosted.org/packages/5c/56/0caa2b5745d66a39aa239c01059f6918fc76ed8380033d2f44bf297d141d/ruff-0.7.2-py3-none-linux_armv6l.whl", hash = "sha256:b73f873b5f52092e63ed540adefc3c36f1f803790ecf2590e1df8bf0a9f72cb8", size = 10373973 },
|
|
266
|
+
{ url = "https://files.pythonhosted.org/packages/1a/33/cad6ff306731f335d481c50caa155b69a286d5b388e87ff234cd2a4b3557/ruff-0.7.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:5b813ef26db1015953daf476202585512afd6a6862a02cde63f3bafb53d0b2d4", size = 10171140 },
|
|
267
|
+
{ url = "https://files.pythonhosted.org/packages/97/f5/6a2ca5c9ba416226eac9cf8121a1baa6f06655431937e85f38ffcb9d0d01/ruff-0.7.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:853277dbd9675810c6826dad7a428d52a11760744508340e66bf46f8be9701d9", size = 9809333 },
|
|
268
|
+
{ url = "https://files.pythonhosted.org/packages/16/83/e3e87f13d1a1dc205713632978cd7bc287a59b08bc95780dbe359b9aefcb/ruff-0.7.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21aae53ab1490a52bf4e3bf520c10ce120987b047c494cacf4edad0ba0888da2", size = 10622987 },
|
|
269
|
+
{ url = "https://files.pythonhosted.org/packages/22/16/97ccab194480e99a2e3c77ae132b3eebfa38c2112747570c403a4a13ba3a/ruff-0.7.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ccc7e0fc6e0cb3168443eeadb6445285abaae75142ee22b2b72c27d790ab60ba", size = 10184640 },
|
|
270
|
+
{ url = "https://files.pythonhosted.org/packages/97/1b/82ff05441b036f68817296c14f24da47c591cb27acfda473ee571a5651ac/ruff-0.7.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd77877a4e43b3a98e5ef4715ba3862105e299af0c48942cc6d51ba3d97dc859", size = 11210203 },
|
|
271
|
+
{ url = "https://files.pythonhosted.org/packages/a6/96/7ecb30a7ef7f942e2d8e0287ad4c1957dddc6c5097af4978c27cfc334f97/ruff-0.7.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:e00163fb897d35523c70d71a46fbaa43bf7bf9af0f4534c53ea5b96b2e03397b", size = 11870894 },
|
|
272
|
+
{ url = "https://files.pythonhosted.org/packages/06/6a/c716bb126218227f8e604a9c484836257708a05ee3d2ebceb666ff3d3867/ruff-0.7.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f3c54b538633482dc342e9b634d91168fe8cc56b30a4b4f99287f4e339103e88", size = 11449533 },
|
|
273
|
+
{ url = "https://files.pythonhosted.org/packages/e6/2f/3a5f9f9478904e5ae9506ea699109070ead1e79aac041e872cbaad8a7458/ruff-0.7.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7b792468e9804a204be221b14257566669d1db5c00d6bb335996e5cd7004ba80", size = 12607919 },
|
|
274
|
+
{ url = "https://files.pythonhosted.org/packages/a0/57/4642e57484d80d274750dcc872ea66655bbd7e66e986fede31e1865b463d/ruff-0.7.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dba53ed84ac19ae4bfb4ea4bf0172550a2285fa27fbb13e3746f04c80f7fa088", size = 11016915 },
|
|
275
|
+
{ url = "https://files.pythonhosted.org/packages/4d/6d/59be6680abee34c22296ae3f46b2a3b91662b8b18ab0bf388b5eb1355c97/ruff-0.7.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b19fafe261bf741bca2764c14cbb4ee1819b67adb63ebc2db6401dcd652e3748", size = 10625424 },
|
|
276
|
+
{ url = "https://files.pythonhosted.org/packages/82/e7/f6a643683354c9bc7879d2f228ee0324fea66d253de49273a0814fba1927/ruff-0.7.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:28bd8220f4d8f79d590db9e2f6a0674f75ddbc3847277dd44ac1f8d30684b828", size = 10233692 },
|
|
277
|
+
{ url = "https://files.pythonhosted.org/packages/d7/48/b4e02fc835cd7ed1ee7318d9c53e48bcf6b66301f55925a7dcb920e45532/ruff-0.7.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9fd67094e77efbea932e62b5d2483006154794040abb3a5072e659096415ae1e", size = 10751825 },
|
|
278
|
+
{ url = "https://files.pythonhosted.org/packages/1e/06/6c5ee6ab7bb4cbad9e8bb9b2dd0d818c759c90c1c9e057c6ed70334b97f4/ruff-0.7.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:576305393998b7bd6c46018f8104ea3a9cb3fa7908c21d8580e3274a3b04b691", size = 11074811 },
|
|
279
|
+
{ url = "https://files.pythonhosted.org/packages/a1/16/8969304f25bcd0e4af1778342e63b715e91db8a2dbb51807acd858cba915/ruff-0.7.2-py3-none-win32.whl", hash = "sha256:fa993cfc9f0ff11187e82de874dfc3611df80852540331bc85c75809c93253a8", size = 8650268 },
|
|
280
|
+
{ url = "https://files.pythonhosted.org/packages/d9/18/c4b00d161def43fe5968e959039c8f6ce60dca762cec4a34e4e83a4210a0/ruff-0.7.2-py3-none-win_amd64.whl", hash = "sha256:dd8800cbe0254e06b8fec585e97554047fb82c894973f7ff18558eee33d1cb88", size = 9433693 },
|
|
281
|
+
{ url = "https://files.pythonhosted.org/packages/7f/7b/c920673ac01c19814dd15fc617c02301c522f3d6812ca2024f4588ed4549/ruff-0.7.2-py3-none-win_arm64.whl", hash = "sha256:bb8368cd45bba3f57bb29cbb8d64b4a33f8415d0149d2655c5c8539452ce7760", size = 8735845 },
|
|
282
|
+
]
|
|
283
|
+
|
|
284
|
+
[[package]]
|
|
285
|
+
name = "shellingham"
|
|
286
|
+
version = "1.5.4"
|
|
287
|
+
source = { registry = "https://pypi.org/simple" }
|
|
288
|
+
sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310 }
|
|
289
|
+
wheels = [
|
|
290
|
+
{ url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755 },
|
|
291
|
+
]
|
|
292
|
+
|
|
293
|
+
[[package]]
|
|
294
|
+
name = "tomli"
|
|
295
|
+
version = "2.0.2"
|
|
296
|
+
source = { registry = "https://pypi.org/simple" }
|
|
297
|
+
sdist = { url = "https://files.pythonhosted.org/packages/35/b9/de2a5c0144d7d75a57ff355c0c24054f965b2dc3036456ae03a51ea6264b/tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed", size = 16096 }
|
|
298
|
+
wheels = [
|
|
299
|
+
{ url = "https://files.pythonhosted.org/packages/cf/db/ce8eda256fa131af12e0a76d481711abe4681b6923c27efb9a255c9e4594/tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38", size = 13237 },
|
|
300
|
+
]
|
|
301
|
+
|
|
302
|
+
[[package]]
|
|
303
|
+
name = "typer"
|
|
304
|
+
version = "0.12.5"
|
|
305
|
+
source = { registry = "https://pypi.org/simple" }
|
|
306
|
+
dependencies = [
|
|
307
|
+
{ name = "click" },
|
|
308
|
+
{ name = "rich" },
|
|
309
|
+
{ name = "shellingham" },
|
|
310
|
+
{ name = "typing-extensions" },
|
|
311
|
+
]
|
|
312
|
+
sdist = { url = "https://files.pythonhosted.org/packages/c5/58/a79003b91ac2c6890fc5d90145c662fd5771c6f11447f116b63300436bc9/typer-0.12.5.tar.gz", hash = "sha256:f592f089bedcc8ec1b974125d64851029c3b1af145f04aca64d69410f0c9b722", size = 98953 }
|
|
313
|
+
wheels = [
|
|
314
|
+
{ url = "https://files.pythonhosted.org/packages/a8/2b/886d13e742e514f704c33c4caa7df0f3b89e5a25ef8db02aa9ca3d9535d5/typer-0.12.5-py3-none-any.whl", hash = "sha256:62fe4e471711b147e3365034133904df3e235698399bc4de2b36c8579298d52b", size = 47288 },
|
|
315
|
+
]
|
|
316
|
+
|
|
317
|
+
[[package]]
|
|
318
|
+
name = "typing-extensions"
|
|
319
|
+
version = "4.12.2"
|
|
320
|
+
source = { registry = "https://pypi.org/simple" }
|
|
321
|
+
sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 }
|
|
322
|
+
wheels = [
|
|
323
|
+
{ url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 },
|
|
324
|
+
]
|