Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
862e800
Update numpy version constraint to allow 1.24.x
rly Nov 21, 2025
51a42e9
Merge pull request #1 from rly/feature/numpy-1.24.0-support
rly Nov 21, 2025
a25e4c7
Fix pytest 9 deprecation warnings
rly Nov 21, 2025
0903e7f
Merge pull request #3 from rly/feature/fix-pytest-deprecations
rly Nov 21, 2025
d88e9c4
Update moto to v5+ and fix mock_s3 to mock_aws imports
rly Nov 21, 2025
c80bcfe
Remove redundant decorator
rly Nov 21, 2025
29ac167
Merge pull request #4 from rly/feature/fix-cloud-cache-tests
rly Nov 22, 2025
a47faab
Fix pynwb compatibility issues
rly Nov 22, 2025
a335024
Fix pynwb compatibility issues
rly Nov 22, 2025
5341672
Merge pull request #5 from rly/feature/fix-nwb-tests
rly Nov 22, 2025
79c4c2e
Fix pytest.warns(None) TypeError in newer pytest versions
rly Nov 22, 2025
31f3763
Fix GitHub Actions for macos-latest ARM64 transition
rly Nov 22, 2025
078c2a7
Fix aiohttp ClientSession creation outside async context
rly Nov 22, 2025
6c390a5
Merge pull request #6 from rly/feature/fix-misc-test-errors
rly Nov 22, 2025
89c8c45
Fix numpy 1.24+ inhomogeneous array in gaze_mapper
rly Nov 23, 2025
88fe8d9
Fix test_fitgaussian2D_failure mock for numpy 1.24+ compatibility
rly Nov 23, 2025
bc9ca74
Merge pull request #7 from rly/feature/fix-inhomogeneous-array-errors
rly Nov 23, 2025
8a2e8f8
Drop Python 3.8/3.9 support and handle missing glymur gracefully
rly Nov 24, 2025
3f7c9cf
Remove Python 3.12 support due to numpy 1.24.x incompatibility
rly Nov 24, 2025
a1d6897
Merge pull request #8 from rly/feature/fix-jpeg-glymur-errors
rly Nov 24, 2025
3cb026c
Update macOS CI runners to macos-15-intel and add macos-latest (ARM64)
rly Jan 11, 2026
00608e7
Add tolerance for curve fitting tests on ARM64
rly Jan 11, 2026
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
15 changes: 8 additions & 7 deletions .github/workflows/github-actions-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
name: Lint
runs-on: "ubuntu-latest"
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: flake8 linting
run: |
pip install flake8
Expand All @@ -43,15 +43,16 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: ["macos-latest", "windows-latest", "ubuntu-latest"]
python-version: ["3.8", "3.9", "3.10", "3.11"]
# macos-15-intel for x86_64, macos-latest for ARM64
os: ["macos-15-intel", "macos-latest", "windows-latest", "ubuntu-latest"]
python-version: ["3.10", "3.11"]
fail-fast: false
defaults:
run:
shell: bash -l {0}
steps:
- uses: actions/checkout@v2
- uses: conda-incubator/setup-miniconda@v2
- uses: actions/checkout@v4
- uses: conda-incubator/setup-miniconda@v3
with:
auto-update-conda: true
python-version: ${{ matrix.python-version }}
Expand All @@ -71,9 +72,9 @@ jobs:
runs-on: ["self-hosted"]
strategy:
matrix:
image: ["allensdk_local_py38:latest"]
image: ["allensdk_local_py310:latest"]
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: run test in docker
run: |
docker run \
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ jobs:
runs-on: ["self-hosted"]
strategy:
matrix:
image: ["allensdk_local_py38:latest"]
image: ["allensdk_local_py310:latest"]
branch: ["master", "rc/**"]
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
with:
ref: ${{ matrix.branch }}
- name: run test in docker
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/notebook_runner.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ jobs:
strategy:
matrix:
os: ["ubuntu-latest"]
python-version: ["3.8"]
python-version: ["3.10"]
fail-fast: false
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ def from_nwb(
cls,
nwbfile: NWBFile
) -> "RunningAcquisition":
running_module = nwbfile.modules['running']
running_module = nwbfile.processing['running']
dx_interface = running_module.get_data_interface('dx')

