MergedOptions¶
This is the main class and the entry point for the programmer.
It provides a mechanism to treat multiple dictionaries as if they were one dictionary.
Also provided is the ability to reference parts and still maintain the idea of it being one dictionary.
With the ability to delete from the dictionary and the ability to convert values on access.
Instantition¶
It is recommended you use one of the class methods on MergedOptions to create a MergedOptions object.
-
classmethod
MergedOptions.
using
(*options, **kwargs)¶ Convenience for calling update multiple times
m = MergedOptions.using({"a": 1}, {"b": 2})
is equivalent to:
m = MergedOptions() m.update({"a": 1}) m.update({"b": 2})
Any kwargs given to
using
is passed intoupdate
for each provided dictionary.
-
MergedOptions.
Attributes
(**kwargs)¶ This is way of extracting attributes from an object and creating a MergedOptions from that:
obj = type("obj", (object, ), {"one": "two", "two": "three", "four": "five"}) result = MergedOptions.Attributes(obj, ("one", "four"), lift="global") assertEqual(as_dict(), {"global": {"one": "two", "four": "five"}})
-
MergedOptions.
KeyValuePairs
(**kwargs)¶ This allows us to create a MergedOptions from (key, value) pairs.
result = MergedOptions.KeyValuePairs([(["one"], "two"), (["three", "four"], "five")]) assertEqual(result.as_dict(), {"one": "two", "three": {"four": "five"}})
Instance Methods¶
-
class
option_merge.
MergedOptions
(prefix=None, storage=None, dont_prefix=None, converters=None, ignore_converters=False)¶ Wrapper around multiple dictionaries to behave as one.
Usage:
options = MergedOptions.using(options1, options2, source="SomePlace")
Is equivalent to:
options = MergedOptions() options.update(options1, source="SomePlace") options.update(options2, source="SomePlace")
The later an option is added, the more influence it has. i.e. when a key is accessed, later options are looked at first.
When you delete a key, it removes it from the first dictionary it can find. This means a key can change value when deleted rather than disappearing altogether
It will also merge deeply.
So:
options1 = {'a':{'b':1, 'c':3}, 'b':5} options2 = {'a':{'b':4'}, 'd':7} merged = MergedOptions.using(options1, options2) merged['a'] == MergedOptions(prefix='a', <same_options>) merged['a']['b'] == 4 merged['a']['c'] == 3 merged['d'] == 7
You can also change deeply nested keys:
# You can get keys with "a.b" but setting them must separate the parts of the structure merged[["a", "b"]] = 5 merged["a"].as_dict() == {"b": 5, "c": 3}
Note
MergedOptions uses a cache system to avoid having to repeatedly iterate through the underlying data structures.
A side effect of this caching is that changes in the underlying structures won’t cause a cache invalidation in the MergedOptions object.
If you wish for changes to be made, make them on the MergedOptions object. (Note that changing a merged options object is an additive operation and will not change the underlying data)
Note
When instantiating a MergedOptions directly, it’s recommended the only option you specify is
dont_prefix
which is a list of types that you want to be treated as concrete values instead of being converted into a MergedOptions dictionary.This is necessary for subtypes of dictionaries or anything that returns True from
is_dict
.-
__contains__
(path)¶ Ask storage if it has a path
m = MergedOptions.using({"a": 1}) assert "a" in m assert "b" not in m
-
__delitem__
(path)¶ Delete a key from the storage
m = MergedOptions.using({"a": 1}, {"a": 2}) assert m['a'] == 2 del m['a'] assert m['a'] == 1
-
__eq__
(other)¶ Equal to another merged options if has same storage and prefix
-
__iter__
()¶ Iterate over the keys
-
__len__
()¶ Get number of keys we have
-
__setitem__
(path, value)¶ Set a key in the storage
This takes into account the prefix on this option as well as the provided path.
m = MergedOptions.using({"a": 1}) assertEqual(m.as_dict(), {"a": 1}) a = m["a"] a['b'] = 2 assertEqual(m.as_dict(), {"a": 1, "b": 2})
-
add_converter
(converter)¶ Add a converter to our collection
-
as_dict
(key='', ignore_converters=True, seen=None, ignore=None)¶ Collapse the storage at this prefix into a single dictionary
-
get
(path, default=None, ignore_converters=False)¶ Get some path or return default value
m = MergedOptions.using({"a": 1}) assert m.get("a") == 1 assert m.get("b") == None assert m.get("b", 2) == 2
You may also specify
ignore_converters
and it won’t take the the converters into account.m = MergedOptions.using({"a": 1}) m.add_converter(Converter(convert=add_one, convert_path=["a"])) m.converters.activate() assert m["a"] == 2 assert m.get("a", ignore_converters=True) == 1
-
install_converters
(converters, make_converter)¶ For each specified converter, make a converter function and install a converter for that name.
def make_converter(name, transformer): def convert(path, val): return transformer(val) return convert m = MergedOptions.using({"a": 1, "b": 2}) m.install_converters({"a": lambda v: v+1, "b": lambda v: v*2}, make_converter) m.converters.activate() assert m['a'] == 2 assert m['b'] == 4
-
items
(ignore_converters=False)¶ Iterate over [(key, value), …] pairs
-
keys
(ignore_converters=False)¶ Return a de-duplicated list of the keys we know about
-
source_for
(path, chain=None)¶ Proxy self.storage.source_for
Source is specifying in calls to
__init__
andupdate
and this will find the entries for the specified path an return the first source it finds.
-
update
(options, source=None, **kwargs)¶ Add new options to the storage under this prefix.
The later options are added, the more influence they have.
-
wrapped
()¶ Return a MergedOptions with this inside
Equivalent to:
m = MergedOptions.using("a") wrapped = MergedOptions.using(m, converters=m.converters, dont_prefix=m.dont_prefix)
-