You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
So far, HydPy does not yet provide a way to save the project settings made in module pub. If you need to use the same settings later or, for example, in different scripts, it is currently the best practice to write one Python script that makes these settings, for example:
my_settings.py
from hydpy import HydPy, pub
hp = HydPy('my_project')
pub.timegrids = '2000-01-01', '2001-01-01', '1d'
pub.options.checkseries = False
pub.networkmanager.currentdir = 'my_folder'
Then, you can import these settings in each script (or in the Python Interpreter) as follows:
my_script.py
from my_settings import *
...
The idea is, to improve the string representation of the pub module (which is already the object of the HydPy-class Pub, derived from Module). As always, the string representation should be executable code as far as possible and be applicable to restore the current configuration of the object. To achieve this goal, we would also need to improve the string representations of some other classes (e.g. Options and ControlManager).
An additional method of class Pub to save the string representation directly into a file would be convenient.
We should discuss if we also want to support saving custom settings. For example, one could also use the module pub to save some constants relevant for different scripts. But this would be restricted to using objects already coming with suitable string representations. So, strings and numbers would be fine, but more complex objects probably not. (We could implement a check into method setattr of module Pub, which executes the string representation for each given attribute and compares the old with the new object.)
Would something like this probably be a solution to save and load the project settings (hydpy.filetools), probably not sophisticated enough looking at the discussions above:
defsave_projectsettings(filepath: str="settings.py", write_timegrids: bool=False) ->None:
"""Save the project settings to file 'filepath'. If the 'filepath is relative, it is interpreted as relative to the root directory of the |HydPy| project. |Options| attributes of module |pub|, base and current directories of the file managers as well as relevant attributes of the |SequenceManager| are written. If 'write_timegrids=True', the currently defined |TimeGrids| attribute of module |pub| is written, too. >>> from hydpy.core.testtools import prepare_full_example_2 >>> _, pub, TestIO = prepare_full_example_2() We write the project settings to the settings file >>> with TestIO(): ... save_projectsettings() ... with open("HydPy-H-Lahn/settings.py") as file: ... print(file.read()) Trying to save non defined timegrids lead to an error: >>> with TestIO(): ... save_projectsettings(write_timegrids=True) Traceback (most recent call last): ... hydpy.core.exceptiontools.AttributeNotReady: Attribute timegrids of module `pub` is not defined at the moment. We define the pub.timegrids and save them to the settings file in the subdirectory settings >>> pub.timegrids = "2010-01-01","2010-01-10","1d" >>> with TestIO(): ... save_projectsettings("settings/settings.py", write_timegrids = True) ... with open("HydPy-H-Lahn/settings/settings.py") as file: ... print(file.read()) """ifnotos.path.isabs(filepath):
filepath=os.path.abspath(os.path.join(hydpy.pub.networkmanager.projectdir, filepath))
lines= ["# -*- coding: utf-8 -*-\nimport hydpy\n\n"]
ifwrite_timegrids:
lines.append("hydpy.pub.timegrids="+repr(hydpy.pub.timegrids).replace("Timegrid", "hydpy.Timegrid")+"\n\n")
lines.append(f"hydpy.pub.options.simulationstep('{str(hydpy.pub.options.simulationstep)}')")
# save optionslines.append(f"hydpy.pub.options.parameterstep('{str(hydpy.pub.options.parameterstep)}')")
lines.append(f"hydpy.pub.options.checkprojectstructure({bool(hydpy.pub.options.checkprojectstructure)})")
lines.append(f"hydpy.pub.options.checkseries({bool(hydpy.pub.options.checkseries)})")
lines.append(f"hydpy.pub.options.ellipsis({hydpy.pub.options.ellipsis})")
lines.append(f"hydpy.pub.options.printprogress({bool(hydpy.pub.options.printprogress)})")
lines.append(f"hydpy.pub.options.reprdigits({hydpy.pub.options.reprdigits})")
lines.append(f"hydpy.pub.options.timestampleft({bool(hydpy.pub.options.timestampleft)})")
lines.append(f"hydpy.pub.options.trimvariables({bool(hydpy.pub.options.trimvariables)})")
lines.append(f"hydpy.pub.options.usecython({bool(hydpy.pub.options.usecython)})")
lines.append(f"hydpy.pub.options.usedefaultvalues({bool(hydpy.pub.options.usedefaultvalues)})")
lines.append(f"hydpy.pub.options.utclongitude({hydpy.pub.options.utclongitude})")
lines.append(f"hydpy.pub.options.utcoffset({hydpy.pub.options.utcoffset})")
lines.append(f"hydpy.pub.options.warnmissingcontrolfile({bool(hydpy.pub.options.warnmissingcontrolfile)})")
lines.append(f"hydpy.pub.options.warnmissingobsfile({bool(hydpy.pub.options.warnmissingobsfile)})")
lines.append(f"hydpy.pub.options.warnmissingsimfile({bool(hydpy.pub.options.warnmissingsimfile)})")
lines.append(f"hydpy.pub.options.warnsimulationstep({bool(hydpy.pub.options.warnsimulationstep)})")
lines.append(f"hydpy.pub.options.warntrim({bool(hydpy.pub.options.warntrim)})\n\n")
# save filemanager directorieslines.append(f"hydpy.pub.networkmanager.BASEDIR='{hydpy.pub.networkmanager.BASEDIR}'")
lines.append(f"hydpy.pub.networkmanager.currentdir='{hydpy.pub.networkmanager.currentdir}'\n")
lines.append(f"hydpy.pub.controlmanager.BASEDIR='{hydpy.pub.controlmanager.BASEDIR}'")
lines.append(f"hydpy.pub.controlmanager.currentdir='{hydpy.pub.controlmanager.currentdir}'\n")
lines.append(f"hydpy.pub.conditionmanager.prefix('{hydpy.pub.conditionmanager.prefix}')")
lines.append(f"hydpy.pub.conditionmanager.BASEDIR='{hydpy.pub.conditionmanager.BASEDIR}'\n")
currentdir=hydpy.pub.conditionmanager._currentdirifnotcurrentdir:
lines.append("hydpy.pub.conditionmanager.currentdir=None\n")
else:
lines.append(f"hydpy.pub.conditionmanager.currentdir='{hydpy.pub.conditionmanager.currentdir}'\n")
lines.append(f"hydpy.pub.sequencemanager.filetype('{hydpy.pub.sequencemanager.filetype}')")
lines.append(f"hydpy.pub.sequencemanager.reset({bool(hydpy.pub.sequencemanager.reset)})")
lines.append(f"hydpy.pub.sequencemanager.overwrite({bool(hydpy.pub.sequencemanager.overwrite)})")
lines.append(f"hydpy.pub.sequencemanager.aggregation('{hydpy.pub.sequencemanager.aggregation}')")
lines.append(f"hydpy.pub.sequencemanager.convention('{hydpy.pub.sequencemanager.convention}')")
lines.append(f"hydpy.pub.sequencemanager.BASEDIR='{hydpy.pub.sequencemanager.BASEDIR}'")
lines.append(f"hydpy.pub.sequencemanager.currentdir='{hydpy.pub.sequencemanager.currentdir}'\n\n")
text="".join("\n".join(lines))
dirpath=os.path.dirname(filepath)
ifnotos.path.exists(dirpath):
os.makedirs(dirpath)
ifhydpy.pub.options.printprogress:
print(f"Directory {dirpath} has been created.")
withopen(filepath, mode="w", encoding="utf-8") assettingsfile:
settingsfile.write(text)
ifhydpy.pub.options.printprogress:
print(
f"Saved project settings in file: {filepath}'."
)
defload_projectsettings(filepath: str="settings.py") ->None:
"""Load the project settings from file 'filepath'. If the 'filepath is relative, it is interpreted as relative to the root directory of the |HydPy| project. Trying to read project settings from a non-existent file leads to an error >>> load_projectsettings("file.py") Traceback (most recent call last): ... RuntimeError: The settings file `C:\Users\Klein\HydPy_Ressourcen\hydpy_tutorial\HydPy-H-Lahn\file.py` does not exist. >>> from hydpy.core.testtools import prepare_full_example_2 >>> _, pub, TestIO = prepare_full_example_2() We define the pub.timegrids and save the project settings >>> pub.timegrids = "2010-01-01","2010-01-10","1d" >>> with TestIO(): ... save_projectsettings(write_timegrids = True) and change some project settings >>> with TestIO(): ... pub.sequencemanager.filetype="nc" ... pub.sequencemanager.currentdir="input" ... pub.sequencemanager.BASEDIR="sequences" ... pub.options.checkseries=False ... pub.timegrids = "2011-01-01","2011-01-10","1d" After reloading the project settings the values hold their old values >>> with TestIO(): ... load_projectsettings() >>> pub.sequencemanager.filetype 'asc' >>> pub.sequencemanager.currentdir 'default' >>> pub.sequencemanager.BASEDIR 'series' >>> pub.options.checkseries TRUE >>> pub.timegrids ... pub.options.checkseries=False ... pub.timegrids = "2011-01-01","2011-01-10","1d" Timegrids("2010-01-01 00:00:00", "2010-01-10 00:00:00", "1d") """ifnotos.path.isabs(filepath):
filepath=os.path.abspath(os.path.join(hydpy.pub.networkmanager.projectdir, filepath))
ifnotos.path.exists(filepath):
raiseRuntimeError(
f"The settings file `{filepath}` does not exist."
)
runpy.run_path(filepath)
(I edited your code example slightly to enable code highlighting.)
Good idea to make writing the Timegrids instance optional. Maybe we could also add an option like ignore_timegrids to load_projectsettings.
I think, your suggestion covers all the essential functionalities I mentioned above (except for the last paragraph about custom settings). But we should split everything into separate methods. One toy refactoring based on your example:
I also thought about adding ignore_timegrids to read_projectsettings but I was not sure about the standard value, False would be my prefered option but it is probably inconsistent with the prefered option write_timegrids = False when writing project settings.
Yes, I am also very uncertain about the "best" default values here (even for both functions). Is this maybe a hint at not providing any default values so that users are forced to think about the right choice in each situation?
Activity
BGWKlein commentedon Oct 22, 2024
Would something like this probably be a solution to save and load the project settings (hydpy.filetools), probably not sophisticated enough looking at the discussions above:
tyralla commentedon Oct 23, 2024
(I edited your code example slightly to enable code highlighting.)
Good idea to make writing the
Timegridsinstance optional. Maybe we could also add an option likeignore_timegridstoload_projectsettings.I think, your suggestion covers all the essential functionalities I mentioned above (except for the last paragraph about custom settings). But we should split everything into separate methods. One toy refactoring based on your example:
Or:
Including this in HydPy 6.2 should not require too much effort.
BGWKlein commentedon Oct 23, 2024
The extension of class Options looks great.
I also thought about adding ignore_timegrids to read_projectsettings but I was not sure about the standard value, False would be my prefered option but it is probably inconsistent with the prefered option write_timegrids = False when writing project settings.
tyralla commentedon Oct 24, 2024
Yes, I am also very uncertain about the "best" default values here (even for both functions). Is this maybe a hint at not providing any default values so that users are forced to think about the right choice in each situation?
BGWKlein commentedon Oct 24, 2024
I agree with you, providing no default values is probably less convinient for the user but we make sure that the user knows what he is doing.