Repository: scikit-hep/hist
Pull request #535 (2023-09-12 15:07:42)

feat: add features from boost-histogram 1.4
- feat: adding overflow disable option to cat axes
- ci: add 3.12 testing



Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 6d473da..071f0b2 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -37,6 +37,7 @@ jobs:
         - "3.9"
         - "3.10"
         - "3.11"
+        - "3.12"
     name: Check Python ${{ matrix.python-version }}
     steps:
     - uses: actions/checkout@v4
@@ -44,6 +45,7 @@ jobs:
     - uses: actions/setup-python@v4
       with:
         python-version: ${{ matrix.python-version }}
+        allow-prereleases: true
 
     - name: Requirements check
       run: python -m pip list
@@ -55,9 +57,9 @@ jobs:
       run: python -m pytest
 
     - name: Install plotting requirements too
-      if: matrix.python-version != '3.10'
+      if: matrix.python-version != '3.12'
       run: python -m pip install -e ".[test,plot]"
 
     - name: Test plotting too
-      if: matrix.python-version != '3.10'
+      if: matrix.python-version != '3.12'
       run: python -m pytest --mpl
diff --git a/docs/user-guide/notebooks/Histogram.ipynb b/docs/user-guide/notebooks/Histogram.ipynb
index cda9dd5..0aa0b31 100644
--- a/docs/user-guide/notebooks/Histogram.ipynb
+++ b/docs/user-guide/notebooks/Histogram.ipynb
@@ -220,7 +220,7 @@
     "    Unif=np.random.uniform(size=1000),\n",
     "    Greet=[\"hi\"] * 800 + [\"hello\"] * 200,\n",
     "    Yes=[True] * 600 + [False] * 400,\n",
-    "    Int=np.ones(1000),\n",
+    "    Int=np.ones(1000, dtype=int),\n",
     ")"
    ]
   },
