Skip to main content

SORACOM Harvest Files utility library

Project description

soracom_lib

A lightweight Python client library for SORACOM Harvest Files.

Provides authentication, iterative file listing, download, upload, and related helpers.
All functionality lives in soracom_harvest_files.py.

No dependencies beyond the standard library and requests.


Features

  • Credential loading from .netrc (no credentials in source code)
  • Authentication via SORACOM SAM user — calls POST /auth and returns a reusable token dict
  • Iterative directory traversal with optional time-range filter and pagination
  • Per-file download with skip-existing and overwrite options
  • File upload with automatic Content-Type detection
  • Recency helper for filtering by last-modified time

Requirements

Python 3.11 +
requests any recent version

Installation

No package installation is needed. Copy the file into your project:

your_project/
└── soracom_harvest_files.py

Install the only external dependency:

pip install requests

Credentials setup

soracom_harvest_files never reads credentials from source code or environment variables.
It uses the standard netrc mechanism so secrets stay out of your repository.

1. Create a SORACOM SAM user

Generate an Auth Key in the SORACOM User Console under Security → SAM Users.

2. Write the netrc file

Windows — create %USERPROFILE%\.netrc:

machine api.soracom.io
login   keyId-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
password secret-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Linux / macOS — create ~/.netrc and restrict permissions:

cat >> ~/.netrc << 'EOF'
machine api.soracom.io
login   keyId-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
password secret-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
EOF
chmod 600 ~/.netrc

3. Add netrc to .gitignore

.netrc

Quick start

import soracom_harvest_files as sf

# Load credentials (returns AuthInfo; raises RuntimeError on failure)
auth_info = sf.read_auth_keys()

# Call POST /auth and get a session token dict
token = sf.authenticate(auth_info)

# List all files under a path (returns list[FileEntry])
files = sf.list_files_iterative("logs/XXXXXXXXXXXXXXXXX/", token)
print([e.path for e in files])

# Download each file individually
import os
for entry in files:
    local_path = os.path.join("./download", os.path.basename(entry.path))
    sf.download_and_save(entry.path, local_path, token)

# Upload a file
sf.upload_file_to_soracom(
    file_path="./results/output.log",
    upload_path="logs/XXXXXXXXXXXXXXXX/output.log",
    token=token,
)

API reference

read_auth_keys(base_dir=None) -> AuthInfo

Load credentials from .netrc and return an AuthInfo object.

Parameter Type Description
base_dir str | None When provided, also searches for .netrc in this directory (takes priority over the home directory).

Raises RuntimeError if no valid credentials are found.


AuthInfo

Frozen dataclass holding the loaded credentials.

@dataclass(frozen=True)
class AuthInfo:
    api_key: str    # authKeyId from netrc login field
    api_token: str  # authKey from netrc password field

authenticate(auth: AuthInfo) -> dict

Call POST /auth with the credentials in auth and return the session token dict issued by the server.

token = sf.authenticate(auth_info)
# → {"apiKey": "...", "token": "..."}

Raises RuntimeError if the request fails or the server returns a non-200 status.


list_files_iterative(base_path, token, *, limit=None, page_size=100, start_time=None, end_time=None) -> list[FileEntry]

Iteratively traverse directories and list all files under base_path in Harvest Files.

Parameter Type Description
base_path str Root path in Harvest Files, e.g. "logs/XXXXX.../"
token dict Token dict from authenticate()
limit int | None Cap on total results; None = no limit
page_size int Number of entries per API request (default 100, max 100)
start_time datetime | None Exclude files modified before this time. Naive datetimes are treated as UTC.
end_time datetime | None Exclude files modified after this time. Naive datetimes are treated as UTC.

Returns a list of FileEntry objects. Each entry has a path attribute (full remote path string) and a last_modified attribute (UTC-aware datetime, or None when absent from the API response).


download_and_save(remote_path, local_path, token, *, overwrite=False, chunk_size=1048576) -> bool

Download a single file from Harvest Files and save it to the local filesystem.

Parameter Type Description
remote_path str File path in Harvest Files (e.g. "logs/XXXXX.../file.log")
local_path str Destination path on local filesystem
token dict Token dict from authenticate()
overwrite bool If False (default) and the local file already exists, the download is skipped and True is returned
chunk_size int Streaming chunk size in bytes (default 1 MiB)

Returns True on success (including skip), False on failure.
Parent directories are created automatically.


upload_file_to_soracom(file_path, upload_path, token) -> bool

