ionq_core.session

Session lifecycle manager for IonQ QPU sessions.

Sessions allow you to reserve priority access to a QPU backend. The SessionManager class wraps the session create / end / status APIs and supports both sync and async context managers for automatic cleanup.

Example:
from ionq_core import IonQClient, SessionManager

client = IonQClient()

# Context manager creates and automatically ends the session
with SessionManager(client, "qpu.aria-1", max_jobs=10) as session:
    print(session.session_id)
    print(session.status())  # "started"
    # ... submit jobs using session.session_id ...

# Or reconnect to an existing session
session = SessionManager.from_id(client, "existing-session-id")
print(session.status())
  1# SPDX-FileCopyrightText: 2026 IonQ, Inc.
  2# SPDX-License-Identifier: Apache-2.0
  3
  4"""Session lifecycle manager for IonQ QPU sessions.
  5
  6Sessions allow you to reserve priority access to a QPU backend. The
  7`SessionManager` class wraps the session create / end / status APIs
  8and supports both sync and async context managers for automatic cleanup.
  9
 10Example:
 11    ```python
 12    from ionq_core import IonQClient, SessionManager
 13
 14    client = IonQClient()
 15
 16    # Context manager creates and automatically ends the session
 17    with SessionManager(client, "qpu.aria-1", max_jobs=10) as session:
 18        print(session.session_id)
 19        print(session.status())  # "started"
 20        # ... submit jobs using session.session_id ...
 21
 22    # Or reconnect to an existing session
 23    session = SessionManager.from_id(client, "existing-session-id")
 24    print(session.status())
 25    ```
 26"""
 27
 28from __future__ import annotations
 29
 30__all__ = ["SessionManager"]
 31
 32import logging
 33from typing import TYPE_CHECKING
 34
 35from .api.default import create_session, end_session, get_session
 36from .exceptions import IonQError
 37from .models.create_session_request import CreateSessionRequest
 38from .models.session_cost_limit import SessionCostLimit
 39from .models.session_settings_request import SessionSettingsRequest
 40from .types import UNSET, Unset
 41
 42if TYPE_CHECKING:
 43    from .client import AuthenticatedClient
 44
 45logger = logging.getLogger("ionq_core")
 46
 47
 48class SessionManager:
 49    """Convenience wrapper around session create / end / status APIs.
 50
 51    Can be used as both a sync and async context manager. On exit the
 52    session is automatically ended. Exceptions during close are logged
 53    and suppressed so that cleanup does not mask the original error.
 54
 55    Args:
 56        client: An authenticated API client.
 57        backend: The backend to create a session on (e.g. ``"qpu.aria-1"``).
 58        max_jobs: Optional maximum number of jobs for this session.
 59        max_time: Optional maximum session duration in minutes.
 60        max_cost: Optional maximum cost in USD for the session.
 61
 62    Examples:
 63        Sync context manager:
 64
 65        ```python
 66        with SessionManager(client, "qpu.aria-1", max_jobs=10) as session:
 67            print(session.session_id)
 68        ```
 69
 70        Async context manager:
 71
 72        ```python
 73        async with SessionManager(client, "qpu.aria-1") as session:
 74            print(session.session_id)
 75        ```
 76    """
 77
 78    def __init__(
 79        self,
 80        client: AuthenticatedClient,
 81        backend: str,
 82        *,
 83        max_jobs: int | None = None,
 84        max_time: int | None = None,
 85        max_cost: float | None = None,
 86    ) -> None:
 87        self._client = client
 88        self._backend = backend
 89        self._max_jobs = max_jobs
 90        self._max_time = max_time
 91        self._max_cost = max_cost
 92        self._session_id: str | None = None
 93
 94    @classmethod
 95    def from_id(cls, client: AuthenticatedClient, session_id: str) -> SessionManager:
 96        """Reconnect to an existing session without creating a new one.
 97
 98        This is useful for resuming work with a session that was created
 99        in a previous process or by another client.
100
101        Args:
102            client: An authenticated API client.
103            session_id: The ID of the existing session.
104
105        Returns:
106            A `SessionManager` bound to the given session ID. The ``backend``
107            field will be empty since it is not needed for status checks
108            or ending the session.
109        """
110        mgr = cls(client, backend="")
111        mgr._session_id = session_id
112        return mgr
113
114    @property
115    def session_id(self) -> str | None:
116        """The session ID, or ``None`` if `open` has not been called."""
117        return self._session_id
118
119    def _build_settings(self) -> SessionSettingsRequest | Unset:
120        kw: dict = {}
121        if self._max_jobs is not None:
122            kw["job_count_limit"] = self._max_jobs
123        if self._max_time is not None:
124            kw["duration_limit_min"] = self._max_time
125        if self._max_cost is not None:
126            kw["cost_limit"] = SessionCostLimit(unit="usd", value=self._max_cost)
127        return SessionSettingsRequest(**kw) if kw else UNSET
128
129    def open(self) -> None:
130        """Create a new session on the configured backend.
131
132        Raises:
133            IonQError: If a session is already open or creation fails.
134        """
135        if self._session_id is not None:
136            raise IonQError("Session already open")
137        body = CreateSessionRequest(backend=self._backend, settings=self._build_settings())
138        session = create_session.sync(client=self._client, body=body)
139        if session is None:
140            raise IonQError("Failed to create session")
141        self._session_id = session.id
142        logger.info("Opened session %s", self._session_id)
143
144    def close(self) -> None:
145        """End the session. Suppresses exceptions so cleanup is safe."""
146        if self._session_id is None:
147            return
148        try:
149            end_session.sync(session_id=self._session_id, client=self._client)
150            logger.info("Closed session %s", self._session_id)
151        except Exception:
152            logger.warning("Failed to end session %s", self._session_id, exc_info=True)
153
154    def status(self) -> str:
155        """Get the current session status (e.g. ``"created"``, ``"started"``, ``"ended"``).
156
157        Raises:
158            IonQError: If no session is open or the status fetch fails.
159        """
160        if self._session_id is None:
161            raise IonQError("No session ID; call open() first")
162        session = get_session.sync(session_id=self._session_id, client=self._client)
163        if session is None:
164            raise IonQError(f"Failed to fetch session {self._session_id}")
165        return session.status
166
167    async def async_open(self) -> None:
168        """Async version of `open`."""
169        if self._session_id is not None:
170            raise IonQError("Session already open")
171        body = CreateSessionRequest(backend=self._backend, settings=self._build_settings())
172        session = await create_session.asyncio(client=self._client, body=body)
173        if session is None:
174            raise IonQError("Failed to create session")
175        self._session_id = session.id
176        logger.info("Opened session %s", self._session_id)
177
178    async def async_close(self) -> None:
179        """End the session (async). Suppresses exceptions so cleanup is safe."""
180        if self._session_id is None:
181            return
182        try:
183            await end_session.asyncio(session_id=self._session_id, client=self._client)
184            logger.info("Closed session %s", self._session_id)
185        except Exception:
186            logger.warning("Failed to end session %s", self._session_id, exc_info=True)
187
188    async def async_status(self) -> str:
189        """Async version of `status`."""
190        if self._session_id is None:
191            raise IonQError("No session ID; call open() first")
192        session = await get_session.asyncio(session_id=self._session_id, client=self._client)
193        if session is None:
194            raise IonQError(f"Failed to fetch session {self._session_id}")
195        return session.status
196
197    def __enter__(self) -> SessionManager:
198        self.open()
199        return self
200
201    def __exit__(self, *exc: object) -> None:
202        self.close()
203
204    async def __aenter__(self) -> SessionManager:
205        await self.async_open()
206        return self
207
208    async def __aexit__(self, *exc: object) -> None:
209        await self.async_close()
class SessionManager:
 49class SessionManager:
 50    """Convenience wrapper around session create / end / status APIs.
 51
 52    Can be used as both a sync and async context manager. On exit the
 53    session is automatically ended. Exceptions during close are logged
 54    and suppressed so that cleanup does not mask the original error.
 55
 56    Args:
 57        client: An authenticated API client.
 58        backend: The backend to create a session on (e.g. ``"qpu.aria-1"``).
 59        max_jobs: Optional maximum number of jobs for this session.
 60        max_time: Optional maximum session duration in minutes.
 61        max_cost: Optional maximum cost in USD for the session.
 62
 63    Examples:
 64        Sync context manager:
 65
 66        ```python
 67        with SessionManager(client, "qpu.aria-1", max_jobs=10) as session:
 68            print(session.session_id)
 69        ```
 70
 71        Async context manager:
 72
 73        ```python
 74        async with SessionManager(client, "qpu.aria-1") as session:
 75            print(session.session_id)
 76        ```
 77    """
 78
 79    def __init__(
 80        self,
 81        client: AuthenticatedClient,
 82        backend: str,
 83        *,
 84        max_jobs: int | None = None,
 85        max_time: int | None = None,
 86        max_cost: float | None = None,
 87    ) -> None:
 88        self._client = client
 89        self._backend = backend
 90        self._max_jobs = max_jobs
 91        self._max_time = max_time
 92        self._max_cost = max_cost
 93        self._session_id: str | None = None
 94
 95    @classmethod
 96    def from_id(cls, client: AuthenticatedClient, session_id: str) -> SessionManager:
 97        """Reconnect to an existing session without creating a new one.
 98
 99        This is useful for resuming work with a session that was created
100        in a previous process or by another client.
101
102        Args:
103            client: An authenticated API client.
104            session_id: The ID of the existing session.
105
106        Returns:
107            A `SessionManager` bound to the given session ID. The ``backend``
108            field will be empty since it is not needed for status checks
109            or ending the session.
110        """
111        mgr = cls(client, backend="")
112        mgr._session_id = session_id
113        return mgr
114
115    @property
116    def session_id(self) -> str | None:
117        """The session ID, or ``None`` if `open` has not been called."""
118        return self._session_id
119
120    def _build_settings(self) -> SessionSettingsRequest | Unset:
121        kw: dict = {}
122        if self._max_jobs is not None:
123            kw["job_count_limit"] = self._max_jobs
124        if self._max_time is not None:
125            kw["duration_limit_min"] = self._max_time
126        if self._max_cost is not None:
127            kw["cost_limit"] = SessionCostLimit(unit="usd", value=self._max_cost)
128        return SessionSettingsRequest(**kw) if kw else UNSET
129
130    def open(self) -> None:
131        """Create a new session on the configured backend.
132
133        Raises:
134            IonQError: If a session is already open or creation fails.
135        """
136        if self._session_id is not None:
137            raise IonQError("Session already open")
138        body = CreateSessionRequest(backend=self._backend, settings=self._build_settings())
139        session = create_session.sync(client=self._client, body=body)
140        if session is None:
141            raise IonQError("Failed to create session")
142        self._session_id = session.id
143        logger.info("Opened session %s", self._session_id)
144
145    def close(self) -> None:
146        """End the session. Suppresses exceptions so cleanup is safe."""
147        if self._session_id is None:
148            return
149        try:
150            end_session.sync(session_id=self._session_id, client=self._client)
151            logger.info("Closed session %s", self._session_id)
152        except Exception:
153            logger.warning("Failed to end session %s", self._session_id, exc_info=True)
154
155    def status(self) -> str:
156        """Get the current session status (e.g. ``"created"``, ``"started"``, ``"ended"``).
157
158        Raises:
159            IonQError: If no session is open or the status fetch fails.
160        """
161        if self._session_id is None:
162            raise IonQError("No session ID; call open() first")
163        session = get_session.sync(session_id=self._session_id, client=self._client)
164        if session is None:
165            raise IonQError(f"Failed to fetch session {self._session_id}")
166        return session.status
167
168    async def async_open(self) -> None:
169        """Async version of `open`."""
170        if self._session_id is not None:
171            raise IonQError("Session already open")
172        body = CreateSessionRequest(backend=self._backend, settings=self._build_settings())
173        session = await create_session.asyncio(client=self._client, body=body)
174        if session is None:
175            raise IonQError("Failed to create session")
176        self._session_id = session.id
177        logger.info("Opened session %s", self._session_id)
178
179    async def async_close(self) -> None:
180        """End the session (async). Suppresses exceptions so cleanup is safe."""
181        if self._session_id is None:
182            return
183        try:
184            await end_session.asyncio(session_id=self._session_id, client=self._client)
185            logger.info("Closed session %s", self._session_id)
186        except Exception:
187            logger.warning("Failed to end session %s", self._session_id, exc_info=True)
188
189    async def async_status(self) -> str:
190        """Async version of `status`."""
191        if self._session_id is None:
192            raise IonQError("No session ID; call open() first")
193        session = await get_session.asyncio(session_id=self._session_id, client=self._client)
194        if session is None:
195            raise IonQError(f"Failed to fetch session {self._session_id}")
196        return session.status
197
198    def __enter__(self) -> SessionManager:
199        self.open()
200        return self
201
202    def __exit__(self, *exc: object) -> None:
203        self.close()
204
205    async def __aenter__(self) -> SessionManager:
206        await self.async_open()
207        return self
208
209    async def __aexit__(self, *exc: object) -> None:
210        await self.async_close()

