Skip to content

Commit

Permalink
Merge read_directly into read_h5ad (#25)
Browse files Browse the repository at this point in the history
* Merge read_directly into read_h5ad

* update read unit test

* File property

---------

Co-authored-by: Roman Mukhin <[email protected]>
  • Loading branch information
siberianisaev and rm1113 authored Sep 10, 2024
1 parent 0b31523 commit de83e06
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 17 deletions.
12 changes: 12 additions & 0 deletions cap_anndata/cap_anndata.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ def __init__(self, file: h5py.File, path_to_content: str = "/") -> None:
self._path_to_content = path_to_content
self._X: X_NOTATION = None

@property
def file(self) -> h5py.File:
return self._file

@property
def X(self) -> X_NOTATION:
if self._X is None:
Expand Down Expand Up @@ -394,3 +398,11 @@ def __repr__(self) -> str:

def __str__(self) -> str:
return self.create_repr()

def __enter__(self):
return self

def __exit__(self, *args):
if self._file is not None:
self._file.close()
logger.debug("CapAnnData closed!")
27 changes: 16 additions & 11 deletions cap_anndata/reader.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import logging
import contextlib
import h5py
import warnings

from cap_anndata import CapAnnData


logger = logging.getLogger(__name__)


@contextlib.contextmanager
def read_h5ad(file_path: str, edit: bool = False):
"""
This is the main read method for CapAnnData.
Expand All @@ -21,24 +20,30 @@ def read_h5ad(file_path: str, edit: bool = False):
file = h5py.File(file_path, mode)
cap_adata = CapAnnData(file)
logger.debug(f"Successfully read anndata file path {file_path}")
yield cap_adata
return cap_adata

except Exception as error:
logger.error(f"Error during read anndata file at path: {file_path}, error = {error}!")
raise error

finally:
file.close()
logger.debug("AnnData closed!")

def deprecated(message):
def deprecated_decorator(func):
def deprecated_func(*args, **kwargs):
warnings.warn("{} is a deprecated function. {}".format(func.__name__, message),
category=DeprecationWarning,
stacklevel=2)
warnings.simplefilter('default', DeprecationWarning)
return func(*args, **kwargs)
return deprecated_func
return deprecated_decorator


# TODO: remove deprecated function
@deprecated("It will be removed in the next version of package. Please replace it with `read_h5ad`.")
def read_directly(file_path: str, edit: bool = False) -> CapAnnData:
"""
Must be used only in specific cases.
User is responsible to close the h5py file when the work with CapAnnData instance done.
"""
mode = "r+" if edit else "r"
logger.debug(f"Read file {file_path} mode={mode} directly...")
file = h5py.File(file_path, mode)
cap_adata = CapAnnData(file)
return cap_adata
return read_h5ad(file_path, edit)
49 changes: 43 additions & 6 deletions test/test_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,57 @@

from cap_anndata.reader import read_h5ad, read_directly
from test.context import get_base_anndata
import pytest


def test_read_anndata_file():
def prepare_h5ad_file(name):
adata = get_base_anndata()
temp_folder = tempfile.mkdtemp()
file_path = os.path.join(temp_folder, "test_read_anndata_file.h5ad")
file_path = os.path.join(temp_folder, name)
adata.write_h5ad(file_path)
del adata
return file_path

with read_h5ad(file_path=file_path) as cap_adata:

@pytest.mark.parametrize("edit", [True, False])
def test_read_in_context(edit):
file_path = prepare_h5ad_file("test_read_in_context.h5ad")

with read_h5ad(file_path=file_path, edit=edit) as cap_adata:
assert cap_adata is not None, "CapAnnData file must be valid!"
cap_adata.read_obs()
cap_adata.read_uns()
if edit:
cap_adata.overwrite()

os.remove(file_path)


@pytest.mark.parametrize("edit", [True, False])
def test_read_as_function(edit):
file_path = prepare_h5ad_file("test_read_as_function.h5ad")

cap_adata = read_h5ad(file_path=file_path, edit=edit)
assert cap_adata is not None, "CapAnnData file must be valid!"
cap_adata.read_obs()
cap_adata.read_uns()
if edit:
cap_adata.overwrite()

cap_adata.file.close()
os.remove(file_path)


@pytest.mark.parametrize("edit", [True, False])
def test_read_directly(edit):
# TODO: remove deprecated function and unit test
file_path = prepare_h5ad_file("test_read_directly.h5ad")

cap_adata = read_directly(file_path=file_path)
cap_adata = read_directly(file_path=file_path, edit=edit)
assert cap_adata is not None, "CapAnnData file must be valid!"
cap_adata._file.close()
cap_adata.read_obs()
cap_adata.read_uns()
if edit:
cap_adata.overwrite()

cap_adata.file.close()
os.remove(file_path)

0 comments on commit de83e06

Please sign in to comment.