diff options
| author | Árni Dagur <arni@dagur.eu> | 2021-06-26 16:20:29 +0000 |
|---|---|---|
| committer | Árni Dagur <arni@dagur.eu> | 2021-06-26 19:10:50 +0000 |
| commit | fb1832feb868a81e2d10a1707c57df4623304313 (patch) | |
| tree | f77f31d340f19b0e4d96a92ccc23cdeb6b135738 | |
| parent | 0d5c8b8cfa1025c29b96db3c52ff54a7d204923f (diff) | |
Create a custom exception type for this library
| -rw-r--r-- | CHANGELOG.md | 3 | ||||
| -rw-r--r-- | adblock/__init__.py | 29 | ||||
| -rw-r--r-- | adblock/adblock.pyi | 21 | ||||
| -rw-r--r-- | src/lib.rs | 40 | ||||
| -rw-r--r-- | tests/test_engine.py | 11 | ||||
| -rw-r--r-- | tests/test_exceptions.py | 10 | ||||
| -rw-r--r-- | tests/test_imports.py | 6 |
7 files changed, 112 insertions, 8 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 245a88a..a62516d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md | |||
| @@ -5,6 +5,9 @@ This project adheres to [Semantic Versioning](http://semver.org/) and [Keep a Ch | |||
| 5 | 5 | ||
| 6 | ## Unreleased | 6 | ## Unreleased |
| 7 | --- | 7 | --- |
| 8 | ### Breaks | ||
| 9 | * Library now throws the custom `adblock.AdblockException` exception, instead of `ValueError`. | ||
| 10 | |||
| 8 | 11 | ||
| 9 | ## 0.4.4 - (2021-04-13) | 12 | ## 0.4.4 - (2021-04-13) |
| 10 | --- | 13 | --- |
diff --git a/adblock/__init__.py b/adblock/__init__.py index 0cc1876..85d3614 100644 --- a/adblock/__init__.py +++ b/adblock/__init__.py | |||
| @@ -1,4 +1,29 @@ | |||
| 1 | from adblock.adblock import __version__, Engine, FilterSet, BlockerResult, UrlSpecificResources | 1 | from adblock.adblock import ( |
| 2 | __version__, | ||
| 3 | Engine, | ||
| 4 | FilterSet, | ||
| 5 | BlockerResult, | ||
| 6 | UrlSpecificResources, | ||
| 7 | AdblockException, | ||
| 8 | BlockerException, | ||
| 9 | SerializationError, | ||
| 10 | DeserializationError, | ||
| 11 | OptimizedFilterExistence, | ||
| 12 | BadFilterAddUnsupported, | ||
| 13 | FilterExists, | ||
| 14 | ) | ||
| 2 | 15 | ||
| 3 | 16 | ||
| 4 | __all__ = ("Engine", "FilterSet", "BlockerResult", "UrlSpecificResources") | 17 | __all__ = ( |
| 18 | "Engine", | ||
| 19 | "FilterSet", | ||
| 20 | "BlockerResult", | ||
| 21 | "UrlSpecificResources", | ||
| 22 | "AdblockException", | ||
| 23 | "BlockerException", | ||
| 24 | "SerializationError", | ||
| 25 | "DeserializationError", | ||
| 26 | "OptimizedFilterExistence", | ||
| 27 | "BadFilterAddUnsupported", | ||
| 28 | "FilterExists", | ||
| 29 | ) | ||
diff --git a/adblock/adblock.pyi b/adblock/adblock.pyi index d977629..0515674 100644 --- a/adblock/adblock.pyi +++ b/adblock/adblock.pyi | |||
| @@ -1,5 +1,26 @@ | |||
| 1 | from typing import Optional, Dict, List, Set | 1 | from typing import Optional, Dict, List, Set |
| 2 | 2 | ||
| 3 | class AdblockException(Exception): | ||
| 4 | pass | ||
| 5 | |||
| 6 | class BlockerException(AdblockException): | ||
| 7 | pass | ||
| 8 | |||
| 9 | class SerializationError(BlockerException): | ||
| 10 | pass | ||
| 11 | |||
| 12 | class DeserializationError(BlockerException): | ||
| 13 | pass | ||
| 14 | |||
| 15 | class OptimizedFilterExistence(BlockerException): | ||
| 16 | pass | ||
| 17 | |||
| 18 | class BadFilterAddUnsupported(BlockerException): | ||
| 19 | pass | ||
| 20 | |||
| 21 | class FilterExists(BlockerException): | ||
| 22 | pass | ||
| 23 | |||
| 3 | class BlockerResult: | 24 | class BlockerResult: |
| 4 | matched: bool | 25 | matched: bool |
| 5 | explicit_cancel: bool | 26 | explicit_cancel: bool |
| @@ -17,7 +17,8 @@ use adblock::engine::Engine as RustEngine; | |||
| 17 | use adblock::lists::FilterFormat; | 17 | use adblock::lists::FilterFormat; |
| 18 | use adblock::lists::FilterSet as RustFilterSet; | 18 | use adblock::lists::FilterSet as RustFilterSet; |
| 19 | use pyo3::class::PyObjectProtocol; | 19 | use pyo3::class::PyObjectProtocol; |
| 20 | use pyo3::exceptions::PyValueError; | 20 | use pyo3::create_exception; |
| 21 | use pyo3::exceptions::PyException; | ||
| 21 | use pyo3::prelude::*; | 22 | use pyo3::prelude::*; |
| 22 | use pyo3::types::PyBytes; | 23 | use pyo3::types::PyBytes; |
| 23 | use pyo3::PyErr; | 24 | use pyo3::PyErr; |
| @@ -31,12 +32,28 @@ use std::io::{Read, Write}; | |||
| 31 | 32 | ||
| 32 | /// Brave's adblocking library in Python! | 33 | /// Brave's adblocking library in Python! |
| 33 | #[pymodule] | 34 | #[pymodule] |
| 34 | fn adblock(_py: Python<'_>, m: &PyModule) -> PyResult<()> { | 35 | fn adblock(py: Python<'_>, m: &PyModule) -> PyResult<()> { |
| 35 | m.add("__version__", env!("CARGO_PKG_VERSION"))?; | 36 | m.add("__version__", env!("CARGO_PKG_VERSION"))?; |
| 36 | m.add_class::<Engine>()?; | 37 | m.add_class::<Engine>()?; |
| 37 | m.add_class::<FilterSet>()?; | 38 | m.add_class::<FilterSet>()?; |
| 38 | m.add_class::<BlockerResult>()?; | 39 | m.add_class::<BlockerResult>()?; |
| 39 | m.add_class::<UrlSpecificResources>()?; | 40 | m.add_class::<UrlSpecificResources>()?; |
| 41 | m.add("AdblockException", py.get_type::<AdblockException>())?; | ||
| 42 | m.add("BlockerException", py.get_type::<BlockerException>())?; | ||
| 43 | m.add("SerializationError", py.get_type::<SerializationError>())?; | ||
| 44 | m.add( | ||
| 45 | "DeserializationError", | ||
| 46 | py.get_type::<DeserializationError>(), | ||
| 47 | )?; | ||
| 48 | m.add( | ||
| 49 | "OptimizedFilterExistence", | ||
| 50 | py.get_type::<OptimizedFilterExistence>(), | ||
| 51 | )?; | ||
| 52 | m.add( | ||
| 53 | "BadFilterAddUnsupported", | ||
| 54 | py.get_type::<BadFilterAddUnsupported>(), | ||
| 55 | )?; | ||
| 56 | m.add("FilterExists", py.get_type::<FilterExists>())?; | ||
| 40 | Ok(()) | 57 | Ok(()) |
| 41 | } | 58 | } |
| 42 | 59 | ||
| @@ -141,9 +158,24 @@ impl Display for BlockerError { | |||
| 141 | } | 158 | } |
| 142 | } | 159 | } |
| 143 | 160 | ||
| 161 | create_exception!(adblock, AdblockException, PyException); | ||
| 162 | create_exception!(adblock, BlockerException, AdblockException); | ||
| 163 | create_exception!(adblock, SerializationError, BlockerException); | ||
| 164 | create_exception!(adblock, DeserializationError, BlockerException); | ||
| 165 | create_exception!(adblock, OptimizedFilterExistence, BlockerException); | ||
| 166 | create_exception!(adblock, BadFilterAddUnsupported, BlockerException); | ||
| 167 | create_exception!(adblock, FilterExists, BlockerException); | ||
| 168 | |||
| 144 | impl Into<PyErr> for BlockerError { | 169 | impl Into<PyErr> for BlockerError { |
| 145 | fn into(self) -> PyErr { | 170 | fn into(self) -> PyErr { |
| 146 | PyErr::new::<PyValueError, _>(format!("{:?}", self)) | 171 | let msg = format!("{:?}", self); |
| 172 | match self { | ||
| 173 | Self::SerializationError => PyErr::new::<SerializationError, _>(msg), | ||
| 174 | Self::DeserializationError => PyErr::new::<DeserializationError, _>(msg), | ||
| 175 | Self::OptimizedFilterExistence => PyErr::new::<OptimizedFilterExistence, _>(msg), | ||
| 176 | Self::BadFilterAddUnsupported => PyErr::new::<BadFilterAddUnsupported, _>(msg), | ||
| 177 | Self::FilterExists => PyErr::new::<FilterExists, _>(msg), | ||
| 178 | } | ||
| 147 | } | 179 | } |
| 148 | } | 180 | } |
| 149 | 181 | ||
| @@ -163,7 +195,7 @@ fn filter_format_from_string(filter_format: &str) -> PyResult<FilterFormat> { | |||
| 163 | match filter_format { | 195 | match filter_format { |
| 164 | "standard" => Ok(FilterFormat::Standard), | 196 | "standard" => Ok(FilterFormat::Standard), |
| 165 | "hosts" => Ok(FilterFormat::Hosts), | 197 | "hosts" => Ok(FilterFormat::Hosts), |
| 166 | _ => Err(PyErr::new::<PyValueError, _>("Invalid format value")), | 198 | _ => Err(PyErr::new::<AdblockException, _>("Invalid format value")), |
| 167 | } | 199 | } |
| 168 | } | 200 | } |
| 169 | 201 | ||
diff --git a/tests/test_engine.py b/tests/test_engine.py index a3d2898..8a7421c 100644 --- a/tests/test_engine.py +++ b/tests/test_engine.py | |||
| @@ -52,6 +52,17 @@ def test_serde_file(tmpdir): | |||
| 52 | assert deserialization_result is None | 52 | assert deserialization_result is None |
| 53 | 53 | ||
| 54 | 54 | ||
| 55 | def test_deserialize_corrupt(tmpdir): | ||
| 56 | path = str(tmpdir / "corrupt_cache.dat") | ||
| 57 | with open(path, "w", encoding="utf-8") as f: | ||
| 58 | f.write("abc") | ||
| 59 | |||
| 60 | engine = empty_engine() | ||
| 61 | with pytest.raises(adblock.DeserializationError): | ||
| 62 | engine.deserialize_from_file(path) | ||
| 63 | with pytest.raises(adblock.DeserializationError): | ||
| 64 | engine.deserialize(b"abc") | ||
| 65 | |||
| 55 | def test_serde(): | 66 | def test_serde(): |
| 56 | engine = empty_engine() | 67 | engine = empty_engine() |
| 57 | serialization_result = engine.serialize() | 68 | serialization_result = engine.serialize() |
diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py new file mode 100644 index 0000000..497b3ef --- /dev/null +++ b/tests/test_exceptions.py | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | import adblock | ||
| 2 | |||
| 3 | def test_correct_baseclasses(): | ||
| 4 | assert issubclass(adblock.AdblockException, Exception) | ||
| 5 | assert issubclass(adblock.BlockerException, adblock.AdblockException) | ||
| 6 | assert issubclass(adblock.SerializationError, adblock.BlockerException) | ||
| 7 | assert issubclass(adblock.DeserializationError, adblock.BlockerException) | ||
| 8 | assert issubclass(adblock.OptimizedFilterExistence, adblock.BlockerException) | ||
| 9 | assert issubclass(adblock.BadFilterAddUnsupported, adblock.BlockerException) | ||
| 10 | assert issubclass(adblock.FilterExists, adblock.BlockerException) \ No newline at end of file | ||
diff --git a/tests/test_imports.py b/tests/test_imports.py index 7692483..09ef5d5 100644 --- a/tests/test_imports.py +++ b/tests/test_imports.py | |||
| @@ -14,16 +14,18 @@ def get_added_classes(): | |||
| 14 | match = re.match(r"m\.add_class::<(.+)>\(\)\?;", line.strip()) | 14 | match = re.match(r"m\.add_class::<(.+)>\(\)\?;", line.strip()) |
| 15 | if match is not None: | 15 | if match is not None: |
| 16 | classes.append(match.group(1)) | 16 | classes.append(match.group(1)) |
| 17 | continue | ||
| 17 | return classes | 18 | return classes |
| 18 | 19 | ||
| 19 | 20 | ||
| 20 | def test_added_classes(): | 21 | def test_added_classes(): |
| 21 | """ | 22 | """ |
| 22 | Make sure that there's no class that we added in Rust but didn't import in | 23 | Make sure that there's no class that we added in Rust but didn't import in |
| 23 | `__init__.py` and vice versa. | 24 | `__init__.py`. |
| 24 | """ | 25 | """ |
| 25 | added_classes = get_added_classes() | 26 | added_classes = get_added_classes() |
| 26 | assert added_classes == list(adblock.__all__) | 27 | for c in added_classes: |
| 28 | assert c in adblock.__all__ | ||
| 27 | 29 | ||
| 28 | 30 | ||
| 29 | def test_dunder_all_classes_imported(): | 31 | def test_dunder_all_classes_imported(): |
