diff --git a/py/desiutil/depend.py b/py/desiutil/depend.py index 9347c14..a54bd93 100644 --- a/py/desiutil/depend.py +++ b/py/desiutil/depend.py @@ -131,13 +131,11 @@ def getdep(header, name): for i in range(100): namekey = 'DEPNAM{:02d}'.format(i) verkey = 'DEPVER{:02d}'.format(i) - if namekey in header: - if header[namekey] == name: - return header[verkey] - elif i == 0: - continue # ok if DEPNAM00 is missing; continue to DEPNAME01 - else: - raise KeyError('{} version not found'.format(name)) + if namekey in header and header[namekey] == name: + return header[verkey] + + # if we exited the loop, the name wasn't found in any DEPNAMnn + raise KeyError('{} version not found'.format(name)) def hasdep(header, name): @@ -173,12 +171,11 @@ def iterdep(header): for i in range(100): namekey = 'DEPNAM{:02d}'.format(i) verkey = 'DEPVER{:02d}'.format(i) - if namekey not in header and i == 0: - continue if namekey in header: yield (header[namekey], header[verkey]) else: - return + continue # ok if some DEPNAMnn are missing + return @@ -229,6 +226,37 @@ def mergedep(srchdr, dsthdr, conflict='src'): setdep(dsthdr, name, version) +def removedep(header, name): + """ + Removed dependency ``name`` from ``header``. + + Parameters + ---------- + header : dict-like + header object with DEPNAMnn/DEPVERnn keywords + name : str + name of dependency to remove + + Notes + ----- + Modifies header in-place + + Raises + ------ + ValueError + If ``name`` is not present in any of the DEPNAMnn keys + """ + for i in range(100): + namekey = 'DEPNAM{:02d}'.format(i) + verkey = 'DEPVER{:02d}'.format(i) + if (namekey in header) and (header[namekey] == name): + del header[namekey] + del header[verkey] + break + else: + raise ValueError(f'{name} not found in header DEPNAMnn keywords') + + def add_dependencies(header, module_names=None, long_python=False, envvar_names=None): '''Adds ``DEPNAMnn``, ``DEPVERnn`` keywords to header for imported modules. @@ -289,6 +317,29 @@ def add_dependencies(header, module_names=None, long_python=False, setdep(header, envvar, 'NOT_SET') +def remove_dependencies(header): + """ + Remove all DEPNAMnn/DEPVERnn dependencies from a header + + Parameters + ---------- + header : dict-like + header with DEPNAMnn/DEPVERnn keywords to remove + + Notes + ----- + Updates header in-place + """ + # Assemble version keys first to avoid removing while iterating + keys = list() + for name, version in iterdep(header): + keys.append(name) + + # now remove all keys + for name in keys: + removedep(header, name) + + class Dependencies(object): """Dictionary-like object to track dependencies. diff --git a/py/desiutil/test/test_depend.py b/py/desiutil/test/test_depend.py index 23ae3b0..ebc034d 100644 --- a/py/desiutil/test/test_depend.py +++ b/py/desiutil/test/test_depend.py @@ -6,8 +6,8 @@ import sys import os from collections import OrderedDict -from ..depend import (setdep, getdep, hasdep, iterdep, mergedep, Dependencies, - add_dependencies) +from ..depend import (setdep, getdep, hasdep, iterdep, mergedep, removedep, + Dependencies, add_dependencies, remove_dependencies) from .. import __version__ as desiutil_version try: @@ -281,3 +281,44 @@ def test_add_dependencies(self): hdr = OrderedDict() add_dependencies(hdr) self.assertTrue(getdep(hdr, 'DESI_ROOT'), 'NOT_SET') + + def test_remove_dependencies(self): + """test removedep and remove_dependencies""" + + # add and remove a single dependency + hdr = dict(HELLO='not a DEPNAMnn/DEPVERnn keyword') + setdep(hdr, 'blat', 'foo') + self.assertTrue(hasdep(hdr, 'blat')) + self.assertTrue('HELLO' in hdr) + removedep(hdr, 'blat') + self.assertFalse(hasdep(hdr, 'blat')) + self.assertTrue('HELLO' in hdr) + + # remove a single dependency in the middle while preserving others + setdep(hdr, 'blat', 'foo') + setdep(hdr, 'biz', 'bat') + setdep(hdr, 'kum', 'quat') + removedep(hdr, 'biz') + self.assertTrue(hasdep(hdr, 'blat')) + self.assertFalse(hasdep(hdr, 'biz')) + self.assertTrue(hasdep(hdr, 'kum')) + self.assertTrue('HELLO' in hdr) + + # Add another dependency doesn't trip on the one that was removed + setdep(hdr, 'abc', 'xyz') + self.assertTrue(hasdep(hdr, 'blat')) + self.assertTrue(hasdep(hdr, 'kum')) + self.assertTrue(hasdep(hdr, 'abc')) + self.assertTrue('HELLO' in hdr) + + # remove all dependencies + remove_dependencies(hdr) + self.assertFalse(hasdep(hdr, 'blat')) + self.assertFalse(hasdep(hdr, 'biz')) + self.assertFalse(hasdep(hdr, 'kum')) + self.assertTrue('HELLO' in hdr) + for key in hdr: + self.assertFalse(key.startswith('DEP')) + + with self.assertRaises(ValueError): + removedep(hdr, 'not_there')