Compare commits

...

10 Commits

Author SHA1 Message Date
mbsantiago
23ccf7f5d6 fix: validate raw recording metadata on load 2026-08-08 10:54:56 +01:00
mbsantiago
8f01a914d7 test: cover raw formatter round trips 2026-08-07 19:59:32 +01:00
mbsantiago
01b0166346 Merge branch 'fix/raw-format-stack' of github.com:macaodha/batdetect2 into fix/raw-format-stack 2026-08-07 19:54:18 +01:00
Santiago Martinez Balvanera
149c5b85ef Add progress reporting and multiprocessing 2026-08-07 19:49:19 +01:00
Santiago Martinez Balvanera
b8f23d8b4b Avoid np stack error for empty arrays 2026-08-07 19:49:19 +01:00
Santiago Martinez Balvanera
ffa37ebb43
Merge pull request #73 from macaodha/enhancement/smaller-bottleneck
Some checks are pending
CI / Checks (push) Waiting to run
CI / Tests (Python ${{ matrix.python-version }}) (3.10) (push) Waiting to run
CI / Tests (Python ${{ matrix.python-version }}) (3.11) (push) Waiting to run
CI / Tests (Python ${{ matrix.python-version }}) (3.12) (push) Waiting to run
Docs Pages / Build Docs (push) Waiting to run
Docs Pages / Deploy Docs (push) Blocked by required conditions
Add configurable bottleneck blocks and aggregation strategies
2026-08-07 19:44:21 +01:00
mbsantiago
ac805be169 fix: address bottleneck config review feedback 2026-08-07 19:39:50 +01:00
mbsantiago
cf49c6e3da fix: align bottleneck aggregation configuration 2026-08-07 18:33:27 +01:00
Santiago Martinez Balvanera
906f97df33 Add efficient self attention layers 2026-08-04 08:43:08 +01:00
Santiago Martinez Balvanera
575e0b3d28 Add vertical mean block 2026-07-15 16:55:29 +01:00
9 changed files with 776 additions and 214 deletions

View File

@ -25,8 +25,8 @@ dependencies = [
"soundevent[audio,geometry,plot]>=2.10.0", "soundevent[audio,geometry,plot]>=2.10.0",
"soundfile>=0.12.1", "soundfile>=0.12.1",
"tensorboard>=2.16.2", "tensorboard>=2.16.2",
"torch>=1.13.1", "torch>=2.0.0",
"torchaudio>=1.13.1", "torchaudio>=2.0.0",
"xarray>=2024.0.0", "xarray>=2024.0.0",
] ]
requires-python = ">=3.10,<3.14" requires-python = ">=3.10,<3.14"

View File

@ -70,11 +70,16 @@ __all__ = [
"FreqCoordConvUpBlock", "FreqCoordConvUpBlock",
"StandardConvUpBlock", "StandardConvUpBlock",
"SelfAttention", "SelfAttention",
"EfficientSelfAttention",
"VerticalMean",
"ConvConfig", "ConvConfig",
"EfficientSelfAttentionConfig",
"FreqCoordConvDownConfig", "FreqCoordConvDownConfig",
"StandardConvDownConfig", "StandardConvDownConfig",
"FreqCoordConvUpConfig", "FreqCoordConvUpConfig",
"StandardConvUpConfig", "StandardConvUpConfig",
"VerticalConvConfig",
"VerticalMeanConfig",
"LayerConfig", "LayerConfig",
"build_layer", "build_layer",
] ]
@ -145,8 +150,8 @@ class SelfAttentionConfig(BaseConfig):
attention_channels : int attention_channels : int
Dimensionality of the query, key, and value projections. Dimensionality of the query, key, and value projections.
temperature : float temperature : float
Scaling factor applied to the weighted values before the final Divisor applied together with ``attention_channels`` when scaling
linear projection. Defaults to ``1``. dot-product attention logits. Defaults to ``1``.
""" """
name: Literal["SelfAttention"] = "SelfAttention" name: Literal["SelfAttention"] = "SelfAttention"
@ -306,6 +311,126 @@ class SelfAttention(Block):
) )
class EfficientSelfAttentionConfig(BaseConfig):
"""Configuration for an ``EfficientSelfAttention`` block.
Attributes
----------
name : str
Discriminator field; always ``"EfficientSelfAttention"``.
attention_channels : int
Dimensionality of the query, key, and value projections.
temperature : float
Divisor applied together with ``attention_channels`` when scaling
dot-product attention logits. Defaults to ``1``.
"""
name: Literal["EfficientSelfAttention"] = "EfficientSelfAttention"
attention_channels: int
temperature: float = 1.0
class EfficientSelfAttention(Block):
"""An optimized self-attention block operating along the time axis.
Applies a scaled dot-product self-attention mechanism across the time
steps of an input feature map. This version uses a fused QKV linear
projection and PyTorch's native scaled dot-product attention (SDPA)
for optimal memory usage and execution speed.
Parameters
----------
in_channels : int
Number of input channels (features per time step).
attention_channels : int
Dimensionality of the query, key, and value projections.
temperature : float, default=1.0
Divisor applied together with ``attention_channels`` when scaling
the dot-product scores before softmax.
Attributes
----------
qkv_proj : nn.Linear
Fused linear projection for queries, keys, and values.
pro_fun : nn.Linear
Final linear projection applied to the attended values.
temperature : float
Scaling divisor used when computing attention scores.
att_dim : int
Dimensionality of the attention space (``attention_channels``).
"""
def __init__(
self,
in_channels: int,
attention_channels: int,
temperature: float = 1.0,
):
super().__init__()
self.in_channels = in_channels
self.out_channels = in_channels
self.temperature = temperature
self.att_dim = attention_channels
self.output_channels = in_channels
self.qkv_proj = nn.Linear(in_channels, 3 * attention_channels)
self.pro_fun = nn.Linear(attention_channels, in_channels)
self.scale_factor = 1.0 / (self.temperature * self.att_dim)
def forward(self, x: torch.Tensor) -> torch.Tensor:
"""Apply self-attention along the time dimension."""
x = x.squeeze(2).permute(0, 2, 1)
# Single projection pass
qkv = self.qkv_proj(x)
# Split along the last dimension into Q, K, V
query, key, value = torch.chunk(qkv, 3, dim=-1)
att = F.scaled_dot_product_attention(
query,
key,
value,
attn_mask=None,
dropout_p=0.0,
is_causal=False,
scale=self.scale_factor,
)
op = self.pro_fun(att)
return op.permute(0, 2, 1).unsqueeze(2)
def compute_attention_weights(self, x: torch.Tensor) -> torch.Tensor:
"""Return the softmax attention weight matrix.
Useful for visualising which time steps attend to which others.
"""
x = x.squeeze(2).permute(0, 2, 1)
qkv = self.qkv_proj(x)
query, key, _ = torch.chunk(qkv, 3, dim=-1)
kk_qq = torch.bmm(key, query.permute(0, 2, 1)) * self.scale_factor
att_weights = F.softmax(kk_qq, dim=1)
return att_weights
@block_registry.register(EfficientSelfAttentionConfig)
@staticmethod
def from_config(
config: EfficientSelfAttentionConfig,
input_channels: int,
input_height: int,
) -> "EfficientSelfAttention":
return EfficientSelfAttention(
in_channels=input_channels,
attention_channels=config.attention_channels,
temperature=config.temperature,
)
class ConvConfig(BaseConfig): class ConvConfig(BaseConfig):
"""Configuration for a basic ConvBlock.""" """Configuration for a basic ConvBlock."""
@ -460,6 +585,10 @@ class VerticalConv(Block):
""" """
return F.relu_(self.bn(self.conv(x))) return F.relu_(self.bn(self.conv(x)))
def get_output_height(self, input_height: int) -> int:
"""Return the collapsed output height."""
return 1
@block_registry.register(VerticalConvConfig) @block_registry.register(VerticalConvConfig)
@staticmethod @staticmethod
def from_config( def from_config(
@ -474,6 +603,94 @@ class VerticalConv(Block):
) )
class VerticalMeanConfig(BaseConfig):
"""Configuration for a ``VerticalMean`` block.
Attributes
----------
name : str
Discriminator field; always ``"VerticalMean"``.
"""
name: Literal["VerticalMean"] = "VerticalMean"
"""Discriminator field indicating the block type."""
channels: int
"""Number of output channels."""
class VerticalMean(Block):
"""Mean pooling block operating along the height dimension.
Applies a 2D mean pooling operation, followed by a 2D convolution,
followed by a batch normalization and ReLU activation.
Sequence: Mean Pool -> Conv -> BN -> ReLU.
Parameters
----------
in_channels : int
Number of channels in the input tensor.
out_channels : int
Number of output channels after the mean pooling.
input_height : int
The height (H dimension) of the input tensor. The convolutional kernel
will be sized `(1, 1)`.
"""
def __init__(
self,
in_channels: int,
out_channels: int,
input_height: int,
):
super().__init__()
self.in_channels = in_channels
self.out_channels = out_channels
self.input_height = input_height
self.conv = nn.Conv2d(
in_channels,
out_channels,
kernel_size=(1, 1),
padding=0,
)
self.batch_norm = nn.BatchNorm2d(out_channels)
def forward(self, x: torch.Tensor) -> torch.Tensor:
"""Apply avg pooling -> Conv -> BN -> ReLU.
Parameters
----------
x : torch.Tensor
Input tensor, shape `(B, C_in, H, W)`.
Returns
-------
torch.Tensor
Output tensor, shape `(B, C_out, 1, W)`.
"""
x = x.mean(dim=2, keepdim=True)
x = self.conv(x)
return F.relu(self.batch_norm(x), inplace=True)
def get_output_height(self, input_height: int) -> int:
"""Return the collapsed output height."""
return 1
@block_registry.register(VerticalMeanConfig)
@staticmethod
def from_config(
config: VerticalMeanConfig,
input_channels: int,
input_height: int,
):
return VerticalMean(
in_channels=input_channels,
out_channels=config.channels,
input_height=input_height,
)
class FreqCoordConvDownConfig(BaseConfig): class FreqCoordConvDownConfig(BaseConfig):
"""Configuration for a FreqCoordConvDownBlock.""" """Configuration for a FreqCoordConvDownBlock."""
@ -951,13 +1168,16 @@ LayerConfig = Annotated[
| FreqCoordConvUpConfig | FreqCoordConvUpConfig
| StandardConvUpConfig | StandardConvUpConfig
| SelfAttentionConfig | SelfAttentionConfig
| EfficientSelfAttentionConfig
| VerticalConvConfig
| VerticalMeanConfig
| LayerGroupConfig, | LayerGroupConfig,
Field(discriminator="name"), Field(discriminator="name"),
] ]
"""Type alias for the discriminated union of block configuration models.""" """Type alias for the discriminated union of block configuration models."""
class LayerGroup(nn.Module): class LayerGroup(Block):
"""Sequential chain of blocks that acts as a single composite block. """Sequential chain of blocks that acts as a single composite block.
Wraps multiple ``Block`` instances in an ``nn.Sequential`` container, Wraps multiple ``Block`` instances in an ``nn.Sequential`` container,

View File

