powsybl / powsybl-core

A framework to build power system oriented software
https://www.powsybl.org
Mozilla Public License 2.0
126 stars 41 forks source link

Programmaticaly changeable PlatformConfigProvider #2383

Open nicolas-pierr opened 1 year ago

nicolas-pierr commented 1 year ago

Widely inspired from the classic PlatformConfigProvider, it would be something like this :

public class CustomPlatformConfig implements PlatformConfigProvider {
    private static final String NAME = "custom";
    private String path = "~/.itools";
    private String configName = "config";
    private FileSystem fs;

    public CustomPlatformConfig() {
        fs = FileSystems.getDefault();
    }

    @Override
    public String getName() {
        return NAME;
    }

    static ModuleConfigRepository loadModuleRepository(Path[] configDirs, String configName) {
        List<ModuleConfigRepository> repositoriesFromPath = Arrays.stream(configDirs)
                .map(configDir -> PlatformConfig.loadModuleRepository(configDir, configName))
                .collect(Collectors.toList());
        List<ModuleConfigRepository> repositories = new ArrayList<>();
        repositories.add(new EnvironmentModuleConfigRepository(System.getenv(), FileSystems.getDefault()));
        repositories.addAll(repositoriesFromPath);
        return new StackedModuleConfigRepository(repositories);
    }

    @Override
    public PlatformConfig getPlatformConfig() {
        return new PlatformConfig(
                loadModuleRepository(new Path[] {fs.getPath(path)}, configName),
                fs.getPath(path));
    }

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }

    public String getConfigName() {
        return configName;
    }

    public void setConfigName(String configName) {
        this.configName = configName;
    }

    public FileSystem getFs() {
        return fs;
    }

    public void setFs(FileSystem fs) {
        this.fs = fs;
    }

}
sylvlecl commented 1 year ago

The difficulty to implement this is that in general the platform config is read:

Today, it's not obvious for an application to know when this read will occur. Therefore, even if you are able to set the path in the config provider, it is likely that it will have no effect at all, because getPlatformConfig will have been called earlier.

In particular, it is very possible that in pypowsybl, the config creation is done as soon as you import the python module, which does not leave any room for the python user to set the config path.

Even if it's not the case, it will be hard for the user to understand when the setter stops to be effective. For example:

import pypowsybl as pp
pp.set_config_path('/path/to/my/config.yml') # ok, works
n = pp.network.load('network.xiidm')  # will read the config
pp.set_config_path('/other/path/to/my/config.yml') # completely useless

So, if we want to make this work, it means that everywhere where PlatformConfig is used today, we need to reload it from the config provider, and we must find a way to guarantee that it's done this way in the future --> API needs probably to evolve with a breaking change.

sylvlecl commented 1 year ago

A consequence of the above thoughts: if we go for the "settable path" approach, we should allow it only as long as config has not been read, and throw an exception otherwise, with a clear message.

nicolas-pierr commented 1 year ago

I agree that we should not allow calling the setter if this has no impact on the config loaded. It is easy to know if it was loaded since the result getPlatformConfig is cached. I just checked pypowsybl and it does not load the config when I import the package.