diff --git a/pyproject.toml b/pyproject.toml
index 26b88cc..9b6de15 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -22,6 +22,7 @@ classifiers = [
     "Programming Language :: Python :: 3.9",
     "Programming Language :: Python :: 3.10",
     "Programming Language :: Python :: 3.11",
+    "Programming Language :: Python :: 3.12",
     "Topic :: Scientific/Engineering",
     "Topic :: Scientific/Engineering :: Information Analysis",
     "Topic :: Scientific/Engineering :: Mathematics",
@@ -37,7 +38,8 @@ requires-python = ">=3.7"
 dependencies = [
     "boost-histogram>=1.3.1,<1.5",
     "histoprint>=2.2.0",
-    "numpy>=1.14.5",
+    'numpy>=1.14.5;python_version<"3.12"',
+    'numpy>=1.26.0b1;python_version>="3.12"',
     'typing-extensions>=4;python_version<"3.11"',
 ]
 dynamic = ["version"]
@@ -72,8 +74,8 @@ dask = [
 test = [
     "pytest >=6",
     "pytest-mpl >=0.12",
-    "dask[dataframe] >=2022; python_version>='3.8'",
-    "dask_histogram >=2023.1; python_version>='3.8'",
+    "dask[dataframe] >=2022; python_version>='3.8' and python_version<'3.12'",
+    "dask_histogram >=2023.1; python_version>='3.8' and python_version<'3.12'",
 ]
 dev = [
     "pytest >=6",
@@ -185,7 +187,7 @@ messages_control.disable = [
 [tool.ruff]
 select = [
   "E", "F", "W", # flake8
-  "B",  "B904",  # flake8-bugbear
+  "B",           # flake8-bugbear
   "I",           # isort
   "ARG",         # flake8-unused-arguments
   "C4",          # flake8-comprehensions
diff --git a/src/hist/axis/__init__.py b/src/hist/axis/__init__.py
index 1735809..4b20033 100644
--- a/src/hist/axis/__init__.py
+++ b/src/hist/axis/__init__.py
@@ -2,6 +2,7 @@ from __future__ import annotations
 
 from typing import Any, Iterable
 
+import boost_histogram as bh
 import boost_histogram.axis as bha
 
 import hist
@@ -212,12 +213,23 @@ class IntCategory(AxesMixin, bha.IntCategory, family=hist):
         label: str = "",
         metadata: Any = None,
         growth: bool = False,
+        flow: bool = True,
+        overflow: bool | None = None,
         __dict__: dict[str, Any] | None = None,
     ) -> None:
+        has_flow = flow if overflow is None else overflow
+        if tuple(int(x) for x in bh.__version__.split(".")[:2]) < (1, 4):
+            if not has_flow:
+                msg = "Boost-histogram 1.4+ required for flowless Category axes"
+                raise TypeError(msg)
+            kwargs = {}
+        else:
+            kwargs = {"overflow": has_flow}
         super().__init__(
             categories,
             metadata=metadata,
             growth=growth,
+            **kwargs,
             __dict__=__dict__,
         )
         self._ax.metadata["name"] = name
@@ -235,12 +247,23 @@ class StrCategory(AxesMixin, bha.StrCategory, family=hist):
         label: str = "",
         metadata: Any = None,
         growth: bool = False,
+        flow: bool = True,
+        overflow: bool | None = None,
         __dict__: dict[str, Any] | None = None,
     ) -> None:
+        has_flow = flow if overflow is None else overflow
+        if tuple(int(x) for x in bh.__version__.split(".")[:2]) < (1, 4):
+            if not has_flow:
+                msg = "Boost-histogram 1.4+ required for flowless Category axes"
+                raise TypeError(msg)
+            kwargs = {}
+        else:
+            kwargs = {"overflow": has_flow}
         super().__init__(
             categories,
             metadata=metadata,
             growth=growth,
+            **kwargs,
             __dict__=__dict__,
         )
         self._ax.metadata["name"] = name


Test patch:
diff --git a/tests/test_general.py b/tests/test_general.py
index 06e8834..5c0d85e 100644
--- a/tests/test_general.py
+++ b/tests/test_general.py
@@ -198,8 +198,8 @@ def test_general_fill():
 
     # IntCategory
     h = Hist(
-        axis.IntCategory(range(10), name="x"),
-        axis.IntCategory(range(10), name="y"),
+        axis.IntCategory(range(10), name="x", flow=False),
+        axis.IntCategory(range(10), name="y", overflow=False),
         axis.IntCategory(range(2), name="z"),
     ).fill(
         x=[3, 3, 3, 4, 5, 5, 5],
@@ -207,6 +207,10 @@ def test_general_fill():
         z=[0, 0, 1, 1, 1, 1, 1],
     )
 
+    assert not h.axes[0].traits.overflow
+    assert not h.axes[1].traits.overflow
+    assert h.axes[2].traits.overflow
+
     z_one_only = h[{2: bh.loc(1)}]
     for idx_x in range(10):
         for idx_y in range(10):
@@ -219,8 +223,8 @@ def test_general_fill():
 
     # StrCategory
     h = Hist(
-        axis.StrCategory("FT", name="x"),
-        axis.StrCategory(list("FT"), name="y"),
+        axis.StrCategory("FT", name="x", flow=False),
+        axis.StrCategory(list("FT"), name="y", overflow=False),
         axis.StrCategory(["F", "T"], name="z"),
     ).fill(
         ["T", "T", "T", "T", "T", "F", "T"],
@@ -228,6 +232,10 @@ def test_general_fill():
         ["F", "F", "T", "T", "T", "T", "T"],
     )
 
+    assert not h.axes[0].traits.overflow
+    assert not h.axes[1].traits.overflow
+    assert h.axes[2].traits.overflow
+
     z_one_only = h[{2: bh.loc("T")}]
     assert z_one_only[bh.loc("F"), bh.loc("F")] == 0
     assert z_one_only[bh.loc("F"), bh.loc("T")] == 1
@@ -282,7 +290,7 @@ def test_general_access():
     h = Hist(
         axis.Regular(50, -5, 5, name="Norm", label="normal distribution"),
         axis.Regular(50, -5, 5, name="Unif", label="uniform distribution"),
-        axis.StrCategory(["hi", "hello"], name="Greet"),
+        axis.StrCategory(["hi", "hello"], name="Greet", flow=False),
         axis.Boolean(name="Yes"),
         axis.Integer(0, 1000, name="Int"),
     ).fill(