Skip to content

0.14.4: failing test: neo/test/coretest/test_analogsignal.py::TestAnalogSignalArrayMethods::test__time_slice_deepcopy_data #1848

@sanjayankur31

Description

@sanjayankur31

Describe the bug
While updating the Fedora package to 0.14.4, we're seeing a failing test that is reproducible locally on a Fedora 44 machine.

To Reproduce

  • On a Fedora machine, download the release tar and extract it.
  • Create a new virtual environment
  • install the package: pip install .[dev]
  • run pytest, e.g.: pytest -v -s --ignore neo/test/iotest/ --ignore neo/test/rawiotest/ --deselect=neo/test/utils/test_datasets.py::TestDownloadDataset

Expected behaviour
Tests pass

Observed behaviour

$ pytest -v -s --ignore neo/test/iotest/ --ignore neo/test/rawiotest/  --deselect=neo/test/utils/test_datasets.py::TestDownloadDataset
==================================================================================== test session starts =====================================================================================
platform linux -- Python 3.14.4, pytest-9.0.3, pluggy-1.6.0 -- /home/asinha/.local/share/virtualenvs/neo/bin/python3
cachedir: .pytest_cache
rootdir: /home/asinha/Documents/02_Code/00_mine/fedora-stuff/packages/packages/python-neo/python-neo-0.14.4
configfile: pyproject.toml
collected 642 items / 1 deselected / 641 selected

...
...
========================================================================================== FAILURES ==========================================================================================
________________________________________________________________ TestAnalogSignalArrayMethods.test__time_slice_deepcopy_data _________________________________________________________________

self = <neo.test.coretest.test_analogsignal.TestAnalogSignalArrayMethods testMethod=test__time_slice_deepcopy_data>

    def test__time_slice_deepcopy_data(self):
        result = self.signal1.time_slice(None, None)

        # Change values of original array
        self.signal1[2] = 7.3 * self.signal1.units

>       np.testing.assert_raises(AssertionError, assert_array_equal, self.signal1, result)

