Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(chore): export read_elem and write_elem from the main package #1598

Merged
merged 24 commits into from
Aug 30, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
a6969b7
(chore): export `read_elem` and `write_elem` from the main package
ilan-gold Aug 14, 2024
1cc2b48
(chore): pr number
ilan-gold Aug 14, 2024
c89238d
(fix): agnostic way of importing
ilan-gold Aug 14, 2024
31c7ad0
(fix): add `RWAble` to `api.md`
ilan-gold Aug 14, 2024
99e0216
(fix): `md` file import
ilan-gold Aug 14, 2024
08b741b
Merge branch 'main' into ig/{read,write}_elem_out_of_experimental
ilan-gold Aug 26, 2024
ac354e1
Merge branch 'main' into ig/{read,write}_elem_out_of_experimental
ilan-gold Aug 28, 2024
54b27d7
(fix): export `InMemoryElem`
ilan-gold Aug 28, 2024
568620c
(chore): release note
ilan-gold Aug 28, 2024
9185bea
(chore): move `InMemoryElem` to the "extras" section
ilan-gold Aug 28, 2024
d051ab4
(chore): add warnings for old `experimental` imports
ilan-gold Aug 30, 2024
8419935
Merge branch 'main' into ig/{read,write}_elem_out_of_experimental
ilan-gold Aug 30, 2024
5013a5c
(fix): dont blanket warn
ilan-gold Aug 30, 2024
02427f5
Merge branch 'ig/{read,write}_elem_out_of_experimental' of github.com…
ilan-gold Aug 30, 2024
b332f04
(fix): split `__deprecated__` from `__all__`
ilan-gold Aug 30, 2024
bcac6b3
Update src/anndata/experimental/__init__.py
ilan-gold Aug 30, 2024
30395aa
Merge branch 'ig/{read,write}_elem_out_of_experimental' of github.com…
ilan-gold Aug 30, 2024
481109d
(fix): `key` <-`name`
ilan-gold Aug 30, 2024
8600429
(fix): remove `__dir__`
ilan-gold Aug 30, 2024
07ffa14
fix deprecation system
flying-sheep Aug 30, 2024
5cbe741
Merge branch 'main' into ig/{read,write}_elem_out_of_experimental
flying-sheep Aug 30, 2024
8accd32
(fix): remove from `experimental` in note
ilan-gold Aug 30, 2024
20cd124
(fix): fix old note
ilan-gold Aug 30, 2024
a39d178
Merge branch 'ig/{read,write}_elem_out_of_experimental' of github.com…
ilan-gold Aug 30, 2024
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
3 changes: 2 additions & 1 deletion benchmarks/benchmarks/sparse_dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
from scipy import sparse

from anndata import AnnData
from anndata.experimental import sparse_dataset, write_elem
from anndata._io.specs import write_elem
from anndata.experimental import sparse_dataset


def make_alternating_mask(n):
Expand Down
38 changes: 32 additions & 6 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,16 @@ Reading other file formats.

```

Reading individual portions (`obs`, `varm` etc.) of the `AnnData` object.

```{eval-rst}
.. autosummary::
:toctree: generated/

read_elem

```

## Writing

Writing to anndata’s native file format `.h5ad`.
Expand All @@ -74,6 +84,16 @@ Writing to other formats.
AnnData.write_zarr
```

Writing individual portions (`obs`, `varm` etc.) of the `AnnData` object.

```{eval-rst}
.. autosummary::
:toctree: generated/

write_elem

```

(experimental-api)=

## Experimental API
Expand All @@ -100,8 +120,6 @@ Interface for accessing on-disk sparse data:
:toctree: generated/

experimental.sparse_dataset
experimental.CSRDataset
experimental.CSCDataset
```

Out of core concatenation
Expand All @@ -119,8 +137,6 @@ Low level methods for reading and writing elements of an `AnnData` object to a s
.. autosummary::
:toctree: generated/

experimental.read_elem
experimental.write_elem
experimental.read_elem_as_dask
```

