# parameter_info - Documenting algorithm parameters¶

When evaluating algorithm performance, it is crucial to keep track of the parameterization used for each run. Otherwise, when looking at a growing set of results for different parameterization of the algorithm, it can be very hard to find out what the actual differences were, and maybe even to reproduce a good result I got earlier on before trying out a gazillion of other parameterizations.

For for information, please see https://www.fme.lan/x/TYMBAg.

## parameter_info¶

class parameter_info.parameter_info.ParameterInfo(dict=None, /, **kwargs)[source]

Bases: `UserDict`

Currently just a dict.

clear() None.  Remove all items from D.
get(k[, d]) D[k] if k in D, else d.  d defaults to None.
items() a set-like object providing a view on D's items
keys() a set-like object providing a view on D's keys
pop(k[, d]) v, remove specified key and return the corresponding value.

If key is not found, d is returned if given, otherwise KeyError is raised.

popitem() (k, v), remove and return some (key, value) pair

as a 2-tuple; but raise KeyError if D is empty.

setdefault(k[, d]) D.get(k,d), also set D[k]=d if k not in D
update([E, ]**F) None.  Update D from mapping/iterable E and F.

If E present and has a .keys() method, does: for k in E: D[k] = E[k] If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v In either case, this is followed by: for k, v in F.items(): D[k] = v

values() an object providing a view on D's values

## indirect_info¶

class parameter_info.indirect_info.IndirectParameterInfo(information_source)[source]

Bases: `ParameterInfo`

Extension of ParameterInfo which is basically an adapter for data retrieved from a user-defined information source. As writing to a remote source usually makes no sense, any attempts to write items will result in an AssignmentOnReadOnlyKeyError.

NOTE: Mixed dictionaries containing both queries and elemental values that may also be written could be supported, but that would make this class more complex, so it will not be done without a concrete use case.

Bases: `RuntimeError`

Raised if value assignment is tried on a read-only key.

with_traceback()

Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.

to_dict()[source]

Returns:

pure dict

register_key(key, source_key=None, default=None)[source]

Adds a source query for the given source_key into the dict under the given key (overwriting any pre-existing entry for that key).

Parameters:
• key – External key, use to access data in self

• source_key – Key relayed to self._information_source. If None, the external key is also used internally.

• default – Default value to assign if the key is not supported (and no exception is raised by self._information_source)

Returns:

None

register_keys(keys=None)[source]

Adds given keys without values to the dictionary, overwriting any pre-existing keys of the same name. If keys is a dictionary, it is interpreted as a mapping of external keys (those to be used with self) and internal keys (those to be interpreted by self._information_source). See register_key() for further info.

Parameters:

keys – Keys to be supported (must be supported by information source at time of read access). If None is given, all keys declared as supported by self._information_source are registered.

Returns:

None

setdefault(k[, d]) D.get(k,d), also set D[k]=d if k not in D[source]
copy()[source]

Unlike the other methods below, this one will actually copy references to the queries

get(k[, d]) D[k] if k in D, else d.  d defaults to None.[source]
items() a set-like object providing a view on D's items[source]
values() an object providing a view on D's values[source]
clear() None.  Remove all items from D.
keys() a set-like object providing a view on D's keys
pop(k[, d]) v, remove specified key and return the corresponding value.

If key is not found, d is returned if given, otherwise KeyError is raised.

popitem() (k, v), remove and return some (key, value) pair

as a 2-tuple; but raise KeyError if D is empty.

update([E, ]**F) None.  Update D from mapping/iterable E and F.

If E present and has a .keys() method, does: for k in E: D[k] = E[k] If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v In either case, this is followed by: for k, v in F.items(): D[k] = v

class parameter_info.indirect_info.InformationSource(source_key_failure_handler=<function raise_InformationSourceKeyError>, **source_query_data)[source]

Bases: `object`

Wrapper for some information source for indirect/lazy data retrieval. Once set up, new query objects can be created with create_query( source_key[, default] ). The query returned can be executed via (), resulting in actual value computation via _get_from_source.

create_query(source_key=None, default=None)[source]

Returns a query object for the given source_key using the currently set query_class and source data.

Parameters:
• source_key – Key to query the source for (once executed).

• default – If key_failure_handler does not raise an exception, this is the value returned when the query is executed, but the key does not exist at that time.

Returns:

Object of type query_class encapsulating all information to retrieve the data when executed.

get_supported_keys()[source]

Should return a comprehensive list of all supported keys. As it may not be possible to know all supported keys in advance (i.e. before requesting them from the actual source), there is no guarantee to the caller to receive ALL supported keys. However, all returned keys MUST be supported.

Returns:

Tuple with all (known) supported keys.

classmethod get_from_source(source_key, default=None, key_failure_handler=<function raise_InformationSourceKeyError>, **source_query_data)[source]

