summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md1
-rw-r--r--src/lib.rs77
-rw-r--r--tests/test_repr.py37
3 files changed, 109 insertions, 6 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e225fa9..ca861ed 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,7 @@ This project adheres to [Semantic Versioning](http://semver.org/) and [Keep a Ch
12 12
13### Changes 13### Changes
14* Updated PyO3 to version `0.13`. 14* Updated PyO3 to version `0.13`.
15* Changed `__repr__` methods of classes to be more idiomatic.
15 16
16### Fixes 17### Fixes
17 18
diff --git a/src/lib.rs b/src/lib.rs
index bef0fb2..54031dd 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -99,8 +99,13 @@ impl Into<BlockerResult> for RustBlockerResult {
99impl PyObjectProtocol for BlockerResult { 99impl PyObjectProtocol for BlockerResult {
100 fn __repr__(&self) -> PyResult<String> { 100 fn __repr__(&self) -> PyResult<String> {
101 Ok(format!( 101 Ok(format!(
102 "BlockerResult({}, {}, {:?}, {:?}, {:?}, {:?})", 102 "BlockerResult(matched={}, important={}, redirect={}, exception={}, filter={}, error={})",
103 self.matched, self.important, self.redirect, self.exception, self.filter, self.error 103 self.matched.diy_python_repr(),
104 self.important.diy_python_repr(),
105 self.redirect.diy_python_repr(),
106 self.exception.diy_python_repr(),
107 self.filter.diy_python_repr(),
108 self.error.diy_python_repr(),
104 )) 109 ))
105 } 110 }
106} 111}
@@ -173,6 +178,7 @@ fn filter_format_from_string(filter_format: &str) -> PyResult<FilterFormat> {
173#[derive(Clone)] 178#[derive(Clone)]
174pub struct FilterSet { 179pub struct FilterSet {
175 filter_set: RustFilterSet, 180 filter_set: RustFilterSet,
181 debug: bool,
176} 182}
177 183
178#[pymethods] 184#[pymethods]
@@ -186,6 +192,7 @@ impl FilterSet {
186 pub fn new(debug: bool) -> Self { 192 pub fn new(debug: bool) -> Self {
187 Self { 193 Self {
188 filter_set: RustFilterSet::new(debug), 194 filter_set: RustFilterSet::new(debug),
195 debug,
189 } 196 }
190 } 197 }
191 198
@@ -215,6 +222,14 @@ impl FilterSet {
215 Ok(()) 222 Ok(())
216 } 223 }
217} 224}
225
226#[pyproto]
227impl PyObjectProtocol for FilterSet {
228 fn __repr__(&self) -> PyResult<String> {
229 Ok(format!("FilterSet(debug={})", self.debug.diy_python_repr()))
230 }
231}
232
218/// Contains cosmetic filter information intended to be injected into a 233/// Contains cosmetic filter information intended to be injected into a
219/// particular hostname. 234/// particular hostname.
220#[pyclass] 235#[pyclass]
@@ -260,12 +275,12 @@ impl Into<UrlSpecificResources> for RustUrlSpecificResources {
260impl PyObjectProtocol for UrlSpecificResources { 275impl PyObjectProtocol for UrlSpecificResources {
261 fn __repr__(&self) -> PyResult<String> { 276 fn __repr__(&self) -> PyResult<String> {
262 Ok(format!( 277 Ok(format!(
263 "UrlSpecificResources<{} hide selectors, {} style selectors, {} exceptions, injected_javascript={:?}, generichide={}>", 278 "UrlSpecificResources<{} hide selectors, {} style selectors, {} exceptions, injected_javascript={}, generichide={}>",
264 self.hide_selectors.len(), 279 self.hide_selectors.len(),
265 self.style_selectors.len(), 280 self.style_selectors.len(),
266 self.exceptions.len(), 281 self.exceptions.len(),
267 self.injected_script, 282 self.injected_script.diy_python_repr(),
268 self.generichide, 283 self.generichide.diy_python_repr(),
269 )) 284 ))
270 } 285 }
271} 286}
@@ -293,6 +308,7 @@ impl PyObjectProtocol for UrlSpecificResources {
293#[text_signature = "($self, filter_set, optimize)"] 308#[text_signature = "($self, filter_set, optimize)"]
294pub struct Engine { 309pub struct Engine {
295 engine: RustEngine, 310 engine: RustEngine,
311 optimize: bool,
296} 312}
297 313
298#[pymethods] 314#[pymethods]
@@ -302,7 +318,7 @@ impl Engine {
302 #[args(filter_set, optimize = true)] 318 #[args(filter_set, optimize = true)]
303 pub fn new(filter_set: FilterSet, optimize: bool) -> Self { 319 pub fn new(filter_set: FilterSet, optimize: bool) -> Self {
304 let engine = RustEngine::from_filter_set(filter_set.filter_set, optimize); 320 let engine = RustEngine::from_filter_set(filter_set.filter_set, optimize);
305 Self { engine } 321 Self { engine, optimize }
306 } 322 }
307 323
308 /// Check if the given `url`—pointing to a resource of type `request_type`— 324 /// Check if the given `url`—pointing to a resource of type `request_type`—
@@ -523,3 +539,52 @@ impl Engine {
523 .hidden_class_id_selectors(&classes, &ids, &exceptions)) 539 .hidden_class_id_selectors(&classes, &ids, &exceptions))
524 } 540 }
525} 541}
542
543#[pyproto]
544impl PyObjectProtocol for Engine {
545 fn __repr__(&self) -> PyResult<String> {
546 Ok(format!(
547 "Engine<optimize={}>",
548 self.optimize.diy_python_repr()
549 ))
550 }
551}
552
553/// PyO3 doesn't offer the ability to get the Python representation of a Rust
554/// object, so we make our own trait.
555trait DiyPythonRepr {
556 fn diy_python_repr(&self) -> String;
557}
558
559impl<T> DiyPythonRepr for Option<T>
560where
561 T: DiyPythonRepr,
562{
563 fn diy_python_repr(&self) -> String {
564 match self {
565 None => "None".to_owned(),
566 Some(x) => x.diy_python_repr(),
567 }
568 }
569}
570
571impl DiyPythonRepr for String {
572 fn diy_python_repr(&self) -> String {
573 let mut res = format!("{:?}", self);
574 // This is safe to do since we know that `res` will always be of
575 // length >= 2.
576 res.replace_range(0..1, "'");
577 res.replace_range(res.len() - 1..res.len(), "'");
578 res
579 }
580}
581
582impl DiyPythonRepr for bool {
583 fn diy_python_repr(&self) -> String {
584 if *self {
585 "True".to_owned()
586 } else {
587 "False".to_owned()
588 }
589 }
590}
diff --git a/tests/test_repr.py b/tests/test_repr.py
new file mode 100644
index 0000000..9c1f961
--- /dev/null
+++ b/tests/test_repr.py
@@ -0,0 +1,37 @@
1import adblock
2import re
3
4
5def assert_acceptable_repr(obj):
6 # Default repr is r"<[A-Za-z]+ object at 0x[0-9a-f]+>"
7 assert "object at" not in repr(obj)
8 assert re.match(r"[A-Z][a-zA-Z]+\(.*\)", repr(obj)) or re.match(
9 r"([A-Z][a-zA-Z]+)?<.*>", repr(obj)
10 )
11
12
13def test_has_nondefault_repr():
14 for b in (True, False):
15 fs = adblock.FilterSet(debug=b)
16 assert_acceptable_repr(fs)
17 assert repr(b) in repr(fs)
18
19 fs.add_filters(["||example.com^"])
20
21 e = adblock.Engine(fs)
22 assert_acceptable_repr(e)
23
24 result = e.check_network_urls(
25 "https://example.com/picture.png", "https://example.net", "image"
26 )
27 assert_acceptable_repr(result)
28 assert repr(result) == (
29 "BlockerResult(matched={}, important={}, redirect={}, exception={}, filter={}, error={})".format(
30 repr(result.matched),
31 repr(result.important),
32 repr(result.redirect),
33 repr(result.exception),
34 repr(result.filter),
35 repr(result.error),
36 )
37 )