Upload a local file to Harvest Files via HTTP PUT.
Content-Type is detected automatically from the file extension; falls back to application/octet-stream.

Returns True on success (HTTP 200 or 204), False on failure.


get_with_auth(url, token) -> requests.Response | None

Authenticated GET. Returns the response object on success (status < 400), or None on error.


delete_with_auth(url, token) -> requests.Response | None

Authenticated DELETE. Returns the response object on success (status < 400), or None on error.


is_recent(last_modified_ms, within_seconds, *, now=None) -> bool

Return True if last_modified_ms (milliseconds since epoch, as returned by the Harvest Files API) falls within the past within_seconds seconds.

Parameter Type Description
last_modified_ms int Milliseconds since epoch
within_seconds int Threshold in seconds
now datetime | None Reference time (UTC). Defaults to datetime.now(timezone.utc)
if sf.is_recent(entry["lastModifiedTime"], within_seconds=7 * 86_400):
    print("Modified within the last week")

LEVEL_INFO / LEVEL_WARN / LEVEL_ERROR

Log level constants used internally by log_status(). You can also pass these directly when calling log_status().

LEVEL_INFO  = "INFO"
LEVEL_WARN  = "WARN"
LEVEL_ERROR = "ERROR"

Bulk download pattern

download_and_save() operates on one file at a time, giving you full control over size limits, delete-after-download, and error handling per file.

import os
import soracom_harvest_files as sf

auth_info = sf.read_auth_keys()
token = sf.authenticate(auth_info)

files = sf.list_files_iterative("logs/XXXXXXXXXXXXXXXX/", token)
save_dir = "./download/XXXXXXXXXXXXXXXX"
os.makedirs(save_dir, exist_ok=True)

LIMIT_BYTES = 200 * 1024 * 1024  # 200 MB
downloaded_bytes = 0

for entry in sorted(files, key=lambda e: e.path):
    if downloaded_bytes >= LIMIT_BYTES:
        break

    local_path = os.path.join(save_dir, os.path.basename(entry.path))
    ok = sf.download_and_save(entry.path, local_path, token)

    if ok:
        downloaded_bytes += os.path.getsize(local_path)
        # Optionally delete from Harvest Files after download:
        # sf.delete_with_auth(f"{sf.API_BASE}/files/private/{entry.path}", token)

Logging

soracom_harvest_files uses Python's standard logging module under the logger name soracom_harvest_files.
By default a NullHandler is attached, so no output appears unless your application configures logging.
To integrate with your own logging config:

import logging

# Show INFO and above from this library on stderr
logging.basicConfig(level=logging.INFO)
logging.getLogger("soracom_harvest_files").propagate = True

# Or suppress all output from this library
logging.getLogger("soracom_harvest_files").setLevel(logging.CRITICAL)

License

Copyright (c) 2025-2026 tkxu

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

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

soracom_lib-0.1.8.tar.gz (9.1 kB view details)

Uploaded Source

Built Distribution

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

soracom_lib-0.1.8-py3-none-any.whl (11.6 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: soracom_lib-0.1.8.tar.gz
  • Upload date:
  • Size: 9.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for soracom_lib-0.1.8.tar.gz
Algorithm Hash digest
SHA256 429947c09d7174d4bdb162cca420ad331bcb707805a16e00005ebc788ab842b8
MD5 3eb9f7c80e15021bdebae0475eb3db1d
BLAKE2b-256 e0cbb5b6c89bd233e183574510dd6d8a1163d872ff38fd86ea8fa4eaf77889dc

See more details on using hashes here.

Provenance

The following attestation bundles were made for soracom_lib-0.1.8.tar.gz:

Publisher: publish.yml on tkxu/soracom-lib

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

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

File metadata

  • Download URL: soracom_lib-0.1.8-py3-none-any.whl
  • Upload date:
  • Size: 11.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for soracom_lib-0.1.8-py3-none-any.whl
Algorithm Hash digest
SHA256 02fbb395a72beea76856a288967b5a60614eff742c54a9bc1f0ee6e38c922069
MD5 5a8e3c2c5a4f39d79a84a3ad88df4ee3
BLAKE2b-256 a61f6444b4517e7424cb0ed0adb64c6285acdb19c68411963499b080117f092e

See more details on using hashes here.

Provenance

The following attestation bundles were made for soracom_lib-0.1.8-py3-none-any.whl:

Publisher: publish.yml on tkxu/soracom-lib

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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