Rose Configuration API

CLI

The rose config command provides a command line tool for reading, processing and dumping files written in the Rose Configuration Format:

$ echo -e "
> [foo]
> bar=baz
> " > rose.conf

$ rose config foo --file rose.conf
bar=baz

For more information see the rose config command line reference.

Python

Rose provides a Python API for loading, processing, editing and dumping Rose configurations via the metomi.rose.config module located within the Rose Python library.

Simple INI-style configuration data model, loader and dumper.

Synopsis:
>>> # Create a config file.
>>> with open('config.conf', 'w+') as config_file:
...     _ = config_file.write('''
... [foo]
... bar=Bar
... !baz=Baz
...      ''')
>>> # Load in a config file.
>>> try:
...     config_node = load('config.conf')
... except ConfigSyntaxError:
...     # Handle exception.
...     pass
>>> # Retrieve config settings.
>>> config_node.get(['foo', 'bar'])
{'value': 'Bar', 'state': '', 'comments': []}
>>> # Set new config settings.
>>> _ = config_node.set(['foo', 'new'], 'New')
>>> # Overwrite existing config settings.
>>> _ = config_node.set(['foo', 'baz'], state=ConfigNode.STATE_NORMAL,
...                     value='NewBaz')
>>> # Write out config to a file.
>>> dump(config_node, sys.stdout)
[foo]
bar=Bar
baz=NewBaz
new=New
Classes:

metomi.rose.config.ConfigNode([value, ...])

Represent a node in a configuration file.

metomi.rose.config.ConfigNodeDiff()

Represent differences between two ConfigNode instances.

metomi.rose.config.ConfigDumper([char_assign])

Dumper of a ConfigNode object in Rose INI format.

metomi.rose.config.ConfigLoader([...])

Loader of an INI format configuration into a ConfigNode object.

Functions:

metomi.rose.config.load(source[, root])

Shorthand for ConfigLoader.load().

metomi.rose.config.dump(root[, target, ...])

Shorthand for ConfigDumper.dump().

Limitations:
  • The loader does not handle trailing comments.

What about the standard library ConfigParser? Well, it is problematic:
  • The comment character and style is hard-coded.

  • The assignment character is hard-coded.

  • A duplicated section header causes an exception to be raised.

  • Option keys are transformed to lower case by default.

  • It is far too complicated and confusing.

exception metomi.rose.config.ConfigDecodeError(path, unicode_decode_err)[source]

Exception that should be raised when loading a configuration file that is not encoded in a UTF-8 compatible charset.

Parameters:
  • path (str) – Path to the config file

  • unicode_decode_err (UnicodeDecodeError) – The original exception raised when doing bytes.decode()

class metomi.rose.config.ConfigDumper(char_assign='=')[source]

Dumper of a ConfigNode object in Rose INI format.

Examples