@ -21,14 +21,18 @@ This module provides:
from typing import Annotated, List from typing import Annotated, List
import torch import torch
from pydantic import Field from pydantic import Field, model_validator
from torch import nn from torch import nn
from batdetect2.core.configs import BaseConfig from batdetect2.core.configs import BaseConfig
from batdetect2.models.blocks import ( from batdetect2.models.blocks import (
Block, Block,
EfficientSelfAttentionConfig,
SelfAttentionConfig, SelfAttentionConfig,
VerticalConv, VerticalConv,
VerticalConvConfig,
VerticalMean,
VerticalMeanConfig,
build_layer, build_layer,
) )
from batdetect2.models.types import BottleneckProtocol from batdetect2.models.types import BottleneckProtocol
@ -94,6 +98,7 @@ class Bottleneck(Block):
in_channels: int, in_channels: int,
out_channels: int, out_channels: int,
bottleneck_channels: int | None = None, bottleneck_channels: int | None = None,
frequency_aggregator: Block | None = None,
layers: List[torch.nn.Module] | None = None, layers: List[torch.nn.Module] | None = None,
) -> None: ) -> None:
"""Initialise the Bottleneck layer. """Initialise the Bottleneck layer.
@ -125,11 +130,14 @@ class Bottleneck(Block):
) )
self.layers = nn.ModuleList(layers or []) self.layers = nn.ModuleList(layers or [])
self.conv_vert = VerticalConv( if frequency_aggregator is None:
in_channels=in_channels, frequency_aggregator = VerticalConv(
out_channels=self.bottleneck_channels, in_channels=in_channels,
input_height=input_height, out_channels=self.bottleneck_channels,
) input_height=input_height,
)
self.conv_vert = frequency_aggregator
def forward(self, x: torch.Tensor) -> torch.Tensor: def forward(self, x: torch.Tensor) -> torch.Tensor:
"""Process the encoder's bottleneck features. """Process the encoder's bottleneck features.
@ -160,12 +168,19 @@ class Bottleneck(Block):
BottleneckLayerConfig = Annotated[ BottleneckLayerConfig = Annotated[
SelfAttentionConfig, SelfAttentionConfig | EfficientSelfAttentionConfig,
Field(discriminator="name"), Field(discriminator="name"),
] ]
"""Type alias for the discriminated union of block configs usable in the Bottleneck.""" """Type alias for the discriminated union of block configs usable in the Bottleneck."""
FrequencyAggregationLayerConfig = Annotated[
(VerticalConvConfig | VerticalMeanConfig),
Field(discriminator="name"),
]
"""Type alias for the discriminated union of block configs usable in the FrequencyAggregation."""
class BottleneckConfig(BaseConfig): class BottleneckConfig(BaseConfig):
"""Configuration for the bottleneck component. """Configuration for the bottleneck component.
@ -182,17 +197,79 @@ class BottleneckConfig(BaseConfig):
""" """
channels: int channels: int
frequency_aggregation: FrequencyAggregationLayerConfig | None = None
layers: List[BottleneckLayerConfig] = Field(default_factory=list) layers: List[BottleneckLayerConfig] = Field(default_factory=list)
@model_validator(mode="after")
def set_default_frequency_aggregation(self) -> "BottleneckConfig":
"""Default frequency aggregation to the bottleneck channel count."""
if self.frequency_aggregation is None:
self.frequency_aggregation = VerticalConvConfig(
channels=self.channels
)
return self
DEFAULT_BOTTLENECK_CONFIG: BottleneckConfig = BottleneckConfig( DEFAULT_BOTTLENECK_CONFIG: BottleneckConfig = BottleneckConfig(
channels=256, channels=256,
frequency_aggregation=VerticalConvConfig(channels=256),
layers=[ layers=[
SelfAttentionConfig(attention_channels=256), SelfAttentionConfig(attention_channels=256),
], ],
) )
def build_frequency_aggregation(
input_height: int,
in_channels: int,
config: FrequencyAggregationLayerConfig,
) -> Block:
"""Build a block for aggregating frequency information.
Parameters
----------
input_height : int
Height (number of frequency bins) of the input tensor from the
encoder. Must be positive.
in_channels : int
Number of channels in the input tensor from the encoder. Must be
positive.
config : FrequencyAggregationLayerConfig, optional
Configuration specifying the output channel count and any
additional layers. Uses ``VerticalConvConfig`` if ``None``.
Returns
-------
Block
An initialised ``VerticalConv`` module.
Raises
------
AssertionError
If any configured layer changes the height of the feature map
(bottleneck layers must preserve height so that it can be restored
by repetition).
"""
if config.name == "VerticalConv":
return VerticalConv(
in_channels=in_channels,
out_channels=config.channels,
input_height=input_height,
)
if config.name == "VerticalMean":
return VerticalMean(
in_channels=in_channels,
out_channels=config.channels,
input_height=input_height,
)
raise NotImplementedError(
f"Unknown frequency aggregation layer: {config.name}"
)
def build_bottleneck( def build_bottleneck(
input_height: int, input_height: int,
in_channels: int, in_channels: int,
@ -231,13 +308,26 @@ def build_bottleneck(
by repetition). by repetition).
""" """
config = config or DEFAULT_BOTTLENECK_CONFIG config = config or DEFAULT_BOTTLENECK_CONFIG
frequency_aggregation = config.frequency_aggregation
if frequency_aggregation is None:
raise ValueError("frequency_aggregation must be configured.")
current_channels = in_channels frequency_aggregator = build_frequency_aggregation(
current_height = input_height input_height=input_height,
in_channels=in_channels,
config=frequency_aggregation,
)
current_channels = frequency_aggregator.out_channels
current_height = frequency_aggregator.get_output_height(input_height)
assert current_height == 1, (
"Bottleneck frequency aggregation should collapse spectrogram height"
)
layers = [] layers = []
for layer_config in config.layers: for layer_config in config.layers:
previous_height = current_height
layer = build_layer( layer = build_layer(
input_height=current_height, input_height=current_height,
in_channels=current_channels, in_channels=current_channels,
@ -245,7 +335,7 @@ def build_bottleneck(
) )
current_height = layer.get_output_height(current_height) current_height = layer.get_output_height(current_height)
current_channels = layer.out_channels current_channels = layer.out_channels
assert current_height == input_height, ( assert current_height == previous_height, (
"Bottleneck layers should not change the spectrogram height" "Bottleneck layers should not change the spectrogram height"
) )
layers.append(layer) layers.append(layer)
@ -253,6 +343,7 @@ def build_bottleneck(
return Bottleneck( return Bottleneck(
input_height=input_height, input_height=input_height,
in_channels=in_channels, in_channels=in_channels,
out_channels=config.channels, out_channels=current_channels,
frequency_aggregator=frequency_aggregator,
layers=layers, layers=layers,
) )

View File

@ -204,7 +204,7 @@ class RawFormatter(OutputFormatterProtocol[ClipDetections]):
def pred_from_xr(self, dataset: xr.Dataset) -> ClipDetections: def pred_from_xr(self, dataset: xr.Dataset) -> ClipDetections:
clip_data = dataset clip_data = dataset
recording = data.Recording.model_construct( recording = data.Recording.model_validate(
json.loads(clip_data.attrs["recording"]) json.loads(clip_data.attrs["recording"])
) )

View File

@ -61,3 +61,102 @@ def test_roundtrip(
).all() ).all()
assert (recovered_prediction.features == detection.features).all() assert (recovered_prediction.features == detection.features).all()
assert recovered_prediction.geometry == detection.geometry assert recovered_prediction.geometry == detection.geometry
def test_roundtrip_recovers_recording_metadata(
sample_formatter,
create_recording,
create_clip,
sample_targets: TargetProtocol,
tmp_path: Path,
):
recording = create_recording(
tags=[data.Tag(key="source", value="test-recorder")],
duration=2,
samplerate=384_000,
time_expansion=10,
)
clip = create_clip(recording=recording, start_time=0.25, end_time=0.75)
detection = Detection(
geometry=data.BoundingBox(
coordinates=[0.3, 45_000, 0.4, 70_000],
),
detection_score=0.5,
class_scores=np.ones(len(sample_targets.class_names)),
features=np.ones(32),
)
prediction = ClipDetections(clip=clip, detections=[detection])
path = tmp_path / "predictions"
sample_formatter.save(predictions=[prediction], path=path)
recovered = sample_formatter.load(path=path)
assert len(recovered) == 1
assert recovered[0].clip.recording.model_dump(mode="json") == (
recording.model_dump(mode="json")
)
def test_roundtrip_empty_detections(
sample_formatter,
clip: data.Clip,
tmp_path: Path,
):
prediction = ClipDetections(clip=clip, detections=[])
path = tmp_path / "predictions"
sample_formatter.save(predictions=[prediction], path=path)
recovered = sample_formatter.load(path=path)
assert len(recovered) == 1
assert recovered[0].detections == []
assert recovered[0].clip.uuid == prediction.clip.uuid
assert recovered[0].clip.start_time == prediction.clip.start_time
assert recovered[0].clip.end_time == prediction.clip.end_time
def test_roundtrip_loads_with_multiprocessing(
clip: data.Clip,
sample_targets: TargetProtocol,
tmp_path: Path,
):
save_formatter = build_output_formatter(
config=RawOutputConfig(),
targets=sample_targets,
)
load_formatter = build_output_formatter(
config=RawOutputConfig(n_jobs=2),
targets=sample_targets,
)
predictions = [
ClipDetections(
clip=data.Clip(
recording=clip.recording,
start_time=index,
end_time=index + 0.5,
),
detections=[
Detection(
geometry=data.BoundingBox(
coordinates=[index, 45_000, index + 0.1, 70_000],
),
detection_score=0.5,
class_scores=np.ones(len(sample_targets.class_names)),
features=np.ones(32),
)
],
)
for index in range(2)
]
path = tmp_path / "predictions"
save_formatter.save(predictions=predictions, path=path)
recovered = load_formatter.load(path=path)
assert len(recovered) == len(predictions)
assert {item.clip.uuid for item in recovered} == {
item.clip.uuid for item in predictions
}

View File

@ -10,6 +10,8 @@ from hypothesis import strategies as st
from batdetect2 import api from batdetect2 import api
from batdetect2.detector import parameters from batdetect2.detector import parameters
from batdetect2.models.backbones import UNetBackboneConfig
from batdetect2.train import load_model_from_checkpoint
@settings(deadline=None, max_examples=5) @settings(deadline=None, max_examples=5)
@ -72,3 +74,16 @@ def test_can_import_model_without_pickle_on_test_data(
model=model_with_pickle, model=model_with_pickle,
) )
assert predictions_without_pickle == predictions_with_pickle assert predictions_without_pickle == predictions_with_pickle
def test_bundled_checkpoint_loads_with_current_config_schema() -> None:
"""Bundled checkpoints remain loadable after config schema changes."""
model, configs = load_model_from_checkpoint()
assert model.class_names
assert isinstance(configs.model.architecture, UNetBackboneConfig)
frequency_aggregation = (
configs.model.architecture.bottleneck.frequency_aggregation
)
assert frequency_aggregation is not None
assert frequency_aggregation.name

View File

@ -4,6 +4,8 @@ import torch
from batdetect2.models.blocks import ( from batdetect2.models.blocks import (
ConvBlock, ConvBlock,
ConvConfig, ConvConfig,
EfficientSelfAttention,
EfficientSelfAttentionConfig,
FreqCoordConvDownBlock, FreqCoordConvDownBlock,
FreqCoordConvDownConfig, FreqCoordConvDownConfig,
FreqCoordConvUpBlock, FreqCoordConvUpBlock,
@ -18,6 +20,8 @@ from batdetect2.models.blocks import (
StandardConvUpConfig, StandardConvUpConfig,
VerticalConv, VerticalConv,
VerticalConvConfig, VerticalConvConfig,
VerticalMean,
VerticalMeanConfig,
build_layer, build_layer,
) )
@ -99,6 +103,20 @@ def test_vertical_conv_forward_shape(dummy_input):
assert block.out_channels == out_channels assert block.out_channels == out_channels
def test_vertical_mean_forward_shape(dummy_input):
"""Test that VerticalMean collapses the height dimension to 1."""
in_channels = dummy_input.size(1)
input_height = dummy_input.size(2)
out_channels = 32
block = VerticalMean(in_channels, out_channels, input_height)
output = block(dummy_input)
assert output.shape == (2, out_channels, 1, 32)
assert block.out_channels == out_channels
assert block.get_output_height(input_height) == 1
def test_self_attention_forward_shape(dummy_bottleneck_input): def test_self_attention_forward_shape(dummy_bottleneck_input):
"""Test that SelfAttention maintains the exact shape.""" """Test that SelfAttention maintains the exact shape."""
in_channels = dummy_bottleneck_input.size(1) in_channels = dummy_bottleneck_input.size(1)
@ -113,6 +131,20 @@ def test_self_attention_forward_shape(dummy_bottleneck_input):
assert block.out_channels == in_channels assert block.out_channels == in_channels
def test_efficient_self_attention_forward_shape(dummy_bottleneck_input):
"""Test that EfficientSelfAttention maintains the exact shape."""
in_channels = dummy_bottleneck_input.size(1)
attention_channels = 32
block = EfficientSelfAttention(
in_channels=in_channels, attention_channels=attention_channels
)
output = block(dummy_bottleneck_input)
assert output.shape == dummy_bottleneck_input.shape
assert block.out_channels == in_channels
def test_self_attention_weights(dummy_bottleneck_input): def test_self_attention_weights(dummy_bottleneck_input):
"""Test that attention weights sum to 1 over the time sequence.""" """Test that attention weights sum to 1 over the time sequence."""
in_channels = dummy_bottleneck_input.size(1) in_channels = dummy_bottleneck_input.size(1)
@ -131,6 +163,51 @@ def test_self_attention_weights(dummy_bottleneck_input):
assert torch.allclose(sum_weights, torch.ones_like(sum_weights), atol=1e-5) assert torch.allclose(sum_weights, torch.ones_like(sum_weights), atol=1e-5)
def test_efficient_self_attention_matches_self_attention_with_copied_weights(
dummy_bottleneck_input,
):
"""Temporarily compare efficient and original attention outputs."""
in_channels = dummy_bottleneck_input.size(1)
attention_channels = 32
block = SelfAttention(
in_channels=in_channels,
attention_channels=attention_channels,
)
efficient_block = EfficientSelfAttention(
in_channels=in_channels,
attention_channels=attention_channels,
)
# Match the fused QKV projection to the original separate projections.
with torch.no_grad():
efficient_block.qkv_proj.weight[:attention_channels].copy_(
block.query_fun.weight
)
efficient_block.qkv_proj.bias[:attention_channels].copy_(
block.query_fun.bias
)
efficient_block.qkv_proj.weight[
attention_channels : 2 * attention_channels
].copy_(block.key_fun.weight)
efficient_block.qkv_proj.bias[
attention_channels : 2 * attention_channels
].copy_(block.key_fun.bias)
efficient_block.qkv_proj.weight[2 * attention_channels :].copy_(
block.value_fun.weight
)
efficient_block.qkv_proj.bias[2 * attention_channels :].copy_(
block.value_fun.bias
)
efficient_block.pro_fun.weight.copy_(block.pro_fun.weight)
efficient_block.pro_fun.bias.copy_(block.pro_fun.bias)
output = block(dummy_bottleneck_input)
efficient_output = efficient_block(dummy_bottleneck_input)
torch.testing.assert_close(output, efficient_output)
@pytest.mark.parametrize( @pytest.mark.parametrize(
"layer_config, expected_type", "layer_config, expected_type",
[ [
@ -140,7 +217,12 @@ def test_self_attention_weights(dummy_bottleneck_input):
(FreqCoordConvDownConfig(out_channels=32), FreqCoordConvDownBlock), (FreqCoordConvDownConfig(out_channels=32), FreqCoordConvDownBlock),
(FreqCoordConvUpConfig(out_channels=32), FreqCoordConvUpBlock), (FreqCoordConvUpConfig(out_channels=32), FreqCoordConvUpBlock),
(SelfAttentionConfig(attention_channels=32), SelfAttention), (SelfAttentionConfig(attention_channels=32), SelfAttention),
(
EfficientSelfAttentionConfig(attention_channels=32),
EfficientSelfAttention,
),
(VerticalConvConfig(channels=32), VerticalConv), (VerticalConvConfig(channels=32), VerticalConv),
(VerticalMeanConfig(channels=32), VerticalMean),
], ],
) )
def test_build_layer_factory(layer_config, expected_type): def test_build_layer_factory(layer_config, expected_type):

View File

@ -0,0 +1,55 @@
import torch
from batdetect2.models.blocks import (
SelfAttention,
SelfAttentionConfig,
VerticalMeanConfig,
)
from batdetect2.models.bottleneck import (
Bottleneck,
BottleneckConfig,
build_bottleneck,
)
def test_bottleneck_layers_use_frequency_aggregation_channels() -> None:
"""Layers after frequency aggregation are built for aggregated channels."""
config = BottleneckConfig(
channels=128,
frequency_aggregation=VerticalMeanConfig(channels=128),
layers=[SelfAttentionConfig(attention_channels=32)],
)
bottleneck = build_bottleneck(
input_height=8,
in_channels=64,
config=config,
)
assert isinstance(bottleneck, Bottleneck)
attention = bottleneck.layers[0]
assert isinstance(attention, SelfAttention)
assert attention.in_channels == 128
output = bottleneck(torch.randn(2, 64, 8, 10))
assert output.shape == (2, 128, 8, 10)
def test_bottleneck_default_frequency_aggregation_matches_channels() -> None:
"""Minimal configs keep advertised and actual output channels in sync."""
config = BottleneckConfig(channels=128, layers=[])
bottleneck = build_bottleneck(
input_height=8,
in_channels=64,
config=config,
)
assert isinstance(bottleneck, Bottleneck)
assert bottleneck.out_channels == 128
assert bottleneck.conv_vert.out_channels == 128
output = bottleneck(torch.randn(2, 64, 8, 10))
assert output.shape == (2, bottleneck.out_channels, 8, 10)

394
uv.lock generated
View File

@ -453,7 +453,7 @@ wheels = [
[[package]] [[package]]
name = "batdetect2" name = "batdetect2"
version = "2.0.0b1" version = "2.0.0b2"
source = { editable = "." } source = { editable = "." }
dependencies = [ dependencies = [
{ name = "click" }, { name = "click" },
@ -557,8 +557,8 @@ requires-dist = [
{ name = "soundevent", extras = ["audio", "geometry", "plot"], specifier = ">=2.10.0" }, { name = "soundevent", extras = ["audio", "geometry", "plot"], specifier = ">=2.10.0" },
{ name = "soundfile", specifier = ">=0.12.1" }, { name = "soundfile", specifier = ">=0.12.1" },
{ name = "tensorboard", specifier = ">=2.16.2" }, { name = "tensorboard", specifier = ">=2.16.2" },
{ name = "torch", specifier = ">=1.13.1" }, { name = "torch", specifier = ">=2.0.0" },
{ name = "torchaudio", specifier = ">=1.13.1" }, { name = "torchaudio", specifier = ">=2.0.0" },
{ name = "xarray", specifier = ">=2024.0.0" }, { name = "xarray", specifier = ">=2024.0.0" },
] ]
@ -626,10 +626,10 @@ name = "bitsandbytes"
version = "0.49.2" version = "0.49.2"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
{ name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
{ name = "packaging", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "packaging" },
{ name = "torch", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "torch" },
] ]
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/d8/7d/f1fe0992334b18cd8494f89aeec1dcc674635584fcd9f115784fea3a1d05/bitsandbytes-0.49.2-py3-none-macosx_14_0_arm64.whl", hash = "sha256:87be5975edeac5396d699ecbc39dfc47cf2c026daaf2d5852a94368611a6823f", size = 131940, upload-time = "2026-02-16T21:26:04.572Z" }, { url = "https://files.pythonhosted.org/packages/d8/7d/f1fe0992334b18cd8494f89aeec1dcc674635584fcd9f115784fea3a1d05/bitsandbytes-0.49.2-py3-none-macosx_14_0_arm64.whl", hash = "sha256:87be5975edeac5396d699ecbc39dfc47cf2c026daaf2d5852a94368611a6823f", size = 131940, upload-time = "2026-02-16T21:26:04.572Z" },
@ -1070,7 +1070,7 @@ resolution-markers = [
"python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'", "python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
] ]
dependencies = [ dependencies = [
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/66/54/eb9bfc647b19f2009dd5c7f5ec51c4e6ca831725f1aea7a993034f483147/contourpy-1.3.2.tar.gz", hash = "sha256:b6945942715a034c671b7fc54f9588126b0b8bf23db2696e3ca8328f3ff0ab54", size = 13466130, upload-time = "2025-04-15T17:47:53.79Z" } sdist = { url = "https://files.pythonhosted.org/packages/66/54/eb9bfc647b19f2009dd5c7f5ec51c4e6ca831725f1aea7a993034f483147/contourpy-1.3.2.tar.gz", hash = "sha256:b6945942715a034c671b7fc54f9588126b0b8bf23db2696e3ca8328f3ff0ab54", size = 13466130, upload-time = "2025-04-15T17:47:53.79Z" }
wheels = [ wheels = [
@ -1151,7 +1151,7 @@ resolution-markers = [
"python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'", "python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
] ]
dependencies = [ dependencies = [
{ name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" } },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880", size = 13466174, upload-time = "2025-07-26T12:03:12.549Z" } sdist = { url = "https://files.pythonhosted.org/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880", size = 13466174, upload-time = "2025-07-26T12:03:12.549Z" }
wheels = [ wheels = [
@ -1345,7 +1345,7 @@ name = "cuda-bindings"
version = "13.2.0" version = "13.2.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "cuda-pathfinder", marker = "(platform_machine != 'ARM64' and sys_platform == 'win32') or sys_platform == 'linux'" }, { name = "cuda-pathfinder" },
] ]
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/1a/fe/7351d7e586a8b4c9f89731bfe4cf0148223e8f9903ff09571f78b3fb0682/cuda_bindings-13.2.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08b395f79cb89ce0cd8effff07c4a1e20101b873c256a1aeb286e8fd7bd0f556", size = 5744254, upload-time = "2026-03-11T00:12:29.798Z" }, { url = "https://files.pythonhosted.org/packages/1a/fe/7351d7e586a8b4c9f89731bfe4cf0148223e8f9903ff09571f78b3fb0682/cuda_bindings-13.2.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08b395f79cb89ce0cd8effff07c4a1e20101b873c256a1aeb286e8fd7bd0f556", size = 5744254, upload-time = "2026-03-11T00:12:29.798Z" },
@ -1376,37 +1376,37 @@ wheels = [
[package.optional-dependencies] [package.optional-dependencies]
cublas = [ cublas = [
{ name = "nvidia-cublas", marker = "(platform_machine != 'ARM64' and sys_platform == 'win32') or sys_platform == 'linux'" }, { name = "nvidia-cublas" },
] ]
cudart = [ cudart = [
{ name = "nvidia-cuda-runtime", marker = "(platform_machine != 'ARM64' and sys_platform == 'win32') or sys_platform == 'linux'" }, { name = "nvidia-cuda-runtime" },
] ]
cufft = [ cufft = [
{ name = "nvidia-cufft", marker = "(platform_machine != 'ARM64' and sys_platform == 'win32') or sys_platform == 'linux'" }, { name = "nvidia-cufft" },
] ]
cufile = [ cufile = [
{ name = "nvidia-cufile", marker = "sys_platform == 'linux'" }, { name = "nvidia-cufile" },
] ]
cupti = [ cupti = [
{ name = "nvidia-cuda-cupti", marker = "(platform_machine != 'ARM64' and sys_platform == 'win32') or sys_platform == 'linux'" }, { name = "nvidia-cuda-cupti" },
] ]
curand = [ curand = [
{ name = "nvidia-curand", marker = "(platform_machine != 'ARM64' and sys_platform == 'win32') or sys_platform == 'linux'" }, { name = "nvidia-curand" },
] ]
cusolver = [ cusolver = [
{ name = "nvidia-cusolver", marker = "(platform_machine != 'ARM64' and sys_platform == 'win32') or sys_platform == 'linux'" }, { name = "nvidia-cusolver" },
] ]
cusparse = [ cusparse = [
{ name = "nvidia-cusparse", marker = "(platform_machine != 'ARM64' and sys_platform == 'win32') or sys_platform == 'linux'" }, { name = "nvidia-cusparse" },
] ]
nvjitlink = [ nvjitlink = [
{ name = "nvidia-nvjitlink", marker = "(platform_machine != 'ARM64' and sys_platform == 'win32') or sys_platform == 'linux'" }, { name = "nvidia-nvjitlink" },
] ]
nvrtc = [ nvrtc = [
{ name = "nvidia-cuda-nvrtc", marker = "(platform_machine != 'ARM64' and sys_platform == 'win32') or sys_platform == 'linux'" }, { name = "nvidia-cuda-nvrtc" },
] ]
nvtx = [ nvtx = [
{ name = "nvidia-nvtx", marker = "(platform_machine != 'ARM64' and sys_platform == 'win32') or sys_platform == 'linux'" }, { name = "nvidia-nvtx" },
] ]
[[package]] [[package]]
@ -1875,7 +1875,7 @@ name = "exceptiongroup"
version = "1.3.1" version = "1.3.1"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "typing-extensions", marker = "python_full_version < '3.11'" }, { name = "typing-extensions" },
] ]
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" } 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" }
wheels = [ wheels = [
@ -2417,7 +2417,7 @@ name = "gunicorn"
version = "25.3.0" version = "25.3.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "packaging", marker = "platform_machine != 'ARM64' or sys_platform != 'win32'" }, { name = "packaging" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/c4/f4/e78fa054248fab913e2eab0332c6c2cb07421fca1ce56d8fe43b6aef57a4/gunicorn-25.3.0.tar.gz", hash = "sha256:f74e1b2f9f76f6cd1ca01198968bd2dd65830edc24b6e8e4d78de8320e2fe889", size = 634883, upload-time = "2026-03-27T00:00:26.092Z" } sdist = { url = "https://files.pythonhosted.org/packages/c4/f4/e78fa054248fab913e2eab0332c6c2cb07421fca1ce56d8fe43b6aef57a4/gunicorn-25.3.0.tar.gz", hash = "sha256:f74e1b2f9f76f6cd1ca01198968bd2dd65830edc24b6e8e4d78de8320e2fe889", size = 634883, upload-time = "2026-03-27T00:00:26.092Z" }
wheels = [ wheels = [
@ -2638,17 +2638,17 @@ resolution-markers = [
"python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'", "python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
] ]
dependencies = [ dependencies = [
{ name = "colorama", marker = "python_full_version < '3.11' and sys_platform == 'win32'" }, { name = "colorama", marker = "sys_platform == 'win32'" },
{ name = "decorator", marker = "python_full_version < '3.11'" }, { name = "decorator" },
{ name = "exceptiongroup", marker = "python_full_version < '3.11'" }, { name = "exceptiongroup" },
{ name = "jedi", marker = "python_full_version < '3.11'" }, { name = "jedi" },
{ name = "matplotlib-inline", marker = "python_full_version < '3.11'" }, { name = "matplotlib-inline" },
{ name = "pexpect", marker = "python_full_version < '3.11' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, { name = "pexpect", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" },
{ name = "prompt-toolkit", marker = "python_full_version < '3.11'" }, { name = "prompt-toolkit" },
{ name = "pygments", marker = "python_full_version < '3.11'" }, { name = "pygments" },
{ name = "stack-data", marker = "python_full_version < '3.11'" }, { name = "stack-data" },
{ name = "traitlets", marker = "python_full_version < '3.11'" }, { name = "traitlets" },
{ name = "typing-extensions", marker = "python_full_version < '3.11'" }, { name = "typing-extensions" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/40/18/f8598d287006885e7136451fdea0755af4ebcbfe342836f24deefaed1164/ipython-8.39.0.tar.gz", hash = "sha256:4110ae96012c379b8b6db898a07e186c40a2a1ef5d57a7fa83166047d9da7624", size = 5513971, upload-time = "2026-03-27T10:02:13.94Z" } sdist = { url = "https://files.pythonhosted.org/packages/40/18/f8598d287006885e7136451fdea0755af4ebcbfe342836f24deefaed1164/ipython-8.39.0.tar.gz", hash = "sha256:4110ae96012c379b8b6db898a07e186c40a2a1ef5d57a7fa83166047d9da7624", size = 5513971, upload-time = "2026-03-27T10:02:13.94Z" }
wheels = [ wheels = [
@ -2674,18 +2674,18 @@ resolution-markers = [
"python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'", "python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
] ]
dependencies = [ dependencies = [
{ name = "colorama", marker = "python_full_version >= '3.11' and sys_platform == 'win32'" }, { name = "colorama", marker = "sys_platform == 'win32'" },
{ name = "decorator", marker = "python_full_version >= '3.11'" }, { name = "decorator" },
{ name = "ipython-pygments-lexers", marker = "python_full_version >= '3.11'" }, { name = "ipython-pygments-lexers" },
{ name = "jedi", marker = "python_full_version >= '3.11'" }, { name = "jedi" },
{ name = "matplotlib-inline", marker = "python_full_version >= '3.11'" }, { name = "matplotlib-inline" },
{ name = "pexpect", marker = "python_full_version >= '3.11' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, { name = "pexpect", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" },
{ name = "prompt-toolkit", marker = "python_full_version >= '3.11'" }, { name = "prompt-toolkit" },
{ name = "psutil", marker = "python_full_version >= '3.11'" }, { name = "psutil" },
{ name = "pygments", marker = "python_full_version >= '3.11'" }, { name = "pygments" },
{ name = "stack-data", marker = "python_full_version >= '3.11'" }, { name = "stack-data" },
{ name = "traitlets", marker = "python_full_version >= '3.11'" }, { name = "traitlets" },
{ name = "typing-extensions", marker = "python_full_version == '3.11.*'" }, { name = "typing-extensions", marker = "python_full_version < '3.12'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/cd/c4/87cda5842cf5c31837c06ddb588e11c3c35d8ece89b7a0108c06b8c9b00a/ipython-9.13.0.tar.gz", hash = "sha256:7e834b6afc99f020e3f05966ced34792f40267d64cb1ea9043886dab0dde5967", size = 4430549, upload-time = "2026-04-24T12:24:55.221Z" } sdist = { url = "https://files.pythonhosted.org/packages/cd/c4/87cda5842cf5c31837c06ddb588e11c3c35d8ece89b7a0108c06b8c9b00a/ipython-9.13.0.tar.gz", hash = "sha256:7e834b6afc99f020e3f05966ced34792f40267d64cb1ea9043886dab0dde5967", size = 4430549, upload-time = "2026-04-24T12:24:55.221Z" }
wheels = [ wheels = [
@ -2697,7 +2697,7 @@ name = "ipython-pygments-lexers"
version = "1.1.1" version = "1.1.1"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "pygments", marker = "python_full_version >= '3.11'" }, { name = "pygments" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/ef/4c/5dd1d8af08107f88c7f741ead7a40854b8ac24ddf9ae850afbcf698aa552/ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81", size = 8393, upload-time = "2025-01-17T11:24:34.505Z" } sdist = { url = "https://files.pythonhosted.org/packages/ef/4c/5dd1d8af08107f88c7f741ead7a40854b8ac24ddf9ae850afbcf698aa552/ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81", size = 8393, upload-time = "2025-01-17T11:24:34.505Z" }
wheels = [ wheels = [
@ -3460,7 +3460,7 @@ resolution-markers = [
"python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'", "python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
] ]
dependencies = [ dependencies = [
{ name = "mdurl", marker = "python_full_version < '3.11'" }, { name = "mdurl" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596, upload-time = "2023-06-03T06:41:14.443Z" } sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596, upload-time = "2023-06-03T06:41:14.443Z" }
wheels = [ wheels = [
@ -3486,7 +3486,7 @@ resolution-markers = [
"python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'", "python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
] ]
dependencies = [ dependencies = [
{ name = "mdurl", marker = "python_full_version >= '3.11'" }, { name = "mdurl" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/06/ff/7841249c247aa650a76b9ee4bbaeae59370dc8bfd2f6c01f3630c35eb134/markdown_it_py-4.2.0.tar.gz", hash = "sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49", size = 82454, upload-time = "2026-05-07T12:08:28.36Z" } sdist = { url = "https://files.pythonhosted.org/packages/06/ff/7841249c247aa650a76b9ee4bbaeae59370dc8bfd2f6c01f3630c35eb134/markdown_it_py-4.2.0.tar.gz", hash = "sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49", size = 82454, upload-time = "2026-05-07T12:08:28.36Z" }
wheels = [ wheels = [
@ -3961,12 +3961,12 @@ resolution-markers = [
"python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'", "python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
] ]
dependencies = [ dependencies = [
{ name = "docutils", version = "0.21.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "docutils", version = "0.21.2", source = { registry = "https://pypi.org/simple" } },
{ name = "jinja2", marker = "python_full_version < '3.11'" }, { name = "jinja2" },
{ name = "markdown-it-py", version = "3.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "markdown-it-py", version = "3.0.0", source = { registry = "https://pypi.org/simple" } },
{ name = "mdit-py-plugins", marker = "python_full_version < '3.11'" }, { name = "mdit-py-plugins" },
{ name = "pyyaml", marker = "python_full_version < '3.11'" }, { name = "pyyaml" },
{ name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" } },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/66/a5/9626ba4f73555b3735ad86247a8077d4603aa8628537687c839ab08bfe44/myst_parser-4.0.1.tar.gz", hash = "sha256:5cfea715e4f3574138aecbf7d54132296bfd72bb614d31168f48c477a830a7c4", size = 93985, upload-time = "2025-02-12T10:53:03.833Z" } sdist = { url = "https://files.pythonhosted.org/packages/66/a5/9626ba4f73555b3735ad86247a8077d4603aa8628537687c839ab08bfe44/myst_parser-4.0.1.tar.gz", hash = "sha256:5cfea715e4f3574138aecbf7d54132296bfd72bb614d31168f48c477a830a7c4", size = 93985, upload-time = "2025-02-12T10:53:03.833Z" }
wheels = [ wheels = [
@ -3992,12 +3992,12 @@ resolution-markers = [
"python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'", "python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
] ]
dependencies = [ dependencies = [
{ name = "docutils", version = "0.22.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "docutils", version = "0.22.4", source = { registry = "https://pypi.org/simple" } },
{ name = "jinja2", marker = "python_full_version >= '3.11'" }, { name = "jinja2" },
{ name = "markdown-it-py", version = "4.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "markdown-it-py", version = "4.2.0", source = { registry = "https://pypi.org/simple" } },
{ name = "mdit-py-plugins", marker = "python_full_version >= '3.11'" }, { name = "mdit-py-plugins" },
{ name = "pyyaml", marker = "python_full_version >= '3.11'" }, { name = "pyyaml" },
{ name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" },
{ name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/33/fa/7b45eef11b7971f0beb29d27b7bfe0d747d063aa29e170d9edd004733c8a/myst_parser-5.0.0.tar.gz", hash = "sha256:f6f231452c56e8baa662cc352c548158f6a16fcbd6e3800fc594978002b94f3a", size = 98535, upload-time = "2026-01-15T09:08:18.036Z" } sdist = { url = "https://files.pythonhosted.org/packages/33/fa/7b45eef11b7971f0beb29d27b7bfe0d747d063aa29e170d9edd004733c8a/myst_parser-5.0.0.tar.gz", hash = "sha256:f6f231452c56e8baa662cc352c548158f6a16fcbd6e3800fc594978002b94f3a", size = 98535, upload-time = "2026-01-15T09:08:18.036Z" }
@ -4086,9 +4086,9 @@ resolution-markers = [
"python_full_version < '3.11' and platform_machine == 'ARM64' and sys_platform == 'win32'", "python_full_version < '3.11' and platform_machine == 'ARM64' and sys_platform == 'win32'",
] ]
dependencies = [ dependencies = [
{ name = "certifi", marker = "python_full_version < '3.11' and platform_machine == 'ARM64' and sys_platform == 'win32'" }, { name = "certifi" },
{ name = "cftime", marker = "python_full_version < '3.11' and platform_machine == 'ARM64' and sys_platform == 'win32'" }, { name = "cftime" },
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' and platform_machine == 'ARM64' and sys_platform == 'win32'" }, { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/0e/76/7bc801796dee752c1ce9cd6935564a6ee79d5c9d9ef9192f57b156495a35/netcdf4-1.7.3.tar.gz", hash = "sha256:83f122fc3415e92b1d4904fd6a0898468b5404c09432c34beb6b16c533884673", size = 836095, upload-time = "2025-10-13T18:38:00.76Z" } sdist = { url = "https://files.pythonhosted.org/packages/0e/76/7bc801796dee752c1ce9cd6935564a6ee79d5c9d9ef9192f57b156495a35/netcdf4-1.7.3.tar.gz", hash = "sha256:83f122fc3415e92b1d4904fd6a0898468b5404c09432c34beb6b16c533884673", size = 836095, upload-time = "2025-10-13T18:38:00.76Z" }
@ -4114,8 +4114,8 @@ resolution-markers = [
"python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'", "python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
] ]
dependencies = [ dependencies = [
{ name = "certifi", marker = "python_full_version >= '3.11' or platform_machine != 'ARM64' or sys_platform != 'win32'" }, { name = "certifi" },
{ name = "cftime", marker = "python_full_version >= '3.11' or platform_machine != 'ARM64' or sys_platform != 'win32'" }, { name = "cftime" },
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and platform_machine != 'ARM64') or (python_full_version < '3.11' and sys_platform != 'win32')" }, { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and platform_machine != 'ARM64') or (python_full_version < '3.11' and sys_platform != 'win32')" },
{ name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
] ]
@ -4427,7 +4427,7 @@ name = "nvidia-cudnn-cu13"
version = "9.19.0.56" version = "9.19.0.56"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "nvidia-cublas", marker = "(platform_machine != 'ARM64' and sys_platform == 'win32') or sys_platform == 'linux'" }, { name = "nvidia-cublas" },
] ]
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/f1/84/26025437c1e6b61a707442184fa0c03d083b661adf3a3eecfd6d21677740/nvidia_cudnn_cu13-9.19.0.56-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:6ed29ffaee1176c612daf442e4dd6cfeb6a0caa43ddcbeb59da94953030b1be4", size = 433781201, upload-time = "2026-02-03T20:40:53.805Z" }, { url = "https://files.pythonhosted.org/packages/f1/84/26025437c1e6b61a707442184fa0c03d083b661adf3a3eecfd6d21677740/nvidia_cudnn_cu13-9.19.0.56-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:6ed29ffaee1176c612daf442e4dd6cfeb6a0caa43ddcbeb59da94953030b1be4", size = 433781201, upload-time = "2026-02-03T20:40:53.805Z" },
@ -4439,7 +4439,7 @@ name = "nvidia-cufft"
version = "12.0.0.61" version = "12.0.0.61"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "nvidia-nvjitlink", marker = "(platform_machine != 'ARM64' and sys_platform == 'win32') or sys_platform == 'linux'" }, { name = "nvidia-nvjitlink" },
] ]
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/8b/ae/f417a75c0259e85c1d2f83ca4e960289a5f814ed0cea74d18c353d3e989d/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2708c852ef8cd89d1d2068bdbece0aa188813a0c934db3779b9b1faa8442e5f5", size = 214053554, upload-time = "2025-09-04T08:31:38.196Z" }, { url = "https://files.pythonhosted.org/packages/8b/ae/f417a75c0259e85c1d2f83ca4e960289a5f814ed0cea74d18c353d3e989d/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2708c852ef8cd89d1d2068bdbece0aa188813a0c934db3779b9b1faa8442e5f5", size = 214053554, upload-time = "2025-09-04T08:31:38.196Z" },
@ -4469,9 +4469,9 @@ name = "nvidia-cusolver"
version = "12.0.4.66" version = "12.0.4.66"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "nvidia-cublas", marker = "(platform_machine != 'ARM64' and sys_platform == 'win32') or sys_platform == 'linux'" }, { name = "nvidia-cublas" },
{ name = "nvidia-cusparse", marker = "(platform_machine != 'ARM64' and sys_platform == 'win32') or sys_platform == 'linux'" }, { name = "nvidia-cusparse" },
{ name = "nvidia-nvjitlink", marker = "(platform_machine != 'ARM64' and sys_platform == 'win32') or sys_platform == 'linux'" }, { name = "nvidia-nvjitlink" },
] ]
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/c8/c3/b30c9e935fc01e3da443ec0116ed1b2a009bb867f5324d3f2d7e533e776b/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:02c2457eaa9e39de20f880f4bd8820e6a1cfb9f9a34f820eb12a155aa5bc92d2", size = 223467760, upload-time = "2025-09-04T08:33:04.222Z" }, { url = "https://files.pythonhosted.org/packages/c8/c3/b30c9e935fc01e3da443ec0116ed1b2a009bb867f5324d3f2d7e533e776b/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:02c2457eaa9e39de20f880f4bd8820e6a1cfb9f9a34f820eb12a155aa5bc92d2", size = 223467760, upload-time = "2025-09-04T08:33:04.222Z" },
@ -4483,7 +4483,7 @@ name = "nvidia-cusparse"
version = "12.6.3.3" version = "12.6.3.3"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "nvidia-nvjitlink", marker = "(platform_machine != 'ARM64' and sys_platform == 'win32') or sys_platform == 'linux'" }, { name = "nvidia-nvjitlink" },
] ]
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/f8/94/5c26f33738ae35276672f12615a64bd008ed5be6d1ebcb23579285d960a9/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:80bcc4662f23f1054ee334a15c72b8940402975e0eab63178fc7e670aa59472c", size = 162155568, upload-time = "2025-09-04T08:33:42.864Z" }, { url = "https://files.pythonhosted.org/packages/f8/94/5c26f33738ae35276672f12615a64bd008ed5be6d1ebcb23579285d960a9/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:80bcc4662f23f1054ee334a15c72b8940402975e0eab63178fc7e670aa59472c", size = 162155568, upload-time = "2025-09-04T08:33:42.864Z" },
@ -4762,8 +4762,8 @@ resolution-markers = [
"python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'", "python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
] ]
dependencies = [ dependencies = [
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } },
{ name = "types-pytz", marker = "python_full_version < '3.11'" }, { name = "types-pytz" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/92/5d/be23854a73fda69f1dbdda7bc10fbd6f930bd1fa87aaec389f00c901c1e8/pandas_stubs-2.3.3.260113.tar.gz", hash = "sha256:076e3724bcaa73de78932b012ec64b3010463d377fa63116f4e6850643d93800", size = 116131, upload-time = "2026-01-13T22:30:16.704Z" } sdist = { url = "https://files.pythonhosted.org/packages/92/5d/be23854a73fda69f1dbdda7bc10fbd6f930bd1fa87aaec389f00c901c1e8/pandas_stubs-2.3.3.260113.tar.gz", hash = "sha256:076e3724bcaa73de78932b012ec64b3010463d377fa63116f4e6850643d93800", size = 116131, upload-time = "2026-01-13T22:30:16.704Z" }
wheels = [ wheels = [
@ -4789,7 +4789,7 @@ resolution-markers = [
"python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'", "python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
] ]
dependencies = [ dependencies = [
{ name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" } },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/27/1d/297ff2c7ea50a768a2247621d6451abb2a07c0e9be7ca6d36ebe371658e5/pandas_stubs-3.0.0.260204.tar.gz", hash = "sha256:bf9294b76352effcffa9cb85edf0bed1339a7ec0c30b8e1ac3d66b4228f1fbc3", size = 109383, upload-time = "2026-02-04T15:17:17.247Z" } sdist = { url = "https://files.pythonhosted.org/packages/27/1d/297ff2c7ea50a768a2247621d6451abb2a07c0e9be7ca6d36ebe371658e5/pandas_stubs-3.0.0.260204.tar.gz", hash = "sha256:bf9294b76352effcffa9cb85edf0bed1339a7ec0c30b8e1ac3d66b4228f1fbc3", size = 109383, upload-time = "2026-02-04T15:17:17.247Z" }
wheels = [ wheels = [
@ -4828,7 +4828,7 @@ name = "pexpect"
version = "4.9.0" version = "4.9.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "ptyprocess", marker = "platform_machine != 'ARM64' or sys_platform != 'win32'" }, { name = "ptyprocess" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450, upload-time = "2023-11-25T09:07:26.339Z" } sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450, upload-time = "2023-11-25T09:07:26.339Z" }
wheels = [ wheels = [
@ -5311,14 +5311,14 @@ resolution-markers = [
"python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'", "python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
] ]
dependencies = [ dependencies = [
{ name = "accessible-pygments", marker = "python_full_version < '3.11'" }, { name = "accessible-pygments" },
{ name = "babel", marker = "python_full_version < '3.11'" }, { name = "babel" },
{ name = "beautifulsoup4", marker = "python_full_version < '3.11'" }, { name = "beautifulsoup4" },
{ name = "docutils", version = "0.21.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "docutils", version = "0.21.2", source = { registry = "https://pypi.org/simple" } },
{ name = "packaging", marker = "python_full_version < '3.11'" }, { name = "packaging" },
{ name = "pygments", marker = "python_full_version < '3.11'" }, { name = "pygments" },
{ name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" } },
{ name = "typing-extensions", marker = "python_full_version < '3.11'" }, { name = "typing-extensions" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/67/ea/3ab478cccacc2e8ef69892c42c44ae547bae089f356c4b47caf61730958d/pydata_sphinx_theme-0.15.4.tar.gz", hash = "sha256:7762ec0ac59df3acecf49fd2f889e1b4565dbce8b88b2e29ee06fdd90645a06d", size = 2400673, upload-time = "2024-06-25T19:28:45.041Z" } sdist = { url = "https://files.pythonhosted.org/packages/67/ea/3ab478cccacc2e8ef69892c42c44ae547bae089f356c4b47caf61730958d/pydata_sphinx_theme-0.15.4.tar.gz", hash = "sha256:7762ec0ac59df3acecf49fd2f889e1b4565dbce8b88b2e29ee06fdd90645a06d", size = 2400673, upload-time = "2024-06-25T19:28:45.041Z" }
wheels = [ wheels = [
@ -5344,14 +5344,14 @@ resolution-markers = [
"python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'", "python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
] ]
dependencies = [ dependencies = [
{ name = "accessible-pygments", marker = "python_full_version >= '3.11'" }, { name = "accessible-pygments" },
{ name = "babel", marker = "python_full_version >= '3.11'" }, { name = "babel" },
{ name = "beautifulsoup4", marker = "python_full_version >= '3.11'" }, { name = "beautifulsoup4" },
{ name = "docutils", version = "0.22.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "docutils", version = "0.22.4", source = { registry = "https://pypi.org/simple" } },
{ name = "pygments", marker = "python_full_version >= '3.11'" }, { name = "pygments" },
{ name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" },
{ name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
{ name = "typing-extensions", marker = "python_full_version >= '3.11'" }, { name = "typing-extensions" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/00/20/bb50f9de3a6de69e6abd6b087b52fa2418a0418b19597601605f855ad044/pydata_sphinx_theme-0.16.1.tar.gz", hash = "sha256:a08b7f0b7f70387219dc659bff0893a7554d5eb39b59d3b8ef37b8401b7642d7", size = 2412693, upload-time = "2024-12-17T10:53:39.537Z" } sdist = { url = "https://files.pythonhosted.org/packages/00/20/bb50f9de3a6de69e6abd6b087b52fa2418a0418b19597601605f855ad044/pydata_sphinx_theme-0.16.1.tar.gz", hash = "sha256:a08b7f0b7f70387219dc659bff0893a7554d5eb39b59d3b8ef37b8401b7642d7", size = 2412693, upload-time = "2024-12-17T10:53:39.537Z" }
wheels = [ wheels = [
@ -5390,7 +5390,7 @@ resolution-markers = [
"python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'", "python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
] ]
dependencies = [ dependencies = [
{ name = "cffi", marker = "python_full_version < '3.11'" }, { name = "cffi" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/2e/ea/762d00f6f518423cd889e39b12028844cc95f91a6413cf7136e184864821/pygit2-1.18.2.tar.gz", hash = "sha256:eca87e0662c965715b7f13491d5e858df2c0908341dee9bde2bc03268e460f55", size = 797200, upload-time = "2025-08-16T13:52:36.853Z" } sdist = { url = "https://files.pythonhosted.org/packages/2e/ea/762d00f6f518423cd889e39b12028844cc95f91a6413cf7136e184864821/pygit2-1.18.2.tar.gz", hash = "sha256:eca87e0662c965715b7f13491d5e858df2c0908341dee9bde2bc03268e460f55", size = 797200, upload-time = "2025-08-16T13:52:36.853Z" }
wheels = [ wheels = [
@ -5451,7 +5451,7 @@ resolution-markers = [
"python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'", "python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
] ]
dependencies = [ dependencies = [
{ name = "cffi", marker = "python_full_version >= '3.11'" }, { name = "cffi" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/3a/a4/10ce00feef5c43eddacab19ae6610c4d4ef3ab77e544e9ee938772cd1c17/pygit2-1.19.2.tar.gz", hash = "sha256:cbeb3dbca9ca6ee3d5ea5d02f5e844c2d6084a2d5d6621e3e06aa2b11c645bfd", size = 803448, upload-time = "2026-03-29T14:57:27.565Z" } sdist = { url = "https://files.pythonhosted.org/packages/3a/a4/10ce00feef5c43eddacab19ae6610c4d4ef3ab77e544e9ee938772cd1c17/pygit2-1.19.2.tar.gz", hash = "sha256:cbeb3dbca9ca6ee3d5ea5d02f5e844c2d6084a2d5d6621e3e06aa2b11c645bfd", size = 803448, upload-time = "2026-03-29T14:57:27.565Z" }
wheels = [ wheels = [
@ -5880,15 +5880,15 @@ resolution-markers = [
"python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'", "python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
] ]
dependencies = [ dependencies = [
{ name = "affine", marker = "python_full_version < '3.12'" }, { name = "affine" },
{ name = "attrs", marker = "python_full_version < '3.12'" }, { name = "attrs" },
{ name = "certifi", marker = "python_full_version < '3.12'" }, { name = "certifi" },
{ name = "click", marker = "python_full_version < '3.12'" }, { name = "click" },
{ name = "click-plugins", marker = "python_full_version < '3.12'" }, { name = "click-plugins" },
{ name = "cligj", marker = "python_full_version < '3.12'" }, { name = "cligj" },
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version != '3.11.*'" },
{ name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" },
{ name = "pyparsing", marker = "python_full_version < '3.12'" }, { name = "pyparsing" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/ec/fa/fce8dc9f09e5bc6520b6fc1b4ecfa510af9ca06eb42ad7bdff9c9b8989d0/rasterio-1.4.4.tar.gz", hash = "sha256:c95424e2c7f009b8f7df1095d645c52895cd332c0c2e1b4c2e073ea28b930320", size = 445004, upload-time = "2025-12-12T18:01:08.971Z" } sdist = { url = "https://files.pythonhosted.org/packages/ec/fa/fce8dc9f09e5bc6520b6fc1b4ecfa510af9ca06eb42ad7bdff9c9b8989d0/rasterio-1.4.4.tar.gz", hash = "sha256:c95424e2c7f009b8f7df1095d645c52895cd332c0c2e1b4c2e073ea28b930320", size = 445004, upload-time = "2025-12-12T18:01:08.971Z" }
wheels = [ wheels = [
@ -5938,13 +5938,13 @@ resolution-markers = [
"python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'", "python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
] ]
dependencies = [ dependencies = [
{ name = "affine", marker = "python_full_version >= '3.12'" }, { name = "affine" },
{ name = "attrs", marker = "python_full_version >= '3.12'" }, { name = "attrs" },
{ name = "certifi", marker = "python_full_version >= '3.12'" }, { name = "certifi" },
{ name = "click", marker = "python_full_version >= '3.12'" }, { name = "click" },
{ name = "cligj", marker = "python_full_version >= '3.12'" }, { name = "cligj" },
{ name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" } },
{ name = "pyparsing", marker = "python_full_version >= '3.12'" }, { name = "pyparsing" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/f6/88/edb4b66b6cb2c13f123af5a3896bf70c0cbe73ab3cd4243cb4eb0212a0f6/rasterio-1.5.0.tar.gz", hash = "sha256:1e0ea56b02eea4989b36edf8e58a5a3ef40e1b7edcb04def2603accd5ab3ee7b", size = 452184, upload-time = "2026-01-05T16:06:47.169Z" } sdist = { url = "https://files.pythonhosted.org/packages/f6/88/edb4b66b6cb2c13f123af5a3896bf70c0cbe73ab3cd4243cb4eb0212a0f6/rasterio-1.5.0.tar.gz", hash = "sha256:1e0ea56b02eea4989b36edf8e58a5a3ef40e1b7edcb04def2603accd5ab3ee7b", size = 452184, upload-time = "2026-01-05T16:06:47.169Z" }
wheels = [ wheels = [
@ -6228,10 +6228,10 @@ resolution-markers = [
"python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'", "python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
] ]
dependencies = [ dependencies = [
{ name = "joblib", marker = "python_full_version < '3.11'" }, { name = "joblib" },
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } },
{ name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" } },
{ name = "threadpoolctl", marker = "python_full_version < '3.11'" }, { name = "threadpoolctl" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/98/c2/a7855e41c9d285dfe86dc50b250978105dce513d6e459ea66a6aeb0e1e0c/scikit_learn-1.7.2.tar.gz", hash = "sha256:20e9e49ecd130598f1ca38a1d85090e1a600147b9c02fa6f15d69cb53d968fda", size = 7193136, upload-time = "2025-09-09T08:21:29.075Z" } sdist = { url = "https://files.pythonhosted.org/packages/98/c2/a7855e41c9d285dfe86dc50b250978105dce513d6e459ea66a6aeb0e1e0c/scikit_learn-1.7.2.tar.gz", hash = "sha256:20e9e49ecd130598f1ca38a1d85090e1a600147b9c02fa6f15d69cb53d968fda", size = 7193136, upload-time = "2025-09-09T08:21:29.075Z" }
wheels = [ wheels = [
@ -6281,10 +6281,10 @@ resolution-markers = [
"python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'", "python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
] ]
dependencies = [ dependencies = [
{ name = "joblib", marker = "python_full_version >= '3.11'" }, { name = "joblib" },
{ name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" } },
{ name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" } },
{ name = "threadpoolctl", marker = "python_full_version >= '3.11'" }, { name = "threadpoolctl" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/0e/d4/40988bf3b8e34feec1d0e6a051446b1f66225f8529b9309becaeef62b6c4/scikit_learn-1.8.0.tar.gz", hash = "sha256:9bccbb3b40e3de10351f8f5068e105d0f4083b1a65fa07b6634fbc401a6287fd", size = 7335585, upload-time = "2025-12-10T07:08:53.618Z" } sdist = { url = "https://files.pythonhosted.org/packages/0e/d4/40988bf3b8e34feec1d0e6a051446b1f66225f8529b9309becaeef62b6c4/scikit_learn-1.8.0.tar.gz", hash = "sha256:9bccbb3b40e3de10351f8f5068e105d0f4083b1a65fa07b6634fbc401a6287fd", size = 7335585, upload-time = "2025-12-10T07:08:53.618Z" }
wheels = [ wheels = [
@ -6325,7 +6325,7 @@ resolution-markers = [
"python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'", "python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
] ]
dependencies = [ dependencies = [
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/0f/37/6964b830433e654ec7485e45a00fc9a27cf868d622838f6b6d9c5ec0d532/scipy-1.15.3.tar.gz", hash = "sha256:eae3cf522bc7df64b42cad3925c876e1b0b6c35c1337c93e12c0f366f55b0eaf", size = 59419214, upload-time = "2025-05-08T16:13:05.955Z" } sdist = { url = "https://files.pythonhosted.org/packages/0f/37/6964b830433e654ec7485e45a00fc9a27cf868d622838f6b6d9c5ec0d532/scipy-1.15.3.tar.gz", hash = "sha256:eae3cf522bc7df64b42cad3925c876e1b0b6c35c1337c93e12c0f366f55b0eaf", size = 59419214, upload-time = "2025-05-08T16:13:05.955Z" }
wheels = [ wheels = [
@ -6395,7 +6395,7 @@ resolution-markers = [
"python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'", "python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
] ]
dependencies = [ dependencies = [
{ name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" } },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/7a/97/5a3609c4f8d58b039179648e62dd220f89864f56f7357f5d4f45c29eb2cc/scipy-1.17.1.tar.gz", hash = "sha256:95d8e012d8cb8816c226aef832200b1d45109ed4464303e997c5b13122b297c0", size = 30573822, upload-time = "2026-02-23T00:26:24.851Z" } sdist = { url = "https://files.pythonhosted.org/packages/7a/97/5a3609c4f8d58b039179648e62dd220f89864f56f7357f5d4f45c29eb2cc/scipy-1.17.1.tar.gz", hash = "sha256:95d8e012d8cb8816c226aef832200b1d45109ed4464303e997c5b13122b297c0", size = 30573822, upload-time = "2026-02-23T00:26:24.851Z" }
wheels = [ wheels = [
@ -6748,23 +6748,23 @@ resolution-markers = [
"python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'", "python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
] ]
dependencies = [ dependencies = [
{ name = "alabaster", marker = "python_full_version < '3.11'" }, { name = "alabaster" },
{ name = "babel", marker = "python_full_version < '3.11'" }, { name = "babel" },
{ name = "colorama", marker = "python_full_version < '3.11' and sys_platform == 'win32'" }, { name = "colorama", marker = "sys_platform == 'win32'" },
{ name = "docutils", version = "0.21.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "docutils", version = "0.21.2", source = { registry = "https://pypi.org/simple" } },
{ name = "imagesize", marker = "python_full_version < '3.11'" }, { name = "imagesize" },
{ name = "jinja2", marker = "python_full_version < '3.11'" }, { name = "jinja2" },
{ name = "packaging", marker = "python_full_version < '3.11'" }, { name = "packaging" },
{ name = "pygments", marker = "python_full_version < '3.11'" }, { name = "pygments" },
{ name = "requests", marker = "python_full_version < '3.11'" }, { name = "requests" },
{ name = "snowballstemmer", marker = "python_full_version < '3.11'" }, { name = "snowballstemmer" },
{ name = "sphinxcontrib-applehelp", marker = "python_full_version < '3.11'" }, { name = "sphinxcontrib-applehelp" },
{ name = "sphinxcontrib-devhelp", marker = "python_full_version < '3.11'" }, { name = "sphinxcontrib-devhelp" },
{ name = "sphinxcontrib-htmlhelp", marker = "python_full_version < '3.11'" }, { name = "sphinxcontrib-htmlhelp" },
{ name = "sphinxcontrib-jsmath", marker = "python_full_version < '3.11'" }, { name = "sphinxcontrib-jsmath" },
{ name = "sphinxcontrib-qthelp", marker = "python_full_version < '3.11'" }, { name = "sphinxcontrib-qthelp" },
{ name = "sphinxcontrib-serializinghtml", marker = "python_full_version < '3.11'" }, { name = "sphinxcontrib-serializinghtml" },
{ name = "tomli", marker = "python_full_version < '3.11'" }, { name = "tomli" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/be0b61178fe2cdcb67e2a92fc9ebb488e3c51c4f74a36a7824c0adf23425/sphinx-8.1.3.tar.gz", hash = "sha256:43c1911eecb0d3e161ad78611bc905d1ad0e523e4ddc202a58a821773dc4c927", size = 8184611, upload-time = "2024-10-13T20:27:13.93Z" } sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/be0b61178fe2cdcb67e2a92fc9ebb488e3c51c4f74a36a7824c0adf23425/sphinx-8.1.3.tar.gz", hash = "sha256:43c1911eecb0d3e161ad78611bc905d1ad0e523e4ddc202a58a821773dc4c927", size = 8184611, upload-time = "2024-10-13T20:27:13.93Z" }
wheels = [ wheels = [
@ -6782,23 +6782,23 @@ resolution-markers = [
"python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'", "python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
] ]
dependencies = [ dependencies = [
{ name = "alabaster", marker = "python_full_version == '3.11.*'" }, { name = "alabaster" },
{ name = "babel", marker = "python_full_version == '3.11.*'" }, { name = "babel" },
{ name = "colorama", marker = "python_full_version == '3.11.*' and sys_platform == 'win32'" }, { name = "colorama", marker = "sys_platform == 'win32'" },
{ name = "docutils", version = "0.22.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, { name = "docutils", version = "0.22.4", source = { registry = "https://pypi.org/simple" } },
{ name = "imagesize", marker = "python_full_version == '3.11.*'" }, { name = "imagesize" },
{ name = "jinja2", marker = "python_full_version == '3.11.*'" }, { name = "jinja2" },
{ name = "packaging", marker = "python_full_version == '3.11.*'" }, { name = "packaging" },
{ name = "pygments", marker = "python_full_version == '3.11.*'" }, { name = "pygments" },
{ name = "requests", marker = "python_full_version == '3.11.*'" }, { name = "requests" },
{ name = "roman-numerals", marker = "python_full_version == '3.11.*'" }, { name = "roman-numerals" },
{ name = "snowballstemmer", marker = "python_full_version == '3.11.*'" }, { name = "snowballstemmer" },
{ name = "sphinxcontrib-applehelp", marker = "python_full_version == '3.11.*'" }, { name = "sphinxcontrib-applehelp" },
{ name = "sphinxcontrib-devhelp", marker = "python_full_version == '3.11.*'" }, { name = "sphinxcontrib-devhelp" },
{ name = "sphinxcontrib-htmlhelp", marker = "python_full_version == '3.11.*'" }, { name = "sphinxcontrib-htmlhelp" },
{ name = "sphinxcontrib-jsmath", marker = "python_full_version == '3.11.*'" }, { name = "sphinxcontrib-jsmath" },
{ name = "sphinxcontrib-qthelp", marker = "python_full_version == '3.11.*'" }, { name = "sphinxcontrib-qthelp" },
{ name = "sphinxcontrib-serializinghtml", marker = "python_full_version == '3.11.*'" }, { name = "sphinxcontrib-serializinghtml" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/42/50/a8c6ccc36d5eacdfd7913ddccd15a9cee03ecafc5ee2bc40e1f168d85022/sphinx-9.0.4.tar.gz", hash = "sha256:594ef59d042972abbc581d8baa577404abe4e6c3b04ef61bd7fc2acbd51f3fa3", size = 8710502, upload-time = "2025-12-04T07:45:27.343Z" } sdist = { url = "https://files.pythonhosted.org/packages/42/50/a8c6ccc36d5eacdfd7913ddccd15a9cee03ecafc5ee2bc40e1f168d85022/sphinx-9.0.4.tar.gz", hash = "sha256:594ef59d042972abbc581d8baa577404abe4e6c3b04ef61bd7fc2acbd51f3fa3", size = 8710502, upload-time = "2025-12-04T07:45:27.343Z" }
wheels = [ wheels = [
@ -6820,23 +6820,23 @@ resolution-markers = [
"python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'", "python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
] ]
dependencies = [ dependencies = [
{ name = "alabaster", marker = "python_full_version >= '3.12'" }, { name = "alabaster" },
{ name = "babel", marker = "python_full_version >= '3.12'" }, { name = "babel" },
{ name = "colorama", marker = "python_full_version >= '3.12' and sys_platform == 'win32'" }, { name = "colorama", marker = "sys_platform == 'win32'" },
{ name = "docutils", version = "0.22.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, { name = "docutils", version = "0.22.4", source = { registry = "https://pypi.org/simple" } },
{ name = "imagesize", marker = "python_full_version >= '3.12'" }, { name = "imagesize" },
{ name = "jinja2", marker = "python_full_version >= '3.12'" }, { name = "jinja2" },
{ name = "packaging", marker = "python_full_version >= '3.12'" }, { name = "packaging" },
{ name = "pygments", marker = "python_full_version >= '3.12'" }, { name = "pygments" },
{ name = "requests", marker = "python_full_version >= '3.12'" }, { name = "requests" },
{ name = "roman-numerals", marker = "python_full_version >= '3.12'" }, { name = "roman-numerals" },
{ name = "snowballstemmer", marker = "python_full_version >= '3.12'" }, { name = "snowballstemmer" },
{ name = "sphinxcontrib-applehelp", marker = "python_full_version >= '3.12'" }, { name = "sphinxcontrib-applehelp" },
{ name = "sphinxcontrib-devhelp", marker = "python_full_version >= '3.12'" }, { name = "sphinxcontrib-devhelp" },
{ name = "sphinxcontrib-htmlhelp", marker = "python_full_version >= '3.12'" }, { name = "sphinxcontrib-htmlhelp" },
{ name = "sphinxcontrib-jsmath", marker = "python_full_version >= '3.12'" }, { name = "sphinxcontrib-jsmath" },
{ name = "sphinxcontrib-qthelp", marker = "python_full_version >= '3.12'" }, { name = "sphinxcontrib-qthelp" },
{ name = "sphinxcontrib-serializinghtml", marker = "python_full_version >= '3.12'" }, { name = "sphinxcontrib-serializinghtml" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/cd/bd/f08eb0f4eed5c83f1ba2a3bd18f7745a2b1525fad70660a1c00224ec468a/sphinx-9.1.0.tar.gz", hash = "sha256:7741722357dd75f8190766926071fed3bdc211c74dd2d7d4df5404da95930ddb", size = 8718324, upload-time = "2025-12-31T15:09:27.646Z" } sdist = { url = "https://files.pythonhosted.org/packages/cd/bd/f08eb0f4eed5c83f1ba2a3bd18f7745a2b1525fad70660a1c00224ec468a/sphinx-9.1.0.tar.gz", hash = "sha256:7741722357dd75f8190766926071fed3bdc211c74dd2d7d4df5404da95930ddb", size = 8718324, upload-time = "2025-12-31T15:09:27.646Z" }
wheels = [ wheels = [
@ -6854,12 +6854,12 @@ resolution-markers = [
"python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'", "python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
] ]
dependencies = [ dependencies = [
{ name = "colorama", marker = "python_full_version < '3.11'" }, { name = "colorama" },
{ name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" } },
{ name = "starlette", marker = "python_full_version < '3.11'" }, { name = "starlette" },
{ name = "uvicorn", marker = "python_full_version < '3.11'" }, { name = "uvicorn" },
{ name = "watchfiles", marker = "python_full_version < '3.11'" }, { name = "watchfiles" },
{ name = "websockets", marker = "python_full_version < '3.11'" }, { name = "websockets" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/a5/2c/155e1de2c1ba96a72e5dba152c509a8b41e047ee5c2def9e9f0d812f8be7/sphinx_autobuild-2024.10.3.tar.gz", hash = "sha256:248150f8f333e825107b6d4b86113ab28fa51750e5f9ae63b59dc339be951fb1", size = 14023, upload-time = "2024-10-02T23:15:30.172Z" } sdist = { url = "https://files.pythonhosted.org/packages/a5/2c/155e1de2c1ba96a72e5dba152c509a8b41e047ee5c2def9e9f0d812f8be7/sphinx_autobuild-2024.10.3.tar.gz", hash = "sha256:248150f8f333e825107b6d4b86113ab28fa51750e5f9ae63b59dc339be951fb1", size = 14023, upload-time = "2024-10-02T23:15:30.172Z" }
wheels = [ wheels = [
@ -6885,13 +6885,13 @@ resolution-markers = [
"python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'", "python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
] ]
dependencies = [ dependencies = [
{ name = "colorama", marker = "python_full_version >= '3.11'" }, { name = "colorama" },
{ name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" },
{ name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
{ name = "starlette", marker = "python_full_version >= '3.11'" }, { name = "starlette" },
{ name = "uvicorn", marker = "python_full_version >= '3.11'" }, { name = "uvicorn" },
{ name = "watchfiles", marker = "python_full_version >= '3.11'" }, { name = "watchfiles" },
{ name = "websockets", marker = "python_full_version >= '3.11'" }, { name = "websockets" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/e0/3c/a59a3a453d4133777f7ed2e83c80b7dc817d43c74b74298ca0af869662ad/sphinx_autobuild-2025.8.25.tar.gz", hash = "sha256:9cf5aab32853c8c31af572e4fecdc09c997e2b8be5a07daf2a389e270e85b213", size = 15200, upload-time = "2025-08-25T18:44:55.436Z" } sdist = { url = "https://files.pythonhosted.org/packages/e0/3c/a59a3a453d4133777f7ed2e83c80b7dc817d43c74b74298ca0af869662ad/sphinx_autobuild-2025.8.25.tar.gz", hash = "sha256:9cf5aab32853c8c31af572e4fecdc09c997e2b8be5a07daf2a389e270e85b213", size = 15200, upload-time = "2025-08-25T18:44:55.436Z" }
wheels = [ wheels = [
@ -6909,7 +6909,7 @@ resolution-markers = [
"python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'", "python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
] ]
dependencies = [ dependencies = [
{ name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" } },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/26/f0/43c6a5ff3e7b08a8c3b32f81b859f1b518ccc31e45f22e2b41ced38be7b9/sphinx_autodoc_typehints-3.0.1.tar.gz", hash = "sha256:b9b40dd15dee54f6f810c924f863f9cf1c54f9f3265c495140ea01be7f44fa55", size = 36282, upload-time = "2025-01-16T18:25:30.958Z" } sdist = { url = "https://files.pythonhosted.org/packages/26/f0/43c6a5ff3e7b08a8c3b32f81b859f1b518ccc31e45f22e2b41ced38be7b9/sphinx_autodoc_typehints-3.0.1.tar.gz", hash = "sha256:b9b40dd15dee54f6f810c924f863f9cf1c54f9f3265c495140ea01be7f44fa55", size = 36282, upload-time = "2025-01-16T18:25:30.958Z" }
wheels = [ wheels = [
@ -6927,7 +6927,7 @@ resolution-markers = [
"python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'", "python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
] ]
dependencies = [ dependencies = [
{ name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" } },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/1d/f6/bdd93582b2aaad2cfe9eb5695a44883c8bc44572dd3c351a947acbb13789/sphinx_autodoc_typehints-3.6.1.tar.gz", hash = "sha256:fa0b686ae1b85965116c88260e5e4b82faec3687c2e94d6a10f9b36c3743e2fe", size = 37563, upload-time = "2026-01-02T15:23:46.543Z" } sdist = { url = "https://files.pythonhosted.org/packages/1d/f6/bdd93582b2aaad2cfe9eb5695a44883c8bc44572dd3c351a947acbb13789/sphinx_autodoc_typehints-3.6.1.tar.gz", hash = "sha256:fa0b686ae1b85965116c88260e5e4b82faec3687c2e94d6a10f9b36c3743e2fe", size = 37563, upload-time = "2026-01-02T15:23:46.543Z" }
wheels = [ wheels = [
@ -6949,7 +6949,7 @@ resolution-markers = [
"python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'", "python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
] ]
dependencies = [ dependencies = [
{ name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" } },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/75/75/9a5695b3d3b848a43cfe91c7d7d3c5ab543eb3bc5c2a12ffaf5003a2a4f6/sphinx_autodoc_typehints-3.10.2.tar.gz", hash = "sha256:34db651eb14343ba16bd9c03e0f5093971f9bbd3cc6b0a1470adecd578940b9c", size = 74241, upload-time = "2026-04-15T22:09:48.87Z" } sdist = { url = "https://files.pythonhosted.org/packages/75/75/9a5695b3d3b848a43cfe91c7d7d3c5ab543eb3bc5c2a12ffaf5003a2a4f6/sphinx_autodoc_typehints-3.10.2.tar.gz", hash = "sha256:34db651eb14343ba16bd9c03e0f5093971f9bbd3cc6b0a1470adecd578940b9c", size = 74241, upload-time = "2026-04-15T22:09:48.87Z" }
wheels = [ wheels = [
@ -6967,8 +6967,8 @@ resolution-markers = [
"python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'", "python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
] ]
dependencies = [ dependencies = [
{ name = "pydata-sphinx-theme", version = "0.15.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "pydata-sphinx-theme", version = "0.15.4", source = { registry = "https://pypi.org/simple" } },
{ name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" } },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/45/19/d002ed96bdc7738c15847c730e1e88282d738263deac705d5713b4d8fa94/sphinx_book_theme-1.1.4.tar.gz", hash = "sha256:73efe28af871d0a89bd05856d300e61edce0d5b2fbb7984e84454be0fedfe9ed", size = 439188, upload-time = "2025-02-20T16:32:32.581Z" } sdist = { url = "https://files.pythonhosted.org/packages/45/19/d002ed96bdc7738c15847c730e1e88282d738263deac705d5713b4d8fa94/sphinx_book_theme-1.1.4.tar.gz", hash = "sha256:73efe28af871d0a89bd05856d300e61edce0d5b2fbb7984e84454be0fedfe9ed", size = 439188, upload-time = "2025-02-20T16:32:32.581Z" }
wheels = [ wheels = [
@ -6994,8 +6994,8 @@ resolution-markers = [
"python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'", "python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
] ]
dependencies = [ dependencies = [
{ name = "pydata-sphinx-theme", version = "0.16.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "pydata-sphinx-theme", version = "0.16.1", source = { registry = "https://pypi.org/simple" } },
{ name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" },
{ name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/eb/f7/154786f3cfb7692cd7acc24b6dfe4dcd1146b66f376b17df9e47125555e9/sphinx_book_theme-1.2.0.tar.gz", hash = "sha256:4a7ebfc7da4395309ac942ddfc38fbec5c5254c3be22195e99ad12586fbda9e3", size = 443962, upload-time = "2026-03-09T23:20:30.442Z" } sdist = { url = "https://files.pythonhosted.org/packages/eb/f7/154786f3cfb7692cd7acc24b6dfe4dcd1146b66f376b17df9e47125555e9/sphinx_book_theme-1.2.0.tar.gz", hash = "sha256:4a7ebfc7da4395309ac942ddfc38fbec5c5254c3be22195e99ad12586fbda9e3", size = 443962, upload-time = "2026-03-09T23:20:30.442Z" }
@ -7163,8 +7163,8 @@ name = "standard-aifc"
version = "3.13.0" version = "3.13.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "audioop-lts", marker = "python_full_version >= '3.13'" }, { name = "audioop-lts" },
{ name = "standard-chunk", marker = "python_full_version >= '3.13'" }, { name = "standard-chunk" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/c4/53/6050dc3dde1671eb3db592c13b55a8005e5040131f7509cef0215212cb84/standard_aifc-3.13.0.tar.gz", hash = "sha256:64e249c7cb4b3daf2fdba4e95721f811bde8bdfc43ad9f936589b7bb2fae2e43", size = 15240, upload-time = "2024-10-30T16:01:31.772Z" } sdist = { url = "https://files.pythonhosted.org/packages/c4/53/6050dc3dde1671eb3db592c13b55a8005e5040131f7509cef0215212cb84/standard_aifc-3.13.0.tar.gz", hash = "sha256:64e249c7cb4b3daf2fdba4e95721f811bde8bdfc43ad9f936589b7bb2fae2e43", size = 15240, upload-time = "2024-10-30T16:01:31.772Z" }
wheels = [ wheels = [
@ -7185,7 +7185,7 @@ name = "standard-sunau"
version = "3.13.0" version = "3.13.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "audioop-lts", marker = "python_full_version >= '3.13'" }, { name = "audioop-lts" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/66/e3/ce8d38cb2d70e05ffeddc28bb09bad77cfef979eb0a299c9117f7ed4e6a9/standard_sunau-3.13.0.tar.gz", hash = "sha256:b319a1ac95a09a2378a8442f403c66f4fd4b36616d6df6ae82b8e536ee790908", size = 9368, upload-time = "2024-10-30T16:01:41.626Z" } sdist = { url = "https://files.pythonhosted.org/packages/66/e3/ce8d38cb2d70e05ffeddc28bb09bad77cfef979eb0a299c9117f7ed4e6a9/standard_sunau-3.13.0.tar.gz", hash = "sha256:b319a1ac95a09a2378a8442f403c66f4fd4b36616d6df6ae82b8e536ee790908", size = 9368, upload-time = "2024-10-30T16:01:41.626Z" }
wheels = [ wheels = [
@ -7924,9 +7924,9 @@ resolution-markers = [
"python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'", "python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
] ]
dependencies = [ dependencies = [
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } },
{ name = "packaging", marker = "python_full_version < '3.11'" }, { name = "packaging" },
{ name = "pandas", marker = "python_full_version < '3.11'" }, { name = "pandas" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/19/ec/e50d833518f10b0c24feb184b209bb6856f25b919ba8c1f89678b930b1cd/xarray-2025.6.1.tar.gz", hash = "sha256:a84f3f07544634a130d7dc615ae44175419f4c77957a7255161ed99c69c7c8b0", size = 3003185, upload-time = "2025-06-12T03:04:09.099Z" } sdist = { url = "https://files.pythonhosted.org/packages/19/ec/e50d833518f10b0c24feb184b209bb6856f25b919ba8c1f89678b930b1cd/xarray-2025.6.1.tar.gz", hash = "sha256:a84f3f07544634a130d7dc615ae44175419f4c77957a7255161ed99c69c7c8b0", size = 3003185, upload-time = "2025-06-12T03:04:09.099Z" }
wheels = [ wheels = [
@ -7952,9 +7952,9 @@ resolution-markers = [
"python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'", "python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
] ]
dependencies = [ dependencies = [
{ name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" } },
{ name = "packaging", marker = "python_full_version >= '3.11'" }, { name = "packaging" },
{ name = "pandas", marker = "python_full_version >= '3.11'" }, { name = "pandas" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/4b/a6/6fe936a798a3a38a79c7422d1a31afd2e9a14690fcb0ccff96bc01f04bf2/xarray-2026.4.0.tar.gz", hash = "sha256:c4ac9a01a945d90d5b1628e2af045099a9d4943536d4f2ee3ae963c3b222d15b", size = 3132311, upload-time = "2026-04-13T19:45:36.688Z" } sdist = { url = "https://files.pythonhosted.org/packages/4b/a6/6fe936a798a3a38a79c7422d1a31afd2e9a14690fcb0ccff96bc01f04bf2/xarray-2026.4.0.tar.gz", hash = "sha256:c4ac9a01a945d90d5b1628e2af045099a9d4943536d4f2ee3ae963c3b222d15b", size = 3132311, upload-time = "2026-04-13T19:45:36.688Z" }
wheels = [ wheels = [