From dc61904aeea9fe10cf9c3f67b7a2f0b1ee97d148 Mon Sep 17 00:00:00 2001 From: Jake Lishman Date: Thu, 13 Apr 2023 12:54:25 +0100 Subject: [PATCH] Wrap io::Error in QASM2ParseError on failed file read Previously, if Python thought the target file for `qasm2.load` existed, but Rust was unable to open it, the result `io:Error` would be propagated up to Python space verbatim, and these situations usually have confusing error messages. This could happen, for example, if the target was a directory on Windows. --- crates/qasm2/src/lib.rs | 12 +++++++++++- test/python/qasm2/test_parse_errors.py | 5 +++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/crates/qasm2/src/lib.rs b/crates/qasm2/src/lib.rs index 1ae817364d11..fab26c5b8e93 100644 --- a/crates/qasm2/src/lib.rs +++ b/crates/qasm2/src/lib.rs @@ -13,6 +13,8 @@ use pyo3::prelude::*; use pyo3::Python; +use crate::error::QASM2ParseError; + mod bytecode; mod error; mod expr; @@ -94,6 +96,7 @@ fn bytecode_from_string( /// without loading the entire token and parse tree into memory at once. #[pyfunction] fn bytecode_from_file( + py: Python<'_>, path: std::ffi::OsString, include_path: Vec, custom_instructions: Vec, @@ -101,7 +104,14 @@ fn bytecode_from_file( strict: bool, ) -> PyResult { bytecode::BytecodeIterator::new( - lex::TokenStream::from_path(path, strict)?, + lex::TokenStream::from_path(&path, strict).map_err(|err| { + let exc = QASM2ParseError::new_err(format!( + "failed to read a token stream from file '{}'", + path.to_string_lossy() + )); + exc.set_cause(py, Some(err.into())); + exc + })?, include_path, &custom_instructions, &custom_classical, diff --git a/test/python/qasm2/test_parse_errors.py b/test/python/qasm2/test_parse_errors.py index 801559aac7a1..1cc90cdaef3c 100644 --- a/test/python/qasm2/test_parse_errors.py +++ b/test/python/qasm2/test_parse_errors.py @@ -220,6 +220,11 @@ def test_eof(self, statement): with self.assertRaisesRegex(qiskit.qasm2.QASM2ParseError, "unexpected end-of-file"): qiskit.qasm2.loads(full) + def test_loading_directory(self): + """Test that the correct error is raised when a file fails to open.""" + with self.assertRaisesRegex(qiskit.qasm2.QASM2ParseError, "failed to read"): + qiskit.qasm2.load(".") + class TestVersion(QiskitTestCase): def test_invalid_version(self):