Skip to content

Commit

Permalink
Merge branch 'main' into update_adjust_time
Browse files Browse the repository at this point in the history
  • Loading branch information
malininae authored Aug 23, 2024
2 parents debd788 + 343368e commit edf1878
Show file tree
Hide file tree
Showing 22 changed files with 151 additions and 814 deletions.
193 changes: 96 additions & 97 deletions conda-linux-64.lock

Large diffs are not rendered by default.

108 changes: 0 additions & 108 deletions doc/quickstart/configure.rst
Original file line number Diff line number Diff line change
Expand Up @@ -440,71 +440,6 @@ corresponding command line arguments ``--search_esgf=when_missing`` or
tool by pressing the ``Ctrl`` and ``C`` keys on your keyboard simultaneously
several times, edit the recipe so it contains fewer datasets and try again.

For downloading some files, you may need to log in to be able to download the
data.

See the
`ESGF user guide <https://esgf.github.io/esgf-user-support/user_guide.html>`_
for instructions on how to create an ESGF OpenID account if you do not have
one yet.
Note that the OpenID account consists of 3 components instead of the usual
two, in addition a username and password you also need the hostname of the
provider of the ID; for example
`esgf-data.dkrz.de <https://esgf-data.dkrz.de/user/add/?next=http://esgf-data.dkrz.de/projects/esgf-dkrz/>`_.
Even though the account is issued by a particular host, the same OpenID
account can be used to download data from all hosts in the ESGF.

Next, configure your system so the ``esmvaltool`` can use your credentials.
This can be done using the keyring_ package or they can be stored in a
:ref:`configuration file <config_esgf_pyclient>`.

.. _keyring:

Storing credentials in keyring
------------------------------
First install the keyring package. Note that this requires a supported
backend that may not be available on compute clusters, see the
`keyring documentation <https://pypi.org/project/keyring>`__ for more
information.

.. code-block:: bash
pip install keyring
Next, set your username and password by running the commands:

.. code-block:: bash
keyring set ESGF hostname
keyring set ESGF username
keyring set ESGF password
for example, if you created an account on the host `esgf-data.dkrz.de`_ with username
'cookiemonster' and password 'Welcome01', run the command

.. code-block:: bash
keyring set ESGF hostname
this will display the text

.. code-block:: bash
Password for 'hostname' in 'ESGF':
type ``esgf-data.dkrz.de`` (the characters will not be shown) and press ``Enter``.
Repeat the same procedure with ``keyring set ESGF username``, type ``cookiemonster``
and press ``Enter`` and ``keyring set ESGF password``, type ``Welcome01`` and
press ``Enter``.
To check that you entered your credentials correctly, run:
.. code-block:: bash
keyring get ESGF hostname
keyring get ESGF username
keyring get ESGF password
.. _config_esgf_pyclient:

Configuration file
Expand All @@ -514,49 +449,6 @@ An optional configuration file can be created for configuring how the tool uses
to find and download data.
The name of this file is ``~/.esmvaltool/esgf-pyclient.yml``.

