qbiocode.utils.tutorial_data module#

Locate the fixture files the tutorial notebooks read.

The notebooks live in one tree, tutorial/, but their fixtures are spread across three directories inside it, because several are shared between notebooks and are deliberately committed only once:

tutorial/QuVINE/datasets/

the graph benchmarks (pbmc5k_graph_*) and the balanced per-task subsets

tutorial/QProfiler/data/

pbmc5k_small_cd4_vs_cd8.h5ad and the sc_binary/*.csv exports

tutorial/QSage/data/

qprofiler_benchmarks.csv, the QProfiler results table QSage trains on

A notebook cannot simply read ./data, then: the file it needs may belong to a sibling notebook’s directory. It also cannot read the copy Sphinx renders – docs/source/tutorials/ is generated from this tree at build time without the fixtures, since nbsphinx_execute = 'never' means nothing runs there.

Every notebook previously derived its own path with

_REPO = os.path.dirname(os.path.dirname(os.path.abspath(qbiocode.__file__)))
H5AD_DIR = os.environ.get("QBC_DATA", os.path.join(_REPO, "tutorial", "QuVINE", "datasets"))

which has three faults this module fixes. It assumes an editable install – for a normal pip install qbiocode the derived root is site-packages and the path simply does not exist. It knows about exactly one of the three fixture directories, so a file that is committed once and shared is unreachable from the notebook that does not own it. And when the path is wrong the failure surfaces as whatever anndata.read_h5ad says about a missing file, naming neither the override that would fix it nor the directories that were tried.

tutorial_data_path() is the single resolution path. In order:

  1. every directory in the QBC_DATA environment variable (os.pathsep-separated, so several may be listed);

  2. the three directories above, relative to a source checkout – located from the installed package and by walking up from the current working directory, so it resolves whether the notebook runs from a clone with the package installed editable, or from a clone with the package installed normally;

  3. the notebook-local conventions ./data, ./datasets and ..

If nothing matches, the raised FileNotFoundError lists every directory searched and how to point at the data explicitly.

Summary#

Functions:

tutorial_data_dirs

Return the directories tutorial_data_path() searches, in order.

tutorial_data_path

Return the absolute path of a tutorial fixture.

Reference#

REPO_DATA_DIRS = (('tutorial', 'QuVINE', 'datasets'), ('tutorial', 'QProfiler', 'data'), ('tutorial', 'QSage', 'data'))#

Fixture directories, relative to the root of a source checkout, in the order they are searched. Paths are stored as tuples of components so they build correctly on Windows.

LOCAL_DATA_DIRS = (('data',), ('datasets',), ())#

Directories relative to the current working directory, which for a notebook is the directory the notebook itself lives in.

DATA_ENV_VAR = 'QBC_DATA'#

Environment variable holding one or more override directories.

tutorial_data_dirs()[source]#

Return the directories tutorial_data_path() searches, in order.

Returns:

Absolute paths. Directories that do not exist are kept, so callers and error messages can report exactly where the search looked.

Return type:

list of str

Examples

>>> from qbiocode.utils import tutorial_data_dirs
>>> any("QuVINE" in d for d in tutorial_data_dirs())
True
tutorial_data_path(filename, search_dirs=None)[source]#

Return the absolute path of a tutorial fixture.

Parameters:
  • filename (str) – Fixture file name, e.g. "pbmc5k_small_cd4_vs_cd8.h5ad". An absolute path, or a relative path that already resolves from the current working directory, is returned unchanged – so a notebook may hard-code a path when it has one and still call this function.

  • search_dirs (sequence of str, optional) – Directories to search instead of tutorial_data_dirs().

Returns:

Absolute path to an existing file.

Return type:

str

Raises:

FileNotFoundError – If no directory holds filename. The message lists every directory searched and how to override the location.

Notes

When QBC_DATA is set but does not contain filename and a repository directory does, the fixture is still returned – and the substitution is logged at WARNING. Resolving silently against a directory the caller did not ask for is how a stale fixture gets read for an entire session without anyone noticing.

Examples

>>> import anndata as ad
>>> from qbiocode.utils import tutorial_data_path
>>> adata = ad.read_h5ad(tutorial_data_path("pbmc5k_small_cd4_vs_cd8.h5ad"))