Contribute¶
Bug reports, questions, and documentation fixes are welcome at github.com/nirmallab/scimappro/issues.
Read the license before you fork
The Scimappro Academic License does not permit distributing a modified copy — that includes publishing a public fork. You may patch your own copy for internal use, and you are encouraged to send those patches upstream instead of carrying them. Contributions are accepted under the project's license.
Development setup¶
git clone https://github.com/nirmallab/scimappro.git
cd scimappro
uv sync --all-extras
uv run pytest
On a cloud-synced folder, build the environment outside the tree:
Test against the other supported interpreters with uv sync --all-extras -p 3.13
and -p 3.14.
API conventions¶
These are enforced by tests in tests/test_ported_api_surface.py and
tests/test_spatialdata_support.py, not just by review.
- Public names are camelCase.
spatialDistance, notspatial_distance;imageId, notimageid. The exception is where a third-party API already fixes the spelling. - The first argument is
data. Neveradata, and never acceptadata=as an alias in Python. It accepts anAnnData, an.h5adpath, aSpatialData, or a.zarrstore. - Never name a local variable
data— it shadows the parameter. UseplotData,matrix,coordsDf,subsetAdata. There is a test for this. - Route every input through
scimappro/_data.py. No inlineisinstance(data, str)branches:
from scimappro._data import finalizeData, openStream, resolveData
resolved = resolveData(data, sdataTable=sdataTable, streamData=streamData,
verbose=verbose, imageId=imageId,
xCoordinate=xCoordinate, yCoordinate=yCoordinate)
if resolved.streaming:
with openStream(resolved, edit=True) as capAdata:
...
return None
adata = resolved.adata
...
return finalizeData(resolved, adata, outputDir, verbose)
Read-only consumers (plots, exporters, graph builders) call loadTable
instead, or scimappro.pl._utils.load_data inside scimappro.pl.
- sdataTable is the last named parameter, immediately before **kwargs, so
functions with a required second positional argument keep working.
- Import spatialdata lazily, inside functions, never at module level.
- Standard parameters: outputDir=None, streamData=False,
maxWorkers=None (defaulting to max(1, cpu_count() - 1)), verbose,
label.
- CLIs expose --data with --adata as an argparse alias (dest="data"),
plus --sdataTable.
Docstring style¶
The API reference on this site is generated straight from docstrings by mkdocstrings, so the docstring is the documentation. Google style, with a module-level abstract:
"""
!!! abstract "Short Description"
`sp.tl.spatialDistance` computes the average shortest distance between
every pair of cell types, per image.
## Function
"""
and per function:
def spatialDistance(data, phenotype="phenotype", ..., sdataTable=None):
"""
Compute the average distance between every pair of cell phenotypes.
Parameters:
data (AnnData | SpatialData | str, required):
Cell table: an `AnnData`, a path to an `.h5ad` file, a
`SpatialData`, or a path to a `.zarr` store.
phenotype (str, optional):
Column in `obs` holding the cell type labels.
outputDir (str, optional):
Directory to write the updated object to. With `None` the object is
returned instead.
Returns:
adata (anndata.AnnData):
The input with `uns[label]` set to the distance matrix. `None` in
streaming mode, where the file on disk is updated.
Example:
```python
adata = sp.tl.spatialDistance(adata, phenotype="phenotype")
```
"""
Rules that keep the rendered pages consistent:
- Document every parameter, including the universal ones (
data,outputDir,streamData,sdataTable,maxWorkers,verbose,label). - Refer to functions as
sp.tl.…, matchingimport scimappro as sp. - Blank line between parameters — mkdocstrings needs it to keep them separate.
- Every public function gets an
Example:block with runnable code.
Adding a function¶
- Write it following the conventions above.
- Add it to the subpackage's
__init__.py. - Add a four-line stub page under
docs/api/<subpackage>/<name>.md:
- Add it to
nav:inmkdocs.ymland to the table indocs/api/index.md. - Add tests: the non-streaming path, the streaming path if it has one, stream-vs-memory equality of whatever slot it writes, SpatialData parity, and the error paths for missing layers/columns and invalid arguments.
Building the docs¶
uv sync --group docs
uv run mkdocs serve # live reload at http://127.0.0.1:8000
uv run mkdocs build --strict # what CI runs; fails on broken links
--strict is what gates a pull request, so a dead cross-reference or an
orphaned nav entry fails the build rather than shipping.
The tutorial notebooks¶
mkdocs-jupyter renders the notebooks in docs/tutorials/nbs/ as committed
— execute: false, so CI never runs them. That keeps the docs build fast and
deterministic, and it means the outputs on the site are whatever is in git.
So execute them yourself before committing, against the demo data in
example_data/:
Each notebook resolves the data directory in its first cell, and each one
recomputes what it needs from adata_scimap.h5ad rather than depending on an
earlier notebook having run.
Never hand-edit a notebook's outputs. If a cell cannot run headlessly —
pl.image_viewer needs napari and a window, helpers.addROI_omero needs an
OMERO export — put the code in a markdown fence with an admonition saying it is
not executed, rather than pasting in output from somewhere else.
Documentation deploys automatically to https://pro.scimap.xyz from
.github/workflows/docs.yml on every push to main.
Porting a function from scimap¶
The repo carries a skill file at skills/scimap-to-scimappro/SKILL.md with the
full mapping tables and the streaming/efficiency patterns. In brief: keep the
old numerical behaviour unless the maintainer explicitly accepts a new standard,
snapshot new expectations under a distinct fixture name, and add a
stream-vs-memory equality test for the field the function writes.