Source code for neuralfetch.utils.runner
# Copyright (c) Meta Platforms, Inc. and affiliates.
# All rights reserved.
#
# This source code is licensed under the license found in the
# LICENSE file in the root directory of this source tree.
"""Study download runner.
Download a registered study's raw dataset into the configured study root.
The CLI wiring lives in :mod:`neuralfetch.commands.download`; this module is
library-only.
"""
from __future__ import annotations
import logging
import shutil
from pathlib import Path
from neuralfetch.utils.study_info import root_study_folder
from neuralset.events import study
logger = logging.getLogger(__name__)
# Sub-directories of a study folder that hold derived/downloaded artifacts and
# are therefore safe to wipe on a ``--clean`` re-download.
_CLEANABLE_SUBDIRS = ("download", "prepare")
[docs]
def download_study(
name: str,
path: str | Path | None = None,
overwrite: bool = False,
clean: bool = False,
assume_yes: bool = False,
) -> None:
"""Download a study's raw dataset into ``path/<StudyName>/``.
Parameters
----------
name : str
Registered study class name (e.g. ``"Grootswagers2022Human"``).
path : str | Path | None
Root folder for study data. Defaults to
:func:`~neuralfetch.utils.root_study_folder` (i.e. the
``NEURALSET_STUDY_FOLDER`` environment variable).
overwrite : bool
If True, force re-download even when a success-file exists (files
already on disk are re-fetched/verified rather than trusted).
clean : bool
If True, delete the study's ``download/`` and ``prepare/`` folders
first, then re-download from scratch (implies ``overwrite=True``).
This is destructive; the caller is prompted for confirmation unless
``assume_yes`` is set.
assume_yes : bool
Skip the interactive confirmation for ``clean`` (for non-interactive
use). Ignored when ``clean`` is False.
"""
cls = study._resolve_study(name)
if cls is None:
raise ValueError(f"Unknown study: {name!r}")
root = Path(path) if path is not None else root_study_folder()
inst = cls(path=root)
if clean:
_clean_study_dirs(inst.path, assume_yes=assume_yes)
overwrite = True
inst.download(overwrite=overwrite)
def _clean_study_dirs(study_path: Path, assume_yes: bool = False) -> None:
"""Delete a study's derived-artifact sub-directories before a fresh download.
Only ``download/`` and ``prepare/`` are removed; anything else in the study
folder (manually staged data, notes, etc.) is left untouched. When
``assume_yes`` is False the user must confirm on stdin, since this
permanently deletes data that may be expensive to re-fetch.
"""
targets = [study_path / sub for sub in _CLEANABLE_SUBDIRS]
existing = [t for t in targets if t.exists()]
if not existing:
return
if not assume_yes:
print("The following directories will be permanently deleted:")
for t in existing:
print(f" {t}")
reply = input("Proceed? [y/N] ").strip().lower()
if reply not in ("y", "yes"):
raise SystemExit("Aborted: nothing was deleted.")
for t in existing:
logger.info("Removing %s", t)
shutil.rmtree(t)
[docs]
def list_downloadable_studies() -> list[str]:
"""Return all registered study names (sorted)."""
study._resolve_study("") # full scan of registered packages
return sorted(study.STUDIES.keys())