Expand All @@ -141,8 +157,6 @@ Types used by the former:
:toctree: generated/

experimental.IOSpec
experimental.InMemoryElem
experimental.RWAble
experimental.Read
experimental.Write
experimental.ReadCallback
Expand All @@ -168,3 +182,15 @@ Types used by the former:
settings
settings.override
```

## Custom Types/Classes for Readable/Writeable Elements

```{eval-rst}
.. autosummary::
:toctree: generated/

CSRDataset
CSCDataset
RWAble
InMemoryElem
```
6 changes: 3 additions & 3 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,13 +142,13 @@ def setup(app: Sphinx):
"anndata._types.WriteCallback": "anndata.experimental.WriteCallback",
"anndata._types.Read": "anndata.experimental.Read",
"anndata._types.Write": "anndata.experimental.Write",
"anndata._types.RWAble": "anndata.experimental.RWAble",
"anndata._types.RWAble": "anndata.RWAble",
}
autodoc_type_aliases = dict(
NDArray=":data:`~numpy.typing.NDArray`",
RWAble=":data:`~anndata.experimental.RWAble`",
RWAble=":data:`~anndata.RWAble`",
**{
f"{v}variantInMemoryType": ":data:`~anndata.experimental.InMemoryElem`"
f"{v}variantInMemoryType": ":data:`~anndata.InMemoryElem`"
for v in ["In", "Co", "Contra"]
},
)
Expand Down
6 changes: 3 additions & 3 deletions docs/fileformat-prose.md
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,7 @@ That is, we store an indicator array (or mask) of null values alongside the arra
:sync: hdf5

```python
>>> from anndata.experimental import write_elem
>>> from anndata import write_elem
>>> null_store = h5py.File("tmp.h5", mode="w")
>>> int_array = pd.array([1, None, 3, 4])
>>> int_array
Expand All @@ -498,7 +498,7 @@ nullable_integer/values <HDF5 dataset "values": shape (4,), type "<i8">
:sync: zarr

