ESMValGroup / ESMValCore

ESMValCore: A community tool for pre-processing data from Earth system models in CMIP and running analysis scripts.
https://www.esmvaltool.org
Apache License 2.0
42 stars 38 forks source link

Error message is broken when non-existent rootpath is used from Python API #1568

Closed bouweandela closed 1 year ago

bouweandela commented 2 years ago

To reproduce (with v2.5):

import esmvalcore.experimental as esmvaltool
esmvaltool.CFG

gives

Config({'auxiliary_data_dir': PosixPath('/home/bandela/auxiliary_data'),
        'compress_netcdf': False,
        'config_developer_file': None,
        'download_dir': PosixPath('/home/bandela/climate_data'),
        'drs': {'CMIP3': 'ESGF',
                'CMIP5': 'ESGF',
                'CMIP6': 'ESGF',
                'CORDEX': 'ESGF',
                'obs4MIPs': 'ESGF'},
        'exit_on_warning': False,
        'extra_facets_dir': (),
        'log_level': 'info',
        'max_parallel_tasks': None,
        'offline': True,
        'output_dir': PosixPath('/home/bandela/esmvaltool_output'),
        'output_file_type': 'png',
        'profile_diagnostic': False,
        'remove_preproc_dir': True,
        'resume_from': [],
        'rootpath': {'default': [PosixPath('/home/bandela/climate_data')]},
        'save_intermediary_cubes': False})

(note that /home/bandela/climate_data does not exist)

all_recipes = esmvaltool.get_all_recipes()
examples = all_recipes.find("example")
recipe_python = examples[2]
output = recipe_python.run()

results in

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Input In [6], in <cell line: 1>()
----> 1 output = recipe_python.run()

File /srv/conda/envs/notebook/lib/python3.10/site-packages/esmvalcore/experimental/recipe.py:134, in Recipe.run(self, task, session)
    131     session['diagnostics'] = task
    133 with log_to_dir(session.run_dir):
--> 134     self._engine = self._load(session=session)
    135     self._engine.run()
    137 shutil.copy2(self.path, session.run_dir)

File /srv/conda/envs/notebook/lib/python3.10/site-packages/esmvalcore/experimental/recipe.py:99, in Recipe._load(self, session)
     95 config_user = session.to_config_user()
     97 logger.info(pprint.pformat(config_user))
---> 99 return RecipeEngine(raw_recipe=self.data,
    100                     config_user=config_user,
    101                     recipe_file=self.path)

File /srv/conda/envs/notebook/lib/python3.10/site-packages/esmvalcore/_recipe.py:1244, in Recipe.__init__(self, raw_recipe, config_user, initialize_tasks, recipe_file)
   1241 self.entity = self._initialize_provenance(
   1242     raw_recipe.get('documentation', {}))
   1243 try:
-> 1244     self.tasks = self.initialize_tasks() if initialize_tasks else None
   1245 except RecipeError as exc:
   1246     self._log_recipe_errors(exc)

File /srv/conda/envs/notebook/lib/python3.10/site-packages/esmvalcore/_recipe.py:1853, in Recipe.initialize_tasks(self)
   1851 def initialize_tasks(self):
   1852     """Define tasks in recipe."""
-> 1853     tasks = self._create_tasks()
   1854     tasks = tasks.flatten()
   1855     logger.info("These tasks will be executed: %s",
   1856                 ', '.join(t.name for t in tasks))

File /srv/conda/envs/notebook/lib/python3.10/site-packages/esmvalcore/_recipe.py:1828, in Recipe._create_tasks(self)
   1825     priority += 1
   1827 # Create preprocessor tasks
-> 1828 new_tasks, failed = self._create_preprocessor_tasks(
   1829     diagnostic_name, diagnostic, tasknames_to_run,
   1830     any_diag_script_is_run)
   1831 failed_tasks.extend(failed)
   1832 for task in new_tasks:

File /srv/conda/envs/notebook/lib/python3.10/site-packages/esmvalcore/_recipe.py:1788, in Recipe._create_preprocessor_tasks(self, diagnostic_name, diagnostic, tasknames_to_run, any_diag_script_is_run)
   1786 logger.info("Creating preprocessor task %s", task_name)
   1787 try:
-> 1788     task = _get_preprocessor_task(
   1789         variables=diagnostic['preprocessor_output']
   1790         [variable_group],
   1791         profiles=self._preprocessors,
   1792         config_user=self._cfg,
   1793         task_name=task_name,
   1794     )
   1795 except RecipeError as ex:
   1796     failed_tasks.append(ex)

File /srv/conda/envs/notebook/lib/python3.10/site-packages/esmvalcore/_recipe.py:1203, in _get_preprocessor_task(variables, profiles, config_user, task_name)
   1200         derive_tasks.append(task)
   1202 # Create (final) preprocessor task
-> 1203 task = _get_single_preprocessor_task(
   1204     variables,
   1205     profile,
   1206     config_user,
   1207     ancestor_tasks=derive_tasks,
   1208     name=task_name,
   1209 )
   1211 return task