Queries a value from the information source. Is also used by the query objects. If self._get_from_source() is not successful, the key_failure_handler will be called with the received InformationSourceKeyError. If this does not raise an exception, default is returned.

Parameters:
• source_key – Key to query from source

• default – Value to return if source_key is not found (and no exception is raised by key_failure_handler)

• key_failure_handler – What to do with the InformationSourceKeyError thrown by _get_from_source on key not found.

• source_query_data – Additional arguments the source may need.

Returns:

Queried value for source_key

Raises:

cls.InformationSourceValueRetrievalError if key was valid, but value could not be computed, or whatever exception key_failure_handler raises on key error.

exception InformationSourceKeyError[source]

Bases: `RuntimeError`

Raised if a key could not be retrieved by the information source.

with_traceback()

Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.

exception InformationSourceValueRetrievalError(source_key, **source_query_data)[source]

Bases: `RuntimeError`

Raised if a value could not be retrieved from the information source, although the key was valid

with_traceback()

Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.

class parameter_info.indirect_info.Query(getter_cb, *getter_args, **getter_kwargs)[source]

Bases: `object`

Class encapsulating a call to a source getter function that can be deferred. No caching is performed here.

## cached_info¶

class parameter_info.cached_info.CachedParameterInfo(default_information_source_query)[source]

Extension of IndirectParameterInfo providing a cache for data retrieved from a user-defined information source.

clear_cached_values()[source]

Clears all cached values, forcing to update from source on next request. Works recursively, as long as the child CachedParameterInfo is a direct child.

Returns:

None

update_cache_from_source()[source]

Refreshes (overwrites) all (possibly cached) values with those from the information source. Works recursively, as long as the child CachedParameterInfo is a direct child.

Returns:

None

Bases: `RuntimeError`

Raised if value assignment is tried on a read-only key.

with_traceback()

Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.

clear() None.  Remove all items from D.
copy()

Unlike the other methods below, this one will actually copy references to the queries

get(k[, d]) D[k] if k in D, else d.  d defaults to None.
items() a set-like object providing a view on D's items
keys() a set-like object providing a view on D's keys
pop(k[, d]) v, remove specified key and return the corresponding value.

If key is not found, d is returned if given, otherwise KeyError is raised.

popitem() (k, v), remove and return some (key, value) pair

as a 2-tuple; but raise KeyError if D is empty.

register_key(key, source_key=None, default=None)

Adds a source query for the given source_key into the dict under the given key (overwriting any pre-existing entry for that key).

Parameters:
• key – External key, use to access data in self

• source_key – Key relayed to self._information_source. If None, the external key is also used internally.

• default – Default value to assign if the key is not supported (and no exception is raised by self._information_source)

Returns:

None

register_keys(keys=None)

Adds given keys without values to the dictionary, overwriting any pre-existing keys of the same name. If keys is a dictionary, it is interpreted as a mapping of external keys (those to be used with self) and internal keys (those to be interpreted by self._information_source). See register_key() for further info.

Parameters:

keys – Keys to be supported (must be supported by information source at time of read access). If None is given, all keys declared as supported by self._information_source are registered.

Returns:

None

setdefault(k[, d]) D.get(k,d), also set D[k]=d if k not in D
to_dict()

Returns:

pure dict

update([E, ]**F) None.  Update D from mapping/iterable E and F.

If E present and has a .keys() method, does: for k in E: D[k] = E[k] If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v In either case, this is followed by: for k, v in F.items(): D[k] = v

values() an object providing a view on D's values
class parameter_info.cached_info.CachedInformationSource(source_key_failure_handler=<function raise_InformationSourceKeyError>, **source_query_data)[source]

Convenience class, just exchanging the query_class with one supporting caching.

exception InformationSourceKeyError

Bases: `RuntimeError`

Raised if a key could not be retrieved by the information source.

with_traceback()

Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.

exception InformationSourceValueRetrievalError(source_key, **source_query_data)

Bases: `RuntimeError`

Raised if a value could not be retrieved from the information source, although the key was valid

with_traceback()

Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.

create_query(source_key=None, default=None)

Returns a query object for the given source_key using the currently set query_class and source data.

Parameters:
• source_key – Key to query the source for (once executed).

• default – If key_failure_handler does not raise an exception, this is the value returned when the query is executed, but the key does not exist at that time.

Returns:

Object of type query_class encapsulating all information to retrieve the data when executed.

classmethod get_from_source(source_key, default=None, key_failure_handler=<function raise_InformationSourceKeyError>, **source_query_data)

Queries a value from the information source. Is also used by the query objects. If self._get_from_source() is not successful, the key_failure_handler will be called with the received InformationSourceKeyError. If this does not raise an exception, default is returned.

Parameters:
• source_key – Key to query from source

• default – Value to return if source_key is not found (and no exception is raised by key_failure_handler)

