def __call__( path=None, dataset=None, recursive=False, check=True, if_dirty='save-before'): refds = require_dataset(dataset, check_installed=True, purpose='uninstall') res_kwargs = dict(action='uninstall', logger=lgr, refds=refds.path) if not path: # if no path is given, ie. refds is supposed to be uninstalled # check if refds is a subdataset itself, if not die # we only need to test that for the refds, everything else # will be guaranteed to be a subdataset parentds = refds.get_superdataset( datalad_only=False, topmost=False, # unless it is properly registered we have no way of # reinstalling it registered_only=True) if parentds is None: yield dict( res_kwargs, path=refds.path, type='dataset', status='error', message="will not uninstall top-level dataset " "(consider `remove` command)", ) return saw_subds = False for ds in itertools.chain(Subdatasets.__call__( # it is critical to pass the dataset arg as-is # to not invalidate the path argument semantics # in subdatasets() dataset=dataset, path=path, fulfilled=True, # makes no sense to ignore subdatasets further down recursive=True, # important to start at the bottom for proper deinit bottomup=True, # doesn't make sense for uninstall #recursion_limit=recursion_limit, return_type='generator', result_renderer='disabled', result_xfm='datasets') if path or recursive else [], [refds] if not path else []): if ds != refds: saw_subds = True # TODO generator # this should yield what it did handle_dirty_dataset(ds, mode=if_dirty) # we confirmed the super dataset presence above for r in _uninstall_dataset(ds, check=check, has_super=True, **res_kwargs): yield r # there is nothing to save at the end if path and not saw_subds: lgr.warning( 'path constraints did not match an installed subdataset: %s', path)
def __call__( path=None, source=None, dataset=None, recursive=False, recursion_limit=None, get_data=True, description=None, reckless=None, jobs='auto', ): refds_path = Interface.get_refds_path(dataset) if not (dataset or path): raise InsufficientArgumentsError( "Neither dataset nor target path(s) provided") if dataset and not path: # act on the whole dataset if nothing else was specified path = refds_path # we have to have a single dataset to operate on refds = require_dataset(dataset, check_installed=True, purpose='get content') content_by_ds = {} # use subdatasets() to discover any relevant content that is not # already present in the root dataset (refds) for sdsres in Subdatasets.__call__( contains=path, # maintain path argument semantics and pass in dataset arg # as is dataset=dataset, # always come from the top to get sensible generator behavior bottomup=False, # when paths are given, they will constrain the recursion # automatically, and we need to enable recursion so we can # location path in subdatasets several levels down recursive=True if path else recursive, recursion_limit=None if path else recursion_limit, return_type='generator', on_failure='ignore'): if sdsres.get('type', None) != 'dataset': # if it is not about a 'dataset' it is likely content in # the root dataset if sdsres.get('status', None) == 'impossible' and \ sdsres.get('message', None) == \ 'path not contained in any matching subdataset': target_path = Path(sdsres['path']) if refds.pathobj != target_path and \ refds.pathobj not in target_path.parents: yield dict( action='get', path=str(target_path), status='error', message=('path not associated with dataset %s', refds), ) continue # check if we need to obtain anything underneath this path # the subdataset() call above will only look _until_ it # hits the targetpath for res in _install_targetpath( refds, Path(sdsres['path']), recursive, recursion_limit, reckless, refds_path, description, jobs=jobs, ): # fish out the datasets that 'contains' a targetpath # and store them for later if res.get('status', None) in ('ok', 'notneeded') and \ 'contains' in res: dsrec = content_by_ds.get(res['path'], set()) dsrec.update(res['contains']) content_by_ds[res['path']] = dsrec if res.get('status', None) != 'notneeded': # all those messages on not having installed anything # are a bit pointless # "notneeded" for annex get comes below yield res else: # dunno what this is, send upstairs yield sdsres # must continue for both conditional branches above # the rest is about stuff in real subdatasets continue # instance of the closest existing dataset for this result ds = Dataset(sdsres['parentds'] if sdsres.get('state', None) == 'absent' else sdsres['path']) assert 'contains' in sdsres # explore the unknown for target_path in sdsres.get('contains', []): # essentially the same as done above for paths in the root # dataset, but here we are starting from the closest # discovered subdataset for res in _install_targetpath( ds, Path(target_path), recursive, recursion_limit, reckless, refds_path, description, jobs=jobs, ): known_ds = res['path'] in content_by_ds if res.get('status', None) in ('ok', 'notneeded') and \ 'contains' in res: dsrec = content_by_ds.get(res['path'], set()) dsrec.update(res['contains']) content_by_ds[res['path']] = dsrec # prevent double-reporting of datasets that have been # installed by explorative installation to get to target # paths, prior in this loop if res.get('status', None) != 'notneeded' or not known_ds: yield res if not get_data: # done already return # and now annex-get, this could all be done in parallel now for ds, content in content_by_ds.items(): for res in _get_targetpaths(Dataset(ds), content, refds.path, source, jobs): if res['path'] not in content_by_ds: # we had reports on datasets and subdatasets already # before the annex stage yield res