Skip to content

fix: moran's calculation parallelization#4148

Open
ilan-gold wants to merge 1 commit into
mainfrom
ig/moran_numba_66
Open

fix: moran's calculation parallelization#4148
ilan-gold wants to merge 1 commit into
mainfrom
ig/moran_numba_66

Conversation

@ilan-gold

@ilan-gold ilan-gold commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Was getting the following beginning with numba>=0.66rc1:

Details =================================== FAILURES =================================== __________________ test_consistency[morans_i-single-threaded] __________________ [gw1] linux -- Python 3.14.5 /home/runner/.local/share/hatch/env/virtual/scanpy/B9PcT7QG/hatch-test.pre/bin/python3

metric = <function morans_i at 0x7fae9eda6140>

@pytest.mark.usefixtures("_threading")
def test_consistency(metric) -> None:
    pbmc = pbmc68k_reduced()
    pbmc.layers["raw"] = pbmc.raw.X.copy()
    g = pbmc.obsp["connectivities"]
    equality_check = partial(np.testing.assert_allclose, atol=1e-11)

    # This can fail
    equality_check(
        metric(g, pbmc.obs["percent_mito"]),
        metric(g, pbmc.obs["percent_mito"]),
    )
    equality_check(
        metric(g, pbmc.obs["percent_mito"]),
        metric(pbmc, vals=pbmc.obs["percent_mito"]),
    )

    equality_check(  # Test that series and vectors return same value
        metric(g, pbmc.obs["percent_mito"]),
        metric(g, pbmc.obs["percent_mito"].values),
    )

    equality_check(
        metric(pbmc, obsm="X_pca"),
        metric(g, pbmc.obsm["X_pca"].T),
    )

    all_genes = metric(pbmc, layer="raw")
    first_gene = metric(
        pbmc, vals=pbmc[:, pbmc.var_names[0]].layers["raw"].toarray().ravel()
    )
  np.testing.assert_allclose(all_genes[0], first_gene, rtol=1e-9)

E AssertionError:
E Not equal to tolerance rtol=1e-09, atol=0
E
E Mismatched elements: 1 / 1 (100%)
E Max absolute difference among violations: 7.9750179849e-08
E Max relative difference among violations: 1.8702209022e-07
E ACTUAL: array(0.426421)
E DESIRED: array(0.426421)

tests/test_metrics.py:72: AssertionError
__________________ test_consistency[morans_i-multi-threaded] ___________________
[gw1] linux -- Python 3.14.5 /home/runner/.local/share/hatch/env/virtual/scanpy/B9PcT7QG/hatch-test.pre/bin/python3
data[i] -= regressor[i] @ coeff

tests/test_scaling.py::test_scale[mask-center-int64-array-csc_matrix]
tests/test_scaling.py::test_scale[mask-center-int64-anndata-csc_matrix]
tests/test_scaling.py::test_scale[mask-center-float32-anndata-csc_matrix]
tests/test_scaling.py::test_scale[mask-center-float32-array-csc_matrix]
/home/runner/.local/share/hatch/env/virtual/scanpy/B9PcT7QG/hatch-test.pre/lib/python3.14/site-packages/scipy/sparse/_index.py:216: SparseEfficiencyWarning: Changing the sparsity structure of a csc_matrix is expensive. lil and dok are more efficient.
self._set_arrayXarray(i, j, x)

tests/test_scaling.py::test_scale[mask-center-int64-anndata-csr_matrix]
tests/test_scaling.py::test_scale[mask-center-float32-array-csr_matrix]
tests/test_scaling.py::test_scale[mask-center-float32-anndata-csr_matrix]
tests/test_scaling.py::test_scale[mask-center-int64-array-csr_matrix]
/home/runner/.local/share/hatch/env/virtual/scanpy/B9PcT7QG/hatch-test.pre/lib/python3.14/site-packages/scipy/sparse/_index.py:216: SparseEfficiencyWarning: Changing the sparsity structure of a csr_matrix is expensive. lil and dok are more efficient.
self._set_arrayXarray(i, j, x)

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html

  • generated xml file: /home/runner/work/scanpy/scanpy/test-data/test-results.xml -
    =========================== short test summary info ============================
    FAILED tests/test_metrics.py::test_consistency[morans_i-single-threaded] - AssertionError:
    Not equal to tolerance rtol=1e-09, atol=0

Mismatched elements: 1 / 1 (100%)
Max absolute difference among violations: 7.9750179849e-08
Max relative difference among violations: 1.8702209022e-07
ACTUAL: array(0.426421)
DESIRED: array(0.426421)
FAILED tests/test_metrics.py::test_consistency[morans_i-multi-threaded] - AssertionError:
Not equal to tolerance rtol=1e-09, atol=0

Mismatched elements: 1 / 1 (100%)
Max absolute difference among violations: 7.9750179849e-08
Max relative difference among violations: 1.8702209022e-07
ACTUAL: array(0.426421)
DESIRED: array(0.426421)

