From 4b2edf167de342bf17d86b0bef638b7bda535a7a Mon Sep 17 00:00:00 2001 From: Nina Burgdorfer Date: Thu, 3 Oct 2024 12:09:25 +0200 Subject: [PATCH 01/37] copied over aviso config and first commit with launch flexpart --- aggregator/aggregator_flexpart.py | 293 +++++++++++++++++++++++++++ aviso/README.md | 28 +++ aviso/config.yaml | 15 ++ aviso/listener_diss.yaml | 20 ++ aviso/run_docker_script.sh | 80 ++++++++ config_infrastructure/config_s3.yaml | 7 + 6 files changed, 443 insertions(+) create mode 100644 aggregator/aggregator_flexpart.py create mode 100644 aviso/README.md create mode 100644 aviso/config.yaml create mode 100644 aviso/listener_diss.yaml create mode 100755 aviso/run_docker_script.sh create mode 100644 config_infrastructure/config_s3.yaml diff --git a/aggregator/aggregator_flexpart.py b/aggregator/aggregator_flexpart.py new file mode 100644 index 0000000..e9f78e2 --- /dev/null +++ b/aggregator/aggregator_flexpart.py @@ -0,0 +1,293 @@ +#!/usr/bin/env python3 + +import argparse +import datetime +import json +import logging +import sqlite3 +import subprocess +import sys +import os +import yaml + +import boto3 +from botocore.exceptions import ClientError + + +def parse_arguments(): + """ + Parse command-line arguments. + + Returns: + argparse.Namespace: Parsed arguments. + """ + parser = argparse.ArgumentParser( + description="Launch Flexpart after processing checks." + ) + parser.add_argument("--date", required=True, help="Date in YYYYMMDD format") + parser.add_argument("--time", required=True, help="Time in HH format") + parser.add_argument( + "--step", required=True, help="Step identifier (lead time in hours)" + ) + parser.add_argument("--db_path", required=True, help="Path to the SQLite database") + return parser.parse_args() + + +def connect_db(db_path): + """ + Establish a connection to the SQLite database. + + Args: + db_path (str): Path to the SQLite database. + + Returns: + sqlite3.Connection: SQLite connection object. + """ + try: + conn = sqlite3.connect(db_path) + logging.info(f"Connected to SQLite database at {db_path}.") + return conn + except sqlite3.Error as e: + logging.error(f"SQLite connection error: {e}") + sys.exit(1) + + +def is_row_processed(conn, forecast_ref_time, step): + """ + Check if a specific row in the database has been processed. + + Args: + conn (sqlite3.Connection): SQLite connection object. + forecast_ref_time (str): Forecast reference time in YYYYMMDDHHMM format. + step (str): Step identifier. + + Returns: + bool: True if processed, False otherwise. + """ + try: + cursor = conn.cursor() + cursor.execute( + """ + SELECT processed FROM uploaded + WHERE forecast_ref_time = ? AND step = ? + """, + (forecast_ref_time, step), + ) + result = cursor.fetchone() + if result: + return result[0] == 1 + else: + logging.warning( + f"No row found for forecast_ref_time={forecast_ref_time} and step={step}." + ) + return False + except sqlite3.Error as e: + logging.error(f"SQLite query error: {e}") + sys.exit(1) + + +def generate_flexpart_start_times(frt_dt, lead_time, tdelta, tfreq_f): + """ + Generate a list of Flexpart run start times. + + Args: + frt_dt (datetime.datetime): Forecast reference datetime. + lead_time (int): Lead time in hours. + tdelta (int): Number of timesteps to run Flexpart with. + tfreq_f (int): Frequency of Flexpart runs in hours. + + Returns: + list of datetime.datetime: List of Flexpart run start times. + """ + lt_dt = frt_dt + datetime.timedelta(hours=lead_time) + lt_tmp = lt_dt - datetime.timedelta(hours=tdelta) + min_start_time = lt_tmp + datetime.timedelta( + hours=tfreq_f - (lt_tmp.hour % tfreq_f) + ) + max_start_time = lt_dt.replace(hour=(lt_dt.hour - (lt_dt.hour % tfreq_f))) + + list_start_times = [] + current_start = min_start_time + delta = datetime.timedelta(hours=tfreq_f) + while current_start <= max_start_time: + list_start_times.append(current_start) + current_start += delta + + return list_start_times + + +def convert_time_to_frt(time, tfreq): + """ + Convert time object into IFS forecast objects to use. + + Args: + time (datetime.datetime): Datetime object. + tfreq (int): Frequency of IFS forecast times in hours. + + Returns: + str: Forecast reference time (YYYYMMDDHH) followed by the lead time (HH) + """ + if time.hour % tfreq != 0: + frt_st = time - datetime.timedelta(hours=time.hour % tfreq) + lt = time.hour % tfreq + else: + frt_st = time - datetime.timedelta(hours=tfreq) + lt = tfreq + return frt_st.strftime("%Y%m%d%H%M") + f"{lt:02}" + + +def fetch_processed_items(conn, frt_s): + """ + Fetch all processed items from the database. + + Args: + conn (sqlite3.Connection): SQLite connection object. + frt_s (set of str): Set of forecast reference times (stripped of last two characters). + + Returns: + set of str: Set of processed item identifiers. + """ + processed_items = set() + try: + cursor = conn.cursor() + for frt in frt_s: + cursor.execute( + """ + SELECT processed, step FROM uploaded + WHERE forecast_ref_time = ? + """, + (frt,), + ) + items_f = cursor.fetchall() + for item in items_f: + if item[0]: # processed == True + processed_items.add(frt + f"{int(item[1]):02}") + except sqlite3.Error as e: + logging.error(f"SQLite query error while fetching processed items: {e}") + sys.exit(1) + return processed_items + + +def define_config(st: datetime.datetime, et: datetime.datetime): + + logging.info(f'Start and end time to configure Flexpart: {st} and {et} ') + + configuration = { + 'IBDATE': st.strftime("%Y%m%d"), + 'IBTIME': st.strftime("%H"), + 'IEDATE': et.strftime("%Y%m%d"), + 'IETIME': et.strftime("%H") + } + + logging.info(f'Configuration to run Flexpart: {json.dumps(configuration)}') + + return configuration + + +def main(): + args = parse_arguments() + + # Initialize variables + DATE = args.date + TIME = args.time + STEP = args.step + DB_PATH = args.db_path + + # Constants + TINCR = 1 # How many hours between time steps + TDELTA = 6 # Number of timesteps to run Flexpart with (temporarily set to 6 timesteps but operational config is 90) + TFREQ_F = 6 # Frequency of Flexpart runs in hours + TFREQ = 6 # Frequency of IFS forecast times in hours + + # Connect to the database + conn = connect_db(DB_PATH) + + # Generate forecast reference time datetime object + frt_dt = datetime.datetime.strptime(f"{DATE}{int(TIME):02d}00", "%Y%m%d%H%M") + + # Check if the specific row is processed + if not is_row_processed(conn, frt_dt, STEP): + logging.info("File processing incomplete. Exiting before launching Flexpart.") + conn.close() + sys.exit(0) + + lead_time = int(STEP) + list_start_times = generate_flexpart_start_times(frt_dt, lead_time, TDELTA, TFREQ_F) + + logging.info(f"Generated {len(list_start_times)} Flexpart start times.") + + all_steps = set() + all_list_ltf = [] + all_list_lt = [] + + for start_time in list_start_times: + logging.info(f"Start time: {start_time}") + list_ltf = [] + list_lt = [] + for i in range(0, TDELTA, TINCR): + time = start_time + datetime.timedelta(hours=i) + forecast = convert_time_to_frt(time, TFREQ) + list_ltf.append(forecast) + list_lt.append(time) + all_steps.add(forecast) + all_list_ltf.append(list_ltf) + all_list_lt.append(list_lt) + + # Generate forecast ref time by stripping the last two characters (lead time) + frt_s = { + datetime.datetime.strptime(forecast[:-2], "%Y%m%d%H%M") + for forecast in all_steps + } + + # Fetch processed items from the database + processed_items = fetch_processed_items(conn, frt_s) + + # Iterate over all_list_ltf to determine if Flexpart should be launched + for i, flexpart_run in enumerate(all_list_ltf): + tstart = all_list_lt[i][0] + tend = all_list_lt[i][-1] + if all(item in processed_items for item in flexpart_run): + + configuration = define_config(tstart, tend) + + with open('config_flexpart.yaml', 'r') as file: + config = yaml.safe_load(file) + + input_bucket_name = config['s3_buckets']['input']['name'] + input_bucket_url = config['s3_buckets']['input']['endpoint_url'] + output_bucket_name = config['s3_buckets']['output']['name'] + output_bucket_url = config['s3_buckets']['output']['endpoint_url'] + + # Create a list of environment variables for Podman + env_vars = [f"-e {key}={value}" for key, value in configuration.items()] + + env_vars.append(f"-e INPUT_S3_NAME={input_bucket_name}") + env_vars.append(f"-e INPUT_S3_URL={input_bucket_url}") + env_vars.append(f"-e OUTPUT_S3_NAME={output_bucket_name}") + env_vars.append(f"-e OUTPUT_S3_URL={output_bucket_url}") + + # Define the command + command = ['/bin/sh', '-c', 'ulimit -a && bash entrypoint.sh'] + + # Join the command list to make it usable in the Docker command + command_str = ' '.join(command) + + podman_command = f"docker run {' '.join(env_vars)} --rm container-registry.meteoswiss.ch/flexpart-poc/flexpart:containerize {command_str}" + + # Log the command (optional) + print(f"Running: {podman_command}") + + # Execute the Podman command + subprocess.run(podman_command, shell=True, check=True) + else: + logging.info( + f"NOT ENOUGH DATA TO LAUNCH FLEXPART FROM {tstart.strftime('%m/%d/%Y, %H:%M')} " + f"TO {tend.strftime('%m/%d/%Y, %H:%M')}" + ) + + # Close the database connection + conn.close() + + +if __name__ == "__main__": + main() diff --git a/aviso/README.md b/aviso/README.md new file mode 100644 index 0000000..1894c23 --- /dev/null +++ b/aviso/README.md @@ -0,0 +1,28 @@ +# Aviso Setup Guide + +## Documentation + +To set up Aviso on the EWC, follow the steps in the documentation: + +1. [Aviso Notification System on EWC (Points 1 and 2)](https://confluence.ecmwf.int/display/EWCLOUDKB/Aviso+Notification+System+on+EWC) +2. [Create a config file (Section 2.2)](https://confluence.ecmwf.int/display/UDOC/Setting+up+Aviso+Notification+System+for+ECMWF%27+events) + +## Configuration + +Once you've created the `~/.marsrc/mars.email` and `~/.marsrc/mars.token` files as described in Section 2.2 of the documentation, save the `config.yaml`, `listener_diss.yaml`, and `run_docker_script.sh` files from the `aviso/` folder of this repository into the `~/.aviso/` directory of your VM. + +## Running Aviso as a Service + +Refer to the [documentation](https://confluence.ecmwf.int/display/EWCLOUDKB/Aviso+Notification+System+on+EWC) for detailed instructions. + +To apply changes made to the `~/.aviso/config.yaml` file, restart the Aviso service: + +```bash +sudo systemctl restart aviso.service +``` +## Replaying Notifications +To replay notifications for a specific time range, use the following command: + +```bash +aviso listen --from 2020-01-20T00:00:00.0Z --to 2020-01-22T00:00:00.0Z +``` diff --git a/aviso/config.yaml b/aviso/config.yaml new file mode 100644 index 0000000..3314133 --- /dev/null +++ b/aviso/config.yaml @@ -0,0 +1,15 @@ +username_file: ~/.marsrc/mars.email +key_file: ~/.marsrc/mars.token +notification_engine: + type: etcd_rest + host: aviso.ecmwf.int + port: 443 + https: true +configuration_engine: + type: etcd_rest + host: aviso.ecmwf.int + port: 443 + https: true +schema_parser: ecmwf +remote_schema: True +auth_type: ecmwf diff --git a/aviso/listener_diss.yaml b/aviso/listener_diss.yaml new file mode 100644 index 0000000..1fb19b3 --- /dev/null +++ b/aviso/listener_diss.yaml @@ -0,0 +1,20 @@ +listeners: + - event: dissemination + request: + destination: S7Y + class: od + expver: 0001 + domain: g + stream: [oper, scda] + triggers: + - type: log + path: avisoLogs/log_aviso_notifications.log + - type: command + working_dir: $HOME + command: > + .aviso/my_docker_script.sh \ + --step "${request.step}" \ + --date "${request.date}" \ + --time "${request.time}" \ + --location "${location}" >> avisoLogs/log_run_${request.date}_${request.time}.log 2>&1 + diff --git a/aviso/run_docker_script.sh b/aviso/run_docker_script.sh new file mode 100755 index 0000000..51dd1b2 --- /dev/null +++ b/aviso/run_docker_script.sh @@ -0,0 +1,80 @@ +#!/bin/bash + +echo Notification received + +# Retrieve ECR login password and log in to Docker +aws ecr get-login-password --region eu-central-2 | docker login --username AWS --password-stdin 493666016161.dkr.ecr.eu-central-2.amazonaws.com +if [ $? -ne 0 ]; then + echo "Docker login failed. Exiting." + exit 1 +fi + +while [[ $# -gt 0 ]] +do +key="$1" + +case $key in + --date) + DATE="$2" + shift # past argument + shift # past value + ;; + --location) + LOCATION="$2" + shift # past argument + shift # past value + ;; + --time) + TIME="$2" + shift # past argument + shift # past value + ;; + --step) + STEP="$2" + shift # past argument + shift # past value + ;; +esac +done + +echo Notification received for file $LOCATION, date $DATE, time $TIME, step $STEP + +# Run pre-processing for Flexpart +# Note: The tag of the Docker image (after the colon ':') is manually updated here +# after each push to reflect the latest version of the image. +docker run \ + --mount type=bind,source="$HOME/.sqlite/",destination=/src/db/ \ + --env-file .env \ + 493666016161.dkr.ecr.eu-central-2.amazonaws.com/numericalweatherpredictions/flexpart_ifs/flexprep:2409.ee22f6c67c86b9f85185edb02924e6ab523fa0bc \ + --step "$STEP" \ + --date "$DATE" \ + --time "$TIME" \ + --location "$LOCATION" + + +# Check if the Docker run was successful +if [ $? -ne 0 ]; then + echo "Docker run processing failed." + exit 1 +fi + +echo "Docker container processing executed successfully." + +# ====== Second part: Run flexpart ====== +# Call the Python script to handle the second part + +# Path to the SQLite database +DB_PATH="$HOME/.sqlite/sqlite3-db" +python3 aggregator_flexpart.py \ + --date "$DATE" \ + --time "$TIME" \ + --step "$STEP" \ + --db_path "$DB_PATH" + +# Capture the exit status of the Python script +if [ $? -ne 0 ]; then + echo "Flexpart launch script encountered an error." + exit 1 +fi + +echo "Flexpart launch script executed successfully." \ No newline at end of file diff --git a/config_infrastructure/config_s3.yaml b/config_infrastructure/config_s3.yaml new file mode 100644 index 0000000..3fc33e3 --- /dev/null +++ b/config_infrastructure/config_s3.yaml @@ -0,0 +1,7 @@ +s3_buckets: + input: + endpoint_url: https://object-store.os-api.cci1.ecmwf.int + name: flexprep-output + output: + endpoint_url: https://object-store.os-api.cci1.ecmwf.int + name: flexpart-output \ No newline at end of file From 53ad009e496010c93a8cd1c00a82f16aab089b40 Mon Sep 17 00:00:00 2001 From: Nina Burgdorfer Date: Thu, 3 Oct 2024 17:20:12 +0200 Subject: [PATCH 02/37] define S3 config and flexprep time settings in .env --- .env | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .env diff --git a/.env b/.env new file mode 100644 index 0000000..38b2e1f --- /dev/null +++ b/.env @@ -0,0 +1,27 @@ +# .env + +## Flexprep (Flexpart pre-processing) + +# Input/output S3 bucket settings +INPUT_FLEXPREP_S3_ENDPOINT_URL=https://object-store.os-api.cci1.ecmwf.int +INPUT_FLEXPREP_S3_NAME=flexpart-input +OUTPUT_FLEXPREP_S3_ENDPOINT_URL=https://object-store.os-api.cci1.ecmwf.int +OUTPUT_FLEXPREP_S3_NAME=flexprep-output + +# Timestep settings +# Duration in hours between the processed IFS forecasts +# needed by Flexpart-IFS (Global domain: 3h, Europe domain: 1h) +TIME_FLEXPREP_INCREMENT=1 +TIME_FLEXPREP_START=0 + +## Flexpart + +# Input/output S3 bucket settings +INPUT_FLEXPART_S3_ENDPOINT_URL=${OUTPUT_FLEXPREP_S3_ENDPOINT_URL} +INPUT_FLEXPART_S3_NAME=${OUTPUT_FLEXPREP_S3_NAME} +OUTPUT_FLEXPART_S3_ENDPOINT_URL=https://object-store.os-api.cci1.ecmwf.int +OUTPUT_FLEXPART_S3_NAME=flexpart-output + +# EWC buckets access and secret keys +S3_ACCESS_KEY= +S3_SECRET_KEY= From 8f3ca404ef5aaf62590981d394620c745fd5824e Mon Sep 17 00:00:00 2001 From: Nina Burgdorfer Date: Thu, 3 Oct 2024 17:32:59 +0200 Subject: [PATCH 03/37] rm config files transfered to .env --- config_infrastructure/config_s3.yaml | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 config_infrastructure/config_s3.yaml diff --git a/config_infrastructure/config_s3.yaml b/config_infrastructure/config_s3.yaml deleted file mode 100644 index 3fc33e3..0000000 --- a/config_infrastructure/config_s3.yaml +++ /dev/null @@ -1,7 +0,0 @@ -s3_buckets: - input: - endpoint_url: https://object-store.os-api.cci1.ecmwf.int - name: flexprep-output - output: - endpoint_url: https://object-store.os-api.cci1.ecmwf.int - name: flexpart-output \ No newline at end of file From 062e01079decc028854bfdefa7065a32f5a51c47 Mon Sep 17 00:00:00 2001 From: Nina Burgdorfer Date: Wed, 9 Oct 2024 15:51:48 +0200 Subject: [PATCH 04/37] change env var names with pydantic mechanism --- .env | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/.env b/.env index 38b2e1f..1e882f0 100644 --- a/.env +++ b/.env @@ -3,25 +3,23 @@ ## Flexprep (Flexpart pre-processing) # Input/output S3 bucket settings -INPUT_FLEXPREP_S3_ENDPOINT_URL=https://object-store.os-api.cci1.ecmwf.int -INPUT_FLEXPREP_S3_NAME=flexpart-input -OUTPUT_FLEXPREP_S3_ENDPOINT_URL=https://object-store.os-api.cci1.ecmwf.int -OUTPUT_FLEXPREP_S3_NAME=flexprep-output +SVC__MAIN__S3_BUCKETS__INPUT__ENDPOINT_URL=https://object-store.os-api.cci1.ecmwf.int +SVC__MAIN__S3_BUCKETS__INPUT__NAME=flexpart-input +SVC__MAIN__S3_BUCKETS__OUTPUT__ENDPOINT_URL=https://object-store.os-api.cci1.ecmwf.int +SVC__MAIN__S3_BUCKETS__OUTPUT__NAME=flexprep-output # Timestep settings -# Duration in hours between the processed IFS forecasts -# needed by Flexpart-IFS (Global domain: 3h, Europe domain: 1h) -TIME_FLEXPREP_INCREMENT=1 -TIME_FLEXPREP_START=0 +SVC__MAIN__TIME_SETTINGS__TINCR=1 # Interval in hours for forecasts (Global: 3h, Europe: 1h) +SVC__MAIN__TIME_SETTINGS__TSTART=0 # Start time -## Flexpart +## Flexpart-IFS # Input/output S3 bucket settings -INPUT_FLEXPART_S3_ENDPOINT_URL=${OUTPUT_FLEXPREP_S3_ENDPOINT_URL} -INPUT_FLEXPART_S3_NAME=${OUTPUT_FLEXPREP_S3_NAME} -OUTPUT_FLEXPART_S3_ENDPOINT_URL=https://object-store.os-api.cci1.ecmwf.int -OUTPUT_FLEXPART_S3_NAME=flexpart-output +SVC__MAIN__S3_BUCKETS__INPUT__ENDPOINT_URL=${SVC__MAIN__S3_BUCKETS__OUTPUT__ENDPOINT_URL} # Inherit from Flexprep output +SVC__MAIN__S3_BUCKETS__INPUT__NAME=${SVC__MAIN__S3_BUCKETS__OUTPUT__NAME} # Inherit from Flexprep output +SVC__MAIN__S3_BUCKETS__OUTPUT__ENDPOINT_URL=https://object-store.os-api.cci1.ecmwf.int +SVC__MAIN__S3_BUCKETS__OUTPUT__NAME=flexpart-output # EWC buckets access and secret keys -S3_ACCESS_KEY= -S3_SECRET_KEY= +SVC__LOGGING__S3_ACCESS_KEY= +SVC__LOGGING__S3_SECRET_KEY= From 3dfbc4c9a4f8518c5041a17ff052e94db7a9d027 Mon Sep 17 00:00:00 2001 From: Nina Burgdorfer Date: Wed, 9 Oct 2024 17:26:27 +0200 Subject: [PATCH 05/37] script in python instead of bash --- aviso/listener_diss.yaml | 2 +- aviso/run_docker_script.py | 102 +++++++++++++++++++++++++++++++++++++ aviso/run_docker_script.sh | 80 ----------------------------- 3 files changed, 103 insertions(+), 81 deletions(-) create mode 100755 aviso/run_docker_script.py delete mode 100755 aviso/run_docker_script.sh diff --git a/aviso/listener_diss.yaml b/aviso/listener_diss.yaml index 1fb19b3..d59f636 100644 --- a/aviso/listener_diss.yaml +++ b/aviso/listener_diss.yaml @@ -12,7 +12,7 @@ listeners: - type: command working_dir: $HOME command: > - .aviso/my_docker_script.sh \ + python3 .aviso/my_docker_script.py \ --step "${request.step}" \ --date "${request.date}" \ --time "${request.time}" \ diff --git a/aviso/run_docker_script.py b/aviso/run_docker_script.py new file mode 100755 index 0000000..bb5dad5 --- /dev/null +++ b/aviso/run_docker_script.py @@ -0,0 +1,102 @@ +import subprocess +import argparse +import os +import sys + +def main(): + # Retrieve ECR login password and log in to Docker + try: + login_command = [ + "aws", "ecr", "get-login-password", "--region", "eu-central-2" + ] + login_password = subprocess.check_output(login_command).strip() + + docker_login_command = [ + "docker", "login", "--username", "AWS", "--password-stdin", "493666016161.dkr.ecr.eu-central-2.amazonaws.com" + ] + process = subprocess.Popen(docker_login_command, stdin=subprocess.PIPE) + process.communicate(input=login_password) + + if process.returncode != 0: + print("Docker login failed. Exiting.") + sys.exit(1) + except subprocess.CalledProcessError as e: + print(f"Error logging in to Docker: {e}") + sys.exit(1) + + # Argument parsing + parser = argparse.ArgumentParser() + parser.add_argument('--date', required=True, help='Date parameter') + parser.add_argument('--location', required=True, help='Location parameter') + parser.add_argument('--time', required=True, help='Time parameter') + parser.add_argument('--step', required=True, help='Step parameter') + + args = parser.parse_args() + + date = args.date + location = args.location + time = args.time + step = args.step + + print(f"Notification received for file {location}, date {date}, time {time}, step {step}") + + # Run pre-processing for Flexpart + docker_image = "493666016161.dkr.ecr.eu-central-2.amazonaws.com/numericalweatherpredictions/flexpart_ifs/flexprep:2409.ee22f6c67c86b9f85185edb02924e6ab523fa0bc" + + try: + docker_run_command = [ + "docker", "run", + "--mount", f"type=bind,source={os.path.expanduser('~/.sqlite/')},destination=/src/db/", + "--env-file", ".env", + docker_image, + "--step", step, + "--date", date, + "--time", time, + "--location", location + ] + + subprocess.check_call(docker_run_command) + except subprocess.CalledProcessError: + print("Docker run processing failed.") + sys.exit(1) + + print("Docker container processing executed successfully.") + + # ====== Second part: Run aggregator_flexpart.py ====== + db_path = os.path.expanduser('~/.sqlite/sqlite3-db') + + try: + aggregator_command = [ + "python3", "aggregator/aggregator_flexpart.py", + "--date", date, + "--time", time, + "--step", step, + "--db_path", db_path + ] + + subprocess.check_call(aggregator_command) + except subprocess.CalledProcessError: + print("Aggregator launch script encountered an error.") + sys.exit(1) + + print("Aggregator launch script executed successfully.") + + # ====== Third part: Run launch_flexpart.py ====== + try: + launch_command = [ + "python3", "launch_flexpart.py", + "--date", date, + "--time", time, + "--step", step, + "--db_path", db_path + ] + + subprocess.check_call(launch_command) + except subprocess.CalledProcessError: + print("Launch Flexpart script encountered an error.") + sys.exit(1) + + print("Launch Flexpart script executed successfully.") + +if __name__ == "__main__": + main() diff --git a/aviso/run_docker_script.sh b/aviso/run_docker_script.sh deleted file mode 100755 index 51dd1b2..0000000 --- a/aviso/run_docker_script.sh +++ /dev/null @@ -1,80 +0,0 @@ -#!/bin/bash - -echo Notification received - -# Retrieve ECR login password and log in to Docker -aws ecr get-login-password --region eu-central-2 | docker login --username AWS --password-stdin 493666016161.dkr.ecr.eu-central-2.amazonaws.com -if [ $? -ne 0 ]; then - echo "Docker login failed. Exiting." - exit 1 -fi - -while [[ $# -gt 0 ]] -do -key="$1" - -case $key in - --date) - DATE="$2" - shift # past argument - shift # past value - ;; - --location) - LOCATION="$2" - shift # past argument - shift # past value - ;; - --time) - TIME="$2" - shift # past argument - shift # past value - ;; - --step) - STEP="$2" - shift # past argument - shift # past value - ;; -esac -done - -echo Notification received for file $LOCATION, date $DATE, time $TIME, step $STEP - -# Run pre-processing for Flexpart -# Note: The tag of the Docker image (after the colon ':') is manually updated here -# after each push to reflect the latest version of the image. -docker run \ - --mount type=bind,source="$HOME/.sqlite/",destination=/src/db/ \ - --env-file .env \ - 493666016161.dkr.ecr.eu-central-2.amazonaws.com/numericalweatherpredictions/flexpart_ifs/flexprep:2409.ee22f6c67c86b9f85185edb02924e6ab523fa0bc \ - --step "$STEP" \ - --date "$DATE" \ - --time "$TIME" \ - --location "$LOCATION" - - -# Check if the Docker run was successful -if [ $? -ne 0 ]; then - echo "Docker run processing failed." - exit 1 -fi - -echo "Docker container processing executed successfully." - -# ====== Second part: Run flexpart ====== -# Call the Python script to handle the second part - -# Path to the SQLite database -DB_PATH="$HOME/.sqlite/sqlite3-db" -python3 aggregator_flexpart.py \ - --date "$DATE" \ - --time "$TIME" \ - --step "$STEP" \ - --db_path "$DB_PATH" - -# Capture the exit status of the Python script -if [ $? -ne 0 ]; then - echo "Flexpart launch script encountered an error." - exit 1 -fi - -echo "Flexpart launch script executed successfully." \ No newline at end of file From 95bb6e433c62656916c27feeae98673f36fc2dee Mon Sep 17 00:00:00 2001 From: Nina Burgdorfer Date: Thu, 10 Oct 2024 19:40:05 +0200 Subject: [PATCH 06/37] extract launch flexpart from aggregator --- aggregator/aggregator_flexpart.py | 41 ++--------- aviso/run_docker_script.py | 111 +++++++++++++++++++++--------- 2 files changed, 84 insertions(+), 68 deletions(-) diff --git a/aggregator/aggregator_flexpart.py b/aggregator/aggregator_flexpart.py index e9f78e2..2f572b9 100644 --- a/aggregator/aggregator_flexpart.py +++ b/aggregator/aggregator_flexpart.py @@ -202,7 +202,6 @@ def main(): # Connect to the database conn = connect_db(DB_PATH) - # Generate forecast reference time datetime object frt_dt = datetime.datetime.strptime(f"{DATE}{int(TIME):02d}00", "%Y%m%d%H%M") # Check if the specific row is processed @@ -243,47 +242,15 @@ def main(): processed_items = fetch_processed_items(conn, frt_s) # Iterate over all_list_ltf to determine if Flexpart should be launched + configs = [] for i, flexpart_run in enumerate(all_list_ltf): tstart = all_list_lt[i][0] tend = all_list_lt[i][-1] if all(item in processed_items for item in flexpart_run): + config = define_config(tstart, tend) + configs.append(config) - configuration = define_config(tstart, tend) - - with open('config_flexpart.yaml', 'r') as file: - config = yaml.safe_load(file) - - input_bucket_name = config['s3_buckets']['input']['name'] - input_bucket_url = config['s3_buckets']['input']['endpoint_url'] - output_bucket_name = config['s3_buckets']['output']['name'] - output_bucket_url = config['s3_buckets']['output']['endpoint_url'] - - # Create a list of environment variables for Podman - env_vars = [f"-e {key}={value}" for key, value in configuration.items()] - - env_vars.append(f"-e INPUT_S3_NAME={input_bucket_name}") - env_vars.append(f"-e INPUT_S3_URL={input_bucket_url}") - env_vars.append(f"-e OUTPUT_S3_NAME={output_bucket_name}") - env_vars.append(f"-e OUTPUT_S3_URL={output_bucket_url}") - - # Define the command - command = ['/bin/sh', '-c', 'ulimit -a && bash entrypoint.sh'] - - # Join the command list to make it usable in the Docker command - command_str = ' '.join(command) - - podman_command = f"docker run {' '.join(env_vars)} --rm container-registry.meteoswiss.ch/flexpart-poc/flexpart:containerize {command_str}" - - # Log the command (optional) - print(f"Running: {podman_command}") - - # Execute the Podman command - subprocess.run(podman_command, shell=True, check=True) - else: - logging.info( - f"NOT ENOUGH DATA TO LAUNCH FLEXPART FROM {tstart.strftime('%m/%d/%Y, %H:%M')} " - f"TO {tend.strftime('%m/%d/%Y, %H:%M')}" - ) + print(json.dumps(configs)) # Close the database connection conn.close() diff --git a/aviso/run_docker_script.py b/aviso/run_docker_script.py index bb5dad5..ff4ebd5 100755 --- a/aviso/run_docker_script.py +++ b/aviso/run_docker_script.py @@ -1,27 +1,45 @@ import subprocess import argparse +import json import os import sys +import logging + +def run_command(command, capture_output=False): + """ + Helper function to run shell commands and handle errors. + """ + try: + if capture_output: + return subprocess.check_output(command).strip() + else: + subprocess.check_call(command) + except subprocess.CalledProcessError as e: + logging.error(f"Command '{' '.join(command)}' failed with error: {e}") + sys.exit(1) def main(): + logging.basicConfig(level=logging.INFO) + # Retrieve ECR login password and log in to Docker try: - login_command = [ - "aws", "ecr", "get-login-password", "--region", "eu-central-2" - ] - login_password = subprocess.check_output(login_command).strip() - + login_command = ["aws", "ecr", "get-login-password", "--region", "eu-central-2"] + login_password = run_command(login_command, capture_output=True) + docker_login_command = [ - "docker", "login", "--username", "AWS", "--password-stdin", "493666016161.dkr.ecr.eu-central-2.amazonaws.com" + "docker", "login", "--username", "AWS", "--password-stdin", + "493666016161.dkr.ecr.eu-central-2.amazonaws.com" ] + process = subprocess.Popen(docker_login_command, stdin=subprocess.PIPE) process.communicate(input=login_password) - + if process.returncode != 0: - print("Docker login failed. Exiting.") + logging.error("Docker login failed. Exiting.") sys.exit(1) + except subprocess.CalledProcessError as e: - print(f"Error logging in to Docker: {e}") + logging.error(f"Error logging in to Docker: {e}") sys.exit(1) # Argument parsing @@ -38,15 +56,20 @@ def main(): time = args.time step = args.step - print(f"Notification received for file {location}, date {date}, time {time}, step {step}") + logging.info(f"Notification received for file {location}, date {date}, time {time}, step {step}") # Run pre-processing for Flexpart docker_image = "493666016161.dkr.ecr.eu-central-2.amazonaws.com/numericalweatherpredictions/flexpart_ifs/flexprep:2409.ee22f6c67c86b9f85185edb02924e6ab523fa0bc" + db_mount = os.path.expanduser('~/.sqlite/') + + if not os.path.exists(db_mount): + logging.error(f"SQLite database directory {db_mount} does not exist.") + sys.exit(1) try: docker_run_command = [ "docker", "run", - "--mount", f"type=bind,source={os.path.expanduser('~/.sqlite/')},destination=/src/db/", + "--mount", f"type=bind,source={db_mount},destination=/src/db/", "--env-file", ".env", docker_image, "--step", step, @@ -55,48 +78,74 @@ def main(): "--location", location ] - subprocess.check_call(docker_run_command) + run_command(docker_run_command) + except subprocess.CalledProcessError: - print("Docker run processing failed.") + logging.error("Docker run processing failed.") sys.exit(1) - print("Docker container processing executed successfully.") + logging.info("Docker container processing executed successfully.") # ====== Second part: Run aggregator_flexpart.py ====== db_path = os.path.expanduser('~/.sqlite/sqlite3-db') + if not os.path.exists(db_path): + logging.error(f"Database file {db_path} does not exist.") + sys.exit(1) + try: + script_dir = os.path.dirname(os.path.abspath(__file__)) + aggregator_script_path = os.path.join(script_dir, '..', 'aggregator', 'aggregator_flexpart.py') + aggregator_command = [ - "python3", "aggregator/aggregator_flexpart.py", + "python3", aggregator_script_path, "--date", date, "--time", time, "--step", step, "--db_path", db_path ] - - subprocess.check_call(aggregator_command) + + output = run_command(aggregator_command, capture_output=True) + try: + configurations = json.loads(output.decode('utf-8')) + except json.JSONDecodeError as e: + logging.error(f"JSON decode error: {e}") + sys.exit(1) + except subprocess.CalledProcessError: - print("Aggregator launch script encountered an error.") + logging.error("Aggregator script encountered an error.") sys.exit(1) - print("Aggregator launch script executed successfully.") + logging.info("Aggregator launch script executed successfully.") - # ====== Third part: Run launch_flexpart.py ====== + # ====== Third part: Run Flexpart ====== try: - launch_command = [ - "python3", "launch_flexpart.py", - "--date", date, - "--time", time, - "--step", step, - "--db_path", db_path - ] - - subprocess.check_call(launch_command) + # Check if configurations is an empty list + if not configurations: + logging.error("Not enough data to launch Flexpart.") + sys.exit(1) + + # Loop through each configuration and execute Flexpart + for config in configurations: + env_vars = [f"-e {key}={value}" for key, value in config.items()] + command = ['/bin/sh', '-c', 'ulimit -a && bash entrypoint.sh'] + command_str = ' '.join(command) + + docker_command = ( + f"docker run {' '.join(env_vars)} --rm " + "container-registry.meteoswiss.ch/flexpart-poc/flexpart:containerize " + f"{command_str}" + ) + + logging.info(f"Running: {docker_command}") + run_command(docker_command) + except subprocess.CalledProcessError: - print("Launch Flexpart script encountered an error.") + logging.error("Launch Flexpart script encountered an error.") sys.exit(1) - print("Launch Flexpart script executed successfully.") + logging.info("Launch Flexpart script executed successfully.") + if __name__ == "__main__": main() From 7ba581ba7d1b75a4e64bbe6f0f2733365620422b Mon Sep 17 00:00:00 2001 From: ninaburg Date: Mon, 14 Oct 2024 15:36:16 +0200 Subject: [PATCH 07/37] adopt mch cookiecutter template --- .gitignore | 103 ++++++ .mch-ci.yml | 140 +++++++ .python-version | 1 + Dockerfile | 47 +++ HISTORY.rst | 8 + Jenkinsfile | 345 ++++++++++++++++++ LICENSE | 17 +- README.rst | 79 ++++ doc/conf.py | 159 ++++++++ doc/history.rst | 1 + doc/index.rst | 18 + doc/readme.rst | 1 + flex_container_orchestrator/__init__.py | 10 + .../config/__init__.py | 0 .../config/service_settings.py | 14 + .../config/settings.yaml | 10 + .../domain/__init__.py | 0 .../domain/greeting.py | 6 + flex_container_orchestrator/main.py | 31 ++ .../routers/__init__.py | 0 .../routers/greeting_router.py | 16 + .../services/__init__.py | 0 .../services/greeting_service.py | 25 ++ k8s/base/deployment.yaml | 61 ++++ k8s/base/kustomization.yaml | 6 + k8s/base/route.yaml | 19 + k8s/base/service.yaml | 16 + k8s/overlays/depl/kustomization.yaml | 15 + k8s/overlays/devt/kustomization.yaml | 15 + k8s/overlays/prod/kustomization.yaml | 15 + pyproject.toml | 63 ++++ sonar-project.properties | 9 + test/__init__.py | 0 test/integration/__init__.py | 0 test/integration/conftest.py | 11 + test/integration/routers/__init__.py | 0 .../routers/test_greeting_router.py | 13 + test/unit/__init__.py | 0 test/unit/services/__init__.py | 0 test/unit/services/test_greeting_service.py | 16 + uvicorn_logging_settings.json | 1 + 41 files changed, 1283 insertions(+), 8 deletions(-) create mode 100644 .gitignore create mode 100644 .mch-ci.yml create mode 100644 .python-version create mode 100644 Dockerfile create mode 100644 HISTORY.rst create mode 100644 Jenkinsfile create mode 100644 README.rst create mode 100755 doc/conf.py create mode 100644 doc/history.rst create mode 100644 doc/index.rst create mode 100644 doc/readme.rst create mode 100644 flex_container_orchestrator/__init__.py create mode 100644 flex_container_orchestrator/config/__init__.py create mode 100644 flex_container_orchestrator/config/service_settings.py create mode 100644 flex_container_orchestrator/config/settings.yaml create mode 100644 flex_container_orchestrator/domain/__init__.py create mode 100644 flex_container_orchestrator/domain/greeting.py create mode 100644 flex_container_orchestrator/main.py create mode 100644 flex_container_orchestrator/routers/__init__.py create mode 100644 flex_container_orchestrator/routers/greeting_router.py create mode 100644 flex_container_orchestrator/services/__init__.py create mode 100644 flex_container_orchestrator/services/greeting_service.py create mode 100644 k8s/base/deployment.yaml create mode 100644 k8s/base/kustomization.yaml create mode 100644 k8s/base/route.yaml create mode 100644 k8s/base/service.yaml create mode 100644 k8s/overlays/depl/kustomization.yaml create mode 100644 k8s/overlays/devt/kustomization.yaml create mode 100644 k8s/overlays/prod/kustomization.yaml create mode 100644 pyproject.toml create mode 100644 sonar-project.properties create mode 100644 test/__init__.py create mode 100644 test/integration/__init__.py create mode 100644 test/integration/conftest.py create mode 100644 test/integration/routers/__init__.py create mode 100644 test/integration/routers/test_greeting_router.py create mode 100644 test/unit/__init__.py create mode 100644 test/unit/services/__init__.py create mode 100644 test/unit/services/test_greeting_service.py create mode 100644 uvicorn_logging_settings.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..546757d --- /dev/null +++ b/.gitignore @@ -0,0 +1,103 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ +junit* +test_reports/ +security_reports/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +doc/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# dotenv +.env + +# virtualenv +.venv +venv/ +ENV/ + +# IDE project settings +.editorconfig +.spyproject +.idea/ + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ diff --git a/.mch-ci.yml b/.mch-ci.yml new file mode 100644 index 0000000..75ef781 --- /dev/null +++ b/.mch-ci.yml @@ -0,0 +1,140 @@ +# Default configuration used by CI/CD. +# You can run locally 'mchbuild build' to create the images, +# 'mchbuild test' to run the tests using the images. The 'deploy' steps +# can be run separately. +default: + - build: + - getVersion: + gitCalculateVersion: + - imageTester: + containerBuildImage: + fullImageName: ${var.image}-tester + target: tester + extraArgs: + - --build-arg + - VERSION=${var.version} + - imageRunner: + containerBuildImage: + fullImageName: ${var.image} + target: runner + extraArgs: + - --build-arg + - VERSION=${var.version} + pullAlways: false + - docs: + - script: mkdir -p doc/_build + - pythonDocs: + fullImageName: ${var.image}-tester + packageManager: '' + pullAlways: false + - test: + - getVersion: + gitCalculateVersion: + - unit: + - script: mkdir -p test_reports + - pythonTest: + fullImageName: ${var.image}-tester + packageManager: '' + pullAlways: false + - pythonCoverage: + fullImageName: ${var.image}-tester + packageManager: '' + pullAlways: false + - lint: + - script: mkdir -p test_reports + - pythonLint: + fullImageName: ${var.image}-tester + packageManager: '' + pullAlways: false + - pythonTypeChecker: + fullImageName: ${var.image}-tester + packageManager: '' + pullAlways: false + - verify: + - configurationSecurityScan: + - securityReport: + format: [ 'html', 'table', 'print'] + severity: [ 'CRITICAL', 'HIGH' ] + target: config://k8s + prefix: k8s + qualityGate: + threshold: 5 + criticalFactor: 5 + highFactor: 1 + - imageSecurityScan: + - securityReport: + format: [ 'html', 'sbom', 'table', 'print'] + severity: [ 'CRITICAL', 'HIGH' ] + removeLocalImage: false + pullAlways: false + qualityGate: + threshold: 20 + criticalFactor: 5 + highFactor: 1 + - publishSbom: + - securityPublishSbom: + - deploy: + - addNextTag: + gitAddNextTag: + - addTag: + gitAddTag: + - docs: + openshiftPublishDocs: + docSrc: doc/_build/ + docType: python + - image: + containerPublishImage: + removeLocalImage: false + - cpDelete: + openshiftDelete: + - cp: + openshiftDeploy: + - cpRestart: + openshiftRestart: + - clean: + - getVersion: + gitCalculateVersion: + - images: + script: | + if test -n "${var.version}"; then + podman image rm -f $(podman image ls -q \ + -f "label=ch.meteoswiss.project=${var.project}-${var.version}") || : + fi + +# Alternative configuration to install and test the service locally using +# the local Python installation. Besides 'mchbuild local.build' and +# 'mchbuild local.test' it is possible to run the job using +# 'mchbuild local.run'. +local: + - build: + - install: + pythonInstall: + - docs: + pythonDocs: + - format: + pythonFormat: + inPlace: true + - test: + - unit: + - script: mkdir -p test_reports + - pythonTest: + - pythonCoverage: + - lint: + - script: mkdir -p test_reports + - pythonLint: + - pythonTypeChecker: + - run: + - main: + - pythonRun: + commands: | + echo 'Try it out at http://localhost:8080/flex-container-orchestrator/swagger-ui.html' + echo 'Shut it down with Ctrl-C' + poetry run uvicorn --port 8080 --reload flex_container_orchestrator.main:app + +variables: + project: flex-container-orchestrator + solution: dispersionmodelling + ocpHostNameForEnv: + devt: api.cpnonprod.meteoswiss.ch:6443 + depl: api.cpnonprod.meteoswiss.ch:6443 + prod: api.cp.meteoswiss.ch:6443 \ No newline at end of file diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..e4fba21 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.12 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..2684bff --- /dev/null +++ b/Dockerfile @@ -0,0 +1,47 @@ +FROM dockerhub.apps.cp.meteoswiss.ch/mch/python/builder AS builder +ARG VERSION +LABEL ch.meteoswiss.project=flex-container-orchestrator-${VERSION} + +COPY poetry.lock pyproject.toml /src/app-root/ + +WORKDIR /src/app-root + +RUN poetry export -o requirements.txt --without-hashes \ + && poetry export --with dev -o requirements_dev.txt --without-hashes + + +FROM dockerhub.apps.cp.meteoswiss.ch/mch/python-3.12:latest-slim AS base +ARG VERSION +LABEL ch.meteoswiss.project=flex-container-orchestrator-${VERSION} + +COPY --from=builder /src/app-root/requirements.txt /src/app-root/requirements.txt + +WORKDIR /src/app-root + +RUN pip install -r requirements.txt --no-cache-dir --no-deps --root-user-action=ignore + +COPY uvicorn_logging_settings.json /src/app-root/uvicorn_logging_settings.json + +COPY flex_container_orchestrator /src/app-root/flex_container_orchestrator + +FROM base AS tester +ARG VERSION +LABEL ch.meteoswiss.project=flex-container-orchestrator-${VERSION} + +COPY --from=builder /src/app-root/requirements_dev.txt /src/app-root/requirements_dev.txt +RUN pip install -r /src/app-root/requirements_dev.txt --no-cache-dir --no-deps --root-user-action=ignore + +COPY pyproject.toml /src/app-root/ +COPY test /src/app-root/test + +FROM base AS runner +ARG VERSION +LABEL ch.meteoswiss.project=flex-container-orchestrator-${VERSION} + +ENV VERSION=$VERSION + +# For running outside of OpenShift, we want to make sure that the container is run without root privileges +# uid 1001 is defined in the base-container-images for this purpose +USER 1001 + +CMD ["uvicorn", "--host", "0.0.0.0", "--port", "8080", "flex_container_orchestrator.main:app", "--log-config", "uvicorn_logging_settings.json"] diff --git a/HISTORY.rst b/HISTORY.rst new file mode 100644 index 0000000..a866ce2 --- /dev/null +++ b/HISTORY.rst @@ -0,0 +1,8 @@ +======= +History +======= + +1.0.0 (2024-10-14) +------------------ + +* First release diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000..3f24ad9 --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,345 @@ +class Globals { + // sets the pipeline to execute all steps related to building the service + static boolean build = false + + // sets to abort the pipeline if the Sonarqube QualityGate fails + static boolean qualityGateAbortPipeline = false + + // sets the pipeline to execute all steps related to releasing the service + static boolean release = false + + // sets the pipeline to execute all steps related to deployment of the service + static boolean deploy = false + + // sets the pipeline to execute all steps related to delete the service from the container platform + static boolean deleteContainer = false + + // sets the pipeline to execute all steps related to trigger the security scan + static boolean runSecurityScan = false + + // the project name in container platform + static String ocpProject = '' + + // Container deployment environment + static String deployEnv = '' + + // the image tag used for tagging the image + static String imageTag = '' + + // the service version + static String version = '' + + // sets the pipeline to execute all steps related to restart the service + static boolean restart = false +} + + +pipeline { + agent { label 'podman' } + + parameters { + choice(choices: ['Build', 'Deploy', 'Release', 'Restart', 'Delete', 'Security-Scan'], + description: 'Build type', + name: 'buildChoice') + + choice(choices: ['devt', 'depl', 'prod'], + description: 'Environment', + name: 'environment') + + booleanParam(name: 'PUBLISH_DOCUMENTATION', defaultValue: false, description: 'Publishes the generated documentation') + } + + options { + // New jobs should wait until older jobs are finished + disableConcurrentBuilds() + // Discard old builds + buildDiscarder(logRotator(artifactDaysToKeepStr: '7', artifactNumToKeepStr: '1', daysToKeepStr: '45', numToKeepStr: '10')) + // Timeout the pipeline build after 1 hour + timeout(time: 1, unit: 'HOURS') + gitLabConnection('CollabGitLab') + } + + environment { + PATH = "$workspace/.venv-mchbuild/bin:$HOME/tools/openshift-client-tools:$HOME/tools/trivy:$PATH" + KUBECONFIG = "$workspace/.kube/config" + HTTP_PROXY = 'http://proxy.meteoswiss.ch:8080' + HTTPS_PROXY = 'http://proxy.meteoswiss.ch:8080' + SCANNER_HOME = tool name: 'Sonarqube-certs-PROD', type: 'hudson.plugins.sonar.SonarRunnerInstallation' + } + + stages { + stage('Preflight') { + steps { + updateGitlabCommitStatus name: 'Build', state: 'running' + + script { + echo '---- INSTALL MCHBUILD ----' + sh ''' + python -m venv .venv-mchbuild + PIP_INDEX_URL=https://hub.meteoswiss.ch/nexus/repository/python-all/simple \ + .venv-mchbuild/bin/pip install --upgrade mchbuild + ''' + echo '---- INITIALIZE PARAMETERS ----' + Globals.deployEnv = params.environment + Globals.ocpProject = Globals.deployEnv + ? sh(script: "mchbuild openshiftExposeProperties -s deploymentEnvironment=${Globals.deployEnv} -g ocpProject", + returnStdout: true) : '' + // Determine the type of build + switch (params.buildChoice) { + case 'Build': + Globals.build = true + break + case 'Deploy': + Globals.deploy = true + break + case 'Release': + Globals.release = true + Globals.build = true + break + case 'Delete': + Globals.deleteContainer = true + break + case 'Security-Scan': + Globals.runSecurityScan = true + break + case 'Restart': + Globals.restart = true + break + } + + if (Globals.release) { + echo '---- TAGGING RELEASE ----' + sh 'mchbuild deploy.addNextTag' + } + + if (Globals.build || Globals.deploy || Globals.runSecurityScan) { + def versionAndTag = sh( + script: 'mchbuild -g version -g image build.getVersion', + returnStdout: true + ).split('\n') + Globals.version = versionAndTag[0] + Globals.imageTag = versionAndTag[1] + echo "Using version ${Globals.version} and image tag ${Globals.imageTag}" + } + } + } + } + + + stage('Build') { + when { expression { Globals.build } } + steps { + echo '---- BUILD IMAGE ----' + sh """ + mchbuild -s version=${Globals.version} -s image=${Globals.imageTag} \ + build.imageTester test.unit + """ + } + post { + always { + junit keepLongStdio: true, testResults: 'test_reports/junit*.xml' + } + } + } + + + stage('Scan') { + when { expression { Globals.build } } + steps { + echo '---- LINT & TYPE CHECK ----' + sh "mchbuild -s image=${Globals.imageTag} test.lint" + script { + try { + recordIssues(qualityGates: [[threshold: 10, type: 'TOTAL', unstable: false]], tools: [myPy(pattern: 'test_reports/mypy.log')]) + } + catch (err) { + error "Too many mypy issues, exiting now..." + } + } + + echo("---- MISCONFIGURATIONS CHECK ----") + sh "mchbuild verify.configurationSecurityScan" + + echo("---- SONARQUBE ANALYSIS ----") + withSonarQubeEnv("Sonarqube-PROD") { + // fix source path in coverage.xml + // (required because coverage is calculated using podman which uses a differing file structure) + // https://stackoverflow.com/questions/57220171/sonarqube-client-fails-to-parse-pytest-coverage-results + sh "sed -i 's/\\/src\\/app-root/.\\//g' test_reports/coverage.xml" + sh "${SCANNER_HOME}/bin/sonar-scanner" + } + + echo("---- SONARQUBE QUALITY GATE ----") + timeout(time: 1, unit: 'HOURS') { + // Parameter indicates whether to set pipeline to UNSTABLE if Quality Gate fails + // true = set pipeline to UNSTABLE, false = don't + waitForQualityGate abortPipeline: Globals.qualityGateAbortPipeline + } + } + } + + + + + stage('Create Artifacts') { + when { expression { Globals.build || Globals.deploy || params.PUBLISH_DOCUMENTATION } } + steps { + script { + if (Globals.build || Globals.deploy) { + echo '---- CREATE IMAGE ----' + sh """ + mchbuild -s version=${Globals.version} -s image=${Globals.imageTag} \ + build.imageRunner + """ + } + if (params.PUBLISH_DOCUMENTATION) { + echo '---- CREATE DOCUMENTATION ----' + sh """ + mchbuild -s version=${Globals.version} -s image=${Globals.imageTag} \ + build.docs + """ + } + } + } + } + + stage('Publish Artifacts') { + when { expression { Globals.deploy || Globals.release || params.PUBLISH_DOCUMENTATION } } + environment { + REGISTRY_AUTH_FILE = "$workspace/.containers/auth.json" + } + steps { + script { + if (Globals.deploy || Globals.release) { + echo "---- PUBLISH IMAGE ----" + withCredentials([usernamePassword(credentialsId: 'openshift-nexus', + passwordVariable: 'NXPASS', + usernameVariable: 'NXUSER')]) { + sh "mchbuild deploy.image -s fullImageName=${Globals.imageTag}" + } + } + } + script { + if (params.PUBLISH_DOCUMENTATION) { + echo "---- PUBLISH DOCUMENTATION ----" + withCredentials([string(credentialsId: 'documentation-main-prod-token', + variable: 'DOC_TOKEN')]) { + sh """ + mchbuild deploy.docs -s deploymentEnvironment=prod \ + -s docVersion=${Globals.version} + """ + } + } + } + } + } + + stage('Image Security Scan') { + when { + expression { Globals.runSecurityScan} + } + steps { + script { + echo '---- RUN SECURITY SCAN ----' + sh "mchbuild verify.imageSecurityScan -s deploymentEnvironment=${Globals.deployEnv}" + } + } + } + + stage('Deploy') { + when { expression { Globals.deploy } } + environment { + REGISTRY_AUTH_FILE = "$workspace/.containers/auth.json" + } + steps { + script { + // manual confirmation step for production deployment + if (Globals.deployEnv.contains('prod')) { + input message: 'Are you sure you want to deploy to PROD?', ok: 'Deploy' + } + + // we tag the current commit as the one deployed to the target environment + sh "mchbuild -s gitTag=${Globals.deployEnv} deploy.addTag" + + withCredentials([usernamePassword(credentialsId: 'openshift-nexus', + passwordVariable: 'NXPASS', + usernameVariable: 'NXUSER')]) { + echo 'Push to image registry' + sh """ + mchbuild deploy.image -s deploymentEnvironment=${Globals.deployEnv} \ + -s imageToTag=${Globals.imageTag} + """ + } + + withCredentials([usernamePassword(credentialsId: 'openshift-nexus', + passwordVariable: 'NXPASS', + usernameVariable: 'NXUSER'), + string(credentialsId: "${Globals.ocpProject}-token", + variable: 'OCP_TOKEN')]) { + sh "mchbuild deploy.cp -s deploymentEnvironment=${Globals.deployEnv}" + } + + // The security report is uploaded once the image has been deployed + withCredentials([string(credentialsId: 'dependency-track-token-prod', variable: 'DTRACK_TOKEN')]) { + catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') { + echo 'Upload the security report to the dependency track' + sh """ + mchbuild verify.imageSecurityScan \ + verify.publishSbom -s deploymentEnvironment=${Globals.deployEnv} + """ + } + } + } + } + } + + + stage('Restart Deployment') { + when { expression { Globals.restart } } + steps { + withCredentials([string(credentialsId: "${Globals.ocpProject}-token", + variable: 'OCP_TOKEN')]) { + sh "mchbuild deploy.cpRestart -s deploymentEnvironment=${Globals.deployEnv}" + } + } + } + + + stage('Delete Deployment') { + when { expression { Globals.deleteContainer } } + steps { + withCredentials([string(credentialsId: "${Globals.ocpProject}-token", + variable: 'OCP_TOKEN')]) { + sh "mchbuild deploy.cpDelete -s deploymentEnvironment=${Globals.deployEnv}" + } + } + } + } + + + post { + cleanup { + sh """ + mchbuild -s image=${Globals.imageTag} \ + -s deploymentEnvironment=${Globals.deployEnv} clean + """ + } + aborted { + updateGitlabCommitStatus name: 'Build', state: 'canceled' + } + failure { + updateGitlabCommitStatus name: 'Build', state: 'failed' + echo 'Sending email' + sh 'df -h' + emailext(subject: "${currentBuild.fullDisplayName}: ${currentBuild.currentResult}", + attachLog: true, + attachmentsPattern: 'generatedFile.txt', + body: "Job '${env.JOB_NAME} #${env.BUILD_NUMBER}': ${env.BUILD_URL}", + recipientProviders: [requestor(), developers()]) + } + success { + echo 'Build succeeded' + updateGitlabCommitStatus name: 'Build', state: 'success' + } + } +} diff --git a/LICENSE b/LICENSE index ccdb596..1530322 100644 --- a/LICENSE +++ b/LICENSE @@ -1,20 +1,21 @@ BSD 3-Clause License Copyright (c) 2024, MeteoSwiss +Authors: Nina Burgdorfer Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: -1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. -3. Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..b596907 --- /dev/null +++ b/README.rst @@ -0,0 +1,79 @@ +=============== +Getting started +=============== + +--------------------- +Setup dev environment +--------------------- + +Instead of running the steps below manually, you can install `mchbuild` and then +install, test and run the application: + +.. code-block:: console + + $ pipx install mchbuild + $ cd flex-container-orchestrator + $ mchbuild local.build local.test + $ mchbuild local.run + +Try it out at and stop it with Ctrl-C. More information can be found in :file:`.mch-ci.yml`. + +------------------------------------------------ +Install dependencies & start the project locally +------------------------------------------------ + +1. Enter the project folder: + +.. code-block:: console + + $ cd flex-container-orchestrator + +2. Install packages + +.. code-block:: console + + $ poetry install + +3. Run the flex-container-orchestrator + +.. code-block:: console + + $ poetry run uvicorn --port 8080 --reload flex_container_orchestrator.main:app + +------------------------------- +Run the tests and quality tools +------------------------------- + +1. Run tests + +.. code-block:: console + + $ poetry run pytest + +2. Run pylint + +.. code-block:: console + + $ poetry run pylint flex_container_orchestrator + + +3. Run mypy + +.. code-block:: console + + $ poetry run mypy flex_container_orchestrator + + +---------------------- +Generate documentation +---------------------- + +.. code-block:: console + + $ poetry run sphinx-build doc doc/_build + +Then open the index.html file generated in *flex-container-orchestrator/build/sphinx/html* + + +.. HINT:: + All **poetry run** prefixes in the commands can be avoided if running them within the poetry shell diff --git a/doc/conf.py b/doc/conf.py new file mode 100755 index 0000000..b43c867 --- /dev/null +++ b/doc/conf.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# flex-container-orchestrator documentation build configuration file, created by +# sphinx-quickstart on Fri Jun 9 13:47:02 2017. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +# If extensions (or modules to document with autodoc) are in another +# directory, add these directories to sys.path here. If the directory is +# relative to the documentation root, use os.path.abspath to make it +# absolute, like shown here. +# +import os +import sys +sys.path.insert(0, os.path.abspath('..')) + +# -- General configuration --------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +# +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode', 'sphinx.ext.napoleon', 'autoapi.extension'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'flex-container-orchestrator' +copyright = u"2024, MeteoSwiss" +author = u"Nina Burgdorfer" + +# The version info for the project you're documenting, acts as replacement +# for |version| and |release|, also used in various other places throughout +# the built documents. +# + +# The version is fetched from the environment variable VERSION +version = release = os.getenv("VERSION", default="") + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = "en" + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This patterns also effect to html_static_path and html_extra_path +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = False + +# -- Options for HTML output ------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'pydata_sphinx_theme' + +# Theme options are theme-specific and customize the look and feel of a +# theme further. For a list of options available for each theme, see the +# documentation. +# +# html_theme_options = {} + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# -- Options for HTMLHelp output --------------------------------------- + +# Output file base name for HTML help builder. +htmlhelp_basename = 'flex_container_orchestrator/doc' + +# -- Options for LaTeX output ------------------------------------------ + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # + # 'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + # + # 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # + # 'preamble': '', + + # Latex figure (float) alignment + # + # 'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass +# [howto, manual, or own class]). +latex_documents = [ + (master_doc, 'flex_container_orchestrator.tex', + u'flex-container-orchestrator Documentation', + u'Nina Burgdorfer', 'manual'), +] + +# -- Options for manual page output ------------------------------------ + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [(master_doc, 'flex_container_orchestrator', + u'flex-container-orchestrator Documentation', + [author], 1)] + +# -- Options for Texinfo output ---------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, 'flex_container_orchestrator', + u'flex-container-orchestrator Documentation', author, + 'flex_container_orchestrator', + 'Service listening to Aviso and launching Flexpart-IFS', + 'Miscellaneous'), +] + +# improve parameters description +napoleon_use_param = False + +# avoid the display of redundant module names +add_module_names = False + +# autoapi module configuration +autoapi_dirs = ['../flex_container_orchestrator'] +autoapi_options = ['members', 'undoc-members', 'show-inheritance', 'show-module-summary', 'imported-members'] diff --git a/doc/history.rst b/doc/history.rst new file mode 100644 index 0000000..2506499 --- /dev/null +++ b/doc/history.rst @@ -0,0 +1 @@ +.. include:: ../HISTORY.rst diff --git a/doc/index.rst b/doc/index.rst new file mode 100644 index 0000000..cf76abb --- /dev/null +++ b/doc/index.rst @@ -0,0 +1,18 @@ +flex-container-orchestrator documentation +================================================ + +Service listening to Aviso and launching Flexpart-IFS + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + readme + autoapi/index + history + +Indices and tables +================== +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/doc/readme.rst b/doc/readme.rst new file mode 100644 index 0000000..72a3355 --- /dev/null +++ b/doc/readme.rst @@ -0,0 +1 @@ +.. include:: ../README.rst diff --git a/flex_container_orchestrator/__init__.py b/flex_container_orchestrator/__init__.py new file mode 100644 index 0000000..9e25878 --- /dev/null +++ b/flex_container_orchestrator/__init__.py @@ -0,0 +1,10 @@ +""" Initializations """ +import os + +from mchpy.audit import logger +from flex_container_orchestrator.config.service_settings import ServiceSettings + +CONFIG = ServiceSettings('settings.yaml', os.path.join(os.path.dirname(__file__), 'config')) + +# Configure logger +logger.apply_logging_settings(CONFIG.logging) diff --git a/flex_container_orchestrator/config/__init__.py b/flex_container_orchestrator/config/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/flex_container_orchestrator/config/service_settings.py b/flex_container_orchestrator/config/service_settings.py new file mode 100644 index 0000000..55c85a2 --- /dev/null +++ b/flex_container_orchestrator/config/service_settings.py @@ -0,0 +1,14 @@ +from pydantic import BaseModel + +from mchpy.audit.logger import LoggingSettings +from mchpy.config.base_settings import BaseServiceSettings + + +class AppSettings(BaseModel): + """The main application settings""" + app_name: str + + +class ServiceSettings(BaseServiceSettings): + logging: LoggingSettings + main: AppSettings diff --git a/flex_container_orchestrator/config/settings.yaml b/flex_container_orchestrator/config/settings.yaml new file mode 100644 index 0000000..16b2318 --- /dev/null +++ b/flex_container_orchestrator/config/settings.yaml @@ -0,0 +1,10 @@ +logging: + root_log_level: INFO + # When running in k8s, use JSON logger via env setting + # formatter: json + formatter: "standard" + child_log_levels: + mchpy: DEBUG + mchpy.audit.http_audit: INFO +main: + app_name: flex-container-orchestrator diff --git a/flex_container_orchestrator/domain/__init__.py b/flex_container_orchestrator/domain/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/flex_container_orchestrator/domain/greeting.py b/flex_container_orchestrator/domain/greeting.py new file mode 100644 index 0000000..e921010 --- /dev/null +++ b/flex_container_orchestrator/domain/greeting.py @@ -0,0 +1,6 @@ +from pydantic import BaseModel # pylint: disable=no-name-in-module + + +class Greeting(BaseModel): + """The greeting's model""" + message: str diff --git a/flex_container_orchestrator/main.py b/flex_container_orchestrator/main.py new file mode 100644 index 0000000..b37f6e3 --- /dev/null +++ b/flex_container_orchestrator/main.py @@ -0,0 +1,31 @@ +"""" Define application """ +import os + +from mchpy.web import fastapi_app +from flex_container_orchestrator.routers import greeting_router + +_SERVICE_NAMESPACE = 'flex-container-orchestrator' +_URL_PREFIX = f'/{_SERVICE_NAMESPACE}/api/v1' + +# Create the application instance +app = fastapi_app.create( + title='flex-container-orchestrator', + description='Service listening to Aviso and launching Flexpart-IFS', + contact={ + 'name': 'Nina Burgdorfer', + 'email': 'nina.burgdorfer@meteoswiss.ch', + }, + version=os.getenv('VERSION') or '1.0.0', + base_path=f'/{_SERVICE_NAMESPACE}', + docs_url=f'/{_SERVICE_NAMESPACE}/swagger-ui.html', + openapi_url=f'/{_SERVICE_NAMESPACE}/openapi.json', + redoc_url=None, +) + +# include routers +app.include_router(greeting_router.router, prefix=_URL_PREFIX) + +# If we're running in standalone mode, run the application +if __name__ == '__main__': + import uvicorn + uvicorn.run(app, host='0.0.0.0', port=8080) diff --git a/flex_container_orchestrator/routers/__init__.py b/flex_container_orchestrator/routers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/flex_container_orchestrator/routers/greeting_router.py b/flex_container_orchestrator/routers/greeting_router.py new file mode 100644 index 0000000..0b8f470 --- /dev/null +++ b/flex_container_orchestrator/routers/greeting_router.py @@ -0,0 +1,16 @@ +"""Greeting FastAPI router""" +from http import HTTPStatus +import logging + +from fastapi import APIRouter + +from flex_container_orchestrator.services import greeting_service +from flex_container_orchestrator.domain.greeting import Greeting + +router = APIRouter() +_LOGGER = logging.getLogger(__name__) + + +@router.get('/greeting/{name}', status_code=HTTPStatus.OK) +def get_greeting(name: str) -> Greeting: + return greeting_service.get_greeting(name) diff --git a/flex_container_orchestrator/services/__init__.py b/flex_container_orchestrator/services/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/flex_container_orchestrator/services/greeting_service.py b/flex_container_orchestrator/services/greeting_service.py new file mode 100644 index 0000000..9faf97c --- /dev/null +++ b/flex_container_orchestrator/services/greeting_service.py @@ -0,0 +1,25 @@ +""" +Services providing core functionality. +""" + +import logging + +from flex_container_orchestrator import CONFIG +from flex_container_orchestrator.domain.greeting import Greeting + +logger = logging.getLogger(__name__) + + +def get_greeting(name: str) -> Greeting: + """ + Get personalized greeting + + :param name: name, as the name implies + :type name: str + + :return: personalized greeting + :rtype: Greeting + """ + logger.debug('Personalizing greeting for %s...', name) + + return Greeting(message=f'Hello, {name} from {CONFIG.main.app_name}!') diff --git a/k8s/base/deployment.yaml b/k8s/base/deployment.yaml new file mode 100644 index 0000000..b74e084 --- /dev/null +++ b/k8s/base/deployment.yaml @@ -0,0 +1,61 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: flex-container-orchestrator + logFormat: json + name: flex-container-orchestrator +spec: + progressDeadlineSeconds: 600 + replicas: 1 + revisionHistoryLimit: 10 + selector: + matchLabels: + app: flex-container-orchestrator + strategy: + rollingUpdate: + maxSurge: 25% + maxUnavailable: 25% + type: RollingUpdate + template: + metadata: + labels: + app: flex-container-orchestrator + spec: + containers: + - name: flex-container-orchestrator + image: flex-container-orchestrator + imagePullPolicy: Always + readinessProbe: + timeoutSeconds: 3 + initialDelaySeconds: 10 + periodSeconds: 15 + httpGet: + path: "/flex-container-orchestrator/management/health/readiness" + port: 8080 + livenessProbe: + timeoutSeconds: 3 + initialDelaySeconds: 20 + periodSeconds: 20 + httpGet: + path: "/flex-container-orchestrator/management/health/liveness" + port: 8080 + ports: + - containerPort: 8080 + protocol: TCP + resources: + requests: + cpu: 5m + memory: 100Mi + limits: + cpu: "1" + memory: 100Mi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + env: + - name: LOGGING__FORMATTER + value: json + envFrom: + - configMapRef: + name: flex-container-orchestrator-config + diff --git a/k8s/base/kustomization.yaml b/k8s/base/kustomization.yaml new file mode 100644 index 0000000..95a466b --- /dev/null +++ b/k8s/base/kustomization.yaml @@ -0,0 +1,6 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - deployment.yaml + - service.yaml + - route.yaml diff --git a/k8s/base/route.yaml b/k8s/base/route.yaml new file mode 100644 index 0000000..14968af --- /dev/null +++ b/k8s/base/route.yaml @@ -0,0 +1,19 @@ +apiVersion: route.openshift.io/v1 +kind: Route +metadata: + labels: + app: flex-container-orchestrator + name: flex-container-orchestrator +spec: + host: "" + path: / + to: + kind: Service + name: flex-container-orchestrator + weight: 100 + tls: + termination: edge + insecureEdgeTerminationPolicy: Allow +status: + ingress: + - routerName: router diff --git a/k8s/base/service.yaml b/k8s/base/service.yaml new file mode 100644 index 0000000..750fab6 --- /dev/null +++ b/k8s/base/service.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + app: flex-container-orchestrator + name: flex-container-orchestrator +spec: + ports: + - name: http + port: 8080 + protocol: TCP + targetPort: 8080 + selector: + app: flex-container-orchestrator + type: ClusterIP + diff --git a/k8s/overlays/depl/kustomization.yaml b/k8s/overlays/depl/kustomization.yaml new file mode 100644 index 0000000..52f9391 --- /dev/null +++ b/k8s/overlays/depl/kustomization.yaml @@ -0,0 +1,15 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +namespace: dispersionmodelling-main-depl +generatorOptions: + disableNameSuffixHash: true +resources: + - ../../base +images: + - name: flex-container-orchestrator + newName: docker-all-nexus.meteoswiss.ch/dispersionmodelling/flex-container-orchestrator + newTag: depl +configMapGenerator: + - name: flex-container-orchestrator-config + literals: + - LOGGING__ROOT_LOG_LEVEL=INFO \ No newline at end of file diff --git a/k8s/overlays/devt/kustomization.yaml b/k8s/overlays/devt/kustomization.yaml new file mode 100644 index 0000000..3c3d033 --- /dev/null +++ b/k8s/overlays/devt/kustomization.yaml @@ -0,0 +1,15 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +namespace: dispersionmodelling-main-devt +generatorOptions: + disableNameSuffixHash: true +resources: + - ../../base +images: + - name: flex-container-orchestrator + newName: docker-all-nexus.meteoswiss.ch/dispersionmodelling/flex-container-orchestrator + newTag: devt +configMapGenerator: + - name: flex-container-orchestrator-config + literals: + - LOGGING__ROOT_LOG_LEVEL=DEBUG \ No newline at end of file diff --git a/k8s/overlays/prod/kustomization.yaml b/k8s/overlays/prod/kustomization.yaml new file mode 100644 index 0000000..c0d72d8 --- /dev/null +++ b/k8s/overlays/prod/kustomization.yaml @@ -0,0 +1,15 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +namespace: dispersionmodelling-main-prod +generatorOptions: + disableNameSuffixHash: true +resources: + - ../../base +images: + - name: flex-container-orchestrator + newName: docker-all-nexus.meteoswiss.ch/dispersionmodelling/flex-container-orchestrator + newTag: prod +configMapGenerator: + - name: flex-container-orchestrator-config + literals: + - LOGGING__ROOT_LOG_LEVEL=INFO \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..c8a6d09 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,63 @@ +[[tool.poetry.source]] +name = "meteoswiss" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" + +[tool.poetry] +name = "flex-container-orchestrator" +version = "1.0" +description = "Service listening to Aviso and launching Flexpart-IFS" +authors = ["Nina Burgdorfer "] +readme = "README.rst" +include = ["HISTORY.rst"] + + +[tool.poetry.dependencies] +python = "~3.12" +mchpy = { extras = ["fastapi"], version = "^2.1.1" } + +[tool.poetry.group.dev.dependencies] +mypy = "*" +pydata-sphinx-theme = "*" +pylint = "*" +pytest = "*" +pytest-cov = "*" +sphinx = "*" +sphinx-autoapi = "*" +yapf = "*" + +[tool.yapf] +based_on_style = "pep8" +column_limit = "120" + +[tool.pylint.master] +disable = [ + 'C0114', # missing-module-docstring + 'C0115', # missing-class-docstring + 'C0116', # missing-function-docstring + 'W0511', # fix me or to-do comments are already covered by SonarQube +] + +[tool.pylint.basic] +argument-naming-style = 'any' +attr-naming-style = 'any' + +[tool.pylint.format] +# Maximum number of lines in a module. +max-line-length = 120 + +[tool.pylint.design] +# Minimum number of public methods for a class (see R0903). +min-public-methods = 0 + +[tool.mypy] +ignore_missing_imports = true +disallow_untyped_defs = true + +[[tool.mypy.overrides]] +# to avoid mypy errors when importing modules from commons +# aka "module is installed, but missing library stubs or py.typed marker" +module = ["mchpy.*"] + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 0000000..1c0a2bc --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,9 @@ +sonar.projectVersion=n/a +sonar.projectName=flex-container-orchestrator +sonar.projectKey=flex-container-orchestrator_RphmWnZVdwWO +sonar.python.version=3.12 +sonar.python.xunit.reportPath=test_reports/junit.xml +sonar.python.coverage.reportPaths=test_reports/coverage.xml +sonar.python.pylint.reportPath=test_reports/pylint.log +sonar.coverage.exclusions=test/**,doc/**, test_reports/** +sonar.exclusions=doc/**, security_reports/** diff --git a/test/__init__.py b/test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/integration/__init__.py b/test/integration/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/integration/conftest.py b/test/integration/conftest.py new file mode 100644 index 0000000..2f0ee91 --- /dev/null +++ b/test/integration/conftest.py @@ -0,0 +1,11 @@ +import pytest + +from fastapi.testclient import TestClient + +from flex_container_orchestrator.main import app + + +@pytest.fixture +def test_client(): + with TestClient(app) as client: + yield client diff --git a/test/integration/routers/__init__.py b/test/integration/routers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/integration/routers/test_greeting_router.py b/test/integration/routers/test_greeting_router.py new file mode 100644 index 0000000..83f0270 --- /dev/null +++ b/test/integration/routers/test_greeting_router.py @@ -0,0 +1,13 @@ +from http import HTTPStatus + + +def test_get(test_client): + # given + name = 'MeteoSwiss' + + # when + response = test_client.get(f'/flex-container-orchestrator/api/v1/greeting/{name}') + + # then + assert response.status_code == HTTPStatus.OK + assert response.json() == {'message': f'Hello, {name} from flex-container-orchestrator!'} diff --git a/test/unit/__init__.py b/test/unit/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/unit/services/__init__.py b/test/unit/services/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/unit/services/test_greeting_service.py b/test/unit/services/test_greeting_service.py new file mode 100644 index 0000000..fee63b1 --- /dev/null +++ b/test/unit/services/test_greeting_service.py @@ -0,0 +1,16 @@ +from flex_container_orchestrator import CONFIG +from flex_container_orchestrator.services import greeting_service +from flex_container_orchestrator.domain.greeting import Greeting + +def test_greeting_service(): + # given + name = 'World' + CONFIG.main.app_name = 'the test app' + + # when + result = greeting_service.get_greeting(name) + + expected = Greeting(message=f'Hello, {name} from the test app!') + + # then + assert result == expected \ No newline at end of file diff --git a/uvicorn_logging_settings.json b/uvicorn_logging_settings.json new file mode 100644 index 0000000..cb608f6 --- /dev/null +++ b/uvicorn_logging_settings.json @@ -0,0 +1 @@ +{"version": 1} From 58eee9dd94eccf0fc94b38952bbfd2baba242e53 Mon Sep 17 00:00:00 2001 From: ninaburg Date: Mon, 14 Oct 2024 16:29:19 +0200 Subject: [PATCH 08/37] adapt actual code to blueprint --- .env | 25 -------- README.rst | 6 ++ .../config/aviso}/README.md | 0 .../config/aviso}/config.yaml | 0 .../config/aviso}/listener_diss.yaml | 2 +- .../domain}/aggregator_flexpart.py | 0 flex_container_orchestrator/main.py | 40 +++++------- .../routers/__init__.py | 0 .../routers/greeting_router.py | 16 ----- .../services/flexpart_service.py | 19 +----- .../services/greeting_service.py | 25 -------- k8s/base/deployment.yaml | 61 ------------------- k8s/base/kustomization.yaml | 6 -- k8s/base/route.yaml | 19 ------ k8s/base/service.yaml | 16 ----- k8s/overlays/depl/kustomization.yaml | 15 ----- k8s/overlays/devt/kustomization.yaml | 15 ----- k8s/overlays/prod/kustomization.yaml | 15 ----- test/__init__.py | 0 test/integration/__init__.py | 0 test/integration/conftest.py | 11 ---- test/integration/routers/__init__.py | 0 .../routers/test_greeting_router.py | 13 ---- test/unit/__init__.py | 0 test/unit/services/__init__.py | 0 test/unit/services/test_greeting_service.py | 16 ----- 26 files changed, 22 insertions(+), 298 deletions(-) delete mode 100644 .env rename {aviso => flex_container_orchestrator/config/aviso}/README.md (100%) rename {aviso => flex_container_orchestrator/config/aviso}/config.yaml (100%) rename {aviso => flex_container_orchestrator/config/aviso}/listener_diss.yaml (89%) rename {aggregator => flex_container_orchestrator/domain}/aggregator_flexpart.py (100%) delete mode 100644 flex_container_orchestrator/routers/__init__.py delete mode 100644 flex_container_orchestrator/routers/greeting_router.py rename aviso/run_docker_script.py => flex_container_orchestrator/services/flexpart_service.py (87%) delete mode 100644 flex_container_orchestrator/services/greeting_service.py delete mode 100644 k8s/base/deployment.yaml delete mode 100644 k8s/base/kustomization.yaml delete mode 100644 k8s/base/route.yaml delete mode 100644 k8s/base/service.yaml delete mode 100644 k8s/overlays/depl/kustomization.yaml delete mode 100644 k8s/overlays/devt/kustomization.yaml delete mode 100644 k8s/overlays/prod/kustomization.yaml delete mode 100644 test/__init__.py delete mode 100644 test/integration/__init__.py delete mode 100644 test/integration/conftest.py delete mode 100644 test/integration/routers/__init__.py delete mode 100644 test/integration/routers/test_greeting_router.py delete mode 100644 test/unit/__init__.py delete mode 100644 test/unit/services/__init__.py delete mode 100644 test/unit/services/test_greeting_service.py diff --git a/.env b/.env deleted file mode 100644 index 1e882f0..0000000 --- a/.env +++ /dev/null @@ -1,25 +0,0 @@ -# .env - -## Flexprep (Flexpart pre-processing) - -# Input/output S3 bucket settings -SVC__MAIN__S3_BUCKETS__INPUT__ENDPOINT_URL=https://object-store.os-api.cci1.ecmwf.int -SVC__MAIN__S3_BUCKETS__INPUT__NAME=flexpart-input -SVC__MAIN__S3_BUCKETS__OUTPUT__ENDPOINT_URL=https://object-store.os-api.cci1.ecmwf.int -SVC__MAIN__S3_BUCKETS__OUTPUT__NAME=flexprep-output - -# Timestep settings -SVC__MAIN__TIME_SETTINGS__TINCR=1 # Interval in hours for forecasts (Global: 3h, Europe: 1h) -SVC__MAIN__TIME_SETTINGS__TSTART=0 # Start time - -## Flexpart-IFS - -# Input/output S3 bucket settings -SVC__MAIN__S3_BUCKETS__INPUT__ENDPOINT_URL=${SVC__MAIN__S3_BUCKETS__OUTPUT__ENDPOINT_URL} # Inherit from Flexprep output -SVC__MAIN__S3_BUCKETS__INPUT__NAME=${SVC__MAIN__S3_BUCKETS__OUTPUT__NAME} # Inherit from Flexprep output -SVC__MAIN__S3_BUCKETS__OUTPUT__ENDPOINT_URL=https://object-store.os-api.cci1.ecmwf.int -SVC__MAIN__S3_BUCKETS__OUTPUT__NAME=flexpart-output - -# EWC buckets access and secret keys -SVC__LOGGING__S3_ACCESS_KEY= -SVC__LOGGING__S3_SECRET_KEY= diff --git a/README.rst b/README.rst index b596907..7d17b94 100644 --- a/README.rst +++ b/README.rst @@ -1,3 +1,9 @@ +flex-container-orchestrator +=========================== + +The flex-container-orchestrator manages Aviso notifications and triggers the flexprep and flexpart-ifs containers, +as well as the file aggregation script for Flexpart. + =============== Getting started =============== diff --git a/aviso/README.md b/flex_container_orchestrator/config/aviso/README.md similarity index 100% rename from aviso/README.md rename to flex_container_orchestrator/config/aviso/README.md diff --git a/aviso/config.yaml b/flex_container_orchestrator/config/aviso/config.yaml similarity index 100% rename from aviso/config.yaml rename to flex_container_orchestrator/config/aviso/config.yaml diff --git a/aviso/listener_diss.yaml b/flex_container_orchestrator/config/aviso/listener_diss.yaml similarity index 89% rename from aviso/listener_diss.yaml rename to flex_container_orchestrator/config/aviso/listener_diss.yaml index d59f636..74b11a8 100644 --- a/aviso/listener_diss.yaml +++ b/flex_container_orchestrator/config/aviso/listener_diss.yaml @@ -12,7 +12,7 @@ listeners: - type: command working_dir: $HOME command: > - python3 .aviso/my_docker_script.py \ + python3 ../../services/aviso/run_docker_script.py \ --step "${request.step}" \ --date "${request.date}" \ --time "${request.time}" \ diff --git a/aggregator/aggregator_flexpart.py b/flex_container_orchestrator/domain/aggregator_flexpart.py similarity index 100% rename from aggregator/aggregator_flexpart.py rename to flex_container_orchestrator/domain/aggregator_flexpart.py diff --git a/flex_container_orchestrator/main.py b/flex_container_orchestrator/main.py index b37f6e3..40bc388 100644 --- a/flex_container_orchestrator/main.py +++ b/flex_container_orchestrator/main.py @@ -1,31 +1,19 @@ -"""" Define application """ import os +import logging +from flex_container_orchestrator.services import flexpart_service +import argparse -from mchpy.web import fastapi_app -from flex_container_orchestrator.routers import greeting_router +logger = logging.getLogger(__name__) -_SERVICE_NAMESPACE = 'flex-container-orchestrator' -_URL_PREFIX = f'/{_SERVICE_NAMESPACE}/api/v1' +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('--date', required=True, help='Date parameter') + parser.add_argument('--location', required=True, help='Location parameter') + parser.add_argument('--time', required=True, help='Time parameter') + parser.add_argument('--step', required=True, help='Step parameter') + args = parser.parse_args() -# Create the application instance -app = fastapi_app.create( - title='flex-container-orchestrator', - description='Service listening to Aviso and launching Flexpart-IFS', - contact={ - 'name': 'Nina Burgdorfer', - 'email': 'nina.burgdorfer@meteoswiss.ch', - }, - version=os.getenv('VERSION') or '1.0.0', - base_path=f'/{_SERVICE_NAMESPACE}', - docs_url=f'/{_SERVICE_NAMESPACE}/swagger-ui.html', - openapi_url=f'/{_SERVICE_NAMESPACE}/openapi.json', - redoc_url=None, -) + flexpart_service.launch_containers(args.date, args.location, args.time, args.step) -# include routers -app.include_router(greeting_router.router, prefix=_URL_PREFIX) - -# If we're running in standalone mode, run the application -if __name__ == '__main__': - import uvicorn - uvicorn.run(app, host='0.0.0.0', port=8080) +if __name__ == "__main__": + main() diff --git a/flex_container_orchestrator/routers/__init__.py b/flex_container_orchestrator/routers/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/flex_container_orchestrator/routers/greeting_router.py b/flex_container_orchestrator/routers/greeting_router.py deleted file mode 100644 index 0b8f470..0000000 --- a/flex_container_orchestrator/routers/greeting_router.py +++ /dev/null @@ -1,16 +0,0 @@ -"""Greeting FastAPI router""" -from http import HTTPStatus -import logging - -from fastapi import APIRouter - -from flex_container_orchestrator.services import greeting_service -from flex_container_orchestrator.domain.greeting import Greeting - -router = APIRouter() -_LOGGER = logging.getLogger(__name__) - - -@router.get('/greeting/{name}', status_code=HTTPStatus.OK) -def get_greeting(name: str) -> Greeting: - return greeting_service.get_greeting(name) diff --git a/aviso/run_docker_script.py b/flex_container_orchestrator/services/flexpart_service.py similarity index 87% rename from aviso/run_docker_script.py rename to flex_container_orchestrator/services/flexpart_service.py index ff4ebd5..a3d6a99 100755 --- a/aviso/run_docker_script.py +++ b/flex_container_orchestrator/services/flexpart_service.py @@ -1,5 +1,4 @@ import subprocess -import argparse import json import os import sys @@ -18,7 +17,7 @@ def run_command(command, capture_output=False): logging.error(f"Command '{' '.join(command)}' failed with error: {e}") sys.exit(1) -def main(): +def launch_containers(date, location, time, step): logging.basicConfig(level=logging.INFO) # Retrieve ECR login password and log in to Docker @@ -42,22 +41,6 @@ def main(): logging.error(f"Error logging in to Docker: {e}") sys.exit(1) - # Argument parsing - parser = argparse.ArgumentParser() - parser.add_argument('--date', required=True, help='Date parameter') - parser.add_argument('--location', required=True, help='Location parameter') - parser.add_argument('--time', required=True, help='Time parameter') - parser.add_argument('--step', required=True, help='Step parameter') - - args = parser.parse_args() - - date = args.date - location = args.location - time = args.time - step = args.step - - logging.info(f"Notification received for file {location}, date {date}, time {time}, step {step}") - # Run pre-processing for Flexpart docker_image = "493666016161.dkr.ecr.eu-central-2.amazonaws.com/numericalweatherpredictions/flexpart_ifs/flexprep:2409.ee22f6c67c86b9f85185edb02924e6ab523fa0bc" db_mount = os.path.expanduser('~/.sqlite/') diff --git a/flex_container_orchestrator/services/greeting_service.py b/flex_container_orchestrator/services/greeting_service.py deleted file mode 100644 index 9faf97c..0000000 --- a/flex_container_orchestrator/services/greeting_service.py +++ /dev/null @@ -1,25 +0,0 @@ -""" -Services providing core functionality. -""" - -import logging - -from flex_container_orchestrator import CONFIG -from flex_container_orchestrator.domain.greeting import Greeting - -logger = logging.getLogger(__name__) - - -def get_greeting(name: str) -> Greeting: - """ - Get personalized greeting - - :param name: name, as the name implies - :type name: str - - :return: personalized greeting - :rtype: Greeting - """ - logger.debug('Personalizing greeting for %s...', name) - - return Greeting(message=f'Hello, {name} from {CONFIG.main.app_name}!') diff --git a/k8s/base/deployment.yaml b/k8s/base/deployment.yaml deleted file mode 100644 index b74e084..0000000 --- a/k8s/base/deployment.yaml +++ /dev/null @@ -1,61 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app: flex-container-orchestrator - logFormat: json - name: flex-container-orchestrator -spec: - progressDeadlineSeconds: 600 - replicas: 1 - revisionHistoryLimit: 10 - selector: - matchLabels: - app: flex-container-orchestrator - strategy: - rollingUpdate: - maxSurge: 25% - maxUnavailable: 25% - type: RollingUpdate - template: - metadata: - labels: - app: flex-container-orchestrator - spec: - containers: - - name: flex-container-orchestrator - image: flex-container-orchestrator - imagePullPolicy: Always - readinessProbe: - timeoutSeconds: 3 - initialDelaySeconds: 10 - periodSeconds: 15 - httpGet: - path: "/flex-container-orchestrator/management/health/readiness" - port: 8080 - livenessProbe: - timeoutSeconds: 3 - initialDelaySeconds: 20 - periodSeconds: 20 - httpGet: - path: "/flex-container-orchestrator/management/health/liveness" - port: 8080 - ports: - - containerPort: 8080 - protocol: TCP - resources: - requests: - cpu: 5m - memory: 100Mi - limits: - cpu: "1" - memory: 100Mi - terminationMessagePath: /dev/termination-log - terminationMessagePolicy: File - env: - - name: LOGGING__FORMATTER - value: json - envFrom: - - configMapRef: - name: flex-container-orchestrator-config - diff --git a/k8s/base/kustomization.yaml b/k8s/base/kustomization.yaml deleted file mode 100644 index 95a466b..0000000 --- a/k8s/base/kustomization.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization -resources: - - deployment.yaml - - service.yaml - - route.yaml diff --git a/k8s/base/route.yaml b/k8s/base/route.yaml deleted file mode 100644 index 14968af..0000000 --- a/k8s/base/route.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: route.openshift.io/v1 -kind: Route -metadata: - labels: - app: flex-container-orchestrator - name: flex-container-orchestrator -spec: - host: "" - path: / - to: - kind: Service - name: flex-container-orchestrator - weight: 100 - tls: - termination: edge - insecureEdgeTerminationPolicy: Allow -status: - ingress: - - routerName: router diff --git a/k8s/base/service.yaml b/k8s/base/service.yaml deleted file mode 100644 index 750fab6..0000000 --- a/k8s/base/service.yaml +++ /dev/null @@ -1,16 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - labels: - app: flex-container-orchestrator - name: flex-container-orchestrator -spec: - ports: - - name: http - port: 8080 - protocol: TCP - targetPort: 8080 - selector: - app: flex-container-orchestrator - type: ClusterIP - diff --git a/k8s/overlays/depl/kustomization.yaml b/k8s/overlays/depl/kustomization.yaml deleted file mode 100644 index 52f9391..0000000 --- a/k8s/overlays/depl/kustomization.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization -namespace: dispersionmodelling-main-depl -generatorOptions: - disableNameSuffixHash: true -resources: - - ../../base -images: - - name: flex-container-orchestrator - newName: docker-all-nexus.meteoswiss.ch/dispersionmodelling/flex-container-orchestrator - newTag: depl -configMapGenerator: - - name: flex-container-orchestrator-config - literals: - - LOGGING__ROOT_LOG_LEVEL=INFO \ No newline at end of file diff --git a/k8s/overlays/devt/kustomization.yaml b/k8s/overlays/devt/kustomization.yaml deleted file mode 100644 index 3c3d033..0000000 --- a/k8s/overlays/devt/kustomization.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization -namespace: dispersionmodelling-main-devt -generatorOptions: - disableNameSuffixHash: true -resources: - - ../../base -images: - - name: flex-container-orchestrator - newName: docker-all-nexus.meteoswiss.ch/dispersionmodelling/flex-container-orchestrator - newTag: devt -configMapGenerator: - - name: flex-container-orchestrator-config - literals: - - LOGGING__ROOT_LOG_LEVEL=DEBUG \ No newline at end of file diff --git a/k8s/overlays/prod/kustomization.yaml b/k8s/overlays/prod/kustomization.yaml deleted file mode 100644 index c0d72d8..0000000 --- a/k8s/overlays/prod/kustomization.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization -namespace: dispersionmodelling-main-prod -generatorOptions: - disableNameSuffixHash: true -resources: - - ../../base -images: - - name: flex-container-orchestrator - newName: docker-all-nexus.meteoswiss.ch/dispersionmodelling/flex-container-orchestrator - newTag: prod -configMapGenerator: - - name: flex-container-orchestrator-config - literals: - - LOGGING__ROOT_LOG_LEVEL=INFO \ No newline at end of file diff --git a/test/__init__.py b/test/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/test/integration/__init__.py b/test/integration/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/test/integration/conftest.py b/test/integration/conftest.py deleted file mode 100644 index 2f0ee91..0000000 --- a/test/integration/conftest.py +++ /dev/null @@ -1,11 +0,0 @@ -import pytest - -from fastapi.testclient import TestClient - -from flex_container_orchestrator.main import app - - -@pytest.fixture -def test_client(): - with TestClient(app) as client: - yield client diff --git a/test/integration/routers/__init__.py b/test/integration/routers/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/test/integration/routers/test_greeting_router.py b/test/integration/routers/test_greeting_router.py deleted file mode 100644 index 83f0270..0000000 --- a/test/integration/routers/test_greeting_router.py +++ /dev/null @@ -1,13 +0,0 @@ -from http import HTTPStatus - - -def test_get(test_client): - # given - name = 'MeteoSwiss' - - # when - response = test_client.get(f'/flex-container-orchestrator/api/v1/greeting/{name}') - - # then - assert response.status_code == HTTPStatus.OK - assert response.json() == {'message': f'Hello, {name} from flex-container-orchestrator!'} diff --git a/test/unit/__init__.py b/test/unit/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/test/unit/services/__init__.py b/test/unit/services/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/test/unit/services/test_greeting_service.py b/test/unit/services/test_greeting_service.py deleted file mode 100644 index fee63b1..0000000 --- a/test/unit/services/test_greeting_service.py +++ /dev/null @@ -1,16 +0,0 @@ -from flex_container_orchestrator import CONFIG -from flex_container_orchestrator.services import greeting_service -from flex_container_orchestrator.domain.greeting import Greeting - -def test_greeting_service(): - # given - name = 'World' - CONFIG.main.app_name = 'the test app' - - # when - result = greeting_service.get_greeting(name) - - expected = Greeting(message=f'Hello, {name} from the test app!') - - # then - assert result == expected \ No newline at end of file From 5b0f51457c43fd39e9b6686452fe0afdc601426b Mon Sep 17 00:00:00 2001 From: ninaburg Date: Tue, 15 Oct 2024 11:06:12 +0200 Subject: [PATCH 09/37] add boto3 --- poetry.lock | 2395 ++++++++++++++++++++++++++++++++++++++++++++++++ pyproject.toml | 1 + 2 files changed, 2396 insertions(+) create mode 100644 poetry.lock diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..20104f6 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,2395 @@ +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. + +[[package]] +name = "accessible-pygments" +version = "0.0.5" +description = "A collection of accessible pygments styles" +optional = false +python-versions = ">=3.9" +files = [ + {file = "accessible_pygments-0.0.5-py3-none-any.whl", hash = "sha256:88ae3211e68a1d0b011504b2ffc1691feafce124b845bd072ab6f9f66f34d4b7"}, + {file = "accessible_pygments-0.0.5.tar.gz", hash = "sha256:40918d3e6a2b619ad424cb91e556bd3bd8865443d9f22f1dcdf79e33c8046872"}, +] + +[package.dependencies] +pygments = ">=1.5" + +[package.extras] +dev = ["pillow", "pkginfo (>=1.10)", "playwright", "pre-commit", "setuptools", "twine (>=5.0)"] +tests = ["hypothesis", "pytest"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "alabaster" +version = "1.0.0" +description = "A light, configurable Sphinx theme" +optional = false +python-versions = ">=3.10" +files = [ + {file = "alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b"}, + {file = "alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e"}, +] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "annotated-types" +version = "0.7.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.8" +files = [ + {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, + {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, +] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "anyio" +version = "4.6.1" +description = "High level compatibility layer for multiple asynchronous event loop implementations" +optional = false +python-versions = ">=3.9" +files = [ + {file = "anyio-4.6.1-py3-none-any.whl", hash = "sha256:0632863c9044798a494a05cab0b159dfad6a3f064094863a45878320eb4e8ed2"}, + {file = "anyio-4.6.1.tar.gz", hash = "sha256:936e6613a08e8f71a300cfffca1c1c0806335607247696ac45f9b32c63bfb9aa"}, +] + +[package.dependencies] +idna = ">=2.8" +sniffio = ">=1.1" + +[package.extras] +doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21.0b1)"] +trio = ["trio (>=0.26.1)"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "astroid" +version = "3.3.5" +description = "An abstract syntax tree for Python with inference support." +optional = false +python-versions = ">=3.9.0" +files = [ + {file = "astroid-3.3.5-py3-none-any.whl", hash = "sha256:a9d1c946ada25098d790e079ba2a1b112157278f3fb7e718ae6a9252f5835dc8"}, + {file = "astroid-3.3.5.tar.gz", hash = "sha256:5cfc40ae9f68311075d27ef68a4841bdc5cc7f6cf86671b49f00607d30188e2d"}, +] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "asyncstdlib" +version = "3.12.5" +description = "The missing async toolbox" +optional = false +python-versions = "~=3.8" +files = [ + {file = "asyncstdlib-3.12.5-py3-none-any.whl", hash = "sha256:277084c59caf8148c9cd83c7425f37aba7f43078645129ec6918d0f1a0adc95a"}, + {file = "asyncstdlib-3.12.5.tar.gz", hash = "sha256:7eb0025f6acb59e46743458570712fe9700dcc7b370b6929daf07bcc1ccc27db"}, +] + +[package.extras] +doc = ["sphinx", "sphinxcontrib-trio"] +test = ["black", "coverage", "flake8", "flake8-2020", "flake8-bugbear", "mypy", "pytest", "pytest-cov"] +typetest = ["mypy", "pyright", "typing-extensions"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "authlib" +version = "1.3.2" +description = "The ultimate Python library in building OAuth and OpenID Connect servers and clients." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Authlib-1.3.2-py2.py3-none-any.whl", hash = "sha256:ede026a95e9f5cdc2d4364a52103f5405e75aa156357e831ef2bfd0bc5094dfc"}, + {file = "authlib-1.3.2.tar.gz", hash = "sha256:4b16130117f9eb82aa6eec97f6dd4673c3f960ac0283ccdae2897ee4bc030ba2"}, +] + +[package.dependencies] +cryptography = "*" + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "babel" +version = "2.16.0" +description = "Internationalization utilities" +optional = false +python-versions = ">=3.8" +files = [ + {file = "babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b"}, + {file = "babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316"}, +] + +[package.extras] +dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "beautifulsoup4" +version = "4.12.3" +description = "Screen-scraping library" +optional = false +python-versions = ">=3.6.0" +files = [ + {file = "beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed"}, + {file = "beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051"}, +] + +[package.dependencies] +soupsieve = ">1.2" + +[package.extras] +cchardet = ["cchardet"] +chardet = ["chardet"] +charset-normalizer = ["charset-normalizer"] +html5lib = ["html5lib"] +lxml = ["lxml"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "boto3" +version = "1.35.40" +description = "The AWS SDK for Python" +optional = false +python-versions = ">= 3.8" +files = [ + {file = "boto3-1.35.40-py3-none-any.whl", hash = "sha256:9352f6d61f15c789231a5d608613f03425059072ed862c32e1ed102b17206abf"}, + {file = "boto3-1.35.40.tar.gz", hash = "sha256:33c6a7aeab316f7e0b3ad8552afe95a4a10bfd58519d00741c4d4f3047da8382"}, +] + +[package.dependencies] +botocore = ">=1.35.40,<1.36.0" +jmespath = ">=0.7.1,<2.0.0" +s3transfer = ">=0.10.0,<0.11.0" + +[package.extras] +crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "botocore" +version = "1.35.40" +description = "Low-level, data-driven core of boto 3." +optional = false +python-versions = ">= 3.8" +files = [ + {file = "botocore-1.35.40-py3-none-any.whl", hash = "sha256:072cc47f29cb1de4fa77ce6632e4f0480af29b70816973ff415fbaa3f50bd1db"}, + {file = "botocore-1.35.40.tar.gz", hash = "sha256:547e0a983856c7d7aeaa30fca2a283873c57c07366cd806d2d639856341b3c31"}, +] + +[package.dependencies] +jmespath = ">=0.7.1,<2.0.0" +python-dateutil = ">=2.1,<3.0.0" +urllib3 = {version = ">=1.25.4,<2.2.0 || >2.2.0,<3", markers = "python_version >= \"3.10\""} + +[package.extras] +crt = ["awscrt (==0.22.0)"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "certifi" +version = "2024.8.30" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, + {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, +] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "cffi" +version = "1.17.1" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, + {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be"}, + {file = "cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c"}, + {file = "cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15"}, + {file = "cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401"}, + {file = "cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"}, + {file = "cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655"}, + {file = "cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0"}, + {file = "cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4"}, + {file = "cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93"}, + {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3"}, + {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8"}, + {file = "cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65"}, + {file = "cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903"}, + {file = "cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e"}, + {file = "cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd"}, + {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed"}, + {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9"}, + {file = "cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d"}, + {file = "cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a"}, + {file = "cffi-1.17.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1"}, + {file = "cffi-1.17.1-cp38-cp38-win32.whl", hash = "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8"}, + {file = "cffi-1.17.1-cp38-cp38-win_amd64.whl", hash = "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1"}, + {file = "cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16"}, + {file = "cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e"}, + {file = "cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7"}, + {file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"}, + {file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"}, +] + +[package.dependencies] +pycparser = "*" + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "charset-normalizer" +version = "3.4.0" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5ed2e36c3e9b4f21dd9422f6893dec0abf2cca553af509b10cd630f878d3eb99"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d3ff7fc90b98c637bda91c89d51264a3dcf210cade3a2c6f838c7268d7a4ca"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1110e22af8ca26b90bd6364fe4c763329b0ebf1ee213ba32b68c73de5752323d"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:86f4e8cca779080f66ff4f191a685ced73d2f72d50216f7112185dc02b90b9b7"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f683ddc7eedd742e2889d2bfb96d69573fde1d92fcb811979cdb7165bb9c7d3"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27623ba66c183eca01bf9ff833875b459cad267aeeb044477fedac35e19ba907"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f606a1881d2663630ea5b8ce2efe2111740df4b687bd78b34a8131baa007f79b"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0b309d1747110feb25d7ed6b01afdec269c647d382c857ef4663bbe6ad95a912"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:136815f06a3ae311fae551c3df1f998a1ebd01ddd424aa5603a4336997629e95"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:14215b71a762336254351b00ec720a8e85cada43b987da5a042e4ce3e82bd68e"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:79983512b108e4a164b9c8d34de3992f76d48cadc9554c9e60b43f308988aabe"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-win32.whl", hash = "sha256:c94057af19bc953643a33581844649a7fdab902624d2eb739738a30e2b3e60fc"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:55f56e2ebd4e3bc50442fbc0888c9d8c94e4e06a933804e2af3e89e2f9c1c749"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-win32.whl", hash = "sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-win32.whl", hash = "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-win32.whl", hash = "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dbe03226baf438ac4fda9e2d0715022fd579cb641c4cf639fa40d53b2fe6f3e2"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd9a8bd8900e65504a305bf8ae6fa9fbc66de94178c420791d0293702fce2df7"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8831399554b92b72af5932cdbbd4ddc55c55f631bb13ff8fe4e6536a06c5c51"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a14969b8691f7998e74663b77b4c36c0337cb1df552da83d5c9004a93afdb574"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcaf7c1524c0542ee2fc82cc8ec337f7a9f7edee2532421ab200d2b920fc97cf"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425c5f215d0eecee9a56cdb703203dda90423247421bf0d67125add85d0c4455"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:d5b054862739d276e09928de37c79ddeec42a6e1bfc55863be96a36ba22926f6"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:f3e73a4255342d4eb26ef6df01e3962e73aa29baa3124a8e824c5d3364a65748"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:2f6c34da58ea9c1a9515621f4d9ac379871a8f21168ba1b5e09d74250de5ad62"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:f09cb5a7bbe1ecae6e87901a2eb23e0256bb524a79ccc53eb0b7629fbe7677c4"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:0099d79bdfcf5c1f0c2c72f91516702ebf8b0b8ddd8905f97a8aecf49712c621"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-win32.whl", hash = "sha256:9c98230f5042f4945f957d006edccc2af1e03ed5e37ce7c373f00a5a4daa6149"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:62f60aebecfc7f4b82e3f639a7d1433a20ec32824db2199a11ad4f5e146ef5ee"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:af73657b7a68211996527dbfeffbb0864e043d270580c5aef06dc4b659a4b578"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cab5d0b79d987c67f3b9e9c53f54a61360422a5a0bc075f43cab5621d530c3b6"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9289fd5dddcf57bab41d044f1756550f9e7cf0c8e373b8cdf0ce8773dc4bd417"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b493a043635eb376e50eedf7818f2f322eabbaa974e948bd8bdd29eb7ef2a51"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fa2566ca27d67c86569e8c85297aaf413ffab85a8960500f12ea34ff98e4c41"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8e538f46104c815be19c975572d74afb53f29650ea2025bbfaef359d2de2f7f"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fd30dc99682dc2c603c2b315bded2799019cea829f8bf57dc6b61efde6611c8"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2006769bd1640bdf4d5641c69a3d63b71b81445473cac5ded39740a226fa88ab"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:dc15e99b2d8a656f8e666854404f1ba54765871104e50c8e9813af8a7db07f12"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ab2e5bef076f5a235c3774b4f4028a680432cded7cad37bba0fd90d64b187d19"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:4ec9dd88a5b71abfc74e9df5ebe7921c35cbb3b641181a531ca65cdb5e8e4dea"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:43193c5cda5d612f247172016c4bb71251c784d7a4d9314677186a838ad34858"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:aa693779a8b50cd97570e5a0f343538a8dbd3e496fa5dcb87e29406ad0299654"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-win32.whl", hash = "sha256:7706f5850360ac01d80c89bcef1640683cc12ed87f42579dab6c5d3ed6888613"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:c3e446d253bd88f6377260d07c895816ebf33ffffd56c1c792b13bff9c3e1ade"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:980b4f289d1d90ca5efcf07958d3eb38ed9c0b7676bf2831a54d4f66f9c27dfa"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f28f891ccd15c514a0981f3b9db9aa23d62fe1a99997512b0491d2ed323d229a"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8aacce6e2e1edcb6ac625fb0f8c3a9570ccc7bfba1f63419b3769ccf6a00ed0"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7af3717683bea4c87acd8c0d3d5b44d56120b26fd3f8a692bdd2d5260c620a"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ff2ed8194587faf56555927b3aa10e6fb69d931e33953943bc4f837dfee2242"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e91f541a85298cf35433bf66f3fab2a4a2cff05c127eeca4af174f6d497f0d4b"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:309a7de0a0ff3040acaebb35ec45d18db4b28232f21998851cfa709eeff49d62"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:285e96d9d53422efc0d7a17c60e59f37fbf3dfa942073f666db4ac71e8d726d0"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5d447056e2ca60382d460a604b6302d8db69476fd2015c81e7c35417cfabe4cd"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:20587d20f557fe189b7947d8e7ec5afa110ccf72a3128d61a2a387c3313f46be"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:130272c698667a982a5d0e626851ceff662565379baf0ff2cc58067b81d4f11d"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ab22fbd9765e6954bc0bcff24c25ff71dcbfdb185fcdaca49e81bac68fe724d3"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7782afc9b6b42200f7362858f9e73b1f8316afb276d316336c0ec3bd73312742"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-win32.whl", hash = "sha256:2de62e8801ddfff069cd5c504ce3bc9672b23266597d4e4f50eda28846c322f2"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:95c3c157765b031331dd4db3c775e58deaee050a3042fcad72cbc4189d7c8dca"}, + {file = "charset_normalizer-3.4.0-py3-none-any.whl", hash = "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079"}, + {file = "charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e"}, +] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "coverage" +version = "7.6.3" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.9" +files = [ + {file = "coverage-7.6.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6da42bbcec130b188169107ecb6ee7bd7b4c849d24c9370a0c884cf728d8e976"}, + {file = "coverage-7.6.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c222958f59b0ae091f4535851cbb24eb57fc0baea07ba675af718fb5302dddb2"}, + {file = "coverage-7.6.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab84a8b698ad5a6c365b08061920138e7a7dd9a04b6feb09ba1bfae68346ce6d"}, + {file = "coverage-7.6.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70a6756ce66cd6fe8486c775b30889f0dc4cb20c157aa8c35b45fd7868255c5c"}, + {file = "coverage-7.6.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c2e6fa98032fec8282f6b27e3f3986c6e05702828380618776ad794e938f53a"}, + {file = "coverage-7.6.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:921fbe13492caf6a69528f09d5d7c7d518c8d0e7b9f6701b7719715f29a71e6e"}, + {file = "coverage-7.6.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:6d99198203f0b9cb0b5d1c0393859555bc26b548223a769baf7e321a627ed4fc"}, + {file = "coverage-7.6.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:87cd2e29067ea397a47e352efb13f976eb1b03e18c999270bb50589323294c6e"}, + {file = "coverage-7.6.3-cp310-cp310-win32.whl", hash = "sha256:a3328c3e64ea4ab12b85999eb0779e6139295bbf5485f69d42cf794309e3d007"}, + {file = "coverage-7.6.3-cp310-cp310-win_amd64.whl", hash = "sha256:bca4c8abc50d38f9773c1ec80d43f3768df2e8576807d1656016b9d3eeaa96fd"}, + {file = "coverage-7.6.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c51ef82302386d686feea1c44dbeef744585da16fcf97deea2a8d6c1556f519b"}, + {file = "coverage-7.6.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0ca37993206402c6c35dc717f90d4c8f53568a8b80f0bf1a1b2b334f4d488fba"}, + {file = "coverage-7.6.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c77326300b839c44c3e5a8fe26c15b7e87b2f32dfd2fc9fee1d13604347c9b38"}, + {file = "coverage-7.6.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e484e479860e00da1f005cd19d1c5d4a813324e5951319ac3f3eefb497cc549"}, + {file = "coverage-7.6.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c6c0f4d53ef603397fc894a895b960ecd7d44c727df42a8d500031716d4e8d2"}, + {file = "coverage-7.6.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:37be7b5ea3ff5b7c4a9db16074dc94523b5f10dd1f3b362a827af66a55198175"}, + {file = "coverage-7.6.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:43b32a06c47539fe275106b376658638b418c7cfdfff0e0259fbf877e845f14b"}, + {file = "coverage-7.6.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ee77c7bef0724165e795b6b7bf9c4c22a9b8468a6bdb9c6b4281293c6b22a90f"}, + {file = "coverage-7.6.3-cp311-cp311-win32.whl", hash = "sha256:43517e1f6b19f610a93d8227e47790722c8bf7422e46b365e0469fc3d3563d97"}, + {file = "coverage-7.6.3-cp311-cp311-win_amd64.whl", hash = "sha256:04f2189716e85ec9192df307f7c255f90e78b6e9863a03223c3b998d24a3c6c6"}, + {file = "coverage-7.6.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:27bd5f18d8f2879e45724b0ce74f61811639a846ff0e5c0395b7818fae87aec6"}, + {file = "coverage-7.6.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d546cfa78844b8b9c1c0533de1851569a13f87449897bbc95d698d1d3cb2a30f"}, + {file = "coverage-7.6.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9975442f2e7a5cfcf87299c26b5a45266ab0696348420049b9b94b2ad3d40234"}, + {file = "coverage-7.6.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:583049c63106c0555e3ae3931edab5669668bbef84c15861421b94e121878d3f"}, + {file = "coverage-7.6.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2341a78ae3a5ed454d524206a3fcb3cec408c2a0c7c2752cd78b606a2ff15af4"}, + {file = "coverage-7.6.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a4fb91d5f72b7e06a14ff4ae5be625a81cd7e5f869d7a54578fc271d08d58ae3"}, + {file = "coverage-7.6.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e279f3db904e3b55f520f11f983cc8dc8a4ce9b65f11692d4718ed021ec58b83"}, + {file = "coverage-7.6.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:aa23ce39661a3e90eea5f99ec59b763b7d655c2cada10729ed920a38bfc2b167"}, + {file = "coverage-7.6.3-cp312-cp312-win32.whl", hash = "sha256:52ac29cc72ee7e25ace7807249638f94c9b6a862c56b1df015d2b2e388e51dbd"}, + {file = "coverage-7.6.3-cp312-cp312-win_amd64.whl", hash = "sha256:40e8b1983080439d4802d80b951f4a93d991ef3261f69e81095a66f86cf3c3c6"}, + {file = "coverage-7.6.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9134032f5aa445ae591c2ba6991d10136a1f533b1d2fa8f8c21126468c5025c6"}, + {file = "coverage-7.6.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:99670790f21a96665a35849990b1df447993880bb6463a0a1d757897f30da929"}, + {file = "coverage-7.6.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2dc7d6b380ca76f5e817ac9eef0c3686e7834c8346bef30b041a4ad286449990"}, + {file = "coverage-7.6.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f7b26757b22faf88fcf232f5f0e62f6e0fd9e22a8a5d0d5016888cdfe1f6c1c4"}, + {file = "coverage-7.6.3-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c59d6a4a4633fad297f943c03d0d2569867bd5372eb5684befdff8df8522e39"}, + {file = "coverage-7.6.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f263b18692f8ed52c8de7f40a0751e79015983dbd77b16906e5b310a39d3ca21"}, + {file = "coverage-7.6.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:79644f68a6ff23b251cae1c82b01a0b51bc40c8468ca9585c6c4b1aeee570e0b"}, + {file = "coverage-7.6.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:71967c35828c9ff94e8c7d405469a1fb68257f686bca7c1ed85ed34e7c2529c4"}, + {file = "coverage-7.6.3-cp313-cp313-win32.whl", hash = "sha256:e266af4da2c1a4cbc6135a570c64577fd3e6eb204607eaff99d8e9b710003c6f"}, + {file = "coverage-7.6.3-cp313-cp313-win_amd64.whl", hash = "sha256:ea52bd218d4ba260399a8ae4bb6b577d82adfc4518b93566ce1fddd4a49d1dce"}, + {file = "coverage-7.6.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:8d4c6ea0f498c7c79111033a290d060c517853a7bcb2f46516f591dab628ddd3"}, + {file = "coverage-7.6.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:331b200ad03dbaa44151d74daeb7da2cf382db424ab923574f6ecca7d3b30de3"}, + {file = "coverage-7.6.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54356a76b67cf8a3085818026bb556545ebb8353951923b88292556dfa9f812d"}, + {file = "coverage-7.6.3-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ebec65f5068e7df2d49466aab9128510c4867e532e07cb6960075b27658dca38"}, + {file = "coverage-7.6.3-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d33a785ea8354c480515e781554d3be582a86297e41ccbea627a5c632647f2cd"}, + {file = "coverage-7.6.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f7ddb920106bbbbcaf2a274d56f46956bf56ecbde210d88061824a95bdd94e92"}, + {file = "coverage-7.6.3-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:70d24936ca6c15a3bbc91ee9c7fc661132c6f4c9d42a23b31b6686c05073bde5"}, + {file = "coverage-7.6.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c30e42ea11badb147f0d2e387115b15e2bd8205a5ad70d6ad79cf37f6ac08c91"}, + {file = "coverage-7.6.3-cp313-cp313t-win32.whl", hash = "sha256:365defc257c687ce3e7d275f39738dcd230777424117a6c76043459db131dd43"}, + {file = "coverage-7.6.3-cp313-cp313t-win_amd64.whl", hash = "sha256:23bb63ae3f4c645d2d82fa22697364b0046fbafb6261b258a58587441c5f7bd0"}, + {file = "coverage-7.6.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:da29ceabe3025a1e5a5aeeb331c5b1af686daab4ff0fb4f83df18b1180ea83e2"}, + {file = "coverage-7.6.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:df8c05a0f574d480947cba11b947dc41b1265d721c3777881da2fb8d3a1ddfba"}, + {file = "coverage-7.6.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec1e3b40b82236d100d259854840555469fad4db64f669ab817279eb95cd535c"}, + {file = "coverage-7.6.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b4adeb878a374126f1e5cf03b87f66279f479e01af0e9a654cf6d1509af46c40"}, + {file = "coverage-7.6.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:43d6a66e33b1455b98fc7312b124296dad97a2e191c80320587234a77b1b736e"}, + {file = "coverage-7.6.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1990b1f4e2c402beb317840030bb9f1b6a363f86e14e21b4212e618acdfce7f6"}, + {file = "coverage-7.6.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:12f9515d875859faedb4144fd38694a761cd2a61ef9603bf887b13956d0bbfbb"}, + {file = "coverage-7.6.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:99ded130555c021d99729fabd4ddb91a6f4cc0707df4b1daf912c7850c373b13"}, + {file = "coverage-7.6.3-cp39-cp39-win32.whl", hash = "sha256:c3a79f56dee9136084cf84a6c7c4341427ef36e05ae6415bf7d787c96ff5eaa3"}, + {file = "coverage-7.6.3-cp39-cp39-win_amd64.whl", hash = "sha256:aac7501ae73d4a02f4b7ac8fcb9dc55342ca98ffb9ed9f2dfb8a25d53eda0e4d"}, + {file = "coverage-7.6.3-pp39.pp310-none-any.whl", hash = "sha256:b9853509b4bf57ba7b1f99b9d866c422c9c5248799ab20e652bbb8a184a38181"}, + {file = "coverage-7.6.3.tar.gz", hash = "sha256:bb7d5fe92bd0dc235f63ebe9f8c6e0884f7360f88f3411bfed1350c872ef2054"}, +] + +[package.extras] +toml = ["tomli"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "cryptography" +version = "43.0.1" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +optional = false +python-versions = ">=3.7" +files = [ + {file = "cryptography-43.0.1-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:8385d98f6a3bf8bb2d65a73e17ed87a3ba84f6991c155691c51112075f9ffc5d"}, + {file = "cryptography-43.0.1-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27e613d7077ac613e399270253259d9d53872aaf657471473ebfc9a52935c062"}, + {file = "cryptography-43.0.1-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68aaecc4178e90719e95298515979814bda0cbada1256a4485414860bd7ab962"}, + {file = "cryptography-43.0.1-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:de41fd81a41e53267cb020bb3a7212861da53a7d39f863585d13ea11049cf277"}, + {file = "cryptography-43.0.1-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f98bf604c82c416bc829e490c700ca1553eafdf2912a91e23a79d97d9801372a"}, + {file = "cryptography-43.0.1-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:61ec41068b7b74268fa86e3e9e12b9f0c21fcf65434571dbb13d954bceb08042"}, + {file = "cryptography-43.0.1-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:014f58110f53237ace6a408b5beb6c427b64e084eb451ef25a28308270086494"}, + {file = "cryptography-43.0.1-cp37-abi3-win32.whl", hash = "sha256:2bd51274dcd59f09dd952afb696bf9c61a7a49dfc764c04dd33ef7a6b502a1e2"}, + {file = "cryptography-43.0.1-cp37-abi3-win_amd64.whl", hash = "sha256:666ae11966643886c2987b3b721899d250855718d6d9ce41b521252a17985f4d"}, + {file = "cryptography-43.0.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:ac119bb76b9faa00f48128b7f5679e1d8d437365c5d26f1c2c3f0da4ce1b553d"}, + {file = "cryptography-43.0.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bbcce1a551e262dfbafb6e6252f1ae36a248e615ca44ba302df077a846a8806"}, + {file = "cryptography-43.0.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58d4e9129985185a06d849aa6df265bdd5a74ca6e1b736a77959b498e0505b85"}, + {file = "cryptography-43.0.1-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d03a475165f3134f773d1388aeb19c2d25ba88b6a9733c5c590b9ff7bbfa2e0c"}, + {file = "cryptography-43.0.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:511f4273808ab590912a93ddb4e3914dfd8a388fed883361b02dea3791f292e1"}, + {file = "cryptography-43.0.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:80eda8b3e173f0f247f711eef62be51b599b5d425c429b5d4ca6a05e9e856baa"}, + {file = "cryptography-43.0.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:38926c50cff6f533f8a2dae3d7f19541432610d114a70808f0926d5aaa7121e4"}, + {file = "cryptography-43.0.1-cp39-abi3-win32.whl", hash = "sha256:a575913fb06e05e6b4b814d7f7468c2c660e8bb16d8d5a1faf9b33ccc569dd47"}, + {file = "cryptography-43.0.1-cp39-abi3-win_amd64.whl", hash = "sha256:d75601ad10b059ec832e78823b348bfa1a59f6b8d545db3a24fd44362a1564cb"}, + {file = "cryptography-43.0.1-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ea25acb556320250756e53f9e20a4177515f012c9eaea17eb7587a8c4d8ae034"}, + {file = "cryptography-43.0.1-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c1332724be35d23a854994ff0b66530119500b6053d0bd3363265f7e5e77288d"}, + {file = "cryptography-43.0.1-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:fba1007b3ef89946dbbb515aeeb41e30203b004f0b4b00e5e16078b518563289"}, + {file = "cryptography-43.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5b43d1ea6b378b54a1dc99dd8a2b5be47658fe9a7ce0a58ff0b55f4b43ef2b84"}, + {file = "cryptography-43.0.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:88cce104c36870d70c49c7c8fd22885875d950d9ee6ab54df2745f83ba0dc365"}, + {file = "cryptography-43.0.1-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:9d3cdb25fa98afdd3d0892d132b8d7139e2c087da1712041f6b762e4f807cc96"}, + {file = "cryptography-43.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e710bf40870f4db63c3d7d929aa9e09e4e7ee219e703f949ec4073b4294f6172"}, + {file = "cryptography-43.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7c05650fe8023c5ed0d46793d4b7d7e6cd9c04e68eabe5b0aeea836e37bdcec2"}, + {file = "cryptography-43.0.1.tar.gz", hash = "sha256:203e92a75716d8cfb491dc47c79e17d0d9207ccffcbcb35f598fbe463ae3444d"}, +] + +[package.dependencies] +cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} + +[package.extras] +docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] +docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"] +nox = ["nox"] +pep8test = ["check-sdist", "click", "mypy", "ruff"] +sdist = ["build"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["certifi", "cryptography-vectors (==43.0.1)", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] +test-randomorder = ["pytest-randomly"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "dill" +version = "0.3.9" +description = "serialize all of Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "dill-0.3.9-py3-none-any.whl", hash = "sha256:468dff3b89520b474c0397703366b7b95eebe6303f108adf9b19da1f702be87a"}, + {file = "dill-0.3.9.tar.gz", hash = "sha256:81aa267dddf68cbfe8029c42ca9ec6a4ab3b22371d1c450abc54422577b4512c"}, +] + +[package.extras] +graph = ["objgraph (>=1.7.2)"] +profile = ["gprof2dot (>=2022.7.29)"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "docutils" +version = "0.21.2" +description = "Docutils -- Python Documentation Utilities" +optional = false +python-versions = ">=3.9" +files = [ + {file = "docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2"}, + {file = "docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f"}, +] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "fastapi" +version = "0.114.2" +description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" +optional = false +python-versions = ">=3.8" +files = [ + {file = "fastapi-0.114.2-py3-none-any.whl", hash = "sha256:44474a22913057b1acb973ab90f4b671ba5200482e7622816d79105dcece1ac5"}, + {file = "fastapi-0.114.2.tar.gz", hash = "sha256:0adb148b62edb09e8c6eeefa3ea934e8f276dabc038c5a82989ea6346050c3da"}, +] + +[package.dependencies] +pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" +starlette = ">=0.37.2,<0.39.0" +typing-extensions = ">=4.8.0" + +[package.extras] +all = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] +standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "jinja2 (>=2.11.2)", "python-multipart (>=0.0.7)", "uvicorn[standard] (>=0.12.0)"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "h11" +version = "0.14.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +optional = false +python-versions = ">=3.7" +files = [ + {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, + {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, +] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "httpcore" +version = "1.0.6" +description = "A minimal low-level HTTP client." +optional = false +python-versions = ">=3.8" +files = [ + {file = "httpcore-1.0.6-py3-none-any.whl", hash = "sha256:27b59625743b85577a8c0e10e55b50b5368a4f2cfe8cc7bcfa9cf00829c2682f"}, + {file = "httpcore-1.0.6.tar.gz", hash = "sha256:73f6dbd6eb8c21bbf7ef8efad555481853f5f6acdeaff1edb0694289269ee17f"}, +] + +[package.dependencies] +certifi = "*" +h11 = ">=0.13,<0.15" + +[package.extras] +asyncio = ["anyio (>=4.0,<5.0)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] +trio = ["trio (>=0.22.0,<1.0)"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "httptools" +version = "0.6.1" +description = "A collection of framework independent HTTP protocol utils." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "httptools-0.6.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d2f6c3c4cb1948d912538217838f6e9960bc4a521d7f9b323b3da579cd14532f"}, + {file = "httptools-0.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:00d5d4b68a717765b1fabfd9ca755bd12bf44105eeb806c03d1962acd9b8e563"}, + {file = "httptools-0.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:639dc4f381a870c9ec860ce5c45921db50205a37cc3334e756269736ff0aac58"}, + {file = "httptools-0.6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e57997ac7fb7ee43140cc03664de5f268813a481dff6245e0075925adc6aa185"}, + {file = "httptools-0.6.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0ac5a0ae3d9f4fe004318d64b8a854edd85ab76cffbf7ef5e32920faef62f142"}, + {file = "httptools-0.6.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3f30d3ce413088a98b9db71c60a6ada2001a08945cb42dd65a9a9fe228627658"}, + {file = "httptools-0.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:1ed99a373e327f0107cb513b61820102ee4f3675656a37a50083eda05dc9541b"}, + {file = "httptools-0.6.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7a7ea483c1a4485c71cb5f38be9db078f8b0e8b4c4dc0210f531cdd2ddac1ef1"}, + {file = "httptools-0.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:85ed077c995e942b6f1b07583e4eb0a8d324d418954fc6af913d36db7c05a5a0"}, + {file = "httptools-0.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b0bb634338334385351a1600a73e558ce619af390c2b38386206ac6a27fecfc"}, + {file = "httptools-0.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d9ceb2c957320def533671fc9c715a80c47025139c8d1f3797477decbc6edd2"}, + {file = "httptools-0.6.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4f0f8271c0a4db459f9dc807acd0eadd4839934a4b9b892f6f160e94da309837"}, + {file = "httptools-0.6.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6a4f5ccead6d18ec072ac0b84420e95d27c1cdf5c9f1bc8fbd8daf86bd94f43d"}, + {file = "httptools-0.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:5cceac09f164bcba55c0500a18fe3c47df29b62353198e4f37bbcc5d591172c3"}, + {file = "httptools-0.6.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:75c8022dca7935cba14741a42744eee13ba05db00b27a4b940f0d646bd4d56d0"}, + {file = "httptools-0.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:48ed8129cd9a0d62cf4d1575fcf90fb37e3ff7d5654d3a5814eb3d55f36478c2"}, + {file = "httptools-0.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f58e335a1402fb5a650e271e8c2d03cfa7cea46ae124649346d17bd30d59c90"}, + {file = "httptools-0.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93ad80d7176aa5788902f207a4e79885f0576134695dfb0fefc15b7a4648d503"}, + {file = "httptools-0.6.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9bb68d3a085c2174c2477eb3ffe84ae9fb4fde8792edb7bcd09a1d8467e30a84"}, + {file = "httptools-0.6.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b512aa728bc02354e5ac086ce76c3ce635b62f5fbc32ab7082b5e582d27867bb"}, + {file = "httptools-0.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:97662ce7fb196c785344d00d638fc9ad69e18ee4bfb4000b35a52efe5adcc949"}, + {file = "httptools-0.6.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8e216a038d2d52ea13fdd9b9c9c7459fb80d78302b257828285eca1c773b99b3"}, + {file = "httptools-0.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3e802e0b2378ade99cd666b5bffb8b2a7cc8f3d28988685dc300469ea8dd86cb"}, + {file = "httptools-0.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4bd3e488b447046e386a30f07af05f9b38d3d368d1f7b4d8f7e10af85393db97"}, + {file = "httptools-0.6.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe467eb086d80217b7584e61313ebadc8d187a4d95bb62031b7bab4b205c3ba3"}, + {file = "httptools-0.6.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3c3b214ce057c54675b00108ac42bacf2ab8f85c58e3f324a4e963bbc46424f4"}, + {file = "httptools-0.6.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8ae5b97f690badd2ca27cbf668494ee1b6d34cf1c464271ef7bfa9ca6b83ffaf"}, + {file = "httptools-0.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:405784577ba6540fa7d6ff49e37daf104e04f4b4ff2d1ac0469eaa6a20fde084"}, + {file = "httptools-0.6.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:95fb92dd3649f9cb139e9c56604cc2d7c7bf0fc2e7c8d7fbd58f96e35eddd2a3"}, + {file = "httptools-0.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dcbab042cc3ef272adc11220517278519adf8f53fd3056d0e68f0a6f891ba94e"}, + {file = "httptools-0.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cf2372e98406efb42e93bfe10f2948e467edfd792b015f1b4ecd897903d3e8d"}, + {file = "httptools-0.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:678fcbae74477a17d103b7cae78b74800d795d702083867ce160fc202104d0da"}, + {file = "httptools-0.6.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e0b281cf5a125c35f7f6722b65d8542d2e57331be573e9e88bc8b0115c4a7a81"}, + {file = "httptools-0.6.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:95658c342529bba4e1d3d2b1a874db16c7cca435e8827422154c9da76ac4e13a"}, + {file = "httptools-0.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:7ebaec1bf683e4bf5e9fbb49b8cc36da482033596a415b3e4ebab5a4c0d7ec5e"}, + {file = "httptools-0.6.1.tar.gz", hash = "sha256:c6e26c30455600b95d94b1b836085138e82f177351454ee841c148f93a9bad5a"}, +] + +[package.extras] +test = ["Cython (>=0.29.24,<0.30.0)"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "httpx" +version = "0.27.2" +description = "The next generation HTTP client." +optional = false +python-versions = ">=3.8" +files = [ + {file = "httpx-0.27.2-py3-none-any.whl", hash = "sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0"}, + {file = "httpx-0.27.2.tar.gz", hash = "sha256:f7c2be1d2f3c3c3160d441802406b206c2b76f5947b11115e6df10c6c65e66c2"}, +] + +[package.dependencies] +anyio = "*" +certifi = "*" +httpcore = "==1.*" +idna = "*" +sniffio = "*" + +[package.extras] +brotli = ["brotli", "brotlicffi"] +cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] +zstd = ["zstandard (>=0.18.0)"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "idna" +version = "3.10" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.6" +files = [ + {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, + {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, +] + +[package.extras] +all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "imagesize" +version = "1.4.1" +description = "Getting image size from png/jpeg/jpeg2000/gif file" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, + {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, +] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "importlib-metadata" +version = "8.5.0" +description = "Read metadata from Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b"}, + {file = "importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7"}, +] + +[package.dependencies] +zipp = ">=3.20" + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] +perf = ["ipython"] +test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] +type = ["pytest-mypy"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "isort" +version = "5.13.2" +description = "A Python utility / library to sort Python imports." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, + {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, +] + +[package.extras] +colors = ["colorama (>=0.4.6)"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "itsdangerous" +version = "2.2.0" +description = "Safely pass data to untrusted environments and back." +optional = false +python-versions = ">=3.8" +files = [ + {file = "itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef"}, + {file = "itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173"}, +] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "jinja2" +version = "3.1.4" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +files = [ + {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, + {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "jmespath" +version = "1.0.1" +description = "JSON Matching Expressions" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980"}, + {file = "jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe"}, +] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "markupsafe" +version = "3.0.1" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.9" +files = [ + {file = "MarkupSafe-3.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:db842712984e91707437461930e6011e60b39136c7331e971952bb30465bc1a1"}, + {file = "MarkupSafe-3.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3ffb4a8e7d46ed96ae48805746755fadd0909fea2306f93d5d8233ba23dda12a"}, + {file = "MarkupSafe-3.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67c519635a4f64e495c50e3107d9b4075aec33634272b5db1cde839e07367589"}, + {file = "MarkupSafe-3.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48488d999ed50ba8d38c581d67e496f955821dc183883550a6fbc7f1aefdc170"}, + {file = "MarkupSafe-3.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f31ae06f1328595d762c9a2bf29dafd8621c7d3adc130cbb46278079758779ca"}, + {file = "MarkupSafe-3.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:80fcbf3add8790caddfab6764bde258b5d09aefbe9169c183f88a7410f0f6dea"}, + {file = "MarkupSafe-3.0.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3341c043c37d78cc5ae6e3e305e988532b072329639007fd408a476642a89fd6"}, + {file = "MarkupSafe-3.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cb53e2a99df28eee3b5f4fea166020d3ef9116fdc5764bc5117486e6d1211b25"}, + {file = "MarkupSafe-3.0.1-cp310-cp310-win32.whl", hash = "sha256:db15ce28e1e127a0013dfb8ac243a8e392db8c61eae113337536edb28bdc1f97"}, + {file = "MarkupSafe-3.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:4ffaaac913c3f7345579db4f33b0020db693f302ca5137f106060316761beea9"}, + {file = "MarkupSafe-3.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:26627785a54a947f6d7336ce5963569b5d75614619e75193bdb4e06e21d447ad"}, + {file = "MarkupSafe-3.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b954093679d5750495725ea6f88409946d69cfb25ea7b4c846eef5044194f583"}, + {file = "MarkupSafe-3.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:973a371a55ce9ed333a3a0f8e0bcfae9e0d637711534bcb11e130af2ab9334e7"}, + {file = "MarkupSafe-3.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:244dbe463d5fb6d7ce161301a03a6fe744dac9072328ba9fc82289238582697b"}, + {file = "MarkupSafe-3.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d98e66a24497637dd31ccab090b34392dddb1f2f811c4b4cd80c230205c074a3"}, + {file = "MarkupSafe-3.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ad91738f14eb8da0ff82f2acd0098b6257621410dcbd4df20aaa5b4233d75a50"}, + {file = "MarkupSafe-3.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7044312a928a66a4c2a22644147bc61a199c1709712069a344a3fb5cfcf16915"}, + {file = "MarkupSafe-3.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a4792d3b3a6dfafefdf8e937f14906a51bd27025a36f4b188728a73382231d91"}, + {file = "MarkupSafe-3.0.1-cp311-cp311-win32.whl", hash = "sha256:fa7d686ed9883f3d664d39d5a8e74d3c5f63e603c2e3ff0abcba23eac6542635"}, + {file = "MarkupSafe-3.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:9ba25a71ebf05b9bb0e2ae99f8bc08a07ee8e98c612175087112656ca0f5c8bf"}, + {file = "MarkupSafe-3.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8ae369e84466aa70f3154ee23c1451fda10a8ee1b63923ce76667e3077f2b0c4"}, + {file = "MarkupSafe-3.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40f1e10d51c92859765522cbd79c5c8989f40f0419614bcdc5015e7b6bf97fc5"}, + {file = "MarkupSafe-3.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a4cb365cb49b750bdb60b846b0c0bc49ed62e59a76635095a179d440540c346"}, + {file = "MarkupSafe-3.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee3941769bd2522fe39222206f6dd97ae83c442a94c90f2b7a25d847d40f4729"}, + {file = "MarkupSafe-3.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62fada2c942702ef8952754abfc1a9f7658a4d5460fabe95ac7ec2cbe0d02abc"}, + {file = "MarkupSafe-3.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4c2d64fdba74ad16138300815cfdc6ab2f4647e23ced81f59e940d7d4a1469d9"}, + {file = "MarkupSafe-3.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:fb532dd9900381d2e8f48172ddc5a59db4c445a11b9fab40b3b786da40d3b56b"}, + {file = "MarkupSafe-3.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0f84af7e813784feb4d5e4ff7db633aba6c8ca64a833f61d8e4eade234ef0c38"}, + {file = "MarkupSafe-3.0.1-cp312-cp312-win32.whl", hash = "sha256:cbf445eb5628981a80f54087f9acdbf84f9b7d862756110d172993b9a5ae81aa"}, + {file = "MarkupSafe-3.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:a10860e00ded1dd0a65b83e717af28845bb7bd16d8ace40fe5531491de76b79f"}, + {file = "MarkupSafe-3.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e81c52638315ff4ac1b533d427f50bc0afc746deb949210bc85f05d4f15fd772"}, + {file = "MarkupSafe-3.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:312387403cd40699ab91d50735ea7a507b788091c416dd007eac54434aee51da"}, + {file = "MarkupSafe-3.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ae99f31f47d849758a687102afdd05bd3d3ff7dbab0a8f1587981b58a76152a"}, + {file = "MarkupSafe-3.0.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c97ff7fedf56d86bae92fa0a646ce1a0ec7509a7578e1ed238731ba13aabcd1c"}, + {file = "MarkupSafe-3.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7420ceda262dbb4b8d839a4ec63d61c261e4e77677ed7c66c99f4e7cb5030dd"}, + {file = "MarkupSafe-3.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:45d42d132cff577c92bfba536aefcfea7e26efb975bd455db4e6602f5c9f45e7"}, + {file = "MarkupSafe-3.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4c8817557d0de9349109acb38b9dd570b03cc5014e8aabf1cbddc6e81005becd"}, + {file = "MarkupSafe-3.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6a54c43d3ec4cf2a39f4387ad044221c66a376e58c0d0e971d47c475ba79c6b5"}, + {file = "MarkupSafe-3.0.1-cp313-cp313-win32.whl", hash = "sha256:c91b394f7601438ff79a4b93d16be92f216adb57d813a78be4446fe0f6bc2d8c"}, + {file = "MarkupSafe-3.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:fe32482b37b4b00c7a52a07211b479653b7fe4f22b2e481b9a9b099d8a430f2f"}, + {file = "MarkupSafe-3.0.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:17b2aea42a7280db02ac644db1d634ad47dcc96faf38ab304fe26ba2680d359a"}, + {file = "MarkupSafe-3.0.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:852dc840f6d7c985603e60b5deaae1d89c56cb038b577f6b5b8c808c97580f1d"}, + {file = "MarkupSafe-3.0.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0778de17cff1acaeccc3ff30cd99a3fd5c50fc58ad3d6c0e0c4c58092b859396"}, + {file = "MarkupSafe-3.0.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:800100d45176652ded796134277ecb13640c1a537cad3b8b53da45aa96330453"}, + {file = "MarkupSafe-3.0.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d06b24c686a34c86c8c1fba923181eae6b10565e4d80bdd7bc1c8e2f11247aa4"}, + {file = "MarkupSafe-3.0.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:33d1c36b90e570ba7785dacd1faaf091203d9942bc036118fab8110a401eb1a8"}, + {file = "MarkupSafe-3.0.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:beeebf760a9c1f4c07ef6a53465e8cfa776ea6a2021eda0d0417ec41043fe984"}, + {file = "MarkupSafe-3.0.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:bbde71a705f8e9e4c3e9e33db69341d040c827c7afa6789b14c6e16776074f5a"}, + {file = "MarkupSafe-3.0.1-cp313-cp313t-win32.whl", hash = "sha256:82b5dba6eb1bcc29cc305a18a3c5365d2af06ee71b123216416f7e20d2a84e5b"}, + {file = "MarkupSafe-3.0.1-cp313-cp313t-win_amd64.whl", hash = "sha256:730d86af59e0e43ce277bb83970530dd223bf7f2a838e086b50affa6ec5f9295"}, + {file = "MarkupSafe-3.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4935dd7883f1d50e2ffecca0aa33dc1946a94c8f3fdafb8df5c330e48f71b132"}, + {file = "MarkupSafe-3.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e9393357f19954248b00bed7c56f29a25c930593a77630c719653d51e7669c2a"}, + {file = "MarkupSafe-3.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40621d60d0e58aa573b68ac5e2d6b20d44392878e0bfc159012a5787c4e35bc8"}, + {file = "MarkupSafe-3.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f94190df587738280d544971500b9cafc9b950d32efcb1fba9ac10d84e6aa4e6"}, + {file = "MarkupSafe-3.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b6a387d61fe41cdf7ea95b38e9af11cfb1a63499af2759444b99185c4ab33f5b"}, + {file = "MarkupSafe-3.0.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8ad4ad1429cd4f315f32ef263c1342166695fad76c100c5d979c45d5570ed58b"}, + {file = "MarkupSafe-3.0.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e24bfe89c6ac4c31792793ad9f861b8f6dc4546ac6dc8f1c9083c7c4f2b335cd"}, + {file = "MarkupSafe-3.0.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2a4b34a8d14649315c4bc26bbfa352663eb51d146e35eef231dd739d54a5430a"}, + {file = "MarkupSafe-3.0.1-cp39-cp39-win32.whl", hash = "sha256:242d6860f1fd9191aef5fae22b51c5c19767f93fb9ead4d21924e0bcb17619d8"}, + {file = "MarkupSafe-3.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:93e8248d650e7e9d49e8251f883eed60ecbc0e8ffd6349e18550925e31bd029b"}, + {file = "markupsafe-3.0.1.tar.gz", hash = "sha256:3e683ee4f5d0fa2dde4db77ed8dd8a876686e3fc417655c2ece9a90576905344"}, +] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "mccabe" +version = "0.7.0" +description = "McCabe checker, plugin for flake8" +optional = false +python-versions = ">=3.6" +files = [ + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "mchpy" +version = "2.2.0" +description = "Common library for MCH python services." +optional = false +python-versions = ">=3.10,<3.13" +files = [ + {file = "mchpy-2.2.0-py3-none-any.whl", hash = "sha256:87e2a801e562767283ed67f0495209ae380017755d73f347baa14dbedfcad7fc"}, + {file = "mchpy-2.2.0.tar.gz", hash = "sha256:a2f72f315e8830219e013cbd74ef2b9c26f2e2b7aeee15f1bd06360de41133c4"}, +] + +[package.dependencies] +asyncstdlib = "*" +authlib = {version = ">=1.3.1", optional = true, markers = "extra == \"fastapi\""} +fastapi = {version = ">=0.114.0,<0.115.0", optional = true, markers = "extra == \"fastapi\""} +httpx = "*" +itsdangerous = {version = "*", optional = true, markers = "extra == \"fastapi\""} +pydantic = ">=2.7.1,<3.0.0" +pydantic-settings = ">=2.2.1,<3.0.0" +pyjwt = {version = "*", optional = true, markers = "extra == \"fastapi\""} +python-dateutil = ">=2.9.0,<3.0.0" +python-json-logger = "*" +pyyaml = ">=6.0,<7.0" +requests = ">=2.32.1,<3.0.0" +types-requests = ">=2.28.11,<3.0.0" +uvicorn = {version = "*", extras = ["standard"], optional = true, markers = "extra == \"fastapi\""} + +[package.extras] +aiobotocore = ["aiobotocore (>=2.13.0)"] +boto3 = ["boto3"] +fastapi = ["authlib (>=1.3.1)", "fastapi (>=0.114.0,<0.115.0)", "itsdangerous", "pyjwt", "uvicorn[standard]"] +kafka = ["aiokafka (>=0.11.0,<0.12.0)", "cloudevents", "confluent-kafka"] +prometheus-fastapi = ["starlette_exporter (>=0.23.0,<0.24.0)"] +s3 = ["boto3"] +s3fs = ["aiobotocore (>=2.13.0)", "s3fs (>=2024.3.1)"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "mypy" +version = "1.11.2" +description = "Optional static typing for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mypy-1.11.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d42a6dd818ffce7be66cce644f1dff482f1d97c53ca70908dff0b9ddc120b77a"}, + {file = "mypy-1.11.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:801780c56d1cdb896eacd5619a83e427ce436d86a3bdf9112527f24a66618fef"}, + {file = "mypy-1.11.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41ea707d036a5307ac674ea172875f40c9d55c5394f888b168033177fce47383"}, + {file = "mypy-1.11.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6e658bd2d20565ea86da7d91331b0eed6d2eee22dc031579e6297f3e12c758c8"}, + {file = "mypy-1.11.2-cp310-cp310-win_amd64.whl", hash = "sha256:478db5f5036817fe45adb7332d927daa62417159d49783041338921dcf646fc7"}, + {file = "mypy-1.11.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:75746e06d5fa1e91bfd5432448d00d34593b52e7e91a187d981d08d1f33d4385"}, + {file = "mypy-1.11.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a976775ab2256aadc6add633d44f100a2517d2388906ec4f13231fafbb0eccca"}, + {file = "mypy-1.11.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd953f221ac1379050a8a646585a29574488974f79d8082cedef62744f0a0104"}, + {file = "mypy-1.11.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:57555a7715c0a34421013144a33d280e73c08df70f3a18a552938587ce9274f4"}, + {file = "mypy-1.11.2-cp311-cp311-win_amd64.whl", hash = "sha256:36383a4fcbad95f2657642a07ba22ff797de26277158f1cc7bd234821468b1b6"}, + {file = "mypy-1.11.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e8960dbbbf36906c5c0b7f4fbf2f0c7ffb20f4898e6a879fcf56a41a08b0d318"}, + {file = "mypy-1.11.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:06d26c277962f3fb50e13044674aa10553981ae514288cb7d0a738f495550b36"}, + {file = "mypy-1.11.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6e7184632d89d677973a14d00ae4d03214c8bc301ceefcdaf5c474866814c987"}, + {file = "mypy-1.11.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3a66169b92452f72117e2da3a576087025449018afc2d8e9bfe5ffab865709ca"}, + {file = "mypy-1.11.2-cp312-cp312-win_amd64.whl", hash = "sha256:969ea3ef09617aff826885a22ece0ddef69d95852cdad2f60c8bb06bf1f71f70"}, + {file = "mypy-1.11.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:37c7fa6121c1cdfcaac97ce3d3b5588e847aa79b580c1e922bb5d5d2902df19b"}, + {file = "mypy-1.11.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4a8a53bc3ffbd161b5b2a4fff2f0f1e23a33b0168f1c0778ec70e1a3d66deb86"}, + {file = "mypy-1.11.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ff93107f01968ed834f4256bc1fc4475e2fecf6c661260066a985b52741ddce"}, + {file = "mypy-1.11.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:edb91dded4df17eae4537668b23f0ff6baf3707683734b6a818d5b9d0c0c31a1"}, + {file = "mypy-1.11.2-cp38-cp38-win_amd64.whl", hash = "sha256:ee23de8530d99b6db0573c4ef4bd8f39a2a6f9b60655bf7a1357e585a3486f2b"}, + {file = "mypy-1.11.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:801ca29f43d5acce85f8e999b1e431fb479cb02d0e11deb7d2abb56bdaf24fd6"}, + {file = "mypy-1.11.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:af8d155170fcf87a2afb55b35dc1a0ac21df4431e7d96717621962e4b9192e70"}, + {file = "mypy-1.11.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7821776e5c4286b6a13138cc935e2e9b6fde05e081bdebf5cdb2bb97c9df81d"}, + {file = "mypy-1.11.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:539c570477a96a4e6fb718b8d5c3e0c0eba1f485df13f86d2970c91f0673148d"}, + {file = "mypy-1.11.2-cp39-cp39-win_amd64.whl", hash = "sha256:3f14cd3d386ac4d05c5a39a51b84387403dadbd936e17cb35882134d4f8f0d24"}, + {file = "mypy-1.11.2-py3-none-any.whl", hash = "sha256:b499bc07dbdcd3de92b0a8b29fdf592c111276f6a12fe29c30f6c417dd546d12"}, + {file = "mypy-1.11.2.tar.gz", hash = "sha256:7f9993ad3e0ffdc95c2a14b66dee63729f021968bff8ad911867579c65d13a79"}, +] + +[package.dependencies] +mypy-extensions = ">=1.0.0" +typing-extensions = ">=4.6.0" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +install-types = ["pip"] +mypyc = ["setuptools (>=50)"] +reports = ["lxml"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.5" +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "packaging" +version = "24.1" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, + {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, +] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "platformdirs" +version = "4.3.6" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, + {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, +] + +[package.extras] +docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"] +type = ["mypy (>=1.11.2)"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "pluggy" +version = "1.5.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "pycparser" +version = "2.22" +description = "C parser in Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, + {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, +] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "pydantic" +version = "2.9.2" +description = "Data validation using Python type hints" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic-2.9.2-py3-none-any.whl", hash = "sha256:f048cec7b26778210e28a0459867920654d48e5e62db0958433636cde4254f12"}, + {file = "pydantic-2.9.2.tar.gz", hash = "sha256:d155cef71265d1e9807ed1c32b4c8deec042a44a50a4188b25ac67ecd81a9c0f"}, +] + +[package.dependencies] +annotated-types = ">=0.6.0" +pydantic-core = "2.23.4" +typing-extensions = {version = ">=4.6.1", markers = "python_version < \"3.13\""} + +[package.extras] +email = ["email-validator (>=2.0.0)"] +timezone = ["tzdata"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "pydantic-core" +version = "2.23.4" +description = "Core functionality for Pydantic validation and serialization" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic_core-2.23.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b10bd51f823d891193d4717448fab065733958bdb6a6b351967bd349d48d5c9b"}, + {file = "pydantic_core-2.23.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4fc714bdbfb534f94034efaa6eadd74e5b93c8fa6315565a222f7b6f42ca1166"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63e46b3169866bd62849936de036f901a9356e36376079b05efa83caeaa02ceb"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed1a53de42fbe34853ba90513cea21673481cd81ed1be739f7f2efb931b24916"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cfdd16ab5e59fc31b5e906d1a3f666571abc367598e3e02c83403acabc092e07"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255a8ef062cbf6674450e668482456abac99a5583bbafb73f9ad469540a3a232"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a7cd62e831afe623fbb7aabbb4fe583212115b3ef38a9f6b71869ba644624a2"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f09e2ff1f17c2b51f2bc76d1cc33da96298f0a036a137f5440ab3ec5360b624f"}, + {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e38e63e6f3d1cec5a27e0afe90a085af8b6806ee208b33030e65b6516353f1a3"}, + {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0dbd8dbed2085ed23b5c04afa29d8fd2771674223135dc9bc937f3c09284d071"}, + {file = "pydantic_core-2.23.4-cp310-none-win32.whl", hash = "sha256:6531b7ca5f951d663c339002e91aaebda765ec7d61b7d1e3991051906ddde119"}, + {file = "pydantic_core-2.23.4-cp310-none-win_amd64.whl", hash = "sha256:7c9129eb40958b3d4500fa2467e6a83356b3b61bfff1b414c7361d9220f9ae8f"}, + {file = "pydantic_core-2.23.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:77733e3892bb0a7fa797826361ce8a9184d25c8dffaec60b7ffe928153680ba8"}, + {file = "pydantic_core-2.23.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b84d168f6c48fabd1f2027a3d1bdfe62f92cade1fb273a5d68e621da0e44e6d"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df49e7a0861a8c36d089c1ed57d308623d60416dab2647a4a17fe050ba85de0e"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff02b6d461a6de369f07ec15e465a88895f3223eb75073ffea56b84d9331f607"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:996a38a83508c54c78a5f41456b0103c30508fed9abcad0a59b876d7398f25fd"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d97683ddee4723ae8c95d1eddac7c192e8c552da0c73a925a89fa8649bf13eea"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:216f9b2d7713eb98cb83c80b9c794de1f6b7e3145eef40400c62e86cee5f4e1e"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6f783e0ec4803c787bcea93e13e9932edab72068f68ecffdf86a99fd5918878b"}, + {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d0776dea117cf5272382634bd2a5c1b6eb16767c223c6a5317cd3e2a757c61a0"}, + {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d5f7a395a8cf1621939692dba2a6b6a830efa6b3cee787d82c7de1ad2930de64"}, + {file = "pydantic_core-2.23.4-cp311-none-win32.whl", hash = "sha256:74b9127ffea03643e998e0c5ad9bd3811d3dac8c676e47db17b0ee7c3c3bf35f"}, + {file = "pydantic_core-2.23.4-cp311-none-win_amd64.whl", hash = "sha256:98d134c954828488b153d88ba1f34e14259284f256180ce659e8d83e9c05eaa3"}, + {file = "pydantic_core-2.23.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f3e0da4ebaef65158d4dfd7d3678aad692f7666877df0002b8a522cdf088f231"}, + {file = "pydantic_core-2.23.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f69a8e0b033b747bb3e36a44e7732f0c99f7edd5cea723d45bc0d6e95377ffee"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:723314c1d51722ab28bfcd5240d858512ffd3116449c557a1336cbe3919beb87"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb2802e667b7051a1bebbfe93684841cc9351004e2badbd6411bf357ab8d5ac8"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d18ca8148bebe1b0a382a27a8ee60350091a6ddaf475fa05ef50dc35b5df6327"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33e3d65a85a2a4a0dc3b092b938a4062b1a05f3a9abde65ea93b233bca0e03f2"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:128585782e5bfa515c590ccee4b727fb76925dd04a98864182b22e89a4e6ed36"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:68665f4c17edcceecc112dfed5dbe6f92261fb9d6054b47d01bf6371a6196126"}, + {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:20152074317d9bed6b7a95ade3b7d6054845d70584216160860425f4fbd5ee9e"}, + {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9261d3ce84fa1d38ed649c3638feefeae23d32ba9182963e465d58d62203bd24"}, + {file = "pydantic_core-2.23.4-cp312-none-win32.whl", hash = "sha256:4ba762ed58e8d68657fc1281e9bb72e1c3e79cc5d464be146e260c541ec12d84"}, + {file = "pydantic_core-2.23.4-cp312-none-win_amd64.whl", hash = "sha256:97df63000f4fea395b2824da80e169731088656d1818a11b95f3b173747b6cd9"}, + {file = "pydantic_core-2.23.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7530e201d10d7d14abce4fb54cfe5b94a0aefc87da539d0346a484ead376c3cc"}, + {file = "pydantic_core-2.23.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:df933278128ea1cd77772673c73954e53a1c95a4fdf41eef97c2b779271bd0bd"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cb3da3fd1b6a5d0279a01877713dbda118a2a4fc6f0d821a57da2e464793f05"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c6dcb030aefb668a2b7009c85b27f90e51e6a3b4d5c9bc4c57631292015b0d"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:696dd8d674d6ce621ab9d45b205df149399e4bb9aa34102c970b721554828510"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2971bb5ffe72cc0f555c13e19b23c85b654dd2a8f7ab493c262071377bfce9f6"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8394d940e5d400d04cad4f75c0598665cbb81aecefaca82ca85bd28264af7f9b"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0dff76e0602ca7d4cdaacc1ac4c005e0ce0dcfe095d5b5259163a80d3a10d327"}, + {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7d32706badfe136888bdea71c0def994644e09fff0bfe47441deaed8e96fdbc6"}, + {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ed541d70698978a20eb63d8c5d72f2cc6d7079d9d90f6b50bad07826f1320f5f"}, + {file = "pydantic_core-2.23.4-cp313-none-win32.whl", hash = "sha256:3d5639516376dce1940ea36edf408c554475369f5da2abd45d44621cb616f769"}, + {file = "pydantic_core-2.23.4-cp313-none-win_amd64.whl", hash = "sha256:5a1504ad17ba4210df3a045132a7baeeba5a200e930f57512ee02909fc5c4cb5"}, + {file = "pydantic_core-2.23.4-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d4488a93b071c04dc20f5cecc3631fc78b9789dd72483ba15d423b5b3689b555"}, + {file = "pydantic_core-2.23.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:81965a16b675b35e1d09dd14df53f190f9129c0202356ed44ab2728b1c905658"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ffa2ebd4c8530079140dd2d7f794a9d9a73cbb8e9d59ffe24c63436efa8f271"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:61817945f2fe7d166e75fbfb28004034b48e44878177fc54d81688e7b85a3665"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:29d2c342c4bc01b88402d60189f3df065fb0dda3654744d5a165a5288a657368"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5e11661ce0fd30a6790e8bcdf263b9ec5988e95e63cf901972107efc49218b13"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d18368b137c6295db49ce7218b1a9ba15c5bc254c96d7c9f9e924a9bc7825ad"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ec4e55f79b1c4ffb2eecd8a0cfba9955a2588497d96851f4c8f99aa4a1d39b12"}, + {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:374a5e5049eda9e0a44c696c7ade3ff355f06b1fe0bb945ea3cac2bc336478a2"}, + {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5c364564d17da23db1106787675fc7af45f2f7b58b4173bfdd105564e132e6fb"}, + {file = "pydantic_core-2.23.4-cp38-none-win32.whl", hash = "sha256:d7a80d21d613eec45e3d41eb22f8f94ddc758a6c4720842dc74c0581f54993d6"}, + {file = "pydantic_core-2.23.4-cp38-none-win_amd64.whl", hash = "sha256:5f5ff8d839f4566a474a969508fe1c5e59c31c80d9e140566f9a37bba7b8d556"}, + {file = "pydantic_core-2.23.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a4fa4fc04dff799089689f4fd502ce7d59de529fc2f40a2c8836886c03e0175a"}, + {file = "pydantic_core-2.23.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0a7df63886be5e270da67e0966cf4afbae86069501d35c8c1b3b6c168f42cb36"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcedcd19a557e182628afa1d553c3895a9f825b936415d0dbd3cd0bbcfd29b4b"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f54b118ce5de9ac21c363d9b3caa6c800341e8c47a508787e5868c6b79c9323"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86d2f57d3e1379a9525c5ab067b27dbb8a0642fb5d454e17a9ac434f9ce523e3"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de6d1d1b9e5101508cb37ab0d972357cac5235f5c6533d1071964c47139257df"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1278e0d324f6908e872730c9102b0112477a7f7cf88b308e4fc36ce1bdb6d58c"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9a6b5099eeec78827553827f4c6b8615978bb4b6a88e5d9b93eddf8bb6790f55"}, + {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e55541f756f9b3ee346b840103f32779c695a19826a4c442b7954550a0972040"}, + {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a5c7ba8ffb6d6f8f2ab08743be203654bb1aaa8c9dcb09f82ddd34eadb695605"}, + {file = "pydantic_core-2.23.4-cp39-none-win32.whl", hash = "sha256:37b0fe330e4a58d3c58b24d91d1eb102aeec675a3db4c292ec3928ecd892a9a6"}, + {file = "pydantic_core-2.23.4-cp39-none-win_amd64.whl", hash = "sha256:1498bec4c05c9c787bde9125cfdcc63a41004ff167f495063191b863399b1a29"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f455ee30a9d61d3e1a15abd5068827773d6e4dc513e795f380cdd59932c782d5"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1e90d2e3bd2c3863d48525d297cd143fe541be8bbf6f579504b9712cb6b643ec"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e203fdf807ac7e12ab59ca2bfcabb38c7cf0b33c41efeb00f8e5da1d86af480"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e08277a400de01bc72436a0ccd02bdf596631411f592ad985dcee21445bd0068"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f220b0eea5965dec25480b6333c788fb72ce5f9129e8759ef876a1d805d00801"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d06b0c8da4f16d1d1e352134427cb194a0a6e19ad5db9161bf32b2113409e728"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ba1a0996f6c2773bd83e63f18914c1de3c9dd26d55f4ac302a7efe93fb8e7433"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:9a5bce9d23aac8f0cf0836ecfc033896aa8443b501c58d0602dbfd5bd5b37753"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:78ddaaa81421a29574a682b3179d4cf9e6d405a09b99d93ddcf7e5239c742e21"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:883a91b5dd7d26492ff2f04f40fbb652de40fcc0afe07e8129e8ae779c2110eb"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88ad334a15b32a791ea935af224b9de1bf99bcd62fabf745d5f3442199d86d59"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:233710f069d251feb12a56da21e14cca67994eab08362207785cf8c598e74577"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:19442362866a753485ba5e4be408964644dd6a09123d9416c54cd49171f50744"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:624e278a7d29b6445e4e813af92af37820fafb6dcc55c012c834f9e26f9aaaef"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f5ef8f42bec47f21d07668a043f077d507e5bf4e668d5c6dfe6aaba89de1a5b8"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:aea443fffa9fbe3af1a9ba721a87f926fe548d32cab71d188a6ede77d0ff244e"}, + {file = "pydantic_core-2.23.4.tar.gz", hash = "sha256:2584f7cf844ac4d970fba483a717dbe10c1c1c96a969bf65d61ffe94df1b2863"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "pydantic-settings" +version = "2.5.2" +description = "Settings management using Pydantic" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic_settings-2.5.2-py3-none-any.whl", hash = "sha256:2c912e55fd5794a59bf8c832b9de832dcfdf4778d79ff79b708744eed499a907"}, + {file = "pydantic_settings-2.5.2.tar.gz", hash = "sha256:f90b139682bee4d2065273d5185d71d37ea46cfe57e1b5ae184fc6a0b2484ca0"}, +] + +[package.dependencies] +pydantic = ">=2.7.0" +python-dotenv = ">=0.21.0" + +[package.extras] +azure-key-vault = ["azure-identity (>=1.16.0)", "azure-keyvault-secrets (>=4.8.0)"] +toml = ["tomli (>=2.0.1)"] +yaml = ["pyyaml (>=6.0.1)"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "pydata-sphinx-theme" +version = "0.15.4" +description = "Bootstrap-based Sphinx theme from the PyData community" +optional = false +python-versions = ">=3.9" +files = [ + {file = "pydata_sphinx_theme-0.15.4-py3-none-any.whl", hash = "sha256:2136ad0e9500d0949f96167e63f3e298620040aea8f9c74621959eda5d4cf8e6"}, + {file = "pydata_sphinx_theme-0.15.4.tar.gz", hash = "sha256:7762ec0ac59df3acecf49fd2f889e1b4565dbce8b88b2e29ee06fdd90645a06d"}, +] + +[package.dependencies] +accessible-pygments = "*" +Babel = "*" +beautifulsoup4 = "*" +docutils = "!=0.17.0" +packaging = "*" +pygments = ">=2.7" +sphinx = ">=5" +typing-extensions = "*" + +[package.extras] +a11y = ["pytest-playwright"] +dev = ["pandoc", "pre-commit", "pydata-sphinx-theme[doc,test]", "pyyaml", "sphinx-theme-builder[cli]", "tox"] +doc = ["ablog (>=0.11.8)", "colorama", "graphviz", "ipykernel", "ipyleaflet", "ipywidgets", "jupyter_sphinx", "jupyterlite-sphinx", "linkify-it-py", "matplotlib", "myst-parser", "nbsphinx", "numpy", "numpydoc", "pandas", "plotly", "rich", "sphinx-autoapi (>=3.0.0)", "sphinx-copybutton", "sphinx-design", "sphinx-favicon (>=1.0.1)", "sphinx-sitemap", "sphinx-togglebutton", "sphinxcontrib-youtube (>=1.4.1)", "sphinxext-rediraffe", "xarray"] +i18n = ["Babel", "jinja2"] +test = ["pytest", "pytest-cov", "pytest-regressions", "sphinx[test]"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "pygments" +version = "2.18.0" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, + {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, +] + +[package.extras] +windows-terminal = ["colorama (>=0.4.6)"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "pyjwt" +version = "2.9.0" +description = "JSON Web Token implementation in Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "PyJWT-2.9.0-py3-none-any.whl", hash = "sha256:3b02fb0f44517787776cf48f2ae25d8e14f300e6d7545a4315cee571a415e850"}, + {file = "pyjwt-2.9.0.tar.gz", hash = "sha256:7e1e5b56cc735432a7369cbfa0efe50fa113ebecdc04ae6922deba8b84582d0c"}, +] + +[package.extras] +crypto = ["cryptography (>=3.4.0)"] +dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pytest (>=6.0.0,<7.0.0)", "sphinx", "sphinx-rtd-theme", "zope.interface"] +docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"] +tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "pylint" +version = "3.3.1" +description = "python code static checker" +optional = false +python-versions = ">=3.9.0" +files = [ + {file = "pylint-3.3.1-py3-none-any.whl", hash = "sha256:2f846a466dd023513240bc140ad2dd73bfc080a5d85a710afdb728c420a5a2b9"}, + {file = "pylint-3.3.1.tar.gz", hash = "sha256:9f3dcc87b1203e612b78d91a896407787e708b3f189b5fa0b307712d49ff0c6e"}, +] + +[package.dependencies] +astroid = ">=3.3.4,<=3.4.0-dev0" +colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} +dill = {version = ">=0.3.7", markers = "python_version >= \"3.12\""} +isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" +mccabe = ">=0.6,<0.8" +platformdirs = ">=2.2.0" +tomlkit = ">=0.10.1" + +[package.extras] +spelling = ["pyenchant (>=3.2,<4.0)"] +testutils = ["gitpython (>3)"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "pytest" +version = "8.3.3" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2"}, + {file = "pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.5,<2" + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "pytest-cov" +version = "5.0.0" +description = "Pytest plugin for measuring coverage." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"}, + {file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"}, +] + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "python-dotenv" +version = "1.0.1" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, + {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "python-json-logger" +version = "2.0.7" +description = "A python library adding a json log formatter" +optional = false +python-versions = ">=3.6" +files = [ + {file = "python-json-logger-2.0.7.tar.gz", hash = "sha256:23e7ec02d34237c5aa1e29a070193a4ea87583bb4e7f8fd06d3de8264c4b2e1c"}, + {file = "python_json_logger-2.0.7-py3-none-any.whl", hash = "sha256:f380b826a991ebbe3de4d897aeec42760035ac760345e57b812938dc8b35e2bd"}, +] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "pyyaml" +version = "6.0.2" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, + {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, + {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, + {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, + {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, + {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, + {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, + {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, + {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, + {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, + {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, + {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, + {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, + {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, + {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, + {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, + {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, +] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "requests" +version = "2.32.3" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.8" +files = [ + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "s3transfer" +version = "0.10.3" +description = "An Amazon S3 Transfer Manager" +optional = false +python-versions = ">= 3.8" +files = [ + {file = "s3transfer-0.10.3-py3-none-any.whl", hash = "sha256:263ed587a5803c6c708d3ce44dc4dfedaab4c1a32e8329bab818933d79ddcf5d"}, + {file = "s3transfer-0.10.3.tar.gz", hash = "sha256:4f50ed74ab84d474ce614475e0b8d5047ff080810aac5d01ea25231cfc944b0c"}, +] + +[package.dependencies] +botocore = ">=1.33.2,<2.0a.0" + +[package.extras] +crt = ["botocore[crt] (>=1.33.2,<2.0a.0)"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "sniffio" +version = "1.3.1" +description = "Sniff out which async library your code is running under" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, + {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, +] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "snowballstemmer" +version = "2.2.0" +description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." +optional = false +python-versions = "*" +files = [ + {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, + {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, +] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "soupsieve" +version = "2.6" +description = "A modern CSS selector implementation for Beautiful Soup." +optional = false +python-versions = ">=3.8" +files = [ + {file = "soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9"}, + {file = "soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb"}, +] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "sphinx" +version = "8.1.3" +description = "Python documentation generator" +optional = false +python-versions = ">=3.10" +files = [ + {file = "sphinx-8.1.3-py3-none-any.whl", hash = "sha256:09719015511837b76bf6e03e42eb7595ac8c2e41eeb9c29c5b755c6b677992a2"}, + {file = "sphinx-8.1.3.tar.gz", hash = "sha256:43c1911eecb0d3e161ad78611bc905d1ad0e523e4ddc202a58a821773dc4c927"}, +] + +[package.dependencies] +alabaster = ">=0.7.14" +babel = ">=2.13" +colorama = {version = ">=0.4.6", markers = "sys_platform == \"win32\""} +docutils = ">=0.20,<0.22" +imagesize = ">=1.3" +Jinja2 = ">=3.1" +packaging = ">=23.0" +Pygments = ">=2.17" +requests = ">=2.30.0" +snowballstemmer = ">=2.2" +sphinxcontrib-applehelp = ">=1.0.7" +sphinxcontrib-devhelp = ">=1.0.6" +sphinxcontrib-htmlhelp = ">=2.0.6" +sphinxcontrib-jsmath = ">=1.0.1" +sphinxcontrib-qthelp = ">=1.0.6" +sphinxcontrib-serializinghtml = ">=1.1.9" + +[package.extras] +docs = ["sphinxcontrib-websupport"] +lint = ["flake8 (>=6.0)", "mypy (==1.11.1)", "pyright (==1.1.384)", "pytest (>=6.0)", "ruff (==0.6.9)", "sphinx-lint (>=0.9)", "tomli (>=2)", "types-Pillow (==10.2.0.20240822)", "types-Pygments (==2.18.0.20240506)", "types-colorama (==0.4.15.20240311)", "types-defusedxml (==0.7.0.20240218)", "types-docutils (==0.21.0.20241005)", "types-requests (==2.32.0.20240914)", "types-urllib3 (==1.26.25.14)"] +test = ["cython (>=3.0)", "defusedxml (>=0.7.1)", "pytest (>=8.0)", "setuptools (>=70.0)", "typing_extensions (>=4.9)"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "sphinx-autoapi" +version = "3.3.2" +description = "Sphinx API documentation generator" +optional = false +python-versions = ">=3.8" +files = [ + {file = "sphinx_autoapi-3.3.2-py2.py3-none-any.whl", hash = "sha256:08afa656f7fcd45fe7dd64bf9f44698ddb8ca7c2d5cd0614c7455912ed580324"}, + {file = "sphinx_autoapi-3.3.2.tar.gz", hash = "sha256:ebf8b44b2ebab5c28f0263ec6c2f8acdd156e9b2d539a58eca39d2f368445173"}, +] + +[package.dependencies] +astroid = {version = ">=3.0.0a1", markers = "python_version >= \"3.12\""} +Jinja2 = "*" +PyYAML = "*" +sphinx = ">=6.1.0" + +[package.extras] +docs = ["furo", "sphinx", "sphinx-design"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "sphinxcontrib-applehelp" +version = "2.0.0" +description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5"}, + {file = "sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1"}, +] + +[package.extras] +lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] +standalone = ["Sphinx (>=5)"] +test = ["pytest"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "sphinxcontrib-devhelp" +version = "2.0.0" +description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp documents" +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2"}, + {file = "sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad"}, +] + +[package.extras] +lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] +standalone = ["Sphinx (>=5)"] +test = ["pytest"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "sphinxcontrib-htmlhelp" +version = "2.1.0" +description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8"}, + {file = "sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9"}, +] + +[package.extras] +lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] +standalone = ["Sphinx (>=5)"] +test = ["html5lib", "pytest"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "sphinxcontrib-jsmath" +version = "1.0.1" +description = "A sphinx extension which renders display math in HTML via JavaScript" +optional = false +python-versions = ">=3.5" +files = [ + {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, + {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, +] + +[package.extras] +test = ["flake8", "mypy", "pytest"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "sphinxcontrib-qthelp" +version = "2.0.0" +description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp documents" +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb"}, + {file = "sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab"}, +] + +[package.extras] +lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] +standalone = ["Sphinx (>=5)"] +test = ["defusedxml (>=0.7.1)", "pytest"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "sphinxcontrib-serializinghtml" +version = "2.0.0" +description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)" +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331"}, + {file = "sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d"}, +] + +[package.extras] +lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] +standalone = ["Sphinx (>=5)"] +test = ["pytest"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "starlette" +version = "0.38.6" +description = "The little ASGI library that shines." +optional = false +python-versions = ">=3.8" +files = [ + {file = "starlette-0.38.6-py3-none-any.whl", hash = "sha256:4517a1409e2e73ee4951214ba012052b9e16f60e90d73cfb06192c19203bbb05"}, + {file = "starlette-0.38.6.tar.gz", hash = "sha256:863a1588f5574e70a821dadefb41e4881ea451a47a3cd1b4df359d4ffefe5ead"}, +] + +[package.dependencies] +anyio = ">=3.4.0,<5" + +[package.extras] +full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7)", "pyyaml"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "tomli" +version = "2.0.2" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38"}, + {file = "tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed"}, +] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "tomlkit" +version = "0.13.2" +description = "Style preserving TOML library" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde"}, + {file = "tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79"}, +] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "types-requests" +version = "2.32.0.20240914" +description = "Typing stubs for requests" +optional = false +python-versions = ">=3.8" +files = [ + {file = "types-requests-2.32.0.20240914.tar.gz", hash = "sha256:2850e178db3919d9bf809e434eef65ba49d0e7e33ac92d588f4a5e295fffd405"}, + {file = "types_requests-2.32.0.20240914-py3-none-any.whl", hash = "sha256:59c2f673eb55f32a99b2894faf6020e1a9f4a402ad0f192bfee0b64469054310"}, +] + +[package.dependencies] +urllib3 = ">=2" + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "typing-extensions" +version = "4.12.2" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, +] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "urllib3" +version = "2.2.3" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.8" +files = [ + {file = "urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac"}, + {file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "uvicorn" +version = "0.31.1" +description = "The lightning-fast ASGI server." +optional = false +python-versions = ">=3.8" +files = [ + {file = "uvicorn-0.31.1-py3-none-any.whl", hash = "sha256:adc42d9cac80cf3e51af97c1851648066841e7cfb6993a4ca8de29ac1548ed41"}, + {file = "uvicorn-0.31.1.tar.gz", hash = "sha256:f5167919867b161b7bcaf32646c6a94cdbd4c3aa2eb5c17d36bb9aa5cfd8c493"}, +] + +[package.dependencies] +click = ">=7.0" +colorama = {version = ">=0.4", optional = true, markers = "sys_platform == \"win32\" and extra == \"standard\""} +h11 = ">=0.8" +httptools = {version = ">=0.5.0", optional = true, markers = "extra == \"standard\""} +python-dotenv = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} +pyyaml = {version = ">=5.1", optional = true, markers = "extra == \"standard\""} +uvloop = {version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1", optional = true, markers = "(sys_platform != \"win32\" and sys_platform != \"cygwin\") and platform_python_implementation != \"PyPy\" and extra == \"standard\""} +watchfiles = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} +websockets = {version = ">=10.4", optional = true, markers = "extra == \"standard\""} + +[package.extras] +standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "uvloop" +version = "0.20.0" +description = "Fast implementation of asyncio event loop on top of libuv" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "uvloop-0.20.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9ebafa0b96c62881d5cafa02d9da2e44c23f9f0cd829f3a32a6aff771449c996"}, + {file = "uvloop-0.20.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:35968fc697b0527a06e134999eef859b4034b37aebca537daeb598b9d45a137b"}, + {file = "uvloop-0.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b16696f10e59d7580979b420eedf6650010a4a9c3bd8113f24a103dfdb770b10"}, + {file = "uvloop-0.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b04d96188d365151d1af41fa2d23257b674e7ead68cfd61c725a422764062ae"}, + {file = "uvloop-0.20.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:94707205efbe809dfa3a0d09c08bef1352f5d3d6612a506f10a319933757c006"}, + {file = "uvloop-0.20.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:89e8d33bb88d7263f74dc57d69f0063e06b5a5ce50bb9a6b32f5fcbe655f9e73"}, + {file = "uvloop-0.20.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e50289c101495e0d1bb0bfcb4a60adde56e32f4449a67216a1ab2750aa84f037"}, + {file = "uvloop-0.20.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e237f9c1e8a00e7d9ddaa288e535dc337a39bcbf679f290aee9d26df9e72bce9"}, + {file = "uvloop-0.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:746242cd703dc2b37f9d8b9f173749c15e9a918ddb021575a0205ec29a38d31e"}, + {file = "uvloop-0.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82edbfd3df39fb3d108fc079ebc461330f7c2e33dbd002d146bf7c445ba6e756"}, + {file = "uvloop-0.20.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:80dc1b139516be2077b3e57ce1cb65bfed09149e1d175e0478e7a987863b68f0"}, + {file = "uvloop-0.20.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4f44af67bf39af25db4c1ac27e82e9665717f9c26af2369c404be865c8818dcf"}, + {file = "uvloop-0.20.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:4b75f2950ddb6feed85336412b9a0c310a2edbcf4cf931aa5cfe29034829676d"}, + {file = "uvloop-0.20.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:77fbc69c287596880ecec2d4c7a62346bef08b6209749bf6ce8c22bbaca0239e"}, + {file = "uvloop-0.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6462c95f48e2d8d4c993a2950cd3d31ab061864d1c226bbf0ee2f1a8f36674b9"}, + {file = "uvloop-0.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:649c33034979273fa71aa25d0fe120ad1777c551d8c4cd2c0c9851d88fcb13ab"}, + {file = "uvloop-0.20.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3a609780e942d43a275a617c0839d85f95c334bad29c4c0918252085113285b5"}, + {file = "uvloop-0.20.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aea15c78e0d9ad6555ed201344ae36db5c63d428818b4b2a42842b3870127c00"}, + {file = "uvloop-0.20.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:f0e94b221295b5e69de57a1bd4aeb0b3a29f61be6e1b478bb8a69a73377db7ba"}, + {file = "uvloop-0.20.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:fee6044b64c965c425b65a4e17719953b96e065c5b7e09b599ff332bb2744bdf"}, + {file = "uvloop-0.20.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:265a99a2ff41a0fd56c19c3838b29bf54d1d177964c300dad388b27e84fd7847"}, + {file = "uvloop-0.20.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b10c2956efcecb981bf9cfb8184d27d5d64b9033f917115a960b83f11bfa0d6b"}, + {file = "uvloop-0.20.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e7d61fe8e8d9335fac1bf8d5d82820b4808dd7a43020c149b63a1ada953d48a6"}, + {file = "uvloop-0.20.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2beee18efd33fa6fdb0976e18475a4042cd31c7433c866e8a09ab604c7c22ff2"}, + {file = "uvloop-0.20.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d8c36fdf3e02cec92aed2d44f63565ad1522a499c654f07935c8f9d04db69e95"}, + {file = "uvloop-0.20.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a0fac7be202596c7126146660725157d4813aa29a4cc990fe51346f75ff8fde7"}, + {file = "uvloop-0.20.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d0fba61846f294bce41eb44d60d58136090ea2b5b99efd21cbdf4e21927c56a"}, + {file = "uvloop-0.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95720bae002ac357202e0d866128eb1ac82545bcf0b549b9abe91b5178d9b541"}, + {file = "uvloop-0.20.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:36c530d8fa03bfa7085af54a48f2ca16ab74df3ec7108a46ba82fd8b411a2315"}, + {file = "uvloop-0.20.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e97152983442b499d7a71e44f29baa75b3b02e65d9c44ba53b10338e98dedb66"}, + {file = "uvloop-0.20.0.tar.gz", hash = "sha256:4603ca714a754fc8d9b197e325db25b2ea045385e8a3ad05d3463de725fdf469"}, +] + +[package.extras] +docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] +test = ["Cython (>=0.29.36,<0.30.0)", "aiohttp (==3.9.0b0)", "aiohttp (>=3.8.1)", "flake8 (>=5.0,<6.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=23.0.0,<23.1.0)", "pycodestyle (>=2.9.0,<2.10.0)"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "watchfiles" +version = "0.24.0" +description = "Simple, modern and high performance file watching and code reload in python." +optional = false +python-versions = ">=3.8" +files = [ + {file = "watchfiles-0.24.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:083dc77dbdeef09fa44bb0f4d1df571d2e12d8a8f985dccde71ac3ac9ac067a0"}, + {file = "watchfiles-0.24.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e94e98c7cb94cfa6e071d401ea3342767f28eb5a06a58fafdc0d2a4974f4f35c"}, + {file = "watchfiles-0.24.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82ae557a8c037c42a6ef26c494d0631cacca040934b101d001100ed93d43f361"}, + {file = "watchfiles-0.24.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:acbfa31e315a8f14fe33e3542cbcafc55703b8f5dcbb7c1eecd30f141df50db3"}, + {file = "watchfiles-0.24.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b74fdffce9dfcf2dc296dec8743e5b0332d15df19ae464f0e249aa871fc1c571"}, + {file = "watchfiles-0.24.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:449f43f49c8ddca87c6b3980c9284cab6bd1f5c9d9a2b00012adaaccd5e7decd"}, + {file = "watchfiles-0.24.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4abf4ad269856618f82dee296ac66b0cd1d71450fc3c98532d93798e73399b7a"}, + {file = "watchfiles-0.24.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f895d785eb6164678ff4bb5cc60c5996b3ee6df3edb28dcdeba86a13ea0465e"}, + {file = "watchfiles-0.24.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7ae3e208b31be8ce7f4c2c0034f33406dd24fbce3467f77223d10cd86778471c"}, + {file = "watchfiles-0.24.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2efec17819b0046dde35d13fb8ac7a3ad877af41ae4640f4109d9154ed30a188"}, + {file = "watchfiles-0.24.0-cp310-none-win32.whl", hash = "sha256:6bdcfa3cd6fdbdd1a068a52820f46a815401cbc2cb187dd006cb076675e7b735"}, + {file = "watchfiles-0.24.0-cp310-none-win_amd64.whl", hash = "sha256:54ca90a9ae6597ae6dc00e7ed0a040ef723f84ec517d3e7ce13e63e4bc82fa04"}, + {file = "watchfiles-0.24.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:bdcd5538e27f188dd3c804b4a8d5f52a7fc7f87e7fd6b374b8e36a4ca03db428"}, + {file = "watchfiles-0.24.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2dadf8a8014fde6addfd3c379e6ed1a981c8f0a48292d662e27cabfe4239c83c"}, + {file = "watchfiles-0.24.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6509ed3f467b79d95fc62a98229f79b1a60d1b93f101e1c61d10c95a46a84f43"}, + {file = "watchfiles-0.24.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8360f7314a070c30e4c976b183d1d8d1585a4a50c5cb603f431cebcbb4f66327"}, + {file = "watchfiles-0.24.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:316449aefacf40147a9efaf3bd7c9bdd35aaba9ac5d708bd1eb5763c9a02bef5"}, + {file = "watchfiles-0.24.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73bde715f940bea845a95247ea3e5eb17769ba1010efdc938ffcb967c634fa61"}, + {file = "watchfiles-0.24.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3770e260b18e7f4e576edca4c0a639f704088602e0bc921c5c2e721e3acb8d15"}, + {file = "watchfiles-0.24.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa0fd7248cf533c259e59dc593a60973a73e881162b1a2f73360547132742823"}, + {file = "watchfiles-0.24.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d7a2e3b7f5703ffbd500dabdefcbc9eafeff4b9444bbdd5d83d79eedf8428fab"}, + {file = "watchfiles-0.24.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d831ee0a50946d24a53821819b2327d5751b0c938b12c0653ea5be7dea9c82ec"}, + {file = "watchfiles-0.24.0-cp311-none-win32.whl", hash = "sha256:49d617df841a63b4445790a254013aea2120357ccacbed00253f9c2b5dc24e2d"}, + {file = "watchfiles-0.24.0-cp311-none-win_amd64.whl", hash = "sha256:d3dcb774e3568477275cc76554b5a565024b8ba3a0322f77c246bc7111c5bb9c"}, + {file = "watchfiles-0.24.0-cp311-none-win_arm64.whl", hash = "sha256:9301c689051a4857d5b10777da23fafb8e8e921bcf3abe6448a058d27fb67633"}, + {file = "watchfiles-0.24.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:7211b463695d1e995ca3feb38b69227e46dbd03947172585ecb0588f19b0d87a"}, + {file = "watchfiles-0.24.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4b8693502d1967b00f2fb82fc1e744df128ba22f530e15b763c8d82baee15370"}, + {file = "watchfiles-0.24.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdab9555053399318b953a1fe1f586e945bc8d635ce9d05e617fd9fe3a4687d6"}, + {file = "watchfiles-0.24.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:34e19e56d68b0dad5cff62273107cf5d9fbaf9d75c46277aa5d803b3ef8a9e9b"}, + {file = "watchfiles-0.24.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:41face41f036fee09eba33a5b53a73e9a43d5cb2c53dad8e61fa6c9f91b5a51e"}, + {file = "watchfiles-0.24.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5148c2f1ea043db13ce9b0c28456e18ecc8f14f41325aa624314095b6aa2e9ea"}, + {file = "watchfiles-0.24.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7e4bd963a935aaf40b625c2499f3f4f6bbd0c3776f6d3bc7c853d04824ff1c9f"}, + {file = "watchfiles-0.24.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c79d7719d027b7a42817c5d96461a99b6a49979c143839fc37aa5748c322f234"}, + {file = "watchfiles-0.24.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:32aa53a9a63b7f01ed32e316e354e81e9da0e6267435c7243bf8ae0f10b428ef"}, + {file = "watchfiles-0.24.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ce72dba6a20e39a0c628258b5c308779b8697f7676c254a845715e2a1039b968"}, + {file = "watchfiles-0.24.0-cp312-none-win32.whl", hash = "sha256:d9018153cf57fc302a2a34cb7564870b859ed9a732d16b41a9b5cb2ebed2d444"}, + {file = "watchfiles-0.24.0-cp312-none-win_amd64.whl", hash = "sha256:551ec3ee2a3ac9cbcf48a4ec76e42c2ef938a7e905a35b42a1267fa4b1645896"}, + {file = "watchfiles-0.24.0-cp312-none-win_arm64.whl", hash = "sha256:b52a65e4ea43c6d149c5f8ddb0bef8d4a1e779b77591a458a893eb416624a418"}, + {file = "watchfiles-0.24.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:3d2e3ab79a1771c530233cadfd277fcc762656d50836c77abb2e5e72b88e3a48"}, + {file = "watchfiles-0.24.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:327763da824817b38ad125dcd97595f942d720d32d879f6c4ddf843e3da3fe90"}, + {file = "watchfiles-0.24.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd82010f8ab451dabe36054a1622870166a67cf3fce894f68895db6f74bbdc94"}, + {file = "watchfiles-0.24.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d64ba08db72e5dfd5c33be1e1e687d5e4fcce09219e8aee893a4862034081d4e"}, + {file = "watchfiles-0.24.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1cf1f6dd7825053f3d98f6d33f6464ebdd9ee95acd74ba2c34e183086900a827"}, + {file = "watchfiles-0.24.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:43e3e37c15a8b6fe00c1bce2473cfa8eb3484bbeecf3aefbf259227e487a03df"}, + {file = "watchfiles-0.24.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88bcd4d0fe1d8ff43675360a72def210ebad3f3f72cabfeac08d825d2639b4ab"}, + {file = "watchfiles-0.24.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:999928c6434372fde16c8f27143d3e97201160b48a614071261701615a2a156f"}, + {file = "watchfiles-0.24.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:30bbd525c3262fd9f4b1865cb8d88e21161366561cd7c9e1194819e0a33ea86b"}, + {file = "watchfiles-0.24.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:edf71b01dec9f766fb285b73930f95f730bb0943500ba0566ae234b5c1618c18"}, + {file = "watchfiles-0.24.0-cp313-none-win32.whl", hash = "sha256:f4c96283fca3ee09fb044f02156d9570d156698bc3734252175a38f0e8975f07"}, + {file = "watchfiles-0.24.0-cp313-none-win_amd64.whl", hash = "sha256:a974231b4fdd1bb7f62064a0565a6b107d27d21d9acb50c484d2cdba515b9366"}, + {file = "watchfiles-0.24.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:ee82c98bed9d97cd2f53bdb035e619309a098ea53ce525833e26b93f673bc318"}, + {file = "watchfiles-0.24.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fd92bbaa2ecdb7864b7600dcdb6f2f1db6e0346ed425fbd01085be04c63f0b05"}, + {file = "watchfiles-0.24.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f83df90191d67af5a831da3a33dd7628b02a95450e168785586ed51e6d28943c"}, + {file = "watchfiles-0.24.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fca9433a45f18b7c779d2bae7beeec4f740d28b788b117a48368d95a3233ed83"}, + {file = "watchfiles-0.24.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b995bfa6bf01a9e09b884077a6d37070464b529d8682d7691c2d3b540d357a0c"}, + {file = "watchfiles-0.24.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ed9aba6e01ff6f2e8285e5aa4154e2970068fe0fc0998c4380d0e6278222269b"}, + {file = "watchfiles-0.24.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5171ef898299c657685306d8e1478a45e9303ddcd8ac5fed5bd52ad4ae0b69b"}, + {file = "watchfiles-0.24.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4933a508d2f78099162da473841c652ad0de892719043d3f07cc83b33dfd9d91"}, + {file = "watchfiles-0.24.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:95cf3b95ea665ab03f5a54765fa41abf0529dbaf372c3b83d91ad2cfa695779b"}, + {file = "watchfiles-0.24.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:01def80eb62bd5db99a798d5e1f5f940ca0a05986dcfae21d833af7a46f7ee22"}, + {file = "watchfiles-0.24.0-cp38-none-win32.whl", hash = "sha256:4d28cea3c976499475f5b7a2fec6b3a36208656963c1a856d328aeae056fc5c1"}, + {file = "watchfiles-0.24.0-cp38-none-win_amd64.whl", hash = "sha256:21ab23fdc1208086d99ad3f69c231ba265628014d4aed31d4e8746bd59e88cd1"}, + {file = "watchfiles-0.24.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:b665caeeda58625c3946ad7308fbd88a086ee51ccb706307e5b1fa91556ac886"}, + {file = "watchfiles-0.24.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5c51749f3e4e269231510da426ce4a44beb98db2dce9097225c338f815b05d4f"}, + {file = "watchfiles-0.24.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82b2509f08761f29a0fdad35f7e1638b8ab1adfa2666d41b794090361fb8b855"}, + {file = "watchfiles-0.24.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a60e2bf9dc6afe7f743e7c9b149d1fdd6dbf35153c78fe3a14ae1a9aee3d98b"}, + {file = "watchfiles-0.24.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f7d9b87c4c55e3ea8881dfcbf6d61ea6775fffed1fedffaa60bd047d3c08c430"}, + {file = "watchfiles-0.24.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:78470906a6be5199524641f538bd2c56bb809cd4bf29a566a75051610bc982c3"}, + {file = "watchfiles-0.24.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:07cdef0c84c03375f4e24642ef8d8178e533596b229d32d2bbd69e5128ede02a"}, + {file = "watchfiles-0.24.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d337193bbf3e45171c8025e291530fb7548a93c45253897cd764a6a71c937ed9"}, + {file = "watchfiles-0.24.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ec39698c45b11d9694a1b635a70946a5bad066b593af863460a8e600f0dff1ca"}, + {file = "watchfiles-0.24.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2e28d91ef48eab0afb939fa446d8ebe77e2f7593f5f463fd2bb2b14132f95b6e"}, + {file = "watchfiles-0.24.0-cp39-none-win32.whl", hash = "sha256:7138eff8baa883aeaa074359daabb8b6c1e73ffe69d5accdc907d62e50b1c0da"}, + {file = "watchfiles-0.24.0-cp39-none-win_amd64.whl", hash = "sha256:b3ef2c69c655db63deb96b3c3e587084612f9b1fa983df5e0c3379d41307467f"}, + {file = "watchfiles-0.24.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:632676574429bee8c26be8af52af20e0c718cc7f5f67f3fb658c71928ccd4f7f"}, + {file = "watchfiles-0.24.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:a2a9891723a735d3e2540651184be6fd5b96880c08ffe1a98bae5017e65b544b"}, + {file = "watchfiles-0.24.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a7fa2bc0efef3e209a8199fd111b8969fe9db9c711acc46636686331eda7dd4"}, + {file = "watchfiles-0.24.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01550ccf1d0aed6ea375ef259706af76ad009ef5b0203a3a4cce0f6024f9b68a"}, + {file = "watchfiles-0.24.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:96619302d4374de5e2345b2b622dc481257a99431277662c30f606f3e22f42be"}, + {file = "watchfiles-0.24.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:85d5f0c7771dcc7a26c7a27145059b6bb0ce06e4e751ed76cdf123d7039b60b5"}, + {file = "watchfiles-0.24.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:951088d12d339690a92cef2ec5d3cfd957692834c72ffd570ea76a6790222777"}, + {file = "watchfiles-0.24.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49fb58bcaa343fedc6a9e91f90195b20ccb3135447dc9e4e2570c3a39565853e"}, + {file = "watchfiles-0.24.0.tar.gz", hash = "sha256:afb72325b74fa7a428c009c1b8be4b4d7c2afedafb2982827ef2156646df2fe1"}, +] + +[package.dependencies] +anyio = ">=3.0.0" + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "websockets" +version = "13.1" +description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "websockets-13.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f48c749857f8fb598fb890a75f540e3221d0976ed0bf879cf3c7eef34151acee"}, + {file = "websockets-13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c7e72ce6bda6fb9409cc1e8164dd41d7c91466fb599eb047cfda72fe758a34a7"}, + {file = "websockets-13.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f779498eeec470295a2b1a5d97aa1bc9814ecd25e1eb637bd9d1c73a327387f6"}, + {file = "websockets-13.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4676df3fe46956fbb0437d8800cd5f2b6d41143b6e7e842e60554398432cf29b"}, + {file = "websockets-13.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7affedeb43a70351bb811dadf49493c9cfd1ed94c9c70095fd177e9cc1541fa"}, + {file = "websockets-13.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1971e62d2caa443e57588e1d82d15f663b29ff9dfe7446d9964a4b6f12c1e700"}, + {file = "websockets-13.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5f2e75431f8dc4a47f31565a6e1355fb4f2ecaa99d6b89737527ea917066e26c"}, + {file = "websockets-13.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:58cf7e75dbf7e566088b07e36ea2e3e2bd5676e22216e4cad108d4df4a7402a0"}, + {file = "websockets-13.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c90d6dec6be2c7d03378a574de87af9b1efea77d0c52a8301dd831ece938452f"}, + {file = "websockets-13.1-cp310-cp310-win32.whl", hash = "sha256:730f42125ccb14602f455155084f978bd9e8e57e89b569b4d7f0f0c17a448ffe"}, + {file = "websockets-13.1-cp310-cp310-win_amd64.whl", hash = "sha256:5993260f483d05a9737073be197371940c01b257cc45ae3f1d5d7adb371b266a"}, + {file = "websockets-13.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:61fc0dfcda609cda0fc9fe7977694c0c59cf9d749fbb17f4e9483929e3c48a19"}, + {file = "websockets-13.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ceec59f59d092c5007e815def4ebb80c2de330e9588e101cf8bd94c143ec78a5"}, + {file = "websockets-13.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c1dca61c6db1166c48b95198c0b7d9c990b30c756fc2923cc66f68d17dc558fd"}, + {file = "websockets-13.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:308e20f22c2c77f3f39caca508e765f8725020b84aa963474e18c59accbf4c02"}, + {file = "websockets-13.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62d516c325e6540e8a57b94abefc3459d7dab8ce52ac75c96cad5549e187e3a7"}, + {file = "websockets-13.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87c6e35319b46b99e168eb98472d6c7d8634ee37750d7693656dc766395df096"}, + {file = "websockets-13.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5f9fee94ebafbc3117c30be1844ed01a3b177bb6e39088bc6b2fa1dc15572084"}, + {file = "websockets-13.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7c1e90228c2f5cdde263253fa5db63e6653f1c00e7ec64108065a0b9713fa1b3"}, + {file = "websockets-13.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6548f29b0e401eea2b967b2fdc1c7c7b5ebb3eeb470ed23a54cd45ef078a0db9"}, + {file = "websockets-13.1-cp311-cp311-win32.whl", hash = "sha256:c11d4d16e133f6df8916cc5b7e3e96ee4c44c936717d684a94f48f82edb7c92f"}, + {file = "websockets-13.1-cp311-cp311-win_amd64.whl", hash = "sha256:d04f13a1d75cb2b8382bdc16ae6fa58c97337253826dfe136195b7f89f661557"}, + {file = "websockets-13.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9d75baf00138f80b48f1eac72ad1535aac0b6461265a0bcad391fc5aba875cfc"}, + {file = "websockets-13.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9b6f347deb3dcfbfde1c20baa21c2ac0751afaa73e64e5b693bb2b848efeaa49"}, + {file = "websockets-13.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de58647e3f9c42f13f90ac7e5f58900c80a39019848c5547bc691693098ae1bd"}, + {file = "websockets-13.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1b54689e38d1279a51d11e3467dd2f3a50f5f2e879012ce8f2d6943f00e83f0"}, + {file = "websockets-13.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf1781ef73c073e6b0f90af841aaf98501f975d306bbf6221683dd594ccc52b6"}, + {file = "websockets-13.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d23b88b9388ed85c6faf0e74d8dec4f4d3baf3ecf20a65a47b836d56260d4b9"}, + {file = "websockets-13.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3c78383585f47ccb0fcf186dcb8a43f5438bd7d8f47d69e0b56f71bf431a0a68"}, + {file = "websockets-13.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d6d300f8ec35c24025ceb9b9019ae9040c1ab2f01cddc2bcc0b518af31c75c14"}, + {file = "websockets-13.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a9dcaf8b0cc72a392760bb8755922c03e17a5a54e08cca58e8b74f6902b433cf"}, + {file = "websockets-13.1-cp312-cp312-win32.whl", hash = "sha256:2f85cf4f2a1ba8f602298a853cec8526c2ca42a9a4b947ec236eaedb8f2dc80c"}, + {file = "websockets-13.1-cp312-cp312-win_amd64.whl", hash = "sha256:38377f8b0cdeee97c552d20cf1865695fcd56aba155ad1b4ca8779a5b6ef4ac3"}, + {file = "websockets-13.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a9ab1e71d3d2e54a0aa646ab6d4eebfaa5f416fe78dfe4da2839525dc5d765c6"}, + {file = "websockets-13.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b9d7439d7fab4dce00570bb906875734df13d9faa4b48e261c440a5fec6d9708"}, + {file = "websockets-13.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:327b74e915cf13c5931334c61e1a41040e365d380f812513a255aa804b183418"}, + {file = "websockets-13.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:325b1ccdbf5e5725fdcb1b0e9ad4d2545056479d0eee392c291c1bf76206435a"}, + {file = "websockets-13.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:346bee67a65f189e0e33f520f253d5147ab76ae42493804319b5716e46dddf0f"}, + {file = "websockets-13.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91a0fa841646320ec0d3accdff5b757b06e2e5c86ba32af2e0815c96c7a603c5"}, + {file = "websockets-13.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:18503d2c5f3943e93819238bf20df71982d193f73dcecd26c94514f417f6b135"}, + {file = "websockets-13.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:a9cd1af7e18e5221d2878378fbc287a14cd527fdd5939ed56a18df8a31136bb2"}, + {file = "websockets-13.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:70c5be9f416aa72aab7a2a76c90ae0a4fe2755c1816c153c1a2bcc3333ce4ce6"}, + {file = "websockets-13.1-cp313-cp313-win32.whl", hash = "sha256:624459daabeb310d3815b276c1adef475b3e6804abaf2d9d2c061c319f7f187d"}, + {file = "websockets-13.1-cp313-cp313-win_amd64.whl", hash = "sha256:c518e84bb59c2baae725accd355c8dc517b4a3ed8db88b4bc93c78dae2974bf2"}, + {file = "websockets-13.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:c7934fd0e920e70468e676fe7f1b7261c1efa0d6c037c6722278ca0228ad9d0d"}, + {file = "websockets-13.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:149e622dc48c10ccc3d2760e5f36753db9cacf3ad7bc7bbbfd7d9c819e286f23"}, + {file = "websockets-13.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a569eb1b05d72f9bce2ebd28a1ce2054311b66677fcd46cf36204ad23acead8c"}, + {file = "websockets-13.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95df24ca1e1bd93bbca51d94dd049a984609687cb2fb08a7f2c56ac84e9816ea"}, + {file = "websockets-13.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8dbb1bf0c0a4ae8b40bdc9be7f644e2f3fb4e8a9aca7145bfa510d4a374eeb7"}, + {file = "websockets-13.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:035233b7531fb92a76beefcbf479504db8c72eb3bff41da55aecce3a0f729e54"}, + {file = "websockets-13.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:e4450fc83a3df53dec45922b576e91e94f5578d06436871dce3a6be38e40f5db"}, + {file = "websockets-13.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:463e1c6ec853202dd3657f156123d6b4dad0c546ea2e2e38be2b3f7c5b8e7295"}, + {file = "websockets-13.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:6d6855bbe70119872c05107e38fbc7f96b1d8cb047d95c2c50869a46c65a8e96"}, + {file = "websockets-13.1-cp38-cp38-win32.whl", hash = "sha256:204e5107f43095012b00f1451374693267adbb832d29966a01ecc4ce1db26faf"}, + {file = "websockets-13.1-cp38-cp38-win_amd64.whl", hash = "sha256:485307243237328c022bc908b90e4457d0daa8b5cf4b3723fd3c4a8012fce4c6"}, + {file = "websockets-13.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9b37c184f8b976f0c0a231a5f3d6efe10807d41ccbe4488df8c74174805eea7d"}, + {file = "websockets-13.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:163e7277e1a0bd9fb3c8842a71661ad19c6aa7bb3d6678dc7f89b17fbcc4aeb7"}, + {file = "websockets-13.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4b889dbd1342820cc210ba44307cf75ae5f2f96226c0038094455a96e64fb07a"}, + {file = "websockets-13.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:586a356928692c1fed0eca68b4d1c2cbbd1ca2acf2ac7e7ebd3b9052582deefa"}, + {file = "websockets-13.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7bd6abf1e070a6b72bfeb71049d6ad286852e285f146682bf30d0296f5fbadfa"}, + {file = "websockets-13.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2aad13a200e5934f5a6767492fb07151e1de1d6079c003ab31e1823733ae79"}, + {file = "websockets-13.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:df01aea34b6e9e33572c35cd16bae5a47785e7d5c8cb2b54b2acdb9678315a17"}, + {file = "websockets-13.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e54affdeb21026329fb0744ad187cf812f7d3c2aa702a5edb562b325191fcab6"}, + {file = "websockets-13.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9ef8aa8bdbac47f4968a5d66462a2a0935d044bf35c0e5a8af152d58516dbeb5"}, + {file = "websockets-13.1-cp39-cp39-win32.whl", hash = "sha256:deeb929efe52bed518f6eb2ddc00cc496366a14c726005726ad62c2dd9017a3c"}, + {file = "websockets-13.1-cp39-cp39-win_amd64.whl", hash = "sha256:7c65ffa900e7cc958cd088b9a9157a8141c991f8c53d11087e6fb7277a03f81d"}, + {file = "websockets-13.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5dd6da9bec02735931fccec99d97c29f47cc61f644264eb995ad6c0c27667238"}, + {file = "websockets-13.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:2510c09d8e8df777177ee3d40cd35450dc169a81e747455cc4197e63f7e7bfe5"}, + {file = "websockets-13.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1c3cf67185543730888b20682fb186fc8d0fa6f07ccc3ef4390831ab4b388d9"}, + {file = "websockets-13.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bcc03c8b72267e97b49149e4863d57c2d77f13fae12066622dc78fe322490fe6"}, + {file = "websockets-13.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:004280a140f220c812e65f36944a9ca92d766b6cc4560be652a0a3883a79ed8a"}, + {file = "websockets-13.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e2620453c075abeb0daa949a292e19f56de518988e079c36478bacf9546ced23"}, + {file = "websockets-13.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9156c45750b37337f7b0b00e6248991a047be4aa44554c9886fe6bdd605aab3b"}, + {file = "websockets-13.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:80c421e07973a89fbdd93e6f2003c17d20b69010458d3a8e37fb47874bd67d51"}, + {file = "websockets-13.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82d0ba76371769d6a4e56f7e83bb8e81846d17a6190971e38b5de108bde9b0d7"}, + {file = "websockets-13.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e9875a0143f07d74dc5e1ded1c4581f0d9f7ab86c78994e2ed9e95050073c94d"}, + {file = "websockets-13.1-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a11e38ad8922c7961447f35c7b17bffa15de4d17c70abd07bfbe12d6faa3e027"}, + {file = "websockets-13.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:4059f790b6ae8768471cddb65d3c4fe4792b0ab48e154c9f0a04cefaabcd5978"}, + {file = "websockets-13.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:25c35bf84bf7c7369d247f0b8cfa157f989862c49104c5cf85cb5436a641d93e"}, + {file = "websockets-13.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:83f91d8a9bb404b8c2c41a707ac7f7f75b9442a0a876df295de27251a856ad09"}, + {file = "websockets-13.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a43cfdcddd07f4ca2b1afb459824dd3c6d53a51410636a2c7fc97b9a8cf4842"}, + {file = "websockets-13.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48a2ef1381632a2f0cb4efeff34efa97901c9fbc118e01951ad7cfc10601a9bb"}, + {file = "websockets-13.1-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:459bf774c754c35dbb487360b12c5727adab887f1622b8aed5755880a21c4a20"}, + {file = "websockets-13.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:95858ca14a9f6fa8413d29e0a585b31b278388aa775b8a81fa24830123874678"}, + {file = "websockets-13.1-py3-none-any.whl", hash = "sha256:a9a396a6ad26130cdae92ae10c36af09d9bfe6cafe69670fd3b6da9b07b4044f"}, + {file = "websockets-13.1.tar.gz", hash = "sha256:a3b3366087c1bc0a2795111edcadddb8b3b59509d5db5d7ea3fdd69f954a8878"}, +] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "yapf" +version = "0.40.2" +description = "A formatter for Python code" +optional = false +python-versions = ">=3.7" +files = [ + {file = "yapf-0.40.2-py3-none-any.whl", hash = "sha256:adc8b5dd02c0143108878c499284205adb258aad6db6634e5b869e7ee2bd548b"}, + {file = "yapf-0.40.2.tar.gz", hash = "sha256:4dab8a5ed7134e26d57c1647c7483afb3f136878b579062b786c9ba16b94637b"}, +] + +[package.dependencies] +importlib-metadata = ">=6.6.0" +platformdirs = ">=3.5.1" +tomli = ">=2.0.1" + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[[package]] +name = "zipp" +version = "3.20.2" +description = "Backport of pathlib-compatible object wrapper for zip files" +optional = false +python-versions = ">=3.8" +files = [ + {file = "zipp-3.20.2-py3-none-any.whl", hash = "sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350"}, + {file = "zipp-3.20.2.tar.gz", hash = "sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29"}, +] + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] +type = ["pytest-mypy"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "meteoswiss" + +[metadata] +lock-version = "2.0" +python-versions = "~3.12" +content-hash = "7cab465d8cd7c20274b71d8f2d3be736a023e4b1cd17f7b0445c6900b40f895b" diff --git a/pyproject.toml b/pyproject.toml index c8a6d09..3335b8b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,6 +14,7 @@ include = ["HISTORY.rst"] [tool.poetry.dependencies] python = "~3.12" mchpy = { extras = ["fastapi"], version = "^2.1.1" } +boto3 = "^1.35.40" [tool.poetry.group.dev.dependencies] mypy = "*" From 991e618c338245b25bef2ca31744401597fdc364 Mon Sep 17 00:00:00 2001 From: ninaburg Date: Tue, 15 Oct 2024 11:43:13 +0200 Subject: [PATCH 10/37] .env file to be included in git --- .gitignore | 2 -- flex_container_orchestrator/config/.env | 25 +++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 flex_container_orchestrator/config/.env diff --git a/.gitignore b/.gitignore index 546757d..e3d5c5b 100644 --- a/.gitignore +++ b/.gitignore @@ -80,8 +80,6 @@ celerybeat-schedule # SageMath parsed files *.sage.py -# dotenv -.env # virtualenv .venv diff --git a/flex_container_orchestrator/config/.env b/flex_container_orchestrator/config/.env new file mode 100644 index 0000000..1e882f0 --- /dev/null +++ b/flex_container_orchestrator/config/.env @@ -0,0 +1,25 @@ +# .env + +## Flexprep (Flexpart pre-processing) + +# Input/output S3 bucket settings +SVC__MAIN__S3_BUCKETS__INPUT__ENDPOINT_URL=https://object-store.os-api.cci1.ecmwf.int +SVC__MAIN__S3_BUCKETS__INPUT__NAME=flexpart-input +SVC__MAIN__S3_BUCKETS__OUTPUT__ENDPOINT_URL=https://object-store.os-api.cci1.ecmwf.int +SVC__MAIN__S3_BUCKETS__OUTPUT__NAME=flexprep-output + +# Timestep settings +SVC__MAIN__TIME_SETTINGS__TINCR=1 # Interval in hours for forecasts (Global: 3h, Europe: 1h) +SVC__MAIN__TIME_SETTINGS__TSTART=0 # Start time + +## Flexpart-IFS + +# Input/output S3 bucket settings +SVC__MAIN__S3_BUCKETS__INPUT__ENDPOINT_URL=${SVC__MAIN__S3_BUCKETS__OUTPUT__ENDPOINT_URL} # Inherit from Flexprep output +SVC__MAIN__S3_BUCKETS__INPUT__NAME=${SVC__MAIN__S3_BUCKETS__OUTPUT__NAME} # Inherit from Flexprep output +SVC__MAIN__S3_BUCKETS__OUTPUT__ENDPOINT_URL=https://object-store.os-api.cci1.ecmwf.int +SVC__MAIN__S3_BUCKETS__OUTPUT__NAME=flexpart-output + +# EWC buckets access and secret keys +SVC__LOGGING__S3_ACCESS_KEY= +SVC__LOGGING__S3_SECRET_KEY= From 5ff0efd78a961abbb08ad830aefce0ecf4e68951 Mon Sep 17 00:00:00 2001 From: ninaburg Date: Tue, 15 Oct 2024 11:45:04 +0200 Subject: [PATCH 11/37] Fixes --- flex_container_orchestrator/config/.env | 15 +++++++++------ .../config/aviso/listener_diss.yaml | 2 +- .../domain/aggregator_flexpart.py | 3 +-- flex_container_orchestrator/domain/greeting.py | 6 ------ .../services/flexpart_service.py | 9 ++++++--- 5 files changed, 17 insertions(+), 18 deletions(-) delete mode 100644 flex_container_orchestrator/domain/greeting.py diff --git a/flex_container_orchestrator/config/.env b/flex_container_orchestrator/config/.env index 1e882f0..4c37310 100644 --- a/flex_container_orchestrator/config/.env +++ b/flex_container_orchestrator/config/.env @@ -9,17 +9,20 @@ SVC__MAIN__S3_BUCKETS__OUTPUT__ENDPOINT_URL=https://object-store.os-api.cci1.ecm SVC__MAIN__S3_BUCKETS__OUTPUT__NAME=flexprep-output # Timestep settings -SVC__MAIN__TIME_SETTINGS__TINCR=1 # Interval in hours for forecasts (Global: 3h, Europe: 1h) -SVC__MAIN__TIME_SETTINGS__TSTART=0 # Start time +# Interval in hours for forecasts (Global: 3h, Europe: 1h) +SVC__MAIN__TIME_SETTINGS__TINCR=1 +SVC__MAIN__TIME_SETTINGS__TSTART=0 ## Flexpart-IFS # Input/output S3 bucket settings -SVC__MAIN__S3_BUCKETS__INPUT__ENDPOINT_URL=${SVC__MAIN__S3_BUCKETS__OUTPUT__ENDPOINT_URL} # Inherit from Flexprep output -SVC__MAIN__S3_BUCKETS__INPUT__NAME=${SVC__MAIN__S3_BUCKETS__OUTPUT__NAME} # Inherit from Flexprep output +# Inherit from Flexprep output +SVC__MAIN__S3_BUCKETS__INPUT__ENDPOINT_URL=https://object-store.os-api.cci1.ecmwf.int +# Inherit from Flexprep output +SVC__MAIN__S3_BUCKETS__INPUT__NAME=flexprep-output SVC__MAIN__S3_BUCKETS__OUTPUT__ENDPOINT_URL=https://object-store.os-api.cci1.ecmwf.int SVC__MAIN__S3_BUCKETS__OUTPUT__NAME=flexpart-output # EWC buckets access and secret keys -SVC__LOGGING__S3_ACCESS_KEY= -SVC__LOGGING__S3_SECRET_KEY= +S3_ACCESS_KEY= +S3_SECRET_KEY= diff --git a/flex_container_orchestrator/config/aviso/listener_diss.yaml b/flex_container_orchestrator/config/aviso/listener_diss.yaml index 74b11a8..3e4e192 100644 --- a/flex_container_orchestrator/config/aviso/listener_diss.yaml +++ b/flex_container_orchestrator/config/aviso/listener_diss.yaml @@ -12,7 +12,7 @@ listeners: - type: command working_dir: $HOME command: > - python3 ../../services/aviso/run_docker_script.py \ + python3 flex-container-orchestrator/flex-container-orchestrator/main.py \ --step "${request.step}" \ --date "${request.date}" \ --time "${request.time}" \ diff --git a/flex_container_orchestrator/domain/aggregator_flexpart.py b/flex_container_orchestrator/domain/aggregator_flexpart.py index 2f572b9..ea6a5a4 100644 --- a/flex_container_orchestrator/domain/aggregator_flexpart.py +++ b/flex_container_orchestrator/domain/aggregator_flexpart.py @@ -77,7 +77,7 @@ def is_row_processed(conn, forecast_ref_time, step): if result: return result[0] == 1 else: - logging.warning( + logging.info( f"No row found for forecast_ref_time={forecast_ref_time} and step={step}." ) return False @@ -187,7 +187,6 @@ def define_config(st: datetime.datetime, et: datetime.datetime): def main(): args = parse_arguments() - # Initialize variables DATE = args.date TIME = args.time STEP = args.step diff --git a/flex_container_orchestrator/domain/greeting.py b/flex_container_orchestrator/domain/greeting.py deleted file mode 100644 index e921010..0000000 --- a/flex_container_orchestrator/domain/greeting.py +++ /dev/null @@ -1,6 +0,0 @@ -from pydantic import BaseModel # pylint: disable=no-name-in-module - - -class Greeting(BaseModel): - """The greeting's model""" - message: str diff --git a/flex_container_orchestrator/services/flexpart_service.py b/flex_container_orchestrator/services/flexpart_service.py index a3d6a99..8ed88c1 100755 --- a/flex_container_orchestrator/services/flexpart_service.py +++ b/flex_container_orchestrator/services/flexpart_service.py @@ -53,7 +53,7 @@ def launch_containers(date, location, time, step): docker_run_command = [ "docker", "run", "--mount", f"type=bind,source={db_mount},destination=/src/db/", - "--env-file", ".env", + "--env-file", "flex_container_orchestrator/config/.env", docker_image, "--step", step, "--date", date, @@ -67,7 +67,7 @@ def launch_containers(date, location, time, step): logging.error("Docker run processing failed.") sys.exit(1) - logging.info("Docker container processing executed successfully.") + logging.info("Pre-processing container executed successfully.") # ====== Second part: Run aggregator_flexpart.py ====== db_path = os.path.expanduser('~/.sqlite/sqlite3-db') @@ -78,7 +78,7 @@ def launch_containers(date, location, time, step): try: script_dir = os.path.dirname(os.path.abspath(__file__)) - aggregator_script_path = os.path.join(script_dir, '..', 'aggregator', 'aggregator_flexpart.py') + aggregator_script_path = os.path.join(script_dir, '..', 'domain', 'aggregator_flexpart.py') aggregator_command = [ "python3", aggregator_script_path, @@ -89,6 +89,9 @@ def launch_containers(date, location, time, step): ] output = run_command(aggregator_command, capture_output=True) + if not output: + logging.info("Flexpart can't be launched. Not enough pre-processed files. Exiting.") + sys.exit(0) try: configurations = json.loads(output.decode('utf-8')) except json.JSONDecodeError as e: From d87eb1ed116cc9504140138157adadcfcb21e63a Mon Sep 17 00:00:00 2001 From: ninaburg Date: Tue, 15 Oct 2024 16:55:23 +0200 Subject: [PATCH 12/37] update flexprep image --- flex_container_orchestrator/services/flexpart_service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flex_container_orchestrator/services/flexpart_service.py b/flex_container_orchestrator/services/flexpart_service.py index 8ed88c1..dc9f462 100755 --- a/flex_container_orchestrator/services/flexpart_service.py +++ b/flex_container_orchestrator/services/flexpart_service.py @@ -42,7 +42,7 @@ def launch_containers(date, location, time, step): sys.exit(1) # Run pre-processing for Flexpart - docker_image = "493666016161.dkr.ecr.eu-central-2.amazonaws.com/numericalweatherpredictions/flexpart_ifs/flexprep:2409.ee22f6c67c86b9f85185edb02924e6ab523fa0bc" + docker_image = "493666016161.dkr.ecr.eu-central-2.amazonaws.com/numericalweatherpredictions/dispersionmodelling/flexpart-ifs/flexprep:2410.d02afb52a3021ad13aa734fc400e84026347ce7b" db_mount = os.path.expanduser('~/.sqlite/') if not os.path.exists(db_mount): From 8b073742a0bbc2f60c0b9c04598067eaab64d423 Mon Sep 17 00:00:00 2001 From: ninaburg Date: Tue, 15 Oct 2024 16:55:40 +0200 Subject: [PATCH 13/37] update poetry.lock --- poetry.lock | 150 ++++++++++++++++++++++++++++------------------------ 1 file changed, 81 insertions(+), 69 deletions(-) diff --git a/poetry.lock b/poetry.lock index 20104f6..cbcf0c7 100644 --- a/poetry.lock +++ b/poetry.lock @@ -57,13 +57,13 @@ reference = "meteoswiss" [[package]] name = "anyio" -version = "4.6.1" +version = "4.6.2.post1" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false python-versions = ">=3.9" files = [ - {file = "anyio-4.6.1-py3-none-any.whl", hash = "sha256:0632863c9044798a494a05cab0b159dfad6a3f064094863a45878320eb4e8ed2"}, - {file = "anyio-4.6.1.tar.gz", hash = "sha256:936e6613a08e8f71a300cfffca1c1c0806335607247696ac45f9b32c63bfb9aa"}, + {file = "anyio-4.6.2.post1-py3-none-any.whl", hash = "sha256:6d170c36fba3bdd840c73d3868c1e777e33676a69c3a72cf0a0d5d6d8009b61d"}, + {file = "anyio-4.6.2.post1.tar.gz", hash = "sha256:4c8bc31ccdb51c7f7bd251f51c609e038d63e34219b44aa86e47576389880b4c"}, ] [package.dependencies] @@ -721,51 +721,58 @@ reference = "meteoswiss" [[package]] name = "httptools" -version = "0.6.1" +version = "0.6.2" description = "A collection of framework independent HTTP protocol utils." optional = false python-versions = ">=3.8.0" files = [ - {file = "httptools-0.6.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d2f6c3c4cb1948d912538217838f6e9960bc4a521d7f9b323b3da579cd14532f"}, - {file = "httptools-0.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:00d5d4b68a717765b1fabfd9ca755bd12bf44105eeb806c03d1962acd9b8e563"}, - {file = "httptools-0.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:639dc4f381a870c9ec860ce5c45921db50205a37cc3334e756269736ff0aac58"}, - {file = "httptools-0.6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e57997ac7fb7ee43140cc03664de5f268813a481dff6245e0075925adc6aa185"}, - {file = "httptools-0.6.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0ac5a0ae3d9f4fe004318d64b8a854edd85ab76cffbf7ef5e32920faef62f142"}, - {file = "httptools-0.6.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3f30d3ce413088a98b9db71c60a6ada2001a08945cb42dd65a9a9fe228627658"}, - {file = "httptools-0.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:1ed99a373e327f0107cb513b61820102ee4f3675656a37a50083eda05dc9541b"}, - {file = "httptools-0.6.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7a7ea483c1a4485c71cb5f38be9db078f8b0e8b4c4dc0210f531cdd2ddac1ef1"}, - {file = "httptools-0.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:85ed077c995e942b6f1b07583e4eb0a8d324d418954fc6af913d36db7c05a5a0"}, - {file = "httptools-0.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b0bb634338334385351a1600a73e558ce619af390c2b38386206ac6a27fecfc"}, - {file = "httptools-0.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d9ceb2c957320def533671fc9c715a80c47025139c8d1f3797477decbc6edd2"}, - {file = "httptools-0.6.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4f0f8271c0a4db459f9dc807acd0eadd4839934a4b9b892f6f160e94da309837"}, - {file = "httptools-0.6.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6a4f5ccead6d18ec072ac0b84420e95d27c1cdf5c9f1bc8fbd8daf86bd94f43d"}, - {file = "httptools-0.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:5cceac09f164bcba55c0500a18fe3c47df29b62353198e4f37bbcc5d591172c3"}, - {file = "httptools-0.6.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:75c8022dca7935cba14741a42744eee13ba05db00b27a4b940f0d646bd4d56d0"}, - {file = "httptools-0.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:48ed8129cd9a0d62cf4d1575fcf90fb37e3ff7d5654d3a5814eb3d55f36478c2"}, - {file = "httptools-0.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f58e335a1402fb5a650e271e8c2d03cfa7cea46ae124649346d17bd30d59c90"}, - {file = "httptools-0.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93ad80d7176aa5788902f207a4e79885f0576134695dfb0fefc15b7a4648d503"}, - {file = "httptools-0.6.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9bb68d3a085c2174c2477eb3ffe84ae9fb4fde8792edb7bcd09a1d8467e30a84"}, - {file = "httptools-0.6.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b512aa728bc02354e5ac086ce76c3ce635b62f5fbc32ab7082b5e582d27867bb"}, - {file = "httptools-0.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:97662ce7fb196c785344d00d638fc9ad69e18ee4bfb4000b35a52efe5adcc949"}, - {file = "httptools-0.6.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8e216a038d2d52ea13fdd9b9c9c7459fb80d78302b257828285eca1c773b99b3"}, - {file = "httptools-0.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3e802e0b2378ade99cd666b5bffb8b2a7cc8f3d28988685dc300469ea8dd86cb"}, - {file = "httptools-0.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4bd3e488b447046e386a30f07af05f9b38d3d368d1f7b4d8f7e10af85393db97"}, - {file = "httptools-0.6.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe467eb086d80217b7584e61313ebadc8d187a4d95bb62031b7bab4b205c3ba3"}, - {file = "httptools-0.6.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3c3b214ce057c54675b00108ac42bacf2ab8f85c58e3f324a4e963bbc46424f4"}, - {file = "httptools-0.6.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8ae5b97f690badd2ca27cbf668494ee1b6d34cf1c464271ef7bfa9ca6b83ffaf"}, - {file = "httptools-0.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:405784577ba6540fa7d6ff49e37daf104e04f4b4ff2d1ac0469eaa6a20fde084"}, - {file = "httptools-0.6.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:95fb92dd3649f9cb139e9c56604cc2d7c7bf0fc2e7c8d7fbd58f96e35eddd2a3"}, - {file = "httptools-0.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dcbab042cc3ef272adc11220517278519adf8f53fd3056d0e68f0a6f891ba94e"}, - {file = "httptools-0.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cf2372e98406efb42e93bfe10f2948e467edfd792b015f1b4ecd897903d3e8d"}, - {file = "httptools-0.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:678fcbae74477a17d103b7cae78b74800d795d702083867ce160fc202104d0da"}, - {file = "httptools-0.6.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e0b281cf5a125c35f7f6722b65d8542d2e57331be573e9e88bc8b0115c4a7a81"}, - {file = "httptools-0.6.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:95658c342529bba4e1d3d2b1a874db16c7cca435e8827422154c9da76ac4e13a"}, - {file = "httptools-0.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:7ebaec1bf683e4bf5e9fbb49b8cc36da482033596a415b3e4ebab5a4c0d7ec5e"}, - {file = "httptools-0.6.1.tar.gz", hash = "sha256:c6e26c30455600b95d94b1b836085138e82f177351454ee841c148f93a9bad5a"}, + {file = "httptools-0.6.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0238f07780782c018e9801d8f5f5aea3a4680a1af132034b444f677718c6fe88"}, + {file = "httptools-0.6.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:10d28e5597d4349390c640232c9366ddc15568114f56724fe30a53de9686b6ab"}, + {file = "httptools-0.6.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9ddaf99e362ae4169f6a8b3508f3487264e0a1b1e58c0b07b86407bc9ecee831"}, + {file = "httptools-0.6.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efc9d039b6b8a36b182bc60774bb5d456b8ff9ec44cf97719f2f38bb1dcdd546"}, + {file = "httptools-0.6.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b57cb8a4a8a8ffdaf0395326ef3b9c1aba36e58a421438fc04c002a1f511db63"}, + {file = "httptools-0.6.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b73cda1326738eab5d60640ca0b87ac4e4db09a099423c41b59a5681917e8d1d"}, + {file = "httptools-0.6.2-cp310-cp310-win_amd64.whl", hash = "sha256:352a496244360deb1c1d108391d76cd6f3dd9f53ccf975a082e74c6761af30c9"}, + {file = "httptools-0.6.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2e9d225b178a6cc700c23cf2f5daf85a10f93f1db7c34e9ee4ee0bbc29ad458a"}, + {file = "httptools-0.6.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d49b14fcc9b12a52da8667587efa124a18e1a3eb63bbbcabf9882f4008d171d6"}, + {file = "httptools-0.6.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d5c33d98b2311ddbe06e92b12b14de334dcfbe64ebcbb2c7a34b5c6036db512"}, + {file = "httptools-0.6.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53cd2d776700bf0ed0e6fb203d716b041712ea4906479031cc5ac5421ecaa7d2"}, + {file = "httptools-0.6.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7da016a0dab1fcced89dfff8537033c5dc200015e14023368f3f4a69e39b8716"}, + {file = "httptools-0.6.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4d6e0ba155a1b3159551ac6b4551eb20028617e2e4bb71f2c61efed0756e6825"}, + {file = "httptools-0.6.2-cp311-cp311-win_amd64.whl", hash = "sha256:ad44569b0f508e046ffe85b4a547d5b68d1548fd90767df69449cc28021ee709"}, + {file = "httptools-0.6.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c92d2b7c1a914ab2f66454961eeaf904f4fe7529b93ff537619d22c18b82d070"}, + {file = "httptools-0.6.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:78f920a75c1dbcb5a48a495f384d73ceb41e437a966c318eb7e56f1c1ad1df3e"}, + {file = "httptools-0.6.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56bcd9ba0adf16edb4e3e45b8b9346f5b3b2372402e953d54c84b345d0f691e0"}, + {file = "httptools-0.6.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e350a887adb38ac65c93c2f395b60cf482baca61fd396ed8d6fd313dbcce6fac"}, + {file = "httptools-0.6.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ddc328c2a2daf2cf4bdc7bbc8a458dc4c840637223d4b8e01bce2168cc79fd23"}, + {file = "httptools-0.6.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ddaf38943dbb32333a182c894b6092a68b56c5e36d0c54ba3761d28119b15447"}, + {file = "httptools-0.6.2-cp312-cp312-win_amd64.whl", hash = "sha256:052f7f50e4a38f069478143878371ed17937f268349bcd68f6f7a9de9fcfce21"}, + {file = "httptools-0.6.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:406f7dc5d9db68cd9ac638d14c74d077085f76b45f704d3ec38d43b842b3cb44"}, + {file = "httptools-0.6.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:77e22c33123ce11231ff2773d8905e20b45d77a69459def7481283b72a583955"}, + {file = "httptools-0.6.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41965586b02715c3d83dd9153001f654e5b621de0c5255f5ef0635485212d0c0"}, + {file = "httptools-0.6.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93b1839d54b80a06a51a31b90d024a1770e250d00de57e7ae069bafba932f398"}, + {file = "httptools-0.6.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:8fdb4634040d1dbde7e0b373e19668cdb61c0ee8690d3b4064ac748d85365bca"}, + {file = "httptools-0.6.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c30902f9b9da0d74668b6f71d7b57081a4879d9a5ea93d5922dbe15b15b3b24a"}, + {file = "httptools-0.6.2-cp313-cp313-win_amd64.whl", hash = "sha256:cf61238811a75335751b4b17f8b221a35f93f2d57489296742adf98412d2a568"}, + {file = "httptools-0.6.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8d80878cb40ebf88a48839ff7206ceb62e4b54327e0c2f9f15ee12edbd8b907e"}, + {file = "httptools-0.6.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5141ccc9dbd8cdc59d1e93e318d405477a940dc6ebadcb8d9f8da17d2812d353"}, + {file = "httptools-0.6.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bb67d47f045f56e9a5da4deccf710bdde21212e4b1f4776b7a542449f6a7682"}, + {file = "httptools-0.6.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76dcb8f5c866f1537ccbaad01ebb3611890d281ef8d25e050d1cc3d90fba6b3d"}, + {file = "httptools-0.6.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:1b7bc59362143dc2d02896dde94004ef54ff1989ceedf4b389ad3b530f312364"}, + {file = "httptools-0.6.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c7a5715b1f46e9852442f496c0df2f8c393cc8f293f5396d2c8d95cac852fb51"}, + {file = "httptools-0.6.2-cp38-cp38-win_amd64.whl", hash = "sha256:3f0246ca7f78fa8e3902ddb985b9f55509d417a862f4634a8fa63a7a496266c8"}, + {file = "httptools-0.6.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1099f73952e18c718ccaaf7a97ae58c94a91839c3d247c6184326f85a2eda7b4"}, + {file = "httptools-0.6.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c3e45d004531330030f7d07abe4865bc17963b9989bc1941cebbf7224010fb82"}, + {file = "httptools-0.6.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4f2fea370361a90cb9330610a95303587eda9d1e69930dbbee9978eac1d5946"}, + {file = "httptools-0.6.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0481154c91725f7e7b729a535190388be6c7cbae3bbf0e793343ca386282312"}, + {file = "httptools-0.6.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d25f8fdbc6cc6561353c7a384d76295e6a85a4945115b8bc347855db150e8c77"}, + {file = "httptools-0.6.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:054bdee08e4f7c15c186f6e7dbc8f0cf974b8dd1832b5f17f988faf8b12815c9"}, + {file = "httptools-0.6.2-cp39-cp39-win_amd64.whl", hash = "sha256:4502620722b453c2c6306fad392c515dcb804dfa9c6d3b90d8926a07a7a01109"}, + {file = "httptools-0.6.2.tar.gz", hash = "sha256:ae694efefcb61317c79b2fa1caebc122060992408e389bb00889567e463a47f1"}, ] [package.extras] -test = ["Cython (>=0.29.24,<0.30.0)"] +test = ["Cython (>=0.29.24)"] [package.source] type = "legacy" @@ -1088,38 +1095,43 @@ reference = "meteoswiss" [[package]] name = "mypy" -version = "1.11.2" +version = "1.12.0" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" files = [ - {file = "mypy-1.11.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d42a6dd818ffce7be66cce644f1dff482f1d97c53ca70908dff0b9ddc120b77a"}, - {file = "mypy-1.11.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:801780c56d1cdb896eacd5619a83e427ce436d86a3bdf9112527f24a66618fef"}, - {file = "mypy-1.11.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41ea707d036a5307ac674ea172875f40c9d55c5394f888b168033177fce47383"}, - {file = "mypy-1.11.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6e658bd2d20565ea86da7d91331b0eed6d2eee22dc031579e6297f3e12c758c8"}, - {file = "mypy-1.11.2-cp310-cp310-win_amd64.whl", hash = "sha256:478db5f5036817fe45adb7332d927daa62417159d49783041338921dcf646fc7"}, - {file = "mypy-1.11.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:75746e06d5fa1e91bfd5432448d00d34593b52e7e91a187d981d08d1f33d4385"}, - {file = "mypy-1.11.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a976775ab2256aadc6add633d44f100a2517d2388906ec4f13231fafbb0eccca"}, - {file = "mypy-1.11.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd953f221ac1379050a8a646585a29574488974f79d8082cedef62744f0a0104"}, - {file = "mypy-1.11.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:57555a7715c0a34421013144a33d280e73c08df70f3a18a552938587ce9274f4"}, - {file = "mypy-1.11.2-cp311-cp311-win_amd64.whl", hash = "sha256:36383a4fcbad95f2657642a07ba22ff797de26277158f1cc7bd234821468b1b6"}, - {file = "mypy-1.11.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e8960dbbbf36906c5c0b7f4fbf2f0c7ffb20f4898e6a879fcf56a41a08b0d318"}, - {file = "mypy-1.11.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:06d26c277962f3fb50e13044674aa10553981ae514288cb7d0a738f495550b36"}, - {file = "mypy-1.11.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6e7184632d89d677973a14d00ae4d03214c8bc301ceefcdaf5c474866814c987"}, - {file = "mypy-1.11.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3a66169b92452f72117e2da3a576087025449018afc2d8e9bfe5ffab865709ca"}, - {file = "mypy-1.11.2-cp312-cp312-win_amd64.whl", hash = "sha256:969ea3ef09617aff826885a22ece0ddef69d95852cdad2f60c8bb06bf1f71f70"}, - {file = "mypy-1.11.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:37c7fa6121c1cdfcaac97ce3d3b5588e847aa79b580c1e922bb5d5d2902df19b"}, - {file = "mypy-1.11.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4a8a53bc3ffbd161b5b2a4fff2f0f1e23a33b0168f1c0778ec70e1a3d66deb86"}, - {file = "mypy-1.11.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ff93107f01968ed834f4256bc1fc4475e2fecf6c661260066a985b52741ddce"}, - {file = "mypy-1.11.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:edb91dded4df17eae4537668b23f0ff6baf3707683734b6a818d5b9d0c0c31a1"}, - {file = "mypy-1.11.2-cp38-cp38-win_amd64.whl", hash = "sha256:ee23de8530d99b6db0573c4ef4bd8f39a2a6f9b60655bf7a1357e585a3486f2b"}, - {file = "mypy-1.11.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:801ca29f43d5acce85f8e999b1e431fb479cb02d0e11deb7d2abb56bdaf24fd6"}, - {file = "mypy-1.11.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:af8d155170fcf87a2afb55b35dc1a0ac21df4431e7d96717621962e4b9192e70"}, - {file = "mypy-1.11.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7821776e5c4286b6a13138cc935e2e9b6fde05e081bdebf5cdb2bb97c9df81d"}, - {file = "mypy-1.11.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:539c570477a96a4e6fb718b8d5c3e0c0eba1f485df13f86d2970c91f0673148d"}, - {file = "mypy-1.11.2-cp39-cp39-win_amd64.whl", hash = "sha256:3f14cd3d386ac4d05c5a39a51b84387403dadbd936e17cb35882134d4f8f0d24"}, - {file = "mypy-1.11.2-py3-none-any.whl", hash = "sha256:b499bc07dbdcd3de92b0a8b29fdf592c111276f6a12fe29c30f6c417dd546d12"}, - {file = "mypy-1.11.2.tar.gz", hash = "sha256:7f9993ad3e0ffdc95c2a14b66dee63729f021968bff8ad911867579c65d13a79"}, + {file = "mypy-1.12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4397081e620dc4dc18e2f124d5e1d2c288194c2c08df6bdb1db31c38cd1fe1ed"}, + {file = "mypy-1.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:684a9c508a283f324804fea3f0effeb7858eb03f85c4402a967d187f64562469"}, + {file = "mypy-1.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6cabe4cda2fa5eca7ac94854c6c37039324baaa428ecbf4de4567279e9810f9e"}, + {file = "mypy-1.12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:060a07b10e999ac9e7fa249ce2bdcfa9183ca2b70756f3bce9df7a92f78a3c0a"}, + {file = "mypy-1.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:0eff042d7257f39ba4ca06641d110ca7d2ad98c9c1fb52200fe6b1c865d360ff"}, + {file = "mypy-1.12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4b86de37a0da945f6d48cf110d5206c5ed514b1ca2614d7ad652d4bf099c7de7"}, + {file = "mypy-1.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:20c7c5ce0c1be0b0aea628374e6cf68b420bcc772d85c3c974f675b88e3e6e57"}, + {file = "mypy-1.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a64ee25f05fc2d3d8474985c58042b6759100a475f8237da1f4faf7fcd7e6309"}, + {file = "mypy-1.12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:faca7ab947c9f457a08dcb8d9a8664fd438080e002b0fa3e41b0535335edcf7f"}, + {file = "mypy-1.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:5bc81701d52cc8767005fdd2a08c19980de9ec61a25dbd2a937dfb1338a826f9"}, + {file = "mypy-1.12.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8462655b6694feb1c99e433ea905d46c478041a8b8f0c33f1dab00ae881b2164"}, + {file = "mypy-1.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:923ea66d282d8af9e0f9c21ffc6653643abb95b658c3a8a32dca1eff09c06475"}, + {file = "mypy-1.12.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1ebf9e796521f99d61864ed89d1fb2926d9ab6a5fab421e457cd9c7e4dd65aa9"}, + {file = "mypy-1.12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e478601cc3e3fa9d6734d255a59c7a2e5c2934da4378f3dd1e3411ea8a248642"}, + {file = "mypy-1.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:c72861b7139a4f738344faa0e150834467521a3fba42dc98264e5aa9507dd601"}, + {file = "mypy-1.12.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:52b9e1492e47e1790360a43755fa04101a7ac72287b1a53ce817f35899ba0521"}, + {file = "mypy-1.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:48d3e37dd7d9403e38fa86c46191de72705166d40b8c9f91a3de77350daa0893"}, + {file = "mypy-1.12.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2f106db5ccb60681b622ac768455743ee0e6a857724d648c9629a9bd2ac3f721"}, + {file = "mypy-1.12.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:233e11b3f73ee1f10efada2e6da0f555b2f3a5316e9d8a4a1224acc10e7181d3"}, + {file = "mypy-1.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:4ae8959c21abcf9d73aa6c74a313c45c0b5a188752bf37dace564e29f06e9c1b"}, + {file = "mypy-1.12.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:eafc1b7319b40ddabdc3db8d7d48e76cfc65bbeeafaa525a4e0fa6b76175467f"}, + {file = "mypy-1.12.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9b9ce1ad8daeb049c0b55fdb753d7414260bad8952645367e70ac91aec90e07e"}, + {file = "mypy-1.12.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bfe012b50e1491d439172c43ccb50db66d23fab714d500b57ed52526a1020bb7"}, + {file = "mypy-1.12.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2c40658d4fa1ab27cb53d9e2f1066345596af2f8fe4827defc398a09c7c9519b"}, + {file = "mypy-1.12.0-cp38-cp38-win_amd64.whl", hash = "sha256:dee78a8b9746c30c1e617ccb1307b351ded57f0de0d287ca6276378d770006c0"}, + {file = "mypy-1.12.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6b5df6c8a8224f6b86746bda716bbe4dbe0ce89fd67b1fa4661e11bfe38e8ec8"}, + {file = "mypy-1.12.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5feee5c74eb9749e91b77f60b30771563327329e29218d95bedbe1257e2fe4b0"}, + {file = "mypy-1.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:77278e8c6ffe2abfba6db4125de55f1024de9a323be13d20e4f73b8ed3402bd1"}, + {file = "mypy-1.12.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:dcfb754dea911039ac12434d1950d69a2f05acd4d56f7935ed402be09fad145e"}, + {file = "mypy-1.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:06de0498798527451ffb60f68db0d368bd2bae2bbfb5237eae616d4330cc87aa"}, + {file = "mypy-1.12.0-py3-none-any.whl", hash = "sha256:fd313226af375d52e1e36c383f39bf3836e1f192801116b31b090dfcd3ec5266"}, + {file = "mypy-1.12.0.tar.gz", hash = "sha256:65a22d87e757ccd95cbbf6f7e181e6caa87128255eb2b6be901bb71b26d8a99d"}, ] [package.dependencies] From a9fd246e2943c24d36188102e1e8e2971c603a04 Mon Sep 17 00:00:00 2001 From: ninaburg Date: Thu, 17 Oct 2024 13:03:13 +0000 Subject: [PATCH 14/37] remove mchpy package and copy files in project --- flex_container_orchestrator/__init__.py | 2 +- .../config/base_settings.py | 106 ++ .../config/http_audit.py | 45 + flex_container_orchestrator/config/logger.py | 251 ++++ .../config/service_settings.py | 4 +- .../config/settings.yaml | 2 +- poetry.lock | 1129 +---------------- pyproject.toml | 13 +- 8 files changed, 421 insertions(+), 1131 deletions(-) create mode 100644 flex_container_orchestrator/config/base_settings.py create mode 100644 flex_container_orchestrator/config/http_audit.py create mode 100644 flex_container_orchestrator/config/logger.py diff --git a/flex_container_orchestrator/__init__.py b/flex_container_orchestrator/__init__.py index 9e25878..e88d065 100644 --- a/flex_container_orchestrator/__init__.py +++ b/flex_container_orchestrator/__init__.py @@ -1,7 +1,7 @@ """ Initializations """ import os -from mchpy.audit import logger +from flex_container_orchestrator.config import logger from flex_container_orchestrator.config.service_settings import ServiceSettings CONFIG = ServiceSettings('settings.yaml', os.path.join(os.path.dirname(__file__), 'config')) diff --git a/flex_container_orchestrator/config/base_settings.py b/flex_container_orchestrator/config/base_settings.py new file mode 100644 index 0000000..bc4c2a4 --- /dev/null +++ b/flex_container_orchestrator/config/base_settings.py @@ -0,0 +1,106 @@ +""" +Note: This script from mchpy (MeteoSwiss Blueprint) was added here because I couldmchpyy from outside of the MeteoSwiss network. + +This module introduces two custom classes that leverage Pydantic's capabilities: a custom BaseModel called + SubscriptableBaseModel and a custom BaseSettings class BaseServiceSettings. + +Both these custom classes inherit Pydantic's core functionalities. This includes data validation, parsing, +serialization, error handling, and behavior customization. +""" +import logging +import os +from typing import Any, Type + +import yaml +from pydantic import BaseModel +from pydantic.fields import FieldInfo +from pydantic.v1.utils import deep_update +from pydantic_settings import BaseSettings, PydanticBaseSettingsSource, SettingsConfigDict, InitSettingsSource + +logger = logging.getLogger(__name__) + + +class SubscriptableBaseModel(BaseModel): + """ + The custom BaseModel is an extension of Pydantic's BaseModel class. It enhances attribute access by overriding the + __getitem__ method, enabling the use of square brackets ([]). This means attributes can be accessed in the same way + as dictionary keys. For instance, model['name'] can be used in place of model.name. + """ + + def __getitem__(self, item: str) -> Any: + return getattr(self, item) + + +class BaseServiceSettings(BaseSettings): + """ + The Custom BaseSettings class is a derivative of Pydantic's BaseSettings class. It introduces the ability to read + settings values from a series of YAML files, providing an additional source for configuration data. + """ + model_config = SettingsConfigDict(env_nested_delimiter='__', extra='allow') + + def __init__(self, settings_file_names: str | list[str], settings_dirname: str, **values: Any): + """ + Initializes the service settings by loading values from the specified YAML filename(s). + The YAML settings will be loaded in the same order as given in the list (or as a single settings file), + with later files taking precedence. + :param settings_file_names: Names of YAML files containing settings. + :param settings_dirname: Directory where the settings YAML files are located. + :param values: Additional settings values (if provided). Refer to Pydantic's BaseSettings for details. + :raises FileNotFoundError: If none of the specified YAML settings files exist. + """ + + if isinstance(settings_file_names, str): + settings_file_names = [settings_file_names] + + # we want to harmlessly be able to pass files that do not exist in reality + existing_settings_file_names = [] + for fname in settings_file_names: + if os.path.exists(os.path.join(settings_dirname, fname)): + existing_settings_file_names.append(fname) + else: + logger.warning('Given YAML settings file "%s" does not exist.', fname) + + if not existing_settings_file_names: + raise FileNotFoundError('Could not find the specified YAML settings file(s).') + + super().__init__(_settings_filenames=existing_settings_file_names, _settings_dirname=settings_dirname, **values) + + # pylint: disable=too-many-arguments + @classmethod + def settings_customise_sources( + cls, + settings_cls: Type[BaseSettings], + init_settings: InitSettingsSource, # type: ignore # the init_settings is always a InitSettingsSource + env_settings: PydanticBaseSettingsSource, + dotenv_settings: PydanticBaseSettingsSource, + file_secret_settings: PydanticBaseSettingsSource, + ) -> tuple[PydanticBaseSettingsSource, PydanticBaseSettingsSource, PydanticBaseSettingsSource, + PydanticBaseSettingsSource]: + load_yaml_config = _YamlConfigSource(settings_cls=settings_cls, + filenames=init_settings.init_kwargs.pop('_settings_filenames'), + dirname=init_settings.init_kwargs.pop('_settings_dirname')) + + # environment variables have highest precedence, then yaml values etc. + return env_settings, load_yaml_config, init_settings, file_secret_settings + + +class _YamlConfigSource(PydanticBaseSettingsSource): + _yaml_content_dict: dict[str, Any] + + def get_field_value(self, field: FieldInfo, field_name: str) -> tuple[Any, str, bool]: + # this method needs to be implemented but is not used since the whole parsed + # yaml file is returned as dictionary without inspecting the values or fields + raise NotImplementedError() + + def __init__(self, filenames: list[str], dirname: str, settings_cls: type[BaseSettings]): + self._yaml_content_dict = {} + + for filename in filenames: + with open(os.path.join(dirname, filename), encoding='utf-8') as file: + self._yaml_content_dict = deep_update(self._yaml_content_dict, yaml.safe_load(file)) + + super().__init__(settings_cls) + + def __call__(self) -> dict[str, Any]: + return self._yaml_content_dict + diff --git a/flex_container_orchestrator/config/http_audit.py b/flex_container_orchestrator/config/http_audit.py new file mode 100644 index 0000000..5fb6834 --- /dev/null +++ b/flex_container_orchestrator/config/http_audit.py @@ -0,0 +1,45 @@ +""" +Note: This script from mchpy (MeteoSwiss Blueprint) was added here because I couldn't install mchpy from outside of the MeteoSwiss network. + +Instrumentation of web applications to include the request_id in all responses +and make it available to other services (e.g. logging) +""" +# noinspection PyPackageRequirements +import contextvars +import logging +import uuid + +from typing import Callable + +X_REQUEST_ID = "X-REQUEST-ID" +request_id = contextvars.ContextVar('request_id', default='') + +logger = logging.getLogger(__name__) + + +def get_request_id() -> str: + """ + Get the request_id valid for the entire request. + """ + return request_id.get() + + +def extract_request_id_from(extract_request_fn: Callable[[str], str | None]) -> str: + """ + Extracts the request id from the incoming request by using the given function + + :param extract_request_fn: request id extract function, accepting as argument the request id name. + Must return the request id string value, or None + """ + rid = extract_request_fn(X_REQUEST_ID) or _generate_request_id() + request_id.set(rid) + + return rid + + +def _generate_request_id() -> str: + rid = str(uuid.uuid4()) + logger.debug('Generated new request_id %s', rid) + + return rid + diff --git a/flex_container_orchestrator/config/logger.py b/flex_container_orchestrator/config/logger.py new file mode 100644 index 0000000..fbcfa12 --- /dev/null +++ b/flex_container_orchestrator/config/logger.py @@ -0,0 +1,251 @@ +""" +Note: This script from mchpy (MeteoSwiss Blueprint) was added here because I couldn't install mchpy from outside of the MeteoSwiss network. + +Logging configuration including http request auditing +""" +import logging.config +import time +from enum import Enum +from typing import Any, Sequence + +from pydantic import BaseModel, field_validator +from pydantic_settings import SettingsConfigDict +from pythonjsonlogger import jsonlogger + +from flex_container_orchestrator.config import http_audit + +_logger: dict = { + 'version': 1, + # without the following option, using apply_logging_settings too late is dangerous because all loggers which were + # previously known would be silently disabled + 'disable_existing_loggers': False, + 'filters': { + 'standard_request_id': { + '()': 'flex_container_orchestrator.config.logger.StandardRequestIdFilter', + }, + 'json_request_id': { + '()': 'flex_container_orchestrator.config.logger.JsonRequestIdFilter', + }, + }, + 'formatters': { + 'standard': { + 'format': '{asctime} {request_id}{levelname:>8s} {process} --- [{threadName:>15s}] {name_with_func:40}: ' + '{message}', + 'style': '{' + }, + 'json': { + '()': 'flex_container_orchestrator.config.logger.LogstashJsonFormatter' + }, + }, + 'handlers': { + 'console': { + 'class': 'logging.StreamHandler', + 'level': 'DEBUG', + 'filters': ['standard_request_id'], + 'formatter': 'standard', + }, + }, + 'loggers': {}, + 'root': { + 'handlers': ['console'], + 'level': 'DEBUG', + }, +} + + +class LogLevel(str, Enum): + """ + Wrapper class for Python logging levels so that we can make sure that no unknown logging level is taken. + """ + CRITICAL = 'CRITICAL' + ERROR = 'ERROR' + WARNING = 'WARNING' + INFO = 'INFO' + DEBUG = 'DEBUG' + NOTSET = 'NOTSET' + + +class FormatterType(str, Enum): + """ + Wrapper class for the formatters we provide + """ + STANDARD = 'standard' + JSON = 'json' + + +class LoggingSettings(BaseModel): + """ + This class defines the structure for our logging configuration. + + As formatters, both standard and json formatting can be chosen. + The supported logging levels are: CRITICAL, ERROR, WARNING, INFO, DEBUG, NOTSET. These levels can be set for the + root logger or for specific child loggers. + + As a default, the root logging level DEBUG and the standard formatter are taken. + """ + model_config = SettingsConfigDict(extra='forbid') + + formatter: FormatterType = FormatterType.STANDARD + root_log_level: LogLevel = LogLevel.DEBUG + child_log_levels: dict[str, LogLevel] = {} + + @field_validator('child_log_levels') + def logger_name_does_not_contain_reserved_key(cls, child_log_levels: dict[str, LogLevel]) -> dict[str, LogLevel]: + for key in child_log_levels.keys(): + if key in ['formatter', 'root', 'root_log_level', '']: + raise ValueError('Using empty or reserved name for child logger.') + return child_log_levels + + +def apply_logging_settings(logging_settings: LoggingSettings = LoggingSettings()) -> None: + """ + Configures the python logger for the current application. + + This sets standard logging formats and attaches a single handler to the root logger. Therefore, by propagating + messages up the logger hierarchy, all loggers automatically use the same format and formatter. + Note: That does not work for any loggers existing before calling this method, if propagation is disabled for them. + + Log levels for the root logger and arbitrary other loggers can be set by calling with appropriate logging settings. + + When no logging settings are given, the default settings are used. + + To allow configuring log levels for individual loggers, and to make the configured log format produce meaningful + messages, your application code should: + + * never log directly to the root logger (like ``logging.debug('message')``) + + * instead create a logger per module and log to that instance: + | ``logger = logging.getLogger(__name__)`` + | ``logger.debug('message')`` + + * not create additional log handlers or disable propagation for any loggers. + + :param logging_settings: the settings according to which logging should be configured + """ + config = {'logging': {'formatter': logging_settings.formatter.value, 'root': logging_settings.root_log_level.value}} + for logger_name, log_level in logging_settings.child_log_levels.items(): + config['logging'][logger_name] = log_level.name + + if config and 'logging' in config: + logging_config_ = config['logging'] + + _set_formatter(logging_config_) + _set_root_logger(logging_config_) + _set_loggers(logging_config_) + + logging.config.dictConfig(_logger) + # Use a period instead of the comma before the milliseconds part + logging.Formatter.default_msec_format = '%s.%03d' + # Use UTC timestamps + logging.Formatter.converter = time.gmtime + logging.captureWarnings(True) + + +def _set_formatter(logging_config: dict) -> None: + if 'formatter' in logging_config: + formatter = logging_config['formatter'] + _logger['handlers']['console']['formatter'] = formatter + _logger['handlers']['console']['filters'] = [formatter + '_request_id'] + + +def _set_root_logger(logging_config: dict) -> None: + if 'root' in logging_config: + _logger['root']['level'] = logging_config['root'] + + +def _set_loggers(logging_config: dict) -> None: + loggers = [(logger, level) for logger, level in logging_config.items() if logger not in ['formatter', 'root']] + for (logger, level) in loggers: + if logger in _logger['loggers']: + _logger['loggers'][logger]['level'] = level + else: + _logger['loggers'][logger] = {'level': level} + + +class StandardRequestIdFilter(logging.Filter): + """ + Class used to include the http request id into the logged record, used with + standard formatter. + """ + + def filter(self, record: logging.LogRecord) -> bool: + request_id = http_audit.get_request_id() + # request_id is only in the log when existing + record.request_id = '[' + request_id + '] ' if request_id else '' + # logger and function name concatenated and truncated from the left + record.name_with_func = (record.name + '.' + record.funcName)[-40:] + return True + + +class JsonRequestIdFilter(logging.Filter): + """ + Class used to include the http request id into the logged record, used with json formatter. + """ + + def filter(self, record: logging.LogRecord) -> bool: + # request_id is only added when existing + request_id = http_audit.get_request_id() + if request_id: + record.request_id = request_id + record.correlationId = request_id + return True + + +class MessageContainsFilter(logging.Filter): + """Filter log messages which contain a substring.""" + def __init__(self, substrings: Sequence[str], *args: Any, **kwargs: Any) -> None: + """Constructor. + + :param substrings: messages which contain any of these substrings will + not be logged + """ + super().__init__(*args, **kwargs) + self._substrings = substrings + + def filter(self, record: logging.LogRecord) -> bool: + msg = record.getMessage() + return not any(substring in msg for substring in self._substrings) + + +class LogstashJsonFormatter(jsonlogger.JsonFormatter): + """Format JSON log entries in the same way as logstash-logback-encoder. + https://github.com/logfellow/logstash-logback-encoder + + The JSON records have the following fields: + - message: log message, + - @timestamp: timestamp in ISO format, with milliseconds and time zone + - level: 'DEBUG', 'INFO', 'WARN', 'ERROR', 'CRITICAL' + - level_value: 10000, 20000, 30000, 40000, 50000, useful for filtering + - logger_name: logger + - func_name: this is not available in the logs from Spring + - exc_info: the Spring logs have stack_trace instead + """ + + # 'message' is added by default to record. + # 'exc_info' is transformed later when existing, cannot be easily transformed + # to 'stack_trace'. + _logged_fields = ( + ('asctime', '@timestamp'), + ('threadName', 'thread_name'), + ('levelname', 'level'), + ('name', 'logger_name'), + ('funcName', 'func_name'), + ) + _log_levels = ['DEBUG', 'INFO', 'WARN', 'ERROR', 'CRITICAL'] + + def add_fields(self, log_record: dict[str, Any], record: logging.LogRecord, message_dict: dict[str, Any]) -> None: + super().add_fields(log_record, record, message_dict) + converter = self.converter(record.created) + log_record['@timestamp'] = time.strftime('%Y-%m-%dT%H:%M:%S.%%03d%z', converter) % record.msecs + for src_field, field in self._logged_fields: + value = record.__dict__.get(src_field) + if value is not None: + if field == 'level': + if value == 'WARNING': + value = 'WARN' + try: + log_record['level_value'] = (self._log_levels.index(value) + 1) * 10_000 + except ValueError: + pass + log_record[field] = value + diff --git a/flex_container_orchestrator/config/service_settings.py b/flex_container_orchestrator/config/service_settings.py index 55c85a2..ead746a 100644 --- a/flex_container_orchestrator/config/service_settings.py +++ b/flex_container_orchestrator/config/service_settings.py @@ -1,7 +1,7 @@ from pydantic import BaseModel -from mchpy.audit.logger import LoggingSettings -from mchpy.config.base_settings import BaseServiceSettings +from flex_container_orchestrator.config.logger import LoggingSettings +from flex_container_orchestrator.config.base_settings import BaseServiceSettings class AppSettings(BaseModel): diff --git a/flex_container_orchestrator/config/settings.yaml b/flex_container_orchestrator/config/settings.yaml index 16b2318..fd9d186 100644 --- a/flex_container_orchestrator/config/settings.yaml +++ b/flex_container_orchestrator/config/settings.yaml @@ -5,6 +5,6 @@ logging: formatter: "standard" child_log_levels: mchpy: DEBUG - mchpy.audit.http_audit: INFO + flex_container_orechestrator.config.http_audit: INFO main: app_name: flex-container-orchestrator diff --git a/poetry.lock b/poetry.lock index cbcf0c7..184fe50 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. [[package]] name = "accessible-pygments" @@ -18,11 +18,6 @@ pygments = ">=1.5" dev = ["pillow", "pkginfo (>=1.10)", "playwright", "pre-commit", "setuptools", "twine (>=5.0)"] tests = ["hypothesis", "pytest"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "alabaster" version = "1.0.0" @@ -34,11 +29,6 @@ files = [ {file = "alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "annotated-types" version = "0.7.0" @@ -50,36 +40,6 @@ files = [ {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - -[[package]] -name = "anyio" -version = "4.6.2.post1" -description = "High level compatibility layer for multiple asynchronous event loop implementations" -optional = false -python-versions = ">=3.9" -files = [ - {file = "anyio-4.6.2.post1-py3-none-any.whl", hash = "sha256:6d170c36fba3bdd840c73d3868c1e777e33676a69c3a72cf0a0d5d6d8009b61d"}, - {file = "anyio-4.6.2.post1.tar.gz", hash = "sha256:4c8bc31ccdb51c7f7bd251f51c609e038d63e34219b44aa86e47576389880b4c"}, -] - -[package.dependencies] -idna = ">=2.8" -sniffio = ">=1.1" - -[package.extras] -doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] -test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21.0b1)"] -trio = ["trio (>=0.26.1)"] - -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "astroid" version = "3.3.5" @@ -91,51 +51,6 @@ files = [ {file = "astroid-3.3.5.tar.gz", hash = "sha256:5cfc40ae9f68311075d27ef68a4841bdc5cc7f6cf86671b49f00607d30188e2d"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - -[[package]] -name = "asyncstdlib" -version = "3.12.5" -description = "The missing async toolbox" -optional = false -python-versions = "~=3.8" -files = [ - {file = "asyncstdlib-3.12.5-py3-none-any.whl", hash = "sha256:277084c59caf8148c9cd83c7425f37aba7f43078645129ec6918d0f1a0adc95a"}, - {file = "asyncstdlib-3.12.5.tar.gz", hash = "sha256:7eb0025f6acb59e46743458570712fe9700dcc7b370b6929daf07bcc1ccc27db"}, -] - -[package.extras] -doc = ["sphinx", "sphinxcontrib-trio"] -test = ["black", "coverage", "flake8", "flake8-2020", "flake8-bugbear", "mypy", "pytest", "pytest-cov"] -typetest = ["mypy", "pyright", "typing-extensions"] - -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - -[[package]] -name = "authlib" -version = "1.3.2" -description = "The ultimate Python library in building OAuth and OpenID Connect servers and clients." -optional = false -python-versions = ">=3.8" -files = [ - {file = "Authlib-1.3.2-py2.py3-none-any.whl", hash = "sha256:ede026a95e9f5cdc2d4364a52103f5405e75aa156357e831ef2bfd0bc5094dfc"}, - {file = "authlib-1.3.2.tar.gz", hash = "sha256:4b16130117f9eb82aa6eec97f6dd4673c3f960ac0283ccdae2897ee4bc030ba2"}, -] - -[package.dependencies] -cryptography = "*" - -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "babel" version = "2.16.0" @@ -150,11 +65,6 @@ files = [ [package.extras] dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "beautifulsoup4" version = "4.12.3" @@ -176,44 +86,34 @@ charset-normalizer = ["charset-normalizer"] html5lib = ["html5lib"] lxml = ["lxml"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "boto3" -version = "1.35.40" +version = "1.35.42" description = "The AWS SDK for Python" optional = false -python-versions = ">= 3.8" +python-versions = ">=3.8" files = [ - {file = "boto3-1.35.40-py3-none-any.whl", hash = "sha256:9352f6d61f15c789231a5d608613f03425059072ed862c32e1ed102b17206abf"}, - {file = "boto3-1.35.40.tar.gz", hash = "sha256:33c6a7aeab316f7e0b3ad8552afe95a4a10bfd58519d00741c4d4f3047da8382"}, + {file = "boto3-1.35.42-py3-none-any.whl", hash = "sha256:e1f36f8be453505cebcc3da178ea081b2a06c0e5e1cdee774f1067599b8d9c3e"}, + {file = "boto3-1.35.42.tar.gz", hash = "sha256:a5b00f8b82dce62870759f04861747944da834d64a64355970120c475efdafc0"}, ] [package.dependencies] -botocore = ">=1.35.40,<1.36.0" +botocore = ">=1.35.42,<1.36.0" jmespath = ">=0.7.1,<2.0.0" s3transfer = ">=0.10.0,<0.11.0" [package.extras] crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "botocore" -version = "1.35.40" +version = "1.35.42" description = "Low-level, data-driven core of boto 3." optional = false -python-versions = ">= 3.8" +python-versions = ">=3.8" files = [ - {file = "botocore-1.35.40-py3-none-any.whl", hash = "sha256:072cc47f29cb1de4fa77ce6632e4f0480af29b70816973ff415fbaa3f50bd1db"}, - {file = "botocore-1.35.40.tar.gz", hash = "sha256:547e0a983856c7d7aeaa30fca2a283873c57c07366cd806d2d639856341b3c31"}, + {file = "botocore-1.35.42-py3-none-any.whl", hash = "sha256:05af0bb8b9cea7ce7bc589c332348d338a21b784e9d088a588fd10ec145007ff"}, + {file = "botocore-1.35.42.tar.gz", hash = "sha256:af348636f73dc24b7e2dc760a34d08c8f2f94366e9b4c78d877307b128abecef"}, ] [package.dependencies] @@ -224,11 +124,6 @@ urllib3 = {version = ">=1.25.4,<2.2.0 || >2.2.0,<3", markers = "python_version > [package.extras] crt = ["awscrt (==0.22.0)"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "certifi" version = "2024.8.30" @@ -240,95 +135,6 @@ files = [ {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - -[[package]] -name = "cffi" -version = "1.17.1" -description = "Foreign Function Interface for Python calling C code." -optional = false -python-versions = ">=3.8" -files = [ - {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, - {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17"}, - {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8"}, - {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e"}, - {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be"}, - {file = "cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c"}, - {file = "cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15"}, - {file = "cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401"}, - {file = "cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d"}, - {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6"}, - {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f"}, - {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"}, - {file = "cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655"}, - {file = "cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0"}, - {file = "cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4"}, - {file = "cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93"}, - {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3"}, - {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8"}, - {file = "cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65"}, - {file = "cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903"}, - {file = "cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e"}, - {file = "cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd"}, - {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed"}, - {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9"}, - {file = "cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d"}, - {file = "cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a"}, - {file = "cffi-1.17.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1"}, - {file = "cffi-1.17.1-cp38-cp38-win32.whl", hash = "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8"}, - {file = "cffi-1.17.1-cp38-cp38-win_amd64.whl", hash = "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1"}, - {file = "cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16"}, - {file = "cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3"}, - {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595"}, - {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a"}, - {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e"}, - {file = "cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7"}, - {file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"}, - {file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"}, -] - -[package.dependencies] -pycparser = "*" - -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "charset-normalizer" version = "3.4.0" @@ -443,30 +249,6 @@ files = [ {file = "charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - -[[package]] -name = "click" -version = "8.1.7" -description = "Composable command line interface toolkit" -optional = false -python-versions = ">=3.7" -files = [ - {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, - {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "colorama" version = "0.4.6" @@ -478,11 +260,6 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "coverage" version = "7.6.3" @@ -557,65 +334,6 @@ files = [ [package.extras] toml = ["tomli"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - -[[package]] -name = "cryptography" -version = "43.0.1" -description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." -optional = false -python-versions = ">=3.7" -files = [ - {file = "cryptography-43.0.1-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:8385d98f6a3bf8bb2d65a73e17ed87a3ba84f6991c155691c51112075f9ffc5d"}, - {file = "cryptography-43.0.1-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27e613d7077ac613e399270253259d9d53872aaf657471473ebfc9a52935c062"}, - {file = "cryptography-43.0.1-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68aaecc4178e90719e95298515979814bda0cbada1256a4485414860bd7ab962"}, - {file = "cryptography-43.0.1-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:de41fd81a41e53267cb020bb3a7212861da53a7d39f863585d13ea11049cf277"}, - {file = "cryptography-43.0.1-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f98bf604c82c416bc829e490c700ca1553eafdf2912a91e23a79d97d9801372a"}, - {file = "cryptography-43.0.1-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:61ec41068b7b74268fa86e3e9e12b9f0c21fcf65434571dbb13d954bceb08042"}, - {file = "cryptography-43.0.1-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:014f58110f53237ace6a408b5beb6c427b64e084eb451ef25a28308270086494"}, - {file = "cryptography-43.0.1-cp37-abi3-win32.whl", hash = "sha256:2bd51274dcd59f09dd952afb696bf9c61a7a49dfc764c04dd33ef7a6b502a1e2"}, - {file = "cryptography-43.0.1-cp37-abi3-win_amd64.whl", hash = "sha256:666ae11966643886c2987b3b721899d250855718d6d9ce41b521252a17985f4d"}, - {file = "cryptography-43.0.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:ac119bb76b9faa00f48128b7f5679e1d8d437365c5d26f1c2c3f0da4ce1b553d"}, - {file = "cryptography-43.0.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bbcce1a551e262dfbafb6e6252f1ae36a248e615ca44ba302df077a846a8806"}, - {file = "cryptography-43.0.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58d4e9129985185a06d849aa6df265bdd5a74ca6e1b736a77959b498e0505b85"}, - {file = "cryptography-43.0.1-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d03a475165f3134f773d1388aeb19c2d25ba88b6a9733c5c590b9ff7bbfa2e0c"}, - {file = "cryptography-43.0.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:511f4273808ab590912a93ddb4e3914dfd8a388fed883361b02dea3791f292e1"}, - {file = "cryptography-43.0.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:80eda8b3e173f0f247f711eef62be51b599b5d425c429b5d4ca6a05e9e856baa"}, - {file = "cryptography-43.0.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:38926c50cff6f533f8a2dae3d7f19541432610d114a70808f0926d5aaa7121e4"}, - {file = "cryptography-43.0.1-cp39-abi3-win32.whl", hash = "sha256:a575913fb06e05e6b4b814d7f7468c2c660e8bb16d8d5a1faf9b33ccc569dd47"}, - {file = "cryptography-43.0.1-cp39-abi3-win_amd64.whl", hash = "sha256:d75601ad10b059ec832e78823b348bfa1a59f6b8d545db3a24fd44362a1564cb"}, - {file = "cryptography-43.0.1-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ea25acb556320250756e53f9e20a4177515f012c9eaea17eb7587a8c4d8ae034"}, - {file = "cryptography-43.0.1-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c1332724be35d23a854994ff0b66530119500b6053d0bd3363265f7e5e77288d"}, - {file = "cryptography-43.0.1-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:fba1007b3ef89946dbbb515aeeb41e30203b004f0b4b00e5e16078b518563289"}, - {file = "cryptography-43.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5b43d1ea6b378b54a1dc99dd8a2b5be47658fe9a7ce0a58ff0b55f4b43ef2b84"}, - {file = "cryptography-43.0.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:88cce104c36870d70c49c7c8fd22885875d950d9ee6ab54df2745f83ba0dc365"}, - {file = "cryptography-43.0.1-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:9d3cdb25fa98afdd3d0892d132b8d7139e2c087da1712041f6b762e4f807cc96"}, - {file = "cryptography-43.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e710bf40870f4db63c3d7d929aa9e09e4e7ee219e703f949ec4073b4294f6172"}, - {file = "cryptography-43.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7c05650fe8023c5ed0d46793d4b7d7e6cd9c04e68eabe5b0aeea836e37bdcec2"}, - {file = "cryptography-43.0.1.tar.gz", hash = "sha256:203e92a75716d8cfb491dc47c79e17d0d9207ccffcbcb35f598fbe463ae3444d"}, -] - -[package.dependencies] -cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} - -[package.extras] -docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] -docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"] -nox = ["nox"] -pep8test = ["check-sdist", "click", "mypy", "ruff"] -sdist = ["build"] -ssh = ["bcrypt (>=3.1.5)"] -test = ["certifi", "cryptography-vectors (==43.0.1)", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] -test-randomorder = ["pytest-randomly"] - -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "dill" version = "0.3.9" @@ -631,11 +349,6 @@ files = [ graph = ["objgraph (>=1.7.2)"] profile = ["gprof2dot (>=2022.7.29)"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "docutils" version = "0.21.2" @@ -647,168 +360,6 @@ files = [ {file = "docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - -[[package]] -name = "fastapi" -version = "0.114.2" -description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" -optional = false -python-versions = ">=3.8" -files = [ - {file = "fastapi-0.114.2-py3-none-any.whl", hash = "sha256:44474a22913057b1acb973ab90f4b671ba5200482e7622816d79105dcece1ac5"}, - {file = "fastapi-0.114.2.tar.gz", hash = "sha256:0adb148b62edb09e8c6eeefa3ea934e8f276dabc038c5a82989ea6346050c3da"}, -] - -[package.dependencies] -pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" -starlette = ">=0.37.2,<0.39.0" -typing-extensions = ">=4.8.0" - -[package.extras] -all = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] -standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "jinja2 (>=2.11.2)", "python-multipart (>=0.0.7)", "uvicorn[standard] (>=0.12.0)"] - -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - -[[package]] -name = "h11" -version = "0.14.0" -description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" -optional = false -python-versions = ">=3.7" -files = [ - {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, - {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, -] - -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - -[[package]] -name = "httpcore" -version = "1.0.6" -description = "A minimal low-level HTTP client." -optional = false -python-versions = ">=3.8" -files = [ - {file = "httpcore-1.0.6-py3-none-any.whl", hash = "sha256:27b59625743b85577a8c0e10e55b50b5368a4f2cfe8cc7bcfa9cf00829c2682f"}, - {file = "httpcore-1.0.6.tar.gz", hash = "sha256:73f6dbd6eb8c21bbf7ef8efad555481853f5f6acdeaff1edb0694289269ee17f"}, -] - -[package.dependencies] -certifi = "*" -h11 = ">=0.13,<0.15" - -[package.extras] -asyncio = ["anyio (>=4.0,<5.0)"] -http2 = ["h2 (>=3,<5)"] -socks = ["socksio (==1.*)"] -trio = ["trio (>=0.22.0,<1.0)"] - -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - -[[package]] -name = "httptools" -version = "0.6.2" -description = "A collection of framework independent HTTP protocol utils." -optional = false -python-versions = ">=3.8.0" -files = [ - {file = "httptools-0.6.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0238f07780782c018e9801d8f5f5aea3a4680a1af132034b444f677718c6fe88"}, - {file = "httptools-0.6.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:10d28e5597d4349390c640232c9366ddc15568114f56724fe30a53de9686b6ab"}, - {file = "httptools-0.6.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9ddaf99e362ae4169f6a8b3508f3487264e0a1b1e58c0b07b86407bc9ecee831"}, - {file = "httptools-0.6.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efc9d039b6b8a36b182bc60774bb5d456b8ff9ec44cf97719f2f38bb1dcdd546"}, - {file = "httptools-0.6.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b57cb8a4a8a8ffdaf0395326ef3b9c1aba36e58a421438fc04c002a1f511db63"}, - {file = "httptools-0.6.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b73cda1326738eab5d60640ca0b87ac4e4db09a099423c41b59a5681917e8d1d"}, - {file = "httptools-0.6.2-cp310-cp310-win_amd64.whl", hash = "sha256:352a496244360deb1c1d108391d76cd6f3dd9f53ccf975a082e74c6761af30c9"}, - {file = "httptools-0.6.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2e9d225b178a6cc700c23cf2f5daf85a10f93f1db7c34e9ee4ee0bbc29ad458a"}, - {file = "httptools-0.6.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d49b14fcc9b12a52da8667587efa124a18e1a3eb63bbbcabf9882f4008d171d6"}, - {file = "httptools-0.6.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d5c33d98b2311ddbe06e92b12b14de334dcfbe64ebcbb2c7a34b5c6036db512"}, - {file = "httptools-0.6.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53cd2d776700bf0ed0e6fb203d716b041712ea4906479031cc5ac5421ecaa7d2"}, - {file = "httptools-0.6.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7da016a0dab1fcced89dfff8537033c5dc200015e14023368f3f4a69e39b8716"}, - {file = "httptools-0.6.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4d6e0ba155a1b3159551ac6b4551eb20028617e2e4bb71f2c61efed0756e6825"}, - {file = "httptools-0.6.2-cp311-cp311-win_amd64.whl", hash = "sha256:ad44569b0f508e046ffe85b4a547d5b68d1548fd90767df69449cc28021ee709"}, - {file = "httptools-0.6.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c92d2b7c1a914ab2f66454961eeaf904f4fe7529b93ff537619d22c18b82d070"}, - {file = "httptools-0.6.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:78f920a75c1dbcb5a48a495f384d73ceb41e437a966c318eb7e56f1c1ad1df3e"}, - {file = "httptools-0.6.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56bcd9ba0adf16edb4e3e45b8b9346f5b3b2372402e953d54c84b345d0f691e0"}, - {file = "httptools-0.6.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e350a887adb38ac65c93c2f395b60cf482baca61fd396ed8d6fd313dbcce6fac"}, - {file = "httptools-0.6.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ddc328c2a2daf2cf4bdc7bbc8a458dc4c840637223d4b8e01bce2168cc79fd23"}, - {file = "httptools-0.6.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ddaf38943dbb32333a182c894b6092a68b56c5e36d0c54ba3761d28119b15447"}, - {file = "httptools-0.6.2-cp312-cp312-win_amd64.whl", hash = "sha256:052f7f50e4a38f069478143878371ed17937f268349bcd68f6f7a9de9fcfce21"}, - {file = "httptools-0.6.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:406f7dc5d9db68cd9ac638d14c74d077085f76b45f704d3ec38d43b842b3cb44"}, - {file = "httptools-0.6.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:77e22c33123ce11231ff2773d8905e20b45d77a69459def7481283b72a583955"}, - {file = "httptools-0.6.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41965586b02715c3d83dd9153001f654e5b621de0c5255f5ef0635485212d0c0"}, - {file = "httptools-0.6.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93b1839d54b80a06a51a31b90d024a1770e250d00de57e7ae069bafba932f398"}, - {file = "httptools-0.6.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:8fdb4634040d1dbde7e0b373e19668cdb61c0ee8690d3b4064ac748d85365bca"}, - {file = "httptools-0.6.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c30902f9b9da0d74668b6f71d7b57081a4879d9a5ea93d5922dbe15b15b3b24a"}, - {file = "httptools-0.6.2-cp313-cp313-win_amd64.whl", hash = "sha256:cf61238811a75335751b4b17f8b221a35f93f2d57489296742adf98412d2a568"}, - {file = "httptools-0.6.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8d80878cb40ebf88a48839ff7206ceb62e4b54327e0c2f9f15ee12edbd8b907e"}, - {file = "httptools-0.6.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5141ccc9dbd8cdc59d1e93e318d405477a940dc6ebadcb8d9f8da17d2812d353"}, - {file = "httptools-0.6.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bb67d47f045f56e9a5da4deccf710bdde21212e4b1f4776b7a542449f6a7682"}, - {file = "httptools-0.6.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76dcb8f5c866f1537ccbaad01ebb3611890d281ef8d25e050d1cc3d90fba6b3d"}, - {file = "httptools-0.6.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:1b7bc59362143dc2d02896dde94004ef54ff1989ceedf4b389ad3b530f312364"}, - {file = "httptools-0.6.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c7a5715b1f46e9852442f496c0df2f8c393cc8f293f5396d2c8d95cac852fb51"}, - {file = "httptools-0.6.2-cp38-cp38-win_amd64.whl", hash = "sha256:3f0246ca7f78fa8e3902ddb985b9f55509d417a862f4634a8fa63a7a496266c8"}, - {file = "httptools-0.6.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1099f73952e18c718ccaaf7a97ae58c94a91839c3d247c6184326f85a2eda7b4"}, - {file = "httptools-0.6.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c3e45d004531330030f7d07abe4865bc17963b9989bc1941cebbf7224010fb82"}, - {file = "httptools-0.6.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4f2fea370361a90cb9330610a95303587eda9d1e69930dbbee9978eac1d5946"}, - {file = "httptools-0.6.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0481154c91725f7e7b729a535190388be6c7cbae3bbf0e793343ca386282312"}, - {file = "httptools-0.6.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d25f8fdbc6cc6561353c7a384d76295e6a85a4945115b8bc347855db150e8c77"}, - {file = "httptools-0.6.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:054bdee08e4f7c15c186f6e7dbc8f0cf974b8dd1832b5f17f988faf8b12815c9"}, - {file = "httptools-0.6.2-cp39-cp39-win_amd64.whl", hash = "sha256:4502620722b453c2c6306fad392c515dcb804dfa9c6d3b90d8926a07a7a01109"}, - {file = "httptools-0.6.2.tar.gz", hash = "sha256:ae694efefcb61317c79b2fa1caebc122060992408e389bb00889567e463a47f1"}, -] - -[package.extras] -test = ["Cython (>=0.29.24)"] - -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - -[[package]] -name = "httpx" -version = "0.27.2" -description = "The next generation HTTP client." -optional = false -python-versions = ">=3.8" -files = [ - {file = "httpx-0.27.2-py3-none-any.whl", hash = "sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0"}, - {file = "httpx-0.27.2.tar.gz", hash = "sha256:f7c2be1d2f3c3c3160d441802406b206c2b76f5947b11115e6df10c6c65e66c2"}, -] - -[package.dependencies] -anyio = "*" -certifi = "*" -httpcore = "==1.*" -idna = "*" -sniffio = "*" - -[package.extras] -brotli = ["brotli", "brotlicffi"] -cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] -http2 = ["h2 (>=3,<5)"] -socks = ["socksio (==1.*)"] -zstd = ["zstandard (>=0.18.0)"] - -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "idna" version = "3.10" @@ -823,11 +374,6 @@ files = [ [package.extras] all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "imagesize" version = "1.4.1" @@ -839,11 +385,6 @@ files = [ {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "importlib-metadata" version = "8.5.0" @@ -867,11 +408,6 @@ perf = ["ipython"] test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] type = ["pytest-mypy"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "iniconfig" version = "2.0.0" @@ -883,11 +419,6 @@ files = [ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "isort" version = "5.13.2" @@ -902,27 +433,6 @@ files = [ [package.extras] colors = ["colorama (>=0.4.6)"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - -[[package]] -name = "itsdangerous" -version = "2.2.0" -description = "Safely pass data to untrusted environments and back." -optional = false -python-versions = ">=3.8" -files = [ - {file = "itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef"}, - {file = "itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173"}, -] - -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "jinja2" version = "3.1.4" @@ -940,11 +450,6 @@ MarkupSafe = ">=2.0" [package.extras] i18n = ["Babel (>=2.7)"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "jmespath" version = "1.0.1" @@ -956,11 +461,6 @@ files = [ {file = "jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "markupsafe" version = "3.0.1" @@ -1031,11 +531,6 @@ files = [ {file = "markupsafe-3.0.1.tar.gz", hash = "sha256:3e683ee4f5d0fa2dde4db77ed8dd8a876686e3fc417655c2ece9a90576905344"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "mccabe" version = "0.7.0" @@ -1047,52 +542,6 @@ files = [ {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - -[[package]] -name = "mchpy" -version = "2.2.0" -description = "Common library for MCH python services." -optional = false -python-versions = ">=3.10,<3.13" -files = [ - {file = "mchpy-2.2.0-py3-none-any.whl", hash = "sha256:87e2a801e562767283ed67f0495209ae380017755d73f347baa14dbedfcad7fc"}, - {file = "mchpy-2.2.0.tar.gz", hash = "sha256:a2f72f315e8830219e013cbd74ef2b9c26f2e2b7aeee15f1bd06360de41133c4"}, -] - -[package.dependencies] -asyncstdlib = "*" -authlib = {version = ">=1.3.1", optional = true, markers = "extra == \"fastapi\""} -fastapi = {version = ">=0.114.0,<0.115.0", optional = true, markers = "extra == \"fastapi\""} -httpx = "*" -itsdangerous = {version = "*", optional = true, markers = "extra == \"fastapi\""} -pydantic = ">=2.7.1,<3.0.0" -pydantic-settings = ">=2.2.1,<3.0.0" -pyjwt = {version = "*", optional = true, markers = "extra == \"fastapi\""} -python-dateutil = ">=2.9.0,<3.0.0" -python-json-logger = "*" -pyyaml = ">=6.0,<7.0" -requests = ">=2.32.1,<3.0.0" -types-requests = ">=2.28.11,<3.0.0" -uvicorn = {version = "*", extras = ["standard"], optional = true, markers = "extra == \"fastapi\""} - -[package.extras] -aiobotocore = ["aiobotocore (>=2.13.0)"] -boto3 = ["boto3"] -fastapi = ["authlib (>=1.3.1)", "fastapi (>=0.114.0,<0.115.0)", "itsdangerous", "pyjwt", "uvicorn[standard]"] -kafka = ["aiokafka (>=0.11.0,<0.12.0)", "cloudevents", "confluent-kafka"] -prometheus-fastapi = ["starlette_exporter (>=0.23.0,<0.24.0)"] -s3 = ["boto3"] -s3fs = ["aiobotocore (>=2.13.0)", "s3fs (>=2024.3.1)"] - -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "mypy" version = "1.12.0" @@ -1144,11 +593,6 @@ install-types = ["pip"] mypyc = ["setuptools (>=50)"] reports = ["lxml"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "mypy-extensions" version = "1.0.0" @@ -1160,11 +604,6 @@ files = [ {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "packaging" version = "24.1" @@ -1176,11 +615,6 @@ files = [ {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "platformdirs" version = "4.3.6" @@ -1197,11 +631,6 @@ docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-a test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"] type = ["mypy (>=1.11.2)"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "pluggy" version = "1.5.0" @@ -1217,27 +646,6 @@ files = [ dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - -[[package]] -name = "pycparser" -version = "2.22" -description = "C parser in Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, - {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, -] - -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "pydantic" version = "2.9.2" @@ -1258,11 +666,6 @@ typing-extensions = {version = ">=4.6.1", markers = "python_version < \"3.13\""} email = ["email-validator (>=2.0.0)"] timezone = ["tzdata"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "pydantic-core" version = "2.23.4" @@ -1364,11 +767,6 @@ files = [ [package.dependencies] typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "pydantic-settings" version = "2.5.2" @@ -1389,11 +787,6 @@ azure-key-vault = ["azure-identity (>=1.16.0)", "azure-keyvault-secrets (>=4.8.0 toml = ["tomli (>=2.0.1)"] yaml = ["pyyaml (>=6.0.1)"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "pydata-sphinx-theme" version = "0.15.4" @@ -1422,11 +815,6 @@ doc = ["ablog (>=0.11.8)", "colorama", "graphviz", "ipykernel", "ipyleaflet", "i i18n = ["Babel", "jinja2"] test = ["pytest", "pytest-cov", "pytest-regressions", "sphinx[test]"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "pygments" version = "2.18.0" @@ -1441,33 +829,6 @@ files = [ [package.extras] windows-terminal = ["colorama (>=0.4.6)"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - -[[package]] -name = "pyjwt" -version = "2.9.0" -description = "JSON Web Token implementation in Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "PyJWT-2.9.0-py3-none-any.whl", hash = "sha256:3b02fb0f44517787776cf48f2ae25d8e14f300e6d7545a4315cee571a415e850"}, - {file = "pyjwt-2.9.0.tar.gz", hash = "sha256:7e1e5b56cc735432a7369cbfa0efe50fa113ebecdc04ae6922deba8b84582d0c"}, -] - -[package.extras] -crypto = ["cryptography (>=3.4.0)"] -dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pytest (>=6.0.0,<7.0.0)", "sphinx", "sphinx-rtd-theme", "zope.interface"] -docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"] -tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] - -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "pylint" version = "3.3.1" @@ -1492,11 +853,6 @@ tomlkit = ">=0.10.1" spelling = ["pyenchant (>=3.2,<4.0)"] testutils = ["gitpython (>3)"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "pytest" version = "8.3.3" @@ -1517,11 +873,6 @@ pluggy = ">=1.5,<2" [package.extras] dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "pytest-cov" version = "5.0.0" @@ -1540,11 +891,6 @@ pytest = ">=4.6" [package.extras] testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "python-dateutil" version = "2.9.0.post0" @@ -1559,11 +905,6 @@ files = [ [package.dependencies] six = ">=1.5" -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "python-dotenv" version = "1.0.1" @@ -1578,11 +919,6 @@ files = [ [package.extras] cli = ["click (>=5.0)"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "python-json-logger" version = "2.0.7" @@ -1594,11 +930,6 @@ files = [ {file = "python_json_logger-2.0.7-py3-none-any.whl", hash = "sha256:f380b826a991ebbe3de4d897aeec42760035ac760345e57b812938dc8b35e2bd"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "pyyaml" version = "6.0.2" @@ -1661,11 +992,6 @@ files = [ {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "requests" version = "2.32.3" @@ -1687,17 +1013,12 @@ urllib3 = ">=1.21.1,<3" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "s3transfer" version = "0.10.3" description = "An Amazon S3 Transfer Manager" optional = false -python-versions = ">= 3.8" +python-versions = ">=3.8" files = [ {file = "s3transfer-0.10.3-py3-none-any.whl", hash = "sha256:263ed587a5803c6c708d3ce44dc4dfedaab4c1a32e8329bab818933d79ddcf5d"}, {file = "s3transfer-0.10.3.tar.gz", hash = "sha256:4f50ed74ab84d474ce614475e0b8d5047ff080810aac5d01ea25231cfc944b0c"}, @@ -1709,11 +1030,6 @@ botocore = ">=1.33.2,<2.0a.0" [package.extras] crt = ["botocore[crt] (>=1.33.2,<2.0a.0)"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "six" version = "1.16.0" @@ -1725,27 +1041,6 @@ files = [ {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - -[[package]] -name = "sniffio" -version = "1.3.1" -description = "Sniff out which async library your code is running under" -optional = false -python-versions = ">=3.7" -files = [ - {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, - {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, -] - -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "snowballstemmer" version = "2.2.0" @@ -1757,11 +1052,6 @@ files = [ {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "soupsieve" version = "2.6" @@ -1773,11 +1063,6 @@ files = [ {file = "soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "sphinx" version = "8.1.3" @@ -1812,11 +1097,6 @@ docs = ["sphinxcontrib-websupport"] lint = ["flake8 (>=6.0)", "mypy (==1.11.1)", "pyright (==1.1.384)", "pytest (>=6.0)", "ruff (==0.6.9)", "sphinx-lint (>=0.9)", "tomli (>=2)", "types-Pillow (==10.2.0.20240822)", "types-Pygments (==2.18.0.20240506)", "types-colorama (==0.4.15.20240311)", "types-defusedxml (==0.7.0.20240218)", "types-docutils (==0.21.0.20241005)", "types-requests (==2.32.0.20240914)", "types-urllib3 (==1.26.25.14)"] test = ["cython (>=3.0)", "defusedxml (>=0.7.1)", "pytest (>=8.0)", "setuptools (>=70.0)", "typing_extensions (>=4.9)"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "sphinx-autoapi" version = "3.3.2" @@ -1837,11 +1117,6 @@ sphinx = ">=6.1.0" [package.extras] docs = ["furo", "sphinx", "sphinx-design"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "sphinxcontrib-applehelp" version = "2.0.0" @@ -1858,11 +1133,6 @@ lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] standalone = ["Sphinx (>=5)"] test = ["pytest"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "sphinxcontrib-devhelp" version = "2.0.0" @@ -1879,11 +1149,6 @@ lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] standalone = ["Sphinx (>=5)"] test = ["pytest"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "sphinxcontrib-htmlhelp" version = "2.1.0" @@ -1900,11 +1165,6 @@ lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] standalone = ["Sphinx (>=5)"] test = ["html5lib", "pytest"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "sphinxcontrib-jsmath" version = "1.0.1" @@ -1919,11 +1179,6 @@ files = [ [package.extras] test = ["flake8", "mypy", "pytest"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "sphinxcontrib-qthelp" version = "2.0.0" @@ -1940,11 +1195,6 @@ lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] standalone = ["Sphinx (>=5)"] test = ["defusedxml (>=0.7.1)", "pytest"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "sphinxcontrib-serializinghtml" version = "2.0.0" @@ -1961,33 +1211,6 @@ lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] standalone = ["Sphinx (>=5)"] test = ["pytest"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - -[[package]] -name = "starlette" -version = "0.38.6" -description = "The little ASGI library that shines." -optional = false -python-versions = ">=3.8" -files = [ - {file = "starlette-0.38.6-py3-none-any.whl", hash = "sha256:4517a1409e2e73ee4951214ba012052b9e16f60e90d73cfb06192c19203bbb05"}, - {file = "starlette-0.38.6.tar.gz", hash = "sha256:863a1588f5574e70a821dadefb41e4881ea451a47a3cd1b4df359d4ffefe5ead"}, -] - -[package.dependencies] -anyio = ">=3.4.0,<5" - -[package.extras] -full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7)", "pyyaml"] - -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "tomli" version = "2.0.2" @@ -1999,11 +1222,6 @@ files = [ {file = "tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "tomlkit" version = "0.13.2" @@ -2015,30 +1233,6 @@ files = [ {file = "tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - -[[package]] -name = "types-requests" -version = "2.32.0.20240914" -description = "Typing stubs for requests" -optional = false -python-versions = ">=3.8" -files = [ - {file = "types-requests-2.32.0.20240914.tar.gz", hash = "sha256:2850e178db3919d9bf809e434eef65ba49d0e7e33ac92d588f4a5e295fffd405"}, - {file = "types_requests-2.32.0.20240914-py3-none-any.whl", hash = "sha256:59c2f673eb55f32a99b2894faf6020e1a9f4a402ad0f192bfee0b64469054310"}, -] - -[package.dependencies] -urllib3 = ">=2" - -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "typing-extensions" version = "4.12.2" @@ -2050,11 +1244,6 @@ files = [ {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "urllib3" version = "2.2.3" @@ -2072,290 +1261,6 @@ h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - -[[package]] -name = "uvicorn" -version = "0.31.1" -description = "The lightning-fast ASGI server." -optional = false -python-versions = ">=3.8" -files = [ - {file = "uvicorn-0.31.1-py3-none-any.whl", hash = "sha256:adc42d9cac80cf3e51af97c1851648066841e7cfb6993a4ca8de29ac1548ed41"}, - {file = "uvicorn-0.31.1.tar.gz", hash = "sha256:f5167919867b161b7bcaf32646c6a94cdbd4c3aa2eb5c17d36bb9aa5cfd8c493"}, -] - -[package.dependencies] -click = ">=7.0" -colorama = {version = ">=0.4", optional = true, markers = "sys_platform == \"win32\" and extra == \"standard\""} -h11 = ">=0.8" -httptools = {version = ">=0.5.0", optional = true, markers = "extra == \"standard\""} -python-dotenv = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} -pyyaml = {version = ">=5.1", optional = true, markers = "extra == \"standard\""} -uvloop = {version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1", optional = true, markers = "(sys_platform != \"win32\" and sys_platform != \"cygwin\") and platform_python_implementation != \"PyPy\" and extra == \"standard\""} -watchfiles = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} -websockets = {version = ">=10.4", optional = true, markers = "extra == \"standard\""} - -[package.extras] -standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] - -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - -[[package]] -name = "uvloop" -version = "0.20.0" -description = "Fast implementation of asyncio event loop on top of libuv" -optional = false -python-versions = ">=3.8.0" -files = [ - {file = "uvloop-0.20.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9ebafa0b96c62881d5cafa02d9da2e44c23f9f0cd829f3a32a6aff771449c996"}, - {file = "uvloop-0.20.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:35968fc697b0527a06e134999eef859b4034b37aebca537daeb598b9d45a137b"}, - {file = "uvloop-0.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b16696f10e59d7580979b420eedf6650010a4a9c3bd8113f24a103dfdb770b10"}, - {file = "uvloop-0.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b04d96188d365151d1af41fa2d23257b674e7ead68cfd61c725a422764062ae"}, - {file = "uvloop-0.20.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:94707205efbe809dfa3a0d09c08bef1352f5d3d6612a506f10a319933757c006"}, - {file = "uvloop-0.20.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:89e8d33bb88d7263f74dc57d69f0063e06b5a5ce50bb9a6b32f5fcbe655f9e73"}, - {file = "uvloop-0.20.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e50289c101495e0d1bb0bfcb4a60adde56e32f4449a67216a1ab2750aa84f037"}, - {file = "uvloop-0.20.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e237f9c1e8a00e7d9ddaa288e535dc337a39bcbf679f290aee9d26df9e72bce9"}, - {file = "uvloop-0.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:746242cd703dc2b37f9d8b9f173749c15e9a918ddb021575a0205ec29a38d31e"}, - {file = "uvloop-0.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82edbfd3df39fb3d108fc079ebc461330f7c2e33dbd002d146bf7c445ba6e756"}, - {file = "uvloop-0.20.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:80dc1b139516be2077b3e57ce1cb65bfed09149e1d175e0478e7a987863b68f0"}, - {file = "uvloop-0.20.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4f44af67bf39af25db4c1ac27e82e9665717f9c26af2369c404be865c8818dcf"}, - {file = "uvloop-0.20.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:4b75f2950ddb6feed85336412b9a0c310a2edbcf4cf931aa5cfe29034829676d"}, - {file = "uvloop-0.20.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:77fbc69c287596880ecec2d4c7a62346bef08b6209749bf6ce8c22bbaca0239e"}, - {file = "uvloop-0.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6462c95f48e2d8d4c993a2950cd3d31ab061864d1c226bbf0ee2f1a8f36674b9"}, - {file = "uvloop-0.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:649c33034979273fa71aa25d0fe120ad1777c551d8c4cd2c0c9851d88fcb13ab"}, - {file = "uvloop-0.20.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3a609780e942d43a275a617c0839d85f95c334bad29c4c0918252085113285b5"}, - {file = "uvloop-0.20.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aea15c78e0d9ad6555ed201344ae36db5c63d428818b4b2a42842b3870127c00"}, - {file = "uvloop-0.20.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:f0e94b221295b5e69de57a1bd4aeb0b3a29f61be6e1b478bb8a69a73377db7ba"}, - {file = "uvloop-0.20.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:fee6044b64c965c425b65a4e17719953b96e065c5b7e09b599ff332bb2744bdf"}, - {file = "uvloop-0.20.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:265a99a2ff41a0fd56c19c3838b29bf54d1d177964c300dad388b27e84fd7847"}, - {file = "uvloop-0.20.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b10c2956efcecb981bf9cfb8184d27d5d64b9033f917115a960b83f11bfa0d6b"}, - {file = "uvloop-0.20.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e7d61fe8e8d9335fac1bf8d5d82820b4808dd7a43020c149b63a1ada953d48a6"}, - {file = "uvloop-0.20.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2beee18efd33fa6fdb0976e18475a4042cd31c7433c866e8a09ab604c7c22ff2"}, - {file = "uvloop-0.20.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d8c36fdf3e02cec92aed2d44f63565ad1522a499c654f07935c8f9d04db69e95"}, - {file = "uvloop-0.20.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a0fac7be202596c7126146660725157d4813aa29a4cc990fe51346f75ff8fde7"}, - {file = "uvloop-0.20.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d0fba61846f294bce41eb44d60d58136090ea2b5b99efd21cbdf4e21927c56a"}, - {file = "uvloop-0.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95720bae002ac357202e0d866128eb1ac82545bcf0b549b9abe91b5178d9b541"}, - {file = "uvloop-0.20.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:36c530d8fa03bfa7085af54a48f2ca16ab74df3ec7108a46ba82fd8b411a2315"}, - {file = "uvloop-0.20.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e97152983442b499d7a71e44f29baa75b3b02e65d9c44ba53b10338e98dedb66"}, - {file = "uvloop-0.20.0.tar.gz", hash = "sha256:4603ca714a754fc8d9b197e325db25b2ea045385e8a3ad05d3463de725fdf469"}, -] - -[package.extras] -docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] -test = ["Cython (>=0.29.36,<0.30.0)", "aiohttp (==3.9.0b0)", "aiohttp (>=3.8.1)", "flake8 (>=5.0,<6.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=23.0.0,<23.1.0)", "pycodestyle (>=2.9.0,<2.10.0)"] - -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - -[[package]] -name = "watchfiles" -version = "0.24.0" -description = "Simple, modern and high performance file watching and code reload in python." -optional = false -python-versions = ">=3.8" -files = [ - {file = "watchfiles-0.24.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:083dc77dbdeef09fa44bb0f4d1df571d2e12d8a8f985dccde71ac3ac9ac067a0"}, - {file = "watchfiles-0.24.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e94e98c7cb94cfa6e071d401ea3342767f28eb5a06a58fafdc0d2a4974f4f35c"}, - {file = "watchfiles-0.24.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82ae557a8c037c42a6ef26c494d0631cacca040934b101d001100ed93d43f361"}, - {file = "watchfiles-0.24.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:acbfa31e315a8f14fe33e3542cbcafc55703b8f5dcbb7c1eecd30f141df50db3"}, - {file = "watchfiles-0.24.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b74fdffce9dfcf2dc296dec8743e5b0332d15df19ae464f0e249aa871fc1c571"}, - {file = "watchfiles-0.24.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:449f43f49c8ddca87c6b3980c9284cab6bd1f5c9d9a2b00012adaaccd5e7decd"}, - {file = "watchfiles-0.24.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4abf4ad269856618f82dee296ac66b0cd1d71450fc3c98532d93798e73399b7a"}, - {file = "watchfiles-0.24.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f895d785eb6164678ff4bb5cc60c5996b3ee6df3edb28dcdeba86a13ea0465e"}, - {file = "watchfiles-0.24.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7ae3e208b31be8ce7f4c2c0034f33406dd24fbce3467f77223d10cd86778471c"}, - {file = "watchfiles-0.24.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2efec17819b0046dde35d13fb8ac7a3ad877af41ae4640f4109d9154ed30a188"}, - {file = "watchfiles-0.24.0-cp310-none-win32.whl", hash = "sha256:6bdcfa3cd6fdbdd1a068a52820f46a815401cbc2cb187dd006cb076675e7b735"}, - {file = "watchfiles-0.24.0-cp310-none-win_amd64.whl", hash = "sha256:54ca90a9ae6597ae6dc00e7ed0a040ef723f84ec517d3e7ce13e63e4bc82fa04"}, - {file = "watchfiles-0.24.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:bdcd5538e27f188dd3c804b4a8d5f52a7fc7f87e7fd6b374b8e36a4ca03db428"}, - {file = "watchfiles-0.24.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2dadf8a8014fde6addfd3c379e6ed1a981c8f0a48292d662e27cabfe4239c83c"}, - {file = "watchfiles-0.24.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6509ed3f467b79d95fc62a98229f79b1a60d1b93f101e1c61d10c95a46a84f43"}, - {file = "watchfiles-0.24.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8360f7314a070c30e4c976b183d1d8d1585a4a50c5cb603f431cebcbb4f66327"}, - {file = "watchfiles-0.24.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:316449aefacf40147a9efaf3bd7c9bdd35aaba9ac5d708bd1eb5763c9a02bef5"}, - {file = "watchfiles-0.24.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73bde715f940bea845a95247ea3e5eb17769ba1010efdc938ffcb967c634fa61"}, - {file = "watchfiles-0.24.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3770e260b18e7f4e576edca4c0a639f704088602e0bc921c5c2e721e3acb8d15"}, - {file = "watchfiles-0.24.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa0fd7248cf533c259e59dc593a60973a73e881162b1a2f73360547132742823"}, - {file = "watchfiles-0.24.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d7a2e3b7f5703ffbd500dabdefcbc9eafeff4b9444bbdd5d83d79eedf8428fab"}, - {file = "watchfiles-0.24.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d831ee0a50946d24a53821819b2327d5751b0c938b12c0653ea5be7dea9c82ec"}, - {file = "watchfiles-0.24.0-cp311-none-win32.whl", hash = "sha256:49d617df841a63b4445790a254013aea2120357ccacbed00253f9c2b5dc24e2d"}, - {file = "watchfiles-0.24.0-cp311-none-win_amd64.whl", hash = "sha256:d3dcb774e3568477275cc76554b5a565024b8ba3a0322f77c246bc7111c5bb9c"}, - {file = "watchfiles-0.24.0-cp311-none-win_arm64.whl", hash = "sha256:9301c689051a4857d5b10777da23fafb8e8e921bcf3abe6448a058d27fb67633"}, - {file = "watchfiles-0.24.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:7211b463695d1e995ca3feb38b69227e46dbd03947172585ecb0588f19b0d87a"}, - {file = "watchfiles-0.24.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4b8693502d1967b00f2fb82fc1e744df128ba22f530e15b763c8d82baee15370"}, - {file = "watchfiles-0.24.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdab9555053399318b953a1fe1f586e945bc8d635ce9d05e617fd9fe3a4687d6"}, - {file = "watchfiles-0.24.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:34e19e56d68b0dad5cff62273107cf5d9fbaf9d75c46277aa5d803b3ef8a9e9b"}, - {file = "watchfiles-0.24.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:41face41f036fee09eba33a5b53a73e9a43d5cb2c53dad8e61fa6c9f91b5a51e"}, - {file = "watchfiles-0.24.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5148c2f1ea043db13ce9b0c28456e18ecc8f14f41325aa624314095b6aa2e9ea"}, - {file = "watchfiles-0.24.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7e4bd963a935aaf40b625c2499f3f4f6bbd0c3776f6d3bc7c853d04824ff1c9f"}, - {file = "watchfiles-0.24.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c79d7719d027b7a42817c5d96461a99b6a49979c143839fc37aa5748c322f234"}, - {file = "watchfiles-0.24.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:32aa53a9a63b7f01ed32e316e354e81e9da0e6267435c7243bf8ae0f10b428ef"}, - {file = "watchfiles-0.24.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ce72dba6a20e39a0c628258b5c308779b8697f7676c254a845715e2a1039b968"}, - {file = "watchfiles-0.24.0-cp312-none-win32.whl", hash = "sha256:d9018153cf57fc302a2a34cb7564870b859ed9a732d16b41a9b5cb2ebed2d444"}, - {file = "watchfiles-0.24.0-cp312-none-win_amd64.whl", hash = "sha256:551ec3ee2a3ac9cbcf48a4ec76e42c2ef938a7e905a35b42a1267fa4b1645896"}, - {file = "watchfiles-0.24.0-cp312-none-win_arm64.whl", hash = "sha256:b52a65e4ea43c6d149c5f8ddb0bef8d4a1e779b77591a458a893eb416624a418"}, - {file = "watchfiles-0.24.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:3d2e3ab79a1771c530233cadfd277fcc762656d50836c77abb2e5e72b88e3a48"}, - {file = "watchfiles-0.24.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:327763da824817b38ad125dcd97595f942d720d32d879f6c4ddf843e3da3fe90"}, - {file = "watchfiles-0.24.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd82010f8ab451dabe36054a1622870166a67cf3fce894f68895db6f74bbdc94"}, - {file = "watchfiles-0.24.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d64ba08db72e5dfd5c33be1e1e687d5e4fcce09219e8aee893a4862034081d4e"}, - {file = "watchfiles-0.24.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1cf1f6dd7825053f3d98f6d33f6464ebdd9ee95acd74ba2c34e183086900a827"}, - {file = "watchfiles-0.24.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:43e3e37c15a8b6fe00c1bce2473cfa8eb3484bbeecf3aefbf259227e487a03df"}, - {file = "watchfiles-0.24.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88bcd4d0fe1d8ff43675360a72def210ebad3f3f72cabfeac08d825d2639b4ab"}, - {file = "watchfiles-0.24.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:999928c6434372fde16c8f27143d3e97201160b48a614071261701615a2a156f"}, - {file = "watchfiles-0.24.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:30bbd525c3262fd9f4b1865cb8d88e21161366561cd7c9e1194819e0a33ea86b"}, - {file = "watchfiles-0.24.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:edf71b01dec9f766fb285b73930f95f730bb0943500ba0566ae234b5c1618c18"}, - {file = "watchfiles-0.24.0-cp313-none-win32.whl", hash = "sha256:f4c96283fca3ee09fb044f02156d9570d156698bc3734252175a38f0e8975f07"}, - {file = "watchfiles-0.24.0-cp313-none-win_amd64.whl", hash = "sha256:a974231b4fdd1bb7f62064a0565a6b107d27d21d9acb50c484d2cdba515b9366"}, - {file = "watchfiles-0.24.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:ee82c98bed9d97cd2f53bdb035e619309a098ea53ce525833e26b93f673bc318"}, - {file = "watchfiles-0.24.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fd92bbaa2ecdb7864b7600dcdb6f2f1db6e0346ed425fbd01085be04c63f0b05"}, - {file = "watchfiles-0.24.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f83df90191d67af5a831da3a33dd7628b02a95450e168785586ed51e6d28943c"}, - {file = "watchfiles-0.24.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fca9433a45f18b7c779d2bae7beeec4f740d28b788b117a48368d95a3233ed83"}, - {file = "watchfiles-0.24.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b995bfa6bf01a9e09b884077a6d37070464b529d8682d7691c2d3b540d357a0c"}, - {file = "watchfiles-0.24.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ed9aba6e01ff6f2e8285e5aa4154e2970068fe0fc0998c4380d0e6278222269b"}, - {file = "watchfiles-0.24.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5171ef898299c657685306d8e1478a45e9303ddcd8ac5fed5bd52ad4ae0b69b"}, - {file = "watchfiles-0.24.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4933a508d2f78099162da473841c652ad0de892719043d3f07cc83b33dfd9d91"}, - {file = "watchfiles-0.24.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:95cf3b95ea665ab03f5a54765fa41abf0529dbaf372c3b83d91ad2cfa695779b"}, - {file = "watchfiles-0.24.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:01def80eb62bd5db99a798d5e1f5f940ca0a05986dcfae21d833af7a46f7ee22"}, - {file = "watchfiles-0.24.0-cp38-none-win32.whl", hash = "sha256:4d28cea3c976499475f5b7a2fec6b3a36208656963c1a856d328aeae056fc5c1"}, - {file = "watchfiles-0.24.0-cp38-none-win_amd64.whl", hash = "sha256:21ab23fdc1208086d99ad3f69c231ba265628014d4aed31d4e8746bd59e88cd1"}, - {file = "watchfiles-0.24.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:b665caeeda58625c3946ad7308fbd88a086ee51ccb706307e5b1fa91556ac886"}, - {file = "watchfiles-0.24.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5c51749f3e4e269231510da426ce4a44beb98db2dce9097225c338f815b05d4f"}, - {file = "watchfiles-0.24.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82b2509f08761f29a0fdad35f7e1638b8ab1adfa2666d41b794090361fb8b855"}, - {file = "watchfiles-0.24.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a60e2bf9dc6afe7f743e7c9b149d1fdd6dbf35153c78fe3a14ae1a9aee3d98b"}, - {file = "watchfiles-0.24.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f7d9b87c4c55e3ea8881dfcbf6d61ea6775fffed1fedffaa60bd047d3c08c430"}, - {file = "watchfiles-0.24.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:78470906a6be5199524641f538bd2c56bb809cd4bf29a566a75051610bc982c3"}, - {file = "watchfiles-0.24.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:07cdef0c84c03375f4e24642ef8d8178e533596b229d32d2bbd69e5128ede02a"}, - {file = "watchfiles-0.24.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d337193bbf3e45171c8025e291530fb7548a93c45253897cd764a6a71c937ed9"}, - {file = "watchfiles-0.24.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ec39698c45b11d9694a1b635a70946a5bad066b593af863460a8e600f0dff1ca"}, - {file = "watchfiles-0.24.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2e28d91ef48eab0afb939fa446d8ebe77e2f7593f5f463fd2bb2b14132f95b6e"}, - {file = "watchfiles-0.24.0-cp39-none-win32.whl", hash = "sha256:7138eff8baa883aeaa074359daabb8b6c1e73ffe69d5accdc907d62e50b1c0da"}, - {file = "watchfiles-0.24.0-cp39-none-win_amd64.whl", hash = "sha256:b3ef2c69c655db63deb96b3c3e587084612f9b1fa983df5e0c3379d41307467f"}, - {file = "watchfiles-0.24.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:632676574429bee8c26be8af52af20e0c718cc7f5f67f3fb658c71928ccd4f7f"}, - {file = "watchfiles-0.24.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:a2a9891723a735d3e2540651184be6fd5b96880c08ffe1a98bae5017e65b544b"}, - {file = "watchfiles-0.24.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a7fa2bc0efef3e209a8199fd111b8969fe9db9c711acc46636686331eda7dd4"}, - {file = "watchfiles-0.24.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01550ccf1d0aed6ea375ef259706af76ad009ef5b0203a3a4cce0f6024f9b68a"}, - {file = "watchfiles-0.24.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:96619302d4374de5e2345b2b622dc481257a99431277662c30f606f3e22f42be"}, - {file = "watchfiles-0.24.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:85d5f0c7771dcc7a26c7a27145059b6bb0ce06e4e751ed76cdf123d7039b60b5"}, - {file = "watchfiles-0.24.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:951088d12d339690a92cef2ec5d3cfd957692834c72ffd570ea76a6790222777"}, - {file = "watchfiles-0.24.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49fb58bcaa343fedc6a9e91f90195b20ccb3135447dc9e4e2570c3a39565853e"}, - {file = "watchfiles-0.24.0.tar.gz", hash = "sha256:afb72325b74fa7a428c009c1b8be4b4d7c2afedafb2982827ef2156646df2fe1"}, -] - -[package.dependencies] -anyio = ">=3.0.0" - -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - -[[package]] -name = "websockets" -version = "13.1" -description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" -optional = false -python-versions = ">=3.8" -files = [ - {file = "websockets-13.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f48c749857f8fb598fb890a75f540e3221d0976ed0bf879cf3c7eef34151acee"}, - {file = "websockets-13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c7e72ce6bda6fb9409cc1e8164dd41d7c91466fb599eb047cfda72fe758a34a7"}, - {file = "websockets-13.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f779498eeec470295a2b1a5d97aa1bc9814ecd25e1eb637bd9d1c73a327387f6"}, - {file = "websockets-13.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4676df3fe46956fbb0437d8800cd5f2b6d41143b6e7e842e60554398432cf29b"}, - {file = "websockets-13.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7affedeb43a70351bb811dadf49493c9cfd1ed94c9c70095fd177e9cc1541fa"}, - {file = "websockets-13.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1971e62d2caa443e57588e1d82d15f663b29ff9dfe7446d9964a4b6f12c1e700"}, - {file = "websockets-13.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5f2e75431f8dc4a47f31565a6e1355fb4f2ecaa99d6b89737527ea917066e26c"}, - {file = "websockets-13.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:58cf7e75dbf7e566088b07e36ea2e3e2bd5676e22216e4cad108d4df4a7402a0"}, - {file = "websockets-13.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c90d6dec6be2c7d03378a574de87af9b1efea77d0c52a8301dd831ece938452f"}, - {file = "websockets-13.1-cp310-cp310-win32.whl", hash = "sha256:730f42125ccb14602f455155084f978bd9e8e57e89b569b4d7f0f0c17a448ffe"}, - {file = "websockets-13.1-cp310-cp310-win_amd64.whl", hash = "sha256:5993260f483d05a9737073be197371940c01b257cc45ae3f1d5d7adb371b266a"}, - {file = "websockets-13.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:61fc0dfcda609cda0fc9fe7977694c0c59cf9d749fbb17f4e9483929e3c48a19"}, - {file = "websockets-13.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ceec59f59d092c5007e815def4ebb80c2de330e9588e101cf8bd94c143ec78a5"}, - {file = "websockets-13.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c1dca61c6db1166c48b95198c0b7d9c990b30c756fc2923cc66f68d17dc558fd"}, - {file = "websockets-13.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:308e20f22c2c77f3f39caca508e765f8725020b84aa963474e18c59accbf4c02"}, - {file = "websockets-13.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62d516c325e6540e8a57b94abefc3459d7dab8ce52ac75c96cad5549e187e3a7"}, - {file = "websockets-13.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87c6e35319b46b99e168eb98472d6c7d8634ee37750d7693656dc766395df096"}, - {file = "websockets-13.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5f9fee94ebafbc3117c30be1844ed01a3b177bb6e39088bc6b2fa1dc15572084"}, - {file = "websockets-13.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7c1e90228c2f5cdde263253fa5db63e6653f1c00e7ec64108065a0b9713fa1b3"}, - {file = "websockets-13.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6548f29b0e401eea2b967b2fdc1c7c7b5ebb3eeb470ed23a54cd45ef078a0db9"}, - {file = "websockets-13.1-cp311-cp311-win32.whl", hash = "sha256:c11d4d16e133f6df8916cc5b7e3e96ee4c44c936717d684a94f48f82edb7c92f"}, - {file = "websockets-13.1-cp311-cp311-win_amd64.whl", hash = "sha256:d04f13a1d75cb2b8382bdc16ae6fa58c97337253826dfe136195b7f89f661557"}, - {file = "websockets-13.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9d75baf00138f80b48f1eac72ad1535aac0b6461265a0bcad391fc5aba875cfc"}, - {file = "websockets-13.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9b6f347deb3dcfbfde1c20baa21c2ac0751afaa73e64e5b693bb2b848efeaa49"}, - {file = "websockets-13.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de58647e3f9c42f13f90ac7e5f58900c80a39019848c5547bc691693098ae1bd"}, - {file = "websockets-13.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1b54689e38d1279a51d11e3467dd2f3a50f5f2e879012ce8f2d6943f00e83f0"}, - {file = "websockets-13.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf1781ef73c073e6b0f90af841aaf98501f975d306bbf6221683dd594ccc52b6"}, - {file = "websockets-13.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d23b88b9388ed85c6faf0e74d8dec4f4d3baf3ecf20a65a47b836d56260d4b9"}, - {file = "websockets-13.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3c78383585f47ccb0fcf186dcb8a43f5438bd7d8f47d69e0b56f71bf431a0a68"}, - {file = "websockets-13.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d6d300f8ec35c24025ceb9b9019ae9040c1ab2f01cddc2bcc0b518af31c75c14"}, - {file = "websockets-13.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a9dcaf8b0cc72a392760bb8755922c03e17a5a54e08cca58e8b74f6902b433cf"}, - {file = "websockets-13.1-cp312-cp312-win32.whl", hash = "sha256:2f85cf4f2a1ba8f602298a853cec8526c2ca42a9a4b947ec236eaedb8f2dc80c"}, - {file = "websockets-13.1-cp312-cp312-win_amd64.whl", hash = "sha256:38377f8b0cdeee97c552d20cf1865695fcd56aba155ad1b4ca8779a5b6ef4ac3"}, - {file = "websockets-13.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a9ab1e71d3d2e54a0aa646ab6d4eebfaa5f416fe78dfe4da2839525dc5d765c6"}, - {file = "websockets-13.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b9d7439d7fab4dce00570bb906875734df13d9faa4b48e261c440a5fec6d9708"}, - {file = "websockets-13.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:327b74e915cf13c5931334c61e1a41040e365d380f812513a255aa804b183418"}, - {file = "websockets-13.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:325b1ccdbf5e5725fdcb1b0e9ad4d2545056479d0eee392c291c1bf76206435a"}, - {file = "websockets-13.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:346bee67a65f189e0e33f520f253d5147ab76ae42493804319b5716e46dddf0f"}, - {file = "websockets-13.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91a0fa841646320ec0d3accdff5b757b06e2e5c86ba32af2e0815c96c7a603c5"}, - {file = "websockets-13.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:18503d2c5f3943e93819238bf20df71982d193f73dcecd26c94514f417f6b135"}, - {file = "websockets-13.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:a9cd1af7e18e5221d2878378fbc287a14cd527fdd5939ed56a18df8a31136bb2"}, - {file = "websockets-13.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:70c5be9f416aa72aab7a2a76c90ae0a4fe2755c1816c153c1a2bcc3333ce4ce6"}, - {file = "websockets-13.1-cp313-cp313-win32.whl", hash = "sha256:624459daabeb310d3815b276c1adef475b3e6804abaf2d9d2c061c319f7f187d"}, - {file = "websockets-13.1-cp313-cp313-win_amd64.whl", hash = "sha256:c518e84bb59c2baae725accd355c8dc517b4a3ed8db88b4bc93c78dae2974bf2"}, - {file = "websockets-13.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:c7934fd0e920e70468e676fe7f1b7261c1efa0d6c037c6722278ca0228ad9d0d"}, - {file = "websockets-13.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:149e622dc48c10ccc3d2760e5f36753db9cacf3ad7bc7bbbfd7d9c819e286f23"}, - {file = "websockets-13.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a569eb1b05d72f9bce2ebd28a1ce2054311b66677fcd46cf36204ad23acead8c"}, - {file = "websockets-13.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95df24ca1e1bd93bbca51d94dd049a984609687cb2fb08a7f2c56ac84e9816ea"}, - {file = "websockets-13.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8dbb1bf0c0a4ae8b40bdc9be7f644e2f3fb4e8a9aca7145bfa510d4a374eeb7"}, - {file = "websockets-13.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:035233b7531fb92a76beefcbf479504db8c72eb3bff41da55aecce3a0f729e54"}, - {file = "websockets-13.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:e4450fc83a3df53dec45922b576e91e94f5578d06436871dce3a6be38e40f5db"}, - {file = "websockets-13.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:463e1c6ec853202dd3657f156123d6b4dad0c546ea2e2e38be2b3f7c5b8e7295"}, - {file = "websockets-13.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:6d6855bbe70119872c05107e38fbc7f96b1d8cb047d95c2c50869a46c65a8e96"}, - {file = "websockets-13.1-cp38-cp38-win32.whl", hash = "sha256:204e5107f43095012b00f1451374693267adbb832d29966a01ecc4ce1db26faf"}, - {file = "websockets-13.1-cp38-cp38-win_amd64.whl", hash = "sha256:485307243237328c022bc908b90e4457d0daa8b5cf4b3723fd3c4a8012fce4c6"}, - {file = "websockets-13.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9b37c184f8b976f0c0a231a5f3d6efe10807d41ccbe4488df8c74174805eea7d"}, - {file = "websockets-13.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:163e7277e1a0bd9fb3c8842a71661ad19c6aa7bb3d6678dc7f89b17fbcc4aeb7"}, - {file = "websockets-13.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4b889dbd1342820cc210ba44307cf75ae5f2f96226c0038094455a96e64fb07a"}, - {file = "websockets-13.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:586a356928692c1fed0eca68b4d1c2cbbd1ca2acf2ac7e7ebd3b9052582deefa"}, - {file = "websockets-13.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7bd6abf1e070a6b72bfeb71049d6ad286852e285f146682bf30d0296f5fbadfa"}, - {file = "websockets-13.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2aad13a200e5934f5a6767492fb07151e1de1d6079c003ab31e1823733ae79"}, - {file = "websockets-13.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:df01aea34b6e9e33572c35cd16bae5a47785e7d5c8cb2b54b2acdb9678315a17"}, - {file = "websockets-13.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e54affdeb21026329fb0744ad187cf812f7d3c2aa702a5edb562b325191fcab6"}, - {file = "websockets-13.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9ef8aa8bdbac47f4968a5d66462a2a0935d044bf35c0e5a8af152d58516dbeb5"}, - {file = "websockets-13.1-cp39-cp39-win32.whl", hash = "sha256:deeb929efe52bed518f6eb2ddc00cc496366a14c726005726ad62c2dd9017a3c"}, - {file = "websockets-13.1-cp39-cp39-win_amd64.whl", hash = "sha256:7c65ffa900e7cc958cd088b9a9157a8141c991f8c53d11087e6fb7277a03f81d"}, - {file = "websockets-13.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5dd6da9bec02735931fccec99d97c29f47cc61f644264eb995ad6c0c27667238"}, - {file = "websockets-13.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:2510c09d8e8df777177ee3d40cd35450dc169a81e747455cc4197e63f7e7bfe5"}, - {file = "websockets-13.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1c3cf67185543730888b20682fb186fc8d0fa6f07ccc3ef4390831ab4b388d9"}, - {file = "websockets-13.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bcc03c8b72267e97b49149e4863d57c2d77f13fae12066622dc78fe322490fe6"}, - {file = "websockets-13.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:004280a140f220c812e65f36944a9ca92d766b6cc4560be652a0a3883a79ed8a"}, - {file = "websockets-13.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e2620453c075abeb0daa949a292e19f56de518988e079c36478bacf9546ced23"}, - {file = "websockets-13.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9156c45750b37337f7b0b00e6248991a047be4aa44554c9886fe6bdd605aab3b"}, - {file = "websockets-13.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:80c421e07973a89fbdd93e6f2003c17d20b69010458d3a8e37fb47874bd67d51"}, - {file = "websockets-13.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82d0ba76371769d6a4e56f7e83bb8e81846d17a6190971e38b5de108bde9b0d7"}, - {file = "websockets-13.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e9875a0143f07d74dc5e1ded1c4581f0d9f7ab86c78994e2ed9e95050073c94d"}, - {file = "websockets-13.1-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a11e38ad8922c7961447f35c7b17bffa15de4d17c70abd07bfbe12d6faa3e027"}, - {file = "websockets-13.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:4059f790b6ae8768471cddb65d3c4fe4792b0ab48e154c9f0a04cefaabcd5978"}, - {file = "websockets-13.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:25c35bf84bf7c7369d247f0b8cfa157f989862c49104c5cf85cb5436a641d93e"}, - {file = "websockets-13.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:83f91d8a9bb404b8c2c41a707ac7f7f75b9442a0a876df295de27251a856ad09"}, - {file = "websockets-13.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a43cfdcddd07f4ca2b1afb459824dd3c6d53a51410636a2c7fc97b9a8cf4842"}, - {file = "websockets-13.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48a2ef1381632a2f0cb4efeff34efa97901c9fbc118e01951ad7cfc10601a9bb"}, - {file = "websockets-13.1-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:459bf774c754c35dbb487360b12c5727adab887f1622b8aed5755880a21c4a20"}, - {file = "websockets-13.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:95858ca14a9f6fa8413d29e0a585b31b278388aa775b8a81fa24830123874678"}, - {file = "websockets-13.1-py3-none-any.whl", hash = "sha256:a9a396a6ad26130cdae92ae10c36af09d9bfe6cafe69670fd3b6da9b07b4044f"}, - {file = "websockets-13.1.tar.gz", hash = "sha256:a3b3366087c1bc0a2795111edcadddb8b3b59509d5db5d7ea3fdd69f954a8878"}, -] - -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "yapf" version = "0.40.2" @@ -2372,11 +1277,6 @@ importlib-metadata = ">=6.6.0" platformdirs = ">=3.5.1" tomli = ">=2.0.1" -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [[package]] name = "zipp" version = "3.20.2" @@ -2396,12 +1296,7 @@ enabler = ["pytest-enabler (>=2.2)"] test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] type = ["pytest-mypy"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "meteoswiss" - [metadata] lock-version = "2.0" python-versions = "~3.12" -content-hash = "7cab465d8cd7c20274b71d8f2d3be736a023e4b1cd17f7b0445c6900b40f895b" +content-hash = "39657942890c54bb8f806848949acec6751da01c95d80a8720eb9c9de5d3d294" diff --git a/pyproject.toml b/pyproject.toml index 3335b8b..c9d401b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,3 @@ -[[tool.poetry.source]] -name = "meteoswiss" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" - [tool.poetry] name = "flex-container-orchestrator" version = "1.0" @@ -13,8 +9,10 @@ include = ["HISTORY.rst"] [tool.poetry.dependencies] python = "~3.12" -mchpy = { extras = ["fastapi"], version = "^2.1.1" } boto3 = "^1.35.40" +pydantic = "^2.9.2" +pydantic-settings = "^2.5.2" +python-json-logger = "^2.0.7" [tool.poetry.group.dev.dependencies] mypy = "*" @@ -54,11 +52,6 @@ min-public-methods = 0 ignore_missing_imports = true disallow_untyped_defs = true -[[tool.mypy.overrides]] -# to avoid mypy errors when importing modules from commons -# aka "module is installed, but missing library stubs or py.typed marker" -module = ["mchpy.*"] - [build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" From 9e8fe833e9218cb0d2b59d8f9267fed8c9812a6d Mon Sep 17 00:00:00 2001 From: ninaburg Date: Thu, 17 Oct 2024 13:27:01 +0000 Subject: [PATCH 15/37] fix container cleaned up after stopped --- flex_container_orchestrator/services/flexpart_service.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flex_container_orchestrator/services/flexpart_service.py b/flex_container_orchestrator/services/flexpart_service.py index dc9f462..6de60f3 100755 --- a/flex_container_orchestrator/services/flexpart_service.py +++ b/flex_container_orchestrator/services/flexpart_service.py @@ -41,7 +41,7 @@ def launch_containers(date, location, time, step): logging.error(f"Error logging in to Docker: {e}") sys.exit(1) - # Run pre-processing for Flexpart + # ====== First part: Run pre-processing for Flexpart ====== docker_image = "493666016161.dkr.ecr.eu-central-2.amazonaws.com/numericalweatherpredictions/dispersionmodelling/flexpart-ifs/flexprep:2410.d02afb52a3021ad13aa734fc400e84026347ce7b" db_mount = os.path.expanduser('~/.sqlite/') @@ -51,7 +51,7 @@ def launch_containers(date, location, time, step): try: docker_run_command = [ - "docker", "run", + "docker", "run", "--rm", "--mount", f"type=bind,source={db_mount},destination=/src/db/", "--env-file", "flex_container_orchestrator/config/.env", docker_image, From f767a29da3bccee4089b4ea6e9c84dab1414aa35 Mon Sep 17 00:00:00 2001 From: ninaburg Date: Thu, 17 Oct 2024 16:00:38 +0000 Subject: [PATCH 16/37] fix env var --- flex_container_orchestrator/config/.env | 8 +- .../services/flexpart_service.py | 3 +- poetry.lock | 130 +++++++++++++----- pyproject.toml | 1 + 4 files changed, 101 insertions(+), 41 deletions(-) diff --git a/flex_container_orchestrator/config/.env b/flex_container_orchestrator/config/.env index 4c37310..6ec6984 100644 --- a/flex_container_orchestrator/config/.env +++ b/flex_container_orchestrator/config/.env @@ -17,11 +17,11 @@ SVC__MAIN__TIME_SETTINGS__TSTART=0 # Input/output S3 bucket settings # Inherit from Flexprep output -SVC__MAIN__S3_BUCKETS__INPUT__ENDPOINT_URL=https://object-store.os-api.cci1.ecmwf.int +SVC__MAIN__FLEXPART_S3_BUCKETS__INPUT__ENDPOINT_URL=https://object-store.os-api.cci1.ecmwf.int # Inherit from Flexprep output -SVC__MAIN__S3_BUCKETS__INPUT__NAME=flexprep-output -SVC__MAIN__S3_BUCKETS__OUTPUT__ENDPOINT_URL=https://object-store.os-api.cci1.ecmwf.int -SVC__MAIN__S3_BUCKETS__OUTPUT__NAME=flexpart-output +SVC__MAIN__FLEXPART_S3_BUCKETS__INPUT__NAME=flexprep-output +SVC__MAIN__FLEXPART_S3_BUCKETS__OUTPUT__ENDPOINT_URL=https://object-store.os-api.cci1.ecmwf.int +SVC__MAIN__FLEXPART_S3_BUCKETS__OUTPUT__NAME=flexpart-output # EWC buckets access and secret keys S3_ACCESS_KEY= diff --git a/flex_container_orchestrator/services/flexpart_service.py b/flex_container_orchestrator/services/flexpart_service.py index 6de60f3..020d775 100755 --- a/flex_container_orchestrator/services/flexpart_service.py +++ b/flex_container_orchestrator/services/flexpart_service.py @@ -44,6 +44,7 @@ def launch_containers(date, location, time, step): # ====== First part: Run pre-processing for Flexpart ====== docker_image = "493666016161.dkr.ecr.eu-central-2.amazonaws.com/numericalweatherpredictions/dispersionmodelling/flexpart-ifs/flexprep:2410.d02afb52a3021ad13aa734fc400e84026347ce7b" db_mount = os.path.expanduser('~/.sqlite/') + env_file_path = os.path.expanduser('~/flex-container-orchestrator/flex_container_orchestrator/config/.env') if not os.path.exists(db_mount): logging.error(f"SQLite database directory {db_mount} does not exist.") @@ -53,7 +54,7 @@ def launch_containers(date, location, time, step): docker_run_command = [ "docker", "run", "--rm", "--mount", f"type=bind,source={db_mount},destination=/src/db/", - "--env-file", "flex_container_orchestrator/config/.env", + "--env-file", env_file_path, docker_image, "--step", step, "--date", date, diff --git a/poetry.lock b/poetry.lock index 184fe50..a6012f7 100644 --- a/poetry.lock +++ b/poetry.lock @@ -20,13 +20,13 @@ tests = ["hypothesis", "pytest"] [[package]] name = "alabaster" -version = "1.0.0" +version = "0.7.16" description = "A light, configurable Sphinx theme" optional = false -python-versions = ">=3.10" +python-versions = ">=3.9" files = [ - {file = "alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b"}, - {file = "alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e"}, + {file = "alabaster-0.7.16-py3-none-any.whl", hash = "sha256:b46733c07dce03ae4e150330b975c75737fa60f0a7c591b6c8bf4928a28e2c92"}, + {file = "alabaster-0.7.16.tar.gz", hash = "sha256:75a8b99c28a5dad50dd7f8ccdd447a121ddb3892da9e53d1ca5cca3106d58d65"}, ] [[package]] @@ -40,6 +40,17 @@ files = [ {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, ] +[[package]] +name = "anyascii" +version = "0.3.2" +description = "Unicode to ASCII transliteration" +optional = false +python-versions = ">=3.3" +files = [ + {file = "anyascii-0.3.2-py3-none-any.whl", hash = "sha256:3b3beef6fc43d9036d3b0529050b0c48bfad8bc960e9e562d7223cfb94fe45d4"}, + {file = "anyascii-0.3.2.tar.gz", hash = "sha256:9d5d32ef844fe225b8bc7cba7f950534fae4da27a9bf3a6bea2cb0ea46ce4730"}, +] + [[package]] name = "astroid" version = "3.3.5" @@ -51,6 +62,25 @@ files = [ {file = "astroid-3.3.5.tar.gz", hash = "sha256:5cfc40ae9f68311075d27ef68a4841bdc5cc7f6cf86671b49f00607d30188e2d"}, ] +[[package]] +name = "awscli" +version = "1.35.8" +description = "Universal Command Line Environment for AWS." +optional = false +python-versions = ">=3.8" +files = [ + {file = "awscli-1.35.8-py3-none-any.whl", hash = "sha256:20fd63d2acdfa2349bc6fcc63fad2da719a473e65d138b88f6835aee630a5bfa"}, + {file = "awscli-1.35.8.tar.gz", hash = "sha256:54b4e0fb53b9b5b5f921b4cf4d491d665f9ce650b08be3feec6c0b049328ea40"}, +] + +[package.dependencies] +botocore = "1.35.42" +colorama = ">=0.2.5,<0.4.7" +docutils = ">=0.10,<0.17" +PyYAML = ">=3.10,<6.1" +rsa = ">=3.1.2,<4.8" +s3transfer = ">=0.10.0,<0.11.0" + [[package]] name = "babel" version = "2.16.0" @@ -351,13 +381,13 @@ profile = ["gprof2dot (>=2022.7.29)"] [[package]] name = "docutils" -version = "0.21.2" +version = "0.16" description = "Docutils -- Python Documentation Utilities" optional = false -python-versions = ">=3.9" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ - {file = "docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2"}, - {file = "docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f"}, + {file = "docutils-0.16-py2.py3-none-any.whl", hash = "sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af"}, + {file = "docutils-0.16.tar.gz", hash = "sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc"}, ] [[package]] @@ -646,6 +676,17 @@ files = [ dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] +[[package]] +name = "pyasn1" +version = "0.6.1" +description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629"}, + {file = "pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034"}, +] + [[package]] name = "pydantic" version = "2.9.2" @@ -1013,6 +1054,20 @@ urllib3 = ">=1.21.1,<3" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] +[[package]] +name = "rsa" +version = "4.7.2" +description = "Pure-Python RSA implementation" +optional = false +python-versions = ">=3.5, <4" +files = [ + {file = "rsa-4.7.2-py3-none-any.whl", hash = "sha256:78f9a9bf4e7be0c5ded4583326e7461e3a3c5aae24073648b4bdfa797d78c9d2"}, + {file = "rsa-4.7.2.tar.gz", hash = "sha256:9d689e6ca1b3038bc82bf8d23e944b6b6037bc02301a574935b2dd946e0353b9"}, +] + +[package.dependencies] +pyasn1 = ">=0.1.3" + [[package]] name = "s3transfer" version = "0.10.3" @@ -1065,57 +1120,60 @@ files = [ [[package]] name = "sphinx" -version = "8.1.3" +version = "5.3.0" description = "Python documentation generator" optional = false -python-versions = ">=3.10" +python-versions = ">=3.6" files = [ - {file = "sphinx-8.1.3-py3-none-any.whl", hash = "sha256:09719015511837b76bf6e03e42eb7595ac8c2e41eeb9c29c5b755c6b677992a2"}, - {file = "sphinx-8.1.3.tar.gz", hash = "sha256:43c1911eecb0d3e161ad78611bc905d1ad0e523e4ddc202a58a821773dc4c927"}, + {file = "Sphinx-5.3.0.tar.gz", hash = "sha256:51026de0a9ff9fc13c05d74913ad66047e104f56a129ff73e174eb5c3ee794b5"}, + {file = "sphinx-5.3.0-py3-none-any.whl", hash = "sha256:060ca5c9f7ba57a08a1219e547b269fadf125ae25b06b9fa7f66768efb652d6d"}, ] [package.dependencies] -alabaster = ">=0.7.14" -babel = ">=2.13" -colorama = {version = ">=0.4.6", markers = "sys_platform == \"win32\""} -docutils = ">=0.20,<0.22" +alabaster = ">=0.7,<0.8" +babel = ">=2.9" +colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} +docutils = ">=0.14,<0.20" imagesize = ">=1.3" -Jinja2 = ">=3.1" -packaging = ">=23.0" -Pygments = ">=2.17" -requests = ">=2.30.0" -snowballstemmer = ">=2.2" -sphinxcontrib-applehelp = ">=1.0.7" -sphinxcontrib-devhelp = ">=1.0.6" -sphinxcontrib-htmlhelp = ">=2.0.6" -sphinxcontrib-jsmath = ">=1.0.1" -sphinxcontrib-qthelp = ">=1.0.6" -sphinxcontrib-serializinghtml = ">=1.1.9" +Jinja2 = ">=3.0" +packaging = ">=21.0" +Pygments = ">=2.12" +requests = ">=2.5.0" +snowballstemmer = ">=2.0" +sphinxcontrib-applehelp = "*" +sphinxcontrib-devhelp = "*" +sphinxcontrib-htmlhelp = ">=2.0.0" +sphinxcontrib-jsmath = "*" +sphinxcontrib-qthelp = "*" +sphinxcontrib-serializinghtml = ">=1.1.5" [package.extras] docs = ["sphinxcontrib-websupport"] -lint = ["flake8 (>=6.0)", "mypy (==1.11.1)", "pyright (==1.1.384)", "pytest (>=6.0)", "ruff (==0.6.9)", "sphinx-lint (>=0.9)", "tomli (>=2)", "types-Pillow (==10.2.0.20240822)", "types-Pygments (==2.18.0.20240506)", "types-colorama (==0.4.15.20240311)", "types-defusedxml (==0.7.0.20240218)", "types-docutils (==0.21.0.20241005)", "types-requests (==2.32.0.20240914)", "types-urllib3 (==1.26.25.14)"] -test = ["cython (>=3.0)", "defusedxml (>=0.7.1)", "pytest (>=8.0)", "setuptools (>=70.0)", "typing_extensions (>=4.9)"] +lint = ["docutils-stubs", "flake8 (>=3.5.0)", "flake8-bugbear", "flake8-comprehensions", "flake8-simplify", "isort", "mypy (>=0.981)", "sphinx-lint", "types-requests", "types-typed-ast"] +test = ["cython", "html5lib", "pytest (>=4.6)", "typed_ast"] [[package]] name = "sphinx-autoapi" -version = "3.3.2" +version = "2.1.1" description = "Sphinx API documentation generator" optional = false -python-versions = ">=3.8" +python-versions = ">=3.7" files = [ - {file = "sphinx_autoapi-3.3.2-py2.py3-none-any.whl", hash = "sha256:08afa656f7fcd45fe7dd64bf9f44698ddb8ca7c2d5cd0614c7455912ed580324"}, - {file = "sphinx_autoapi-3.3.2.tar.gz", hash = "sha256:ebf8b44b2ebab5c28f0263ec6c2f8acdd156e9b2d539a58eca39d2f368445173"}, + {file = "sphinx-autoapi-2.1.1.tar.gz", hash = "sha256:fbadb96e79020d6b0ec45d888517bf816d6b587a2d340fbe1ec31135e300a6c8"}, + {file = "sphinx_autoapi-2.1.1-py2.py3-none-any.whl", hash = "sha256:d8da890477bd18e3327cafdead9d5a44a7d798476c6fa32492100e288250a5a3"}, ] [package.dependencies] -astroid = {version = ">=3.0.0a1", markers = "python_version >= \"3.12\""} +anyascii = "*" +astroid = ">=2.7" Jinja2 = "*" PyYAML = "*" -sphinx = ">=6.1.0" +sphinx = ">=5.2.0" [package.extras] docs = ["furo", "sphinx", "sphinx-design"] +dotnet = ["sphinxcontrib-dotnetdomain"] +go = ["sphinxcontrib-golangdomain"] [[package]] name = "sphinxcontrib-applehelp" @@ -1299,4 +1357,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = "~3.12" -content-hash = "39657942890c54bb8f806848949acec6751da01c95d80a8720eb9c9de5d3d294" +content-hash = "5f334a878dfdceecb0b27402ce1270aff5959eaff06f29db7a7b784ddf6f4129" diff --git a/pyproject.toml b/pyproject.toml index c9d401b..1b3a01d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,6 +13,7 @@ boto3 = "^1.35.40" pydantic = "^2.9.2" pydantic-settings = "^2.5.2" python-json-logger = "^2.0.7" +awscli = "^1.35.8" [tool.poetry.group.dev.dependencies] mypy = "*" From c1151e2402bff0905147d2e5cdaa7dd826b74f01 Mon Sep 17 00:00:00 2001 From: ninaburg Date: Wed, 23 Oct 2024 15:29:19 +0000 Subject: [PATCH 17/37] unittests --- tests/domain/test_aggregator_flexpart.py | 108 +++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 tests/domain/test_aggregator_flexpart.py diff --git a/tests/domain/test_aggregator_flexpart.py b/tests/domain/test_aggregator_flexpart.py new file mode 100644 index 0000000..d407df2 --- /dev/null +++ b/tests/domain/test_aggregator_flexpart.py @@ -0,0 +1,108 @@ +import datetime +import json +import sqlite3 +from unittest.mock import MagicMock, patch + +import pytest + +from flex_container_orchestrator.domain.aggregator_flexpart import ( + convert_time_to_frt, define_config, fetch_processed_items, + generate_flexpart_start_times, is_row_processed) + + +@pytest.mark.parametrize( + "fetchone_return, expected_result", + [ + ((1,), True), # Case where row is processed + ((0,), False), # Case where row is not processed + ], +) +def test_is_row_processed(fetchone_return, expected_result): + mock_conn = MagicMock() + mock_cursor = MagicMock() + + mock_conn.cursor.return_value = mock_cursor + + mock_cursor.execute.return_value = ( + mock_cursor # execute returns the cursor itself + ) + mock_cursor.fetchone.return_value = ( + fetchone_return # fetchone returns the tuple for testing + ) + + result = is_row_processed(mock_conn, "202310220600", "12") + + assert result == expected_result + + +@pytest.mark.parametrize( + "frt_dt, lead_time, tdelta, tfreq_f, expected", + [ + ( + datetime.datetime(2023, 10, 22, 6, 0), + 12, + 24, + 6, + [ + datetime.datetime(2023, 10, 22, 0, 0), + datetime.datetime(2023, 10, 22, 6, 0), + datetime.datetime(2023, 10, 22, 12, 0), + datetime.datetime(2023, 10, 22, 18, 0), + ], + ), + ( + datetime.datetime(2023, 10, 22, 6, 0), + 6, + 12, + 3, + [ + datetime.datetime(2023, 10, 22, 3, 0), + datetime.datetime(2023, 10, 22, 6, 0), + datetime.datetime(2023, 10, 22, 9, 0), + datetime.datetime(2023, 10, 22, 12, 0), + ], + ), + ], +) +def test_generate_flexpart_start_times( + frt_dt, lead_time, tdelta, tfreq_f, expected +): + result = generate_flexpart_start_times(frt_dt, lead_time, tdelta, tfreq_f) + assert result == expected + + +@pytest.mark.parametrize( + "time, tfreq, expected", + [ + (datetime.datetime(2023, 10, 22, 10, 0), 6, "20231022060004"), + (datetime.datetime(2023, 10, 22, 12, 0), 6, "20231022060006"), + ], +) +def test_convert_time_to_frt(time, tfreq, expected): + result = convert_time_to_frt(time, tfreq) + assert result == expected + + +@patch("sqlite3.connect") +def test_fetch_processed_items(mock_connect): + mock_conn = MagicMock() + mock_cursor = MagicMock() + mock_conn.cursor.return_value = mock_cursor + mock_cursor.fetchall.return_value = [(True, "12"), (False, "24")] + frt_s = {"202310220600"} + result = fetch_processed_items(mock_conn, frt_s) + assert result == {"20231022060012"} + + +def test_define_config(): + st = datetime.datetime(2023, 10, 22, 6, 0) + et = datetime.datetime(2023, 10, 22, 18, 0) + result = define_config(st, et) + expected_config = { + "IBDATE": "20231022", + "IBTIME": "06", + "IEDATE": "20231022", + "IETIME": "18", + } + assert result == expected_config + From d4a13d0ff68e539cea2cd8786ab8455da9b8f2e0 Mon Sep 17 00:00:00 2001 From: ninaburg Date: Wed, 23 Oct 2024 15:36:09 +0000 Subject: [PATCH 18/37] [squash commit] adopt mch blueprint and clean for quality tests --- .mch-ci.yml | 125 +- .pre-commit-config.yaml | 94 ++ Dockerfile | 47 - HISTORY.rst | 4 +- Jenkinsfile | 312 +--- README.md | 2 - doc/conf.py | 87 +- doc/index.rst | 9 +- doc/installation.rst | 24 + doc/usage.rst | 7 + flex_container_orchestrator/__init__.py | 6 +- flex_container_orchestrator/config/.env | 4 + .../config/aviso/__init__.py | 0 .../config/aviso/listener_diss.yaml | 1 - .../config/base_settings.py | 67 +- .../config/http_audit.py | 7 +- flex_container_orchestrator/config/logger.py | 166 ++- .../config/service_settings.py | 12 +- .../config/settings.yaml | 9 + .../domain/aggregator_flexpart.py | 217 ++- flex_container_orchestrator/main.py | 16 +- .../services/flexpart_service.py | 101 +- poetry.lock | 1297 +++++++++++++---- pyproject.toml | 52 +- sonar-project.properties | 8 +- test/__init__.py | 0 test/conftest.py | 1 + .../domain/test_aggregator_flexpart.py | 15 +- test/services/test_flexpart_service.py | 24 + uvicorn_logging_settings.json | 1 - 30 files changed, 1773 insertions(+), 942 deletions(-) create mode 100644 .pre-commit-config.yaml delete mode 100644 Dockerfile delete mode 100644 README.md create mode 100644 doc/installation.rst create mode 100644 doc/usage.rst create mode 100644 flex_container_orchestrator/config/aviso/__init__.py create mode 100644 test/__init__.py create mode 100644 test/conftest.py rename {tests => test}/domain/test_aggregator_flexpart.py (89%) create mode 100644 test/services/test_flexpart_service.py delete mode 100644 uvicorn_logging_settings.json diff --git a/.mch-ci.yml b/.mch-ci.yml index 75ef781..ec23222 100644 --- a/.mch-ci.yml +++ b/.mch-ci.yml @@ -1,140 +1,53 @@ -# Default configuration used by CI/CD. -# You can run locally 'mchbuild build' to create the images, -# 'mchbuild test' to run the tests using the images. The 'deploy' steps -# can be run separately. +# To test with your local Python environment, run 'mchbuild build test'. +# To use a container image, set for example '-s pythonImageName=3.11'. default: - build: - - getVersion: - gitCalculateVersion: - - imageTester: - containerBuildImage: - fullImageName: ${var.image}-tester - target: tester - extraArgs: - - --build-arg - - VERSION=${var.version} - - imageRunner: - containerBuildImage: - fullImageName: ${var.image} - target: runner - extraArgs: - - --build-arg - - VERSION=${var.version} - pullAlways: false + - install: + - pythonInstall: - docs: - - script: mkdir -p doc/_build - pythonDocs: - fullImageName: ${var.image}-tester - packageManager: '' pullAlways: false - test: - - getVersion: - gitCalculateVersion: - unit: - - script: mkdir -p test_reports - pythonTest: - fullImageName: ${var.image}-tester - packageManager: '' pullAlways: false - pythonCoverage: - fullImageName: ${var.image}-tester - packageManager: '' pullAlways: false - lint: - - script: mkdir -p test_reports - pythonLint: - fullImageName: ${var.image}-tester - packageManager: '' pullAlways: false - pythonTypeChecker: - fullImageName: ${var.image}-tester - packageManager: '' pullAlways: false - verify: - - configurationSecurityScan: + - securityScan: - securityReport: - format: [ 'html', 'table', 'print'] + format: [ 'html', 'sbom', 'table', 'print' ] severity: [ 'CRITICAL', 'HIGH' ] - target: config://k8s - prefix: k8s + target: file://poetry.lock qualityGate: threshold: 5 criticalFactor: 5 highFactor: 1 - - imageSecurityScan: - - securityReport: - format: [ 'html', 'sbom', 'table', 'print'] - severity: [ 'CRITICAL', 'HIGH' ] - removeLocalImage: false - pullAlways: false - qualityGate: - threshold: 20 - criticalFactor: 5 - highFactor: 1 - publishSbom: - securityPublishSbom: + - unitWithoutCoverage: + # Set pythonImageName to run with image of non-default Python version + pythonTest: + cacheDeps: false + withCoverage: false - deploy: - - addNextTag: - gitAddNextTag: - - addTag: - gitAddTag: - docs: openshiftPublishDocs: docSrc: doc/_build/ docType: python - - image: - containerPublishImage: - removeLocalImage: false - - cpDelete: - openshiftDelete: - - cp: - openshiftDeploy: - - cpRestart: - openshiftRestart: - - clean: - - getVersion: - gitCalculateVersion: - - images: - script: | - if test -n "${var.version}"; then - podman image rm -f $(podman image ls -q \ - -f "label=ch.meteoswiss.project=${var.project}-${var.version}") || : - fi + - pypi: + pythonPublishPackage: + setPackageVersion: true + pythonImageName: '3.12' + pullAlways: false -# Alternative configuration to install and test the service locally using -# the local Python installation. Besides 'mchbuild local.build' and -# 'mchbuild local.test' it is possible to run the job using -# 'mchbuild local.run'. -local: - - build: - - install: - pythonInstall: - - docs: - pythonDocs: - - format: - pythonFormat: - inPlace: true - - test: - - unit: - - script: mkdir -p test_reports - - pythonTest: - - pythonCoverage: - - lint: - - script: mkdir -p test_reports - - pythonLint: - - pythonTypeChecker: - - run: - - main: - - pythonRun: - commands: | - echo 'Try it out at http://localhost:8080/flex-container-orchestrator/swagger-ui.html' - echo 'Shut it down with Ctrl-C' - poetry run uvicorn --port 8080 --reload flex_container_orchestrator.main:app variables: - project: flex-container-orchestrator - solution: dispersionmodelling + project: 'flex-container-orchestrator' ocpHostNameForEnv: - devt: api.cpnonprod.meteoswiss.ch:6443 - depl: api.cpnonprod.meteoswiss.ch:6443 - prod: api.cp.meteoswiss.ch:6443 \ No newline at end of file + prod: api.cp.meteoswiss.ch:6443 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..7a706bf --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,94 @@ +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.4.0 + hooks: + - id: check-ast + - id: check-case-conflict + - id: check-docstring-first + - id: check-symlinks + - id: check-toml + - id: check-yaml + - id: debug-statements + - id: end-of-file-fixer + - id: trailing-whitespace +- repo: local + hooks: + - id: forbidden-files-git-orig + name: find git merge backup files + language: fail + entry: "Found git merge backup file(s): review and remove them" + files: "\\.orig$" +- repo: local + hooks: + - id: rstcheck + name: rstcheck + description: Check REST files for correctness + language: system + entry: rstcheck + types: [rst] +- repo: local + hooks: + - id: codespell + name: codespell + description: Check for spelling errors + language: system + entry: codespell +- repo: local + hooks: + - id: black + name: black + description: Format Python code + language: system + entry: black + types_or: [python, pyi] +- repo: local + hooks: + - id: isort + name: isort + description: Group and sort Python imports + language: system + entry: isort + types_or: [python, pyi, cython] +- repo: local + hooks: + - id: pydocstyle + name: pydocstyle + description: Check docstrings in Python code for compliance with conventions + language: system + entry: pydocstyle + types: [python] + files: ^src/ +# SR It would be handy to abort if one linter fails b/c the same error often +# SR triggers multiple linters. That way, if flake8 fails, pylint and mypy +# SR (which in large code bases can take some time to run) could be skipped +# SR before fixing the error. Unfortunately, pre-commit only provides the global +# SR option fail_fast, which would abort even after the small fixers and +# SR formatters changed something. A potential solution could be to write a +# SR small bash script run-linters.sh that runs flake8, pylint and run-mypy.sh +# SR in that order and aborts on error. +# TODO: There is significant work involved in getting pylint going. This can be its separate task +# - repo: local +# hooks: +# - id: pylint +# name: pylint +# description: Check Python code for correctness, consistency and adherence to best practices +# language: system +# entry: pylint +# types: [python] +- repo: local + hooks: + - id: flake8 + name: flake8 + description: Check Python code for correctness, consistency and adherence to best practices + language: system + entry: flake8 + types: [python] +- repo: local + hooks: + - id: poetry-check + name: poetry-check + description: run poetry check to validate config + entry: poetry check + language: python + pass_filenames: false + files: ^(.*/)?(poetry\.lock|pyproject\.toml)$ diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 2684bff..0000000 --- a/Dockerfile +++ /dev/null @@ -1,47 +0,0 @@ -FROM dockerhub.apps.cp.meteoswiss.ch/mch/python/builder AS builder -ARG VERSION -LABEL ch.meteoswiss.project=flex-container-orchestrator-${VERSION} - -COPY poetry.lock pyproject.toml /src/app-root/ - -WORKDIR /src/app-root - -RUN poetry export -o requirements.txt --without-hashes \ - && poetry export --with dev -o requirements_dev.txt --without-hashes - - -FROM dockerhub.apps.cp.meteoswiss.ch/mch/python-3.12:latest-slim AS base -ARG VERSION -LABEL ch.meteoswiss.project=flex-container-orchestrator-${VERSION} - -COPY --from=builder /src/app-root/requirements.txt /src/app-root/requirements.txt - -WORKDIR /src/app-root - -RUN pip install -r requirements.txt --no-cache-dir --no-deps --root-user-action=ignore - -COPY uvicorn_logging_settings.json /src/app-root/uvicorn_logging_settings.json - -COPY flex_container_orchestrator /src/app-root/flex_container_orchestrator - -FROM base AS tester -ARG VERSION -LABEL ch.meteoswiss.project=flex-container-orchestrator-${VERSION} - -COPY --from=builder /src/app-root/requirements_dev.txt /src/app-root/requirements_dev.txt -RUN pip install -r /src/app-root/requirements_dev.txt --no-cache-dir --no-deps --root-user-action=ignore - -COPY pyproject.toml /src/app-root/ -COPY test /src/app-root/test - -FROM base AS runner -ARG VERSION -LABEL ch.meteoswiss.project=flex-container-orchestrator-${VERSION} - -ENV VERSION=$VERSION - -# For running outside of OpenShift, we want to make sure that the container is run without root privileges -# uid 1001 is defined in the base-container-images for this purpose -USER 1001 - -CMD ["uvicorn", "--host", "0.0.0.0", "--port", "8080", "flex_container_orchestrator.main:app", "--log-config", "uvicorn_logging_settings.json"] diff --git a/HISTORY.rst b/HISTORY.rst index a866ce2..584f226 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -2,7 +2,7 @@ History ======= -1.0.0 (2024-10-14) +0.1.0 (2024-10-25) ------------------ -* First release +* First release on PyPI. diff --git a/Jenkinsfile b/Jenkinsfile index 3f24ad9..51e7c23 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,140 +1,71 @@ class Globals { - // sets the pipeline to execute all steps related to building the service - static boolean build = false + // the library version + static String version = 'latest' - // sets to abort the pipeline if the Sonarqube QualityGate fails - static boolean qualityGateAbortPipeline = false - - // sets the pipeline to execute all steps related to releasing the service - static boolean release = false - - // sets the pipeline to execute all steps related to deployment of the service - static boolean deploy = false - - // sets the pipeline to execute all steps related to delete the service from the container platform - static boolean deleteContainer = false - - // sets the pipeline to execute all steps related to trigger the security scan - static boolean runSecurityScan = false - - // the project name in container platform - static String ocpProject = '' - - // Container deployment environment - static String deployEnv = '' - - // the image tag used for tagging the image - static String imageTag = '' - - // the service version - static String version = '' - - // sets the pipeline to execute all steps related to restart the service - static boolean restart = false + // the tag used when publishing documentation + static String documentationTag = 'latest' } - pipeline { - agent { label 'podman' } + agent {label 'podman'} parameters { - choice(choices: ['Build', 'Deploy', 'Release', 'Restart', 'Delete', 'Security-Scan'], - description: 'Build type', - name: 'buildChoice') + booleanParam(name: 'RELEASE_BUILD', defaultValue: false, description: 'Creates and publishes a new release') + booleanParam(name: 'PUBLISH_DOCUMENTATION', defaultValue: false, description: 'Publishes the generated documentation') + } - choice(choices: ['devt', 'depl', 'prod'], - description: 'Environment', - name: 'environment') + environment { + SCANNER_HOME = tool name: 'Sonarqube-certs-PROD', type: 'hudson.plugins.sonar.SonarRunnerInstallation' - booleanParam(name: 'PUBLISH_DOCUMENTATION', defaultValue: false, description: 'Publishes the generated documentation') + PATH = "$workspace/.venv-mchbuild/bin:$HOME/tools/openshift-client-tools:$HOME/tools/trivy:$PATH" + KUBECONFIG = "$workspace/.kube/config" + HTTP_PROXY = 'http://proxy.meteoswiss.ch:8080' + HTTPS_PROXY = 'http://proxy.meteoswiss.ch:8080' + NO_PROXY = '.meteoswiss.ch,localhost' } options { + gitLabConnection('CollabGitLab') + // New jobs should wait until older jobs are finished disableConcurrentBuilds() // Discard old builds buildDiscarder(logRotator(artifactDaysToKeepStr: '7', artifactNumToKeepStr: '1', daysToKeepStr: '45', numToKeepStr: '10')) // Timeout the pipeline build after 1 hour timeout(time: 1, unit: 'HOURS') - gitLabConnection('CollabGitLab') - } - - environment { - PATH = "$workspace/.venv-mchbuild/bin:$HOME/tools/openshift-client-tools:$HOME/tools/trivy:$PATH" - KUBECONFIG = "$workspace/.kube/config" - HTTP_PROXY = 'http://proxy.meteoswiss.ch:8080' - HTTPS_PROXY = 'http://proxy.meteoswiss.ch:8080' - SCANNER_HOME = tool name: 'Sonarqube-certs-PROD', type: 'hudson.plugins.sonar.SonarRunnerInstallation' } stages { - stage('Preflight') { + stage('Init') { steps { + sh ''' + python -m venv .venv-mchbuild + PIP_INDEX_URL=https://hub.meteoswiss.ch/nexus/repository/python-all/simple \ + .venv-mchbuild/bin/pip install --upgrade mchbuild + ''' updateGitlabCommitStatus name: 'Build', state: 'running' + } + } - script { - echo '---- INSTALL MCHBUILD ----' - sh ''' - python -m venv .venv-mchbuild - PIP_INDEX_URL=https://hub.meteoswiss.ch/nexus/repository/python-all/simple \ - .venv-mchbuild/bin/pip install --upgrade mchbuild - ''' - echo '---- INITIALIZE PARAMETERS ----' - Globals.deployEnv = params.environment - Globals.ocpProject = Globals.deployEnv - ? sh(script: "mchbuild openshiftExposeProperties -s deploymentEnvironment=${Globals.deployEnv} -g ocpProject", - returnStdout: true) : '' - // Determine the type of build - switch (params.buildChoice) { - case 'Build': - Globals.build = true - break - case 'Deploy': - Globals.deploy = true - break - case 'Release': - Globals.release = true - Globals.build = true - break - case 'Delete': - Globals.deleteContainer = true - break - case 'Security-Scan': - Globals.runSecurityScan = true - break - case 'Restart': - Globals.restart = true - break + stage('Test') { + parallel { + stage('python 3.10') { + steps { + sh 'mchbuild -s pythonImageName=\'"3.10"\' -s testReportName=junit-3.10.xml verify.unitWithoutCoverage' } - - if (Globals.release) { - echo '---- TAGGING RELEASE ----' - sh 'mchbuild deploy.addNextTag' + } + stage('python 3.11') { + steps { + sh 'mchbuild -s pythonImageName=\'"3.11"\' -s testReportName=junit-3.11.xml verify.unitWithoutCoverage' } - - if (Globals.build || Globals.deploy || Globals.runSecurityScan) { - def versionAndTag = sh( - script: 'mchbuild -g version -g image build.getVersion', - returnStdout: true - ).split('\n') - Globals.version = versionAndTag[0] - Globals.imageTag = versionAndTag[1] - echo "Using version ${Globals.version} and image tag ${Globals.imageTag}" + } + stage('python 3.12') { + steps { + sh 'mchbuild -s pythonImageName=\'"3.12"\' build test' + recordIssues(qualityGates: [[threshold: 10, type: 'TOTAL', unstable: false]], tools: [myPy(pattern: 'test_reports/mypy.log')]) } } } - } - - - stage('Build') { - when { expression { Globals.build } } - steps { - echo '---- BUILD IMAGE ----' - sh """ - mchbuild -s version=${Globals.version} -s image=${Globals.imageTag} \ - build.imageTester test.unit - """ - } post { always { junit keepLongStdio: true, testResults: 'test_reports/junit*.xml' @@ -142,23 +73,10 @@ pipeline { } } - stage('Scan') { - when { expression { Globals.build } } steps { - echo '---- LINT & TYPE CHECK ----' - sh "mchbuild -s image=${Globals.imageTag} test.lint" - script { - try { - recordIssues(qualityGates: [[threshold: 10, type: 'TOTAL', unstable: false]], tools: [myPy(pattern: 'test_reports/mypy.log')]) - } - catch (err) { - error "Too many mypy issues, exiting now..." - } - } - - echo("---- MISCONFIGURATIONS CHECK ----") - sh "mchbuild verify.configurationSecurityScan" + echo("---- DEPENDENCIES SECURITY SCAN ----") + sh "mchbuild verify.securityScan" echo("---- SONARQUBE ANALYSIS ----") withSonarQubeEnv("Sonarqube-PROD") { @@ -173,164 +91,54 @@ pipeline { timeout(time: 1, unit: 'HOURS') { // Parameter indicates whether to set pipeline to UNSTABLE if Quality Gate fails // true = set pipeline to UNSTABLE, false = don't - waitForQualityGate abortPipeline: Globals.qualityGateAbortPipeline - } - } - } - - - - - stage('Create Artifacts') { - when { expression { Globals.build || Globals.deploy || params.PUBLISH_DOCUMENTATION } } - steps { - script { - if (Globals.build || Globals.deploy) { - echo '---- CREATE IMAGE ----' - sh """ - mchbuild -s version=${Globals.version} -s image=${Globals.imageTag} \ - build.imageRunner - """ - } - if (params.PUBLISH_DOCUMENTATION) { - echo '---- CREATE DOCUMENTATION ----' - sh """ - mchbuild -s version=${Globals.version} -s image=${Globals.imageTag} \ - build.docs - """ - } - } - } - } - - stage('Publish Artifacts') { - when { expression { Globals.deploy || Globals.release || params.PUBLISH_DOCUMENTATION } } - environment { - REGISTRY_AUTH_FILE = "$workspace/.containers/auth.json" - } - steps { - script { - if (Globals.deploy || Globals.release) { - echo "---- PUBLISH IMAGE ----" - withCredentials([usernamePassword(credentialsId: 'openshift-nexus', - passwordVariable: 'NXPASS', - usernameVariable: 'NXUSER')]) { - sh "mchbuild deploy.image -s fullImageName=${Globals.imageTag}" - } - } - } - script { - if (params.PUBLISH_DOCUMENTATION) { - echo "---- PUBLISH DOCUMENTATION ----" - withCredentials([string(credentialsId: 'documentation-main-prod-token', - variable: 'DOC_TOKEN')]) { - sh """ - mchbuild deploy.docs -s deploymentEnvironment=prod \ - -s docVersion=${Globals.version} - """ - } - } - } - } - } - - stage('Image Security Scan') { - when { - expression { Globals.runSecurityScan} - } - steps { - script { - echo '---- RUN SECURITY SCAN ----' - sh "mchbuild verify.imageSecurityScan -s deploymentEnvironment=${Globals.deployEnv}" + waitForQualityGate abortPipeline: true } } } - stage('Deploy') { - when { expression { Globals.deploy } } - environment { - REGISTRY_AUTH_FILE = "$workspace/.containers/auth.json" - } + stage('Release') { + when { expression { params.RELEASE_BUILD } } steps { + echo 'Build a wheel and publish' script { - // manual confirmation step for production deployment - if (Globals.deployEnv.contains('prod')) { - input message: 'Are you sure you want to deploy to PROD?', ok: 'Deploy' + withCredentials([string(credentialsId: 'python-mch-nexus-secret', variable: 'PYPIPASS')]) { + sh "PYPIUSER=python-mch mchbuild deploy.pypi" + Globals.version = sh(script: 'git describe --tags --abbrev=0', returnStdout: true).trim() + Globals.documentationTag = Globals.version + env.TAG_NAME = Globals.documentationTag } - // we tag the current commit as the one deployed to the target environment - sh "mchbuild -s gitTag=${Globals.deployEnv} deploy.addTag" - - withCredentials([usernamePassword(credentialsId: 'openshift-nexus', - passwordVariable: 'NXPASS', - usernameVariable: 'NXUSER')]) { - echo 'Push to image registry' - sh """ - mchbuild deploy.image -s deploymentEnvironment=${Globals.deployEnv} \ - -s imageToTag=${Globals.imageTag} - """ - } - - withCredentials([usernamePassword(credentialsId: 'openshift-nexus', - passwordVariable: 'NXPASS', - usernameVariable: 'NXUSER'), - string(credentialsId: "${Globals.ocpProject}-token", - variable: 'OCP_TOKEN')]) { - sh "mchbuild deploy.cp -s deploymentEnvironment=${Globals.deployEnv}" - } - - // The security report is uploaded once the image has been deployed + echo("---- PUBLISH DEPENDENCIES TO DEPENDENCY REGISTRY ----") withCredentials([string(credentialsId: 'dependency-track-token-prod', variable: 'DTRACK_TOKEN')]) { catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') { - echo 'Upload the security report to the dependency track' - sh """ - mchbuild verify.imageSecurityScan \ - verify.publishSbom -s deploymentEnvironment=${Globals.deployEnv} - """ + sh "mchbuild verify.publishSbom -s version=${Globals.version}" } } } - } - } - - - stage('Restart Deployment') { - when { expression { Globals.restart } } - steps { - withCredentials([string(credentialsId: "${Globals.ocpProject}-token", - variable: 'OCP_TOKEN')]) { - sh "mchbuild deploy.cpRestart -s deploymentEnvironment=${Globals.deployEnv}" - } } } - - stage('Delete Deployment') { - when { expression { Globals.deleteContainer } } + stage('Publish Documentation') { + when { expression { params.PUBLISH_DOCUMENTATION } } steps { - withCredentials([string(credentialsId: "${Globals.ocpProject}-token", - variable: 'OCP_TOKEN')]) { - sh "mchbuild deploy.cpDelete -s deploymentEnvironment=${Globals.deployEnv}" + withCredentials([string(credentialsId: 'documentation-main-prod-token', + variable: 'DOC_TOKEN')]) { + sh """ + mchbuild -s pythonImageName=3.12 -s deploymentEnvironment=prod \ + -s docVersion=${Globals.documentationTag} deploy.docs + """ } } } } - post { - cleanup { - sh """ - mchbuild -s image=${Globals.imageTag} \ - -s deploymentEnvironment=${Globals.deployEnv} clean - """ - } aborted { updateGitlabCommitStatus name: 'Build', state: 'canceled' } failure { updateGitlabCommitStatus name: 'Build', state: 'failed' echo 'Sending email' - sh 'df -h' emailext(subject: "${currentBuild.fullDisplayName}: ${currentBuild.currentResult}", attachLog: true, attachmentsPattern: 'generatedFile.txt', diff --git a/README.md b/README.md deleted file mode 100644 index 7c9b84a..0000000 --- a/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# flex-container-orchestrator -The flex-container-orchestrator manages Aviso notifications and triggers the flexprep and flexpart containers, as well as the file aggregation script for Flexpart. diff --git a/doc/conf.py b/doc/conf.py index b43c867..3f19620 100755 --- a/doc/conf.py +++ b/doc/conf.py @@ -20,7 +20,9 @@ # import os import sys -sys.path.insert(0, os.path.abspath('..')) +from importlib.metadata import version + +sys.path.insert(0, os.path.abspath("..")) # -- General configuration --------------------------------------------- @@ -30,32 +32,36 @@ # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode', 'sphinx.ext.napoleon', 'autoapi.extension'] +extensions = [ + "sphinx.ext.autodoc", + "sphinx.ext.viewcode", + "sphinx.ext.napoleon", + "sphinxcontrib.autodoc_pydantic", +] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] -source_suffix = '.rst' +source_suffix = ".rst" # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. -project = u'flex-container-orchestrator' -copyright = u"2024, MeteoSwiss" -author = u"Nina Burgdorfer" +project = "flex-container-orchestrator" +copyright = "2024, MeteoSwiss" +author = "Nina Burgdorfer" # The version info for the project you're documenting, acts as replacement # for |version| and |release|, also used in various other places throughout # the built documents. # - -# The version is fetched from the environment variable VERSION -version = release = os.getenv("VERSION", default="") +# The short X.Y version. +version = version("flex-container-orchestrator") # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -67,10 +73,10 @@ # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False @@ -80,7 +86,7 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = 'pydata_sphinx_theme' +html_theme = "pydata_sphinx_theme" # Theme options are theme-specific and customize the look and feel of a # theme further. For a list of options available for each theme, see the @@ -91,12 +97,12 @@ # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path = ["_static"] # -- Options for HTMLHelp output --------------------------------------- # Output file base name for HTML help builder. -htmlhelp_basename = 'flex_container_orchestrator/doc' +htmlhelp_basename = "flex_container_orchestrator/doc" # -- Options for LaTeX output ------------------------------------------ @@ -104,15 +110,12 @@ # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', - # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', - # Additional stuff for the LaTeX preamble. # # 'preamble': '', - # Latex figure (float) alignment # # 'figure_align': 'htbp', @@ -122,18 +125,28 @@ # (source start file, target name, title, author, documentclass # [howto, manual, or own class]). latex_documents = [ - (master_doc, 'flex_container_orchestrator.tex', - u'flex-container-orchestrator Documentation', - u'Nina Burgdorfer', 'manual'), + ( + master_doc, + "flex_container_orchestrator.tex", + "flex-container-orchestrator Documentation", + "Nina Burgdorfer", + "manual", + ), ] # -- Options for manual page output ------------------------------------ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [(master_doc, 'flex_container_orchestrator', - u'flex-container-orchestrator Documentation', - [author], 1)] +man_pages = [ + ( + master_doc, + "flex_container_orchestrator", + "flex-container-orchestrator Documentation", + [author], + 1, + ) +] # -- Options for Texinfo output ---------------------------------------- @@ -141,11 +154,15 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'flex_container_orchestrator', - u'flex-container-orchestrator Documentation', author, - 'flex_container_orchestrator', - 'Service listening to Aviso and launching Flexpart-IFS', - 'Miscellaneous'), + ( + master_doc, + "flex_container_orchestrator", + "flex-container-orchestrator Documentation", + author, + "flex_container_orchestrator", + "The flex-container-orchestrator manages Aviso notifications and triggers the flexprep and flexpart containers, as well as the file aggregation script for Flexpart.", + "Miscellaneous", + ), ] # improve parameters description @@ -155,5 +172,11 @@ add_module_names = False # autoapi module configuration -autoapi_dirs = ['../flex_container_orchestrator'] -autoapi_options = ['members', 'undoc-members', 'show-inheritance', 'show-module-summary', 'imported-members'] +autoapi_dirs = ["../flex_container_orchestrator"] +autoapi_options = [ + "members", + "undoc-members", + "show-inheritance", + "show-module-summary", + "imported-members", +] diff --git a/doc/index.rst b/doc/index.rst index cf76abb..daee681 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -1,14 +1,13 @@ -flex-container-orchestrator documentation -================================================ - -Service listening to Aviso and launching Flexpart-IFS +Welcome to flex-container-orchestrator +============================================= .. toctree:: :maxdepth: 2 :caption: Contents: readme - autoapi/index + installation + usage history Indices and tables diff --git a/doc/installation.rst b/doc/installation.rst new file mode 100644 index 0000000..edc5a41 --- /dev/null +++ b/doc/installation.rst @@ -0,0 +1,24 @@ +.. highlight:: shell + +============ +Installation +============ + + +Stable release +-------------- + +To install flex-container-orchestrator, run this command in your terminal: + +.. code-block:: console + + $ pip install flex-container-orchestrator + +This is the preferred method to install flex-container-orchestrator, as it +will always install the most recent stable release. + +If you don't have `pip`_ installed, this `Python installation guide`_ can guide +you through the process. + +.. _pip: https://pip.pypa.io +.. _Python installation guide: http://docs.python-guide.org/en/latest/starting/installation/ diff --git a/doc/usage.rst b/doc/usage.rst new file mode 100644 index 0000000..e2640a5 --- /dev/null +++ b/doc/usage.rst @@ -0,0 +1,7 @@ +===== +Usage +===== + +To use flex-container-orchestrator in a project:: + + import flex_container_orchestrator diff --git a/flex_container_orchestrator/__init__.py b/flex_container_orchestrator/__init__.py index e88d065..6c257cb 100644 --- a/flex_container_orchestrator/__init__.py +++ b/flex_container_orchestrator/__init__.py @@ -1,10 +1,14 @@ """ Initializations """ + import os from flex_container_orchestrator.config import logger from flex_container_orchestrator.config.service_settings import ServiceSettings -CONFIG = ServiceSettings('settings.yaml', os.path.join(os.path.dirname(__file__), 'config')) +# mypy: ignore-errors +CONFIG = ServiceSettings( + "settings.yaml", os.path.join(os.path.dirname(__file__), "config") +) # Configure logger logger.apply_logging_settings(CONFIG.logging) diff --git a/flex_container_orchestrator/config/.env b/flex_container_orchestrator/config/.env index 6ec6984..563e9be 100644 --- a/flex_container_orchestrator/config/.env +++ b/flex_container_orchestrator/config/.env @@ -13,6 +13,10 @@ SVC__MAIN__S3_BUCKETS__OUTPUT__NAME=flexprep-output SVC__MAIN__TIME_SETTINGS__TINCR=1 SVC__MAIN__TIME_SETTINGS__TSTART=0 +# IMAGE +DOCKER_IMAGE=493666016161.dkr.ecr.eu-central-2.amazonaws.com/numericalweatherpredictions/dispersionmodelling/flexpart-ifs/flexprep:2410.10201ac80416862805f519b4b2371dde82b5ddfe + + ## Flexpart-IFS # Input/output S3 bucket settings diff --git a/flex_container_orchestrator/config/aviso/__init__.py b/flex_container_orchestrator/config/aviso/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/flex_container_orchestrator/config/aviso/listener_diss.yaml b/flex_container_orchestrator/config/aviso/listener_diss.yaml index 3e4e192..70019ce 100644 --- a/flex_container_orchestrator/config/aviso/listener_diss.yaml +++ b/flex_container_orchestrator/config/aviso/listener_diss.yaml @@ -17,4 +17,3 @@ listeners: --date "${request.date}" \ --time "${request.time}" \ --location "${location}" >> avisoLogs/log_run_${request.date}_${request.time}.log 2>&1 - diff --git a/flex_container_orchestrator/config/base_settings.py b/flex_container_orchestrator/config/base_settings.py index bc4c2a4..cbb27b5 100644 --- a/flex_container_orchestrator/config/base_settings.py +++ b/flex_container_orchestrator/config/base_settings.py @@ -1,5 +1,6 @@ """ -Note: This script from mchpy (MeteoSwiss Blueprint) was added here because I couldmchpyy from outside of the MeteoSwiss network. +Note: This script from mchpy (MeteoSwiss Blueprint) was added here because I could not use +mchpyy from outside of the MeteoSwiss network. This module introduces two custom classes that leverage Pydantic's capabilities: a custom BaseModel called SubscriptableBaseModel and a custom BaseSettings class BaseServiceSettings. @@ -7,6 +8,7 @@ Both these custom classes inherit Pydantic's core functionalities. This includes data validation, parsing, serialization, error handling, and behavior customization. """ + import logging import os from typing import Any, Type @@ -15,7 +17,8 @@ from pydantic import BaseModel from pydantic.fields import FieldInfo from pydantic.v1.utils import deep_update -from pydantic_settings import BaseSettings, PydanticBaseSettingsSource, SettingsConfigDict, InitSettingsSource +from pydantic_settings import (BaseSettings, InitSettingsSource, + PydanticBaseSettingsSource, SettingsConfigDict) logger = logging.getLogger(__name__) @@ -36,9 +39,12 @@ class BaseServiceSettings(BaseSettings): The Custom BaseSettings class is a derivative of Pydantic's BaseSettings class. It introduces the ability to read settings values from a series of YAML files, providing an additional source for configuration data. """ - model_config = SettingsConfigDict(env_nested_delimiter='__', extra='allow') - def __init__(self, settings_file_names: str | list[str], settings_dirname: str, **values: Any): + model_config = SettingsConfigDict(env_nested_delimiter="__", extra="allow") + + def __init__( + self, settings_file_names: str | list[str], settings_dirname: str, **values: Any + ): """ Initializes the service settings by loading values from the specified YAML filename(s). The YAML settings will be loaded in the same order as given in the list (or as a single settings file), @@ -61,24 +67,36 @@ def __init__(self, settings_file_names: str | list[str], settings_dirname: str, logger.warning('Given YAML settings file "%s" does not exist.', fname) if not existing_settings_file_names: - raise FileNotFoundError('Could not find the specified YAML settings file(s).') + raise FileNotFoundError( + "Could not find the specified YAML settings file(s)." + ) - super().__init__(_settings_filenames=existing_settings_file_names, _settings_dirname=settings_dirname, **values) + super().__init__( + _settings_filenames=existing_settings_file_names, + _settings_dirname=settings_dirname, + **values + ) # pylint: disable=too-many-arguments @classmethod def settings_customise_sources( - cls, - settings_cls: Type[BaseSettings], - init_settings: InitSettingsSource, # type: ignore # the init_settings is always a InitSettingsSource - env_settings: PydanticBaseSettingsSource, - dotenv_settings: PydanticBaseSettingsSource, - file_secret_settings: PydanticBaseSettingsSource, - ) -> tuple[PydanticBaseSettingsSource, PydanticBaseSettingsSource, PydanticBaseSettingsSource, - PydanticBaseSettingsSource]: - load_yaml_config = _YamlConfigSource(settings_cls=settings_cls, - filenames=init_settings.init_kwargs.pop('_settings_filenames'), - dirname=init_settings.init_kwargs.pop('_settings_dirname')) + cls, + settings_cls: Type[BaseSettings], + init_settings: InitSettingsSource, # type: ignore # the init_settings is always a InitSettingsSource + env_settings: PydanticBaseSettingsSource, + dotenv_settings: PydanticBaseSettingsSource, + file_secret_settings: PydanticBaseSettingsSource, + ) -> tuple[ + PydanticBaseSettingsSource, + PydanticBaseSettingsSource, + PydanticBaseSettingsSource, + PydanticBaseSettingsSource, + ]: + load_yaml_config = _YamlConfigSource( + settings_cls=settings_cls, + filenames=init_settings.init_kwargs.pop("_settings_filenames"), + dirname=init_settings.init_kwargs.pop("_settings_dirname"), + ) # environment variables have highest precedence, then yaml values etc. return env_settings, load_yaml_config, init_settings, file_secret_settings @@ -87,20 +105,25 @@ def settings_customise_sources( class _YamlConfigSource(PydanticBaseSettingsSource): _yaml_content_dict: dict[str, Any] - def get_field_value(self, field: FieldInfo, field_name: str) -> tuple[Any, str, bool]: + def get_field_value( + self, field: FieldInfo, field_name: str + ) -> tuple[Any, str, bool]: # this method needs to be implemented but is not used since the whole parsed # yaml file is returned as dictionary without inspecting the values or fields raise NotImplementedError() - def __init__(self, filenames: list[str], dirname: str, settings_cls: type[BaseSettings]): + def __init__( + self, filenames: list[str], dirname: str, settings_cls: type[BaseSettings] + ): self._yaml_content_dict = {} for filename in filenames: - with open(os.path.join(dirname, filename), encoding='utf-8') as file: - self._yaml_content_dict = deep_update(self._yaml_content_dict, yaml.safe_load(file)) + with open(os.path.join(dirname, filename), encoding="utf-8") as file: + self._yaml_content_dict = deep_update( + self._yaml_content_dict, yaml.safe_load(file) + ) super().__init__(settings_cls) def __call__(self) -> dict[str, Any]: return self._yaml_content_dict - diff --git a/flex_container_orchestrator/config/http_audit.py b/flex_container_orchestrator/config/http_audit.py index 5fb6834..8cc8c95 100644 --- a/flex_container_orchestrator/config/http_audit.py +++ b/flex_container_orchestrator/config/http_audit.py @@ -4,15 +4,15 @@ Instrumentation of web applications to include the request_id in all responses and make it available to other services (e.g. logging) """ + # noinspection PyPackageRequirements import contextvars import logging import uuid - from typing import Callable X_REQUEST_ID = "X-REQUEST-ID" -request_id = contextvars.ContextVar('request_id', default='') +request_id = contextvars.ContextVar("request_id", default="") logger = logging.getLogger(__name__) @@ -39,7 +39,6 @@ def extract_request_id_from(extract_request_fn: Callable[[str], str | None]) -> def _generate_request_id() -> str: rid = str(uuid.uuid4()) - logger.debug('Generated new request_id %s', rid) + logger.debug("Generated new request_id %s", rid) return rid - diff --git a/flex_container_orchestrator/config/logger.py b/flex_container_orchestrator/config/logger.py index fbcfa12..ddbb97e 100644 --- a/flex_container_orchestrator/config/logger.py +++ b/flex_container_orchestrator/config/logger.py @@ -3,6 +3,7 @@ Logging configuration including http request auditing """ + import logging.config import time from enum import Enum @@ -15,40 +16,40 @@ from flex_container_orchestrator.config import http_audit _logger: dict = { - 'version': 1, + "version": 1, # without the following option, using apply_logging_settings too late is dangerous because all loggers which were # previously known would be silently disabled - 'disable_existing_loggers': False, - 'filters': { - 'standard_request_id': { - '()': 'flex_container_orchestrator.config.logger.StandardRequestIdFilter', + "disable_existing_loggers": False, + "filters": { + "standard_request_id": { + "()": "flex_container_orchestrator.config.logger.StandardRequestIdFilter", }, - 'json_request_id': { - '()': 'flex_container_orchestrator.config.logger.JsonRequestIdFilter', + "json_request_id": { + "()": "flex_container_orchestrator.config.logger.JsonRequestIdFilter", }, }, - 'formatters': { - 'standard': { - 'format': '{asctime} {request_id}{levelname:>8s} {process} --- [{threadName:>15s}] {name_with_func:40}: ' - '{message}', - 'style': '{' + "formatters": { + "standard": { + "format": "{asctime} {request_id}{levelname:>8s} {process} --- [{threadName:>15s}] {name_with_func:40}: " + "{message}", + "style": "{", }, - 'json': { - '()': 'flex_container_orchestrator.config.logger.LogstashJsonFormatter' + "json": { + "()": "flex_container_orchestrator.config.logger.LogstashJsonFormatter" }, }, - 'handlers': { - 'console': { - 'class': 'logging.StreamHandler', - 'level': 'DEBUG', - 'filters': ['standard_request_id'], - 'formatter': 'standard', + "handlers": { + "console": { + "class": "logging.StreamHandler", + "level": "DEBUG", + "filters": ["standard_request_id"], + "formatter": "standard", }, }, - 'loggers': {}, - 'root': { - 'handlers': ['console'], - 'level': 'DEBUG', + "loggers": {}, + "root": { + "handlers": ["console"], + "level": "DEBUG", }, } @@ -57,20 +58,22 @@ class LogLevel(str, Enum): """ Wrapper class for Python logging levels so that we can make sure that no unknown logging level is taken. """ - CRITICAL = 'CRITICAL' - ERROR = 'ERROR' - WARNING = 'WARNING' - INFO = 'INFO' - DEBUG = 'DEBUG' - NOTSET = 'NOTSET' + + CRITICAL = "CRITICAL" + ERROR = "ERROR" + WARNING = "WARNING" + INFO = "INFO" + DEBUG = "DEBUG" + NOTSET = "NOTSET" class FormatterType(str, Enum): """ Wrapper class for the formatters we provide """ - STANDARD = 'standard' - JSON = 'json' + + STANDARD = "standard" + JSON = "json" class LoggingSettings(BaseModel): @@ -83,21 +86,26 @@ class LoggingSettings(BaseModel): As a default, the root logging level DEBUG and the standard formatter are taken. """ - model_config = SettingsConfigDict(extra='forbid') + + model_config = SettingsConfigDict(extra="forbid") formatter: FormatterType = FormatterType.STANDARD root_log_level: LogLevel = LogLevel.DEBUG child_log_levels: dict[str, LogLevel] = {} - @field_validator('child_log_levels') - def logger_name_does_not_contain_reserved_key(cls, child_log_levels: dict[str, LogLevel]) -> dict[str, LogLevel]: + @field_validator("child_log_levels") + def logger_name_does_not_contain_reserved_key( # pylint: disable=no-self-argument + cls, child_log_levels: dict[str, LogLevel] + ) -> dict[str, LogLevel]: for key in child_log_levels.keys(): - if key in ['formatter', 'root', 'root_log_level', '']: - raise ValueError('Using empty or reserved name for child logger.') + if key in ["formatter", "root", "root_log_level", ""]: + raise ValueError("Using empty or reserved name for child logger.") return child_log_levels -def apply_logging_settings(logging_settings: LoggingSettings = LoggingSettings()) -> None: +def apply_logging_settings( + logging_settings: LoggingSettings = LoggingSettings(), +) -> None: """ Configures the python logger for the current application. @@ -122,12 +130,17 @@ def apply_logging_settings(logging_settings: LoggingSettings = LoggingSettings() :param logging_settings: the settings according to which logging should be configured """ - config = {'logging': {'formatter': logging_settings.formatter.value, 'root': logging_settings.root_log_level.value}} + config = { + "logging": { + "formatter": logging_settings.formatter.value, + "root": logging_settings.root_log_level.value, + } + } for logger_name, log_level in logging_settings.child_log_levels.items(): - config['logging'][logger_name] = log_level.name + config["logging"][logger_name] = log_level.name - if config and 'logging' in config: - logging_config_ = config['logging'] + if config and "logging" in config: + logging_config_ = config["logging"] _set_formatter(logging_config_) _set_root_logger(logging_config_) @@ -135,31 +148,35 @@ def apply_logging_settings(logging_settings: LoggingSettings = LoggingSettings() logging.config.dictConfig(_logger) # Use a period instead of the comma before the milliseconds part - logging.Formatter.default_msec_format = '%s.%03d' + logging.Formatter.default_msec_format = "%s.%03d" # Use UTC timestamps logging.Formatter.converter = time.gmtime logging.captureWarnings(True) def _set_formatter(logging_config: dict) -> None: - if 'formatter' in logging_config: - formatter = logging_config['formatter'] - _logger['handlers']['console']['formatter'] = formatter - _logger['handlers']['console']['filters'] = [formatter + '_request_id'] + if "formatter" in logging_config: + formatter = logging_config["formatter"] + _logger["handlers"]["console"]["formatter"] = formatter + _logger["handlers"]["console"]["filters"] = [formatter + "_request_id"] def _set_root_logger(logging_config: dict) -> None: - if 'root' in logging_config: - _logger['root']['level'] = logging_config['root'] + if "root" in logging_config: + _logger["root"]["level"] = logging_config["root"] def _set_loggers(logging_config: dict) -> None: - loggers = [(logger, level) for logger, level in logging_config.items() if logger not in ['formatter', 'root']] - for (logger, level) in loggers: - if logger in _logger['loggers']: - _logger['loggers'][logger]['level'] = level + loggers = [ + (logger, level) + for logger, level in logging_config.items() + if logger not in ["formatter", "root"] + ] + for logger, level in loggers: + if logger in _logger["loggers"]: + _logger["loggers"][logger]["level"] = level else: - _logger['loggers'][logger] = {'level': level} + _logger["loggers"][logger] = {"level": level} class StandardRequestIdFilter(logging.Filter): @@ -171,9 +188,9 @@ class StandardRequestIdFilter(logging.Filter): def filter(self, record: logging.LogRecord) -> bool: request_id = http_audit.get_request_id() # request_id is only in the log when existing - record.request_id = '[' + request_id + '] ' if request_id else '' + record.request_id = "[" + request_id + "] " if request_id else "" # logger and function name concatenated and truncated from the left - record.name_with_func = (record.name + '.' + record.funcName)[-40:] + record.name_with_func = (record.name + "." + record.funcName)[-40:] return True @@ -193,6 +210,7 @@ def filter(self, record: logging.LogRecord) -> bool: class MessageContainsFilter(logging.Filter): """Filter log messages which contain a substring.""" + def __init__(self, substrings: Sequence[str], *args: Any, **kwargs: Any) -> None: """Constructor. @@ -225,27 +243,35 @@ class LogstashJsonFormatter(jsonlogger.JsonFormatter): # 'exc_info' is transformed later when existing, cannot be easily transformed # to 'stack_trace'. _logged_fields = ( - ('asctime', '@timestamp'), - ('threadName', 'thread_name'), - ('levelname', 'level'), - ('name', 'logger_name'), - ('funcName', 'func_name'), + ("asctime", "@timestamp"), + ("threadName", "thread_name"), + ("levelname", "level"), + ("name", "logger_name"), + ("funcName", "func_name"), ) - _log_levels = ['DEBUG', 'INFO', 'WARN', 'ERROR', 'CRITICAL'] - - def add_fields(self, log_record: dict[str, Any], record: logging.LogRecord, message_dict: dict[str, Any]) -> None: + _log_levels = ["DEBUG", "INFO", "WARN", "ERROR", "CRITICAL"] + + def add_fields( + self, + log_record: dict[str, Any], + record: logging.LogRecord, + message_dict: dict[str, Any], + ) -> None: super().add_fields(log_record, record, message_dict) converter = self.converter(record.created) - log_record['@timestamp'] = time.strftime('%Y-%m-%dT%H:%M:%S.%%03d%z', converter) % record.msecs + log_record["@timestamp"] = ( + time.strftime("%Y-%m-%dT%H:%M:%S.%%03d%z", converter) % record.msecs + ) for src_field, field in self._logged_fields: value = record.__dict__.get(src_field) if value is not None: - if field == 'level': - if value == 'WARNING': - value = 'WARN' + if field == "level": + if value == "WARNING": + value = "WARN" try: - log_record['level_value'] = (self._log_levels.index(value) + 1) * 10_000 + log_record["level_value"] = ( + self._log_levels.index(value) + 1 + ) * 10_000 except ValueError: pass log_record[field] = value - diff --git a/flex_container_orchestrator/config/service_settings.py b/flex_container_orchestrator/config/service_settings.py index ead746a..c4815e6 100644 --- a/flex_container_orchestrator/config/service_settings.py +++ b/flex_container_orchestrator/config/service_settings.py @@ -1,12 +1,20 @@ from pydantic import BaseModel +from flex_container_orchestrator.config.base_settings import \ + BaseServiceSettings from flex_container_orchestrator.config.logger import LoggingSettings -from flex_container_orchestrator.config.base_settings import BaseServiceSettings + + +class TimeSettings(BaseModel): + tincr: int + tdelta: int + tfreq_f: int + tfreq: int class AppSettings(BaseModel): - """The main application settings""" app_name: str + time_settings: TimeSettings class ServiceSettings(BaseServiceSettings): diff --git a/flex_container_orchestrator/config/settings.yaml b/flex_container_orchestrator/config/settings.yaml index fd9d186..264e69d 100644 --- a/flex_container_orchestrator/config/settings.yaml +++ b/flex_container_orchestrator/config/settings.yaml @@ -8,3 +8,12 @@ logging: flex_container_orechestrator.config.http_audit: INFO main: app_name: flex-container-orchestrator + time_settings: + # Number of hours between timesteps + tincr: 1 + # Number of timesteps to run Flexpart with (temporarily set to 6 timesteps but operational config is 90) + tdelta: 6 + # Frequency of Flexpart runs in hour + tfreq_f: 6 + # Frequency of IFS runs in hour + tfreq: 6 diff --git a/flex_container_orchestrator/domain/aggregator_flexpart.py b/flex_container_orchestrator/domain/aggregator_flexpart.py index ea6a5a4..28ab40c 100644 --- a/flex_container_orchestrator/domain/aggregator_flexpart.py +++ b/flex_container_orchestrator/domain/aggregator_flexpart.py @@ -5,16 +5,13 @@ import json import logging import sqlite3 -import subprocess import sys -import os -import yaml +from typing import List, Optional, Set, Tuple -import boto3 -from botocore.exceptions import ClientError +from flex_container_orchestrator import CONFIG -def parse_arguments(): +def parse_arguments() -> argparse.Namespace: """ Parse command-line arguments. @@ -33,7 +30,7 @@ def parse_arguments(): return parser.parse_args() -def connect_db(db_path): +def connect_db(db_path: str) -> sqlite3.Connection: """ Establish a connection to the SQLite database. @@ -45,20 +42,22 @@ def connect_db(db_path): """ try: conn = sqlite3.connect(db_path) - logging.info(f"Connected to SQLite database at {db_path}.") + logging.info("Connected to SQLite database at %s.", db_path) return conn except sqlite3.Error as e: - logging.error(f"SQLite connection error: {e}") + logging.error("SQLite connection error: %s", e) sys.exit(1) -def is_row_processed(conn, forecast_ref_time, step): +def is_row_processed( + conn: sqlite3.Connection, forecast_ref_time: datetime.datetime, step: str +) -> bool: """ Check if a specific row in the database has been processed. Args: conn (sqlite3.Connection): SQLite connection object. - forecast_ref_time (str): Forecast reference time in YYYYMMDDHHMM format. + forecast_ref_time (datetime): Forecast reference time step (str): Step identifier. Returns: @@ -76,17 +75,20 @@ def is_row_processed(conn, forecast_ref_time, step): result = cursor.fetchone() if result: return result[0] == 1 - else: - logging.info( - f"No row found for forecast_ref_time={forecast_ref_time} and step={step}." - ) - return False + logging.info( + "No row found for forecast_ref_time=%s and step=%s.", + forecast_ref_time, + step, + ) + return False except sqlite3.Error as e: - logging.error(f"SQLite query error: {e}") + logging.error("SQLite query error: %s", e) sys.exit(1) -def generate_flexpart_start_times(frt_dt, lead_time, tdelta, tfreq_f): +def generate_flexpart_start_times( + frt_dt: datetime.datetime, lead_time: int, tdelta: int, tfreq_f: int +) -> List[datetime.datetime]: """ Generate a list of Flexpart run start times. @@ -104,7 +106,7 @@ def generate_flexpart_start_times(frt_dt, lead_time, tdelta, tfreq_f): min_start_time = lt_tmp + datetime.timedelta( hours=tfreq_f - (lt_tmp.hour % tfreq_f) ) - max_start_time = lt_dt.replace(hour=(lt_dt.hour - (lt_dt.hour % tfreq_f))) + max_start_time = lt_dt.replace(hour=lt_dt.hour - (lt_dt.hour % tfreq_f)) list_start_times = [] current_start = min_start_time @@ -116,7 +118,7 @@ def generate_flexpart_start_times(frt_dt, lead_time, tdelta, tfreq_f): return list_start_times -def convert_time_to_frt(time, tfreq): +def convert_time_to_frt(time: datetime.datetime, tfreq: int) -> str: """ Convert time object into IFS forecast objects to use. @@ -136,7 +138,9 @@ def convert_time_to_frt(time, tfreq): return frt_st.strftime("%Y%m%d%H%M") + f"{lt:02}" -def fetch_processed_items(conn, frt_s): +def fetch_processed_items( + conn: sqlite3.Connection, frt_s: Set[datetime.datetime] +) -> Set[str]: """ Fetch all processed items from the database. @@ -161,97 +165,174 @@ def fetch_processed_items(conn, frt_s): items_f = cursor.fetchall() for item in items_f: if item[0]: # processed == True - processed_items.add(frt + f"{int(item[1]):02}") + frt_str = ( + frt.strftime("%Y%m%d%H%M") + if isinstance(frt, datetime.datetime) + else str(frt) + ) + processed_items.add(frt_str + f"{int(item[1]):02}") except sqlite3.Error as e: - logging.error(f"SQLite query error while fetching processed items: {e}") + logging.error("SQLite query error while fetching processed items: %s", e) sys.exit(1) return processed_items -def define_config(st: datetime.datetime, et: datetime.datetime): +def define_config(st: datetime.datetime, et: datetime.datetime) -> dict: + """ + Define configuration for Flexpart. - logging.info(f'Start and end time to configure Flexpart: {st} and {et} ') + Args: + st (datetime.datetime): Start time. + et (datetime.datetime): End time. + + Returns: + dict: Configuration dictionary for Flexpart. + """ + logging.info("Start and end time to configure Flexpart: %s and %s ", st, et) configuration = { - 'IBDATE': st.strftime("%Y%m%d"), - 'IBTIME': st.strftime("%H"), - 'IEDATE': et.strftime("%Y%m%d"), - 'IETIME': et.strftime("%H") + "IBDATE": st.strftime("%Y%m%d"), + "IBTIME": st.strftime("%H"), + "IEDATE": et.strftime("%Y%m%d"), + "IETIME": et.strftime("%H"), } - logging.info(f'Configuration to run Flexpart: {json.dumps(configuration)}') + logging.info("Configuration to run Flexpart: %s", json.dumps(configuration)) return configuration -def main(): - args = parse_arguments() +# mypy: ignore-errors +def get_time_settings(config) -> dict: + """ + Retrieve time settings from config. - DATE = args.date - TIME = args.time - STEP = args.step - DB_PATH = args.db_path + Args: + config: Configuration object. - # Constants - TINCR = 1 # How many hours between time steps - TDELTA = 6 # Number of timesteps to run Flexpart with (temporarily set to 6 timesteps but operational config is 90) - TFREQ_F = 6 # Frequency of Flexpart runs in hours - TFREQ = 6 # Frequency of IFS forecast times in hours + Returns: + dict: Dictionary with time settings. + """ + return { + "tincr": config.main.time_settings.tincr, + "tdelta": config.main.time_settings.tdelta, + "tfreq_f": config.main.time_settings.tfreq_f, + "tfreq": config.main.time_settings.tfreq, + } - # Connect to the database - conn = connect_db(DB_PATH) - frt_dt = datetime.datetime.strptime(f"{DATE}{int(TIME):02d}00", "%Y%m%d%H%M") +def parse_forecast_datetime(date_str: str, time_str: str) -> datetime.datetime: + """ + Parse forecast date and time strings into a datetime object. - # Check if the specific row is processed - if not is_row_processed(conn, frt_dt, STEP): - logging.info("File processing incomplete. Exiting before launching Flexpart.") - conn.close() - sys.exit(0) + Args: + date_str (str): Date in YYYYMMDD format. + time_str (str): Time in HH format. - lead_time = int(STEP) - list_start_times = generate_flexpart_start_times(frt_dt, lead_time, TDELTA, TFREQ_F) + Returns: + datetime.datetime: Parsed datetime object. + """ + return datetime.datetime.strptime(f"{date_str}{int(time_str):02d}00", "%Y%m%d%H%M") - logging.info(f"Generated {len(list_start_times)} Flexpart start times.") +def generate_forecast_times( + list_start_times: List[datetime.datetime], time_settings: dict +) -> Tuple[List[List[str]], List[List[datetime.datetime]], Set[str]]: + """ + Generate forecast times for Flexpart runs. + + Args: + list_start_times (list of datetime.datetime): List of Flexpart run start times. + time_settings (dict): Time settings dictionary. + + Returns: + tuple: Tuple containing lists of forecast times, lead times, and all steps. + """ all_steps = set() all_list_ltf = [] all_list_lt = [] - for start_time in list_start_times: - logging.info(f"Start time: {start_time}") + logging.info("Start time: %s", start_time) list_ltf = [] list_lt = [] - for i in range(0, TDELTA, TINCR): + for i in range(0, time_settings["tdelta"], time_settings["tincr"]): time = start_time + datetime.timedelta(hours=i) - forecast = convert_time_to_frt(time, TFREQ) + forecast = convert_time_to_frt(time, time_settings["tfreq"]) list_ltf.append(forecast) list_lt.append(time) all_steps.add(forecast) all_list_ltf.append(list_ltf) all_list_lt.append(list_lt) + return all_list_ltf, all_list_lt, all_steps + + +def strip_lead_time(forecast: str) -> datetime.datetime: + """ + Strip lead time from forecast string. + + Args: + forecast (str): Forecast reference time with lead time. + + Returns: + datetime.datetime: Forecast datetime without lead time. + """ + return datetime.datetime.strptime(forecast[:-2], "%Y%m%d%H%M") - # Generate forecast ref time by stripping the last two characters (lead time) - frt_s = { - datetime.datetime.strptime(forecast[:-2], "%Y%m%d%H%M") - for forecast in all_steps - } - # Fetch processed items from the database - processed_items = fetch_processed_items(conn, frt_s) +def create_flexpart_configs( + all_list_lt: List[List[datetime.datetime]], + all_list_ltf: List[List[str]], + processed_items: Set[str], +) -> List[dict]: + """ + Create Flexpart configurations based on processed items. - # Iterate over all_list_ltf to determine if Flexpart should be launched + Args: + all_list_lt (list of list of datetime.datetime): List of lead times. + all_list_ltf (list of list of str): List of forecast reference times with lead times. + processed_items (set of str): Set of processed item identifiers. + + Returns: + list of dict: List of Flexpart configuration dictionaries. + """ configs = [] for i, flexpart_run in enumerate(all_list_ltf): - tstart = all_list_lt[i][0] - tend = all_list_lt[i][-1] if all(item in processed_items for item in flexpart_run): - config = define_config(tstart, tend) + config = define_config(all_list_lt[i][0], all_list_lt[i][-1]) configs.append(config) + return configs + + +def main() -> None: + """ + Main function to parse arguments, connect to database, and run Flexpart processing. + """ + args = parse_arguments() + time_settings = get_time_settings(CONFIG) + conn = connect_db(args.db_path) + frt_dt = parse_forecast_datetime(args.date, args.time) + + if not is_row_processed(conn, frt_dt, args.step): + logging.info("File processing incomplete. Exiting before launching Flexpart.") + conn.close() + sys.exit(0) + + list_start_times = generate_flexpart_start_times( + frt_dt, + int(args.step), + time_settings["tdelta"], + time_settings["tfreq_f"], + ) + all_list_ltf, all_list_lt, all_steps = generate_forecast_times( + list_start_times, time_settings + ) + + frt_set = {strip_lead_time(forecast) for forecast in all_steps} + processed_items = fetch_processed_items(conn, frt_set) + configs = create_flexpart_configs(all_list_lt, all_list_ltf, processed_items) print(json.dumps(configs)) - # Close the database connection conn.close() diff --git a/flex_container_orchestrator/main.py b/flex_container_orchestrator/main.py index 40bc388..d7e0bf8 100644 --- a/flex_container_orchestrator/main.py +++ b/flex_container_orchestrator/main.py @@ -1,19 +1,21 @@ -import os +import argparse import logging + from flex_container_orchestrator.services import flexpart_service -import argparse logger = logging.getLogger(__name__) -def main(): + +def main() -> None: parser = argparse.ArgumentParser() - parser.add_argument('--date', required=True, help='Date parameter') - parser.add_argument('--location', required=True, help='Location parameter') - parser.add_argument('--time', required=True, help='Time parameter') - parser.add_argument('--step', required=True, help='Step parameter') + parser.add_argument("--date", required=True, help="Date parameter") + parser.add_argument("--location", required=True, help="Location parameter") + parser.add_argument("--time", required=True, help="Time parameter") + parser.add_argument("--step", required=True, help="Step parameter") args = parser.parse_args() flexpart_service.launch_containers(args.date, args.location, args.time, args.step) + if __name__ == "__main__": main() diff --git a/flex_container_orchestrator/services/flexpart_service.py b/flex_container_orchestrator/services/flexpart_service.py index 020d775..f2bdb3a 100755 --- a/flex_container_orchestrator/services/flexpart_service.py +++ b/flex_container_orchestrator/services/flexpart_service.py @@ -1,23 +1,26 @@ -import subprocess import json +import logging import os +import subprocess import sys -import logging -def run_command(command, capture_output=False): + +def run_command(command: list[str] | str, capture_output: bool = False) -> bytes | None: """ Helper function to run shell commands and handle errors. """ try: if capture_output: return subprocess.check_output(command).strip() - else: - subprocess.check_call(command) + subprocess.check_call(command) except subprocess.CalledProcessError as e: logging.error(f"Command '{' '.join(command)}' failed with error: {e}") sys.exit(1) -def launch_containers(date, location, time, step): + return None + + +def launch_containers(date: str, location: str, time: str, step: str) -> None: logging.basicConfig(level=logging.INFO) # Retrieve ECR login password and log in to Docker @@ -26,8 +29,12 @@ def launch_containers(date, location, time, step): login_password = run_command(login_command, capture_output=True) docker_login_command = [ - "docker", "login", "--username", "AWS", "--password-stdin", - "493666016161.dkr.ecr.eu-central-2.amazonaws.com" + "docker", + "login", + "--username", + "AWS", + "--password-stdin", + "493666016161.dkr.ecr.eu-central-2.amazonaws.com", ] process = subprocess.Popen(docker_login_command, stdin=subprocess.PIPE) @@ -38,31 +45,38 @@ def launch_containers(date, location, time, step): sys.exit(1) except subprocess.CalledProcessError as e: - logging.error(f"Error logging in to Docker: {e}") + logging.error("Error logging in to Docker: %s", e) sys.exit(1) # ====== First part: Run pre-processing for Flexpart ====== - docker_image = "493666016161.dkr.ecr.eu-central-2.amazonaws.com/numericalweatherpredictions/dispersionmodelling/flexpart-ifs/flexprep:2410.d02afb52a3021ad13aa734fc400e84026347ce7b" - db_mount = os.path.expanduser('~/.sqlite/') - env_file_path = os.path.expanduser('~/flex-container-orchestrator/flex_container_orchestrator/config/.env') + db_mount = os.path.expanduser("~/.sqlite/") if not os.path.exists(db_mount): - logging.error(f"SQLite database directory {db_mount} does not exist.") + logging.error("SQLite database directory %s does not exist.", db_mount) sys.exit(1) try: docker_run_command = [ - "docker", "run", "--rm", - "--mount", f"type=bind,source={db_mount},destination=/src/db/", - "--env-file", env_file_path, - docker_image, - "--step", step, - "--date", date, - "--time", time, - "--location", location + "docker", + "run", + "--rm", + "--mount", + f"type=bind,source={db_mount},destination=/src/db/", + "--env-file", + os.path.expanduser( + "~/flex-container-orchestrator/flex_container_orchestrator/config/.env" + ), + os.getenv("DOCKER_IMAGE"), + "--step", + step, + "--date", + date, + "--time", + time, + "--location", + location, ] - - run_command(docker_run_command) + run_command(docker_run_command) # type: ignore except subprocess.CalledProcessError: logging.error("Docker run processing failed.") @@ -71,32 +85,41 @@ def launch_containers(date, location, time, step): logging.info("Pre-processing container executed successfully.") # ====== Second part: Run aggregator_flexpart.py ====== - db_path = os.path.expanduser('~/.sqlite/sqlite3-db') + db_path = os.path.expanduser("~/.sqlite/sqlite3-db") if not os.path.exists(db_path): - logging.error(f"Database file {db_path} does not exist.") + logging.error("Database file %s does not exist.", db_path) sys.exit(1) try: script_dir = os.path.dirname(os.path.abspath(__file__)) - aggregator_script_path = os.path.join(script_dir, '..', 'domain', 'aggregator_flexpart.py') + aggregator_script_path = os.path.join( + script_dir, "..", "domain", "aggregator_flexpart.py" + ) aggregator_command = [ - "python3", aggregator_script_path, - "--date", date, - "--time", time, - "--step", step, - "--db_path", db_path + "python3", + aggregator_script_path, + "--date", + date, + "--time", + time, + "--step", + step, + "--db_path", + db_path, ] output = run_command(aggregator_command, capture_output=True) if not output: - logging.info("Flexpart can't be launched. Not enough pre-processed files. Exiting.") + logging.info( + "Flexpart can't be launched. Not enough pre-processed files. Exiting." + ) sys.exit(0) try: - configurations = json.loads(output.decode('utf-8')) + configurations = json.loads(output.decode("utf-8")) except json.JSONDecodeError as e: - logging.error(f"JSON decode error: {e}") + logging.error("JSON decode error: %s", e) sys.exit(1) except subprocess.CalledProcessError: @@ -115,8 +138,8 @@ def launch_containers(date, location, time, step): # Loop through each configuration and execute Flexpart for config in configurations: env_vars = [f"-e {key}={value}" for key, value in config.items()] - command = ['/bin/sh', '-c', 'ulimit -a && bash entrypoint.sh'] - command_str = ' '.join(command) + command = ["/bin/sh", "-c", "ulimit -a && bash entrypoint.sh"] + command_str = " ".join(command) docker_command = ( f"docker run {' '.join(env_vars)} --rm " @@ -124,7 +147,7 @@ def launch_containers(date, location, time, step): f"{command_str}" ) - logging.info(f"Running: {docker_command}") + logging.info("Running: %s", docker_command) run_command(docker_command) except subprocess.CalledProcessError: @@ -132,7 +155,3 @@ def launch_containers(date, location, time, step): sys.exit(1) logging.info("Launch Flexpart script executed successfully.") - - -if __name__ == "__main__": - main() diff --git a/poetry.lock b/poetry.lock index a6012f7..13d11c5 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. [[package]] name = "accessible-pygments" @@ -18,17 +18,27 @@ pygments = ">=1.5" dev = ["pillow", "pkginfo (>=1.10)", "playwright", "pre-commit", "setuptools", "twine (>=5.0)"] tests = ["hypothesis", "pytest"] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "alabaster" -version = "0.7.16" +version = "1.0.0" description = "A light, configurable Sphinx theme" optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" files = [ - {file = "alabaster-0.7.16-py3-none-any.whl", hash = "sha256:b46733c07dce03ae4e150330b975c75737fa60f0a7c591b6c8bf4928a28e2c92"}, - {file = "alabaster-0.7.16.tar.gz", hash = "sha256:75a8b99c28a5dad50dd7f8ccdd447a121ddb3892da9e53d1ca5cca3106d58d65"}, + {file = "alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b"}, + {file = "alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e"}, ] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "annotated-types" version = "0.7.0" @@ -40,16 +50,10 @@ files = [ {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, ] -[[package]] -name = "anyascii" -version = "0.3.2" -description = "Unicode to ASCII transliteration" -optional = false -python-versions = ">=3.3" -files = [ - {file = "anyascii-0.3.2-py3-none-any.whl", hash = "sha256:3b3beef6fc43d9036d3b0529050b0c48bfad8bc960e9e562d7223cfb94fe45d4"}, - {file = "anyascii-0.3.2.tar.gz", hash = "sha256:9d5d32ef844fe225b8bc7cba7f950534fae4da27a9bf3a6bea2cb0ea46ce4730"}, -] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" [[package]] name = "astroid" @@ -62,24 +66,41 @@ files = [ {file = "astroid-3.3.5.tar.gz", hash = "sha256:5cfc40ae9f68311075d27ef68a4841bdc5cc7f6cf86671b49f00607d30188e2d"}, ] +[package.dependencies] +typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.11\""} + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] -name = "awscli" -version = "1.35.8" -description = "Universal Command Line Environment for AWS." +name = "autodoc-pydantic" +version = "2.2.0" +description = "Seamlessly integrate pydantic models in your Sphinx documentation." optional = false -python-versions = ">=3.8" +python-versions = ">=3.8.1,<4.0.0" files = [ - {file = "awscli-1.35.8-py3-none-any.whl", hash = "sha256:20fd63d2acdfa2349bc6fcc63fad2da719a473e65d138b88f6835aee630a5bfa"}, - {file = "awscli-1.35.8.tar.gz", hash = "sha256:54b4e0fb53b9b5b5f921b4cf4d491d665f9ce650b08be3feec6c0b049328ea40"}, + {file = "autodoc_pydantic-2.2.0-py3-none-any.whl", hash = "sha256:8c6a36fbf6ed2700ea9c6d21ea76ad541b621fbdf16b5a80ee04673548af4d95"}, ] [package.dependencies] -botocore = "1.35.42" -colorama = ">=0.2.5,<0.4.7" -docutils = ">=0.10,<0.17" -PyYAML = ">=3.10,<6.1" -rsa = ">=3.1.2,<4.8" -s3transfer = ">=0.10.0,<0.11.0" +pydantic = ">=2.0,<3.0.0" +pydantic-settings = ">=2.0,<3.0.0" +Sphinx = ">=4.0" + +[package.extras] +docs = ["myst-parser (>=3.0.0,<4.0.0)", "sphinx-copybutton (>=0.5.0,<0.6.0)", "sphinx-rtd-theme (>=2.0.0,<3.0.0)", "sphinx-tabs (>=3,<4)", "sphinxcontrib-mermaid (>=0.9.0,<0.10.0)"] +erdantic = ["erdantic (<2.0)"] +linting = ["ruff (>=0.4.0,<0.5.0)"] +security = ["pip-audit (>=2.7.2,<3.0.0)"] +test = ["coverage (>=7,<8)", "defusedxml (>=0.7.1)", "pytest (>=8.0.0,<9.0.0)", "pytest-sugar (>=1.0.0,<2.0.0)"] +type-checking = ["mypy (>=1.9,<2.0)", "types-docutils (>=0.20,<0.21)", "typing-extensions (>=4.11,<5.0)"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" [[package]] name = "babel" @@ -95,6 +116,11 @@ files = [ [package.extras] dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "beautifulsoup4" version = "4.12.3" @@ -116,34 +142,95 @@ charset-normalizer = ["charset-normalizer"] html5lib = ["html5lib"] lxml = ["lxml"] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + +[[package]] +name = "black" +version = "24.10.0" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.9" +files = [ + {file = "black-24.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6668650ea4b685440857138e5fe40cde4d652633b1bdffc62933d0db4ed9812"}, + {file = "black-24.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1c536fcf674217e87b8cc3657b81809d3c085d7bf3ef262ead700da345bfa6ea"}, + {file = "black-24.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:649fff99a20bd06c6f727d2a27f401331dc0cc861fb69cde910fe95b01b5928f"}, + {file = "black-24.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:fe4d6476887de70546212c99ac9bd803d90b42fc4767f058a0baa895013fbb3e"}, + {file = "black-24.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5a2221696a8224e335c28816a9d331a6c2ae15a2ee34ec857dcf3e45dbfa99ad"}, + {file = "black-24.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f9da3333530dbcecc1be13e69c250ed8dfa67f43c4005fb537bb426e19200d50"}, + {file = "black-24.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4007b1393d902b48b36958a216c20c4482f601569d19ed1df294a496eb366392"}, + {file = "black-24.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:394d4ddc64782e51153eadcaaca95144ac4c35e27ef9b0a42e121ae7e57a9175"}, + {file = "black-24.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e39e0fae001df40f95bd8cc36b9165c5e2ea88900167bddf258bacef9bbdc3"}, + {file = "black-24.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d37d422772111794b26757c5b55a3eade028aa3fde43121ab7b673d050949d65"}, + {file = "black-24.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14b3502784f09ce2443830e3133dacf2c0110d45191ed470ecb04d0f5f6fcb0f"}, + {file = "black-24.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:30d2c30dc5139211dda799758559d1b049f7f14c580c409d6ad925b74a4208a8"}, + {file = "black-24.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cbacacb19e922a1d75ef2b6ccaefcd6e93a2c05ede32f06a21386a04cedb981"}, + {file = "black-24.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1f93102e0c5bb3907451063e08b9876dbeac810e7da5a8bfb7aeb5a9ef89066b"}, + {file = "black-24.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ddacb691cdcdf77b96f549cf9591701d8db36b2f19519373d60d31746068dbf2"}, + {file = "black-24.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:680359d932801c76d2e9c9068d05c6b107f2584b2a5b88831c83962eb9984c1b"}, + {file = "black-24.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:17374989640fbca88b6a448129cd1745c5eb8d9547b464f281b251dd00155ccd"}, + {file = "black-24.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:63f626344343083322233f175aaf372d326de8436f5928c042639a4afbbf1d3f"}, + {file = "black-24.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfa1d0cb6200857f1923b602f978386a3a2758a65b52e0950299ea014be6800"}, + {file = "black-24.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:2cd9c95431d94adc56600710f8813ee27eea544dd118d45896bb734e9d7a0dc7"}, + {file = "black-24.10.0-py3-none-any.whl", hash = "sha256:3bb2b7a1f7b685f85b11fed1ef10f8a9148bceb49853e47a294a3dd963c1dd7d"}, + {file = "black-24.10.0.tar.gz", hash = "sha256:846ea64c97afe3bc677b761787993be4991810ecc7a4a937816dd6bddedc4875"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=0.9.0" +platformdirs = ">=2" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.10)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "boto3" -version = "1.35.42" +version = "1.35.48" description = "The AWS SDK for Python" optional = false -python-versions = ">=3.8" +python-versions = ">= 3.8" files = [ - {file = "boto3-1.35.42-py3-none-any.whl", hash = "sha256:e1f36f8be453505cebcc3da178ea081b2a06c0e5e1cdee774f1067599b8d9c3e"}, - {file = "boto3-1.35.42.tar.gz", hash = "sha256:a5b00f8b82dce62870759f04861747944da834d64a64355970120c475efdafc0"}, + {file = "boto3-1.35.48-py3-none-any.whl", hash = "sha256:60889bb6e21f0af662ac9404e00125d3b8a5808f190e89462e5ddf73604adfc1"}, + {file = "boto3-1.35.48.tar.gz", hash = "sha256:5007a5cdd09e4db9309adf2ee090455a34ae639bd10a68a1fefca72cd77070fc"}, ] [package.dependencies] -botocore = ">=1.35.42,<1.36.0" +botocore = ">=1.35.48,<1.36.0" jmespath = ">=0.7.1,<2.0.0" s3transfer = ">=0.10.0,<0.11.0" [package.extras] crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "botocore" -version = "1.35.42" +version = "1.35.48" description = "Low-level, data-driven core of boto 3." optional = false -python-versions = ">=3.8" +python-versions = ">= 3.8" files = [ - {file = "botocore-1.35.42-py3-none-any.whl", hash = "sha256:05af0bb8b9cea7ce7bc589c332348d338a21b784e9d088a588fd10ec145007ff"}, - {file = "botocore-1.35.42.tar.gz", hash = "sha256:af348636f73dc24b7e2dc760a34d08c8f2f94366e9b4c78d877307b128abecef"}, + {file = "botocore-1.35.48-py3-none-any.whl", hash = "sha256:34fa25fd717208b05745e60f271a39636108fa87a3512fbca18e7e6f787a3239"}, + {file = "botocore-1.35.48.tar.gz", hash = "sha256:3e766cc251053c9ef98542fdf225381ed58531769c3811a6282bd7247f7e2bdf"}, ] [package.dependencies] @@ -154,6 +241,11 @@ urllib3 = {version = ">=1.25.4,<2.2.0 || >2.2.0,<3", markers = "python_version > [package.extras] crt = ["awscrt (==0.22.0)"] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "certifi" version = "2024.8.30" @@ -165,6 +257,27 @@ files = [ {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, ] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + +[[package]] +name = "cfgv" +version = "3.4.0" +description = "Validate configuration and produce human readable error messages." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, + {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, +] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "charset-normalizer" version = "3.4.0" @@ -279,6 +392,52 @@ files = [ {file = "charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e"}, ] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + +[[package]] +name = "codespell" +version = "2.3.0" +description = "Codespell" +optional = false +python-versions = ">=3.8" +files = [ + {file = "codespell-2.3.0-py3-none-any.whl", hash = "sha256:a9c7cef2501c9cfede2110fd6d4e5e62296920efe9abfb84648df866e47f58d1"}, + {file = "codespell-2.3.0.tar.gz", hash = "sha256:360c7d10f75e65f67bad720af7007e1060a5d395670ec11a7ed1fed9dd17471f"}, +] + +[package.extras] +dev = ["Pygments", "build", "chardet", "pre-commit", "pytest", "pytest-cov", "pytest-dependency", "ruff", "tomli", "twine"] +hard-encoding-detection = ["chardet"] +toml = ["tomli"] +types = ["chardet (>=5.1.0)", "mypy", "pytest", "pytest-cov", "pytest-dependency"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "colorama" version = "0.4.6" @@ -290,80 +449,93 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "coverage" -version = "7.6.3" +version = "7.6.4" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.9" files = [ - {file = "coverage-7.6.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6da42bbcec130b188169107ecb6ee7bd7b4c849d24c9370a0c884cf728d8e976"}, - {file = "coverage-7.6.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c222958f59b0ae091f4535851cbb24eb57fc0baea07ba675af718fb5302dddb2"}, - {file = "coverage-7.6.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab84a8b698ad5a6c365b08061920138e7a7dd9a04b6feb09ba1bfae68346ce6d"}, - {file = "coverage-7.6.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70a6756ce66cd6fe8486c775b30889f0dc4cb20c157aa8c35b45fd7868255c5c"}, - {file = "coverage-7.6.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c2e6fa98032fec8282f6b27e3f3986c6e05702828380618776ad794e938f53a"}, - {file = "coverage-7.6.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:921fbe13492caf6a69528f09d5d7c7d518c8d0e7b9f6701b7719715f29a71e6e"}, - {file = "coverage-7.6.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:6d99198203f0b9cb0b5d1c0393859555bc26b548223a769baf7e321a627ed4fc"}, - {file = "coverage-7.6.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:87cd2e29067ea397a47e352efb13f976eb1b03e18c999270bb50589323294c6e"}, - {file = "coverage-7.6.3-cp310-cp310-win32.whl", hash = "sha256:a3328c3e64ea4ab12b85999eb0779e6139295bbf5485f69d42cf794309e3d007"}, - {file = "coverage-7.6.3-cp310-cp310-win_amd64.whl", hash = "sha256:bca4c8abc50d38f9773c1ec80d43f3768df2e8576807d1656016b9d3eeaa96fd"}, - {file = "coverage-7.6.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c51ef82302386d686feea1c44dbeef744585da16fcf97deea2a8d6c1556f519b"}, - {file = "coverage-7.6.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0ca37993206402c6c35dc717f90d4c8f53568a8b80f0bf1a1b2b334f4d488fba"}, - {file = "coverage-7.6.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c77326300b839c44c3e5a8fe26c15b7e87b2f32dfd2fc9fee1d13604347c9b38"}, - {file = "coverage-7.6.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e484e479860e00da1f005cd19d1c5d4a813324e5951319ac3f3eefb497cc549"}, - {file = "coverage-7.6.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c6c0f4d53ef603397fc894a895b960ecd7d44c727df42a8d500031716d4e8d2"}, - {file = "coverage-7.6.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:37be7b5ea3ff5b7c4a9db16074dc94523b5f10dd1f3b362a827af66a55198175"}, - {file = "coverage-7.6.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:43b32a06c47539fe275106b376658638b418c7cfdfff0e0259fbf877e845f14b"}, - {file = "coverage-7.6.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ee77c7bef0724165e795b6b7bf9c4c22a9b8468a6bdb9c6b4281293c6b22a90f"}, - {file = "coverage-7.6.3-cp311-cp311-win32.whl", hash = "sha256:43517e1f6b19f610a93d8227e47790722c8bf7422e46b365e0469fc3d3563d97"}, - {file = "coverage-7.6.3-cp311-cp311-win_amd64.whl", hash = "sha256:04f2189716e85ec9192df307f7c255f90e78b6e9863a03223c3b998d24a3c6c6"}, - {file = "coverage-7.6.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:27bd5f18d8f2879e45724b0ce74f61811639a846ff0e5c0395b7818fae87aec6"}, - {file = "coverage-7.6.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d546cfa78844b8b9c1c0533de1851569a13f87449897bbc95d698d1d3cb2a30f"}, - {file = "coverage-7.6.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9975442f2e7a5cfcf87299c26b5a45266ab0696348420049b9b94b2ad3d40234"}, - {file = "coverage-7.6.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:583049c63106c0555e3ae3931edab5669668bbef84c15861421b94e121878d3f"}, - {file = "coverage-7.6.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2341a78ae3a5ed454d524206a3fcb3cec408c2a0c7c2752cd78b606a2ff15af4"}, - {file = "coverage-7.6.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a4fb91d5f72b7e06a14ff4ae5be625a81cd7e5f869d7a54578fc271d08d58ae3"}, - {file = "coverage-7.6.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e279f3db904e3b55f520f11f983cc8dc8a4ce9b65f11692d4718ed021ec58b83"}, - {file = "coverage-7.6.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:aa23ce39661a3e90eea5f99ec59b763b7d655c2cada10729ed920a38bfc2b167"}, - {file = "coverage-7.6.3-cp312-cp312-win32.whl", hash = "sha256:52ac29cc72ee7e25ace7807249638f94c9b6a862c56b1df015d2b2e388e51dbd"}, - {file = "coverage-7.6.3-cp312-cp312-win_amd64.whl", hash = "sha256:40e8b1983080439d4802d80b951f4a93d991ef3261f69e81095a66f86cf3c3c6"}, - {file = "coverage-7.6.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9134032f5aa445ae591c2ba6991d10136a1f533b1d2fa8f8c21126468c5025c6"}, - {file = "coverage-7.6.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:99670790f21a96665a35849990b1df447993880bb6463a0a1d757897f30da929"}, - {file = "coverage-7.6.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2dc7d6b380ca76f5e817ac9eef0c3686e7834c8346bef30b041a4ad286449990"}, - {file = "coverage-7.6.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f7b26757b22faf88fcf232f5f0e62f6e0fd9e22a8a5d0d5016888cdfe1f6c1c4"}, - {file = "coverage-7.6.3-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c59d6a4a4633fad297f943c03d0d2569867bd5372eb5684befdff8df8522e39"}, - {file = "coverage-7.6.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f263b18692f8ed52c8de7f40a0751e79015983dbd77b16906e5b310a39d3ca21"}, - {file = "coverage-7.6.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:79644f68a6ff23b251cae1c82b01a0b51bc40c8468ca9585c6c4b1aeee570e0b"}, - {file = "coverage-7.6.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:71967c35828c9ff94e8c7d405469a1fb68257f686bca7c1ed85ed34e7c2529c4"}, - {file = "coverage-7.6.3-cp313-cp313-win32.whl", hash = "sha256:e266af4da2c1a4cbc6135a570c64577fd3e6eb204607eaff99d8e9b710003c6f"}, - {file = "coverage-7.6.3-cp313-cp313-win_amd64.whl", hash = "sha256:ea52bd218d4ba260399a8ae4bb6b577d82adfc4518b93566ce1fddd4a49d1dce"}, - {file = "coverage-7.6.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:8d4c6ea0f498c7c79111033a290d060c517853a7bcb2f46516f591dab628ddd3"}, - {file = "coverage-7.6.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:331b200ad03dbaa44151d74daeb7da2cf382db424ab923574f6ecca7d3b30de3"}, - {file = "coverage-7.6.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54356a76b67cf8a3085818026bb556545ebb8353951923b88292556dfa9f812d"}, - {file = "coverage-7.6.3-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ebec65f5068e7df2d49466aab9128510c4867e532e07cb6960075b27658dca38"}, - {file = "coverage-7.6.3-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d33a785ea8354c480515e781554d3be582a86297e41ccbea627a5c632647f2cd"}, - {file = "coverage-7.6.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f7ddb920106bbbbcaf2a274d56f46956bf56ecbde210d88061824a95bdd94e92"}, - {file = "coverage-7.6.3-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:70d24936ca6c15a3bbc91ee9c7fc661132c6f4c9d42a23b31b6686c05073bde5"}, - {file = "coverage-7.6.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c30e42ea11badb147f0d2e387115b15e2bd8205a5ad70d6ad79cf37f6ac08c91"}, - {file = "coverage-7.6.3-cp313-cp313t-win32.whl", hash = "sha256:365defc257c687ce3e7d275f39738dcd230777424117a6c76043459db131dd43"}, - {file = "coverage-7.6.3-cp313-cp313t-win_amd64.whl", hash = "sha256:23bb63ae3f4c645d2d82fa22697364b0046fbafb6261b258a58587441c5f7bd0"}, - {file = "coverage-7.6.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:da29ceabe3025a1e5a5aeeb331c5b1af686daab4ff0fb4f83df18b1180ea83e2"}, - {file = "coverage-7.6.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:df8c05a0f574d480947cba11b947dc41b1265d721c3777881da2fb8d3a1ddfba"}, - {file = "coverage-7.6.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec1e3b40b82236d100d259854840555469fad4db64f669ab817279eb95cd535c"}, - {file = "coverage-7.6.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b4adeb878a374126f1e5cf03b87f66279f479e01af0e9a654cf6d1509af46c40"}, - {file = "coverage-7.6.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:43d6a66e33b1455b98fc7312b124296dad97a2e191c80320587234a77b1b736e"}, - {file = "coverage-7.6.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1990b1f4e2c402beb317840030bb9f1b6a363f86e14e21b4212e618acdfce7f6"}, - {file = "coverage-7.6.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:12f9515d875859faedb4144fd38694a761cd2a61ef9603bf887b13956d0bbfbb"}, - {file = "coverage-7.6.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:99ded130555c021d99729fabd4ddb91a6f4cc0707df4b1daf912c7850c373b13"}, - {file = "coverage-7.6.3-cp39-cp39-win32.whl", hash = "sha256:c3a79f56dee9136084cf84a6c7c4341427ef36e05ae6415bf7d787c96ff5eaa3"}, - {file = "coverage-7.6.3-cp39-cp39-win_amd64.whl", hash = "sha256:aac7501ae73d4a02f4b7ac8fcb9dc55342ca98ffb9ed9f2dfb8a25d53eda0e4d"}, - {file = "coverage-7.6.3-pp39.pp310-none-any.whl", hash = "sha256:b9853509b4bf57ba7b1f99b9d866c422c9c5248799ab20e652bbb8a184a38181"}, - {file = "coverage-7.6.3.tar.gz", hash = "sha256:bb7d5fe92bd0dc235f63ebe9f8c6e0884f7360f88f3411bfed1350c872ef2054"}, + {file = "coverage-7.6.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5f8ae553cba74085db385d489c7a792ad66f7f9ba2ee85bfa508aeb84cf0ba07"}, + {file = "coverage-7.6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8165b796df0bd42e10527a3f493c592ba494f16ef3c8b531288e3d0d72c1f6f0"}, + {file = "coverage-7.6.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7c8b95bf47db6d19096a5e052ffca0a05f335bc63cef281a6e8fe864d450a72"}, + {file = "coverage-7.6.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ed9281d1b52628e81393f5eaee24a45cbd64965f41857559c2b7ff19385df51"}, + {file = "coverage-7.6.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0809082ee480bb8f7416507538243c8863ac74fd8a5d2485c46f0f7499f2b491"}, + {file = "coverage-7.6.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d541423cdd416b78626b55f123412fcf979d22a2c39fce251b350de38c15c15b"}, + {file = "coverage-7.6.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:58809e238a8a12a625c70450b48e8767cff9eb67c62e6154a642b21ddf79baea"}, + {file = "coverage-7.6.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c9b8e184898ed014884ca84c70562b4a82cbc63b044d366fedc68bc2b2f3394a"}, + {file = "coverage-7.6.4-cp310-cp310-win32.whl", hash = "sha256:6bd818b7ea14bc6e1f06e241e8234508b21edf1b242d49831831a9450e2f35fa"}, + {file = "coverage-7.6.4-cp310-cp310-win_amd64.whl", hash = "sha256:06babbb8f4e74b063dbaeb74ad68dfce9186c595a15f11f5d5683f748fa1d172"}, + {file = "coverage-7.6.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:73d2b73584446e66ee633eaad1a56aad577c077f46c35ca3283cd687b7715b0b"}, + {file = "coverage-7.6.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:51b44306032045b383a7a8a2c13878de375117946d68dcb54308111f39775a25"}, + {file = "coverage-7.6.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b3fb02fe73bed561fa12d279a417b432e5b50fe03e8d663d61b3d5990f29546"}, + {file = "coverage-7.6.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed8fe9189d2beb6edc14d3ad19800626e1d9f2d975e436f84e19efb7fa19469b"}, + {file = "coverage-7.6.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b369ead6527d025a0fe7bd3864e46dbee3aa8f652d48df6174f8d0bac9e26e0e"}, + {file = "coverage-7.6.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ade3ca1e5f0ff46b678b66201f7ff477e8fa11fb537f3b55c3f0568fbfe6e718"}, + {file = "coverage-7.6.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:27fb4a050aaf18772db513091c9c13f6cb94ed40eacdef8dad8411d92d9992db"}, + {file = "coverage-7.6.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4f704f0998911abf728a7783799444fcbbe8261c4a6c166f667937ae6a8aa522"}, + {file = "coverage-7.6.4-cp311-cp311-win32.whl", hash = "sha256:29155cd511ee058e260db648b6182c419422a0d2e9a4fa44501898cf918866cf"}, + {file = "coverage-7.6.4-cp311-cp311-win_amd64.whl", hash = "sha256:8902dd6a30173d4ef09954bfcb24b5d7b5190cf14a43170e386979651e09ba19"}, + {file = "coverage-7.6.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:12394842a3a8affa3ba62b0d4ab7e9e210c5e366fbac3e8b2a68636fb19892c2"}, + {file = "coverage-7.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2b6b4c83d8e8ea79f27ab80778c19bc037759aea298da4b56621f4474ffeb117"}, + {file = "coverage-7.6.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d5b8007f81b88696d06f7df0cb9af0d3b835fe0c8dbf489bad70b45f0e45613"}, + {file = "coverage-7.6.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b57b768feb866f44eeed9f46975f3d6406380275c5ddfe22f531a2bf187eda27"}, + {file = "coverage-7.6.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5915fcdec0e54ee229926868e9b08586376cae1f5faa9bbaf8faf3561b393d52"}, + {file = "coverage-7.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0b58c672d14f16ed92a48db984612f5ce3836ae7d72cdd161001cc54512571f2"}, + {file = "coverage-7.6.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:2fdef0d83a2d08d69b1f2210a93c416d54e14d9eb398f6ab2f0a209433db19e1"}, + {file = "coverage-7.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8cf717ee42012be8c0cb205dbbf18ffa9003c4cbf4ad078db47b95e10748eec5"}, + {file = "coverage-7.6.4-cp312-cp312-win32.whl", hash = "sha256:7bb92c539a624cf86296dd0c68cd5cc286c9eef2d0c3b8b192b604ce9de20a17"}, + {file = "coverage-7.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:1032e178b76a4e2b5b32e19d0fd0abbce4b58e77a1ca695820d10e491fa32b08"}, + {file = "coverage-7.6.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:023bf8ee3ec6d35af9c1c6ccc1d18fa69afa1cb29eaac57cb064dbb262a517f9"}, + {file = "coverage-7.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b0ac3d42cb51c4b12df9c5f0dd2f13a4f24f01943627120ec4d293c9181219ba"}, + {file = "coverage-7.6.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8fe4984b431f8621ca53d9380901f62bfb54ff759a1348cd140490ada7b693c"}, + {file = "coverage-7.6.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5fbd612f8a091954a0c8dd4c0b571b973487277d26476f8480bfa4b2a65b5d06"}, + {file = "coverage-7.6.4-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dacbc52de979f2823a819571f2e3a350a7e36b8cb7484cdb1e289bceaf35305f"}, + {file = "coverage-7.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:dab4d16dfef34b185032580e2f2f89253d302facba093d5fa9dbe04f569c4f4b"}, + {file = "coverage-7.6.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:862264b12ebb65ad8d863d51f17758b1684560b66ab02770d4f0baf2ff75da21"}, + {file = "coverage-7.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5beb1ee382ad32afe424097de57134175fea3faf847b9af002cc7895be4e2a5a"}, + {file = "coverage-7.6.4-cp313-cp313-win32.whl", hash = "sha256:bf20494da9653f6410213424f5f8ad0ed885e01f7e8e59811f572bdb20b8972e"}, + {file = "coverage-7.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:182e6cd5c040cec0a1c8d415a87b67ed01193ed9ad458ee427741c7d8513d963"}, + {file = "coverage-7.6.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a181e99301a0ae128493a24cfe5cfb5b488c4e0bf2f8702091473d033494d04f"}, + {file = "coverage-7.6.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:df57bdbeffe694e7842092c5e2e0bc80fff7f43379d465f932ef36f027179806"}, + {file = "coverage-7.6.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bcd1069e710600e8e4cf27f65c90c7843fa8edfb4520fb0ccb88894cad08b11"}, + {file = "coverage-7.6.4-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99b41d18e6b2a48ba949418db48159d7a2e81c5cc290fc934b7d2380515bd0e3"}, + {file = "coverage-7.6.4-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6b1e54712ba3474f34b7ef7a41e65bd9037ad47916ccb1cc78769bae324c01a"}, + {file = "coverage-7.6.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:53d202fd109416ce011578f321460795abfe10bb901b883cafd9b3ef851bacfc"}, + {file = "coverage-7.6.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:c48167910a8f644671de9f2083a23630fbf7a1cb70ce939440cd3328e0919f70"}, + {file = "coverage-7.6.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cc8ff50b50ce532de2fa7a7daae9dd12f0a699bfcd47f20945364e5c31799fef"}, + {file = "coverage-7.6.4-cp313-cp313t-win32.whl", hash = "sha256:b8d3a03d9bfcaf5b0141d07a88456bb6a4c3ce55c080712fec8418ef3610230e"}, + {file = "coverage-7.6.4-cp313-cp313t-win_amd64.whl", hash = "sha256:f3ddf056d3ebcf6ce47bdaf56142af51bb7fad09e4af310241e9db7a3a8022e1"}, + {file = "coverage-7.6.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9cb7fa111d21a6b55cbf633039f7bc2749e74932e3aa7cb7333f675a58a58bf3"}, + {file = "coverage-7.6.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:11a223a14e91a4693d2d0755c7a043db43d96a7450b4f356d506c2562c48642c"}, + {file = "coverage-7.6.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a413a096c4cbac202433c850ee43fa326d2e871b24554da8327b01632673a076"}, + {file = "coverage-7.6.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00a1d69c112ff5149cabe60d2e2ee948752c975d95f1e1096742e6077affd376"}, + {file = "coverage-7.6.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f76846299ba5c54d12c91d776d9605ae33f8ae2b9d1d3c3703cf2db1a67f2c0"}, + {file = "coverage-7.6.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fe439416eb6380de434886b00c859304338f8b19f6f54811984f3420a2e03858"}, + {file = "coverage-7.6.4-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:0294ca37f1ba500667b1aef631e48d875ced93ad5e06fa665a3295bdd1d95111"}, + {file = "coverage-7.6.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6f01ba56b1c0e9d149f9ac85a2f999724895229eb36bd997b61e62999e9b0901"}, + {file = "coverage-7.6.4-cp39-cp39-win32.whl", hash = "sha256:bc66f0bf1d7730a17430a50163bb264ba9ded56739112368ba985ddaa9c3bd09"}, + {file = "coverage-7.6.4-cp39-cp39-win_amd64.whl", hash = "sha256:c481b47f6b5845064c65a7bc78bc0860e635a9b055af0df46fdf1c58cebf8e8f"}, + {file = "coverage-7.6.4-pp39.pp310-none-any.whl", hash = "sha256:3c65d37f3a9ebb703e710befdc489a38683a5b152242664b973a7b7b22348a4e"}, + {file = "coverage-7.6.4.tar.gz", hash = "sha256:29fc0f17b1d3fea332f8001d4558f8214af7f1d87a345f3a133c901d60347c73"}, ] +[package.dependencies] +tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} + [package.extras] toml = ["tomli"] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "dill" version = "0.3.9" @@ -379,17 +551,123 @@ files = [ graph = ["objgraph (>=1.7.2)"] profile = ["gprof2dot (>=2022.7.29)"] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + +[[package]] +name = "distlib" +version = "0.3.9" +description = "Distribution utilities" +optional = false +python-versions = "*" +files = [ + {file = "distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87"}, + {file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"}, +] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "docutils" -version = "0.16" +version = "0.21.2" description = "Docutils -- Python Documentation Utilities" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.9" +files = [ + {file = "docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2"}, + {file = "docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f"}, +] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + +[[package]] +name = "exceptiongroup" +version = "1.2.2" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, + {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + +[[package]] +name = "filelock" +version = "3.16.1" +description = "A platform independent file lock." +optional = false +python-versions = ">=3.8" +files = [ + {file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"}, + {file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"}, +] + +[package.extras] +docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4.1)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"] +typing = ["typing-extensions (>=4.12.2)"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + +[[package]] +name = "flake8" +version = "7.1.1" +description = "the modular source code checker: pep8 pyflakes and co" +optional = false +python-versions = ">=3.8.1" +files = [ + {file = "flake8-7.1.1-py2.py3-none-any.whl", hash = "sha256:597477df7860daa5aa0fdd84bf5208a043ab96b8e96ab708770ae0364dd03213"}, + {file = "flake8-7.1.1.tar.gz", hash = "sha256:049d058491e228e03e67b390f311bbf88fce2dbaa8fa673e7aea87b7198b8d38"}, +] + +[package.dependencies] +mccabe = ">=0.7.0,<0.8.0" +pycodestyle = ">=2.12.0,<2.13.0" +pyflakes = ">=3.2.0,<3.3.0" + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + +[[package]] +name = "identify" +version = "2.6.1" +description = "File identification library for Python" +optional = false +python-versions = ">=3.8" files = [ - {file = "docutils-0.16-py2.py3-none-any.whl", hash = "sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af"}, - {file = "docutils-0.16.tar.gz", hash = "sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc"}, + {file = "identify-2.6.1-py2.py3-none-any.whl", hash = "sha256:53863bcac7caf8d2ed85bd20312ea5dcfc22226800f6d6881f232d861db5a8f0"}, + {file = "identify-2.6.1.tar.gz", hash = "sha256:91478c5fb7c3aac5ff7bf9b4344f803843dc586832d5f110d672b19aa1984c98"}, ] +[package.extras] +license = ["ukkonen"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "idna" version = "3.10" @@ -404,6 +682,11 @@ files = [ [package.extras] all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "imagesize" version = "1.4.1" @@ -415,6 +698,11 @@ files = [ {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, ] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "importlib-metadata" version = "8.5.0" @@ -438,6 +726,11 @@ perf = ["ipython"] test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] type = ["pytest-mypy"] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "iniconfig" version = "2.0.0" @@ -449,6 +742,11 @@ files = [ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "isort" version = "5.13.2" @@ -463,6 +761,11 @@ files = [ [package.extras] colors = ["colorama (>=0.4.6)"] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "jinja2" version = "3.1.4" @@ -480,6 +783,11 @@ MarkupSafe = ">=2.0" [package.extras] i18n = ["Babel (>=2.7)"] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "jmespath" version = "1.0.1" @@ -491,76 +799,115 @@ files = [ {file = "jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe"}, ] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + +[[package]] +name = "markdown-it-py" +version = "3.0.0" +description = "Python port of markdown-it. Markdown parsing, done right!" +optional = false +python-versions = ">=3.8" +files = [ + {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, + {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, +] + +[package.dependencies] +mdurl = ">=0.1,<1.0" + +[package.extras] +benchmarking = ["psutil", "pytest", "pytest-benchmark"] +code-style = ["pre-commit (>=3.0,<4.0)"] +compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] +linkify = ["linkify-it-py (>=1,<3)"] +plugins = ["mdit-py-plugins"] +profiling = ["gprof2dot"] +rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "markupsafe" -version = "3.0.1" +version = "3.0.2" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.9" files = [ - {file = "MarkupSafe-3.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:db842712984e91707437461930e6011e60b39136c7331e971952bb30465bc1a1"}, - {file = "MarkupSafe-3.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3ffb4a8e7d46ed96ae48805746755fadd0909fea2306f93d5d8233ba23dda12a"}, - {file = "MarkupSafe-3.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67c519635a4f64e495c50e3107d9b4075aec33634272b5db1cde839e07367589"}, - {file = "MarkupSafe-3.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48488d999ed50ba8d38c581d67e496f955821dc183883550a6fbc7f1aefdc170"}, - {file = "MarkupSafe-3.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f31ae06f1328595d762c9a2bf29dafd8621c7d3adc130cbb46278079758779ca"}, - {file = "MarkupSafe-3.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:80fcbf3add8790caddfab6764bde258b5d09aefbe9169c183f88a7410f0f6dea"}, - {file = "MarkupSafe-3.0.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3341c043c37d78cc5ae6e3e305e988532b072329639007fd408a476642a89fd6"}, - {file = "MarkupSafe-3.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cb53e2a99df28eee3b5f4fea166020d3ef9116fdc5764bc5117486e6d1211b25"}, - {file = "MarkupSafe-3.0.1-cp310-cp310-win32.whl", hash = "sha256:db15ce28e1e127a0013dfb8ac243a8e392db8c61eae113337536edb28bdc1f97"}, - {file = "MarkupSafe-3.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:4ffaaac913c3f7345579db4f33b0020db693f302ca5137f106060316761beea9"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:26627785a54a947f6d7336ce5963569b5d75614619e75193bdb4e06e21d447ad"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b954093679d5750495725ea6f88409946d69cfb25ea7b4c846eef5044194f583"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:973a371a55ce9ed333a3a0f8e0bcfae9e0d637711534bcb11e130af2ab9334e7"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:244dbe463d5fb6d7ce161301a03a6fe744dac9072328ba9fc82289238582697b"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d98e66a24497637dd31ccab090b34392dddb1f2f811c4b4cd80c230205c074a3"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ad91738f14eb8da0ff82f2acd0098b6257621410dcbd4df20aaa5b4233d75a50"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7044312a928a66a4c2a22644147bc61a199c1709712069a344a3fb5cfcf16915"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a4792d3b3a6dfafefdf8e937f14906a51bd27025a36f4b188728a73382231d91"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-win32.whl", hash = "sha256:fa7d686ed9883f3d664d39d5a8e74d3c5f63e603c2e3ff0abcba23eac6542635"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:9ba25a71ebf05b9bb0e2ae99f8bc08a07ee8e98c612175087112656ca0f5c8bf"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8ae369e84466aa70f3154ee23c1451fda10a8ee1b63923ce76667e3077f2b0c4"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40f1e10d51c92859765522cbd79c5c8989f40f0419614bcdc5015e7b6bf97fc5"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a4cb365cb49b750bdb60b846b0c0bc49ed62e59a76635095a179d440540c346"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee3941769bd2522fe39222206f6dd97ae83c442a94c90f2b7a25d847d40f4729"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62fada2c942702ef8952754abfc1a9f7658a4d5460fabe95ac7ec2cbe0d02abc"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4c2d64fdba74ad16138300815cfdc6ab2f4647e23ced81f59e940d7d4a1469d9"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:fb532dd9900381d2e8f48172ddc5a59db4c445a11b9fab40b3b786da40d3b56b"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0f84af7e813784feb4d5e4ff7db633aba6c8ca64a833f61d8e4eade234ef0c38"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-win32.whl", hash = "sha256:cbf445eb5628981a80f54087f9acdbf84f9b7d862756110d172993b9a5ae81aa"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:a10860e00ded1dd0a65b83e717af28845bb7bd16d8ace40fe5531491de76b79f"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e81c52638315ff4ac1b533d427f50bc0afc746deb949210bc85f05d4f15fd772"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:312387403cd40699ab91d50735ea7a507b788091c416dd007eac54434aee51da"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ae99f31f47d849758a687102afdd05bd3d3ff7dbab0a8f1587981b58a76152a"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c97ff7fedf56d86bae92fa0a646ce1a0ec7509a7578e1ed238731ba13aabcd1c"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7420ceda262dbb4b8d839a4ec63d61c261e4e77677ed7c66c99f4e7cb5030dd"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:45d42d132cff577c92bfba536aefcfea7e26efb975bd455db4e6602f5c9f45e7"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4c8817557d0de9349109acb38b9dd570b03cc5014e8aabf1cbddc6e81005becd"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6a54c43d3ec4cf2a39f4387ad044221c66a376e58c0d0e971d47c475ba79c6b5"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-win32.whl", hash = "sha256:c91b394f7601438ff79a4b93d16be92f216adb57d813a78be4446fe0f6bc2d8c"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:fe32482b37b4b00c7a52a07211b479653b7fe4f22b2e481b9a9b099d8a430f2f"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:17b2aea42a7280db02ac644db1d634ad47dcc96faf38ab304fe26ba2680d359a"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:852dc840f6d7c985603e60b5deaae1d89c56cb038b577f6b5b8c808c97580f1d"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0778de17cff1acaeccc3ff30cd99a3fd5c50fc58ad3d6c0e0c4c58092b859396"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:800100d45176652ded796134277ecb13640c1a537cad3b8b53da45aa96330453"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d06b24c686a34c86c8c1fba923181eae6b10565e4d80bdd7bc1c8e2f11247aa4"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:33d1c36b90e570ba7785dacd1faaf091203d9942bc036118fab8110a401eb1a8"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:beeebf760a9c1f4c07ef6a53465e8cfa776ea6a2021eda0d0417ec41043fe984"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:bbde71a705f8e9e4c3e9e33db69341d040c827c7afa6789b14c6e16776074f5a"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-win32.whl", hash = "sha256:82b5dba6eb1bcc29cc305a18a3c5365d2af06ee71b123216416f7e20d2a84e5b"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-win_amd64.whl", hash = "sha256:730d86af59e0e43ce277bb83970530dd223bf7f2a838e086b50affa6ec5f9295"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4935dd7883f1d50e2ffecca0aa33dc1946a94c8f3fdafb8df5c330e48f71b132"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e9393357f19954248b00bed7c56f29a25c930593a77630c719653d51e7669c2a"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40621d60d0e58aa573b68ac5e2d6b20d44392878e0bfc159012a5787c4e35bc8"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f94190df587738280d544971500b9cafc9b950d32efcb1fba9ac10d84e6aa4e6"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b6a387d61fe41cdf7ea95b38e9af11cfb1a63499af2759444b99185c4ab33f5b"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8ad4ad1429cd4f315f32ef263c1342166695fad76c100c5d979c45d5570ed58b"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e24bfe89c6ac4c31792793ad9f861b8f6dc4546ac6dc8f1c9083c7c4f2b335cd"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2a4b34a8d14649315c4bc26bbfa352663eb51d146e35eef231dd739d54a5430a"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-win32.whl", hash = "sha256:242d6860f1fd9191aef5fae22b51c5c19767f93fb9ead4d21924e0bcb17619d8"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:93e8248d650e7e9d49e8251f883eed60ecbc0e8ffd6349e18550925e31bd029b"}, - {file = "markupsafe-3.0.1.tar.gz", hash = "sha256:3e683ee4f5d0fa2dde4db77ed8dd8a876686e3fc417655c2ece9a90576905344"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a"}, + {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"}, ] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "mccabe" version = "0.7.0" @@ -572,57 +919,108 @@ files = [ {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, ] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + +[[package]] +name = "mchbuild" +version = "0.4.8" +description = "A sample Python project" +optional = false +python-versions = ">=3.6" +files = [ + {file = "mchbuild-0.4.8-py3-none-any.whl", hash = "sha256:65f72da684518bf584664a47b7a1acdba95690ddc21d4858a1881fc8e3eed608"}, + {file = "mchbuild-0.4.8.tar.gz", hash = "sha256:b1852b2f786fafbe404a5de763d4b7e09446a3af69525eb2720615182ad84a85"}, +] + +[package.dependencies] +PyYAML = "*" +types-PyYAML = "*" + +[package.extras] +dev = ["pydata-sphinx-theme", "sphinx", "sphinx-autoapi"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + +[[package]] +name = "mdurl" +version = "0.1.2" +description = "Markdown URL utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, + {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, +] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "mypy" -version = "1.12.0" +version = "1.13.0" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" files = [ - {file = "mypy-1.12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4397081e620dc4dc18e2f124d5e1d2c288194c2c08df6bdb1db31c38cd1fe1ed"}, - {file = "mypy-1.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:684a9c508a283f324804fea3f0effeb7858eb03f85c4402a967d187f64562469"}, - {file = "mypy-1.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6cabe4cda2fa5eca7ac94854c6c37039324baaa428ecbf4de4567279e9810f9e"}, - {file = "mypy-1.12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:060a07b10e999ac9e7fa249ce2bdcfa9183ca2b70756f3bce9df7a92f78a3c0a"}, - {file = "mypy-1.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:0eff042d7257f39ba4ca06641d110ca7d2ad98c9c1fb52200fe6b1c865d360ff"}, - {file = "mypy-1.12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4b86de37a0da945f6d48cf110d5206c5ed514b1ca2614d7ad652d4bf099c7de7"}, - {file = "mypy-1.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:20c7c5ce0c1be0b0aea628374e6cf68b420bcc772d85c3c974f675b88e3e6e57"}, - {file = "mypy-1.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a64ee25f05fc2d3d8474985c58042b6759100a475f8237da1f4faf7fcd7e6309"}, - {file = "mypy-1.12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:faca7ab947c9f457a08dcb8d9a8664fd438080e002b0fa3e41b0535335edcf7f"}, - {file = "mypy-1.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:5bc81701d52cc8767005fdd2a08c19980de9ec61a25dbd2a937dfb1338a826f9"}, - {file = "mypy-1.12.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8462655b6694feb1c99e433ea905d46c478041a8b8f0c33f1dab00ae881b2164"}, - {file = "mypy-1.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:923ea66d282d8af9e0f9c21ffc6653643abb95b658c3a8a32dca1eff09c06475"}, - {file = "mypy-1.12.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1ebf9e796521f99d61864ed89d1fb2926d9ab6a5fab421e457cd9c7e4dd65aa9"}, - {file = "mypy-1.12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e478601cc3e3fa9d6734d255a59c7a2e5c2934da4378f3dd1e3411ea8a248642"}, - {file = "mypy-1.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:c72861b7139a4f738344faa0e150834467521a3fba42dc98264e5aa9507dd601"}, - {file = "mypy-1.12.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:52b9e1492e47e1790360a43755fa04101a7ac72287b1a53ce817f35899ba0521"}, - {file = "mypy-1.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:48d3e37dd7d9403e38fa86c46191de72705166d40b8c9f91a3de77350daa0893"}, - {file = "mypy-1.12.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2f106db5ccb60681b622ac768455743ee0e6a857724d648c9629a9bd2ac3f721"}, - {file = "mypy-1.12.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:233e11b3f73ee1f10efada2e6da0f555b2f3a5316e9d8a4a1224acc10e7181d3"}, - {file = "mypy-1.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:4ae8959c21abcf9d73aa6c74a313c45c0b5a188752bf37dace564e29f06e9c1b"}, - {file = "mypy-1.12.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:eafc1b7319b40ddabdc3db8d7d48e76cfc65bbeeafaa525a4e0fa6b76175467f"}, - {file = "mypy-1.12.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9b9ce1ad8daeb049c0b55fdb753d7414260bad8952645367e70ac91aec90e07e"}, - {file = "mypy-1.12.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bfe012b50e1491d439172c43ccb50db66d23fab714d500b57ed52526a1020bb7"}, - {file = "mypy-1.12.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2c40658d4fa1ab27cb53d9e2f1066345596af2f8fe4827defc398a09c7c9519b"}, - {file = "mypy-1.12.0-cp38-cp38-win_amd64.whl", hash = "sha256:dee78a8b9746c30c1e617ccb1307b351ded57f0de0d287ca6276378d770006c0"}, - {file = "mypy-1.12.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6b5df6c8a8224f6b86746bda716bbe4dbe0ce89fd67b1fa4661e11bfe38e8ec8"}, - {file = "mypy-1.12.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5feee5c74eb9749e91b77f60b30771563327329e29218d95bedbe1257e2fe4b0"}, - {file = "mypy-1.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:77278e8c6ffe2abfba6db4125de55f1024de9a323be13d20e4f73b8ed3402bd1"}, - {file = "mypy-1.12.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:dcfb754dea911039ac12434d1950d69a2f05acd4d56f7935ed402be09fad145e"}, - {file = "mypy-1.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:06de0498798527451ffb60f68db0d368bd2bae2bbfb5237eae616d4330cc87aa"}, - {file = "mypy-1.12.0-py3-none-any.whl", hash = "sha256:fd313226af375d52e1e36c383f39bf3836e1f192801116b31b090dfcd3ec5266"}, - {file = "mypy-1.12.0.tar.gz", hash = "sha256:65a22d87e757ccd95cbbf6f7e181e6caa87128255eb2b6be901bb71b26d8a99d"}, + {file = "mypy-1.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6607e0f1dd1fb7f0aca14d936d13fd19eba5e17e1cd2a14f808fa5f8f6d8f60a"}, + {file = "mypy-1.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8a21be69bd26fa81b1f80a61ee7ab05b076c674d9b18fb56239d72e21d9f4c80"}, + {file = "mypy-1.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b2353a44d2179846a096e25691d54d59904559f4232519d420d64da6828a3a7"}, + {file = "mypy-1.13.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0730d1c6a2739d4511dc4253f8274cdd140c55c32dfb0a4cf8b7a43f40abfa6f"}, + {file = "mypy-1.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:c5fc54dbb712ff5e5a0fca797e6e0aa25726c7e72c6a5850cfd2adbc1eb0a372"}, + {file = "mypy-1.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:581665e6f3a8a9078f28d5502f4c334c0c8d802ef55ea0e7276a6e409bc0d82d"}, + {file = "mypy-1.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3ddb5b9bf82e05cc9a627e84707b528e5c7caaa1c55c69e175abb15a761cec2d"}, + {file = "mypy-1.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:20c7ee0bc0d5a9595c46f38beb04201f2620065a93755704e141fcac9f59db2b"}, + {file = "mypy-1.13.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3790ded76f0b34bc9c8ba4def8f919dd6a46db0f5a6610fb994fe8efdd447f73"}, + {file = "mypy-1.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:51f869f4b6b538229c1d1bcc1dd7d119817206e2bc54e8e374b3dfa202defcca"}, + {file = "mypy-1.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5c7051a3461ae84dfb5dd15eff5094640c61c5f22257c8b766794e6dd85e72d5"}, + {file = "mypy-1.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:39bb21c69a5d6342f4ce526e4584bc5c197fd20a60d14a8624d8743fffb9472e"}, + {file = "mypy-1.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:164f28cb9d6367439031f4c81e84d3ccaa1e19232d9d05d37cb0bd880d3f93c2"}, + {file = "mypy-1.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a4c1bfcdbce96ff5d96fc9b08e3831acb30dc44ab02671eca5953eadad07d6d0"}, + {file = "mypy-1.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:a0affb3a79a256b4183ba09811e3577c5163ed06685e4d4b46429a271ba174d2"}, + {file = "mypy-1.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a7b44178c9760ce1a43f544e595d35ed61ac2c3de306599fa59b38a6048e1aa7"}, + {file = "mypy-1.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5d5092efb8516d08440e36626f0153b5006d4088c1d663d88bf79625af3d1d62"}, + {file = "mypy-1.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2904956dac40ced10931ac967ae63c5089bd498542194b436eb097a9f77bc8"}, + {file = "mypy-1.13.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:7bfd8836970d33c2105562650656b6846149374dc8ed77d98424b40b09340ba7"}, + {file = "mypy-1.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:9f73dba9ec77acb86457a8fc04b5239822df0c14a082564737833d2963677dbc"}, + {file = "mypy-1.13.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:100fac22ce82925f676a734af0db922ecfea991e1d7ec0ceb1e115ebe501301a"}, + {file = "mypy-1.13.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7bcb0bb7f42a978bb323a7c88f1081d1b5dee77ca86f4100735a6f541299d8fb"}, + {file = "mypy-1.13.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bde31fc887c213e223bbfc34328070996061b0833b0a4cfec53745ed61f3519b"}, + {file = "mypy-1.13.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:07de989f89786f62b937851295ed62e51774722e5444a27cecca993fc3f9cd74"}, + {file = "mypy-1.13.0-cp38-cp38-win_amd64.whl", hash = "sha256:4bde84334fbe19bad704b3f5b78c4abd35ff1026f8ba72b29de70dda0916beb6"}, + {file = "mypy-1.13.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0246bcb1b5de7f08f2826451abd947bf656945209b140d16ed317f65a17dc7dc"}, + {file = "mypy-1.13.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7f5b7deae912cf8b77e990b9280f170381fdfbddf61b4ef80927edd813163732"}, + {file = "mypy-1.13.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7029881ec6ffb8bc233a4fa364736789582c738217b133f1b55967115288a2bc"}, + {file = "mypy-1.13.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3e38b980e5681f28f033f3be86b099a247b13c491f14bb8b1e1e134d23bb599d"}, + {file = "mypy-1.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:a6789be98a2017c912ae6ccb77ea553bbaf13d27605d2ca20a76dfbced631b24"}, + {file = "mypy-1.13.0-py3-none-any.whl", hash = "sha256:9c250883f9fd81d212e0952c92dbfcc96fc237f4b7c92f56ac81fd48460b3e5a"}, + {file = "mypy-1.13.0.tar.gz", hash = "sha256:0291a61b6fbf3e6673e3405cfcc0e7650bebc7939659fdca2702958038bd835e"}, ] [package.dependencies] mypy-extensions = ">=1.0.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} typing-extensions = ">=4.6.0" [package.extras] dmypy = ["psutil (>=4.0)"] +faster-cache = ["orjson"] install-types = ["pip"] mypyc = ["setuptools (>=50)"] reports = ["lxml"] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "mypy-extensions" version = "1.0.0" @@ -634,6 +1032,27 @@ files = [ {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + +[[package]] +name = "nodeenv" +version = "1.9.1" +description = "Node.js virtual environment builder" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" +files = [ + {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, + {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, +] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "packaging" version = "24.1" @@ -645,6 +1064,27 @@ files = [ {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, ] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + +[[package]] +name = "pathspec" +version = "0.12.1" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, +] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "platformdirs" version = "4.3.6" @@ -661,6 +1101,11 @@ docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-a test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"] type = ["mypy (>=1.11.2)"] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "pluggy" version = "1.5.0" @@ -676,17 +1121,50 @@ files = [ dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + +[[package]] +name = "pre-commit" +version = "4.0.1" +description = "A framework for managing and maintaining multi-language pre-commit hooks." +optional = false +python-versions = ">=3.9" +files = [ + {file = "pre_commit-4.0.1-py2.py3-none-any.whl", hash = "sha256:efde913840816312445dc98787724647c65473daefe420785f885e8ed9a06878"}, + {file = "pre_commit-4.0.1.tar.gz", hash = "sha256:80905ac375958c0444c65e9cebebd948b3cdb518f335a091a670a89d652139d2"}, +] + +[package.dependencies] +cfgv = ">=2.0.0" +identify = ">=1.0.0" +nodeenv = ">=0.11.1" +pyyaml = ">=5.1" +virtualenv = ">=20.10.0" + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] -name = "pyasn1" -version = "0.6.1" -description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" +name = "pycodestyle" +version = "2.12.1" +description = "Python style guide checker" optional = false python-versions = ">=3.8" files = [ - {file = "pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629"}, - {file = "pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034"}, + {file = "pycodestyle-2.12.1-py2.py3-none-any.whl", hash = "sha256:46f0fb92069a7c28ab7bb558f05bfc0110dac69a0cd23c61ea0040283a9d78b3"}, + {file = "pycodestyle-2.12.1.tar.gz", hash = "sha256:6838eae08bbce4f6accd5d5572075c63626a15ee3e6f842df996bf62f6d73521"}, ] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "pydantic" version = "2.9.2" @@ -707,6 +1185,11 @@ typing-extensions = {version = ">=4.6.1", markers = "python_version < \"3.13\""} email = ["email-validator (>=2.0.0)"] timezone = ["tzdata"] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "pydantic-core" version = "2.23.4" @@ -808,15 +1291,20 @@ files = [ [package.dependencies] typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "pydantic-settings" -version = "2.5.2" +version = "2.6.0" description = "Settings management using Pydantic" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic_settings-2.5.2-py3-none-any.whl", hash = "sha256:2c912e55fd5794a59bf8c832b9de832dcfdf4778d79ff79b708744eed499a907"}, - {file = "pydantic_settings-2.5.2.tar.gz", hash = "sha256:f90b139682bee4d2065273d5185d71d37ea46cfe57e1b5ae184fc6a0b2484ca0"}, + {file = "pydantic_settings-2.6.0-py3-none-any.whl", hash = "sha256:4a819166f119b74d7f8c765196b165f95cc7487ce58ea27dec8a5a26be0970e0"}, + {file = "pydantic_settings-2.6.0.tar.gz", hash = "sha256:44a1804abffac9e6a30372bb45f6cafab945ef5af25e66b1c634c01dd39e0188"}, ] [package.dependencies] @@ -828,6 +1316,11 @@ azure-key-vault = ["azure-identity (>=1.16.0)", "azure-keyvault-secrets (>=4.8.0 toml = ["tomli (>=2.0.1)"] yaml = ["pyyaml (>=6.0.1)"] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "pydata-sphinx-theme" version = "0.15.4" @@ -856,6 +1349,27 @@ doc = ["ablog (>=0.11.8)", "colorama", "graphviz", "ipykernel", "ipyleaflet", "i i18n = ["Babel", "jinja2"] test = ["pytest", "pytest-cov", "pytest-regressions", "sphinx[test]"] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + +[[package]] +name = "pyflakes" +version = "3.2.0" +description = "passive checker of Python programs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"}, + {file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"}, +] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "pygments" version = "2.18.0" @@ -870,6 +1384,11 @@ files = [ [package.extras] windows-terminal = ["colorama (>=0.4.6)"] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "pylint" version = "3.3.1" @@ -884,16 +1403,26 @@ files = [ [package.dependencies] astroid = ">=3.3.4,<=3.4.0-dev0" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} -dill = {version = ">=0.3.7", markers = "python_version >= \"3.12\""} +dill = [ + {version = ">=0.2", markers = "python_version < \"3.11\""}, + {version = ">=0.3.6", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, + {version = ">=0.3.7", markers = "python_version >= \"3.12\""}, +] isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" mccabe = ">=0.6,<0.8" platformdirs = ">=2.2.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} tomlkit = ">=0.10.1" [package.extras] spelling = ["pyenchant (>=3.2,<4.0)"] testutils = ["gitpython (>3)"] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "pytest" version = "8.3.3" @@ -907,13 +1436,20 @@ files = [ [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" packaging = "*" pluggy = ">=1.5,<2" +tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "pytest-cov" version = "5.0.0" @@ -932,6 +1468,11 @@ pytest = ">=4.6" [package.extras] testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "python-dateutil" version = "2.9.0.post0" @@ -946,6 +1487,11 @@ files = [ [package.dependencies] six = ">=1.5" +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "python-dotenv" version = "1.0.1" @@ -960,6 +1506,11 @@ files = [ [package.extras] cli = ["click (>=5.0)"] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "python-json-logger" version = "2.0.7" @@ -971,6 +1522,11 @@ files = [ {file = "python_json_logger-2.0.7-py3-none-any.whl", hash = "sha256:f380b826a991ebbe3de4d897aeec42760035ac760345e57b812938dc8b35e2bd"}, ] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "pyyaml" version = "6.0.2" @@ -1033,6 +1589,11 @@ files = [ {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, ] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "requests" version = "2.32.3" @@ -1054,26 +1615,98 @@ urllib3 = ">=1.21.1,<3" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + +[[package]] +name = "rich" +version = "13.9.3" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "rich-13.9.3-py3-none-any.whl", hash = "sha256:9836f5096eb2172c9e77df411c1b009bace4193d6a481d534fea75ebba758283"}, + {file = "rich-13.9.3.tar.gz", hash = "sha256:bc1e01b899537598cf02579d2b9f4a415104d3fc439313a7a2c165d76557a08e"}, +] + +[package.dependencies] +markdown-it-py = ">=2.2.0" +pygments = ">=2.13.0,<3.0.0" +typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.11\""} + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<9)"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + +[[package]] +name = "rstcheck" +version = "6.2.4" +description = "Checks syntax of reStructuredText and code blocks nested within it" +optional = false +python-versions = ">=3.8" +files = [ + {file = "rstcheck-6.2.4-py3-none-any.whl", hash = "sha256:23de2575ba0af1adcddea87a20d69187f0fb9dd8270f59eb98d63461c95375a7"}, + {file = "rstcheck-6.2.4.tar.gz", hash = "sha256:384942563dfbfcc85903a587ecf050447217c46b51e266ed3fe51371bc599015"}, +] + +[package.dependencies] +rstcheck-core = ">=1.1" +typer = ">=0.12.0" + +[package.extras] +dev = ["rstcheck[docs,sphinx,testing,toml,type-check]", "tox (>=3.15)"] +docs = ["myst-parser (>=3)", "sphinx (>=5.0)", "sphinx-autobuild (>=2021.3.14)", "sphinx-click (>=4.0.3)", "sphinx-rtd-theme (>=1.2)", "sphinxcontrib-spelling (>=7.3)"] +sphinx = ["sphinx (>=5.0)"] +testing = ["coverage-conditional-plugin (>=0.5)", "coverage[toml] (>=6.0)", "pytest (>=7.2)", "pytest-cov (>=3.0)", "pytest-randomly (>=3.0)", "pytest-sugar (>=0.9.5)"] +toml = ["tomli (>=2.0)"] +type-check = ["mypy (>=1.0)"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] -name = "rsa" -version = "4.7.2" -description = "Pure-Python RSA implementation" +name = "rstcheck-core" +version = "1.2.1" +description = "Checks syntax of reStructuredText and code blocks nested within it" optional = false -python-versions = ">=3.5, <4" +python-versions = ">=3.8" files = [ - {file = "rsa-4.7.2-py3-none-any.whl", hash = "sha256:78f9a9bf4e7be0c5ded4583326e7461e3a3c5aae24073648b4bdfa797d78c9d2"}, - {file = "rsa-4.7.2.tar.gz", hash = "sha256:9d689e6ca1b3038bc82bf8d23e944b6b6037bc02301a574935b2dd946e0353b9"}, + {file = "rstcheck-core-1.2.1.tar.gz", hash = "sha256:9b330020d912e2864f23f332c1a0569463ca3b06b8fee7b7bdd201b055f7f831"}, + {file = "rstcheck_core-1.2.1-py3-none-any.whl", hash = "sha256:1c100de418b6c9e14d9cf6558644d0ab103fdc447f891313882d02df3a3c52ba"}, ] [package.dependencies] -pyasn1 = ">=0.1.3" +docutils = ">=0.7" +pydantic = ">=2" + +[package.extras] +dev = ["rstcheck-core[docs,sphinx,testing,toml,type-check,yaml]", "tox (>=3.15)"] +docs = ["m2r2 (>=0.3.2)", "sphinx (>=5.0,!=7.2.5)", "sphinx-autobuild (>=2021.3.14)", "sphinx-autodoc-typehints (>=1.15)", "sphinx-rtd-theme (>=1.2)", "sphinxcontrib-apidoc (>=0.3)", "sphinxcontrib-spelling (>=7.3)"] +sphinx = ["sphinx (>=5.0)"] +testing = ["coverage-conditional-plugin (>=0.5)", "coverage[toml] (>=6.0)", "pytest (>=7.2)", "pytest-cov (>=3.0)", "pytest-mock (>=3.7)", "pytest-randomly (>=3.0)", "pytest-sugar (>=0.9.5)"] +toml = ["tomli (>=2.0)"] +type-check = ["mypy (>=1.0)", "types-PyYAML (>=6.0.0)", "types-docutils (>=0.18)"] +yaml = ["pyyaml (>=6.0.0)"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" [[package]] name = "s3transfer" version = "0.10.3" description = "An Amazon S3 Transfer Manager" optional = false -python-versions = ">=3.8" +python-versions = ">= 3.8" files = [ {file = "s3transfer-0.10.3-py3-none-any.whl", hash = "sha256:263ed587a5803c6c708d3ce44dc4dfedaab4c1a32e8329bab818933d79ddcf5d"}, {file = "s3transfer-0.10.3.tar.gz", hash = "sha256:4f50ed74ab84d474ce614475e0b8d5047ff080810aac5d01ea25231cfc944b0c"}, @@ -1085,6 +1718,27 @@ botocore = ">=1.33.2,<2.0a.0" [package.extras] crt = ["botocore[crt] (>=1.33.2,<2.0a.0)"] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + +[[package]] +name = "shellingham" +version = "1.5.4" +description = "Tool to Detect Surrounding Shell" +optional = false +python-versions = ">=3.7" +files = [ + {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, + {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, +] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "six" version = "1.16.0" @@ -1096,6 +1750,11 @@ files = [ {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "snowballstemmer" version = "2.2.0" @@ -1107,6 +1766,11 @@ files = [ {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, ] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "soupsieve" version = "2.6" @@ -1118,62 +1782,78 @@ files = [ {file = "soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb"}, ] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "sphinx" -version = "5.3.0" +version = "8.1.3" description = "Python documentation generator" optional = false -python-versions = ">=3.6" +python-versions = ">=3.10" files = [ - {file = "Sphinx-5.3.0.tar.gz", hash = "sha256:51026de0a9ff9fc13c05d74913ad66047e104f56a129ff73e174eb5c3ee794b5"}, - {file = "sphinx-5.3.0-py3-none-any.whl", hash = "sha256:060ca5c9f7ba57a08a1219e547b269fadf125ae25b06b9fa7f66768efb652d6d"}, + {file = "sphinx-8.1.3-py3-none-any.whl", hash = "sha256:09719015511837b76bf6e03e42eb7595ac8c2e41eeb9c29c5b755c6b677992a2"}, + {file = "sphinx-8.1.3.tar.gz", hash = "sha256:43c1911eecb0d3e161ad78611bc905d1ad0e523e4ddc202a58a821773dc4c927"}, ] [package.dependencies] -alabaster = ">=0.7,<0.8" -babel = ">=2.9" -colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} -docutils = ">=0.14,<0.20" +alabaster = ">=0.7.14" +babel = ">=2.13" +colorama = {version = ">=0.4.6", markers = "sys_platform == \"win32\""} +docutils = ">=0.20,<0.22" imagesize = ">=1.3" -Jinja2 = ">=3.0" -packaging = ">=21.0" -Pygments = ">=2.12" -requests = ">=2.5.0" -snowballstemmer = ">=2.0" -sphinxcontrib-applehelp = "*" -sphinxcontrib-devhelp = "*" -sphinxcontrib-htmlhelp = ">=2.0.0" -sphinxcontrib-jsmath = "*" -sphinxcontrib-qthelp = "*" -sphinxcontrib-serializinghtml = ">=1.1.5" +Jinja2 = ">=3.1" +packaging = ">=23.0" +Pygments = ">=2.17" +requests = ">=2.30.0" +snowballstemmer = ">=2.2" +sphinxcontrib-applehelp = ">=1.0.7" +sphinxcontrib-devhelp = ">=1.0.6" +sphinxcontrib-htmlhelp = ">=2.0.6" +sphinxcontrib-jsmath = ">=1.0.1" +sphinxcontrib-qthelp = ">=1.0.6" +sphinxcontrib-serializinghtml = ">=1.1.9" +tomli = {version = ">=2", markers = "python_version < \"3.11\""} [package.extras] docs = ["sphinxcontrib-websupport"] -lint = ["docutils-stubs", "flake8 (>=3.5.0)", "flake8-bugbear", "flake8-comprehensions", "flake8-simplify", "isort", "mypy (>=0.981)", "sphinx-lint", "types-requests", "types-typed-ast"] -test = ["cython", "html5lib", "pytest (>=4.6)", "typed_ast"] +lint = ["flake8 (>=6.0)", "mypy (==1.11.1)", "pyright (==1.1.384)", "pytest (>=6.0)", "ruff (==0.6.9)", "sphinx-lint (>=0.9)", "tomli (>=2)", "types-Pillow (==10.2.0.20240822)", "types-Pygments (==2.18.0.20240506)", "types-colorama (==0.4.15.20240311)", "types-defusedxml (==0.7.0.20240218)", "types-docutils (==0.21.0.20241005)", "types-requests (==2.32.0.20240914)", "types-urllib3 (==1.26.25.14)"] +test = ["cython (>=3.0)", "defusedxml (>=0.7.1)", "pytest (>=8.0)", "setuptools (>=70.0)", "typing_extensions (>=4.9)"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" [[package]] name = "sphinx-autoapi" -version = "2.1.1" +version = "3.3.2" description = "Sphinx API documentation generator" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "sphinx-autoapi-2.1.1.tar.gz", hash = "sha256:fbadb96e79020d6b0ec45d888517bf816d6b587a2d340fbe1ec31135e300a6c8"}, - {file = "sphinx_autoapi-2.1.1-py2.py3-none-any.whl", hash = "sha256:d8da890477bd18e3327cafdead9d5a44a7d798476c6fa32492100e288250a5a3"}, + {file = "sphinx_autoapi-3.3.2-py2.py3-none-any.whl", hash = "sha256:08afa656f7fcd45fe7dd64bf9f44698ddb8ca7c2d5cd0614c7455912ed580324"}, + {file = "sphinx_autoapi-3.3.2.tar.gz", hash = "sha256:ebf8b44b2ebab5c28f0263ec6c2f8acdd156e9b2d539a58eca39d2f368445173"}, ] [package.dependencies] -anyascii = "*" -astroid = ">=2.7" +astroid = [ + {version = ">=2.7", markers = "python_version < \"3.12\""}, + {version = ">=3.0.0a1", markers = "python_version >= \"3.12\""}, +] Jinja2 = "*" PyYAML = "*" -sphinx = ">=5.2.0" +sphinx = ">=6.1.0" [package.extras] docs = ["furo", "sphinx", "sphinx-design"] -dotnet = ["sphinxcontrib-dotnetdomain"] -go = ["sphinxcontrib-golangdomain"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" [[package]] name = "sphinxcontrib-applehelp" @@ -1191,6 +1871,11 @@ lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] standalone = ["Sphinx (>=5)"] test = ["pytest"] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "sphinxcontrib-devhelp" version = "2.0.0" @@ -1207,6 +1892,11 @@ lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] standalone = ["Sphinx (>=5)"] test = ["pytest"] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "sphinxcontrib-htmlhelp" version = "2.1.0" @@ -1223,6 +1913,11 @@ lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] standalone = ["Sphinx (>=5)"] test = ["html5lib", "pytest"] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "sphinxcontrib-jsmath" version = "1.0.1" @@ -1237,6 +1932,11 @@ files = [ [package.extras] test = ["flake8", "mypy", "pytest"] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "sphinxcontrib-qthelp" version = "2.0.0" @@ -1253,6 +1953,11 @@ lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] standalone = ["Sphinx (>=5)"] test = ["defusedxml (>=0.7.1)", "pytest"] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "sphinxcontrib-serializinghtml" version = "2.0.0" @@ -1269,6 +1974,11 @@ lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] standalone = ["Sphinx (>=5)"] test = ["pytest"] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "tomli" version = "2.0.2" @@ -1280,6 +1990,11 @@ files = [ {file = "tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed"}, ] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "tomlkit" version = "0.13.2" @@ -1291,6 +2006,49 @@ files = [ {file = "tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79"}, ] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + +[[package]] +name = "typer" +version = "0.12.5" +description = "Typer, build great CLIs. Easy to code. Based on Python type hints." +optional = false +python-versions = ">=3.7" +files = [ + {file = "typer-0.12.5-py3-none-any.whl", hash = "sha256:62fe4e471711b147e3365034133904df3e235698399bc4de2b36c8579298d52b"}, + {file = "typer-0.12.5.tar.gz", hash = "sha256:f592f089bedcc8ec1b974125d64851029c3b1af145f04aca64d69410f0c9b722"}, +] + +[package.dependencies] +click = ">=8.0.0" +rich = ">=10.11.0" +shellingham = ">=1.3.0" +typing-extensions = ">=3.7.4.3" + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + +[[package]] +name = "types-pyyaml" +version = "6.0.12.20240917" +description = "Typing stubs for PyYAML" +optional = false +python-versions = ">=3.8" +files = [ + {file = "types-PyYAML-6.0.12.20240917.tar.gz", hash = "sha256:d1405a86f9576682234ef83bcb4e6fff7c9305c8b1fbad5e0bcd4f7dbdc9c587"}, + {file = "types_PyYAML-6.0.12.20240917-py3-none-any.whl", hash = "sha256:392b267f1c0fe6022952462bf5d6523f31e37f6cea49b14cee7ad634b6301570"}, +] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "typing-extensions" version = "4.12.2" @@ -1302,6 +2060,11 @@ files = [ {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "urllib3" version = "2.2.3" @@ -1319,6 +2082,36 @@ h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + +[[package]] +name = "virtualenv" +version = "20.27.0" +description = "Virtual Python Environment builder" +optional = false +python-versions = ">=3.8" +files = [ + {file = "virtualenv-20.27.0-py3-none-any.whl", hash = "sha256:44a72c29cceb0ee08f300b314848c86e57bf8d1f13107a5e671fb9274138d655"}, + {file = "virtualenv-20.27.0.tar.gz", hash = "sha256:2ca56a68ed615b8fe4326d11a0dca5dfbe8fd68510fb6c6349163bed3c15f2b2"}, +] + +[package.dependencies] +distlib = ">=0.3.7,<1" +filelock = ">=3.12.2,<4" +platformdirs = ">=3.9.1,<5" + +[package.extras] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] + +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "yapf" version = "0.40.2" @@ -1335,6 +2128,11 @@ importlib-metadata = ">=6.6.0" platformdirs = ">=3.5.1" tomli = ">=2.0.1" +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [[package]] name = "zipp" version = "3.20.2" @@ -1354,7 +2152,12 @@ enabler = ["pytest-enabler (>=2.2)"] test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] type = ["pytest-mypy"] +[package.source] +type = "legacy" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +reference = "pypi-mch" + [metadata] lock-version = "2.0" -python-versions = "~3.12" -content-hash = "5f334a878dfdceecb0b27402ce1270aff5959eaff06f29db7a7b784ddf6f4129" +python-versions = ">=3.10,<3.13" +content-hash = "d9b90125e39be676222bb9578cc5dda6dbd614f647f98f6a6ac2ff206990536c" diff --git a/pyproject.toml b/pyproject.toml index 1b3a01d..23d8999 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,29 +1,48 @@ [tool.poetry] name = "flex-container-orchestrator" +# Keep only Major and Minor version in this configuration, +# the patch version is calculated when releasing the library version = "1.0" -description = "Service listening to Aviso and launching Flexpart-IFS" +description = "The flex-container-orchestrator manages Aviso notifications and triggers the flexprep and flexpart containers, as well as the file aggregation script for Flexpart." authors = ["Nina Burgdorfer "] readme = "README.rst" include = ["HISTORY.rst"] +[[tool.poetry.source]] +name = "pypi-mch" +url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" +priority = "primary" + +[[tool.poetry.source]] +name = "pypi-mch-publish" +# only hub entrypoint can be used for publishing libraries (accessible from CI/CD server) +url = "https://hub.meteoswiss.ch/nexus/repository/python-mch/" +priority = "explicit" [tool.poetry.dependencies] -python = "~3.12" -boto3 = "^1.35.40" +python = ">=3.10,<3.13" pydantic = "^2.9.2" -pydantic-settings = "^2.5.2" +pydantic-settings = "^2.6.0" python-json-logger = "^2.0.7" -awscli = "^1.35.8" +boto3 = "^1.35.48" +mchbuild = "^0.4.8" [tool.poetry.group.dev.dependencies] -mypy = "*" -pydata-sphinx-theme = "*" -pylint = "*" -pytest = "*" -pytest-cov = "*" -sphinx = "*" -sphinx-autoapi = "*" -yapf = "*" +mypy = "^1.10.0" +pydata-sphinx-theme = "^0.15.2" +pylint = "^3.0.2" +pytest = "^8.2.1" +pytest-cov = "^5.0.0" +sphinx = "^8.0.2" +sphinx-autoapi = "^3.1.0" +yapf = "^0.40.2" +autodoc-pydantic = "^2.2.0" +pre-commit = "^4.0.1" +rstcheck = "^6.2.4" +codespell = "^2.3.0" +black = "^24.10.0" +flake8 = "^7.1.1" + [tool.yapf] based_on_style = "pep8" @@ -37,10 +56,6 @@ disable = [ 'W0511', # fix me or to-do comments are already covered by SonarQube ] -[tool.pylint.basic] -argument-naming-style = 'any' -attr-naming-style = 'any' - [tool.pylint.format] # Maximum number of lines in a module. max-line-length = 120 @@ -53,6 +68,9 @@ min-public-methods = 0 ignore_missing_imports = true disallow_untyped_defs = true +[tool.coverage.run] +omit = ["test/**", "doc/**"] + [build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" diff --git a/sonar-project.properties b/sonar-project.properties index 1c0a2bc..4feb982 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -1,9 +1,9 @@ -sonar.projectVersion=n/a +sonar.projectVersion=1.0 sonar.projectName=flex-container-orchestrator -sonar.projectKey=flex-container-orchestrator_RphmWnZVdwWO +sonar.projectKey=flex-container-orchestrator_HHdkxpsLGpPi sonar.python.version=3.12 sonar.python.xunit.reportPath=test_reports/junit.xml sonar.python.coverage.reportPaths=test_reports/coverage.xml sonar.python.pylint.reportPath=test_reports/pylint.log -sonar.coverage.exclusions=test/**,doc/**, test_reports/** -sonar.exclusions=doc/**, security_reports/** +sonar.coverage.exclusions=test/**,flex_container_orchestrator/** +sonar.exclusions=doc/**,security_reports/**,test_reports/** diff --git a/test/__init__.py b/test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/conftest.py b/test/conftest.py new file mode 100644 index 0000000..5871ed8 --- /dev/null +++ b/test/conftest.py @@ -0,0 +1 @@ +import pytest diff --git a/tests/domain/test_aggregator_flexpart.py b/test/domain/test_aggregator_flexpart.py similarity index 89% rename from tests/domain/test_aggregator_flexpart.py rename to test/domain/test_aggregator_flexpart.py index d407df2..cfbc812 100644 --- a/tests/domain/test_aggregator_flexpart.py +++ b/test/domain/test_aggregator_flexpart.py @@ -1,6 +1,4 @@ import datetime -import json -import sqlite3 from unittest.mock import MagicMock, patch import pytest @@ -23,14 +21,12 @@ def test_is_row_processed(fetchone_return, expected_result): mock_conn.cursor.return_value = mock_cursor - mock_cursor.execute.return_value = ( - mock_cursor # execute returns the cursor itself - ) + mock_cursor.execute.return_value = mock_cursor # execute returns the cursor itself mock_cursor.fetchone.return_value = ( fetchone_return # fetchone returns the tuple for testing ) - result = is_row_processed(mock_conn, "202310220600", "12") + result = is_row_processed(mock_conn, datetime.datetime(2023, 10, 22, 6, 0), "12") assert result == expected_result @@ -64,9 +60,7 @@ def test_is_row_processed(fetchone_return, expected_result): ), ], ) -def test_generate_flexpart_start_times( - frt_dt, lead_time, tdelta, tfreq_f, expected -): +def test_generate_flexpart_start_times(frt_dt, lead_time, tdelta, tfreq_f, expected): result = generate_flexpart_start_times(frt_dt, lead_time, tdelta, tfreq_f) assert result == expected @@ -89,7 +83,7 @@ def test_fetch_processed_items(mock_connect): mock_cursor = MagicMock() mock_conn.cursor.return_value = mock_cursor mock_cursor.fetchall.return_value = [(True, "12"), (False, "24")] - frt_s = {"202310220600"} + frt_s = {datetime.datetime(2023, 10, 22, 6, 0)} result = fetch_processed_items(mock_conn, frt_s) assert result == {"20231022060012"} @@ -105,4 +99,3 @@ def test_define_config(): "IETIME": "18", } assert result == expected_config - diff --git a/test/services/test_flexpart_service.py b/test/services/test_flexpart_service.py new file mode 100644 index 0000000..6ddec56 --- /dev/null +++ b/test/services/test_flexpart_service.py @@ -0,0 +1,24 @@ +import logging + +import pytest + +from flex_container_orchestrator.services.flexpart_service import run_command + + +# Mock logging +@pytest.fixture(autouse=True) +def mock_logging(caplog): + with caplog.at_level(logging.INFO): + yield caplog + + +def test_run_command_success(): + command = ["echo", "Hello"] + result = run_command(command, capture_output=True) + assert result == b"Hello" + + +def test_run_command_failure(): + command = ["false"] # This command will fail + with pytest.raises(SystemExit): + run_command(command) diff --git a/uvicorn_logging_settings.json b/uvicorn_logging_settings.json deleted file mode 100644 index cb608f6..0000000 --- a/uvicorn_logging_settings.json +++ /dev/null @@ -1 +0,0 @@ -{"version": 1} From cc1059bb91bfa23453574ecdd5a0a143f3587a3a Mon Sep 17 00:00:00 2001 From: ninaburg Date: Wed, 30 Oct 2024 15:48:10 +0100 Subject: [PATCH 19/37] refactor .env --- flex_container_orchestrator/__init__.py | 3 +++ flex_container_orchestrator/config/.env | 31 ++++++++++++++++++------- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/flex_container_orchestrator/__init__.py b/flex_container_orchestrator/__init__.py index 6c257cb..73ab1a5 100644 --- a/flex_container_orchestrator/__init__.py +++ b/flex_container_orchestrator/__init__.py @@ -1,10 +1,13 @@ """ Initializations """ import os +from dotenv import load_dotenv from flex_container_orchestrator.config import logger from flex_container_orchestrator.config.service_settings import ServiceSettings +load_dotenv(os.path.join(os.path.dirname(__file__), 'config/.env')) # Load .env variables + # mypy: ignore-errors CONFIG = ServiceSettings( "settings.yaml", os.path.join(os.path.dirname(__file__), "config") diff --git a/flex_container_orchestrator/config/.env b/flex_container_orchestrator/config/.env index 563e9be..816d474 100644 --- a/flex_container_orchestrator/config/.env +++ b/flex_container_orchestrator/config/.env @@ -1,25 +1,30 @@ +#------ # .env +#------ -## Flexprep (Flexpart pre-processing) +#----------- Flexprep (Flexpart pre-processing) -# Input/output S3 bucket settings +## Input/output S3 bucket settings SVC__MAIN__S3_BUCKETS__INPUT__ENDPOINT_URL=https://object-store.os-api.cci1.ecmwf.int SVC__MAIN__S3_BUCKETS__INPUT__NAME=flexpart-input SVC__MAIN__S3_BUCKETS__OUTPUT__ENDPOINT_URL=https://object-store.os-api.cci1.ecmwf.int SVC__MAIN__S3_BUCKETS__OUTPUT__NAME=flexprep-output -# Timestep settings -# Interval in hours for forecasts (Global: 3h, Europe: 1h) +## Time settings +# Interval in hours for forecasts (Europe: 1h, Global: 3h) SVC__MAIN__TIME_SETTINGS__TINCR=1 SVC__MAIN__TIME_SETTINGS__TSTART=0 -# IMAGE -DOCKER_IMAGE=493666016161.dkr.ecr.eu-central-2.amazonaws.com/numericalweatherpredictions/dispersionmodelling/flexpart-ifs/flexprep:2410.10201ac80416862805f519b4b2371dde82b5ddfe +## IMAGE +TAG=2410.10201ac80416862805f519b4b2371dde82b5ddfe +ECR_REPO=493666016161.dkr.ecr.eu-central-2.amazonaws.com/numericalweatherpredictions/dispersionmodelling/flexpart-ifs/flexprep +## sqlite DB +DB_MOUNT=~/.sqlite/ -## Flexpart-IFS +#----------- Flexpart-IFS -# Input/output S3 bucket settings +## Input/output S3 bucket settings # Inherit from Flexprep output SVC__MAIN__FLEXPART_S3_BUCKETS__INPUT__ENDPOINT_URL=https://object-store.os-api.cci1.ecmwf.int # Inherit from Flexprep output @@ -27,6 +32,14 @@ SVC__MAIN__FLEXPART_S3_BUCKETS__INPUT__NAME=flexprep-output SVC__MAIN__FLEXPART_S3_BUCKETS__OUTPUT__ENDPOINT_URL=https://object-store.os-api.cci1.ecmwf.int SVC__MAIN__FLEXPART_S3_BUCKETS__OUTPUT__NAME=flexpart-output -# EWC buckets access and secret keys +## EWC buckets access and secret keys S3_ACCESS_KEY= S3_SECRET_KEY= + +## Time settings +# Number of timestep(s) to run Flexpart with (temporarily set to 6 timesteps but operational config is 90) +TDELTA=6 +# Frequency of Flexpart runs in hour(s) +TFREQ_F=6 +# Frequency of IFS runs in hour(s) +TFREQ=6 From 2fd120674ede983f3c12d4b7a235fd6cc0b1b7a7 Mon Sep 17 00:00:00 2001 From: ninaburg Date: Wed, 30 Oct 2024 15:51:23 +0100 Subject: [PATCH 20/37] improve env. var and fix paths --- flex_container_orchestrator/config/aviso/listener_diss.yaml | 6 +++--- flex_container_orchestrator/services/flexpart_service.py | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/flex_container_orchestrator/config/aviso/listener_diss.yaml b/flex_container_orchestrator/config/aviso/listener_diss.yaml index 70019ce..b3a8666 100644 --- a/flex_container_orchestrator/config/aviso/listener_diss.yaml +++ b/flex_container_orchestrator/config/aviso/listener_diss.yaml @@ -10,10 +10,10 @@ listeners: - type: log path: avisoLogs/log_aviso_notifications.log - type: command - working_dir: $HOME + working_dir: $HOME/flex-container-orchestrator/ command: > - python3 flex-container-orchestrator/flex-container-orchestrator/main.py \ + poetry run python3 flex_container_orchestrator/main.py \ --step "${request.step}" \ --date "${request.date}" \ --time "${request.time}" \ - --location "${location}" >> avisoLogs/log_run_${request.date}_${request.time}.log 2>&1 + --location "${location}" >> ../avisoLogs/log_run_${request.date}_${request.time}.log 2>&1 diff --git a/flex_container_orchestrator/services/flexpart_service.py b/flex_container_orchestrator/services/flexpart_service.py index f2bdb3a..95e36b3 100755 --- a/flex_container_orchestrator/services/flexpart_service.py +++ b/flex_container_orchestrator/services/flexpart_service.py @@ -49,7 +49,8 @@ def launch_containers(date: str, location: str, time: str, step: str) -> None: sys.exit(1) # ====== First part: Run pre-processing for Flexpart ====== - db_mount = os.path.expanduser("~/.sqlite/") + db_mount = os.path.expanduser(f"{os.getenv('DB_MOUNT')}") + docker_image = f"{os.getenv('ECR_REPO')}:{os.getenv('TAG')}" if not os.path.exists(db_mount): logging.error("SQLite database directory %s does not exist.", db_mount) @@ -66,7 +67,7 @@ def launch_containers(date: str, location: str, time: str, step: str) -> None: os.path.expanduser( "~/flex-container-orchestrator/flex_container_orchestrator/config/.env" ), - os.getenv("DOCKER_IMAGE"), + docker_image, "--step", step, "--date", From cafa117d8483c9624501da97fbb8221ac0bdb57e Mon Sep 17 00:00:00 2001 From: ninaburg Date: Wed, 30 Oct 2024 15:51:53 +0100 Subject: [PATCH 21/37] poetry packages update --- poetry.lock | 504 ++++--------------------------------------------- pyproject.toml | 13 +- 2 files changed, 35 insertions(+), 482 deletions(-) diff --git a/poetry.lock b/poetry.lock index 13d11c5..ceed3da 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. [[package]] name = "accessible-pygments" @@ -18,11 +18,6 @@ pygments = ">=1.5" dev = ["pillow", "pkginfo (>=1.10)", "playwright", "pre-commit", "setuptools", "twine (>=5.0)"] tests = ["hypothesis", "pytest"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "alabaster" version = "1.0.0" @@ -34,11 +29,6 @@ files = [ {file = "alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "annotated-types" version = "0.7.0" @@ -50,11 +40,6 @@ files = [ {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "astroid" version = "3.3.5" @@ -69,17 +54,12 @@ files = [ [package.dependencies] typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.11\""} -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "autodoc-pydantic" version = "2.2.0" description = "Seamlessly integrate pydantic models in your Sphinx documentation." optional = false -python-versions = ">=3.8.1,<4.0.0" +python-versions = "<4.0.0,>=3.8.1" files = [ {file = "autodoc_pydantic-2.2.0-py3-none-any.whl", hash = "sha256:8c6a36fbf6ed2700ea9c6d21ea76ad541b621fbdf16b5a80ee04673548af4d95"}, ] @@ -97,11 +77,6 @@ security = ["pip-audit (>=2.7.2,<3.0.0)"] test = ["coverage (>=7,<8)", "defusedxml (>=0.7.1)", "pytest (>=8.0.0,<9.0.0)", "pytest-sugar (>=1.0.0,<2.0.0)"] type-checking = ["mypy (>=1.9,<2.0)", "types-docutils (>=0.20,<0.21)", "typing-extensions (>=4.11,<5.0)"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "babel" version = "2.16.0" @@ -116,11 +91,6 @@ files = [ [package.extras] dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "beautifulsoup4" version = "4.12.3" @@ -142,11 +112,6 @@ charset-normalizer = ["charset-normalizer"] html5lib = ["html5lib"] lxml = ["lxml"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "black" version = "24.10.0" @@ -193,44 +158,34 @@ d = ["aiohttp (>=3.10)"] jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] uvloop = ["uvloop (>=0.15.2)"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "boto3" -version = "1.35.48" +version = "1.35.51" description = "The AWS SDK for Python" optional = false -python-versions = ">= 3.8" +python-versions = ">=3.8" files = [ - {file = "boto3-1.35.48-py3-none-any.whl", hash = "sha256:60889bb6e21f0af662ac9404e00125d3b8a5808f190e89462e5ddf73604adfc1"}, - {file = "boto3-1.35.48.tar.gz", hash = "sha256:5007a5cdd09e4db9309adf2ee090455a34ae639bd10a68a1fefca72cd77070fc"}, + {file = "boto3-1.35.51-py3-none-any.whl", hash = "sha256:c922f6a18958af9d8af0489d6d8503b517029d8159b26aa4859a8294561c72e9"}, + {file = "boto3-1.35.51.tar.gz", hash = "sha256:a57c6c7012ecb40c43e565a6f7a891f39efa990ff933eab63cd456f7501c2731"}, ] [package.dependencies] -botocore = ">=1.35.48,<1.36.0" +botocore = ">=1.35.51,<1.36.0" jmespath = ">=0.7.1,<2.0.0" s3transfer = ">=0.10.0,<0.11.0" [package.extras] crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "botocore" -version = "1.35.48" +version = "1.35.51" description = "Low-level, data-driven core of boto 3." optional = false -python-versions = ">= 3.8" +python-versions = ">=3.8" files = [ - {file = "botocore-1.35.48-py3-none-any.whl", hash = "sha256:34fa25fd717208b05745e60f271a39636108fa87a3512fbca18e7e6f787a3239"}, - {file = "botocore-1.35.48.tar.gz", hash = "sha256:3e766cc251053c9ef98542fdf225381ed58531769c3811a6282bd7247f7e2bdf"}, + {file = "botocore-1.35.51-py3-none-any.whl", hash = "sha256:4d65b00111bd12b98e9f920ecab602cf619cc6a6d0be6e5dd53f517e4b92901c"}, + {file = "botocore-1.35.51.tar.gz", hash = "sha256:a9b3d1da76b3e896ad74605c01d88f596324a3337393d4bfbfa0d6c35822ca9c"}, ] [package.dependencies] @@ -241,11 +196,6 @@ urllib3 = {version = ">=1.25.4,<2.2.0 || >2.2.0,<3", markers = "python_version > [package.extras] crt = ["awscrt (==0.22.0)"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "certifi" version = "2024.8.30" @@ -257,11 +207,6 @@ files = [ {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "cfgv" version = "3.4.0" @@ -273,11 +218,6 @@ files = [ {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "charset-normalizer" version = "3.4.0" @@ -392,11 +332,6 @@ files = [ {file = "charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "click" version = "8.1.7" @@ -411,11 +346,6 @@ files = [ [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "codespell" version = "2.3.0" @@ -433,11 +363,6 @@ hard-encoding-detection = ["chardet"] toml = ["tomli"] types = ["chardet (>=5.1.0)", "mypy", "pytest", "pytest-cov", "pytest-dependency"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "colorama" version = "0.4.6" @@ -449,11 +374,6 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "coverage" version = "7.6.4" @@ -531,11 +451,6 @@ tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.1 [package.extras] toml = ["tomli"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "dill" version = "0.3.9" @@ -551,11 +466,6 @@ files = [ graph = ["objgraph (>=1.7.2)"] profile = ["gprof2dot (>=2022.7.29)"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "distlib" version = "0.3.9" @@ -567,11 +477,6 @@ files = [ {file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "docutils" version = "0.21.2" @@ -583,11 +488,6 @@ files = [ {file = "docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "exceptiongroup" version = "1.2.2" @@ -602,11 +502,6 @@ files = [ [package.extras] test = ["pytest (>=6)"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "filelock" version = "3.16.1" @@ -623,11 +518,6 @@ docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2. testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"] typing = ["typing-extensions (>=4.12.2)"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "flake8" version = "7.1.1" @@ -644,11 +534,6 @@ mccabe = ">=0.7.0,<0.8.0" pycodestyle = ">=2.12.0,<2.13.0" pyflakes = ">=3.2.0,<3.3.0" -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "identify" version = "2.6.1" @@ -663,11 +548,6 @@ files = [ [package.extras] license = ["ukkonen"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "idna" version = "3.10" @@ -682,11 +562,6 @@ files = [ [package.extras] all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "imagesize" version = "1.4.1" @@ -698,11 +573,6 @@ files = [ {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "importlib-metadata" version = "8.5.0" @@ -726,11 +596,6 @@ perf = ["ipython"] test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] type = ["pytest-mypy"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "iniconfig" version = "2.0.0" @@ -742,11 +607,6 @@ files = [ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "isort" version = "5.13.2" @@ -761,11 +621,6 @@ files = [ [package.extras] colors = ["colorama (>=0.4.6)"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "jinja2" version = "3.1.4" @@ -783,11 +638,6 @@ MarkupSafe = ">=2.0" [package.extras] i18n = ["Babel (>=2.7)"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "jmespath" version = "1.0.1" @@ -799,10 +649,19 @@ files = [ {file = "jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" +[[package]] +name = "load-dotenv" +version = "0.1.0" +description = "Automatically and implicitly load environment variables from .env file" +optional = false +python-versions = "*" +files = [ + {file = "load-dotenv-0.1.0.tar.gz", hash = "sha256:bbe5f40072d4a61eadca66de6c222df5a2d935d6d41b703be1ff75396f635145"}, + {file = "load_dotenv-0.1.0-py3-none-any.whl", hash = "sha256:614803f720153fb8a5f97124a72aaa3930a67aa5547cbe8603d01ea7f9ac1adf"}, +] + +[package.dependencies] +python-dotenv = "*" [[package]] name = "markdown-it-py" @@ -828,11 +687,6 @@ profiling = ["gprof2dot"] rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "markupsafe" version = "3.0.2" @@ -903,11 +757,6 @@ files = [ {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "mccabe" version = "0.7.0" @@ -919,34 +768,6 @@ files = [ {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - -[[package]] -name = "mchbuild" -version = "0.4.8" -description = "A sample Python project" -optional = false -python-versions = ">=3.6" -files = [ - {file = "mchbuild-0.4.8-py3-none-any.whl", hash = "sha256:65f72da684518bf584664a47b7a1acdba95690ddc21d4858a1881fc8e3eed608"}, - {file = "mchbuild-0.4.8.tar.gz", hash = "sha256:b1852b2f786fafbe404a5de763d4b7e09446a3af69525eb2720615182ad84a85"}, -] - -[package.dependencies] -PyYAML = "*" -types-PyYAML = "*" - -[package.extras] -dev = ["pydata-sphinx-theme", "sphinx", "sphinx-autoapi"] - -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "mdurl" version = "0.1.2" @@ -958,11 +779,6 @@ files = [ {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "mypy" version = "1.13.0" @@ -1016,11 +832,6 @@ install-types = ["pip"] mypyc = ["setuptools (>=50)"] reports = ["lxml"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "mypy-extensions" version = "1.0.0" @@ -1032,27 +843,17 @@ files = [ {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "nodeenv" version = "1.9.1" description = "Node.js virtual environment builder" optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "packaging" version = "24.1" @@ -1064,11 +865,6 @@ files = [ {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "pathspec" version = "0.12.1" @@ -1080,11 +876,6 @@ files = [ {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "platformdirs" version = "4.3.6" @@ -1101,11 +892,6 @@ docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-a test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"] type = ["mypy (>=1.11.2)"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "pluggy" version = "1.5.0" @@ -1121,11 +907,6 @@ files = [ dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "pre-commit" version = "4.0.1" @@ -1144,11 +925,6 @@ nodeenv = ">=0.11.1" pyyaml = ">=5.1" virtualenv = ">=20.10.0" -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "pycodestyle" version = "2.12.1" @@ -1160,11 +936,6 @@ files = [ {file = "pycodestyle-2.12.1.tar.gz", hash = "sha256:6838eae08bbce4f6accd5d5572075c63626a15ee3e6f842df996bf62f6d73521"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "pydantic" version = "2.9.2" @@ -1185,11 +956,6 @@ typing-extensions = {version = ">=4.6.1", markers = "python_version < \"3.13\""} email = ["email-validator (>=2.0.0)"] timezone = ["tzdata"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "pydantic-core" version = "2.23.4" @@ -1291,11 +1057,6 @@ files = [ [package.dependencies] typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "pydantic-settings" version = "2.6.0" @@ -1316,11 +1077,6 @@ azure-key-vault = ["azure-identity (>=1.16.0)", "azure-keyvault-secrets (>=4.8.0 toml = ["tomli (>=2.0.1)"] yaml = ["pyyaml (>=6.0.1)"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "pydata-sphinx-theme" version = "0.15.4" @@ -1349,11 +1105,6 @@ doc = ["ablog (>=0.11.8)", "colorama", "graphviz", "ipykernel", "ipyleaflet", "i i18n = ["Babel", "jinja2"] test = ["pytest", "pytest-cov", "pytest-regressions", "sphinx[test]"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "pyflakes" version = "3.2.0" @@ -1365,11 +1116,6 @@ files = [ {file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "pygments" version = "2.18.0" @@ -1384,11 +1130,6 @@ files = [ [package.extras] windows-terminal = ["colorama (>=0.4.6)"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "pylint" version = "3.3.1" @@ -1418,11 +1159,6 @@ tomlkit = ">=0.10.1" spelling = ["pyenchant (>=3.2,<4.0)"] testutils = ["gitpython (>3)"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "pytest" version = "8.3.3" @@ -1445,11 +1181,6 @@ tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "pytest-cov" version = "5.0.0" @@ -1468,11 +1199,6 @@ pytest = ">=4.6" [package.extras] testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "python-dateutil" version = "2.9.0.post0" @@ -1487,11 +1213,6 @@ files = [ [package.dependencies] six = ">=1.5" -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "python-dotenv" version = "1.0.1" @@ -1506,11 +1227,6 @@ files = [ [package.extras] cli = ["click (>=5.0)"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "python-json-logger" version = "2.0.7" @@ -1522,11 +1238,6 @@ files = [ {file = "python_json_logger-2.0.7-py3-none-any.whl", hash = "sha256:f380b826a991ebbe3de4d897aeec42760035ac760345e57b812938dc8b35e2bd"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "pyyaml" version = "6.0.2" @@ -1589,11 +1300,6 @@ files = [ {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "requests" version = "2.32.3" @@ -1615,11 +1321,6 @@ urllib3 = ">=1.21.1,<3" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "rich" version = "13.9.3" @@ -1639,11 +1340,6 @@ typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.1 [package.extras] jupyter = ["ipywidgets (>=7.5.1,<9)"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "rstcheck" version = "6.2.4" @@ -1667,11 +1363,6 @@ testing = ["coverage-conditional-plugin (>=0.5)", "coverage[toml] (>=6.0)", "pyt toml = ["tomli (>=2.0)"] type-check = ["mypy (>=1.0)"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "rstcheck-core" version = "1.2.1" @@ -1696,17 +1387,12 @@ toml = ["tomli (>=2.0)"] type-check = ["mypy (>=1.0)", "types-PyYAML (>=6.0.0)", "types-docutils (>=0.18)"] yaml = ["pyyaml (>=6.0.0)"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "s3transfer" version = "0.10.3" description = "An Amazon S3 Transfer Manager" optional = false -python-versions = ">= 3.8" +python-versions = ">=3.8" files = [ {file = "s3transfer-0.10.3-py3-none-any.whl", hash = "sha256:263ed587a5803c6c708d3ce44dc4dfedaab4c1a32e8329bab818933d79ddcf5d"}, {file = "s3transfer-0.10.3.tar.gz", hash = "sha256:4f50ed74ab84d474ce614475e0b8d5047ff080810aac5d01ea25231cfc944b0c"}, @@ -1718,11 +1404,6 @@ botocore = ">=1.33.2,<2.0a.0" [package.extras] crt = ["botocore[crt] (>=1.33.2,<2.0a.0)"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "shellingham" version = "1.5.4" @@ -1734,11 +1415,6 @@ files = [ {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "six" version = "1.16.0" @@ -1750,11 +1426,6 @@ files = [ {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "snowballstemmer" version = "2.2.0" @@ -1766,11 +1437,6 @@ files = [ {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "soupsieve" version = "2.6" @@ -1782,11 +1448,6 @@ files = [ {file = "soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "sphinx" version = "8.1.3" @@ -1822,20 +1483,15 @@ docs = ["sphinxcontrib-websupport"] lint = ["flake8 (>=6.0)", "mypy (==1.11.1)", "pyright (==1.1.384)", "pytest (>=6.0)", "ruff (==0.6.9)", "sphinx-lint (>=0.9)", "tomli (>=2)", "types-Pillow (==10.2.0.20240822)", "types-Pygments (==2.18.0.20240506)", "types-colorama (==0.4.15.20240311)", "types-defusedxml (==0.7.0.20240218)", "types-docutils (==0.21.0.20241005)", "types-requests (==2.32.0.20240914)", "types-urllib3 (==1.26.25.14)"] test = ["cython (>=3.0)", "defusedxml (>=0.7.1)", "pytest (>=8.0)", "setuptools (>=70.0)", "typing_extensions (>=4.9)"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "sphinx-autoapi" -version = "3.3.2" +version = "3.3.3" description = "Sphinx API documentation generator" optional = false python-versions = ">=3.8" files = [ - {file = "sphinx_autoapi-3.3.2-py2.py3-none-any.whl", hash = "sha256:08afa656f7fcd45fe7dd64bf9f44698ddb8ca7c2d5cd0614c7455912ed580324"}, - {file = "sphinx_autoapi-3.3.2.tar.gz", hash = "sha256:ebf8b44b2ebab5c28f0263ec6c2f8acdd156e9b2d539a58eca39d2f368445173"}, + {file = "sphinx_autoapi-3.3.3-py3-none-any.whl", hash = "sha256:5c7349b42d45a492a611cb81fb48583d5148e9eab7fc6b1f326dc9273b9191e3"}, + {file = "sphinx_autoapi-3.3.3.tar.gz", hash = "sha256:c44fd719580e9a3684ff82019f4f7f39fc970e3030ffd325936654a6f4d31f22"}, ] [package.dependencies] @@ -1847,14 +1503,6 @@ Jinja2 = "*" PyYAML = "*" sphinx = ">=6.1.0" -[package.extras] -docs = ["furo", "sphinx", "sphinx-design"] - -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "sphinxcontrib-applehelp" version = "2.0.0" @@ -1871,11 +1519,6 @@ lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] standalone = ["Sphinx (>=5)"] test = ["pytest"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "sphinxcontrib-devhelp" version = "2.0.0" @@ -1892,11 +1535,6 @@ lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] standalone = ["Sphinx (>=5)"] test = ["pytest"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "sphinxcontrib-htmlhelp" version = "2.1.0" @@ -1913,11 +1551,6 @@ lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] standalone = ["Sphinx (>=5)"] test = ["html5lib", "pytest"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "sphinxcontrib-jsmath" version = "1.0.1" @@ -1932,11 +1565,6 @@ files = [ [package.extras] test = ["flake8", "mypy", "pytest"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "sphinxcontrib-qthelp" version = "2.0.0" @@ -1953,11 +1581,6 @@ lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] standalone = ["Sphinx (>=5)"] test = ["defusedxml (>=0.7.1)", "pytest"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "sphinxcontrib-serializinghtml" version = "2.0.0" @@ -1974,11 +1597,6 @@ lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] standalone = ["Sphinx (>=5)"] test = ["pytest"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "tomli" version = "2.0.2" @@ -1990,11 +1608,6 @@ files = [ {file = "tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "tomlkit" version = "0.13.2" @@ -2006,11 +1619,6 @@ files = [ {file = "tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "typer" version = "0.12.5" @@ -2028,27 +1636,6 @@ rich = ">=10.11.0" shellingham = ">=1.3.0" typing-extensions = ">=3.7.4.3" -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - -[[package]] -name = "types-pyyaml" -version = "6.0.12.20240917" -description = "Typing stubs for PyYAML" -optional = false -python-versions = ">=3.8" -files = [ - {file = "types-PyYAML-6.0.12.20240917.tar.gz", hash = "sha256:d1405a86f9576682234ef83bcb4e6fff7c9305c8b1fbad5e0bcd4f7dbdc9c587"}, - {file = "types_PyYAML-6.0.12.20240917-py3-none-any.whl", hash = "sha256:392b267f1c0fe6022952462bf5d6523f31e37f6cea49b14cee7ad634b6301570"}, -] - -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "typing-extensions" version = "4.12.2" @@ -2060,11 +1647,6 @@ files = [ {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "urllib3" version = "2.2.3" @@ -2082,20 +1664,15 @@ h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "virtualenv" -version = "20.27.0" +version = "20.27.1" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.8" files = [ - {file = "virtualenv-20.27.0-py3-none-any.whl", hash = "sha256:44a72c29cceb0ee08f300b314848c86e57bf8d1f13107a5e671fb9274138d655"}, - {file = "virtualenv-20.27.0.tar.gz", hash = "sha256:2ca56a68ed615b8fe4326d11a0dca5dfbe8fd68510fb6c6349163bed3c15f2b2"}, + {file = "virtualenv-20.27.1-py3-none-any.whl", hash = "sha256:f11f1b8a29525562925f745563bfd48b189450f61fb34c4f9cc79dd5aa32a1f4"}, + {file = "virtualenv-20.27.1.tar.gz", hash = "sha256:142c6be10212543b32c6c45d3d3893dff89112cc588b7d0879ae5a1ec03a47ba"}, ] [package.dependencies] @@ -2107,11 +1684,6 @@ platformdirs = ">=3.9.1,<5" docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "yapf" version = "0.40.2" @@ -2128,11 +1700,6 @@ importlib-metadata = ">=6.6.0" platformdirs = ">=3.5.1" tomli = ">=2.0.1" -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [[package]] name = "zipp" version = "3.20.2" @@ -2152,12 +1719,7 @@ enabler = ["pytest-enabler (>=2.2)"] test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] type = ["pytest-mypy"] -[package.source] -type = "legacy" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -reference = "pypi-mch" - [metadata] lock-version = "2.0" python-versions = ">=3.10,<3.13" -content-hash = "d9b90125e39be676222bb9578cc5dda6dbd614f647f98f6a6ac2ff206990536c" +content-hash = "389caedc6b13402b512ebc5cfa5e47df15747efd982c07e7b468263316ee2f33" diff --git a/pyproject.toml b/pyproject.toml index 23d8999..e4a8849 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,16 +8,6 @@ authors = ["Nina Burgdorfer "] readme = "README.rst" include = ["HISTORY.rst"] -[[tool.poetry.source]] -name = "pypi-mch" -url = "https://service.meteoswiss.ch/nexus/repository/python-all/simple" -priority = "primary" - -[[tool.poetry.source]] -name = "pypi-mch-publish" -# only hub entrypoint can be used for publishing libraries (accessible from CI/CD server) -url = "https://hub.meteoswiss.ch/nexus/repository/python-mch/" -priority = "explicit" [tool.poetry.dependencies] python = ">=3.10,<3.13" @@ -25,7 +15,8 @@ pydantic = "^2.9.2" pydantic-settings = "^2.6.0" python-json-logger = "^2.0.7" boto3 = "^1.35.48" -mchbuild = "^0.4.8" +# mchbuild = "^0.4.8" +load-dotenv = "^0.1.0" [tool.poetry.group.dev.dependencies] mypy = "^1.10.0" From ec4cfb9cbb003d3488a9eac3f0c09cc7eb69cfb0 Mon Sep 17 00:00:00 2001 From: ninaburg Date: Fri, 1 Nov 2024 08:33:21 +0100 Subject: [PATCH 22/37] fix exe poetry location and clean up env. var --- flex_container_orchestrator/__init__.py | 5 ++++- flex_container_orchestrator/config/.env | 10 +++++----- .../config/aviso/listener_diss.yaml | 2 +- .../services/flexpart_service.py | 11 ++++++----- 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/flex_container_orchestrator/__init__.py b/flex_container_orchestrator/__init__.py index 73ab1a5..3b2edcd 100644 --- a/flex_container_orchestrator/__init__.py +++ b/flex_container_orchestrator/__init__.py @@ -1,12 +1,15 @@ """ Initializations """ import os + from dotenv import load_dotenv from flex_container_orchestrator.config import logger from flex_container_orchestrator.config.service_settings import ServiceSettings -load_dotenv(os.path.join(os.path.dirname(__file__), 'config/.env')) # Load .env variables +load_dotenv( + os.path.join(os.path.dirname(__file__), "config/.env") +) # Load .env variables # mypy: ignore-errors CONFIG = ServiceSettings( diff --git a/flex_container_orchestrator/config/.env b/flex_container_orchestrator/config/.env index 816d474..3d96aa6 100644 --- a/flex_container_orchestrator/config/.env +++ b/flex_container_orchestrator/config/.env @@ -16,7 +16,7 @@ SVC__MAIN__TIME_SETTINGS__TINCR=1 SVC__MAIN__TIME_SETTINGS__TSTART=0 ## IMAGE -TAG=2410.10201ac80416862805f519b4b2371dde82b5ddfe +TAG=2410.d61a35cadc7965c78a4f9ed8bae66e69772d14fe ECR_REPO=493666016161.dkr.ecr.eu-central-2.amazonaws.com/numericalweatherpredictions/dispersionmodelling/flexpart-ifs/flexprep ## sqlite DB @@ -26,11 +26,11 @@ DB_MOUNT=~/.sqlite/ ## Input/output S3 bucket settings # Inherit from Flexprep output -SVC__MAIN__FLEXPART_S3_BUCKETS__INPUT__ENDPOINT_URL=https://object-store.os-api.cci1.ecmwf.int +FLEXPART_S3_BUCKETS__INPUT__ENDPOINT_URL=https://object-store.os-api.cci1.ecmwf.int # Inherit from Flexprep output -SVC__MAIN__FLEXPART_S3_BUCKETS__INPUT__NAME=flexprep-output -SVC__MAIN__FLEXPART_S3_BUCKETS__OUTPUT__ENDPOINT_URL=https://object-store.os-api.cci1.ecmwf.int -SVC__MAIN__FLEXPART_S3_BUCKETS__OUTPUT__NAME=flexpart-output +FLEXPART_S3_BUCKETS__INPUT__NAME=flexprep-output +FLEXPART_S3_BUCKETS__OUTPUT__ENDPOINT_URL=https://object-store.os-api.cci1.ecmwf.int +FLEXPART_S3_BUCKETS__OUTPUT__NAME=flexpart-output ## EWC buckets access and secret keys S3_ACCESS_KEY= diff --git a/flex_container_orchestrator/config/aviso/listener_diss.yaml b/flex_container_orchestrator/config/aviso/listener_diss.yaml index b3a8666..0c7a88d 100644 --- a/flex_container_orchestrator/config/aviso/listener_diss.yaml +++ b/flex_container_orchestrator/config/aviso/listener_diss.yaml @@ -12,7 +12,7 @@ listeners: - type: command working_dir: $HOME/flex-container-orchestrator/ command: > - poetry run python3 flex_container_orchestrator/main.py \ + $HOME/.local/bin/poetry run python3 flex_container_orchestrator/main.py \ --step "${request.step}" \ --date "${request.date}" \ --time "${request.time}" \ diff --git a/flex_container_orchestrator/services/flexpart_service.py b/flex_container_orchestrator/services/flexpart_service.py index 95e36b3..d2eb637 100755 --- a/flex_container_orchestrator/services/flexpart_service.py +++ b/flex_container_orchestrator/services/flexpart_service.py @@ -51,6 +51,9 @@ def launch_containers(date: str, location: str, time: str, step: str) -> None: # ====== First part: Run pre-processing for Flexpart ====== db_mount = os.path.expanduser(f"{os.getenv('DB_MOUNT')}") docker_image = f"{os.getenv('ECR_REPO')}:{os.getenv('TAG')}" + env_file_path = os.path.expanduser( + "~/flex-container-orchestrator/flex_container_orchestrator/config/.env" + ) if not os.path.exists(db_mount): logging.error("SQLite database directory %s does not exist.", db_mount) @@ -64,9 +67,7 @@ def launch_containers(date: str, location: str, time: str, step: str) -> None: "--mount", f"type=bind,source={db_mount},destination=/src/db/", "--env-file", - os.path.expanduser( - "~/flex-container-orchestrator/flex_container_orchestrator/config/.env" - ), + env_file_path, docker_image, "--step", step, @@ -86,7 +87,7 @@ def launch_containers(date: str, location: str, time: str, step: str) -> None: logging.info("Pre-processing container executed successfully.") # ====== Second part: Run aggregator_flexpart.py ====== - db_path = os.path.expanduser("~/.sqlite/sqlite3-db") + db_path = os.path.join(db_mount, "sqlite3-db") if not os.path.exists(db_path): logging.error("Database file %s does not exist.", db_path) @@ -143,7 +144,7 @@ def launch_containers(date: str, location: str, time: str, step: str) -> None: command_str = " ".join(command) docker_command = ( - f"docker run {' '.join(env_vars)} --rm " + f"docker run --env-file {env_file_path} {' '.join(env_vars)} --rm " "container-registry.meteoswiss.ch/flexpart-poc/flexpart:containerize " f"{command_str}" ) From 1c3ab5c87129bea7d6f7f2d8c372519f8006f076 Mon Sep 17 00:00:00 2001 From: ninaburg Date: Tue, 5 Nov 2024 08:58:33 +0000 Subject: [PATCH 23/37] comments: call python script directly, no subprocess --- .../domain/aggregator_flexpart.py | 47 +++++-------- .../services/flexpart_service.py | 66 ++++++------------- 2 files changed, 37 insertions(+), 76 deletions(-) diff --git a/flex_container_orchestrator/domain/aggregator_flexpart.py b/flex_container_orchestrator/domain/aggregator_flexpart.py index 28ab40c..b002811 100644 --- a/flex_container_orchestrator/domain/aggregator_flexpart.py +++ b/flex_container_orchestrator/domain/aggregator_flexpart.py @@ -10,26 +10,6 @@ from flex_container_orchestrator import CONFIG - -def parse_arguments() -> argparse.Namespace: - """ - Parse command-line arguments. - - Returns: - argparse.Namespace: Parsed arguments. - """ - parser = argparse.ArgumentParser( - description="Launch Flexpart after processing checks." - ) - parser.add_argument("--date", required=True, help="Date in YYYYMMDD format") - parser.add_argument("--time", required=True, help="Time in HH format") - parser.add_argument( - "--step", required=True, help="Step identifier (lead time in hours)" - ) - parser.add_argument("--db_path", required=True, help="Path to the SQLite database") - return parser.parse_args() - - def connect_db(db_path: str) -> sqlite3.Connection: """ Establish a connection to the SQLite database. @@ -303,23 +283,31 @@ def create_flexpart_configs( return configs -def main() -> None: +def run_aggregator(date: str, time: str, step: int, db_path: str) -> List[dict]: """ - Main function to parse arguments, connect to database, and run Flexpart processing. + Run the aggregator function with the provided arguments. + + Args: + date (str): Date in YYYYMMDD format. + time (str): Time in HH format. + step (int): Step identifier (lead time in hours). + db_path (str): Path to the SQLite database. + + Returns: + List[dict]: List of configuration dictionaries for Flexpart. """ - args = parse_arguments() time_settings = get_time_settings(CONFIG) - conn = connect_db(args.db_path) - frt_dt = parse_forecast_datetime(args.date, args.time) + conn = connect_db(db_path) + frt_dt = parse_forecast_datetime(date, time) - if not is_row_processed(conn, frt_dt, args.step): + if not is_row_processed(conn, frt_dt, step): logging.info("File processing incomplete. Exiting before launching Flexpart.") conn.close() sys.exit(0) list_start_times = generate_flexpart_start_times( frt_dt, - int(args.step), + step, time_settings["tdelta"], time_settings["tfreq_f"], ) @@ -331,10 +319,7 @@ def main() -> None: processed_items = fetch_processed_items(conn, frt_set) configs = create_flexpart_configs(all_list_lt, all_list_ltf, processed_items) - print(json.dumps(configs)) - conn.close() + return configs -if __name__ == "__main__": - main() diff --git a/flex_container_orchestrator/services/flexpart_service.py b/flex_container_orchestrator/services/flexpart_service.py index d2eb637..cd8e8ae 100755 --- a/flex_container_orchestrator/services/flexpart_service.py +++ b/flex_container_orchestrator/services/flexpart_service.py @@ -4,6 +4,8 @@ import subprocess import sys +from flex_container_orchestrator.domain.aggregator_flexpart import run_aggregator + def run_command(command: list[str] | str, capture_output: bool = False) -> bytes | None: """ @@ -50,7 +52,7 @@ def launch_containers(date: str, location: str, time: str, step: str) -> None: # ====== First part: Run pre-processing for Flexpart ====== db_mount = os.path.expanduser(f"{os.getenv('DB_MOUNT')}") - docker_image = f"{os.getenv('ECR_REPO')}:{os.getenv('TAG')}" + docker_image = f"{os.getenv('FLEXPREP_ECR_REPO')}:{os.getenv('FLEXPREP_TAG')}" env_file_path = os.path.expanduser( "~/flex-container-orchestrator/flex_container_orchestrator/config/.env" ) @@ -94,38 +96,10 @@ def launch_containers(date: str, location: str, time: str, step: str) -> None: sys.exit(1) try: - script_dir = os.path.dirname(os.path.abspath(__file__)) - aggregator_script_path = os.path.join( - script_dir, "..", "domain", "aggregator_flexpart.py" - ) - - aggregator_command = [ - "python3", - aggregator_script_path, - "--date", - date, - "--time", - time, - "--step", - step, - "--db_path", - db_path, - ] + configurations = run_aggregator(date, time, int(step), db_path) - output = run_command(aggregator_command, capture_output=True) - if not output: - logging.info( - "Flexpart can't be launched. Not enough pre-processed files. Exiting." - ) - sys.exit(0) - try: - configurations = json.loads(output.decode("utf-8")) - except json.JSONDecodeError as e: - logging.error("JSON decode error: %s", e) - sys.exit(1) - - except subprocess.CalledProcessError: - logging.error("Aggregator script encountered an error.") + except Exception as e: + logging.error("Aggregator encountered an error: %s", e) sys.exit(1) logging.info("Aggregator launch script executed successfully.") @@ -134,22 +108,24 @@ def launch_containers(date: str, location: str, time: str, step: str) -> None: try: # Check if configurations is an empty list if not configurations: - logging.error("Not enough data to launch Flexpart.") - sys.exit(1) + logging.info("Not enough data to launch Flexpart.") + sys.exit(0) # Loop through each configuration and execute Flexpart + docker_image = f"{os.getenv('FLEXPART_ECR_REPO')}:{os.getenv('FLEXPART_TAG')}" for config in configurations: - env_vars = [f"-e {key}={value}" for key, value in config.items()] - command = ["/bin/sh", "-c", "ulimit -a && bash entrypoint.sh"] - command_str = " ".join(command) - - docker_command = ( - f"docker run --env-file {env_file_path} {' '.join(env_vars)} --rm " - "container-registry.meteoswiss.ch/flexpart-poc/flexpart:containerize " - f"{command_str}" - ) - - logging.info("Running: %s", docker_command) + env_vars = [["-e", f"{key.strip()}={value}"] for key, value in config.items()] + + # Build the Docker command as a list + docker_command = [ + "docker", "run", + "--env-file", env_file_path, + *[item for sublist in env_vars for item in sublist], + "--rm", + docker_image, + ] + + logging.info("Running: %s", " ".join(docker_command)) run_command(docker_command) except subprocess.CalledProcessError: From a7c080ef6e73e59b5f572416bc4503ac5564ec32 Mon Sep 17 00:00:00 2001 From: ninaburg Date: Wed, 6 Nov 2024 21:10:23 +0000 Subject: [PATCH 24/37] comment:rm pre-commit, use mypy from mch template --- .pre-commit-config.yaml | 94 ---------- poetry.lock | 380 ++-------------------------------------- pyproject.toml | 8 +- 3 files changed, 13 insertions(+), 469 deletions(-) delete mode 100644 .pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml deleted file mode 100644 index 7a706bf..0000000 --- a/.pre-commit-config.yaml +++ /dev/null @@ -1,94 +0,0 @@ -repos: -- repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 - hooks: - - id: check-ast - - id: check-case-conflict - - id: check-docstring-first - - id: check-symlinks - - id: check-toml - - id: check-yaml - - id: debug-statements - - id: end-of-file-fixer - - id: trailing-whitespace -- repo: local - hooks: - - id: forbidden-files-git-orig - name: find git merge backup files - language: fail - entry: "Found git merge backup file(s): review and remove them" - files: "\\.orig$" -- repo: local - hooks: - - id: rstcheck - name: rstcheck - description: Check REST files for correctness - language: system - entry: rstcheck - types: [rst] -- repo: local - hooks: - - id: codespell - name: codespell - description: Check for spelling errors - language: system - entry: codespell -- repo: local - hooks: - - id: black - name: black - description: Format Python code - language: system - entry: black - types_or: [python, pyi] -- repo: local - hooks: - - id: isort - name: isort - description: Group and sort Python imports - language: system - entry: isort - types_or: [python, pyi, cython] -- repo: local - hooks: - - id: pydocstyle - name: pydocstyle - description: Check docstrings in Python code for compliance with conventions - language: system - entry: pydocstyle - types: [python] - files: ^src/ -# SR It would be handy to abort if one linter fails b/c the same error often -# SR triggers multiple linters. That way, if flake8 fails, pylint and mypy -# SR (which in large code bases can take some time to run) could be skipped -# SR before fixing the error. Unfortunately, pre-commit only provides the global -# SR option fail_fast, which would abort even after the small fixers and -# SR formatters changed something. A potential solution could be to write a -# SR small bash script run-linters.sh that runs flake8, pylint and run-mypy.sh -# SR in that order and aborts on error. -# TODO: There is significant work involved in getting pylint going. This can be its separate task -# - repo: local -# hooks: -# - id: pylint -# name: pylint -# description: Check Python code for correctness, consistency and adherence to best practices -# language: system -# entry: pylint -# types: [python] -- repo: local - hooks: - - id: flake8 - name: flake8 - description: Check Python code for correctness, consistency and adherence to best practices - language: system - entry: flake8 - types: [python] -- repo: local - hooks: - - id: poetry-check - name: poetry-check - description: run poetry check to validate config - entry: poetry check - language: python - pass_filenames: false - files: ^(.*/)?(poetry\.lock|pyproject\.toml)$ diff --git a/poetry.lock b/poetry.lock index ceed3da..4924339 100644 --- a/poetry.lock +++ b/poetry.lock @@ -112,65 +112,19 @@ charset-normalizer = ["charset-normalizer"] html5lib = ["html5lib"] lxml = ["lxml"] -[[package]] -name = "black" -version = "24.10.0" -description = "The uncompromising code formatter." -optional = false -python-versions = ">=3.9" -files = [ - {file = "black-24.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6668650ea4b685440857138e5fe40cde4d652633b1bdffc62933d0db4ed9812"}, - {file = "black-24.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1c536fcf674217e87b8cc3657b81809d3c085d7bf3ef262ead700da345bfa6ea"}, - {file = "black-24.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:649fff99a20bd06c6f727d2a27f401331dc0cc861fb69cde910fe95b01b5928f"}, - {file = "black-24.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:fe4d6476887de70546212c99ac9bd803d90b42fc4767f058a0baa895013fbb3e"}, - {file = "black-24.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5a2221696a8224e335c28816a9d331a6c2ae15a2ee34ec857dcf3e45dbfa99ad"}, - {file = "black-24.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f9da3333530dbcecc1be13e69c250ed8dfa67f43c4005fb537bb426e19200d50"}, - {file = "black-24.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4007b1393d902b48b36958a216c20c4482f601569d19ed1df294a496eb366392"}, - {file = "black-24.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:394d4ddc64782e51153eadcaaca95144ac4c35e27ef9b0a42e121ae7e57a9175"}, - {file = "black-24.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e39e0fae001df40f95bd8cc36b9165c5e2ea88900167bddf258bacef9bbdc3"}, - {file = "black-24.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d37d422772111794b26757c5b55a3eade028aa3fde43121ab7b673d050949d65"}, - {file = "black-24.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14b3502784f09ce2443830e3133dacf2c0110d45191ed470ecb04d0f5f6fcb0f"}, - {file = "black-24.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:30d2c30dc5139211dda799758559d1b049f7f14c580c409d6ad925b74a4208a8"}, - {file = "black-24.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cbacacb19e922a1d75ef2b6ccaefcd6e93a2c05ede32f06a21386a04cedb981"}, - {file = "black-24.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1f93102e0c5bb3907451063e08b9876dbeac810e7da5a8bfb7aeb5a9ef89066b"}, - {file = "black-24.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ddacb691cdcdf77b96f549cf9591701d8db36b2f19519373d60d31746068dbf2"}, - {file = "black-24.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:680359d932801c76d2e9c9068d05c6b107f2584b2a5b88831c83962eb9984c1b"}, - {file = "black-24.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:17374989640fbca88b6a448129cd1745c5eb8d9547b464f281b251dd00155ccd"}, - {file = "black-24.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:63f626344343083322233f175aaf372d326de8436f5928c042639a4afbbf1d3f"}, - {file = "black-24.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfa1d0cb6200857f1923b602f978386a3a2758a65b52e0950299ea014be6800"}, - {file = "black-24.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:2cd9c95431d94adc56600710f8813ee27eea544dd118d45896bb734e9d7a0dc7"}, - {file = "black-24.10.0-py3-none-any.whl", hash = "sha256:3bb2b7a1f7b685f85b11fed1ef10f8a9148bceb49853e47a294a3dd963c1dd7d"}, - {file = "black-24.10.0.tar.gz", hash = "sha256:846ea64c97afe3bc677b761787993be4991810ecc7a4a937816dd6bddedc4875"}, -] - -[package.dependencies] -click = ">=8.0.0" -mypy-extensions = ">=0.4.3" -packaging = ">=22.0" -pathspec = ">=0.9.0" -platformdirs = ">=2" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} - -[package.extras] -colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.10)"] -jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] -uvloop = ["uvloop (>=0.15.2)"] - [[package]] name = "boto3" -version = "1.35.51" +version = "1.35.55" description = "The AWS SDK for Python" optional = false python-versions = ">=3.8" files = [ - {file = "boto3-1.35.51-py3-none-any.whl", hash = "sha256:c922f6a18958af9d8af0489d6d8503b517029d8159b26aa4859a8294561c72e9"}, - {file = "boto3-1.35.51.tar.gz", hash = "sha256:a57c6c7012ecb40c43e565a6f7a891f39efa990ff933eab63cd456f7501c2731"}, + {file = "boto3-1.35.55-py3-none-any.whl", hash = "sha256:c7a0a0bc5ae3bed5d38e8bfe5a56b31621e79bdd7c1ea6e5ba4326d820cde3a5"}, + {file = "boto3-1.35.55.tar.gz", hash = "sha256:82fa8cdb00731aeffe7a5829821ae78d75c7ae959b638c15ff3b4681192ace90"}, ] [package.dependencies] -botocore = ">=1.35.51,<1.36.0" +botocore = ">=1.35.55,<1.36.0" jmespath = ">=0.7.1,<2.0.0" s3transfer = ">=0.10.0,<0.11.0" @@ -179,13 +133,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.35.51" +version = "1.35.55" description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">=3.8" files = [ - {file = "botocore-1.35.51-py3-none-any.whl", hash = "sha256:4d65b00111bd12b98e9f920ecab602cf619cc6a6d0be6e5dd53f517e4b92901c"}, - {file = "botocore-1.35.51.tar.gz", hash = "sha256:a9b3d1da76b3e896ad74605c01d88f596324a3337393d4bfbfa0d6c35822ca9c"}, + {file = "botocore-1.35.55-py3-none-any.whl", hash = "sha256:3d54739e498534c9d7a6e9732ae2d17ed29c7d5e29fe36c956d8488b859538b0"}, + {file = "botocore-1.35.55.tar.gz", hash = "sha256:61ae18f688250372d7b6046e35c86f8fd09a7c0f0064b52688f3490b4d6c9d6b"}, ] [package.dependencies] @@ -207,17 +161,6 @@ files = [ {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, ] -[[package]] -name = "cfgv" -version = "3.4.0" -description = "Validate configuration and produce human readable error messages." -optional = false -python-versions = ">=3.8" -files = [ - {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, - {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, -] - [[package]] name = "charset-normalizer" version = "3.4.0" @@ -332,37 +275,6 @@ files = [ {file = "charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e"}, ] -[[package]] -name = "click" -version = "8.1.7" -description = "Composable command line interface toolkit" -optional = false -python-versions = ">=3.7" -files = [ - {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, - {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[[package]] -name = "codespell" -version = "2.3.0" -description = "Codespell" -optional = false -python-versions = ">=3.8" -files = [ - {file = "codespell-2.3.0-py3-none-any.whl", hash = "sha256:a9c7cef2501c9cfede2110fd6d4e5e62296920efe9abfb84648df866e47f58d1"}, - {file = "codespell-2.3.0.tar.gz", hash = "sha256:360c7d10f75e65f67bad720af7007e1060a5d395670ec11a7ed1fed9dd17471f"}, -] - -[package.extras] -dev = ["Pygments", "build", "chardet", "pre-commit", "pytest", "pytest-cov", "pytest-dependency", "ruff", "tomli", "twine"] -hard-encoding-detection = ["chardet"] -toml = ["tomli"] -types = ["chardet (>=5.1.0)", "mypy", "pytest", "pytest-cov", "pytest-dependency"] - [[package]] name = "colorama" version = "0.4.6" @@ -466,17 +378,6 @@ files = [ graph = ["objgraph (>=1.7.2)"] profile = ["gprof2dot (>=2022.7.29)"] -[[package]] -name = "distlib" -version = "0.3.9" -description = "Distribution utilities" -optional = false -python-versions = "*" -files = [ - {file = "distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87"}, - {file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"}, -] - [[package]] name = "docutils" version = "0.21.2" @@ -502,52 +403,6 @@ files = [ [package.extras] test = ["pytest (>=6)"] -[[package]] -name = "filelock" -version = "3.16.1" -description = "A platform independent file lock." -optional = false -python-versions = ">=3.8" -files = [ - {file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"}, - {file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"}, -] - -[package.extras] -docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4.1)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"] -typing = ["typing-extensions (>=4.12.2)"] - -[[package]] -name = "flake8" -version = "7.1.1" -description = "the modular source code checker: pep8 pyflakes and co" -optional = false -python-versions = ">=3.8.1" -files = [ - {file = "flake8-7.1.1-py2.py3-none-any.whl", hash = "sha256:597477df7860daa5aa0fdd84bf5208a043ab96b8e96ab708770ae0364dd03213"}, - {file = "flake8-7.1.1.tar.gz", hash = "sha256:049d058491e228e03e67b390f311bbf88fce2dbaa8fa673e7aea87b7198b8d38"}, -] - -[package.dependencies] -mccabe = ">=0.7.0,<0.8.0" -pycodestyle = ">=2.12.0,<2.13.0" -pyflakes = ">=3.2.0,<3.3.0" - -[[package]] -name = "identify" -version = "2.6.1" -description = "File identification library for Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "identify-2.6.1-py2.py3-none-any.whl", hash = "sha256:53863bcac7caf8d2ed85bd20312ea5dcfc22226800f6d6881f232d861db5a8f0"}, - {file = "identify-2.6.1.tar.gz", hash = "sha256:91478c5fb7c3aac5ff7bf9b4344f803843dc586832d5f110d672b19aa1984c98"}, -] - -[package.extras] -license = ["ukkonen"] - [[package]] name = "idna" version = "3.10" @@ -663,30 +518,6 @@ files = [ [package.dependencies] python-dotenv = "*" -[[package]] -name = "markdown-it-py" -version = "3.0.0" -description = "Python port of markdown-it. Markdown parsing, done right!" -optional = false -python-versions = ">=3.8" -files = [ - {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, - {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, -] - -[package.dependencies] -mdurl = ">=0.1,<1.0" - -[package.extras] -benchmarking = ["psutil", "pytest", "pytest-benchmark"] -code-style = ["pre-commit (>=3.0,<4.0)"] -compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] -linkify = ["linkify-it-py (>=1,<3)"] -plugins = ["mdit-py-plugins"] -profiling = ["gprof2dot"] -rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] -testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] - [[package]] name = "markupsafe" version = "3.0.2" @@ -768,17 +599,6 @@ files = [ {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, ] -[[package]] -name = "mdurl" -version = "0.1.2" -description = "Markdown URL utilities" -optional = false -python-versions = ">=3.7" -files = [ - {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, - {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, -] - [[package]] name = "mypy" version = "1.13.0" @@ -843,17 +663,6 @@ files = [ {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] -[[package]] -name = "nodeenv" -version = "1.9.1" -description = "Node.js virtual environment builder" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -files = [ - {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, - {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, -] - [[package]] name = "packaging" version = "24.1" @@ -865,17 +674,6 @@ files = [ {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, ] -[[package]] -name = "pathspec" -version = "0.12.1" -description = "Utility library for gitignore style pattern matching of file paths." -optional = false -python-versions = ">=3.8" -files = [ - {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, - {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, -] - [[package]] name = "platformdirs" version = "4.3.6" @@ -907,35 +705,6 @@ files = [ dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] -[[package]] -name = "pre-commit" -version = "4.0.1" -description = "A framework for managing and maintaining multi-language pre-commit hooks." -optional = false -python-versions = ">=3.9" -files = [ - {file = "pre_commit-4.0.1-py2.py3-none-any.whl", hash = "sha256:efde913840816312445dc98787724647c65473daefe420785f885e8ed9a06878"}, - {file = "pre_commit-4.0.1.tar.gz", hash = "sha256:80905ac375958c0444c65e9cebebd948b3cdb518f335a091a670a89d652139d2"}, -] - -[package.dependencies] -cfgv = ">=2.0.0" -identify = ">=1.0.0" -nodeenv = ">=0.11.1" -pyyaml = ">=5.1" -virtualenv = ">=20.10.0" - -[[package]] -name = "pycodestyle" -version = "2.12.1" -description = "Python style guide checker" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pycodestyle-2.12.1-py2.py3-none-any.whl", hash = "sha256:46f0fb92069a7c28ab7bb558f05bfc0110dac69a0cd23c61ea0040283a9d78b3"}, - {file = "pycodestyle-2.12.1.tar.gz", hash = "sha256:6838eae08bbce4f6accd5d5572075c63626a15ee3e6f842df996bf62f6d73521"}, -] - [[package]] name = "pydantic" version = "2.9.2" @@ -1059,13 +828,13 @@ typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" [[package]] name = "pydantic-settings" -version = "2.6.0" +version = "2.6.1" description = "Settings management using Pydantic" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic_settings-2.6.0-py3-none-any.whl", hash = "sha256:4a819166f119b74d7f8c765196b165f95cc7487ce58ea27dec8a5a26be0970e0"}, - {file = "pydantic_settings-2.6.0.tar.gz", hash = "sha256:44a1804abffac9e6a30372bb45f6cafab945ef5af25e66b1c634c01dd39e0188"}, + {file = "pydantic_settings-2.6.1-py3-none-any.whl", hash = "sha256:7fb0637c786a558d3103436278a7c4f1cfd29ba8973238a50c5bb9a55387da87"}, + {file = "pydantic_settings-2.6.1.tar.gz", hash = "sha256:e0f92546d8a9923cb8941689abf85d6601a8c19a23e97a34b2964a2e3f813ca0"}, ] [package.dependencies] @@ -1105,17 +874,6 @@ doc = ["ablog (>=0.11.8)", "colorama", "graphviz", "ipykernel", "ipyleaflet", "i i18n = ["Babel", "jinja2"] test = ["pytest", "pytest-cov", "pytest-regressions", "sphinx[test]"] -[[package]] -name = "pyflakes" -version = "3.2.0" -description = "passive checker of Python programs" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"}, - {file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"}, -] - [[package]] name = "pygments" version = "2.18.0" @@ -1146,8 +904,8 @@ astroid = ">=3.3.4,<=3.4.0-dev0" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} dill = [ {version = ">=0.2", markers = "python_version < \"3.11\""}, - {version = ">=0.3.6", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, {version = ">=0.3.7", markers = "python_version >= \"3.12\""}, + {version = ">=0.3.6", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, ] isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" mccabe = ">=0.6,<0.8" @@ -1321,72 +1079,6 @@ urllib3 = ">=1.21.1,<3" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] -[[package]] -name = "rich" -version = "13.9.3" -description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" -optional = false -python-versions = ">=3.8.0" -files = [ - {file = "rich-13.9.3-py3-none-any.whl", hash = "sha256:9836f5096eb2172c9e77df411c1b009bace4193d6a481d534fea75ebba758283"}, - {file = "rich-13.9.3.tar.gz", hash = "sha256:bc1e01b899537598cf02579d2b9f4a415104d3fc439313a7a2c165d76557a08e"}, -] - -[package.dependencies] -markdown-it-py = ">=2.2.0" -pygments = ">=2.13.0,<3.0.0" -typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.11\""} - -[package.extras] -jupyter = ["ipywidgets (>=7.5.1,<9)"] - -[[package]] -name = "rstcheck" -version = "6.2.4" -description = "Checks syntax of reStructuredText and code blocks nested within it" -optional = false -python-versions = ">=3.8" -files = [ - {file = "rstcheck-6.2.4-py3-none-any.whl", hash = "sha256:23de2575ba0af1adcddea87a20d69187f0fb9dd8270f59eb98d63461c95375a7"}, - {file = "rstcheck-6.2.4.tar.gz", hash = "sha256:384942563dfbfcc85903a587ecf050447217c46b51e266ed3fe51371bc599015"}, -] - -[package.dependencies] -rstcheck-core = ">=1.1" -typer = ">=0.12.0" - -[package.extras] -dev = ["rstcheck[docs,sphinx,testing,toml,type-check]", "tox (>=3.15)"] -docs = ["myst-parser (>=3)", "sphinx (>=5.0)", "sphinx-autobuild (>=2021.3.14)", "sphinx-click (>=4.0.3)", "sphinx-rtd-theme (>=1.2)", "sphinxcontrib-spelling (>=7.3)"] -sphinx = ["sphinx (>=5.0)"] -testing = ["coverage-conditional-plugin (>=0.5)", "coverage[toml] (>=6.0)", "pytest (>=7.2)", "pytest-cov (>=3.0)", "pytest-randomly (>=3.0)", "pytest-sugar (>=0.9.5)"] -toml = ["tomli (>=2.0)"] -type-check = ["mypy (>=1.0)"] - -[[package]] -name = "rstcheck-core" -version = "1.2.1" -description = "Checks syntax of reStructuredText and code blocks nested within it" -optional = false -python-versions = ">=3.8" -files = [ - {file = "rstcheck-core-1.2.1.tar.gz", hash = "sha256:9b330020d912e2864f23f332c1a0569463ca3b06b8fee7b7bdd201b055f7f831"}, - {file = "rstcheck_core-1.2.1-py3-none-any.whl", hash = "sha256:1c100de418b6c9e14d9cf6558644d0ab103fdc447f891313882d02df3a3c52ba"}, -] - -[package.dependencies] -docutils = ">=0.7" -pydantic = ">=2" - -[package.extras] -dev = ["rstcheck-core[docs,sphinx,testing,toml,type-check,yaml]", "tox (>=3.15)"] -docs = ["m2r2 (>=0.3.2)", "sphinx (>=5.0,!=7.2.5)", "sphinx-autobuild (>=2021.3.14)", "sphinx-autodoc-typehints (>=1.15)", "sphinx-rtd-theme (>=1.2)", "sphinxcontrib-apidoc (>=0.3)", "sphinxcontrib-spelling (>=7.3)"] -sphinx = ["sphinx (>=5.0)"] -testing = ["coverage-conditional-plugin (>=0.5)", "coverage[toml] (>=6.0)", "pytest (>=7.2)", "pytest-cov (>=3.0)", "pytest-mock (>=3.7)", "pytest-randomly (>=3.0)", "pytest-sugar (>=0.9.5)"] -toml = ["tomli (>=2.0)"] -type-check = ["mypy (>=1.0)", "types-PyYAML (>=6.0.0)", "types-docutils (>=0.18)"] -yaml = ["pyyaml (>=6.0.0)"] - [[package]] name = "s3transfer" version = "0.10.3" @@ -1404,17 +1096,6 @@ botocore = ">=1.33.2,<2.0a.0" [package.extras] crt = ["botocore[crt] (>=1.33.2,<2.0a.0)"] -[[package]] -name = "shellingham" -version = "1.5.4" -description = "Tool to Detect Surrounding Shell" -optional = false -python-versions = ">=3.7" -files = [ - {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, - {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, -] - [[package]] name = "six" version = "1.16.0" @@ -1619,23 +1300,6 @@ files = [ {file = "tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79"}, ] -[[package]] -name = "typer" -version = "0.12.5" -description = "Typer, build great CLIs. Easy to code. Based on Python type hints." -optional = false -python-versions = ">=3.7" -files = [ - {file = "typer-0.12.5-py3-none-any.whl", hash = "sha256:62fe4e471711b147e3365034133904df3e235698399bc4de2b36c8579298d52b"}, - {file = "typer-0.12.5.tar.gz", hash = "sha256:f592f089bedcc8ec1b974125d64851029c3b1af145f04aca64d69410f0c9b722"}, -] - -[package.dependencies] -click = ">=8.0.0" -rich = ">=10.11.0" -shellingham = ">=1.3.0" -typing-extensions = ">=3.7.4.3" - [[package]] name = "typing-extensions" version = "4.12.2" @@ -1664,26 +1328,6 @@ h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] -[[package]] -name = "virtualenv" -version = "20.27.1" -description = "Virtual Python Environment builder" -optional = false -python-versions = ">=3.8" -files = [ - {file = "virtualenv-20.27.1-py3-none-any.whl", hash = "sha256:f11f1b8a29525562925f745563bfd48b189450f61fb34c4f9cc79dd5aa32a1f4"}, - {file = "virtualenv-20.27.1.tar.gz", hash = "sha256:142c6be10212543b32c6c45d3d3893dff89112cc588b7d0879ae5a1ec03a47ba"}, -] - -[package.dependencies] -distlib = ">=0.3.7,<1" -filelock = ">=3.12.2,<4" -platformdirs = ">=3.9.1,<5" - -[package.extras] -docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] -test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] - [[package]] name = "yapf" version = "0.40.2" @@ -1722,4 +1366,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.10,<3.13" -content-hash = "389caedc6b13402b512ebc5cfa5e47df15747efd982c07e7b468263316ee2f33" +content-hash = "6ae4e1ef90e523f65763423d0e4ec3ab7280e72662ffce5228533c6bfb1de4a1" diff --git a/pyproject.toml b/pyproject.toml index e4a8849..6ff97ed 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,7 +18,7 @@ boto3 = "^1.35.48" # mchbuild = "^0.4.8" load-dotenv = "^0.1.0" -[tool.poetry.group.dev.dependencies] +[tool.poetry.group.dev.dependencies] mypy = "^1.10.0" pydata-sphinx-theme = "^0.15.2" pylint = "^3.0.2" @@ -28,12 +28,6 @@ sphinx = "^8.0.2" sphinx-autoapi = "^3.1.0" yapf = "^0.40.2" autodoc-pydantic = "^2.2.0" -pre-commit = "^4.0.1" -rstcheck = "^6.2.4" -codespell = "^2.3.0" -black = "^24.10.0" -flake8 = "^7.1.1" - [tool.yapf] based_on_style = "pep8" From d174d5101548a7a9c0343c55d4981868ca52981b Mon Sep 17 00:00:00 2001 From: ninaburg Date: Wed, 6 Nov 2024 21:22:51 +0000 Subject: [PATCH 25/37] comments README.rst --- README.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index 7d17b94..0f42f17 100644 --- a/README.rst +++ b/README.rst @@ -1,8 +1,7 @@ flex-container-orchestrator =========================== -The flex-container-orchestrator manages Aviso notifications and triggers the flexprep and flexpart-ifs containers, -as well as the file aggregation script for Flexpart. +The flex-container-orchestrator manages the event driven workflow Flexpart IFS workflow, based on events from Aviso. The repo coordinates both flexprep (pre-processing of raw IFS data) and flexpart-ifs containers, ensuring all required lead time data is processed before launching Flexpart. =============== Getting started @@ -44,7 +43,7 @@ Install dependencies & start the project locally .. code-block:: console - $ poetry run uvicorn --port 8080 --reload flex_container_orchestrator.main:app + $ poetry run python3 flex_container_orchestrator/main.py --date {date} --time {time} --step {step} --location {location} ------------------------------- Run the tests and quality tools From 99c402edfcdb6d0830c107f8bb0f9c08be850ad8 Mon Sep 17 00:00:00 2001 From: ninaburg Date: Wed, 6 Nov 2024 21:46:42 +0000 Subject: [PATCH 26/37] comments: rm mchbuild from pyprojecttoml and update README --- README.rst | 31 +++++++++++++++---------------- pyproject.toml | 3 +-- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/README.rst b/README.rst index 0f42f17..084c048 100644 --- a/README.rst +++ b/README.rst @@ -7,22 +7,6 @@ The flex-container-orchestrator manages the event driven workflow Flexpart IFS w Getting started =============== ---------------------- -Setup dev environment ---------------------- - -Instead of running the steps below manually, you can install `mchbuild` and then -install, test and run the application: - -.. code-block:: console - - $ pipx install mchbuild - $ cd flex-container-orchestrator - $ mchbuild local.build local.test - $ mchbuild local.run - -Try it out at and stop it with Ctrl-C. More information can be found in :file:`.mch-ci.yml`. - ------------------------------------------------ Install dependencies & start the project locally ------------------------------------------------ @@ -68,6 +52,21 @@ Run the tests and quality tools $ poetry run mypy flex_container_orchestrator +--------------------------------------------------- +Setup dev environment - Meteoswiss environment only +--------------------------------------------------- + +Instead of running the steps below manually, you can install `mchbuild` and then +install, test and run the application: + +.. code-block:: console + + $ pipx install mchbuild + $ cd flex-container-orchestrator + $ mchbuild local.build local.test + $ mchbuild local.run + +Try it out at and stop it with Ctrl-C. More information can be found in :file:`.mch-ci.yml`. ---------------------- Generate documentation diff --git a/pyproject.toml b/pyproject.toml index 6ff97ed..231a650 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,10 +15,9 @@ pydantic = "^2.9.2" pydantic-settings = "^2.6.0" python-json-logger = "^2.0.7" boto3 = "^1.35.48" -# mchbuild = "^0.4.8" load-dotenv = "^0.1.0" -[tool.poetry.group.dev.dependencies] +[tool.poetry.group.dev.dependencies] mypy = "^1.10.0" pydata-sphinx-theme = "^0.15.2" pylint = "^3.0.2" From ca4fd31b1ea4cc78bf004bd5294d64a966441766 Mon Sep 17 00:00:00 2001 From: ninaburg Date: Wed, 6 Nov 2024 21:49:52 +0000 Subject: [PATCH 27/37] comments: top comment on mch python common files --- flex_container_orchestrator/config/base_settings.py | 3 +-- flex_container_orchestrator/config/http_audit.py | 2 +- flex_container_orchestrator/config/logger.py | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/flex_container_orchestrator/config/base_settings.py b/flex_container_orchestrator/config/base_settings.py index cbb27b5..bbea1ab 100644 --- a/flex_container_orchestrator/config/base_settings.py +++ b/flex_container_orchestrator/config/base_settings.py @@ -1,6 +1,5 @@ """ -Note: This script from mchpy (MeteoSwiss Blueprint) was added here because I could not use -mchpyy from outside of the MeteoSwiss network. +Note: This script is taken from mchpy (MeteoSwiss' Python common library) it is not currently installable outside of the MeteoSwiss network. This module introduces two custom classes that leverage Pydantic's capabilities: a custom BaseModel called SubscriptableBaseModel and a custom BaseSettings class BaseServiceSettings. diff --git a/flex_container_orchestrator/config/http_audit.py b/flex_container_orchestrator/config/http_audit.py index 8cc8c95..df1c516 100644 --- a/flex_container_orchestrator/config/http_audit.py +++ b/flex_container_orchestrator/config/http_audit.py @@ -1,5 +1,5 @@ """ -Note: This script from mchpy (MeteoSwiss Blueprint) was added here because I couldn't install mchpy from outside of the MeteoSwiss network. +Note: This script is taken from mchpy (MeteoSwiss' Python common library) it is not currently installable outside of the MeteoSwiss network. Instrumentation of web applications to include the request_id in all responses and make it available to other services (e.g. logging) diff --git a/flex_container_orchestrator/config/logger.py b/flex_container_orchestrator/config/logger.py index ddbb97e..5b0b3a6 100644 --- a/flex_container_orchestrator/config/logger.py +++ b/flex_container_orchestrator/config/logger.py @@ -1,5 +1,5 @@ """ -Note: This script from mchpy (MeteoSwiss Blueprint) was added here because I couldn't install mchpy from outside of the MeteoSwiss network. +Note: This script is taken from mchpy (MeteoSwiss' Python common library) it is not currently installable outside of the MeteoSwiss network. Logging configuration including http request auditing """ From 20539346aabf941cc6cb91bfdc0daf1122aacc09 Mon Sep 17 00:00:00 2001 From: ninaburg Date: Wed, 6 Nov 2024 21:56:29 +0000 Subject: [PATCH 28/37] comments: settings description and typo --- flex_container_orchestrator/config/service_settings.py | 4 ++++ flex_container_orchestrator/config/settings.yaml | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/flex_container_orchestrator/config/service_settings.py b/flex_container_orchestrator/config/service_settings.py index c4815e6..b0dacbf 100644 --- a/flex_container_orchestrator/config/service_settings.py +++ b/flex_container_orchestrator/config/service_settings.py @@ -6,9 +6,13 @@ class TimeSettings(BaseModel): + # Number of hours between timesteps tincr: int + # Number of timesteps to run Flexpart with (temporarily set to 6 timesteps but operational config is 90) tdelta: int + # Frequency of Flexpart runs in hours tfreq_f: int + # Frequency of IFS runs in hours tfreq: int diff --git a/flex_container_orchestrator/config/settings.yaml b/flex_container_orchestrator/config/settings.yaml index 264e69d..fe0652c 100644 --- a/flex_container_orchestrator/config/settings.yaml +++ b/flex_container_orchestrator/config/settings.yaml @@ -13,7 +13,7 @@ main: tincr: 1 # Number of timesteps to run Flexpart with (temporarily set to 6 timesteps but operational config is 90) tdelta: 6 - # Frequency of Flexpart runs in hour + # Frequency of Flexpart runs in hours tfreq_f: 6 - # Frequency of IFS runs in hour + # Frequency of IFS runs in hours tfreq: 6 From 66c9cdb2a716e3c895e6da62168b5a06b6123bfa Mon Sep 17 00:00:00 2001 From: ninaburg Date: Wed, 6 Nov 2024 22:08:02 +0000 Subject: [PATCH 29/37] comments: add typing in the parsed args --- flex_container_orchestrator/main.py | 31 +++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/flex_container_orchestrator/main.py b/flex_container_orchestrator/main.py index d7e0bf8..c61aee5 100644 --- a/flex_container_orchestrator/main.py +++ b/flex_container_orchestrator/main.py @@ -8,10 +8,33 @@ def main() -> None: parser = argparse.ArgumentParser() - parser.add_argument("--date", required=True, help="Date parameter") - parser.add_argument("--location", required=True, help="Location parameter") - parser.add_argument("--time", required=True, help="Time parameter") - parser.add_argument("--step", required=True, help="Step parameter") + parser.add_argument( + "--date", + type=str, + required=True, + help="Date parameter in format YYYYMMDD" + ) + + parser.add_argument( + "--location", + type=str, + required=True, + help="Location parameter" + ) + + parser.add_argument( + "--time", + type=str, + required=True, + help="Time parameter in format HH" + ) + + parser.add_argument( + "--step", + type=str, + required=True, + help="Step parameter" + ) args = parser.parse_args() flexpart_service.launch_containers(args.date, args.location, args.time, args.step) From 836382d493ca832529da2416ed421cb586963905 Mon Sep 17 00:00:00 2001 From: ninaburg Date: Wed, 6 Nov 2024 22:16:58 +0000 Subject: [PATCH 30/37] comments: use logging.getLogger(__name__) --- .../domain/aggregator_flexpart.py | 20 +++++++------ .../services/flexpart_service.py | 29 ++++++++++--------- 2 files changed, 26 insertions(+), 23 deletions(-) diff --git a/flex_container_orchestrator/domain/aggregator_flexpart.py b/flex_container_orchestrator/domain/aggregator_flexpart.py index b002811..e3e368d 100644 --- a/flex_container_orchestrator/domain/aggregator_flexpart.py +++ b/flex_container_orchestrator/domain/aggregator_flexpart.py @@ -10,6 +10,8 @@ from flex_container_orchestrator import CONFIG +logger = logging.getLogger(__name__) + def connect_db(db_path: str) -> sqlite3.Connection: """ Establish a connection to the SQLite database. @@ -22,10 +24,10 @@ def connect_db(db_path: str) -> sqlite3.Connection: """ try: conn = sqlite3.connect(db_path) - logging.info("Connected to SQLite database at %s.", db_path) + logger.info("Connected to SQLite database at %s.", db_path) return conn except sqlite3.Error as e: - logging.error("SQLite connection error: %s", e) + logger.error("SQLite connection error: %s", e) sys.exit(1) @@ -55,14 +57,14 @@ def is_row_processed( result = cursor.fetchone() if result: return result[0] == 1 - logging.info( + logger.info( "No row found for forecast_ref_time=%s and step=%s.", forecast_ref_time, step, ) return False except sqlite3.Error as e: - logging.error("SQLite query error: %s", e) + logger.error("SQLite query error: %s", e) sys.exit(1) @@ -152,7 +154,7 @@ def fetch_processed_items( ) processed_items.add(frt_str + f"{int(item[1]):02}") except sqlite3.Error as e: - logging.error("SQLite query error while fetching processed items: %s", e) + logger.error("SQLite query error while fetching processed items: %s", e) sys.exit(1) return processed_items @@ -168,7 +170,7 @@ def define_config(st: datetime.datetime, et: datetime.datetime) -> dict: Returns: dict: Configuration dictionary for Flexpart. """ - logging.info("Start and end time to configure Flexpart: %s and %s ", st, et) + logger.info("Start and end time to configure Flexpart: %s and %s ", st, et) configuration = { "IBDATE": st.strftime("%Y%m%d"), @@ -177,7 +179,7 @@ def define_config(st: datetime.datetime, et: datetime.datetime) -> dict: "IETIME": et.strftime("%H"), } - logging.info("Configuration to run Flexpart: %s", json.dumps(configuration)) + logger.debug("Configuration to run Flexpart: %s", json.dumps(configuration)) return configuration @@ -232,7 +234,7 @@ def generate_forecast_times( all_list_ltf = [] all_list_lt = [] for start_time in list_start_times: - logging.info("Start time: %s", start_time) + logger.info("Start time: %s", start_time) list_ltf = [] list_lt = [] for i in range(0, time_settings["tdelta"], time_settings["tincr"]): @@ -301,7 +303,7 @@ def run_aggregator(date: str, time: str, step: int, db_path: str) -> List[dict]: frt_dt = parse_forecast_datetime(date, time) if not is_row_processed(conn, frt_dt, step): - logging.info("File processing incomplete. Exiting before launching Flexpart.") + logger.info("File processing incomplete. Exiting before launching Flexpart.") conn.close() sys.exit(0) diff --git a/flex_container_orchestrator/services/flexpart_service.py b/flex_container_orchestrator/services/flexpart_service.py index cd8e8ae..2dc7905 100755 --- a/flex_container_orchestrator/services/flexpart_service.py +++ b/flex_container_orchestrator/services/flexpart_service.py @@ -6,6 +6,8 @@ from flex_container_orchestrator.domain.aggregator_flexpart import run_aggregator +logger = logging.getLogger(__name__) + def run_command(command: list[str] | str, capture_output: bool = False) -> bytes | None: """ @@ -16,14 +18,13 @@ def run_command(command: list[str] | str, capture_output: bool = False) -> bytes return subprocess.check_output(command).strip() subprocess.check_call(command) except subprocess.CalledProcessError as e: - logging.error(f"Command '{' '.join(command)}' failed with error: {e}") + logger.error(f"Command '{' '.join(command)}' failed with error: {e}") sys.exit(1) return None def launch_containers(date: str, location: str, time: str, step: str) -> None: - logging.basicConfig(level=logging.INFO) # Retrieve ECR login password and log in to Docker try: @@ -43,11 +44,11 @@ def launch_containers(date: str, location: str, time: str, step: str) -> None: process.communicate(input=login_password) if process.returncode != 0: - logging.error("Docker login failed. Exiting.") + logger.error("Docker login failed. Exiting.") sys.exit(1) except subprocess.CalledProcessError as e: - logging.error("Error logging in to Docker: %s", e) + logger.error("Error logging in to Docker: %s", e) sys.exit(1) # ====== First part: Run pre-processing for Flexpart ====== @@ -58,7 +59,7 @@ def launch_containers(date: str, location: str, time: str, step: str) -> None: ) if not os.path.exists(db_mount): - logging.error("SQLite database directory %s does not exist.", db_mount) + logger.error("SQLite database directory %s does not exist.", db_mount) sys.exit(1) try: @@ -83,32 +84,32 @@ def launch_containers(date: str, location: str, time: str, step: str) -> None: run_command(docker_run_command) # type: ignore except subprocess.CalledProcessError: - logging.error("Docker run processing failed.") + logger.error("Docker run processing failed.") sys.exit(1) - logging.info("Pre-processing container executed successfully.") + logger.info("Pre-processing container executed successfully.") # ====== Second part: Run aggregator_flexpart.py ====== db_path = os.path.join(db_mount, "sqlite3-db") if not os.path.exists(db_path): - logging.error("Database file %s does not exist.", db_path) + logger.error("Database file %s does not exist.", db_path) sys.exit(1) try: configurations = run_aggregator(date, time, int(step), db_path) except Exception as e: - logging.error("Aggregator encountered an error: %s", e) + logger.error("Aggregator encountered an error: %s", e) sys.exit(1) - logging.info("Aggregator launch script executed successfully.") + logger.info("Aggregator launch script executed successfully.") # ====== Third part: Run Flexpart ====== try: # Check if configurations is an empty list if not configurations: - logging.info("Not enough data to launch Flexpart.") + logger.info("Not enough data to launch Flexpart.") sys.exit(0) # Loop through each configuration and execute Flexpart @@ -125,11 +126,11 @@ def launch_containers(date: str, location: str, time: str, step: str) -> None: docker_image, ] - logging.info("Running: %s", " ".join(docker_command)) + logger.info("Running: %s", " ".join(docker_command)) run_command(docker_command) except subprocess.CalledProcessError: - logging.error("Launch Flexpart script encountered an error.") + logger.error("Launch Flexpart script encountered an error.") sys.exit(1) - logging.info("Launch Flexpart script executed successfully.") + logger.info("Launch Flexpart script executed successfully.") From 30e663f499cf93a92c32a853e73f59d8d6e88342 Mon Sep 17 00:00:00 2001 From: ninaburg Date: Wed, 6 Nov 2024 22:26:58 +0000 Subject: [PATCH 31/37] comments: extract login_ecr() --- .../services/flexpart_service.py | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/flex_container_orchestrator/services/flexpart_service.py b/flex_container_orchestrator/services/flexpart_service.py index 2dc7905..b77bda1 100755 --- a/flex_container_orchestrator/services/flexpart_service.py +++ b/flex_container_orchestrator/services/flexpart_service.py @@ -1,4 +1,3 @@ -import json import logging import os import subprocess @@ -8,7 +7,6 @@ logger = logging.getLogger(__name__) - def run_command(command: list[str] | str, capture_output: bool = False) -> bytes | None: """ Helper function to run shell commands and handle errors. @@ -23,21 +21,23 @@ def run_command(command: list[str] | str, capture_output: bool = False) -> bytes return None - -def launch_containers(date: str, location: str, time: str, step: str) -> None: - - # Retrieve ECR login password and log in to Docker +def login_ecr(region="eu-central-2", repo_url="493666016161.dkr.ecr.eu-central-2.amazonaws.com"): + """ + Log in to AWS ECR by retrieving the login password and passing it to Docker login. + """ try: - login_command = ["aws", "ecr", "get-login-password", "--region", "eu-central-2"] + # Step 1: Get the ECR login password + login_command = ["aws", "ecr", "get-login-password", "--region", region] login_password = run_command(login_command, capture_output=True) + # Step 2: Log in to Docker using the password docker_login_command = [ "docker", "login", "--username", "AWS", "--password-stdin", - "493666016161.dkr.ecr.eu-central-2.amazonaws.com", + repo_url, ] process = subprocess.Popen(docker_login_command, stdin=subprocess.PIPE) @@ -51,6 +51,10 @@ def launch_containers(date: str, location: str, time: str, step: str) -> None: logger.error("Error logging in to Docker: %s", e) sys.exit(1) +def launch_containers(date: str, location: str, time: str, step: str) -> None: + # Retrieve ECR login password and log in to Docker + login_ecr() + # ====== First part: Run pre-processing for Flexpart ====== db_mount = os.path.expanduser(f"{os.getenv('DB_MOUNT')}") docker_image = f"{os.getenv('FLEXPREP_ECR_REPO')}:{os.getenv('FLEXPREP_TAG')}" From 2d0dd20d94b364975ebf259d092c8a607070f6d0 Mon Sep 17 00:00:00 2001 From: ninaburg Date: Wed, 6 Nov 2024 22:29:15 +0000 Subject: [PATCH 32/37] comments: descriptive env. var. --- flex_container_orchestrator/config/.env | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/flex_container_orchestrator/config/.env b/flex_container_orchestrator/config/.env index 3d96aa6..e4c4ae7 100644 --- a/flex_container_orchestrator/config/.env +++ b/flex_container_orchestrator/config/.env @@ -16,8 +16,8 @@ SVC__MAIN__TIME_SETTINGS__TINCR=1 SVC__MAIN__TIME_SETTINGS__TSTART=0 ## IMAGE -TAG=2410.d61a35cadc7965c78a4f9ed8bae66e69772d14fe -ECR_REPO=493666016161.dkr.ecr.eu-central-2.amazonaws.com/numericalweatherpredictions/dispersionmodelling/flexpart-ifs/flexprep +FLEXPREP_TAG=2411.85789914152b732ef17ad65bd7d6e57ecb30ee3d +FLEXPREP_ECR_REPO=493666016161.dkr.ecr.eu-central-2.amazonaws.com/numericalweatherpredictions/dispersionmodelling/flexpart-ifs/flexprep ## sqlite DB DB_MOUNT=~/.sqlite/ @@ -31,11 +31,14 @@ FLEXPART_S3_BUCKETS__INPUT__ENDPOINT_URL=https://object-store.os-api.cci1.ecmwf. FLEXPART_S3_BUCKETS__INPUT__NAME=flexprep-output FLEXPART_S3_BUCKETS__OUTPUT__ENDPOINT_URL=https://object-store.os-api.cci1.ecmwf.int FLEXPART_S3_BUCKETS__OUTPUT__NAME=flexpart-output - ## EWC buckets access and secret keys S3_ACCESS_KEY= S3_SECRET_KEY= +## IMAGE +FLEXPART_TAG=2411.b41e06a177edb101e546f6b180b342e7e32bff8f +FLEXPART_ECR_REPO=493666016161.dkr.ecr.eu-central-2.amazonaws.com/numericalweatherpredictions/dispersionmodelling/flexpart-ifs/flexpart-containerize + ## Time settings # Number of timestep(s) to run Flexpart with (temporarily set to 6 timesteps but operational config is 90) TDELTA=6 From 34afdee4b951c3fe9a4135c4198aa6b01f2bc112 Mon Sep 17 00:00:00 2001 From: ninaburg Date: Wed, 6 Nov 2024 22:31:50 +0000 Subject: [PATCH 33/37] comments:renamed module lead_time_aggregator.py --- .../{aggregator_flexpart.py => lead_time_aggregator.py} | 5 +---- flex_container_orchestrator/services/flexpart_service.py | 4 ++-- ...t_aggregator_flexpart.py => test_lead_time_aggregator.py} | 0 3 files changed, 3 insertions(+), 6 deletions(-) rename flex_container_orchestrator/domain/{aggregator_flexpart.py => lead_time_aggregator.py} (99%) rename test/domain/{test_aggregator_flexpart.py => test_lead_time_aggregator.py} (100%) diff --git a/flex_container_orchestrator/domain/aggregator_flexpart.py b/flex_container_orchestrator/domain/lead_time_aggregator.py similarity index 99% rename from flex_container_orchestrator/domain/aggregator_flexpart.py rename to flex_container_orchestrator/domain/lead_time_aggregator.py index e3e368d..83b7c1c 100644 --- a/flex_container_orchestrator/domain/aggregator_flexpart.py +++ b/flex_container_orchestrator/domain/lead_time_aggregator.py @@ -1,12 +1,9 @@ -#!/usr/bin/env python3 - -import argparse import datetime import json import logging import sqlite3 import sys -from typing import List, Optional, Set, Tuple +from typing import List, Set, Tuple from flex_container_orchestrator import CONFIG diff --git a/flex_container_orchestrator/services/flexpart_service.py b/flex_container_orchestrator/services/flexpart_service.py index b77bda1..e5115b9 100755 --- a/flex_container_orchestrator/services/flexpart_service.py +++ b/flex_container_orchestrator/services/flexpart_service.py @@ -3,7 +3,7 @@ import subprocess import sys -from flex_container_orchestrator.domain.aggregator_flexpart import run_aggregator +from flex_container_orchestrator.domain.lead_time_aggregator import run_aggregator logger = logging.getLogger(__name__) @@ -93,7 +93,7 @@ def launch_containers(date: str, location: str, time: str, step: str) -> None: logger.info("Pre-processing container executed successfully.") - # ====== Second part: Run aggregator_flexpart.py ====== + # ====== Second part: Run lead_time_aggregator.py ====== db_path = os.path.join(db_mount, "sqlite3-db") if not os.path.exists(db_path): diff --git a/test/domain/test_aggregator_flexpart.py b/test/domain/test_lead_time_aggregator.py similarity index 100% rename from test/domain/test_aggregator_flexpart.py rename to test/domain/test_lead_time_aggregator.py From b66f191376ffd0540c3d4472070d36d6c5a860d4 Mon Sep 17 00:00:00 2001 From: ninaburg Date: Wed, 6 Nov 2024 22:43:14 +0000 Subject: [PATCH 34/37] descriptive function names --- flex_container_orchestrator/domain/lead_time_aggregator.py | 5 ++--- flex_container_orchestrator/services/flexpart_service.py | 2 -- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/flex_container_orchestrator/domain/lead_time_aggregator.py b/flex_container_orchestrator/domain/lead_time_aggregator.py index 83b7c1c..c0adaad 100644 --- a/flex_container_orchestrator/domain/lead_time_aggregator.py +++ b/flex_container_orchestrator/domain/lead_time_aggregator.py @@ -28,7 +28,7 @@ def connect_db(db_path: str) -> sqlite3.Connection: sys.exit(1) -def is_row_processed( +def is_lead_time_processed( conn: sqlite3.Connection, forecast_ref_time: datetime.datetime, step: str ) -> bool: """ @@ -299,7 +299,7 @@ def run_aggregator(date: str, time: str, step: int, db_path: str) -> List[dict]: conn = connect_db(db_path) frt_dt = parse_forecast_datetime(date, time) - if not is_row_processed(conn, frt_dt, step): + if not is_lead_time_processed(conn, frt_dt, step): logger.info("File processing incomplete. Exiting before launching Flexpart.") conn.close() sys.exit(0) @@ -321,4 +321,3 @@ def run_aggregator(date: str, time: str, step: int, db_path: str) -> List[dict]: conn.close() return configs - diff --git a/flex_container_orchestrator/services/flexpart_service.py b/flex_container_orchestrator/services/flexpart_service.py index e5115b9..9aee5d8 100755 --- a/flex_container_orchestrator/services/flexpart_service.py +++ b/flex_container_orchestrator/services/flexpart_service.py @@ -136,5 +136,3 @@ def launch_containers(date: str, location: str, time: str, step: str) -> None: except subprocess.CalledProcessError: logger.error("Launch Flexpart script encountered an error.") sys.exit(1) - - logger.info("Launch Flexpart script executed successfully.") From fc413e83c0e9f92281b56caf52d157ce50dc9b38 Mon Sep 17 00:00:00 2001 From: ninaburg Date: Wed, 6 Nov 2024 22:46:15 +0000 Subject: [PATCH 35/37] comments clean up --- flex_container_orchestrator/domain/lead_time_aggregator.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flex_container_orchestrator/domain/lead_time_aggregator.py b/flex_container_orchestrator/domain/lead_time_aggregator.py index c0adaad..125bce1 100644 --- a/flex_container_orchestrator/domain/lead_time_aggregator.py +++ b/flex_container_orchestrator/domain/lead_time_aggregator.py @@ -142,14 +142,14 @@ def fetch_processed_items( (frt,), ) items_f = cursor.fetchall() - for item in items_f: - if item[0]: # processed == True + for processed, step in items_f: + if processed: frt_str = ( frt.strftime("%Y%m%d%H%M") if isinstance(frt, datetime.datetime) else str(frt) ) - processed_items.add(frt_str + f"{int(item[1]):02}") + processed_items.add(frt_str + f"{int(step):02}") except sqlite3.Error as e: logger.error("SQLite query error while fetching processed items: %s", e) sys.exit(1) From d84223a92f39b6c21519e670d50267ce0b9a5d5a Mon Sep 17 00:00:00 2001 From: ninaburg Date: Fri, 8 Nov 2024 12:33:47 +0000 Subject: [PATCH 36/37] comments: improve understability of the aggregator module --- .../domain/lead_time_aggregator.py | 242 +++++++++--------- .../services/flexpart_service.py | 5 - test/domain/test_lead_time_aggregator.py | 8 +- 3 files changed, 122 insertions(+), 133 deletions(-) diff --git a/flex_container_orchestrator/domain/lead_time_aggregator.py b/flex_container_orchestrator/domain/lead_time_aggregator.py index 125bce1..5e72d3a 100644 --- a/flex_container_orchestrator/domain/lead_time_aggregator.py +++ b/flex_container_orchestrator/domain/lead_time_aggregator.py @@ -3,7 +3,6 @@ import logging import sqlite3 import sys -from typing import List, Set, Tuple from flex_container_orchestrator import CONFIG @@ -28,48 +27,15 @@ def connect_db(db_path: str) -> sqlite3.Connection: sys.exit(1) -def is_lead_time_processed( - conn: sqlite3.Connection, forecast_ref_time: datetime.datetime, step: str -) -> bool: - """ - Check if a specific row in the database has been processed. - - Args: - conn (sqlite3.Connection): SQLite connection object. - forecast_ref_time (datetime): Forecast reference time - step (str): Step identifier. - - Returns: - bool: True if processed, False otherwise. - """ - try: - cursor = conn.cursor() - cursor.execute( - """ - SELECT processed FROM uploaded - WHERE forecast_ref_time = ? AND step = ? - """, - (forecast_ref_time, step), - ) - result = cursor.fetchone() - if result: - return result[0] == 1 - logger.info( - "No row found for forecast_ref_time=%s and step=%s.", - forecast_ref_time, - step, - ) - return False - except sqlite3.Error as e: - logger.error("SQLite query error: %s", e) - sys.exit(1) - - def generate_flexpart_start_times( frt_dt: datetime.datetime, lead_time: int, tdelta: int, tfreq_f: int -) -> List[datetime.datetime]: +) -> list[datetime.datetime]: """ - Generate a list of Flexpart run start times. + Generates a list of start reference times for running Flexpart simulations. + + The start times will depend on the forecast reference time and lead time that + have been just pre-processed, the desired number of time steps for the Flexpart run, + and the frequency of the flexpart runs. Args: frt_dt (datetime.datetime): Forecast reference datetime. @@ -97,31 +63,37 @@ def generate_flexpart_start_times( return list_start_times -def convert_time_to_frt(time: datetime.datetime, tfreq: int) -> str: +def generate_forecast_label(lead_time: datetime.datetime, tfreq: int) -> str: """ - Convert time object into IFS forecast objects to use. + Returns a string in the format "{reference_time}_{step}" based on + the given lead_time (i.e. forecast reference time + step). + + The reference_time corresponds to the latest IFS forecast run for the specified lead time. + If the lead_time aligns with the start of an IFS simulation, it uses the previous forecast + run with the appropriate lead time instead of the forecast at step 0. Args: - time (datetime.datetime): Datetime object. + lead_time (datetime.datetime): Forecasts leadtime tfreq (int): Frequency of IFS forecast times in hours. Returns: str: Forecast reference time (YYYYMMDDHH) followed by the lead time (HH) + in the format "{reference_time}_{step}" """ - if time.hour % tfreq != 0: - frt_st = time - datetime.timedelta(hours=time.hour % tfreq) - lt = time.hour % tfreq + if lead_time.hour % tfreq != 0: + frt_st = lead_time - datetime.timedelta(hours=lead_time.hour % tfreq) + lt = lead_time.hour % tfreq else: - frt_st = time - datetime.timedelta(hours=tfreq) + frt_st = lead_time - datetime.timedelta(hours=tfreq) lt = tfreq return frt_st.strftime("%Y%m%d%H%M") + f"{lt:02}" -def fetch_processed_items( - conn: sqlite3.Connection, frt_s: Set[datetime.datetime] -) -> Set[str]: +def fetch_processed_forecasts( + conn: sqlite3.Connection, frt_s: set[datetime.datetime] +) -> set[str]: """ - Fetch all processed items from the database. + Fetch all processed forecasts from the database for a specific reference time. Args: conn (sqlite3.Connection): SQLite connection object. @@ -142,7 +114,7 @@ def fetch_processed_items( (frt,), ) items_f = cursor.fetchall() - for processed, step in items_f: + for processed, step in items_f: if processed: frt_str = ( frt.strftime("%Y%m%d%H%M") @@ -156,24 +128,24 @@ def fetch_processed_items( return processed_items -def define_config(st: datetime.datetime, et: datetime.datetime) -> dict: +def define_config(start_time: datetime.datetime, end_time: datetime.datetime) -> dict: """ - Define configuration for Flexpart. + Define input configuration for Flexpart based on provided start and end tims. Args: - st (datetime.datetime): Start time. - et (datetime.datetime): End time. + start_time (datetime.datetime): Start time. + end_time (datetime.datetime): End time. Returns: dict: Configuration dictionary for Flexpart. """ - logger.info("Start and end time to configure Flexpart: %s and %s ", st, et) + logger.debug("Start and end time to configure Flexpart: %s and %s ", start_time, end_time) configuration = { - "IBDATE": st.strftime("%Y%m%d"), - "IBTIME": st.strftime("%H"), - "IEDATE": et.strftime("%Y%m%d"), - "IETIME": et.strftime("%H"), + "IBDATE": start_time.strftime("%Y%m%d"), # Start date in YYYYMMDD format + "IBTIME": start_time.strftime("%H"), # Start time in HH format + "IEDATE": end_time.strftime("%Y%m%d"), # End date in YYYYMMDD format + "IETIME": end_time.strftime("%H"), # End time in HH format } logger.debug("Configuration to run Flexpart: %s", json.dumps(configuration)) @@ -215,34 +187,42 @@ def parse_forecast_datetime(date_str: str, time_str: str) -> datetime.datetime: def generate_forecast_times( - list_start_times: List[datetime.datetime], time_settings: dict -) -> Tuple[List[List[str]], List[List[datetime.datetime]], Set[str]]: + start_times: list[datetime.datetime], time_settings: dict +) -> tuple[list[list[str]], list[list[datetime.datetime]], set[str]]: """ - Generate forecast times for Flexpart runs. + Generates a list of all required forecasts for Flexpart simulations. Args: - list_start_times (list of datetime.datetime): List of Flexpart run start times. - time_settings (dict): Time settings dictionary. - + start_times (list[datetime]): List of Flexpart run start reference times. + time_settings (dict[str, int]): Configuration containing 'tdelta' (total forecast duration in hours), + 'tincr' (increment step in hours), and 'tfreq' (frequency interval). Returns: - tuple: Tuple containing lists of forecast times, lead times, and all steps. + tuple[list[list[str]], list[list[datetime]], set[str]]: + - all_input_forecasts: A nested list where each sublist contains forecast labels + for each Flexpart run in the format "{reference_time}_{step}". + - all_flexpart_leadtimes: A nested list where each sublist contains datetime objects + representing the leadtimes (reference_time + step) for each Flexpart run. + - all_input_forecasts_set: A set of unique forecasts in the format "{reference_time}_{step}" + required for Flexpart simulations. """ - all_steps = set() - all_list_ltf = [] - all_list_lt = [] - for start_time in list_start_times: - logger.info("Start time: %s", start_time) - list_ltf = [] - list_lt = [] - for i in range(0, time_settings["tdelta"], time_settings["tincr"]): - time = start_time + datetime.timedelta(hours=i) - forecast = convert_time_to_frt(time, time_settings["tfreq"]) - list_ltf.append(forecast) - list_lt.append(time) - all_steps.add(forecast) - all_list_ltf.append(list_ltf) - all_list_lt.append(list_lt) - return all_list_ltf, all_list_lt, all_steps + time_delta = time_settings['tdelta'] + time_increment = time_settings['tincr'] + run_frequency = time_settings['tfreq'] + + all_input_forecasts_set = set() + all_input_forecasts = [] + all_flexpart_leadtimes = [] + + for start_time in start_times: + lead_times = [start_time + datetime.timedelta(hours=i) for i in range(0, time_delta, time_increment)] + input_forecasts = [generate_forecast_label(lt, run_frequency) for lt in lead_times] + + all_input_forecasts_set.update(input_forecasts) + all_input_forecasts.append(input_forecasts) + all_flexpart_leadtimes.append(lead_times) + + return all_input_forecasts, all_flexpart_leadtimes, all_input_forecasts_set + def strip_lead_time(forecast: str) -> datetime.datetime: @@ -259,65 +239,79 @@ def strip_lead_time(forecast: str) -> datetime.datetime: def create_flexpart_configs( - all_list_lt: List[List[datetime.datetime]], - all_list_ltf: List[List[str]], - processed_items: Set[str], -) -> List[dict]: + all_flexpart_leadtimes: list[list[datetime.datetime]], + all_input_forecasts: list[list[str]], + processed_forecasts: set[str], +) -> list[dict]: """ - Create Flexpart configurations based on processed items. + Create Flexpart input configurations based on processed forecasts. Args: - all_list_lt (list of list of datetime.datetime): List of lead times. - all_list_ltf (list of list of str): List of forecast reference times with lead times. - processed_items (set of str): Set of processed item identifiers. + - all_input_forecasts: A nested list where each sublist contains forecast labels + for each Flexpart run in the format "{reference_time}_{step}". + - all_flexpart_leadtimes: A nested list where each sublist contains datetime objects + representing the lead times for each Flexpart run. + - processed_forecasts (set of str): Set of forecasts marked as processed and retrieved from the DB for + which have the same reference times as the forecasts needed for the Flexpart simulation Returns: - list of dict: List of Flexpart configuration dictionaries. + list of dict: List of Flexpart configuration dictionaries. If no valid + configurations can be created, an empty list is returned. """ configs = [] - for i, flexpart_run in enumerate(all_list_ltf): - if all(item in processed_items for item in flexpart_run): - config = define_config(all_list_lt[i][0], all_list_lt[i][-1]) + for run_index, input_forecasts_for_run in enumerate(all_input_forecasts): + if all(forecast in processed_forecasts for forecast in input_forecasts_for_run): + config = define_config(all_flexpart_leadtimes[run_index][0], all_flexpart_leadtimes[run_index][-1]) configs.append(config) return configs -def run_aggregator(date: str, time: str, step: int, db_path: str) -> List[dict]: +def run_aggregator(date: str, time: str, step: int, db_path: str) -> list[dict]: """ - Run the aggregator function with the provided arguments. + Checks if Flexpart can be launched with the processed new lead time and prepares input configurations. Args: - date (str): Date in YYYYMMDD format. - time (str): Time in HH format. - step (int): Step identifier (lead time in hours). + date (str): The forecast reference date in YYYYMMDD format. + time (str): The forecast reference time in HH format. + step (int): The lead time in hours. db_path (str): Path to the SQLite database. Returns: - List[dict]: List of configuration dictionaries for Flexpart. + list[dict]: List of configuration dictionaries for Flexpart. """ time_settings = get_time_settings(CONFIG) - conn = connect_db(db_path) - frt_dt = parse_forecast_datetime(date, time) - - if not is_lead_time_processed(conn, frt_dt, step): - logger.info("File processing incomplete. Exiting before launching Flexpart.") - conn.close() - sys.exit(0) - - list_start_times = generate_flexpart_start_times( - frt_dt, - step, - time_settings["tdelta"], - time_settings["tfreq_f"], - ) - all_list_ltf, all_list_lt, all_steps = generate_forecast_times( - list_start_times, time_settings - ) - frt_set = {strip_lead_time(forecast) for forecast in all_steps} - processed_items = fetch_processed_items(conn, frt_set) + # Use a context manager for the database connection to ensure proper resource cleanup + with connect_db(db_path) as conn: + try: + forecast_leadtime = parse_forecast_datetime(date, time) + start_times = generate_flexpart_start_times( + forecast_leadtime, + step, + time_settings["tdelta"], + time_settings["tfreq_f"] + ) + + input_forecasts, flexpart_leadtimes, input_forecasts_set = generate_forecast_times( + start_times, time_settings + ) - configs = create_flexpart_configs(all_list_lt, all_list_ltf, processed_items) - conn.close() + # Retrieve processed forecasts from the database + processed_forecasts = fetch_processed_forecasts( + conn, {strip_lead_time(forecast) for forecast in input_forecasts_set} + ) - return configs + # Create input configurations if processed forecasts are ready + configs = create_flexpart_configs( + flexpart_leadtimes, input_forecasts, processed_forecasts + ) + + if not configs: + logger.info("Not enough pre-processed forecasts to run Flexpart.") + sys.exit(0) + + return configs + + except Exception as e: + logger.error("An error occurred while running the aggregator: %s", e) + raise diff --git a/flex_container_orchestrator/services/flexpart_service.py b/flex_container_orchestrator/services/flexpart_service.py index 9aee5d8..968a48a 100755 --- a/flex_container_orchestrator/services/flexpart_service.py +++ b/flex_container_orchestrator/services/flexpart_service.py @@ -111,11 +111,6 @@ def launch_containers(date: str, location: str, time: str, step: str) -> None: # ====== Third part: Run Flexpart ====== try: - # Check if configurations is an empty list - if not configurations: - logger.info("Not enough data to launch Flexpart.") - sys.exit(0) - # Loop through each configuration and execute Flexpart docker_image = f"{os.getenv('FLEXPART_ECR_REPO')}:{os.getenv('FLEXPART_TAG')}" for config in configurations: diff --git a/test/domain/test_lead_time_aggregator.py b/test/domain/test_lead_time_aggregator.py index cfbc812..522df39 100644 --- a/test/domain/test_lead_time_aggregator.py +++ b/test/domain/test_lead_time_aggregator.py @@ -3,9 +3,9 @@ import pytest -from flex_container_orchestrator.domain.aggregator_flexpart import ( +from flex_container_orchestrator.domain.lead_time_aggregatorimport ( convert_time_to_frt, define_config, fetch_processed_items, - generate_flexpart_start_times, is_row_processed) + generate_flexpart_start_times, is_lead_time_processed) @pytest.mark.parametrize( @@ -15,7 +15,7 @@ ((0,), False), # Case where row is not processed ], ) -def test_is_row_processed(fetchone_return, expected_result): +def test_lead_time_processed(fetchone_return, expected_result): mock_conn = MagicMock() mock_cursor = MagicMock() @@ -26,7 +26,7 @@ def test_is_row_processed(fetchone_return, expected_result): fetchone_return # fetchone returns the tuple for testing ) - result = is_row_processed(mock_conn, datetime.datetime(2023, 10, 22, 6, 0), "12") + result = is_lead_time_processed(mock_conn, datetime.datetime(2023, 10, 22, 6, 0), "12") assert result == expected_result From 98e7dd511510b6e01997de86ef818c96ff731921 Mon Sep 17 00:00:00 2001 From: ninaburg Date: Fri, 8 Nov 2024 14:14:18 +0000 Subject: [PATCH 37/37] fix tests --- test/domain/test_lead_time_aggregator.py | 37 +++++------------------- 1 file changed, 7 insertions(+), 30 deletions(-) diff --git a/test/domain/test_lead_time_aggregator.py b/test/domain/test_lead_time_aggregator.py index 522df39..2979d53 100644 --- a/test/domain/test_lead_time_aggregator.py +++ b/test/domain/test_lead_time_aggregator.py @@ -3,32 +3,9 @@ import pytest -from flex_container_orchestrator.domain.lead_time_aggregatorimport ( - convert_time_to_frt, define_config, fetch_processed_items, - generate_flexpart_start_times, is_lead_time_processed) - - -@pytest.mark.parametrize( - "fetchone_return, expected_result", - [ - ((1,), True), # Case where row is processed - ((0,), False), # Case where row is not processed - ], -) -def test_lead_time_processed(fetchone_return, expected_result): - mock_conn = MagicMock() - mock_cursor = MagicMock() - - mock_conn.cursor.return_value = mock_cursor - - mock_cursor.execute.return_value = mock_cursor # execute returns the cursor itself - mock_cursor.fetchone.return_value = ( - fetchone_return # fetchone returns the tuple for testing - ) - - result = is_lead_time_processed(mock_conn, datetime.datetime(2023, 10, 22, 6, 0), "12") - - assert result == expected_result +from flex_container_orchestrator.domain.lead_time_aggregator import ( + generate_forecast_label, define_config, fetch_processed_forecasts, + generate_flexpart_start_times) @pytest.mark.parametrize( @@ -72,19 +49,19 @@ def test_generate_flexpart_start_times(frt_dt, lead_time, tdelta, tfreq_f, expec (datetime.datetime(2023, 10, 22, 12, 0), 6, "20231022060006"), ], ) -def test_convert_time_to_frt(time, tfreq, expected): - result = convert_time_to_frt(time, tfreq) +def test_generate_forecast_label(time, tfreq, expected): + result = generate_forecast_label(time, tfreq) assert result == expected @patch("sqlite3.connect") -def test_fetch_processed_items(mock_connect): +def test_fetch_processed_forecasts(mock_connect): mock_conn = MagicMock() mock_cursor = MagicMock() mock_conn.cursor.return_value = mock_cursor mock_cursor.fetchall.return_value = [(True, "12"), (False, "24")] frt_s = {datetime.datetime(2023, 10, 22, 6, 0)} - result = fetch_processed_items(mock_conn, frt_s) + result = fetch_processed_forecasts(mock_conn, frt_s) assert result == {"20231022060012"}