# -*- coding: utf-8 -*-
"""This module provides features for handling public (global) project data."""
# import...
# ...from standard library
from __future__ import annotations
import types
# ...from HydPy
import hydpy
from hydpy.core import exceptiontools
from hydpy.core import filetools
from hydpy.core import indextools
from hydpy.core import optiontools
from hydpy.core import propertytools
from hydpy.core import selectiontools
from hydpy.core import timetools
from hydpy.core.typingtools import *
if TYPE_CHECKING:
from hydpy.cythons import configutils
class _PubProperty(propertytools.DefaultProperty[T_contra, T_co]):
def __init__(self) -> None:
super().__init__(self._fget)
def _fget(self, obj: Any) -> NoReturn:
raise exceptiontools.AttributeNotReady(
f"Attribute {self.name} of module `pub` is not defined at the moment."
)
class _ProjectnameProperty(_PubProperty[str, str]):
"""The name of the current project and the project's root directory.
One can manually set |Pub.projectname|:
>>> from hydpy import create_projectstructure, HydPy, pub, TestIO
>>> pub.projectname = "project_A"
However, the usual way is to pass the project name to the constructor of class
|HydPy|, which automatically sets |Pub.projectname|:
>>> hp = HydPy("project_B")
>>> pub.projectname
'project_B'
Changing |Pub.projectname| lets all file managers handled by the |pub| module
forget their eventually previously memorised but now outdated working directories:
>>> with TestIO(clear_all=True), pub.options.printprogress(True):
... create_projectstructure("project_B")
... for idx, filemanager in enumerate(pub.filemanagers):
... filemanager.currentdir = f"dir_{idx}" # doctest: +ELLIPSIS
Directory ...project_B...dir_0 has been created.
Directory ...project_B...dir_1 has been created.
Directory ...project_B...dir_2 has been created.
Directory ...project_B...dir_3 has been created.
>>> pub.projectname = "project_C"
>>> import os
>>> with TestIO(clear_all=True), pub.options.printprogress(True):
... create_projectstructure("project_C")
... os.makedirs("project_C/conditions/test")
... for filemanager in pub.filemanagers:
... _ = filemanager.currentdir # doctest: +ELLIPSIS
The name of the network manager's current working directory has not been \
previously defined and is hence set to `default`.
Directory ...project_C...default has been created.
The name of the control manager's current working directory has not been \
previously defined and is hence set to `default`.
Directory ...project_C...default has been created.
The name of the condition manager's current working directory has not been \
previously defined and is hence set to `test`.
The name of the sequence manager's current working directory has not been \
previously defined and is hence set to `default`.
Directory ...project_C...default has been created.
.. testsetup::
>>> del pub.timegrids
"""
def call_fset(self, obj: Any, value: str) -> None:
try:
for filemanager in hydpy.pub.filemanagers:
filemanager.currentdir = None # type: ignore[assignment]
except exceptiontools.AttributeNotReady:
pass
super().call_fset(obj, value)
[docs]
class TimegridsProperty(
_PubProperty[
Union[
timetools.Timegrids,
timetools.Timegrid,
tuple[
timetools.DateConstrArg,
timetools.DateConstrArg,
timetools.PeriodConstrArg,
],
],
timetools.Timegrids,
]
):
"""|DefaultProperty| specialised for |Timegrids| objects.
For convenience, property |TimegridsProperty| can create a |Timegrids| object from
a combination of a first and last date (of type |str| or |Date|) and a step size
(of type |str| or |Period|):
>>> from hydpy import pub, Timegrid, Timegrids
>>> pub.timegrids = "2000-01-01", "2010-01-01", "1d"
The given date and period information applies for the |Timegrids.init|, the
|Timegrids.sim|, and the |Timegrids.eval_| attribute:
>>> pub.timegrids.init
Timegrid("2000-01-01 00:00:00",
"2010-01-01 00:00:00",
"1d")
>>> pub.timegrids.sim
Timegrid("2000-01-01 00:00:00",
"2010-01-01 00:00:00",
"1d")
>>> pub.timegrids.eval_
Timegrid("2000-01-01 00:00:00",
"2010-01-01 00:00:00",
"1d")
Alternatively, you can assign a ready |Timegrids| object directly:
>>> pub.timegrids = Timegrids(Timegrid("2000-01-01", "2010-01-01", "1d"),
... Timegrid("2000-01-01", "2001-01-01", "1d"))
>>> pub.timegrids
Timegrids(init=Timegrid("2000-01-01 00:00:00",
"2010-01-01 00:00:00",
"1d"),
sim=Timegrid("2000-01-01 00:00:00",
"2001-01-01 00:00:00",
"1d"),
eval_=Timegrid("2000-01-01 00:00:00",
"2001-01-01 00:00:00",
"1d"))
"""
[docs]
def call_fset(
self,
obj: Any,
value: Union[
timetools.Timegrids,
timetools.Timegrid,
tuple[
timetools.DateConstrArg,
timetools.DateConstrArg,
timetools.PeriodConstrArg,
],
],
) -> None:
"""Try to convert the given input value(s)."""
try:
timegrids = timetools.Timegrids(*value)
except TypeError:
timegrids = timetools.Timegrids(value) # type: ignore
# this will most likely fail, we just want to reuse
# the standard error message
super().call_fset(obj, timegrids)
[docs]
class Pub(types.ModuleType):
"""Base class of the singleton module instance |pub|.
You can import |pub| like "normal" modules:
>>> from hydpy import pub
However, if you try to access unprepared attributes, |Pub| returns the following
error message:
>>> pub.timegrids
Traceback (most recent call last):
...
hydpy.core.exceptiontools.AttributeNotReady: Attribute timegrids of module `pub` \
is not defined at the moment.
After setting an attribute value successfully, it is accessible (we select the
`timegrids` attribute here, as its setter supplies a little magic to make defining
new |Timegrids| objects more convenient:
>>> pub.timegrids = None
Traceback (most recent call last):
...
ValueError: While trying to define a new `Timegrids` object based on the \
arguments `None`, the following error occurred: Initialising a `Timegrids` object \
either requires one, two, or three `Timegrid` objects or two dates objects (of type \
`Date`, `datetime`, or `str`) and one period object (of type `Period`, `timedelta`, \
or `str`), but objects of the types `None, None, and None` are given.
>>> pub.timegrids = "2000-01-01", "2001-01-01", "1d"
>>> pub.timegrids
Timegrids("2000-01-01 00:00:00",
"2001-01-01 00:00:00",
"1d")
After deleting it, the attribute is no longer accessible:
>>> del pub.timegrids
>>> pub.timegrids
Traceback (most recent call last):
...
hydpy.core.exceptiontools.AttributeNotReady: Attribute timegrids of module `pub` \
is not defined at the moment.
"""
options: optiontools.Options
config: configutils.Config
scriptfunctions: dict[str, Callable[..., Optional[int]]]
projectname = _ProjectnameProperty()
indexer = _PubProperty[indextools.Indexer, indextools.Indexer]()
networkmanager = _PubProperty[filetools.NetworkManager, filetools.NetworkManager]()
controlmanager = _PubProperty[filetools.ControlManager, filetools.ControlManager]()
conditionmanager = _PubProperty[
filetools.ConditionManager, filetools.ConditionManager
]()
sequencemanager = _PubProperty[
filetools.SequenceManager, filetools.SequenceManager
]()
timegrids = TimegridsProperty()
selections = _PubProperty[selectiontools.Selections, selectiontools.Selections]()
def __init__(self, name: str, doc: Optional[str] = None) -> None:
super().__init__(name=name, doc=doc)
self.options = optiontools.Options()
self.scriptfunctions = {}
@property
def filemanagers(self) -> Iterator[filetools.FileManager]:
"""Yield all file managers.
>>> from hydpy import HydPy, pub
>>> hp = HydPy("test")
>>> for filemanager in pub.filemanagers:
... type(filemanager).__name__
'NetworkManager'
'ControlManager'
'ConditionManager'
'SequenceManager'
"""
yield self.networkmanager
yield self.controlmanager
yield self.conditionmanager
yield self.sequencemanager