python-adblock

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

commit 129bfc10146973ce8a33fcd383c56c4f8071a492
Author: Árni Dagur <agudmundsson@fc-md.umd.edu>
Date:   Sat, 28 Mar 2020 23:15:47 -0400

first commit

Diffstat:
A.github/workflows/ci.yml | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A.gitignore | 243+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ACargo.toml | 19+++++++++++++++++++
Apyproject.toml | 15+++++++++++++++
Asrc/lib.rs | 181+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 531 insertions(+), 0 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml @@ -0,0 +1,72 @@ +name: CI + +on: + release: + types: [created] + push: + schedule: + # Runs every Thursday at 20:23 GMT to avoid bit rot + - cron: "20 23 * * 4" + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v1 + + - name: Install latest nightly + uses: actions-rs/toolchain@v1 + with: + toolchain: nightly + override: true + + - name: Lint with rustfmt + uses: actions-rs/cargo@v1 + with: + command: fmt + + - name: Lint with clippy + uses: actions-rs/cargo@v1 + with: + command: clippy + args: --all-targets --all-features + + build: + runs-on: ${{ matrix.os }} + needs: lint + strategy: + fail-fast: false + matrix: + python-version: [3.5, 3.6, 3.7, 3.8] + os: [ubuntu-latest, macos-latest, windows-latest] + exclude: + # There is a known issue where Python C extensions + # can not be built on Python 3.5 on Windows. + # https://github.com/actions/virtual-environments/issues/34 + # Therefore, ujson can't be compiled and the build would fail. + - os: windows-latest + python-version: 3.5 + + steps: + - name: Checkout + uses: actions/checkout@v1 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install poetry + poetry install + - name: Install latest nightly + uses: actions-rs/toolchain@v1 + with: + toolchain: nightly + override: true + + - name: Build Python package + run: poetry run maturin develop --release +\ No newline at end of file diff --git a/.gitignore b/.gitignore @@ -0,0 +1,243 @@ +# -- Rust -- +/target +Cargo.lock + +# -- Editors -- +# Created by https://www.gitignore.io/api/vim,emacs,visualstudiocode +# Edit at https://www.gitignore.io/?templates=vim,emacs,visualstudiocode + +### Emacs ### +# -*- mode: gitignore; -*- +*~ +\#*\# +/.emacs.desktop +/.emacs.desktop.lock +*.elc +auto-save-list +tramp +.\#* + +# Org-mode +.org-id-locations +*_archive + +# flymake-mode +*_flymake.* + +# eshell files +/eshell/history +/eshell/lastdir + +# elpa packages +/elpa/ + +# reftex files +*.rel + +# AUCTeX auto folder +/auto/ + +# cask packages +.cask/ +dist/ + +# Flycheck +flycheck_*.el + +# server auth directory +/server/ + +# projectiles files +.projectile + +# directory configuration +.dir-locals.el + +# network security +/network-security.data + + +### Vim ### +# Swap +[._]*.s[a-v][a-z] +[._]*.sw[a-p] +[._]s[a-rt-v][a-z] +[._]ss[a-gi-z] +[._]sw[a-p] + +# Session +Session.vim +Sessionx.vim + +# Temporary +.netrwhist + +# Auto-generated tag files +tags + +# Persistent undo +[._]*.un~ + +# Coc configuration directory +.vim + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history + +# End of https://www.gitignore.io/api/vim,emacs,visualstudiocode + +# -- Python -- +# Python virtual environment +.env + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# static files generated from Django application using `collectstatic` +media +static diff --git a/Cargo.toml b/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "python-adblock" +version = "0.1.0" +authors = ["Árni Dagur <arni@dagur.eu"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +pyo3 = "0.9" +adblock = "0.2" + +[lib] +name="adblock" +crate-type = ["rlib", "cdylib"] + +[features] +default = ["pyo3/extension-module"] +\ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml @@ -0,0 +1,15 @@ +[tool.poetry] +name = "adblock" +version = "0.1.0" +description = "Brave's adblocking in Python" +authors = ["Árni Dagur <arni@dagur.eu>"] +license = "Apache-2.0" +readme = "README.md" +repository = "https://github.com/ArniDagur/python-adblock" +keywords = [] + +[tool.poetry.dependencies] +python = "^3.5" + +[tool.poetry.dev-dependencies] +maturin = "*" diff --git a/src/lib.rs b/src/lib.rs @@ -0,0 +1,181 @@ +//! Python bindings for Brave's adblocking library, which is written in Rust. +#![deny( + future_incompatible, + nonstandard_style, + rust_2018_idioms, + missing_copy_implementations, + trivial_casts, + trivial_numeric_casts, + unsafe_code, + unused_qualifications +)] + +use pyo3::class::PyObjectProtocol; +use pyo3::prelude::*; + +use adblock::blocker::BlockerResult as RustBlockerResult; +use adblock::engine::Engine as RustEngine; + +/// Brave's adblocking library in Python! +#[pymodule] +fn adblock(_py: Python<'_>, m: &PyModule) -> PyResult<()> { + m.add("__version__", env!("CARGO_PKG_VERSION"))?; + m.add_class::<Engine>()?; + Ok(()) +} + +#[pyclass] +pub struct BlockerResult { + #[pyo3(get)] + pub matched: bool, + #[pyo3(get)] + pub explicit_cancel: bool, + #[pyo3(get)] + pub important: bool, + #[pyo3(get)] + pub redirect: Option<String>, + #[pyo3(get)] + pub exception: Option<String>, + #[pyo3(get)] + pub filter: Option<String>, + #[pyo3(get)] + pub error: Option<String>, +} + +impl Into<BlockerResult> for RustBlockerResult { + fn into(self) -> BlockerResult { + BlockerResult { + matched: self.matched, + explicit_cancel: self.explicit_cancel, + important: self.important, + redirect: self.redirect, + exception: self.exception, + filter: self.filter, + error: self.error, + } + } +} + +#[pyproto] +impl PyObjectProtocol for BlockerResult { + fn __repr__(&self) -> PyResult<String> { + Ok(format!( + "BlockerResult({}, {}, {}, {:?}, {:?}, {:?}, {:?})", + self.matched, + self.explicit_cancel, + self.important, + self.redirect, + self.exception, + self.filter, + self.error + )) + } +} + +#[pyclass] +pub struct Engine { + engine: RustEngine, +} + +#[pymethods] +impl Engine { + #[new] + pub fn from_rules(network_filters: Vec<String>) -> Self { + let engine = RustEngine::from_rules(&network_filters); + Self { engine } + } + + /// ## Request types + /// Examples of valid `request_type` parameters include: + /// * `beacon` + /// * `csp_report` + /// * `document` + /// * `font` + /// * `media` + /// * `object` + /// * `script` + /// * `stylesheet` + /// * and et cetera... + pub fn check_network_urls( + &self, + url: &str, + source_url: &str, + request_type: &str, + ) -> BlockerResult { + let blocker_result = self + .engine + .check_network_urls(url, source_url, request_type); + blocker_result.into() + } + + pub fn check_network_urls_with_hostnames( + &self, + url: &str, + hostname: &str, + source_hostname: &str, + request_type: &str, + third_party_request: Option<bool>, + ) -> BlockerResult { + let blocker_result = self.engine.check_network_urls_with_hostnames( + url, + hostname, + source_hostname, + request_type, + third_party_request, + ); + blocker_result.into() + } + + pub fn check_network_urls_with_hostnames_subset( + &self, + url: &str, + hostname: &str, + source_hostname: &str, + request_type: &str, + third_party_request: Option<bool>, + previously_matched_rule: bool, + force_check_exceptions: bool, + ) -> BlockerResult { + let blocker_result = self.engine.check_network_urls_with_hostnames_subset( + url, + hostname, + source_hostname, + request_type, + third_party_request, + previously_matched_rule, + force_check_exceptions, + ); + blocker_result.into() + } + + pub fn filter_exists(&self, filter: &str) -> bool { + self.engine.filter_exists(filter) + } + + pub fn tags_enable(&mut self, tags: Vec<&str>) { + self.engine.tags_enable(&tags); + } + + pub fn tags_disable(&mut self, tags: Vec<&str>) { + self.engine.tags_disable(&tags); + } + + pub fn tag_exists(&self, tag: &str) -> bool { + self.engine.tag_exists(tag) + } + + // pub fn hidden_class_id_selectors( + // &self, + // classes: Vec<String>, + // ids: Vec<String>, + // exceptions: &PySet, + // ) -> PyResult<Vec<String>> { + // let mut exception_hashset: HashSet<String> = HashSet::new(); + // for exception in exceptions.iter() { + // let exception_pystr = PyString::from(exception); + // exception_hashset.insert(exception.to_string()); + // } + // Ok(self.engine + // .hidden_class_id_selectors(&classes, &ids, &exception_hashset)) + // } +}