diff --git a/esmvalcore/_task.py b/esmvalcore/_task.py index 9312b7497e..7975d983d8 100644 --- a/esmvalcore/_task.py +++ b/esmvalcore/_task.py @@ -8,6 +8,7 @@ import pprint import subprocess import sys +import textwrap import threading import time from copy import deepcopy @@ -264,13 +265,10 @@ def get_product_attributes(self) -> dict: for product in self.products } - def str(self): + def print_ancestors(self): """Return a nicely formatted description.""" - def _indent(txt): - return '\n'.join('\t' + line for line in txt.split('\n')) - txt = 'ancestors:\n{}'.format('\n\n'.join( - _indent(str(task)) + textwrap.indent(str(task), prefix=' ') for task in self.ancestors) if self.ancestors else 'None') return txt @@ -642,8 +640,8 @@ def __repr__(self): settings_string = pprint.pformat(self.settings) string = (f"{self.__class__.__name__}: {self.name}\n" f"script: {self.script}\n" - f"settings:\n{settings_string}\n") - + f"settings:\n{settings_string}\n" + f"{self.print_ancestors()}\n") return string diff --git a/esmvalcore/preprocessor/__init__.py b/esmvalcore/preprocessor/__init__.py index a4359e209d..1be87e59af 100644 --- a/esmvalcore/preprocessor/__init__.py +++ b/esmvalcore/preprocessor/__init__.py @@ -488,10 +488,11 @@ def __str__(self): ] products = '\n\n'.join('\n'.join([str(p), pformat(p.settings)]) for p in self.products) - txt = "{}:\norder: {}\n{}\n{}".format( + txt = "{}: {}\norder: {}\n{}\n{}".format( self.__class__.__name__, + self.name, order, products, - super(PreprocessingTask, self).str(), + self.print_ancestors(), ) return txt diff --git a/tests/unit/task/test_print.py b/tests/unit/task/test_print.py new file mode 100644 index 0000000000..0db6b32423 --- /dev/null +++ b/tests/unit/task/test_print.py @@ -0,0 +1,159 @@ +"""Test that a task tree can be printed in a human readable form.""" +import copy +import textwrap + +import pytest + +from esmvalcore._task import DiagnosticTask +from esmvalcore.preprocessor import PreprocessingTask, PreprocessorFile + + +@pytest.fixture +def preproc_file(): + return PreprocessorFile( + attributes={'filename': '/output/preproc/file.nc'}, + settings={ + 'extract_levels': { + 'scheme': 'linear', + 'levels': [95000] + }, + }, + ) + + +@pytest.fixture +def preproc_task(preproc_file): + return PreprocessingTask(products=[preproc_file]) + + +@pytest.fixture +def diagnostic_task(tmp_path): + mock_script = tmp_path / 'script.py' + mock_script.touch() + settings = { + 'run_dir': str('/output/run'), + 'profile_diagnostic': False, + } + task = DiagnosticTask(mock_script, settings, output_dir='/output/run') + task.script = '/some/where/esmvaltool/diag_scripts/test.py' + return task + + +def test_repr_preproc_task(preproc_task): + """Test printing a preprocessor task.""" + preproc_task.name = 'diag_1/tas' + result = str(preproc_task) + print(result) + + reference = textwrap.dedent(""" + PreprocessingTask: diag_1/tas + order: ['extract_levels', 'save'] + PreprocessorFile: /output/preproc/file.nc + {'extract_levels': {'levels': [95000], 'scheme': 'linear'}, + 'save': {'filename': '/output/preproc/file.nc'}} + ancestors: + None + """) + + assert result.strip() == reference.strip() + + +def test_repr_diagnostic_task(diagnostic_task): + """Test printing a diagnostic task.""" + diagnostic_task.name = 'diag_1/script_1' + result = str(diagnostic_task) + print(result) + + reference = textwrap.dedent(""" + DiagnosticTask: diag_1/script_1 + script: /some/where/esmvaltool/diag_scripts/test.py + settings: + {'profile_diagnostic': False, 'run_dir': '/output/run'} + ancestors: + None + """) + + assert result.strip() == reference.strip() + + +def test_repr_simple_tree(preproc_task, diagnostic_task): + """Test the most common task tree.""" + preproc_task.name = 'diag_1/tas' + diagnostic_task.name = 'diag_1/script_1' + diagnostic_task.ancestors = [preproc_task] + result = str(diagnostic_task) + print(result) + + reference = textwrap.dedent(""" + DiagnosticTask: diag_1/script_1 + script: /some/where/esmvaltool/diag_scripts/test.py + settings: + {'profile_diagnostic': False, 'run_dir': '/output/run'} + ancestors: + PreprocessingTask: diag_1/tas + order: ['extract_levels', 'save'] + PreprocessorFile: /output/preproc/file.nc + {'extract_levels': {'levels': [95000], 'scheme': 'linear'}, + 'save': {'filename': '/output/preproc/file.nc'}} + ancestors: + None + """) + + assert result.strip() == reference.strip() + + +def test_repr_full_tree(preproc_task, diagnostic_task): + """Test a more comlicated task tree.""" + derive_input_task_1 = copy.deepcopy(preproc_task) + derive_input_task_1.name = 'diag_1/tas_derive_input_1' + + derive_input_task_2 = copy.deepcopy(preproc_task) + derive_input_task_2.name = 'diag_1/tas_derive_input_2' + + preproc_task.name = 'diag_1/tas' + preproc_task.ancestors = [derive_input_task_1, derive_input_task_2] + + diagnostic_task_1 = copy.deepcopy(diagnostic_task) + diagnostic_task_1.name = 'diag_1/script_1' + diagnostic_task_1.ancestors = [preproc_task] + + diagnostic_task.name = 'diag_1/script_2' + diagnostic_task.ancestors = [diagnostic_task_1] + result = str(diagnostic_task) + print(result) + + reference = textwrap.dedent(""" + DiagnosticTask: diag_1/script_2 + script: /some/where/esmvaltool/diag_scripts/test.py + settings: + {'profile_diagnostic': False, 'run_dir': '/output/run'} + ancestors: + DiagnosticTask: diag_1/script_1 + script: /some/where/esmvaltool/diag_scripts/test.py + settings: + {'profile_diagnostic': False, 'run_dir': '/output/run'} + ancestors: + PreprocessingTask: diag_1/tas + order: ['extract_levels', 'save'] + PreprocessorFile: /output/preproc/file.nc + {'extract_levels': {'levels': [95000], 'scheme': 'linear'}, + 'save': {'filename': '/output/preproc/file.nc'}} + ancestors: + PreprocessingTask: diag_1/tas_derive_input_1 + order: ['extract_levels', 'save'] + PreprocessorFile: /output/preproc/file.nc + {'extract_levels': {'levels': [95000], 'scheme': 'linear'}, + 'save': {'filename': '/output/preproc/file.nc'}} + ancestors: + None + + PreprocessingTask: diag_1/tas_derive_input_2 + order: ['extract_levels', 'save'] + PreprocessorFile: /output/preproc/file.nc + {'extract_levels': {'levels': [95000], 'scheme': 'linear'}, + 'save': {'filename': '/output/preproc/file.nc'}} + ancestors: + None + """) + + assert result.strip() == reference.strip()