Skip to main content

blissclient

A python client for the BLISS REST API, the high-level client is fully typed ready for auto-completion in any modern IDE.

Getting Started

Set the BLISSAPI_URL

export BLISSAPI_URL=http://localhost:5000

Then:

from blissclient import BlissClient

client = BlissClient()

# or the blissapi url can be set directly when instantiating the client:
client = BlissClient("http://localhost:5000")

omega = client.hardware.get("omega")
print(omega.position)

future = omega.move(100)
# Wait for the call to temrinate, blocking
future.get()

Execute calls in the session:

import time
from blissclient import BlissClient, get_object

client = BlissClient()

test_session = client.session
future = test_session.call("ascan", get_object("omega"), 0, 10, 10, 0.1, get_object("diode"))

# Ask for the current future state
print(future.state)

# Block until terminated
result = future.get()

# The redis scan key, can be used with `blissdata``
print(result["key"])

get_object("<name>") are translated to the relevant beacon objects.

See the test suite for more examples.

Timeouts

The default timeout for httpx is 5 seconds. This can be a little short for some requests to the BLISS REST API. The timeout can be tuned either when instantiating the client:

client = BlissClient("http://localhost:5000", timeout=10)

or via the environement variable BLISSAPI_TIMEOUT:

export BLISSAPI_TIMEOUT=30

Events

Events are propagated via a websocket using socket.io. blissclient provides a helper function to create connect functions to instantiate the socket.io connection in either threaded or asyncio forms:

connect = self._client.create_connect()
connect()

connect = self._client.create_connect(async_client=True)
await connect()

This function should be run somewhere in the background to ensure event reception. After that objects will be set in evented mode to limit polling.

The client can then subscribe to hardware object events:

omega = client.hardware.get("omega")

def property_event(data: dict[str, any]):
    for key, value in data.items():
        # for example position
        logger.info(f"property event key=`{key}` value=`{value}`")

def online_event(online: bool):
    logger.info(f"onine event {online}")

def locked_event(reason: str):
    logger.info(f"locked event {reason}")

omega.subscribe("property", property_event)
omega.subscribe("online", online_event)
omega.subscribe("locked", locked_event)

Events also allow stdout to be captured from a session call:

connect = client.create_connect(async_client=True)
task = asyncio.create_task(connect())

future = test_session.call(
    "ascan",
    get_object("robx"),
    0,
    5,
    5,
    0.2,
    get_object("diode1"),
    in_terminal=True,
    emit_stdout=True,
)

response = future.get()
print(future.stdout)

Future API

The future returned by each call tries to emulate a celery future.

future = test_session.call(...)

# Any progress if the task supports it
future.progress

# Assume the called function returns a bliss `Scan` object
future = test_session.call(
    "ascan",
    ...
    has_scan_factory=True,
)

# Wait for the scan to start
progress = future.wait_scan()

# Now access the bliss `Scan` key that can be used with blissdata
print(progress["scan"]["key"])


# Ask for the current state (a single REST call)
future.state

# Blocking until terminated (polls the REST API in the background)
# default `monitor_interval` = 5s
future.get(monitor_interval=1)

# Kill the current task
future.kill()

# If socketio was connected and the call requested `emit_stdout=True`
print(future.stdout)

Accessing scan data via blissdata

blissclient will attempt to autoload scan info and provide access to scan data via blissdata. By default blissclient will try to infer the BLISSDATA_URL from the current BLISSAPI_URL, this assumes that redis is running on the same machine as the REST API. In situations where this is not the case you can manually set the blissdata url, where the default port at ESRF is 25002:

export BLISSDATA_URL="redis://myhost:25002"

Once set blissclient will try to resolve the scans:

# blissdata url can also be set when instantiating the client
client = BlissClient(blissdata_url="redis://myhost:25002")
...

future = test_session.call(
    "ascan",
    ...
)

scan = future.get()
scan.info
    {'type': 'ascan',
    'save': False,
    ...
    }

scan.stream('timer:epoch')[:]
    array([1.7732378e+09])

You can completely disable this behaviour by setting:

export BLISSCLIENT_AUTOLOAD_SCAN=false

or when you instantiate the client:

client = BlissClient(autoload_scans=False)

Metadata

Release files for blissclient 1.4.1

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

Source distribution (sdist)

Source distribution for blissclient 1.4.1
File Size Uploaded Provenance
blissclient-1.4.1.tar.gz 23.9 kB Without provenance Details

Built distribution (wheel)

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

Total release size: 61.8 kB

Release files / blissclient-1.4.1.tar.gz

Download URL blissclient-1.4.1.tar.gz
Size 23.9 kB
Tags Source
SHA-256 checksum
How to use checksums
89f6d9665564380a1702d103832b5c0d475680ce476e767c7e565d74fed3ac8b
BLAKE2b-256 checksum
How to use checksums
19638410cfce1cb9a19df7c01f93f72d5cf72b2f20f92361cfbab70d292df1c4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.10.13

Release files / blissclient-1.4.1-py3-none-any.whl

Download URL blissclient-1.4.1-py3-none-any.whl
Size 38.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4affb06db5a24a753767408c72c73b46ce372047a422a81535961af7c5140f1f
BLAKE2b-256 checksum
How to use checksums
ee2c0815c8f9c4bcc02aceffba2742c09f7b3af12f9789970d6eb2ffff32e387
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.10.13

Security for blissclient 1.4.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

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