Skip to content

The mesoscopy core module

Command loading

The root cli group lazy-loads commands through LazyGroup. Subcommands are listed in LAZY_SUBCOMMANDS, a table of name -> ("module:attribute", summary), and a module is imported only when its subcommand runs.

The summary is what mesoscopy --help prints next to each command. It lives in the table rather than in the command's docstring so that rendering help does not import every subcommand. The subcommand's own --help still comes from its docstring.

To add a subcommand, define it with @click.command() or @click.group() in its module and add an entry to LAZY_SUBCOMMANDS. Do not register it with cli.add_command(). mkdocs-click only walks lazy subcommands when none are registered eagerly, so an eager command would drop the others from the CLI reference.

Import layers

The same idea applies to imports. Each stage's command module (mesoscopy/<stage>/__init__.py) and mesoscopy.io import only cheap modules at the top, such as click, numpy, h5py, mesoscopy.io and mesoscopy.timer. Expensive libraries (pynwb, dask, zarr, pandas, scipy, scikit-learn, pims, napari, plotly, matplotlib) and the numerics modules that use them are imported inside the function that needs them.

The numerics modules themselves (preprocess.compute, preprocess.qa, process.region, process.regression, register.transform, register.landmarks_gui, convert.video, and so on) keep conventional top-level imports. banned-module-level-imports in pyproject.toml lists the expensive libraries, and per-file-ignores excludes the numerics modules from that rule. Names needed only in annotations go in an if typing.TYPE_CHECKING: block at the top of the module.

Main entry point to the mesoscopy CLI

LazyGroup

Bases: Group

Click group that imports a subcommand's module only when that subcommand is looked up.

Source code in src/mesoscopy/__init__.py
class LazyGroup(click.Group):
    """Click group that imports a subcommand's module only when that subcommand is looked up."""

    def __init__(
        self, *args: typing.Any, lazy_subcommands: dict[str, tuple[str, str]] | None = None, **kwargs: typing.Any
    ) -> None:
        """Initialise the group.

        Args:
            *args: Passed to `click.Group`.
            lazy_subcommands: Mapping of subcommand name to `("module:attribute", summary)` of its click command.
            **kwargs: Passed to `click.Group`.
        """
        super().__init__(*args, **kwargs)
        self.lazy_subcommands = lazy_subcommands or {}

    def list_commands(self, ctx: click.Context) -> list[str]:
        """List registered and lazy subcommand names without importing the lazy ones.

        Args:
            ctx: Click context.

        Returns:
            Sorted subcommand names.
        """
        return sorted([*super().list_commands(ctx), *self.lazy_subcommands])

    def get_command(self, ctx: click.Context, cmd_name: str) -> click.Command | None:
        """Return the subcommand, importing its module if it is lazy.

        Args:
            ctx: Click context.
            cmd_name: Subcommand name.

        Returns:
            The click command, or `None` if no such subcommand exists.
        """
        if cmd_name in self.lazy_subcommands:
            return self._load_lazy_command(cmd_name)
        return super().get_command(ctx, cmd_name)

    def format_commands(self, ctx: click.Context, formatter: click.HelpFormatter) -> None:
        """Write the subcommand listing, using stored summaries for lazy subcommands instead of importing them.

        Args:
            ctx: Click context.
            formatter: Help formatter to write to.
        """
        rows = []
        for name in self.list_commands(ctx):
            if name in self.lazy_subcommands:
                rows.append((name, self.lazy_subcommands[name][1]))
                continue
            cmd = super().get_command(ctx, name)
            if cmd is not None and not cmd.hidden:
                rows.append((name, cmd.get_short_help_str(limit=formatter.width - 6 - len(name))))
        if rows:
            with formatter.section("Commands"):
                formatter.write_dl(rows)

    def _load_lazy_command(self, cmd_name: str) -> click.Command:
        module_name, attr_name = self.lazy_subcommands[cmd_name][0].split(":")
        cmd = getattr(importlib.import_module(module_name), attr_name)
        if not isinstance(cmd, click.Command):
            msg = f"Lazy subcommand {cmd_name!r} resolved to {cmd!r}, not a click command."
            raise TypeError(msg)
        return cmd

__init__(*args, lazy_subcommands=None, **kwargs)

Initialise the group.

Parameters:

  • *args (Any, default: () ) –

    Passed to click.Group.

  • lazy_subcommands (dict[str, tuple[str, str]] | None, default: None ) –

    Mapping of subcommand name to ("module:attribute", summary) of its click command.

  • **kwargs (Any, default: {} ) –

    Passed to click.Group.

Source code in src/mesoscopy/__init__.py
def __init__(
    self, *args: typing.Any, lazy_subcommands: dict[str, tuple[str, str]] | None = None, **kwargs: typing.Any
) -> None:
    """Initialise the group.

    Args:
        *args: Passed to `click.Group`.
        lazy_subcommands: Mapping of subcommand name to `("module:attribute", summary)` of its click command.
        **kwargs: Passed to `click.Group`.
    """
    super().__init__(*args, **kwargs)
    self.lazy_subcommands = lazy_subcommands or {}

list_commands(ctx)

List registered and lazy subcommand names without importing the lazy ones.

Parameters:

  • ctx (Context) –

    Click context.

Returns:

  • list[str] –

    Sorted subcommand names.

Source code in src/mesoscopy/__init__.py
def list_commands(self, ctx: click.Context) -> list[str]:
    """List registered and lazy subcommand names without importing the lazy ones.

    Args:
        ctx: Click context.

    Returns:
        Sorted subcommand names.
    """
    return sorted([*super().list_commands(ctx), *self.lazy_subcommands])

get_command(ctx, cmd_name)

Return the subcommand, importing its module if it is lazy.

Parameters:

  • ctx (Context) –

    Click context.

  • cmd_name (str) –

    Subcommand name.

Returns:

  • Command | None –

    The click command, or None if no such subcommand exists.

Source code in src/mesoscopy/__init__.py
def get_command(self, ctx: click.Context, cmd_name: str) -> click.Command | None:
    """Return the subcommand, importing its module if it is lazy.

    Args:
        ctx: Click context.
        cmd_name: Subcommand name.

    Returns:
        The click command, or `None` if no such subcommand exists.
    """
    if cmd_name in self.lazy_subcommands:
        return self._load_lazy_command(cmd_name)
    return super().get_command(ctx, cmd_name)

format_commands(ctx, formatter)

Write the subcommand listing, using stored summaries for lazy subcommands instead of importing them.

Parameters:

  • ctx (Context) –

    Click context.

  • formatter (HelpFormatter) –

    Help formatter to write to.

Source code in src/mesoscopy/__init__.py
def format_commands(self, ctx: click.Context, formatter: click.HelpFormatter) -> None:
    """Write the subcommand listing, using stored summaries for lazy subcommands instead of importing them.

    Args:
        ctx: Click context.
        formatter: Help formatter to write to.
    """
    rows = []
    for name in self.list_commands(ctx):
        if name in self.lazy_subcommands:
            rows.append((name, self.lazy_subcommands[name][1]))
            continue
        cmd = super().get_command(ctx, name)
        if cmd is not None and not cmd.hidden:
            rows.append((name, cmd.get_short_help_str(limit=formatter.width - 6 - len(name))))
    if rows:
        with formatter.section("Commands"):
            formatter.write_dl(rows)

cli()

Widefield calcium imaging analysis pipeline.

Source code in src/mesoscopy/__init__.py
@click.group(cls=LazyGroup, lazy_subcommands=LAZY_SUBCOMMANDS)
@click.version_option(__version__)
def cli():
    """Widefield calcium imaging analysis pipeline."""

sample(path, out_dir, index, crop=0, vmin=0, vmax=255, key='frames')

Sample an image frame from an HDF5 file and export it as a PNG.

Source code in src/mesoscopy/__init__.py
@click.command()
@click.argument("path", type=click.Path(exists=True))
@click.argument("out_dir", type=click.Path(dir_okay=True))
@click.option("--index", default=0, show_default=True, help="Index of frame to sample.")
@click.option("--crop", default=0)
@click.option("--vmin", type=float, default=0)
@click.option("--vmax", type=float, default=255)
@click.option("--key", type=str, default=None, help="Activity column")
def sample(path, out_dir, index, crop=0, vmin=0, vmax=255, key="frames"):
    """Sample an image frame from an HDF5 file and export it as a PNG."""
    import h5py
    from matplotlib import pyplot as plt

    click.echo(f"Sampling {path} at index {index}.")

    f = h5py.File(path)
    d = f[f"/{key}"]

    os.makedirs(out_dir, exist_ok=True)
    outpath = out_dir + os.sep + path.split("/")[-1].replace(".h5", f"_sample-{index}.png")

    if crop > 0:
        plt.imsave(outpath, d[index, crop:-crop, crop:-crop], vmin=vmin, vmax=vmax)
    else:
        plt.imsave(outpath, d[index], vmin=vmin, vmax=vmax, cmap="jet")
    click.echo(f"Saved sample at {outpath}")

start_time(path)

Print a recording's start time.

This is the first /timestamps entry of an HDF5 file, or the session_start_time of an NWB file, as stored.

Source code in src/mesoscopy/__init__.py
@click.command("start-time")
@click.argument("path", type=click.Path(exists=True, dir_okay=False))
def start_time(path: str) -> None:
    """Print a recording's start time.

    This is the first /timestamps entry of an HDF5 file, or the session_start_time of an NWB file, as stored.
    """  # noqa: DOC501
    from mesoscopy import io

    try:
        click.echo(io.read_start_time(path))
    except ValueError as err:
        raise click.ClickException(str(err)) from err