Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implemented custom errors #186

Merged
merged 7 commits into from
Feb 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,31 @@ jobs:
command: build
args: "-o dist --interpreter python${{ matrix.python-version }}"
target: ${{ steps.target.outputs.target }}

check-docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Set up rust toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
- run: |
git config user.name github-actions
git config user.email [email protected]

# venv required by maturin
python3 -m venv .venv
source .venv/bin/activate

make install-test-requirements
make install-doc-requirements
# Required for pdoc to be able to import the sources
make dev-install
make doc
8 changes: 0 additions & 8 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,9 @@ name = "fastexcel"
crate-type = ["cdylib"]

[dependencies]
anyhow = "1.0.80"
calamine = { version = "0.24.0", features = ["dates"] }
chrono = { version = "0.4.34", default-features = false }
pyo3 = { version = "0.20.3", features = ["extension-module", "anyhow", "abi3-py38"] }
pyo3 = { version = "0.20.3", features = ["extension-module", "abi3-py38"] }

[dependencies.arrow]
version = "50.0.0"
Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ format:
$(ruff) --fix
$(format)
$(fmt)
$(clippy) --fix --lib -p fastexcel --allow-dirty --allow-staged

install-test-requirements:
pip install -U -r test-requirements.txt -r build-requirements.txt
Expand Down
29 changes: 27 additions & 2 deletions python/fastexcel/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,19 @@

import pyarrow as pa

from ._fastexcel import __version__, _ExcelReader, _ExcelSheet
from ._fastexcel import (
ArrowError,
CalamineCellError,
CalamineError,
PrettyWood marked this conversation as resolved.
Show resolved Hide resolved
CannotRetrieveCellDataError,
FastExcelError,
InvalidParametersError,
SheetNotFoundError,
UnsupportedColumnTypeCombinationError,
__version__,
_ExcelReader,
_ExcelSheet,
)
from ._fastexcel import read_excel as _read_excel


Expand Down Expand Up @@ -202,4 +214,17 @@ def read_excel(path: Path | str) -> ExcelReader:
return ExcelReader(_read_excel(expanduser(path)))


__all__ = ("ExcelReader", "ExcelSheet", "read_excel", "__version__")
__all__ = (
"__version__",
"read_excel",
"ExcelReader",
"ExcelSheet",
"FastExcelError",
"CannotRetrieveCellDataError",
"CalamineCellError",
"CalamineError",
"SheetNotFoundError",
"ArrowError",
"InvalidParametersError",
"UnsupportedColumnTypeCombinationError",
)
10 changes: 10 additions & 0 deletions python/fastexcel/_fastexcel.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,13 @@ def read_excel(path: str) -> _ExcelReader:
"""Reads an excel file and returns an ExcelReader"""

__version__: str

# Exceptions
class FastExcelError(Exception): ...
class UnsupportedColumnTypeCombinationError(FastExcelError): ...
class CannotRetrieveCellDataError(FastExcelError): ...
class CalamineCellError(FastExcelError): ...
class CalamineError(FastExcelError): ...
class SheetNotFoundError(FastExcelError): ...
class ArrowError(FastExcelError): ...
class InvalidParametersError(FastExcelError): ...
60 changes: 60 additions & 0 deletions python/tests/test_errors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
from __future__ import annotations

import fastexcel
import pytest
from utils import path_for_fixture


def test_does_not_exist() -> None:
expected_message = """calamine error: Cannot detect file format
Context:
0: Could not open workbook at path_does_not_exist.nope
1: could not load excel file at path_does_not_exist.nope"""

with pytest.raises(fastexcel.CalamineError, match=expected_message) as exc_info:
fastexcel.read_excel("path_does_not_exist.nope")

assert exc_info.value.__doc__ == "Generic calamine error"

# Should also work with the base error type
with pytest.raises(fastexcel.FastExcelError, match=expected_message):
fastexcel.read_excel("path_does_not_exist.nope")


def test_sheet_not_found_error() -> None:
excel_reader = fastexcel.read_excel(path_for_fixture("fixture-single-sheet.xlsx"))
expected_message = """sheet at index 42 not found
Context:
0: Sheet index 42 is out of range. File has 1 sheets"""

with pytest.raises(fastexcel.SheetNotFoundError, match=expected_message) as exc_info:
excel_reader.load_sheet(42)

assert exc_info.value.__doc__ == "Sheet was not found"

# Should also work with the base error type
with pytest.raises(fastexcel.FastExcelError, match=expected_message):
excel_reader.load_sheet(42)


@pytest.mark.parametrize(
"exc_class, expected_docstring",
[
(fastexcel.FastExcelError, "The base class for all fastexcel errors"),
(
fastexcel.UnsupportedColumnTypeCombinationError,
"Column contains an unsupported type combination",
),
(fastexcel.CannotRetrieveCellDataError, "Data for a given cell cannot be retrieved"),
(
fastexcel.CalamineCellError,
"calamine returned an error regarding the content of the cell",
),
(fastexcel.CalamineError, "Generic calamine error"),
(fastexcel.SheetNotFoundError, "Sheet was not found"),
(fastexcel.ArrowError, "Generic arrow error"),
(fastexcel.InvalidParametersError, "Provided parameters are invalid"),
],
)
def test_docstrings(exc_class: type[Exception], expected_docstring: str) -> None:
assert exc_class.__doc__ == expected_docstring
4 changes: 3 additions & 1 deletion python/tests/test_fastexcel.py
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,9 @@ def test_sheet_with_pagination_out_of_bound():
excel_reader = fastexcel.read_excel(path_for_fixture("fixture-single-sheet-with-types.xlsx"))
assert excel_reader.sheet_names == ["Sheet1"]

with pytest.raises(RuntimeError, match="To many rows skipped. Max height is 4"):
with pytest.raises(
fastexcel.InvalidParametersError, match="Too many rows skipped. Max height is 4"
):
excel_reader.load_sheet(
0,
skip_rows=1000000,
Expand Down
Loading
Loading