diff --git a/qiskit_ibm/api/clients/runtime.py b/qiskit_ibm/api/clients/runtime.py index 4aae5de02..ddc71bff9 100644 --- a/qiskit_ibm/api/clients/runtime.py +++ b/qiskit_ibm/api/clients/runtime.py @@ -158,7 +158,7 @@ def program_update(self, program_id: str, program_data: str) -> None: Args: program_id: Program ID. - program_data: Program data. + program_data: Program data (base64 encoded). """ self.api.program(program_id).update(program_data) diff --git a/qiskit_ibm/api/rest/runtime.py b/qiskit_ibm/api/rest/runtime.py index 8ae956dc2..d5ceff21e 100644 --- a/qiskit_ibm/api/rest/runtime.py +++ b/qiskit_ibm/api/rest/runtime.py @@ -238,11 +238,11 @@ def update(self, program_data: str) -> None: """Update a program. Args: - program_data: Program data. + program_data: Program data (base64 encoded). """ url = self.get_url("data") self.session.put(url, data=program_data, - headers={'Content-Type': 'text/plain'}) + headers={'Content-Type': 'application/octet-stream'}) class ProgramJob(RestAdapterBase): diff --git a/qiskit_ibm/runtime/ibm_runtime_service.py b/qiskit_ibm/runtime/ibm_runtime_service.py index 185df03dd..cd6a45897 100644 --- a/qiskit_ibm/runtime/ibm_runtime_service.py +++ b/qiskit_ibm/runtime/ibm_runtime_service.py @@ -12,7 +12,6 @@ """Qiskit runtime service.""" -import base64 import logging from typing import Dict, Callable, Optional, Union, List, Any, Type import json @@ -24,7 +23,7 @@ from .runtime_job import RuntimeJob from .runtime_program import RuntimeProgram, ProgramParameter, ProgramResult, ParameterNamespace -from .utils import RuntimeEncoder, RuntimeDecoder +from .utils import RuntimeEncoder, RuntimeDecoder, to_base64_string from .exceptions import (QiskitRuntimeError, RuntimeDuplicateProgramError, RuntimeProgramNotFound, RuntimeJobNotFound) from .program.result_decoder import ResultDecoder @@ -339,7 +338,7 @@ def upload_program( data = file.read() try: - program_data = base64.b64encode(data.encode('utf-8')).decode('utf-8') + program_data = to_base64_string(data) response = self._api_client.program_create(program_data=program_data, **program_metadata) except RequestsApiError as ex: @@ -412,12 +411,22 @@ def update_program( Args: program_id: Program ID. data: Program data or path of the file containing program data to upload. + + Raises: + RuntimeProgramNotFound: If the program doesn't exist. + QiskitRuntimeError: If the request failed. """ if "def main(" not in data: # This is the program file with open(data, "r") as file: data = file.read() - self._api_client.program_update(program_id, data) + try: + program_data = to_base64_string(data) + self._api_client.program_update(program_id, program_data) + except RequestsApiError as ex: + if ex.status_code == 404: + raise RuntimeProgramNotFound(f"Program not found: {ex.message}") from None + raise QiskitRuntimeError(f"Failed to update program: {ex}") from None def delete_program(self, program_id: str) -> None: """Delete a runtime program. diff --git a/qiskit_ibm/runtime/utils.py b/qiskit_ibm/runtime/utils.py index 5b46f00ca..b0e4c2a57 100644 --- a/qiskit_ibm/runtime/utils.py +++ b/qiskit_ibm/runtime/utils.py @@ -40,6 +40,18 @@ from qiskit.result import Result +def to_base64_string(data: str) -> str: + """Convert string to base64 string. + + Args: + data: string to convert + + Returns: + data as base64 string + """ + return base64.b64encode(data.encode('utf-8')).decode('utf-8') + + def _serialize_and_encode( data: Any, serializer: Callable,