Skip to content

Commit 6d7cb00

Browse files
committed
minimal Python error handling
1 parent 73adace commit 6d7cb00

File tree

6 files changed

+67
-27
lines changed

6 files changed

+67
-27
lines changed

python/DEVELOP.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
```
22
uv sync --no-install-package async-tiff
3-
uv run --no-project maturin develop
3+
uv run --no-project maturin develop --uv
44
uv run --no-project mkdocs serve
5-
uv run --no-project pytest --verbose
5+
uv run --no-project pytest tests --verbose
66
```

python/pyproject.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,12 @@ dev-dependencies = [
3232
"mkdocstrings>=0.27.0",
3333
"numpy>=1",
3434
"obstore>=0.5.1",
35-
"pip>=24.2",
3635
"pytest-asyncio>=0.26.0",
3736
"pytest>=8.3.3",
3837
"ruff>=0.8.4",
3938
]
4039

4140
[tool.pytest.ini_options]
4241
addopts = "--color=yes"
43-
asyncio_default_fixture_loop_scope="function"
42+
asyncio_default_fixture_loop_scope = "function"
4443
asyncio_mode = "auto"

python/src/error.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
use async_tiff::error::AsyncTiffError;
2+
use pyo3::create_exception;
3+
use pyo3::exceptions::PyFileNotFoundError;
4+
use pyo3::prelude::*;
5+
6+
// Base exception
7+
// Note that this is named `BaseError` instead of `ObstoreError` to not leak the name "obstore" to
8+
// other Rust-Python libraries using pyo3-object_store.
9+
create_exception!(
10+
async_tiff,
11+
AsyncTiffException,
12+
pyo3::exceptions::PyException,
13+
"A general error from the underlying Rust async-tiff library."
14+
);
15+
16+
#[allow(missing_docs)]
17+
pub enum PyAsyncTiffError {
18+
AsyncTiffError(async_tiff::error::AsyncTiffError),
19+
}
20+
21+
impl From<AsyncTiffError> for PyAsyncTiffError {
22+
fn from(value: AsyncTiffError) -> Self {
23+
Self::AsyncTiffError(value)
24+
}
25+
}
26+
27+
impl From<PyAsyncTiffError> for PyErr {
28+
fn from(error: PyAsyncTiffError) -> Self {
29+
match error {
30+
PyAsyncTiffError::AsyncTiffError(err) => match err {
31+
AsyncTiffError::ObjectStore(err) => match err {
32+
object_store::Error::NotFound { path: _, source: _ } => {
33+
PyFileNotFoundError::new_err(err.to_string())
34+
}
35+
_ => AsyncTiffException::new_err(err.to_string()),
36+
},
37+
_ => AsyncTiffException::new_err(err.to_string()),
38+
},
39+
}
40+
}
41+
}
42+
43+
pub type PyAsyncTiffResult<T> = std::result::Result<T, PyAsyncTiffError>;

python/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
mod decoder;
44
mod enums;
5+
mod error;
56
mod geo;
67
mod ifd;
78
mod reader;

python/src/tiff.rs

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use pyo3::prelude::*;
99
use pyo3::types::PyType;
1010
use pyo3_async_runtimes::tokio::future_into_py;
1111

12+
use crate::error::PyAsyncTiffResult;
1213
use crate::reader::StoreInput;
1314
use crate::tile::PyTile;
1415
use crate::PyImageFileDirectory;
@@ -19,6 +20,20 @@ pub(crate) struct PyTIFF {
1920
reader: Arc<dyn AsyncFileReader>,
2021
}
2122

23+
async fn open(
24+
reader: Arc<dyn AsyncFileReader>,
25+
prefetch: u64,
26+
multiplier: f64,
27+
) -> PyAsyncTiffResult<PyTIFF> {
28+
let metadata_fetch = ReadaheadMetadataCache::new(reader.clone())
29+
.with_initial_size(prefetch)
30+
.with_multiplier(multiplier);
31+
let mut metadata_reader = TiffMetadataReader::try_open(&metadata_fetch).await?;
32+
let ifds = metadata_reader.read_all_ifds(&metadata_fetch).await?;
33+
let tiff = TIFF::new(ifds);
34+
Ok(PyTIFF { tiff, reader })
35+
}
36+
2237
#[pymethods]
2338
impl PyTIFF {
2439
#[classmethod]
@@ -33,18 +48,11 @@ impl PyTIFF {
3348
) -> PyResult<Bound<'py, PyAny>> {
3449
let reader = store.into_async_file_reader(path);
3550

36-
let cog_reader = future_into_py(py, async move {
37-
let metadata_fetch = ReadaheadMetadataCache::new(reader.clone())
38-
.with_initial_size(prefetch)
39-
.with_multiplier(multiplier);
40-
let mut metadata_reader = TiffMetadataReader::try_open(&metadata_fetch).await.unwrap();
41-
let ifds = metadata_reader
42-
.read_all_ifds(&metadata_fetch)
43-
.await
44-
.unwrap();
45-
let tiff = TIFF::new(ifds);
46-
Ok(PyTIFF { tiff, reader })
47-
})?;
51+
let cog_reader =
52+
future_into_py(
53+
py,
54+
async move { Ok(open(reader, prefetch, multiplier).await?) },
55+
)?;
4856
Ok(cog_reader)
4957
}
5058

python/uv.lock

Lines changed: 0 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)