Skip to main content

SpectrumX Data System SDK

Project description

SpectrumX Data System | SDK

PyPI - Version PyPI - Python Versions Pepy Total Downloads

The SpectrumX Data System (SDS) SDK is a Python package that provides a simple interface for interacting with the SDS Gateway. The SDK is designed to be easy to use and to provide a high-level interface for common tasks, such as uploading and downloading files, searching for files, and managing RF datasets.

[!NOTE]

SDS is not meant for personal files or as a backup tool. Files may be rejected by the Gateway when uploaded, or deleted without warning. All uploaded files have an expiration date. Do not upload sensitive, personally identifiable, confidential information, or any file that you do not have permission to share. Do not upload binary executables.

If you own data in https://sds.crc.nd.edu that needs to be permanently deleted, please reach out to the team at crc-sds-list [·at·] nd.edu, as SDS may retain uploaded data for a period of time after deletion.

Getting Started

Installation

uv add spectrumx

# or one of:
#   poetry add spectrumx
#   pip install spectrumx
#   conda install spectrumx
#   ...

[!NOTE] When not using uv, make sure you are using Python 3.12 or higher (python --version).

Basic Usage

  1. In a file named .env, enter the secret_token provided to you:

    SDS_SECRET_TOKEN=your-secret-token-no-quotes
    

    OR set the environment variable SDS_SECRET_TOKEN to your secret token:

    # the env var takes precedence over the .env file
    export SDS_SECRET_TOKEN=your-secret-token
    
  2. Then, in your Python script or Jupyter notebook

    See ./tests/e2e_examples/check_build_acceptance.py for more examples.

    from pathlib import Path
    from random import randint, random
    from spectrumx.client import Client
    
    # Example of files upload, listing, and download from SDS.
    
    # NOTE: the SDS client-server interaction is stateless, so it is
    #   not recommended to have multiple clients writing to the same
    #   locations simultaneously, as they may overrule each other
    #   and cause loss of data. See "Concurrent Access" in the
    #   usage guide to learn more.
    sds = Client(
        host="sds.crc.nd.edu",
        # env_file=Path(".env"),  # default, recommended to keep tokens out of version control
        # env_config={"SDS_SECRET_TOKEN": "my-custom-token"},  # alternative way to pass the access token
    )
    
    # when in dry-run (default), no changes are made to the SDS or the local filesystem
    # to enable the changes, set dry_run to False, as in:
    # sds.dry_run = False
    
    # authenticate using either the token from
    # the .env file or in the config passed in
    sds.authenticate()
    
    # local_dir has your own local files that will be uploaded to the SDS
    reference_name: str = "my_spectrum_capture"
    local_dir: Path = Path(reference_name)
    
    # or, if the directory doesn't exist, let's create some fake data
    if not local_dir.exists():
        local_dir.mkdir(exist_ok=True)
        num_files = 10
        for file_idx in range(num_files):
            num_lines = randint(10, 100)  # noqa: S311
            file_name = f"capture_{file_idx}.csv"
            with (local_dir / file_name).open(mode="w", encoding="utf-8") as file_ptr:
                fake_nums = [random() for _ in range(num_lines)]  # noqa: S311
                file_ptr.write("\n".join(map(str, fake_nums)))
    
    # upload all files in a directory to the SDS
    # sds.dry_run = False   # uncomment to actually upload the files
    sds.upload(
        local_path=local_dir,  # may be a single file or a directory
        sds_path=reference_name,  # files will be created under this virtual directory
        verbose=True,  # shows a progress bar (default)
    )
    
    # download the files from an SDS directory
    # sds.dry_run = False
    local_downloads = Path("sds-downloads") / "files" / reference_name
    sds.download(
        from_sds_path=reference_name,  # files will be downloaded from this virtual dir
        to_local_path=local_downloads,  # download to this location (it may be created)
        overwrite=False,  # do not overwrite local existing files (default)
        verbose=True,  # shows a progress bar (default)
    )
    
    if not sds.dry_run:
        print("Downloaded files:")
        for file_path in local_downloads.iterdir():
            print(file_path)
    else:
        print("Turn off dry-run to download and write files.")
    

Error Handling

The SDK provides context-aware exceptions that can be caught and handled in your code.

