toolsandogh 0.2.0__tar.gz → 0.3.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- toolsandogh-0.3.2/.github/workflows/ci.yml +78 -0
- toolsandogh-0.3.2/.github/workflows/release.yml +60 -0
- {toolsandogh-0.2.0 → toolsandogh-0.3.2}/.gitignore +3 -0
- {toolsandogh-0.2.0 → toolsandogh-0.3.2}/.pre-commit-config.yaml +29 -27
- toolsandogh-0.3.2/DESIGN.md +255 -0
- toolsandogh-0.3.2/PKG-INFO +102 -0
- toolsandogh-0.3.2/README.md +64 -0
- toolsandogh-0.3.2/demos/demo_iscat_50nm_bead.py +193 -0
- {toolsandogh-0.2.0 → toolsandogh-0.3.2}/pyproject.toml +41 -48
- toolsandogh-0.3.2/src/toolsandogh/__init__.py +70 -0
- toolsandogh-0.3.2/src/toolsandogh/_link.py +223 -0
- {toolsandogh-0.2.0 → toolsandogh-0.3.2}/src/toolsandogh/_load_video.py +5 -2
- toolsandogh-0.3.2/src/toolsandogh/_locate.py +723 -0
- toolsandogh-0.3.2/src/toolsandogh/_simulate_particles.py +227 -0
- toolsandogh-0.3.2/src/toolsandogh/_simulate_psf.py +1418 -0
- {toolsandogh-0.2.0 → toolsandogh-0.3.2}/src/toolsandogh/_store_video.py +7 -14
- toolsandogh-0.3.2/src/toolsandogh/tests/conftest.py +49 -0
- toolsandogh-0.3.2/src/toolsandogh/tests/test_link.py +299 -0
- toolsandogh-0.3.2/src/toolsandogh/tests/test_locate.py +549 -0
- toolsandogh-0.3.2/src/toolsandogh/tests/test_simulate_particles.py +202 -0
- toolsandogh-0.3.2/src/toolsandogh/tests/test_simulate_psf.py +979 -0
- toolsandogh-0.3.2/src/toolsandogh.egg-info/PKG-INFO +102 -0
- {toolsandogh-0.2.0 → toolsandogh-0.3.2}/src/toolsandogh.egg-info/SOURCES.txt +13 -2
- toolsandogh-0.3.2/src/toolsandogh.egg-info/requires.txt +24 -0
- toolsandogh-0.3.2/src/toolsandogh.egg-info/scm_file_list.json +35 -0
- toolsandogh-0.3.2/src/toolsandogh.egg-info/scm_version.json +8 -0
- toolsandogh-0.3.2/uv.lock +3696 -0
- toolsandogh-0.2.0/DESIGN.md +0 -150
- toolsandogh-0.2.0/PKG-INFO +0 -81
- toolsandogh-0.2.0/README.md +0 -40
- toolsandogh-0.2.0/src/toolsandogh/__init__.py +0 -23
- toolsandogh-0.2.0/src/toolsandogh/_plot_video.py +0 -0
- toolsandogh-0.2.0/src/toolsandogh/_render.py +0 -15
- toolsandogh-0.2.0/src/toolsandogh/tests/conftest.py +0 -8
- toolsandogh-0.2.0/src/toolsandogh.egg-info/PKG-INFO +0 -81
- toolsandogh-0.2.0/src/toolsandogh.egg-info/requires.txt +0 -27
- toolsandogh-0.2.0/uv.lock +0 -3637
- {toolsandogh-0.2.0 → toolsandogh-0.3.2}/LICENSE +0 -0
- {toolsandogh-0.2.0 → toolsandogh-0.3.2}/setup.cfg +0 -0
- {toolsandogh-0.2.0 → toolsandogh-0.3.2}/src/toolsandogh/_canonicalize_video.py +0 -0
- {toolsandogh-0.2.0 → toolsandogh-0.3.2}/src/toolsandogh/_generate_video.py +0 -0
- {toolsandogh-0.2.0 → toolsandogh-0.3.2}/src/toolsandogh/_rolling.py +0 -0
- {toolsandogh-0.2.0 → toolsandogh-0.3.2}/src/toolsandogh/_rvt.py +0 -0
- {toolsandogh-0.2.0 → toolsandogh-0.3.2}/src/toolsandogh/_validate_video.py +0 -0
- {toolsandogh-0.2.0 → toolsandogh-0.3.2}/src/toolsandogh/tests/__init__.py +0 -0
- {toolsandogh-0.2.0 → toolsandogh-0.3.2}/src/toolsandogh/tests/test_io.py +0 -0
- {toolsandogh-0.2.0 → toolsandogh-0.3.2}/src/toolsandogh/tests/test_rolling.py +0 -0
- {toolsandogh-0.2.0 → toolsandogh-0.3.2}/src/toolsandogh/tests/testfile.tiff +0 -0
- {toolsandogh-0.2.0 → toolsandogh-0.3.2}/src/toolsandogh.egg-info/dependency_links.txt +0 -0
- {toolsandogh-0.2.0 → toolsandogh-0.3.2}/src/toolsandogh.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
concurrency:
|
|
9
|
+
group: ci-${{ github.ref }}
|
|
10
|
+
cancel-in-progress: true
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
# Run the full pre-commit suite (ruff, pyright, validate-pyproject, uv-lock,
|
|
14
|
+
# and the local pytest hook) exactly as developers do locally. Uses the
|
|
15
|
+
# committed uv.lock via --frozen, so any lockfile drift fails loudly.
|
|
16
|
+
pre-commit:
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
timeout-minutes: 8
|
|
19
|
+
env:
|
|
20
|
+
# Uniform cache path across OSes so the actions/cache `path` below
|
|
21
|
+
# matches what conftest.py writes (it honors this env var via
|
|
22
|
+
# expanduser). Local dev uses the platform-canonical default instead.
|
|
23
|
+
JAX_COMPILATION_CACHE_DIR: ~/.cache/jax-compilation-cache
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@main
|
|
26
|
+
- uses: astral-sh/setup-uv@d31148d669074a8d0a63714ba94f3201e7020bc3
|
|
27
|
+
with:
|
|
28
|
+
enable-cache: true
|
|
29
|
+
python-version: "3.12"
|
|
30
|
+
- name: Cache JAX compilations
|
|
31
|
+
uses: actions/cache@v5
|
|
32
|
+
with:
|
|
33
|
+
path: ~/.cache/jax-compilation-cache
|
|
34
|
+
key: jax-cc-precommit-py3.12-${{ hashFiles('uv.lock') }}
|
|
35
|
+
restore-keys: |
|
|
36
|
+
jax-cc-precommit-py3.12-
|
|
37
|
+
- run: uv run --frozen pre-commit run --all-files --show-diff-on-failure
|
|
38
|
+
|
|
39
|
+
# Portability matrix: prove the declared floors and the latest versions both
|
|
40
|
+
# work across the supported Python versions and operating systems. Each cell
|
|
41
|
+
# re-resolves dependencies directly from pyproject.toml (--no-project bypasses
|
|
42
|
+
# uv.lock), so the lowest-direct cell exercises the real lower bounds.
|
|
43
|
+
test:
|
|
44
|
+
runs-on: ${{ matrix.os }}
|
|
45
|
+
timeout-minutes: 8
|
|
46
|
+
env:
|
|
47
|
+
JAX_COMPILATION_CACHE_DIR: ~/.cache/jax-compilation-cache
|
|
48
|
+
# Self-diagnosing safety net for hangs: dump all thread stacks on
|
|
49
|
+
# SIGABRT/crash, and log every JAX JIT compilation with its timing so a
|
|
50
|
+
# stuck job shows exactly which compile/test was in flight.
|
|
51
|
+
PYTHONFAULTHANDLER: "1"
|
|
52
|
+
JAX_LOG_COMPILES: "1"
|
|
53
|
+
# Disable BLAS threads for now because they have shown to cause deadlocks
|
|
54
|
+
# in our test suite.
|
|
55
|
+
OMP_NUM_THREADS: "1"
|
|
56
|
+
OPENBLAS_NUM_THREADS: "1"
|
|
57
|
+
MKL_NUM_THREADS: "1"
|
|
58
|
+
strategy:
|
|
59
|
+
fail-fast: false
|
|
60
|
+
matrix:
|
|
61
|
+
os: [ubuntu-latest, windows-latest, macos-latest]
|
|
62
|
+
python: ["3.12", "3.13"]
|
|
63
|
+
resolution: [highest, lowest-direct]
|
|
64
|
+
steps:
|
|
65
|
+
- uses: actions/checkout@main
|
|
66
|
+
- uses: astral-sh/setup-uv@d31148d669074a8d0a63714ba94f3201e7020bc3
|
|
67
|
+
with:
|
|
68
|
+
enable-cache: true
|
|
69
|
+
python-version: ${{ matrix.python }}
|
|
70
|
+
- name: Cache JAX compilations
|
|
71
|
+
uses: actions/cache@v5
|
|
72
|
+
with:
|
|
73
|
+
path: ~/.cache/jax-compilation-cache
|
|
74
|
+
key: jax-cc-${{ runner.os }}-py${{ matrix.python }}-${{ matrix.resolution }}-${{ hashFiles('uv.lock') }}
|
|
75
|
+
restore-keys: |
|
|
76
|
+
jax-cc-${{ runner.os }}-py${{ matrix.python }}-${{ matrix.resolution }}-
|
|
77
|
+
jax-cc-${{ runner.os }}-py${{ matrix.python }}-
|
|
78
|
+
- run: uv run --no-project --with-editable . --with "pytest>=8.0" --with "pytest-timeout>=2.4" --python ${{ matrix.python }} --resolution ${{ matrix.resolution }} pytest --timeout=180 --timeout-method=thread -v --durations=20
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
# Never cancel an in-flight release; only one release per tag at a time.
|
|
9
|
+
concurrency:
|
|
10
|
+
group: release-${{ github.ref }}
|
|
11
|
+
cancel-in-progress: false
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
# Build the sdist and wheel from the tagged commit. setuptools-scm derives
|
|
18
|
+
# the version from git, so a full history checkout is required.
|
|
19
|
+
build:
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
timeout-minutes: 10
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@main
|
|
24
|
+
with:
|
|
25
|
+
fetch-depth: 0
|
|
26
|
+
- uses: astral-sh/setup-uv@d31148d669074a8d0a63714ba94f3201e7020bc3
|
|
27
|
+
with:
|
|
28
|
+
enable-cache: true
|
|
29
|
+
python-version: "3.12"
|
|
30
|
+
- run: uv build -o dist
|
|
31
|
+
- uses: actions/upload-artifact@v7
|
|
32
|
+
with:
|
|
33
|
+
name: dist
|
|
34
|
+
path: dist/
|
|
35
|
+
|
|
36
|
+
# Publish to PyPI via Trusted Publishing (OIDC, no stored tokens) and create
|
|
37
|
+
# a GitHub Release with the built artifacts attached. Requires a one-time
|
|
38
|
+
# Trusted Publisher registration on pypi.org pointing at this workflow file
|
|
39
|
+
# (release.yml), the `pypi` environment, and this repository.
|
|
40
|
+
publish:
|
|
41
|
+
needs: build
|
|
42
|
+
runs-on: ubuntu-latest
|
|
43
|
+
timeout-minutes: 5
|
|
44
|
+
environment: pypi
|
|
45
|
+
permissions:
|
|
46
|
+
id-token: write # PyPI Trusted Publishing (OIDC)
|
|
47
|
+
contents: write # create GitHub Release
|
|
48
|
+
steps:
|
|
49
|
+
- uses: actions/download-artifact@v8
|
|
50
|
+
with:
|
|
51
|
+
name: dist
|
|
52
|
+
path: dist/
|
|
53
|
+
- name: Publish to PyPI
|
|
54
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
55
|
+
- name: Create GitHub Release
|
|
56
|
+
uses: softprops/action-gh-release@v3
|
|
57
|
+
with:
|
|
58
|
+
files: dist/*
|
|
59
|
+
name: ${{ github.ref_name }}
|
|
60
|
+
generate_release_notes: true
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
repos:
|
|
2
2
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
3
|
-
rev:
|
|
3
|
+
rev: v6.0.0
|
|
4
4
|
hooks:
|
|
5
5
|
- id: check-case-conflict
|
|
6
6
|
- id: check-illegal-windows-names
|
|
@@ -12,39 +12,41 @@ repos:
|
|
|
12
12
|
- id: check-added-large-files
|
|
13
13
|
|
|
14
14
|
- repo: https://github.com/astral-sh/uv-pre-commit
|
|
15
|
-
rev: 0.
|
|
15
|
+
rev: 0.11.26
|
|
16
16
|
hooks:
|
|
17
17
|
- id: uv-lock
|
|
18
18
|
|
|
19
|
-
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
20
|
-
rev: v0.14.0
|
|
21
|
-
hooks:
|
|
22
|
-
- id: ruff-check
|
|
23
|
-
args: [ --fix ]
|
|
24
|
-
- id: ruff-format
|
|
25
|
-
|
|
26
|
-
- repo: https://github.com/numpy/numpydoc
|
|
27
|
-
rev: v1.9.0
|
|
28
|
-
hooks:
|
|
29
|
-
- id: numpydoc-validation
|
|
30
|
-
|
|
31
19
|
- repo: https://github.com/abravalheri/validate-pyproject
|
|
32
|
-
rev: v0.
|
|
20
|
+
rev: v0.25
|
|
33
21
|
hooks:
|
|
34
22
|
- id: validate-pyproject
|
|
35
23
|
additional_dependencies: ["validate-pyproject-schema-store[all]"]
|
|
36
24
|
|
|
37
|
-
- repo: https://github.com/RobertCraigie/pyright-python
|
|
38
|
-
rev: v1.1.406
|
|
39
|
-
hooks:
|
|
40
|
-
- id: pyright
|
|
41
|
-
|
|
42
25
|
- repo: local
|
|
43
26
|
hooks:
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
27
|
+
- id: ruff-check
|
|
28
|
+
name: ruff check
|
|
29
|
+
entry: uv run ruff check --fix --exit-non-zero-on-fix
|
|
30
|
+
language: system
|
|
31
|
+
types: [python]
|
|
32
|
+
require_serial: true
|
|
33
|
+
- id: ruff-format
|
|
34
|
+
name: ruff format
|
|
35
|
+
entry: uv run ruff format
|
|
36
|
+
language: system
|
|
37
|
+
types: [python]
|
|
38
|
+
require_serial: true
|
|
39
|
+
- id: pyright
|
|
40
|
+
name: pyright
|
|
41
|
+
entry: uv run pyright
|
|
42
|
+
language: system
|
|
43
|
+
types: [python]
|
|
44
|
+
pass_filenames: false
|
|
45
|
+
always_run: true
|
|
46
|
+
- id: pytest
|
|
47
|
+
name: pytest
|
|
48
|
+
entry: uv run pytest
|
|
49
|
+
language: system
|
|
50
|
+
types: [python]
|
|
51
|
+
pass_filenames: false
|
|
52
|
+
always_run: true
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
# Design & Architecture Overview for the toolsandogh package
|
|
2
|
+
|
|
3
|
+
*Last updated: 2026-07-02*
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Project Overview
|
|
8
|
+
|
|
9
|
+
toolsandogh is a Python library (>= 3.12) for processing large-scale scientific data sets (e.g., microscopy volumes, multi-modal imaging, high-throughput experiments).
|
|
10
|
+
|
|
11
|
+
The library embraces **lazy, parallel computation** via Dask, **labeled, N-dimensional arrays** via xarray, and **type-safe, well-documented** code.
|
|
12
|
+
|
|
13
|
+
### Demos
|
|
14
|
+
|
|
15
|
+
Standalone demos live in the top-level `demos/` directory. They import `toolsandogh` and exercise its public API. Demos should keep dependencies to a minimum (ideally only the library itself plus a rendering backend like Matplotlib), so they remain runnable without pulling in heavy dev dependencies.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Repository Layout
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
toolsandogh/
|
|
23
|
+
|
|
|
24
|
+
|-- DESIGN.md # <- THIS FILE (design & conventions)
|
|
25
|
+
|-- README.md # User-oriented introduction, installation, examples
|
|
26
|
+
|-- pyproject.toml # Build system, metadata, dependencies, ruff config
|
|
27
|
+
|-- .pre-commit-config.yaml # Pre-commit hooks (ruff, isort, pyright, etc.)
|
|
28
|
+
|-- .gitignore # Files/folders that Git should ignore
|
|
29
|
+
|-- uv.lock # Deterministic lockfile generated by uv
|
|
30
|
+
|
|
|
31
|
+
|-- demos/ # Standalone demo scripts (import toolsandogh; keep deps light)
|
|
32
|
+
| `-- demo_iscat_50nm_bead.py
|
|
33
|
+
|
|
|
34
|
+
`-- src/
|
|
35
|
+
`-- toolsandogh/ # The actual Python package
|
|
36
|
+
|-- __init__.py
|
|
37
|
+
|-- _canonicalize_video.py # All file names start with an underscore
|
|
38
|
+
|-- _generate_video.py
|
|
39
|
+
|-- _link.py
|
|
40
|
+
|-- _load_video.py
|
|
41
|
+
|-- _locate.py
|
|
42
|
+
|-- _rolling.py
|
|
43
|
+
|-- _rvt.py
|
|
44
|
+
|-- _simulate_particles.py
|
|
45
|
+
|-- _simulate_psf.py
|
|
46
|
+
|-- _store_video.py
|
|
47
|
+
|-- _validate_video.py
|
|
48
|
+
|
|
|
49
|
+
`-- tests/ # Tests for all toolsandogh functionality
|
|
50
|
+
|-- __init__.py
|
|
51
|
+
|-- conftest.py
|
|
52
|
+
|-- test_io.py
|
|
53
|
+
|-- test_link.py
|
|
54
|
+
|-- test_locate.py
|
|
55
|
+
|-- test_rolling.py
|
|
56
|
+
|-- test_simulate_particles.py
|
|
57
|
+
`-- test_simulate_psf.py
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Coding Conventions
|
|
63
|
+
|
|
64
|
+
### 1. Coding Guidelines
|
|
65
|
+
|
|
66
|
+
The following guidelines apply to all code in the toolsandogh package. They are intended to keep the codebase consistent, readable, and easy to maintain for both human contributors and AI assistants. Where a guideline and a deadline conflict, correctness always wins.
|
|
67
|
+
|
|
68
|
+
#### 1.1 Naming and module layout
|
|
69
|
+
|
|
70
|
+
* Use ``snake_case`` for functions and variables, ``PascalCase`` for classes, and ``UPPER_CASE`` for module-level constants.
|
|
71
|
+
* Every module file name starts with an underscore (e.g. ``_locate.py``). Modules are private by default; only objects explicitly re-exported from ``toolsandogh/__init__.py`` form the public API. Importing from a private module directly is unsupported and may break without notice.
|
|
72
|
+
* New code should live in a new module when it addresses a distinct concern; otherwise it belongs in the existing module that covers the same concern. As a guideline rather than a hard rule, prefer one file per feature or workflow stage.
|
|
73
|
+
|
|
74
|
+
#### 1.2 Type hints
|
|
75
|
+
|
|
76
|
+
* All public functions, methods, and class attributes carry full type hints. Private helpers may omit them only when the annotation would be redundant with the surrounding context.
|
|
77
|
+
* Hints should be as specific as possible without becoming brittle: prefer ``Literal["r", "w"]`` over ``str`` for closed sets of options; prefer concrete container types over bare ``Any``; avoid ``Optional`` where ``X | None`` reads more clearly.
|
|
78
|
+
* Document variance explicitly when it matters (e.g., on protocols or abstract base classes).
|
|
79
|
+
|
|
80
|
+
#### 1.3 Inputs and outputs
|
|
81
|
+
|
|
82
|
+
* Publicly visible functions should be liberal in what they accept and conservative in what they emit.
|
|
83
|
+
* Accept the widest reasonable input: file paths, raw arrays, tuples, or plain lists where convenient, and let the function coerce them into the canonical representation.
|
|
84
|
+
* Always emit stable, well-typed values (typically ``xarray.DataArray`` or ``polars.DataFrame``). Do not leak implementation types such as NumPy arrays or pandas DataFrames through the public API.
|
|
85
|
+
|
|
86
|
+
#### 1.4 Data structures
|
|
87
|
+
|
|
88
|
+
* Array data is encoded as ``xarray.DataArray``, ideally lazily backed by Dask so that operations stay parallel and out-of-core.
|
|
89
|
+
* Tabular data is encoded as ``polars.DataFrame``.
|
|
90
|
+
|
|
91
|
+
#### 1.5 Simplicity
|
|
92
|
+
|
|
93
|
+
* Prefer free functions over classes. Functionality should be provided mainly through free functions, with classes reserved for genuine stateful objects.
|
|
94
|
+
* Each function should have one clear purpose that can be summed up in a single-line docstring. If a function's purpose resists a one-line summary, it is probably doing too much.
|
|
95
|
+
* Keep classes small and focused. Prefer composition over inheritance.
|
|
96
|
+
|
|
97
|
+
#### 1.6 Error handling
|
|
98
|
+
|
|
99
|
+
* Raise specific, informative exceptions that explain what went wrong and why. Include the offending value where helpful.
|
|
100
|
+
* Never silently coerce types or swallow exceptions. A bare ``except`` or an unchecked type coercion hides bugs and should be avoided.
|
|
101
|
+
* Validate inputs at the boundary of the public API; prefer explicit checks over defensive programming scattered through the internals.
|
|
102
|
+
|
|
103
|
+
### 2. Documentation -- numpydoc
|
|
104
|
+
|
|
105
|
+
All public objects (functions, classes, methods) must be documented using the **numpydoc** style. This format is machine-parsable (e.g., for Sphinx) and familiar to scientific Python users.
|
|
106
|
+
|
|
107
|
+
#### 2.1 Required and optional sections
|
|
108
|
+
|
|
109
|
+
Every public docstring must contain the following sections **in this exact order**. Optional sections may be omitted when they carry no content, but if present they must appear at the position shown:
|
|
110
|
+
|
|
111
|
+
1. **Short summary** -- a single, imperative line ending with a period. No blank line between the signature and the summary. Avoid restating the function name.
|
|
112
|
+
2. *(blank line)*
|
|
113
|
+
3. **Extended summary** -- one or more paragraphs giving context, assumptions, and edge cases. May be omitted if the short summary is self-explanatory.
|
|
114
|
+
4. **Parameters** -- always present for any object that accepts arguments.
|
|
115
|
+
5. **Returns** -- always present for any object that returns a value other than `None`.
|
|
116
|
+
6. *(optional)* **Yields** -- for generators, instead of (not in addition to) **Returns**.
|
|
117
|
+
7. *(optional)* **Raises** -- one ``exception_type`` per entry, followed by a description of the condition that triggers it. List every exception the function may raise intentionally.
|
|
118
|
+
8. *(optional)* **Notes** -- implementation details, algorithmic notes, performance caveats, and limitations.
|
|
119
|
+
9. *(optional)* **References** -- numbered citations using the ``.. [1]`` reST footnote syntax. Each reference is cited in the body text as ``[1]_``.
|
|
120
|
+
10. *(optional)* **Examples** -- runnable code in doctest form.
|
|
121
|
+
|
|
122
|
+
Do **not** invent sections outside this list (no *Todo*, no *Author*, no *Version*). Use the project changelog and metadata for that information.
|
|
123
|
+
|
|
124
|
+
#### 2.2 Section headers
|
|
125
|
+
|
|
126
|
+
Each section header is a single line of the form
|
|
127
|
+
|
|
128
|
+
```rst
|
|
129
|
+
Parameters
|
|
130
|
+
----------
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
i.e. the title (capitalised as shown above, with the exact spelling and singular/plural form from the numpydoc spec) followed by a line of hyphens of matching length. Section titles are not underlined with ``=`` or ``~``. There is exactly one blank line before and after each header.
|
|
134
|
+
|
|
135
|
+
#### 2.3 Parameters section
|
|
136
|
+
|
|
137
|
+
* One entry per parameter, in the same order as the function signature.
|
|
138
|
+
* Each entry begins with ``name`` followed by `` : `` and a type description, then a newline and an indented description.
|
|
139
|
+
* The type uses the **runtime expression**, not the annotation verbatim: write ``int or tuple of int`` rather than ``int | tuple[int, ...]``. Reserved words ``True``, ``False``, ``None`` are written in full, not as types.
|
|
140
|
+
* Append ``, optional`` to the type for any parameter that has a default value (including keyword-only and positional-or-keyword parameters with a default). Do **not** append it for required parameters, not even required keyword-only ones.
|
|
141
|
+
* The description states the **default** in prose, e.g. "The default is ``3``." For boolean defaults, write the behaviour out: "If ``True`` (default), ...".
|
|
142
|
+
* Parameters that accept ``*args`` / ``**kwargs`` are documented with the names ``*args`` and ``**kwargs`` respectively.
|
|
143
|
+
|
|
144
|
+
#### 2.4 Returns section
|
|
145
|
+
|
|
146
|
+
* For a single return value: a type line, then an indented description. Do not invent a name.
|
|
147
|
+
* For multiple named return values (e.g. a ``NamedTuple``), document each component with its name, type, and description, mirroring the Parameters layout.
|
|
148
|
+
* If the function returns `None` as a meaningful sentinel, document it. If it returns nothing, the **Returns** section is omitted entirely.
|
|
149
|
+
|
|
150
|
+
#### 2.5 Types and type hints
|
|
151
|
+
|
|
152
|
+
* Type hints live in the signature and should be as specific as possible. The docstring repeats the type in prose for the benefit of readers and Sphinx, but does not introduce new type information.
|
|
153
|
+
* Use `Literal` types for closed sets of string options (e.g. ``Literal["r", "w"]``) and document the accepted values explicitly in the parameter description.
|
|
154
|
+
* Write collection types out in prose (``list of str``) rather than ``list[str]`` in the docstring.
|
|
155
|
+
|
|
156
|
+
#### 2.6 Cross-references and formatting
|
|
157
|
+
|
|
158
|
+
* Use reST roles for cross-references: ``:func:`package.module.func``` for functions, ``:class:`package.module.Class``` for classes, ``:meth:`` for methods, ``:attr:`` for attributes, ``:mod:`` for modules. Always give the fully qualified path.
|
|
159
|
+
* Inline code is written as double backticks (```` ``code`` ````), not single backticks.
|
|
160
|
+
* Math may be written with ``:math:`expr``` inline or the ``.. math::`` directive for display equations.
|
|
161
|
+
* Keep paragraphs wrapped at a reasonable column (around 79 characters) and separated by a single blank line.
|
|
162
|
+
|
|
163
|
+
#### 2.7 Examples section
|
|
164
|
+
|
|
165
|
+
* Examples use the doctest ``>>>`` prompt and must be **runnable** in isolation (all imports shown, no reliance on prior cells).
|
|
166
|
+
* Prefer showing the expected output as a comment or repr block, so the example doubles as a regression test when run with ``pytest --doctest-modules``.
|
|
167
|
+
* Avoid side effects that modify global state or the filesystem. Where unavoidable, clean up within the example.
|
|
168
|
+
* Each example should illustrate one feature; do not bundle unrelated demonstrations.
|
|
169
|
+
|
|
170
|
+
#### 2.8 Comprehensive example
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
def median_filter(
|
|
174
|
+
data: xr.DataArray,
|
|
175
|
+
size: int | tuple[int, ...] = 3,
|
|
176
|
+
*,
|
|
177
|
+
preserve_nan: bool = True,
|
|
178
|
+
) -> xr.DataArray:
|
|
179
|
+
"""
|
|
180
|
+
Apply a median filter to an ``xarray.DataArray`` using a sliding window.
|
|
181
|
+
|
|
182
|
+
Parameters
|
|
183
|
+
----------
|
|
184
|
+
data : xarray.DataArray
|
|
185
|
+
The input array. The array can be lazily backed by Dask; the
|
|
186
|
+
operation will therefore be executed in parallel when the result
|
|
187
|
+
is computed.
|
|
188
|
+
|
|
189
|
+
size : int or tuple of int, optional
|
|
190
|
+
The size of the filtering window along each dimension. If a
|
|
191
|
+
single integer is supplied it is used for all dimensions. The
|
|
192
|
+
default is ``3`` (a 3x3x... window).
|
|
193
|
+
|
|
194
|
+
preserve_nan : bool, optional
|
|
195
|
+
If ``True`` (default), NaN values are ignored when computing the
|
|
196
|
+
median, i.e. they act as "missing data". If ``False`` any window
|
|
197
|
+
containing a NaN will produce a NaN in the output.
|
|
198
|
+
|
|
199
|
+
Returns
|
|
200
|
+
-------
|
|
201
|
+
xarray.DataArray
|
|
202
|
+
A new ``DataArray`` with the same coordinates and attributes as
|
|
203
|
+
``data`` but containing the filtered values. The returned object
|
|
204
|
+
retains the original chunking layout, so the computation remains
|
|
205
|
+
lazy until ``compute()`` or ``persist()`` is called.
|
|
206
|
+
|
|
207
|
+
Notes
|
|
208
|
+
-----
|
|
209
|
+
* The implementation uses :func:`dask.array.map_overlap` under the
|
|
210
|
+
hood, which means that the filter works on arbitrarily large
|
|
211
|
+
datasets that do not fit into memory.
|
|
212
|
+
* For integer ``size`` values larger than the chunk size along a
|
|
213
|
+
dimension, the function will rechunk automatically. This may
|
|
214
|
+
trigger a temporary increase in memory usage.
|
|
215
|
+
|
|
216
|
+
References
|
|
217
|
+
----------
|
|
218
|
+
.. [1] J. R. Smith, *Median filtering of large image stacks*, J. Imaging,
|
|
219
|
+
2023, https://doi.org/10.1234/jim.2023.001
|
|
220
|
+
|
|
221
|
+
Examples
|
|
222
|
+
--------
|
|
223
|
+
>>> import xarray as xr, numpy as np
|
|
224
|
+
>>> arr = xr.DataArray(np.random.rand(100, 100), dims=('y', 'x'))
|
|
225
|
+
>>> filtered = median_filter(arr, size=5)
|
|
226
|
+
>>> filtered.compute() # triggers Dask execution
|
|
227
|
+
<xarray.DataArray (y: 100, x: 100)>
|
|
228
|
+
"""
|
|
229
|
+
# implementation follows ...
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### 3. Testing
|
|
233
|
+
|
|
234
|
+
Tests live in ``src/toolsandogh/tests/`` and use **pytest** as the sole test runner. The project keeps the testing story simple: plain pytest tests, no property-based testing frameworks, no separate test harnesses.
|
|
235
|
+
|
|
236
|
+
#### 3.1 Test files and naming
|
|
237
|
+
|
|
238
|
+
* One test file per feature or workflow stage under test (e.g. ``test_locate.py``, ``test_link.py``, ``test_io.py``), mirroring the module layout.
|
|
239
|
+
* Test functions are named ``test_<unit>_<scenario>`` so that names describe what is being verified and under which conditions (e.g. ``test_locate_empty_image_returns_empty_dataframe``).
|
|
240
|
+
* Shared fixtures live in ``conftest.py``; keep fixtures small, focused, and reusable across files.
|
|
241
|
+
|
|
242
|
+
#### 3.2 Writing tests
|
|
243
|
+
|
|
244
|
+
* Write tests that exercise correctness, not coverage numbers. Coverage is a useful signal but never a goal in itself: a passing test that proves the right behaviour is worth more than a percentage point.
|
|
245
|
+
* Cover edge cases explicitly: empty arrays, single-element arrays, NaN inputs, mismatched shapes, and boundary conditions.
|
|
246
|
+
* Prefer deterministic inputs. Where randomness is unavoidable, set a fixed seed within the test so that runs are reproducible.
|
|
247
|
+
* Exercise both lazy and computed paths where relevant: assert that an operation returns a lazy result and that calling ``.compute()`` produces the expected values.
|
|
248
|
+
|
|
249
|
+
#### 3.3 Doctests
|
|
250
|
+
|
|
251
|
+
* Docstring examples (see section 2.7) double as regression tests when run with ``pytest --doctest-modules``. Keep them runnable in isolation and free of side effects that could break the suite.
|
|
252
|
+
|
|
253
|
+
### 4. Linting
|
|
254
|
+
|
|
255
|
+
Formatting and import style are enforced by **ruff**, configured in ``pyproject.toml``. In short: code must be free of pycodestyle and pyflakes errors and follow PEP-8, with imports sorted as with **isort**. Refer to the ruff configuration for the authoritative list of rules.
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: toolsandogh
|
|
3
|
+
Version: 0.3.2
|
|
4
|
+
Summary: A collection of Python scripts for iSCAT microscopy data analysis
|
|
5
|
+
Author-email: Marco Heisig <marco.heisig@mpl.mpg.de>
|
|
6
|
+
License-Expression: GPL-3.0-or-later
|
|
7
|
+
Project-URL: Repository, https://github.com/SandoghdarLab/toolsandogh
|
|
8
|
+
Project-URL: Issues, https://github.com/SandoghdarLab/toolsandogh/issues
|
|
9
|
+
Keywords: tools,utilities,python,microscopy
|
|
10
|
+
Requires-Python: >=3.12
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Requires-Dist: bioio>=3.4
|
|
14
|
+
Requires-Dist: bioio-dv>=1.2
|
|
15
|
+
Requires-Dist: bioio-imageio>=1.3
|
|
16
|
+
Requires-Dist: bioio-lif>=1.3
|
|
17
|
+
Requires-Dist: bioio-nd2>=2.0
|
|
18
|
+
Requires-Dist: bioio-ome-tiff>=1.4
|
|
19
|
+
Requires-Dist: bioio-ome-zarr>=3.5
|
|
20
|
+
Requires-Dist: bioio-tifffile>=1.3
|
|
21
|
+
Requires-Dist: bioio-tiff-glob>=1.2
|
|
22
|
+
Requires-Dist: dask>=2026.1
|
|
23
|
+
Requires-Dist: fsspec>=2025.3
|
|
24
|
+
Requires-Dist: imageio[ffmpeg]>=2.37
|
|
25
|
+
Requires-Dist: imgrvt>=1.0
|
|
26
|
+
Requires-Dist: jax>=0.10.2
|
|
27
|
+
Requires-Dist: jaxtyping>=0.3
|
|
28
|
+
Requires-Dist: numpy>=2.3
|
|
29
|
+
Requires-Dist: ome-types>=0.6
|
|
30
|
+
Requires-Dist: polars>=1.0
|
|
31
|
+
Requires-Dist: pyarrow>=22.0
|
|
32
|
+
Requires-Dist: scikit-image>=0.23
|
|
33
|
+
Requires-Dist: scipy>=1.17
|
|
34
|
+
Requires-Dist: tifffile>=2026.5
|
|
35
|
+
Requires-Dist: tqdm>=4.66
|
|
36
|
+
Requires-Dist: xarray>=2026.1
|
|
37
|
+
Dynamic: license-file
|
|
38
|
+
|
|
39
|
+
# toolsandogh
|
|
40
|
+
|
|
41
|
+
A Python toolbox for iSCAT microscopy data analysis, developed by the Sandoghdar Division of the Max Planck Institute for the Science of Light (MPL).
|
|
42
|
+
|
|
43
|
+
## Overview
|
|
44
|
+
|
|
45
|
+
`toolsandogh` provides a small, reusable library for loading, processing, analyzing, and storing microscopy videos. It is designed around a canonical 5D data model---`T` (time), `C` (channel), `Z` (slice), `Y` (row), `X` (column)---implemented as lazy [xarray](https://docs.xarray.dev/) arrays backed by [Dask](https://www.dask.org/).
|
|
46
|
+
|
|
47
|
+
## Features
|
|
48
|
+
|
|
49
|
+
- **Lazy, out-of-core computation**: Videos are kept as Dask arrays so you can work with datasets larger than memory.
|
|
50
|
+
- **Labeled, metadata-rich arrays**: Results are `xarray.DataArray` objects with `TCZYX` dimensions and OME metadata.
|
|
51
|
+
- **Multiple microscopy formats**: Read and write RAW/BIN, TIFF, OME-TIFF, OME-Zarr, ND2, and other formats supported by [bioio](https://github.com/bioio-devs/bioio).
|
|
52
|
+
- **Reusable analysis primitives**: Rolling statistics, radial variance transform, synthetic data generation, and more.
|
|
53
|
+
|
|
54
|
+
## Installation
|
|
55
|
+
|
|
56
|
+
This project uses the [uv package manager](https://docs.astral.sh/uv/). Clone the repository and run:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
uv sync
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
To install in editable mode for development:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
uv pip install -e ".[dev]"
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Quick start
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
import toolsandogh as sand
|
|
72
|
+
|
|
73
|
+
# Load a video (any bioio-supported format or raw BIN/RAW file).
|
|
74
|
+
video = sand.load_video("movie.nd2")
|
|
75
|
+
|
|
76
|
+
# Apply a rolling average along the time axis.
|
|
77
|
+
smoothed = sand.rolling_average(video, window_size=5, dim="T")
|
|
78
|
+
|
|
79
|
+
# Save the result back to disk.
|
|
80
|
+
sand.store_video(smoothed, "smoothed.ome.tiff")
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Use `help(sand.load_video)` or `help(sand.store_video)` for details on the available arguments.
|
|
84
|
+
|
|
85
|
+
## Repository layout
|
|
86
|
+
|
|
87
|
+
- `src/toolsandogh/` --- the main Python package.
|
|
88
|
+
- `src/toolsandogh/tests/` --- `pytest` test suite.
|
|
89
|
+
- `DESIGN.md` --- design conventions and coding standards.
|
|
90
|
+
|
|
91
|
+
## Contributing
|
|
92
|
+
|
|
93
|
+
Please see `DESIGN.md` for coding conventions, documentation standards, and architecture decisions. In brief:
|
|
94
|
+
|
|
95
|
+
- All public functions must be type-hinted and documented in numpydoc style.
|
|
96
|
+
- Code is linted with `ruff` and type-checked with `pyright`.
|
|
97
|
+
- Prefer free functions; keep data in `xarray.DataArray`/`Dataset` form.
|
|
98
|
+
- Tests go in `src/toolsandogh/tests/`.
|
|
99
|
+
|
|
100
|
+
## License
|
|
101
|
+
|
|
102
|
+
GPL-3.0-or-later.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# toolsandogh
|
|
2
|
+
|
|
3
|
+
A Python toolbox for iSCAT microscopy data analysis, developed by the Sandoghdar Division of the Max Planck Institute for the Science of Light (MPL).
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
`toolsandogh` provides a small, reusable library for loading, processing, analyzing, and storing microscopy videos. It is designed around a canonical 5D data model---`T` (time), `C` (channel), `Z` (slice), `Y` (row), `X` (column)---implemented as lazy [xarray](https://docs.xarray.dev/) arrays backed by [Dask](https://www.dask.org/).
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- **Lazy, out-of-core computation**: Videos are kept as Dask arrays so you can work with datasets larger than memory.
|
|
12
|
+
- **Labeled, metadata-rich arrays**: Results are `xarray.DataArray` objects with `TCZYX` dimensions and OME metadata.
|
|
13
|
+
- **Multiple microscopy formats**: Read and write RAW/BIN, TIFF, OME-TIFF, OME-Zarr, ND2, and other formats supported by [bioio](https://github.com/bioio-devs/bioio).
|
|
14
|
+
- **Reusable analysis primitives**: Rolling statistics, radial variance transform, synthetic data generation, and more.
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
This project uses the [uv package manager](https://docs.astral.sh/uv/). Clone the repository and run:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
uv sync
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
To install in editable mode for development:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
uv pip install -e ".[dev]"
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Quick start
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
import toolsandogh as sand
|
|
34
|
+
|
|
35
|
+
# Load a video (any bioio-supported format or raw BIN/RAW file).
|
|
36
|
+
video = sand.load_video("movie.nd2")
|
|
37
|
+
|
|
38
|
+
# Apply a rolling average along the time axis.
|
|
39
|
+
smoothed = sand.rolling_average(video, window_size=5, dim="T")
|
|
40
|
+
|
|
41
|
+
# Save the result back to disk.
|
|
42
|
+
sand.store_video(smoothed, "smoothed.ome.tiff")
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Use `help(sand.load_video)` or `help(sand.store_video)` for details on the available arguments.
|
|
46
|
+
|
|
47
|
+
## Repository layout
|
|
48
|
+
|
|
49
|
+
- `src/toolsandogh/` --- the main Python package.
|
|
50
|
+
- `src/toolsandogh/tests/` --- `pytest` test suite.
|
|
51
|
+
- `DESIGN.md` --- design conventions and coding standards.
|
|
52
|
+
|
|
53
|
+
## Contributing
|
|
54
|
+
|
|
55
|
+
Please see `DESIGN.md` for coding conventions, documentation standards, and architecture decisions. In brief:
|
|
56
|
+
|
|
57
|
+
- All public functions must be type-hinted and documented in numpydoc style.
|
|
58
|
+
- Code is linted with `ruff` and type-checked with `pyright`.
|
|
59
|
+
- Prefer free functions; keep data in `xarray.DataArray`/`Dataset` form.
|
|
60
|
+
- Tests go in `src/toolsandogh/tests/`.
|
|
61
|
+
|
|
62
|
+
## License
|
|
63
|
+
|
|
64
|
+
GPL-3.0-or-later.
|