Adding New Storage Backends
We welcome the addition of new storage backends to ExEngine! If you’ve created a storage backend that could benefit others, please consider opening a Pull Request.
This guide outlines the process of creating new data storage backends.
Code Organization and Packaging
When adding a new storage backend to ExEngine, follow this directory structure:
src/exengine/
└── storage_backends/
└── your_new_storage/
├── __init__.py
├── your_storage_implementation.py
└── test/
├── __init__.py
└── test_your_storage.py
Replace your_new_storage with an appropriate name for your storage backend.
You may also want to edit the __init__.py file in the your_new_storage directory to import the Class from your storage implementation file (see the NDStorage backend for an example of this).
Additional Dependencies
If your storage backend requires additional dependencies, add them to the pyproject.toml file:
Open the
pyproject.tomlfile in the root directory of the project.Add a new optional dependency group for your storage backend:
[project.optional-dependencies] your_new_storage = ["your_dependency1", "your_dependency2"]
Update the
allgroup to include your new storage backend:all = [ "mmpycorex", "ndstorage", "your_dependency1", "your_dependency2" ]
Add it to the
storage backendsgroup:# storage backends your_new_storage = ["your_dependency1", "your_dependency2"]
Implementing a New Storage Backend
All new storage backends should inherit from the DataStorage abstract base class. This ensures compatibility with the ExEngine framework.
The DataStorage abstract base class is defined in exengine/kernel/data_storage_base.py. You can find the full definition and method requirements there.
Here’s a basic structure for implementing a new storage backend:
from exengine.kernel.data_storage_base import DataStorage
class YourNewStorage(DataStorage):
def __init__(self):
super().__init__()
# Your storage-specific initialization code here
# Implement the abstract methods from DataStorage
# Refer to data_storage_base.py for the full list of methods to implement
When implementing your storage backend, make sure to override all abstract methods from the DataStorage base class. These methods define the interface that ExEngine expects from a storage backend.
Adding Tests
Create a
test_your_storage.pyfile in thetest/directory of your storage backend.Write pytest test cases for your storage backend functionality. For example:
import pytest import numpy as np from exengine.storage_backends.your_new_storage import YourNewStorage def test_your_storage_initialization(): storage = YourNewStorage() assert isinstance(storage, YourNewStorage) def test_put_and_get_data(): storage = YourNewStorage() data = np.array([1, 2, 3]) metadata = {"key": "value"} coordinates = {"time": 0, "channel": "DAPI"} storage.put(coordinates, data, metadata) assert coordinates in storage np.testing.assert_array_equal(storage.get_data(coordinates), data) assert storage.get_metadata(coordinates) == metadata # Add more test cases as needed
Running Tests
To run tests for your new storage backend:
Install the test dependencies. In the ExEngine root directory, run:
pip install -e ".[test,your_new_storage]"
Run pytest for your storage backend:
pytest -v src/exengine/storage_backends/your_new_storage/test
Adding Documentation
Add documentation for your new storage backend in the
docs/directory.Create a new RST file, e.g.,
docs/usage/backends/your_new_storage.rst, describing how to use your storage backend.Update
docs/usage/backends.rstto include your new storage backend documentation.
To build the documentation locally, you may need to install the required dependencies. In the exengine/docs directory, run:
pip install -r requirements.txt
Then, to build, in the exengine/docs directory, run:
make clean && make html
then open _build/html/index.html in a web browser to view the documentation.