commit fb1832feb868a81e2d10a1707c57df4623304313
parent 0d5c8b8cfa1025c29b96db3c52ff54a7d204923f
Author: Árni Dagur <arni@dagur.eu>
Date: Sat, 26 Jun 2021 16:20:29 +0000
Create a custom exception type for this library
Diffstat:
7 files changed, 113 insertions(+), 8 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
@@ -5,6 +5,9 @@ This project adheres to [Semantic Versioning](http://semver.org/) and [Keep a Ch
## Unreleased
---
+### Breaks
+* Library now throws the custom `adblock.AdblockException` exception, instead of `ValueError`.
+
## 0.4.4 - (2021-04-13)
---
diff --git a/adblock/__init__.py b/adblock/__init__.py
@@ -1,4 +1,29 @@
-from adblock.adblock import __version__, Engine, FilterSet, BlockerResult, UrlSpecificResources
+from adblock.adblock import (
+ __version__,
+ Engine,
+ FilterSet,
+ BlockerResult,
+ UrlSpecificResources,
+ AdblockException,
+ BlockerException,
+ SerializationError,
+ DeserializationError,
+ OptimizedFilterExistence,
+ BadFilterAddUnsupported,
+ FilterExists,
+)
-__all__ = ("Engine", "FilterSet", "BlockerResult", "UrlSpecificResources")
+__all__ = (
+ "Engine",
+ "FilterSet",
+ "BlockerResult",
+ "UrlSpecificResources",
+ "AdblockException",
+ "BlockerException",
+ "SerializationError",
+ "DeserializationError",
+ "OptimizedFilterExistence",
+ "BadFilterAddUnsupported",
+ "FilterExists",
+)
diff --git a/adblock/adblock.pyi b/adblock/adblock.pyi
@@ -1,5 +1,26 @@
from typing import Optional, Dict, List, Set
+class AdblockException(Exception):
+ pass
+
+class BlockerException(AdblockException):
+ pass
+
+class SerializationError(BlockerException):
+ pass
+
+class DeserializationError(BlockerException):
+ pass
+
+class OptimizedFilterExistence(BlockerException):
+ pass
+
+class BadFilterAddUnsupported(BlockerException):
+ pass
+
+class FilterExists(BlockerException):
+ pass
+
class BlockerResult:
matched: bool
explicit_cancel: bool
diff --git a/src/lib.rs b/src/lib.rs
@@ -17,7 +17,8 @@ use adblock::engine::Engine as RustEngine;
use adblock::lists::FilterFormat;
use adblock::lists::FilterSet as RustFilterSet;
use pyo3::class::PyObjectProtocol;
-use pyo3::exceptions::PyValueError;
+use pyo3::create_exception;
+use pyo3::exceptions::PyException;
use pyo3::prelude::*;
use pyo3::types::PyBytes;
use pyo3::PyErr;
@@ -31,12 +32,28 @@ use std::io::{Read, Write};
/// Brave's adblocking library in Python!
#[pymodule]
-fn adblock(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
+fn adblock(py: Python<'_>, m: &PyModule) -> PyResult<()> {
m.add("__version__", env!("CARGO_PKG_VERSION"))?;
m.add_class::<Engine>()?;
m.add_class::<FilterSet>()?;
m.add_class::<BlockerResult>()?;
m.add_class::<UrlSpecificResources>()?;
+ m.add("AdblockException", py.get_type::<AdblockException>())?;
+ m.add("BlockerException", py.get_type::<BlockerException>())?;
+ m.add("SerializationError", py.get_type::<SerializationError>())?;
+ m.add(
+ "DeserializationError",
+ py.get_type::<DeserializationError>(),
+ )?;
+ m.add(
+ "OptimizedFilterExistence",
+ py.get_type::<OptimizedFilterExistence>(),
+ )?;
+ m.add(
+ "BadFilterAddUnsupported",
+ py.get_type::<BadFilterAddUnsupported>(),
+ )?;
+ m.add("FilterExists", py.get_type::<FilterExists>())?;
Ok(())
}
@@ -141,9 +158,24 @@ impl Display for BlockerError {
}
}
+create_exception!(adblock, AdblockException, PyException);
+create_exception!(adblock, BlockerException, AdblockException);
+create_exception!(adblock, SerializationError, BlockerException);
+create_exception!(adblock, DeserializationError, BlockerException);
+create_exception!(adblock, OptimizedFilterExistence, BlockerException);
+create_exception!(adblock, BadFilterAddUnsupported, BlockerException);
+create_exception!(adblock, FilterExists, BlockerException);
+
impl Into<PyErr> for BlockerError {
fn into(self) -> PyErr {
- PyErr::new::<PyValueError, _>(format!("{:?}", self))
+ let msg = format!("{:?}", self);
+ match self {
+ Self::SerializationError => PyErr::new::<SerializationError, _>(msg),
+ Self::DeserializationError => PyErr::new::<DeserializationError, _>(msg),
+ Self::OptimizedFilterExistence => PyErr::new::<OptimizedFilterExistence, _>(msg),
+ Self::BadFilterAddUnsupported => PyErr::new::<BadFilterAddUnsupported, _>(msg),
+ Self::FilterExists => PyErr::new::<FilterExists, _>(msg),
+ }
}
}
@@ -163,7 +195,7 @@ fn filter_format_from_string(filter_format: &str) -> PyResult<FilterFormat> {
match filter_format {
"standard" => Ok(FilterFormat::Standard),
"hosts" => Ok(FilterFormat::Hosts),
- _ => Err(PyErr::new::<PyValueError, _>("Invalid format value")),
+ _ => Err(PyErr::new::<AdblockException, _>("Invalid format value")),
}
}
diff --git a/tests/test_engine.py b/tests/test_engine.py
@@ -52,6 +52,17 @@ def test_serde_file(tmpdir):
assert deserialization_result is None
+def test_deserialize_corrupt(tmpdir):
+ path = str(tmpdir / "corrupt_cache.dat")
+ with open(path, "w", encoding="utf-8") as f:
+ f.write("abc")
+
+ engine = empty_engine()
+ with pytest.raises(adblock.DeserializationError):
+ engine.deserialize_from_file(path)
+ with pytest.raises(adblock.DeserializationError):
+ engine.deserialize(b"abc")
+
def test_serde():
engine = empty_engine()
serialization_result = engine.serialize()
diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py
@@ -0,0 +1,10 @@
+import adblock
+
+def test_correct_baseclasses():
+ assert issubclass(adblock.AdblockException, Exception)
+ assert issubclass(adblock.BlockerException, adblock.AdblockException)
+ assert issubclass(adblock.SerializationError, adblock.BlockerException)
+ assert issubclass(adblock.DeserializationError, adblock.BlockerException)
+ assert issubclass(adblock.OptimizedFilterExistence, adblock.BlockerException)
+ assert issubclass(adblock.BadFilterAddUnsupported, adblock.BlockerException)
+ assert issubclass(adblock.FilterExists, adblock.BlockerException)
+\ No newline at end of file
diff --git a/tests/test_imports.py b/tests/test_imports.py
@@ -14,16 +14,18 @@ def get_added_classes():
match = re.match(r"m\.add_class::<(.+)>\(\)\?;", line.strip())
if match is not None:
classes.append(match.group(1))
+ continue
return classes
def test_added_classes():
"""
Make sure that there's no class that we added in Rust but didn't import in
- `__init__.py` and vice versa.
+ `__init__.py`.
"""
added_classes = get_added_classes()
- assert added_classes == list(adblock.__all__)
+ for c in added_classes:
+ assert c in adblock.__all__
def test_dunder_all_classes_imported():