From 44ba59f7115d4e2493fdb253db3ef99e73867481 Mon Sep 17 00:00:00 2001 From: Dmitriy Date: Sat, 6 Jan 2024 19:10:08 +0600 Subject: [PATCH 1/4] feat: implement more detailed error types --- Cargo.toml | 4 +- python/python_calamine/__init__.py | 8 +++ python/python_calamine/_python_calamine.pyi | 4 ++ src/lib.rs | 8 ++- src/types/mod.rs | 4 ++ src/types/workbook.rs | 5 +- src/utils.rs | 38 ++++++++++++- tests/data/error.ods | 0 tests/data/error.xlsb | 0 tests/data/error.xlsx | 0 tests/data/password.ods | Bin 0 -> 12997 bytes tests/data/password.xls | Bin 0 -> 6656 bytes tests/data/password.xlsb | Bin 0 -> 16384 bytes tests/data/password.xlsx | Bin 0 -> 10752 bytes tests/test_base.py | 58 +++++++++++++++++++- 15 files changed, 121 insertions(+), 8 deletions(-) create mode 100644 tests/data/error.ods create mode 100644 tests/data/error.xlsb create mode 100644 tests/data/error.xlsx create mode 100644 tests/data/password.ods create mode 100644 tests/data/password.xls create mode 100644 tests/data/password.xlsb create mode 100644 tests/data/password.xlsx diff --git a/Cargo.toml b/Cargo.toml index 753eab4..741b2e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,9 @@ name = "python_calamine" crate-type = ["cdylib"] [dependencies] -calamine = { version = "0.23.1", features = ["dates"] } +calamine = { git = "https://github.com/dimastbk/calamine", rev = "a2b6fc63a7c938798b7ed4acc276b41058496deb", features = [ + "dates", +] } pyo3 = { version = "0.19.2", features = [ "extension-module", "chrono", diff --git a/python/python_calamine/__init__.py b/python/python_calamine/__init__.py index b192511..9771394 100644 --- a/python/python_calamine/__init__.py +++ b/python/python_calamine/__init__.py @@ -2,9 +2,13 @@ CalamineError, CalamineSheet, CalamineWorkbook, + PasswordError, SheetMetadata, SheetTypeEnum, SheetVisibleEnum, + WorksheetNotFound, + XmlError, + ZipError, load_workbook, ) @@ -12,8 +16,12 @@ "CalamineError", "CalamineSheet", "CalamineWorkbook", + "PasswordError", "SheetMetadata", "SheetTypeEnum", "SheetVisibleEnum", + "WorksheetNotFound", + "XmlError", + "ZipError", "load_workbook", ) diff --git a/python/python_calamine/_python_calamine.pyi b/python/python_calamine/_python_calamine.pyi index 093d7a6..67037a4 100644 --- a/python/python_calamine/_python_calamine.pyi +++ b/python/python_calamine/_python_calamine.pyi @@ -109,6 +109,10 @@ class CalamineWorkbook: def get_sheet_by_index(self, index: int) -> CalamineSheet: ... class CalamineError(Exception): ... +class PasswordError(CalamineError): ... +class WorksheetNotFound(CalamineError): ... +class XmlError(CalamineError): ... +class ZipError(CalamineError): ... def load_workbook( path_or_filelike: str | os.PathLike | ReadBuffer, diff --git a/src/lib.rs b/src/lib.rs index 8ccceab..9e83bcf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,8 +3,8 @@ use pyo3::prelude::*; mod types; mod utils; use crate::types::{ - CalamineError, CalamineSheet, CalamineWorkbook, CellValue, SheetMetadata, SheetTypeEnum, - SheetVisibleEnum, + CalamineError, CalamineSheet, CalamineWorkbook, CellValue, PasswordError, SheetMetadata, + SheetTypeEnum, SheetVisibleEnum, WorksheetNotFound, XmlError, ZipError, }; #[pyfunction] @@ -21,5 +21,9 @@ fn _python_calamine(py: Python, m: &PyModule) -> PyResult<()> { m.add_class::()?; m.add_class::()?; m.add("CalamineError", py.get_type::())?; + m.add("PasswordError", py.get_type::())?; + m.add("WorksheetNotFound", py.get_type::())?; + m.add("XmlError", py.get_type::())?; + m.add("ZipError", py.get_type::())?; Ok(()) } diff --git a/src/types/mod.rs b/src/types/mod.rs index 1c202d3..dc66572 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -9,3 +9,7 @@ pub use sheet::{CalamineSheet, SheetMetadata, SheetTypeEnum, SheetVisibleEnum}; pub use workbook::CalamineWorkbook; create_exception!(python_calamine, CalamineError, PyException); +create_exception!(python_calamine, PasswordError, CalamineError); +create_exception!(python_calamine, WorksheetNotFound, CalamineError); +create_exception!(python_calamine, XmlError, CalamineError); +create_exception!(python_calamine, ZipError, CalamineError); diff --git a/src/types/workbook.rs b/src/types/workbook.rs index e55711e..c8de77a 100644 --- a/src/types/workbook.rs +++ b/src/types/workbook.rs @@ -8,8 +8,9 @@ use pyo3::prelude::*; use pyo3::types::{PyString, PyType}; use pyo3_file::PyFileLikeObject; +use super::WorksheetNotFound; use crate::utils::err_to_py; -use crate::{CalamineError, CalamineSheet, SheetMetadata}; +use crate::{CalamineSheet, SheetMetadata}; enum SheetsEnum { File(Sheets>), @@ -167,7 +168,7 @@ impl CalamineWorkbook { let name = self .sheet_names .get(index) - .ok_or_else(|| CalamineError::new_err("Workbook is empty"))? + .ok_or_else(|| WorksheetNotFound::new_err(format!("Worksheet '{}' not found", index)))? .to_string(); let range = self .sheets diff --git a/src/utils.rs b/src/utils.rs index 5c00b1f..c4e16a2 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,12 +1,46 @@ -use calamine::Error; +use calamine::{Error, OdsError, XlsError, XlsbError, XlsxError}; use pyo3::exceptions::PyIOError; use pyo3::PyErr; -use crate::types::CalamineError; +use crate::types::{CalamineError, PasswordError, WorksheetNotFound, XmlError, ZipError}; pub fn err_to_py(e: Error) -> PyErr { match e { Error::Io(err) => PyIOError::new_err(err.to_string()), + Error::Ods(ref err) => match err { + OdsError::Io(error) => PyIOError::new_err(error.to_string()), + OdsError::Zip(error) => ZipError::new_err(error.to_string()), + OdsError::Xml(error) => XmlError::new_err(error.to_string()), + OdsError::XmlAttr(error) => XmlError::new_err(error.to_string()), + OdsError::Password => PasswordError::new_err(err.to_string()), + OdsError::WorksheetNotFound(error) => WorksheetNotFound::new_err(error.to_string()), + _ => CalamineError::new_err(err.to_string()), + }, + Error::Xls(ref err) => match err { + XlsError::Io(error) => PyIOError::new_err(error.to_string()), + XlsError::Password => PasswordError::new_err(err.to_string()), + XlsError::WorksheetNotFound(error) => WorksheetNotFound::new_err(error.to_string()), + _ => CalamineError::new_err(err.to_string()), + }, + Error::Xlsx(ref err) => match err { + XlsxError::Io(error) => PyIOError::new_err(error.to_string()), + XlsxError::Zip(error) => ZipError::new_err(error.to_string()), + XlsxError::Xml(error) => XmlError::new_err(error.to_string()), + XlsxError::XmlAttr(error) => XmlError::new_err(error.to_string()), + XlsxError::XmlEof(error) => XmlError::new_err(error.to_string()), + XlsxError::Password => PasswordError::new_err(err.to_string()), + XlsxError::WorksheetNotFound(error) => WorksheetNotFound::new_err(error.to_string()), + _ => CalamineError::new_err(err.to_string()), + }, + Error::Xlsb(ref err) => match err { + XlsbError::Io(error) => PyIOError::new_err(error.to_string()), + XlsbError::Zip(error) => ZipError::new_err(error.to_string()), + XlsbError::Xml(error) => XmlError::new_err(error.to_string()), + XlsbError::XmlAttr(error) => XmlError::new_err(error.to_string()), + XlsbError::Password => PasswordError::new_err(err.to_string()), + XlsbError::WorksheetNotFound(error) => WorksheetNotFound::new_err(error.to_string()), + _ => CalamineError::new_err(err.to_string()), + }, _ => CalamineError::new_err(e.to_string()), } } diff --git a/tests/data/error.ods b/tests/data/error.ods new file mode 100644 index 0000000..e69de29 diff --git a/tests/data/error.xlsb b/tests/data/error.xlsb new file mode 100644 index 0000000..e69de29 diff --git a/tests/data/error.xlsx b/tests/data/error.xlsx new file mode 100644 index 0000000..e69de29 diff --git a/tests/data/password.ods b/tests/data/password.ods new file mode 100644 index 0000000000000000000000000000000000000000..f5517ea64184aa2232c4a3491ebe791fc479c8b8 GIT binary patch literal 12997 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 z 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 literal 0 HcmV?d00001 diff --git a/tests/data/password.xlsx b/tests/data/password.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..98071367942d0f42c562ed37a838c96766314d40 GIT binary patch literal 10752 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 GpZ^0>V~KMB literal 0 HcmV?d00001 diff --git a/tests/test_base.py b/tests/test_base.py index 1b1c1ec..31ac84d 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -3,7 +3,7 @@ from pathlib import Path import pytest -from python_calamine import CalamineWorkbook +from python_calamine import CalamineWorkbook, PasswordError, WorksheetNotFound, ZipError PATH = Path(__file__).parent / "data" @@ -218,6 +218,62 @@ def test_nrows(): ] +@pytest.mark.parametrize( + "path", + [ + PATH / "base.xlsx", + PATH / "base.xls", + PATH / "base.xlsb", + PATH / "base.ods", + ], +) +def test_worksheet_errors(path): + reader = CalamineWorkbook.from_object(path) + with pytest.raises(WorksheetNotFound): + reader.get_sheet_by_name("Sheet4") + + +@pytest.mark.parametrize( + "path", + [ + PATH / "password.xlsx", + PATH / "password.xls", + PATH / "password.xlsb", + PATH / "password.ods", + ], +) +def test_password_errors(path): + with pytest.raises(PasswordError): + CalamineWorkbook.from_object(path) + + +@pytest.mark.parametrize( + "path", + [ + PATH / "error.xlsx", + PATH / "error.xlsb", + PATH / "error.ods", + ], +) +def test_zip_errors(path): + with pytest.raises(ZipError): + CalamineWorkbook.from_path(path) + + +@pytest.mark.parametrize( + "path", + [ + PATH / "base1.xlsx", + PATH / "base1.xls", + PATH / "base1.xlsb", + PATH / "base1.ods", + ], +) +def test_io_errors(path): + with pytest.raises(IOError): + CalamineWorkbook.from_path(path) + + @pytest.mark.parametrize( "path", [ From bf6948db5cf4f9a309d237fcb06c703f9548587f Mon Sep 17 00:00:00 2001 From: Dmitriy Date: Sat, 20 Jan 2024 21:12:27 +0600 Subject: [PATCH 2/4] rename files --- tests/data/{error.ods => empty_file.ods} | 0 tests/data/{error.xlsb => empty_file.xlsb} | 0 tests/data/{error.xlsx => empty_file.xlsx} | 0 tests/test_base.py | 14 +++++++------- 4 files changed, 7 insertions(+), 7 deletions(-) rename tests/data/{error.ods => empty_file.ods} (100%) rename tests/data/{error.xlsb => empty_file.xlsb} (100%) rename tests/data/{error.xlsx => empty_file.xlsx} (100%) diff --git a/tests/data/error.ods b/tests/data/empty_file.ods similarity index 100% rename from tests/data/error.ods rename to tests/data/empty_file.ods diff --git a/tests/data/error.xlsb b/tests/data/empty_file.xlsb similarity index 100% rename from tests/data/error.xlsb rename to tests/data/empty_file.xlsb diff --git a/tests/data/error.xlsx b/tests/data/empty_file.xlsx similarity index 100% rename from tests/data/error.xlsx rename to tests/data/empty_file.xlsx diff --git a/tests/test_base.py b/tests/test_base.py index 31ac84d..0b239ef 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -250,9 +250,9 @@ def test_password_errors(path): @pytest.mark.parametrize( "path", [ - PATH / "error.xlsx", - PATH / "error.xlsb", - PATH / "error.ods", + PATH / "empty_file.xlsx", + PATH / "empty_file.xlsb", + PATH / "empty_file.ods", ], ) def test_zip_errors(path): @@ -263,10 +263,10 @@ def test_zip_errors(path): @pytest.mark.parametrize( "path", [ - PATH / "base1.xlsx", - PATH / "base1.xls", - PATH / "base1.xlsb", - PATH / "base1.ods", + PATH / "non_existent_file.xlsx", + PATH / "non_existent_file.xls", + PATH / "non_existent_file.xlsb", + PATH / "non_existent_file.ods", ], ) def test_io_errors(path): From 11cef31ebcbceb8a04d8c990dcaa2730cf534830 Mon Sep 17 00:00:00 2001 From: Dmitriy Date: Sat, 20 Jan 2024 21:25:35 +0600 Subject: [PATCH 3/4] clean code --- src/types/workbook.rs | 3 +-- src/utils.rs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/types/workbook.rs b/src/types/workbook.rs index c34721f..17c8d02 100644 --- a/src/types/workbook.rs +++ b/src/types/workbook.rs @@ -8,9 +8,8 @@ use pyo3::prelude::*; use pyo3::types::{PyString, PyType}; use pyo3_file::PyFileLikeObject; -use super::WorksheetNotFound; use crate::utils::err_to_py; -use crate::{CalamineSheet, SheetMetadata}; +use crate::{CalamineSheet, SheetMetadata, WorksheetNotFound}; enum SheetsEnum { File(Sheets>), diff --git a/src/utils.rs b/src/utils.rs index c4e16a2..f79bd0a 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -2,7 +2,7 @@ use calamine::{Error, OdsError, XlsError, XlsbError, XlsxError}; use pyo3::exceptions::PyIOError; use pyo3::PyErr; -use crate::types::{CalamineError, PasswordError, WorksheetNotFound, XmlError, ZipError}; +use crate::{CalamineError, PasswordError, WorksheetNotFound, XmlError, ZipError}; pub fn err_to_py(e: Error) -> PyErr { match e { From dbc853f712266da4dbf1d8ae4b548a54e9ede6f2 Mon Sep 17 00:00:00 2001 From: Dmitriy Date: Sat, 20 Jan 2024 21:26:03 +0600 Subject: [PATCH 4/4] remove worksheet_range_at --- src/types/workbook.rs | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/src/types/workbook.rs b/src/types/workbook.rs index 17c8d02..36c0d1d 100644 --- a/src/types/workbook.rs +++ b/src/types/workbook.rs @@ -40,16 +40,6 @@ impl SheetsEnum { SheetsEnum::FileLike(f) => f.worksheet_range(name), } } - - fn worksheet_range_at( - &mut self, - index: usize, - ) -> Option, Error>> { - match self { - SheetsEnum::File(f) => f.worksheet_range_at(index), - SheetsEnum::FileLike(f) => f.worksheet_range_at(index), - } - } } #[pyclass] @@ -166,12 +156,6 @@ impl CalamineWorkbook { .get(index) .ok_or_else(|| WorksheetNotFound::new_err(format!("Worksheet '{}' not found", index)))? .to_string(); - let range = self - .sheets - .worksheet_range_at(index) - .unwrap_or_else(|| Err(Error::Msg("Workbook is empty"))) - .map_err(err_to_py)?; - - Ok(CalamineSheet::new(name, range)) + self.get_sheet_by_name(&name) } }