and suspect this could be the cause

  • Closes #
  • Tests included or not required because:
  • Release notes not necessary because: fix in preparation for upcoming release

@codecov

codecov Bot commented Jun 8, 2026

Copy link
Copy Markdown

❌ 2 Tests Failed:

Tests completed Failed Passed Skipped
2628 2 2626 150
View the top 3 failed test(s) by shortest run time
tests/test_datasets.py::test_burczynski06
Stack Traces | 0.046s run time
+ Exception Group Traceback (most recent call last):
  |   File ".................................................../home/runner/.local.../scanpy/B9PcT7QG/hatch-test.stable/lib/python3.14....../site-packages/_pytest/runner.py", line 353, in from_call
  |     result: TResult | None = func()
  |                              ~~~~^^
  |   File ".................................................../home/runner/.local.../scanpy/B9PcT7QG/hatch-test.stable/lib/python3.14....../site-packages/_pytest/runner.py", line 245, in <lambda>
  |     lambda: runtest_hook(item=item, **kwds),
  |             ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^
  |   File ".................................................../home/runner/.local.../scanpy/B9PcT7QG/hatch-test.stable/lib/python3.14.../site-packages/pluggy/_hooks.py", line 512, in __call__
  |     return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
  |            ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |   File ".................................................../home/runner/.local.../scanpy/B9PcT7QG/hatch-test.stable/lib/python3.14.../site-packages/pluggy/_manager.py", line 120, in _hookexec
  |     return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
  |            ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |   File ".................................................../home/runner/.local.../scanpy/B9PcT7QG/hatch-test.stable/lib/python3.14............/site-packages/pluggy/_callers.py", line 167, in _multicall
  |     raise exception
  |   File ".................................................../home/runner/.local.../scanpy/B9PcT7QG/hatch-test.stable/lib/python3.14............/site-packages/pluggy/_callers.py", line 139, in _multicall
  |     teardown.throw(exception)
  |     ~~~~~~~~~~~~~~^^^^^^^^^^^
  |   File ".................................................../home/runner/.local.../scanpy/B9PcT7QG/hatch-test.stable/lib/python3.14.../site-packages/_pytest/logging.py", line 843, in pytest_runtest_setup
  |     yield
  |   File ".................................................../home/runner/.local.../scanpy/B9PcT7QG/hatch-test.stable/lib/python3.14............/site-packages/pluggy/_callers.py", line 139, in _multicall
  |     teardown.throw(exception)
  |     ~~~~~~~~~~~~~~^^^^^^^^^^^
  |   File ".................................................../home/runner/.local.../scanpy/B9PcT7QG/hatch-test.stable/lib/python3.14.../site-packages/_pytest/capture.py", line 895, in pytest_runtest_setup
  |     return (yield)
  |             ^^^^^
  |   File ".................................................../home/runner/.local.../scanpy/B9PcT7QG/hatch-test.stable/lib/python3.14............/site-packages/pluggy/_callers.py", line 121, in _multicall
  |     res = hook_impl.function(*args)
  |   File ".................................................../home/runner/.local.../scanpy/B9PcT7QG/hatch-test.stable/lib/python3.14...................../site-packages/_pytest/unraisableexception.py", line 153, in pytest_runtest_setup
  |     collect_unraisable(item.config)
  |     ~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^
  |   File ".................................................../home/runner/.local.../scanpy/B9PcT7QG/hatch-test.stable/lib/python3.14...................../site-packages/_pytest/unraisableexception.py", line 81, in collect_unraisable
  |     raise ExceptionGroup("multiple unraisable exception warnings", errors)
  | ExceptionGroup: multiple unraisable exception warnings (5 sub-exceptions)
  +-+---------------- 1 ----------------
    | Traceback (most recent call last):
    |   File ".../_temp/uv-python-dir/cpython-3.14.5-linux-x86_64-gnu/lib/python3.14/tempfile.py", line 484, in __del__
    |     _warnings.warn(self.warn_message, ResourceWarning)
    |     ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    | ResourceWarning: Implicitly cleaning up <HTTPError 500: 'Internal Server Error'>
    | 
    | The above exception was the direct cause of the following exception:
    | 
    | Traceback (most recent call last):
    |   File ".................................................../home/runner/.local.../scanpy/B9PcT7QG/hatch-test.stable/lib/python3.14...................../site-packages/_pytest/unraisableexception.py", line 67, in collect_unraisable
    |     warnings.warn(pytest.PytestUnraisableExceptionWarning(msg))
    |     ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    | pytest.PytestUnraisableExceptionWarning: Exception ignored while calling deallocator <function _TemporaryFileCloser.__del__ at 0x7f29d2ab85c0>: None
    | 
    +---------------- 2 ----------------
    | Traceback (most recent call last):
    |   File ".../_temp/uv-python-dir/cpython-3.14.5-linux-x86_64-gnu/lib/python3.14/tempfile.py", line 484, in __del__
    |     _warnings.warn(self.warn_message, ResourceWarning)
    |     ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    | ResourceWarning: Implicitly cleaning up <HTTPError 500: 'Internal Server Error'>
    | 
    | The above exception was the direct cause of the following exception:
    | 
    | Traceback (most recent call last):
    |   File ".................................................../home/runner/.local.../scanpy/B9PcT7QG/hatch-test.stable/lib/python3.14...................../site-packages/_pytest/unraisableexception.py", line 67, in collect_unraisable
    |     warnings.warn(pytest.PytestUnraisableExceptionWarning(msg))
    |     ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    | pytest.PytestUnraisableExceptionWarning: Exception ignored while calling deallocator <function _TemporaryFileCloser.__del__ at 0x7f29d2ab85c0>: None
    | 
    +---------------- 3 ----------------
    | Traceback (most recent call last):
    |   File ".../_temp/uv-python-dir/cpython-3.14.5-linux-x86_64-gnu/lib/python3.14/tempfile.py", line 484, in __del__
    |     _warnings.warn(self.warn_message, ResourceWarning)
    |     ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    | ResourceWarning: Implicitly cleaning up <HTTPError 500: 'Internal Server Error'>
    | 
    | The above exception was the direct cause of the following exception:
    | 
    | Traceback (most recent call last):
    |   File ".................................................../home/runner/.local.../scanpy/B9PcT7QG/hatch-test.stable/lib/python3.14...................../site-packages/_pytest/unraisableexception.py", line 67, in collect_unraisable
    |     warnings.warn(pytest.PytestUnraisableExceptionWarning(msg))
    |     ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    | pytest.PytestUnraisableExceptionWarning: Exception ignored while calling deallocator <function _TemporaryFileCloser.__del__ at 0x7f29d2ab85c0>: None
    | 
    +---------------- 4 ----------------
    | Traceback (most recent call last):
    |   File ".../_temp/uv-python-dir/cpython-3.14.5-linux-x86_64-gnu/lib/python3.14/tempfile.py", line 484, in __del__
    |     _warnings.warn(self.warn_message, ResourceWarning)
    |     ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    | ResourceWarning: Implicitly cleaning up <HTTPError 500: 'Internal Server Error'>
    | 
    | The above exception was the direct cause of the following exception:
    | 
    | Traceback (most recent call last):
    |   File ".................................................../home/runner/.local.../scanpy/B9PcT7QG/hatch-test.stable/lib/python3.14...................../site-packages/_pytest/unraisableexception.py", line 67, in collect_unraisable
    |     warnings.warn(pytest.PytestUnraisableExceptionWarning(msg))
    |     ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    | pytest.PytestUnraisableExceptionWarning: Exception ignored while calling deallocator <function _TemporaryFileCloser.__del__ at 0x7f29d2ab85c0>: None
    | 
    +---------------- 5 ----------------
    | Traceback (most recent call last):
    |   File ".../_temp/uv-python-dir/cpython-3.14.5-linux-x86_64-gnu/lib/python3.14/tempfile.py", line 484, in __del__
    |     _warnings.warn(self.warn_message, ResourceWarning)
    |     ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    | ResourceWarning: Implicitly cleaning up <HTTPError 500: 'Internal Server Error'>
    | 
    | The above exception was the direct cause of the following exception:
    | 
    | Traceback (most recent call last):
    |   File ".................................................../home/runner/.local.../scanpy/B9PcT7QG/hatch-test.stable/lib/python3.14...................../site-packages/_pytest/unraisableexception.py", line 67, in collect_unraisable
    |     warnings.warn(pytest.PytestUnraisableExceptionWarning(msg))
    |     ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    | pytest.PytestUnraisableExceptionWarning: Exception ignored while calling deallocator <function _TemporaryFileCloser.__del__ at 0x7f29d2ab85c0>: None
    | 
    +------------------------------------
