Skip to content

Commit

Permalink
build: support python 3.12 (#168)
Browse files Browse the repository at this point in the history
* test: test 3.12

* try pin numpy

* skip aics

* no xarray on py312

* remove deprecated

* fix import
  • Loading branch information
tlambert03 authored Sep 28, 2023
1 parent 063da7c commit d18bee0
Show file tree
Hide file tree
Showing 9 changed files with 42 additions and 40 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ jobs:
platform: "ubuntu-latest"
- python-version: "3.8"
platform: "windows-latest"
- python-version: "3.12-dev"
platform: "ubuntu-latest"

steps:
- uses: actions/checkout@v4
Expand Down
9 changes: 6 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,32 +11,35 @@ requires-python = ">=3.7"
license = { text = "BSD 3-Clause License" }
authors = [{ email = "talley.lambert@gmail.com", name = "Talley Lambert" }]
classifiers = [
"Development Status :: 3 - Alpha",
"Development Status :: 4 - Beta",
"License :: OSI Approved :: BSD License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
]
dynamic = ["version"]
dependencies = ["resource-backed-dask-array", "typing-extensions", "numpy"]

[project.optional-dependencies]
legacy = ["imagecodecs"]
test = [
"aicsimageio",
"aicsimageio; python_version < '3.12'",
"dask[array]",
"imagecodecs",
"lxml",
"numpy >=1.26; python_version >= '3.12'",
"numpy; python_version < '3.12'",
"ome-types",
"psutil",
"pytest-codspeed",
"pytest-cov",
"pytest-pretty",
"pytest>=6.0",
"xarray",
"xarray; python_version < '3.12'",
]
dev = [
"aicsimageio",
Expand Down
10 changes: 3 additions & 7 deletions src/nd2/_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import math
import re
import warnings
from datetime import datetime
from datetime import datetime, timezone
from itertools import product
from typing import TYPE_CHECKING, BinaryIO, NamedTuple, cast

Expand Down Expand Up @@ -95,12 +95,8 @@ def is_new_format(path: str) -> bool:
return fh.read(4) == NEW_HEADER_MAGIC


def jdn_to_datetime_local(jdn: float) -> datetime:
return datetime.fromtimestamp((jdn - 2440587.5) * 86400.0)


def jdn_to_datetime_utc(jdn: float) -> datetime:
return datetime.utcfromtimestamp((jdn - 2440587.5) * 86400.0)
def jdn_to_datetime(jdn: float, tz: timezone = timezone.utc) -> datetime:
return datetime.fromtimestamp((jdn - 2440587.5) * 86400.0, tz)


def rgb_int_to_tuple(rgb: int) -> tuple[int, int, int]:
Expand Down
4 changes: 1 addition & 3 deletions src/nd2/readers/_modern/modern_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -545,9 +545,7 @@ def _acquisition_date(self) -> datetime.datetime | str | None:

time = self._cached_global_metadata().get("time", {})
jdn = time.get("absoluteJulianDayNumber")
if jdn:
return _util.jdn_to_datetime_utc(jdn)
return None
return _util.jdn_to_datetime(jdn) if jdn else None

def binary_data(self) -> BinaryLayers | None:
from nd2._binary import BinaryLayer, BinaryLayers, decode_binary_mask
Expand Down
21 changes: 15 additions & 6 deletions tests/test_events.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,30 @@
from pathlib import Path

import nd2
import pandas as pd
import numpy as np
import pytest

DATA = Path(__file__).parent / "data"


EXPECTED_COORDS = ([0] * 5 + [1000] * 5 + [2000] * 5) * 3


@pytest.mark.parametrize("fname", ["t3p3z5c3.nd2", "t3p3c3z5.nd2", "t1t1t1p3c3z5.nd2"])
def test_events(fname: str) -> None:
with nd2.ND2File(DATA / fname) as f:
events = f.events()
events = f.events(orient="list")
assert np.array_equal(events["X Coord [µm]"], EXPECTED_COORDS)
assert np.array_equal(events["Y Coord [µm]"], EXPECTED_COORDS)


df = pd.DataFrame(events)
expected_coords = ([0] * 5 + [1000] * 5 + [2000] * 5) * 3
assert all(df["X Coord [µm]"] == expected_coords)
assert all(df["Y Coord [µm]"] == expected_coords)
@pytest.mark.parametrize("fname", ["t3p3z5c3.nd2", "t3p3c3z5.nd2", "t1t1t1p3c3z5.nd2"])
def test_events_pandas(fname: str) -> None:
pd = pytest.importorskip("pandas")
with nd2.ND2File(DATA / fname) as f:
df = pd.DataFrame(f.events())
assert all(df["X Coord [µm]"] == EXPECTED_COORDS)
assert all(df["Y Coord [µm]"] == EXPECTED_COORDS)
assert all(df["Z-Series"] == [-1, -0.5, 0, 0.5, 1] * 9)
assert all(df["T Index"] == [0] * 15 + [1] * 15 + [2] * 15)
assert all(df["Z Index"] == [0, 1, 2, 3, 4] * 9)
Expand Down
13 changes: 9 additions & 4 deletions tests/test_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,17 @@

import dask.array as da
import pytest
import xarray as xr
from nd2 import ND2File, _util, structures
from nd2._parse._chunk_decode import ND2_FILE_SIGNATURE

sys.path.append(str(Path(__file__).parent.parent / "scripts"))
from nd2_describe import get_nd2_stats # noqa: E402

try:
import xarray as xr
except ImportError:
xr = None

if TYPE_CHECKING:
from typing_extensions import Literal

Expand Down Expand Up @@ -104,9 +108,10 @@ def test_metadata_extraction_legacy(old_nd2):
assert isinstance(nd.experiment, list)
assert isinstance(nd.text_info, dict)
assert isinstance(nd.metadata, structures.Metadata)
xarr = nd.to_xarray()
assert isinstance(xarr, xr.DataArray)
assert isinstance(xarr.data, da.Array)
if xr is not None:
xarr = nd.to_xarray()
assert isinstance(xarr, xr.DataArray)
assert isinstance(xarr.data, da.Array)

with pytest.warns(UserWarning, match="not implemented"):
nd.events()
Expand Down
5 changes: 4 additions & 1 deletion tests/test_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import dask.array as da
import numpy as np
import pytest
import xarray as xr
from nd2 import ND2File, imread
from nd2._parse._chunk_decode import get_version
from nd2._util import AXIS, is_supported_file
Expand All @@ -23,6 +22,7 @@ def test_read_safety(new_nd2: Path):

def test_position(new_nd2: Path):
"""use position to extract a single stage position with asarray."""
pytest.importorskip("xarray")
if new_nd2.stat().st_size > 250_000_000:
pytest.skip("skipping read on big files")
with ND2File(new_nd2) as nd:
Expand Down Expand Up @@ -53,6 +53,7 @@ def test_dask_closed(single_nd2):


def test_full_read(new_nd2):
pytest.importorskip("xarray")
with ND2File(new_nd2) as nd:
if new_nd2.stat().st_size > 500_000_000:
pytest.skip("skipping full read on big files")
Expand Down Expand Up @@ -82,6 +83,7 @@ def test_full_read_legacy(old_nd2):


def test_xarray(new_nd2):
xr = pytest.importorskip("xarray")
with ND2File(new_nd2) as nd:
xarr = nd.to_xarray()
assert isinstance(xarr, xr.DataArray)
Expand All @@ -90,6 +92,7 @@ def test_xarray(new_nd2):


def test_xarray_legacy(old_nd2):
xr = pytest.importorskip("xarray")
with ND2File(old_nd2) as nd:
xarr = nd.to_xarray()
assert isinstance(xarr, xr.DataArray)
Expand Down
2 changes: 2 additions & 0 deletions tests/test_readme.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

@pytest.mark.skipif(os.name == "nt", reason="paths annoying on windows")
def test_readme(capsys):
pytest.importorskip("xarray")

code = README.read_text().split("```python")[1].split("```")[0]
code = code.replace("some_file.nd2", str(SAMPLE.absolute()))
exec(code)
Expand Down
16 changes: 0 additions & 16 deletions tests/test_segfaults.py

This file was deleted.

0 comments on commit d18bee0

Please sign in to comment.