diff --git a/traitlets/config/loader.py b/traitlets/config/loader.py index c389af17..f385226d 100644 --- a/traitlets/config/loader.py +++ b/traitlets/config/loader.py @@ -61,10 +61,12 @@ def print_help(self, file=None): # Config class for holding config information #----------------------------------------------------------------------------- + def execfile(fname, glob): with open(fname, 'rb') as f: exec(compile(f.read(), fname, 'exec'), glob, glob) + class LazyConfigValue(HasTraits): """Proxy object for exposing methods on configurable containers @@ -82,6 +84,15 @@ class LazyConfigValue(HasTraits): _prepend = List() _inserts = List() + def __bool__(self): + # be falsy if we're empty + return bool( + self._extend + or self._prepend + or self._inserts + or self._update + ) + def append(self, obj): self._extend.append(obj) @@ -92,7 +103,6 @@ def prepend(self, other): """like list.extend, but for the front""" self._prepend[:0] = other - def merge_into(self, other): """ Merge with another earlier LazyConfig Value or an earlier container. @@ -126,8 +136,6 @@ def merge_into(self, other): # other is a container, reify now. return self.get_value(other) - - def insert(self, index, other): if not isinstance(index, int): raise TypeError("An integer is required") @@ -272,7 +280,15 @@ def __contains__(self, key): return False return remainder in self[first] - return super(Config, self).__contains__(key) + if super(Config, self).__contains__(key): + item = self[key] + if isinstance(item, LazyConfigValue) and not item: + # don't consider empty lazy config present + # since it doesn't contain anything + return False + return True + return False + # .has_key is deprecated for dictionaries. has_key = __contains__ @@ -345,6 +361,9 @@ def __delattr__(self, key): except KeyError as e: raise AttributeError(e) + def __repr__(self): + return 'Config('+super().__repr__()+')' + #----------------------------------------------------------------------------- # Config loading classes diff --git a/traitlets/config/tests/test_loader.py b/traitlets/config/tests/test_loader.py index 4263fb66..0b6db8a2 100644 --- a/traitlets/config/tests/test_loader.py +++ b/traitlets/config/tests/test_loader.py @@ -485,7 +485,34 @@ def test_getattr_not_section(self): self.assertNotIn('foo', cfg) foo = cfg.foo assert isinstance(foo, LazyConfigValue) - self.assertIn('foo', cfg) + # empty lazy value indicates no config is actually there + assert 'foo' not in cfg + foo.append('x') + assert 'foo' in cfg + + def test_lazy_truthiness(self): + cfg = Config() + lazy = cfg.empty_trait + assert not lazy + assert not cfg.empty_trait + + cfg.append_trait.append('x') + assert cfg.append_trait + + cfg.prepend_trait.prepend('x') + assert cfg.prepend_trait + + cfg.extend_trait.extend(['x']) + assert cfg.extend_trait + + cfg.Class.insert_trait.insert(0, 'x') + assert cfg.Class.insert_trait + + cfg.Class.update_trait.update({'key': 'value'}) + assert cfg.Class.update_trait + + cfg.Class.add_trait.add('item') + assert cfg.Class.add_trait def test_getattr_private_missing(self): cfg = Config() @@ -508,13 +535,15 @@ def test_lazy_config_repr(self): assert repr([0,1]) in repr2 assert 'value=' in repr2 - def test_getitem_not_section(self): cfg = Config() self.assertNotIn('foo', cfg) foo = cfg['foo'] assert isinstance(foo, LazyConfigValue) - self.assertIn('foo', cfg) + # empty lazy value indicates no config is actually there + assert 'foo' not in cfg + foo.append('x') + assert 'foo' in cfg def test_merge_no_copies(self): c = Config() @@ -655,3 +684,18 @@ def test_merge_multi_lazy_update_III(self): c.merge(c2) self.assertEqual(c.Foo.trait._update, {"a": 1, "z": 26, "b": 1}) + + def test_empty_lazy_not_in(self): + c = Config() + lazy1 = c.Foo.trait + # 'in' check looks for *actual* config + # so creation of a lazy config handle + # doesn't mean we have config there + assert 'trait' not in c.Foo + # make sure that subsequent lazy access + # doesn't create a new LazyConfigValue, though + c.Foo.trait.append("x") + assert 'trait' in c.Foo + assert c.Foo.trait + assert lazy1 is c.Foo.trait + assert lazy1