Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ jobs:
fail-fast: false
matrix:
python-version:
- "3.9"
- "3.10"
- "3.11"
- "3.12"
Expand Down Expand Up @@ -78,7 +77,7 @@ jobs:

- uses: actions/setup-python@v6
with:
python-version: '3.9'
python-version: '3.10'

- name: Setup uv
uses: astral-sh/setup-uv@v7
Expand Down
9 changes: 6 additions & 3 deletions docs/user-guide/notebooks/SVGHistogram.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@
"\n",
" boxes = []\n",
" for height, left_edge, right_edge in zip(\n",
" norm_vals, norm_edges[:-1], norm_edges[1:]\n",
" norm_vals, norm_edges[:-1], norm_edges[1:], strict=True\n",
" ):\n",
" boxes.append(\n",
" rect.pad(\n",
Expand Down Expand Up @@ -340,10 +340,13 @@
"\n",
" boxes = []\n",
" for r, up_edge, bottom_edge in zip(\n",
" range(len(norm_edges[1])), norm_edges[1][:-1], norm_edges[1][1:]\n",
" range(len(norm_edges[1])), norm_edges[1][:-1], norm_edges[1][1:], strict=True\n",
" ):\n",
" for c, left_edge, right_edge in zip(\n",
" range(len(norm_edges[0])), norm_edges[0][:-1], norm_edges[0][1:]\n",
" range(len(norm_edges[0])),\n",
" norm_edges[0][:-1],\n",
" norm_edges[0][1:],\n",
" strict=True,\n",
" ):\n",
" opacity = 1 - norm_vals[r][c]\n",
" boxes.append(\n",
Expand Down
29 changes: 14 additions & 15 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ classifiers = [
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
Expand All @@ -38,11 +37,11 @@ keywords = [
"boost-histogram",
"dask-histogram",
]
requires-python = ">=3.9"
requires-python = ">=3.10"
dependencies = [
"boost-histogram>=1.5,<1.7",
"histoprint>=2.2.0",
'numpy>=1.19.3',
'numpy>=1.21.3',
'typing-extensions>=4;python_version<"3.11"',
]
dynamic = ["version"]
Expand All @@ -60,16 +59,16 @@ Changelog = "https://hist.readthedocs.io/en/latest/changelog.html"
[project.optional-dependencies]
# Keep in sync with dependency-groups below
mpl = [
"matplotlib >=3.3.3",
"mplhep >=0.3.17",
"matplotlib >=3.10.0",
"mplhep >=0.3.31",
]
plot = [
"matplotlib >=3.3.3",
"mplhep >=0.3.17",
"matplotlib >=3.10.0",
"mplhep >=0.3.31",
]
fit = [
"scipy >=1.5.4",
"iminuit >=2",
"scipy >=1.7.0",
"iminuit >=2.9.0",
]
dask = [
"dask[dataframe] >=2022,<2025",
Expand All @@ -78,12 +77,12 @@ dask = [

[dependency-groups]
plot = [
"matplotlib >=3.3.3",
"mplhep >=0.3.17",
"matplotlib >=3.10.0",
"mplhep >=0.3.31",
]
fit = [
"scipy >=1.5.4",
"iminuit >=2; python_version<'3.14'",
"scipy >=1.7.0",
"iminuit >=2.9.0",
]
dask = [
"dask[dataframe] >=2022,<2025",
Expand Down Expand Up @@ -141,7 +140,7 @@ filterwarnings = [
[tool.mypy]
warn_unused_configs = true
files = "src"
python_version = "3.9"
python_version = "3.10"
strict = true
enable_error_code = ["ignore-without-code", "truthy-bool", "redundant-expr"]
warn_unreachable = true
Expand All @@ -161,7 +160,7 @@ ignore_missing_imports = true


[tool.pylint]
py-version = "3.9"
py-version = "3.10"
extension-pkg-allow-list = ["boost_histogram._core"]
reports.output-format = "colorized"
similarities.ignore-imports = "yes"
Expand Down
22 changes: 1 addition & 21 deletions src/hist/_compat/builtins.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,6 @@
from __future__ import annotations

import sys

if sys.version_info < (3, 10):
import builtins
import itertools
from collections.abc import Iterator
from typing import Any

def zip(*iterables: Any, strict: bool = False) -> Iterator[tuple[Any, ...]]:
if strict:
marker = object()
for each in itertools.zip_longest(*iterables, fillvalue=marker):
for val in each:
if val is marker:
raise ValueError("zip() arguments are not the same length")
yield each
else:
yield from builtins.zip(*iterables)

else:
from builtins import zip # noqa: UP029
from builtins import zip # noqa: UP029

__all__ = ["zip"]

Expand Down
31 changes: 16 additions & 15 deletions src/hist/basehist.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@
import operator
import typing
import warnings
from collections.abc import Generator, Iterator, Mapping, Sequence
from collections.abc import Callable, Generator, Iterator, Mapping, Sequence
from typing import (
Any,
Callable,
Protocol,
SupportsIndex,
Union,
Expand Down Expand Up @@ -45,9 +44,9 @@ def __lt__(self, __other: Any) -> bool: ...
InnerIndexing = Union[
SupportsIndex, str, Callable[[bh.axis.Axis], int], slice, "ellipsis"
]
IndexingWithMapping = Union[InnerIndexing, Mapping[Union[int, str], InnerIndexing]]
IndexingExpr = Union[IndexingWithMapping, tuple[IndexingWithMapping, ...]]
AxisTypes = Union[AxisProtocol, tuple[int, float, float]]
IndexingWithMapping = InnerIndexing | Mapping[int | str, InnerIndexing]
IndexingExpr = IndexingWithMapping | tuple[IndexingWithMapping, ...]
AxisTypes = AxisProtocol | tuple[int, float, float]


# Workaround for bug in mplhep
Expand Down Expand Up @@ -234,11 +233,11 @@ def from_columns(
for ax in axes:
if isinstance(ax, str):
assert ax in data, f"{ax} must be present in data={list(data)}"
cats = set(data[ax])
cats = set(data[ax]) # type: ignore[arg-type]
if all(isinstance(a, str) for a in cats):
axes_list.append(hist.axis.StrCategory(sorted(cats), name=ax))
axes_list.append(hist.axis.StrCategory(sorted(cats), name=ax)) # type: ignore[arg-type]
elif all(isinstance(a, int) for a in cats):
axes_list.append(hist.axis.IntCategory(sorted(cats), name=ax))
axes_list.append(hist.axis.IntCategory(sorted(cats), name=ax)) # type: ignore[arg-type]
else:
raise TypeError(
f"{ax} must be all int or strings if axis not given"
Expand All @@ -252,7 +251,7 @@ def from_columns(

self = cls(*axes_list, storage=storage)
data_list = {x.name: data[x.name] for x in axes_list}
self.fill(**data_list, weight=weight_arr)
self.fill(**data_list, weight=weight_arr) # type: ignore[arg-type]
return self

def project(self, *args: int | str) -> Self | float | bh.accumulators.Accumulator:
Expand Down Expand Up @@ -331,7 +330,7 @@ def fill_flattened(
user_args_broadcast = broadcast[:1]
user_kwargs_broadcast = {}
non_user_kwargs_broadcast = dict(
zip(non_user_kwargs.keys(), broadcast[1:])
zip(non_user_kwargs.keys(), broadcast[1:], strict=True)
)
else:
# Result must be broadcast, so unpack and rebuild
Expand All @@ -342,22 +341,24 @@ def fill_flattened(
user_args_broadcast = ()
user_kwargs_broadcast = {
k: v
for k, v in zip(destructured, broadcast[: len(destructured)])
for k, v in zip(
destructured, broadcast[: len(destructured)], strict=True
)
if k in axis_names
}
non_user_kwargs_broadcast = dict(
zip(non_user_kwargs, broadcast[len(destructured) :])
zip(non_user_kwargs, broadcast[len(destructured) :], strict=True)
)
# Multiple args: broadcast and flatten!
else:
inputs = (*args, *kwargs.values(), *non_user_kwargs.values())
broadcast = interop.broadcast_and_flatten(inputs)
user_args_broadcast = broadcast[: len(args)]
user_kwargs_broadcast = dict(
zip(kwargs, broadcast[len(args) : len(args) + len(kwargs)])
zip(kwargs, broadcast[len(args) : len(args) + len(kwargs)], strict=True)
)
non_user_kwargs_broadcast = dict(
zip(non_user_kwargs, broadcast[len(args) + len(kwargs) :])
zip(non_user_kwargs, broadcast[len(args) + len(kwargs) :], strict=True)
)
return self.fill(
*user_args_broadcast,
Expand Down Expand Up @@ -715,7 +716,7 @@ def stack(self, axis: int | str) -> hist.stack.Stack:
stack_histograms: Iterator[BaseHist] = [ # type: ignore[assignment]
self[{axis: i}] for i in range(len(self.axes[axis]))
]
for name, h in zip(self.axes[axis], stack_histograms):
for name, h in zip(self.axes[axis], stack_histograms, strict=True):
h.name = name

return hist.stack.Stack(*stack_histograms)
Expand Down
4 changes: 2 additions & 2 deletions src/hist/interop.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from __future__ import annotations

from collections.abc import Iterator, Sequence
from typing import Any, Callable, Protocol, TypeVar, cast
from collections.abc import Callable, Iterator, Sequence
from typing import Any, Protocol, TypeVar, cast

import numpy as np

Expand Down
12 changes: 8 additions & 4 deletions src/hist/namedhist.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,19 +98,23 @@ def fill_flattened( # type: ignore[override]
# Partition into user and non-user args
user_kwargs_broadcast = {
k: v
for k, v in zip(destructured, broadcast[: len(destructured)])
for k, v in zip(
destructured, broadcast[: len(destructured)], strict=True
)
if k in axis_names
}
non_user_kwargs_broadcast = dict(
zip(non_user_kwargs, broadcast[len(destructured) :])
zip(non_user_kwargs, broadcast[len(destructured) :], strict=True)
)
# Multiple args: broadcast and flatten!
else:
inputs = (*kwargs.values(), *non_user_kwargs.values())
broadcast = interop.broadcast_and_flatten(inputs)
user_kwargs_broadcast = dict(zip(kwargs, broadcast[: len(kwargs)]))
user_kwargs_broadcast = dict(
zip(kwargs, broadcast[: len(kwargs)], strict=True)
)
non_user_kwargs_broadcast = dict(
zip(non_user_kwargs, broadcast[len(kwargs) :])
zip(non_user_kwargs, broadcast[len(kwargs) :], strict=True)
)
return self.fill(
**user_kwargs_broadcast,
Expand Down
12 changes: 6 additions & 6 deletions src/hist/plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

import inspect
import sys
from collections.abc import Iterable
from typing import Any, Callable, Literal, NamedTuple, Union
from collections.abc import Callable, Iterable
from typing import Any, Literal, NamedTuple, TypeAlias

import numpy as np

Expand Down Expand Up @@ -56,10 +56,10 @@ class PullArtists(NamedTuple):
patch_artist: list[matplotlib.patches.Rectangle]


MainAxisArtists = Union[FitResultArtists, Hist1DArtists]
MainAxisArtists: TypeAlias = FitResultArtists | Hist1DArtists

RatioArtists = Union[RatioErrorbarArtists, RatioBarArtists]
RatiolikeArtists = Union[RatioArtists, PullArtists]
RatioArtists = RatioErrorbarArtists | RatioBarArtists
RatiolikeArtists = RatioArtists | PullArtists


def __dir__() -> tuple[str, ...]:
Expand Down Expand Up @@ -631,7 +631,7 @@ def _plot_ratiolike(
perr = np.sqrt(np.diagonal(pcov))

fp_label = "Fit"
for name, value, error in zip(parnames, popt, perr):
for name, value, error in zip(parnames, popt, perr, strict=True):
fp_label += "\n "
fp_label += fit_fmt.format(name=name, value=value, error=error)
fp_kwargs["label"] = fp_label
Expand Down
4 changes: 2 additions & 2 deletions src/hist/quick_construct.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from __future__ import annotations

from collections.abc import Iterable
from typing import TYPE_CHECKING, Any, Callable
from collections.abc import Callable, Iterable
from typing import TYPE_CHECKING, Any

import numpy as np

Expand Down
16 changes: 9 additions & 7 deletions src/hist/svgplots.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from __future__ import annotations

from typing import Any, Callable
import itertools
from collections.abc import Callable
from typing import Any

import numpy as np
from boost_histogram.axis import Axis
Expand Down Expand Up @@ -70,7 +72,7 @@ def svg_hist_1d(h: hist.BaseHist) -> svg:
(edges,) = h.axes.edges
norm_edges = (edges - edges[0]) / (edges[-1] - edges[0])
density = h.density()
max_dens: float = np.amax(density) or 1 # type: ignore[redundant-expr, unreachable]
max_dens: float = np.amax(density) or 1
norm_vals: np.typing.NDArray[Any] = density / max_dens

arr: np.typing.NDArray[np.float64] = np.empty(
Expand Down Expand Up @@ -121,7 +123,7 @@ def svg_hist_1d_c(h: hist.BaseHist) -> svg:
(edges,) = h.axes.edges
norm_edges = (edges - edges[0]) / (edges[-1] - edges[0]) * np.pi * 2
density = h.density()
max_dens = np.amax(density) or 1 # type: ignore[redundant-expr, var-annotated, unreachable]
max_dens = np.amax(density) or 1
norm_vals: np.typing.NDArray[Any] = density / max_dens

arr: np.typing.NDArray[np.float64] = np.empty((2, len(norm_vals) * 2), dtype=float)
Expand All @@ -132,7 +134,7 @@ def svg_hist_1d_c(h: hist.BaseHist) -> svg:
xs = arr[1] * np.cos(arr[0])
ys = arr[1] * np.sin(arr[0])

points = " ".join(f"{x:3g},{y:.3g}" for x, y in zip(xs, ys))
points = " ".join(f"{x:3g},{y:.3g}" for x, y in zip(xs, ys, strict=True))
bins = polygon(points=points, style="fill:none; stroke:currentColor;")

center = circle(
Expand All @@ -155,13 +157,13 @@ def svg_hist_2d(h: hist.BaseHist) -> svg:
ey = -(e1 - e1[0]) / (e1[-1] - e1[0]) * height

density = h.density()
max_dens = np.amax(density) or 1 # type: ignore[redundant-expr, var-annotated, unreachable]
max_dens = np.amax(density) or 1
norm_vals: np.typing.NDArray[Any] = density / max_dens

boxes = []
for r, (up_edge, bottom_edge) in enumerate(zip(ey[:-1], ey[1:])):
for r, (up_edge, bottom_edge) in enumerate(itertools.pairwise(ey)):
ht = up_edge - bottom_edge
for c, (left_edge, right_edge) in enumerate(zip(ex[:-1], ex[1:])):
for c, (left_edge, right_edge) in enumerate(itertools.pairwise(ex)):
opacity = norm_vals[c, r]
wt = left_edge - right_edge
boxes.append(
Expand Down
Loading