From 65ee03ce58c00c0e111023a2b236c47617c3c1a1 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 3 Dec 2024 13:48:22 +0000 Subject: [PATCH 1/4] [pre-commit.ci] pre-commit autoupdate (#62) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.7.2 → v0.8.1](https://github.com/astral-sh/ruff-pre-commit/compare/v0.7.2...v0.8.1) - [github.com/jshwi/docsig: v0.64.0 → v0.65.0](https://github.com/jshwi/docsig/compare/v0.64.0...v0.65.0) * fix: pre-commit docsig --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Gert Mertes --- .pre-commit-config.yaml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 62a9a76..9148b7c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -40,7 +40,7 @@ repos: - --force-single-line-imports - --profile black - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.7.2 + rev: v0.8.1 hooks: - id: ruff args: @@ -64,7 +64,7 @@ repos: hooks: - id: pyproject-fmt - repo: https://github.com/jshwi/docsig # Check docstrings against function sig - rev: v0.64.0 + rev: v0.65.0 hooks: - id: docsig args: @@ -74,7 +74,6 @@ repos: - --check-protected # Check protected methods - --check-class # Check class docstrings - --disable=E113 # Disable empty docstrings - - --summary # Print a summary ci: autoupdate_schedule: monthly ci: From 6ee6947fa07415922aef1358a50bafdbed73a5f7 Mon Sep 17 00:00:00 2001 From: Gert Mertes <13658335+gmertes@users.noreply.github.com> Date: Fri, 13 Dec 2024 13:19:06 +0000 Subject: [PATCH 2/4] feat: requests command json -> mars (#66) --- src/anemoi/utils/commands/requests.py | 44 +++++++++++++++++++++++++++ src/anemoi/utils/mars/requests.py | 22 ++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 src/anemoi/utils/commands/requests.py create mode 100644 src/anemoi/utils/mars/requests.py diff --git a/src/anemoi/utils/commands/requests.py b/src/anemoi/utils/commands/requests.py new file mode 100644 index 0000000..5f3cd18 --- /dev/null +++ b/src/anemoi/utils/commands/requests.py @@ -0,0 +1,44 @@ +# (C) Copyright 2024 European Centre for Medium-Range Weather Forecasts. +# This software is licensed under the terms of the Apache Licence Version 2.0 +# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +# In applying this licence, ECMWF does not waive the privileges and immunities +# granted to it by virtue of its status as an intergovernmental organisation +# nor does it submit to any jurisdiction. + +import json + +from anemoi.utils.mars.requests import print_request + +from . import Command + + +class Requests(Command): + """Convert a JSON requests file to MARS format.""" + + def add_arguments(self, command_parser): + command_parser.add_argument("input") + command_parser.add_argument("output") + command_parser.add_argument("--verb", default="retrieve") + command_parser.add_argument("--only-one-field", action="store_true") + + def run(self, args): + with open(args.input) as f: + requests = json.load(f) + + if args.only_one_field: + for r in requests: + for key in ( + "grid", + "area", + ): + r.pop(key, None) + for k, v in list(r.items()): + if isinstance(v, list): + r[k] = v[-1] + + with open(args.output, "w") as f: + for r in requests: + print_request(args.verb, r, file=f) + + +command = Requests diff --git a/src/anemoi/utils/mars/requests.py b/src/anemoi/utils/mars/requests.py new file mode 100644 index 0000000..66bd568 --- /dev/null +++ b/src/anemoi/utils/mars/requests.py @@ -0,0 +1,22 @@ +# (C) Copyright 2024 European Centre for Medium-Range Weather Forecasts. +# This software is licensed under the terms of the Apache Licence Version 2.0 +# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +# In applying this licence, ECMWF does not waive the privileges and immunities +# granted to it by virtue of its status as an intergovernmental organisation +# nor does it submit to any jurisdiction. + +import sys + + +def print_request(verb, request, file=sys.stdout): + r = [verb] + for k, v in request.items(): + if not isinstance(v, (list, tuple, set)): + v = [v] + v = [str(_) for _ in v] + v = "/".join(v) + r.append(f"{k}={v}") + + r = ",\n ".join(r) + print(r, file=file) + print(file=file) From dbc5a58e166edccd81594774ca6ea7066186c62a Mon Sep 17 00:00:00 2001 From: b8raoult <53792887+b8raoult@users.noreply.github.com> Date: Fri, 13 Dec 2024 14:18:13 +0000 Subject: [PATCH 3/4] Support from different logging message per threads/processes (#65) * Support from different logging message per threads/processes * Support from different logging message per threads/processes * add merge_configs() --- src/anemoi/utils/config.py | 8 ++++++++ src/anemoi/utils/logs.py | 40 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 src/anemoi/utils/logs.py diff --git a/src/anemoi/utils/config.py b/src/anemoi/utils/config.py index 64da8d0..9c89b4b 100644 --- a/src/anemoi/utils/config.py +++ b/src/anemoi/utils/config.py @@ -376,3 +376,11 @@ def find(metadata, what, result=None, *, select: callable = None): find(v, what, result) return result + + +def merge_configs(*configs): + result = {} + for config in configs: + _merge_dicts(result, config) + + return result diff --git a/src/anemoi/utils/logs.py b/src/anemoi/utils/logs.py new file mode 100644 index 0000000..47d4798 --- /dev/null +++ b/src/anemoi/utils/logs.py @@ -0,0 +1,40 @@ +# (C) Copyright 2024 Anemoi contributors. +# +# This software is licensed under the terms of the Apache Licence Version 2.0 +# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +# +# In applying this licence, ECMWF does not waive the privileges and immunities +# granted to it by virtue of its status as an intergovernmental organisation +# nor does it submit to any jurisdiction. + + +"""Logging utilities.""" + +import logging +import threading + +thread_local = threading.local() + + +LOGGER = logging.getLogger(__name__) + + +def set_logging_name(name): + thread_local.logging_name = name + + +class ThreadCustomFormatter(logging.Formatter): + def format(self, record): + record.logging_name = thread_local.logging_name + return super().format(record) + + +def enable_logging_name(name="main"): + thread_local.logging_name = name + + formatter = ThreadCustomFormatter("%(asctime)s - %(logging_name)s - %(levelname)s - %(message)s") + + logger = logging.getLogger() + + for handler in logger.handlers: + handler.setFormatter(formatter) From 5a7c19695cbc0d48c37cf662ef2b7989ae3ac803 Mon Sep 17 00:00:00 2001 From: Mario Santa Cruz <48736305+JPXKQX@users.noreply.github.com> Date: Fri, 13 Dec 2024 15:21:13 +0100 Subject: [PATCH 4/4] feat: add omegaconf dict and list as input to DotDict (#61) * feat: add omegaconf dict and list as input to DotDict * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- src/anemoi/utils/config.py | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/anemoi/utils/config.py b/src/anemoi/utils/config.py index 9c89b4b..c547672 100644 --- a/src/anemoi/utils/config.py +++ b/src/anemoi/utils/config.py @@ -50,14 +50,14 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) for k, v in self.items(): - if isinstance(v, dict): + if isinstance(v, dict) or is_omegaconf_dict(v): self[k] = DotDict(v) - if isinstance(v, list): - self[k] = [DotDict(i) if isinstance(i, dict) else i for i in v] + if isinstance(v, list) or is_omegaconf_list(v): + self[k] = [DotDict(i) if isinstance(i, dict) or is_omegaconf_dict(i) else i for i in v] if isinstance(v, tuple): - self[k] = [DotDict(i) if isinstance(i, dict) else i for i in v] + self[k] = [DotDict(i) if isinstance(i, dict) or is_omegaconf_dict(i) else i for i in v] @classmethod def from_file(cls, path: str): @@ -106,6 +106,24 @@ def __repr__(self) -> str: return f"DotDict({super().__repr__()})" +def is_omegaconf_dict(value) -> bool: + try: + from omegaconf import DictConfig + + return isinstance(value, DictConfig) + except ImportError: + return False + + +def is_omegaconf_list(value) -> bool: + try: + from omegaconf import ListConfig + + return isinstance(value, ListConfig) + except ImportError: + return False + + CONFIG = {} CHECKED = {} CONFIG_LOCK = threading.RLock()