Convenience wrapper around session create / end / status APIs.

Can be used as both a sync and async context manager. On exit the session is automatically ended. Exceptions during close are logged and suppressed so that cleanup does not mask the original error.

Arguments:
  • client: An authenticated API client.
  • backend: The backend to create a session on (e.g. "qpu.aria-1").
  • max_jobs: Optional maximum number of jobs for this session.
  • max_time: Optional maximum session duration in minutes.
  • max_cost: Optional maximum cost in USD for the session.
Examples:

Sync context manager:

with SessionManager(client, "qpu.aria-1", max_jobs=10) as session:
    print(session.session_id)

Async context manager:

async with SessionManager(client, "qpu.aria-1") as session:
    print(session.session_id)
SessionManager( client: ionq_core.AuthenticatedClient, backend: str, *, max_jobs: int | None = None, max_time: int | None = None, max_cost: float | None = None)
79    def __init__(
80        self,
81        client: AuthenticatedClient,
82        backend: str,
83        *,
84        max_jobs: int | None = None,
85        max_time: int | None = None,
86        max_cost: float | None = None,
87    ) -> None:
88        self._client = client
89        self._backend = backend
90        self._max_jobs = max_jobs
91        self._max_time = max_time
92        self._max_cost = max_cost
93        self._session_id: str | None = None
@classmethod
def from_id( cls, client: ionq_core.AuthenticatedClient, session_id: str) -> SessionManager:
 95    @classmethod
 96    def from_id(cls, client: AuthenticatedClient, session_id: str) -> SessionManager:
 97        """Reconnect to an existing session without creating a new one.
 98
 99        This is useful for resuming work with a session that was created
100        in a previous process or by another client.
101
102        Args:
103            client: An authenticated API client.
104            session_id: The ID of the existing session.
105
106        Returns:
107            A `SessionManager` bound to the given session ID. The ``backend``
108            field will be empty since it is not needed for status checks
109            or ending the session.
110        """
111        mgr = cls(client, backend="")
112        mgr._session_id = session_id
113        return mgr