• key_failure_handler – What to do with the InformationSourceKeyError thrown by _get_from_source on key not found.

• source_query_data – Additional arguments the source may need.

Returns:

Queried value for source_key

Raises:

cls.InformationSourceValueRetrievalError if key was valid, but value could not be computed, or whatever exception key_failure_handler raises on key error.

get_supported_keys()

Should return a comprehensive list of all supported keys. As it may not be possible to know all supported keys in advance (i.e. before requesting them from the actual source), there is no guarantee to the caller to receive ALL supported keys. However, all returned keys MUST be supported.

Returns:

Tuple with all (known) supported keys.

class parameter_info.cached_info.CachedQuery(getter_cb, *getter_args, **getter_kwargs)[source]

Bases: `Query`

clear()[source]

Clears the cache

Returns:

None

update()[source]

Refreshes the cache from the source.

Returns:

None

## network_info¶

class parameter_info.network_info.MlabNetworkInfoSource(context, field_not_found_error_handler=<function raise_InformationSourceKeyError>, **source_query_data)[source]

Can query a MeVisLab network for field values. Do not inherit CachedInformationSourceQ since it currently does not support automatic cache invalidation on (field) value change. Also, it is usually not necessary, as field value computation is cheap.

exception InformationSourceKeyError

Bases: `RuntimeError`

Raised if a key could not be retrieved by the information source.

with_traceback()

Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.

exception InformationSourceValueRetrievalError(source_key, **source_query_data)

Bases: `RuntimeError`

Raised if a value could not be retrieved from the information source, although the key was valid

with_traceback()

Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.

create_query(source_key=None, default=None)

Returns a query object for the given source_key using the currently set query_class and source data.

Parameters:
• source_key – Key to query the source for (once executed).

• default – If key_failure_handler does not raise an exception, this is the value returned when the query is executed, but the key does not exist at that time.

Returns:

Object of type query_class encapsulating all information to retrieve the data when executed.

classmethod get_from_source(source_key, default=None, key_failure_handler=<function raise_InformationSourceKeyError>, **source_query_data)

Queries a value from the information source. Is also used by the query objects. If self._get_from_source() is not successful, the key_failure_handler will be called with the received InformationSourceKeyError. If this does not raise an exception, default is returned.

Parameters:
• source_key – Key to query from source

• default – Value to return if source_key is not found (and no exception is raised by key_failure_handler)

• key_failure_handler – What to do with the InformationSourceKeyError thrown by _get_from_source on key not found.

• source_query_data – Additional arguments the source may need.

Returns:

Queried value for source_key

Raises:

cls.InformationSourceValueRetrievalError if key was valid, but value could not be computed, or whatever exception key_failure_handler raises on key error.

get_supported_keys()

Should return a comprehensive list of all supported keys. As it may not be possible to know all supported keys in advance (i.e. before requesting them from the actual source), there is no guarantee to the caller to receive ALL supported keys. However, all returned keys MUST be supported.

Returns:

Tuple with all (known) supported keys.

parameter_info.network_info.createMlabNetworkInfo(context, field_not_found_error_handler=<function raise_InformationSourceKeyError>)[source]

Convenience method to create an IndirectParameterInfo (non-cached, as caching does not make sense for field values) from the provided network context wrapped by a MlabNetworkInfoSource object.

Parameters:
• context – Network context to look for the keys (=field names) in.

• field_not_found_error_handler – Executed on query execution if the field does not exist in the contect.

## utils¶

parameter_info.utils.dump_dict(d, msg_prefix=' ')[source]

Just for nicer debug printing

Parameters:
• d – Dictionary to print

• msg_prefix – Additional message prefix

Returns:

None

parameter_info.utils.to_ParameterInfo(kv)[source]

Recursively converts given key value store kv (possibly a dict) into a new “ParameterInfo” object.

Returns:

ParameterInfo with the same keys and values as the given source dictionary

parameter_info.utils.to_dict(kv)[source]

Recursively converts given key value store kv (possibly a ParameterInfo object) into an “ordinary” dict. Use especially if kv contains nodes with keys with indirect value access to replace those with the actual values.

Returns:

Pure dict with “scalar” (non-query) values

parameter_info.utils.to_flat_dict(kv, general_prefix='', group_prefix='', group_postfix='.', omitEmptyDictionaries=False)[source]

Returns a flattened (i.e. depth 1) version of the given dictionary, combining keys so that the descend path is clear. Example:

```to_flat_dict( { "a":1, "b": { "x":10, "y":{ "z": 20 }}, c:{} ) ->
{
"a":1,
"b.x": 10,
"b.y.z": 20
"c": {}
}
```

Note that the empty dictionary c would be omitted if omitEmptyDictionaries were set to False.

Parameters:
• kv – Dictionary to flatten

