testtools¶
This module implements tools for testing HydPy and its models.
Module testtools
implements the following members:
StdOutErr
Replaces sys.stdout and sys.stderr temporarily when calling methodperform_tests()
of classTester
.
Tester
Tests either a base or an application model.
mock_datetime_now()
Let class methodnow()
of classdatetime
of moduledatetime
return the given date for testing purposes within a “with-block”.
warn_later()
Suppress warnings and print them upon exit.
ArrayDescriptor
A descriptor for handling values ofArray
objects.
Test
Base class forIntegrationTest
andUnitTest
.
PlottingOptions
Plotting options of classIntegrationTest
.
IntegrationTest
Defines model integration doctests.
UnitTest
Defines unit doctests for a single model method.
TestIO
Prepare an environment for testing IO functionalities.
make_abc_testable()
Return a concrete version of the given abstract base class for testing purposes.
NumericalDifferentiator
Approximate the derivatives ofModelSequence
values based on the finite difference approach.
update_integrationtests()
Write the docstring of the given application model, updated with the current simulation results, to file.
check_methodorder()
Check that HydPy calls the methods of the given application model in the correct order for each simulation step.
check_selectedvariables()
Perform consistency checks regarding theParameter
andSequence_
subclasses selected by the givenMethod
subclass.
perform_consistencychecks()
Perform all available consistency checks for the given application model.
save_autofig()
Save a figure automatically generated during testing in the special autofig sub-package so that Sphinx can include it into the documentation later.
- class hydpy.core.testtools.StdOutErr(indent: int = 0)[source]¶
Bases:
object
Replaces sys.stdout and sys.stderr temporarily when calling method
perform_tests()
of classTester
.
- class hydpy.core.testtools.Tester[source]¶
Bases:
object
Tests either a base or an application model.
Usually, a
Tester
object is initialised at the end of the __init__ file of its base model or the end of the module of an application model.>>> from hydpy.models import hland, hland_v1
>>> hland.tester.package 'hydpy.models.hland' >>> hland_v1.tester.package 'hydpy.models'
- property filenames: List[str]¶
The filenames which define the considered base or application model.
>>> from hydpy.models import hland, hland_v1 >>> from pprint import pprint >>> pprint(hland.tester.filenames) ['__init__.py', 'hland_aides.py', 'hland_constants.py', 'hland_control.py', 'hland_derived.py', 'hland_factors.py', 'hland_fixed.py', 'hland_fluxes.py', 'hland_inputs.py', 'hland_logs.py', 'hland_masks.py', 'hland_model.py', 'hland_outlets.py', 'hland_parameters.py', 'hland_sequences.py', 'hland_states.py'] >>> hland_v1.tester.filenames ['hland_v1.py']
- property modulenames: List[str]¶
The module names to be taken into account for testing.
>>> from hydpy.models import hland, hland_v1 >>> from pprint import pprint >>> pprint(hland.tester.modulenames) ['hland_aides', 'hland_constants', 'hland_control', 'hland_derived', 'hland_factors', 'hland_fixed', 'hland_fluxes', 'hland_inputs', 'hland_logs', 'hland_masks', 'hland_model', 'hland_outlets', 'hland_parameters', 'hland_sequences', 'hland_states'] >>> hland_v1.tester.modulenames ['hland_v1']
- perform_tests() None [source]¶
Perform all doctests either in Python or in Cython mode depending on the state of
usecython
set in modulepub
.Usually,
perform_tests()
is triggered automatically by aCythonizer
object assigned to the same base or application model as aTester
object. However, you are free to call it any time when in doubt of the functionality of a particular base or application model. Doing so might change some of the states of your current configuration, but only temporarily (besides “projectname”) we pick theTimegrids
object of modulepub
as an example, which is changed multiple times during testing but finally reset to the original value):>>> from hydpy import pub >>> pub.projectname = "test" >>> pub.timegrids = "2000-01-01", "2001-01-01", "1d"
>>> from hydpy.models import hland, hland_v1 >>> hland.tester.perform_tests() Test package hydpy.models.hland in ...ython mode. * hland_aides: no failures occurred * hland_constants: no failures occurred * hland_control: no failures occurred * hland_derived: no failures occurred * hland_factors: no failures occurred * hland_fixed: no failures occurred * hland_fluxes: no failures occurred * hland_inputs: no failures occurred * hland_logs: no failures occurred * hland_masks: no failures occurred * hland_model: no failures occurred * hland_outlets: no failures occurred * hland_parameters: no failures occurred * hland_sequences: no failures occurred * hland_states: no failures occurred
>>> hland_v1.tester.perform_tests() Test module hland_v1 in ...ython mode. * hland_v1: no failures occurred
>>> pub.projectname 'test' >>> pub.timegrids Timegrids("2000-01-01 00:00:00", "2001-01-01 00:00:00", "1d")
To show the reporting of possible errors, we change the string representation of parameter
ZoneType
temporarily. Again, theTimegrids
object is reset to its initial state after testing:>>> from unittest import mock >>> with mock.patch( ... "hydpy.models.hland.hland_control.ZoneType.__repr__", ... return_value="damaged"): ... hland.tester.perform_tests() Test package hydpy.models.hland in ...ython mode. * hland_aides: no failures occurred * hland_constants: no failures occurred * hland_control: ******...hland_control.py", line ..., in hydpy.models.hland.hland_control.ZoneType Failed example: zonetype Expected: zonetype(FIELD, FOREST, GLACIER, ILAKE, ILAKE, FIELD) Got: damaged ********************************************************************** 1 items had failures: 1 of 6 in hydpy.models.hland.hland_control.ZoneType ***Test Failed*** 1 failures. * hland_derived: no failures occurred ... * hland_states: no failures occurred
>>> pub.projectname 'test' >>> pub.timegrids Timegrids("2000-01-01 00:00:00", "2001-01-01 00:00:00", "1d")
- class hydpy.core.testtools.Array[source]¶
Bases:
object
Assures that attributes are
ndarray
objects.
- class hydpy.core.testtools.ArrayDescriptor[source]¶
Bases:
object
A descriptor for handling values of
Array
objects.
- class hydpy.core.testtools.Test[source]¶
Bases:
object
Base class for
IntegrationTest
andUnitTest
.This base class defines the printing of the test results primarily. How the tests shall be prepared and performed is to be defined in its subclasses.
- inits = <hydpy.core.testtools.Array object>¶
Stores arrays for setting the same values of parameters and/or sequences before each new experiment.
- class hydpy.core.testtools.PlottingOptions[source]¶
Bases:
object
Plotting options of class
IntegrationTest
.- axis1: IOSequence | Iterable[IOSequence] | None¶
- axis2: IOSequence | Iterable[IOSequence] | None¶
- class hydpy.core.testtools.IntegrationTest(element: Element, seqs=None, inits=None)[source]¶
Bases:
Test
Defines model integration doctests.
The functionality of
IntegrationTest
is easiest to understand by inspecting doctests like the ones of modulesllake_v1
orarma_v1
.Note that all condition sequences (state and logging sequences) are initialised in accordance with the values are given as inits values. The values of the simulation sequences of outlet and sender nodes are always set to zero before each test run. All other parameter and sequence values can be changed between different test runs.
- plotting_options = <hydpy.core.testtools.PlottingOptions object>¶
- property raw_first_col_strings¶
The raw date strings of the first column, except the header.
- property dateformat: str¶
Format string for printing dates in the first column of the table.
See the documentation on module
datetime
for the format strings allowed.You can query and change property
dateformat
:>>> from hydpy import Element, IntegrationTest, prepare_model, pub >>> pub.timegrids = "2000-01-01", "2001-01-01", "1d" >>> element = Element("element", outlets="node") >>> element.model = prepare_model("hland_v1") >>> __package__ = "testpackage" >>> tester = IntegrationTest(element) >>> tester.dateformat '%Y-%m-%d %H:%M:%S'
Passing an ill-defined format string leads to the following error:
>>> tester.dateformat = 999 Traceback (most recent call last): ... ValueError: The given date format `999` is not a valid format string for `datetime` objects. Please read the documentation on module datetime of the Python standard library for for further information.
>>> tester.dateformat = "%x" >>> tester.dateformat '%x'
- get_output_array(parseqs)[source]¶
Return the array containing the output results of the given sequence.
- prepare_node_sequences()[source]¶
Prepare the simulations series of all nodes.
This preparation might not be suitable for all types of integration tests. Prepare those node sequences manually, for which this method does not result in the desired outcome.
- prepare_input_model_sequences()[source]¶
Configure the input sequences of the model in a manner that allows for applying their time-series data in integration tests.
- extract_print_sequences()[source]¶
Return a list of all input, factor, flux, and state sequences of the model and the simulation sequences of all nodes.
- prepare_model(update_parameters: bool, use_conditions: Dict[str, Dict[str, float | ArrayFloat]] | None) None [source]¶
Derive the secondary parameter values, prepare all required time series and set the initial conditions.
- plot(filename: str, axis1: IOSequence | Iterable[IOSequence] | None = None, axis2: IOSequence | Iterable[IOSequence] | None = None)[source]¶
Save a plotly HTML file plotting the current test results.
- (Optional) arguments:
filename: Name of the file. If necessary, the file ending html is added automatically. The file is stored in the html_ folder of subpackage docs.
act_sequences: List of the sequences to be shown initially (deprecated).
axis1: sequences to be shown initially on the first axis.
axis2: sequences to be shown initially on the second axis.
- class hydpy.core.testtools.UnitTest(model, method, first_example=1, last_example=1, parseqs=None)[source]¶
Bases:
Test
Defines unit doctests for a single model method.
- nexts = <hydpy.core.testtools.Array object>¶
Stores arrays for setting different values of parameters and/or sequences before each new experiment.
- results = <hydpy.core.testtools.Array object>¶
Stores arrays with the resulting values of parameters and/or sequences of each new experiment.
- property nmb_examples¶
The number of examples to be calculated.
- property idx0¶
The first index of the examples selected for printing.
- property idx1¶
The last index of the examples selected for printing.
- get_output_array(parseqs)[source]¶
Return the array containing the output results of the given parameter or sequence.
- property raw_first_col_strings¶
The raw integer strings of the first column, except the header.
- class hydpy.core.testtools.Open[source]¶
Bases:
object
Replace
Open
in doctests temporarily.Class
Open
to intended to make writing to files visible and testable in docstrings. Therefore, Python’s built-in functionOpen
is temporarily replaced by another object, printing the filename and the file content, as shown in the following example:>>> import os >>> path = os.path.join("folder", "test.py") >>> from hydpy import Open >>> with Open(): ... with open(path, "w") as file_: ... file_.write("first line\n") ... file_.writelines(["\n", "third line\n"]) ~~~~~~~~~~~~~~ folder/test.py -------------- first line third line ~~~~~~~~~~~~~~
Note that, for simplicity, the UNIX style path separator / is used to print the file path on all systems.
Class
Open
is rather restricted at the moment. Functionalities like reading are not supported so far:>>> with Open(): ... with open(path, "r") as file_: ... file_.read() Traceback (most recent call last): ... NotImplementedError: Reading is not possible at the moment. Please see the documentation on class `Open` of module `testtools` for further information.
>>> with Open(): ... with open(path, "r") as file_: ... file_.readline() Traceback (most recent call last): ... NotImplementedError: Reading is not possible at the moment. Please see the documentation on class `Open` of module `testtools` for further information.
>>> with Open(): ... with open(path, "r") as file_: ... file_.readlines() Traceback (most recent call last): ... NotImplementedError: Reading is not possible at the moment. Please see the documentation on class `Open` of module `testtools` for further information.
- class hydpy.core.testtools.TestIO(clear_own: bool = False, clear_all: bool = False)[source]¶
Bases:
object
Prepare an environment for testing IO functionalities.
Primarily,
TestIO
changes the current working during the execution of with| blocks. Inspecting your current working directory,os
will likely find no file called testfile.txt:>>> import os >>> os.path.exists("testfile.txt") False
If some tests require writing such a file, this should be done within HydPy’s iotesting folder in subpackage tests, which is achieved by applying the with statement on
TestIO
:>>> from hydpy import TestIO >>> with TestIO(): ... open("testfile.txt", "w").close() ... print(os.path.exists("testfile.txt")) True
After the with block, the working directory is reset automatically:
>>> os.path.exists("testfile.txt") False
Nevertheless, testfile.txt still exists in the folder iotesting:
>>> with TestIO(): ... print(os.path.exists("testfile.txt")) True
Optionally, files and folders created within the current with block can be removed automatically by setting clear_own to
True
(modified files and folders are not affected):>>> with TestIO(clear_own=True): ... open("testfile.txt", "w").close() ... os.makedirs("testfolder") ... print(os.path.exists("testfile.txt"), ... os.path.exists("testfolder")) True True >>> with TestIO(clear_own=True): ... print(os.path.exists("testfile.txt"), ... os.path.exists("testfolder")) True False
Alternatively, all files and folders contained in folder iotesting can be removed after leaving the with block:
>>> with TestIO(clear_all=True): ... os.makedirs("testfolder") ... print(os.path.exists("testfile.txt"), ... os.path.exists("testfolder")) True True >>> with TestIO(clear_own=True): ... print(os.path.exists("testfile.txt"), ... os.path.exists("testfolder")) False False
For just clearing the iofolder, one can call method
clear()
alternatively:>>> with TestIO(): ... open("testfile.txt", "w").close() ... print(os.path.exists("testfile.txt")) True >>> TestIO.clear() >>> with TestIO(): ... print(os.path.exists("testfile.txt")) False
Note that class
TestIO
copies all eventually generated .coverage files into the test subpackage to assure no covered lines are reported as uncovered.
- hydpy.core.testtools.make_abc_testable(abstract: Type) Type [source]¶
Return a concrete version of the given abstract base class for testing purposes.
Abstract base classes cannot be (and, at least in production code, should not be) instantiated:
>>> from hydpy.core.netcdftools import NetCDFVariableBase >>> ncvar = NetCDFVariableBase() Traceback (most recent call last): ... TypeError: Can't instantiate abstract class NetCDFVariableBase with abstract methods array, read, subdevicenames
However, it is convenient to do so for testing (partly) abstract base classes in doctests. The derived class returned by function
make_abc_testable()
is identical with the original one, except that its protection against initialisation is disabled:>>> from hydpy import make_abc_testable, classname >>> ncvar = make_abc_testable(NetCDFVariableBase)(False, 1)
To avoid confusion,
make_abc_testable()
appends an underscore to the original class name:>>> classname(ncvar) 'NetCDFVariableBase_'
- hydpy.core.testtools.mock_datetime_now(testdatetime)[source]¶
Let class method
now()
of classdatetime
of moduledatetime
return the given date for testing purposes within a “with-block”.>>> import datetime >>> testdate = datetime.datetime(2000, 10, 1, 12, 30, 0, 999) >>> testdate == datetime.datetime.now() False >>> from hydpy import classname >>> classname(datetime.datetime) 'datetime' >>> from hydpy.core.testtools import mock_datetime_now >>> with mock_datetime_now(testdate): ... testdate == datetime.datetime.now() ... classname(datetime.datetime) True '_DateTime' >>> testdate == datetime.datetime.now() False >>> classname(datetime.datetime) 'datetime'
The following test shows that mocking
datetime
does not interfere with initialisingDate
objects and that the relevant exceptions are properly handled:>>> from hydpy import Date >>> with mock_datetime_now(testdate): ... Date(datetime.datetime(2000, 10, 1, 12, 30, 0, 999)) Traceback (most recent call last): ... ValueError: While trying to initialise a `Date` object based on argument `2000-10-01 12:30:00.000999`, the following error occurred: For `Date` instances, the microsecond must be zero, but for the given `datetime` object it is `999` instead.
>>> classname(datetime.datetime) 'datetime'
- class hydpy.core.testtools.NumericalDifferentiator(xsequence: sequencetools.ModelSequence, ysequences: Iterable[sequencetools.ModelSequence], methods: Iterable['modeltools.Method'], dx: float = 1e-06, method: Literal['forward', 'central', 'backward'] = 'forward')[source]¶
Bases:
object
Approximate the derivatives of
ModelSequence
values based on the finite difference approach.Class
NumericalDifferentiator
is thought for testing purposes only. See, for example, the documentation on methodCalc_RHMDH_V1
, which uses aNumericalDifferentiator
object to validate that this method calculates the derivative of sequenceRHM
(ysequence) with respect to sequenceH
(xsequence) correctly. Therefore, it must know the relationship betweenRHM
andH
, being defined by methodCalc_RHM_V1
.See also the documentation on method
Calc_AMDH_UMDH_V1
, which explains how to apply classNumericalDifferentiator
on multiple target sequences (ysequences). Note that, in order to calculate the correct derivatives of sequencesAM
andUM
, we need not only to passCalc_AM_UM_V1
, but also methodsCalc_RHM_V1
andCalc_RHV_V1
, as sequencesRHM
andRHV
, which are required for calculatingAM
andUM
, depend onH
themselves.Numerical approximations of derivatives are of limited precision.
NumericalDifferentiator
achieves the second order of accuracy due to using the coefficients given here. If results are too inaccurate, you might improve them by changing the finite difference method (backward or central instead of forward) or by changing the default interval width dx.
- hydpy.core.testtools.update_integrationtests(applicationmodel: module | str, resultfilepath: str = 'update_integrationtests.txt') None [source]¶
Write the docstring of the given application model, updated with the current simulation results, to file.
Sometimes, even tiny model-related changes bring a great deal of work concerning HydPy’s integration test strategy. For example, if you modify the value of a fixed parameter, the results of possibly dozens of integration tests of your application model might become wrong. In such situations, function
update_integrationtests()
helps you in replacing all integration tests results at once. Therefore, it calculates the new results, updates the old module docstring and writes it. You only need to copy-paste the printed result into the affected module. But be aware that functionupdate_integrationtests()
cannot guarantee the correctness of the new results. Whenever in doubt if the new results are really correct under all possible conditions, you should inspect and replace each integration test result manually.In the following example, we disable method
Pass_Outputs_V1
temporarily. Accordingly, application modelconv_v001
does not pass any output to its outlet nodes, which is why the last four columns of both integration test tables now contain zero value only (we can perform this mocking-based test in Python-mode only):>>> from hydpy import pub, TestIO, update_integrationtests >>> from unittest import mock >>> pass_output = "hydpy.models.conv.conv_model.Pass_Outputs_V1.__call__" >>> with TestIO(), pub.options.usecython(False), mock.patch(pass_output): ... update_integrationtests("conv_v001", "temp.txt") ... with open("temp.txt") as resultfile: ... print(resultfile.read()) Number of replacements: 2 Nearest-neighbour interpolation. ... test() | date | inputs | outputs | in1 | in2 | out1 | out2 | out3 | out4 | --------------------------------------------------------------------------------------------- | 2000-01-01 | 1.0 4.0 | 1.0 4.0 1.0 1.0 | 1.0 | 4.0 | 0.0 | 0.0 | 0.0 | 0.0 | | 2000-01-02 | 2.0 nan | 2.0 nan 2.0 2.0 | 2.0 | nan | 0.0 | 0.0 | 0.0 | 0.0 | | 2000-01-03 | nan nan | nan nan nan nan | nan | nan | 0.0 | 0.0 | 0.0 | 0.0 | ... test() | date | inputs | outputs | in1 | in2 | out1 | out2 | out3 | out4 | --------------------------------------------------------------------------------------------- | 2000-01-01 | 1.0 4.0 | 1.0 4.0 1.0 1.0 | 1.0 | 4.0 | 0.0 | 0.0 | 0.0 | 0.0 | | 2000-01-02 | 2.0 nan | 2.0 2.0 2.0 2.0 | 2.0 | nan | 0.0 | 0.0 | 0.0 | 0.0 | | 2000-01-03 | nan nan | nan nan nan nan | nan | nan | 0.0 | 0.0 | 0.0 | 0.0 |
- hydpy.core.testtools.check_methodorder(model: modeltools.Model, indent: int = 0) str [source]¶
Check that HydPy calls the methods of the given application model in the correct order for each simulation step.
The purpose of this function is to help model developers ensure that each method uses only the values of those sequences that have been calculated by other methods beforehand. HydPy’s test routines apply
check_methodorder()
automatically on each available application model. Alternatively, you can also execute it at the end of the docstring of an individual application model “manually”, which suppresses the automatic execution and allows to check and discuss exceptional cases wherecheck_methodorder()
generates false alarms.Function
check_methodorder()
relies on the class constants REQUIREDSEQUENCES, UPDATEDSEQUENCES, and RESULTSEQUENCES of all relevantMethod
subclasses. Hence, the correctness of its results depends on the correctness of these tuples. However, even if those tuples are well-defined, one cannot expectcheck_methodorder()
to catch all kinds of order-related errors. For example, consider the case where one method calculates only some values of a multi-dimensional sequence and another method the remaining ones.check_methodorder()
would not report anything when a third method, relying on the completeness of the sequence’s values, were called after the first but before the second method.We use the quite complex model
lland_v3
as an example.check_methodorder()
does not report any problems:>>> from hydpy.core.testtools import check_methodorder >>> from hydpy.models.lland_v3 import Model >>> print(check_methodorder(Model))
To show how
check_methodorder()
reports errors, we modify the RESULTSEQUENCES tuples of methodsCalc_TKor_V1
,Calc_DryAirPressure_V1
, andCalc_QA_V1
:>>> from hydpy.models.lland.lland_model import ( ... Calc_TKor_V1, Calc_DryAirPressure_V1, Calc_QA_V1) >>> results_tkor = Calc_TKor_V1.RESULTSEQUENCES >>> results_dryairpressure = Calc_DryAirPressure_V1.RESULTSEQUENCES >>> results_qa = Calc_QA_V1.RESULTSEQUENCES >>> Calc_TKor_V1.RESULTSEQUENCES = () >>> Calc_DryAirPressure_V1.RESULTSEQUENCES = () >>> Calc_QA_V1.RESULTSEQUENCES += results_tkor
Now, none of the relevant models calculates the value of sequence
DryAirPressure
. ForTKor
, there is still a method (Calc_QA_V1
) calculating its values, but at a too-late stage of the simulation step:>>> print(check_methodorder(Model)) Method Calc_SaturationVapourPressure_V1 requires the following sequences, which are not among the result sequences of any of its predecessors: TKor ... Method Calc_DensityAir_V1 requires the following sequences, which are not among the result sequences of any of its predecessors: TKor and DryAirPressure ... Method Calc_EvB_V2 requires the following sequences, which are not among the result sequences of any of its predecessors: TKor
To tidy up, we need to revert the above changes:
>>> Calc_TKor_V1.RESULTSEQUENCES = results_tkor >>> Calc_DryAirPressure_V1.RESULTSEQUENCES = results_dryairpressure >>> Calc_QA_V1.RESULTSEQUENCES = results_qa >>> print(check_methodorder(Model))
- hydpy.core.testtools.check_selectedvariables(method: modeltools.Method, indent: int = 0) str [source]¶
Perform consistency checks regarding the
Parameter
andSequence_
subclasses selected by the givenMethod
subclass.The purpose of this function is to help model developers ensure that the class tuples CONTROLPARAMETERS, DERIVEDPARAMETERS, FIXEDPARAMETERS, SOLVERPARAMETERS, REQUIREDSEQUENCES, UPDATEDSEQUENCES, and RESULTSEQUENCES contain the correct parameter and sequence subclasses. HydPy’s test routines apply
check_selectedvariables()
automatically on each method of each available application model. Alternatively, you can also execute it at the end of the docstring of an individualMethod
subclass “manually”, which suppresses the automatic execution and allows to check and discuss exceptional cases wherecheck_selectedvariables()
generates false alarms.Do not expect
check_selectedvariables()
to catch all possible errors. Also, false positives might occur. However, in our experience, functioncheck_selectedvariables()
is of great help to prevent the most common mistakes when defining the parameter and sequence classes relevant for a specific method.As an example, we select method
Calc_WindSpeed2m_V1
of base modellland
.check_selectedvariables()
does not reportany problems:>>> from hydpy.core.testtools import check_selectedvariables >>> from hydpy.models.lland.lland_model import ( ... Calc_WindSpeed2m_V1, Return_AdjustedWindSpeed_V1) >>> print(check_selectedvariables(Calc_WindSpeed2m_V1))
To show how
check_selectedvariables()
reports errors, we clear the RESULTSEQUENCES tuple of methodCalc_WindSpeed2m_V1
. Nowcheck_selectedvariables()
realises the usage of the flux sequence object windspeed2m within the source code of methodCalc_WindSpeed2m_V1
, which is neither available within the REQUIREDSEQUENCES, the UPDATEDSEQUENCES, nor the`RESULTSEQUENCES` tuple:>>> resultseqs = Calc_WindSpeed2m_V1.RESULTSEQUENCES >>> Calc_WindSpeed2m_V1.RESULTSEQUENCES = () >>> print(check_selectedvariables(Calc_WindSpeed2m_V1)) Definitely missing: windspeed2m
After putting the wrong flux sequence class
WindSpeed10m
into the tuple, we get an additional warning pointing to our mistake:>>> from hydpy.models.lland.lland_fluxes import WindSpeed10m >>> Calc_WindSpeed2m_V1.RESULTSEQUENCES = WindSpeed10m, >>> print(check_selectedvariables(Calc_WindSpeed2m_V1)) Definitely missing: windspeed2m Possibly erroneously selected (RESULTSEQUENCES): WindSpeed10m
Method
Calc_WindSpeed2m_V1
usesReturn_AdjustedWindSpeed_V1
as a submethod. Hence,Calc_WindSpeed2m_V1
most likely needs to select each variable selected byReturn_AdjustedWindSpeed_V1
. After adding additional variables to the DERIVEDPARAMETERS tuple ofReturn_AdjustedWindSpeed_V1
, we get another warning message:>>> from hydpy.models.lland.lland_derived import ( ... Days, Hours, Seconds) >>> derivedpars = Return_AdjustedWindSpeed_V1.DERIVEDPARAMETERS >>> Return_AdjustedWindSpeed_V1.DERIVEDPARAMETERS = Days, Hours, Seconds >>> print(check_selectedvariables(Calc_WindSpeed2m_V1)) Definitely missing: windspeed2m Possibly missing (DERIVEDPARAMETERS): Return_AdjustedWindSpeed_V1: Seconds, Hours, and Days Possibly erroneously selected (RESULTSEQUENCES): WindSpeed10m
Finally,
check_selectedvariables()
checks for duplicates both within and between the different tuples:>>> from hydpy.models.lland.lland_inputs import WindSpeed, TemL >>> requiredseqs = Calc_WindSpeed2m_V1.REQUIREDSEQUENCES >>> Calc_WindSpeed2m_V1.REQUIREDSEQUENCES = WindSpeed, WindSpeed, TemL >>> Calc_WindSpeed2m_V1.UPDATEDSEQUENCES = TemL, >>> print(check_selectedvariables(Calc_WindSpeed2m_V1)) Definitely missing: windspeed2m Possibly missing (DERIVEDPARAMETERS): Return_AdjustedWindSpeed_V1: Seconds, Hours, and Days Possibly erroneously selected (REQUIREDSEQUENCES): TemL Possibly erroneously selected (UPDATEDSEQUENCES): TemL Possibly erroneously selected (RESULTSEQUENCES): WindSpeed10m Duplicates: TemL and WindSpeed
To tidy up, we need to revert the above changes:
>>> Calc_WindSpeed2m_V1.RESULTSEQUENCES = resultseqs >>> Return_AdjustedWindSpeed_V1.DERIVEDPARAMETERS = derivedpars >>> Calc_WindSpeed2m_V1.REQUIREDSEQUENCES = requiredseqs >>> Calc_WindSpeed2m_V1.UPDATEDSEQUENCES = () >>> print(check_selectedvariables(Calc_WindSpeed2m_V1))
Some methods such as
Pick_Q_V1
of base modelarma
rely on the len attribute of 1-dimensional sequences. Functioncheck_selectedvariables()
does not report false alarms in such cases:>>> from hydpy.models.arma.arma_model import Pick_Q_V1 >>> print(check_selectedvariables(Pick_Q_V1))
Some methods such as
Update_ESnow_V1
of base modellland
update a sequence (meaning, they require its old value and calculate a new one), but their submethods (in this caseReturn_BackwardEulerError_V1
) just require them as input. Functioncheck_selectedvariables()
does not report false alarms in such cases:>>> from hydpy.models.lland.lland_model import Update_ESnow_V1 >>> print(check_selectedvariables(Update_ESnow_V1))
- hydpy.core.testtools.perform_consistencychecks(applicationmodel=typing.Union[module, str], indent: int = 0) str [source]¶
Perform all available consistency checks for the given application model.
At the moment, function
perform_consistencychecks()
calls functioncheck_selectedvariables()
for each relevant model method and functioncheck_methodorder()
for the application model itself. Note thatperform_consistencychecks()
executes only those checks not already executed in the doctest of the respective method or model. This alternative allows model developers to perform the tests themselves whenever exceptional cases result in misleading error reports and discuss any related potential pitfalls in the official documentation.As an example, we apply
perform_consistencychecks()
on the application modellland_v3
. It does not report any potential problems (not already discussed in the documentation on the individual model methods):>>> from hydpy.core.testtools import perform_consistencychecks >>> print(perform_consistencychecks("lland_v3"))
To show how
perform_consistencychecks()
reports errors, we modify the RESULTSEQUENCES tuple of methodCalc_DryAirPressure_V1
:>>> from hydpy.models.lland.lland_model import ( ... Calc_DryAirPressure_V1) >>> results_dryairpressure = Calc_DryAirPressure_V1.RESULTSEQUENCES >>> Calc_DryAirPressure_V1.RESULTSEQUENCES = () >>> print(perform_consistencychecks("lland_v3")) Potential consistency problems for individual methods: Method Calc_DryAirPressure_V1: Definitely missing: dryairpressure Potential consistency problems between methods: Method Calc_DensityAir_V1 requires the following sequences, which are not among the result sequences of any of its predecessors: DryAirPressure
To tidy up, we need to revert the above changes:
>>> Calc_DryAirPressure_V1.RESULTSEQUENCES = results_dryairpressure >>> print(perform_consistencychecks("lland_v3"))
- hydpy.core.testtools.save_autofig(filename: str, figure: Figure | None = None) None [source]¶
Save a figure automatically generated during testing in the special autofig sub-package so that Sphinx can include it into the documentation later.
When passing no figure, function
save_autofig()
takes the currently active one.
- hydpy.core.testtools.warn_later() Iterator[None] [source]¶
Suppress warnings and print them upon exit.
The context manager
warn_later()
helps demonstrate functionalities in doctests that emit warnings:>>> import warnings >>> def get_number(): ... warnings.warn("This is a warning.") ... return 1
>>> get_number() Traceback (most recent call last): ... UserWarning: This is a warning.
>>> from hydpy.core.testtools import warn_later >>> with warn_later(): ... get_number() 1 UserWarning: This is a warning.