Factory#

Defines ExperimentFactory and Loader related classes.

class phileas.factory.Loader(name: str, instruments_factory: ExperimentFactory)[source]#

Bases: ABC

Class used to initiate a connection to an instrument, and then configure it. In order to add the support for a new instrument, you should subclass it and register it in an ExperimentFactory.

name: ClassVar[str]#

Name of the loader, which is usually the name of the loaded instrument. It is to be matched with the bench configuration loader field.

interfaces: ClassVar[set[str]]#

Interfaces the loaded instrument supports. Interfaces are arbitrary names referred to in the experiment configuration file, allowing to choose which bench instrument is used for which experiment instrument, depending on its interface field.

instruments_factory: ExperimentFactory#

Reference to the instrument factory which has instantiated the loader. This allows loaders to have access, among other things, to already instantiated instruments.

logger: Logger#

Logging handler that should be used for logging loader-specific information.

abstract initiate_connection(configuration: dict) Any[source]#

Initialize a connection to a given instrument, given its bench configuration, and return it.

abstract configure(instrument: Any, configuration: dict)[source]#

Configure an instrument - whose connection has already been initiated - using its experiment configuration. The instrument should be modified, and is not returned.

get_effective_configuration(instrument: Any, configuration: None | dict[str, phileas.iteration.base.DataTree] = None) dict[str, phileas.iteration.base.DataTree][source]#

Returns the effective configuration of an instrument. The purpose of this method is to be called after configure(), in order to verify that the required parameters have been properly applied. It might be useful in different situations, for example:

  • the instrument might have a parameter with which the driver uses a best effort approach. If the user requests an unsupported value, it automatically chooses a supported value. For example, the only integer values might be supported, and the driver rounds the user-supplied parameter.

  • Setting a parameter might not be deterministic. For example, the effective position of a piezoelectric actuator differs from its target.

  • The user manually configures an instrument, and wants to get its configuration back. In this case, dump_state() might also be interesting.

If configuration is not supplied, the entire configuration of the instrument is returned. It should have the same structure as the argument of configure().

Otherwise, if configuration is supplied, its values are replaced with the effective parameter values. This is typically used to verify the value of a configuration that has just been applied with configure(). Note that, in this case, configuration might be modified, and it is likely that get_effective_configuration (instrument, configuration) is configuration.

It is not necessary to implement this method.

get_id(instrument: Any) str[source]#

Returns a unique identifier of an instrument. Two physically different instruments are expected to have different identifiers.

dump_state(instrument: Any) phileas.iteration.base.DataTree[source]#

Dump the state of an instrument. It should, as much as possible, represent the exact state of the instrument. It should contain the value of parameters that can be configured with configure(), but also other ones. These might include, among others, calibration values, the exact mode which is used, firmware and driver versions, etc.

The purpose of this state is to enable comparing similar instruments, to find out if they can be swapped or how to make them interchangeable.

It is not necessary to implement this method. However, when it is implemented, together with restore_state(), it allows to guarantee that the instrument always starts in the same state. It can also be used to capture a manual configuration, in order to replicate it later.

restore_state(instrument: Any, state: phileas.iteration.base.DataTree)[source]#

Put the instrument in a previously recorded state. See dump_state().

classmethod get_markdown_documentation() str[source]#

Generate a markdown documentation of the loader, which is based on the docstrings of the initiate_connection() and configure() methods.

phileas.factory.build_loader(name_: str, interfaces_: set[str], initiate_connection: Callable[[dict], Any], configure: Callable[[Any, dict], None]) type[Loader][source]#

Build a loader class from its name, interfaces and different methods. Note that using this function does not allow a loader instance to access the instrument factory that uses it.

phileas.factory.register_default_loader(loader: type[Loader] | tuple[str, set[str], Callable[[dict], Any], Callable[[Any, dict], None]]) type[Loader] | tuple[str, set[str], Callable[[dict], Any], Callable[[Any, dict], None]][source]#

Register a loader to be added on init by every new ExperimentFactory. See _add_loader() for the specifications of the arguments.

This function can either be used directly, or as a class decorator.

phileas.factory.clear_default_loaders()[source]#

Clear the default loaders database.

class phileas.factory.BenchInstrument(name: 'str', loader: 'Loader', configuration: 'dict[Key, DataTree]', instrument: 'Any | None' = None)[source]#

Bases: object

loader: Loader#

Own loader of the instrument, which is not shared with any other instrument.

configuration: dict[Key, DataTree]#

Bench configuration of the instrument, stripped of the reserved keywords entries.

instrument: Any | None = None#

None before initialization.

class phileas.factory.ExperimentInstrument(name: 'str', interface: 'str', bench_instrument: 'BenchInstrument', configuration: 'dict[str, Any]')[source]#

Bases: object

configuration: dict[str, Any]#

Experiment configuration of the instrument, stripped of the reserved keywords entries

class phileas.factory.InstrumentState(loader: str, interfaces: list[str], configuration: phileas.iteration.base.DataTree, id: str | None, state: phileas.iteration.base.DataTree)[source]#

Bases: object

Serializable state of a BenchInstrument.

loader: str#

Name of its loader.

interfaces: list[str]#

Supported interfaces of the loader.

configuration: DataTree#

Configuration applied to the instrument.

id: str | None#

Identifier of the instrument, see Loader.get_id().

state: DataTree#

State of the instrument, see Loader.dump_state().

class phileas.factory.BenchState(version: str, instruments: dict[str, InstrumentState])[source]#

Bases: object

Serializable state of a whole bench.

version: str#

Version of Phileas.

