pjdev-armis-sdk
Async Python SDK for the Armis API, built on httpx and pydantic.
Table of Contents
Installation
pip install pjdev-armis-sdk
Usage
Configure once at startup (reads from environment variables prefixed with ARMIS_):
from pjdev_armis_sdk import config_service
config_service.init(
instance_url="https://your-tenant.armis.com",
secret_key="your-secret-key",
)
Then call any of the resource modules:
from pjdev_armis_sdk import devices, search
# AQL search
result = await search.aql_search("in:devices type:MOBILE_PHONE")
# Lookup a single device by id
device = await devices.get_device(id=12345)
Auth (Authorization header with a temporary access token) is handled automatically
by the underlying httpx.AsyncClient. Tokens are refreshed on the fly when they expire
or when the API returns 401.
Retries and backoff
Every endpoint call retries transient failures with capped, jittered exponential
backoff. Retry N waits http_retry_delay_seconds * 2 ** (N - 1) seconds, clamped
to http_retry_max_delay_seconds and then scaled by a random factor in [0.5, 1.0].
A Retry-After header on a retried response is honored instead — both the
delay-seconds and HTTP-date forms — clamped to the same ceiling.
Config knobs (all readable as ARMIS_-prefixed environment variables):
| Setting | Default | Meaning |
|---|---|---|
http_retry_max_count |
5 |
Maximum attempts per call. |
http_retry_delay_seconds |
2 |
Base delay; doubles each retry. |
http_retry_max_delay_seconds |
60.0 |
Ceiling for any single wait. |
http_retry_jitter |
True |
Spread out callers that failed together. |
What is retried: any status not listed in a call's status_codes_to_ignore,
plus transport failures (httpx.TransportError — connect errors, read/write and
pool timeouts, protocol errors). 400, 403 and 404 are treated as
deterministic and fail immediately.
401 is retried, with one deliberate exception. For ordinary endpoints a 401
means a stale access token, and it is worth retrying — note that
ArmisAccessTokenAuth has already re-authenticated and replayed the request once,
with no delay, before the retry layer ever sees it, so what retrying adds is
elapsed time for a transient condition to clear. Against the token endpoint
itself a 401 means the secret key is wrong, which no retry can fix, so
access_token.get_access_token() and failed token fetches inside the auth flow
fail fast on any 4xx and retry only on 429 or 5xx.
Consumers that need different behavior can override per call site without changing the shared defaults:
from pjdev_armis_sdk.api_utilities import async_retry_http
# Retry a status that this call site would otherwise treat as fatal.
@async_retry_http(status_codes_to_ignore=[400, 403], retry_status_codes=[409])
async def my_call() -> None: ...
retry_status_codes takes precedence over status_codes_to_ignore; passing only
the latter behaves exactly as it always has. It is an override layered on the base
rule, never an allowlist — it widens what is retried, never narrows it.
Writes that are not safe to repeat
Creates, and actions like _run_now/ that trigger work, are marked
idempotent=False. On those calls the SDK retries only failures that prove the
request never landed:
| Never landed — safe to retry | ConnectError, ConnectTimeout, PoolTimeout (no connection was ever established, so nothing was transmitted) and 401 (rejected at the authentication boundary before the handler runs) |
| May have landed — not retried | ReadTimeout, WriteTimeout, ReadError, WriteError, RemoteProtocolError (the request was on the wire and the response was lost) and every 4xx/5xx other than 401 |
The question for a write is not "is this failure transient?" but "could the
server have applied the write and still returned this?" A 504 or a
ReadTimeout means the request arrived and the response was lost — the write may
well have landed, so repeating it creates a duplicate. Armis offers no idempotency
key, so nothing about the request lets the SDK work this out for itself; it has to
be declared at the call site.
Note this is not a read/write split. A connection refused mid-deploy is the commonest transient failure there is, and it stays retryable on every endpoint including creates, because a refused connection cannot have written anything.
Affected: create_user, create_site, create_boundary, create_integration,
create_integration_v2, run_silk_integration, run_va_integration,
add_site_integration_id, add_site_network_equipment,
bulk_add_site_network_equipment.
The last three are a deliberate conservative guess, not a known fact: they POST
an id into a collection, and if Armis stores that collection as a set they are
harmless to repeat and the flag costs retry resilience for nothing. The guess is
made on cost asymmetry — a wrong False costs availability, a wrong True costs
duplicate data — and confirmation from Armis that POSTing an already-present id is
a no-op would settle it.
Not affected, because repeating them is harmless: every read, the update_* /
patch_* calls (they set fields to given values), the delete_* calls,
add_device_tags / remove_device_tags (set semantics), and
bulk_upsert_devices (upsert keyed by macAddress).
Upgrading
Which statuses retry is unchanged for callers that pass only status_codes_to_ignore.
Two timing changes are visible without any code change on your side:
-
Transport failures other than
ConnectError— read/write and pool timeouts, protocol errors — are now retried instead of propagating after one attempt. A call that used to fail fast on aReadTimeoutnow consumes its retry budget and the wall-clock time that goes with it. -
Jitter is on by default, so a delay is 0.5x–1.0x of the computed value. At the default
http_retry_delay_seconds=2the schedule was 2, 4, 8, 16 and is now 1–2, 2–4, 4–8, 8–16. Sethttp_retry_jitter=Falsefor the exact old schedule. -
Creates no longer survive a transient
5xxor429. This is the change most likely to affect you, and it is a reduction in write resilience, not only a bug fix. The ten non-idempotent calls listed above previously retried those statuses up tohttp_retry_max_counttimes; they now fail on the first one. If you have code relying oncreate_userriding out a brief503, it will now raise where it used to succeed, and the caller has to decide whether to retry.The trade is deliberate: a duplicate user is worse than a failed create the caller can retry on purpose. Note this closes a pre-existing hole as well as a recent one — a
504on a create could already produce duplicate rows before this release, because any status outsidestatus_codes_to_ignorewas retried. Connection failures are unaffected and still retry, since they cannot duplicate anything.
License
pjdev-armis-sdk is distributed under the terms of the MIT license.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pjdev_armis_sdk-5.2.1.tar.gz.
File metadata
- Download URL: pjdev_armis_sdk-5.2.1.tar.gz
- Upload date:
- Size: 22.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
Hatch/1.18.0 {"ci":true,"cpu":"aarch64","distro":{"id":"noble","libc":{"lib":"glibc","version":"2.39"},"name":"Ubuntu","version":"24.04"},"implementation":{"name":"CPython","version":"3.12.3"},"installer":{"name":"hatch","version":"1.18.0"},"openssl_version":"OpenSSL 3.0.13 30 Jan 2024","python":"3.12.3","system":{"name":"Linux","release":"7.0.12-linuxkit"}} HTTPX2/2.12.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c44c33dd4358387bfb04833b9ba959f583a0ca0e40f43d41848d84794fddd2bc
|
|
| MD5 |
a39e090fdc98cd634220ca5fc971b35c
|
|
| BLAKE2b-256 |
f05c7b33a4bd9530f03510a6841caf046d49c796704f0fbc8d81802b3bcf54a6
|
File details
Details for the file pjdev_armis_sdk-5.2.1-py3-none-any.whl.
File metadata
- Download URL: pjdev_armis_sdk-5.2.1-py3-none-any.whl
- Upload date:
- Size: 28.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
Hatch/1.18.0 {"ci":true,"cpu":"aarch64","distro":{"id":"noble","libc":{"lib":"glibc","version":"2.39"},"name":"Ubuntu","version":"24.04"},"implementation":{"name":"CPython","version":"3.12.3"},"installer":{"name":"hatch","version":"1.18.0"},"openssl_version":"OpenSSL 3.0.13 30 Jan 2024","python":"3.12.3","system":{"name":"Linux","release":"7.0.12-linuxkit"}} HTTPX2/2.12.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d05c0d3a6f74515d8b222809f985f655edf4178c5fa46515068d03496892a03c
|
|
| MD5 |
75ebb155f2ac0267e08d3a7ecea80b74
|
|
| BLAKE2b-256 |
7b133c1328e877de948b4cafb42dbb8aa9c867d662d05477a93eddea160ab3a7
|