# ======== Authentication ========
from pathlib import Path
from spectrumx.client import Client
from spectrumx.errors import AuthError, NetworkError

sds = Client(host="sds.crc.nd.edu")
try:
    sds.authenticate()
except NetworkError as err:
    print(f"Failed to connect to the SDS: {err}")
    # check your host= parameter and network connection
    # if you're hosting the SDS Gateway, make sure it is accessible
except AuthError as err:
    print(f"Failed to authenticate: {err}")
    # TODO: take action

# ======== File operations ========

from time import sleep
from spectrumx.errors import NetworkError, SDSError, ServiceError

# ...
local_dir: Path = Path("my_spectrum_files")
reference_name: str = "my_spectrum_files"
is_success = False
retries_left: int = 5
while not is_success and retries_left > 0:
    try:
        retries_left -= 1
        # the sds.upload will restart a partial file transfer from zero,
        # but it won't re-upload already finished files.
        sds.upload(
            local_path=local_dir,
            sds_path=reference_name,
            verbose=True,
        )
        is_success = True
    except (NetworkError, ServiceError) as err:
        # NetworkError refers to connection issues between client and SDS Gateway
        # ServiceError refers to issues with the SDS Gateway itself (e.g. HTTP 500)
        # sleep longer with each retry, at least 5s, up to 5min
        sleep_time = max(5, 5 / (retries_left**2) * 60)
        print(f"Failed to reach the gateway; sleeping {sleep_time}s")
        print(f"Error: {err}")
        if retries_left > 0:
            sleep(sleep_time)
        continue
    except SDSError as err:
        print(f"Another SDS error occurred: {err}")
        # other errors might include e.g. OSError
        #   if listed files cannot be found.
        # TODO: take action or break
        break

Concurrent Access

The SDS client-server interaction is stateless, meaning that each request contains all the information needed to complete that request. One positive outcome is that it allows multiple clients to interact with the SDS Gateway at the same time. However, this opens up the possibility of having multiple clients writing to the same locations simultaneously, causing loss of data by overruling each other's writes (race condition).

For example, if two clients are uploading files with the same directory, file names, and at the same time, only the last file successfully uploaded (from the Gateway's perspective) is guaranteed to be kept, which might not be aligned with the user's expectations.

To avoid potential race conditions, it is not recommended to have multiple clients writing to the same locations simultaneously. Neither the SDK nor the Gateway currently take any measure to detect this, in part, because any measure towards it would either be incomplete, or it would make our APIs stateful and significantly increase code complexity.

If this is needed, SDK users have a few options:

  1. Restructure their architecture to forward writes to a single centralized client responsible for them.
  2. Restructure the code by writing to different locations and/or at different application stages. The latter assumes all conflicting clients are part of the same application.
  3. Implement a custom locking mechanism for writes to serve their specific use case.

One writer (an SDK client that creates, updates, and/or deletes contents) and multiple readers are generally safe.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

spectrumx-0.1.8.tar.gz (65.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

spectrumx-0.1.8-py3-none-any.whl (43.1 kB view details)

Uploaded Python 3

File details

Details for the file spectrumx-0.1.8.tar.gz.

File metadata

  • Download URL: spectrumx-0.1.8.tar.gz
  • Upload date:
  • Size: 65.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.6.9

File hashes

Hashes for spectrumx-0.1.8.tar.gz
Algorithm Hash digest
SHA256 f388760a41aa045eb638eb2e875cceb94e183927987a4786a8fe6a9ef69da966
MD5 96b65cc21a9b1e81317cb645b7aa7773
BLAKE2b-256 5fb3599c3819f229e58f7e3eb61c160275c6bc2967cb93a17e76e184059a7d13

See more details on using hashes here.

File details

Details for the file spectrumx-0.1.8-py3-none-any.whl.

File metadata

  • Download URL: spectrumx-0.1.8-py3-none-any.whl
  • Upload date:
  • Size: 43.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.6.9

File hashes

Hashes for spectrumx-0.1.8-py3-none-any.whl
Algorithm Hash digest
SHA256 f9c15c4a30065727c8812e6a8db0650b4f00f3e365c60a23796fd8de37bd001b
MD5 aa85a098fa2871b9be4e6a2b24cf2f40
BLAKE2b-256 290d9228715711f52a7bebdb13072bed37505fe2af734afcd3265f7834c5095c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page