vinegar.data_source
Sub modules
Data source backed by an SQLite database. |
|
Data source backed by a text file. |
|
YAML-based source for configuration data, using pattern-based targeting. |
Module API
Sources providing configuration information associated with each system.
Different sources can be used to fill retrieve configuration information for
systems. A very flexible one is vinegar.data_source.yaml_target, which fills
the configuration tree by parsing a central YAML file specifies target
information (which files apply to which system) and the YAML files specified by
this central file in order to retrieve the actual data.
Multiple data sources can easily be chained by using the
get_composite_data_source function.
All source implementations have in common that they must specify a
get_instance function that takes a dict with configuration data as its
only parameter. This function must return an instance of data_source. The key
name in that configuration dict is reserved for use by the calling code
and should be ignored by the data source.
Data sources are thread safe.
- class vinegar.data_source.DataSource
Source that provides configuration information for a system.
The information provided can (and typically will) be different for each targeted system.
This information is then passed to the templating mechanism when generating files requested by a client. This way, different files can be generated for each system.
Often, it can be useful to have more than one data source. In this case, the data returned by each of the sources should be merged with the data returned by the other sources. The
get_composite_data_sourcefunction provides a convenient tool for such a setup.If possible, data sources should preserve the key order in dictionaries.
Each data source has to implement the
get_datamethod. This method is used to collect the data for a system with a known identifier. It also has to implement thefind_systemmethod. This method is used to find the system identifier using a key and an associated value.It is perfectly legal for a data source to be able to provide data for a system, but not be able to find the system ID given the data. For example, a data source may provide the same data to a group of systems and in that case it it is impossible to identify a specific system using the data.
Data source have to be implemented in a thread-safe manner, so that
get_dataandfind_systemcan safely be used by different threads.- abstract find_system(lookup_key: str, lookup_value: Any) str | None
Find a system given the specified key and value.
If no system can be found, the data source returns
None.- Parameters:
lookup_key – key for which to look. The interpretation of the key is up to the data source. Some data sources might use a flat structure, while others might support hierarchical data-structures. In the latter case, the use of the colon (:) as a hierarchy separator in the key is encouraged, but not required.
lookup_value – value for which to look. The interpreation of the value is up to the data source.
- Returns:
system identifier or
Noneif no system could be identified using the specified key and value.
- abstract get_data(system_id: str, preceding_data: Mapping[Any, Any], preceding_data_version: str) Tuple[Mapping[Any, Any], str]
Return data associated with the specified system.
If the data source does not have any information associated with the specified system ID, it should return an empty dictionary.
The return value of this method is in fact a tuple of the configuration data and a version string. The version string can be used by the calling code to decide whether the data has changed and thus caches have to be discarded. For example, the results of rendering a template might be cached and the cached version might be used as long as the version string returned by this method does not change. This means that implementations have to be careful to never return the same version string when the data for a system has changed. The
vinegar.utils.versionprovides utility functions for generating version strings in a way that makes accidental collisions unlikely.Please note that it is not the job of a data source to merge the
preceding_datawith the data provided by itself. The calling code takes care of this. Code wanting to use multiple data sources in a chain can use theget_composite_data_sourcefunction.Implementations are encouraged to use caching to improve performance when this method is repeatedly called for the same systems.
- Parameters:
system_id – ID of the system for which data is requested.
preceding_data – Data provided by the data source(s) that come earlier in the chain. This may be empty if there are no preceding data sources or if they did not provide any data for the system.
preceding_data_version – Version of the
preceding_data. This is an arbitrary string (typically a hash) that can be used to detect when the data provided by the preceding sources has changed.
- Returns:
tuple where the first element is the data associated with the specified system and the second element is a version string that changes whenver the returned data changes (for the same system).
- class vinegar.data_source.DataSourceAware
Marker interface indicating that a component needs a
DataSource.An object can implement this interface in order to indicate to the creating code that it wants a data source to be injected into it through the
set_data_sourcemethod.This is useful when there are several different implementations of a component and some of them require a data source and others do not. The container creating the components can decouple the logic (and configuration) for creating them from the logic that injects the data source.
In general, a component that implements this interface should still try to provide as much of its functionality as reasonably possible, if no data source has been injected into it.
Code wanting to inject a data source into a component that might possibly need it can use the
inject_data_sourcehelper function.- set_data_source(data_source: DataSource) None
Set the data source to be used by this component.
Calling
inject_data_sourcemight be preferable to calling this method directly.In general, code using this method should not assume that this method is thread safe, even if the object that implements it is considered safe in general. This means that this method should typically be called only once, directly after creating an object.
- Parameters:
data_source – data source to be injected.
- vinegar.data_source.get_composite_data_source(data_sources: Sequence[Tuple[str, Mapping[Any, Any]] | DataSource], merge_lists: bool = False, merge_sets: bool = True) DataSource
Return a data source that is a composite of the specified data sources.
The returned data source takes the provided initial data and passes it to the first data source in the chain. It then takes the result of that data source, merges it with the initial data, and passes the merged data to the next data source in the chain. This process goes on until the last data source has been reached. At this point, the result of the last data source is merged with the result of the previous data source and returned.
The data source returned by this function internally uses
merge_data_treesto merge the data returned by a source with the preceding data. Themerge_listsoption is passed on to that function and defines whether lists are also merged. By default, only dictionaries are merged.- Parameters:
data_sources – sequence of data sources that are chained together. Each item in the sequence can either be an instance of
DataSourceor a tuple. If it is a tuple, the first element must be the name of the data source (as passed toget_data_source) and the second one must be the corresponding configuration.merge_lists – defines whether lists are merged when merging data or whether a list in one dictionary replaces the list in the other dictionary. Please refer to the documentation for
merge_data_treesfor details.merge_sets – defines whether sets are merged when merging data or whether a set in one dictionary replaces the set in the other dictionary. Please refer to the documentation for
merge_data_treesfor details.
- Returns:
composite data source that chains the specified data sources together.
- vinegar.data_source.get_data_source(name: str, config: Mapping[Any, Any]) DataSource
Create the an instance of the data source with the specified name, using the specified configuration.
- Parameters:
name – name of the data source. If the name contains a dot, it is treated as an absolute module name. Otherwise it is treated as a name of one of the modules inside the
vinegar.data_sourcemodule.- Param:
config: configuration data for the data source. The meaning of that data is up to the implementation of the data source.
- Returns:
newly created data source.
- vinegar.data_source.inject_data_source(obj: Any, data_source: DataSource) None
Inject a data source into an object.
This data source is only injected if
objis an instance ofDataSourceAware, so it is safe to call this function for any object.In general, code using this function should not assume that it is thread safe, even if the object that is the target of the injection is considered thread safe in general. This means that this function should typically be called only once, directly after creating an object.
- Parameters:
obj – object that might or might not be an instance of
DataSourceAware.data_source – data source to be injected. It is only injected if
objisDataSourceAware.
- vinegar.data_source.merge_data_trees(tree1: Mapping[Any, Any], tree2: Mapping[Any, Any], merge_lists: bool = False, merge_sets: bool = True) Mapping[Any, Any]
Merge two mappings, returning the resulting dictionary.
In general, the resulting dictionary is formed by taking the key-value pairs from both mappings and putting them into a single dictionary. If the same key is present in both dictionaries, the value from the second dictionary takes precedence.
If the value is itself a mapping, the merge process is applied recursively.
If the value is a sequence, the process depends on the
merge_listsoption. If it is set toTrue, the resulting list is created by first adding all elements from the first sequence and then appending all elements from the second sequence, except for those elements that were already present in the first sequence. Ifmerge_listsis set toFalse, the second sequence simply replaces the first one (like for non-sequence types).If the value is a set, the process depends on the
merge_setsoption. If it is set toTrue(the default), the resulting set is created by calculating the union of both sets (set1 | set2). If it is set toFalse, the second set simply replaces the first one (like for non-set types).In this context, the
str,bytes,bytearray, andmemoryviewtypes are not treated as sequences. Values of this type always replace each other and are not merged.If the value associated with a key is a mapping in one mapping, but not in the other one, an exception is thrown. The same applies when
merge_listsisTrueand the value associated with a key is a sequence in one mapping, but not in the other one.The resulting dictionary preserves key order. This means that it first contains all keys from the first mapping and then those keys from the second mapping that were not also present in the first mapping.
- Parameters:
tree1 – mapping that shall be used as a base for the merge process.
tree2 – mapping that is merged into the data from
tree1, taking precedence in case of key collisions.merge_lists –
Trueif sequences in the mappings shall be merged, too,Falseif they shall replace each other. The default isFalse.merge_sets –
Trueif sets in the mappings shall be merged, too,Falseif they shall replace each other. The default isTrue.
- Returns:
insertion-order preserving dictionary that contains the merged data from
tree1andtree2.