File /srv/conda/envs/notebook/lib/python3.10/site-packages/esmvalcore/_recipe.py:1058, in _get_single_preprocessor_task(variables, profile, config_user, name, ancestor_tasks)
   1055     check.check_for_temporal_preprocs(profile)
   1056     ancestor_products = None
-> 1058 products = _get_preprocessor_products(
   1059     variables=variables,
   1060     profile=profile,
   1061     order=order,
   1062     ancestor_products=ancestor_products,
   1063     config_user=config_user,
   1064     name=name,
   1065 )
   1067 if not products:
   1068     raise RecipeError(
   1069         "Did not find any input data for task {}".format(name))

File /srv/conda/envs/notebook/lib/python3.10/site-packages/esmvalcore/_recipe.py:957, in _get_preprocessor_products(variables, profile, order, ancestor_products, config_user, name)
    955 if not ancestors:
    956     try:
--> 957         ancestors = _get_ancestors(variable, config_user)
    958     except RecipeError as ex:
    959         if _allow_skipping(ancestors, variable, config_user):

File /srv/conda/envs/notebook/lib/python3.10/site-packages/esmvalcore/_recipe.py:622, in _get_ancestors(variable, config_user)
    619 def _get_ancestors(variable, config_user):
    620     """Get the input files for a single dataset and setup provenance."""
    621     (input_files, dirnames,
--> 622      filenames) = _get_input_files(variable, config_user)
    624     logger.debug(
    625         "Using input files for variable %s of dataset %s:\n%s",
    626         variable['short_name'],
   (...)
    630             for f in input_files),
    631     )
    632     check.data_availability(input_files, variable, dirnames, filenames)

File /srv/conda/envs/notebook/lib/python3.10/site-packages/esmvalcore/_recipe.py:588, in _get_input_files(variable, config_user)
    585     variable['start_year'] = start_year
    586     variable['end_year'] = end_year
    587 (input_files, dirnames,
--> 588  filenames) = get_input_filelist(variable=variable,
    589                                  rootpath=config_user['rootpath'],
    590                                  drs=config_user['drs'])
    592 # Set up downloading from ESGF if requested.
    593 if (not config_user['offline']
    594         and variable['project'] in esgf.facets.FACETS):

File /srv/conda/envs/notebook/lib/python3.10/site-packages/esmvalcore/_data_finder.py:498, in get_input_filelist(variable, rootpath, drs)
    496 if variable['project'] == 'CMIP5' and variable['frequency'] == 'fx':
    497     variable['ensemble'] = 'r0i0p0'
--> 498 (files, dirnames, filenames) = _find_input_files(variable, rootpath, drs)
    499 # do time gating only for non-fx variables
    500 if variable['frequency'] != 'fx':

File /srv/conda/envs/notebook/lib/python3.10/site-packages/esmvalcore/_data_finder.py:485, in _find_input_files(variable, rootpath, drs)
    483 short_name = variable['short_name']
    484 variable['short_name'] = variable['original_short_name']
--> 485 input_dirs = _find_input_dirs(variable, rootpath, drs)
    486 filenames_glob = _get_filenames_glob(variable, drs)
    487 files = find_files(input_dirs, filenames_glob)

File /srv/conda/envs/notebook/lib/python3.10/site-packages/esmvalcore/_data_finder.py:449, in _find_input_dirs(variable, rootpath, drs)
    446 """Return a the full paths to input directories."""
    447 project = variable['project']
--> 449 root = get_rootpath(rootpath, project)
    450 path_template = _select_drs('input_dir', drs, project)
    452 dirnames = []

File /srv/conda/envs/notebook/lib/python3.10/site-packages/esmvalcore/_data_finder.py:439, in get_rootpath(rootpath, project)
    434 nonexistent = tuple(p for p in rootpath[key]
    435                     if not os.path.exists(p))
    436 if nonexistent and (key, nonexistent) not in ROOTPATH_WARNED:
    437     logger.warning(
    438         "'%s' rootpaths '%s' set in config-user.yml do not exist",
--> 439         key, ', '.join(nonexistent))
    440     ROOTPATH_WARNED.add((key, nonexistent))
    441 return rootpath[key]

TypeError: sequence item 0: expected str instance, PosixPath found
bouweandela commented 2 years ago

Related error, it looks like this happens if the output directory does not exist:

---------------------------------------------------------------------------
FileNotFoundError                         Traceback (most recent call last)
File ~/.conda/envs/esmvaltool/lib/python3.10/pathlib.py:1173, in Path.mkdir(self, mode, parents, exist_ok)
   1172 try:
-> 1173     self._accessor.mkdir(self, mode)
   1174 except FileNotFoundError:

FileNotFoundError: [Errno 2] No such file or directory: '/work/scratch-pw/bandela/esmvaltool_output/recipe_python_20220429_152839/run'

During handling of the above exception, another exception occurred:

FileNotFoundError                         Traceback (most recent call last)
File ~/.conda/envs/esmvaltool/lib/python3.10/pathlib.py:1173, in Path.mkdir(self, mode, parents, exist_ok)
   1172 try:
