summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorÁrni Dagur <agudmundsson@fc-md.umd.edu>2020-03-28 23:15:47 -0400
committerÁrni Dagur <agudmundsson@fc-md.umd.edu>2020-03-28 23:15:47 -0400
commit129bfc10146973ce8a33fcd383c56c4f8071a492 (patch)
tree453a3a4f4776e29859df94c4a97354940e3dc919
first commit
-rw-r--r--.github/workflows/ci.yml72
-rw-r--r--.gitignore243
-rw-r--r--Cargo.toml18
-rw-r--r--pyproject.toml15
-rw-r--r--src/lib.rs181
5 files changed, 529 insertions, 0 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..24f06e1
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,72 @@
1name: CI
2
3on:
4 release:
5 types: [created]
6 push:
7 schedule:
8 # Runs every Thursday at 20:23 GMT to avoid bit rot
9 - cron: "20 23 * * 4"
10
11jobs:
12 lint:
13 runs-on: ubuntu-latest
14 steps:
15 - name: Checkout
16 uses: actions/checkout@v1
17
18 - name: Install latest nightly
19 uses: actions-rs/toolchain@v1
20 with:
21 toolchain: nightly
22 override: true
23
24 - name: Lint with rustfmt
25 uses: actions-rs/cargo@v1
26 with:
27 command: fmt
28
29 - name: Lint with clippy
30 uses: actions-rs/cargo@v1
31 with:
32 command: clippy
33 args: --all-targets --all-features
34
35 build:
36 runs-on: ${{ matrix.os }}
37 needs: lint
38 strategy:
39 fail-fast: false
40 matrix:
41 python-version: [3.5, 3.6, 3.7, 3.8]
42 os: [ubuntu-latest, macos-latest, windows-latest]
43 exclude:
44 # There is a known issue where Python C extensions
45 # can not be built on Python 3.5 on Windows.
46 # https://github.com/actions/virtual-environments/issues/34
47 # Therefore, ujson can't be compiled and the build would fail.
48 - os: windows-latest
49 python-version: 3.5
50
51 steps:
52 - name: Checkout
53 uses: actions/checkout@v1
54
55 - name: Set up Python ${{ matrix.python-version }}
56 uses: actions/setup-python@v1
57 with:
58 python-version: ${{ matrix.python-version }}
59
60 - name: Install dependencies
61 run: |
62 python -m pip install --upgrade pip
63 pip install poetry
64 poetry install
65 - name: Install latest nightly
66 uses: actions-rs/toolchain@v1
67 with:
68 toolchain: nightly
69 override: true
70
71 - name: Build Python package
72 run: poetry run maturin develop --release \ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..fb366a8
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,243 @@
1# -- Rust --
2/target
3Cargo.lock
4
5# -- Editors --
6# Created by https://www.gitignore.io/api/vim,emacs,visualstudiocode
7# Edit at https://www.gitignore.io/?templates=vim,emacs,visualstudiocode
8
9### Emacs ###
10# -*- mode: gitignore; -*-
11*~
12\#*\#
13/.emacs.desktop
14/.emacs.desktop.lock
15*.elc
16auto-save-list
17tramp
18.\#*
19
20# Org-mode
21.org-id-locations
22*_archive
23
24# flymake-mode
25*_flymake.*
26
27# eshell files
28/eshell/history
29/eshell/lastdir
30
31# elpa packages
32/elpa/
33
34# reftex files
35*.rel
36
37# AUCTeX auto folder
38/auto/
39
40# cask packages
41.cask/
42dist/
43
44# Flycheck
45flycheck_*.el
46
47# server auth directory
48/server/
49
50# projectiles files
51.projectile
52
53# directory configuration
54.dir-locals.el
55
56# network security
57/network-security.data
58
59
60### Vim ###
61# Swap
62[._]*.s[a-v][a-z]
63[._]*.sw[a-p]
64[._]s[a-rt-v][a-z]
65[._]ss[a-gi-z]
66[._]sw[a-p]
67
68# Session
69Session.vim
70Sessionx.vim
71
72# Temporary
73.netrwhist
74
75# Auto-generated tag files
76tags
77
78# Persistent undo
79[._]*.un~
80
81# Coc configuration directory
82.vim
83
84### VisualStudioCode ###
85.vscode/*
86!.vscode/settings.json
87!.vscode/tasks.json
88!.vscode/launch.json
89!.vscode/extensions.json
90
91### VisualStudioCode Patch ###
92# Ignore all local history of files
93.history
94
95# End of https://www.gitignore.io/api/vim,emacs,visualstudiocode
96
97# -- Python --
98# Python virtual environment
99.env
100
101# Byte-compiled / optimized / DLL files
102__pycache__/
103*.py[cod]
104*$py.class
105
106# C extensions
107*.so
108
109# Distribution / packaging
110.Python
111build/
112develop-eggs/
113dist/
114downloads/
115eggs/
116.eggs/
117lib/
118lib64/
119parts/
120sdist/
121var/
122wheels/
123pip-wheel-metadata/
124share/python-wheels/
125*.egg-info/
126.installed.cfg
127*.egg
128MANIFEST
129
130# PyInstaller
131# Usually these files are written by a python script from a template
132# before PyInstaller builds the exe, so as to inject date/other infos into it.
133*.manifest
134*.spec
135
136# Installer logs
137pip-log.txt
138pip-delete-this-directory.txt
139
140# Unit test / coverage reports
141htmlcov/
142.tox/
143.nox/
144.coverage
145.coverage.*
146.cache
147nosetests.xml
148coverage.xml
149*.cover
150*.py,cover
151.hypothesis/
152.pytest_cache/
153cover/
154
155# Translations
156*.mo
157*.pot
158
159# Django stuff:
160*.log
161local_settings.py
162db.sqlite3
163db.sqlite3-journal
164
165# Flask stuff:
166instance/
167.webassets-cache
168
169# Scrapy stuff:
170.scrapy
171
172# Sphinx documentation
173docs/_build/
174
175# PyBuilder
176.pybuilder/
177target/
178
179# Jupyter Notebook
180.ipynb_checkpoints
181
182# IPython
183profile_default/
184ipython_config.py
185
186# pyenv
187# For a library or package, you might want to ignore these files since the code is
188# intended to run in multiple environments; otherwise, check them in:
189# .python-version
190
191# pipenv
192# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
193# However, in case of collaboration, if having platform-specific dependencies or dependencies
194# having no cross-platform support, pipenv may install dependencies that don't work, or not
195# install all needed dependencies.
196#Pipfile.lock
197
198# PEP 582; used by e.g. github.com/David-OConnor/pyflow
199__pypackages__/
200
201# Celery stuff
202celerybeat-schedule
203celerybeat.pid
204
205# SageMath parsed files
206*.sage.py
207
208# Environments
209.env
210.venv
211env/
212venv/
213ENV/
214env.bak/
215venv.bak/
216
217# Spyder project settings
218.spyderproject
219.spyproject
220
221# Rope project settings
222.ropeproject
223
224# mkdocs documentation
225/site
226
227# mypy
228.mypy_cache/
229.dmypy.json
230dmypy.json
231
232# Pyre type checker
233.pyre/
234
235# pytype static type analyzer
236.pytype/
237
238# Cython debug symbols
239cython_debug/
240
241# static files generated from Django application using `collectstatic`
242media
243static
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..334176d
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,18 @@
1[package]
2name = "python-adblock"
3version = "0.1.0"
4authors = ["Árni Dagur <arni@dagur.eu"]
5edition = "2018"
6
7# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
8
9[dependencies]
10pyo3 = "0.9"
11adblock = "0.2"
12
13[lib]
14name="adblock"
15crate-type = ["rlib", "cdylib"]
16
17[features]
18default = ["pyo3/extension-module"] \ No newline at end of file
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..79fd056
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,15 @@
1[tool.poetry]
2name = "adblock"
3version = "0.1.0"
4description = "Brave's adblocking in Python"
5authors = ["Árni Dagur <arni@dagur.eu>"]
6license = "Apache-2.0"
7readme = "README.md"
8repository = "https://github.com/ArniDagur/python-adblock"
9keywords = []
10
11[tool.poetry.dependencies]
12python = "^3.5"
13
14[tool.poetry.dev-dependencies]
15maturin = "*"
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..86512f7
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,181 @@
1//! Python bindings for Brave's adblocking library, which is written in Rust.
2#![deny(
3 future_incompatible,
4 nonstandard_style,
5 rust_2018_idioms,
6 missing_copy_implementations,
7 trivial_casts,
8 trivial_numeric_casts,
9 unsafe_code,
10 unused_qualifications
11)]
12
13use pyo3::class::PyObjectProtocol;
14use pyo3::prelude::*;
15
16use adblock::blocker::BlockerResult as RustBlockerResult;
17use adblock::engine::Engine as RustEngine;
18
19/// Brave's adblocking library in Python!
20#[pymodule]
21fn adblock(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
22 m.add("__version__", env!("CARGO_PKG_VERSION"))?;
23 m.add_class::<Engine>()?;
24 Ok(())
25}
26
27#[pyclass]
28pub struct BlockerResult {
29 #[pyo3(get)]
30 pub matched: bool,
31 #[pyo3(get)]
32 pub explicit_cancel: bool,
33 #[pyo3(get)]
34 pub important: bool,
35 #[pyo3(get)]
36 pub redirect: Option<String>,
37 #[pyo3(get)]
38 pub exception: Option<String>,
39 #[pyo3(get)]
40 pub filter: Option<String>,
41 #[pyo3(get)]
42 pub error: Option<String>,
43}
44
45impl Into<BlockerResult> for RustBlockerResult {
46 fn into(self) -> BlockerResult {
47 BlockerResult {
48 matched: self.matched,
49 explicit_cancel: self.explicit_cancel,
50 important: self.important,
51 redirect: self.redirect,
52 exception: self.exception,
53 filter: self.filter,
54 error: self.error,
55 }
56 }
57}
58
59#[pyproto]
60impl PyObjectProtocol for BlockerResult {
61 fn __repr__(&self) -> PyResult<String> {
62 Ok(format!(
63 "BlockerResult({}, {}, {}, {:?}, {:?}, {:?}, {:?})",
64 self.matched,
65 self.explicit_cancel,
66 self.important,
67 self.redirect,
68 self.exception,
69 self.filter,
70 self.error
71 ))
72 }
73}
74
75#[pyclass]
76pub struct Engine {
77 engine: RustEngine,
78}
79
80#[pymethods]
81impl Engine {
82 #[new]
83 pub fn from_rules(network_filters: Vec<String>) -> Self {
84 let engine = RustEngine::from_rules(&network_filters);
85 Self { engine }
86 }
87
88 /// ## Request types
89 /// Examples of valid `request_type` parameters include:
90 /// * `beacon`
91 /// * `csp_report`
92 /// * `document`
93 /// * `font`
94 /// * `media`
95 /// * `object`
96 /// * `script`
97 /// * `stylesheet`
98 /// * and et cetera...
99 pub fn check_network_urls(
100 &self,
101 url: &str,
102 source_url: &str,
103 request_type: &str,
104 ) -> BlockerResult {
105 let blocker_result = self
106 .engine
107 .check_network_urls(url, source_url, request_type);
108 blocker_result.into()
109 }
110
111 pub fn check_network_urls_with_hostnames(
112 &self,
113 url: &str,
114 hostname: &str,
115 source_hostname: &str,
116 request_type: &str,
117 third_party_request: Option<bool>,
118 ) -> BlockerResult {
119 let blocker_result = self.engine.check_network_urls_with_hostnames(
120 url,
121 hostname,
122 source_hostname,
123 request_type,
124 third_party_request,
125 );
126 blocker_result.into()
127 }
128
129 pub fn check_network_urls_with_hostnames_subset(
130 &self,
131 url: &str,
132 hostname: &str,
133 source_hostname: &str,
134 request_type: &str,
135 third_party_request: Option<bool>,
136 previously_matched_rule: bool,
137 force_check_exceptions: bool,
138 ) -> BlockerResult {
139 let blocker_result = self.engine.check_network_urls_with_hostnames_subset(
140 url,
141 hostname,
142 source_hostname,
143 request_type,
144 third_party_request,
145 previously_matched_rule,
146 force_check_exceptions,
147 );
148 blocker_result.into()
149 }
150
151 pub fn filter_exists(&self, filter: &str) -> bool {
152 self.engine.filter_exists(filter)
153 }
154
155 pub fn tags_enable(&mut self, tags: Vec<&str>) {
156 self.engine.tags_enable(&tags);
157 }
158
159 pub fn tags_disable(&mut self, tags: Vec<&str>) {
160 self.engine.tags_disable(&tags);
161 }
162
163 pub fn tag_exists(&self, tag: &str) -> bool {
164 self.engine.tag_exists(tag)
165 }
166
167 // pub fn hidden_class_id_selectors(
168 // &self,
169 // classes: Vec<String>,
170 // ids: Vec<String>,
171 // exceptions: &PySet,
172 // ) -> PyResult<Vec<String>> {
173 // let mut exception_hashset: HashSet<String> = HashSet::new();
174 // for exception in exceptions.iter() {
175 // let exception_pystr = PyString::from(exception);
176 // exception_hashset.insert(exception.to_string());
177 // }
178 // Ok(self.engine
179 // .hidden_class_id_selectors(&classes, &ids, &exception_hashset))
180 // }
181}