```python
>>> from anndata.experimental import write_elem
>>> from anndata import write_elem
>>> null_store = zarr.open()
>>> int_array = pd.array([1, None, 3, 4])
>>> int_array
Expand Down Expand Up @@ -635,7 +635,7 @@ function:

```python
>>> import awkward as ak
>>> from anndata.experimental import read_elem
>>> from anndata import read_elem
>>> awkward_group = store["varm/transcript"]
>>> ak.from_buffers(
... awkward_group.attrs["form"],
Expand Down
2 changes: 1 addition & 1 deletion docs/release-notes/0.10.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

* Concatenate on-disk anndata objects with {func}`anndata.experimental.concat_on_disk` {pr}`955` {user}`selmanozleyen`
* AnnData can now hold dask arrays with `scipy.sparse.spmatrix` chunks {pr}`1114` {user}`ivirshup`
* Public API for interacting with on disk sparse arrays: {func}`~anndata.experimental.sparse_dataset`, {class}`~anndata.experimental.CSRDataset`, and {class}`~anndata.experimental.CSCDataset` {pr}`765` {user}`ilan-gold` {user}`ivirshup`
* Public API for interacting with on disk sparse arrays: {func}`~anndata.experimental.sparse_dataset`, {class}`~anndata.CSRDataset`, and {class}`~anndata.CSCDataset` {pr}`765` {user}`ilan-gold` {user}`ivirshup`
* Improved performance for simple slices of OOC sparse arrays {pr}`1131` {user}`ivirshup`

**Improved errors and warnings**
Expand Down
2 changes: 1 addition & 1 deletion docs/release-notes/0.10.2.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#### Bugfix

* Added compatibility layer for packages relying on `anndata._core.sparse_dataset.SparseDataset`.
Note that this API is *deprecated* and new code should use {class}`~anndata.experimental.CSRDataset`, {class}`~anndata.experimental.CSCDataset`, and {func}`~anndata.experimental.sparse_dataset` instead.
Note that this API is *deprecated* and new code should use {class}`~anndata.CSRDataset`, {class}`~anndata.CSCDataset`, and {func}`~anndata.experimental.sparse_dataset` instead.
{pr}`1185` {user}`ivirshup`
* Handle deprecation warning from `pd.Categorical.map` thrown during `anndata.concat` {pr}`1189` {user}`flying-sheep` {user}`ivirshup`
* Fixed extra steps being included in IO tracebacks {pr}`1193` {user}`flying-sheep`
Expand Down
2 changes: 1 addition & 1 deletion docs/release-notes/0.8.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ This should make it much easier to support new datatypes, use partial access, an

- Each element should be tagged with an `encoding_type` and `encoding_version`. See updated docs on the {doc}`file format </fileformat-prose>`
- Support for nullable integer and boolean data arrays. More data types to come!
- Experimental support for low level access to the IO API via {func}`~anndata.experimental.read_elem` and {func}`~anndata.experimental.write_elem`
- Experimental support for low level access to the IO API via {func}`~anndata.read_elem` and {func}`~anndata.write_elem`

#### Features

Expand Down
1 change: 1 addition & 0 deletions docs/release-notes/1598.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Export {func}`~anndata.write_elem` and {func}`~anndata.read_elem` directly from the main package instead of `experimental` {user}`ilan-gold`
38 changes: 36 additions & 2 deletions src/anndata/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

from __future__ import annotations

import types
import warnings
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from typing import Any

try: # See https://github.com/maresb/hatch-vcs-footgun-example
from setuptools_scm import get_version

Expand All @@ -24,6 +31,7 @@
from ._core.anndata import AnnData
from ._core.merge import concat
from ._core.raw import Raw
from ._core.sparse_dataset import CSCDataset, CSRDataset
from ._io import (
read_csv,
read_excel,
Expand All @@ -35,19 +43,27 @@
read_umi_tools,
read_zarr,
)
from ._io.specs import read_elem, write_elem
from ._settings import settings
from ._types import InMemoryElem
from ._types import RWAble as _RWAble
from ._warnings import (
ExperimentalFeatureWarning,
ImplicitModificationWarning,
OldFormatWarning,
WriteWarning,
)

# Sphinx can’t find data docstrings when objects are re-exported
RWAble = _RWAble
"""A serializable object, excluding :class:`anndata.AnnData` objects i.e., something that can be stored in `uns` or `obsm`."""


# Experimental needs to be imported last
from . import experimental # isort: skip
from . import experimental # noqa: E402 isort: skip

# We use these in tests by attribute access
from . import _io, logging # noqa: F401 isort: skip
from . import _io, logging # noqa: F401, E402 isort: skip


def read(*args, **kwargs):
Expand Down Expand Up @@ -75,10 +91,28 @@ def read(*args, **kwargs):
"read_text",
"read_mtx",
"read_zarr",
"read_elem",
"write_elem",
"InMemoryElem",
"RWAble",
"CSRDataset",
"CSCDataset",
"OldFormatWarning",
"WriteWarning",
"ImplicitModificationWarning",
"ExperimentalFeatureWarning",
"experimental",
"settings",
]


class CustomExperimental(types.ModuleType):
def __getattribute__(self, key: str) -> Any:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not just define def __getattr__(key: str) -> Any: ... in the experimental module directly?

https://peps.python.org/pep-0562/ exists since 3.7

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has the advantage of never having to thinking about this again. As we move things out of experimental into the public API, we'll never have to update this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe you could do __getattr__ without a circular import? I guess I can try but I just kind of assumed not

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although now that I think about it, maybe a blanket warning isn't such a good idea...would want to be explicit about deprecations

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can just import inside of __getattr__, no?

experimental/__init__.py:

[…]

def __getattr__(key: str) -> Any:
    import anndata

    if key in anndata.__all__: ...

