Skip to main content

pyamselect

Python client library and CLI for AirMettle Select. Run SQL queries directly against data in your Azure Blob Storage — CSV, JSON, and Parquet — without moving or ingesting it.

Installation

pip install pyamselect

Requires Python 3.10+. Dependencies (httpx[http2], crc32c) are installed automatically.

Verify the install:

amselect --version

Quick Start

You need the following, provided with your AirMettle Select subscription:

  • Your subscription ID and API key
  • The query endpoint (host and port) and the metadata service endpoint (URL)
  • Data in an Azure storage account that is registered under your subscription

1. Prepare Your Data

Before an object can be queried, the service generates query metadata for it. Run this once per object:

amselect prepare \
    --endpoint "https://<metadata-endpoint>" \
    --blob-url "https://<account>.blob.core.windows.net/<container>/<blob>" \
    --subscription-id "<subscription-id>" \
    --api-key "<api-key>"

The service accesses the blob with the access key stored in the storage account's bucket registration, so the storage account must be registered under your subscription first.

The command prints a JSON result and waits (up to 5 minutes) for the operation to complete. Pass --overwrite to regenerate metadata after an object has changed, or --no-wait to return as soon as an asynchronous prepare is accepted.

2. Run a Query

amselect query -H "<query-host>" -r '{
  "select_request": {
    "expression": "SELECT * FROM Object WHERE age > 30",
    "container": "<container>",
    "blob": "<blob>",
    "storage_account": "<account>",
    "subscription_id": "<subscription-id>",
    "api_key": "<api-key>",
    "input_options": {"type": "csv", "value": {"csv_header_config": "use"}},
    "output_options": {"type": "csv", "value": {"recordDelimiter": "\n", "fieldDelimiter": ","}}
  }
}'

Results stream to stdout; pass -o results.csv to write them to a file instead. Larger requests can be kept in a file and passed with --request-file request.json.

3. Query from Python

from pyamselect import AMSelectClient, SelectRequest, CSVInputOptions, CSVOutputOptions

request = SelectRequest(
    expression="SELECT * FROM Object WHERE age > 30",
    container="<container>",
    blob="<blob>",
    storage_account="<account>",
    subscription_id="<subscription-id>",
    api_key="<api-key>",
    input_options=CSVInputOptions(csv_header_config="use", csv_field_delimiter=","),
    output_options=CSVOutputOptions(),
)

with AMSelectClient("<query-host>") as client:
    result = client.select_to_string(request)
    print(result)

An async client with the same API is also available — see Async Client.

Building Requests

Dataclasses vs Raw Dicts

Input/output options can be passed as typed dataclasses (IDE autocomplete, validation) or raw dicts (the same JSON structure sent on the wire).

# Dataclass
from pyamselect import CSVInputOptions
input_options = CSVInputOptions(csv_header_config="use", csv_field_delimiter=",")

# Equivalent raw dict
input_options = {"type": "csv", "value": {"csv_header_config": "use", "csv_field_delimiter": ","}}

CSV Convenience Factory

request = SelectRequest.csv_query(
    expression="SELECT name, age FROM Object",
    container="mycontainer",
    blob="people.csv",
    storage_account="myaccount",
    subscription_id="sub-123",
    api_key="key-456",
)

JSON and Parquet Input

from pyamselect import JSONInputOptions, JSONOutputOptions, ParquetInputOptions, CSVOutputOptions

# JSON lines
request = SelectRequest(
    expression="""SELECT t."Timestamp", t.devname FROM Object as t WHERE t.windows_event_id='4624'""",
    container="mycontainer",
    blob="events.jsonl",
    storage_account="myaccount",
    input_options=JSONInputOptions(json_type="lines"),
    output_options=JSONOutputOptions(record_delimiter="\n"),
)

# Parquet
request = SelectRequest(
    expression="SELECT col1, col2 FROM Object",
    container="mycontainer",
    blob="data.parquet",
    storage_account="myaccount",
    input_options=ParquetInputOptions(),
    output_options=CSVOutputOptions(),
)

Request JSON Format

The request JSON sent to the service has the following format:

{
  "select_request": {
    "expression": "SELECT * FROM Object",
    "expression_type": "sql",
    "container": "mycontainer",
    "blob": "myfile.csv",
    "storage_account": "myaccount",
    "subscription_id": "sub-123",
    "api_key": "key-456",
    "input_options": {
      "type": "csv",
      "value": { "csv_header_config": "use", "csv_field_delimiter": "," }
    },
    "output_options": {
      "type": "csv",
      "value": { "recordDelimiter": "\n", "fieldDelimiter": "," }
    }
  }
}

The select_request envelope is optional when using SelectRequest.from_json() -- bare inner objects are accepted too.

Client API

Both clients share the same methods: select(), select_to_string(), select_to_file(), and select_to_stream().

Sync Client

with AMSelectClient("<query-host>") as client:
    # Collect all data as a string
    result = client.select_to_string(request)

    # Write directly to a file
    client.select_to_file(request, "output.csv")

    # Write to any writable binary stream
    client.select_to_stream(request, stream)

Streaming Events

For full control over the event stream (data, stats, continue events):

from pyamselect import AMSelectClient, EventType