-> 1173     self._accessor.mkdir(self, mode)
   1174 except FileNotFoundError:

FileNotFoundError: [Errno 2] No such file or directory: '/work/scratch-pw/bandela/esmvaltool_output/recipe_python_20220429_152839'

During handling of the above exception, another exception occurred:

FileNotFoundError                         Traceback (most recent call last)
File ~/.conda/envs/esmvaltool/lib/python3.10/pathlib.py:1173, in Path.mkdir(self, mode, parents, exist_ok)
   1172 try:
-> 1173     self._accessor.mkdir(self, mode)
   1174 except FileNotFoundError:

FileNotFoundError: [Errno 2] No such file or directory: '/work/scratch-pw/bandela/esmvaltool_output'

During handling of the above exception, another exception occurred:

FileNotFoundError                         Traceback (most recent call last)
File ~/.conda/envs/esmvaltool/lib/python3.10/pathlib.py:1173, in Path.mkdir(self, mode, parents, exist_ok)
   1172 try:
-> 1173     self._accessor.mkdir(self, mode)
   1174 except FileNotFoundError:

FileNotFoundError: [Errno 2] No such file or directory: '/work/scratch-pw/bandela'

During handling of the above exception, another exception occurred:

FileNotFoundError                         Traceback (most recent call last)
File ~/.conda/envs/esmvaltool/lib/python3.10/pathlib.py:1173, in Path.mkdir(self, mode, parents, exist_ok)
   1172 try:
-> 1173     self._accessor.mkdir(self, mode)
   1174 except FileNotFoundError:

FileNotFoundError: [Errno 2] No such file or directory: '/work/scratch-pw'

During handling of the above exception, another exception occurred:

PermissionError                           Traceback (most recent call last)
Input In [6], in <cell line: 1>()
----> 1 output = example_recipe.run()

File ~/.conda/envs/esmvaltool/lib/python3.10/site-packages/esmvalcore/experimental/recipe.py:133, in Recipe.run(self, task, session)
    130 if task:
    131     session['diagnostics'] = task
--> 133 with log_to_dir(session.run_dir):
    134     self._engine = self._load(session=session)
    135     self._engine.run()

File ~/.conda/envs/esmvaltool/lib/python3.10/contextlib.py:135, in _GeneratorContextManager.__enter__(self)
    133 del self.args, self.kwds, self.func
    134 try:
--> 135     return next(self.gen)
    136 except StopIteration:
    137     raise RuntimeError("generator didn't yield") from None

File ~/.conda/envs/esmvaltool/lib/python3.10/site-packages/esmvalcore/experimental/_logging.py:30, in log_to_dir(drc)
      8 @contextmanager
      9 def log_to_dir(drc: Path):
     10     """Log messages to the specified directory.
     11 
     12     This is a context manager to temporarily redirect the logging when
   (...)
     28         Location where the logs should be stored.
     29     """
---> 30     drc.mkdir(parents=True, exist_ok=True)
     32     # create file handler which logs even debug messages
     33     debug_log_file = logging.FileHandler(drc / 'main_log_debug.txt')

File ~/.conda/envs/esmvaltool/lib/python3.10/pathlib.py:1177, in Path.mkdir(self, mode, parents, exist_ok)
   1175     if not parents or self.parent == self:
   1176         raise
-> 1177     self.parent.mkdir(parents=True, exist_ok=True)
   1178     self.mkdir(mode, parents=False, exist_ok=exist_ok)
   1179 except OSError:
   1180     # Cannot rely on checking for EEXIST, since the operating system
   1181     # could give priority to other errors like EACCES or EROFS

File ~/.conda/envs/esmvaltool/lib/python3.10/pathlib.py:1177, in Path.mkdir(self, mode, parents, exist_ok)
   1175     if not parents or self.parent == self:
   1176         raise
-> 1177     self.parent.mkdir(parents=True, exist_ok=True)
   1178     self.mkdir(mode, parents=False, exist_ok=exist_ok)
   1179 except OSError:
   1180     # Cannot rely on checking for EEXIST, since the operating system
   1181     # could give priority to other errors like EACCES or EROFS

    [... skipping similar frames: Path.mkdir at line 1177 (2 times)]

File ~/.conda/envs/esmvaltool/lib/python3.10/pathlib.py:1177, in Path.mkdir(self, mode, parents, exist_ok)
   1175     if not parents or self.parent == self:
   1176         raise
-> 1177     self.parent.mkdir(parents=True, exist_ok=True)
   1178     self.mkdir(mode, parents=False, exist_ok=exist_ok)
   1179 except OSError:
   1180     # Cannot rely on checking for EEXIST, since the operating system
   1181     # could give priority to other errors like EACCES or EROFS

File ~/.conda/envs/esmvaltool/lib/python3.10/pathlib.py:1173, in Path.mkdir(self, mode, parents, exist_ok)
   1169 """
   1170 Create a new directory at this given path.
   1171 """
   1172 try:
-> 1173     self._accessor.mkdir(self, mode)
   1174 except FileNotFoundError:
   1175     if not parents or self.parent == self:

PermissionError: [Errno 13] Permission denied: '/work'