Layers and Priority
This tutorial explains how layers work and how priority determines which value you see when multiple layers define the same key.
Defining Layers
Layers are defined when you create a LayeredConfigTree. The order matters:
later layers have higher priority and override earlier ones.
from layered_config_tree import LayeredConfigTree
tree = LayeredConfigTree(
layers=["defaults", "component", "user"]
)
In this example, "user" has the highest priority, followed by
"component", then "defaults".
How Priority Works
When you read a value, the tree returns the value from the highest-priority layer that has it set:
tree.update({"timeout": 30}, layer="defaults", source="system defaults")
tree.update({"timeout": 60}, layer="component", source="web server component")
tree.update({"timeout": 10}, layer="user", source="command line")
print(tree.timeout)
10
user is the highest priority layer and set timeout to 10, so that’s what we
get (even though the component and default layers also have values).
Partial Overrides
You don’t need to override everything at every layer. Values are only overridden where explicitly set:
config = LayeredConfigTree(layers=["defaults", "overrides"])
config.update(
{"server": {"host": "localhost", "port": 8080, "debug": True}},
layer="defaults",
)
config.update(
{"server": {"host": "prod.example.com", "debug": False}},
layer="overrides",
)
# 'host' and 'debug' are overridden; 'port' comes from defaults
print(config.server.host)
print(config.server.port)
print(config.server.debug)
prod.example.com
8080
False
Reading from a Specific Layer
Normally you get the highest-priority value. However, you can request a value
from a specific layer using the :meth`~layered_config_tree.main.LayeredConfigTree.get`
with the layer argument:
print(config.server.get("host", layer="defaults"))
print(config.server.get("host", layer="overrides"))
localhost
prod.example.com
If the requested layer doesn’t have a value for that key, a MissingLayerError
is raised:
try:
config.server.get("port", layer="overrides")
except Exception as e:
print(type(e).__name__)
MissingLayerError
Note
You can only request values from a specific layer via the get()
method. Dot notation access (e.g. config.server.host) and the
get_tree() method always return
the highest-priority values.
Note
The interaction between the get()
default_value and layer arguments may sometimes be a cause of confusion.
The default_value at a requested layer will only be returned if the requested
value does not exist at all at any layer. If the requested value does exist -
just not at the requested layer - then you’ll get a MissingLayerError.
# url does not exist at any layer, so default_value is returned
print(config.get("url", default_value="http://example.com", layer="overrides"))
# port exists at base layer but not override layer, so MissingLayerError is raised
try:
print(config.get(["server", "port"], default_value=80, layer="overrides"))
except Exception as e:
print(type(e).__name__)
http://example.com
MissingLayerError
No Duplicate Values per Layer
Each key can only be set once per layer. Attempting to set the same key
at the same layer a second time raises a DuplicatedConfigurationError:
dup = LayeredConfigTree(layers=["base"])
dup.update({"x": 1}, layer="base")
try:
dup.update({"x": 2}, layer="base")
except Exception as e:
print(type(e).__name__)
DuplicatedConfigurationError
This prevents accidental overwrites within the same priority level.