tests/test_metrics.py::test_consistency[morans_i-multi-threaded]
Stack Traces | 0.124s run time
metric = <function morans_i at 0x7fcc2bfa9b10>

    #x1B[0m#x1B[37m@pytest#x1B[39;49;00m.mark.usefixtures(#x1B[33m"#x1B[39;49;00m#x1B[33m_threading#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m)#x1B[90m#x1B[39;49;00m
    #x1B[94mdef#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[92mtest_consistency#x1B[39;49;00m(metric) -> #x1B[94mNone#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
        pbmc = pbmc68k_reduced()#x1B[90m#x1B[39;49;00m
        pbmc.layers[#x1B[33m"#x1B[39;49;00m#x1B[33mraw#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m] = pbmc.raw.X.copy()#x1B[90m#x1B[39;49;00m
        g = pbmc.obsp[#x1B[33m"#x1B[39;49;00m#x1B[33mconnectivities#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m]#x1B[90m#x1B[39;49;00m
        equality_check = partial(np.testing.assert_allclose, atol=#x1B[94m1e-11#x1B[39;49;00m)#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
        #x1B[90m# This can fail#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
        equality_check(#x1B[90m#x1B[39;49;00m
            metric(g, pbmc.obs[#x1B[33m"#x1B[39;49;00m#x1B[33mpercent_mito#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m]),#x1B[90m#x1B[39;49;00m
            metric(g, pbmc.obs[#x1B[33m"#x1B[39;49;00m#x1B[33mpercent_mito#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m]),#x1B[90m#x1B[39;49;00m
        )#x1B[90m#x1B[39;49;00m
        equality_check(#x1B[90m#x1B[39;49;00m
            metric(g, pbmc.obs[#x1B[33m"#x1B[39;49;00m#x1B[33mpercent_mito#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m]),#x1B[90m#x1B[39;49;00m
            metric(pbmc, vals=pbmc.obs[#x1B[33m"#x1B[39;49;00m#x1B[33mpercent_mito#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m]),#x1B[90m#x1B[39;49;00m
        )#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
        equality_check(  #x1B[90m# Test that series and vectors return same value#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
            metric(g, pbmc.obs[#x1B[33m"#x1B[39;49;00m#x1B[33mpercent_mito#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m]),#x1B[90m#x1B[39;49;00m
            metric(g, pbmc.obs[#x1B[33m"#x1B[39;49;00m#x1B[33mpercent_mito#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m].values),#x1B[90m#x1B[39;49;00m
        )#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
        equality_check(#x1B[90m#x1B[39;49;00m
            metric(pbmc, obsm=#x1B[33m"#x1B[39;49;00m#x1B[33mX_pca#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m),#x1B[90m#x1B[39;49;00m
            metric(g, pbmc.obsm[#x1B[33m"#x1B[39;49;00m#x1B[33mX_pca#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m].T),#x1B[90m#x1B[39;49;00m
        )#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
        all_genes = metric(pbmc, layer=#x1B[33m"#x1B[39;49;00m#x1B[33mraw#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m)#x1B[90m#x1B[39;49;00m
        first_gene = metric(#x1B[90m#x1B[39;49;00m
            pbmc, vals=pbmc[:, pbmc.var_names[#x1B[94m0#x1B[39;49;00m]].layers[#x1B[33m"#x1B[39;49;00m#x1B[33mraw#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m].toarray().ravel()#x1B[90m#x1B[39;49;00m
        )#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
>       np.testing.assert_allclose(all_genes[#x1B[94m0#x1B[39;49;00m], first_gene, rtol=#x1B[94m1e-7#x1B[39;49;00m)#x1B[90m#x1B[39;49;00m
#x1B[1m#x1B[31mE       AssertionError: #x1B[0m
#x1B[1m#x1B[31mE       Not equal to tolerance rtol=1e-07, atol=0#x1B[0m
#x1B[1m#x1B[31mE       #x1B[0m
#x1B[1m#x1B[31mE       Mismatched elements: 1 / 1 (100%)#x1B[0m
#x1B[1m#x1B[31mE       Max absolute difference among violations: 7.9750179849e-08#x1B[0m
#x1B[1m#x1B[31mE       Max relative difference among violations: 1.8702209022e-07#x1B[0m
#x1B[1m#x1B[31mE        ACTUAL: array(0.426421)#x1B[0m
#x1B[1m#x1B[31mE        DESIRED: array(0.426421)#x1B[0m

#x1B[1m#x1B[31mtests/test_metrics.py#x1B[0m:72: AssertionError
tests/test_metrics.py::test_consistency[morans_i-single-threaded]
Stack Traces | 4.05s run time
metric = <function morans_i at 0x7fcc2bfa9b10>

    #x1B[0m#x1B[37m@pytest#x1B[39;49;00m.mark.usefixtures(#x1B[33m"#x1B[39;49;00m#x1B[33m_threading#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m)#x1B[90m#x1B[39;49;00m
    #x1B[94mdef#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[92mtest_consistency#x1B[39;49;00m(metric) -> #x1B[94mNone#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
        pbmc = pbmc68k_reduced()#x1B[90m#x1B[39;49;00m
        pbmc.layers[#x1B[33m"#x1B[39;49;00m#x1B[33mraw#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m] = pbmc.raw.X.copy()#x1B[90m#x1B[39;49;00m
        g = pbmc.obsp[#x1B[33m"#x1B[39;49;00m#x1B[33mconnectivities#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m]#x1B[90m#x1B[39;49;00m
        equality_check = partial(np.testing.assert_allclose, atol=#x1B[94m1e-11#x1B[39;49;00m)#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
        #x1B[90m# This can fail#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
        equality_check(#x1B[90m#x1B[39;49;00m
            metric(g, pbmc.obs[#x1B[33m"#x1B[39;49;00m#x1B[33mpercent_mito#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m]),#x1B[90m#x1B[39;49;00m
            metric(g, pbmc.obs[#x1B[33m"#x1B[39;49;00m#x1B[33mpercent_mito#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m]),#x1B[90m#x1B[39;49;00m
        )#x1B[90m#x1B[39;49;00m
        equality_check(#x1B[90m#x1B[39;49;00m
            metric(g, pbmc.obs[#x1B[33m"#x1B[39;49;00m#x1B[33mpercent_mito#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m]),#x1B[90m#x1B[39;49;00m
            metric(pbmc, vals=pbmc.obs[#x1B[33m"#x1B[39;49;00m#x1B[33mpercent_mito#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m]),#x1B[90m#x1B[39;49;00m
        )#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
        equality_check(  #x1B[90m# Test that series and vectors return same value#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
            metric(g, pbmc.obs[#x1B[33m"#x1B[39;49;00m#x1B[33mpercent_mito#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m]),#x1B[90m#x1B[39;49;00m
            metric(g, pbmc.obs[#x1B[33m"#x1B[39;49;00m#x1B[33mpercent_mito#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m].values),#x1B[90m#x1B[39;49;00m
        )#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
        equality_check(#x1B[90m#x1B[39;49;00m
            metric(pbmc, obsm=#x1B[33m"#x1B[39;49;00m#x1B[33mX_pca#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m),#x1B[90m#x1B[39;49;00m
            metric(g, pbmc.obsm[#x1B[33m"#x1B[39;49;00m#x1B[33mX_pca#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m].T),#x1B[90m#x1B[39;49;00m
        )#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
        all_genes = metric(pbmc, layer=#x1B[33m"#x1B[39;49;00m#x1B[33mraw#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m)#x1B[90m#x1B[39;49;00m
        first_gene = metric(#x1B[90m#x1B[39;49;00m
            pbmc, vals=pbmc[:, pbmc.var_names[#x1B[94m0#x1B[39;49;00m]].layers[#x1B[33m"#x1B[39;49;00m#x1B[33mraw#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m].toarray().ravel()#x1B[90m#x1B[39;49;00m
        )#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
>       np.testing.assert_allclose(all_genes[#x1B[94m0#x1B[39;49;00m], first_gene, rtol=#x1B[94m1e-7#x1B[39;49;00m)#x1B[90m#x1B[39;49;00m
#x1B[1m#x1B[31mE       AssertionError: #x1B[0m
#x1B[1m#x1B[31mE       Not equal to tolerance rtol=1e-07, atol=0#x1B[0m
#x1B[1m#x1B[31mE       #x1B[0m
#x1B[1m#x1B[31mE       Mismatched elements: 1 / 1 (100%)#x1B[0m
#x1B[1m#x1B[31mE       Max absolute difference among violations: 7.9750179849e-08#x1B[0m
#x1B[1m#x1B[31mE       Max relative difference among violations: 1.8702209022e-07#x1B[0m
#x1B[1m#x1B[31mE        ACTUAL: array(0.426421)#x1B[0m
#x1B[1m#x1B[31mE        DESIRED: array(0.426421)#x1B[0m

#x1B[1m#x1B[31mtests/test_metrics.py#x1B[0m:72: AssertionError
tests/test_datasets.py::test_ebi_expression_atlas
Stack Traces | 4.83s run time
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7f294c4f0d70>

    #x1B[0m#x1B[37m@pytest#x1B[39;49;00m.mark.internet#x1B[90m#x1B[39;49;00m
    #x1B[94mdef#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[92mtest_ebi_expression_atlas#x1B[39;49;00m(monkeypatch: pytest.MonkeyPatch):#x1B[90m#x1B[39;49;00m
        #x1B[94mfrom#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[04m#x1B[96mscanpy#x1B[39;49;00m#x1B[04m#x1B[96m.#x1B[39;49;00m#x1B[04m#x1B[96mdatasets#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[94mimport#x1B[39;49;00m _ebi_expression_atlas #x1B[94mas#x1B[39;49;00m ea_mod#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
        #x1B[90m# make sure we use chunks when testing.#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
        #x1B[90m# This dataset has <8M entries, so 4M entries/chunk = 2 chunks#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
        #x1B[94massert#x1B[39;49;00m #x1B[96mhasattr#x1B[39;49;00m(ea_mod, #x1B[33m"#x1B[39;49;00m#x1B[33mCHUNK_SIZE#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m)#x1B[90m#x1B[39;49;00m
        monkeypatch.setattr(ea_mod, #x1B[33m"#x1B[39;49;00m#x1B[33mCHUNK_SIZE#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[96mint#x1B[39;49;00m(#x1B[94m4e6#x1B[39;49;00m))#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
>       adata = sc.datasets.ebi_expression_atlas(#x1B[33m"#x1B[39;49;00m#x1B[33mE-MTAB-4888#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m)#x1B[90m#x1B[39;49;00m
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#x1B[90m#x1B[39;49;00m

#x1B[1m#x1B[31mtests/test_datasets.py#x1B[0m:84: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
#x1B[1m#x1B[.../scanpy/datasets/_ebi_expression_atlas.py#x1B[0m:159: in ebi_expression_atlas
    #x1B[0mdownload_experiment(accession)#x1B[90m#x1B[39;49;00m
#x1B[1m#x1B[.../scanpy/datasets/_utils.py#x1B[0m:16: in wrapper
    #x1B[0m#x1B[94mreturn#x1B[39;49;00m f(*args, **kwargs)#x1B[90m#x1B[39;49;00m
           ^^^^^^^^^^^^^^^^^^#x1B[90m#x1B[39;49;00m
#x1B[1m#x1B[.../scanpy/datasets/_ebi_expression_atlas.py#x1B[0m:48: in download_experiment
    #x1B[0msniff_url(accession)#x1B[90m#x1B[39;49;00m
#x1B[1m#x1B[.../scanpy/datasets/_ebi_expression_atlas.py#x1B[0m:39: in sniff_url
    #x1B[0m#x1B[94mwith#x1B[39;49;00m urlopen(base_url):  #x1B[90m# Check if server up/ dataset exists#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
         ^^^^^^^^^^^^^^^^^#x1B[90m#x1B[39;49;00m
#x1B[1m#x1B[31m../..................../_temp/uv-python-dir/cpython-3.14.5-linux-x86_64-gnu/lib/python3.14/urllib/request.py#x1B[0m:187: in urlopen
    #x1B[0m#x1B[94mreturn#x1B[39;49;00m opener.open(url, data, timeout)#x1B[90m#x1B[39;49;00m
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#x1B[90m#x1B[39;49;00m
#x1B[1m#x1B[31m../..................../_temp/uv-python-dir/cpython-3.14.5-linux-x86_64-gnu/lib/python3.14/urllib/request.py#x1B[0m:493: in open
    #x1B[0mresponse = meth(req, response)#x1B[90m#x1B[39;49;00m
               ^^^^^^^^^^^^^^^^^^^#x1B[90m#x1B[39;49;00m
#x1B[1m#x1B[31m../..................../_temp/uv-python-dir/cpython-3.14.5-linux-x86_64-gnu/lib/python3.14/urllib/request.py#x1B[0m:602: in http_response
    #x1B[0mresponse = #x1B[96mself#x1B[39;49;00m.parent.error(#x1B[90m#x1B[39;49;00m
#x1B[1m#x1B[31m../..................../_temp/uv-python-dir/cpython-3.14.5-linux-x86_64-gnu/lib/python3.14/urllib/request.py#x1B[0m:531: in error
    #x1B[0m#x1B[94mreturn#x1B[39;49;00m #x1B[96mself#x1B[39;49;00m._call_chain(*args)#x1B[90m#x1B[39;49;00m
           ^^^^^^^^^^^^^^^^^^^^^^^#x1B[90m#x1B[39;49;00m
#x1B[1m#x1B[31m../..................../_temp/uv-python-dir/cpython-3.14.5-linux-x86_64-gnu/lib/python3.14/urllib/request.py#x1B[0m:464: in _call_chain
    #x1B[0mresult = func(*args)#x1B[90m#x1B[39;49;00m
             ^^^^^^^^^^^#x1B[90m#x1B[39;49;00m
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <urllib.request.HTTPDefaultErrorHandler object at 0x7f2978bbf380>
req = <urllib.request.Request object at 0x7f294c2bc050>
fp = <http.client.HTTPResponse object at 0x7f294c2b7790>, code = 500
msg = 'Internal Server Error'
hdrs = <http.client.HTTPMessage object at 0x7f29683ef200>

    #x1B[0m#x1B[94mdef#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[92mhttp_error_default#x1B[39;49;00m(#x1B[96mself#x1B[39;49;00m, req, fp, code, msg, hdrs):#x1B[90m#x1B[39;49;00m
>       #x1B[94mraise#x1B[39;49;00m HTTPError(req.full_url, code, msg, hdrs, fp)#x1B[90m#x1B[39;49;00m
#x1B[1m#x1B[31mE       urllib.error.HTTPError: HTTP Error 500: Internal Server Error#x1B[0m
#x1B[1m#x1B[31mE       https://www.ebi.ac..../sc/experiments/E-MTAB-4888/#x1B[0m

#x1B[1m#x1B[31m../..................../_temp/uv-python-dir/cpython-3.14.5-linux-x86_64-gnu/lib/python3.14/urllib/request.py#x1B[0m:611: HTTPError
tests/test_pca.py::test_pca_chunked
Stack Traces | 8.7s run time
self = <tempfile._TemporaryFileCloser object at 0x7f2978bbc2f0>

    #x1B[0m#x1B[94mdef#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[92m__del__#x1B[39;49;00m(#x1B[96mself#x1B[39;49;00m):#x1B[90m#x1B[39;49;00m
        close_called = #x1B[96mself#x1B[39;49;00m.close_called#x1B[90m#x1B[39;49;00m
        #x1B[96mself#x1B[39;49;00m.cleanup()#x1B[90m#x1B[39;49;00m
        #x1B[94mif#x1B[39;49;00m #x1B[95mnot#x1B[39;49;00m close_called:#x1B[90m#x1B[39;49;00m
>           _warnings.warn(#x1B[96mself#x1B[39;49;00m.warn_message, #x1B[96mResourceWarning#x1B[39;49;00m)#x1B[90m#x1B[39;49;00m
#x1B[1m#x1B[31mE           ResourceWarning: Implicitly cleaning up <HTTPError 500: 'Internal Server Error'>#x1B[0m

#x1B[1m#x1B[31m../...../_temp/uv-python-dir/cpython-3.14.5-linux-x86_64-gnu/lib/python3.14/tempfile.py#x1B[0m:484: ResourceWarning

#x1B[33mThe above exception was the direct cause of the following exception:#x1B[0m

cls = <class '_pytest.runner.CallInfo'>
func = <function call_and_report.<locals>.<lambda> at 0x7f29208b3ed0>
when = 'call'
reraise = (<class '_pytest.outcomes.Exit'>, <class 'KeyboardInterrupt'>)

    #x1B[0m#x1B[37m@classmethod#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
    #x1B[94mdef#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[92mfrom_call#x1B[39;49;00m(#x1B[90m#x1B[39;49;00m
        #x1B[96mcls#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
        func: Callable[[], TResult],#x1B[90m#x1B[39;49;00m
        when: Literal[#x1B[33m"#x1B[39;49;00m#x1B[33mcollect#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[33m"#x1B[39;49;00m#x1B[33msetup#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[33m"#x1B[39;49;00m#x1B[33mcall#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[33m"#x1B[39;49;00m#x1B[33mteardown#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m],#x1B[90m#x1B[39;49;00m
        reraise: #x1B[96mtype#x1B[39;49;00m[#x1B[96mBaseException#x1B[39;49;00m] | #x1B[96mtuple#x1B[39;49;00m[#x1B[96mtype#x1B[39;49;00m[#x1B[96mBaseException#x1B[39;49;00m], ...] | #x1B[94mNone#x1B[39;49;00m = #x1B[94mNone#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
    ) -> CallInfo[TResult]:#x1B[90m#x1B[39;49;00m
    #x1B[90m    #x1B[39;49;00m#x1B[33m"""Call func, wrapping the result in a CallInfo.#x1B[39;49;00m
    #x1B[33m#x1B[39;49;00m
    #x1B[33m    :param func:#x1B[39;49;00m
    #x1B[33m        The function to call. Called without arguments.#x1B[39;49;00m
    #x1B[33m    :type func: Callable[[], _pytest.runner.TResult]#x1B[39;49;00m
    #x1B[33m    :param when:#x1B[39;49;00m
    #x1B[33m        The phase in which the function is called.#x1B[39;49;00m
    #x1B[33m    :param reraise:#x1B[39;49;00m
    #x1B[33m        Exception or exceptions that shall propagate if raised by the#x1B[39;49;00m
    #x1B[33m        function, instead of being wrapped in the CallInfo.#x1B[39;49;00m
    #x1B[33m    """#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
        excinfo = #x1B[94mNone#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
        instant = timing.Instant()#x1B[90m#x1B[39;49;00m
        #x1B[94mtry#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
>           result: TResult | #x1B[94mNone#x1B[39;49;00m = func()#x1B[90m#x1B[39;49;00m
                                     ^^^^^^#x1B[90m#x1B[39;49;00m

#x1B[1m#x1B[31m../../../..../scanpy/B9PcT7QG/hatch-test.stable/lib/python3.14....../site-packages/_pytest/runner.py#x1B[0m:353: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
#x1B[1m#x1B[31m../../../..../scanpy/B9PcT7QG/hatch-test.stable/lib/python3.14....../site-packages/_pytest/runner.py#x1B[0m:245: in <lambda>
    #x1B[0m#x1B[94mlambda#x1B[39;49;00m: runtest_hook(item=item, **kwds),#x1B[90m#x1B[39;49;00m
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#x1B[90m#x1B[39;49;00m
#x1B[1m#x1B[31m../../../..../scanpy/B9PcT7QG/hatch-test.stable/lib/python3.14.../site-packages/pluggy/_hooks.py#x1B[0m:512: in __call__
    #x1B[0m#x1B[94mreturn#x1B[39;49;00m #x1B[96mself#x1B[39;49;00m._hookexec(#x1B[96mself#x1B[39;49;00m.name, #x1B[96mself#x1B[39;49;00m._hookimpls.copy(), kwargs, firstresult)#x1B[90m#x1B[39;49;00m
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#x1B[90m#x1B[39;49;00m
#x1B[1m#x1B[31m../../../..../scanpy/B9PcT7QG/hatch-test.stable/lib/python3.14.../site-packages/pluggy/_manager.py#x1B[0m:120: in _hookexec
    #x1B[0m#x1B[94mreturn#x1B[39;49;00m #x1B[96mself#x1B[39;49;00m._inner_hookexec(hook_name, methods, kwargs, firstresult)#x1B[90m#x1B[39;49;00m
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#x1B[90m#x1B[39;49;00m
#x1B[1m#x1B[31m../../../..../scanpy/B9PcT7QG/hatch-test.stable/lib/python3.14.../site-packages/_pytest/logging.py#x1B[0m:850: in pytest_runtest_call
    #x1B[0m#x1B[94myield#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
#x1B[1m#x1B[31m../../../..../scanpy/B9PcT7QG/hatch-test.stable/lib/python3.14.../site-packages/_pytest/capture.py#x1B[0m:900: in pytest_runtest_call
    #x1B[0m#x1B[94mreturn#x1B[39;49;00m (#x1B[94myield#x1B[39;49;00m)#x1B[90m#x1B[39;49;00m
            ^^^^^#x1B[90m#x1B[39;49;00m
#x1B[1m#x1B[31m../../../..../scanpy/B9PcT7QG/hatch-test.stable/lib/python3.14.../site-packages/_pytest/skipping.py#x1B[0m:268: in pytest_runtest_call
    #x1B[0m#x1B[94mreturn#x1B[39;49;00m (#x1B[94myield#x1B[39;49;00m)#x1B[90m#x1B[39;49;00m
            ^^^^^#x1B[90m#x1B[39;49;00m
#x1B[1m#x1B[31m../../../..../scanpy/B9PcT7QG/hatch-test.stable/lib/python3.14........./site-packages/_pytest/unraisableexception.py#x1B[0m:158: in pytest_runtest_call
    #x1B[0mcollect_unraisable(item.config)#x1B[90m#x1B[39;49;00m
#x1B[1m#x1B[31m../../../..../scanpy/B9PcT7QG/hatch-test.stable/lib/python3.14........./site-packages/_pytest/unraisableexception.py#x1B[0m:79: in collect_unraisable
    #x1B[0m#x1B[94mraise#x1B[39;49;00m errors[#x1B[94m0#x1B[39;49;00m]#x1B[90m#x1B[39;49;00m
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

config = <_pytest.config.Config object at 0x7f29d27b74d0>

    #x1B[0m#x1B[94mdef#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[92mcollect_unraisable#x1B[39;49;00m(config: Config) -> #x1B[94mNone#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
        pop_unraisable = config.stash[unraisable_exceptions].pop#x1B[90m#x1B[39;49;00m
        errors: #x1B[96mlist#x1B[39;49;00m[pytest.PytestUnraisableExceptionWarning | #x1B[96mRuntimeError#x1B[39;49;00m] = []#x1B[90m#x1B[39;49;00m
        meta = #x1B[94mNone#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
        hook_error = #x1B[94mNone#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
        #x1B[94mtry#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
            #x1B[94mwhile#x1B[39;49;00m #x1B[94mTrue#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
                #x1B[94mtry#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
                    meta = pop_unraisable()#x1B[90m#x1B[39;49;00m
                #x1B[94mexcept#x1B[39;49;00m #x1B[96mIndexError#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
                    #x1B[94mbreak#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
                #x1B[94mif#x1B[39;49;00m #x1B[96misinstance#x1B[39;49;00m(meta, #x1B[96mBaseException#x1B[39;49;00m):#x1B[90m#x1B[39;49;00m
                    hook_error = #x1B[96mRuntimeError#x1B[39;49;00m(#x1B[33m"#x1B[39;49;00m#x1B[33mFailed to process unraisable exception#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m)#x1B[90m#x1B[39;49;00m
                    hook_error.__cause__ = meta#x1B[90m#x1B[39;49;00m
                    errors.append(hook_error)#x1B[90m#x1B[39;49;00m
                    #x1B[94mcontinue#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
                msg = meta.msg#x1B[90m#x1B[39;49;00m
                #x1B[94mtry#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
>                   warnings.warn(pytest.PytestUnraisableExceptionWarning(msg))#x1B[90m#x1B[39;49;00m
#x1B[1m#x1B[31mE                   pytest.PytestUnraisableExceptionWarning: Exception ignored while calling deallocator <function _TemporaryFileCloser.__del__ at 0x7f29d2ab85c0>: None#x1B[0m

#x1B[1m#x1B[31m../../../..../scanpy/B9PcT7QG/hatch-test.stable/lib/python3.14........./site-packages/_pytest/unraisableexception.py#x1B[0m:67: PytestUnraisableExceptionWarning

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

@ilan-gold

Copy link
Copy Markdown
Contributor Author

At this point, I think this may just be worth reporting upstream to numba and loosening our tolerance

@ilan-gold ilan-gold force-pushed the ig/moran_numba_66 branch from c1f7f7a to 02d5793 Compare June 8, 2026 15:22
@ilan-gold ilan-gold added this to the 1.12.2 milestone Jun 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant