Skip to content

Commit

Permalink
Add option to load traitlets values from environement. (#856)
Browse files Browse the repository at this point in the history
  • Loading branch information
Carreau authored Nov 27, 2023
1 parent 5064f4e commit 4d75046
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 2 deletions.
49 changes: 47 additions & 2 deletions examples/myapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@
from traitlets.config.configurable import Configurable


class SubConfigurable(Configurable):
subvalue = Int(0, help="The integer subvalue.").tag(config=True)

def describe(self):
print("I am SubConfigurable with:")
print(" subvalue =", self.subvalue)


class Foo(Configurable):
"""A class that has configurable, typed attributes."""

Expand All @@ -44,9 +52,39 @@ class Foo(Configurable):
name = Unicode("Brian", help="First name.").tag(config=True, shortname="B")
mode = Enum(values=["on", "off", "other"], default_value="on").tag(config=True)

def __init__(self, **kwargs):
super().__init__(**kwargs)
# using parent=self allows configuration in the form c.Foo.SubConfigurable.subvalue=1
# while c.SubConfigurable.subvalue=1 will still work, this allow to
# target specific instances of SubConfigurables
self.subconf = SubConfigurable(parent=self)

def describe(self):
print("I am Foo with:")
print(" i =", self.i)
print(" j =", self.j)
print(" name =", self.name)
print(" mode =", self.mode)
self.subconf.describe()


class Bar(Configurable):
enabled = Bool(True, help="Enable bar.").tag(config=True)
mylist = List([1, 2, 3], help="Just a list.").tag(config=True)

def describe(self):
print("I am Bar with:")
print(" enabled = ", self.enabled)
print(" mylist = ", self.mylist)
self.subconf.describe()

def __init__(self, **kwargs):
super().__init__(**kwargs)
# here we do not use parent=self, so configuration in the form
# c.Bar.SubConfigurable.subvalue=1 will not work. Only
# c.SubConfigurable.subvalue=1 will work and affect all instances of
# SubConfigurable
self.subconf = SubConfigurable(config=self.config)


class MyApp(Application):
Expand Down Expand Up @@ -76,8 +114,8 @@ class MyApp(Application):
)

def init_foo(self):
# Pass config to other classes for them to inherit the config.
self.foo = Foo(config=self.config)
# You can pass self as parent to automatically propagate config.
self.foo = Foo(parent=self)

def init_bar(self):
# Pass config to other classes for them to inherit the config.
Expand All @@ -87,19 +125,26 @@ def initialize(self, argv=None):
self.parse_command_line(argv)
if self.config_file:
self.load_config_file(self.config_file)
self.load_config_environ()
self.init_foo()
self.init_bar()

def start(self):
print("app.config:")
print(self.config)
self.describe()
print("try running with --help-all to see all available flags")
assert self.log is not None
self.log.debug("Debug Message")
self.log.info("Info Message")
self.log.warning("Warning Message")
self.log.critical("Critical Message")

def describe(self):
print("I am MyApp with", self.name, self.running, "and 2 sub configurables Foo and bar:")
self.foo.describe()
self.bar.describe()


def main():
app = MyApp()
Expand Down
24 changes: 24 additions & 0 deletions traitlets/config/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
ArgumentError,
Config,
ConfigFileNotFound,
DeferredConfigString,
JSONFileConfigLoader,
KVArgParseConfigLoader,
PyFileConfigLoader,
Expand Down Expand Up @@ -970,6 +971,29 @@ def load_config_file(
new_config.merge(self.cli_config)
self.update_config(new_config)

@catch_config_error
def load_config_environ(self) -> None:
"""Load config files by environment."""

PREFIX = self.name.upper()
new_config = Config()

self.log.debug('Looping through config variables with prefix "%s"', PREFIX)

for k, v in os.environ.items():
if k.startswith(PREFIX):
self.log.debug('Seeing environ "%s"="%s"', k, v)
# use __ instead of . as separator in env variable.
# Warning, case sensitive !
_, *path, key = k.split("__")
section = new_config
for p in path:
section = section[p]
setattr(section, key, DeferredConfigString(v))

new_config.merge(self.cli_config)
self.update_config(new_config)

def _classes_with_config_traits(
self, classes: ClassesType | None = None
) -> t.Generator[type[Configurable], None, None]:
Expand Down

0 comments on commit 4d75046

Please sign in to comment.