python-adblock

Updated Brave adblock library for Python
Log | Files | Refs | README

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:
MCHANGELOG.md | 3+++
Madblock/__init__.py | 29+++++++++++++++++++++++++++--
Madblock/adblock.pyi | 21+++++++++++++++++++++
Msrc/lib.rs | 40++++++++++++++++++++++++++++++++++++----
Mtests/test_engine.py | 11+++++++++++
Atests/test_exceptions.py | 11+++++++++++
Mtests/test_imports.py | 6++++--
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():