instruments: dict[str, InstrumentState]#

Instruments of the bench.

to_yaml(destination: Path | None = None) str | None[source]#

Serialize a bench state to YAML. If you supply a path in destination, directly write the state to the file located there. Otherwise, return a string.

classmethod from_yaml(source: str | Path) BenchState[source]#

Deserialize a bench state from YAML. If you supply a path in source, read the state from the file located there. Otherwise, read the string.

class phileas.factory.Filter[source]#

Bases: ABC

Class used to store an expression tree representing the filtering expressions stored in the filter entry of the experiment configuration.

abstract verifies(instrument: BenchInstrument) bool[source]#

Checks whether a bench instrument satisfies the filter.

static build_filter(filter_entry: dict[str, Any] | list[dict]) Filter[source]#

Filter parser, which is given the filter entry of the experiment configuration file, and returns the corresponding expression tree.

Todo

Only parsing single-level dict is supported for now. Parsing nested filters should be implemented.

class phileas.factory.AttributeFilter(field: str, value: Any)[source]#

Bases: Filter

Filter checking that a bench instrument has the expected entry value in its configuration. This is one of the leaves of the filters expression tree.

verifies(instrument: BenchInstrument) bool[source]#

Checks whether a bench instrument satisfies the filter.

class phileas.factory.ConstantFilter(value: bool)[source]#

Bases: Filter

Literal filter.

verifies(instrument: BenchInstrument) bool[source]#

Checks whether a bench instrument satisfies the filter.

class phileas.factory.FiltersCombination(filter1: Filter, filter2: Filter, operation: Callable[[bool, bool], bool])[source]#

Bases: Filter

Filters combination based on a binary operator.

verifies(instrument: BenchInstrument) bool[source]#

Checks whether a bench instrument satisfies the filter.

class phileas.factory.Connection(src: 'str', src_port: 'list[str]', dst: 'str', dst_port: 'list[str]', attr: 'str')[source]#

Bases: object

class phileas.factory.ExperimentFactory(bench: Path | str | dict[str, phileas.iteration.base.DataTree], experiment: Path | str | IterationTree)[source]#

Bases: object

The experiment factory is used to
  • parse configuration files, cleaning them of reserved keywords;

  • match the experiment instruments to their bench instruments and loaders;

  • initiate the connection to the instruments, and configure them.

loaders: dict[str, type[Loader]]#

The supported loader classes, stored with their name.

bench_file: Path | None#

If the bench configuration is supplied by file, stores its path. Otherwise, stores None.

bench_config: dict[str, DataTree]#

Bench configuration stripped of the reserved keyword entries.

experiment_instruments: dict[str, Any]#

Instruments whose connection has been initiated, configured or not.

experiment_file: Path | None#

If the experiment configuration is supplied by file, stores its path. Otherwise, stores None.

experiment_config: IterationTree#

Experiment configuration stripped of the reserved keyword entries.

initiate_connections()[source]#

Lazily initiate the connections of all the bench instruments, ie. only those that are matched to an experiment instrument are handled.

get_bench_instrument(name: str) Any[source]#

Return the bench instrument whose name is specified, using its loader initiate_connection() method to create it. If the connection to the instrument has already been initiated, the instrument is simply returned.

This is the only way a bench instrument should be retrieved.

configure_instrument(name: str, configuration: dict | None = None)[source]#

Configure an experiment instrument using the given configuration, and the configure() method of its loader. If no configuration is given, use the instrument default configuration from the experiment configuration.

configure_experiment(configuration: dict | None = None)[source]#

Configure multiple instruments at once, using the configure() method of their respective loaders, and the entry of configuration matching their name. If configuration misses the entry of an instrument, the instrument won’t be configured.

If no configuration is given, the default experiment configuration is used to configure the instruments. In this case, all the instruments are configured.

get_effective_instrument_configuration(name: str, configuration: dict | None = None) dict[str, phileas.iteration.base.DataTree][source]#

Return the effective configuration of an instrument. See Loader.get_effective_configuration().

get_effective_experiment_configuration(configuration: dict | None = None) dict[str, dict[str, phileas.iteration.base.DataTree]][source]#

Return the effective configuration of the whole bench. If configuration is not supplied, get the configuration of each instrument of the bench. Otherwise, only retrieves the configuration of instrument which have an entry. The value of the entry is passed to Loader.get_effective_configuration(). If the entry does not

correspond to an instrument, or its loader does not implement get_effective_configuration(), it is ignored.

dump_bench_state(full: bool = True) BenchState[source]#

Dumps the state of the whole bench.

When not full, do not dump the state of the instruments. However, retrieve its loader and ID information.

restore_bench_state(state: BenchState, strict: bool = True)[source]#

Restores the a bench state. If strict, requires the following serialized values to match with their bench values:

Additionally, every loader is required to implement Loader.restore_state().

Otherwise, if not strict, the state of all the instruments with matching names and InstrumentState.loader is restored. Others are ignored.

Raises:

ValueError – if the state cannot be restored due to stored state incompatibilities.

register_loader(loader: type[Loader] | tuple[str, set[str], Callable[[phileas.iteration.base.DataTree], Any], Callable[[Any, phileas.iteration.base.DataTree], Any]])[source]#

Register a new loader for this factory. See _add_loader() for the specifications of the arguments.

get_loaders_markdown_documentation() str[source]#

Return the Markdown documentation of all the registered loaders of this factory, represented by the concatenation of their documentations.

static get_default_loaders_markdown_documentation() str[source]#

Return the Markdown documentation of all the default registered loaders, represented by the concatenation of their documentations.