neo/test/coretest/test_analogsignal.py:551:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/home/asinha/.local/share/virtualenvs/neo/lib64/python3.14/site-packages/numpy/testing/_private/utils.py:1121: in assert_array_equal
    assert_array_compare(operator.__eq__, actual, desired, err_msg=err_msg,
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

    def assert_array_compare(comparison, x, y, err_msg='', verbose=True, header='',
                             precision=6, equal_nan=True, equal_inf=True,
                             *, strict=False, names=('ACTUAL', 'DESIRED')):
        __tracebackhide__ = True  # Hide traceback for py.test
        from numpy._core import all, array2string, errstate, inf, isnan, max, object_

        x = np.asanyarray(x)
        y = np.asanyarray(y)

        # original array for output formatting
        ox, oy = x, y

        def isnumber(x):
            return type(x.dtype)._is_numeric

        def istime(x):
            return x.dtype.char in "Mm"

        def isvstring(x):
            return x.dtype.char == "T"

        def robust_any_difference(x, y):
            # We include work-arounds here to handle three types of slightly
            # pathological ndarray subclasses:
            # (1) all() on fully masked arrays returns np.ma.masked, so we use != True
            #     (np.ma.masked != True evaluates as np.ma.masked, which is falsy).
            # (2) __eq__ on some ndarray subclasses returns Python booleans
            #     instead of element-wise comparisons, so we cast to np.bool() in
            #     that case (or in case __eq__ returns some other value with no
            #     all() method).
            # (3) subclasses with bare-bones __array_function__ implementations may
            #     not implement np.all(), so favor using the .all() method
            # We are not committed to supporting cases (2) and (3), but it's nice to
            # support them if possible.
            result = x == y
            if not hasattr(result, "all") or not callable(result.all):
                result = np.bool(result)
            return result.all() != True

        def func_assert_same_pos(x, y, func=isnan, hasval='nan'):
            """Handling nan/inf.

            Combine results of running func on x and y, checking that they are True
            at the same locations.

            """
            __tracebackhide__ = True  # Hide traceback for py.test

            x_id = func(x)
            y_id = func(y)
            if robust_any_difference(x_id, y_id):
                msg = build_err_msg(
                    [x, y],
                    err_msg + '\n%s location mismatch:'
                    % (hasval), verbose=verbose, header=header,
                    names=names,
                    precision=precision)
                raise AssertionError(msg)
            # If there is a scalar, then here we know the array has the same
            # flag as it everywhere, so we should return the scalar flag.
            # np.ma.masked is also handled and converted to np.False_ (even if the other
            # array has nans/infs etc.; that's OK given the handling later of fully-masked
            # results).
            if isinstance(x_id, bool) or x_id.ndim == 0:
                return np.bool(x_id)
            elif isinstance(y_id, bool) or y_id.ndim == 0:
                return np.bool(y_id)
            else:
                return y_id

        def assert_same_inf_values(x, y, infs_mask):
            """
            Verify all inf values match in the two arrays
            """
            __tracebackhide__ = True  # Hide traceback for py.test

            if not infs_mask.any():
                return
            if x.ndim > 0 and y.ndim > 0:
                x = x[infs_mask]
                y = y[infs_mask]
            else:
                assert infs_mask.all()

            if robust_any_difference(x, y):
                msg = build_err_msg(
                    [x, y],
                    err_msg + '\ninf values mismatch:',
                    verbose=verbose, header=header,
                    names=names,
                    precision=precision)
                raise AssertionError(msg)

        try:
            if strict:
                cond = x.shape == y.shape and x.dtype == y.dtype
            else:
                cond = (x.shape == () or y.shape == ()) or x.shape == y.shape
            if not cond:
                if x.shape != y.shape:
                    reason = f'\n(shapes {x.shape}, {y.shape} mismatch)'
                else:
                    reason = f'\n(dtypes {x.dtype}, {y.dtype} mismatch)'
                msg = build_err_msg([x, y],
                                    err_msg
                                    + reason,
                                    verbose=verbose, header=header,
                                    names=names,
                                    precision=precision)
                raise AssertionError(msg)

            flagged = np.bool(False)
            if isnumber(x) and isnumber(y):
                if equal_nan:
                    flagged = func_assert_same_pos(x, y, func=isnan, hasval='nan')

                if equal_inf:
                    # If equal_nan=True, skip comparing nans below for equality if they are
                    # also infs (e.g. inf+nanj) since that would always fail.
                    isinf_func = lambda xy: np.logical_and(np.isinf(xy), np.invert(flagged))
                    infs_mask = func_assert_same_pos(
                        x, y,
                        func=isinf_func,
                        hasval='inf')
                    assert_same_inf_values(x, y, infs_mask)
                    flagged |= infs_mask

            elif istime(x) and istime(y):
                # If one is datetime64 and the other timedelta64 there is no point
                if equal_nan and x.dtype.type == y.dtype.type:
                    flagged = func_assert_same_pos(x, y, func=isnat, hasval="NaT")

            elif isvstring(x) and isvstring(y):
                dt = x.dtype
                if equal_nan and dt == y.dtype and hasattr(dt, 'na_object'):
                    is_nan = (isinstance(dt.na_object, float) and
                              np.isnan(dt.na_object))
                    bool_errors = 0
                    try:
                        bool(dt.na_object)
                    except TypeError:
                        bool_errors = 1
                    if is_nan or bool_errors:
                        # nan-like NA object
                        flagged = func_assert_same_pos(
                            x, y, func=isnan, hasval=x.dtype.na_object)

            if flagged.ndim > 0:
                x, y = x[~flagged], y[~flagged]
                # Only do the comparison if actual values are left
                if x.size == 0:
                    return
            elif flagged:
                # no sense doing comparison if everything is flagged.
                return

            val = comparison(x, y)
            invalids = np.logical_not(val)

            if isinstance(val, bool):
                cond = val
                reduced = array([val])
            else:
                reduced = val.ravel()
                cond = reduced.all()

            # The below comparison is a hack to ensure that fully masked
            # results, for which val.ravel().all() returns np.ma.masked,
            # do not trigger a failure (np.ma.masked != True evaluates as
            # np.ma.masked, which is falsy).
            if cond != True:
                n_mismatch = reduced.size - reduced.sum(dtype=intp)
                n_elements = flagged.size if flagged.ndim != 0 else reduced.size
                percent_mismatch = 100 * n_mismatch / n_elements
                remarks = [f'Mismatched elements: {n_mismatch} / {n_elements} '
                           f'({percent_mismatch:.3g}%)']
                if invalids.ndim != 0:
                    if flagged.ndim > 0:
>                       positions = np.argwhere(np.asarray(~flagged))[invalids]
                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E                       IndexError: boolean index did not match indexed array along axis 0; size of axis is 55 but size of corresponding boolean axis is 11

/home/asinha/.local/share/virtualenvs/neo/lib64/python3.14/site-packages/numpy/testing/_private/utils.py:911: IndexError
====================================================================================== warnings summary ======================================================================================
neo/test/coretest/test_spiketrainlist.py::TestSpikeTrainList::test_create_from_spiketrain_list
  /home/asinha/Documents/02_Code/00_mine/fedora-stuff/packages/packages/python-neo/python-neo-0.14.4/neo/core/spiketrainlist.py:444: UserWarning: Found multiple values of t_stop, returning the latest
    warnings.warn("Found multiple values of t_stop, returning the latest")

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
================================================================================== short test summary info ===================================================================================
FAILED neo/test/coretest/test_analogsignal.py::TestAnalogSignalArrayMethods::test__time_slice_deepcopy_data - IndexError: boolean index did not match indexed array along axis 0; size of axis is 55 but size of corresponding boolean axis is 11

Environment:

  • OS: Linux, Fedora 44
  • Python version: 3.14.4
  • Neo version: 0.14.4
  • NumPy version: 2.4.4

Additional context

All packages:

uv pip freeze
Using Python 3.14.4 environment at: /home/asinha/.local/share/virtualenvs/neo
black==26.3.1
build==1.5.0
certifi==2026.4.22
cffi==2.0.0
charset-normalizer==3.4.7
click==8.3.3
cryptography==48.0.0
docutils==0.22.4
id==1.6.1
idna==3.14
iniconfig==2.3.0
jaraco-classes==3.4.0
jaraco-context==6.1.2
jaraco-functools==4.4.0
jeepney==0.9.0
keyring==25.7.0
markdown-it-py==4.2.0
mdurl==0.1.2
more-itertools==11.0.2
mypy-extensions==1.1.0
neo @ file:///home/asinha/Documents/02_Code/00_mine/fedora-stuff/packages/packages/python-neo/python-neo-0.14.4
nh3==0.3.5
numpy==2.4.4
packaging==26.2
pathspec==1.1.1
platformdirs==4.9.6
pluggy==1.6.0
pycparser==3.0
pygments==2.20.0
pyproject-hooks==1.2.0
pytest==9.0.3
pytokens==0.4.1
quantities==0.16.4
readme-renderer==44.0
requests==2.34.0
requests-toolbelt==1.0.0
rfc3986==2.0.0
rich==15.0.0
secretstorage==3.5.0
twine==6.2.0
urllib3==2.7.0

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions