datalad/datalad-container

View on GitHub
datalad_container/containers_list.py

Summary

Maintainability
A
1 hr
Test Coverage
"""List known container environments of a dataset"""

__docformat__ = 'restructuredtext'

import logging
import os.path as op

import datalad.support.ansi_colors as ac
from datalad.coreapi import subdatasets
from datalad.distribution.dataset import (
    Dataset,
    EnsureDataset,
    datasetmethod,
    require_dataset,
)
from datalad.interface.base import (
    Interface,
    build_doc,
    eval_results,
)
from datalad.interface.common_opts import recursion_flag
from datalad.interface.results import get_status_dict
from datalad.interface.utils import default_result_renderer
from datalad.support.constraints import EnsureNone
from datalad.support.param import Parameter
from datalad.ui import ui

from datalad_container.utils import get_container_configuration

lgr = logging.getLogger("datalad.containers.containers_list")


@build_doc
# all commands must be derived from Interface
class ContainersList(Interface):
    # first docstring line is used a short description in the cmdline help
    # the rest is put in the verbose help and manpage
    """List containers known to a dataset
    """

    result_renderer = 'tailored'
    # parameters of the command, must be exhaustive
    _params_ = dict(
        dataset=Parameter(
            args=("-d", "--dataset"),
            doc="""specify the dataset to query. If no dataset is given, an
            attempt is made to identify the dataset based on the current
            working directory""",
            constraints=EnsureDataset() | EnsureNone()),
        contains=Parameter(
            args=('--contains',),
            metavar='PATH',
            action='append',
            doc="""when operating recursively, restrict the reported containers
            to those from subdatasets that contain the given path (i.e. the
            subdatasets that are reported by :command:`datalad subdatasets
            --contains=PATH`). Top-level containers are always reported."""),
        recursive=recursion_flag,
    )

    @staticmethod
    @datasetmethod(name='containers_list')
    @eval_results
    def __call__(dataset=None, recursive=False, contains=None):
        ds = require_dataset(dataset, check_installed=True,
                             purpose='list containers')
        refds = ds.path

        if recursive:
            for sub in ds.subdatasets(
                    contains=contains,
                    on_failure='ignore',
                    return_type='generator',
                    result_renderer='disabled'):
                subds = Dataset(sub['path'])
                if subds.is_installed():
                    for c in subds.containers_list(recursive=recursive,
                                                   return_type='generator',
                                                   on_failure='ignore',
                                                   result_filter=None,
                                                   result_renderer=None,
                                                   result_xfm=None):
                        c['name'] = sub['gitmodule_name'] + '/' + c['name']
                        c['refds'] = refds
                        yield c

        # all info is in the dataset config!
        containers = get_container_configuration(ds)

        for k, v in containers.items():
            if 'image' not in v:
                # there is no container location configured
                continue
            res = get_status_dict(
                status='ok',
                action='containers',
                name=k,
                type='file',
                path=op.join(ds.path, v.pop('image')),
                refds=refds,
                parentds=ds.path,
                # TODO
                #state='absent' if ... else 'present'
                **v)
            yield res

    @staticmethod
    def custom_result_renderer(res, **kwargs):
        if res["action"] != "containers":
            default_result_renderer(res)
        else:
            ui.message(
                "{name} -> {path}"
                .format(name=ac.color_word(res["name"], ac.MAGENTA),
                        path=op.relpath(res["path"], res["refds"])))