>>> config_node = ConfigNode()
>>> _ = config_node.set(keys=['foo', 'bar'], value='Bar')
>>> _ = config_node.set(keys=['foo', 'baz'], value='Baz',
...                     comments=['Currently ignored!'],
...                     state=ConfigNode.STATE_USER_IGNORED)
>>> dumper = ConfigDumper()
>>> dumper(config_node, sys.stdout)
[foo]
bar=Bar
#Currently ignored!
!baz=Baz
dump(root, target=<_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'>, sort_sections=None, sort_option_items=None, env_escape_ok=False, concat_mode=False)[source]

Format a ConfigNode object and write result to target.

Parameters:
  • root (ConfigNode) – The root config node.

  • target (object) – An open file handle or a string containing a file path. If not specified, the result is written to sys.stdout.

  • sort_sections (Callable) – An optional argument that should be a function for sorting a list of section keys.

  • sort_option_items (Callable) – An optional argument that should be a function for sorting a list of option (key, value) tuples in string values.

  • env_escape_ok (bool) – An optional argument to indicate that $NAME and ${NAME} syntax in values should be escaped.

  • concat_mode (bool) – Switch on concatenation mode. If True, add [] before root level options.

exception metomi.rose.config.ConfigError[source]

Base class for config errors.

class metomi.rose.config.ConfigLoader(char_assign='=', char_comment='#', allow_sections=True)[source]

Loader of an INI format configuration into a ConfigNode object.

Example

>>> with open('config.conf', 'w+') as config_file:
...     _ = config_file.write('''
... [foo]
... !bar=Bar
... baz=Baz
...     ''')
>>> loader = ConfigLoader()
>>> try:
...     config_node = loader.load('config.conf')
... except ConfigSyntaxError:
...     raise  # Handle exception.
>>> config_node.get(keys=['foo', 'bar'])
{'value': 'Bar', 'state': '!', 'comments': []}
static can_miss_opt_conf_key(key)[source]

Return KEY if key is a string like “(KEY)”, None otherwise.

load(source, node=None, default_comments=None)[source]

Read a source configuration file.

Parameters:
  • source (str) – An open file handle, a string for a file path or a list of [SECTION]KEY=VALUE items.

  • node (ConfigNode) – A ConfigNode object if specified, otherwise created.

Returns:

A new ConfigNode object.

Return type:

ConfigNode

Examples

>>> # Create example config file.
>>> with open('config.conf', 'w+') as config_file:
...     _ = config_file.write('''
... [foo]
... # Some comment
... !bar=Bar
...     ''')
>>> # Load config file.
>>> loader = ConfigLoader()
>>> try:
...     config_node = loader.load('config.conf')
... except ConfigSyntaxError:
...     raise  # Handle exception.
>>> config_node.get(keys=['foo', 'bar'])
{'value': 'Bar', 'state': '!', 'comments': [' Some comment']}
load_with_opts(source, node=None, more_keys=None, used_keys=None, return_config_map=False, mark_opt_confs=False, defines=None)[source]

Read a source configuration file with optional configurations.

Parameters:
  • source (str) – A file path.

  • node (ConfigNode) – A ConfigNode object if specified, otherwise one is created.

  • more_keys (list) – A list of additional optional configuration names. If source is “rose-${TYPE}.conf”, the file of each name should be “opt/rose-${TYPE}-${NAME}.conf”.

  • used_keys (list) – If defined, it should be a list for this method to append to. The key of each successfully loaded optional configuration will be appended to the list (unless the key is already in the list). Missing optional configurations that are specified in more_keys will not raise an error. If not defined, any missing optional configuration will trigger an OSError.

  • mark_opt_configs (bool) – if True, add comments above any settings which have been loaded from an optional config.

  • return_config_map (bool) – If True, construct and return a dict (config_map) containing config names vs their uncombined nodes. Optional configurations use their opt keys as keys, and the main configuration uses ‘None’.

  • defines (list) – A list of [SECTION]KEY=VALUE overrides.

Returns:

node or (node, config_map):
  • node - The loaded configuration as a ConfigNode.

  • config_map - A dictionary containing opt_conf_key: ConfigNode pairs. Only returned if return_config_map is True.

Return type:

tuple

Examples

>>> # Write config file.
>>> with open('config.conf', 'w+') as config_file:
...     _ = config_file.write('''
... [foo]
... bar=Bar
...     ''')
>>> # Write optional config file (foo).
>>> os.mkdir('opt')
>>> with open('opt/config-foo.conf', 'w+') as opt_config_file:
...     _ = opt_config_file.write('''
... [foo]
... bar=Baz
...     ''')
>>> loader = ConfigLoader()
>>> config_node, config_map = loader.load_with_opts(
...     'config.conf', more_keys=['foo'], return_config_map=True)
>>> config_node.get_value(keys=['foo', 'bar'])
'Baz'
>>> original_config_node = config_map[None]
>>> original_config_node.get_value(keys=['foo', 'bar'])
'Bar'
>>> optional_config_node = config_map['foo']
>>> optional_config_node.get_value(keys=['foo', 'bar'])
'Baz'
class metomi.rose.config.ConfigNode(value=None, state='', comments=None)[source]

Represent a node in a configuration file.

Nodes are stored hierarchically, for instance the following config

[foo] bar = Bar

When loaded by ConfigNode.load(file) would result in three levels of ConfigNodes, the first representing “root” (i.e. the top level of the config), one representing the config section “foo” and one representing the setting “bar”.

Examples

>>> # Create a new ConfigNode.
>>> config_node = ConfigNode()
>>> # Add sub-nodes.
>>> _ = config_node.set(keys=['foo', 'bar'], value='Bar')
>>> _ = config_node.set(keys=['foo', 'baz'], value='Baz')
>>> config_node 
{'value': {'foo': {'value': {'bar': {'value': 'Bar',
                                     'state': '', 'comments': []},
                             'baz': {'value': 'Baz',
                                     'state': '', 'comments': []}},
                   'state': '', 'comments': []}},
 'state': '', 'comments': []}
>>> # Set the state of a node.
>>> _ = config_node.set(keys=['foo', 'bar'],
...                     state=ConfigNode.STATE_USER_IGNORED)
>>> # Get the value of the node at a position.
>>> config_node.get_value(keys=['foo', 'baz'])
'Baz'
>>> # Walk over the hierarchical structure of a node.
>>> [keys for keys, sub_node in config_node.walk()]
[['foo'], ['foo', 'baz'], ['foo', 'bar']]
>>> # Walk over the config skipping ignored sections.
>>> [keys for keys, sub_node in config_node.walk(no_ignore=True)]
[['foo'], ['foo', 'baz']]
>>> # Add two ConfigNode instances to create a new "merged" node.
>>> another_config_node = ConfigNode()
>>> _ = another_config_node.set(keys=['new'], value='New')
>>> new_config_node = config_node + another_config_node
>>> [keys for keys, sub_node in new_config_node.walk()]
[['', 'new'], ['foo'], ['foo', 'baz'], ['foo', 'bar']]
STATE_NORMAL = ''

The default state of a ConfigNode.

STATE_SYST_IGNORED = '!!'

ConfigNode state if a metadata opperation has logically ignored the config.

STATE_USER_IGNORED = '!'

ConfigNode state if it has been specifically ignored in the config.

add(config_diff)[source]

Apply a ConfigNodeDiff object to self.

Parameters:

config_diff (ConfigNodeDiff) – A diff to apply to this ConfigNode.

Examples

>>> # Create ConfigNode
>>> config_node = ConfigNode()
>>> _ = config_node.set(keys=['foo', 'bar'], value='Bar')
>>> [keys for keys, sub_node in config_node.walk()]
[['foo'], ['foo', 'bar']]
>>> # Create ConfigNodeDiff
>>> config_node_diff = ConfigNodeDiff()
>>> config_node_diff.set_added_setting(keys=['foo', 'baz'],
...                                    data='Baz')
>>> # Apply ConfigNodeDiff to ConfigNode
>>> config_node.add(config_node_diff)
>>> [keys for keys, sub_node in config_node.walk()]
[['foo'], ['foo', 'baz'], ['foo', 'bar']]
get(keys=None, no_ignore=False)[source]

Return a node at the position of keys, if any.

Parameters:
  • keys (list) – A list defining a hierarchy of node.value ‘keys’. If an entry in keys is the null string, it is skipped.

  • no_ignore (bool) – If True any ignored nodes will not be returned.

Returns:

The config node located at the position of keys or None.

Return type:

ConfigNode

Examples

>>> # Create ConfigNode.
>>> config_node = ConfigNode()
>>> _ = config_node.set(['foo', 'bar'], 'Bar')
>>> _ = config_node.set(['foo', 'baz'], 'Baz',
...     state=ConfigNode.STATE_USER_IGNORED)
>>> # A ConfigNode containing sub-nodes.
>>> config_node.get(keys=['foo']) 
{'value': {'bar': {'value': 'Bar', 'state': '', 'comments': []},
           'baz': {'value': 'Baz', 'state': '!', 'comments': []}},
 'state': '', 'comments': []}
>>> # A bottom level sub-node.
>>> config_node.get(keys=['foo', 'bar'])
{'value': 'Bar', 'state': '', 'comments': []}
>>> # Skip ignored nodes.
>>> print(config_node.get(keys=['foo', 'baz'], no_ignore=True))
None
get_filter(no_ignore)[source]

Return this ConfigNode unless no_ignore and node is ignored.

Parameters:

no_ignore (bool) – If True only return this node if it is not ignored.

Returns:

This ConfigNode unless no_ignore and node is ignored.

Return type:

ConfigNode

Examples

>>> config_node = ConfigNode(value=42)
>>> config_node.get_filter(False)
{'value': 42, 'state': '', 'comments': []}
>>> config_node.get_filter(True)
{'value': 42, 'state': '', 'comments': []}
>>> config_node = ConfigNode(value=42,
...                      state=ConfigNode.STATE_USER_IGNORED)
>>> config_node.get_filter(False)
{'value': 42, 'state': '!', 'comments': []}
>>> print(config_node.get_filter(True))
None
get_value(keys=None, default=None)[source]

Return the value of a normal node at the position of keys, if any.

If the node does not exist or is ignored, return None.

Parameters:
  • keys (list) – A list defining a hierarchy of node.value ‘keys’. If an entry in keys is the null string, it is skipped.

  • default (object) – Return default if the value is not set.

Returns:

The value of this ConfigNode at the position of keys or default if not set.

Return type:

object

Examples

>>> # Create ConfigNode.
>>> config_node = ConfigNode(value=42)
>>> _ = config_node.set(['foo'], 'foo')
>>> _ = config_node.set(['foo', 'bar'], 'Bar')
>>> # Get value without specifying keys returns the value of the
>>> # root ConfigNode (which in this case is a dict of its
>>> # sub-nodes).
>>> config_node.get_value() 
{'foo': {'value': {'bar': {'value': 'Bar', 'state': '',
                           'comments': []}},
         'state': '', 'comments': []}}
>>> # Intermediate level ConfigNode.
>>> config_node.get_value(keys=['foo'])
{'bar': {'value': 'Bar', 'state': '', 'comments': []}}
>>> # Bottom level ConfigNode.
>>> config_node.get_value(keys=['foo', 'bar'])
'Bar'
>>> # If there is no node located at the position of keys or if
>>> # that node is unset then the default value is returned.
>>> config_node.get_value(keys=['foo', 'bar', 'baz'],
...                       default=True)
True
is_ignored()[source]

Return True if current node is in the “ignored” state.

set(keys=None, value=None, state=None, comments=None)[source]

Set node properties at the position of keys, if any.

Parameters:
  • keys (list) – A list defining a hierarchy of node.value ‘keys’. If an entry in keys is the null string, it is skipped.

  • value (object) – The node.value property to set at this position.

  • state (str) – The node.state property to set at this position. If None, the node.state property is unchanged.

  • comments (str) – The node.comments property to set at this position. If None, the node.comments property is unchanged.

Returns:

This config node.

Return type:

ConfigNode

Examples

>>> # Create ConfigNode.
>>> config_node = ConfigNode()
>>> config_node
{'value': {}, 'state': '', 'comments': []}
>>> # Add a sub-node at the position 'foo' with the comment 'Info'.
>>> config_node.set(keys=['foo'], comments='Info')
... 
{'value': {'foo': {'value': {}, 'state': '', 'comments': 'Info'}},
 'state': '', 'comments': []}
>>> # Set the value for the sub-node at the position
>>> # 'foo' to 'Foo'.
>>> config_node.set(keys=['foo'], value='Foo')
... 
{'value': {'foo': {'value': 'Foo', 'state': '',
                   'comments': 'Info'}},
 'state': '', 'comments': []}
>>> # Set the value of the ConfigNode to True, this overwrites all
>>> # sub-nodes!
>>> config_node.set(keys=[''], value=True)
{'value': True, 'state': '', 'comments': []}
unset(keys=None)[source]

Remove a node at the position of keys, if any.

Parameters:

keys (list) – A list defining a hierarchy of node.value ‘keys’. If an entry in keys is the null string, it is skipped.

Returns:

The ConfigNode instance that was removed, else None.

Return type:

ConfigNode

Examples

>>> # Create ConfigNode.
>>> config_node = ConfigNode()
>>> _ = config_node.set(keys=['foo'], value='Foo')
>>> # Unset without providing any keys does nothing.
>>> print(config_node.unset())
None
>>> # Unset with invalid keys does nothing.
>>> print(config_node.unset(keys=['bar']))
None
>>> # Unset with valid keys removes the node from the node.
>>> config_node.unset(keys=['foo'])
{'value': 'Foo', 'state': '', 'comments': []}
>>> config_node
{'value': {}, 'state': '', 'comments': []}
walk(keys=None, no_ignore=False)[source]

Return all keylist - sub-node pairs below keys.

Parameters:
  • keys (list) – A list defining a hierarchy of node.value ‘keys’. If an entry in keys is the null string, it is skipped.

  • no_ignore (bool) – If True any ignored nodes will be skipped.

Yields:
tuple - (keys, sub_node)
  • keys (list) - A list defining a hierarchy of node.value ‘keys’. If a sub-node is at the top level, and does not contain any node children, a null string will be prepended to the returned keylist.

  • sub_node (ConfigNode) - The config node at the position of keys.

Examples

>>> config_node = ConfigNode()
>>> _ = config_node.set(['foo', 'bar'], 'Bar')
>>> _ = config_node.set(['foo', 'baz'], 'Baz',
...                     state=ConfigNode.STATE_USER_IGNORED)
>>> # Walk over the full hierarchy.
>>> [keys for keys, sub_node in config_node.walk()]
[['foo'], ['foo', 'baz'], ['foo', 'bar']]
>>> # Walk over one branch of the hierarchy
>>> [keys for keys, sub_node in config_node.walk(keys=['foo'])]
[['foo', 'baz'], ['foo', 'bar']]
>>> # Skip over ignored nodes.
>>> [keys for keys, sub_node in config_node.walk(no_ignore=True)]
[['foo'], ['foo', 'bar']]
>>> # Invalid/non-existent keys.
>>> [keys for keys, sub_node in config_node.walk(
...     keys=['elephant'])]
[]
class metomi.rose.config.ConfigNodeDiff[source]

Represent differences between two ConfigNode instances.

Examples

>>> # Create a new ConfigNodeDiff.
>>> config_node_diff = ConfigNodeDiff()
>>> config_node_diff.set_added_setting(keys=['bar'],
...                                    data=('Bar', None, None,))
>>> # Create a new ConfigNode.
>>> config_node = ConfigNode()
>>> _ = config_node.set(keys=['baz'], value='Baz')
>>> # Apply the diff to the node.
>>> config_node.add(config_node_diff)
>>> [(keys, sub_node.get_value()) for keys, sub_node in
...  config_node.walk()]
[(['', 'bar'], 'Bar'), (['', 'baz'], 'Baz')]
>>> # Create a ConfigNodeDiff by comparing two ConfigNodes.
>>> another_config_node = ConfigNode()
>>> _ = another_config_node.set(keys=['bar'], value='NewBar')
>>> _ = another_config_node.set(keys=['new'], value='New')
>>> config_node_diff = ConfigNodeDiff()
>>> config_node_diff.set_from_configs(config_node,
...                                   another_config_node)
>>> config_node_diff.get_added()
[(('', 'new'), ('New', '', []))]
>>> config_node_diff.get_removed()
[(('', 'baz'), ('Baz', '', []))]
>>> config_node_diff.get_modified()
[(('', 'bar'), (('Bar', '', []), ('NewBar', '', [])))]
>>> # Inverse a ConfigNodeDiff.
>>> reversed_diff = config_node_diff.get_reversed()
>>> reversed_diff.get_added()
[(('', 'baz'), ('Baz', '', []))]
delete_removed()[source]

Deletes all ‘removed’ keys from this ConfigNodeDiff.

Examples

>>> config_node_diff = ConfigNodeDiff()
>>> config_node_diff.set_removed_setting(['foo'],
...                                      ('foo', None, None))
>>> config_node_diff.delete_removed()
>>> config_node_diff.get_removed()
[]
get_added()[source]

Return a list of tuples of added keys with their data.

The data is a tuple of value, state, comments, where value is set to None for sections.

Returns:

A list of the form [(keys, data), …]
  • keys - The position of an added setting.

  • data - Tuple of the form (value, state, comments) of the properties of the added setting.

Return type:

list

Examples

>>> config_node_diff = ConfigNodeDiff()
>>> config_node_diff.set_added_setting(['foo'],
...                                    ('Foo', None, None))
>>> config_node_diff.get_added()
[(('foo',), ('Foo', None, None))]
get_all_keys()[source]

Return all keys affected by this ConfigNodeDiff.

Returns:

A list containing any keys affected by this diff as tuples, e.g. (‘foo’, ‘bar’).

Return type:

list

Examples

>>> config_node_diff = ConfigNodeDiff()
>>> config_node_diff.set_added_setting(['foo'],('foo', None, None))
>>> config_node_diff.set_removed_setting(['bar'],
...                                      ('bar', None, None))
>>> config_node_diff.get_all_keys()
[('bar',), ('foo',)]
get_as_opt_config()[source]

Return a ConfigNode such that main + new_node = main + diff.

Add all the added settings, add all the modified settings, add all the removed settings as user-ignored.

Returns:

A new ConfigNode instance.

Return type:

ConfigNode

Example

>>> config_node_diff = ConfigNodeDiff()
>>> config_node_diff.set_added_setting(['foo'],
...                                    ('Foo', None, None,))
>>> config_node_diff.set_removed_setting(['bar'],
...                                      ('Bar', None, None,))
>>> config_node = config_node_diff.get_as_opt_config()
>>> list(config_node.walk()) 
[(['', 'bar'], {'value': 'Bar', 'state': '!', 'comments': []}),
 (['', 'foo'], {'value': 'Foo', 'state': '', 'comments': []})]
get_modified()[source]

Return a dict of altered keys with before and after data.

The data is a list of two tuples (before and after) of value, state, comments, where value is set to None for sections.

Returns:

A list of the form [(keys, data), …]:
  • keys - The position of an added setting.

  • data - Tuple of the form (value, state, comments) for the properties of the setting before the modification.

  • old_data - The same tuple as data but representing the properties of the setting after the modification.

Return type:

list

Examples

>>> config_node_diff = ConfigNodeDiff()
>>> config_node_diff.set_modified_setting(
...     ['foo'], ('Foo', None, None), ('New Foo', None, None))
>>> config_node_diff.get_modified()
[(('foo',), (('Foo', None, None), ('New Foo', None, None)))]
get_removed()[source]

Return a dict of removed keys with their data.

The data is a tuple of value, state, comments, where value is set to None for sections.

Returns:

A list of the form [(keys, data), ...]:
  • keys - The position of an added setting.

  • data - Tuple of the form (value, state, comments) of the properties of the removed setting.

Return type:

list

Examples

>>> config_node_diff = ConfigNodeDiff()
>>> config_node_diff.set_removed_setting(keys=['foo'],
...                                      data=('foo', None, None))
>>> config_node_diff.get_removed()
[(('foo',), ('foo', None, None))]
get_reversed()[source]

Return an inverse (add->remove, etc) copy of this ConfigNodeDiff.

Returns:

A new ConfigNodeDiff instance.

Return type:

ConfigNodeDiff

Examples

>>> # Create ConfigNodeDiff instance.
>>> config_node_diff = ConfigNodeDiff()
>>> config_node_diff.set_added_setting(['foo'],('foo', None, None))
>>> config_node_diff.set_removed_setting(['bar'],
...                                      ('bar', None, None))
>>> # Generate reversed diff.
>>> reversed_diff = config_node_diff.get_reversed()
>>> reversed_diff.get_added()
[(('bar',), ('bar', None, None))]
>>> reversed_diff.get_removed()
[(('foo',), ('foo', None, None))]
set_added_setting(keys, data)[source]

Set a config setting to be “added” in this ConfigNodeDiff.

Parameters:
  • keys (list, tuple) – The position of the setting to add.

  • data (tuple) – A tuple of the form (value: object, state: string, comments: string) for the setting to add.

Examples

>>> config_node_diff = ConfigNodeDiff()
>>> config_node_diff.set_added_setting(['foo'],
...                                    ('Foo', None, None,))
>>> config_node_diff.set_added_setting(
...     ['bar'],
...     ('Bar', ConfigNode.STATE_USER_IGNORED, 'Some Info',))
>>> config_node = ConfigNode()
>>> config_node.add(config_node_diff)
>>> list(config_node.walk()) 
[(['', 'foo'], {'value': 'Foo', 'state': '', 'comments': []}),
 (['', 'bar'],
  {'value': 'Bar', 'state': '!', 'comments': 'Some Info'})]
set_from_configs(config_node_1, config_node_2)[source]

Create diff data from two ConfigNode instances.

Parameters:
  • config_node_1 (ConfigNode) – The node for which to base the diff off of.

  • config_node_2 (ConfigNode) – The “new” node the changes of which this diff will “apply”.

Example

>>> # Create two ConfigNode instances to compare.
>>> config_node_1 = ConfigNode()
>>> _ = config_node_1.set(keys=['foo'])
>>> config_node_2 = ConfigNode()
>>> _ = config_node_2.set(keys=['bar'])
>>> # Create a ConfigNodeDiff instance.
>>> config_node_diff = ConfigNodeDiff()
>>> config_node_diff.set_from_configs(config_node_1, config_node_2)
>>> config_node_diff.get_added()
[(('bar',), (None, '', []))]
>>> config_node_diff.get_removed()
[(('foo',), (None, '', []))]
set_modified_setting(keys, old_data, data)[source]

Set a config setting to be “modified” in this ConfigNodeDiff.

If a property in both the old_data and data (new data) are both set to None then no change will be made to any pre-existing value.

Parameters:
  • keys (list, tuple) – The position of the setting to add.

  • old_data (tuple) – A tuple (value: object, state: str, comments: str) for the “current” properties of the setting to modify.

  • data (object) – A tuple (value: object, state: str, comments: str) for “new” properties to change this setting to.

Examples

>>> # Create a ConfigNodeDiff.
>>> config_node_diff = ConfigNodeDiff()
>>> config_node_diff.set_modified_setting(
...     ['foo'], ('Foo', None, None), ('New Foo', None, None))
>>> # Create a ConfigNode.
>>> config_node = ConfigNode()
>>> _ = config_node.set(keys=['foo'], value='Foo',
...                     comments='Some Info')
>>> # Apply the ConfigNodeDiff to the ConfigNode
>>> config_node.add(config_node_diff)
>>> config_node.get(keys=['foo'])
{'value': 'New Foo', 'state': '', 'comments': 'Some Info'}
set_removed_setting(keys, data)[source]

Set a config setting to be “removed” in this ConfigNodeDiff.

Parameters:
  • keys (list) – The position of the setting to add.

  • data (tuple) – A tuple (value: object, state: str, comments: str) of the properties for the setting to remove.

Example

>>> # Create a ConfigNodeDiff.
>>> config_node_diff = ConfigNodeDiff()
>>> config_node_diff.set_removed_setting(['foo'], ('X', 'Y', 'Z'))
>>> # Create a ConfigNode.
>>> config_node = ConfigNode()
>>> _ = config_node.set(keys=['foo'], value='Foo',
...                     comments='Some Info')
>>> # Apply the ConfigNodeDiff to the ConfigNode
>>> config_node.add(config_node_diff)
>>> print(config_node.get(keys=['foo']))
None
exception metomi.rose.config.ConfigSyntaxError(code, file_name, line_num, col_num, line)[source]

Exception raised for syntax error loading a configuration file.

exc.code

Error code. Can be one of: ConfigSyntaxError.BAD_CHAR (bad characters in a name) ConfigSyntaxError.BAD_SYNTAX (general syntax error)

exc.file_name

The name of the file that triggers the error.

exc.line_num

The line number (from 1) in the file with the error.

exc.col_num

The column number (from 0) in the line with the error.

exc.line

The content of the line that contains the error.

Examples

>>> with open('config.conf', 'w+') as config_file:
...     _ = config_file.write('[foo][foo]')
>>> loader = ConfigLoader()
>>> try:
...     loader.load('config.conf')
... except ConfigSyntaxError as exc:
...     print('Error (%s) in file "%s" at %s:%s' % (
...         exc.code, exc.file_name, exc.line_num, exc.col_num))
... 
Error (BAD_CHAR) in file "..." at 1:5
metomi.rose.config.dump(root, target=<_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'>, sort_sections=None, sort_option_items=None, env_escape_ok=False)[source]

Shorthand for ConfigDumper.dump().

metomi.rose.config.load(source, root=None)[source]

Shorthand for ConfigLoader.load().

metomi.rose.config.sort_element(elem_1, elem_2)[source]

Sort pieces of text, numerically if possible.

metomi.rose.config.sort_settings(setting_1, setting_2)[source]

Sort sections and options, by numeric element if possible.