Logon
`````
In the ``logon`` section you can provide arguments that will be passed on to
:py:meth:`pyesgf.logon.LogonManager.logon`.
For example, you can store the hostname, username, and password or your OpenID
account in the file like this:
.. code-block:: yaml
logon:
hostname: "your-hostname"
username: "your-username"
password: "your-password"
for example
.. code-block:: yaml
logon:
hostname: "esgf-data.dkrz.de"
username: "cookiemonster"
password: "Welcome01"
if you created an account on the host `esgf-data.dkrz.de`_ with username
'cookiemonster' and password 'Welcome01'.
Alternatively, you can configure an interactive log in:
.. code-block:: yaml
logon:
interactive: true
Note that storing your password in plain text in the configuration
file is less secure.
On shared systems, make sure the permissions of the file are set so
only you and administrators can read it, i.e.
.. code-block:: bash
ls -l ~/.esmvaltool/esgf-pyclient.yml
shows permissions ``-rw-------``.
Search
``````
Any arguments to :py:obj:`pyesgf.search.connection.SearchConnection` can
Expand Down
2 changes: 1 addition & 1 deletion environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ dependencies:
- fire
- geopy
- humanfriendly
- iris >=3.9.0
- iris >=3.10.0
- iris-esmf-regrid >=0.10.0 # github.com/SciTools-incubator/iris-esmf-regrid/pull/342
- iris-grib
- isodate
Expand Down
4 changes: 0 additions & 4 deletions esmvalcore/_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -444,10 +444,6 @@ def _run(self, recipe: Path, session) -> None:
console_log_level=session['log_level'])
self._log_header(session['config_file'], log_files)

if session['search_esgf'] != 'never':
from .esgf._logon import logon
logon()

# configure resource logger and run program
from ._task import resource_usage_logger
resource_log = session.run_dir / 'resource_usage.txt'
Expand Down
7 changes: 4 additions & 3 deletions esmvalcore/cmor/_fixes/cmip6/awi_esm_1_1_lr.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@ def fix_metadata(self, cubes):
bad_value = 'days since 0000-01-01 00:00:00'
for cube in cubes:
try:
if cube.attributes[parent_units] == bad_value:
cube.attributes[parent_units] = 'days since 0001-01-01 ' \
+ '00:00:00'
if parent_units in cube.attributes:
if cube.attributes[parent_units] == bad_value:
cube.attributes[parent_units] = \
'days since 0001-01-01 00:00:00'
except AttributeError:
pass
return cubes
40 changes: 20 additions & 20 deletions esmvalcore/cmor/_fixes/icon/_base_fixes.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import requests
from iris import NameConstraint
from iris.cube import Cube, CubeList
from iris.experimental.ugrid import Connectivity, Mesh
from iris.mesh import Connectivity, MeshXY

from esmvalcore.cmor._fixes.native_datasets import NativeDatasetFix
from esmvalcore.local import _get_data_sources
Expand All @@ -38,24 +38,24 @@ def __init__(self, *args, **kwargs):
self._horizontal_grids = {}
self._meshes = {}

def _create_mesh(self, cube):
def _create_mesh(self, cube: Cube) -> MeshXY:
"""Create mesh from horizontal grid file.
Note
----
This functions creates a new :class:`iris.experimental.ugrid.Mesh` from
the ``clat`` (already present in the cube), ``clon`` (already present
in the cube), ``vertex_index``, ``vertex_of_cell``, ``vlat``, and
``vlon`` variables of the horizontal grid file.
We do not use :func:`iris.experimental.ugrid.Mesh.from_coords` with the
existing latitude and longitude coordinates here because this would
produce lots of duplicated entries for the node coordinates. The reason
for this is that the node coordinates are constructed from the bounds;
since each node is contained 6 times in the bounds array (each node is
shared by 6 neighboring cells) the number of nodes is 6 times higher
with :func:`iris.experimental.ugrid.Mesh.from_coords` compared to using
the information already present in the horizontal grid file.
This functions creates a new :class:`iris.mesh.MeshXY` from the
``clat`` (already present in the cube), ``clon`` (already present in
the cube), ``vertex_index``, ``vertex_of_cell``, ``vlat``, and ``vlon``
variables of the horizontal grid file.
We do not use :func:`iris.mesh.MeshXY.from_coords` with the existing
latitude and longitude coordinates here because this would produce lots
of duplicated entries for the node coordinates. The reason for this is
that the node coordinates are constructed from the bounds; since each
node is contained 6 times in the bounds array (each node is shared by 6
neighboring cells) the number of nodes is 6 times higher with
:func:`iris.mesh.MeshXY.from_coords` compared to using the information
already present in the horizontal grid file.
"""
horizontal_grid = self.get_horizontal_grid(cube)
Expand Down Expand Up @@ -91,7 +91,7 @@ def _create_mesh(self, cube):
if not np.allclose(
face_lat.bounds,
node_lat.points[conn_node_inds],
**close_kwargs,
**close_kwargs, # type: ignore
):
logger.warning(
"Latitude bounds of the face coordinate ('clat_vertices' in "
Expand All @@ -108,7 +108,7 @@ def _create_mesh(self, cube):
# differ by 360°, which is also okay.
face_lon_bounds_to_check = face_lon.bounds % 360
node_lon_conn_to_check = node_lon.points[conn_node_inds] % 360
idx_notclose = ~np.isclose(
idx_notclose = ~np.isclose( # type: ignore
face_lon_bounds_to_check,
node_lon_conn_to_check,
**close_kwargs,
Expand All @@ -131,7 +131,7 @@ def _create_mesh(self, cube):
start_index=start_index,
location_axis=0,
)
mesh = Mesh(
mesh = MeshXY(
topology_dimension=2,
node_coords_and_axes=[(node_lat, 'y'), (node_lon, 'x')],
connectivities=[connectivity],
Expand Down Expand Up @@ -429,8 +429,8 @@ def get_mesh(self, cube):
Returns
-------
iris.experimental.ugrid.Mesh
Mesh.
iris.mesh.MeshXY
Mesh of the cube.
Raises
------
Expand Down
44 changes: 1 addition & 43 deletions esmvalcore/config/_esgf_pyclient.py
Original file line number Diff line number Diff line change
@@ -1,53 +1,20 @@
"""esgf-pyclient configuration.
The configuration is read from the file ~/.esmvaltool/esgf-pyclient.yml.
There are four sections in the configuration file:
logon: contains keyword arguments to :func:`pyesgf.logon.LogonManager.logon`
search_connection: contains keyword arguments to
:class:`pyesgf.search.connection.SearchConnection`
"""
import importlib
import logging
import os
import stat
from functools import lru_cache
from pathlib import Path
from types import ModuleType
from typing import Optional

import yaml

keyring: Optional[ModuleType] = None
try:
keyring = importlib.import_module('keyring')
except ModuleNotFoundError:
pass

logger = logging.getLogger(__name__)

CONFIG_FILE = Path.home() / '.esmvaltool' / 'esgf-pyclient.yml'


def get_keyring_credentials():
"""Load credentials from keyring."""
logon = {}
if keyring is None:
return logon

for key in ['hostname', 'username', 'password']:
try:
value = keyring.get_password('ESGF', key)
except keyring.errors.NoKeyringError:
# No keyring backend is available
return logon
if value is not None:
logon[key] = value

return logon


def read_config_file():
"""Read the configuration from file."""
if CONFIG_FILE.exists():
Expand Down Expand Up @@ -77,12 +44,6 @@ def read_config_file():
def load_esgf_pyclient_config():
"""Load the esgf-pyclient configuration."""
cfg = {
# Arguments to
# https://esgf-pyclient.readthedocs.io/en/latest/api.html#pyesgf.logon.LogonManager.logon
'logon': {
'interactive': False,
'bootstrap': True,
},
# Arguments to
# https://esgf-pyclient.readthedocs.io/en/latest/api.html#pyesgf.search.connection.SearchConnection
'search_connection': {
Expand All @@ -105,11 +66,8 @@ def load_esgf_pyclient_config():
},
}

keyring_cfg = get_keyring_credentials()
cfg['logon'].update(keyring_cfg)

file_cfg = read_config_file()
for section in ['logon', 'search_connection']:
for section in ['search_connection']:
cfg[section].update(file_cfg.get(section, {}))

if 'cache' in cfg['search_connection']:
Expand Down
6 changes: 1 addition & 5 deletions esmvalcore/esgf/_download.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
from esmvalcore.typing import Facets

from ..local import LocalFile
from ._logon import get_credentials
from .facets import DATASET_MAP, FACETS

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -460,10 +459,7 @@ def _download(self, local_file, url):

logger.debug("Downloading %s to %s", url, tmp_file)
start_time = datetime.datetime.now()
response = requests.get(url,
stream=True,
timeout=TIMEOUT,
cert=get_credentials())
response = requests.get(url, stream=True, timeout=TIMEOUT)
response.raise_for_status()
with tmp_file.open("wb") as file:
# Specify chunk_size to avoid
Expand Down
45 changes: 0 additions & 45 deletions esmvalcore/esgf/_logon.py

This file was deleted.

Loading

0 comments on commit edf1878

Please sign in to comment.