dx = dx_interface.data
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ def from_nwb(
nwbfile: NWBFile,
filtered=True
) -> "RunningSpeed":
running_module = nwbfile.modules['running']
running_module = nwbfile.processing['running']
interface_name = 'speed' if filtered else 'speed_unfiltered'
running_interface = running_module.get_data_interface(interface_name)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os
import warnings
import numpy as np
from pathlib import Path
from typing import Optional, List, Dict
Expand Down Expand Up @@ -184,17 +185,44 @@ def to_nwb(self, nwbfile: NWBFile,

nwbfile.add_stimulus_template(visual_stimulus_image_series)

# DEPRECATED (11/2025): _add_image_index_to_nwb is no longer called.
# The IndexSeries using indexed_timeseries is deprecated in pynwb 2.x
# and would require significant refactoring to use indexed_images instead.
# Since this NWB writing code will not be used for new data generation,
# we skip this step rather than refactor. The stimulus template data
# is still written above; only the IndexSeries linking is skipped.
# See: https://pynwb.readthedocs.io/en/stable/pynwb.image.html#pynwb.image.IndexSeries
if 'image_index' in stimulus_presentations.value \
and self._image_template_key is not None:
nwbfile = self._add_image_index_to_nwb(
nwbfile=nwbfile, presentations=stimulus_presentations)
warnings.warn(
"As of 11/2025, Templates.to_nwb() no longer adds the image "
"index (IndexSeries) to the NWB file. The IndexSeries "
"indexed_timeseries field is deprecated in pynwb 2.x. "
"The stimulus template data is still written.",
UserWarning,
stacklevel=2
)

return nwbfile

def _add_image_index_to_nwb(
self, nwbfile: NWBFile, presentations: Presentations):
"""Adds the image index and start_time for all stimulus templates
to NWB"""
to NWB

.. deprecated:: 2.16.3
This method is deprecated as of 11/2025. The IndexSeries
indexed_timeseries field is deprecated in pynwb 2.x. This method
is no longer called from to_nwb() and will be removed in a future
release.
"""
warnings.warn(
"_add_image_index_to_nwb is deprecated and will be removed in a "
"future release. The IndexSeries indexed_timeseries field is "
"deprecated in pynwb 2.x.",
DeprecationWarning,
stacklevel=2
)
stimulus_templates = self.value[self._image_template_key]
presentations = presentations.value

Expand All @@ -208,7 +236,7 @@ def _add_image_index_to_nwb(
image_index = IndexSeries(
name=nwb_template.name,
data=stimulus_index['image_index'].values,
unit='None',
unit='N/A',
indexed_timeseries=nwb_template,
timestamps=stimulus_index['start_time'].values)
nwbfile.add_stimulus(image_index)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,10 @@ def write_bytes(path: str, stream: Iterable[bytes]):
class AsyncHttpEngine(HttpEngine):

def __init__(
self,
scheme: str,
host: str,
session: Optional[aiohttp.ClientSession] = None,
self,
scheme: str,
host: str,
session: Optional[aiohttp.ClientSession] = None,
**kwargs
):
""" Simple tool for making asynchronous streaming http requests.
Expand All @@ -105,11 +105,11 @@ def __init__(
----------
scheme :
e.g "http" or "https"
host :
host :
will be used as the base for request urls
session :
If provided, this preconstructed session will be used rather than
a new one. Keep in mind that AsyncHttpEngine closes its session
session :
If provided, this preconstructed session will be used rather than
a new one. Keep in mind that AsyncHttpEngine closes its session
when it is garbage collected!
**kwargs :
Will be passed to parent.
Expand All @@ -119,14 +119,25 @@ def __init__(
super(AsyncHttpEngine, self).__init__(scheme, host, **kwargs)

if session:
self.session = session
self._session = session
self._owns_session = False
warnings.warn(
"Recieved preconstructed session, ignoring timeout parameter."
)
else:
self.session = aiohttp.ClientSession(
# Defer session creation until actually needed in an async context
# (aiohttp 3.9+ requires ClientSession to be created within an event loop)
self._session = None
self._owns_session = True

@property
def session(self) -> aiohttp.ClientSession:
"""Lazily create the aiohttp session when first accessed."""
if self._session is None:
self._session = aiohttp.ClientSession(
timeout=aiohttp.client.ClientTimeout(self.timeout)
)
return self._session

async def _stream_coroutine(
self,
Expand Down Expand Up @@ -169,10 +180,10 @@ def stream(
return functools.partial(self._stream_coroutine, route)

def __del__(self):
if hasattr(self, "session"):
if hasattr(self, "_session") and self._session is not None:
nest_asyncio.apply()
loop = asyncio.get_event_loop()
loop.run_until_complete(self.session.close())
loop.run_until_complete(self._session.close())

@staticmethod
def write_bytes(
Expand Down
4 changes: 3 additions & 1 deletion allensdk/brain_observatory/gaze_mapping/_gaze_mapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,9 @@ def pupil_position_on_monitor_in_degrees(self,

mag = np.linalg.norm(self.monitor.position)
meridian = np.degrees(np.arctan(x / mag))
elevation = np.degrees(np.arctan(y / np.linalg.norm([x, mag], axis=0)))
# Use np.vstack to create a homogeneous 2D array (numpy 1.24+ compatibility)
elevation = np.degrees(np.arctan(y / np.linalg.norm(
np.vstack([x, np.full_like(x, mag, dtype=float)]), axis=0)))

angles = np.vstack([meridian, elevation]).T

Expand Down
6 changes: 3 additions & 3 deletions allensdk/brain_observatory/nwb/nwb_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,9 @@ def get_running_speed(self, lowpass=True) -> RunningSpeed:
"""

interface_name = 'speed' if lowpass else 'speed_unfiltered'
values = self.nwbfile.modules['running'].get_data_interface(
values = self.nwbfile.processing['running'].get_data_interface(
interface_name).data[:]
timestamps = self.nwbfile.modules['running'].get_data_interface(
timestamps = self.nwbfile.processing['running'].get_data_interface(
interface_name).timestamps[:]

return RunningSpeed(
Expand All @@ -87,7 +87,7 @@ def get_image(self, name, module, image_api=None) -> sitk.Image:
if image_api is None:
image_api = ImageApi

nwb_img = self.nwbfile.modules[module].get_data_interface(
nwb_img = self.nwbfile.processing[module].get_data_interface(
'images')[name]
data = nwb_img.data
resolution = nwb_img.resolution # px/cm
Expand Down
64 changes: 48 additions & 16 deletions allensdk/mouse_connectivity/grid/utilities/image_utilities.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,23 @@
from __future__ import division
import logging
import os
import sys

from six import iteritems
import numpy as np
import SimpleITK as sitk
from skimage.draw import polygon

from allensdk.config.manifest import Manifest


if sys.version_info[0] > 2:
failed_import = (ImportError, ModuleNotFoundError)
else:
failed_import = (ImportError,)
# Exception types to catch when importing optional JPEG2000 libraries
failed_import = (ImportError, ModuleNotFoundError)


# use np_sitk_convert or sitk_np_convert to access
# TODO: check if this already exists. If not: add more dtypes
# it does not
NUMPY_SITK_TYPE_LOOKUP = {np.dtype(np.float32): sitk.sitkFloat32}
SITK_NUMPY_TYPE_LOOKUP = {v: k for k, v in iteritems(NUMPY_SITK_TYPE_LOOKUP)}
SITK_NUMPY_TYPE_LOOKUP = {v: k for k, v in NUMPY_SITK_TYPE_LOOKUP.items()}


# ITK/Numpy
Expand Down Expand Up @@ -273,19 +269,55 @@ def __read_intensity_image_with_glymur(path):
return glymur.Jp2k(path)[:]


try:
def _get_jpeg_readers():
"""Get the appropriate JPEG2000 readers based on available libraries.

Returns a tuple of (read_segmentation_image, read_intensity_image) functions.
Raises ImportError if neither jpeg_twok nor glymur is available.
"""
# we use a proprietary library called kakadu internally
# (jpeg_twok is a python interface around that library)
# kakadu offers really good performance as well as support for
# advanced jp2 features
# however, since it is proprietary, we can't share it
# alongside the allensdk,
# so we default to glymur (a python openjpeg) for external users.
sys.path.append('/shared/bioapps/itk/itk_shared/jp2/build')
import jpeg_twok
read_segmentation_image = __read_segmentation_image_with_kakadu
read_intensity_image = __read_intensity_image_with_kakadu
except failed_import:
import glymur
read_segmentation_image = __read_segmentation_image_with_glymur
read_intensity_image = __read_intensity_image_with_glymur
try:
sys.path.append('/shared/bioapps/itk/itk_shared/jp2/build')
import jpeg_twok
return (__read_segmentation_image_with_kakadu,
__read_intensity_image_with_kakadu)
except failed_import:
pass

try:
import glymur
return (__read_segmentation_image_with_glymur,
__read_intensity_image_with_glymur)
except failed_import:
pass

raise ImportError(
"Neither jpeg_twok nor glymur is available. "
"Please install glymur (pip install glymur) and ensure openjpeg "
"is installed on your system."
)


try:
read_segmentation_image, read_intensity_image = _get_jpeg_readers()
except ImportError:
# Define placeholder functions that raise on use rather than on import
def read_segmentation_image(path):
raise ImportError(
"Neither jpeg_twok nor glymur is available. "
"Please install glymur (pip install glymur) and ensure openjpeg "
"is installed on your system."
)

def read_intensity_image(path, reduce_level=None, channel=None):
raise ImportError(
"Neither jpeg_twok nor glymur is available. "
"Please install glymur (pip install glymur) and ensure openjpeg "
"is installed on your system."
)
Loading