nipy / nibabel

Python package to access a cacophony of neuro-imaging file formats
http://nipy.org/nibabel/
Other
649 stars 258 forks source link

ENH: Add copy() method to ArrayProxy #1255

Closed effigies closed 10 months ago

effigies commented 1 year ago

Small addition to ArrayProxy class to allow copying. Used in #1090, but not central to it.

codecov[bot] commented 1 year ago

Codecov Report

All modified and coverable lines are covered by tests :white_check_mark:

Comparison is base (41e6dad) 92.24% compared to head (1c1845f) 92.24%.

Additional details and impacted files ```diff @@ Coverage Diff @@ ## master #1255 +/- ## ======================================= Coverage 92.24% 92.24% ======================================= Files 99 99 Lines 12452 12460 +8 Branches 2560 2561 +1 ======================================= + Hits 11486 11494 +8 Misses 643 643 Partials 323 323 ```

:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.

effigies commented 1 year ago

@pauldmccarthy This is bound to interact with indexed gzip. If you have time for a quick read-through, I would appreciate your thoughts.

pauldmccarthy commented 10 months ago

Hi @effigies, apologies for the delay - I missed this mention.

On the surface, I think this looks fine. In the typical case where an ArrayProxy is created with a file path, and keep_file_open=True (rules 2 and 4 in the _should_keep_file_open docs), the two ArrayProxy instances will create independent ImageOpener instances and in turn independent IndexedGzipFile instances, so they will have nothing to do with each other.

In the less common case where an ArrayProxy is created with an IndexedGzipFile instance (rules 1 and 3 in the _should_keep_file_open docs), I think your lock sharing logic is a good idea.

If you like, you could add another (very rudimentary) test with something like this:

def test_copy_with_indexed_gzip_handle():

    import nibabel as nib

    indexed_gzip = pytest.importorskip('indexed_gzip')

    with InTemporaryDirectory():

        # ~24MB
        spec = ((50, 50, 50, 50), np.float32, 352, 1, 0)
        data = np.arange(np.prod(spec[0]), dtype=np.float32).reshape(spec[0])
        nib.Nifti1Image(data, np.eye(4)).to_filename('image.nii.gz')

        with indexed_gzip.IndexedGzipFile('image.nii.gz') as gzf:
            ap1 = nib.arrayproxy.ArrayProxy(gzf, spec)
            ap2 = ap1.copy()
            assert ap1.file_like is gzf
            assert ap2.file_like is gzf
            assert (ap1[ 0,  0,  0, :] == ap2[ 0,  0,  0, :]).all()
            assert (ap1[-1, -1, -1, :] == ap2[-1, -1, -1, :]).all()
effigies commented 10 months ago

Thanks for the review and test! If you think of a more strenuous test or something we can do upstream, happy to discuss more.

effigies commented 10 months ago

Test failure unrelated.