Reconnect to an existing session without creating a new one.

This is useful for resuming work with a session that was created in a previous process or by another client.

Arguments:
  • client: An authenticated API client.
  • session_id: The ID of the existing session.
Returns:

A SessionManager bound to the given session ID. The backend field will be empty since it is not needed for status checks or ending the session.

session_id: str | None
115    @property
116    def session_id(self) -> str | None:
117        """The session ID, or ``None`` if `open` has not been called."""
118        return self._session_id

The session ID, or None if open has not been called.

def open(self) -> None:
130    def open(self) -> None:
131        """Create a new session on the configured backend.
132
133        Raises:
134            IonQError: If a session is already open or creation fails.
135        """
136        if self._session_id is not None:
137            raise IonQError("Session already open")
138        body = CreateSessionRequest(backend=self._backend, settings=self._build_settings())
139        session = create_session.sync(client=self._client, body=body)
140        if session is None:
141            raise IonQError("Failed to create session")
142        self._session_id = session.id
143        logger.info("Opened session %s", self._session_id)

Create a new session on the configured backend.

Raises:
  • IonQError: If a session is already open or creation fails.
def close(self) -> None:
145    def close(self) -> None:
146        """End the session. Suppresses exceptions so cleanup is safe."""
147        if self._session_id is None:
148            return
149        try:
150            end_session.sync(session_id=self._session_id, client=self._client)
151            logger.info("Closed session %s", self._session_id)
152        except Exception:
153            logger.warning("Failed to end session %s", self._session_id, exc_info=True)

