From 0079346d748f262a41f75096df1083ea8a4fa6a4 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Fri, 20 Oct 2023 16:06:19 -0400 Subject: [PATCH] Emit a descriptive error when the QPY version is too new This commit updates the qpy load() function to ensure we are raising a descriptive error message when a user attempts to load a qpy version that's not supported by this version of qiskit. Previously it would fail but with a hard to understand internal error message which was just confusing and not helpful. For example, when trying to load a QPY version 10 payload with qiskit 0.25.2 (which only supported up to version 9) it would raise an error message like: ``` Invalid payload format data kind 'b'p''." ``` which doesn't tell you anything meaningful unless you are intimately familiar with the QPY file format and how the load() function works. With this commit it now checks if the version number is supported immediately and if it's too new it will raise an error message that says exactly that. So in the above scenario instead of that error message it will say: ``` The QPY format version being read, 10, isn't supported by this Qiskit version. Please upgrade your version of Qiskit to load this QPY payload. ``` --- qiskit/qpy/interface.py | 6 ++++++ ...-message-qpy-version-cf0763da22ce2224.yaml | 9 +++++++++ test/python/qpy/test_circuit_load_from_qpy.py | 19 ++++++++++++++++++- 3 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/fix-error-message-qpy-version-cf0763da22ce2224.yaml diff --git a/qiskit/qpy/interface.py b/qiskit/qpy/interface.py index 9886160dacc0..f4a77ec14598 100644 --- a/qiskit/qpy/interface.py +++ b/qiskit/qpy/interface.py @@ -231,6 +231,12 @@ def load( version = struct.unpack("!6sB", file_obj.read(7))[1] file_obj.seek(0) + if version > common.QPY_VERSION: + raise QiskitError( + f"The QPY format version being read, {version}, isn't supported by " + "this Qiskit version. Please upgrade your version of Qiskit to load this QPY payload" + ) + if version < 10: data = formats.FILE_HEADER._make( struct.unpack( diff --git a/releasenotes/notes/fix-error-message-qpy-version-cf0763da22ce2224.yaml b/releasenotes/notes/fix-error-message-qpy-version-cf0763da22ce2224.yaml new file mode 100644 index 000000000000..67690c011126 --- /dev/null +++ b/releasenotes/notes/fix-error-message-qpy-version-cf0763da22ce2224.yaml @@ -0,0 +1,9 @@ +--- +fixes: + - | + Fixed an issue with :func:`.qpy.load` when attempting to load a QPY format + version that is not supported by this version of Qiskit it will now display + a descriptive error message. Previously, it would raise an internal error + because of the incompatibility between the formats which was difficult to + debug. If the QPY format verison is not supported that indicates the Qiskit + version will need to be upgraded to read the QPY payload. diff --git a/test/python/qpy/test_circuit_load_from_qpy.py b/test/python/qpy/test_circuit_load_from_qpy.py index 3684e8aacfe6..ba6aeb81f290 100644 --- a/test/python/qpy/test_circuit_load_from_qpy.py +++ b/test/python/qpy/test_circuit_load_from_qpy.py @@ -13,12 +13,15 @@ """Test cases for the schedule block qpy loading and saving.""" import io +import struct from ddt import ddt, data from qiskit.circuit import QuantumCircuit, QuantumRegister, Qubit from qiskit.providers.fake_provider import FakeHanoi, FakeSherbrooke -from qiskit.qpy import dump, load +from qiskit.exceptions import QiskitError +from qiskit.qpy import dump, load, formats +from qiskit.qpy.common import QPY_VERSION from qiskit.test import QiskitTestCase from qiskit.transpiler import PassManager, TranspileLayout from qiskit.transpiler import passes @@ -71,6 +74,20 @@ def test_rzx_calibration_echo(self, angle): self.assert_roundtrip_equal(rzx_qc) +class TestVersions(QpyCircuitTestCase): + """Test version handling in qpy.""" + + def test_invalid_qpy_version(self): + """Test a descriptive exception is raised if QPY version is too new.""" + with io.BytesIO() as buf: + buf.write( + struct.pack(formats.FILE_HEADER_PACK, b"QISKIT", QPY_VERSION + 4, 42, 42, 1, 2) + ) + buf.seek(0) + with self.assertRaisesRegex(QiskitError, str(QPY_VERSION + 4)): + load(buf) + + @ddt class TestLayout(QpyCircuitTestCase): """Test circuit serialization for layout preservation."""