From eb2bb29b65b0a6ff0b4e44a494cc02a7cdb97d35 Mon Sep 17 00:00:00 2001 From: "Charles Graham, SWT" Date: Mon, 1 Dec 2025 23:25:45 -0600 Subject: [PATCH 01/13] Setup initial timeseries group structure from BLOB --- cwmscli/__main__.py | 1 + cwmscli/commands/commands_cwms.py | 192 +++++++++++++++ cwmscli/commands/timeseries/group.py | 352 +++++++++++++++++++++++++++ 3 files changed, 545 insertions(+) create mode 100644 cwmscli/commands/timeseries/group.py diff --git a/cwmscli/__main__.py b/cwmscli/__main__.py index ba12def..f43d9d8 100644 --- a/cwmscli/__main__.py +++ b/cwmscli/__main__.py @@ -13,3 +13,4 @@ def cli(): cli.add_command(commands_cwms.shefcritimport) cli.add_command(commands_cwms.csv2cwms_cmd) cli.add_command(commands_cwms.blob_group) +cli.add_command(commands_cwms.timeseries) diff --git a/cwmscli/commands/commands_cwms.py b/cwmscli/commands/commands_cwms.py index a7b63a6..42ed51a 100644 --- a/cwmscli/commands/commands_cwms.py +++ b/cwmscli/commands/commands_cwms.py @@ -250,3 +250,195 @@ def list_cmd(**kwargs): # endregion + + +# region Time Series +# ================================================================================ +# Time Series +# ================================================================================ +@click.group( + "timeseries", + help="Manage CWMS Time Series", + epilog=textwrap.dedent( + """ + Example Usage:\n + - Manage Time Series Groups\n + - More Coming Soon! +""" + ), +) +@requires(reqs.cwms) +def timeseries(): + pass + + +# region Time Series Group +# ================================================================================ +# Time Series Group +# ================================================================================ +@timeseries.group( + "group", + help="Manage CWMS Time Series Groups (upload, download, delete, update, list)", + epilog=textwrap.dedent( + """ + Example Usage:\n + - Store Time Series Groups from CLI\n + - Download a Time Series Group to your local filesystem\n + - Update a Time Series Group description, category, and identifiers\n + - Bulk list Time Series Groups for a given office +""" + ), +) +def timeseries_group(): + pass + + +# ================================================================================ +# Store +# ================================================================================ +@timeseries_group.command("store", help="Store a timeseries group") +@click.option( + "--overwrite/--no-overwrite", + default=False, + show_default=True, + help="If true, replace existing timeseries group.", +) +@click.option( + "--input-file", + required=True, + type=click.Path(exists=True, dir_okay=False, readable=True, path_type=str), + help="Specify a relative/absolute path to a JSON file containing the request body", +) +@common_api_options +def timeseries_group_upload(**kwargs): + from cwmscli.commands.timeseries.group import store_cmd + + store_cmd(**kwargs) + + +# ================================================================================ +# Download +# ================================================================================ +@timeseries_group.command("retrieve", help="Download timeseries group") +@click.option("--blob-id", required=True, type=str, help="Blob ID to download.") +@click.option( + "--dest", + default=None, + help="Destination file path. Defaults to blob-id.", +) +@click.option("--dry-run", is_flag=True, help="Show request; do not send.") +@common_api_options +def blob_download(**kwargs): + from cwmscli.commands.timeseries.group import download_cmd + + download_cmd(**kwargs) + + +# ================================================================================ +# Delete +# ================================================================================ +@timeseries_group.command("delete", help="Delete a timeseries group") +@click.option("--blob-id", required=True, type=str, help="Blob ID to delete.") +@click.option("--dry-run", is_flag=True, help="Show request; do not send.") +@common_api_options +def delete_cmd(**kwargs): + from cwmscli.commands.timeseries.group import delete_cmd + + delete_cmd(**kwargs) + + +# ================================================================================ +# Update +# ================================================================================ +@timeseries_group.command("update", help="Update/patch a timeseries group") +@click.option("--blob-id", required=True, type=str, help="Blob ID to update.") +@click.option("--dry-run", is_flag=True, help="Show request; do not send.") +@click.option( + "--description", + default=None, + help="New description JSON or text.", +) +@click.option( + "--media-type", + default=None, + help="New media type (guessed from file if omitted).", +) +@click.option( + "--input-file", + required=False, + type=click.Path(exists=True, dir_okay=False, readable=True, path_type=str), + help="Optional file content to upload with update.", +) +@click.option( + "--overwrite/--no-overwrite", + default=False, + show_default=True, + help="If true, replace existing blob.", +) +@common_api_options +def update_cmd(**kwargs): + from cwmscli.commands.timeseries.group import update_cmd + + update_cmd(**kwargs) + + +# timeseries_category_like=timeseries_category_like, +# category_office_id=category_office_id, +# timeseries_group_like=timeseries_group_like, + + +# ================================================================================ +# List +# ================================================================================ +@timeseries_group.command("list", help="List blobs with optional filters and sorting") +@click.option( + "--include-assigned", + default=False, + show_default=True, + help="Include the assigned timeseries in the returned timeseries groups. (default: true)", +) +@click.option( + "--timeseries-category-like", + help="Posix regular expression matching against the timeseries category id", +) +@click.option( + "--category-office-id", + help="Specifies the owning office of the timeseries group category", +) +@click.option( + "--timeseries-group-like", + help="Posix regular expression matching against the timeseries group id", +) +@click.option( + "--columns", + multiple=True, + callback=csv_to_list, + help="Columns to show (repeat or comma-separate).", +) +@click.option( + "--sort-by", + multiple=True, + callback=csv_to_list, + help="Columns to sort by (repeat or comma-separate).", +) +@click.option( + "--desc/--asc", + default=False, + show_default=True, + help="Sort descending instead of ascending.", +) +@click.option("--limit", type=int, default=None, help="Max rows to show.") +@click.option( + "--to-csv", + type=click.Path(dir_okay=False, writable=True, path_type=str), + help="If set, write results to this CSV file.", +) +@common_api_options +def list_cmd(**kwargs): + from cwmscli.commands.timeseries.group import list_cmd + + list_cmd(**kwargs) + + +# endregion +# endregion diff --git a/cwmscli/commands/timeseries/group.py b/cwmscli/commands/timeseries/group.py new file mode 100644 index 0000000..17fdc73 --- /dev/null +++ b/cwmscli/commands/timeseries/group.py @@ -0,0 +1,352 @@ +import json +import logging +import os +import sys +from typing import Optional, Sequence + +import cwms +import pandas as pd +import requests + +from cwmscli.utils import get_api_key + + +def store_group(**kwargs): + file_data = kwargs.get("file_data") + group_id = kwargs.get("group_id", "").upper() + # Attempt to determine what media type should be used for the mime-type if one is not presented based on the file extension + media = kwargs.get("media_type") or get_media_type(kwargs.get("input_file")) + + logging.debug( + f"Office: {kwargs.get('office')} Output ID: {group_id} Media: {media}" + ) + + group = { + "office-id": kwargs.get("office"), + "id": group_id, + "description": json.dumps(kwargs.get("description")), + "media-type-id": media, + "value": base64.b64encode(file_data).decode("utf-8"), + } + + params = {"fail-if-exists": not kwargs.get("overwrite")} + + if kwargs.get("dry_run"): + logging.info( + f"--dry-run enabled. Would POST to {kwargs.get('api_root')}/groups with params={params}" + ) + logging.info( + f"Group payload summary: office-id={kwargs.get('office')}, id={group_id}, media={media}", + ) + logging.info( + json.dumps( + { + "url": f"{kwargs.get('api_root')}groups", + "params": params, + "group": { + **group, + "value": f"", + }, + }, + indent=2, + ) + ) + sys.exit(0) + + try: + cwms.store_groups(group, fail_if_exists=kwargs.get("overwrite")) + logging.info(f"Successfully stored group with ID: {group_id}") + logging.info( + f"View: {kwargs.get('api_root')}groups/{group_id}?office={kwargs.get('office')}" + ) + except requests.HTTPError as e: + # Include response text when available + detail = getattr(e.response, "text", "") or str(e) + logging.error(f"Failed to store group (HTTP): {detail}") + sys.exit(1) + except Exception as e: + logging.error(f"Failed to store group: {e}") + sys.exit(1) + + +def retrieve_group(**kwargs): + group_id = kwargs.get("group_id", "").upper() + if not group_id: + logging.warning( + "Valid group_id required to download a group. cwms-cli group download --group-id=myid. Run the list directive to see options for your office." + ) + sys.exit(0) + logging.debug(f"Office: {kwargs.get('office')} Group ID: {group_id}") + try: + group = cwms.get_group( + office_id=kwargs.get("office"), + group_id=group_id, + ) + logging.info( + f"Successfully retrieved group with ID: {group_id}", + ) + _save_base64(group, dest=group_id) + logging.info(f"Downloaded group to: {group_id}") + except requests.HTTPError as e: + detail = getattr(e.response, "text", "") or str(e) + logging.error(f"Failed to retrieve group (HTTP): {detail}") + sys.exit(1) + except Exception as e: + logging.error(f"Failed to retrieve group: {e}") + sys.exit(1) + + +def delete_group(**kwargs): + group_id = kwargs.get("group_id").upper() + logging.debug(f"Office: {kwargs.get('office')} Group ID: {group_id}") + + try: + # cwms.delete_group( + # office_id=kwargs.get("office"), + # group_id=kwargs.get("group_id").upper(), + # ) + logging.info(f"Successfully deleted group with ID: {group_id}") + except requests.HTTPError as e: + details = getattr(e.response, "text", "") or str(e) + logging.error(f"Failed to delete group (HTTP): {details}") + sys.exit(1) + except Exception as e: + logging.error(f"Failed to delete group: {e}") + sys.exit(1) + + +def list_groups( + office: Optional[str] = None, + include_assigned: Optional[bool] = None, + timeseries_category_like: Optional[str] = None, + category_office_id: Optional[str] = None, + timeseries_group_like: Optional[str] = None, + columns: Optional[Sequence[str]] = None, + sort_by: Optional[Sequence[str]] = None, + ascending: bool = True, + limit: Optional[int] = None, +) -> pd.DataFrame: + logging.info(f"Listing groups for office: {office!r}...") + result = cwms.get_timeseries_groups( + office_id=office, + category_office_id=category_office_id, + timeseries_group_like=timeseries_group_like, + timeseries_category_like=timeseries_category_like, + include_assigned=include_assigned, + ) + + # Accept either a DataFrame or a JSON/dict-like response + if isinstance(result, pd.DataFrame): + df = result.copy() + else: + # Expecting normal group return structure + data = getattr(result, "json", None) + if callable(data): + data = result.json() + df = pd.DataFrame((data or {})) + + # Allow column filtering + if columns: + keep = [c for c in columns if c in df.columns] + if keep: + df = df[keep] + + # Sort by option + if sort_by: + by = [c for c in sort_by if c in df.columns] + if by: + df = df.sort_values(by=by, ascending=ascending, kind="stable") + + # Optional limit + if limit is not None: + df = df.head(limit) + + logging.info(f"Found {len(df):,} group(s)") + # List the groups in the logger + for _, row in df.iterrows(): + logging.info(f"Group ID: {row['id']}, Description: {row.get('description')}") + return df + + +def store_cmd( + input_file: str, + overwrite: bool, + dry_run: bool, + office: str, + api_root: str, + api_key: str, +): + cwms.init_session(api_root=api_root, api_key=get_api_key(api_key, "")) + try: + file_size = os.path.getsize(input_file) + with open(input_file, "rb") as f: + file_data = f.read() + logging.info(f"Read file: {input_file} ({file_size} bytes)") + except Exception as e: + logging.error(f"Failed to read file: {e}") + sys.exit(1) + + media = media_type or get_media_type(input_file) + group_id_up = group_id.upper() + logging.debug(f"Office={office} GroupID={group_id_up} Media={media}") + + group = { + "office-id": office, + "id": group_id_up, + "description": ( + json.dumps(description) + if isinstance(description, (dict, list)) + else description + ), + "media-type-id": media, + "value": base64.b64encode(file_data).decode("utf-8"), + } + params = {"fail-if-exists": not overwrite} + + if dry_run: + logging.info(f"DRY RUN: would POST {api_root}groups with params={params}") + logging.info( + json.dumps( + { + "url": f"{api_root}groups", + "params": params, + "group": { + **group, + "value": f'', + }, + }, + indent=2, + ) + ) + return + + try: + cwms.store_groups(group, fail_if_exists=not overwrite) + logging.info(f"Uploaded group: {group_id_up}") + logging.info(f"View: {api_root}groups/{group_id_up}?office={office}") + except requests.HTTPError as e: + detail = getattr(e.response, "text", "") or str(e) + logging.error(f"Failed to upload (HTTP): {detail}") + sys.exit(1) + except Exception as e: + logging.error(f"Failed to upload: {e}") + sys.exit(1) + + +def download_cmd( + group_id: str, dest: str, office: str, api_root: str, api_key: str, dry_run: bool +): + if dry_run: + logging.info( + f"DRY RUN: would GET {api_root} group with group-id={group_id} office={office}." + ) + return + cwms.init_session(api_root=api_root, api_key=get_api_key(api_key, "")) + bid = group_id.upper() + logging.debug(f"Office={office} GroupID={bid}") + + try: + group_b64 = cwms.get_group(office_id=office, group_id=bid) + target = dest or bid + _save_base64(group_b64, dest=target) + logging.info(f"Downloaded group to: {target}") + except requests.HTTPError as e: + detail = getattr(e.response, "text", "") or str(e) + logging.error(f"Failed to download (HTTP): {detail}") + sys.exit(1) + except Exception as e: + logging.error(f"Failed to download: {e}") + sys.exit(1) + + +def delete_cmd(group_id: str, office: str, api_root: str, api_key: str, dry_run: bool): + + if dry_run: + logging.info( + f"DRY RUN: would DELETE {api_root} group with group-id={group_id} office={office}" + ) + return + cwms.init_session(api_root=api_root, api_key=api_key) + cwms.delete_group(office_id=office, group_id=group_id) + logging.info(f"Deleted group: {group_id} for office: {office}") + + +def update_cmd( + input_file: str, + group_id: str, + description: str, + media_type: str, + overwrite: bool, + dry_run: bool, + office: str, + api_root: str, + api_key: str, +): + if dry_run: + logging.info( + f"DRY RUN: would PATCH {api_root} group with group-id={group_id} office={office}" + ) + return + file_data = None + if input_file: + try: + file_size = os.path.getsize(input_file) + with open(input_file, "rb") as f: + file_data = f.read() + logging.info(f"Read file: {input_file} ({file_size} bytes)") + except Exception as e: + logging.error(f"Failed to read file: {e}") + sys.exit(1) + # Setup minimum required payload + group = {"office-id": office, "id": group_id.upper()} + if description: + group["description"] = description + if media_type: + group["media-type-id"] = media_type + else: + logging.info("Media type not specified; Retrieving existing media type...") + group_metadata = cwms.get_groups(office_id=office, group_id_like=group_id) + group["media-type-id"] = group_metadata.df.get( + "media-type-id", "application/octet-stream" + )[0] + logging.info(f"Using existing media type: {group['media-type-id']}") + + if file_data: + group["value"] = base64.b64encode(file_data).decode("utf-8") + cwms.init_session(api_root=api_root, api_key=api_key) + cwms.update_group(group, fail_if_not_exists=not overwrite) + + +def list_cmd( + include_assigned: bool, + timeseries_category_like: str, + category_office_id: str, + timeseries_group_like: str, + columns: list[str], + sort_by: list[str], + desc: bool, + limit: int, + to_csv: str, + office: str, + api_root: str, + api_key: str, +): + cwms.init_session(api_root=api_root, api_key=get_api_key(api_key, None)) + df = list_groups( + office=office, + include_assigned=include_assigned, + timeseries_category_like=timeseries_category_like, + category_office_id=category_office_id, + timeseries_group_like=timeseries_group_like, + columns=columns, + sort_by=sort_by, + ascending=not desc, + limit=limit, + ) + if to_csv: + df.to_csv(to_csv, index=False) + logging.info(f"Wrote {len(df)} rows to {to_csv}") + else: + # Friendly console preview + with pd.option_context("display.max_rows", 500, "display.max_columns", None): + logging.info(df.to_string(index=False)) From 388545151ea5a390e6c41228442138fec30da9d4 Mon Sep 17 00:00:00 2001 From: "Charles Graham, SWT" Date: Tue, 2 Dec 2025 22:41:44 +0000 Subject: [PATCH 02/13] Add click options for retrieve --- cwmscli/commands/commands_cwms.py | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/cwmscli/commands/commands_cwms.py b/cwmscli/commands/commands_cwms.py index 42ed51a..07217c2 100644 --- a/cwmscli/commands/commands_cwms.py +++ b/cwmscli/commands/commands_cwms.py @@ -320,11 +320,31 @@ def timeseries_group_upload(**kwargs): # Download # ================================================================================ @timeseries_group.command("retrieve", help="Download timeseries group") -@click.option("--blob-id", required=True, type=str, help="Blob ID to download.") +@click.option( + "--include-assigned", + default=True, + show_default=True, + type=bool, + help="Include the assigned timeseries in the returned timeseries groups. (default: true)", +) +@click.option( + "--timeseries-category-like", + type=str, + help="Posix regular expression matching against the timeseries category id", +) +@click.option( + "--category-office-id", + type=str, + help="Specifies the owning office of the timeseries group category", +) +@click.option( + "--timeseries-group-like", + type=str, + help="Posix regular expression matching against the timeseries group id", +) @click.option( "--dest", - default=None, - help="Destination file path. Defaults to blob-id.", + help="Destination file path. Defaults to stdout.", ) @click.option("--dry-run", is_flag=True, help="Show request; do not send.") @common_api_options From d091df2c1f05c20af532d9b18e5d7e39b003d251 Mon Sep 17 00:00:00 2001 From: "Charles Graham, SWT" Date: Thu, 4 Dec 2025 20:28:13 -0600 Subject: [PATCH 03/13] Regenerate poetry.lock to fix isort checksum --- poetry.lock | 405 +++++++++++++++++++++++++++++----------------------- 1 file changed, 230 insertions(+), 175 deletions(-) diff --git a/poetry.lock b/poetry.lock index 36af8b8..1cf62e9 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 2.0.1 and should not be changed by hand. [[package]] name = "black" @@ -6,6 +6,8 @@ version = "24.10.0" description = "The uncompromising code formatter." optional = false python-versions = ">=3.9" +groups = ["dev"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" 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"}, @@ -48,13 +50,15 @@ uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "certifi" -version = "2025.10.5" +version = "2025.11.12" description = "Python package for providing Mozilla's CA Bundle." optional = true python-versions = ">=3.7" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ - {file = "certifi-2025.10.5-py3-none-any.whl", hash = "sha256:0f212c2744a9bb6de0c56639a6f68afe01ecd92d91f14ae897c4fe7bbeeef0de"}, - {file = "certifi-2025.10.5.tar.gz", hash = "sha256:47c09d31ccf2acf0be3f701ea53595ee7e0b8fa08801c6624be771df09ae7b43"}, + {file = "certifi-2025.11.12-py3-none-any.whl", hash = "sha256:97de8790030bbd5c2d96b7ec782fc2f7820ef8dba6db909ccf95449f2d062d4b"}, + {file = "certifi-2025.11.12.tar.gz", hash = "sha256:d8ab5478f2ecd78af242878415affce761ca6bc54a22a27e026d7c25357c3316"}, ] [[package]] @@ -63,6 +67,8 @@ version = "3.4.0" description = "Validate configuration and produce human readable error messages." optional = false python-versions = ">=3.8" +groups = ["dev"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, @@ -74,6 +80,8 @@ version = "3.4.4" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = true python-versions = ">=3.7" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "charset_normalizer-3.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d"}, {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8"}, @@ -196,6 +204,8 @@ version = "8.1.8" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" +groups = ["main", "dev"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, @@ -210,6 +220,8 @@ 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" +groups = ["main", "dev"] +markers = "python_version <= \"3.11\" and platform_system == \"Windows\" or python_version >= \"3.12\" and platform_system == \"Windows\"" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, @@ -217,13 +229,15 @@ files = [ [[package]] name = "cwms-python" -version = "0.8.0" +version = "1.0.0" description = "Corps water management systems (CWMS) REST API for Data Retrieval of USACE water data" optional = true python-versions = "<4.0,>=3.9" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ - {file = "cwms_python-0.8.0-py3-none-any.whl", hash = "sha256:2ab7f6b6ca54a8f3e8c8a1421eefed008f96cf2b81772bbce82422bf154c173f"}, - {file = "cwms_python-0.8.0.tar.gz", hash = "sha256:71da687f35680ddb88bdba0464eb3585d05289f507872fdcd31e85483f26654b"}, + {file = "cwms_python-1.0.0-py3-none-any.whl", hash = "sha256:c552505f00d57760b806f521a1cb5ec435e3a2ee0ee84b5a083d3e5fdfcd4b8c"}, + {file = "cwms_python-1.0.0.tar.gz", hash = "sha256:03a92ddf6e4558463d628eadaf2d2ca3a99f65e2ea1a68e76067a8bf1bd16f1b"}, ] [package.dependencies] @@ -237,6 +251,8 @@ version = "0.4.0" description = "Distribution utilities" optional = false python-versions = "*" +groups = ["dev"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16"}, {file = "distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d"}, @@ -248,6 +264,8 @@ version = "1.9.0" description = "Distro - an OS platform information API" optional = false python-versions = ">=3.6" +groups = ["dev"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2"}, {file = "distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed"}, @@ -259,6 +277,8 @@ version = "3.19.1" description = "A platform independent file lock." optional = false python-versions = ">=3.9" +groups = ["dev"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "filelock-3.19.1-py3-none-any.whl", hash = "sha256:d38e30481def20772f5baf097c122c3babc4fcdb7e14e57049eb9d88c6dc017d"}, {file = "filelock-3.19.1.tar.gz", hash = "sha256:66eda1888b0171c998b35be2bcc0f6d75c388a7ce20c3f3f37aa8e96c2dddf58"}, @@ -266,13 +286,15 @@ files = [ [[package]] name = "hecdss" -version = "0.1.28" +version = "0.1.29" description = "Python wrapper for the HEC-DSS file database C library." optional = true python-versions = ">=3.8" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ - {file = "hecdss-0.1.28-py3-none-any.whl", hash = "sha256:78a752ea633b1e107384ee73bf99619b645451440bbfadfb19599f799d1f265f"}, - {file = "hecdss-0.1.28.tar.gz", hash = "sha256:1ae8797358136123a5f4939027a052019d18f713d19c5cc91adfe518cbdca31f"}, + {file = "hecdss-0.1.29-py3-none-any.whl", hash = "sha256:6884e5a47c98a761c3429ec4aa3ba382e8a6eb55f1d5ac40416ecb0c10757848"}, + {file = "hecdss-0.1.29.tar.gz", hash = "sha256:39289029d0ec6791e6423f0b91617b473c452fe340448452d8134011ad8f8624"}, ] [package.dependencies] @@ -285,6 +307,8 @@ version = "2.6.15" description = "File identification library for Python" optional = false python-versions = ">=3.9" +groups = ["dev"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "identify-2.6.15-py2.py3-none-any.whl", hash = "sha256:1181ef7608e00704db228516541eb83a88a9f94433a8c80bb9b5bd54b1d81757"}, {file = "identify-2.6.15.tar.gz", hash = "sha256:e4f4864b96c6557ef2a1e1c951771838f4edc9df3a72ec7118b338801b11c7bf"}, @@ -299,6 +323,8 @@ version = "3.11" description = "Internationalized Domain Names in Applications (IDNA)" optional = true python-versions = ">=3.8" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea"}, {file = "idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902"}, @@ -313,22 +339,102 @@ version = "5.13.2" description = "A Python utility / library to sort Python imports." optional = false python-versions = ">=3.8.0" +groups = ["dev"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ - {file = "importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd"}, - {file = "importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000"}, + {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, + {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, ] -[package.dependencies] -zipp = ">=3.20" - [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] -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) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] -type = ["pytest-mypy"] +colors = ["colorama (>=0.4.6)"] + +[[package]] +name = "librt" +version = "0.6.3" +description = "Mypyc runtime library" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" +files = [ + {file = "librt-0.6.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:45660d26569cc22ed30adf583389d8a0d1b468f8b5e518fcf9bfe2cd298f9dd1"}, + {file = "librt-0.6.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:54f3b2177fb892d47f8016f1087d21654b44f7fc4cf6571c1c6b3ea531ab0fcf"}, + {file = "librt-0.6.3-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:c5b31bed2c2f2fa1fcb4815b75f931121ae210dc89a3d607fb1725f5907f1437"}, + {file = "librt-0.6.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8f8ed5053ef9fb08d34f1fd80ff093ccbd1f67f147633a84cf4a7d9b09c0f089"}, + {file = "librt-0.6.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3f0e4bd9bcb0ee34fa3dbedb05570da50b285f49e52c07a241da967840432513"}, + {file = "librt-0.6.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d8f89c8d20dfa648a3f0a56861946eb00e5b00d6b00eea14bc5532b2fcfa8ef1"}, + {file = "librt-0.6.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ecc2c526547eacd20cb9fbba19a5268611dbc70c346499656d6cf30fae328977"}, + {file = "librt-0.6.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:fbedeb9b48614d662822ee514567d2d49a8012037fc7b4cd63f282642c2f4b7d"}, + {file = "librt-0.6.3-cp310-cp310-win32.whl", hash = "sha256:0765b0fe0927d189ee14b087cd595ae636bef04992e03fe6dfdaa383866c8a46"}, + {file = "librt-0.6.3-cp310-cp310-win_amd64.whl", hash = "sha256:8c659f9fb8a2f16dc4131b803fa0144c1dadcb3ab24bb7914d01a6da58ae2457"}, + {file = "librt-0.6.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:61348cc488b18d1b1ff9f3e5fcd5ac43ed22d3e13e862489d2267c2337285c08"}, + {file = "librt-0.6.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:64645b757d617ad5f98c08e07620bc488d4bced9ced91c6279cec418f16056fa"}, + {file = "librt-0.6.3-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:26b8026393920320bb9a811b691d73c5981385d537ffc5b6e22e53f7b65d4122"}, + {file = "librt-0.6.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d998b432ed9ffccc49b820e913c8f327a82026349e9c34fa3690116f6b70770f"}, + {file = "librt-0.6.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e18875e17ef69ba7dfa9623f2f95f3eda6f70b536079ee6d5763ecdfe6cc9040"}, + {file = "librt-0.6.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a218f85081fc3f70cddaed694323a1ad7db5ca028c379c214e3a7c11c0850523"}, + {file = "librt-0.6.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1ef42ff4edd369e84433ce9b188a64df0837f4f69e3d34d3b34d4955c599d03f"}, + {file = "librt-0.6.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0e0f2b79993fec23a685b3e8107ba5f8675eeae286675a216da0b09574fa1e47"}, + {file = "librt-0.6.3-cp311-cp311-win32.whl", hash = "sha256:fd98cacf4e0fabcd4005c452cb8a31750258a85cab9a59fb3559e8078da408d7"}, + {file = "librt-0.6.3-cp311-cp311-win_amd64.whl", hash = "sha256:e17b5b42c8045867ca9d1f54af00cc2275198d38de18545edaa7833d7e9e4ac8"}, + {file = "librt-0.6.3-cp311-cp311-win_arm64.whl", hash = "sha256:87597e3d57ec0120a3e1d857a708f80c02c42ea6b00227c728efbc860f067c45"}, + {file = "librt-0.6.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:74418f718083009108dc9a42c21bf2e4802d49638a1249e13677585fcc9ca176"}, + {file = "librt-0.6.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:514f3f363d1ebc423357d36222c37e5c8e6674b6eae8d7195ac9a64903722057"}, + {file = "librt-0.6.3-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cf1115207a5049d1f4b7b4b72de0e52f228d6c696803d94843907111cbf80610"}, + {file = "librt-0.6.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ad8ba80cdcea04bea7b78fcd4925bfbf408961e9d8397d2ee5d3ec121e20c08c"}, + {file = "librt-0.6.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4018904c83eab49c814e2494b4e22501a93cdb6c9f9425533fe693c3117126f9"}, + {file = "librt-0.6.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8983c5c06ac9c990eac5eb97a9f03fe41dc7e9d7993df74d9e8682a1056f596c"}, + {file = "librt-0.6.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d7769c579663a6f8dbf34878969ac71befa42067ce6bf78e6370bf0d1194997c"}, + {file = "librt-0.6.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d3c9a07eafdc70556f8c220da4a538e715668c0c63cabcc436a026e4e89950bf"}, + {file = "librt-0.6.3-cp312-cp312-win32.whl", hash = "sha256:38320386a48a15033da295df276aea93a92dfa94a862e06893f75ea1d8bbe89d"}, + {file = "librt-0.6.3-cp312-cp312-win_amd64.whl", hash = "sha256:c0ecf4786ad0404b072196b5df774b1bb23c8aacdcacb6c10b4128bc7b00bd01"}, + {file = "librt-0.6.3-cp312-cp312-win_arm64.whl", hash = "sha256:9f2a6623057989ebc469cd9cc8fe436c40117a0147627568d03f84aef7854c55"}, + {file = "librt-0.6.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9e716f9012148a81f02f46a04fc4c663420c6fbfeacfac0b5e128cf43b4413d3"}, + {file = "librt-0.6.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:669ff2495728009a96339c5ad2612569c6d8be4474e68f3f3ac85d7c3261f5f5"}, + {file = "librt-0.6.3-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:349b6873ebccfc24c9efd244e49da9f8a5c10f60f07575e248921aae2123fc42"}, + {file = "librt-0.6.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c74c26736008481c9f6d0adf1aedb5a52aff7361fea98276d1f965c0256ee70"}, + {file = "librt-0.6.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:408a36ddc75e91918cb15b03460bdc8a015885025d67e68c6f78f08c3a88f522"}, + {file = "librt-0.6.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e61ab234624c9ffca0248a707feffe6fac2343758a36725d8eb8a6efef0f8c30"}, + {file = "librt-0.6.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:324462fe7e3896d592b967196512491ec60ca6e49c446fe59f40743d08c97917"}, + {file = "librt-0.6.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:36b2ec8c15030002c7f688b4863e7be42820d7c62d9c6eece3db54a2400f0530"}, + {file = "librt-0.6.3-cp313-cp313-win32.whl", hash = "sha256:25b1b60cb059471c0c0c803e07d0dfdc79e41a0a122f288b819219ed162672a3"}, + {file = "librt-0.6.3-cp313-cp313-win_amd64.whl", hash = "sha256:10a95ad074e2a98c9e4abc7f5b7d40e5ecbfa84c04c6ab8a70fabf59bd429b88"}, + {file = "librt-0.6.3-cp313-cp313-win_arm64.whl", hash = "sha256:17000df14f552e86877d67e4ab7966912224efc9368e998c96a6974a8d609bf9"}, + {file = "librt-0.6.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8e695f25d1a425ad7a272902af8ab8c8d66c1998b177e4b5f5e7b4e215d0c88a"}, + {file = "librt-0.6.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3e84a4121a7ae360ca4da436548a9c1ca8ca134a5ced76c893cc5944426164bd"}, + {file = "librt-0.6.3-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:05f385a414de3f950886ea0aad8f109650d4b712cf9cc14cc17f5f62a9ab240b"}, + {file = "librt-0.6.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:36a8e337461150b05ca2c7bdedb9e591dfc262c5230422cea398e89d0c746cdc"}, + {file = "librt-0.6.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dcbe48f6a03979384f27086484dc2a14959be1613cb173458bd58f714f2c48f3"}, + {file = "librt-0.6.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4bca9e4c260233fba37b15c4ec2f78aa99c1a79fbf902d19dd4a763c5c3fb751"}, + {file = "librt-0.6.3-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:760c25ed6ac968e24803eb5f7deb17ce026902d39865e83036bacbf5cf242aa8"}, + {file = "librt-0.6.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4aa4a93a353ccff20df6e34fa855ae8fd788832c88f40a9070e3ddd3356a9f0e"}, + {file = "librt-0.6.3-cp314-cp314-win32.whl", hash = "sha256:cb92741c2b4ea63c09609b064b26f7f5d9032b61ae222558c55832ec3ad0bcaf"}, + {file = "librt-0.6.3-cp314-cp314-win_amd64.whl", hash = "sha256:fdcd095b1b812d756fa5452aca93b962cf620694c0cadb192cec2bb77dcca9a2"}, + {file = "librt-0.6.3-cp314-cp314-win_arm64.whl", hash = "sha256:822ca79e28720a76a935c228d37da6579edef048a17cd98d406a2484d10eda78"}, + {file = "librt-0.6.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:078cd77064d1640cb7b0650871a772956066174d92c8aeda188a489b58495179"}, + {file = "librt-0.6.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5cc22f7f5c0cc50ed69f4b15b9c51d602aabc4500b433aaa2ddd29e578f452f7"}, + {file = "librt-0.6.3-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:14b345eb7afb61b9fdcdfda6738946bd11b8e0f6be258666b0646af3b9bb5916"}, + {file = "librt-0.6.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d46aa46aa29b067f0b8b84f448fd9719aaf5f4c621cc279164d76a9dc9ab3e8"}, + {file = "librt-0.6.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1b51ba7d9d5d9001494769eca8c0988adce25d0a970c3ba3f2eb9df9d08036fc"}, + {file = "librt-0.6.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ced0925a18fddcff289ef54386b2fc230c5af3c83b11558571124bfc485b8c07"}, + {file = "librt-0.6.3-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:6bac97e51f66da2ca012adddbe9fd656b17f7368d439de30898f24b39512f40f"}, + {file = "librt-0.6.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b2922a0e8fa97395553c304edc3bd36168d8eeec26b92478e292e5d4445c1ef0"}, + {file = "librt-0.6.3-cp314-cp314t-win32.whl", hash = "sha256:f33462b19503ba68d80dac8a1354402675849259fb3ebf53b67de86421735a3a"}, + {file = "librt-0.6.3-cp314-cp314t-win_amd64.whl", hash = "sha256:04f8ce401d4f6380cfc42af0f4e67342bf34c820dae01343f58f472dbac75dcf"}, + {file = "librt-0.6.3-cp314-cp314t-win_arm64.whl", hash = "sha256:afb39550205cc5e5c935762c6bf6a2bb34f7d21a68eadb25e2db7bf3593fecc0"}, + {file = "librt-0.6.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:09262cb2445b6f15d09141af20b95bb7030c6f13b00e876ad8fdd1a9045d6aa5"}, + {file = "librt-0.6.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:57705e8eec76c5b77130d729c0f70190a9773366c555c5457c51eace80afd873"}, + {file = "librt-0.6.3-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3ac2a7835434b31def8ed5355dd9b895bbf41642d61967522646d1d8b9681106"}, + {file = "librt-0.6.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:71f0a5918aebbea1e7db2179a8fe87e8a8732340d9e8b8107401fb407eda446e"}, + {file = "librt-0.6.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aa346e202e6e1ebc01fe1c69509cffe486425884b96cb9ce155c99da1ecbe0e9"}, + {file = "librt-0.6.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:92267f865c7bbd12327a0d394666948b9bf4b51308b52947c0cc453bfa812f5d"}, + {file = "librt-0.6.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:86605d5bac340beb030cbc35859325982a79047ebdfba1e553719c7126a2389d"}, + {file = "librt-0.6.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:98e4bbecbef8d2a60ecf731d735602feee5ac0b32117dbbc765e28b054bac912"}, + {file = "librt-0.6.3-cp39-cp39-win32.whl", hash = "sha256:3caa0634c02d5ff0b2ae4a28052e0d8c5f20d497623dc13f629bd4a9e2a6efad"}, + {file = "librt-0.6.3-cp39-cp39-win_amd64.whl", hash = "sha256:b47395091e7e0ece1e6ebac9b98bf0c9084d1e3d3b2739aa566be7e56e3f7bf2"}, + {file = "librt-0.6.3.tar.gz", hash = "sha256:c724a884e642aa2bbad52bb0203ea40406ad742368a5f90da1b220e970384aae"}, +] [[package]] name = "maison" @@ -336,6 +442,8 @@ version = "1.4.2" description = "Read settings from config files" optional = false python-versions = ">=3.7.1,<4.0.0" +groups = ["dev"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "maison-1.4.2-py3-none-any.whl", hash = "sha256:b63fe6751494935fc453dfb76319af223e4cb8bab32ac5464c2a9ca0edda8765"}, {file = "maison-1.4.2.tar.gz", hash = "sha256:d2abac30a5c6a0749526d70ae95a63c6acf43461a1c10e51410b36734e053ec7"}, @@ -348,52 +456,55 @@ toml = ">=0.10.2,<0.11.0" [[package]] name = "mypy" -version = "1.18.2" +version = "1.19.0" description = "Optional static typing for Python" optional = false python-versions = ">=3.9" +groups = ["dev"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ - {file = "mypy-1.18.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c1eab0cf6294dafe397c261a75f96dc2c31bffe3b944faa24db5def4e2b0f77c"}, - {file = "mypy-1.18.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7a780ca61fc239e4865968ebc5240bb3bf610ef59ac398de9a7421b54e4a207e"}, - {file = "mypy-1.18.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:448acd386266989ef11662ce3c8011fd2a7b632e0ec7d61a98edd8e27472225b"}, - {file = "mypy-1.18.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f9e171c465ad3901dc652643ee4bffa8e9fef4d7d0eece23b428908c77a76a66"}, - {file = "mypy-1.18.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:592ec214750bc00741af1f80cbf96b5013d81486b7bb24cb052382c19e40b428"}, - {file = "mypy-1.18.2-cp310-cp310-win_amd64.whl", hash = "sha256:7fb95f97199ea11769ebe3638c29b550b5221e997c63b14ef93d2e971606ebed"}, - {file = "mypy-1.18.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:807d9315ab9d464125aa9fcf6d84fde6e1dc67da0b6f80e7405506b8ac72bc7f"}, - {file = "mypy-1.18.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:776bb00de1778caf4db739c6e83919c1d85a448f71979b6a0edd774ea8399341"}, - {file = "mypy-1.18.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1379451880512ffce14505493bd9fe469e0697543717298242574882cf8cdb8d"}, - {file = "mypy-1.18.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1331eb7fd110d60c24999893320967594ff84c38ac6d19e0a76c5fd809a84c86"}, - {file = "mypy-1.18.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3ca30b50a51e7ba93b00422e486cbb124f1c56a535e20eff7b2d6ab72b3b2e37"}, - {file = "mypy-1.18.2-cp311-cp311-win_amd64.whl", hash = "sha256:664dc726e67fa54e14536f6e1224bcfce1d9e5ac02426d2326e2bb4e081d1ce8"}, - {file = "mypy-1.18.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:33eca32dd124b29400c31d7cf784e795b050ace0e1f91b8dc035672725617e34"}, - {file = "mypy-1.18.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a3c47adf30d65e89b2dcd2fa32f3aeb5e94ca970d2c15fcb25e297871c8e4764"}, - {file = "mypy-1.18.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d6c838e831a062f5f29d11c9057c6009f60cb294fea33a98422688181fe2893"}, - {file = "mypy-1.18.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01199871b6110a2ce984bde85acd481232d17413868c9807e95c1b0739a58914"}, - {file = "mypy-1.18.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a2afc0fa0b0e91b4599ddfe0f91e2c26c2b5a5ab263737e998d6817874c5f7c8"}, - {file = "mypy-1.18.2-cp312-cp312-win_amd64.whl", hash = "sha256:d8068d0afe682c7c4897c0f7ce84ea77f6de953262b12d07038f4d296d547074"}, - {file = "mypy-1.18.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:07b8b0f580ca6d289e69209ec9d3911b4a26e5abfde32228a288eb79df129fcc"}, - {file = "mypy-1.18.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ed4482847168439651d3feee5833ccedbf6657e964572706a2adb1f7fa4dfe2e"}, - {file = "mypy-1.18.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c3ad2afadd1e9fea5cf99a45a822346971ede8685cc581ed9cd4d42eaf940986"}, - {file = "mypy-1.18.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a431a6f1ef14cf8c144c6b14793a23ec4eae3db28277c358136e79d7d062f62d"}, - {file = "mypy-1.18.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7ab28cc197f1dd77a67e1c6f35cd1f8e8b73ed2217e4fc005f9e6a504e46e7ba"}, - {file = "mypy-1.18.2-cp313-cp313-win_amd64.whl", hash = "sha256:0e2785a84b34a72ba55fb5daf079a1003a34c05b22238da94fcae2bbe46f3544"}, - {file = "mypy-1.18.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:62f0e1e988ad41c2a110edde6c398383a889d95b36b3e60bcf155f5164c4fdce"}, - {file = "mypy-1.18.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8795a039bab805ff0c1dfdb8cd3344642c2b99b8e439d057aba30850b8d3423d"}, - {file = "mypy-1.18.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6ca1e64b24a700ab5ce10133f7ccd956a04715463d30498e64ea8715236f9c9c"}, - {file = "mypy-1.18.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d924eef3795cc89fecf6bedc6ed32b33ac13e8321344f6ddbf8ee89f706c05cb"}, - {file = "mypy-1.18.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:20c02215a080e3a2be3aa50506c67242df1c151eaba0dcbc1e4e557922a26075"}, - {file = "mypy-1.18.2-cp314-cp314-win_amd64.whl", hash = "sha256:749b5f83198f1ca64345603118a6f01a4e99ad4bf9d103ddc5a3200cc4614adf"}, - {file = "mypy-1.18.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:25a9c8fb67b00599f839cf472713f54249a62efd53a54b565eb61956a7e3296b"}, - {file = "mypy-1.18.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c2b9c7e284ee20e7598d6f42e13ca40b4928e6957ed6813d1ab6348aa3f47133"}, - {file = "mypy-1.18.2-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d6985ed057513e344e43a26cc1cd815c7a94602fb6a3130a34798625bc2f07b6"}, - {file = "mypy-1.18.2-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22f27105f1525ec024b5c630c0b9f36d5c1cc4d447d61fe51ff4bd60633f47ac"}, - {file = "mypy-1.18.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:030c52d0ea8144e721e49b1f68391e39553d7451f0c3f8a7565b59e19fcb608b"}, - {file = "mypy-1.18.2-cp39-cp39-win_amd64.whl", hash = "sha256:aa5e07ac1a60a253445797e42b8b2963c9675563a94f11291ab40718b016a7a0"}, - {file = "mypy-1.18.2-py3-none-any.whl", hash = "sha256:22a1748707dd62b58d2ae53562ffc4d7f8bcc727e8ac7cbc69c053ddc874d47e"}, - {file = "mypy-1.18.2.tar.gz", hash = "sha256:06a398102a5f203d7477b2923dda3634c36727fa5c237d8f859ef90c42a9924b"}, + {file = "mypy-1.19.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6148ede033982a8c5ca1143de34c71836a09f105068aaa8b7d5edab2b053e6c8"}, + {file = "mypy-1.19.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a9ac09e52bb0f7fb912f5d2a783345c72441a08ef56ce3e17c1752af36340a39"}, + {file = "mypy-1.19.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:11f7254c15ab3f8ed68f8e8f5cbe88757848df793e31c36aaa4d4f9783fd08ab"}, + {file = "mypy-1.19.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:318ba74f75899b0e78b847d8c50821e4c9637c79d9a59680fc1259f29338cb3e"}, + {file = "mypy-1.19.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cf7d84f497f78b682edd407f14a7b6e1a2212b433eedb054e2081380b7395aa3"}, + {file = "mypy-1.19.0-cp310-cp310-win_amd64.whl", hash = "sha256:c3385246593ac2b97f155a0e9639be906e73534630f663747c71908dfbf26134"}, + {file = "mypy-1.19.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a31e4c28e8ddb042c84c5e977e28a21195d086aaffaf08b016b78e19c9ef8106"}, + {file = "mypy-1.19.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:34ec1ac66d31644f194b7c163d7f8b8434f1b49719d403a5d26c87fff7e913f7"}, + {file = "mypy-1.19.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cb64b0ba5980466a0f3f9990d1c582bcab8db12e29815ecb57f1408d99b4bff7"}, + {file = "mypy-1.19.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:120cffe120cca5c23c03c77f84abc0c14c5d2e03736f6c312480020082f1994b"}, + {file = "mypy-1.19.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7a500ab5c444268a70565e374fc803972bfd1f09545b13418a5174e29883dab7"}, + {file = "mypy-1.19.0-cp311-cp311-win_amd64.whl", hash = "sha256:c14a98bc63fd867530e8ec82f217dae29d0550c86e70debc9667fff1ec83284e"}, + {file = "mypy-1.19.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0fb3115cb8fa7c5f887c8a8d81ccdcb94cff334684980d847e5a62e926910e1d"}, + {file = "mypy-1.19.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f3e19e3b897562276bb331074d64c076dbdd3e79213f36eed4e592272dabd760"}, + {file = "mypy-1.19.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b9d491295825182fba01b6ffe2c6fe4e5a49dbf4e2bb4d1217b6ced3b4797bc6"}, + {file = "mypy-1.19.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6016c52ab209919b46169651b362068f632efcd5eb8ef9d1735f6f86da7853b2"}, + {file = "mypy-1.19.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f188dcf16483b3e59f9278c4ed939ec0254aa8a60e8fc100648d9ab5ee95a431"}, + {file = "mypy-1.19.0-cp312-cp312-win_amd64.whl", hash = "sha256:0e3c3d1e1d62e678c339e7ade72746a9e0325de42cd2cccc51616c7b2ed1a018"}, + {file = "mypy-1.19.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7686ed65dbabd24d20066f3115018d2dce030d8fa9db01aa9f0a59b6813e9f9e"}, + {file = "mypy-1.19.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fd4a985b2e32f23bead72e2fb4bbe5d6aceee176be471243bd831d5b2644672d"}, + {file = "mypy-1.19.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fc51a5b864f73a3a182584b1ac75c404396a17eced54341629d8bdcb644a5bba"}, + {file = "mypy-1.19.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:37af5166f9475872034b56c5efdcf65ee25394e9e1d172907b84577120714364"}, + {file = "mypy-1.19.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:510c014b722308c9bd377993bcbf9a07d7e0692e5fa8fc70e639c1eb19fc6bee"}, + {file = "mypy-1.19.0-cp313-cp313-win_amd64.whl", hash = "sha256:cabbee74f29aa9cd3b444ec2f1e4fa5a9d0d746ce7567a6a609e224429781f53"}, + {file = "mypy-1.19.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:f2e36bed3c6d9b5f35d28b63ca4b727cb0228e480826ffc8953d1892ddc8999d"}, + {file = "mypy-1.19.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a18d8abdda14035c5718acb748faec09571432811af129bf0d9e7b2d6699bf18"}, + {file = "mypy-1.19.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f75e60aca3723a23511948539b0d7ed514dda194bc3755eae0bfc7a6b4887aa7"}, + {file = "mypy-1.19.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8f44f2ae3c58421ee05fe609160343c25f70e3967f6e32792b5a78006a9d850f"}, + {file = "mypy-1.19.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:63ea6a00e4bd6822adbfc75b02ab3653a17c02c4347f5bb0cf1d5b9df3a05835"}, + {file = "mypy-1.19.0-cp314-cp314-win_amd64.whl", hash = "sha256:3ad925b14a0bb99821ff6f734553294aa6a3440a8cb082fe1f5b84dfb662afb1"}, + {file = "mypy-1.19.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0dde5cb375cb94deff0d4b548b993bec52859d1651e073d63a1386d392a95495"}, + {file = "mypy-1.19.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1cf9c59398db1c68a134b0b5354a09a1e124523f00bacd68e553b8bd16ff3299"}, + {file = "mypy-1.19.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3210d87b30e6af9c8faed61be2642fcbe60ef77cec64fa1ef810a630a4cf671c"}, + {file = "mypy-1.19.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e2c1101ab41d01303103ab6ef82cbbfedb81c1a060c868fa7cc013d573d37ab5"}, + {file = "mypy-1.19.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0ea4fd21bb48f0da49e6d3b37ef6bd7e8228b9fe41bbf4d80d9364d11adbd43c"}, + {file = "mypy-1.19.0-cp39-cp39-win_amd64.whl", hash = "sha256:16f76ff3f3fd8137aadf593cb4607d82634fca675e8211ad75c43d86033ee6c6"}, + {file = "mypy-1.19.0-py3-none-any.whl", hash = "sha256:0c01c99d626380752e527d5ce8e69ffbba2046eb8a060db0329690849cf9b6f9"}, + {file = "mypy-1.19.0.tar.gz", hash = "sha256:f6b874ca77f733222641e5c46e4711648c4037ea13646fd0cdc814c2eaec2528"}, ] [package.dependencies] +librt = ">=0.6.2" mypy_extensions = ">=1.0.0" pathspec = ">=0.9.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} @@ -412,6 +523,8 @@ version = "1.1.0" description = "Type system extensions for programs checked with the mypy type checker." optional = false python-versions = ">=3.8" +groups = ["dev"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, @@ -423,6 +536,8 @@ 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" +groups = ["dev"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, @@ -434,6 +549,8 @@ version = "2.0.2" description = "Fundamental package for array computing in Python" optional = true python-versions = ">=3.9" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "numpy-2.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:51129a29dbe56f9ca83438b706e2e69a39892b5eda6cedcb6b0c9fdc9b0d3ece"}, {file = "numpy-2.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f15975dfec0cf2239224d80e32c3170b1d168335eaedee69da84fbe9f1f9cd04"}, @@ -482,95 +599,14 @@ files = [ {file = "numpy-2.0.2.tar.gz", hash = "sha256:883c987dee1880e2a864ab0dc9892292582510604156762362d9326444636e78"}, ] -[[package]] -name = "numpy" -version = "2.3.4" -description = "Fundamental package for array computing in Python" -optional = true -python-versions = ">=3.11" -files = [ - {file = "numpy-2.3.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e78aecd2800b32e8347ce49316d3eaf04aed849cd5b38e0af39f829a4e59f5eb"}, - {file = "numpy-2.3.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7fd09cc5d65bda1e79432859c40978010622112e9194e581e3415a3eccc7f43f"}, - {file = "numpy-2.3.4-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:1b219560ae2c1de48ead517d085bc2d05b9433f8e49d0955c82e8cd37bd7bf36"}, - {file = "numpy-2.3.4-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:bafa7d87d4c99752d07815ed7a2c0964f8ab311eb8168f41b910bd01d15b6032"}, - {file = "numpy-2.3.4-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:36dc13af226aeab72b7abad501d370d606326a0029b9f435eacb3b8c94b8a8b7"}, - {file = "numpy-2.3.4-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a7b2f9a18b5ff9824a6af80de4f37f4ec3c2aab05ef08f51c77a093f5b89adda"}, - {file = "numpy-2.3.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9984bd645a8db6ca15d850ff996856d8762c51a2239225288f08f9050ca240a0"}, - {file = "numpy-2.3.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:64c5825affc76942973a70acf438a8ab618dbd692b84cd5ec40a0a0509edc09a"}, - {file = "numpy-2.3.4-cp311-cp311-win32.whl", hash = "sha256:ed759bf7a70342f7817d88376eb7142fab9fef8320d6019ef87fae05a99874e1"}, - {file = "numpy-2.3.4-cp311-cp311-win_amd64.whl", hash = "sha256:faba246fb30ea2a526c2e9645f61612341de1a83fb1e0c5edf4ddda5a9c10996"}, - {file = "numpy-2.3.4-cp311-cp311-win_arm64.whl", hash = "sha256:4c01835e718bcebe80394fd0ac66c07cbb90147ebbdad3dcecd3f25de2ae7e2c"}, - {file = "numpy-2.3.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ef1b5a3e808bc40827b5fa2c8196151a4c5abe110e1726949d7abddfe5c7ae11"}, - {file = "numpy-2.3.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c2f91f496a87235c6aaf6d3f3d89b17dba64996abadccb289f48456cff931ca9"}, - {file = "numpy-2.3.4-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:f77e5b3d3da652b474cc80a14084927a5e86a5eccf54ca8ca5cbd697bf7f2667"}, - {file = "numpy-2.3.4-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:8ab1c5f5ee40d6e01cbe96de5863e39b215a4d24e7d007cad56c7184fdf4aeef"}, - {file = "numpy-2.3.4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:77b84453f3adcb994ddbd0d1c5d11db2d6bda1a2b7fd5ac5bd4649d6f5dc682e"}, - {file = "numpy-2.3.4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4121c5beb58a7f9e6dfdee612cb24f4df5cd4db6e8261d7f4d7450a997a65d6a"}, - {file = "numpy-2.3.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:65611ecbb00ac9846efe04db15cbe6186f562f6bb7e5e05f077e53a599225d16"}, - {file = "numpy-2.3.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dabc42f9c6577bcc13001b8810d300fe814b4cfbe8a92c873f269484594f9786"}, - {file = "numpy-2.3.4-cp312-cp312-win32.whl", hash = "sha256:a49d797192a8d950ca59ee2d0337a4d804f713bb5c3c50e8db26d49666e351dc"}, - {file = "numpy-2.3.4-cp312-cp312-win_amd64.whl", hash = "sha256:985f1e46358f06c2a09921e8921e2c98168ed4ae12ccd6e5e87a4f1857923f32"}, - {file = "numpy-2.3.4-cp312-cp312-win_arm64.whl", hash = "sha256:4635239814149e06e2cb9db3dd584b2fa64316c96f10656983b8026a82e6e4db"}, - {file = "numpy-2.3.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c090d4860032b857d94144d1a9976b8e36709e40386db289aaf6672de2a81966"}, - {file = "numpy-2.3.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a13fc473b6db0be619e45f11f9e81260f7302f8d180c49a22b6e6120022596b3"}, - {file = "numpy-2.3.4-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:3634093d0b428e6c32c3a69b78e554f0cd20ee420dcad5a9f3b2a63762ce4197"}, - {file = "numpy-2.3.4-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:043885b4f7e6e232d7df4f51ffdef8c36320ee9d5f227b380ea636722c7ed12e"}, - {file = "numpy-2.3.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4ee6a571d1e4f0ea6d5f22d6e5fbd6ed1dc2b18542848e1e7301bd190500c9d7"}, - {file = "numpy-2.3.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fc8a63918b04b8571789688b2780ab2b4a33ab44bfe8ccea36d3eba51228c953"}, - {file = "numpy-2.3.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:40cc556d5abbc54aabe2b1ae287042d7bdb80c08edede19f0c0afb36ae586f37"}, - {file = "numpy-2.3.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ecb63014bb7f4ce653f8be7f1df8cbc6093a5a2811211770f6606cc92b5a78fd"}, - {file = "numpy-2.3.4-cp313-cp313-win32.whl", hash = "sha256:e8370eb6925bb8c1c4264fec52b0384b44f675f191df91cbe0140ec9f0955646"}, - {file = "numpy-2.3.4-cp313-cp313-win_amd64.whl", hash = "sha256:56209416e81a7893036eea03abcb91c130643eb14233b2515c90dcac963fe99d"}, - {file = "numpy-2.3.4-cp313-cp313-win_arm64.whl", hash = "sha256:a700a4031bc0fd6936e78a752eefb79092cecad2599ea9c8039c548bc097f9bc"}, - {file = "numpy-2.3.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:86966db35c4040fdca64f0816a1c1dd8dbd027d90fca5a57e00e1ca4cd41b879"}, - {file = "numpy-2.3.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:838f045478638b26c375ee96ea89464d38428c69170360b23a1a50fa4baa3562"}, - {file = "numpy-2.3.4-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:d7315ed1dab0286adca467377c8381cd748f3dc92235f22a7dfc42745644a96a"}, - {file = "numpy-2.3.4-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:84f01a4d18b2cc4ade1814a08e5f3c907b079c847051d720fad15ce37aa930b6"}, - {file = "numpy-2.3.4-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:817e719a868f0dacde4abdfc5c1910b301877970195db9ab6a5e2c4bd5b121f7"}, - {file = "numpy-2.3.4-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85e071da78d92a214212cacea81c6da557cab307f2c34b5f85b628e94803f9c0"}, - {file = "numpy-2.3.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2ec646892819370cf3558f518797f16597b4e4669894a2ba712caccc9da53f1f"}, - {file = "numpy-2.3.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:035796aaaddfe2f9664b9a9372f089cfc88bd795a67bd1bfe15e6e770934cf64"}, - {file = "numpy-2.3.4-cp313-cp313t-win32.whl", hash = "sha256:fea80f4f4cf83b54c3a051f2f727870ee51e22f0248d3114b8e755d160b38cfb"}, - {file = "numpy-2.3.4-cp313-cp313t-win_amd64.whl", hash = "sha256:15eea9f306b98e0be91eb344a94c0e630689ef302e10c2ce5f7e11905c704f9c"}, - {file = "numpy-2.3.4-cp313-cp313t-win_arm64.whl", hash = "sha256:b6c231c9c2fadbae4011ca5e7e83e12dc4a5072f1a1d85a0a7b3ed754d145a40"}, - {file = "numpy-2.3.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:81c3e6d8c97295a7360d367f9f8553973651b76907988bb6066376bc2252f24e"}, - {file = "numpy-2.3.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7c26b0b2bf58009ed1f38a641f3db4be8d960a417ca96d14e5b06df1506d41ff"}, - {file = "numpy-2.3.4-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:62b2198c438058a20b6704351b35a1d7db881812d8512d67a69c9de1f18ca05f"}, - {file = "numpy-2.3.4-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:9d729d60f8d53a7361707f4b68a9663c968882dd4f09e0d58c044c8bf5faee7b"}, - {file = "numpy-2.3.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd0c630cf256b0a7fd9d0a11c9413b42fef5101219ce6ed5a09624f5a65392c7"}, - {file = "numpy-2.3.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d5e081bc082825f8b139f9e9fe42942cb4054524598aaeb177ff476cc76d09d2"}, - {file = "numpy-2.3.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:15fb27364ed84114438fff8aaf998c9e19adbeba08c0b75409f8c452a8692c52"}, - {file = "numpy-2.3.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:85d9fb2d8cd998c84d13a79a09cc0c1091648e848e4e6249b0ccd7f6b487fa26"}, - {file = "numpy-2.3.4-cp314-cp314-win32.whl", hash = "sha256:e73d63fd04e3a9d6bc187f5455d81abfad05660b212c8804bf3b407e984cd2bc"}, - {file = "numpy-2.3.4-cp314-cp314-win_amd64.whl", hash = "sha256:3da3491cee49cf16157e70f607c03a217ea6647b1cea4819c4f48e53d49139b9"}, - {file = "numpy-2.3.4-cp314-cp314-win_arm64.whl", hash = "sha256:6d9cd732068e8288dbe2717177320723ccec4fb064123f0caf9bbd90ab5be868"}, - {file = "numpy-2.3.4-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:22758999b256b595cf0b1d102b133bb61866ba5ceecf15f759623b64c020c9ec"}, - {file = "numpy-2.3.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9cb177bc55b010b19798dc5497d540dea67fd13a8d9e882b2dae71de0cf09eb3"}, - {file = "numpy-2.3.4-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:0f2bcc76f1e05e5ab58893407c63d90b2029908fa41f9f1cc51eecce936c3365"}, - {file = "numpy-2.3.4-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:8dc20bde86802df2ed8397a08d793da0ad7a5fd4ea3ac85d757bf5dd4ad7c252"}, - {file = "numpy-2.3.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5e199c087e2aa71c8f9ce1cb7a8e10677dc12457e7cc1be4798632da37c3e86e"}, - {file = "numpy-2.3.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85597b2d25ddf655495e2363fe044b0ae999b75bc4d630dc0d886484b03a5eb0"}, - {file = "numpy-2.3.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:04a69abe45b49c5955923cf2c407843d1c85013b424ae8a560bba16c92fe44a0"}, - {file = "numpy-2.3.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e1708fac43ef8b419c975926ce1eaf793b0c13b7356cfab6ab0dc34c0a02ac0f"}, - {file = "numpy-2.3.4-cp314-cp314t-win32.whl", hash = "sha256:863e3b5f4d9915aaf1b8ec79ae560ad21f0b8d5e3adc31e73126491bb86dee1d"}, - {file = "numpy-2.3.4-cp314-cp314t-win_amd64.whl", hash = "sha256:962064de37b9aef801d33bc579690f8bfe6c5e70e29b61783f60bcba838a14d6"}, - {file = "numpy-2.3.4-cp314-cp314t-win_arm64.whl", hash = "sha256:8b5a9a39c45d852b62693d9b3f3e0fe052541f804296ff401a72a1b60edafb29"}, - {file = "numpy-2.3.4-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:6e274603039f924c0fe5cb73438fa9246699c78a6df1bd3decef9ae592ae1c05"}, - {file = "numpy-2.3.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d149aee5c72176d9ddbc6803aef9c0f6d2ceeea7626574fc68518da5476fa346"}, - {file = "numpy-2.3.4-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:6d34ed9db9e6395bb6cd33286035f73a59b058169733a9db9f85e650b88df37e"}, - {file = "numpy-2.3.4-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:fdebe771ca06bb8d6abce84e51dca9f7921fe6ad34a0c914541b063e9a68928b"}, - {file = "numpy-2.3.4-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:957e92defe6c08211eb77902253b14fe5b480ebc5112bc741fd5e9cd0608f847"}, - {file = "numpy-2.3.4-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13b9062e4f5c7ee5c7e5be96f29ba71bc5a37fed3d1d77c37390ae00724d296d"}, - {file = "numpy-2.3.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:81b3a59793523e552c4a96109dde028aa4448ae06ccac5a76ff6532a85558a7f"}, - {file = "numpy-2.3.4.tar.gz", hash = "sha256:a7d018bfedb375a8d979ac758b120ba846a7fe764911a64465fd87b8729f4a6a"}, -] - [[package]] name = "packaging" version = "25.0" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" +groups = ["dev"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, @@ -582,6 +618,8 @@ version = "2.3.3" description = "Powerful data structures for data analysis, time series, and statistics" optional = true python-versions = ">=3.9" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "pandas-2.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:376c6446ae31770764215a6c937f72d917f214b43560603cd60da6408f183b6c"}, {file = "pandas-2.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e19d192383eab2f4ceb30b412b22ea30690c9e618f78870357ae1d682912015a"}, @@ -681,6 +719,8 @@ version = "0.12.1" description = "Utility library for gitignore style pattern matching of file paths." optional = false python-versions = ">=3.8" +groups = ["dev"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, @@ -692,6 +732,8 @@ version = "4.4.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.9" +groups = ["dev"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "platformdirs-4.4.0-py3-none-any.whl", hash = "sha256:abd01743f24e5287cd7a5db3752faf1a2d65353f38ec26d98e25a6db65958c85"}, {file = "platformdirs-4.4.0.tar.gz", hash = "sha256:ca753cf4d81dc309bc67b0ea38fd15dc97bc30ce419a7f58d13eb3bf14c4febf"}, @@ -708,6 +750,8 @@ version = "3.8.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." optional = false python-versions = ">=3.9" +groups = ["dev"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "pre_commit-3.8.0-py2.py3-none-any.whl", hash = "sha256:9a90a53bf82fdd8778d58085faf8d83df56e40dfe18f45b19446e26bf1b3a63f"}, {file = "pre_commit-3.8.0.tar.gz", hash = "sha256:8bb6494d4a20423842e198980c9ecf9f96607a07ea29549e180eef9ae80fe7af"}, @@ -726,6 +770,8 @@ version = "1.10.24" description = "Data validation and settings management using python type hints" optional = false python-versions = ">=3.7" +groups = ["dev"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "pydantic-1.10.24-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eef07ea2fba12f9188cfa2c50cb3eaa6516b56c33e2a8cc3cd288b4190ee6c0c"}, {file = "pydantic-1.10.24-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5a42033fac69b9f1f867ecc3a2159f0e94dceb1abfc509ad57e9e88d49774683"}, @@ -792,6 +838,8 @@ version = "2.9.0.post0" description = "Extensions to the standard Python datetime module" optional = true python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" 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"}, @@ -806,6 +854,8 @@ version = "2025.2" description = "World timezone definitions, modern and historical" optional = true python-versions = "*" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00"}, {file = "pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3"}, @@ -817,6 +867,8 @@ version = "6.0.3" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.8" +groups = ["dev"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "PyYAML-6.0.3-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f"}, {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4"}, @@ -899,6 +951,8 @@ version = "2.32.5" description = "Python HTTP for Humans." optional = true python-versions = ">=3.9" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6"}, {file = "requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf"}, @@ -920,6 +974,8 @@ version = "1.0.0" description = "A utility belt for advanced users of python-requests" optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6"}, {file = "requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06"}, @@ -934,6 +990,8 @@ version = "0.91.0" description = "ruyaml is a fork of ruamel.yaml" optional = false python-versions = ">=3.6" +groups = ["dev"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "ruyaml-0.91.0-py3-none-any.whl", hash = "sha256:50e0ee3389c77ad340e209472e0effd41ae0275246df00cdad0a067532171755"}, {file = "ruyaml-0.91.0.tar.gz", hash = "sha256:6ce9de9f4d082d696d3bde264664d1bcdca8f5a9dff9d1a1f1a127969ab871ab"}, @@ -952,19 +1010,21 @@ version = "80.9.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.9" +groups = ["dev"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922"}, {file = "setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c"}, ] [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\"", "ruff (>=0.8.0) ; sys_platform != \"cygwin\""] -core = ["importlib_metadata (>=6) ; python_version < \"3.10\"", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1) ; python_version < \"3.11\"", "wheel (>=0.43.0)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.8.0)"] +core = ["importlib_metadata (>=6)", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] enabler = ["pytest-enabler (>=2.2)"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21) ; python_version >= \"3.9\" and sys_platform != \"cygwin\"", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf ; sys_platform != \"cygwin\"", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] -type = ["importlib_metadata (>=7.0.2) ; python_version < \"3.10\"", "jaraco.develop (>=7.21) ; sys_platform != \"cygwin\"", "mypy (==1.14.*)", "pytest-mypy"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] +type = ["importlib_metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.14.*)", "pytest-mypy"] [[package]] name = "six" @@ -972,6 +1032,8 @@ version = "1.17.0" description = "Python 2 and 3 compatibility utilities" optional = true python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, @@ -983,6 +1045,8 @@ version = "0.10.2" description = "Python Library for Tom's Obvious, Minimal Language" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +groups = ["dev"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, @@ -994,6 +1058,8 @@ version = "2.3.0" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" +groups = ["dev"] +markers = "python_version < \"3.11\"" files = [ {file = "tomli-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:88bd15eb972f3664f5ed4b57c1634a97153b4bac4479dcb6a495f41921eb7f45"}, {file = "tomli-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:883b1c0d6398a6a9d29b508c331fa56adbcdff647f6ace4dfca0f50e90dfd0ba"}, @@ -1045,6 +1111,8 @@ version = "4.15.0" description = "Backported and Experimental Type Hints for Python 3.9+" optional = false python-versions = ">=3.9" +groups = ["dev"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, @@ -1056,6 +1124,8 @@ version = "2025.2" description = "Provider of IANA time zone data" optional = true python-versions = ">=2" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8"}, {file = "tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9"}, @@ -1067,26 +1137,30 @@ version = "2.5.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = true python-versions = ">=3.9" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc"}, {file = "urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760"}, ] [package.extras] -brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] +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]] name = "virtualenv" -version = "20.35.3" +version = "20.35.4" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.8" +groups = ["dev"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ - {file = "virtualenv-20.35.3-py3-none-any.whl", hash = "sha256:63d106565078d8c8d0b206d48080f938a8b25361e19432d2c9db40d2899c810a"}, - {file = "virtualenv-20.35.3.tar.gz", hash = "sha256:4f1a845d131133bdff10590489610c98c168ff99dc75d6c96853801f7f67af44"}, + {file = "virtualenv-20.35.4-py3-none-any.whl", hash = "sha256:c21c9cede36c9753eeade68ba7d523529f228a403463376cf821eaae2b650f1b"}, + {file = "virtualenv-20.35.4.tar.gz", hash = "sha256:643d3914d73d3eeb0c552cbb12d7e82adf0e504dbf86a3182f8771a153a1971c"}, ] [package.dependencies] @@ -1097,7 +1171,7 @@ typing-extensions = {version = ">=4.13.2", markers = "python_version < \"3.11\"" [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) ; platform_python_implementation == \"PyPy\" or platform_python_implementation == \"GraalVM\" or platform_python_implementation == \"CPython\" and sys_platform == \"win32\" and python_version >= \"3.13\"", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10) ; platform_python_implementation == \"CPython\""] +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 = "yamlfix" @@ -1105,6 +1179,8 @@ version = "1.16.1" description = "A simple opionated yaml formatter that keeps your comments!" optional = false python-versions = ">=3.8" +groups = ["dev"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "yamlfix-1.16.1-py3-none-any.whl", hash = "sha256:8c505ca27cf19181ca8943101b56b8e4ad58f47aa792fbab01339ededaddb7d2"}, {file = "yamlfix-1.16.1.tar.gz", hash = "sha256:f49ba70e457a1add6724a6859505d22f7f222f56f7e31f37822c530fc2e7ec94"}, @@ -1115,28 +1191,7 @@ click = ">=8.1.3" maison = ">=1.4.0,<1.4.3" ruyaml = ">=0.91.0" -[[package]] -name = "zipp" -version = "3.23.0" -description = "Backport of pathlib-compatible object wrapper for zip files" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -markers = "python_version == \"3.9\"" -files = [ - {file = "zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e"}, - {file = "zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166"}, -] - -[package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] -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", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more_itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] -type = ["pytest-mypy"] - [metadata] -lock-version = "2.0" +lock-version = "2.1" python-versions = "^3.9" content-hash = "7ab03cbabe9011d7cbf14e3c328258a1977604214d85565a93c5008018cd199c" From 0ad9fa5a7741580797692281468e353f4ceaa70c Mon Sep 17 00:00:00 2001 From: "Charles Graham, SWT" Date: Thu, 4 Dec 2025 21:19:58 -0600 Subject: [PATCH 04/13] Add logging click option --- cwmscli/utils/__init__.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/cwmscli/utils/__init__.py b/cwmscli/utils/__init__.py index 7f0f41b..04a53de 100644 --- a/cwmscli/utils/__init__.py +++ b/cwmscli/utils/__init__.py @@ -1,3 +1,5 @@ +import logging + import click @@ -7,6 +9,16 @@ def to_uppercase(ctx, param, value): return value.upper() +def _set_log_level(ctx, param, value): + if value is None: + return + level = getattr(logging, value.upper(), None) + if level is None: + raise click.BadParameter(f"Invalid log level: {value}") + logging.getLogger().setLevel(level) + return value + + office_option = click.option( "-o", "--office", @@ -47,6 +59,17 @@ def to_uppercase(ctx, param, value): type=str, help="file storing Api Key. One of api-key or api-key-loc are required", ) +log_level_option = click.option( + "--log-level", + type=click.Choice( + ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"], case_sensitive=False + ), + default=None, + callback=_set_log_level, + expose_value=False, # Callback will set the log level of all methods + is_eager=True, # Run before other commands (to cover any logging statements) + help="Set logging verbosity (overrides default INFO).", +) def get_api_key(api_key: str, api_key_loc: str) -> str: @@ -62,6 +85,7 @@ def get_api_key(api_key: str, api_key_loc: str) -> str: def common_api_options(f): + f = log_level_option(f) f = office_option(f) f = api_root_option(f) f = api_key_option(f) From 92b0ef243f544236cd8fb30aab80db24853f9d7f Mon Sep 17 00:00:00 2001 From: "Charles Graham, SWT" Date: Thu, 4 Dec 2025 23:08:09 -0600 Subject: [PATCH 05/13] Add CLI options for retrieve --- cwmscli/commands/commands_cwms.py | 35 ++++++++++++++++++------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/cwmscli/commands/commands_cwms.py b/cwmscli/commands/commands_cwms.py index 07217c2..131185e 100644 --- a/cwmscli/commands/commands_cwms.py +++ b/cwmscli/commands/commands_cwms.py @@ -321,16 +321,14 @@ def timeseries_group_upload(**kwargs): # ================================================================================ @timeseries_group.command("retrieve", help="Download timeseries group") @click.option( - "--include-assigned", - default=True, - show_default=True, - type=bool, - help="Include the assigned timeseries in the returned timeseries groups. (default: true)", + "--group-id", + type=str, + help="Specifies the timeseries group whose data is to be included in the response", ) @click.option( - "--timeseries-category-like", + "--office", type=str, - help="Posix regular expression matching against the timeseries category id", + help="Specifies the owning office of the timeseries assigned to the group whose data is to be included in the response. This will limit the assigned timeseries returned to only those assigned to the specified office.", ) @click.option( "--category-office-id", @@ -338,20 +336,29 @@ def timeseries_group_upload(**kwargs): help="Specifies the owning office of the timeseries group category", ) @click.option( - "--timeseries-group-like", + "--group-office-id", type=str, - help="Posix regular expression matching against the timeseries group id", + help="Specifies the owning office of the timeseries group", ) @click.option( - "--dest", - help="Destination file path. Defaults to stdout.", + "--category-id", + type=str, + help="Specifies the category containing the timeseries group whose data is to be included in the response.", ) @click.option("--dry-run", is_flag=True, help="Show request; do not send.") +@click.option( + "--dest-dir", + required=False, + type=click.Path(exists=True, dir_okay=True, readable=True, path_type=str), + help="""Specify a relative/absolute path to a directory where the output file will be saved. + The file will be named _.json. + If not specified, it will be written to stdout.""", +) @common_api_options -def blob_download(**kwargs): - from cwmscli.commands.timeseries.group import download_cmd +def timeseries_group_download(**kwargs): + from cwmscli.commands.timeseries.group import retrieve_cmd - download_cmd(**kwargs) + retrieve_cmd(**kwargs) # ================================================================================ From 5830697c7511542cbc36e6fd89c3311cecc3ca69 Mon Sep 17 00:00:00 2001 From: "Charles Graham, SWT" Date: Thu, 4 Dec 2025 23:08:26 -0600 Subject: [PATCH 06/13] Add retrieve command --- cwmscli/commands/timeseries/group.py | 67 +++++++++++++++++----------- 1 file changed, 41 insertions(+), 26 deletions(-) diff --git a/cwmscli/commands/timeseries/group.py b/cwmscli/commands/timeseries/group.py index 17fdc73..2687216 100644 --- a/cwmscli/commands/timeseries/group.py +++ b/cwmscli/commands/timeseries/group.py @@ -9,6 +9,7 @@ import requests from cwmscli.utils import get_api_key +from cwmscli.utils.io import write_to_file def store_group(**kwargs): @@ -69,31 +70,43 @@ def store_group(**kwargs): sys.exit(1) -def retrieve_group(**kwargs): - group_id = kwargs.get("group_id", "").upper() +def retrieve_group( + group_id: str, + category_office_id: str, + group_office_id: str, + category_id: str, + office: str, + dest_dir: Optional[str] = None, +): if not group_id: logging.warning( "Valid group_id required to download a group. cwms-cli group download --group-id=myid. Run the list directive to see options for your office." ) sys.exit(0) - logging.debug(f"Office: {kwargs.get('office')} Group ID: {group_id}") + logging.debug(f"Office: {office} Group ID: {group_id}") try: - group = cwms.get_group( - office_id=kwargs.get("office"), + group = cwms.get_timeseries_group( group_id=group_id, + category_office_id=category_office_id, + group_office_id=group_office_id, + category_id=category_id, + office_id=office, ) logging.info( f"Successfully retrieved group with ID: {group_id}", ) - _save_base64(group, dest=group_id) - logging.info(f"Downloaded group to: {group_id}") + if dest_dir: + write_to_file( + file_path=os.path.join((dest_dir or "."), f"{office}_{group_id}.json"), + data=json.dumps(group.json, indent=2), + ) + else: + logging.info(group.df.to_string(index=False)) + return group except requests.HTTPError as e: detail = getattr(e.response, "text", "") or str(e) logging.error(f"Failed to retrieve group (HTTP): {detail}") sys.exit(1) - except Exception as e: - logging.error(f"Failed to retrieve group: {e}") - sys.exit(1) def delete_group(**kwargs): @@ -233,8 +246,16 @@ def store_cmd( sys.exit(1) -def download_cmd( - group_id: str, dest: str, office: str, api_root: str, api_key: str, dry_run: bool +def retrieve_cmd( + group_id: str, + category_office_id: str, + group_office_id: str, + category_id: str, + office: str, + api_root: str, + api_key: str, + dry_run: bool, + dest_dir: Optional[str] = None, ): if dry_run: logging.info( @@ -242,21 +263,15 @@ def download_cmd( ) return cwms.init_session(api_root=api_root, api_key=get_api_key(api_key, "")) - bid = group_id.upper() - logging.debug(f"Office={office} GroupID={bid}") - try: - group_b64 = cwms.get_group(office_id=office, group_id=bid) - target = dest or bid - _save_base64(group_b64, dest=target) - logging.info(f"Downloaded group to: {target}") - except requests.HTTPError as e: - detail = getattr(e.response, "text", "") or str(e) - logging.error(f"Failed to download (HTTP): {detail}") - sys.exit(1) - except Exception as e: - logging.error(f"Failed to download: {e}") - sys.exit(1) + retrieve_group( + group_id=group_id, + category_office_id=category_office_id, + group_office_id=group_office_id, + category_id=category_id, + office=office, + dest_dir=dest_dir, + ) def delete_cmd(group_id: str, office: str, api_root: str, api_key: str, dry_run: bool): From 35bfc3b7971af229fc56128505b90dc885970e71 Mon Sep 17 00:00:00 2001 From: "Charles Graham, SWT" Date: Thu, 4 Dec 2025 23:08:42 -0600 Subject: [PATCH 07/13] add io utility for writing to disk --- cwmscli/utils/io.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 cwmscli/utils/io.py diff --git a/cwmscli/utils/io.py b/cwmscli/utils/io.py new file mode 100644 index 0000000..cc29c1e --- /dev/null +++ b/cwmscli/utils/io.py @@ -0,0 +1,16 @@ +import logging +import os +from pathlib import Path + + +def write_to_file(file_path: str, data: str, create_dir: bool = False) -> None: + """Writes data to a file at the specified path.""" + if not file_path: + raise ValueError("You must specify a file path to write data to.") + if not data: + raise ValueError("No data provided to write to file.") + if create_dir: + Path(os.path.dirname(file_path)).mkdir(parents=True, exist_ok=True) + with open(file_path, "w") as file: + file.write(data) + logging.info(f"Data written to file: {file_path}") From fc05059cb371d5fe0cbaabd5d94d1e20946b4f9a Mon Sep 17 00:00:00 2001 From: "Charles Graham, SWT" Date: Thu, 4 Dec 2025 23:09:03 -0600 Subject: [PATCH 08/13] Set default log level --- cwmscli/utils/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cwmscli/utils/__init__.py b/cwmscli/utils/__init__.py index 04a53de..f4b8b8a 100644 --- a/cwmscli/utils/__init__.py +++ b/cwmscli/utils/__init__.py @@ -64,7 +64,8 @@ def _set_log_level(ctx, param, value): type=click.Choice( ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"], case_sensitive=False ), - default=None, + default="INFO", + envvar="LOG_LEVEL", callback=_set_log_level, expose_value=False, # Callback will set the log level of all methods is_eager=True, # Run before other commands (to cover any logging statements) From 7235be9400365c9632faa4bec54f53a5b207cc6c Mon Sep 17 00:00:00 2001 From: "Charles Graham, SWT" Date: Fri, 5 Dec 2025 00:06:01 -0600 Subject: [PATCH 09/13] Remove unused method, allow for stdin or file in --- cwmscli/commands/commands_cwms.py | 5 +- cwmscli/commands/timeseries/group.py | 144 ++++++++------------------- 2 files changed, 42 insertions(+), 107 deletions(-) diff --git a/cwmscli/commands/commands_cwms.py b/cwmscli/commands/commands_cwms.py index 131185e..b3294f4 100644 --- a/cwmscli/commands/commands_cwms.py +++ b/cwmscli/commands/commands_cwms.py @@ -305,10 +305,11 @@ def timeseries_group(): ) @click.option( "--input-file", - required=True, + required=False, type=click.Path(exists=True, dir_okay=False, readable=True, path_type=str), - help="Specify a relative/absolute path to a JSON file containing the request body", + help="Specify a relative/absolute path to a JSON file containing the request body. If not specified, it will be read from stdin.", ) +@click.option("--dry-run", is_flag=True, help="Show request; do not send.") @common_api_options def timeseries_group_upload(**kwargs): from cwmscli.commands.timeseries.group import store_cmd diff --git a/cwmscli/commands/timeseries/group.py b/cwmscli/commands/timeseries/group.py index 2687216..598d354 100644 --- a/cwmscli/commands/timeseries/group.py +++ b/cwmscli/commands/timeseries/group.py @@ -12,64 +12,6 @@ from cwmscli.utils.io import write_to_file -def store_group(**kwargs): - file_data = kwargs.get("file_data") - group_id = kwargs.get("group_id", "").upper() - # Attempt to determine what media type should be used for the mime-type if one is not presented based on the file extension - media = kwargs.get("media_type") or get_media_type(kwargs.get("input_file")) - - logging.debug( - f"Office: {kwargs.get('office')} Output ID: {group_id} Media: {media}" - ) - - group = { - "office-id": kwargs.get("office"), - "id": group_id, - "description": json.dumps(kwargs.get("description")), - "media-type-id": media, - "value": base64.b64encode(file_data).decode("utf-8"), - } - - params = {"fail-if-exists": not kwargs.get("overwrite")} - - if kwargs.get("dry_run"): - logging.info( - f"--dry-run enabled. Would POST to {kwargs.get('api_root')}/groups with params={params}" - ) - logging.info( - f"Group payload summary: office-id={kwargs.get('office')}, id={group_id}, media={media}", - ) - logging.info( - json.dumps( - { - "url": f"{kwargs.get('api_root')}groups", - "params": params, - "group": { - **group, - "value": f"", - }, - }, - indent=2, - ) - ) - sys.exit(0) - - try: - cwms.store_groups(group, fail_if_exists=kwargs.get("overwrite")) - logging.info(f"Successfully stored group with ID: {group_id}") - logging.info( - f"View: {kwargs.get('api_root')}groups/{group_id}?office={kwargs.get('office')}" - ) - except requests.HTTPError as e: - # Include response text when available - detail = getattr(e.response, "text", "") or str(e) - logging.error(f"Failed to store group (HTTP): {detail}") - sys.exit(1) - except Exception as e: - logging.error(f"Failed to store group: {e}") - sys.exit(1) - - def retrieve_group( group_id: str, category_office_id: str, @@ -101,7 +43,8 @@ def retrieve_group( data=json.dumps(group.json, indent=2), ) else: - logging.info(group.df.to_string(index=False)) + sys.stdout.write(json.dumps(group.json)) + sys.stdout.write("\n") return group except requests.HTTPError as e: detail = getattr(e.response, "text", "") or str(e) @@ -182,7 +125,7 @@ def list_groups( def store_cmd( - input_file: str, + input_file: str | None, overwrite: bool, dry_run: bool, office: str, @@ -190,60 +133,54 @@ def store_cmd( api_key: str, ): cwms.init_session(api_root=api_root, api_key=get_api_key(api_key, "")) + # ---------------------------- + # Read file or stdin + # ---------------------------- + file_data = None try: - file_size = os.path.getsize(input_file) - with open(input_file, "rb") as f: - file_data = f.read() - logging.info(f"Read file: {input_file} ({file_size} bytes)") + if input_file: + file_size = os.path.getsize(input_file) + with open(input_file, "rb") as f: + file_data = f.read().decode("utf-8") + logging.info(f"Read file: {input_file} ({file_size} bytes)") + else: + # Read from stdin (binary) + logging.info("Reading input from stdin...") + file_data = sys.stdin.buffer.read().decode("utf-8") + if not file_data: + logging.error("No input provided on stdin and no file specified.") + sys.exit(1) + logging.info(f"Read {len(file_data)} bytes from stdin") except Exception as e: - logging.error(f"Failed to read file: {e}") + logging.error(f"Failed to read input: {e}") sys.exit(1) - media = media_type or get_media_type(input_file) - group_id_up = group_id.upper() - logging.debug(f"Office={office} GroupID={group_id_up} Media={media}") - - group = { - "office-id": office, - "id": group_id_up, - "description": ( - json.dumps(description) - if isinstance(description, (dict, list)) - else description - ), - "media-type-id": media, - "value": base64.b64encode(file_data).decode("utf-8"), - } params = {"fail-if-exists": not overwrite} + # ---------------------------- + # Dry run + # ---------------------------- if dry_run: - logging.info(f"DRY RUN: would POST {api_root}groups with params={params}") + logging.info(f"DRY RUN: would POST {api_root}group with params={params}") logging.info( json.dumps( - { - "url": f"{api_root}groups", - "params": params, - "group": { - **group, - "value": f'', - }, - }, + file_data, indent=2, ) ) return + # ---------------------------- + # Upload + # ---------------------------- try: - cwms.store_groups(group, fail_if_exists=not overwrite) - logging.info(f"Uploaded group: {group_id_up}") - logging.info(f"View: {api_root}groups/{group_id_up}?office={office}") + logging.debug(f"Uploading timeseries group: {file_data}") + cwms.store_timeseries_groups(file_data, fail_if_exists=not overwrite) + logging.info(f"Uploaded group successfully.") except requests.HTTPError as e: detail = getattr(e.response, "text", "") or str(e) logging.error(f"Failed to upload (HTTP): {detail}") sys.exit(1) - except Exception as e: - logging.error(f"Failed to upload: {e}") - sys.exit(1) def retrieve_cmd( @@ -262,7 +199,8 @@ def retrieve_cmd( f"DRY RUN: would GET {api_root} group with group-id={group_id} office={office}." ) return - cwms.init_session(api_root=api_root, api_key=get_api_key(api_key, "")) + + cwms.init_session(api_root=api_root, api_key=api_key) retrieve_group( group_id=group_id, @@ -304,14 +242,10 @@ def update_cmd( return file_data = None if input_file: - try: - file_size = os.path.getsize(input_file) - with open(input_file, "rb") as f: - file_data = f.read() - logging.info(f"Read file: {input_file} ({file_size} bytes)") - except Exception as e: - logging.error(f"Failed to read file: {e}") - sys.exit(1) + file_size = os.path.getsize(input_file) + with open(input_file, "rb") as f: + file_data = f.read() + logging.info(f"Read file: {input_file} ({file_size} bytes)") # Setup minimum required payload group = {"office-id": office, "id": group_id.upper()} if description: @@ -346,7 +280,7 @@ def list_cmd( api_root: str, api_key: str, ): - cwms.init_session(api_root=api_root, api_key=get_api_key(api_key, None)) + cwms.init_session(api_root=api_root, api_key=api_key) df = list_groups( office=office, include_assigned=include_assigned, From 3b5de098702e092d6821793cf2ff39f81f1e1e26 Mon Sep 17 00:00:00 2001 From: "Charles Graham, SWT" Date: Fri, 5 Dec 2025 00:18:25 -0600 Subject: [PATCH 10/13] Correct types --- cwmscli/commands/timeseries/group.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cwmscli/commands/timeseries/group.py b/cwmscli/commands/timeseries/group.py index 598d354..017faf2 100644 --- a/cwmscli/commands/timeseries/group.py +++ b/cwmscli/commands/timeseries/group.py @@ -125,12 +125,12 @@ def list_groups( def store_cmd( - input_file: str | None, - overwrite: bool, - dry_run: bool, - office: str, - api_root: str, api_key: str, + input_file: Optional[str], + overwrite: Optional[bool], + dry_run: Optional[bool], + office: Optional[str], + api_root: Optional[str], ): cwms.init_session(api_root=api_root, api_key=get_api_key(api_key, "")) # ---------------------------- From 519ad7046bc881631704a6b352e4daf4cb4b0acf Mon Sep 17 00:00:00 2001 From: "Charles Graham, SWT" Date: Fri, 5 Dec 2025 22:54:49 -0600 Subject: [PATCH 11/13] Ensure office is uppercase, resolve it in CDA with usace/cwms-data-api#1499 --- cwmscli/commands/commands_cwms.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cwmscli/commands/commands_cwms.py b/cwmscli/commands/commands_cwms.py index b3294f4..3aa11aa 100644 --- a/cwmscli/commands/commands_cwms.py +++ b/cwmscli/commands/commands_cwms.py @@ -5,7 +5,7 @@ from cwmscli import requirements as reqs from cwmscli.callbacks import csv_to_list from cwmscli.commands import csv2cwms -from cwmscli.utils import api_key_loc_option, common_api_options +from cwmscli.utils import api_key_loc_option, common_api_options, to_uppercase from cwmscli.utils.deps import requires @@ -329,6 +329,7 @@ def timeseries_group_upload(**kwargs): @click.option( "--office", type=str, + callback=to_uppercase, help="Specifies the owning office of the timeseries assigned to the group whose data is to be included in the response. This will limit the assigned timeseries returned to only those assigned to the specified office.", ) @click.option( From 0e93ca5c95487ebc7f62bc9b365ebaf6e25fd972 Mon Sep 17 00:00:00 2001 From: "Charles Graham, SWT" Date: Fri, 5 Dec 2025 23:09:21 -0600 Subject: [PATCH 12/13] Add and test ts group delete command --- cwmscli/commands/commands_cwms.py | 18 ++++++++++++++++-- cwmscli/commands/timeseries/group.py | 13 +++++++++++-- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/cwmscli/commands/commands_cwms.py b/cwmscli/commands/commands_cwms.py index 3aa11aa..0cf382d 100644 --- a/cwmscli/commands/commands_cwms.py +++ b/cwmscli/commands/commands_cwms.py @@ -366,8 +366,22 @@ def timeseries_group_download(**kwargs): # ================================================================================ # Delete # ================================================================================ -@timeseries_group.command("delete", help="Delete a timeseries group") -@click.option("--blob-id", required=True, type=str, help="Blob ID to delete.") +@timeseries_group.command("delete", help="Deletes requested time series group") +@click.option( + "--group-id", required=True, type=str, help="The time series group to be deleted" +) +@click.option( + "--category-id", + required=True, + type=str, + help="Specifies the time series category of the time series group to be deleted", +) +@click.option( + "--office", + type=str, + callback=to_uppercase, + help="Specifies the owning office of the time series group to be deleted", +) @click.option("--dry-run", is_flag=True, help="Show request; do not send.") @common_api_options def delete_cmd(**kwargs): diff --git a/cwmscli/commands/timeseries/group.py b/cwmscli/commands/timeseries/group.py index 017faf2..af11360 100644 --- a/cwmscli/commands/timeseries/group.py +++ b/cwmscli/commands/timeseries/group.py @@ -212,7 +212,14 @@ def retrieve_cmd( ) -def delete_cmd(group_id: str, office: str, api_root: str, api_key: str, dry_run: bool): +def delete_cmd( + group_id: str, + category_id: str, + office: str, + api_root: str, + api_key: str, + dry_run: bool, +): if dry_run: logging.info( @@ -220,7 +227,9 @@ def delete_cmd(group_id: str, office: str, api_root: str, api_key: str, dry_run: ) return cwms.init_session(api_root=api_root, api_key=api_key) - cwms.delete_group(office_id=office, group_id=group_id) + cwms.delete_timeseries_group( + office_id=office, group_id=group_id, category_id=category_id + ) logging.info(f"Deleted group: {group_id} for office: {office}") From a0068b56e2a4d03ceea5da32bd989bd46b674545 Mon Sep 17 00:00:00 2001 From: "Charles Graham, SWT" Date: Wed, 10 Dec 2025 21:47:09 +0000 Subject: [PATCH 13/13] Wording on root ts group command --- cwmscli/commands/commands_cwms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cwmscli/commands/commands_cwms.py b/cwmscli/commands/commands_cwms.py index 0cf382d..09279d0 100644 --- a/cwmscli/commands/commands_cwms.py +++ b/cwmscli/commands/commands_cwms.py @@ -262,7 +262,7 @@ def list_cmd(**kwargs): epilog=textwrap.dedent( """ Example Usage:\n - - Manage Time Series Groups\n + - cwms-cli timeseries group --help : Manage Time Series Groups\n - More Coming Soon! """ ),