if key in __all__ and hasattr(experimental, key):
msg = f"Importing {key} from `anndata.experimental` is deprecated. Import from `anndata` directly."
warnings.warn(msg, FutureWarning)
return experimental.__getattribute__(key)


name = experimental.__name__
sys.modules[name] = CustomExperimental(name)
5 changes: 3 additions & 2 deletions src/anndata/_core/sparse_dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -620,7 +620,8 @@ def sparse_dataset(group: GroupStorageType) -> CSRDataset | CSCDataset:

>>> import scanpy as sc
>>> import h5py
>>> from anndata.experimental import sparse_dataset, read_elem
>>> from anndata.experimental import sparse_dataset
>>> from anndata import read_elem
>>> sc.datasets.pbmc68k_reduced().raw.to_adata().write_h5ad("pbmc.h5ad")

Initialize a sparse dataset from storage
Expand Down Expand Up @@ -668,7 +669,7 @@ def subset_sparsedataset(d, subset_idx):
_sparsedataset_depr_msg = """\
SparseDataset is deprecated and will be removed in late 2024. It has been replaced by the public classes CSRDataset and CSCDataset.

For instance checks, use `isinstance(X, (anndata.experimental.CSRDataset, anndata.experimental.CSCDataset))` instead.
For instance checks, use `isinstance(X, (anndata.CSRDataset, anndata.CSCDataset))` instead.

For creation, use `anndata.experimental.sparse_dataset(X)` instead.
"""
Expand Down
9 changes: 5 additions & 4 deletions src/anndata/_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

from anndata._core.anndata import AnnData

from ._core.sparse_dataset import BaseCompressedSparseDataset
from ._core.sparse_dataset import CSCDataset, CSRDataset
from .compat import (
AwkArray,
CupyArray,
Expand Down Expand Up @@ -49,7 +49,8 @@
H5Array,
ZarrArray,
ZappyArray,
BaseCompressedSparseDataset,
CSRDataset,
CSCDataset,
DaskArray,
CupyArray,
CupySparseMatrix,
Expand Down Expand Up @@ -182,7 +183,7 @@ def __call__(
Params
------
read_func
:func:`anndata.experimental.read_elem` function to call to read the current element given the ``iospec``.
:func:`anndata.read_elem` function to call to read the current element given the ``iospec``.
elem_name
The key to read in from the group.
elem
Expand Down Expand Up @@ -215,7 +216,7 @@ def __call__(
Params
------
write_func
:func:`anndata.experimental.write_elem` function to call to read the current element given the ``iospec``.
:func:`anndata.write_elem` function to call to read the current element given the ``iospec``.
store
The store to which `elem` should be written.
elem_name
Expand Down
10 changes: 0 additions & 10 deletions src/anndata/experimental/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,12 @@
from anndata._core.sparse_dataset import CSCDataset, CSRDataset, sparse_dataset
from anndata._io.specs import IOSpec, read_elem, read_elem_as_dask, write_elem

from .._types import InMemoryElem as _InMemoryElem
from .._types import Read, ReadCallback, StorageType, Write, WriteCallback
from .._types import RWAble as _RWAble
from ._dispatch_io import read_dispatched, write_dispatched
from .merge import concat_on_disk
from .multi_files import AnnCollection
from .pytorch import AnnLoader

# Sphinx can’t find data docstrings when objects are re-exported
InMemoryElem = _InMemoryElem
"""An in-memory element that can be read and written, including an :class:`anndata.AnnData` objects."""
RWAble = _RWAble
"""A serializable object, excluding :class:`anndata.AnnData` objects i.e., something that can be stored in `uns` or `obsm`."""

__all__ = [
"AnnCollection",
"AnnLoader",
Expand All @@ -30,9 +22,7 @@
"sparse_dataset",
"CSRDataset",
"CSCDataset",
"InMemoryElem",
"Read",
"RWAble",
"Write",
"ReadCallback",
"WriteCallback",
Expand Down
Loading
Loading