End the session. Suppresses exceptions so cleanup is safe.

def status(self) -> str:
155    def status(self) -> str:
156        """Get the current session status (e.g. ``"created"``, ``"started"``, ``"ended"``).
157
158        Raises:
159            IonQError: If no session is open or the status fetch fails.
160        """
161        if self._session_id is None:
162            raise IonQError("No session ID; call open() first")
163        session = get_session.sync(session_id=self._session_id, client=self._client)
164        if session is None:
165            raise IonQError(f"Failed to fetch session {self._session_id}")
166        return session.status

Get the current session status (e.g. "created", "started", "ended").

Raises:
  • IonQError: If no session is open or the status fetch fails.
async def async_open(self) -> None:
168    async def async_open(self) -> None:
169        """Async version of `open`."""
170        if self._session_id is not None:
171            raise IonQError("Session already open")
172        body = CreateSessionRequest(backend=self._backend, settings=self._build_settings())
173        session = await create_session.asyncio(client=self._client, body=body)
174        if session is None:
175            raise IonQError("Failed to create session")
176        self._session_id = session.id
177        logger.info("Opened session %s", self._session_id)

Async version of open.

async def async_close(self) -> None:
179    async def async_close(self) -> None:
180        """End the session (async). Suppresses exceptions so cleanup is safe."""
181        if self._session_id is None:
182            return
183        try:
184            await end_session.asyncio(session_id=self._session_id, client=self._client)
185            logger.info("Closed session %s", self._session_id)
186        except Exception:
187            logger.warning("Failed to end session %s", self._session_id, exc_info=True)

End the session (async). Suppresses exceptions so cleanup is safe.

async def async_status(self) -> str:
189    async def async_status(self) -> str:
190        """Async version of `status`."""
191        if self._session_id is None:
192            raise IonQError("No session ID; call open() first")
193        session = await get_session.asyncio(session_id=self._session_id, client=self._client)
194        if session is None:
195            raise IonQError(f"Failed to fetch session {self._session_id}")
196        return session.status

Async version of status.