with AMSelectClient("<query-host>") as client:
    for event in client.select(request):
        if event.event_type == EventType.DATA:
            print(event.payload.decode("utf-8"), end="")
        elif event.event_type == EventType.STATS:
            print(f"Stats: {event.payload}")
        elif event.event_type == EventType.END:
            print("Done.")

Async Client

import asyncio
from pyamselect import AsyncAMSelectClient

async def main():
    async with AsyncAMSelectClient("<query-host>") as client:
        result = await client.select_to_string(request)

        await client.select_to_file(request, "output.csv")

        await client.select_to_stream(request, stream)

asyncio.run(main())

Async Streaming

import asyncio
from pyamselect import AsyncAMSelectClient, EventType

async def main():
    async with AsyncAMSelectClient("<query-host>") as client:
        async for event in client.select(request):
            if event.event_type == EventType.DATA:
                print(event.payload.decode("utf-8"), end="")

asyncio.run(main())

Configuration

These options apply to both AMSelectClient and AsyncAMSelectClient.

Timeouts

with AMSelectClient("<query-host>", connect_timeout=10.0, read_timeout=300.0) as client:
    result = client.select_to_string(request)

Error Handling

from pyamselect import AMSelectHTTPError, AMSelectStreamError, AMSelectConnectionError

with AMSelectClient("<query-host>") as client:
    try:
        result = client.select_to_string(request)
    except AMSelectConnectionError as e:
        print(f"Connection failed: {e}")
    except AMSelectHTTPError as e:
        print(f"HTTP {e.status_code}: {e.body}")
    except AMSelectStreamError as e:
        print(f"Query error {e.error_code}: {e.error_message}")

CLI Reference

Installing the package provides the amselect command with two subcommands: query and prepare. Query options can also be passed directly to amselect without the query subcommand. All commands exit non-zero on failure.

amselect --version     # Show version
amselect --buildinfo   # Show detailed build information

query

Runs a select request against the query endpoint and streams the results.

# Inline request (results to stdout)
amselect query -H "<query-host>" -r '{"select_request": {...}}'

# Request from a file
amselect query -H "<query-host>" --request-file request.json

# Write results to a file
amselect query -H "<query-host>" --request-file request.json -o results.csv
-H, --host              Query endpoint host (required)
-P, --port              Query endpoint port (default: 443)
-r, --request           JSON request string
    --request-file      Path to JSON request file
-o, --output            Output file path (default: stdout)

prepare

Generates the query metadata for an object via the metadata service. An object must be prepared before it can be queried, and re-prepared (with --overwrite) after its contents change.

amselect prepare \
    --endpoint "https://<metadata-endpoint>" \
    --blob-url "https://<account>.blob.core.windows.net/<container>/<blob>" \
    --subscription-id "<subscription-id>" \
    --api-key "<api-key>"

The service accesses the blob with the access key stored in the storage account's bucket registration; requests for storage accounts not registered under the subscription are rejected. The result is printed as JSON. By default the command waits (up to 5 minutes) for an asynchronous prepare to finish.

    --endpoint          Metadata service base URL (required)
    --blob-url          Full URL of the blob to prepare
    --subscription-id   Subscription ID (required with --blob-url)
    --api-key           Subscription API key (required with --blob-url)
-r, --request           JSON request string (alternative to --blob-url)
    --request-file      Path to JSON request file (alternative to --blob-url)
    --overwrite         Regenerate metadata if it already exists
    --no-wait           Return as soon as an asynchronous prepare is accepted

Common Options

Available on both subcommands:

    --ca-cert           Path to CA certificate bundle
    --insecure-skip-tls-verify
                        Disable TLS certificate verification
    --connect-timeout   Connection timeout in seconds (default: 60)
    --read-timeout      Read timeout in seconds (default: 60)
-v, --verbose           Increase verbosity (-v=INFO, -vv=DEBUG)

License

MIT — see the LICENSE file. Use of the AirMettle Select service itself is governed by your service agreement.

Release files for pyamselect 1.2.6

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pyamselect 1.2.6
File Size Uploaded
pyamselect-1.2.6.tar.gz 13.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pyamselect 1.2.6
File Interpreter ABI Platform
pyamselect-1.2.6-py3-none-any.whl Python 3 none any Details

Total release size: 30.8 kB

Release files / pyamselect-1.2.6.tar.gz

Download URL pyamselect-1.2.6.tar.gz
Size 13.7 kB
Tags Source
SHA-256 checksum
How to use checksums
8f6b90c87794509f6a4e8566b709a427ea813e4bd6f2bf720a2c804d1df762ce
BLAKE2b-256 checksum
How to use checksums
9b6489441fe4836b86e073128491fe3a91bfc8530e8197c7cffd0e428c52691c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

Release files / pyamselect-1.2.6-py3-none-any.whl

Download URL pyamselect-1.2.6-py3-none-any.whl
Size 17.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
391adb23f2897c92423b83ca5df3e1fb83b7fac4283a2f3cf2ce24fe45064f48
BLAKE2b-256 checksum
How to use checksums
c9e3a6c32e24de7c6f73b0ccddb1ff9c544e3dcc47e09f7747683cccd4f7ced2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

Release history Release notifications | RSS feed

This release

1.2.6 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page