• general_prefix – Prefix for each key of the resulting dict

• group_prefix – Prefix for each group’s key in combined keys

• group_postfix – Postfix for each group’s key in combined keys

• omitEmptyDictionaries – If set to True, an entry with an empty dictionary does not show up in the flat version

Returns:

Flat dict containing the composed keys and their values

parameter_info.utils.to_flat_ordered_dict(kv, general_prefix='', group_prefix='', group_postfix='.')[source]

Like to_flat_dict, but returning an OrderedDict sorted by keys

Parameters:
• kv – Dictionary to flatten

• general_prefix – Prefix for each key of the resulting dict

• group_prefix – Prefix for each group’s key in combined keys

• group_postfix – Postfix for each group’s key in combined keys

Returns:

OrderedDict containing the composed keys in alphabetical order (and their values)

parameter_info.utils.to_ordered_dict(kv)[source]

Convenience method to convert a dictionary to an OrderedDict sorted by key, but without recursing into dictionary children.

Parameters:

kv – Source dictionary

Returns:

Ordered dictionary

parameter_info.utils.get_ordered_copy(kv)[source]

Convenience method to convert any kind of dictionary (could also be a ParameterInfo) into an ordered copy of itself sorted by key, with recursing into the dictionary’s children

Parameters:

kv – Source dictionary

Returns:

Copied dictionary

class parameter_info.utils.SelectiveNoIndentEncoder(*args, **kwargs)[source]

Bases: `JSONEncoder`

Replacement for the default JSONEncoder that will prevent indentation for values wrapped in a SelectiveNoIndentEncoder.NoIndent object. Note that using a NoIndent on a composed object, none of the children in its subtree will be indented (additional NoIndent objects there will be tolerated). Adapted from https://stackoverflow.com/a/25935321/5226368 NOTE: Only works correctly with json.dumps, not json.dump! In the latter case, it will have no effect.

class NoIndent(value)[source]

Bases: `object`

Use this class to wrap values that you want to exclude from indentation.

default(o)[source]

Implement this method in a subclass such that it returns a serializable object for `o`, or calls the base implementation (to raise a `TypeError`).

For example, to support arbitrary iterators, you could implement default like this:

```def default(self, o):
try:
iterable = iter(o)
except TypeError:
pass
else:
return list(iterable)
# Let the base class default method raise the TypeError
return JSONEncoder.default(self, o)
```
encode(o)[source]

Return a JSON string representation of a Python data structure.

```>>> from json.encoder import JSONEncoder
>>> JSONEncoder().encode({"foo": ["bar", "baz"]})
'{"foo": ["bar", "baz"]}'
```
iterencode(o, _one_shot=False)

Encode the given object and yield each string representation as available.

For example:

```for chunk in JSONEncoder().iterencode(bigobject):
mysocket.write(chunk)
```
class parameter_info.utils.IndentOnlyNestedSequencesJsonEncoder(*args, **kwargs)[source]

Replacement for the default JSONEncoder that will write non-nested lists, tuples or sets in a single line even with indent > 0. NOTE: Only works correctly with json.dumps, not json.dump! In the latter case, it will have no effect.

class MarkElementarySequences[source]

Bases: `TypeCleaner`

Modifies the TypeCleaner to mark all non-nested sequences by encapsulating them in the NoIndent wrapper.

classmethod get_json_compatible_copy(value, key='', raiseOnError=False)[source]

Creates a copy of the provided data structure, which may be any composition of elementary and container types. Will convert known types that are not supported by Json.dumps() into something that is.

Parameters:
• value – Value to copy (elementary or container)

• key – Optional key identifier used only for error logging (simplifies finding unexpected datatypes in a container)

• raiseOnError – If enabled, a TypeError is raised for each conversion error. Otherwise, only an info message is printed, and the unconvertible value is put out as a string with a _unsupported_type_<typename> postfix.

Returns:

(hopefully) json compatible copy of the provided data structure

encode(o)[source]

Return a JSON string representation of a Python data structure.

```>>> from json.encoder import JSONEncoder
>>> JSONEncoder().encode({"foo": ["bar", "baz"]})
'{"foo": ["bar", "baz"]}'
```
class NoIndent(value)

Bases: `object`

Use this class to wrap values that you want to exclude from indentation.

default(o)

Implement this method in a subclass such that it returns a serializable object for `o`, or calls the base implementation (to raise a `TypeError`).

For example, to support arbitrary iterators, you could implement default like this:

```def default(self, o):
try:
iterable = iter(o)
except TypeError:
pass
else:
return list(iterable)
# Let the base class default method raise the TypeError
return JSONEncoder.default(self, o)
```
iterencode(o, _one_shot=False)

Encode the given object and yield each string representation as available.

For example:

```for chunk in JSONEncoder().iterencode(bigobject):
mysocket.write(chunk)
```